/** * Licensed to the Austrian Association for Software Tool Integration (AASTI) * under one or more contributor license agreements. See the NOTICE file * distributed with this work for additional information regarding copyright * ownership. The AASTI licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.openengsb.core.ekb.transformation.wonderland.internal.operation; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.HashMap; import java.util.List; import java.util.Map; import org.openengsb.core.api.model.ModelDescription; import org.openengsb.core.ekb.api.ModelRegistry; import org.openengsb.core.ekb.api.transformation.TransformationConstants; import org.openengsb.core.ekb.api.transformation.TransformationOperationException; import org.osgi.framework.Version; /** * The instantiate operation is used to support other field types than only strings. It instantiates an object of the * given type with the given parameter for the construction. */ public class InstantiateOperation extends AbstractStandardTransformationOperation { private String typeParam = TransformationConstants.INSTANTIATE_TARGETTYPE_PARAM; private String initFuncParam = TransformationConstants.INSTANTIATE_INITMETHOD_PARAM; private ModelRegistry modelRegistry; public InstantiateOperation(String operationName, ModelRegistry modelRegistry) { super(operationName, InstantiateOperation.class); this.modelRegistry = modelRegistry; } @Override public String getOperationDescription() { return theOperation().does("is used to support other field types than only strings. It instantiates") .cnt(" an object of the given type with the given parameter for the construction.").toString(); } @Override public Integer getOperationInputCount() { return -2; } @Override public Map<String, String> getOperationParameterDescriptions() { Map<String, String> params = new HashMap<String, String>(); params.put(typeParam, "The fully qualified name of the class which shall be instantiated."); params.put(initFuncParam, "Defines which (static) function should be used to instantiate " + "the object. The source field value will be taken as argument for this function. If" + " this parameter is not set, the constructor of the class is used with the source " + "field value as parameter"); return params; } @Override public Object performOperation(List<Object> input, Map<String, String> parameters) throws TransformationOperationException { checkInputSize(input); String targetType = getParameterOrException(parameters, typeParam); String initMethodName = getParameterOrDefault(parameters, initFuncParam, null); return tryInitiatingObject(targetType, initMethodName, input); } /** * Try to perform the actual initiating of the target class object. Returns the target class object or throws a * TransformationOperationException if something went wrong. */ private Object tryInitiatingObject(String targetType, String initMethodName, List<Object> fieldObjects) throws TransformationOperationException { Class<?> targetClass = loadClassByName(targetType); try { if (initMethodName == null) { return initiateByConstructor(targetClass, fieldObjects); } else { return initiateByMethodName(targetClass, initMethodName, fieldObjects); } } catch (Exception e) { String message = "Unable to create the desired object. The instantiate operation will be ignored."; message = String.format(message, targetType); getLogger().error(message); throw new TransformationOperationException(message, e); } } /** * Tries to initiate an object of the target class through the given init method name with the given object as * parameter. */ private Object initiateByMethodName(Class<?> targetClass, String initMethodName, List<Object> objects) throws Exception { Method method = targetClass.getMethod(initMethodName, getClassList(objects)); if (Modifier.isStatic(method.getModifiers())) { return method.invoke(null, objects.toArray()); } else { return method.invoke(targetClass.newInstance(), objects.toArray()); } } /** * Tries to initiate an object of the target class through a constructor with the given object as parameter. */ private Object initiateByConstructor(Class<?> targetClass, List<Object> objects) throws Exception { Constructor<?> constr = targetClass.getConstructor(getClassList(objects)); return constr.newInstance(objects.toArray()); } /** * Returns a list containing the classes of the elements in the given object list as array. */ private Class<?>[] getClassList(List<Object> objects) { Class<?>[] classes = new Class<?>[objects.size()]; for (int i = 0; i < objects.size(); i++) { classes[i] = objects.get(i).getClass(); } return classes; } /** * Tries to load the class with the given name. Throws a TransformationOperationException if this is not possible. */ private Class<?> loadClassByName(String className) throws TransformationOperationException { Exception e; if (className.contains(";")) { try { String[] parts = className.split(";"); ModelDescription description = new ModelDescription(); description.setModelClassName(parts[0]); if (parts.length > 1) { description.setVersionString(new Version(parts[1]).toString()); } return modelRegistry.loadModel(description); } catch (Exception ex) { e = ex; } } else { try { return this.getClass().getClassLoader().loadClass(className); } catch (Exception ex) { e = ex; } } String message = "The class %s can't be found. The instantiate operation will be ignored."; message = String.format(message, className); getLogger().error(message); throw new TransformationOperationException(message, e); } }