/** * 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 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.edit.provider; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import org.eclipse.emf.common.notify.Adapter; import org.eclipse.emf.common.notify.AdapterFactory; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.common.notify.Notifier; import org.eclipse.emf.common.util.Reflect; import org.eclipse.emf.common.util.UniqueEList; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EPackage; import org.eclipse.emf.ecore.EcoreFactory; /** * This provides support for composing several factories for different models * into a single factory serving the union of the model objects. */ public class ComposedAdapterFactory implements AdapterFactory, ComposeableAdapterFactory, IChangeNotifier, IDisposable { /** * A descriptor can create an adapter factory. * They are used as the values in a {@link Descriptor.Registry registry}. */ public interface Descriptor { /** * Creates an adapter factory. * @return a new adapter factory. */ AdapterFactory createAdapterFactory(); /** * A registry is an index that takes a collection of keys, * typically a pair consisting of an EPackage or java.lang.Package, and a java.lang.Class, * and maps it to a {@link Descriptor descriptor}. */ interface Registry { /** * The global registry typically populated by plugin registration. */ Registry INSTANCE = org.eclipse.emf.edit.EMFEditPlugin.getComposedAdapterFactoryDescriptorRegistry(); /** * Returns descriptor that can create a factory for the types. * @param types collections of keys, typically a pair consisting of an EPackage or java.lang.Package, and a java.lang.Class. * @return a descriptor that can create a factory for the types. */ Descriptor getDescriptor(Collection<?> types); /** * A simple registry implementation that supports delegation. */ class Impl extends HashMap<Collection<?>, Object> implements Registry { private static final long serialVersionUID = 1L; /** * The delegate registry should lookup fail locally. */ protected Registry delegateRegistry; /** * Creates an instance. * @param delegateRegistry <code>null</code> or a registration that should act as the delegate. */ public Impl(Registry delegateRegistry) { this.delegateRegistry = delegateRegistry; } public Descriptor getDescriptor(Collection<?> types) { Descriptor descriptor = (Descriptor)get(types); return descriptor == null ? delegatedGetDescriptor(types) : descriptor; } /** * This is called when local lookup fails. */ protected Descriptor delegatedGetDescriptor(Collection<?> types) { if (delegateRegistry != null) { return delegateRegistry.getDescriptor(types); } return null; } } } } /** * This keeps track of all the {@link org.eclipse.emf.common.notify.AdapterFactory} delegates. */ protected List<AdapterFactory> adapterFactories = new ArrayList<AdapterFactory>(); /** * This is used to implement the {@link ComposeableAdapterFactory} interface. */ protected ComposedAdapterFactory parentAdapterFactory; /** * This is used to implement {@link IChangeNotifier} */ protected ChangeNotifier changeNotifier = new ChangeNotifier(); /** * This is used to demand create adapter factories from a registry. */ protected Descriptor.Registry adapterFactoryDescriptorRegistry; public ComposedAdapterFactory() { super(); } /** */ public ComposedAdapterFactory(Descriptor.Registry adapterFactoryDescriptorRegistry) { this.adapterFactoryDescriptorRegistry = adapterFactoryDescriptorRegistry; } /** */ public ComposedAdapterFactory(AdapterFactory adapterFactory) { addAdapterFactory(adapterFactory); } public ComposedAdapterFactory(AdapterFactory [] adapterFactories) { for (int i = 0; i < adapterFactories.length; ++i) { addAdapterFactory(adapterFactories[i]); } } public ComposedAdapterFactory(Collection<? extends AdapterFactory> adapterFactories) { for (AdapterFactory adapterFactory : adapterFactories) { addAdapterFactory(adapterFactory); } } public boolean isFactoryForType(Object type) { for (AdapterFactory adapterFactory : adapterFactories) { if (adapterFactory.isFactoryForType(type)) { return true; } } return false; } public AdapterFactory getFactoryForType(Object type) { return getFactoryForTypes(Collections.singleton(type)); } private static final Object ANY_OBJECT = new Object(); private static final Object ANY_EOBJECT = EcoreFactory.eINSTANCE.createEObject(); public AdapterFactory getFactoryForTypes(Collection<?> types) { AdapterFactory result = null; FactoryLoop: for (AdapterFactory factory : adapterFactories) { if (factory instanceof ComposedAdapterFactory) { AdapterFactory candidate = ((ComposedAdapterFactory)factory).getFactoryForTypes(types); if (candidate != null) { if (!candidate.isFactoryForType(ANY_EOBJECT) && !candidate.isFactoryForType(ANY_OBJECT)) { return candidate; } else if (result == null) { result = candidate; } } } else { for (Object type : types) { if (!factory.isFactoryForType(type)) { continue FactoryLoop; } } if (!factory.isFactoryForType(ANY_EOBJECT) && !factory.isFactoryForType(ANY_OBJECT)) { return factory; } else if (result == null) { result = factory; } } } if (adapterFactoryDescriptorRegistry != null) { Descriptor descriptor = adapterFactoryDescriptorRegistry.getDescriptor(types); if (descriptor != null) { result = descriptor.createAdapterFactory(); addAdapterFactory(result); } } return result == null ? delegatedGetFactoryForTypes(types) : result; } protected AdapterFactory delegatedGetFactoryForTypes(Collection<?> types) { return null; } public Object adapt(Object target, Object type) { Object adapter = target; if (target instanceof Notifier) { adapter = adapt((Notifier)target, type); } if (!(type instanceof Class<?>) || Reflect.isInstance((Class<?>)type, adapter)) { return adapter; } return null; } public Adapter adapt(Notifier target, Object type) { return adapt(target, type, false); } protected Adapter adapt(Notifier target, Object type, boolean isNew) { Adapter result = null; if (target instanceof EObject) { EObject eObject = (EObject)target; EClass eClass = eObject.eClass(); if (eClass != null) { EPackage ePackage = eClass.getEPackage(); Collection<Object> types = new ArrayList<Object>(); types.add(ePackage); if (type != null) { types.add(type); } AdapterFactory delegateAdapterFactory = getFactoryForTypes(types); if (delegateAdapterFactory != null) { result = isNew ? delegateAdapterFactory.adaptNew(target, type) : delegateAdapterFactory.adapt(target, type); } if (result == null) { Collection<EPackage> failedPackageSet = new HashSet<EPackage>(); failedPackageSet.add(ePackage); List<EClass> allSuperTypes = new UniqueEList.FastCompare<EClass>(eClass.getESuperTypes()); for (int i = 0; i < allSuperTypes.size(); ++i) { EClass eSuperType = allSuperTypes.get(i); EPackage eSupertypePackage = eSuperType.getEPackage(); if (failedPackageSet.add(eSupertypePackage)) { Collection<Object> superTypes = new ArrayList<Object>(); superTypes.add(eSupertypePackage); if (type != null) { superTypes.add(type); } delegateAdapterFactory = getFactoryForTypes(superTypes); if (delegateAdapterFactory != null) { result = isNew ? delegateAdapterFactory.adaptNew(target, type) : delegateAdapterFactory.adapt(target, type); if (result != null) { break; } } } allSuperTypes.addAll(eSuperType.getESuperTypes()); } } } } else { result = isNew ? adapt(target, type, new HashSet<Object>(), target.getClass(), true): adapt(target, type, new HashSet<Object>(), target.getClass()); } return result; } protected Adapter adapt(Notifier target, Object type, Collection<Object> failedPackages, Class<?> javaClass) { return adapt(target, type, failedPackages, javaClass, false); } protected Adapter adapt(Notifier target, Object type, Collection<Object> failedPackages, Class<?> javaClass, boolean isNew) { Adapter result = null; String javaPackage = javaClass.getName(); javaPackage = javaPackage.substring(0, javaPackage.lastIndexOf('.')); if (javaPackage.endsWith("impl")) { javaPackage = javaPackage.substring(0, javaPackage.length() - 5); } if (failedPackages.add(javaPackage)) { Collection<Object> types = new ArrayList<Object>(); types.add(javaPackage); if (type != null) { types.add(type); } AdapterFactory delegateAdapterFactory = getFactoryForTypes(types); if (delegateAdapterFactory != null) { result = isNew ? delegateAdapterFactory.adaptNew(target, type) : delegateAdapterFactory.adapt(target, type); } } if (result == null) { Class<?> superclass = javaClass.getSuperclass(); if (superclass != null) { result = adapt(target, type, failedPackages, javaClass.getSuperclass(), isNew); } /* if (result == null) { Class<?> [] interfaces = javaClass.getInterfaces(); for (int i = 0; i < interfaces.length; ++i) { result = adapt(target, type, failedPackages, interfaces[i], isNew); if (result != null) { break; } } } */ } return result; } public Adapter adaptNew(Notifier target, Object type) { return adapt(target, type, true); } public void adaptAllNew(Notifier target) { for (AdapterFactory adapterFactory : adapterFactories) { if (adapterFactory.isFactoryForType(target)) { adapterFactory.adaptAllNew(target); } } } public void insertAdapterFactory(AdapterFactory adapterFactory) { if (!adapterFactories.contains(adapterFactory)) { adapterFactories.add(0, adapterFactory); if (adapterFactory instanceof ComposeableAdapterFactory) { ((ComposeableAdapterFactory)adapterFactory).setParentAdapterFactory(this); } } } public void addAdapterFactory(AdapterFactory adapterFactory) { if (!adapterFactories.contains(adapterFactory)) { adapterFactories.add(adapterFactory); if (adapterFactory instanceof ComposeableAdapterFactory) { ((ComposeableAdapterFactory)adapterFactory).setParentAdapterFactory(this); } } } public void removeAdapterFactory(AdapterFactory adapterFactory) { if (adapterFactories.contains(adapterFactory)) { adapterFactories.remove(adapterFactory); if (adapterFactory instanceof ComposeableAdapterFactory) { ((ComposeableAdapterFactory)adapterFactory).setParentAdapterFactory(null); } } } /** * This returns the root adapter factory that delegates to this factory. */ public ComposeableAdapterFactory getRootAdapterFactory() { return parentAdapterFactory == null ? this : parentAdapterFactory.getRootAdapterFactory(); } /** * This sets the direct parent adapter factory into which this factory is composed. */ public void setParentAdapterFactory(ComposedAdapterFactory parentAdapterFactory) { this.parentAdapterFactory = parentAdapterFactory; } public void addListener(INotifyChangedListener notifyChangedListener) { changeNotifier.add(notifyChangedListener); } public void removeListener(INotifyChangedListener notifyChangedListener) { changeNotifier.remove(notifyChangedListener); } public void fireNotifyChanged(Notification notification) { changeNotifier.fireNotifyChanged(notification); if (parentAdapterFactory != null) { parentAdapterFactory.fireNotifyChanged(notification); } } public void dispose() { for (Object factory : adapterFactories) { if (factory instanceof IDisposable) { ((IDisposable)factory).dispose(); } } } }