/**
* <copyright>
*
* Copyright (c) 2002, 2009 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
*
* </copyright>
*
* $Id: ComposedAdapterFactory.java,v 1.11 2008/01/29 21:13:13 emerks Exp $
*/
package net.enilink.komma.edit.provider;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import net.enilink.komma.common.adapter.IAdapterFactory;
import net.enilink.komma.common.notify.INotification;
import net.enilink.komma.common.notify.NotificationSupport;
import net.enilink.komma.core.IReference;
import net.enilink.komma.core.URI;
import net.enilink.komma.edit.KommaEditPlugin;
import net.enilink.komma.em.concepts.IClass;
import net.enilink.komma.em.concepts.IResource;
import com.google.inject.Inject;
import com.google.inject.Injector;
/**
* This provides support for composing several factories for different models
* into a single factory serving the union of the model objects.
*/
public class ComposedAdapterFactory extends NotificationSupport<INotification>
implements IComposeableAdapterFactory, IDisposable {
/**
* A descriptor can create an adapter factory. They are used as the values
* in a {@link IDescriptor.IRegistry registry}.
*/
public interface IDescriptor {
/**
* 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 IDescriptor descriptor}.
*/
interface IRegistry {
/**
* A simple registry implementation that supports delegation.
*/
class Impl extends ConcurrentHashMap<Collection<?>, Object>
implements IRegistry {
private static final long serialVersionUID = 1L;
/**
* The delegate registry should lookup fail locally.
*/
protected IRegistry delegateRegistry;
/**
* Creates an instance.
*
* @param delegateRegistry
* <code>null</code> or a registration that should
* act as the delegate.
*/
public Impl(IRegistry delegateRegistry) {
this.delegateRegistry = delegateRegistry;
}
/**
* This is called when local lookup fails.
*/
protected IDescriptor delegatedGetDescriptor(Collection<?> types) {
if (delegateRegistry != null) {
return delegateRegistry.getDescriptor(types);
}
return null;
}
public IDescriptor getDescriptor(Collection<?> types) {
IDescriptor descriptor = (IDescriptor) get(types);
return descriptor == null ? delegatedGetDescriptor(types)
: descriptor;
}
}
/**
* The global registry typically populated by plugin registration.
*/
IRegistry INSTANCE = net.enilink.komma.edit.KommaEditPlugin
.getComposedAdapterFactoryDescriptorRegistry();
/**
* Returns descriptor that can create a factory for the types.
*
* @param types
* collections of keys, typically a pair consisting of an
* namespace URI or java.lang.Package, and a
* {@link IClass} or java.lang.Class.
* @return a descriptor that can create a factory for the types.
*/
IDescriptor getDescriptor(Collection<?> types);
}
/**
* Creates an adapter factory.
*
* @return a new adapter factory.
*/
IAdapterFactory createAdapterFactory();
}
/**
* This keeps track of all the
* {@link net.enilink.komma.IAdapterFactory.common.notify.AdapterFactory}
* delegates.
*/
protected List<IAdapterFactory> adapterFactories = new CopyOnWriteArrayList<IAdapterFactory>();
/**
* This is used to demand create adapter factories from a registry.
*/
protected IDescriptor.IRegistry adapterFactoryDescriptorRegistry;
/**
* This is used to implement the {@link IComposeableAdapterFactory}
* interface.
*/
protected ComposedAdapterFactory parentAdapterFactory;
@Inject
protected Injector injector;
public ComposedAdapterFactory() {
super();
}
public ComposedAdapterFactory(
Collection<? extends IAdapterFactory> adapterFactories) {
for (IAdapterFactory adapterFactory : adapterFactories) {
appendAdapterFactory(adapterFactory);
}
}
public ComposedAdapterFactory(IAdapterFactory adapterFactory) {
appendAdapterFactory(adapterFactory);
}
public ComposedAdapterFactory(IAdapterFactory[] adapterFactories) {
for (int i = 0; i < adapterFactories.length; ++i) {
appendAdapterFactory(adapterFactories[i]);
}
}
public ComposedAdapterFactory(
IDescriptor.IRegistry adapterFactoryDescriptorRegistry) {
this.adapterFactoryDescriptorRegistry = adapterFactoryDescriptorRegistry;
}
public Object adapt(Object target, Object type) {
Object adapter = internalAdapt(target, type);
if (!(type instanceof Class<?>)
|| ((Class<?>) type).isInstance(adapter)) {
return adapter;
}
return null;
}
protected Object adaptEntity(IResource target, Object type,
Set<URI> seenNamespaces, final Collection<IClass> seenClasses,
List<IClass> classes) {
Collection<Object> typesCache = new ArrayList<Object>();
if (seenClasses != null) {
seenClasses.addAll(classes);
}
while (!classes.isEmpty()) {
IClass resourceClass = classes.remove(0);
URI name = resourceClass.getURI();
if (name == null) {
continue;
}
URI namespace;
try {
namespace = name.namespace();
} catch (Exception e) {
KommaEditPlugin.INSTANCE.log(e);
continue;
}
if (seenNamespaces.add(namespace)) {
typesCache.add(namespace);
if (type != null) {
typesCache.add(type);
}
IAdapterFactory delegateAdapterFactory = getFactoryForTypes(typesCache);
if (delegateAdapterFactory != null) {
Object result = delegateAdapterFactory.adapt(target, type);
if (result != null) {
return result;
}
}
typesCache.clear();
}
if (seenClasses != null) {
for (IClass superClass : resourceClass
.getDirectNamedSuperClasses()) {
if (!seenClasses.add(superClass)) {
continue;
}
int index = Collections.binarySearch(classes, superClass,
IResource.RANK_COMPARATOR);
if (index < 0) {
index = -(index + 1);
}
classes.add(index, superClass);
}
}
}
IAdapterFactory defaultAdapterFactory = getDefaultAdapterFactory(type);
return defaultAdapterFactory != null ? defaultAdapterFactory.adapt(
target, type) : null;
}
protected IAdapterFactory getDefaultAdapterFactory(Object type) {
return null;
}
protected Object adaptJavaObject(Object target, Object type,
Collection<Object> failedPackages, Class<?> javaClass) {
Object result = null;
Package javaPackage = javaClass.getPackage();
if (failedPackages.add(javaPackage)) {
Collection<Object> types = new ArrayList<Object>();
types.add(javaPackage);
if (type != null) {
types.add(type);
}
IAdapterFactory delegateAdapterFactory = getFactoryForTypes(types);
if (delegateAdapterFactory != null) {
result = delegateAdapterFactory.adapt(target, type);
}
}
if (result == null) {
Class<?> superclass = javaClass.getSuperclass();
if (superclass != null) {
result = adaptJavaObject(target, type, failedPackages,
javaClass.getSuperclass());
}
if (result == null) {
Class<?>[] interfaces = javaClass.getInterfaces();
for (int i = 0; i < interfaces.length; ++i) {
result = adaptJavaObject(target, type, failedPackages,
interfaces[i]);
if (result != null) {
break;
}
}
}
}
if (result == null) {
IAdapterFactory defaultAdapterFactory = getDefaultAdapterFactory(type);
if (defaultAdapterFactory != null) {
result = defaultAdapterFactory.adapt(target, type);
}
}
return result;
}
public void appendAdapterFactory(IAdapterFactory adapterFactory) {
synchronized (adapterFactories) {
// synchronize to ensure that insertion in combination with contains
// is only executed once
if (!adapterFactories.contains(adapterFactory)) {
adapterFactories.add(adapterFactory);
if (adapterFactory instanceof IComposeableAdapterFactory) {
((IComposeableAdapterFactory) adapterFactory)
.setParentAdapterFactory(this);
}
}
}
}
protected IAdapterFactory delegatedGetFactoryForTypes(Collection<?> types) {
return null;
}
public void dispose() {
for (Object factory : adapterFactories) {
if (factory instanceof IDisposable) {
((IDisposable) factory).dispose();
}
}
}
@Override
public void fireNotifications(
Collection<? extends INotification> notifications) {
super.fireNotifications(notifications);
if (parentAdapterFactory != null) {
parentAdapterFactory.fireNotifications(notifications);
}
}
public IAdapterFactory getFactoryForTypes(Collection<?> types) {
IAdapterFactory result = null;
FactoryLoop: for (IAdapterFactory factory : adapterFactories) {
if (factory instanceof ComposedAdapterFactory) {
IAdapterFactory candidate = ((ComposedAdapterFactory) factory)
.getFactoryForTypes(types);
if (candidate != null) {
return candidate;
}
} else {
for (Object type : types) {
if (!factory.isFactoryForType(type)) {
continue FactoryLoop;
}
}
return factory;
}
}
if (adapterFactoryDescriptorRegistry != null) {
IDescriptor descriptor = adapterFactoryDescriptorRegistry
.getDescriptor(types);
if (descriptor != null) {
result = descriptor.createAdapterFactory();
if (null != injector) {
injector.injectMembers(result);
}
appendAdapterFactory(result);
}
}
return result == null ? delegatedGetFactoryForTypes(types) : result;
}
/**
* This returns the root adapter factory that delegates to this factory.
*/
public IComposeableAdapterFactory getRootAdapterFactory() {
return parentAdapterFactory == null ? this : parentAdapterFactory
.getRootAdapterFactory();
}
public void prependAdapterFactory(IAdapterFactory adapterFactory) {
synchronized (adapterFactories) {
// synchronize to ensure that insertion in combination with contains
// is only executed once
if (!adapterFactories.contains(adapterFactory)) {
adapterFactories.add(0, adapterFactory);
if (adapterFactory instanceof IComposeableAdapterFactory) {
((IComposeableAdapterFactory) adapterFactory)
.setParentAdapterFactory(this);
}
}
}
}
protected Object internalAdapt(Object target, Object type) {
Object result = null;
if (target instanceof IReference) {
// class or property
URI uri = ((IReference) target).getURI();
if (uri != null) {
IAdapterFactory factory = getFactoryForTypes(Arrays.asList(
uri.namespace(), type));
if (factory != null) {
result = factory.adapt(target, type);
}
}
}
if (result != null) {
return result;
}
if (target instanceof IResource) {
IResource resource = (IResource) target;
result = adaptEntity(resource, type, new HashSet<URI>(),
new HashSet<IClass>(), sort(resource
.getDirectNamedClasses().toList()));
}
if (result != null) {
return result;
}
if (target != null) {
result = adaptJavaObject(target, type, new HashSet<Object>(),
target.getClass());
}
return result == null ? target : result;
}
/**
* Sort classes according to their "semantic" level.
*
* @param classes
* @return
*/
private List<IClass> sort(List<IClass> classes) {
Collections.sort(classes, IResource.RANK_COMPARATOR);
return classes;
}
public boolean isFactoryForType(Object type) {
for (IAdapterFactory adapterFactory : adapterFactories) {
if (adapterFactory.isFactoryForType(type)) {
return true;
}
}
return false;
}
public void removeAdapterFactory(IAdapterFactory adapterFactory) {
if (adapterFactories.contains(adapterFactory)) {
adapterFactories.remove(adapterFactory);
if (adapterFactory instanceof IComposeableAdapterFactory) {
((IComposeableAdapterFactory) adapterFactory)
.setParentAdapterFactory(null);
}
}
}
/**
* This sets the direct parent adapter factory into which this factory is
* composed.
*/
public void setParentAdapterFactory(
ComposedAdapterFactory parentAdapterFactory) {
this.parentAdapterFactory = parentAdapterFactory;
}
}