/**
 * Copyright (c) 2002-2010 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.impl;


import com.google.gwt.user.client.rpc.GwtTransient;
import java.util.Collection;

import org.eclipse.emf.common.notify.NotificationChain;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.InvocationTargetException;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.common.util.WrappedException;
import org.eclipse.emf.ecore.EAnnotation;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EModelElement;
import org.eclipse.emf.ecore.ENamedElement;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.resource.Resource.Internal;
import org.eclipse.emf.ecore.util.EObjectContainmentWithInverseEList;
import org.eclipse.emf.ecore.util.InternalEList;


/**
 * <!-- begin-user-doc -->
 * An implementation of the model object '<em><b>EModel Element</b></em>'.
 * <!-- end-user-doc -->
 * <p>
 * The following features are implemented:
 * <ul>
 *   <li>{@link org.eclipse.emf.ecore.impl.EModelElementImpl#getEAnnotations <em>EAnnotations</em>}</li>
 * </ul>
 * </p>
 *
 * @generated not
 */
public abstract class EModelElementImpl extends MinimalEObjectImpl.Container implements EModelElement
{
  /**
   * The cached value of the '{@link #getEAnnotations() <em>EAnnotations</em>}' containment reference list.
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @see #getEAnnotations()
   * @generated
   * @ordered
   */
  @GwtTransient
  protected EList<EAnnotation> eAnnotations;

  /**
   * An extensible set of bit flags.
   */
  @GwtTransient
  protected int eFlags;

  /**
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @generated
   */
  protected EModelElementImpl()
  {
    super();
  }

  /**
   * The bit of {@link #eFlags} that is used to represent being {@link #freeze() frozen}.
   */
  protected static final int EFROZEN = 1;

  /**
   * The last bit used by this class; derived classes may use bit values higher than this.
   */
  protected static final int ELAST_EMODEL_ELEMENT_FLAG = EFROZEN;

  protected void freeze()
  {
    setFrozen();
  }

  protected void freeze(Object eModelElement)
  {
    if (eModelElement instanceof EModelElementImpl)
    { 
      ((EModelElementImpl)eModelElement).freeze();
    }
  }

  protected void setFrozen()
  {
    eFlags |= EFROZEN;
  }

  protected boolean isFrozen()
  {
    return (eFlags & EFROZEN) != 0;
  }

  @Override
  protected void eSetDirectResource(Internal resource)
  {
    assert !isFrozen() : "A frozen model should not be modified";
    super.eSetDirectResource(resource);
  }

  /**
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @generated
   */
  @Override
  protected EClass eStaticClass()
  {
    return EcorePackage.Literals.EMODEL_ELEMENT;
  }

  /**
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @generated
   */
  public EList<EAnnotation> getEAnnotations()
  {
    if (eAnnotations == null)
    {
      eAnnotations = new EObjectContainmentWithInverseEList<EAnnotation>(EAnnotation.class, this, EcorePackage.EMODEL_ELEMENT__EANNOTATIONS, EcorePackage.EANNOTATION__EMODEL_ELEMENT);
    }
    return eAnnotations;
  }

  /**
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @generated NOT
   */
  public EAnnotation getEAnnotation(String source)
  {
    if (eAnnotations != null)
    {
      if (eAnnotations instanceof BasicEList<?>)
      {
        int size = eAnnotations.size();
        if (size > 0)
        {
          EAnnotation [] eAnnotationArray = (EAnnotation[])((BasicEList<?>)eAnnotations).data();
          if (source == null)
          {
            for (int i = 0; i < size; ++i)
            {
              EAnnotation eAnnotation = eAnnotationArray[i];
              if (eAnnotation.getSource() == null)
              {
                return eAnnotation;
              }
            }
          }
          else
          {
            for (int i = 0; i < size; ++i)
            {
              EAnnotation eAnnotation = eAnnotationArray[i];
              if (source.equals(eAnnotation.getSource()))
              {
                return eAnnotation;
              }
            }
          }
        }
      }
      else
      {
        if (source == null)
        {
          for (EAnnotation eAnnotation : eAnnotations)
          {
            if (eAnnotation.getSource() == null)
            {
              return eAnnotation;
            }
          }
        }
        else
        {
          for (EAnnotation eAnnotation : eAnnotations)
          {
            if (source.equals(eAnnotation.getSource()))
            {
              return eAnnotation;
            }
          }
        }
      }
    }

    return null;
  }

  /**
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @generated
   */
  @SuppressWarnings("unchecked")
  @Override
  public NotificationChain eInverseAdd(InternalEObject otherEnd, int featureID, NotificationChain msgs)
  {
    switch (featureID)
    {
      case EcorePackage.EMODEL_ELEMENT__EANNOTATIONS:
        return ((InternalEList<InternalEObject>)(InternalEList<?>)getEAnnotations()).basicAdd(otherEnd, msgs);
    }
    return eDynamicInverseAdd(otherEnd, featureID, msgs);
  }

  /**
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @generated
   */
  @Override
  public NotificationChain eInverseRemove(InternalEObject otherEnd, int featureID, NotificationChain msgs)
  {
    switch (featureID)
    {
      case EcorePackage.EMODEL_ELEMENT__EANNOTATIONS:
        return ((InternalEList<?>)getEAnnotations()).basicRemove(otherEnd, msgs);
    }
    return eDynamicInverseRemove(otherEnd, featureID, msgs);
  }

  /**
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @generated
   */
  @Override
  public Object eGet(int featureID, boolean resolve, boolean coreType)
  {
    switch (featureID)
    {
      case EcorePackage.EMODEL_ELEMENT__EANNOTATIONS:
        return getEAnnotations();
    }
    return eDynamicGet(featureID, resolve, coreType);
  }

  /**
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @generated
   */
  @SuppressWarnings("unchecked")
  @Override
  public void eSet(int featureID, Object newValue)
  {
    switch (featureID)
    {
      case EcorePackage.EMODEL_ELEMENT__EANNOTATIONS:
        getEAnnotations().clear();
        getEAnnotations().addAll((Collection<? extends EAnnotation>)newValue);
        return;
    }
    eDynamicSet(featureID, newValue);
  }

  /**
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @generated
   */
  @Override
  public void eUnset(int featureID)
  {
    switch (featureID)
    {
      case EcorePackage.EMODEL_ELEMENT__EANNOTATIONS:
        getEAnnotations().clear();
        return;
    }
    eDynamicUnset(featureID);
  }

  /**
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @generated
   */
  @Override
  public boolean eIsSet(int featureID)
  {
    switch (featureID)
    {
      case EcorePackage.EMODEL_ELEMENT__EANNOTATIONS:
        return eAnnotations != null && !eAnnotations.isEmpty();
    }
    return eDynamicIsSet(featureID);
  }

  /**
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @generated
   */
  @Override
  public Object eInvoke(int operationID, EList<?> arguments) throws InvocationTargetException
  {
    switch (operationID)
    {
      case EcorePackage.EMODEL_ELEMENT___GET_EANNOTATION__STRING:
        return getEAnnotation((String)arguments.get(0));
    }
    return eDynamicInvoke(operationID, arguments);
  }

  @Override
  public String eURIFragmentSegment(EStructuralFeature eStructuralFeature, EObject eObject)
  {
    if (eObject instanceof ENamedElement)
    {
      ENamedElement eNamedElement = (ENamedElement)eObject;
      String name = eNamedElement.getName();
      if (name != null)
      {
        int count = 0;
        for (Object otherEObject : eContents())
        {
          if (otherEObject == eObject)
          {
            break;
          }
          if (otherEObject instanceof ENamedElement)
          {
            ENamedElement otherENamedElement = (ENamedElement)otherEObject;
            if (name.equals(otherENamedElement.getName()))
            {
              ++count;
            }
          }
        }
        name = eEncodeValue(name);
        return 
          count > 0 ?
            name + "." + count : 
            name;
      }
    }
    else if (eObject instanceof EAnnotation)
    {
      EAnnotation eAnnotation = (EAnnotation)eObject;
      String source = eAnnotation.getSource();
      if (source != null)
      {
        int count = 0;
        for (Object otherEObject : eContents())
        {
          if (otherEObject == eObject)
          {
            break;
          }
          if (otherEObject instanceof EAnnotation)
          {
            EAnnotation otherEAnnotation = (EAnnotation)otherEObject;
            if (source.equals(otherEAnnotation.getSource()))
            {
              ++count;
            }
          }
        }
        
        StringBuffer result = new StringBuffer(source.length() + 5);
        result.append('%');
        result.append(URI.encodeSegment(source,  false));
        result.append('%');
        if (count > 0)
        {
          result.append('.');
          result.append(count);
        }
        return result.toString();
      }
    }
    return super.eURIFragmentSegment(eStructuralFeature, eObject);
  }
  
  @Override
  public EObject eObjectForURIFragmentSegment(String uriFragmentSegment)
  {
    int length = uriFragmentSegment.length();
    if (length > 0)
    {
      // Is the first character a special character, i.e., something other than '@'?
      //
      char firstCharacter = uriFragmentSegment.charAt(0);
      if (firstCharacter != '@')
      {
        // Is it the start of a source URI of an annotation?
        //
        if (firstCharacter == '%')
        {
          // Find the closing '%'
          //
          int index = uriFragmentSegment.lastIndexOf("%");
          boolean hasCount = false;
          if (index == length - 1 || (hasCount = uriFragmentSegment.charAt(index + 1) == '.'))
          {
            // Decode all encoded characters.
            //
            String source = URI.decode(uriFragmentSegment.substring(1, index));
            
            // Check for a count, i.e., a '.' followed by a number.
            //
            int count = 0;
            if (hasCount)
            {
              try
              {
                count = Integer.parseInt(uriFragmentSegment.substring(index + 2));
              }
              catch (NumberFormatException exception)
              {
                throw new WrappedException(exception);
              }
            }
            
            // Look for the annotation with the matching source.
            //
            for (Object object : eContents())
            {
              if (object instanceof EAnnotation)
              {
                EAnnotation eAnnotation = (EAnnotation)object;
                if (source.equals(eAnnotation.getSource()) && count-- == 0)
                {
                  return eAnnotation;
                }
              }
            }
            return null;
          }
        }

        // Look for trailing count.
        //
        int index = uriFragmentSegment.lastIndexOf(".");
        String name = index == -1 ? uriFragmentSegment : uriFragmentSegment.substring(0, index);
        int count = 0;
        if (index != -1)
        {
          try
          {
            count = Integer.parseInt(uriFragmentSegment.substring(index + 1));
          }
          catch (NumberFormatException exception)
          {
            // Interpret it as part of the name.
            //
            name = uriFragmentSegment;
          }
        }

        name = URI.decode(name);
    
        // Look for a matching named element.
        //
        for (Object object : eContents())
        {
          if (object instanceof ENamedElement)
          {
            ENamedElement eNamedElement = (ENamedElement)object;
            if (name.equals(eNamedElement.getName()) && count-- == 0)
            {
              return eNamedElement;
            }
          }
        }
  
        return null;
      }
    }
    
    return super.eObjectForURIFragmentSegment(uriFragmentSegment);
  }

}
