/**
 * 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.codegen.ecore.genmodel.impl;


import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import org.eclipse.emf.codegen.ecore.CodeGenEcorePlugin;
import org.eclipse.emf.codegen.ecore.Generator;
import org.eclipse.emf.codegen.ecore.genmodel.GenClass;
import org.eclipse.emf.codegen.ecore.genmodel.GenClassifier;
import org.eclipse.emf.codegen.ecore.genmodel.GenFeature;
import org.eclipse.emf.codegen.ecore.genmodel.GenJDKLevel;
import org.eclipse.emf.codegen.ecore.genmodel.GenModel;
import org.eclipse.emf.codegen.ecore.genmodel.GenModelPackage;
import org.eclipse.emf.codegen.ecore.genmodel.GenOperation;
import org.eclipse.emf.codegen.ecore.genmodel.GenPackage;
import org.eclipse.emf.codegen.ecore.genmodel.GenParameter;
import org.eclipse.emf.codegen.ecore.genmodel.GenProviderKind;
import org.eclipse.emf.codegen.ecore.genmodel.GenRuntimePlatform;
import org.eclipse.emf.codegen.ecore.genmodel.GenRuntimeVersion;
import org.eclipse.emf.codegen.ecore.genmodel.GenTypeParameter;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.NotificationChain;
import org.eclipse.emf.common.util.ECollections;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.Monitor;
import org.eclipse.emf.common.util.UniqueEList;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EEnum;
import org.eclipse.emf.ecore.EGenericType;
import org.eclipse.emf.ecore.EModelElement;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.ETypeParameter;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.impl.ENotificationImpl;
import org.eclipse.emf.ecore.util.EObjectContainmentWithInverseEList;
import org.eclipse.emf.ecore.util.ExtendedMetaData;
import org.eclipse.emf.ecore.util.InternalEList;
import org.eclipse.emf.ecore.xml.type.XMLTypePackage;


/**
 * <!-- begin-user-doc -->
 * An implementation of the model object '<em><b>Gen Class</b></em>'.
 * <!-- end-user-doc -->
 * <p>
 * The following features are implemented:
 * </p>
 * <ul>
 *   <li>{@link org.eclipse.emf.codegen.ecore.genmodel.impl.GenClassImpl#getProvider <em>Provider</em>}</li>
 *   <li>{@link org.eclipse.emf.codegen.ecore.genmodel.impl.GenClassImpl#isImage <em>Image</em>}</li>
 *   <li>{@link org.eclipse.emf.codegen.ecore.genmodel.impl.GenClassImpl#isDynamic <em>Dynamic</em>}</li>
 *   <li>{@link org.eclipse.emf.codegen.ecore.genmodel.impl.GenClassImpl#getEcoreClass <em>Ecore Class</em>}</li>
 *   <li>{@link org.eclipse.emf.codegen.ecore.genmodel.impl.GenClassImpl#getGenFeatures <em>Gen Features</em>}</li>
 *   <li>{@link org.eclipse.emf.codegen.ecore.genmodel.impl.GenClassImpl#getGenOperations <em>Gen Operations</em>}</li>
 *   <li>{@link org.eclipse.emf.codegen.ecore.genmodel.impl.GenClassImpl#getLabelFeature <em>Label Feature</em>}</li>
 * </ul>
 *
 * @generated
 */
public class GenClassImpl extends GenClassifierImpl implements GenClass
{
  /**
   * The default value of the '{@link #getProvider() <em>Provider</em>}' attribute.
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @see #getProvider()
   * @generated
   * @ordered
   */
  protected static final GenProviderKind PROVIDER_EDEFAULT = GenProviderKind.SINGLETON_LITERAL;

  /**
   * The cached value of the '{@link #getProvider() <em>Provider</em>}' attribute.
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @see #getProvider()
   * @generated
   * @ordered
   */
  protected GenProviderKind provider = PROVIDER_EDEFAULT;

  /**
   * The default value of the '{@link #isImage() <em>Image</em>}' attribute.
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @see #isImage()
   * @generated
   * @ordered
   */
  protected static final boolean IMAGE_EDEFAULT = true;

  /**
   * The cached value of the '{@link #isImage() <em>Image</em>}' attribute.
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @see #isImage()
   * @generated
   * @ordered
   */
  protected boolean image = IMAGE_EDEFAULT;

  /**
   * The default value of the '{@link #isDynamic() <em>Dynamic</em>}' attribute.
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @see #isDynamic()
   * @generated
   * @ordered
   */
  protected static final boolean DYNAMIC_EDEFAULT = false;

  /**
   * The cached value of the '{@link #isDynamic() <em>Dynamic</em>}' attribute.
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @see #isDynamic()
   * @generated
   * @ordered
   */
  protected boolean dynamic = DYNAMIC_EDEFAULT;

  /**
   * The cached value of the '{@link #getEcoreClass() <em>Ecore Class</em>}' reference.
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @see #getEcoreClass()
   * @generated
   * @ordered
   */
  protected EClass ecoreClass;

  /**
   * The cached value of the '{@link #getGenFeatures() <em>Gen Features</em>}' containment reference list.
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @see #getGenFeatures()
   * @generated
   * @ordered
   */
  protected EList<GenFeature> genFeatures;

  /**
   * The cached value of the '{@link #getGenOperations() <em>Gen Operations</em>}' containment reference list.
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @see #getGenOperations()
   * @generated
   * @ordered
   */
  protected EList<GenOperation> genOperations;

  /**
   * The cached value of the '{@link #getLabelFeature() <em>Label Feature</em>}' reference.
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @see #getLabelFeature()
   * @generated
   * @ordered
   */
  protected GenFeature labelFeature;

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

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

  /**
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @generated
   */
  public GenProviderKind getProvider()
  {
    return provider;
  }

  public boolean isProviderSingleton()
  {
    return provider == GenProviderKind.SINGLETON_LITERAL;
  }

  /**
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @generated
   */
  public void setProvider(GenProviderKind newProvider)
  {
    GenProviderKind oldProvider = provider;
    provider = newProvider == null ? PROVIDER_EDEFAULT : newProvider;
    if (eNotificationRequired())
      eNotify(new ENotificationImpl(this, Notification.SET, GenModelPackage.GEN_CLASS__PROVIDER, oldProvider, provider));
  }

  /**
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @generated
   */
  public boolean isImage()
  {
    return image;
  }

  /**
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @generated
   */
  public void setImage(boolean newImage)
  {
    boolean oldImage = image;
    image = newImage;
    if (eNotificationRequired())
      eNotify(new ENotificationImpl(this, Notification.SET, GenModelPackage.GEN_CLASS__IMAGE, oldImage, image));
  }

  /**
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @generated
   */
  public boolean isDynamic()
  {
    return dynamic;
  }

  /**
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @generated
   */
  public void setDynamic(boolean newDynamic)
  {
    boolean oldDynamic = dynamic;
    dynamic = newDynamic;
    if (eNotificationRequired())
      eNotify(new ENotificationImpl(this, Notification.SET, GenModelPackage.GEN_CLASS__DYNAMIC, oldDynamic, dynamic));
  }

  /**
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @generated
   */
  public EClass getEcoreClass()
  {
    if (ecoreClass != null && ecoreClass.eIsProxy())
    {
      InternalEObject oldEcoreClass = (InternalEObject)ecoreClass;
      ecoreClass = (EClass)eResolveProxy(oldEcoreClass);
      if (ecoreClass != oldEcoreClass)
      {
        if (eNotificationRequired())
          eNotify(new ENotificationImpl(this, Notification.RESOLVE, GenModelPackage.GEN_CLASS__ECORE_CLASS, oldEcoreClass, ecoreClass));
      }
    }
    return ecoreClass;
  }

  /**
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @generated
   */
  public EClass basicGetEcoreClass()
  {
    return ecoreClass;
  }

  /**
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @generated
   */
  public void setEcoreClass(EClass newEcoreClass)
  {
    EClass oldEcoreClass = ecoreClass;
    ecoreClass = newEcoreClass;
    if (eNotificationRequired())
      eNotify(new ENotificationImpl(this, Notification.SET, GenModelPackage.GEN_CLASS__ECORE_CLASS, oldEcoreClass, ecoreClass));
  }

  /**
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @generated
   */
  public EList<GenFeature> getGenFeatures()
  {
    if (genFeatures == null)
    {
      genFeatures = new EObjectContainmentWithInverseEList<GenFeature>(GenFeature.class, this, GenModelPackage.GEN_CLASS__GEN_FEATURES, GenModelPackage.GEN_FEATURE__GEN_CLASS);
    }
    return genFeatures;
  }

  /**
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @generated
   */
  public EList<GenOperation> getGenOperations()
  {
    if (genOperations == null)
    {
      genOperations = new EObjectContainmentWithInverseEList<GenOperation>(GenOperation.class, this, GenModelPackage.GEN_CLASS__GEN_OPERATIONS, GenModelPackage.GEN_OPERATION__GEN_CLASS);
    }
    return genOperations;
  }

  @Override
  public EClassifier getEcoreClassifier()
  {
    return getEcoreClass();
  }

  @Override
  protected EModelElement basicGetEcoreModelElement()
  {
    return ecoreClass;
  }

  @Override
  public String getImportedMetaType()
  {
    return getGenModel().getImportedName("org.eclipse.emf.ecore.EClass");
  }

  public String getInterfaceName()
  {
    return getGenModel().isSuppressInterfaces() ? getName() : getInterfaceName(getName());
  }

  public String getQualifiedInterfaceName()
  {
    return getInternalQualifiedInterfaceName().replace('$', '.');
  }

  public String getRawQualifiedInterfaceName()
  {
    return getInternalQualifiedInterfaceName(false).replace('$', '.');
  }

  protected String getInternalQualifiedInterfaceName()
  {
    return getInternalQualifiedInterfaceName(getEffectiveComplianceLevel().getValue() >= GenJDKLevel.JDK50);
  }

  protected String getInternalQualifiedInterfaceName(boolean includeTemplateArguments)
  {
    if (isDynamic())
    {
      GenClass genClass = getBaseGenClass();
      return 
        genClass == null ?
          "org.eclipse.emf.ecore.EObject" :
          ((GenClassImpl)genClass).getInternalQualifiedInterfaceName();
    }

    return getEcoreClass().getInstanceClassName() != null ?
      includeTemplateArguments ? getEcoreClass().getInstanceTypeName() : getEcoreClass().getInstanceClassName() :
      getGenPackage().getInterfacePackageName() + "." + getInterfaceName();
  }
  
  public String getRawImportedInstanceClassName()
  {
    return getRawImportedInterfaceName();
  }

  public String getRawInstanceClassName()
  {
    return getRawQualifiedInterfaceName();
  }

  public String getImportedInstanceClassName()
  {
    return getImportedInterfaceName();
  }

  public String getImportedInterfaceName()
  {
    return getGenModel().getImportedName(getInternalQualifiedInterfaceName());
  }

  public String getRawImportedInterfaceName()
  {
    return getGenModel().getImportedName(getInternalQualifiedInterfaceName(false));
  }

  public String getClassName()
  {
    return getGenModel().isSuppressInterfaces() ? getName() : getImplClassName(getName());
  }

  public String getQualifiedClassName()
  {
    return getGenPackage().getClassPackageName() + "." + getClassName();
  }

  public String getImportedClassName()
  {
    return getGenModel().getImportedName(getQualifiedClassName());
  }

  private List<GenClass> baseGenClasses;

  public List<GenClass> getBaseGenClasses()
  {
    if (baseGenClasses == null)
    {
      baseGenClasses = collectGenClasses(getEcoreClass().getESuperTypes(), null);
    }
    return baseGenClasses;
  }

  private List<GenClass> allBaseGenClasses;

  public List<GenClass> getAllBaseGenClasses()
  {
    if (allBaseGenClasses == null)
    {
      allBaseGenClasses = collectGenClasses(getEcoreClass().getEAllSuperTypes(), null);
    }
    return allBaseGenClasses;
  }

  public boolean isRawBaseClass(GenClass baseClass)
  {
    EClass eClass = baseClass.getEcoreClass();
    EList<ETypeParameter> eTypeParameters = eClass.getETypeParameters();
    if (!eTypeParameters.isEmpty())
    {
      for (EGenericType eGenericSuperType : getEcoreClass().getEGenericSuperTypes())
      {
        if (eGenericSuperType.getEClassifier() == eClass)
        {
          return eGenericSuperType.getETypeArguments().isEmpty();
        }
      }
    }
    return false;
  }

  public List<GenClass> getSwitchGenClasses()
  { 
    // Traverse the supertypes to find the maximum depths.
    // Exclude this class itself, which is handled separately in the template.
    //
    Map<EClass, Integer> maxDepths = new LinkedHashMap<EClass, Integer>();
    findMaxSuperTypeDepths(maxDepths, getEcoreClass(), 0);
    maxDepths.remove(getEcoreClass());

    // Order the results in a list of lists, indexed by maximum depth.
    // Exclude EObject, which is handled by the default case.
    //
    List<List<GenClass>> ordered = new ArrayList<List<GenClass>>();
    int resultSize = 0;
    for (Map.Entry<EClass, Integer> entry : maxDepths.entrySet())
    {
      GenClass genClass = findGenClass(entry.getKey());
      if (genClass != null && !genClass.isEObject())
      {
        int depth = entry.getValue();
        while (ordered.size() <= depth)
        {
          ordered.add(new ArrayList<GenClass>());
        }
        ordered.get(depth).add(genClass);
        resultSize++;
      }
    }

    // Traverse the list of lists to build a final ordered result.
    //
    List<GenClass> result = new ArrayList<GenClass>(resultSize);
    for (List<GenClass> genClasses : ordered)
    {
      result.addAll(genClasses);
    }
    return result;
  }

  private void findMaxSuperTypeDepths(Map<EClass, Integer> maxDepths, EClass eClass, int depth)
  {
    Integer existing = maxDepths.get(eClass);
    if (existing != null)
    {
      if (depth > existing)
      {
        maxDepths.put(eClass, depth);
      }
    }
    else
    {
      maxDepths.put(eClass, depth);
      for (EClass base : eClass.getESuperTypes())
      {
        findMaxSuperTypeDepths(maxDepths, base, depth + 1);
      }
    }
  }

  public void addClassPsuedoImports()
  {
    GenModel genModel = getGenModel();
    for (GenClass rootGenClass = this;;)
    {
      GenClass baseGenClass = rootGenClass.getBaseGenClass();
      if (baseGenClass == null)
      {
        String rootExtendsClass = rootGenClass.getGenModel().getRootExtendsClass();
        if ("org.eclipse.emf.ecore.impl.MinimalEObjectImpl$Container".equals(rootExtendsClass) ||
                   "org.eclipse.emf.ecore.impl.MinimalEObjectImpl$Container$Dynamic".equals(rootExtendsClass))
        {
          genModel.addPseudoImport("org.eclipse.emf.ecore.impl.MinimalEObjectImpl.Container");
          genModel.addPseudoImport("org.eclipse.emf.ecore.impl.MinimalEObjectImpl.Container.Dynamic");
        }
        else if (!"org.eclipse.emf.ecore.impl.EObjectImpl".equals(rootExtendsClass))
        {
          genModel.addPseudoImport(rootExtendsClass + ".Container");
        }
        break;
      }
      else
      {
        rootGenClass = baseGenClass;
      }
    }
  }

  public GenClass getBaseGenClass()
  {
    List<EClass> s = getEcoreClass().getESuperTypes();
    return s.isEmpty() ? null : findGenClass(s.get(0));
  }

  public GenClass getClassExtendsGenClass()
  {
    GenClass base = getBaseGenClass();
    while (base != this)
    {
      if (base == null || !base.isInterface()) return base; 
      base = base.getBaseGenClass();
    }
    return null;
  }

  public String getClassExtends()
  {
    String result = getQualifiedClassExtends();
    return "".equals(result) ? "" : " extends " + getGenModel().getImportedName(result);
  }

  public String getQualifiedClassExtends()
  {
    GenClass extendsClass = getClassExtendsGenClass();
    if (extendsClass != null)
    {   
      StringBuilder result = new StringBuilder();
      result.append(extendsClass.getQualifiedClassName());
      if (getEffectiveComplianceLevel().getValue() >= GenJDKLevel.JDK50)
      {
        EClass extendsEClass = extendsClass.getEcoreClass();
        for (EGenericType eGenericType : getEcoreClass().getEAllGenericSuperTypes())
        {
          if (eGenericType.getEClassifier() == extendsEClass)
          {
            result.append(getTypeArguments(this, eGenericType.getETypeArguments(), false));
            break;
          }
        }
      }
      return result.toString();
    }
    else if (!isEObject())
    {
      String rootExtendsClass = getGenModel().getRootExtendsClass();
      if (!isBlank(rootExtendsClass))
      {
        return rootExtendsClass;
      }
    }
    return "";
  }

  public boolean needsRootImplementsInterfaceOperations()
  {
    if (!isMapEntry())
    {
      String rootImplementsInterface = getGenModel().getRootImplementsInterface();
      if (!isBlank(rootImplementsInterface))
      {
        GenClass extendsClass = getClassExtendsGenClass();

        // We assume that the rootExtendsClass already implements it.
        //
        if (extendsClass != null && !rootImplementsInterface.equals(extendsClass.getGenModel().getRootImplementsInterface()))
        {
          return true;
        }
      }
    }
    return false;
  }

  public List<String> getClassImplementsList()
  {
    List<String> result = new UniqueEList<String>();
    GenModel genModel = getGenModel();
    for (String classImplements : getQualifiedClassImplementsList())
    {
      result.add(genModel.getImportedName(classImplements));
    }
    return result;
  }

  public List<String> getQualifiedClassImplementsList()
  {
    List<String> result = new UniqueEList<String>();
    if (isMapEntry())
    {
      if (getEffectiveComplianceLevel().getValue() >= GenJDKLevel.JDK50)
      {
        result.add
          ("org.eclipse.emf.common.util.BasicEMap$Entry" + "<" + 
              getMapEntryKeyFeature().getQualifiedObjectType(this)+ ","  +
              getMapEntryValueFeature().getQualifiedObjectType(this) + ">");
      }
      else
      {
        result.add("org.eclipse.emf.common.util.BasicEMap$Entry");
      }
    }
    else
    {
      if (isExternalInterface() || !getGenModel().isSuppressInterfaces())
      {
        if (getEffectiveComplianceLevel().getValue() >= GenJDKLevel.JDK50 && !getGenTypeParameters().isEmpty())
        {
          StringBuilder stringBuilder = new StringBuilder(getInternalQualifiedInterfaceName());
          stringBuilder.append('<');
          for (Iterator<GenTypeParameter> i = getGenTypeParameters().iterator(); i.hasNext(); )
          {
            GenTypeParameter genTypeParameter = i.next();
            stringBuilder.append(genTypeParameter.getName());
            if (i.hasNext())
            {
              stringBuilder.append(", ");
            }
          }
          stringBuilder.append('>');
          result.add(stringBuilder.toString());
        }
        else
        {
          result.add(getInternalQualifiedInterfaceName());
        }
      }
      String rootImplementsInterface = getGenModel().getRootImplementsInterface();
      if (!isBlank(rootImplementsInterface))
      {
        GenClass extendsClass = getClassExtendsGenClass();

        // We assume that the rootExtendsClass already implements it.
        //
        if (extendsClass != null && !rootImplementsInterface.equals(extendsClass.getGenModel().getRootImplementsInterface()))
        {
          result.add(rootImplementsInterface);
        }
      }
    }

    if (getGenModel().isSuppressInterfaces())
    {
      List<String> interfaceExtends = getQualifiedInterfaceExtendsList();
      GenClassImpl classExtendsClass = (GenClassImpl)getClassExtendsGenClass();
      if (classExtendsClass != null)
      {
        interfaceExtends.removeAll(classExtendsClass.getQualifiedClassImplementsList());
      }
      result.addAll(interfaceExtends);
    }

    return result;
  }

  public String getClassImplements()
  {
    List<String> classImplements = getClassImplementsList();
    if (classImplements.isEmpty())
    {
      return "";
    }

    StringBuffer result = new StringBuffer(" implements ");
    for (Iterator<String> i = classImplements.iterator(); i.hasNext(); )
    {
      result.append(i.next());
      if (i.hasNext()) result.append(", ");
    } 
    return result.toString();
  }

  public boolean needsRootExtendsInterfaceExtendsTag()
  {
    String rootExtendsInterface = getGenModel().getRootExtendsInterface();
    if (rootExtendsInterface == null)
    {
      rootExtendsInterface = "";
    }
    if (isBlank(rootExtendsInterface) || getBaseGenClasses().isEmpty() && getGenPackage().isEcorePackage())
    {
      return false;
    }

    for (GenClass genClass : getAllBaseGenClasses())
    {
      if (genClass.getEcoreClass().getInstanceClassName() == null &&
            rootExtendsInterface.equals(genClass.getGenModel().getRootExtendsInterface()))
      {
        return false;
      }
    }

    return !rootExtendsInterface.equals("org.eclipse.emf.ecore.EObject");
  }

  public List<String> getInterfaceExtendsList()
  {
    List<String> result = new UniqueEList<String>();
    GenModel genModel = getGenModel();
    for (String interfaceExtends : getQualifiedInterfaceExtendsList())
    {
      result.add(genModel.getImportedName(interfaceExtends));
    }
    return result;
  }

  public List<String> getQualifiedInterfaceExtendsList()
  {
    List<String> result = new UniqueEList<String>();

    if (!isExternalInterface())
    {
      String rootExtendsInterface = getGenModel().getRootExtendsInterface();
      if (rootExtendsInterface == null)
      {
        rootExtendsInterface = "";
      } 
      if (getBaseGenClasses().isEmpty())
      {
        if (!isEObject() && !isBlank(rootExtendsInterface))
        {
          result.add(rootExtendsInterface);
        }
        return result;
      }

      boolean needsRootExtendsInterface = true;
      for (GenClass genClass : getAllBaseGenClasses())
      {
        if (genClass.getEcoreClass().getInstanceClassName() == null &&
              rootExtendsInterface.equals(genClass.getGenModel().getRootExtendsInterface()))
        {
          needsRootExtendsInterface = false;
          break;
        }
      }

      if (needsRootExtendsInterface && !isBlank(rootExtendsInterface))
      {
        result.add(rootExtendsInterface);
      }
    }

    boolean includeTypeArguments = getEffectiveComplianceLevel().getValue() >= GenJDKLevel.JDK50;
    for (int i = 0, size = getBaseGenClasses().size(); i < size; i++)
    {
      GenClassImpl genClass = (GenClassImpl)getBaseGenClasses().get(i);
      EGenericType eGenericType = getEcoreClass().getEGenericSuperTypes().get(i);
      if (genClass.isExternalInterface() || genClass.isInterface() || !genClass.getGenModel().isSuppressInterfaces())
      {
        if (includeTypeArguments && !eGenericType.getETypeArguments().isEmpty())
        {
          result.add(genClass.getInternalQualifiedInterfaceName() + getTypeArguments(this, eGenericType.getETypeArguments(), false));
        }
        else
        {
          result.add(genClass.getInternalQualifiedInterfaceName());
        }
      }
    } 

    return result;
  }

  public String getInterfaceExtends()
  {
    List<String> interfaceExtends = getInterfaceExtendsList();
    if (interfaceExtends.isEmpty())
    {
      return "";
    }

    StringBuffer result = new StringBuffer(" extends ");
    for (Iterator<String> i = interfaceExtends.iterator(); i.hasNext(); )
    {
      result.append(i.next());
      if (i.hasNext()) result.append(", ");
    } 
    return result.toString();
  }
  
  public boolean hasGenericSuperTypes()
  {
    if (getEffectiveComplianceLevel().getValue() >= GenJDKLevel.JDK50)
    {
      for (EGenericType eGenericType : getEcoreClass().getEGenericSuperTypes())
      {
        if (eGenericType.getETypeParameter() != null || !eGenericType.getETypeArguments().isEmpty())
        {
          return true;
        }
      }
    }
    return false;
  }

  public String getTypeParameters()
  {
    if (!getGenTypeParameters().isEmpty() && getEffectiveComplianceLevel().getValue() >= GenJDKLevel.JDK50)
    {
      StringBuilder result = new StringBuilder("<");
      for (Iterator<GenTypeParameter> i = getGenTypeParameters().iterator(); i.hasNext(); )
      {
        GenTypeParameter genTypeParameter = i.next();
        result.append(genTypeParameter.getName());
        List<EGenericType> eBounds = genTypeParameter.getEcoreTypeParameter().getEBounds();
        if (!eBounds.isEmpty())
        {
          result.append(" extends ");
          for (Iterator<EGenericType> j = genTypeParameter.getEcoreTypeParameter().getEBounds().iterator(); j.hasNext(); )
          {
            EGenericType eBound = j.next();
            result.append(getTypeArgument(this, eBound, true, false));
            if (j.hasNext())
            {
              result.append(" & ");
            }
          }
        }
        if (i.hasNext())
        {
          result.append(", ");
        }
      }
      
      result.append("> ");
      return result.toString();
    }
    else
    {
      return "";
    }
  }

  public String getClassTypeArguments()
  {
    return getTypeArguments(false, false);
  }

  public String getInterfaceTypeArguments()
  {
    return getTypeArguments(true, false);
  }
  
  @Override
  public String getImportedWildcardInstanceClassName()
  {
    String result = getImportedInstanceClassName();
    if (getEffectiveComplianceLevel().getValue() >= GenJDKLevel.JDK50)
    {
      if (isMapEntry())
      {
        result += "<?, ?>";
      }
      else
      {
        result += getTypeArguments(false, true);
      }
    }
    return result;
  }

  public String getInterfaceWildTypeArguments()
  {
    return getTypeArguments(true, true);
  }
  
  protected String getTypeArguments(boolean isInterface, boolean isWild)
  {
    if (getEffectiveComplianceLevel().getValue() >= GenJDKLevel.JDK50)
    {
      if (!getGenTypeParameters().isEmpty())
      {
        StringBuilder result = new StringBuilder("<");
        for (Iterator<GenTypeParameter> i = getGenTypeParameters().iterator(); i.hasNext(); )
        {
          GenTypeParameter genTypeParameter = i.next();
          result.append(isWild ? "?" : genTypeParameter.getName());
          if (i.hasNext())
          {
            result.append(", ");
          }
        }
        
        result.append('>');
        return result.toString();
      }
      else if (isMapEntry() && isInterface)
      {
        return "<" + getMapEntryKeyFeature().getObjectType(this) + ", " + getMapEntryValueFeature().getObjectType(this) + ">";
      }
    }
    return "";
  }

  public List<GenFeature> getAllGenFeatures()
  {
    return collectGenFeatures(getAllBaseGenClasses(), getGenFeatures(), null);
  }

  public List<GenFeature> getReifiedGenFeatures()
  {
    List<GenFeature> result = new ArrayList<GenFeature>();
    if (getEffectiveComplianceLevel().getValue() >= GenJDKLevel.JDK50)
    {
      GenClass classExtendsGenClass = getClassExtendsGenClass();
      if (classExtendsGenClass != null)
      {
        List<GenFeature> inheritedGenFeatures = classExtendsGenClass.getImplementedGenFeatures();
        GenModel genModel = getGenModel();
        for (GenFeature genFeature : inheritedGenFeatures)
        {
          if ((genFeature.isSet() || 
                 genFeature.isBasicSet() && !genModel.isReflectiveDelegation() || 
                 genFeature.isGet() && 
                   genFeature.isListType() && 
                   !genModel.isDynamicDelegation() && 
                   !genModel.isReflectiveDelegation() &&
                   !genFeature.hasSettingDelegate() &&
                   !genFeature.isVolatile()) &&
                isReifiedType(classExtendsGenClass, genFeature.getEcoreFeature().getEGenericType()))
          {
            result.add(genFeature);
          }
        }
      }
    }
    return result;
  }

  public boolean isReifiedType(GenClass sourceContext, EGenericType eGenericType)
  {
    String baseType = getTypeArgument(sourceContext, eGenericType, false, true);
    String reifiedType = getTypeArgument(this, eGenericType, false, true);
    return !baseType.equals(reifiedType);
  }

  public List<GenFeature> getInheritedGenFeatures()
  {
    return collectGenFeatures(getAllBaseGenClasses(), null, null);
  }

  public List<GenOperation> getAllGenOperations()
  {
    return getAllGenOperations(true);
  }

  public List<GenOperation> getAllGenOperations(boolean excludeOverrides)
  {
    return collectGenOperations(this, getAllBaseGenClasses(), getGenOperations(), null, excludeOverrides);
  }

  public String getFeatureID(GenFeature genFeature)
  {
    return getClassifierID() + "__" + format(genFeature.getName(), '_', null, false, false).toUpperCase(getGenModel().getLocale());
  }

  public String getQualifiedFeatureID(GenFeature genFeature)
  {
    return getGenPackage().getImportedPackageInterfaceName() + "." + getFeatureID(genFeature);
  }

  public String getOperationID(GenOperation genOperation)
  {
    return getOperationID(genOperation, true);
  }

  public String getFeatureValue(GenFeature genFeature)
  {
    List<GenFeature> allFeatures = getAllGenFeatures();
    int i = allFeatures.indexOf(genFeature);
    GenClass base = getBaseGenClass();

    if (base == null)
    {
      return Integer.toString(i);
    }

    int baseCount = base.getFeatureCount();    
    if (i < baseCount)
    {
      return getGenPackage() == base.getGenPackage() ?
        base.getFeatureID(genFeature) : base.getQualifiedFeatureID(genFeature);
    }

    String baseCountID = getGenPackage() == base.getGenPackage() ?
      base.getFeatureCountID() : base.getQualifiedFeatureCountID();
    return baseCountID + " + " + Integer.toString(i - baseCount);
  }

  public String getLocalFeatureIndex(GenFeature genFeature)
  {
    return Integer.toString(getEcoreClass().getEStructuralFeatures().indexOf(genFeature.getEcoreFeature()));
  }

  private class OperationHelper extends GenBaseImpl.UniqueNameHelper
  {
    @Override
    protected String getName(Object o)
    {
      GenOperation genOperation = (GenOperation)o;
      return genOperation.getCapName() + (genOperation.getGenParameters().size() > 0 ? "__" : "") + genOperation.getParameterTypes("_", false);
    }
  }

  private OperationHelper operationHelper;

  @Override
  public void clearCache()
  {
    super.clearCache();

    getAccessorOperations = null;
    setAccessorOperations = null;
    isSetAccessorOperations = null;
    unsetAccessorOperations = null;

    operationHelper = null;
    genClassConstraints = null;
    baseGenClasses = null;
    allBaseGenClasses = null;
    implementedGenOperations = null;
  }

  public String getUniqueName(GenOperation genOperation)
  {
    if (operationHelper == null)
    {
      operationHelper = new OperationHelper();
      
      // Need to ensure that the cached names are computed in the same order and manner as they are in the generated package interface.
      //
      for (GenOperation otherGenOperation : getAllGenOperations(false))
      {
        if (getOverrideGenOperation(otherGenOperation) == null)
        {
          operationHelper.getUniqueName(otherGenOperation);
        }
      }
    }
    return operationHelper.getUniqueName(genOperation);
  }

  public String getOperationID(GenOperation genOperation, boolean diagnosticCode)
  {
    if (diagnosticCode)
    {
      return getClassifierID() + "__" + format(genOperation.getName(), '_', null, false, false).toUpperCase(getGenModel().getLocale());
    }
    else
    {
      String uniqueName = getUniqueName(genOperation);
      return getClassifierID() + "___" + (format(genOperation.getName(), '_', null, false, false) + (genOperation.getGenParameters().size() > 0 ? uniqueName.substring(uniqueName.indexOf("__")) : "")).toUpperCase(getGenModel().getLocale());
    }
  }

  public String getQualifiedOperationID(GenOperation genOperation)
  {
    return getGenPackage().getImportedPackageInterfaceName() + "." + getOperationID(genOperation, false);
  }

  public String getOperationValue(GenOperation genOperation)
  {
    List<GenOperation> allOperations = getAllGenOperations(false);
    int i = allOperations.indexOf(genOperation);
    GenClass base = getBaseGenClass();

    if (base == null)
    {
      return Integer.toString(i);
    }

    int baseCount = base.getOperationCount();    
    if (i < baseCount)
    {
      return getGenPackage() == base.getGenPackage() ?
        base.getOperationID(genOperation, false) : base.getQualifiedOperationID(genOperation);
    }

    String baseCountID = getGenPackage() == base.getGenPackage() ?
      base.getOperationCountID() : base.getQualifiedOperationCountID();
    return baseCountID + " + " + Integer.toString(i - baseCount);
  }

  public String getLocalOperationIndex(GenOperation genOperation)
  {
    return Integer.toString(getEcoreClass().getEOperations().indexOf(genOperation.getEcoreOperation()));
  }

  public String getFlagsField(GenFeature genFeature)
  {
    if (isFlag(genFeature))
    {
      String flagsField = getImplementingGenModel(genFeature).getBooleanFlagsField();
      if (!isBlank(flagsField))
      {
        int flagIndex = getFlagIndex(genFeature);
        if (flagIndex / 32 > 0)
        {
          flagsField += String.valueOf(flagIndex / 32);
        }
        return flagsField;
      }
    }
    return null;
  }

  public int getFlagIndex(GenFeature genFeature)
  {
    if (isFlag(genFeature))
    {
      int reservedBooleanFlags = getImplementingGenModel(genFeature).getBooleanFlagsReservedBits();
      int index = reservedBooleanFlags > 0 ? reservedBooleanFlags : 0;

      for (GenFeature otherGenFeature : getAllGenFeatures())
      {
        if (isFlag(otherGenFeature))
        {
          // If the flag will straddle two fields, bump it to the next one.
          //
          int flagSize = getFlagSize(otherGenFeature);
          if (index / 32 != (index + flagSize - 1) / 32)
          {
            index = ((index / 32) + 1) * 32;
          }

          if (otherGenFeature.getEcoreFeature() == genFeature.getEcoreFeature())
          {
            return index;
          }

          index += flagSize;
        }
        if (isESetFlag(otherGenFeature))
        {
          index++;
        }
      }
    }
    return -1;
  }

  public int getFlagSize(GenFeature genFeature)
  {
    if (isFlag(genFeature))
    {
      if (genFeature.isBooleanType())
      {
        return 1;
      }
      else if (genFeature.isEnumType())
      {
        int choices = genFeature.getTypeGenEnum().getEcoreEnum().getELiterals().size();
        switch (choices)
        {
          case 0:
          case 1:
            return choices;
          default:
            int size = 0;
            for (choices--; choices >= 1; choices >>= 1)
            {
              size++;
            }
            return size;
        }
      }
    }
    return 0;
  }

  public String getFlagMask(GenFeature genFeature)
  {
    int size = getFlagSize(genFeature);
    if (size == 0)
    {
      return "0";
    }

    int mask = (1 << size) - 1;
    return mask == 1 ? "1" : "0x" + Integer.toHexString(mask);
  }

  public String getESetFlagsField(GenFeature genFeature)
  {
    if (isESetFlag(genFeature))
    {
      String isSetFlagsField = getImplementingGenModel(genFeature).getBooleanFlagsField();
      if (!isBlank(isSetFlagsField))
      {
        int isSetFlagIndex = getESetFlagIndex(genFeature);
        if (isSetFlagIndex / 32 > 0)
        {
          isSetFlagsField += String.valueOf(isSetFlagIndex / 32);
        }
        return isSetFlagsField;
      }
    }
    return null;
  }

  public int getESetFlagIndex(GenFeature genFeature)
  {
    if (isESetFlag(genFeature))
    {
      int reservedBooleanFlags = getImplementingGenModel(genFeature).getBooleanFlagsReservedBits();
      int index = reservedBooleanFlags > 0 ? reservedBooleanFlags : 0;

      for (GenFeature otherGenFeature : getAllGenFeatures())
      {
        if (isFlag(otherGenFeature))
        {
          // If the flag will straddle two fields, bump it to the next one.
          //
          int flagSize = getFlagSize(otherGenFeature);
          if (index / 32 != (index + flagSize - 1) / 32)
          {
            index = ((index / 32) + 1) * 32;
          }
          index += flagSize;
        }
        if (isESetFlag(otherGenFeature))
        {
          if (otherGenFeature.getEcoreFeature() == genFeature.getEcoreFeature())
          {
            return index;
          }
          index++;
        }
      }
    }
    return -1;
  }

  public String getFeatureCountID()
  {
    return getClassifierID() + "_FEATURE_COUNT";
  }

  public String getQualifiedFeatureCountID()
  {
    return getGenPackage().getImportedPackageInterfaceName() + "." + getFeatureCountID();
  }

  public String getFeatureCountValue()
  {
    GenClass base = getBaseGenClass();
    if (base == null)
    {
      return Integer.toString(getFeatureCount());
    }

    String baseCountID = getGenPackage() == base.getGenPackage() ?
      base.getFeatureCountID() : base.getQualifiedFeatureCountID();
    return baseCountID + " + " + Integer.toString(getFeatureCount() - base.getFeatureCount());
  }

  public int getFeatureCount()
  {
    return getAllGenFeatures().size();
  }

  public String getOperationCountID()
  {
    return getClassifierID() + "_OPERATION_COUNT";
  }

  public String getQualifiedOperationCountID()
  {
    return getGenPackage().getImportedPackageInterfaceName() + "." + getOperationCountID();
  }

  public String getOperationCountValue()
  {
    GenClass base = getBaseGenClass();
    if (base == null)
    {
      return Integer.toString(getOperationCount());
    }

    String baseCountID = getGenPackage() == base.getGenPackage() ?
      base.getOperationCountID() : base.getQualifiedOperationCountID();
    return baseCountID + " + " + Integer.toString(getOperationCount() - base.getOperationCount());
  }

  public int getOperationCount()
  {
    return getAllGenOperations(false).size();
  }

  public GenOperation getOverrideGenOperation(GenOperation genOperation)
  {
    List<GenOperation> allGenOperations = getAllGenOperations(false);
    int index = allGenOperations.indexOf(genOperation);
    if (index != -1)
    {
      for (int i = allGenOperations.size() - 1; i > index; --i)
      {
        GenOperation otherGenOperation = allGenOperations.get(i);
        if (otherGenOperation.isOverrideOf(this, genOperation))
        {
          return otherGenOperation;
        }
      }
    }
    return null;
  }

  public List<GenOperation> getOverrideGenOperations(final List<GenOperation> baseGenOperations, List<GenOperation> derivedGenOperations)
  {
    return collectGenOperations(this, null, derivedGenOperations, new GenOperationFilter()
    {
      public boolean accept(GenOperation genOperation)
      {
        for (GenOperation baseGenOperation : baseGenOperations)
        {
          if (genOperation.isOverrideOf(GenClassImpl.this, baseGenOperation))
          {
            return true;
          }
        }
        return false;
      }
    }, false);
  }

  public boolean isEObject()
  {
    return "EObject".equals(getName()) && getGenPackage().isEcorePackage();
  }

  public boolean isEObjectExtension()
  {
    if (isMapEntry())
    {
      return false;
    }
    else
    {
      if (isExternalInterface())
      {
        return false;
      }

      for (GenClass genClass : getAllBaseGenClasses())
      {
        if (genClass.isEObjectExtension())
        {
          return true;
        }
      }
  
      return getGenPackage().isEcorePackage() ||
        (!isExternalInterface() && "org.eclipse.emf.ecore.EObject".equals(getGenModel().getRootExtendsInterface()));
    }
  }

  public String getCastFromEObject()
  {
    String qualifiedInterfaceName = getQualifiedInterfaceName();
    return 
      !"org.eclipse.emf.ecore.EObject".equals(qualifiedInterfaceName) ?
      "(" + getGenModel().getImportedName(qualifiedInterfaceName) + ")" :
      "";
  }

  public boolean isAbstract()
  {
    // An interface should be abstract, but this makes sure of that fact.
    //
    return getEcoreClass().isAbstract() || getEcoreClass().isInterface();
  }

  public String getAbstractFlag()
  {
    String result = !isAbstract() ? "!" : "";
    return result + "IS_ABSTRACT";
  }

  public boolean isInterface()
  {
    return getEcoreClass().isInterface();
  }

  public String getInterfaceFlag()
  {
    String result = !getEcoreClass().isInterface() ? "!" : "";
    return result + "IS_INTERFACE";
  }

  public String getGeneratedInstanceClassFlag()
  {
    String result = isExternalInterface() || isDynamic() ? "!" : "";
    return result + "IS_GENERATED_INSTANCE_CLASS";
  }

  public boolean isExternalInterface()
  {
    EClass eClass = getEcoreClass();
    return 
      eClass.eIsSet(EcorePackage.Literals.ECLASSIFIER__INSTANCE_CLASS_NAME) || 
        eClass.eIsSet(EcorePackage.Literals.ECLASSIFIER__INSTANCE_TYPE_NAME);
  }

  public boolean isMapEntry()
  {
    return 
      isJavaUtilMapEntry(getEcoreClass().getInstanceClassName()) &&
      getEcoreClass().getEStructuralFeature("key") != null &&
      getEcoreClass().getEStructuralFeature("value") != null;
  }

  public GenFeature getMapEntryKeyFeature()
  {
    return findGenFeature(getEcoreClass().getEStructuralFeature("key"));
  }

  public GenFeature getMapEntryValueFeature()
  {
    return findGenFeature(getEcoreClass().getEStructuralFeature("value"));
  }

  public List<GenClass> getImplementedGenClasses()
  {
    List<GenClass> allBases = getAllBaseGenClasses();
    GenClass extendedBase = getClassExtendsGenClass();
    List<GenClass> result = 
      extendedBase == null ?
        new ArrayList<GenClass>(allBases) :
        new ArrayList<GenClass>(allBases.subList(allBases.indexOf(extendedBase) + 1, allBases.size()));
    result.add(this);
    return result;
  }

  public List<GenFeature> getImplementedGenFeatures()
  {
    return collectGenFeatures(getImplementedGenClasses(), null, null);
  }

  public GenModel getImplementingGenModel(GenFeature genFeature)
  {
    if (getImplementedGenFeatures().contains(genFeature))
    {
      return getGenModel();
    }
    else
    {
      GenClass classExtendsGenClass = getClassExtendsGenClass();
      return classExtendsGenClass == null ? genFeature.getGenModel() : classExtendsGenClass.getImplementingGenModel(genFeature);
    }
  }

  private List<GenOperation> implementedGenOperations;

  public List<GenOperation> getImplementedGenOperations()
  {
    if (implementedGenOperations == null)
    {
      EList<GenClass> implementedGenClasses = new UniqueEList<GenClass>(getImplementedGenClasses());
      ECollections.reverse(implementedGenClasses);
      if (needsRootImplementsInterfaceOperations())
      {
        GenClass rootImplementsInterface = getGenModel().getRootImplementsInterfaceGenClass();
        if (rootImplementsInterface != null)
        {
          List<GenClass> allBaseClasses = new UniqueEList<GenClass>(rootImplementsInterface.getAllBaseGenClasses());
          for (Iterator<GenClass> i = allBaseClasses.iterator(); i.hasNext(); )
          {
            GenClass genClass = i.next();
            if (genClass.isEObject())
            {
              i.remove();
            }
          }
          allBaseClasses.add(rootImplementsInterface);
          implementedGenClasses.addAll(allBaseClasses);
        }
      }
      implementedGenOperations =
        collectGenOperations
          (this,
           implementedGenClasses,
           null, 
           new CollidingGenOperationFilter());
    }
    return implementedGenOperations;
  }

  public boolean hasImplementedToStringGenOperation()
  {
    for (GenOperation genOperation : getImplementedGenOperations())
    {
      if ("toString".equals(genOperation.getName()) && genOperation.getGenParameters().isEmpty())
      {
        return true;
      }
    }
    return false;
  }

  public List<GenClass> getExtendedGenClasses()
  {
    List<GenClass> allBases = getAllBaseGenClasses();
    GenClass extendedBase = getClassExtendsGenClass();
    int i = extendedBase == null ? 0 : allBases.indexOf(extendedBase) + 1;
    return new ArrayList<GenClass>(allBases.subList(0, i));
  }

  public List<GenFeature> getExtendedGenFeatures()
  {
    return collectGenFeatures(getExtendedGenClasses(), null, null);
  }

  public List<GenOperation> getExtendedGenOperations()
  {
    return
      collectGenOperations
        (this,
         getExtendedGenClasses(),
         null, 
         new CollidingGenOperationFilter());
  }

  public List<GenFeature> getDeclaredGenFeatures()
  {
    return getGenFeatures();
  }

  public List<GenOperation> getDeclaredGenOperations()
  {
    return
      collectGenOperations
        (this,
         Collections.<GenClass>singletonList(this),
         null, 
         new GenOperationFilter()
         {
           public boolean accept(GenOperation genOperation)
           {
             return !genOperation.isSuppressedVisibility();
           }
         });
  }

  public List<GenFeature> getFlagGenFeatures()
  {
    return collectGenFeatures(null, getImplementedGenFeatures(), new GenFeatureFilter()
      {
        public boolean accept(GenFeature genFeature)
        {
          return isFlag(genFeature);
        }
      });
  }

  public List<GenFeature> getFlagGenFeatures(final String staticDefaultValue)
  {
    return collectGenFeatures(null, getFlagGenFeatures(), new GenFeatureFilter()
      {
        public boolean accept(GenFeature genFeature)
        {
          return staticDefaultValue.equalsIgnoreCase(genFeature.getStaticDefaultValue());
        }
      });
  }

  public List<GenFeature> getFlagGenFeaturesWithDefault()
  {
    return collectGenFeatures(null, getFlagGenFeatures(), new GenFeatureFilter()
      {
        public boolean accept(GenFeature genFeature)
        {
          if (genFeature.hasEDefault())
          {
            if (genFeature.isBooleanType())
            {
              return "true".equals(genFeature.getStaticDefaultValue());
            }
            else if (genFeature.isEnumType())
            {
              EEnum eEnum = genFeature.getTypeGenEnum().getEcoreEnum();
              return eEnum.getELiterals().indexOf(eEnum.getEEnumLiteralByLiteral(genFeature.getEcoreFeature().getDefaultValueLiteral())) > 0;
            }
          }
          return false;
        }
      });
  }

  public List<GenFeature> getEGetGenFeatures()
  {
    return getGenModel().isMinimalReflectiveMethods() ? getImplementedGenFeatures() : getAllGenFeatures();
  }

  public List<GenFeature> getEIsSetGenFeatures()
  {
    return getEGetGenFeatures();
  }

  public List<GenFeature> getESetGenFeatures()
  {
    return 
      collectGenFeatures
        (null, 
         getGenModel().isMinimalReflectiveMethods() ? getImplementedGenFeatures() : getAllGenFeatures(), 
         new GenFeatureFilter() 
         {
           public boolean accept(GenFeature genFeature) 
           {
             return genFeature.isChangeable();
           }
         });
  }

  public List<GenFeature> getEUnsetGenFeatures()
  {
    return getESetGenFeatures();
  }

  public List<GenFeature> getEInverseAddGenFeatures()
  {
    return 
     collectGenFeatures
       (null, 
        getGenModel().isMinimalReflectiveMethods() ? getImplementedGenFeatures() : getAllGenFeatures(), 
        new GenFeatureFilter() 
        {
          public boolean accept(GenFeature genFeature) 
          {
            return genFeature.isBidirectional() && (!genFeature.isVolatile() || genFeature.hasDelegateFeature());
          }
        });
  }

  public List<GenFeature> getEInverseRemoveGenFeatures()
  {
    return 
      collectGenFeatures
        (null, 
         getGenModel().isMinimalReflectiveMethods() ? getImplementedGenFeatures() : getAllGenFeatures(), 
         new GenFeatureFilter() 
         {
           public boolean accept(GenFeature genFeature) 
           {
             return genFeature.isEffectiveContains() ||
               (genFeature.isBidirectional() &&
                  (!genFeature.getReverse().isVolatile() || genFeature.getReverse().hasDelegateFeature())) ||
               genFeature.isFeatureMapType();
           }
         });
  }

  public List<GenFeature> getEBasicRemoveFromContainerGenFeatures()
  {
    return 
      collectGenFeatures
        (null, 
         getGenModel().isMinimalReflectiveMethods() ? getImplementedGenFeatures() : getAllGenFeatures(), 
         new GenFeatureFilter() 
         {
           public boolean accept(GenFeature genFeature) 
           {
             return genFeature.isContainer(); 
           }
         });
  }

  public List<GenFeature> getToStringGenFeatures()
  {
    return 
      collectGenFeatures
        (getImplementedGenClasses(), 
         null, 
         new GenFeatureFilter()
         {
           public boolean accept(GenFeature genFeature)
           {
             return genFeature.isField() && !genFeature.isReferenceType();
           }
         });
  }

  public List<GenClass> getMixinGenClasses()
  {
    // Simple cases: no mix-ins for no inheritance or for a single base class.
    //
    List<EClass> superTypes = getEcoreClass().getESuperTypes();
    if (superTypes.isEmpty() || (superTypes.size() == 1 && !superTypes.get(0).isInterface()))
    {
      return Collections.emptyList();
    }

    List<GenClass> allBases = getAllBaseGenClasses();
    List<GenClass> result = new ArrayList<GenClass>(allBases.size());

    // If extending an interface, its mix-ins must be included, since there is no implementation to handle them.
    //
    GenClass baseGenClass = getBaseGenClass();
    if (baseGenClass.isInterface())
    {
      result.addAll(baseGenClass.getMixinGenClasses());
    }

    // Mix-ins are everything after the base class.
    //
    int i = allBases.indexOf(baseGenClass) + 1;
    result.addAll(allBases.subList(i, allBases.size()));
    return result;
  }

  public List<GenFeature> getMixinGenFeatures()
  {
      return collectGenFeatures(getMixinGenClasses(), null, null);  
  }

  public List<GenOperation> getMixinGenOperations()
  {
    return collectGenOperations(this, getMixinGenClasses(), null, new CollidingGenOperationFilter());
  }

  public void initialize(EClass eClass)
  {
    if (eClass != getEcoreClass())
    {
      setEcoreClass(eClass);

      if (getLabelFeatureGen() != null && getLabelFeatureGen().eIsProxy())
      {
        setLabelFeature(null);
      }

      setImage(!eClass.isAbstract());
    }
    
    List<ETypeParameter> typeParameters = eClass.getETypeParameters();
    LOOP:
    for (int i = 0; i < typeParameters.size(); ++i) 
    {
      ETypeParameter typeParameter = typeParameters.get(i);

      for (int j = 0; j < getGenTypeParameters().size(); ++j)
      {
        GenTypeParameter genTypeParameter = getGenTypeParameters().get(j);
        if (genTypeParameter.getEcoreTypeParameter() == typeParameter)
        {
          genTypeParameter.initialize(typeParameter);
          if (i != j)
          {
            getGenTypeParameters().move(i, j);
          }

          continue LOOP;
        }
      }

      GenTypeParameter genTypeParameter = getGenModel().createGenTypeParameter();
      getGenTypeParameters().add(i, genTypeParameter);
      genTypeParameter.initialize(typeParameter);
    }

    int localFeatureIndex = 0;
    LOOP:
    for (EStructuralFeature eStructuralFeature : eClass.getEStructuralFeatures())
    {
      if (eStructuralFeature instanceof EAttribute)
      {
        EAttribute attribute = (EAttribute)eStructuralFeature;
        for (GenFeature genFeature : getGenFeatures())
        {
          if (genFeature.getEcoreFeature() == attribute)
          {
            genFeature.initialize(attribute);
            getGenFeatures().move(localFeatureIndex++, genFeature);
            continue LOOP;
          }
        }

        GenFeature genFeature = getGenModel().createGenFeature();
        getGenFeatures().add(localFeatureIndex++, genFeature);
        genFeature.initialize(attribute);
      }
      else
      {
        EReference reference = (EReference)eStructuralFeature;
        for (GenFeature genFeature : getGenFeatures())
        {
          if (genFeature.getEcoreFeature() == reference)
          {
            genFeature.initialize(reference);
            getGenFeatures().move(localFeatureIndex++, genFeature);
            continue LOOP;
          }
        }

        GenFeature genFeature = getGenModel().createGenFeature();
        getGenFeatures().add(localFeatureIndex++, genFeature);
        genFeature.initialize(reference);
      }
    }
    
    int localOperationIndex = 0;
    OPERATION_LOOP:
    for (EOperation operation : eClass.getEOperations())
    {
      for (GenOperation genOperation : getGenOperations())
      {
        if (genOperation.getEcoreOperation() == operation)
        {
          genOperation.initialize(operation);
          getGenOperations().move(localOperationIndex++, genOperation);
          continue OPERATION_LOOP;
        }
      }

      GenOperation genOperation = getGenModel().createGenOperation();
      getGenOperations().add(localOperationIndex++, genOperation);
      genOperation.initialize(operation);
    }
  }

  @Override
  protected boolean hasModelContribution()
  {
    return true;
  }

  /**
   * @deprecated In EMF 2.2, a {@link org.eclipse.emf.codegen.ecore.generator.Generator Generator} should be used to generate code.
   * This method will be removed after 2.2.
   */
  @Override
  @Deprecated
  public void generate(Monitor progressMonitor)
  {
    try
    {
      if (!canGenerate()) return;

      int fileCount = isInterface() ? 1 : 2;
      if (isExternalInterface()) fileCount--;

      progressMonitor.beginTask("", fileCount);
      progressMonitor.subTask(CodeGenEcorePlugin.INSTANCE.getString("_UI_Generating_message", new Object [] { getFormattedName() }));

      if (!isExternalInterface() && (!getGenModel().isSuppressInterfaces() || isInterface()))
      {
        progressMonitor.subTask
          (CodeGenEcorePlugin.INSTANCE.getString
             ("_UI_GeneratingJavaInterface_message", 
              new Object [] { getGenPackage().getInterfacePackageName() + "." + getInterfaceName() }));
        generate
          (createMonitor(progressMonitor, 1), 
           Generator.EMF_MODEL_PROJECT_STYLE, 
           getGenModel().getEffectiveModelPluginVariables(), 
           getGenModel().getModelDirectory(), 
           getGenPackage().getInterfacePackageName(), 
           getInterfaceName(), 
           getGenModel().getClassEmitter(),
           new Object [] { new Object [] { this, Boolean.TRUE, Boolean.FALSE }});
      }

      if (!isInterface())
      {
        progressMonitor.subTask
          (CodeGenEcorePlugin.INSTANCE.getString("_UI_GeneratingJavaClass_message", new Object [] { getQualifiedClassName() }));
        generate
          (createMonitor(progressMonitor, 1), 
           Generator.EMF_MODEL_PROJECT_STYLE, 
           getGenModel().getEffectiveModelPluginVariables(), 
           getGenModel().getModelDirectory(), 
           getGenPackage().getClassPackageName(), 
           getClassName(), 
           getGenModel().getClassEmitter(),
           new Object [] { new Object [] { this, getGenModel().isSuppressInterfaces() ? Boolean.TRUE : Boolean.FALSE, Boolean.TRUE }});
      }
    }
    finally
    {
      progressMonitor.done();
    }
  }

  @Override
  public String getModelInfo()
  {
    EClass eClass = getEcoreClass();

    StringBuffer result = new StringBuffer();
    if (isMapEntry())
    {
      StringBuffer names = new StringBuffer();
      StringBuffer body = new StringBuffer();
      for (GenFeature genFeature : getGenFeatures())
      {
        appendLineBreak(body);
        body.append(genFeature.getQualifiedModelInfo());
        body.append(' ');
        names.append(genFeature.getEcoreFeature().getName());
        names.append(' ');
      }

      String features = names.toString().trim();
      if (!features.equals("key value"))
      {
        appendLineBreak(result);
        appendModelSetting(result, "features", features);
      }
      result.append(body);
    }
    else if (isExternalInterface())
    {
      appendModelSetting
        (result, 
         "instanceClass", 
         getEffectiveComplianceLevel().getValue() < GenJDKLevel.JDK50 ? eClass.getInstanceClassName() : eClass.getInstanceTypeName());

      if (!getGenTypeParameters().isEmpty())
      {
        StringBuilder typeParameterNames = new StringBuilder();
        for (Iterator<GenTypeParameter> i = getGenTypeParameters().iterator(); i.hasNext(); )
        {
          typeParameterNames.append(i.next().getName());
          if (i.hasNext())
          {
            typeParameterNames.append(' ');
          }
        }
        appendModelSetting(result, "typeParameters", typeParameterNames.toString());
      }
    }
    else
    {
      if (getGenModel().isSuppressInterfaces())
      {
        appendModelSetting(result, "kind", "class");
      }

      if (isInterface())
      {
        appendModelSetting(result, "interface", "true");
      }
      if (isAbstract())
      {
        appendModelSetting(result, "abstract", "true");
      }

      StringBuffer suppressedNames = new StringBuffer();
      StringBuffer suppressedInfo = new StringBuffer();
      for (GenFeature genFeature : getGenFeatures())
      {
        if (genFeature.isSuppressedGetVisibility())
        {
          suppressedNames.append(genFeature.getName());
          suppressedNames.append(' ');
          appendLineBreak(suppressedInfo);
          suppressedInfo.append(genFeature.getQualifiedModelInfo());
          suppressedInfo.append(' ');
        }
      }

      if (suppressedNames.length() > 0)
      {
        appendLineBreak(result);
        appendModelSetting(result, "features", suppressedNames.toString().trim());
        result.append(suppressedInfo);
      }
    }

    if (hasReferenceToClassifierWithInstanceTypeName(eClass.getEGenericSuperTypes()))
    {
      StringBuilder superTypes = new StringBuilder();
      for (EGenericType eGenericType : eClass.getEGenericSuperTypes())
      {
        superTypes.append(getEcoreType(eGenericType));
        superTypes.append(' ');
      }
      appendModelSetting(result, "superTypes", superTypes.toString().trim());
    }
    
    for (GenTypeParameter genTypeParameter : getGenTypeParameters())
    {
      String info = genTypeParameter.getQualifiedModelInfo();
      if (info.length() != 0)
      {
        result.append(info);
        result.append(' ');
      }
    }

    appendAnnotationInfo(result, eClass);

    return result.toString().trim();
  }

  //
  // EMFEdit generation
  //

  public String getProviderClassName()
  {
    return getName() + "ItemProvider";
  }

  public String getQualifiedProviderClassName()
  {
    return getGenPackage().getProviderPackageName() + "." + getProviderClassName();
  }

  public String getImportedProviderClassName()
  {
    return getGenModel().getImportedName(getQualifiedProviderClassName());
  }

  public String getItemIconFileName()
  {
    GenModel genModel = getGenModel();
    if (genModel.getRuntimePlatform() == GenRuntimePlatform.GWT)
    {
      return genModel.getEditDirectory() + "/" + genModel.getEditPluginPackageName().replace(".", "/") + "/icons/full/obj16/" + 
        getName() + ".gif";
    }
    else
    {
      return genModel.getEditIconsDirectory() + "/full/obj16/" + getName() + ".gif";
    }
  }
  
  private static final Set<String> OBJECT_METHODS = 
    new HashSet<String>()
    {
      private static final long serialVersionUID = 1L;

      {
        this.add("clone");
        this.add("getClass");
        this.add("hashCode");
        this.add("finalize");
        this.add("notify");
        this.add("notifyAll");
        this.add("toString");
        this.add("wait");
      }
    };

  public String getItemIconAccessorName()
  {
    String result = safeName(getUncapName());
    if (OBJECT_METHODS.contains(result))
    {
      result += "_";
    }
    return result;
  }

  public String getCreateChildIconFileName(GenFeature feature, GenClass childClass)
  {
    return getCreateChildIconFileName(getGenModel(), feature, childClass);
  }

  public String getCreateChildIconFileName(GenModel genModel, GenFeature feature, GenClass childClass)
  {
    GenClass parentClass = feature.getGenClass();
    if (genModel.getRuntimePlatform() == GenRuntimePlatform.GWT)
    {
      return genModel.getEditDirectory() + "/" + genModel.getEditPluginPackageName().replace(".", "/") + "/icons/full/ctool16/" + 
        "Create" + parentClass.getName() + "_" + feature.getName() + "_" + childClass.getName() + ".gif";
    }
    else
    {
      return genModel.getEditIconsDirectory() + "/full/ctool16/" + 
        "Create" + parentClass.getName() + "_" + feature.getName() + "_" + childClass.getName() + ".gif";
    }
  }

  public GenClass getProviderExtendsGenClass()
  {
    GenClass baseClass = getClassExtendsGenClass();
    while (baseClass != null && 
             (baseClass.getProvider() == GenProviderKind.NONE_LITERAL ||
                !baseClass.getGenModel().hasEditSupport()))
    {
      baseClass = baseClass.getClassExtendsGenClass();
    }
    return baseClass;
  }

  public List<String> getProviderImplementsClassNames()
  {
    List<String> result = new ArrayList<String>(getGenPackage().getProviderSupportedTypes());
    GenClass classExtendsGenClass = getClassExtendsGenClass();
    if (classExtendsGenClass != null)
    {
      result.removeAll(classExtendsGenClass.getGenPackage().getProviderSupportedTypes());
    }
    return result;
  }

  public String getProviderBaseClassName()
  {
    GenClass baseClass = getProviderExtendsGenClass();
    return 
      baseClass != null ? 
        baseClass.getImportedProviderClassName() : 
          isBlank(getGenModel().getProviderRootExtendsClass()) ?
            null:
            getGenModel().getImportedName(getGenModel().getProviderRootExtendsClass());
  }

  public List<GenClass> getProviderImplementedGenClasses()
  {
    List<GenClass> allBases = getAllBaseGenClasses();
    GenClass extendedBase = getProviderExtendsGenClass();
    int i = extendedBase == null ? 0 : allBases.indexOf(extendedBase) + 1;
    List<GenClass> result = new ArrayList<GenClass>(allBases.subList(i, allBases.size()));
    result.add(this);
    return result;
  }

  protected List<GenFeature> getProviderImplementedGenFeatures()
  {
    return collectGenFeatures(getProviderImplementedGenClasses(), null, null);
  }

  public List<GenFeature> getLabelFeatureCandidates()
  {
    return 
      collectGenFeatures
        (getAllBaseGenClasses(), 
         getGenFeatures(), 
         new GenFeatureFilter() 
         {
           public boolean accept(GenFeature genFeature) 
           {
             return !genFeature.isReferenceType() && !genFeature.isListType() && !genFeature.isMapType();
           }
         });
  }

  public List<GenFeature> getPropertyFeatures()
  {
    return 
      collectGenFeatures
        (getProviderImplementedGenClasses(), 
         null, 
         new GenFeatureFilter() 
         {
           public boolean accept(GenFeature genFeature) 
           {
             //FB TBD filter out volatile and other inappropriate links?
             return genFeature.isProperty();
           }
         });
  }

  public List<GenFeature> getNotifyFeatures()
  {
    return 
     collectGenFeatures
       (getProviderImplementedGenClasses(), 
        null, 
        new GenFeatureFilter()
        {
          public boolean accept(GenFeature genFeature)
          {
            return genFeature.isNotify();
          }
        });
  }

  public List<GenFeature> getLabelNotifyFeatures()
  {
    return collectGenFeatures(getProviderImplementedGenClasses(), null,
      new GenFeatureFilter()
      {
        public boolean accept(GenFeature genFeature)
        {
          return genFeature.isNotify() && !genFeature.isChildren();
        }
      });
  }

  public List<GenFeature> getContentNotifyFeatures()
  {
    return collectGenFeatures(getProviderImplementedGenClasses(), null,
      new GenFeatureFilter()
      {
        public boolean accept(GenFeature genFeature)
        {
          return genFeature.isNotify() && genFeature.isChildren() && genFeature != getLabelFeature();
        }
      });
  }

  public List<GenFeature> getLabelAndContentNotifyFeatures()
  {
    GenFeature feature = getLabelFeature();
    return feature != null && feature.isNotify() && feature.isChildren() ?
      Collections.singletonList(feature) :
      Collections.<GenFeature>emptyList();
  }

  public List<GenFeature> getChildrenFeatures()
  {
    return collectGenFeatures
       (getProviderImplementedGenClasses(), 
        null, 
        new GenFeatureFilter()
        {
          public boolean accept(GenFeature genFeature) 
          {
            return genFeature.isChildren();
          }
        });
  }

  public List<GenFeature> getAllChildrenFeatures()
  {
    return 
      collectGenFeatures
        (getAllBaseGenClasses(), 
         getGenFeatures(), 
         new GenFeatureFilter()
         {
           public boolean accept(GenFeature genFeature) 
           {
            return genFeature.isChildren();
           }
         });
  }

  public List<GenFeature> getCreateChildFeatures()
  {
    List<GenFeature> result = new ArrayList<GenFeature>();

    // If this is class has mixed content, the mixed feature should always be included, even if inherited, and come first.
    //
    final GenFeature mixed = getMixedGenFeature();

    if (mixed != null && mixed.isCreateChild())
    {
      result.add(mixed);
    }

    // Add all other create child features that this item provider is responsible for.
    //
    result.addAll(collectGenFeatures(getProviderImplementedGenClasses(), null,
        new GenFeatureFilter()
        {
          public boolean accept(GenFeature genFeature)
          {
            return genFeature.isCreateChild() && genFeature != mixed;
          }
        }));

    return result;
  }
  
  public List<GenFeature> getAllCreateChildFeatures()
  {
    return collectGenFeatures(getAllBaseGenClasses(), getGenFeatures(),
      new GenFeatureFilter()
      {
        public boolean accept(GenFeature genFeature) 
        {
          return genFeature.isCreateChild();
        }
      });
  }

  public List<GenFeature> getAllCreateChildFeaturesIncludingDelegation()
  {
    return getCreateChildFeaturesIncludingDelegation(getAllBaseGenClasses());
  }

  public List<GenFeature> getCreateChildFeaturesIncludingDelegation()
  {
    return getCreateChildFeaturesIncludingDelegation(null);
  }

  private List<GenFeature> getCreateChildFeaturesIncludingDelegation(List<GenClass> genClasses)
  {
    return collectGenFeatures(genClasses, getGenFeatures(),
      new GenFeatureFilter()
      {
        public boolean accept(GenFeature genFeature)
        {
          while (genFeature != null)
          {
            if (genFeature.isCreateChild()) return true;
            genFeature = genFeature.getDelegateFeature();
          }
          return false;
        }
      });
  }

  public List<GenFeature> getCrossPackageCreateChildFeatures()
  {
    GenClass base = getProviderExtendsGenClass();

    // If there is a provider base class from outside this class that has already been generated, get the create
    // child references from it. We'll check for any type-compatible classes introduced in this package.
    //
    if (base == null || base.getGenPackage() == getGenPackage() ||
        getGenModel().getAllGenPackagesWithClassifiers().contains(base.getGenPackage()))
    {
      return Collections.emptyList();
    }

    return collectGenFeatures(base.getProviderImplementedGenClasses(), null,
      new GenFeatureFilter()
      {
        public boolean accept(GenFeature genFeature)
        {
          return genFeature.isCreateChild() && genFeature.isReferenceType();
        }
      });
  }

  public List<GenFeature> getSharedClassCreateChildFeatures()
  {
    List<GenFeature> childrenFeatures = getAllCreateChildFeatures();
    
    // build mapping from classes to list of features that use them
    Map<GenClass, List<GenFeature>> classToFeatureMap = new LinkedHashMap<GenClass, List<GenFeature>>();
    for (GenFeature childGenFeature : childrenFeatures)
    {
      List<GenFeature> childGenFeatureList = childGenFeature.isFeatureMapType() ? childGenFeature.getDelegatedFeatures() : Collections.singletonList(childGenFeature);
      for (GenFeature genFeature : childGenFeatureList)
      {
        List<GenClass> genClasses = getChildrenClasses(genFeature);
        for (GenClass genClass : genClasses)
        {
          List<GenFeature> genFeatures = classToFeatureMap.get(genClass);
          if (genFeatures == null)
          {
            genFeatures = new ArrayList<GenFeature>(5);
            classToFeatureMap.put(genClass, genFeatures);
          }
          genFeatures.add(genFeature);
        }
      }
    }

    // scan feature lists for those with multiple elements and return them
    List<GenFeature> result = new UniqueEList<GenFeature>(childrenFeatures.size());
    for (List<GenFeature> genFeatures : classToFeatureMap.values())
    {
      if (genFeatures.size() > 1) result.addAll(genFeatures);
    }
    return result;
  }

  public boolean hasFeatureMapCreateChildFeatures()
  {
    for (GenFeature genFeature : getAllCreateChildFeatures())
    {
      if (genFeature.isFeatureMapType()) return true;
    }
    return false;
  }

  public List<GenClass> getChildrenClasses(GenFeature genFeature)
  {
    EStructuralFeature feature = genFeature.getEcoreFeature();
    EClassifier eType = feature.getEType();
    List<GenClass> result = getTypeGenClasses(eType, getGenPackage(), getGenModel().getAllGenAndUsedGenPackagesWithClassifiers(), -1);
    if (eType == EcorePackage.Literals.EOBJECT && feature.getEAnnotation(ExtendedMetaData.ANNOTATION_URI) != null)
    {
      result.add(findGenClass(XMLTypePackage.Literals.ANY_TYPE));
    }
    return result;
  }

  public List<GenClass> getCrossPackageChildrenClasses(GenFeature genFeature)
  {
    return getTypeGenClasses(genFeature.getEcoreFeature().getEType(), getGenPackage(), getGenModel().getAllGenPackagesWithClassifiers(), -1);
  }

  public List<ChildCreationData> getChildCreationData()
  {
    return getChildCreationData(null);
  }

  public List<ChildCreationData> getAllChildCreationData(GenModel context)
  {
    UniqueEList<ChildCreationData> result = new UniqueEList<ChildCreationData>();
    List<GenFeature> allCreateChildGenFeatures =
      collectGenFeatures
        (null,
         getAllGenFeatures(),
         new GenFeatureFilter()
         {
           public boolean accept(GenFeature genFeature) 
           {
             return genFeature.isCreateChild();
           }
         });

    GenModel genModel = context == null ? getGenModel() : context;
    List<GenPackage> allGenAndUsedGenPackagesWithClassifiers = genModel.getAllGenAndUsedGenPackagesWithClassifiers();
    for (GenFeature createFeature : allCreateChildGenFeatures)
    {
      if (createFeature.isFeatureMapType())
      {
        for (GenFeature delegatedFeature : createFeature.getDelegatedFeatures(genModel))
        {
          if (delegatedFeature.isReferenceType())
          {
            EStructuralFeature eStructuralFeature = delegatedFeature.getEcoreFeature();
            EClassifier eType = eStructuralFeature.getEType();
            List<GenClass> genClasses = getTypeGenClasses(eType, getGenPackage(), allGenAndUsedGenPackagesWithClassifiers, -1);
            if (eType == EcorePackage.Literals.EOBJECT && eStructuralFeature.getEAnnotation(ExtendedMetaData.ANNOTATION_URI) != null)
            {
              genClasses.add(findGenClass(XMLTypePackage.Literals.ANY_TYPE));
            }
            for (GenClass createClass : genClasses)
            {
              result.add(new ChildCreationData(createFeature, delegatedFeature, createClass));
            }
          }
          else
          {
            result.add(new ChildCreationData(createFeature, delegatedFeature, delegatedFeature.getTypeGenClassifier()));
          }
        }
      }
      else if (createFeature.isReferenceType())
      {
        EStructuralFeature eStructuralFeature = createFeature.getEcoreFeature();
        EClassifier eType = eStructuralFeature.getEType();
        List<GenClass> genClasses = getTypeGenClasses(eType, getGenPackage(), allGenAndUsedGenPackagesWithClassifiers, -1);
        if (eType == EcorePackage.Literals.EOBJECT && eStructuralFeature.getEAnnotation(ExtendedMetaData.ANNOTATION_URI) != null)
        {
          genClasses.add(findGenClass(XMLTypePackage.Literals.ANY_TYPE));
        }
        for (GenClass createClass : genClasses)
        {
          result.add(new ChildCreationData(createFeature, null, createClass));
        }
      }
      else
      {
        result.add(new ChildCreationData(createFeature, null, createFeature.getTypeGenClassifier()));
      }
    }
    return result;
  }

  public List<ChildCreationData> getChildCreationData(GenModel context)
  {
    UniqueEList<ChildCreationData> result = new UniqueEList<ChildCreationData>(getAllChildCreationData(context));

    GenClassImpl baseClass = (GenClassImpl)getProviderExtendsGenClass();
    if (baseClass != null)
    {
      result.removeAll(baseClass.getAllChildCreationData(context));
    }
    return result;
  }

  /**
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @generated
   */
  public GenFeature getLabelFeatureGen()
  {
    if (labelFeature != null && labelFeature.eIsProxy())
    {
      InternalEObject oldLabelFeature = (InternalEObject)labelFeature;
      labelFeature = (GenFeature)eResolveProxy(oldLabelFeature);
      if (labelFeature != oldLabelFeature)
      {
        if (eNotificationRequired())
          eNotify(new ENotificationImpl(this, Notification.RESOLVE, GenModelPackage.GEN_CLASS__LABEL_FEATURE, oldLabelFeature, labelFeature));
      }
    }
    return labelFeature;
  }

  public GenFeature getLabelFeature()
  {
    GenFeature labelFeature = getLabelFeatureGen();
    if (labelFeature != null || eInternalContainer() == null)
      return labelFeature;
    
    Locale locale = getGenModel().getLocale();
    //FB TBD can we come up with a better algorithm for choosing the default label feature?
    for (GenFeature feature : getLabelFeatureCandidates())
    {
      if (!feature.isListType())
      {
        String featureName = feature.getName();
        if (featureName != null)
        {
          if (featureName.equalsIgnoreCase("name")) 
          {
            labelFeature = feature;
          }
          else if (featureName.equalsIgnoreCase("id")) 
          {
            if (labelFeature == null || !labelFeature.getName().toLowerCase(locale).endsWith("name"))
            {
              labelFeature = feature;
            }
          }
          else if (featureName.toLowerCase(locale).endsWith("name")) 
          {
            if (labelFeature == null || 
                 !labelFeature.getName().toLowerCase(locale).endsWith("name") && !labelFeature.getName().equalsIgnoreCase("id"))
            {
              labelFeature = feature;
            }
          }
          else if (featureName.toLowerCase(locale).indexOf("name") != -1)
          {
            if (labelFeature == null || 
                  labelFeature.getName().toLowerCase(locale).indexOf("name") == -1 && !labelFeature.getName().equalsIgnoreCase("id"))
            {
              labelFeature = feature;
            }
          }
          else if (labelFeature == null) 
          {
            labelFeature = feature;
          }
        }
      }
    }

    return labelFeature;
  }

  /**
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @generated
   */
  public GenFeature basicGetLabelFeature()
  {
    return labelFeature;
  }

  /**
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @generated
   */
  public void setLabelFeature(GenFeature newLabelFeature)
  {
    GenFeature oldLabelFeature = labelFeature;
    labelFeature = newLabelFeature;
    if (eNotificationRequired())
      eNotify(new ENotificationImpl(this, Notification.SET, GenModelPackage.GEN_CLASS__LABEL_FEATURE, oldLabelFeature, labelFeature));
  }

  /**
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @generated
   */
  @SuppressWarnings("unchecked")
  @Override
  public NotificationChain eInverseAdd(InternalEObject otherEnd, int featureID, NotificationChain msgs)
  {
    switch (featureID)
    {
      case GenModelPackage.GEN_CLASS__GEN_FEATURES:
        return ((InternalEList<InternalEObject>)(InternalEList<?>)getGenFeatures()).basicAdd(otherEnd, msgs);
      case GenModelPackage.GEN_CLASS__GEN_OPERATIONS:
        return ((InternalEList<InternalEObject>)(InternalEList<?>)getGenOperations()).basicAdd(otherEnd, msgs);
    }
    return super.eInverseAdd(otherEnd, featureID, msgs);
  }

  /**
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @generated
   */
  @Override
  public NotificationChain eInverseRemove(InternalEObject otherEnd, int featureID, NotificationChain msgs)
  {
    switch (featureID)
    {
      case GenModelPackage.GEN_CLASS__GEN_FEATURES:
        return ((InternalEList<?>)getGenFeatures()).basicRemove(otherEnd, msgs);
      case GenModelPackage.GEN_CLASS__GEN_OPERATIONS:
        return ((InternalEList<?>)getGenOperations()).basicRemove(otherEnd, msgs);
    }
    return super.eInverseRemove(otherEnd, featureID, msgs);
  }

  /**
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @generated
   */
  @Override
  public Object eGet(int featureID, boolean resolve, boolean coreType)
  {
    switch (featureID)
    {
      case GenModelPackage.GEN_CLASS__PROVIDER:
        return getProvider();
      case GenModelPackage.GEN_CLASS__IMAGE:
        return isImage();
      case GenModelPackage.GEN_CLASS__DYNAMIC:
        return isDynamic();
      case GenModelPackage.GEN_CLASS__ECORE_CLASS:
        if (resolve) return getEcoreClass();
        return basicGetEcoreClass();
      case GenModelPackage.GEN_CLASS__GEN_FEATURES:
        return getGenFeatures();
      case GenModelPackage.GEN_CLASS__GEN_OPERATIONS:
        return getGenOperations();
      case GenModelPackage.GEN_CLASS__LABEL_FEATURE:
        if (resolve) return getLabelFeature();
        return basicGetLabelFeature();
    }
    return super.eGet(featureID, resolve, coreType);
  }

  /**
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @generated
   */
  @SuppressWarnings("unchecked")
  @Override
  public void eSet(int featureID, Object newValue)
  {
    switch (featureID)
    {
      case GenModelPackage.GEN_CLASS__PROVIDER:
        setProvider((GenProviderKind)newValue);
        return;
      case GenModelPackage.GEN_CLASS__IMAGE:
        setImage((Boolean)newValue);
        return;
      case GenModelPackage.GEN_CLASS__DYNAMIC:
        setDynamic((Boolean)newValue);
        return;
      case GenModelPackage.GEN_CLASS__ECORE_CLASS:
        setEcoreClass((EClass)newValue);
        return;
      case GenModelPackage.GEN_CLASS__GEN_FEATURES:
        getGenFeatures().clear();
        getGenFeatures().addAll((Collection<? extends GenFeature>)newValue);
        return;
      case GenModelPackage.GEN_CLASS__GEN_OPERATIONS:
        getGenOperations().clear();
        getGenOperations().addAll((Collection<? extends GenOperation>)newValue);
        return;
      case GenModelPackage.GEN_CLASS__LABEL_FEATURE:
        setLabelFeature((GenFeature)newValue);
        return;
    }
    super.eSet(featureID, newValue);
  }

  /**
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @generated
   */
  @Override
  public void eUnset(int featureID)
  {
    switch (featureID)
    {
      case GenModelPackage.GEN_CLASS__PROVIDER:
        setProvider(PROVIDER_EDEFAULT);
        return;
      case GenModelPackage.GEN_CLASS__IMAGE:
        setImage(IMAGE_EDEFAULT);
        return;
      case GenModelPackage.GEN_CLASS__DYNAMIC:
        setDynamic(DYNAMIC_EDEFAULT);
        return;
      case GenModelPackage.GEN_CLASS__ECORE_CLASS:
        setEcoreClass((EClass)null);
        return;
      case GenModelPackage.GEN_CLASS__GEN_FEATURES:
        getGenFeatures().clear();
        return;
      case GenModelPackage.GEN_CLASS__GEN_OPERATIONS:
        getGenOperations().clear();
        return;
      case GenModelPackage.GEN_CLASS__LABEL_FEATURE:
        setLabelFeature((GenFeature)null);
        return;
    }
    super.eUnset(featureID);
  }

  /**
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @generated
   */
  @Override
  public boolean eIsSet(int featureID)
  {
    switch (featureID)
    {
      case GenModelPackage.GEN_CLASS__PROVIDER:
        return provider != PROVIDER_EDEFAULT;
      case GenModelPackage.GEN_CLASS__IMAGE:
        return image != IMAGE_EDEFAULT;
      case GenModelPackage.GEN_CLASS__DYNAMIC:
        return dynamic != DYNAMIC_EDEFAULT;
      case GenModelPackage.GEN_CLASS__ECORE_CLASS:
        return ecoreClass != null;
      case GenModelPackage.GEN_CLASS__GEN_FEATURES:
        return genFeatures != null && !genFeatures.isEmpty();
      case GenModelPackage.GEN_CLASS__GEN_OPERATIONS:
        return genOperations != null && !genOperations.isEmpty();
      case GenModelPackage.GEN_CLASS__LABEL_FEATURE:
        return labelFeature != null;
    }
    return super.eIsSet(featureID);
  }

  /**
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @generated
   */
  @Override
  public String toString()
  {
    if (eIsProxy()) return super.toString();

    StringBuilder result = new StringBuilder(super.toString());
    result.append(" (provider: ");
    result.append(provider);
    result.append(", image: ");
    result.append(image);
    result.append(", dynamic: ");
    result.append(dynamic);
    result.append(')');
    return result.toString();
  }

  public String getItemProviderAdapterFactoryClassName()
  {
    return getGenPackage().getItemProviderAdapterFactoryClassName();
  }

  public String getTestCaseClassName()
  {
    return getName() + "Test";
  }

  public String getQualifiedTestCaseClassName()
  {
    return getGenPackage().getTestsPackageName() + "." + getTestCaseClassName();
  }

  public String getImportedTestCaseClassName()
  {
    return getGenModel().getImportedName(getQualifiedTestCaseClassName());
  }

  @Override
  public boolean canGenerate()
  {
    return super.canGenerate() && !isDynamic();
  }

  @Override
  public boolean canGenerateEdit()
  {
    return super.canGenerateEdit() && !isInterface() && getProvider() != GenProviderKind.NONE_LITERAL;
  }

  @Override
  public boolean canGenerateEditor()
  {
    return false;
  }

  /**
   * @deprecated In EMF 2.2, a {@link org.eclipse.emf.codegen.ecore.generator.Generator Generator} should be used to generate code.
   * This method will be removed after 2.2.
   */
  @SuppressWarnings("rawtypes")
  @Override
  @Deprecated
  public void generateEdit(Monitor progressMonitor)
  {
    try
    {
      if (!canGenerateEdit()) return;

      progressMonitor.beginTask("", 2 + getAllCreateChildFeatures().size());
      progressMonitor.subTask
        (CodeGenEcorePlugin.INSTANCE.getString
           ("_UI_GeneratingProvider_message", new Object [] { getFormattedName() }));

      progressMonitor.subTask
        (CodeGenEcorePlugin.INSTANCE.getString("_UI_GeneratingJavaClass_message", new Object [] { getQualifiedProviderClassName() }));
      generate
        (createMonitor(progressMonitor, 1), 
         Generator.EMF_EDIT_PROJECT_STYLE, 
         getGenModel().getEffectiveModelPluginVariables(), 
         getGenModel().getEditDirectory(), 
         getGenPackage().getProviderPackageName(),
         getProviderClassName(), 
         getGenModel().getItemProviderEmitter());

      if (isImage())
      {
        progressMonitor.subTask
          (CodeGenEcorePlugin.INSTANCE.getString("_UI_GeneratingItemIcon_message", new Object [] { getItemIconFileName() }));
        generate
          (createMonitor(progressMonitor, 1), 
           Generator.EMF_EDIT_PROJECT_STYLE, 
           getGenModel().getEffectiveModelPluginVariables(), 
           getItemIconFileName(), 
           ((GenModelImpl)getGenModel()).getItemGIFEmitter(),
           getName());
      }

      if (getGenModel().isCreationCommands() && getGenModel().isCreationIcons())
      {
        for (Iterator iter = getAllCreateChildFeaturesIncludingDelegation().iterator(); iter.hasNext(); )
        {
          GenFeature feature = (GenFeature)iter.next();
          for (Iterator cIter = getChildrenClasses(feature).iterator(); cIter.hasNext(); )
          {
            GenClass childClass = (GenClass)cIter.next();
            progressMonitor.subTask
              (CodeGenEcorePlugin.INSTANCE.getString
                 ("_UI_GeneratingCreateChildIcon_message", new Object [] { getCreateChildIconFileName(feature, childClass) }));
            generate
              (createMonitor(progressMonitor, 1), 
               Generator.EMF_EDIT_PROJECT_STYLE, 
               getGenModel().getEffectiveModelPluginVariables(), 
               getCreateChildIconFileName(feature, childClass), 
               ((GenModelImpl)getGenModel()).getCreateChildGIFEmitter(),
               getName(),
               childClass.getName());
          }
        }
      }
    }
    finally
    {
      progressMonitor.done();
    }
  }

  public boolean hasTests()
  {
    for (GenFeature genFeature : getAllGenFeatures())
    {
      if (((genFeature.isGet() && !genFeature.isSuppressedGetVisibility())
        || (genFeature.isSet() && !genFeature.isSuppressedSetVisibility())
        || (genFeature.isUnset() && !genFeature.isSuppressedUnsetVisibility())
        || (genFeature.isIsSet() && !genFeature.isSuppressedIsSetVisibility()))
          && (genFeature.isVolatile() || genFeature.isDerived()))
      {
        return true;
      }
    }

    return !getAllGenOperations().isEmpty();
  }

  @Override
  public boolean canGenerateTests()
  {
    return getGenModel().canGenerateTests();
  }

  /**
   * @deprecated In EMF 2.2, a {@link org.eclipse.emf.codegen.ecore.generator.Generator Generator} should be used to generate code.
   * This method will be removed after 2.2.
   */
  @Override
  @Deprecated
  public void generateTests(Monitor progressMonitor)
  {
    try
    {
      if (!canGenerateTests())
        return;

      progressMonitor.beginTask("", 1);
      progressMonitor.subTask(CodeGenEcorePlugin.INSTANCE.getString("_UI_GeneratingTestCase_message", new Object [] { getFormattedName() }));

      progressMonitor.subTask(CodeGenEcorePlugin.INSTANCE.getString(
        "_UI_GeneratingJavaClass_message", new Object []{ getQualifiedTestCaseClassName() }));
      generate(
        createMonitor(progressMonitor, 1),
        Generator.EMF_TESTS_PROJECT_STYLE,
        Collections.EMPTY_LIST,
        getGenModel().getTestsDirectory(),
        getGenPackage().getTestsPackageName(),
        getTestCaseClassName(),
        getGenModel().getTestCaseEmitter());
    }
    finally
    {
      progressMonitor.done();
    }
  }

  public boolean reconcile(GenClass oldGenClassVersion)
  {
    if (getEcoreClass().getName().equals(oldGenClassVersion.getEcoreClass().getName()))
    {
      for (GenFeature genFeature : getGenFeatures())
      {
        for (GenFeature oldGenFeatureVersion : oldGenClassVersion.getGenFeatures())
        {
          if (genFeature.reconcile(oldGenFeatureVersion))
          {
            break;
          }
        }
      }

      for (GenOperation genOperation : getGenOperations())
      {
        for (GenOperation oldGenOperation : oldGenClassVersion.getGenOperations())
        {
          if (genOperation.reconcile(oldGenOperation))
          {
            break;
          }
        }
      }

      for (int i = 0, size = Math.min(getGenTypeParameters().size(), oldGenClassVersion.getGenTypeParameters().size()); i < size; i++)
      {
        GenTypeParameter genTypeParameter = getGenTypeParameters().get(i);
        GenTypeParameter oldGenTypeParameterVersion = oldGenClassVersion.getGenTypeParameters().get(i);
        genTypeParameter.reconcile(oldGenTypeParameterVersion);
      }

      reconcileSettings(oldGenClassVersion);
      return true;
    }
    else
    {
      return false;
    }
  }

  protected void reconcileSettings(GenClass oldGenClassVersion)
  {
    setProvider(oldGenClassVersion.getProvider());
    setImage(oldGenClassVersion.isImage());
    GenFeature oldLabelFeature = ((GenClassImpl)oldGenClassVersion).getLabelFeatureGen();
    if (oldLabelFeature != null)
    {
      EStructuralFeature newLabelFeature =  getEcoreClass().getEStructuralFeature(oldLabelFeature.getEcoreFeature().getName());
      if (newLabelFeature != null)
      {
        setLabelFeature(findGenFeature(newLabelFeature));
      } 
    }
    setDynamic(oldGenClassVersion.isDynamic());
    reconcileGenAnnotations(oldGenClassVersion);
  }

  public boolean reconcile()
  {
    try
    {
      EClass eClass = getEcoreClass();
      if (eClass == null || eClass.eIsProxy() || eClass.eResource() == null)
      {
        return false;
      }
      else
      {
        for (Iterator<GenTypeParameter> i = getGenTypeParameters().iterator(); i.hasNext(); )
        {
          GenTypeParameter genTypeParameter = i.next();
          if (!genTypeParameter.reconcile())
          {
            i.remove();
          }
        }

        for (Iterator<GenFeature> i = getGenFeatures().iterator(); i.hasNext(); )
        {
          GenFeature genFeature = i.next();
          if (!genFeature.reconcile())
          {
            i.remove();
          }
        }
  
        for (Iterator<GenOperation> i = getGenOperations().iterator(); i.hasNext(); )
        {
          GenOperation genOperation = i.next();
          if (!genOperation.reconcile())
          {
            i.remove();
          }
        }
  
        return true;
      }
    }
    catch (RuntimeException exception)
    {
      return false;
    }
  }

  private List<String> genClassConstraints;

  @Override
  public List<String> getGenConstraints()
  {
    if (genClassConstraints == null)
    {
      List<String> result = new UniqueEList<String>(super.getGenConstraints());
      for (GenOperation genOperation : getInvariantOperations())
      {
        result.add(genOperation.getName());
      }
      genClassConstraints = result;
    }
    return genClassConstraints;
  }

  /**
   * @deprecated since 2.5
   * @see #getIntrinsicConstraints()
   */
  @Deprecated
  public static final List<String> INTRINSIC_CONSTRAINTS = 
    Arrays.asList
      (new String [] 
       {
        "NoCircularContainment",
        "EveryMultiplicityConforms", 
        "EveryDataValueConforms", 
        "EveryReferenceIsContained", 
        "EveryBidirectionalReferenceIsPaired",
        "EveryProxyResolves",
        "UniqueID",
        "EveryKeyUnique",
        "EveryMapEntryUnique"
       });

  protected List<String> getIntrinsicConstraints()
  {
    List<String> result = new ArrayList<String>(INTRINSIC_CONSTRAINTS);
    int runtimeVersion = getGenModel().getRuntimeVersion().getValue();
	if (runtimeVersion <= GenRuntimeVersion.EMF22_VALUE)
    {
      result.remove("EveryKeyUnique");
      result.remove("EveryMapEntryUnique");
    }
	if (runtimeVersion < GenRuntimeVersion.EMF25_VALUE)
	{
      result.remove("NoCircularContainment");
	}
	if (runtimeVersion < GenRuntimeVersion.EMF26_VALUE)
	{
      result.remove("EveryBidirectionalReferenceIsPaired");
	}
    return result;
  }

  @Override
  public List<String> getAllGenConstraints()
  {
    List<String> result = new ArrayList<String>(getIntrinsicConstraints());
    result.addAll(collectGenConstraints(getAllBaseGenClasses(), getGenConstraints(), null));
    return result;
  }

  @Override
  public GenClassifier getConstraintImplementor(String constraint)
  {
    if (getGenConstraints().contains(constraint))
    {
      return this;
    }
    else
    {
      for (GenClass baseGenClass : getBaseGenClasses())
      {
        if (baseGenClass.getGenConstraints().contains(constraint))
        {
          return baseGenClass;
        }
        else if (baseGenClass.getAllGenConstraints().contains(constraint))
        {
          return baseGenClass.getConstraintImplementor(constraint);
        }
      }
      return null;
    }
  }

  public GenClassifier getConstraintDelegate(String constraint)
  {
    for (GenClass baseGenClass : getBaseGenClasses())
    {
      if (baseGenClass.getGenConstraints().contains(constraint))
      {
        return baseGenClass;
      }
      else if (baseGenClass.getAllGenConstraints().contains(constraint))
      {
        return baseGenClass.getConstraintImplementor(constraint);
      }
    }
    return null;
  }

  @Override
  public boolean hasOnlyDefaultConstraints()
  {
    for (String genConstraint : getAllGenConstraints())
    {
      if (getConstraintImplementor(genConstraint) != null)
      {
        return false;
      }
    }
    return true;
  }

  public List<GenOperation> getInvariantOperations()
  {
    return collectGenOperations(this, null, getGenOperations(), new GenOperationFilter()
      {
        public boolean accept(GenOperation genOperation)
        {
          return genOperation.isInvariant();
        }
      });
  }

  public GenOperation getInvariantOperation(String constraint)
  {
    for (GenOperation genOperation : getInvariantOperations())
    {
      if (genOperation.getName().equals(constraint))
      {
        return genOperation;
      }
    }
    return null;
  }

  public boolean isDocumentRoot()
  {
    return getExtendedMetaData().getDocumentRoot(getEcoreClass().getEPackage()) == getEcoreClass();
  }

  protected boolean isMixed()
  {
    return getExtendedMetaData().getContentKind(getEcoreClass()) == ExtendedMetaData.MIXED_CONTENT;
  }

  public GenFeature getMixedGenFeature()
  {
    if (!isMixed()) return null;
    EAttribute mixedFeature = getExtendedMetaData().getMixedFeature(getEcoreClass());
    return mixedFeature != null ? findGenFeature(mixedFeature) : null;
  }

  public String getListConstructor(GenFeature genFeature)
  {
    StringBuffer sb = new StringBuffer();

    String unsettable = genFeature.isUnsettable() ? ".Unsettable" : "";
    String offsetCorrectionField = hasOffsetCorrection () ? " + " + getOffsetCorrectionField(null) : "";

    boolean isJava5 = getEffectiveComplianceLevel().getValue() >= GenJDKLevel.JDK50;
    if (genFeature.isMapType())
    {
      GenClass mapGenClass = genFeature.getMapEntryTypeGenClass();
      sb.append(getGenModel().getImportedName("org.eclipse.emf.ecore.util.EcoreEMap"));
      sb.append(unsettable);
      if (isJava5)
      {
        sb.append('<');
        sb.append(genFeature.getImportedMapKeyType(this));
        sb.append(',');
        sb.append(genFeature.getImportedMapValueType(this));
        sb.append('>');
      }
      sb.append("(");
      sb.append(mapGenClass.getQualifiedClassifierAccessor());
      sb.append(", ");
      sb.append(genFeature.getImportedMapEntryType());
      sb.append(".class, this, ");
      sb.append(getQualifiedFeatureID(genFeature));
      sb.append(offsetCorrectionField);
      if (genFeature.isBidirectional() && getGenModel().getRuntimeVersion().getValue() >= GenRuntimeVersion.EMF27_VALUE)
      {
        sb.append(", ");
        sb.append(genFeature.getReverse().getGenClass().getQualifiedFeatureID(genFeature.getReverse()));
      }
      sb.append(")");
    }
    else if (genFeature.isFeatureMapType())
    {
      if (genFeature.isWrappedFeatureMapType())
      {
        sb.append(genFeature.getImportedEffectiveFeatureMapWrapperClass());
        sb.append("(new ");
      }
      sb.append(getGenModel().getImportedName("org.eclipse.emf.ecore.util.BasicFeatureMap"));
      sb.append("(this, ");
      sb.append(getQualifiedFeatureID(genFeature));
      sb.append(offsetCorrectionField);
      sb.append(")");
      if (genFeature.isWrappedFeatureMapType())
      {
        sb.append(")");
      }
    }
    else
    {
      EGenericType eGenericType = genFeature.getEcoreFeature().getEGenericType();
      if (getGenModel().isSuppressNotification())
      {
        sb.append(getGenModel().getImportedName("org.eclipse.emf.ecore.util.BasicInternalEList"));
        if (getGenModel().getRuntimeVersion().getValue() >= GenRuntimeVersion.EMF28_VALUE)
        {
          sb.append(unsettable);
        }
        if (isJava5)
        {
          sb.append('<');
          sb.append(genFeature.getListItemType(this));
          sb.append('>');
        }
        sb.append("(");
        sb.append(getTypeArgument(this, eGenericType, true, true));
        sb.append(".class)");
      }
      else if (genFeature.isEffectiveContains())
      {
        if (genFeature.isBidirectional())
        {
          GenFeature reverseFeature = genFeature.getReverse();
          sb.append(getGenModel().getImportedName("org.eclipse.emf.ecore.util.EObjectContainmentWithInverseEList"));
          sb.append(unsettable);
          if (genFeature.isResolveProxies())
          {
            sb.append(".Resolving");
          }
          if (isJava5)
          {
            sb.append('<');
            sb.append(genFeature.getListItemType(this));
            sb.append('>');
          }
          sb.append("(");
          sb.append(getTypeArgument(this, eGenericType, true, true));
          sb.append(".class, this, ");
          sb.append(getQualifiedFeatureID(genFeature));
          sb.append(offsetCorrectionField);
          sb.append(", ");
          sb.append(reverseFeature.getGenClass().getQualifiedFeatureID(reverseFeature));
          if (reverseFeature.getGenClass().hasOffsetCorrection())
          {
            sb.append(" + ");
            sb.append(getOffsetCorrectionField(genFeature));
          }
          sb.append(")");
          appendReifiedFeatureInverseOverride(sb, genFeature);
        }
        else
        {
          sb.append(getGenModel().getImportedName("org.eclipse.emf.ecore.util.EObjectContainmentEList"));
          sb.append(unsettable);
          if (genFeature.isResolveProxies())
          {
            sb.append(".Resolving");
          }
          if (isJava5)
          {
            sb.append('<');
            sb.append(genFeature.getListItemType(this));
            sb.append('>');
          }
          sb.append("(");
          sb.append(getTypeArgument(this, eGenericType, true, true));
          sb.append(".class, this, ");
          sb.append(getQualifiedFeatureID(genFeature));
          sb.append(offsetCorrectionField);
          sb.append(")");
        }
      }
      else if (genFeature.isReferenceType())
      {
        if (genFeature.isBidirectional())
        {
          GenFeature reverseFeature = genFeature.getReverse();
          if (genFeature.isResolveProxies())
          {
            sb.append(getGenModel().getImportedName("org.eclipse.emf.ecore.util.EObjectWithInverseResolvingEList"));
          }
          else
          {
            sb.append(getGenModel().getImportedName("org.eclipse.emf.ecore.util.EObjectWithInverseEList"));
          }
          sb.append(unsettable);
          if (reverseFeature.isListType())
          {
            sb.append(".ManyInverse");
          }
          if (isJava5)
          {
            sb.append('<');
            sb.append(genFeature.getListItemType(this));
            sb.append('>');
          }
          sb.append("(");
          sb.append(getTypeArgument(this, eGenericType, true, true));
          sb.append(".class, this, ");
          sb.append(getQualifiedFeatureID(genFeature));
          sb.append(offsetCorrectionField);
          sb.append(", ");
          sb.append(reverseFeature.getGenClass().getQualifiedFeatureID(reverseFeature));
          if (reverseFeature.getGenClass().hasOffsetCorrection())
          {
            sb.append(" + ");
            sb.append(getOffsetCorrectionField(genFeature));
          }
          sb.append(")");
          appendReifiedFeatureInverseOverride(sb, genFeature);
        }
        else
        {
          if (genFeature.isResolveProxies())
          {
            sb.append(getGenModel().getImportedName("org.eclipse.emf.ecore.util.EObjectResolvingEList"));
          }
          else
          {
            sb.append(getGenModel().getImportedName("org.eclipse.emf.ecore.util.EObjectEList"));
          }
          sb.append(unsettable);
          if (isJava5)
          {
            sb.append('<');
            sb.append(genFeature.getListItemType(this));
            sb.append('>');
          }
          sb.append("(");
          sb.append(getTypeArgument(this, eGenericType, true, true));
          sb.append(".class, this, ");
          sb.append(getQualifiedFeatureID(genFeature));
          sb.append(offsetCorrectionField);
          sb.append(")");
        }
      }
      else
      { //data type
        if (genFeature.isUnique())
        {
          sb.append(getGenModel().getImportedName("org.eclipse.emf.ecore.util.EDataTypeUniqueEList"));
        }
        else
        {
          sb.append(getGenModel().getImportedName("org.eclipse.emf.ecore.util.EDataTypeEList"));
        }
        sb.append(unsettable);
        if (isJava5)
        {
          sb.append('<');
          sb.append(genFeature.getListItemType(this));
          sb.append('>');
        }
        sb.append("(");
        sb.append(isPrimitiveType(eGenericType.getERawType()) ? genFeature.getRawListItemType() : getTypeArgument(this, eGenericType, true, true));
        sb.append(".class, this, ");
        sb.append(getQualifiedFeatureID(genFeature));
        sb.append(offsetCorrectionField);
        sb.append(")");
      }
    }
    return sb.toString();
  }

  private void appendReifiedFeatureInverseOverride(StringBuffer sb, GenFeature genFeature)
  {
    GenClass sourceGenClass = genFeature.getGenClass();
    EGenericType eGenericType = genFeature.getEcoreFeature().getEGenericType();
    if (isReifiedType(sourceGenClass, eGenericType))
    {
      sb.append(" { private static final long serialVersionUID = 1L; @Override public ");
      sb.append(getGenModel().getImportedName("java.lang.Class"));
      sb.append("<?> getInverseFeatureClass() { return ");
      sb.append(getTypeArgument(sourceGenClass, eGenericType, true, true));
      sb.append(".class; } }");
    }
  }

  public boolean isModelRoot()
  {
    return getClassExtendsGenClass() == null || getClassExtendsGenClass().getGenModel() != getGenModel();
  }

  public List<GenFeature> getDeclaredFieldGenFeatures()
  {
    return getImplementedGenFeatures();
  }

  public boolean isFlag(GenFeature genFeature)
  {
    GenModel genModel = getImplementingGenModel(genFeature);
    return genModel.isBooleanFlagsEnabled() && !genFeature.isVolatile() &&
      (genFeature.isBooleanType() || genModel.isPackedEnums() && genFeature.isEnumType());
  }

  public boolean isESetFlag(GenFeature genFeature)
  {
    GenModel genModel = getImplementingGenModel(genFeature);
    return (!genModel.isVirtualDelegation() || genFeature.isPrimitiveType()) && genModel.isBooleanFlagsEnabled() && genFeature.isESetFlag();
  }

  public boolean isField(GenFeature genFeature)
  {
    GenModel genModel = getImplementingGenModel(genFeature);
    return
      !genModel.isReflectiveDelegation() &&
        !genModel.isDynamicDelegation() &&
        (!genModel.isVirtualDelegation() || genFeature.isPrimitiveType()) &&
        genFeature.isField();
  }

  public boolean isESetField(GenFeature genFeature)
  {
    GenModel genModel = getImplementingGenModel(genFeature);
    return
      !genModel.isReflectiveDelegation() &&
        !genModel.isDynamicDelegation() &&
        (!genModel.isVirtualDelegation() || genFeature.isPrimitiveType()) &&
        genFeature.isESetField();
  }

  public class CollidingGenOperationFilter implements GenOperationFilter
  {
    protected List<GenFeature> allGenFeatures = getAllGenFeatures();
    protected List<GenOperation> extendsGenClassOperations;
    protected List<GenFeature> extendsGenClassFeatures;
    
    public CollidingGenOperationFilter()
    {
      GenClass extendsClass = getClassExtendsGenClass();
      if (extendsClass != null)
      {
        extendsGenClassOperations = extendsClass.getAllGenOperations();
        extendsGenClassFeatures = extendsClass.getAllGenFeatures();
      }
      else
      {
        extendsGenClassOperations = Collections.emptyList();
        extendsGenClassFeatures = Collections.emptyList();
      }
    }

    public boolean accept(GenOperation genOperation)
    {
      boolean hasBody = genOperation.hasBody() || genOperation.hasInvocationDelegate();

      String name = genOperation.getName();
      if (name.startsWith("isSet") && genOperation.getGenParameters().isEmpty())
      {
        for (GenFeature genFeature : allGenFeatures)
        {
          if (genFeature.isChangeable() &&
                genFeature.isUnsettable() &&
                name.equals("isSet" + genFeature.getAccessorName()) &&
                (!hasBody ||
                  !extendsGenClassFeatures.contains(genFeature) &&
                    (genFeature.isVolatile() && !genFeature.hasDelegateFeature() ||
                      genFeature.getGenClass() != GenClassImpl.this && !genFeature.getGenClass().hasCollidingIsSetAccessorOperation(genFeature) && genOperation.getGenClass() != GenClassImpl.this)))
          {
            return false;
          }
        }
      }
      else if ((name.startsWith("get") || name.startsWith("is")) && genOperation.getGenParameters().isEmpty())
      {
        String operationType = genOperation.getType(GenClassImpl.this);
        for (GenFeature genFeature : allGenFeatures)
        {
          if (genFeature.getGetAccessor().equals(name) &&
                (genFeature.getType(GenClassImpl.this).equals(operationType) || !extendsGenClassFeatures.contains(genFeature)) &&
                (!hasBody ||
                    !extendsGenClassFeatures.contains(genFeature) &&
                      (genFeature.isVolatile() && (!genFeature.isResolveProxies() || genFeature.isListType()) && !genFeature.hasDelegateFeature() ||
                        genFeature.getGenClass() != GenClassImpl.this && !genFeature.getGenClass().hasCollidingGetAccessorOperation(genFeature) && genOperation.getGenClass() != GenClassImpl.this)))
          {
            return false;
          }
        }
      }
      else if (name.startsWith("set") && genOperation.getGenParameters().size() == 1)
      {
        GenParameter genParameter = genOperation.getGenParameters().get(0);
        for (GenFeature genFeature : allGenFeatures)
        {
          if (genFeature.isChangeable() &&
                !genFeature.isListType() &&
                name.equals("set" + genFeature.getAccessorName()) &&
                genParameter.getType(GenClassImpl.this).equals(genFeature.getType(GenClassImpl.this)) &&
                (!hasBody ||
                  !extendsGenClassFeatures.contains(genFeature) &&
                   (genFeature.isVolatile() && !genFeature.hasDelegateFeature() ||
                     genFeature.getGenClass() != GenClassImpl.this && !genFeature.getGenClass().hasCollidingSetAccessorOperation(genFeature) && genOperation.getGenClass() != GenClassImpl.this)))
          {
            return false;
          }
        }
      }
      else if (name.startsWith("unset") && genOperation.getGenParameters().isEmpty())
      {
        for (GenFeature genFeature : allGenFeatures)
        {
          if (genFeature.isChangeable() &&
                genFeature.isUnsettable() &&
                name.equals("unset" + genFeature.getAccessorName()) &&
                (!hasBody ||
                  !extendsGenClassFeatures.contains(genFeature) &&
                    (genFeature.isVolatile() && !genFeature.hasDelegateFeature() ||
                       genFeature.getGenClass() != GenClassImpl.this && !genFeature.getGenClass().hasCollidingUnsetAccessorOperation(genFeature) && genOperation.getGenClass() != GenClassImpl.this)))
          {
            return false;
          }
        }
      }

      if (!hasBody)
      {
        String operationType = genOperation.getType(GenClassImpl.this);
        for (GenOperation baseOperation : extendsGenClassOperations)
        {
          if (baseOperation.isOverrideOf(GenClassImpl.this, genOperation))
          {
            String baseOperationType = baseOperation.getType(GenClassImpl.this);
            if (operationType== null ? baseOperationType == null : operationType.equals(baseOperationType))
            {
              return false;
            }
          }
        }
      }

      return !genOperation.getGenClass().isEObject();
    }
  }

  // Returns whether this class implements any of the given features.
  public boolean implementsAny(Collection<GenFeature> genFeatures)
  {
    List<GenFeature> implementedGenFeatures = getImplementedGenFeatures();
    if (!implementedGenFeatures.isEmpty())
    {  
      for (GenFeature genFeature : genFeatures)
      {
        if (implementedGenFeatures.contains(genFeature))
        {
          return true;
        }
      }
    }
    return false;
  }

  protected int getNonPrimitiveFeatureCount()
  {
    return 
    collectGenFeatures
      (getAllBaseGenClasses(), 
       getGenFeatures(), 
       new GenFeatureFilter() 
       {
         public boolean accept(GenFeature genFeature) 
         {
           return !genFeature.isPrimitiveType();
         }
       }).size();
  }

  public String getEVirtualValuesField()
  {
    String eVirtualValuesField = null;

    for (GenClass classExtendsGenClass = getClassExtendsGenClass(); eVirtualValuesField == null && classExtendsGenClass != null; classExtendsGenClass = classExtendsGenClass.getClassExtendsGenClass())
    {
      eVirtualValuesField = classExtendsGenClass.getEVirtualValuesField();
    }

    return eVirtualValuesField == null && getGenModel().isVirtualDelegation() && getNonPrimitiveFeatureCount() > 0 ? "eVirtualValues" : null;
  }

  public List<String> getEVirtualIndexBitFields(List<String> eVirtualIndexBitFields)
  {
    if (getGenModel().isVirtualDelegation() && getNonPrimitiveFeatureCount() > 0)
    {
      GenClass classExtendsGenClass = getClassExtendsGenClass();

      for (int i = (classExtendsGenClass == null ? 0 : classExtendsGenClass.getAllEVirtualIndexBitFields(new ArrayList<String>()).size()); i < (getFeatureCount() / 32) + 1; i++)
      {
        eVirtualIndexBitFields.add("eVirtualIndexBits" + i);
      }
    }

    return eVirtualIndexBitFields;
  }

  public List<String> getAllEVirtualIndexBitFields(List<String> allEVirtualIndexBitFields)
  {
    GenClass classExtendsGenClass = getClassExtendsGenClass();
    return getEVirtualIndexBitFields(classExtendsGenClass == null
      ? allEVirtualIndexBitFields : classExtendsGenClass.getAllEVirtualIndexBitFields(allEVirtualIndexBitFields));
  }
  
  public boolean isJavaIOSerializable()
  {
    for (GenClass baseGenClass : getAllBaseGenClasses())
    {
      if ("java.io.Serializable".equals(baseGenClass.getQualifiedInterfaceName()))
      {
        return true;
      }
    }
    GenClass rootImplementsInterfaceGenClass = getGenModel().getRootImplementsInterfaceGenClass();
    if (rootImplementsInterfaceGenClass != null)
    {
      for (GenClass baseGenClass : rootImplementsInterfaceGenClass.getAllBaseGenClasses())
      {
        if ("java.io.Serializable".equals(baseGenClass.getQualifiedInterfaceName()))
        {
          return true;
        }
      }
    }
    return false;
  }
  
  public boolean hasFactoryInterfaceCreateMethod()
  {
    return !isAbstract() && !isMapEntry() && !(getGenModel().isSuppressEMFMetaData() && "org.eclipse.emf.ecore.EObject".equals(getQualifiedInterfaceName()));
  }

  public boolean hasOffsetCorrection()
  {
    GenModel genModel = getGenModel();
    if (genModel.isBinaryCompatibleReflectiveMethods() && genModel.isMinimalReflectiveMethods())
    {
      for (GenClass baseGenClass : getAllBaseGenClasses())
      {
        if (!baseGenClass.isEObject() && baseGenClass.getGenModel()  != genModel)
        {
          return true;
        }
      }
    }
    return false;
  }
  
  public String getOffsetCorrectionField(GenFeature genFeature)
  {
    return 
      genFeature == null ?
        "EOFFSET_CORRECTION" :
        "EOFFSET_CORRECTION_" + genFeature.getUpperName() + "_OPPOSITE";
  }

  public boolean needsHasChildrenMethodOverride()
  {
    boolean optimized = getGenModel().isOptimizedHasChildren();
    GenClass providerExtendsGenClass = getProviderExtendsGenClass();
    boolean optimizedBase =  providerExtendsGenClass != null && providerExtendsGenClass.getGenModel().isOptimizedHasChildren();
    return optimized != optimizedBase;
  }

  protected GenOperation getAccesorOperation(final GenFeature genFeature, final String accessor)
  {
    if (genFeature.isVolatile() &&
          (!accessor.startsWith("get") || !genFeature.isResolveProxies() || genFeature.isListType()) && 
          !genFeature.hasDelegateFeature())
    {
      List<GenOperation> accessorOperations = 
        collectGenOperations
          (this,
           getImplementedGenClasses(),
           null,
           new CollidingGenOperationFilter()
           {
             @Override
             public boolean accept(GenOperation genOperation)
             {
               return accessor.equals(genOperation.getName()) && genOperation.hasBody() && !super.accept(genOperation);
             }
           });
      return accessorOperations.isEmpty() ? null : accessorOperations.get(0);
    }
    else
    {
      return null;
    }
  }

  public GenOperation getGetAccessorOperation(GenFeature genFeature)
  {
    return getAccesorOperation(genFeature, genFeature.getGetAccessor());
  }

  public GenOperation getSetAccessorOperation(GenFeature genFeature)
  {
    return getAccesorOperation(genFeature, "set" + genFeature.getAccessorName());
  }

  public GenOperation getIsSetAccessorOperation(GenFeature genFeature)
  {
    return getAccesorOperation(genFeature, "isSet" + genFeature.getAccessorName());
  }

  public GenOperation getUnsetAccessorOperation(GenFeature genFeature)
  {
    return getAccesorOperation(genFeature, "unset" + genFeature.getAccessorName());
  }

  protected Set<String> getAccessorOperations;
  protected Map<String, Set<String>> setAccessorOperations;
  protected Set<String> isSetAccessorOperations;
  protected Set<String > unsetAccessorOperations;

  protected boolean hasCollidingAccessorOperation(final GenFeature genFeature, final String accessor)
  {
    if (genFeature.isVolatile() &&
          (!accessor.startsWith("get") || !genFeature.isResolveProxies() || genFeature.isListType()) &&
          !genFeature.hasDelegateFeature())
    {
      return false;
    }
    else
    {
      if (getAccessorOperations == null)
      {
        getAccessorOperations = new HashSet<String>();
        setAccessorOperations = new HashMap<String, Set<String>>();
        isSetAccessorOperations = new HashSet<String>();
        unsetAccessorOperations = new HashSet<String>();

        for (GenOperation genOperation : getImplementedGenOperations())
        {
          if (genOperation.hasBody())
          {
            String name = genOperation.getName();
            if (name.startsWith("get"))
            {
              if (genOperation.getGenParameters().isEmpty())
              {
                getAccessorOperations.add(name);
              }
            }
            else if (name.startsWith("set"))
            {
              EList<GenParameter> genParameters = genOperation.getGenParameters();
              if (genParameters.size() == 1)
              {
                Set<String> set = setAccessorOperations.get(name);
                if (set == null)
                {
                  set = new HashSet<String>();
                  setAccessorOperations.put(name, set);
                }
                set.add(genParameters.get(0).getType(this));
              }
            }
            else if (name.startsWith("isSet"))
            {
              if (genOperation.getGenParameters().isEmpty())
              {
                isSetAccessorOperations.add(name);
              }
            }
            else if (name.startsWith("unset"))
            {
              if (genOperation.getGenParameters().isEmpty())
              {
                unsetAccessorOperations.add(name);
              }
            }
          }
        }
      }

      if (accessor.startsWith("get"))
      {
        return getAccessorOperations.contains(accessor);
      }
      else if (accessor.startsWith("set"))
      {
        Set<String> types = setAccessorOperations.get(accessor);
        return types != null && types.contains(genFeature.getType(this));
      }
      else if (accessor.startsWith("isSet"))
      {
        return isSetAccessorOperations.contains(accessor);
        
      }
      else if (accessor.startsWith("unset"))
      {
        return unsetAccessorOperations.contains(accessor);
      }
      
      return false;
    }
  }

  public boolean hasCollidingGetAccessorOperation(GenFeature genFeature)
  {
    return hasCollidingAccessorOperation(genFeature, genFeature.getGetAccessor());
  }
  
  public boolean hasCollidingSetAccessorOperation(GenFeature genFeature)
  {
    return hasCollidingAccessorOperation(genFeature, "set" + genFeature.getAccessorName());
  }
  
  public boolean hasCollidingIsSetAccessorOperation(GenFeature genFeature)
  {
    return hasCollidingAccessorOperation(genFeature, "isSet" + genFeature.getAccessorName());
  }
  
  public boolean hasCollidingUnsetAccessorOperation(GenFeature genFeature)
  {
    return hasCollidingAccessorOperation(genFeature, "unset" + genFeature.getAccessorName());
  }

  public boolean hasStaticFeatures()
  {
    switch (getGenModel().getFeatureDelegation())
    {
      case DYNAMIC_LITERAL:
      case REFLECTIVE_LITERAL:
      {
        GenClass classExtendsGenClass = getClassExtendsGenClass();
        return classExtendsGenClass != null && classExtendsGenClass.hasStaticFeatures();
      }
      default:
      {
        return getAllGenFeatures().size() > 0;
      }
    }
  }

  @Override
  protected String getLink()
  {
    return getImportedInterfaceName();
  }
}
