/* AWE - Amanzi Wireless Explorer
* http://awe.amanzi.org
* (C) 2008-2009, AmanziTel AB
*
* This library is provided under the terms of the Eclipse Public License
* as described at http://www.eclipse.org/legal/epl-v10.html. Any use,
* reproduction or distribution of the library constitutes recipient's
* acceptance of this agreement.
*
* This library is distributed WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.amanzi.neo.providers.context;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.amanzi.neo.db.manager.DatabaseManagerFactory;
import org.amanzi.neo.nodeproperties.INodeProperties;
import org.amanzi.neo.providers.ContextException;
import org.amanzi.neo.providers.IProviderContext;
import org.amanzi.neo.providers.internal.IModelProvider;
import org.amanzi.neo.services.IService;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.Platform;
import org.neo4j.graphdb.GraphDatabaseService;
/**
* TODO Purpose of
* <p>
* </p>
*
* @author Nikolay Lagutko (nikolay.lagutko@amanzitel.com)
* @since 1.0.0
*/
public class ProviderContextImpl implements IProviderContext {
private static final String NODE_PROPERTIES_EXTENSION_POINT = "org.amanzi.nodeproperties";
private static final String SERVICES_EXTENSION_POINT = "org.amanzi.services";
private static final String PROVIDERS_EXTENSION_POINT = "org.amanzi.providers";
private static final String ID_ATTRIBUTE = "id";
private static final String CLASS_ATTRIBUTE = "class";
private static final String PARAMETERS_TAG = "parameters";
private static final String SERVICE_REFERENCE = "serviceReference";
private static final String NODE_PROPERTIES_REFERENCE = "nodePropertiesReference";
private static final String PROVIDER_REFERENCES = "providerReference";
private static final String REFERENCE_ATTRIBUTE = "refId";
private final Map<String, IModelProvider< ? >> providersCache = new HashMap<String, IModelProvider< ? >>();
private final Map<String, IService> servicesCache = new HashMap<String, IService>();
private final Map<String, INodeProperties> nodePropertiesCache = new HashMap<String, INodeProperties>();
private final IExtensionRegistry registry;
private List<String> serviceStack;
private List<String> providerStack;
public ProviderContextImpl() {
registry = Platform.getExtensionRegistry();
}
protected ProviderContextImpl(final IExtensionRegistry registry) {
this.registry = registry;
}
@SuppressWarnings("unchecked")
@Override
public synchronized <T extends IModelProvider< ? >> T get(final String id) throws ContextException {
T result = (T)providersCache.get(id);
if (result == null) {
result = (T)createModelProvider(id);
providersCache.put(id, result);
}
return result;
}
@SuppressWarnings("unchecked")
@Override
public synchronized <T extends INodeProperties> T getProperties(final String id) throws ContextException {
try {
return (T)getNodeProperties(id);
} catch (CoreException e) {
return null;
}
}
protected IService getService(final String id) throws ContextException {
assert !StringUtils.isEmpty(id);
IService result = servicesCache.get(id);
if (result == null) {
result = createService(id);
servicesCache.put(id, result);
}
return result;
}
protected INodeProperties getNodeProperties(final String id) throws CoreException, ContextException {
assert !StringUtils.isEmpty(id);
INodeProperties result = nodePropertiesCache.get(id);
if (result == null) {
result = createNodeProperties(id);
nodePropertiesCache.put(id, result);
}
return result;
}
protected IModelProvider< ? > createModelProvider(final String id) throws ContextException {
assert !StringUtils.isEmpty(id);
if (providerStack == null) {
providerStack = new ArrayList<String>();
}
if (providerStack.contains(id)) {
String message = "A cycle was detected <" + providerStack + ">";
providerStack = null;
throw new ContextException(message);
}
providerStack.add(id);
IConfigurationElement element = findConfigurationElement(PROVIDERS_EXTENSION_POINT, id);
if (element == null) {
throw new ContextException("ModelProvider <" + id + "> was not found in context");
}
try {
return (IModelProvider< ? >)createInstance(element, false);
} catch (Exception e) {
throw new ContextException(e);
} finally {
if (providerStack != null) {
providerStack.remove(id);
}
}
}
protected IService createService(final String id) throws ContextException {
assert !StringUtils.isEmpty(id);
if (serviceStack == null) {
serviceStack = new ArrayList<String>();
}
if (serviceStack.contains(id)) {
String message = "A cycle was detected <" + serviceStack + ">";
serviceStack = null;
throw new ContextException(message);
}
serviceStack.add(id);
IConfigurationElement element = findConfigurationElement(SERVICES_EXTENSION_POINT, id);
if (element == null) {
throw new ContextException("Service <" + id + "> was not found in context");
}
try {
return (IService)createInstance(element, true);
} catch (Exception e) {
throw new ContextException(e);
} finally {
if (serviceStack != null) {
serviceStack.remove(id);
}
}
}
protected INodeProperties createNodeProperties(final String id) throws CoreException, ContextException {
assert !StringUtils.isEmpty(id);
IConfigurationElement element = findConfigurationElement(NODE_PROPERTIES_EXTENSION_POINT, id);
if (element != null) {
try {
INodeProperties result = (INodeProperties)element.createExecutableExtension(CLASS_ATTRIBUTE);
return result;
} catch (ClassCastException e) {
throw new ContextException(e);
}
}
throw new ContextException("NodeProperties <" + id + "> was not found in context");
}
private IConfigurationElement findConfigurationElement(final String extensionPoint, final String id) {
IConfigurationElement result = null;
IConfigurationElement[] nodePropertiesElements = registry.getConfigurationElementsFor(extensionPoint);
for (IConfigurationElement singleElement : nodePropertiesElements) {
if (singleElement.getAttribute(ID_ATTRIBUTE).equals(id)) {
result = singleElement;
break;
}
}
return result;
}
@SuppressWarnings("unchecked")
protected <T> T createInstance(final IConfigurationElement configElement, final boolean isService)
throws ClassNotFoundException, NoSuchMethodException, CoreException, ContextException, InvocationTargetException,
IllegalAccessException, InstantiationException {
String className = configElement.getAttribute(CLASS_ATTRIBUTE);
Class< ? extends T> clazz = (Class< ? extends T>)Class.forName(className);
IConfigurationElement[] parametersBlock = configElement.getChildren(PARAMETERS_TAG);
return createInstance(clazz, parametersBlock, isService);
}
protected <T> T createInstance(final Class< ? extends T> clazz, final IConfigurationElement[] parameterBlock,
final boolean isService) throws ContextException, CoreException, InvocationTargetException, IllegalAccessException,
InstantiationException {
// should be 0 or 1 element
assert parameterBlock != null;
assert parameterBlock.length < 2;
IConfigurationElement singleParameterBlock = null;
if (parameterBlock.length == 1) {
singleParameterBlock = parameterBlock[0];
}
return createInstance(clazz, singleParameterBlock, isService);
}
protected <T> T createInstance(final Class< ? extends T> clazz, final IConfigurationElement parameterBlock,
final boolean isService) throws ContextException, CoreException, InvocationTargetException, IllegalAccessException,
InstantiationException {
Map<Class< ? extends Object>, Object> parametersMap = new HashMap<Class< ? extends Object>, Object>();
if (isService) {
initializeWithDatabaseService(parametersMap);
}
if (parameterBlock != null) {
for (IConfigurationElement parameter : parameterBlock.getChildren()) {
String name = parameter.getName();
String reference = parameter.getAttribute(REFERENCE_ATTRIBUTE);
Object parameterInstance = null;
if (name.equals(SERVICE_REFERENCE)) {
parameterInstance = getService(reference);
} else if (name.equals(NODE_PROPERTIES_REFERENCE)) {
parameterInstance = getNodeProperties(reference);
} else if (name.equals(PROVIDER_REFERENCES)) {
parameterInstance = get(reference);
} else {
throw new ContextException("Unknown parameter <" + name + ">");
}
parametersMap.put(parameterInstance.getClass(), parameterInstance);
}
}
return createInstance(clazz, parametersMap);
}
@SuppressWarnings("unchecked")
protected <T> T createInstance(final Class< ? extends T> clazz, final Map<Class< ? extends Object>, Object> parametersMap)
throws InvocationTargetException, IllegalAccessException, InstantiationException, ContextException {
Constructor< ? > correctConstructor = null;
Object[] arguments = null;
for (Constructor< ? > constructor : clazz.getConstructors()) {
Class< ? >[] parameterTypes = constructor.getParameterTypes();
arguments = new Object[parameterTypes.length];
int i = 0;
if (parameterTypes.length == parametersMap.size()) {
boolean isFound = false;
for (Class< ? > argumentType : parameterTypes) {
for (Entry<Class< ? extends Object>, Object> parameterEntry : parametersMap.entrySet()) {
Class< ? > parameterClass = parameterEntry.getKey();
if (argumentType.isAssignableFrom(parameterClass)) {
isFound = true;
arguments[i++] = parameterEntry.getValue();
break;
}
}
if (!isFound) {
break;
}
}
if (isFound) {
correctConstructor = constructor;
}
}
}
if (correctConstructor == null) {
throw new ContextException("Unable to create <" + clazz.getSimpleName() + ">. Constructor for Parameters <"
+ parametersMap.keySet() + "> not found.");
}
return (T)correctConstructor.newInstance(arguments);
}
private void initializeWithDatabaseService(final Map<Class< ? extends Object>, Object> parametersMap) {
GraphDatabaseService databaseService = DatabaseManagerFactory.getDatabaseManager().getDatabaseService();
parametersMap.put(GraphDatabaseService.class, databaseService);
}
}