/**
* File: PrepareInvokerJavaDynamic Created: 02.03.2010
*
* /****************************************************************************
* *** Copyright (c) 2009-2010 University of Augsburg, Germany <www.ds-lab.org>
*
* 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: Christian Saad, Programming distributed Systems Lab, University
* of Augsburg - initial API and implementation
*******************************************************************************/
package de.uniAugsburg.MAF.core.invoker.javadyn;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.ecore.EClass;
import de.uniAugsburg.MAF.core.MAFCore;
import de.uniAugsburg.MAF.core.MAFLevel;
import de.uniAugsburg.MAF.core.Messages;
import de.uniAugsburg.MAF.core.adapter.attribution.AttributionAdapter;
import de.uniAugsburg.MAF.core.exceptions.InvokerException;
import de.uniAugsburg.MAF.core.invoker.APrepareInvoker;
import de.uniAugsburg.MAF.core.util.GeneralUtils;
import de.uniAugsburg.MAF.core.util.compiler.EclipseMemoryCompiler;
import de.uniAugsburg.MAF.models.attrmm.attributes.AttrDefinition;
import de.uniAugsburg.MAF.models.attrmm.attributes.AttrExtension;
import de.uniAugsburg.MAF.models.attrmm.semanticrules.AttrSemanticRule;
/**
* Prepares java rules which are read as strings and compiled at run time.
*/
public class PrepareInvokerJavaDynamic extends APrepareInvoker
{
/**
* Class for logger.
*/
private static Class<?> cl = PrepareInvokerJavaDynamic.class;
/**
* A lookup map for methods.
*/
private Map<String, Object[]> methodMap;
/**
* Class name for dynamic compilation.
*/
private String CLASS_NAME;
/**
* Class body for dynamic compilation.
*/
private StringBuffer CLASS_BODY;
/**
* Indicates whether rules have been added.
*/
private boolean dynamicMethodsPresent;
/**
* Compiler for java classes.
*/
private EclipseMemoryCompiler emc;
/**
* The constructor.
*
* @param core
* @param aadapter
* @param invokerType
*/
public PrepareInvokerJavaDynamic(MAFCore core, AttributionAdapter aadapter, String invokerType)
{
super(core, aadapter, invokerType);
}
/*
* (non-Javadoc)
*
* @see
* de.uniAugsburg.MAF.core.invoker.IPrepareInvoker#initRulePreparation(java
* .util.Map, java.util.Map)
*/
@Override
protected void resetRulePreparationInternal(Map<Integer, EClass> metaModelMap,
Map<Integer, AttrExtension> attributeExtensionMap)
{
methodMap = new HashMap<String, Object[]>();
// set class name to attribution id
CLASS_NAME = aadapter != null ? aadapter.getAttributionRepoID() : "attr_temp_class";//$NON-NLS-1$
// init body
CLASS_BODY = new StringBuffer();
for (String imp : getDefaultImports())
CLASS_BODY.append(imp + "\n"); //$NON-NLS-1$
CLASS_BODY.append("public class " + CLASS_NAME + " implements IJavaRules\n{\n"); //$NON-NLS-1$ //$NON-NLS-2$
// indicate
dynamicMethodsPresent = false;
}
/*
* (non-Javadoc)
*
* @see
* de.uniAugsburg.MAF.core.invoker.APrepareInvoker#addRuleInternal(org.eclipse
* .emf.ecore.EClass, de.uniAugsburg.MAF.models.attrmm.AttrDefinition,
* de.uniAugsburg.MAF.models.attrmm.AttrSemanticRule)
*/
@Override
protected void addRuleInternal(EClass contextClass, AttrDefinition attrDefinition,
AttrSemanticRule semanticRule)
{
dynamicMethodsPresent = true;
// attribute identifier as method name
String methodName = GeneralUtils.getAttributeID(contextClass, attrDefinition);
String rule = semanticRule.getRule() != null ? semanticRule //$NON-NLS-1$
.getRule() : "return null;";
rule = GeneralUtils.deserializeString(rule);
// add rule to body
if (rule.contains("private") || rule.contains("protected") || rule.contains("public")) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
{
// replace generic rule name with context-specific rule name
rule = rule.replace("public Object " + semanticRule.getId().toLowerCase(),
"public Object " + methodName); //$NON-NLS-1$ //$NON-NLS-2$
// rule already has signature
CLASS_BODY.append("\n" + rule + "\n\n"); //$NON-NLS-1$ //$NON-NLS-2$
}
else
{
// rule has no signature
CLASS_BODY
.append("public Object " //$NON-NLS-1$
+ methodName
+ "(IAttributeAccessor accessor, AttrDefinition attrDefinition, EObject localObject) throws InstantiatorException, VisualizerException, InvokerException, EvaluatorException\n{\n"); //$NON-NLS-1$
CLASS_BODY.append("\n" + rule + "\n}\n\n"); //$NON-NLS-1$ //$NON-NLS-2$
}
}
/*
* (non-Javadoc)
*
* @see
* de.uniAugsburg.MAF.core.invoker.APrepareInvoker#finishRulePreparationInternal
* ()
*/
protected void finishRulePreparationInternal() throws InvokerException
{
if (!dynamicMethodsPresent)
{
// return if no rules have been added
return;
}
try
{
core.logStatus(cl, MAFLevel.INFO, Messages.invokjava_info_compilerules);
// init java compiler
if (emc == null)
emc = new EclipseMemoryCompiler();
// finish and compile class
CLASS_BODY.append("\n}"); //$NON-NLS-1$
// move method-local imports to the beginning
String classText = sanitizeImports(CLASS_BODY.toString());
CLASS_BODY.delete(0, CLASS_BODY.length());
CLASS_BODY.append(classText);
Class<?> compiledClass = emc.compile(CLASS_NAME, CLASS_BODY.toString());
Object compiledClassInstance = compiledClass.newInstance();
List<String> objectMethods = new ArrayList<String>();
for (Method method : Object.class.getMethods())
objectMethods.add(method.getName());
// put all generated methods into map
for (Method javaRule : compiledClass.getMethods())
{
String javaRuleName = javaRule.getName();
if (objectMethods.contains(javaRuleName))
continue;
methodMap.put(javaRuleName, new Object[] { compiledClassInstance, javaRule });
}
core.logStatus(cl, MAFLevel.INFO,
Messages.bind(Messages.invokjava_info_initrules, methodMap.size()));
}
catch (Exception e)
{
String logString = Messages.bind(Messages.invokjava_error_compile, new Object[] {
getInvokerType(), emc.getResultMessages().toString() });
core.logStatus(cl, MAFLevel.ERROR,e, logString);
throw new InvokerException(logString, e);
}
}
/**
* @return the moduleContents
*/
@Override
public Object getCompileUnit()
{
return CLASS_BODY;
}
/**
* The result messages from the EclipseMemoryCompiler.
*
* @return
*/
public String getCompileMessages()
{
if (emc != null)
return emc.getResultMessages().toString();
return "";
}
/**
* @return the default imports.
*/
public static List<String> getDefaultImports()
{
List<String> result = new ArrayList<String>();
result.add("import java.util.ArrayList;"); //$NON-NLS-1$
result.add("import java.util.HashMap;"); //$NON-NLS-1$
result.add("import java.util.HashSet;"); //$NON-NLS-1$
result.add("import java.util.List;"); //$NON-NLS-1$
result.add("import java.util.Map;"); //$NON-NLS-1$
result.add("import java.util.Set;"); //$NON-NLS-1$
result.add("import org.eclipse.emf.common.util.BasicEList;"); //$NON-NLS-1$
result.add("import org.eclipse.emf.common.util.EList;"); //$NON-NLS-1$
result.add("import org.eclipse.emf.ecore.EObject;"); //$NON-NLS-1$
result.add("import de.uniAugsburg.MAF.core.exceptions.InstantiatorException;"); //$NON-NLS-1$
result.add("import de.uniAugsburg.MAF.core.exceptions.InvokerException;"); //$NON-NLS-1$
result.add("import de.uniAugsburg.MAF.core.exceptions.EvaluatorException;"); //$NON-NLS-1$
result.add("import de.uniAugsburg.MAF.core.exceptions.VisualizerException;"); //$NON-NLS-1$
result.add("import de.uniAugsburg.MAF.core.instantiation.IAttributeAccessor;"); //$NON-NLS-1$
result.add("import de.uniAugsburg.MAF.core.invoker.java.IJavaRules;"); //$NON-NLS-1$
result.add("import de.uniAugsburg.MAF.models.attrmm.attributes.AttrDefinition;"); //$NON-NLS-1$
return result;
}
/**
* Extracts the non-default imports in a java rule text.
*
* @param ruleText
* @return
*/
public static Set<String> getNewImports(String ruleText)
{
List<String> defaultImports = getDefaultImports();
Set<String> newImports = new HashSet<String>();
while (true)
{
int index = ruleText.indexOf("import"); //$NON-NLS-1$
if (index == -1)
break;
// remove whitespace from start
ruleText = ruleText.substring(index, ruleText.length());
// extract import
index = ruleText.indexOf(";"); //$NON-NLS-1$
String impString = ruleText.substring(0, index + 1);
// remove import from text
ruleText = ruleText.substring(index + 1, ruleText.length());
if (!defaultImports.contains(impString))
newImports.add(impString);
}
return newImports;
}
/**
* If imports are stored before a method's signature, they must be moved
* into the main import section of the class.
*
* @param ruleText
* @return
*/
public static String sanitizeImports(String classText)
{
String result = classText;
// remove default imports from class text
int startIndex = classText.indexOf("{"); //$NON-NLS-1$
classText = classText.substring(startIndex, classText.length());
// get all new imports in between the methods
Set<String> newImports = getNewImports(classText);
// remove new imports from result
for (String imp : newImports)
{
while (result.contains(imp))
result = result.replace(imp, "");
result = imp + "\n" + result;
}
return result;
}
/*
* (non-Javadoc)
*
* @see
* de.uniAugsburg.MAF.core.invoker.APrepareInvoker#getRuleInternal(org.eclipse
* .emf.ecore.EClass, de.uniAugsburg.MAF.models.attrmm.AttrDefinition)
*/
@Override
protected Object getRuleInternal(EClass contextClass, AttrDefinition attrDefinition)
{
// lookup the rule
return methodMap.get(GeneralUtils.getAttributeID(contextClass, attrDefinition));
}
/*
* (non-Javadoc)
*
* @see de.uniAugsburg.MAF.core.invoker.APrepareInvoker#disposeInternal()
*/
@Override
protected void disposeInternal()
{
if (methodMap != null)
{
methodMap.clear();
methodMap = null;
}
emc = null;
}
}
|