001package org.andromda.translation.ocl.validation;
002
003import java.io.InputStream;
004import java.net.URL;
005import java.util.HashMap;
006import java.util.Iterator;
007import java.util.List;
008import java.util.Map;
009import java.util.Properties;
010import java.util.Stack;
011import org.andromda.core.engine.ModelProcessorException;
012import org.andromda.core.translation.TranslationUtils;
013import org.andromda.metafacades.uml.ModelElementFacade;
014import org.andromda.translation.ocl.BaseTranslator;
015import org.andromda.translation.ocl.node.*;
016import org.andromda.translation.ocl.syntax.ConcreteSyntaxUtils;
017import org.andromda.translation.ocl.syntax.OCLFeatures;
018import org.andromda.translation.ocl.syntax.OCLPatterns;
019import org.apache.commons.lang.StringUtils;
020
021/**
022 * <p/>
023 * Provides translation of OCL validation constraints to the Java language. </p>
024 *
025 * @author Wouter Zoons
026 * @author Chad Brandon
027 */
028public class ValidationJavaTranslator
029        extends BaseTranslator
030{
031    private static Properties features = null;
032
033    static
034    {
035        try
036        {
037            URL featuresUri = ValidationJavaTranslator.class.getResource("features.properties");
038            if (featuresUri == null)
039            {
040                throw new ModelProcessorException("Could not load file --> '" + featuresUri + '\'');
041            }
042            features = new Properties();
043            InputStream stream = featuresUri.openStream();
044            features.load(stream);
045            stream.close();
046            stream = null;
047        }
048        catch (final Throwable throwable)
049        {
050            throw new ValidationTranslatorException(throwable);
051        }
052    }
053
054    /**
055     * The package to which the OCL translator classes belong.
056     */
057    private static final String OCL_TRANSLATOR_PACKAGE = "org.andromda.translation.ocl.validation";
058
059    /**
060     * This is the start of a new constraint. We prepare everything by resetting and initializing the required objects.
061     * @param node
062     */
063    public void caseAContextDeclaration(AContextDeclaration node)
064    {
065        newTranslationLayer();
066        {
067            Object[] temp = node.getContextDeclaration().toArray();
068            for (int ctr = 0; ctr < temp.length; ctr++)
069            {
070                ((PContextDeclaration) temp[ctr]).apply(this);
071            }
072        }
073        mergeTranslationLayers();
074        this.getExpression().appendToTranslatedExpression(translationLayers.peek());
075        translationLayers.clear();
076    }
077
078    /**
079     * @see org.andromda.translation.ocl.analysis.DepthFirstAdapter#caseAClassifierContextDeclaration(org.andromda.translation.ocl.node.AClassifierContextDeclaration)
080     */
081    public void caseAClassifierContextDeclaration(AClassifierContextDeclaration node)
082    {
083        // explicitly call super method so
084        // that we can set the type of the expression
085        super.inAClassifierContextDeclaration(node);
086        Object[] temp = node.getClassifierExpressionBody().toArray();
087        for (int ctr = 0; ctr < temp.length; ctr++)
088            ((PClassifierExpressionBody) temp[ctr]).apply(this);
089    }
090
091    /**
092     * @see org.andromda.translation.ocl.analysis.DepthFirstAdapter#caseAOperationContextDeclaration(org.andromda.translation.ocl.node.AOperationContextDeclaration)
093     */
094    public void caseAOperationContextDeclaration(AOperationContextDeclaration node)
095    {
096        // explicitly call super method so
097        // that we can set the type of the expression
098        super.inAOperationContextDeclaration(node);
099        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}