/**
* Copyright (c) 2007 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM - Initial API and implementation
*/
package org.eclipse.emf.ecore.xmi.impl;
import java.util.Collection;
import java.util.List;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.util.ExtendedMetaData;
import org.eclipse.emf.ecore.xmi.XMLResource;
import org.eclipse.emf.ecore.xml.type.XMLTypePackage;
/**
* An implementation of an element handler that includes support for searching for features in a specified collection of packages
* as well as optional support for searching for substitution group features for super types of the specified type.
* @see XMLResource#OPTION_ELEMENT_HANDLER
*/
public class ElementHandlerImpl implements XMLResource.ElementHandler
{
protected boolean considerSubtypes;
protected Collection<? extends EPackage> ePackages;
/**
* Creates a default instances.
* @param considerSubtypes whether to consider {@link ExtendedMetaData#getBaseType(EDataType) base types} or {@link EClass#getESuperTypes() super types}
* when finding a substitution group.
*/
public ElementHandlerImpl(boolean considerSubtypes)
{
this.considerSubtypes = considerSubtypes;
}
/**
* Creates an instance that will search the given packages for candidate features.
* @param considerSubtypes whether to consider {@link ExtendedMetaData#getBaseType(EDataType) base types} or {@link EClass#getESuperTypes() super types}
* when finding a substitution group.
* @param ePackages the packages to search for candidates.
*/
public ElementHandlerImpl(boolean considerSubtypes, Collection<? extends EPackage> ePackages)
{
this.considerSubtypes = considerSubtypes;
this.ePackages = ePackages;
}
public EStructuralFeature getRoot(ExtendedMetaData extendedMetaData, EClassifier eClassifier)
{
if (extendedMetaData == null)
{
return null;
}
else
{
// Walk up the super types until we reach a root.
//
while (eClassifier != null)
{
// Look for a matching element in the classifier's package but don't bother with the XML type package's document root.
//
EClass eClass = extendedMetaData.getDocumentRoot(eClassifier.getEPackage());
if (eClass != null && eClass != XMLTypePackage.Literals.XML_TYPE_DOCUMENT_ROOT)
{
for (EStructuralFeature element : extendedMetaData.getElements(eClass))
{
if (element.getEType() == eClassifier && element.isChangeable())
{
return element;
}
}
}
// Look for a matching element in the specified packages if there are any.
//
if (ePackages != null)
{
for (EPackage ePackage : ePackages)
{
eClass = extendedMetaData.getDocumentRoot(ePackage);
if (eClass != null)
{
for (EStructuralFeature element : extendedMetaData.getElements(eClass))
{
if (element.getEType() == eClassifier && element.isChangeable())
{
return element;
}
}
}
}
}
eClassifier = getSuperType(extendedMetaData, eClassifier);
}
return null;
}
}
/**
* Returns the {@link ExtendedMetaData#getBaseType(EDataType) base type} or first {@link EClass#getESuperTypes() super type} of the classifier,
* depending on there it is a {@link EDataType data type} or a {@link EClass class}.
* @param extendedMetaData the extended meta data in which to look up type information.
* @param eClassifier the classifier in question.
* @return the {@link ExtendedMetaData#getBaseType(EDataType) base type}, the first {@link EClass#getESuperTypes() super type} of the classifier, or <code>null</code>.
*/
protected EClassifier getSuperType(ExtendedMetaData extendedMetaData, EClassifier eClassifier)
{
if (eClassifier instanceof EDataType)
{
return extendedMetaData.getBaseType((EDataType)eClassifier);
}
else
{
List<EClass> eSuperTypes = ((EClass)eClassifier).getESuperTypes();
if (eSuperTypes.isEmpty())
{
return null;
}
else
{
return eSuperTypes.get(0);
}
}
}
public EStructuralFeature getSubstitutionGroup(ExtendedMetaData extendedMetaData, EStructuralFeature eStructuralFeature, EClassifier eClassifier)
{
if (extendedMetaData == null)
{
return null;
}
else
{
// Look for a substitution group feature in the feature's containing class' containing package.
//
EClass eContainingClass = eStructuralFeature.getEContainingClass();
while (eClassifier != null)
{
EStructuralFeature result = getSubstitutionGroup(extendedMetaData, eContainingClass.getEPackage(), eContainingClass, eStructuralFeature, eClassifier);
if (result != null)
{
return result;
}
else
{
// Look for a substitution group feature in the classifier's containing package.
//
result = getSubstitutionGroup(extendedMetaData, eClassifier.getEPackage(), eContainingClass, eStructuralFeature, eClassifier);
if (result != null)
{
return result;
}
else
{
// Look for a substitution group feature in the additional packages.
//
if (ePackages != null)
{
for (EPackage ePackage : ePackages)
{
result = getSubstitutionGroup(extendedMetaData, ePackage, eContainingClass, eStructuralFeature, eClassifier);
if (result != null)
{
return result;
}
}
}
// Process the super types if that's been specified.
//
if (considerSubtypes)
{
eClassifier = getSuperType(extendedMetaData, eClassifier);
}
else
{
break;
}
}
}
}
return null;
}
}
/**
* Searches the document root object package for a changeable (non-abstract) element
* that is affiliated with the given feature in the given class
* and a classifier that exactly matches the given classifier
* @param extendedMetaData the extended meta data in which to look up type information.
* @param ePackage the package whose document root to search.
* @param eContainingClass the containing class of the feature.
* @param eStructuralFeature the target feature.
* @param eClassifier the type of object being matched.
* @return the substitution group feature or <code>null</code>.
*/
protected EStructuralFeature getSubstitutionGroup
(ExtendedMetaData extendedMetaData, EPackage ePackage, EClass eContainingClass, EStructuralFeature eStructuralFeature, EClassifier eClassifier)
{
EClass eClass = extendedMetaData.getDocumentRoot(ePackage);
if (eClass != null)
{
for (EStructuralFeature element : extendedMetaData.getElements(eClass))
{
if (element.getEType() == eClassifier && element.isChangeable() && extendedMetaData.getAffiliation(eContainingClass, element) == eStructuralFeature)
{
return element;
}
}
}
return null;
}
}