LibraryTranslation.java
package org.andromda.core.translation.library;
import java.io.BufferedReader;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;
import org.andromda.core.common.ComponentContainer;
import org.andromda.core.common.ExceptionUtils;
import org.andromda.core.common.XmlObjectFactory;
import org.andromda.core.templateengine.TemplateEngine;
import org.andromda.core.translation.Translator;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.log4j.Logger;
/**
* The LibraryTranslation object which is the intermediary object between the Library and the child Translation
* instances.
*
* @author Chad Brandon
* @author Bob Fields
*/
public class LibraryTranslation
{
private static final Logger logger = Logger.getLogger(LibraryTranslation.class);
/**
* The parent library to which this LibraryTranslation belongs.
*/
private Library library;
/**
* After processing by the CartridgeTemplate engine, will contain the processed translation.
*/
private Translation translation;
/**
* The name of this library translation instance.
*/
private String name;
/**
* Gets the name of this LibraryTranslation.
*
* @return String
*/
public String getName()
{
return name;
}
/**
* Sets the name.
*
* @param name the name
*/
public void setName(final String name)
{
this.name = name;
}
/**
* The path to the template.
*/
private String template;
/**
* Gets the path to the template for this instance.
*
* @return String
*/
public String getTemplate()
{
return template;
}
/**
* Sets the path to the template.
*
* @param template the path to the template
*/
public void setTemplate(final String template)
{
this.template = template;
}
/**
* Returns the Library that this LibraryTranslation belongs too.
*
* @return Library
*/
public Library getLibrary()
{
return library;
}
/**
* Sets the {@link Library} to which this LibraryInstance belongs.
*
* @param library
*/
public void setLibrary(final Library library)
{
this.library = library;
}
/**
* The name given to the variable containing the context element.
*/
private String variable;
/**
* Gets the variable name which is made available to the translation template.
*
* @return the variable name.
*/
public String getVariable()
{
return this.variable;
}
/**
* Sets the variable name which is made available to the translation template.
*
* @param variable the variable name.
*/
public void setVariable(final String variable)
{
this.variable = variable;
}
/**
* The Translator implementation to use. This is required.
*/
private String translatorClass;
/**
* Sets the Translator class that will perform the translation processing.
*
* @param translatorClass the class of the translator.
*/
public void setTranslator(final String translatorClass)
{
this.translatorClass = translatorClass;
final ComponentContainer container = ComponentContainer.instance();
container.unregisterComponent(translatorClass);
container.registerComponentType(translatorClass);
}
/**
* Gets the Translator instance that will perform processing of the template.
*
* @return Translator
*/
public Translator getTranslator()
{
final String methodName = "LibraryTranslation.getTranslator";
final Translator translator =
(Translator)ComponentContainer.instance().findComponent(this.translatorClass, Translator.class);
if (translator == null)
{
throw new LibraryException(
methodName + " - a translator implementation must be defined, " +
" please check your translator library --> '" + this.library.getResource() + '\'');
}
return translator;
}
/**
* Calls the handlerMethod from a translation fragment. Each handle method must take a String as the first
* argument (the body of the fragment from the translation template) and a Object for the second argument
* (the node being parsed that we may need to retrieve any additional information from).
*
* @param name the name of the fragment to retrieve.
* @param node the node Object which from the parsed expression.
* @param kind the kind of the translation fragment to handle.
*/
public void handleTranslationFragment(
final String name,
final String kind,
final Object node)
{
ExceptionUtils.checkNull("node", node);
if (this.translation != null && this.getTranslator() != null)
{
final String translation = this.getTranslationFragment(name, kind);
final Fragment fragment = this.translation.getFragment(name);
if (fragment != null)
{
String handlerMethod = fragment.getHandlerMethod();
if (StringUtils.isNotBlank(handlerMethod))
{
Class[] argTypes = {String.class, Object.class};
Method method = null;
// add the translation as the first arg
Object[] args = {translation, node};
try
{
method = this.getTranslator().getClass().getMethod(handlerMethod, argTypes);
method.invoke(
this.getTranslator(),
args);
}
catch (final NoSuchMethodException exception)
{
String errMsg =
"the translator '" + this.getTranslator().getClass() + "' must implement the method '" +
handlerMethod + '\'' + StringUtils.join(argTypes, ",") + '\'' +
" in order to handle processing of the fragment --> '" + name + '\'';
logger.error(errMsg);
}
catch (Throwable throwable)
{
if (throwable.getCause()!=null)
{
throwable = throwable.getCause();
}
// At least output the location where the error happened, not the entire stack trace.
StackTraceElement[] trace = throwable.getStackTrace();
String location = " AT " + trace[0].getClassName() + '.' + trace[0].getMethodName() + ':' + trace[0].getLineNumber();
if (throwable.getMessage()!=null)
{
location += ' ' + throwable.getMessage();
}
logger.error(this.getTranslator().getClass() + " " + throwable + " invoking " + this.getTranslator() + " METHOD " + method + " WITH " + Arrays.toString(args) + location + " fragment " + name);
throw new LibraryException(throwable);
}
}
}
}
}
/**
* Gets the current "translated" value of this fragmentName for resulting from the last processTranslation method
*
* @param name the name of the fragment to retrieve.
* @param kind the kind or type of fragment to retrieve (this is the based on the expression type: body, inv, post,
* pre, etc).
* @return String the value of the translated fragment or null of one wasn't found with the specified name.
*/
public String getTranslationFragment(
final String name,
final String kind)
{
String fragment = null;
if (this.translation != null)
{
fragment = this.translation.getTranslated(name, kind);
}
return fragment;
}
/**
* The processed translation template as a Reader.
*
* @param translationInput
*/
protected void setTranslation(final Reader translationInput)
{
ExceptionUtils.checkNull("translationInput", translationInput);
try
{
this.translation = (Translation)XmlObjectFactory.getInstance(Translation.class).getObject(translationInput);
this.translation.setLibraryTranslation(this);
}
catch (final Throwable throwable)
{
throw new LibraryException(throwable);
}
}
/**
* Processes the template belonging to this LibraryTranslation and returns the Translation objects. If
* <code>template</code> hasn't been set (i.e. is null, then this method won't do anything but return a null
* value).
*
* @param templateContext any key/value pairs that should be passed to the TemplateEngine while processing the
* translation template.
* @return Translation the Translation created from the processing the translation template.
*/
public Translation processTranslation(Map<String, Object> templateContext)
{
logger.debug(
"processing translation template --> '" + this.getTemplate() + '\'' + "' with templateContext --> '" +
templateContext + '\'');
if (this.getTemplate() != null)
{
if (templateContext == null)
{
templateContext = new LinkedHashMap<String, Object>();
}
this.getLibrary().populateTemplateContext(templateContext);
try
{
final TemplateEngine engine = this.getLibrary().getTemplateEngine();
final StringWriter output = new StringWriter();
engine.processTemplate(
this.getTemplate(),
templateContext,
output);
final String outputString = output.toString();
final BufferedReader input = new BufferedReader(new StringReader(outputString));
if (logger.isDebugEnabled())
{
logger.debug("processed output --> '" + outputString + '\'');
}
// load Reader into the translation
this.setTranslation(input);
}
catch (final Throwable throwable)
{
throw new LibraryException(throwable);
}
}
return this.translation;
}
/**
* @see Object#toString()
*/
public String toString()
{
return ToStringBuilder.reflectionToString(this);
}
}