View Javadoc
1   package org.andromda.core.translation.library;
2   
3   import java.io.BufferedReader;
4   import java.io.Reader;
5   import java.io.StringReader;
6   import java.io.StringWriter;
7   import java.lang.reflect.Method;
8   import java.util.Arrays;
9   import java.util.LinkedHashMap;
10  import java.util.Map;
11  import org.andromda.core.common.ComponentContainer;
12  import org.andromda.core.common.ExceptionUtils;
13  import org.andromda.core.common.XmlObjectFactory;
14  import org.andromda.core.templateengine.TemplateEngine;
15  import org.andromda.core.translation.Translator;
16  import org.apache.commons.lang.StringUtils;
17  import org.apache.commons.lang.builder.ToStringBuilder;
18  import org.apache.log4j.Logger;
19  
20  /**
21   * The LibraryTranslation object which is the intermediary object between the Library and the child Translation
22   * instances.
23   *
24   * @author Chad Brandon
25   * @author Bob Fields
26   */
27  public class LibraryTranslation
28  {
29      private static final Logger logger = Logger.getLogger(LibraryTranslation.class);
30  
31      /**
32       * The parent library to which this LibraryTranslation belongs.
33       */
34      private Library library;
35  
36      /**
37       * After processing by the CartridgeTemplate engine, will contain the processed translation.
38       */
39      private Translation translation;
40  
41      /**
42       * The name of this library translation instance.
43       */
44      private String name;
45  
46      /**
47       * Gets the name of this LibraryTranslation.
48       *
49       * @return String
50       */
51      public String getName()
52      {
53          return name;
54      }
55  
56      /**
57       * Sets the name.
58       *
59       * @param name the name
60       */
61      public void setName(final String name)
62      {
63          this.name = name;
64      }
65  
66      /**
67       * The path to the template.
68       */
69      private String template;
70  
71      /**
72       * Gets the path to the template for this instance.
73       *
74       * @return String
75       */
76      public String getTemplate()
77      {
78          return template;
79      }
80  
81      /**
82       * Sets the path to the template.
83       *
84       * @param template the path to the template
85       */
86      public void setTemplate(final String template)
87      {
88          this.template = template;
89      }
90  
91      /**
92       * Returns the Library that this LibraryTranslation belongs too.
93       *
94       * @return Library
95       */
96      public Library getLibrary()
97      {
98          return library;
99      }
100 
101     /**
102      * Sets the {@link Library} to which this LibraryInstance belongs.
103      *
104      * @param library
105      */
106     public void setLibrary(final Library library)
107     {
108         this.library = library;
109     }
110 
111     /**
112      * The name given to the variable containing the context element.
113      */
114     private String variable;
115 
116     /**
117      * Gets the variable name which is made available to the translation template.
118      *
119      * @return the variable name.
120      */
121     public String getVariable()
122     {
123         return this.variable;
124     }
125 
126     /**
127      * Sets the variable name which is made available to the translation template.
128      *
129      * @param variable the variable name.
130      */
131     public void setVariable(final String variable)
132     {
133         this.variable = variable;
134     }
135 
136     /**
137      * The Translator implementation to use. This is required.
138      */
139     private String translatorClass;
140 
141     /**
142      * Sets the Translator class that will perform the translation processing.
143      *
144      * @param translatorClass the class of the translator.
145      */
146     public void setTranslator(final String translatorClass)
147     {
148         this.translatorClass = translatorClass;
149         final ComponentContainer container = ComponentContainer.instance();
150         container.unregisterComponent(translatorClass);
151         container.registerComponentType(translatorClass);
152     }
153 
154     /**
155      * Gets the Translator instance that will perform processing of the template.
156      *
157      * @return Translator
158      */
159     public Translator getTranslator()
160     {
161         final String methodName = "LibraryTranslation.getTranslator";
162         final Translator translator =
163             (Translator)ComponentContainer.instance().findComponent(this.translatorClass, Translator.class);
164         if (translator == null)
165         {
166             throw new LibraryException(
167                 methodName + " - a translator implementation must be defined, " +
168                 " please check your translator library --> '" + this.library.getResource() + '\'');
169         }
170         return translator;
171     }
172 
173     /**
174      * Calls the handlerMethod from a translation fragment. Each handle method must take a String as the first
175      * argument (the body of the fragment from the translation template) and a Object for the second argument
176      * (the node being parsed that we may need to retrieve any additional information from).
177      *
178      * @param name the name of the fragment to retrieve.
179      * @param node the node Object which from the parsed expression.
180      * @param kind the kind of the translation fragment to handle.
181      */
182     public void handleTranslationFragment(
183         final String name,
184         final String kind,
185         final Object node)
186     {
187         ExceptionUtils.checkNull("node", node);
188         if (this.translation != null && this.getTranslator() != null)
189         {
190             final String translation = this.getTranslationFragment(name, kind);
191             final Fragment fragment = this.translation.getFragment(name);
192             if (fragment != null)
193             {
194                 String handlerMethod = fragment.getHandlerMethod();
195                 if (StringUtils.isNotBlank(handlerMethod))
196                 {
197                     Class[] argTypes = {String.class, Object.class};
198                     Method method = null;
199                     // add the translation as the first arg
200                     Object[] args = {translation, node};
201 
202                     try
203                     {
204                         method = this.getTranslator().getClass().getMethod(handlerMethod, argTypes);
205 
206                         method.invoke(
207                             this.getTranslator(),
208                             args);
209                     }
210                     catch (final NoSuchMethodException exception)
211                     {
212                         String errMsg =
213                             "the translator '" + this.getTranslator().getClass() + "' must implement the method '" +
214                             handlerMethod + '\'' + StringUtils.join(argTypes, ",") + '\'' +
215                             " in order to handle processing of the fragment --> '" + name + '\'';
216                         logger.error(errMsg);
217                     }
218                     catch (Throwable throwable)
219                     {
220                         if (throwable.getCause()!=null)
221                         {
222                             throwable = throwable.getCause();
223                         }
224                         // At least output the location where the error happened, not the entire stack trace.
225                         StackTraceElement[] trace = throwable.getStackTrace();
226                         String location = " AT " + trace[0].getClassName() + '.' + trace[0].getMethodName() + ':' + trace[0].getLineNumber();
227                         if (throwable.getMessage()!=null)
228                         {
229                             location += ' ' + throwable.getMessage();
230                         }
231                         logger.error(this.getTranslator().getClass() + " " + throwable + " invoking " + this.getTranslator() + " METHOD " + method + " WITH " + Arrays.toString(args) + location + " fragment " + name);
232                         throw new LibraryException(throwable);
233                     }
234                 }
235             }
236         }
237     }
238 
239     /**
240      * Gets the current "translated" value of this fragmentName for resulting from the last processTranslation method
241      *
242      * @param name the name of the fragment to retrieve.
243      * @param kind the kind or type of fragment to retrieve (this is the based on the expression type: body, inv, post,
244      *             pre, etc).
245      * @return String the value of the translated fragment or null of one wasn't found with the specified name.
246      */
247     public String getTranslationFragment(
248         final String name,
249         final String kind)
250     {
251         String fragment = null;
252         if (this.translation != null)
253         {
254             fragment = this.translation.getTranslated(name, kind);
255         }
256         return fragment;
257     }
258 
259     /**
260      * The processed translation template as a Reader.
261      *
262      * @param translationInput
263      */
264     protected void setTranslation(final Reader translationInput)
265     {
266         ExceptionUtils.checkNull("translationInput", translationInput);
267         try
268         {
269             this.translation = (Translation)XmlObjectFactory.getInstance(Translation.class).getObject(translationInput);
270             this.translation.setLibraryTranslation(this);
271         }
272         catch (final Throwable throwable)
273         {
274             throw new LibraryException(throwable);
275         }
276     }
277 
278     /**
279      * Processes the template belonging to this LibraryTranslation and returns the Translation objects. If
280      * <code>template</code> hasn't been set (i.e. is null, then this method won't do anything but return a null
281      * value).
282      *
283      * @param templateContext any key/value pairs that should be passed to the TemplateEngine while processing the
284      *                        translation template.
285      * @return Translation the Translation created from the processing the translation template.
286      */
287     public Translation processTranslation(Map<String, Object> templateContext)
288     {
289         logger.debug(
290             "processing translation template --> '" + this.getTemplate() + '\'' + "' with templateContext --> '" +
291             templateContext + '\'');
292         if (this.getTemplate() != null)
293         {
294             if (templateContext == null)
295             {
296                 templateContext = new LinkedHashMap<String, Object>();
297             }
298             this.getLibrary().populateTemplateContext(templateContext);
299 
300             try
301             {
302                 final TemplateEngine engine = this.getLibrary().getTemplateEngine();
303 
304                 final StringWriter output = new StringWriter();
305                 engine.processTemplate(
306                     this.getTemplate(),
307                     templateContext,
308                     output);
309                 final String outputString = output.toString();
310                 final BufferedReader input = new BufferedReader(new StringReader(outputString));
311                 if (logger.isDebugEnabled())
312                 {
313                     logger.debug("processed output --> '" + outputString + '\'');
314                 }
315 
316                 // load Reader into the translation
317                 this.setTranslation(input);
318             }
319             catch (final Throwable throwable)
320             {
321                 throw new LibraryException(throwable);
322             }
323         }
324         return this.translation;
325     }
326 
327     /**
328      * @see Object#toString()
329      */
330     public String toString()
331     {
332         return ToStringBuilder.reflectionToString(this);
333     }
334 }