View Javadoc

1   package org.ocltf.translation;
2   
3   import java.io.IOException;
4   import java.io.PushbackReader;
5   import java.io.StringReader;
6   import java.util.HashMap;
7   import java.util.Map;
8   
9   import org.apache.commons.logging.Log;
10  import org.apache.commons.logging.LogFactory;
11  import org.ocltf.ExpressionKinds;
12  import org.ocltf.concretesyntax.impl.ConcreteSyntaxUtils;
13  import org.ocltf.parser.OclParser;
14  import org.ocltf.parser.OclParserException;
15  import org.ocltf.parser.analysis.DepthFirstAdapter;
16  import org.ocltf.parser.lexer.Lexer;
17  import org.ocltf.parser.lexer.LexerException;
18  import org.ocltf.parser.node.AClassifierContextDeclaration;
19  import org.ocltf.parser.node.ADefClassifierExpressionBody;
20  import org.ocltf.parser.node.AInvClassifierExpressionBody;
21  import org.ocltf.parser.node.AOperationContextDeclaration;
22  import org.ocltf.parser.node.AOperationExpressionBody;
23  import org.ocltf.parser.node.Node;
24  import org.ocltf.parser.node.Start;
25  import org.ocltf.parser.parser.ParserException;
26  import org.ocltf.translation.library.LibraryTranslation;
27  import org.ocltf.translation.library.LibraryTranslationFinder;
28  import org.ocltf.utils.ExceptionUtils;
29  
30  /***
31   * The "base" translator which all Translator's should extend,
32   * provides some basic functionality, such as the retrieveal of translation
33   * fragments from the translation template file, pre-processing, post-processing, etc.
34   * 
35   * <p>
36   *  The primary methods (in addition to methods you'll extend to handle expression parsing) 
37   *  to take note of when extending this class are:
38   *      <ul>
39   *          <li>
40   *              <a href="#handleTranslationFragment(java.lang.String, org.ocltf.parser.node.Node)">handleTranslationFragment(java.lang.String, Node node)</a> 
41   *          </li>
42   *          <li>
43   *              <a href="#getTranslationFragment(java.lang.String)">getTranslationFragment(java.lang.String)</a>
44   *          </li>
45   *          <li>
46   *              <a href="#getExpression()">getExpression()</a> 
47   *          </li>
48   *          <li>
49   *              <a href="#preProcess()">preProcess()</a> 
50   *          </li>
51   *          <li>
52   *              <a href="#postProcess()">postProcess()</a> 
53   *          </li>
54   *      </ul>
55   * </p>
56   * @author Chad Brandon
57   */
58  public abstract class BaseTranslator extends DepthFirstAdapter implements Translator {
59  	
60      /***
61       * The apache common's Log instance that can be used
62       * by all decendant classes.
63       */
64  	protected Log logger = LogFactory.getLog(this.getClass());
65  	
66  	/***
67  	 * This is set by the "translate" method in order
68  	 * to provide any Translator classes extending this class
69  	 * access to the element that is the context for the expression.
70  	 */
71  	private Object contextElement = null;
72  	
73  	/***
74  	 * Contains the Expression that was/will be populated
75  	 * during execution of the translation method
76  	 */
77  	private Expression translatedExpression = null;
78  	
79  	/***
80  	 * The processed Translation file.
81  	 */
82  	private LibraryTranslation libraryTranslation = null;
83  	
84  	/***
85  	 * Called by the translate method to set the element which
86  	 * provides the context for the traslated expression.
87  	 * 
88  	 * @param contextElement
89  	 */
90  	private void setContextElement(Object contextElement) {
91  		this.contextElement = contextElement;
92  	}
93  	
94  	/***
95  	 * Returns the current value of the Expression.
96  	 * Subclasses will update the translatedExpression of this object
97  	 * as translation occurs.
98  	 * @return Expression
99  	 */
100 	protected Expression getExpression() {
101 		String methodName = "getExpression";
102 		if (this.translatedExpression == null) {
103 			throw new TranslatorException(methodName +
104 				" - translatedExpression can not be null");
105 		}
106 		return translatedExpression;
107 	}
108    
109 	/***
110 	 * Returns the context element for the expression
111 	 * being translated.
112 	 * 
113 	 * @return the context element.
114 	 */
115 	protected Object getContextElement() {
116 		String methodName = "getContextElement";
117 		if (this.contextElement == null) {
118 			throw new TranslatorException(methodName
119 				+ " - the contextElement can not be null");
120 		}
121 		return this.contextElement;
122 	}
123 	
124 	/***
125 	 * Calls the handlerMethod defined on the &lt;fragment/&gt; element
126      * if <code>fragmentName</code> matches one the fragments defined
127      * within the current translation file.  
128      * 
129      * <p>
130      *      <a name="#handlerMethod"/>
131      *      A handlerMethod must have two arguments:
132      *      <ol>
133      *          <li>
134      *              The first argument must be a <code>java.lang.String</code> which will
135      *              be the body of the corresponding <code>kind</code> element 
136      *              for the matching fragment. (i.e. 
137      *              if <code>'context LegalAgreement inv: allInstances -> 
138      *              isUnique(documentTitle')'</code> is being translated, then the body of 
139      *              the element &lt;kind name="inv"/&gt; would be returned)
140      *          </li>
141      *          <li>
142      *             The second argument is of type <code>java.lang.Object</code>
143      *             and is the node that is currently being parsed
144      *             at the time the matching of the <code>fragmentName</code> occurred.
145      *          </li>
146      *      </ol>
147      * <p>
148      * <p>
149      *      For example this handlerMethod might be defined within your translation file to
150      *      handle the 'allInstances' expression:
151      *      <pre>
152      *      &lt;fragment name="(\s*${elementName}\s*\.)?\s*allInstances.*"
153      *                   handlerMethod="handleAllInstances"&gt;
154      *          &lt;kind name="body"&gt;
155      *              from $completeElementName as $lowerCaseElementName 
156      *          &lt;/kind&gt;
157      *      &lt;/fragment&gt;
158      *      </pre>
159      * </p>
160      * <p>
161      *      And the implementation of the <code>handleAllInstances</code>
162      *      method would be:
163      *      <pre>
164      *      public void handleAllInstances(String translation, Object node) {
165      *          //some handling code
166      *      }
167      *      </pre> 
168      * </p>
169 	 * 
170      * @param node the node being parsed, the toString value of this node is 
171      *        what is matched against the translation fragment. 
172      *        We also need to pass the node to our 
173      *        <a href="#handlerMethod">handlerMethod</a>
174      *        so that it can be used it for additional processing (if we need it).
175      * 
176      * @see getTranslationFragment(java.lang.String)
177 	 */
178 	protected void handleTranslationFragment(
179         Node node) {
180 		String methodName = "getTranslatedFragment";
181 		ExceptionUtils.checkNull(methodName, "node", node);
182 		if (this.libraryTranslation!= null) {
183 			this.libraryTranslation.handleTranslationFragment(
184                 TranslationUtils.trimToEmpty(node), 
185                 this.getExpression().getKind(),
186                 node);
187 		}
188 	}
189     
190     /***
191      * Finds the "fragment" with the specified <code>fragmentName</code> 
192      * from the library translation file.  
193      * 
194      * <p>
195      *  <strong>IMPORTANT:</strong> as a best practice, it is recommended that you use 
196      *  <a href="#handleTranslationFragment(java.lang.String, org.ocltf.parser.node.Node)">handleTranslationFragment(java.lang.String, Node node)</a> 
197      *  if at all possible (instead of this method), it will help  your code be cleaner and the methods smaller and 
198      *  more maintainable.
199      * </p>
200      * <p>
201      * Will retrieve the contents of the fragment <code>kind</code> 
202      * that corresponds to the kind of expression currently being translated. 
203      * (i.e. if <code>'context LegalAgreement inv: allInstances -> 
204      * isUnique(documentTitle')</code>' is being translated, then the body of the 
205      * element &lt;kind name="inv"&gt; would be returned).  
206      * </p>
207      * <p>
208      * <strong>NOTE:</strong>You would use this method <strong>instead</strong> of
209      * <a href="#handleTranslationFragment(java.lang.String, org.ocltf.parser.node.Node)">handleTranslationFragment(java.lang.String, Node node)</a>
210      * if you just want to retrieve the value of the fragment and don't want to have a <a href="#handlerMethod">handlerMethod</a> 
211      * which actually handles the processing of the output.  For example you may want 
212      * to add a fragment called 'constraintTail' which would always be added to your 
213      * translation at the end of the constraint, the 'tail'.  There isn't any part of the 
214      * expression that matches this, but you still want to store it in a translation 
215      * template since it could be different between translations within your Translation-Library.
216      * </p> 
217      * 
218      * @param fragmentName the name of the fragment to retrieve from the translation
219      * @return String the output String from the translated fragment.
220      * 
221      * @see handleTranslationFragment(Node node)
222      */
223     protected String getTranslationFragment(String fragmentName) {
224         String methodName = "getTranslatedFragment";
225         ExceptionUtils.checkEmpty(methodName, "fragmentName", fragmentName);
226         String fragmentString = null;        
227         if (this.libraryTranslation!= null) {
228             fragmentString = this.libraryTranslation.getTranslationFragment(
229                 fragmentName, this.getExpression().getKind());
230         }
231         return fragmentString;
232     }
233 
234 	/***
235 	 * Performs any initlization. Subclasses
236      * should override this method if they want to provide
237      * any initilization before translation begins.
238 	 */
239 	protected void preProcess() {
240 		this.contextElement = null;
241 	}
242 
243 	/***
244 	 * @see org.ocltf.translation.ExpressionTranslator#translate(
245      * java.lang.String, org.ocltf.model.ElementFacade, java.lang.String)
246      * 
247      * <strong>NOTE:</strong> null is allowed for contextElement (even though 
248      * it isn't within ExpressionTranslator.translate() since the TraceTranslator
249      * doesn't need a <code>contextElement</code> and we don't want to slow
250      * down the trace by having to read and load a model each time.
251 	 */
252 	public Expression translate(String translationName, Object contextElement, String expression) {
253 		String methodName = "translate";
254 		ExceptionUtils.checkEmpty(methodName, "translationName", translationName);
255 		ExceptionUtils.checkEmpty(methodName, "expression", expression);
256 		try {
257 			//pre processing 
258 			this.preProcess();
259 			//set the context element so translators extending this have the 
260 			//context element available
261 			this.setContextElement(contextElement);
262 			Map templateObjects = new HashMap();
263 			templateObjects.put(Translator.CONTEXT_ELEMENT, contextElement);
264 			this.libraryTranslation = 
265 				LibraryTranslationFinder.findLibraryTranslation(translationName);
266 			if (this.libraryTranslation != null) {
267 				libraryTranslation.processTranslation(templateObjects);	
268 				this.process(expression);
269 			}
270 			//post processing
271 			this.postProcess();
272 		} catch (Exception ex) {
273 			String errMsg = "Error performing " + methodName 
274 				+ " with translationName '" + translationName 
275 				+ "', contextElement '" 
276 				+ contextElement 
277 				+ "' and expression --> '" 
278 				+ expression + "'";
279 
280             //if the exception is something other than a parser
281             //error wrap in a TranslatorException, otherwise
282             //just print the error so its more user friendly.
283             if (!OclParserException.class.isAssignableFrom(ex.getClass())) {
284                 logger.error(errMsg, ex);
285                 throw new TranslatorException(errMsg, ex);
286             } else {
287                 logger.error(errMsg 
288                    + "\n MESSAGE --> '" 
289                    + ex.getMessage() + "'");
290             }
291 		}
292 		return translatedExpression;
293 	}
294 	
295 	/***
296 	 * Parses the expression and applies this Translator to it.
297 	 * 
298 	 * @param expression - the expression to process.
299 	 * @throws IOException if an IO error occurs during processing.
300 	 */
301 	protected void process(String expression) throws IOException {
302 		String methodName = "process";
303 		ExceptionUtils.checkEmpty(methodName, "expression", expression);
304 		try {
305 			Lexer lexer =
306 				new Lexer(new PushbackReader(new StringReader(expression)));
307 			OclParser parser = new OclParser(lexer);
308 			Start startNode = parser.parse();
309 			this.translatedExpression = new Expression(expression);
310 			startNode.apply(this);
311 		} catch (ParserException ex) {
312 			throw new OclParserException(ex.getMessage());
313 		} catch (LexerException ex) {
314 			throw new OclParserException(ex.getMessage());
315 		}
316 	}
317 	
318 	/***
319 	 * Handles the exception in the default way
320 	 * @param methodName
321 	 * @param ex
322 	 */
323 	protected void handleException(String methodName, Exception ex) {
324 		String errMsg = "Error performing " + methodName; 
325 		logger.error(errMsg, ex);
326 		throw new TranslatorException(errMsg, ex);
327 	}
328 	
329 	/***
330      * Performs any post processing.
331 	 * Subclasses should override to perform
332 	 * any final cleanup/processing.
333 	 */
334 	protected void postProcess() {
335 		//currently does nothing --> sub classes should override to perform
336 		//final processing
337 	}
338 	
339 	/***  The Following Are Overriden Parser Generated Methods **/
340 
341 	/***
342 	 * Sets the kind and name of the expression for <code>inv</code> expressions.  If subclasses 
343 	 * override this method, they <strong>MUST</strong> call this method before their 
344 	 * own implementation.
345 	 * @param expressionBody
346 	 */
347 	public void inAInvClassifierExpressionBody(AInvClassifierExpressionBody expressionBody) {
348 		String methodName = "inAInvClassifierExpressionBody";
349 		ExceptionUtils.checkNull(methodName, "expressionBody", expressionBody);
350 		if (this.translatedExpression != null) {
351 			this.translatedExpression.setName(TranslationUtils.trimToEmpty(expressionBody.getName()));
352 			this.translatedExpression.setKind(ExpressionKinds.INV);
353 		}
354 	}
355 
356 	/***
357 	 * Sets the kind and name of the expression for <code>def</code> expressions.  If subclasses 
358 	 * override this method, they <strong>MUST</strong> call this method before their 
359 	 * own implementation.
360 	 * @param expressionBody
361 	 */
362 	public void inADefClassifierExpressionBody(ADefClassifierExpressionBody expressionBody) {
363 		String methodName = "inADefClassifierExpressionBody";
364 		ExceptionUtils.checkNull(methodName, "expressionBody", expressionBody);
365 		if (this.translatedExpression != null) {
366 			this.translatedExpression.setName(TranslationUtils.trimToEmpty(expressionBody.getName()));
367 			this.translatedExpression.setKind(ExpressionKinds.DEF);
368 		}
369 	}
370 	
371 	/***
372      * Sets the kind and name of the expression for classifier contexts.  If subclasses 
373      * override this method, they <strong>MUST</strong> call this method before their 
374      * own implementation.
375 	 * @param classifierExpressionBody
376 	 *
377 	public void inAClassifierExpressionBody(AClassifierExpressionBody classifierExpressionBody) {
378 		String methodName = "inAClassifierExpressionBody";
379 		if (logger.isDebugEnabled()) {
380 			logger.debug("performing " + methodName + 
381 				" with classifierExpressionBody --> " + classifierExpressionBody);
382 		}		
383 		ExceptionUtils.checkNull(methodName, "classifierExpressionBody", classifierExpressionBody);
384 		
385         if (this.translatedExpression != null) {
386     		//sets the name of the expression
387     		this.translatedExpression.setName(
388                  TranslationUtils.getPropertyAsString(classifierExpressionBody, "name"));
389     		
390     		//sets the kind of the expression (inv, def)
391     		this.translatedExpression.setKind(
392                  TranslationUtils.getPropertyAsString(classifierExpressionBody, "classifierStereotype"));
393         }
394 	
395 	}*/
396 	
397 	/***
398 	 * Sets the kind and name of the expression for operation contexts.  If subclasses 
399      * override this method, they <strong>MUST</strong> call this method before their 
400      * own implementation.
401 	 * @param operationExpressionBody
402 	 */
403 	public void inAOperationExpressionBody(AOperationExpressionBody operationExpressionBody) {
404 		String methodName = "inAOperationExpressionBody";
405 		if (logger.isDebugEnabled()) {
406 			logger.debug("performing " + methodName + 
407 				" with operationExpressionBody --> " + operationExpressionBody);
408 		}		
409 		ExceptionUtils.checkNull(methodName, "operationExpressionBody", operationExpressionBody);
410         
411         if (this.translatedExpression != null) {
412     		//sets the name of the expression
413     		this.translatedExpression.setName(
414                 TranslationUtils.getPropertyAsString(operationExpressionBody, "name"));		
415     		
416     		//sets the kind of the expression (body, post, or pre)
417     		this.translatedExpression.setKind(
418                 TranslationUtils.getPropertyAsString(operationExpressionBody, "operationStereotype"));
419         }
420 	}
421 	
422 	/***
423 	 * Sets the element type which represents the context of the expression for expressions
424      * having operations as their context.  If subclasses override this method, 
425      * they <strong>MUST</strong> call this method before their own implementation.
426      * 
427      * @param declaration the AOperationContextDeclaration instance from which
428      *        we retrieve the element type.
429 	 */
430 	public void inAOperationContextDeclaration(AOperationContextDeclaration declaration) {
431 		String methodName = "inAOperationContextDeclaration";
432 		if (logger.isDebugEnabled()) {
433 			logger.debug("performing " + methodName + 
434 				" with declaration --> " + declaration);
435 		}		
436         if (this.translatedExpression != null) {
437     		this.translatedExpression.setContextType(ConcreteSyntaxUtils.getType(
438     			declaration.getName(), 
439     			declaration.getPathNameTail()));
440         }
441 	}
442 	
443 	/***
444      * Sets the element type which represents the context of the expression for expressions
445      * having classifiers as their context. If subclasses override this method, 
446      * they <strong>MUST</strong> call this method before their own implementation.
447      * 
448      * @param declaration the AClassifierContextDeclaration instance from which we retrieve
449      *        the element type.
450 	 */
451 	public void inAClassifierContextDeclaration(AClassifierContextDeclaration declaration) {
452 		String methodName = "inAClassifierContextDeclaration";
453 		if (logger.isDebugEnabled()) {
454 			logger.debug("performing " + methodName + 
455 				" with declaration --> " + declaration);
456 		}		
457         if (this.translatedExpression != null) {
458     		this.translatedExpression.setContextType(ConcreteSyntaxUtils.getType(
459     			declaration.getName(), 
460     			declaration.getPathNameTail()));
461         }
462 	}
463 	
464 }