blob: 207e4a8183c1dbdd997bacda7a3950a056108dc1 [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2005, 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 v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
****************************************************************************/
package org.eclipse.gmf.runtime.emf.type.core;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.dynamichelpers.ExtensionTracker;
import org.eclipse.core.runtime.dynamichelpers.IExtensionChangeHandler;
import org.eclipse.core.runtime.dynamichelpers.IExtensionTracker;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.gmf.runtime.common.core.util.Log;
import org.eclipse.gmf.runtime.emf.type.core.edithelper.IEditHelperAdvice;
import org.eclipse.gmf.runtime.emf.type.core.internal.EMFTypePlugin;
import org.eclipse.gmf.runtime.emf.type.core.internal.EMFTypePluginStatusCodes;
import org.eclipse.gmf.runtime.emf.type.core.internal.descriptors.ElementTypeDescriptor;
import org.eclipse.gmf.runtime.emf.type.core.internal.descriptors.ElementTypeFactoryDescriptor;
import org.eclipse.gmf.runtime.emf.type.core.internal.descriptors.ElementTypeXmlConfig;
import org.eclipse.gmf.runtime.emf.type.core.internal.descriptors.MetamodelDescriptor;
import org.eclipse.gmf.runtime.emf.type.core.internal.descriptors.MetamodelTypeDescriptor;
import org.eclipse.gmf.runtime.emf.type.core.internal.descriptors.SpecializationTypeDescriptor;
import org.eclipse.gmf.runtime.emf.type.core.internal.impl.DefaultMetamodelType;
import org.eclipse.gmf.runtime.emf.type.core.internal.impl.EClassUtil;
import org.eclipse.gmf.runtime.emf.type.core.internal.impl.SpecializationTypeRegistry;
import org.eclipse.gmf.runtime.emf.type.core.internal.l10n.EMFTypeCoreMessages;
/**
* The registry of application element types, contributed by the
* <code>org.eclipse.gmf.runtime.emf.type.core.elementTypes</code> extension
* point, or registered directly through {@link #register(IMetamodelType)} or
* {@link #register(ISpecializationType)}.
*
* @author ldamus
*/
public class ElementTypeRegistry {
/**
* The name of the element type extension point.
*/
private static final String ELEMENT_TYPES_EXT_P_NAME = "elementTypes"; //$NON-NLS-1$
/**
* Empty element type array for convenience.
*/
private static final IElementType[] EMPTY_ELEMENT_TYPE_ARRAY = new IElementType[] {};
/**
* My specialization type registr. Keeps track of the specializations and
* advice bindings and provides methods for finding matching edit helper
* advice.
*/
private final SpecializationTypeRegistry specializationTypeRegistry;
/**
* Metamodel type descriptors stored by nsURI. Each key is a namespace URI
* and each value is a map, whose key is an EClass name and whose value is a
* collection of MetamodelTypeDescriptors.
*/
private final Map metamodelTypeDescriptorsByNsURI;
/**
* All metamodel type descriptors stored by ID. Each value is an instance of
* <code>MetamodelTypeDescriptor</code>.
*/
private final Map metamodelTypeDescriptorsById;
/**
* ElementTypeFactories stored by kind. Each factory declares a string that
* describes the kind of element that it is responsible for creating. Each
* value is a single <code>ElementTypeFactory</code>.
*/
private final Map elementTypeFactoryMap;
/**
* Listeners for changes to this registry.
*/
private final List elementTypeRegistryListeners;
/**
* Singleton instance.
*/
private static ElementTypeRegistry INSTANCE;
private ExtensionTracker extensionTracker;
private IExtensionChangeHandler extensionListener;
/**
* Constructs a new element type registry.
*/
private ElementTypeRegistry(IConfigurationElement[] configs) {
super();
specializationTypeRegistry = new SpecializationTypeRegistry();
metamodelTypeDescriptorsByNsURI = new HashMap();
metamodelTypeDescriptorsById = new HashMap();
elementTypeFactoryMap = new HashMap();
elementTypeRegistryListeners = new ArrayList();
registerNullElementType();
if (EMFTypePlugin.isDynamicAware()) {
startExtensionTracking();
}
load(configs);
}
private void startExtensionTracking() {
extensionListener = new IExtensionChangeHandler() {
public void addExtension(IExtensionTracker tracker,
IExtension extension) {
load(extension.getConfigurationElements());
}
public void removeExtension(IExtension extension, Object[] objects) {
// Extension removal not supported
}
};
IExtensionPoint point = Platform.getExtensionRegistry()
.getExtensionPoint(EMFTypePlugin.getPluginId(),
ELEMENT_TYPES_EXT_P_NAME);
extensionTracker = new ExtensionTracker();
extensionTracker.registerHandler(extensionListener, ExtensionTracker
.createExtensionPointFilter(point));
}
/**
* Gets the singleton element type registry.
*
* @return the singleton element type registry
*/
public static ElementTypeRegistry getInstance() {
if (INSTANCE == null) {
IConfigurationElement[] configs = Platform.getExtensionRegistry()
.getConfigurationElementsFor(EMFTypePlugin.getPluginId(),
ELEMENT_TYPES_EXT_P_NAME);
INSTANCE = new ElementTypeRegistry(configs);
}
return INSTANCE;
}
/**
* Gets the edit helper advice for <code>type</code> in order of most
* general advice to most specific advice. This order is used so that the
* more specific advice can act on or modify the more general advice.
* <P>
* The client context is inferred from the <code>type</code>.
*
* @param type
* the element type for which to obtain editing advice
* @return the array of edit helper advice descriptors
*/
public IEditHelperAdvice[] getEditHelperAdvice(IElementType type) {
IClientContext clientContext = ClientContextManager.getInstance()
.getBinding(type);
return getEditHelperAdvice(type, clientContext);
}
/**
* Gets the edit helper advice for <code>type</code> to which the
* <code>clientContext</code> has been bound, in order of most general
* advice to most specific advice. This order is used so that the more
* specific advice can act on or modify the more general advice.
*
* @param type
* the element type for which to obtain editing advice
* @param clientContext
* the client context
* @return the array of edit helper advice descriptors
*/
public IEditHelperAdvice[] getEditHelperAdvice(IElementType type, IClientContext clientContext) {
IClientContext context = (clientContext == null) ? ClientContextManager
.getDefaultClientContext() : clientContext;
EClass eClass = type.getEClass();
MetamodelTypeDescriptor metamodelType = (eClass != null) ? getMetamodelTypeDescriptor(
eClass, context)
: null;
List result = specializationTypeRegistry.getEditHelperAdvice(type,
metamodelType, context);
return (IEditHelperAdvice[]) result.toArray(new IEditHelperAdvice[] {});
}
/**
* Gets the edit helper advice for <code>eObject</code> in order of most
* general advice to most specific advice. This order is used so that the
* more specific advice can act on or modify the more general advice.
*
* @param eObject
* the model element for which to obtain editing advice
* @param clientContext
* the client context
* @return the array of edit helper advice
*/
public IEditHelperAdvice[] getEditHelperAdvice(EObject eObject,
IClientContext clientContext) {
Collection result;
MetamodelTypeDescriptor desc = getMetamodelTypeDescriptor(eObject, clientContext);
if (desc == null) {
result = specializationTypeRegistry.getEditHelperAdvice(
eObject,
DefaultMetamodelType.getDescriptorInstance(), clientContext);
} else {
result = specializationTypeRegistry.getEditHelperAdvice(eObject, desc, clientContext);
}
return (IEditHelperAdvice[]) result.toArray(new IEditHelperAdvice[] {});
}
/**
* Gets the edit helper advice for <code>eObject</code> in order of most
* general advice to most specific advice. This order is used so that the
* more specific advice can act on or modify the more general advice.
* <P>
* The client context will be inferred from the <code>eObject</code>.
*
* @param eObject
* the model element for which to obtain editing advice
* @return the array of edit helper advice
*/
public IEditHelperAdvice[] getEditHelperAdvice(EObject eObject) {
IClientContext clientContext = ClientContextManager.getInstance()
.getClientContextFor(eObject);
return getEditHelperAdvice(eObject, clientContext);
}
/**
* Gets the edit helper advice registered for <code>o</code>, which can
* be either an EObject or an IElementType or an
* <code>IEditHelperContext</code>.
*
* @param o
* the element or type
* @return the edit helper advice, or <code>null</code> if none.
*/
public IEditHelperAdvice[] getEditHelperAdvice(Object o) {
if (o instanceof EObject) {
return getEditHelperAdvice((EObject) o);
} else if (o instanceof IElementType) {
return getEditHelperAdvice((IElementType) o);
} else if (o instanceof IEditHelperContext) {
IEditHelperContext editHelperContext = (IEditHelperContext) o;
IClientContext clientContext = editHelperContext.getClientContext();
IElementType elementType = editHelperContext.getElementType();
EObject eObject = editHelperContext.getEObject();
if (clientContext != null) {
if (elementType != null) {
return getEditHelperAdvice(elementType, clientContext);
} else if (eObject != null) {
return getEditHelperAdvice(eObject, clientContext);
}
} else {
if (elementType != null) {
return getEditHelperAdvice(elementType);
} else if (eObject != null) {
return getEditHelperAdvice(eObject);
}
}
}
return null;
}
/**
* Gets the array of types that can be contained in the structural
* <code>feature</code> of <code>eContainer</code>. The result will not
* include types that represent abstract EClasses.
*
* @param eContainer
* the container
* @param reference
* the feature
* @param clientContext
* the client context
* @return the array of types
*/
public IElementType[] getContainedTypes(EObject eContainer,
EReference reference, IClientContext clientContext) {
Set result = new HashSet();
EClass containerEClass = eContainer.eClass();
if (reference.isContainment()
&& (containerEClass.getEAllReferences().contains(reference))) {
// Get the reference type (eclass) and all of its subtypes
EClass eType = (EClass) reference.getEType();
Set types = getSubtypes(containerEClass.getEPackage(), eType);
types.add(eType);
// Get the metamodel types for the eclasses
List metamodelTypeDescriptors = getMetamodelTypeDescriptors(types, clientContext);
for (Iterator i = metamodelTypeDescriptors.iterator(); i.hasNext();) {
MetamodelTypeDescriptor nextMetamodelTypeDescriptor = (MetamodelTypeDescriptor) i
.next();
IMetamodelType nextMetamodelType = (IMetamodelType) nextMetamodelTypeDescriptor
.getElementType();
if (nextMetamodelType != null
&& !nextMetamodelType.getEClass().isAbstract()) {
// Add the metamodel type
result.add(nextMetamodelType);
// Add the specialization types that match the given
// container and reference
Collection specializationDescriptors = specializationTypeRegistry
.getMatchingSpecializations(
nextMetamodelTypeDescriptor, eContainer, reference, clientContext);
for (Iterator j = specializationDescriptors.iterator(); j
.hasNext();) {
ElementTypeDescriptor nextDescriptor = (ElementTypeDescriptor) j
.next();
IElementType nextElementType = nextDescriptor
.getElementType();
if (nextElementType != null) {
result.add(nextElementType);
}
}
}
}
}
return (IElementType[]) result.toArray(EMPTY_ELEMENT_TYPE_ARRAY);
}
/**
* Gets the array of types that can be contained in the structural
* <code>feature</code> of <code>eContainer</code>. The result will
* not include types that represent abstract EClasses.
* <P>
* The client context will be inferred from the <code>eContainer</code>.
*
* @param eContainer
* the container
* @param reference
* the feature
* @return the array of types
*/
public IElementType[] getContainedTypes(EObject eContainer,
EReference reference) {
IClientContext clientContext = ClientContextManager.getInstance()
.getClientContextFor(eContainer);
return getContainedTypes(eContainer, reference, clientContext);
}
/**
* Gets the subtypes of <code>eType</code> in the <code>pkg</code>.
*
* @param pkg
* the package in which to find matching classifiers
* @param eType
* the eClass for which to find subtypes
* @return the matching subtypes
*/
private Set getSubtypes(EPackage pkg, final EClass eType) {
Set result = new HashSet();
List classifiers = pkg.getEClassifiers();
for (Iterator i = classifiers.iterator(); i.hasNext();) {
EClassifier nextClassifier = (EClassifier) i.next();
// Bugzilla 298661: assume all models implicitly extend EObject
if (nextClassifier instanceof EClass
&& ((eType == EcorePackage.Literals.EOBJECT) || ((EClass) nextClassifier)
.getEAllSuperTypes().contains(eType))) {
result.add(nextClassifier);
}
}
return result;
}
/**
* Gets the metamodel type for <code>eClass</code> in the client
* <code>context</code>. If there is none registered against the
* <code>eClass</code>, returns the metamodel type for the nearest
* supertype of <code>eClass/code> that has a metamodel type.
*
* @param eClass
* the metaclass
* @param context the client context
* @return the metamodel type for this <code>eClass</code> in the client <code>context</code>, or <code>null</code> if none can be found.
*/
private IMetamodelType getMetamodelType(EClass eClass, IClientContext context) {
MetamodelTypeDescriptor descriptor = getMetamodelTypeDescriptor(eClass, context);
if (descriptor != null) {
return (IMetamodelType) descriptor.getElementType();
}
return null;
}
/**
* Gets the metamodel type registered for <code>eObject</code>'s EClass.
* If there is none registered against the <code>eClass</code>, returns
* the metamodel type for the nearest supertype of
* <code>eClass/code> that has a metamodel type.
*
* @param eObject
* the model element
* @param clientContext the clientContext
* @return the metamodel type for this <code>eObject</code>
*/
private IMetamodelType getMetamodelType(EObject eObject, IClientContext clientContext) {
return getMetamodelType(eObject.eClass(), clientContext);
}
/**
* Convenience method to get an element type for <code>o</code>.
* <P>
* If <code>o</code> is an <code>IElementType</code>, returns
* <code>o</code>.
* <P>
* If <code>o</code> is an <code>EObject</code>, returns the metamodel
* type registered for <code>o</code>'s eClass in the client context that
* is bound to <code>o</code>.
* <P>
* If <code>o</code> is an <code>IEditHelperContext</code>, returns the
* element type in <code>o</code> if specified. Else, returns the
* metamodel type registered for the eClass of the EObject specified in
* <code>o</code> in the client context specified in <code>o</code>. If
* no client context is specified, then the client context bound to the
* EObject is used.
* <P>
* Use {@link #getElementType(EClass, IClientContext)} to get metamodel
* types registered for a specific <code>EClass</code>.
*
* @param o
* the object for which to find an element type.
* @return <code>o</code> itself if it is an element type, otherwise
* returns the registered metamodel type
*/
public IElementType getElementType(Object o) {
if (o instanceof EObject) {
return getElementType((EObject) o);
} else if (o instanceof IElementType) {
return (IElementType) o;
} else if (o instanceof IEditHelperContext) {
IEditHelperContext editHelperContext = (IEditHelperContext) o;
IElementType elementType = editHelperContext.getElementType();
if (elementType != null) {
return elementType;
}
IClientContext clientContext = editHelperContext.getClientContext();
EObject eObject = editHelperContext.getEObject();
if (eObject != null) {
if (clientContext != null) {
return getElementType(eObject, clientContext);
} else {
return getElementType(eObject);
}
}
}
return null;
}
/**
* Gets the registered element type for <code>eClass</code> that
* has no client contexts explicitly bound to it.
*
* @param eClass
* the <code>EClass</code> whose element type is to be found.
* @return the metamodel type registered for <code>eClass</code>
*/
public IElementType getElementType(EClass eClass) {
return getElementType(eClass, null);
}
/**
* Gets the registered element type for <code>eClass</code>.
*
* @param eClass
* the <code>EClass</code> whose element type is to be found.
* @param clientContext
* the client context
* @return the metamodel type registered for <code>eClass</code>
*/
public IElementType getElementType(EClass eClass, IClientContext clientContext) {
IElementType result = getMetamodelType(eClass, clientContext);
if (result == null) {
// at least provide the default type for default editing support
result = DefaultMetamodelType.getInstance();
}
return result;
}
/**
* Gets the registered element type for <code>eObject</code>.
* <P>
* The client context will be inferred from the <code>eObject</code>.
*
* @param eObject
* the <code>EObject</code> whose element type is to be found.
* @return the metamodel type registered for <code>eObject</code>'s
* <code>EClass</code>
*/
public IElementType getElementType(EObject eObject) {
IClientContext clientContext = ClientContextManager.getInstance()
.getClientContextFor(eObject);
return getElementType(eObject, clientContext);
}
/**
* Gets the registered element type for <code>eObject</code> in the
* <code>clientContext</code>.
*
* @param eObject
* the <code>EObject</code> whose element type is to be found.
* @param clientContext
* the client context
* @return the metamodel type registered for <code>eObject</code>'s
* <code>EClass</code>
*/
public IElementType getElementType(EObject eObject,
IClientContext clientContext) {
IElementType result = getMetamodelType(eObject, clientContext);
if (result == null) {
// at least provide the default type for default editing support
result = DefaultMetamodelType.getInstance();
}
return result;
}
/**
* Gets the metamodel type descriptor for <code>eObject</code>'s EClass
* in the client <code>context</code>. If there is none registered
* against the <code>eClass</code> for that <code>context</code>,
* returns the metamodel type for the nearest supertype of
* <code>eClass</code> that has a metamodel type in that
* <code>context</code>.
*
* @param eObject
* the model element
* @param context
* the client context
* @return the metamodel type descriptor
*/
private MetamodelTypeDescriptor getMetamodelTypeDescriptor(EObject eObject,
IClientContext context) {
return getMetamodelTypeDescriptor(eObject.eClass(), context);
}
/**
* Gets the metamodel type descriptor for <code>eClass</code> in the
* client <code>context</code>. If there is none registered against the
* <code>eClass</code> for the client <code>context</code>, returns the
* metamodel type for the nearest supertype of <code>eClass</code> that
* has a metamodel type in the client <code>context</code>.
*
* @param eClass
* the model element eclass
* @param context
* the client context
* @return the metamodel type descriptor
*/
private MetamodelTypeDescriptor getMetamodelTypeDescriptor(EClass eClass,
IClientContext context) {
IClientContext clientContext = context;
if (clientContext == null) {
// use the default context
clientContext = ClientContextManager.getDefaultClientContext();
}
Map metamodelTypeDescriptorsByEClass = (Map) metamodelTypeDescriptorsByNsURI
.get(eClass.getEPackage().getNsURI());
Collection descriptors = metamodelTypeDescriptorsByEClass != null ? (Collection) metamodelTypeDescriptorsByEClass
.get(eClass.getName())
: null;
if (descriptors != null) {
for (Iterator i = descriptors.iterator(); i.hasNext();) {
MetamodelTypeDescriptor descriptor = (MetamodelTypeDescriptor) i
.next();
if (clientContext.includes(descriptor)) {
return descriptor;
}
}
}
// Find the metamodel type for the nearest supertype.
// Bugzilla 298661: assume all models implicitly extend EObject
List supertypes = EClassUtil.getEAllSuperTypes(eClass);
for (int i = supertypes.size() - 1; i >= 0; i--) {
EClass nextEClass = (EClass) supertypes.get(i);
// nsURI could be different in supertypes of the eclass
metamodelTypeDescriptorsByEClass = (Map) metamodelTypeDescriptorsByNsURI.get(nextEClass.getEPackage().getNsURI());
descriptors = metamodelTypeDescriptorsByEClass != null ? (Collection) metamodelTypeDescriptorsByEClass
.get(nextEClass.getName())
: null;
if (descriptors != null) {
for (Iterator j = descriptors.iterator(); j.hasNext();) {
MetamodelTypeDescriptor descriptor = (MetamodelTypeDescriptor) j
.next();
if (clientContext.includes(descriptor)) {
return descriptor;
}
}
}
}
return null;
}
/**
* Gets all of the element types (metamodel type and specialization types)
* that match <code>eObject</code> in breadth-first order (specializations
* before metamodel types).
*
* @param eObject
* the model element to match
* @param clientContext
* the client context
* @return all of the element types that match the model element
*/
public IElementType[] getAllTypesMatching(EObject eObject, IClientContext clientContext) {
List result = new ArrayList();
IMetamodelType metamodelType = getMetamodelType(eObject, clientContext);
if (metamodelType != null) {
// Get the matching specializations
Collection specializations;
MetamodelTypeDescriptor desc = getMetamodelTypeDescriptor(eObject, clientContext);
if (desc == null) {
specializations = Collections.EMPTY_LIST;
} else {
specializations = specializationTypeRegistry
.getSpecializationDescriptorsMatching(eObject, desc, clientContext);
}
for (Iterator i = specializations.iterator(); i.hasNext();) {
SpecializationTypeDescriptor next = (SpecializationTypeDescriptor) i
.next();
IElementType elementType = next.getElementType();
if (elementType != null) {
result.add(elementType);
}
}
// Add the metamodel type
result.add(metamodelType);
// Add the metamodel supertypes in reverse order
IElementType[] superTypes = metamodelType.getAllSuperTypes();
for (int i = superTypes.length - 1; i >= 0; i--) {
result.add(superTypes[i]);
}
}
if (result.isEmpty()) {
// at least provide the default type for default editing behaviour
result.add(DefaultMetamodelType.getInstance());
}
return (IElementType[]) result.toArray(EMPTY_ELEMENT_TYPE_ARRAY);
}
/**
* Gets all of the element types (metamodel type and specialization types)
* that match <code>eObject</code> in breadth-first order (specializations
* before metamodel types).
* <P>
* The client context will be inferred from the <code>eObject</code>.
*
* @param eObject
* the model element to match
* @return all of the element types that match the model element
*/
public IElementType[] getAllTypesMatching(EObject eObject) {
IClientContext clientContext = ClientContextManager.getInstance()
.getClientContextFor(eObject);
return getAllTypesMatching(eObject, clientContext);
}
/**
* Gets an array containing all specializations of the element type for
* <code>id</code>, in breadth-first order.
*
* @param id
* the element type ID
* @return the list of all specializations of this element type
*/
public ISpecializationType[] getSpecializationsOf(String id) {
IElementTypeDescriptor descriptor = getTypeDescriptor(id);
if (descriptor == null) {
return new ISpecializationType[] {};
}
IClientContext clientContext = ClientContextManager.getInstance()
.getBinding(descriptor);
return specializationTypeRegistry
.getAllSpecializationTypes(descriptor, clientContext);
}
/**
* Gets the metamodel types in the registry that are bound to the
* <code>clientContext</code>.
*
* @param clientContext
* the client context
* @return the metamodel types
*/
public IMetamodelType[] getMetamodelTypes(IClientContext clientContext) {
List result = new ArrayList();
for (Iterator i = metamodelTypeDescriptorsById.values().iterator(); i
.hasNext();) {
MetamodelTypeDescriptor descriptor = (MetamodelTypeDescriptor) i
.next();
if (clientContext.includes(descriptor)) {
result.add(descriptor.getElementType());
}
}
return (IMetamodelType[]) result.toArray(new IMetamodelType[result.size()]);
}
/**
* Gets the specialization types in the registry that are bound to the
* <code>clientContext</code>.
*
* @param clientContext
* the client context
* @return the specialization types
*/
public ISpecializationType[] getSpecializationTypes(
IClientContext clientContext) {
List result = new ArrayList();
Collection specializations = specializationTypeRegistry
.getSpecializationTypeDescriptors(clientContext);
for (Iterator i = specializations.iterator(); i.hasNext();) {
result.add(((SpecializationTypeDescriptor) i.next())
.getElementType());
}
return (ISpecializationType[]) result.toArray(new ISpecializationType[result.size()]);
}
/**
* Gets the element types (both metamodel types and specialization types) in
* the registry that are bound to the <code>clientContext</code>.
*
* @param clientContext
* the client context
* @return the element types
*/
public IElementType[] getElementTypes(IClientContext clientContext) {
IMetamodelType[] metamodelTypes = getMetamodelTypes(clientContext);
ISpecializationType[] specializationTypes = getSpecializationTypes(clientContext);
IElementType[] result = new IElementType[metamodelTypes.length
+ specializationTypes.length];
for (int i = 0; i < metamodelTypes.length; i++) {
result[i] = metamodelTypes[i];
}
for (int i = 0; i < specializationTypes.length; i++) {
result[i + metamodelTypes.length] = specializationTypes[i];
}
return result;
}
/**
* Gets the element type for <code>id</code>. May return
* <code>null</code> if this element is not registered.
*
* @param id
* the type ID
* @return the registered type with this ID, or <code>null</code> if there
* is none.
*/
public IElementType getType(String id) {
ElementTypeDescriptor typeDescriptor = getTypeDescriptor(id);
if (typeDescriptor != null) {
return typeDescriptor.getElementType();
} else if (DefaultMetamodelType.ID.equals(id)) {
return DefaultMetamodelType.getInstance();
}
return null;
}
/**
* Gets the element type factory registered to create element types of kind
* <code>kindName</code>.
*
* @param kindName
* the element type kind name
* @return the element type factory for this kind, or
* <code>null/code> if there is none
*/
public IElementTypeFactory getElementTypeFactory(String kindName) {
ElementTypeFactoryDescriptor descriptor = (ElementTypeFactoryDescriptor) elementTypeFactoryMap
.get(kindName);
if (descriptor != null) {
return descriptor.getElementTypeFactory();
}
return null;
}
/**
* Registers <code>metamodelType</code> with this registry, if its ID is
* unique in the registry. The type's EClass does not have to be unique in
* the registry. Metamodel types in the registry are distinguished by the
* client context that is bound to the type.
* <P>
* Notifies clients if the element type was added to the registry.
*
* @param metamodelType
* the element type to register
* @return <code>true</code> if the type was registered,
* <code>false</code> otherwise
*/
public boolean register(IMetamodelType metamodelType) {
if (metamodelType == null
|| getType(metamodelType.getId()) != null) {
return false;
}
MetamodelTypeDescriptor descriptor = new MetamodelTypeDescriptor(
metamodelType);
boolean result = register(descriptor);
if (result) {
fireElementTypeAddedEvent(new ElementTypeAddedEvent(
metamodelType.getId()));
}
return result;
}
/**
* Registers <code>specializationType</code> with this registry, if its ID
* is unique in the registry.
* <P>
* Notifies clients if the element type was added to the registry.
*
* @param specializationType
* the element type to register
* @return <code>true</code> if the type was registered,
* <code>false</code> otherwise
*/
public boolean register(ISpecializationType specializationType) {
if (specializationType == null
|| getType(specializationType.getId()) != null) {
return false;
}
boolean result = specializationTypeRegistry
.registerSpecializationType(specializationType);
if (result) {
fireElementTypeAddedEvent(new ElementTypeAddedEvent(
specializationType.getId()));
}
return result;
}
/**
* Removes specialization types from the registry that specialize more than
* one metamodel type, or do not specialize any metamodel type.
* <P>
* Logs an error when an invalid specialization is found.
*/
private void removeInvalidSpecializations() {
List specializationsToReject = new ArrayList();
Collection specializationTypes = specializationTypeRegistry
.getSpecializationTypeDescriptors();
for (Iterator i = specializationTypes.iterator(); i.hasNext();) {
SpecializationTypeDescriptor nextSpecialization = (SpecializationTypeDescriptor) i
.next();
MetamodelTypeDescriptor metamodelTypeDescriptor = getMetamodelTypeDescriptor(nextSpecialization);
if (metamodelTypeDescriptor == null) {
specializationsToReject.add(nextSpecialization);
}
}
for (Iterator i = specializationsToReject.iterator(); i.hasNext();) {
specializationTypeRegistry
.removeSpecializationType((SpecializationTypeDescriptor) i
.next());
}
}
/**
* Loads the metamodel element <code>configElement</code>.
*
* @param configElement
* the configuration element
* @throws CoreException
* on any problem accessing a configuration element
*/
private void loadMetamodel(IConfigurationElement configElement)
throws CoreException {
MetamodelDescriptor descriptor = new MetamodelDescriptor(configElement);
IConfigurationElement[] typeConfigs = configElement.getChildren();
for (int i = 0; i < typeConfigs.length; i++) {
IConfigurationElement next = typeConfigs[i];
String name = next.getName();
if (name.equals(ElementTypeXmlConfig.E_METAMODEL_TYPE)) {
registerMetamodelType(next, descriptor);
} else if (name.equals(ElementTypeXmlConfig.E_SPECIALIZATION_TYPE)) {
registerSpecializationType(next, descriptor);
} else if (name.equals(ElementTypeXmlConfig.E_ADVICE_BINDING)) {
specializationTypeRegistry.registerAdviceBinding(next,
descriptor);
}
}
}
/**
* Registers the metamodel element type described by
* <code>configElement</code>.
*
* @param configElement
* the configutation element
* @param metamodelDescriptor
* the descriptor for the metamodel containing the EClass for the
* new element type
*
* @throws CoreException
* on any problem accessing a configuration element
*/
private void registerMetamodelType(IConfigurationElement configElement,
MetamodelDescriptor metamodelDescriptor)
throws CoreException {
MetamodelTypeDescriptor descriptor = new MetamodelTypeDescriptor(
configElement, metamodelDescriptor);
register(descriptor);
}
/**
* Registers the specialization element type described by
* <code>configElement</code>.
*
* @param configElement
* the configutation element
* @param metamodelDescriptor
* the descriptor for the metamodel containing the EClass for the
* new element type
*
* @throws CoreException
* on any problem accessing a configuration element
*/
private SpecializationTypeDescriptor registerSpecializationType(
IConfigurationElement configElement,
MetamodelDescriptor metamodelDescriptor)
throws CoreException {
return specializationTypeRegistry.registerSpecializationType(
configElement, metamodelDescriptor);
}
/**
* Adds the metamodel <code>type</code> to this registry. Logs an error if
* a metamodel type has already been registered for the same ID.
*
* @param typeDescriptor
* the descriptor of the type to be added.
* @return <code>true</code> if the type was added, <code>false</code>
* otherwise.
*/
private boolean register(MetamodelTypeDescriptor typeDescriptor) {
if (checkForDuplicate(typeDescriptor)) {
return false;
}
String nsURI = typeDescriptor.getNsURI();
String eClassName = typeDescriptor.getEClassName();
Map metamodelTypeDescriptorsByEClass = (Map) metamodelTypeDescriptorsByNsURI
.get(nsURI);
if (metamodelTypeDescriptorsByEClass == null) {
metamodelTypeDescriptorsByEClass = new HashMap();
metamodelTypeDescriptorsByNsURI.put(nsURI, metamodelTypeDescriptorsByEClass);
}
Collection descriptors = (Collection) metamodelTypeDescriptorsByEClass.get(eClassName);
if (descriptors == null) {
descriptors = new ArrayList();
metamodelTypeDescriptorsByEClass.put(eClassName, descriptors);
}
descriptors.add(typeDescriptor);
metamodelTypeDescriptorsById
.put(typeDescriptor.getId(), typeDescriptor);
return true;
}
/**
* Loads the element type factory from <code>configElement</code>.
*
* @param configElement
* the configuration element
* @throws CoreException
* on any problem accessing a configuration element
*/
private void loadElementTypeFactory(IConfigurationElement configElement)
throws CoreException {
ElementTypeFactoryDescriptor descriptor = new ElementTypeFactoryDescriptor(
configElement);
elementTypeFactoryMap.put(descriptor.getKindName(), descriptor);
}
/**
* Checks to see if an element type with the same ID as
* <code>typeDescriptor</code> has already been registered.
*
* @param typeDescriptor
* @return <code>true</code> if there is no duplicate, <code>false</code>
* otherwise.
*/
private boolean checkForDuplicate(ElementTypeDescriptor typeDescriptor) {
if (metamodelTypeDescriptorsById.containsKey(typeDescriptor.getId())) {
Log
.error(
EMFTypePlugin.getPlugin(),
EMFTypePluginStatusCodes.TYPE_NOT_INITED,
EMFTypeCoreMessages
.bind(
EMFTypeCoreMessages.type_not_init_WARN_,
typeDescriptor.getId(),
EMFTypeCoreMessages.type_reason_duplicate_id_WARN_));
return true;
}
return false;
}
/**
* Gets the metamodel type descriptors that match the EClasses in
* <code>eClasses</code> for the client <code>context</code>.
*
* @param eClasses
* a Set of <code>EClass</code> instances
* @param context
* the client context
* @return a List of <code>modelType</code>s
*/
private List getMetamodelTypeDescriptors(Set eClasses,
IClientContext context) {
List result = new ArrayList();
for (Iterator i = eClasses.iterator(); i.hasNext();) {
EClass nextType = (EClass) i.next();
MetamodelTypeDescriptor metamodelTypeDescriptor = getMetamodelTypeDescriptor(
nextType, context);
if (metamodelTypeDescriptor != null) {
result.add(metamodelTypeDescriptor);
}
}
return result;
}
/**
* Gets the type descriptor for the element type with <code>id</code>
*
* @param id
* the type ID
* @return the type descriptor, or <code>null</code> if none is registered
* with that ID
*/
private ElementTypeDescriptor getTypeDescriptor(String id) {
ElementTypeDescriptor typeDescriptor = (ElementTypeDescriptor) metamodelTypeDescriptorsById
.get(id);
if (typeDescriptor == null) {
// Try the specialization types
typeDescriptor = specializationTypeRegistry
.getSpecializationTypeDescriptor(id);
}
return typeDescriptor;
}
/**
* Gets the descriptor for the metamodel type that the
* <code>specializationTypeDescriptor</code> specializes. There should
* only be one such metamodel type.
*
* @param specializationTypeDescriptor
* @return descriptor for the metamodel type that the
* <code>specializationTypeDescriptor</code> specializes, or
* <code>null</code> if the doesn't specialize any metamodel type,
* or it specializes more than one metamodel type.
*/
private MetamodelTypeDescriptor getMetamodelTypeDescriptor(
SpecializationTypeDescriptor specializationTypeDescriptor) {
MetamodelTypeDescriptor metamodelTypeDescriptor = null;
String[] specializedTypeIds = specializationTypeDescriptor
.getSpecializationTypeIds();
for (int j = 0; j < specializedTypeIds.length; j++) {
ElementTypeDescriptor nextSpecializedType = getTypeDescriptor(specializedTypeIds[j]);
if (nextSpecializedType == null) {
Log
.error(
EMFTypePlugin.getPlugin(),
EMFTypePluginStatusCodes.SPECIALIZATION_TYPE_SPECIALIZES_INVALID_ID,
EMFTypeCoreMessages
.bind(
EMFTypeCoreMessages.specializationType_specializes_invalid_id_ERROR_,
specializationTypeDescriptor
.getId(),
specializedTypeIds[j]));
return null;
}
MetamodelTypeDescriptor specializedMetamodelType = null;
if (nextSpecializedType instanceof MetamodelTypeDescriptor) {
specializedMetamodelType = (MetamodelTypeDescriptor) nextSpecializedType;
} else if (nextSpecializedType instanceof SpecializationTypeDescriptor) {
SpecializationTypeDescriptor nextSpecializationType = (SpecializationTypeDescriptor) nextSpecializedType;
specializedMetamodelType = getMetamodelTypeDescriptor(nextSpecializationType);
}
if (specializedMetamodelType == null) {
// I am specializing a type that doesn't have a metamodel
// type. I will be removed from the element type registry.
metamodelTypeDescriptor = null;
break;
} else if (metamodelTypeDescriptor == null) {
// This is the first metamodel type.
metamodelTypeDescriptor = specializedMetamodelType;
} else if (metamodelTypeDescriptor != specializedMetamodelType) {
// Found that I specialize two different metamodel types
metamodelTypeDescriptor = null;
break;
}
}
if (metamodelTypeDescriptor == null) {
Log
.error(
EMFTypePlugin.getPlugin(),
EMFTypePluginStatusCodes.SPECIALIZATION_TYPE_SPECIALIZES_MULTIPLE_METAMODEL_TYPES,
EMFTypeCoreMessages
.bind(
EMFTypeCoreMessages.specializationType_specializes_multiple_metamodel_types_ERROR_,
specializationTypeDescriptor
.getId()));
}
return metamodelTypeDescriptor;
}
/**
* Loads the element type definitions from the <code>elementTypes</code>
* extension point.
*
* @param configs
* the configuration elements
*/
private synchronized void load(IConfigurationElement[] configs) {
for (int i = 0; i < configs.length; i++) {
IConfigurationElement next = configs[i];
try {
String name = next.getName();
if (name.equals(ElementTypeXmlConfig.E_METAMODEL)) {
loadMetamodel(next);
} else if (name
.equals(ElementTypeXmlConfig.E_ELEMENT_TYPE_FACTORY)) {
loadElementTypeFactory(next);
} else if (name.equals(ElementTypeXmlConfig.E_SPECIALIZATION_TYPE)) {
registerSpecializationType(next, null);
}
} catch (CoreException ce) {
String sourcePluginId = next.getDeclaringExtension()
.getContributor().getName();
Log.error(EMFTypePlugin.getPlugin(), ce.getStatus().getCode(),
EMFTypeCoreMessages.bind(
EMFTypeCoreMessages.xml_parsing_ERROR_,
sourcePluginId, ce.getStatus().getMessage()),
null);
} catch (Exception e) {
String sourcePluginId = next.getDeclaringExtension()
.getContributor().getName();
Log.error(EMFTypePlugin.getPlugin(),
EMFTypePluginStatusCodes.ERROR_PARSING_XML,
EMFTypeCoreMessages.bind(
EMFTypeCoreMessages.xml_parsing_ERROR_,
sourcePluginId, e.getMessage()), e);
}
}
// Remove the specializations that aren't valid.
removeInvalidSpecializations();
}
/**
* Adds <code>l</code> as a listener for changes to this registry. Does
* nothing if <code>l</code> is already registered as a listener.
*
* @param l
* the new listener
*/
public void addElementTypeRegistryListener(IElementTypeRegistryListener l) {
if (!elementTypeRegistryListeners.contains(l)) {
elementTypeRegistryListeners.add(l);
}
}
/**
* Removes <code>l</code> as a listener for changes to this registry. Does
* nothing if <code>l</code> is not registered as a listener.
*
* @param l
* the listener to remove
*/
public void removeElementTypeRegistryListener(IElementTypeRegistryListener l) {
if (elementTypeRegistryListeners.contains(l)) {
elementTypeRegistryListeners.remove(l);
}
}
/**
* Notifies registered listeners that the element type registry has changed.
*
* @param e
* the change event
*/
private void fireElementTypeAddedEvent(ElementTypeAddedEvent e) {
for (Iterator i = elementTypeRegistryListeners.iterator(); i.hasNext();) {
IElementTypeRegistryListener nextListener = (IElementTypeRegistryListener) i
.next();
nextListener.elementTypeAdded(e);
}
}
private void registerNullElementType() {
register(NullElementType.getInstance());
}
}