/**
 * Copyright (c) 2002-2009 IBM Corporation and others.
 * All rights reserved.   This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v2.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v20.html
 *
 * Contributors:
 *   IBM - Initial API and implementation
 */
package org.eclipse.emf.ecore.xmi.impl;


import java.util.Map;

import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EFactory;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.InternalEObject;

import org.eclipse.emf.ecore.xmi.XMIResource;
import org.eclipse.emf.ecore.xmi.XMLHelper;
import org.eclipse.emf.ecore.xmi.XMLResource;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;


/**
 * This class is a SAX handler for creating MOF2 objects from an XMI 2.0 file.
 */
public abstract class XMIHandler extends XMLHandler
{
  protected static final String XMI_ELEMENT_TYPE = "xmi";
  protected static final String XMI_UUID = "uuid";
  protected static final String XMI_EXTENSION = "Extension";

  protected final static String XMI_TYPE_ATTRIB = XMIResource.XMI_NS + ":" + XMLResource.TYPE;
  protected final static String ID_ATTRIB = XMIResource.XMI_NS + ":" + XMIResource.XMI_ID;
  protected final static String VERSION_ATTRIB = XMIResource.XMI_NS + ":" + XMIResource.VERSION_NAME;
  protected final static String UUID_ATTRIB = XMIResource.XMI_NS + ":" + XMI_UUID;
  protected final static String XMI_ELEMENT_NAME = XMIResource.XMI_NS + ":" + XMIResource.XMI_TAG_NAME;

  /**
   * Constructor.
   */
  public XMIHandler(XMLResource xmiResource, XMLHelper helper, Map<?, ?> options)
  {
    super(xmiResource, helper, options);

    hrefAttribute = XMLResource.HREF;
    notFeatures.add(VERSION_ATTRIB);
    notFeatures.add(XMI_TYPE_ATTRIB);
    notFeatures.add(UUID_ATTRIB);
  }

  @Override
  protected void processElement(String name, String prefix, String localName)
  {
    if (localName.equals(XMIResource.XMI_TAG_NAME))
    {
      types.push(XMI_ELEMENT_TYPE); 
      String namespace = helper.getURI(XMIResource.XMI_NS);
      if (namespace != null && namespace.startsWith(XMIResource.XMI_NAMESPACE_PREFIX))
      {
        ((XMIResource)xmlResource).setXMIVersion(namespace.substring(XMIResource.XMI_NAMESPACE_PREFIX.length()));
      }  
      isRoot = false;
    }
    else if (isRoot)
    {   
      String namespace = helper.getURI(XMIResource.XMI_NS);
      if (namespace != null && namespace.startsWith(XMIResource.XMI_NAMESPACE_PREFIX))
      {
        ((XMIResource)xmlResource).setXMIVersion(namespace.substring(XMIResource.XMI_NAMESPACE_PREFIX.length()));
      }  
      super.processElement(name, prefix, localName);
    }
    else
    {
      super.processElement(name, prefix, localName);
    }
  }

  @Override
  protected boolean isTextFeatureValue(Object type)
  {
    return super.isTextFeatureValue(type) && type != XMI_ELEMENT_TYPE;
  }

  @Override
  protected EObject createDocumentRoot(String prefix, String uri, String name, EFactory eFactory, boolean top)
  {
    if (extendedMetaData != null && eFactory != null && extendedMetaData.demandedPackages().contains(eFactory.getEPackage()))
    {
      EClass eClass = (EClass)extendedMetaData.demandType(uri, name);
      @SuppressWarnings("deprecation") EObject newObject = useNewMethods ? createObject(eFactory, eClass, true) : helper.createObject(eFactory, name);          
      validateCreateObjectFromFactory(eFactory, name, newObject);
      handleObjectAttribs(newObject);
      if (top)
      {
        processTopObject(newObject);
      }
      return newObject;
    }
    else
    {
      return super.createDocumentRoot(prefix, uri, name, eFactory, top);
    }
  }
  
  @Override
  protected void createObject(EObject peekObject, EStructuralFeature feature)
  {
    String id = attribs.getValue("xmi:idref");
    if (id != null)
    {
      EReference eReference = (EReference)feature;
      if (!eReference.isContainment())
      {
        if (eReference.isMany())
        {
          if (eReference.isResolveProxies() && (!eReference.getEReferenceType().isAbstract() || getXSIType() != null))
          {
            super.createObject(peekObject, feature);
            return;
          }
          else
          {
            SingleReference ref = new SingleReference
                                        (peekObject,
                                         eReference,
                                         id,
                                         -1,
                                         getLineNumber(),
                                         getColumnNumber());
            forwardSingleReferences.add(ref);
          }
        }
        else
        {
          setValueFromId(peekObject, eReference, id);
        }
        objects.push(null);
        mixedTargets.push(null);
        types.push(OBJECT_TYPE);
        return;
      }
    }
    super.createObject(peekObject, feature);
  }

  @Override
  protected void handleUnknownFeature(String prefix, String name, boolean isElement, EObject peekObject, String value)
  {
    if (XMI_EXTENSION.equals(name) && XMLContentHandlerImpl.isXMINamespace(helper.getURI(prefix)))
    {
      if (extendedMetaData == null)
      {
        setExtendedMetaDataOption(Boolean.TRUE);
      }

      recordUnknownFeature(prefix, name, isElement, peekObject, value);
    }
    else if ("idref".equals(name) && XMLContentHandlerImpl.isXMINamespace(helper.getURI(prefix)))
    {
      handleProxy((InternalEObject)peekObject, "#" + value);
    }
    else
    {
      super.handleUnknownFeature(prefix, name, isElement, peekObject, value);
    }
  }

  @Override
  public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException
  {
    if (documentRoot != null)
    {
      EObject eObject = objects.peekEObject();
      if (eObject == documentRoot && (extendedMetaData == null || extendedMetaData.isDocumentRoot(eObject.eClass())))
      {
        types.pop();
        objects.pop();
        mixedTargets.pop();
        documentRoot= null;
      }
    }
    super.startElement(uri, localName, name, attributes);
  }
} // XMIHandler
