View Javadoc
1   package org.andromda.translation.ocl.validation;
2   
3   import java.io.InputStream;
4   import java.net.URL;
5   import java.util.HashMap;
6   import java.util.Iterator;
7   import java.util.List;
8   import java.util.Map;
9   import java.util.Properties;
10  import java.util.Stack;
11  import org.andromda.core.engine.ModelProcessorException;
12  import org.andromda.core.translation.TranslationUtils;
13  import org.andromda.metafacades.uml.ModelElementFacade;
14  import org.andromda.translation.ocl.BaseTranslator;
15  import org.andromda.translation.ocl.node.*;
16  import org.andromda.translation.ocl.syntax.ConcreteSyntaxUtils;
17  import org.andromda.translation.ocl.syntax.OCLFeatures;
18  import org.andromda.translation.ocl.syntax.OCLPatterns;
19  import org.apache.commons.lang.StringUtils;
20  
21  /**
22   * <p/>
23   * Provides translation of OCL validation constraints to the Java language. </p>
24   *
25   * @author Wouter Zoons
26   * @author Chad Brandon
27   */
28  public class ValidationJavaTranslator
29          extends BaseTranslator
30  {
31      private static Properties features = null;
32  
33      static
34      {
35          try
36          {
37              URL featuresUri = ValidationJavaTranslator.class.getResource("features.properties");
38              if (featuresUri == null)
39              {
40                  throw new ModelProcessorException("Could not load file --> '" + featuresUri + '\'');
41              }
42              features = new Properties();
43              InputStream stream = featuresUri.openStream();
44              features.load(stream);
45              stream.close();
46              stream = null;
47          }
48          catch (final Throwable throwable)
49          {
50              throw new ValidationTranslatorException(throwable);
51          }
52      }
53  
54      /**
55       * The package to which the OCL translator classes belong.
56       */
57      private static final String OCL_TRANSLATOR_PACKAGE = "org.andromda.translation.ocl.validation";
58  
59      /**
60       * This is the start of a new constraint. We prepare everything by resetting and initializing the required objects.
61       * @param node
62       */
63      public void caseAContextDeclaration(AContextDeclaration node)
64      {
65          newTranslationLayer();
66          {
67              Object[] temp = node.getContextDeclaration().toArray();
68              for (int ctr = 0; ctr < temp.length; ctr++)
69              {
70                  ((PContextDeclaration) temp[ctr]).apply(this);
71              }
72          }
73          mergeTranslationLayers();
74          this.getExpression().appendToTranslatedExpression(translationLayers.peek());
75          translationLayers.clear();
76      }
77  
78      /**
79       * @see org.andromda.translation.ocl.analysis.DepthFirstAdapter#caseAClassifierContextDeclaration(org.andromda.translation.ocl.node.AClassifierContextDeclaration)
80       */
81      public void caseAClassifierContextDeclaration(AClassifierContextDeclaration node)
82      {
83          // explicitly call super method so
84          // that we can set the type of the expression
85          super.inAClassifierContextDeclaration(node);
86          Object[] temp = node.getClassifierExpressionBody().toArray();
87          for (int ctr = 0; ctr < temp.length; ctr++)
88              ((PClassifierExpressionBody) temp[ctr]).apply(this);
89      }
90  
91      /**
92       * @see org.andromda.translation.ocl.analysis.DepthFirstAdapter#caseAOperationContextDeclaration(org.andromda.translation.ocl.node.AOperationContextDeclaration)
93       */
94      public void caseAOperationContextDeclaration(AOperationContextDeclaration node)
95      {
96          // explicitly call super method so
97          // that we can set the type of the expression
98          super.inAOperationContextDeclaration(node);
99          Object[] temp = node.getOperationExpressionBody().toArray();
100         for (int ctr = 0; ctr < temp.length; ctr++)
101             ((POperationExpressionBody) temp[ctr]).apply(this);
102     }
103 
104     /**
105      * @see org.andromda.translation.ocl.analysis.DepthFirstAdapter#caseAAttributeOrAssociationContextDeclaration(org.andromda.translation.ocl.node.AAttributeOrAssociationContextDeclaration)
106      */
107     public void caseAAttributeOrAssociationContextDeclaration(AAttributeOrAssociationContextDeclaration node)
108     {
109         super.inAAttributeOrAssociationContextDeclaration(node);
110         Object[] temp = node.getAttributeOrAssociationExpressionBody().toArray();
111         for (int ctr = 0; ctr < temp.length; ctr++)
112             ((PAttributeOrAssociationExpressionBody) temp[ctr]).apply(this);
113     }
114 
115     /**
116      * @see org.andromda.translation.ocl.analysis.DepthFirstAdapter#caseAInvClassifierExpressionBody(org.andromda.translation.ocl.node.AInvClassifierExpressionBody)
117      */
118     public void caseAInvClassifierExpressionBody(AInvClassifierExpressionBody node)
119     {
120         // explicitly call super method so
121         // that we can set the type of the expression
122         super.inAInvClassifierExpressionBody(node);
123         node.getExpression().apply(this);
124     }
125 
126     /**
127      * @see org.andromda.translation.ocl.analysis.DepthFirstAdapter#caseADefClassifierExpressionBody(org.andromda.translation.ocl.node.ADefClassifierExpressionBody)
128      */
129     public void caseADefClassifierExpressionBody(ADefClassifierExpressionBody node)
130     {
131         // explicitly call super method so
132         // that we can set the type of the expression
133         super.inADefClassifierExpressionBody(node);
134         node.getDefinitionExpression().apply(this);
135     }
136 
137     /**
138      * We need to keep track that what follows is in the scope of an arrow feature call, this is important because it
139      * means it is a feature that is implied by the OCL language, rather than the model on which the constraint
140      * applies.
141      * @param node
142      */
143     public void inAArrowPropertyCallExpressionTail(AArrowPropertyCallExpressionTail node)
144     {
145         this.arrowPropertyCallStack.push(Boolean.TRUE);
146     }
147 
148     /**
149      * Undo the arrow feature call trace.
150      * @param node
151      */
152     public void outAArrowPropertyCallExpressionTail(AArrowPropertyCallExpressionTail node)
153     {
154         this.arrowPropertyCallStack.pop();
155     }
156 
157     /**
158      * This indicates we have entered a feature call, we need to mark this to counterpart any previous arrow feature
159      * call flags.
160      * @param node
161      */
162     public void inADotPropertyCallExpressionTail(ADotPropertyCallExpressionTail node)
163     {
164         this.arrowPropertyCallStack.push(Boolean.FALSE);
165     }
166 
167     /**
168      * Undo the dot feature call trace.
169      * @param node
170      */
171     public void outADotPropertyCallExpressionTail(ADotPropertyCallExpressionTail node)
172     {
173         this.arrowPropertyCallStack.pop();
174     }
175 
176     /**
177      * Here we need to make sure the equals sign '=' is not translated into the 'equal' keyword. OCL uses '=' for
178      * comparison as well as for assignment, Java uses '==', '=' and .equals() so we override the default OCL value here
179      * to use '=' instead of 'equal'
180      * @param node
181      */
182     public void caseALetVariableDeclaration(ALetVariableDeclaration node)
183     {
184         inALetVariableDeclaration(node);
185         if (node.getVariableDeclaration() != null)
186         {
187             node.getVariableDeclaration().apply(this);
188         }
189         if (node.getEqual() != null)
190         {
191             write("=");
192         }
193         if (node.getExpression() != null)
194         {
195             node.getExpression().apply(this);
196         }
197         outALetVariableDeclaration(node);
198     }
199 
200     /**
201      * Add a variable to the context.
202      * @param node
203      */
204     public void inALetVariableDeclaration(ALetVariableDeclaration node)
205     {
206         newTranslationLayer(); // this layer will be disposed later on, we do
207         // not write variable declarations
208 
209         AVariableDeclaration variableDeclaration = (AVariableDeclaration) node.getVariableDeclaration();
210         String variableName = variableDeclaration.getName().getText();
211 
212         newTranslationLayer();
213         node.getExpression().apply(this);
214 
215         String variableValue = translationLayers.pop().toString();
216 
217         addLetVariableToContext(variableName, variableValue);
218     }
219 
220     /**
221      * In Java we need to end the declaration statement with a semicolon, this is handled here.
222      * @param node
223      */
224     public void outALetVariableDeclaration(ALetVariableDeclaration node)
225     {
226         write(";");
227         translationLayers.pop();
228     }
229 
230     /**
231      * Renders a variable declaration. Missing types will imply the Object type.
232      * @param node
233      */
234     public void caseAVariableDeclaration(AVariableDeclaration node)
235     {
236         if (node.getTypeDeclaration() == null)
237             write("Object");
238         else
239             node.getTypeDeclaration().apply(this);
240 
241         write(" "); // we need to add a space between the type and the name
242 
243         node.getName().apply(this);
244     }
245 
246     /**
247      * @see org.andromda.translation.ocl.analysis.DepthFirstAdapter#caseATypeDeclaration(org.andromda.translation.ocl.node.ATypeDeclaration)
248      */
249     public void caseATypeDeclaration(ATypeDeclaration node)
250     {
251         node.getType().apply(this);
252     }
253 
254     /**
255      * @see org.andromda.translation.ocl.analysis.DepthFirstAdapter#caseAVariableDeclarationList(org.andromda.translation.ocl.node.AVariableDeclarationList)
256      */
257     public void caseAVariableDeclarationList(AVariableDeclarationList node)
258     {
259         node.getVariableDeclaration().apply(this);
260 
261         if (node.getVariableDeclarationValue() != null)
262             node.getVariableDeclarationValue().apply(this);
263 
264         Object[] temp = node.getVariableDeclarationListTail().toArray();
265         for (int ctr = 0; ctr < temp.length; ctr++)
266             ((PVariableDeclarationListTail) temp[ctr]).apply(this);
267     }
268 
269     /**
270      * @see org.andromda.translation.ocl.analysis.DepthFirstAdapter#caseAVariableDeclarationListTail(org.andromda.translation.ocl.node.AVariableDeclarationListTail)
271      */
272     public void caseAVariableDeclarationListTail(AVariableDeclarationListTail node)
273     {
274         node.getComma().apply(this);
275         node.getVariableDeclaration().apply(this);
276 
277         if (node.getVariableDeclarationValue() != null)
278             node.getVariableDeclarationValue().apply(this);
279     }
280 
281     /**
282      * @see org.andromda.translation.ocl.analysis.DepthFirstAdapter#caseAEqualExpression(org.andromda.translation.ocl.node.AEqualExpression)
283      */
284     public void caseAEqualExpression(AEqualExpression node)
285     {
286         node.getEqual().apply(this);
287         node.getExpression().apply(this);
288     }
289 
290     /**
291      * @see org.andromda.translation.ocl.analysis.DepthFirstAdapter#caseABodyOperationStereotype(org.andromda.translation.ocl.node.ABodyOperationStereotype)
292      */
293     public void caseABodyOperationStereotype(ABodyOperationStereotype node)
294     {
295     }
296 
297     /**
298      * @see org.andromda.translation.ocl.analysis.DepthFirstAdapter#caseAPreOperationStereotype(org.andromda.translation.ocl.node.APreOperationStereotype)
299      */
300     public void caseAPreOperationStereotype(APreOperationStereotype node)
301     {
302     }
303 
304     /**
305      * @see org.andromda.translation.ocl.analysis.DepthFirstAdapter#caseAPostOperationStereotype(org.andromda.translation.ocl.node.APostOperationStereotype)
306      */
307     public void caseAPostOperationStereotype(APostOperationStereotype node)
308     {
309     }
310 
311     /**
312      * @see org.andromda.translation.ocl.analysis.DepthFirstAdapter#caseAMessageExpression(org.andromda.translation.ocl.node.AMessageExpression)
313      */
314     public void caseAMessageExpression(AMessageExpression node)
315     {
316     }
317 
318     /**
319      * @see org.andromda.translation.ocl.analysis.DepthFirstAdapter#caseAIfExpression(org.andromda.translation.ocl.node.AIfExpression)
320      */
321     public void caseAIfExpression(AIfExpression node)
322     {
323         node.getIf().apply(this);
324 
325         write("(");
326         node.getIfBranch().apply(this);
327         write(")");
328 
329         node.getThen().apply(this);
330 
331         write("{");
332         node.getThenBranch().apply(this);
333         write(";");
334         write("}");
335 
336         node.getElse().apply(this);
337 
338         write("{");
339         node.getElseBranch().apply(this);
340         write(";");
341         write("}");
342     }
343 
344     /**
345      * @see org.andromda.translation.ocl.analysis.DepthFirstAdapter#caseAPropertyCallExpression(org.andromda.translation.ocl.node.APropertyCallExpression)
346      */
347     public void caseAPropertyCallExpression(APropertyCallExpression node)
348     {
349         newTranslationLayer();
350         node.getPrimaryExpression().apply(this);
351         Object[] temp = node.getPropertyCallExpressionTail().toArray();
352         for (int ctr = 0; ctr < temp.length; ctr++)
353             ((PPropertyCallExpressionTail) temp[ctr]).apply(this);
354         mergeTranslationLayerAfter();
355     }
356 
357     /**
358      * @see org.andromda.translation.ocl.analysis.DepthFirstAdapter#caseADotPropertyCallExpressionTail(org.andromda.translation.ocl.node.ADotPropertyCallExpressionTail)
359      */
360     public void caseADotPropertyCallExpressionTail(ADotPropertyCallExpressionTail node)
361     {
362         inADotPropertyCallExpressionTail(node);
363         String expression = TranslationUtils.trimToEmpty(node);
364         // we prepend an introspection call if the expression is
365         // an operation call
366         if (OCLPatterns.isOperation(expression))
367         {
368             AFeatureCall featureCall = (AFeatureCall) node.getFeatureCall();
369             String featureCallExpression = TranslationUtils.trimToEmpty(node.getFeatureCall());
370             if (OCLFeatures.isOclIsKindOf(featureCallExpression))
371             {
372                 this.handleOclIsKindOf(featureCall);
373             } else if (OCLFeatures.isOclIsTypeOf(featureCallExpression))
374             {
375                 this.handleOclIsTypeOf(featureCall);
376             } else if (OCLFeatures.isConcat(featureCallExpression))
377             {
378                 this.handleConcat(featureCall);
379             } else
380             {
381                 this.handleDotFeatureCall(featureCall);
382             }
383         }
384         outADotPropertyCallExpressionTail(node);
385     }
386 
387     /**
388      * oclIsKindOf(type) is a special feature defined by OCL on all objects.
389      */
390     private void handleOclIsKindOf(Object node)
391     {
392         String type = this.getParametersAsType(node);
393         if (type != null)
394         {
395             write(" instanceof ");
396             write(type);
397         }
398     }
399 
400     /**
401      * oclIsTypeOf(type) is a special feature defined by OCL on all objects.
402      */
403     private void handleOclIsTypeOf(Object node)
404     {
405         String type = this.getParametersAsType(node);
406         if (type != null)
407         {
408             write(".getClass().getName().equals(");
409             write(type);
410             write(".class.getName())");
411         }
412     }
413 
414     /**
415      * Extracts the parameters from the given <code>node</code> and returns the parameters as a type (or null if none
416      * can be extracted).
417      *
418      * @param node the node from which to extrac the parameters
419      * @return the fully qualified type name.
420      */
421     private String getParametersAsType(Object node)
422     {
423         String type = null;
424         if (node instanceof AFeatureCall)
425         {
426             type = ConcreteSyntaxUtils.getParametersAsString((AFeatureCall) node);
427         } else if (node instanceof AFeatureCallParameters)
428         {
429             type = ConcreteSyntaxUtils.getParametersAsString((AFeatureCallParameters) node);
430         }
431         if (type != null)
432         {
433             type = type.replaceAll("\\s*::\\s*", ".");
434             // if we don't have a package define, attempt to find the model
435             // element
436             // in the same package as the context element.
437             if (type.indexOf('.') == -1)
438             {
439                 if (this.getModelElement() != null)
440                 {
441                     type = this.getModelElement().getPackageName() + '.' + type;
442                 }
443             }
444         }
445         return type;
446     }
447 
448     /**
449      * contact(string) is a special feature defined by OCL on strings.
450      */
451     private void handleConcat(AFeatureCall featureCall)
452     {
453         write(" + \"\" + ");
454         write(OCL_INTROSPECTOR_INVOKE_PREFIX);
455         write(CONTEXT_ELEMENT_NAME);
456         write(",\"");
457         write(ConcreteSyntaxUtils.getParametersAsString(featureCall).replaceAll("\\s*", ""));
458         write("\")");
459     }
460 
461     /**
462      * Handles a <strong>dot </strong> feature call. Its expected that this <code>featureCall</code>'s parent is a
463      * ADotPropertyCallExpressionTail. This is here because dot feature calls must be handled differently than
464      * <code>arrow<code> feature calls.
465      *
466      * @param featureCall the <strong>dot</strong> <code>featureCall</code> to handle.
467      */
468     public void handleDotFeatureCall(AFeatureCall featureCall)
469     {
470         this.prependToTranslationLayer(OCL_INTROSPECTOR_INVOKE_PREFIX);
471         this.appendToTranslationLayer(",\"");
472         this.appendToTranslationLayer(TranslationUtils.deleteWhitespace(featureCall));
473         this.appendToTranslationLayer("\"");
474         if (featureCall.getFeatureCallParameters() != null)
475         {
476             List parameters = ConcreteSyntaxUtils.getParameters(featureCall);
477             if (parameters != null && !parameters.isEmpty())
478             {
479                 write(",new Object[]{");
480                 this.appendToTranslationLayer(OCL_INTROSPECTOR_INVOKE_PREFIX);
481                 this.appendToTranslationLayer(CONTEXT_ELEMENT_NAME);
482                 this.appendToTranslationLayer(",\"");
483                 this.appendToTranslationLayer(ConcreteSyntaxUtils.getParameters(featureCall).get(0));
484                 this.appendToTranslationLayer("\")}");
485             }
486         }
487         this.appendToTranslationLayer(")");
488     }
489 
490     /**
491      * @see org.andromda.translation.ocl.analysis.DepthFirstAdapter#caseAArrowPropertyCallExpressionTail(org.andromda.translation.ocl.node.AArrowPropertyCallExpressionTail)
492      */
493     public void caseAArrowPropertyCallExpressionTail(AArrowPropertyCallExpressionTail node)
494     {
495         inAArrowPropertyCallExpressionTail(node);
496         node.getArrow().apply(this);
497         this.handleArrowFeatureCall((AFeatureCall) node.getFeatureCall());
498         outAArrowPropertyCallExpressionTail(node);
499     }
500 
501     /**
502      * @see org.andromda.translation.ocl.BaseTranslator#isOperationArgument(String)
503      */
504     protected boolean isOperationArgument(String argument)
505     {
506         return super.isOperationArgument(this.getRootName(argument));
507     }
508 
509     /**
510      * Gets the root path name from the given <code>navigationalPath</code> (by trimming off any additional navigational
511      * path).
512      *
513      * @param navigationalPath the navigational property path (i.e. car.door)
514      * @return the root of the path name.
515      */
516     private String getRootName(String navigationalPath)
517     {
518         return StringUtils.trimToEmpty(navigationalPath).replaceAll("\\..*", "");
519     }
520 
521     /**
522      * Gets the tail of the navigational path (that is it removes the root from the path and returns the tail).
523      *
524      * @param navigationalPath the navigational property path (i.e. car.door)
525      * @return the tail of the path name.
526      */
527     private String getPathTail(String navigationalPath)
528     {
529         final int dotIndex = navigationalPath.indexOf('.');
530         return dotIndex != -1 ? navigationalPath.substring(dotIndex + 1, navigationalPath.length()) : navigationalPath;
531     }
532 
533     /**
534      * TODO: improve implementation to reduce the code duplication (avoid having two write statements)
535      * @param node
536      */
537     public void caseAFeaturePrimaryExpression(AFeaturePrimaryExpression node)
538     {
539         inAFeaturePrimaryExpression(node);
540         if (node.getPathName() != null)
541         {
542             final String variableName = ((APathName) node.getPathName()).getName().getText();
543             final String variableValue = getDeclaredLetVariableValue(variableName);
544             final boolean isDeclaredAsLetVariable = (variableValue != null);
545             String featureExpression = TranslationUtils.deleteWhitespace(node);
546             if (isDeclaredAsLetVariable)
547             {
548                 write(variableValue);
549             } else if (node.getFeatureCallParameters() == null || OCLPatterns.isOperation(featureExpression))
550             {
551                 APropertyCallExpression expression = (APropertyCallExpression) node.parent();
552                 String expressionAsString = ConcreteSyntaxUtils.getPrimaryExpression(expression);
553                 // remove any references to 'self.' as we write
554                 expressionAsString = expressionAsString.replaceAll("self\\.", "");
555                 if (OCLFeatures.isSelf(expressionAsString))
556                 {
557                     write(CONTEXT_ELEMENT_NAME);
558                 } else if (StringUtils.isNotBlank(expressionAsString))
559                 {
560                     boolean convertToBoolean = false;
561                     if (node.parent().parent() instanceof AUnaryExpression)
562                     {
563                         AUnaryExpression unaryExpression = (AUnaryExpression) node.parent().parent();
564                         // we convert each unary not expression to boolean
565                         convertToBoolean = unaryExpression.getUnaryOperator() instanceof ANotUnaryOperator;
566                         if (convertToBoolean)
567                         {
568                             this.write(BOOLEAN_WRAP_PREFIX);
569                         }
570                     }
571                     if (OCLFeatures.isOclIsKindOf(expressionAsString))
572                     {
573                         this.write("object");
574                         this.handleOclIsKindOf(node.getFeatureCallParameters());
575                     } else if (OCLFeatures.isOclIsTypeOf(expressionAsString))
576                     {
577                         this.write("object");
578                         this.handleOclIsTypeOf(node.getFeatureCallParameters());
579                     } else
580                     {
581                         // whether or not an introspector call is required
582                         boolean introspectorCall = true;
583                         String invokedObject = CONTEXT_ELEMENT_NAME;
584                         // if we're in an arrow call we assume the invoked
585                         // object is the object for which the arrow call applies
586                         if (this.arrowPropertyCallStack.peek().equals(Boolean.TRUE))
587                         {
588                             invokedObject = "object";
589                         }
590                         if (this.isOperationArgument(expressionAsString))
591                         {
592                             // - if the expression is an argument, then
593                             //   that becomes the invoked object
594                             invokedObject = this.getRootName(expressionAsString);
595                             expressionAsString = this.getPathTail(expressionAsString);
596                             introspectorCall = !invokedObject.equals(expressionAsString);
597                         }
598                         if (introspectorCall)
599                         {
600                             write(OCL_INTROSPECTOR_INVOKE_PREFIX);
601                         }
602                         write(invokedObject);
603                         if (introspectorCall)
604                         {
605                             write(",\"");
606                             write(expressionAsString);
607                         }
608                         if (introspectorCall)
609                         {
610                             write("\")");
611                         }
612                         if (convertToBoolean)
613                         {
614                             this.write(BOOLEAN_WRAP_SUFFIX);
615                         }
616                     }
617                     if (this.requiresBooleanConversion)
618                     {
619                         this.write(BOOLEAN_WRAP_SUFFIX);
620                         this.requiresBooleanConversion = false;
621                     }
622                 }
623             } else
624             {
625                 node.getPathName().apply(this);
626             }
627         }
628         if (node.getIsMarkedPre() != null)
629         {
630             node.getIsMarkedPre().apply(this);
631         }
632         if (node.getQualifiers() != null)
633         {
634             // we use introspection when in an arrow, so passing
635             // feature name as a String without parentheses
636             if (this.arrowPropertyCallStack.peek().equals(Boolean.FALSE))
637             {
638                 node.getQualifiers().apply(this);
639             }
640         }
641         outAFeaturePrimaryExpression(node);
642     }
643 
644     /**
645      * Handles an <strong>arrow </strong> feature call. Its expected that this <code>featureCall</code>'s parent is a
646      * AArrowPropertyCallExpressionTail. This is here because arrow feature calls must be handled differently than
647      * <code>dot<code> feature calls.
648      *
649      * @param featureCall the <strong>arrow</strong> <code>featureCall</code> to handle.
650      */
651     public void handleArrowFeatureCall(AFeatureCall featureCall)
652     {
653         AFeatureCallParameters params = (AFeatureCallParameters) featureCall.getFeatureCallParameters();
654         AActualParameterList list = null;
655         if (params != null)
656         {
657             list = (AActualParameterList) params.getActualParameterList();
658         }
659         boolean arrow = this.arrowPropertyCallStack.peek().equals(Boolean.TRUE) &&
660                 StringUtils.isNotBlank(String.valueOf(list));
661         {
662             newTranslationLayer();
663             final String navigationalPath = ConcreteSyntaxUtils.getArrowFeatureCallResultNavigationalPath(
664                     (APropertyCallExpression) featureCall.parent().parent());
665             // if the result of an arrow feature (collection operation) has a
666             // navigational
667             // path, retrieve it and wrap the current expression with an OCL
668             // introspector call
669             boolean resultNavigationalPath = StringUtils.isNotBlank(navigationalPath);
670             if (resultNavigationalPath)
671             {
672                 write(OCL_INTROSPECTOR_INVOKE_PREFIX);
673             }
674             write(OCL_TRANSLATOR_PACKAGE);
675             write(".OCLCollections.");
676             inAFeatureCall(featureCall);
677             if (featureCall.getPathName() != null)
678             {
679                 featureCall.getPathName().apply(this);
680             }
681             String featureCallName = TranslationUtils.trimToEmpty(featureCall.getPathName());
682             AFeatureCallParameters parameters = (AFeatureCallParameters) featureCall.getFeatureCallParameters();
683             if (parameters != null)
684             {
685                 if (parameters.getLParen() != null)
686                 {
687                     parameters.getLParen().apply(this);
688                 }
689                 mergeTranslationLayerBefore();
690                 AActualParameterList parameterList = (AActualParameterList) parameters.getActualParameterList();
691                 if (parameterList != null)
692                 {
693                     List expressions = parameterList.getCommaExpression();
694 
695                     if (parameterList.getExpression() != null)
696                     {
697                         if (arrow)
698                         {
699                             write(",");
700                             write(features.getProperty(featureCallName));
701                             write(" ");
702                             if (OCLPredicateFeatures.isPredicateFeature(featureCallName))
703                             {
704                                 write(BOOLEAN_WRAP_PREFIX);
705                             }
706                         }
707                         parameterList.getExpression().apply(this);
708                     }
709                     for (int ctr = 0; ctr < expressions.size(); ctr++)
710                     {
711                         Node expression = (Node) expressions.get(ctr);
712                         if (expression != null)
713                         {
714                             write(",");
715                             expression.apply(this);
716                         }
717                     }
718                     if (parameterList.getExpression() != null)
719                     {
720                         if (OCLPredicateFeatures.isPredicateFeature(featureCallName))
721                         {
722                             write(BOOLEAN_WRAP_SUFFIX);
723                         }
724                         if (arrow)
725                             write(";}}");
726                     }
727                 }
728                 if (parameters.getRParen() != null)
729                 {
730                     parameters.getRParen().apply(this);
731                 }
732             }
733             // now since we have a navigational path off of the
734             // result we need to write the path and close off
735             // the call to the OCL introspector.
736             if (resultNavigationalPath)
737             {
738                 write(",\"");
739                 write(navigationalPath);
740                 write("\"");
741                 write(")");
742             }
743             this.outAFeatureCall(featureCall);
744         }
745     }
746 
747     /**
748      * @see org.andromda.translation.ocl.analysis.DepthFirstAdapter#caseALetExp(org.andromda.translation.ocl.node.ALetExp)
749      */
750     public void caseALetExp(ALetExp node)
751     {
752         inALetExp(node);
753         if (node.getLet() != null)
754         {
755             node.getLet().apply(this);
756         }
757         if (node.getLetVariableDeclaration() != null)
758         {
759             node.getLetVariableDeclaration().apply(this);
760         }
761         if (node.getLetExpSub() != null)
762         {
763             node.getLetExpSub().apply(this);
764         }
765         outALetExp(node);
766     }
767 
768     /**
769      * We are ready to store a new context of variables
770      * @param node
771      */
772     public void inALetExp(ALetExp node)
773     {
774         newLetVariableContext();
775     }
776 
777     /**
778      * The variables are out of scope, we need to purge their context.
779      * @param node
780      */
781     public void outALetExp(ALetExp node)
782     {
783         dropLetVariableContext();
784     }
785 
786     /**
787      * @see org.andromda.translation.ocl.analysis.DepthFirstAdapter#caseAVariableDeclarationLetExpSub(org.andromda.translation.ocl.node.AVariableDeclarationLetExpSub)
788      */
789     public void caseAVariableDeclarationLetExpSub(AVariableDeclarationLetExpSub node)
790     {
791         node.getComma().apply(this);
792         node.getLetVariableDeclaration().apply(this);
793         node.getLetExpSub().apply(this);
794     }
795 
796     /**
797      * @see org.andromda.translation.ocl.analysis.DepthFirstAdapter#caseALogicalExp(org.andromda.translation.ocl.node.ALogicalExp)
798      */
799     public void caseALogicalExp(ALogicalExp node)
800     {
801         newTranslationLayer();
802         if (node.getRelationalExpression() != null)
803         {
804             node.getRelationalExpression().apply(this);
805         }
806         Object[] tails = node.getLogicalExpressionTail().toArray();
807         for (int ctr = 0; ctr < tails.length; ctr++)
808         {
809             ((ALogicalExpressionTail) tails[ctr]).apply(this);
810         }
811         mergeTranslationLayerAfter();
812     }
813 
814     /**
815      * @see org.andromda.translation.ocl.analysis.DepthFirstAdapter#caseALogicalExpressionTail(org.andromda.translation.ocl.node.ALogicalExpressionTail)
816      */
817     public void caseALogicalExpressionTail(ALogicalExpressionTail node)
818     {
819         node.getLogicalOperator().apply(this);
820         if (node.getLogicalOperator() instanceof AImpliesLogicalOperator)
821         {
822             prependToTranslationLayer("(");
823         }
824         if (node.getRelationalExpression() != null)
825         {
826             node.getRelationalExpression().apply(this);
827         }
828         if (node.getLogicalOperator() instanceof AImpliesLogicalOperator)
829         {
830             write(":true)");
831         }
832     }
833 
834     /**
835      * @see org.andromda.translation.ocl.analysis.DepthFirstAdapter#caseARelationalExpressionTail(org.andromda.translation.ocl.node.ARelationalExpressionTail)
836      */
837     public void caseARelationalExpressionTail(ARelationalExpressionTail node)
838     {
839         inARelationalExpressionTail(node);
840 
841         newTranslationLayer();
842         write(OCL_TRANSLATOR_PACKAGE);
843         write(".OCLExpressions.");
844         node.getRelationalOperator().apply(this);
845         write("(");
846         mergeTranslationLayerBefore();
847         if (node.getAdditiveExpression() != null)
848         {
849             write(",");
850             node.getAdditiveExpression().apply(this);
851         }
852         write(")");
853         outARelationalExpressionTail(node);
854     }
855 
856     /**
857      * A flag indicating if the expression needs to be converted/wrapped in a Java Boolean instance.
858      */
859     private boolean requiresBooleanConversion = false;
860 
861     /**
862      * @see org.andromda.translation.ocl.analysis.DepthFirstAdapter#inARelationalExpression(org.andromda.translation.ocl.node.ARelationalExpression)
863      */
864     public void inARelationalExpression(ARelationalExpression node)
865     {
866         // in this block of code, we determine whether or not
867         // the next appended expression needs to be
868         // converted/wrapped by a Java Boolean
869         if (node.getRelationalExpressionTail() == null)
870         {
871             Object parent = node.parent();
872             Object expression = null;
873             if (parent instanceof ALogicalExp)
874             {
875                 List tails = ((ALogicalExp) parent).getLogicalExpressionTail();
876                 if (tails != null && !tails.isEmpty())
877                 {
878                     expression = tails.get(0);
879                     // if it's an AND or OR expression set the expression
880                     // to the node
881                     if (OCLPatterns.isAndOrOrExpression(expression))
882                     {
883                         expression = node;
884                     }
885                 }
886             } else if (parent instanceof ALogicalExpressionTail)
887             {
888                 expression = node;
889             }
890             requiresBooleanConversion = expression != null
891                     && OCLPatterns.isNavigationalPath(expression)
892                     && !OCLPatterns.isOperation(node);
893             if (this.requiresBooleanConversion)
894             {
895                 this.write(BOOLEAN_WRAP_PREFIX);
896             }
897         }
898         newTranslationLayer();
899     }
900 
901     /**
902      * @see org.andromda.translation.ocl.analysis.DepthFirstAdapter#outARelationalExpression(org.andromda.translation.ocl.node.ARelationalExpression)
903      */
904     public void outARelationalExpression(ARelationalExpression node)
905     {
906         mergeTranslationLayerAfter();
907     }
908 
909     /**
910      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTName(org.andromda.translation.ocl.node.TName)
911      */
912     public void caseTName(TName node)
913     {
914         write(node.getText());
915     }
916 
917     /**
918      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTAnd(org.andromda.translation.ocl.node.TAnd)
919      */
920     public void caseTAnd(TAnd tAnd)
921     {
922         write("&&");
923     }
924 
925     /**
926      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTOr(org.andromda.translation.ocl.node.TOr)
927      */
928     public void caseTOr(TOr tOr)
929     {
930         write("||");
931     }
932 
933     /**
934      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTXor(org.andromda.translation.ocl.node.TXor)
935      */
936     public void caseTXor(TXor tXor)
937     {
938         write("^");
939     }
940 
941     /**
942      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTImplies(org.andromda.translation.ocl.node.TImplies)
943      */
944     public void caseTImplies(TImplies tImplies)
945     {
946         // convert any non boolean's to boolean
947         this.prependToTranslationLayer(BOOLEAN_WRAP_PREFIX);
948         this.appendToTranslationLayer(BOOLEAN_WRAP_SUFFIX);
949         write("?");
950     }
951 
952     /**
953      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTNot(org.andromda.translation.ocl.node.TNot)
954      */
955     public void caseTNot(TNot tNot)
956     {
957         write("!");
958     }
959 
960     /**
961      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTPlus(org.andromda.translation.ocl.node.TPlus)
962      */
963     public void caseTPlus(TPlus tPlus)
964     {
965         write("+");
966     }
967 
968     /**
969      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTMinus(org.andromda.translation.ocl.node.TMinus)
970      */
971     public void caseTMinus(TMinus tMinus)
972     {
973         write("-");
974     }
975 
976     /**
977      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTMult(org.andromda.translation.ocl.node.TMult)
978      */
979     public void caseTMult(TMult tMult)
980     {
981         write("*");
982     }
983 
984     /**
985      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTDiv(org.andromda.translation.ocl.node.TDiv)
986      */
987     public void caseTDiv(TDiv tDiv)
988     {
989         write("/");
990     }
991 
992     /**
993      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTEqual(org.andromda.translation.ocl.node.TEqual)
994      */
995     public void caseTEqual(TEqual tEqual)
996     {
997         write("equal");
998     }
999 
1000     /**
1001      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTNotEqual(org.andromda.translation.ocl.node.TNotEqual)
1002      */
1003     public void caseTNotEqual(TNotEqual tNotEqual)
1004     {
1005         write("notEqual");
1006     }
1007 
1008     /**
1009      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTLt(org.andromda.translation.ocl.node.TLt)
1010      */
1011     public void caseTLt(TLt tLt)
1012     {
1013         write("less");
1014     }
1015 
1016     /**
1017      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTLteq(org.andromda.translation.ocl.node.TLteq)
1018      */
1019     public void caseTLteq(TLteq tLteq)
1020     {
1021         write("lessOrEqual");
1022     }
1023 
1024     /**
1025      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTGt(org.andromda.translation.ocl.node.TGt)
1026      */
1027     public void caseTGt(TGt tGt)
1028     {
1029         write("greater");
1030     }
1031 
1032     /**
1033      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTGteq(org.andromda.translation.ocl.node.TGteq)
1034      */
1035     public void caseTGteq(TGteq tGteq)
1036     {
1037         write("greaterOrEqual");
1038     }
1039 
1040     /**
1041      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTInv(org.andromda.translation.ocl.node.TInv)
1042      */
1043     public void caseTInv(TInv tInv)
1044     {
1045     }
1046 
1047     /**
1048      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTDef(org.andromda.translation.ocl.node.TDef)
1049      */
1050     public void caseTDef(TDef tDef)
1051     {
1052     }
1053 
1054     /**
1055      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTLet(org.andromda.translation.ocl.node.TLet)
1056      */
1057     public void caseTLet(TLet tLet)
1058     {
1059     }
1060 
1061     /**
1062      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTColon(org.andromda.translation.ocl.node.TColon)
1063      */
1064     public void caseTColon(TColon tColon)
1065     {
1066     }
1067 
1068     /**
1069      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTLBrace(org.andromda.translation.ocl.node.TLBrace)
1070      */
1071     public void caseTLBrace(TLBrace tlBrace)
1072     {
1073         write("{");
1074     }
1075 
1076     /**
1077      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTLBracket(org.andromda.translation.ocl.node.TLBracket)
1078      */
1079     public void caseTLBracket(TLBracket tlBracket)
1080     {
1081         write("[");
1082     }
1083 
1084     /**
1085      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTLParen(org.andromda.translation.ocl.node.TLParen)
1086      */
1087     public void caseTLParen(TLParen tlParen)
1088     {
1089         write("(");
1090     }
1091 
1092     /**
1093      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTRBrace(org.andromda.translation.ocl.node.TRBrace)
1094      */
1095     public void caseTRBrace(TRBrace trBrace)
1096     {
1097         write("}");
1098     }
1099 
1100     /**
1101      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTRBracket(org.andromda.translation.ocl.node.TRBracket)
1102      */
1103     public void caseTRBracket(TRBracket trBracket)
1104     {
1105         write("]");
1106     }
1107 
1108     /**
1109      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTRParen(org.andromda.translation.ocl.node.TRParen)
1110      */
1111     public void caseTRParen(TRParen trParen)
1112     {
1113         write(")");
1114     }
1115 
1116     /**
1117      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTContext(org.andromda.translation.ocl.node.TContext)
1118      */
1119     public void caseTContext(TContext tContext)
1120     {
1121     }
1122 
1123     /**
1124      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTBoolean(org.andromda.translation.ocl.node.TBoolean)
1125      */
1126     public void caseTBoolean(TBoolean tBoolean)
1127     {
1128         write(tBoolean.getText());
1129     }
1130 
1131     /**
1132      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTApostrophe(org.andromda.translation.ocl.node.TApostrophe)
1133      */
1134     public void caseTApostrophe(TApostrophe tApostrophe)
1135     {
1136         write("\'");
1137     }
1138 
1139     /**
1140      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTBlank(org.andromda.translation.ocl.node.TBlank)
1141      */
1142     public void caseTBlank(TBlank tBlank)
1143     {
1144         write(" ");
1145     }
1146 
1147     /**
1148      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTCollection(org.andromda.translation.ocl.node.TCollection)
1149      */
1150     public void caseTCollection(TCollection tCollection)
1151     {
1152         write("java.util.Collection ");
1153     }
1154 
1155     /**
1156      * @param tSingleLineComment
1157      */
1158     public void caseTComment(TSingleLineComment tSingleLineComment)
1159     {
1160         write("// ");
1161     }
1162 
1163     /**
1164      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTEndif(org.andromda.translation.ocl.node.TEndif)
1165      */
1166     public void caseTEndif(TEndif tEndif)
1167     {
1168     }
1169 
1170     /**
1171      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTAttr(org.andromda.translation.ocl.node.TAttr)
1172      */
1173     public void caseTAttr(TAttr tAttr)
1174     {
1175     }
1176 
1177     /**
1178      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTBag(org.andromda.translation.ocl.node.TBag)
1179      */
1180     public void caseTBag(TBag tBag)
1181     {
1182     }
1183 
1184     /**
1185      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTBar(org.andromda.translation.ocl.node.TBar)
1186      */
1187     public void caseTBar(TBar tBar)
1188     {
1189     }
1190 
1191     /**
1192      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTBody(org.andromda.translation.ocl.node.TBody)
1193      */
1194     public void caseTBody(TBody tBody)
1195     {
1196     }
1197 
1198     /**
1199      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTCommercialAt(org.andromda.translation.ocl.node.TCommercialAt)
1200      */
1201     public void caseTCommercialAt(TCommercialAt tCommercialAt)
1202     {
1203     }
1204 
1205     /**
1206      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTDerive(org.andromda.translation.ocl.node.TDerive)
1207      */
1208     public void caseTDerive(TDerive tDerive)
1209     {
1210     }
1211 
1212     /**
1213      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTEndpackage(org.andromda.translation.ocl.node.TEndpackage)
1214      */
1215     public void caseTEndpackage(TEndpackage tEndpackage)
1216     {
1217     }
1218 
1219     /**
1220      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTEnum(org.andromda.translation.ocl.node.TEnum)
1221      */
1222     public void caseTEnum(TEnum tEnum)
1223     {
1224     }
1225 
1226     /**
1227      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTIn(org.andromda.translation.ocl.node.TIn)
1228      */
1229     public void caseTIn(TIn tIn)
1230     {
1231     }
1232 
1233     /**
1234      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTInit(org.andromda.translation.ocl.node.TInit)
1235      */
1236     public void caseTInit(TInit tInit)
1237     {
1238     }
1239 
1240     /**
1241      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTInt(org.andromda.translation.ocl.node.TInt)
1242      */
1243     public void caseTInt(TInt tInt)
1244     {
1245         write(tInt.getText());
1246     }
1247 
1248     /**
1249      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTIsSentOperator(org.andromda.translation.ocl.node.TIsSentOperator)
1250      */
1251     public void caseTIsSentOperator(TIsSentOperator tIsSentOperator)
1252     {
1253     }
1254 
1255     /**
1256      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTMessageOperator(org.andromda.translation.ocl.node.TMessageOperator)
1257      */
1258     public void caseTMessageOperator(TMessageOperator tMessageOperator)
1259     {
1260     }
1261 
1262     /**
1263      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTNewLine(org.andromda.translation.ocl.node.TNewLine)
1264      */
1265     public void caseTNewLine(TNewLine tNewLine)
1266     {
1267     }
1268 
1269     /**
1270      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTOper(org.andromda.translation.ocl.node.TOper)
1271      */
1272     public void caseTOper(TOper tOper)
1273     {
1274     }
1275 
1276     /**
1277      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTOrderedset(org.andromda.translation.ocl.node.TOrderedset)
1278      */
1279     public void caseTOrderedset(TOrderedset tOrderedset)
1280     {
1281     }
1282 
1283     /**
1284      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTPackage(org.andromda.translation.ocl.node.TPackage)
1285      */
1286     public void caseTPackage(TPackage tPackage)
1287     {
1288     }
1289 
1290     /**
1291      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTPost(org.andromda.translation.ocl.node.TPost)
1292      */
1293     public void caseTPost(TPost tPost)
1294     {
1295     }
1296 
1297     /**
1298      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTPre(org.andromda.translation.ocl.node.TPre)
1299      */
1300     public void caseTPre(TPre tPre)
1301     {
1302     }
1303 
1304     /**
1305      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTArrow(org.andromda.translation.ocl.node.TArrow)
1306      */
1307     public void caseTArrow(TArrow tArrow)
1308     {
1309     }
1310 
1311     /**
1312      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTIf(org.andromda.translation.ocl.node.TIf)
1313      */
1314     public void caseTIf(TIf tIf)
1315     {
1316         write("if");
1317     }
1318 
1319     /**
1320      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTElse(org.andromda.translation.ocl.node.TElse)
1321      */
1322     public void caseTElse(TElse tElse)
1323     {
1324         write("else");
1325     }
1326 
1327     /**
1328      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTThen(org.andromda.translation.ocl.node.TThen)
1329      */
1330     public void caseTThen(TThen tThen)
1331     {
1332     }
1333 
1334     /**
1335      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTRange(org.andromda.translation.ocl.node.TRange)
1336      */
1337     public void caseTRange(TRange tRange)
1338     {
1339     }
1340 
1341     /**
1342      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTReal(org.andromda.translation.ocl.node.TReal)
1343      */
1344     public void caseTReal(TReal tReal)
1345     {
1346         write(tReal.getText());
1347     }
1348 
1349     /**
1350      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTComma(org.andromda.translation.ocl.node.TComma)
1351      */
1352     public void caseTComma(TComma tComma)
1353     {
1354         write(", ");
1355     }
1356 
1357     /**
1358      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTDot(org.andromda.translation.ocl.node.TDot)
1359      */
1360     public void caseTDot(TDot tDot)
1361     {
1362         write(".");
1363     }
1364 
1365     /**
1366      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTSemicolon(org.andromda.translation.ocl.node.TSemicolon)
1367      */
1368     public void caseTSemicolon(TSemicolon tSemicolon)
1369     {
1370     }
1371 
1372     /**
1373      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTUnknown(org.andromda.translation.ocl.node.TUnknown)
1374      */
1375     public void caseTUnknown(TUnknown tUnknown)
1376     {
1377     }
1378 
1379     /**
1380      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTScopeOperator(org.andromda.translation.ocl.node.TScopeOperator)
1381      */
1382     public void caseTScopeOperator(TScopeOperator tScopeOperator)
1383     {
1384         write(".");
1385     }
1386 
1387     /**
1388      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTSequence(org.andromda.translation.ocl.node.TSequence)
1389      */
1390     public void caseTSequence(TSequence tSequence)
1391     {
1392     }
1393 
1394     /**
1395      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTSet(org.andromda.translation.ocl.node.TSet)
1396      */
1397     public void caseTSet(TSet tSet)
1398     {
1399     }
1400 
1401     /**
1402      * @param tStringLit
1403      * TODO: this method very naively replaces every single quote by a double quote, this should be updated
1404      */
1405     public void caseTStringLit(TStringLit tStringLit)
1406     {
1407         final StringBuffer buffer = new StringBuffer(tStringLit.getText().replace('\'', '\"'));
1408         write(buffer);
1409     }
1410 
1411     /**
1412      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTTab(org.andromda.translation.ocl.node.TTab)
1413      */
1414     public void caseTTab(TTab tTab)
1415     {
1416     }
1417 
1418     /**
1419      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTTuple(org.andromda.translation.ocl.node.TTuple)
1420      */
1421     public void caseTTuple(TTuple tTuple)
1422     {
1423     }
1424 
1425     /**
1426      * @see org.andromda.translation.ocl.analysis.AnalysisAdapter#caseTTupletype(org.andromda.translation.ocl.node.TTupletype)
1427      */
1428     public void caseTTupletype(TTupletype tTupletype)
1429     {
1430     }
1431 
1432     private final Stack translationLayers = new Stack();
1433 
1434     /**
1435      * Contains Boolean.TRUE on the top when the most recent property call was an arrow property call, contains
1436      * Boolean.FALSE otherwise.
1437      */
1438     private final Stack arrowPropertyCallStack = new Stack();
1439 
1440     /**
1441      * This stack contains elements implementing the Map interface. For each definition of variables a new Map element
1442      * will be pushed onto the stack. This element contains the variables defined in the definition.
1443      * <p/>
1444      * The keys and values contained in the Map are the names of the variables only (String instances).
1445      */
1446     private final Stack letVariableStack = new Stack();
1447 
1448     private void write(Object object)
1449     {
1450         appendToTranslationLayer(String.valueOf(object));
1451     }
1452 
1453     private StringBuffer newTranslationLayer()
1454     {
1455         return (StringBuffer) translationLayers.push(new StringBuffer());
1456     }
1457 
1458     private StringBuffer appendToTranslationLayer(Object appendix)
1459     {
1460         return ((StringBuffer) translationLayers.peek()).append(appendix);
1461     }
1462 
1463     private StringBuffer prependToTranslationLayer(Object appendix)
1464     {
1465         return ((StringBuffer) translationLayers.peek()).insert(0, appendix);
1466     }
1467 
1468     private StringBuffer mergeTranslationLayerAfter()
1469     {
1470         StringBuffer newTop = null;
1471 
1472         if (translationLayers.size() > 1)
1473         {
1474             newTop = appendToTranslationLayer(translationLayers.pop());
1475         }
1476 
1477         return newTop;
1478     }
1479 
1480     private StringBuffer mergeTranslationLayerBefore()
1481     {
1482         StringBuffer newTop = null;
1483 
1484         if (translationLayers.size() > 1)
1485         {
1486             newTop = prependToTranslationLayer(translationLayers.pop());
1487         }
1488 
1489         return newTop;
1490     }
1491 
1492     private StringBuffer mergeTranslationLayers()
1493     {
1494         while (mergeTranslationLayerAfter() != null) ;
1495         return (StringBuffer) translationLayers.peek();
1496     }
1497 
1498     private String getDeclaredLetVariableValue(String variableName)
1499     {
1500         for (final Iterator iterator = letVariableStack.iterator(); iterator.hasNext();)
1501         {
1502             Map variableMap = (Map) iterator.next();
1503             if (variableMap.containsKey(variableName))
1504             {
1505                 return (String) variableMap.get(variableName);
1506             }
1507         }
1508         return null;
1509     }
1510 
1511     private void newLetVariableContext()
1512     {
1513         letVariableStack.push(new HashMap(4));
1514     }
1515 
1516     private void dropLetVariableContext()
1517     {
1518         if (!letVariableStack.isEmpty())
1519             letVariableStack.pop();
1520     }
1521 
1522     private void addLetVariableToContext(String variableName, String variableValue)
1523     {
1524         ((Map) letVariableStack.peek()).put(variableName, variableValue);
1525     }
1526 
1527     /**
1528      * Gets the current context element as a {@link org.andromda.uml.metafacades.ModelElementFacade}.
1529      *
1530      * @return the context element as a model element facade
1531      */
1532     private ModelElementFacade getModelElement()
1533     {
1534         return (ModelElementFacade) this.getContextElement();
1535     }
1536 
1537     /**
1538      *
1539      */
1540     public ValidationJavaTranslator()
1541     {
1542         arrowPropertyCallStack.push(Boolean.FALSE);
1543     }
1544 
1545     /**
1546      * The name of the context element within the translated expression.
1547      */
1548     private static final String CONTEXT_ELEMENT_NAME = "contextElement";
1549 
1550     /**
1551      * The prefix for calling the OCLIntrospector to invoke a property or method on an element.
1552      */
1553     private static final String OCL_INTROSPECTOR_INVOKE_PREFIX = OCL_TRANSLATOR_PACKAGE + ".OCLIntrospector.invoke(";
1554 
1555     /**
1556      * The prefix for converting expressions to boolean expressions
1557      */
1558     private static final String BOOLEAN_WRAP_PREFIX = "Boolean.valueOf(String.valueOf(";
1559 
1560     /**
1561      * The suffix for converting expressions to boolean expressions;
1562      */
1563     private static final String BOOLEAN_WRAP_SUFFIX = ")).booleanValue()";
1564 
1565     /**
1566      * We need to wrap every expression with a converter so that any expressions that return just objects are converted
1567      * to boolean values.
1568      *
1569      * @see org.andromda.translation.ocl.BaseTranslator#postProcess()
1570      */
1571     @Override
1572     public void postProcess()
1573     {
1574         this.getExpression().insertInTranslatedExpression(0, OCL_TRANSLATOR_PACKAGE + ".OCLResultEnsurer.ensure(");
1575         this.getExpression().insertInTranslatedExpression(0, "boolean constraintValid = ");
1576         this.getExpression().insertInTranslatedExpression(0,
1577                 "final Object " + CONTEXT_ELEMENT_NAME + " = this; ");
1578         this.getExpression().appendToTranslatedExpression(");");
1579     }
1580 }