0001: package org.andromda.translation.ocl.validation;
0002:
0003: import org.andromda.core.engine.ModelProcessorException;
0004: import org.andromda.core.translation.TranslationUtils;
0005: import org.andromda.metafacades.uml.ModelElementFacade;
0006: import org.andromda.translation.ocl.BaseTranslator;
0007: import org.andromda.translation.ocl.node.AActualParameterList;
0008: import org.andromda.translation.ocl.node.AArrowPropertyCallExpressionTail;
0009: import org.andromda.translation.ocl.node.AAttributeOrAssociationContextDeclaration;
0010: import org.andromda.translation.ocl.node.ABodyOperationStereotype;
0011: import org.andromda.translation.ocl.node.AClassifierContextDeclaration;
0012: import org.andromda.translation.ocl.node.AContextDeclaration;
0013: import org.andromda.translation.ocl.node.ADefClassifierExpressionBody;
0014: import org.andromda.translation.ocl.node.ADotPropertyCallExpressionTail;
0015: import org.andromda.translation.ocl.node.AEqualExpression;
0016: import org.andromda.translation.ocl.node.AFeatureCall;
0017: import org.andromda.translation.ocl.node.AFeatureCallParameters;
0018: import org.andromda.translation.ocl.node.AFeaturePrimaryExpression;
0019: import org.andromda.translation.ocl.node.AIfExpression;
0020: import org.andromda.translation.ocl.node.AImpliesLogicalOperator;
0021: import org.andromda.translation.ocl.node.AInvClassifierExpressionBody;
0022: import org.andromda.translation.ocl.node.ALetExp;
0023: import org.andromda.translation.ocl.node.ALetVariableDeclaration;
0024: import org.andromda.translation.ocl.node.ALogicalExp;
0025: import org.andromda.translation.ocl.node.ALogicalExpressionTail;
0026: import org.andromda.translation.ocl.node.AMessageExpression;
0027: import org.andromda.translation.ocl.node.ANotUnaryOperator;
0028: import org.andromda.translation.ocl.node.AOperationContextDeclaration;
0029: import org.andromda.translation.ocl.node.APathName;
0030: import org.andromda.translation.ocl.node.APostOperationStereotype;
0031: import org.andromda.translation.ocl.node.APreOperationStereotype;
0032: import org.andromda.translation.ocl.node.APropertyCallExpression;
0033: import org.andromda.translation.ocl.node.ARelationalExpression;
0034: import org.andromda.translation.ocl.node.ARelationalExpressionTail;
0035: import org.andromda.translation.ocl.node.ATypeDeclaration;
0036: import org.andromda.translation.ocl.node.AUnaryExpression;
0037: import org.andromda.translation.ocl.node.AVariableDeclaration;
0038: import org.andromda.translation.ocl.node.AVariableDeclarationLetExpSub;
0039: import org.andromda.translation.ocl.node.AVariableDeclarationList;
0040: import org.andromda.translation.ocl.node.AVariableDeclarationListTail;
0041: import org.andromda.translation.ocl.node.Node;
0042: import org.andromda.translation.ocl.node.PAttributeOrAssociationExpressionBody;
0043: import org.andromda.translation.ocl.node.PClassifierExpressionBody;
0044: import org.andromda.translation.ocl.node.PContextDeclaration;
0045: import org.andromda.translation.ocl.node.POperationExpressionBody;
0046: import org.andromda.translation.ocl.node.PPropertyCallExpressionTail;
0047: import org.andromda.translation.ocl.node.PVariableDeclarationListTail;
0048: import org.andromda.translation.ocl.node.TAnd;
0049: import org.andromda.translation.ocl.node.TApostrophe;
0050: import org.andromda.translation.ocl.node.TArrow;
0051: import org.andromda.translation.ocl.node.TAttr;
0052: import org.andromda.translation.ocl.node.TBag;
0053: import org.andromda.translation.ocl.node.TBar;
0054: import org.andromda.translation.ocl.node.TBlank;
0055: import org.andromda.translation.ocl.node.TBody;
0056: import org.andromda.translation.ocl.node.TBoolean;
0057: import org.andromda.translation.ocl.node.TCollection;
0058: import org.andromda.translation.ocl.node.TColon;
0059: import org.andromda.translation.ocl.node.TComma;
0060: import org.andromda.translation.ocl.node.TCommercialAt;
0061: import org.andromda.translation.ocl.node.TContext;
0062: import org.andromda.translation.ocl.node.TDef;
0063: import org.andromda.translation.ocl.node.TDerive;
0064: import org.andromda.translation.ocl.node.TDiv;
0065: import org.andromda.translation.ocl.node.TDot;
0066: import org.andromda.translation.ocl.node.TElse;
0067: import org.andromda.translation.ocl.node.TEndif;
0068: import org.andromda.translation.ocl.node.TEndpackage;
0069: import org.andromda.translation.ocl.node.TEnum;
0070: import org.andromda.translation.ocl.node.TEqual;
0071: import org.andromda.translation.ocl.node.TGt;
0072: import org.andromda.translation.ocl.node.TGteq;
0073: import org.andromda.translation.ocl.node.TIf;
0074: import org.andromda.translation.ocl.node.TImplies;
0075: import org.andromda.translation.ocl.node.TIn;
0076: import org.andromda.translation.ocl.node.TInit;
0077: import org.andromda.translation.ocl.node.TInt;
0078: import org.andromda.translation.ocl.node.TInv;
0079: import org.andromda.translation.ocl.node.TIsSentOperator;
0080: import org.andromda.translation.ocl.node.TLBrace;
0081: import org.andromda.translation.ocl.node.TLBracket;
0082: import org.andromda.translation.ocl.node.TLParen;
0083: import org.andromda.translation.ocl.node.TLet;
0084: import org.andromda.translation.ocl.node.TLt;
0085: import org.andromda.translation.ocl.node.TLteq;
0086: import org.andromda.translation.ocl.node.TMessageOperator;
0087: import org.andromda.translation.ocl.node.TMinus;
0088: import org.andromda.translation.ocl.node.TMult;
0089: import org.andromda.translation.ocl.node.TName;
0090: import org.andromda.translation.ocl.node.TNewLine;
0091: import org.andromda.translation.ocl.node.TNot;
0092: import org.andromda.translation.ocl.node.TNotEqual;
0093: import org.andromda.translation.ocl.node.TOper;
0094: import org.andromda.translation.ocl.node.TOr;
0095: import org.andromda.translation.ocl.node.TOrderedset;
0096: import org.andromda.translation.ocl.node.TPackage;
0097: import org.andromda.translation.ocl.node.TPlus;
0098: import org.andromda.translation.ocl.node.TPost;
0099: import org.andromda.translation.ocl.node.TPre;
0100: import org.andromda.translation.ocl.node.TRBrace;
0101: import org.andromda.translation.ocl.node.TRBracket;
0102: import org.andromda.translation.ocl.node.TRParen;
0103: import org.andromda.translation.ocl.node.TRange;
0104: import org.andromda.translation.ocl.node.TReal;
0105: import org.andromda.translation.ocl.node.TScopeOperator;
0106: import org.andromda.translation.ocl.node.TSemicolon;
0107: import org.andromda.translation.ocl.node.TSequence;
0108: import org.andromda.translation.ocl.node.TSet;
0109: import org.andromda.translation.ocl.node.TSingleLineComment;
0110: import org.andromda.translation.ocl.node.TStringLit;
0111: import org.andromda.translation.ocl.node.TTab;
0112: import org.andromda.translation.ocl.node.TThen;
0113: import org.andromda.translation.ocl.node.TTuple;
0114: import org.andromda.translation.ocl.node.TTupletype;
0115: import org.andromda.translation.ocl.node.TUnknown;
0116: import org.andromda.translation.ocl.node.TXor;
0117: import org.andromda.translation.ocl.syntax.ConcreteSyntaxUtils;
0118: import org.andromda.translation.ocl.syntax.OCLFeatures;
0119: import org.andromda.translation.ocl.syntax.OCLPatterns;
0120: import org.apache.commons.lang.StringUtils;
0121:
0122: import java.io.InputStream;
0123: import java.net.URL;
0124: import java.util.HashMap;
0125: import java.util.Iterator;
0126: import java.util.List;
0127: import java.util.Map;
0128: import java.util.Properties;
0129: import java.util.Stack;
0130:
0131: /**
0132: * <p/>
0133: * Provides translation of OCL validation constraints to the Java language. </p>
0134: *
0135: * @author Wouter Zoons
0136: * @author Chad Brandon
0137: */
0138: public class ValidationJavaTranslator extends BaseTranslator {
0139: private static Properties features = null;
0140:
0141: static {
0142: try {
0143: URL featuresUri = ValidationJavaTranslator.class
0144: .getResource("features.properties");
0145: if (featuresUri == null) {
0146: throw new ModelProcessorException(
0147: "Could not load file --> '" + featuresUri + "'");
0148: }
0149: features = new Properties();
0150: InputStream stream = featuresUri.openStream();
0151: features.load(stream);
0152: stream.close();
0153: stream = null;
0154:
0155: } catch (final Throwable throwable) {
0156: throw new ValidationTranslatorException(throwable);
0157: }
0158:
0159: }
0160:
0161: /**
0162: * The package to which the OCL translator classes belong.
0163: */
0164: private static final String OCL_TRANSLATOR_PACKAGE = "org.andromda.translation.ocl.validation";
0165:
0166: /**
0167: * This is the start of a new constraint. We prepare everything by resetting and initializing the required objects.
0168: */
0169: public void caseAContextDeclaration(AContextDeclaration node) {
0170: newTranslationLayer();
0171: {
0172: Object temp[] = node.getContextDeclaration().toArray();
0173: for (int ctr = 0; ctr < temp.length; ctr++) {
0174: ((PContextDeclaration) temp[ctr]).apply(this );
0175: }
0176: }
0177: mergeTranslationLayers();
0178: this .getExpression().appendToTranslatedExpression(
0179: translationLayers.peek());
0180: translationLayers.clear();
0181: }
0182:
0183: public void caseAClassifierContextDeclaration(
0184: AClassifierContextDeclaration node) {
0185: // explicity call super method so
0186: // that we can set the type of the expression
0187: super .inAClassifierContextDeclaration(node);
0188: Object temp[] = node.getClassifierExpressionBody().toArray();
0189: for (int ctr = 0; ctr < temp.length; ctr++)
0190: ((PClassifierExpressionBody) temp[ctr]).apply(this );
0191: }
0192:
0193: public void caseAOperationContextDeclaration(
0194: AOperationContextDeclaration node) {
0195: // explicity call super method so
0196: // that we can set the type of the expression
0197: super .inAOperationContextDeclaration(node);
0198: Object temp[] = node.getOperationExpressionBody().toArray();
0199: for (int ctr = 0; ctr < temp.length; ctr++)
0200: ((POperationExpressionBody) temp[ctr]).apply(this );
0201: }
0202:
0203: public void caseAAttributeOrAssociationContextDeclaration(
0204: AAttributeOrAssociationContextDeclaration node) {
0205: super .inAAttributeOrAssociationContextDeclaration(node);
0206: Object temp[] = node.getAttributeOrAssociationExpressionBody()
0207: .toArray();
0208: for (int ctr = 0; ctr < temp.length; ctr++)
0209: ((PAttributeOrAssociationExpressionBody) temp[ctr])
0210: .apply(this );
0211: }
0212:
0213: public void caseAInvClassifierExpressionBody(
0214: AInvClassifierExpressionBody node) {
0215: // explicity call super method so
0216: // that we can set the type of the expression
0217: super .inAInvClassifierExpressionBody(node);
0218: node.getExpression().apply(this );
0219: }
0220:
0221: public void caseADefClassifierExpressionBody(
0222: ADefClassifierExpressionBody node) {
0223: // explicity call super method so
0224: // that we can set the type of the expression
0225: super .inADefClassifierExpressionBody(node);
0226: node.getDefinitionExpression().apply(this );
0227: }
0228:
0229: /**
0230: * We need to keep track that what follows is in the scope of an arrow feature call, this is important because it
0231: * means it is a feature that is implied by the OCL language, rather than the model on which the constraint
0232: * applies.
0233: */
0234: public void inAArrowPropertyCallExpressionTail(
0235: AArrowPropertyCallExpressionTail node) {
0236: this .arrowPropertyCallStack.push(Boolean.TRUE);
0237: }
0238:
0239: /**
0240: * Undo the arrow feature call trace.
0241: */
0242: public void outAArrowPropertyCallExpressionTail(
0243: AArrowPropertyCallExpressionTail node) {
0244: this .arrowPropertyCallStack.pop();
0245: }
0246:
0247: /**
0248: * This indicates we have entered a feature call, we need to mark this to counterpart any previous arrow feature
0249: * call flags.
0250: */
0251: public void inADotPropertyCallExpressionTail(
0252: ADotPropertyCallExpressionTail node) {
0253: this .arrowPropertyCallStack.push(Boolean.FALSE);
0254: }
0255:
0256: /**
0257: * Undo the dot feature call trace.
0258: */
0259: public void outADotPropertyCallExpressionTail(
0260: ADotPropertyCallExpressionTail node) {
0261: this .arrowPropertyCallStack.pop();
0262: }
0263:
0264: /**
0265: * Here we need to make sure the equals sign '=' is not translated into the 'equal' keyword. OCL uses '=' for
0266: * comparison as well as for assignment, Java uses '==', '=' and .equals() so we override the default OCL value here
0267: * to use '=' instead of 'equal'
0268: */
0269: public void caseALetVariableDeclaration(ALetVariableDeclaration node) {
0270: inALetVariableDeclaration(node);
0271: if (node.getVariableDeclaration() != null) {
0272: node.getVariableDeclaration().apply(this );
0273: }
0274: if (node.getEqual() != null) {
0275: write("=");
0276: }
0277: if (node.getExpression() != null) {
0278: node.getExpression().apply(this );
0279: }
0280: outALetVariableDeclaration(node);
0281: }
0282:
0283: /**
0284: * Add a variable to the context.
0285: */
0286: public void inALetVariableDeclaration(ALetVariableDeclaration node) {
0287: newTranslationLayer(); // this layer will be disposed later on, we do
0288: // not write variable declarations
0289:
0290: AVariableDeclaration variableDeclaration = (AVariableDeclaration) node
0291: .getVariableDeclaration();
0292: String variableName = variableDeclaration.getName().getText();
0293:
0294: newTranslationLayer();
0295: node.getExpression().apply(this );
0296:
0297: String variableValue = translationLayers.pop().toString();
0298:
0299: addLetVariableToContext(variableName, variableValue);
0300: }
0301:
0302: /**
0303: * In Java we need to end the declaration statement with a semicolon, this is handled here.
0304: */
0305: public void outALetVariableDeclaration(ALetVariableDeclaration node) {
0306: write(";");
0307: translationLayers.pop();
0308: }
0309:
0310: /**
0311: * Renders a variable declaration. Missing types will imply the java.lang.Object type.
0312: */
0313: public void caseAVariableDeclaration(AVariableDeclaration node) {
0314: if (node.getTypeDeclaration() == null)
0315: write("java.lang.Object");
0316: else
0317: node.getTypeDeclaration().apply(this );
0318:
0319: write(" "); // we need to add a space between the type and the name
0320:
0321: node.getName().apply(this );
0322: }
0323:
0324: public void caseATypeDeclaration(ATypeDeclaration node) {
0325: node.getType().apply(this );
0326: }
0327:
0328: public void caseAVariableDeclarationList(
0329: AVariableDeclarationList node) {
0330: node.getVariableDeclaration().apply(this );
0331:
0332: if (node.getVariableDeclarationValue() != null)
0333: node.getVariableDeclarationValue().apply(this );
0334:
0335: Object temp[] = node.getVariableDeclarationListTail().toArray();
0336: for (int ctr = 0; ctr < temp.length; ctr++)
0337: ((PVariableDeclarationListTail) temp[ctr]).apply(this );
0338: }
0339:
0340: public void caseAVariableDeclarationListTail(
0341: AVariableDeclarationListTail node) {
0342: node.getComma().apply(this );
0343: node.getVariableDeclaration().apply(this );
0344:
0345: if (node.getVariableDeclarationValue() != null)
0346: node.getVariableDeclarationValue().apply(this );
0347: }
0348:
0349: public void caseAEqualExpression(AEqualExpression node) {
0350: node.getEqual().apply(this );
0351: node.getExpression().apply(this );
0352: }
0353:
0354: public void caseABodyOperationStereotype(
0355: ABodyOperationStereotype node) {
0356: }
0357:
0358: public void caseAPreOperationStereotype(APreOperationStereotype node) {
0359: }
0360:
0361: public void caseAPostOperationStereotype(
0362: APostOperationStereotype node) {
0363: }
0364:
0365: public void caseAMessageExpression(AMessageExpression node) {
0366: }
0367:
0368: public void caseAIfExpression(AIfExpression node) {
0369: node.getIf().apply(this );
0370:
0371: write("(");
0372: node.getIfBranch().apply(this );
0373: write(")");
0374:
0375: node.getThen().apply(this );
0376:
0377: write("{");
0378: node.getThenBranch().apply(this );
0379: write(";");
0380: write("}");
0381:
0382: node.getElse().apply(this );
0383:
0384: write("{");
0385: node.getElseBranch().apply(this );
0386: write(";");
0387: write("}");
0388: }
0389:
0390: public void caseAPropertyCallExpression(APropertyCallExpression node) {
0391: newTranslationLayer();
0392: node.getPrimaryExpression().apply(this );
0393: Object temp[] = node.getPropertyCallExpressionTail().toArray();
0394: for (int ctr = 0; ctr < temp.length; ctr++)
0395: ((PPropertyCallExpressionTail) temp[ctr]).apply(this );
0396: mergeTranslationLayerAfter();
0397: }
0398:
0399: public void caseADotPropertyCallExpressionTail(
0400: ADotPropertyCallExpressionTail node) {
0401: inADotPropertyCallExpressionTail(node);
0402: String expression = TranslationUtils.trimToEmpty(node);
0403: // we prepend an introspection call if the expression is
0404: // an operation call
0405: if (OCLPatterns.isOperation(expression)) {
0406: AFeatureCall featureCall = (AFeatureCall) node
0407: .getFeatureCall();
0408: String featureCallExpression = TranslationUtils
0409: .trimToEmpty(node.getFeatureCall());
0410: if (OCLFeatures.isOclIsKindOf(featureCallExpression)) {
0411: this .handleOclIsKindOf(featureCall);
0412: } else if (OCLFeatures.isOclIsTypeOf(featureCallExpression)) {
0413: this .handleOclIsTypeOf(featureCall);
0414: } else if (OCLFeatures.isConcat(featureCallExpression)) {
0415: this .handleConcat(featureCall);
0416: } else {
0417: this .handleDotFeatureCall(featureCall);
0418: }
0419: }
0420: outADotPropertyCallExpressionTail(node);
0421: }
0422:
0423: /**
0424: * oclIsKindOf(type) is a special feature defined by OCL on all objects.
0425: */
0426: private void handleOclIsKindOf(Object node) {
0427: String type = this .getParametersAsType(node);
0428: if (type != null) {
0429: write(" instanceof ");
0430: write(type);
0431: }
0432: }
0433:
0434: /**
0435: * oclIsTypeOf(type) is a special feature defined by OCL on all objects.
0436: */
0437: private void handleOclIsTypeOf(Object node) {
0438: String type = this .getParametersAsType(node);
0439: if (type != null) {
0440: write(".getClass().getName().equals(");
0441: write(type);
0442: write(".class.getName())");
0443: }
0444: }
0445:
0446: /**
0447: * Extracts the parameters from the given <code>node</code> and returns the parameters as a type (or null if none
0448: * can be extracted).
0449: *
0450: * @param node the node from which to extrac the parameters
0451: * @return the fully qualified type name.
0452: */
0453: private String getParametersAsType(Object node) {
0454: String type = null;
0455: if (node instanceof AFeatureCall) {
0456: type = ConcreteSyntaxUtils
0457: .getParametersAsString((AFeatureCall) node);
0458: } else if (node instanceof AFeatureCallParameters) {
0459: type = ConcreteSyntaxUtils
0460: .getParametersAsString((AFeatureCallParameters) node);
0461: }
0462: if (type != null) {
0463: type = type.replaceAll("\\s*::\\s*", ".");
0464: // if we don't have a package define, attempt to find the model
0465: // element
0466: // in the same package as the context element.
0467: if (type.indexOf(".") == -1) {
0468: if (this .getModelElement() != null) {
0469: type = this .getModelElement().getPackageName()
0470: + "." + type;
0471: }
0472: }
0473: }
0474: return type;
0475: }
0476:
0477: /**
0478: * contact(string) is a special feature defined by OCL on strings.
0479: */
0480: private void handleConcat(AFeatureCall featureCall) {
0481: write(" + \"\" + ");
0482: write(OCL_INTROSPECTOR_INVOKE_PREFIX);
0483: write(CONTEXT_ELEMENT_NAME);
0484: write(",\"");
0485: write(ConcreteSyntaxUtils.getParametersAsString(featureCall)
0486: .replaceAll("\\s*", ""));
0487: write("\")");
0488: }
0489:
0490: /**
0491: * Handles a <strong>dot </strong> feature call. Its expected that this <code>featureCall</code>'s parent is a
0492: * ADotPropertyCallExpressionTail. This is here because dot feature calls must be handled differently than
0493: * <code>arrow<code> feature calls.
0494: *
0495: * @param featureCall the <strong>dot</strong> <code>featureCall</code> to handle.
0496: */
0497: public void handleDotFeatureCall(AFeatureCall featureCall) {
0498: this .prependToTranslationLayer(OCL_INTROSPECTOR_INVOKE_PREFIX);
0499: this .appendToTranslationLayer(",\"");
0500: this .appendToTranslationLayer(TranslationUtils
0501: .deleteWhitespace(featureCall));
0502: this .appendToTranslationLayer("\"");
0503: if (featureCall.getFeatureCallParameters() != null) {
0504: List parameters = ConcreteSyntaxUtils
0505: .getParameters(featureCall);
0506: if (parameters != null && !parameters.isEmpty()) {
0507: write(",new Object[]{");
0508: this
0509: .appendToTranslationLayer(OCL_INTROSPECTOR_INVOKE_PREFIX);
0510: this .appendToTranslationLayer(CONTEXT_ELEMENT_NAME);
0511: this .appendToTranslationLayer(",\"");
0512: this .appendToTranslationLayer(ConcreteSyntaxUtils
0513: .getParameters(featureCall).get(0));
0514: this .appendToTranslationLayer("\")}");
0515: }
0516: }
0517: this .appendToTranslationLayer(")");
0518: }
0519:
0520: public void caseAArrowPropertyCallExpressionTail(
0521: AArrowPropertyCallExpressionTail node) {
0522: inAArrowPropertyCallExpressionTail(node);
0523: node.getArrow().apply(this );
0524: this .handleArrowFeatureCall((AFeatureCall) node
0525: .getFeatureCall());
0526: outAArrowPropertyCallExpressionTail(node);
0527: }
0528:
0529: /**
0530: * @see org.andromda.translation.ocl.BaseTranslator#isOperationArgument(java.lang.String)
0531: */
0532: protected boolean isOperationArgument(String argument) {
0533: return super .isOperationArgument(this .getRootName(argument));
0534: }
0535:
0536: /**
0537: * Gets the root path name from the given <code>navigationalPath</code> (by trimming off any additional navigational
0538: * path).
0539: *
0540: * @param navigationalPath the navigational property path (i.e. car.door)
0541: * @return the root of the path name.
0542: */
0543: private String getRootName(String navigationalPath) {
0544: return StringUtils.trimToEmpty(navigationalPath).replaceAll(
0545: "\\..*", "");
0546: }
0547:
0548: /**
0549: * Gets the tail of the navigational path (that is it removes the root from the path and returns the tail).
0550: *
0551: * @param navigationalPath the navigational property path (i.e. car.door)
0552: * @return the tail of the path name.
0553: */
0554: private String getPathTail(String navigationalPath) {
0555: final int dotIndex = navigationalPath.indexOf(".");
0556: return dotIndex != -1 ? navigationalPath.substring(
0557: dotIndex + 1, navigationalPath.length())
0558: : navigationalPath;
0559: }
0560:
0561: /**
0562: * @todo: improve implementation to reduce the code duplication (avoid having two write statements)
0563: */
0564: public void caseAFeaturePrimaryExpression(
0565: AFeaturePrimaryExpression node) {
0566: inAFeaturePrimaryExpression(node);
0567: if (node.getPathName() != null) {
0568: final String variableName = ((APathName) node.getPathName())
0569: .getName().getText();
0570: final String variableValue = getDeclaredLetVariableValue(variableName);
0571: final boolean isDeclaredAsLetVariable = (variableValue != null);
0572: String featureExpression = TranslationUtils
0573: .deleteWhitespace(node);
0574: if (isDeclaredAsLetVariable) {
0575: write(variableValue);
0576: } else if (node.getFeatureCallParameters() == null
0577: || OCLPatterns.isOperation(featureExpression)) {
0578: APropertyCallExpression expression = (APropertyCallExpression) node
0579: .parent();
0580: String expressionAsString = ConcreteSyntaxUtils
0581: .getPrimaryExpression(expression);
0582: // remove any references to 'self.' as we write
0583: expressionAsString = expressionAsString.replaceAll(
0584: "self\\.", "");
0585: if (OCLFeatures.isSelf(expressionAsString)) {
0586: write(CONTEXT_ELEMENT_NAME);
0587: } else if (StringUtils.isNotBlank(expressionAsString)) {
0588: boolean convertToBoolean = false;
0589: if (node.parent().parent() instanceof AUnaryExpression) {
0590: AUnaryExpression unaryExpression = (AUnaryExpression) node
0591: .parent().parent();
0592: // we convert each unary not expression to boolean
0593: convertToBoolean = unaryExpression
0594: .getUnaryOperator() instanceof ANotUnaryOperator;
0595: if (convertToBoolean) {
0596: this .write(BOOLEAN_WRAP_PREFIX);
0597: }
0598: }
0599: if (OCLFeatures.isOclIsKindOf(expressionAsString)) {
0600: this .write("object");
0601: this .handleOclIsKindOf(node
0602: .getFeatureCallParameters());
0603: } else if (OCLFeatures
0604: .isOclIsTypeOf(expressionAsString)) {
0605: this .write("object");
0606: this .handleOclIsTypeOf(node
0607: .getFeatureCallParameters());
0608: } else {
0609: // whether or not an introspector call is required
0610: boolean introspectorCall = true;
0611: String invokedObject = CONTEXT_ELEMENT_NAME;
0612: // if we're in an arrow call we assume the invoked
0613: // object is the object for which the arrow call applies
0614: if (this .arrowPropertyCallStack.peek().equals(
0615: Boolean.TRUE)) {
0616: invokedObject = "object";
0617: }
0618: if (this
0619: .isOperationArgument(expressionAsString)) {
0620: // - if the expression is an argument, then
0621: // that becomes the invoked object
0622: invokedObject = this
0623: .getRootName(expressionAsString);
0624: expressionAsString = this
0625: .getPathTail(expressionAsString);
0626: introspectorCall = !invokedObject
0627: .equals(expressionAsString);
0628: }
0629: if (introspectorCall) {
0630: write(OCL_INTROSPECTOR_INVOKE_PREFIX);
0631: }
0632: write(invokedObject);
0633: if (introspectorCall) {
0634: write(",\"");
0635: write(expressionAsString);
0636: }
0637: if (introspectorCall) {
0638: write("\")");
0639: }
0640: if (convertToBoolean) {
0641: this .write(BOOLEAN_WRAP_SUFFIX);
0642: }
0643: }
0644: if (this .requiresBooleanConversion) {
0645: this .write(BOOLEAN_WRAP_SUFFIX);
0646: this .requiresBooleanConversion = false;
0647: }
0648: }
0649: } else {
0650: node.getPathName().apply(this );
0651: }
0652: }
0653: if (node.getIsMarkedPre() != null) {
0654: node.getIsMarkedPre().apply(this );
0655: }
0656: if (node.getQualifiers() != null) {
0657: // we use introspection when in an arrow, so passing
0658: // feature name as a String without parentheses
0659: if (this .arrowPropertyCallStack.peek()
0660: .equals(Boolean.FALSE)) {
0661: node.getQualifiers().apply(this );
0662: }
0663: }
0664: outAFeaturePrimaryExpression(node);
0665: }
0666:
0667: /**
0668: * Handles an <strong>arrow </strong> feature call. Its expected that this <code>featureCall</code>'s parent is a
0669: * AArrowPropertyCallExpressionTail. This is here because arrow feature calls must be handled differently than
0670: * <code>dot<code> feature calls.
0671: *
0672: * @param featureCall the <strong>arrow</strong> <code>featureCall</code> to handle.
0673: */
0674: public void handleArrowFeatureCall(AFeatureCall featureCall) {
0675: AFeatureCallParameters params = (AFeatureCallParameters) featureCall
0676: .getFeatureCallParameters();
0677: AActualParameterList list = null;
0678: if (params != null) {
0679: list = (AActualParameterList) params
0680: .getActualParameterList();
0681: }
0682: boolean arrow = this .arrowPropertyCallStack.peek().equals(
0683: Boolean.TRUE)
0684: && !String.valueOf(list).trim().equals("");
0685: {
0686: newTranslationLayer();
0687: final String navigationalPath = ConcreteSyntaxUtils
0688: .getArrowFeatureCallResultNavigationalPath((APropertyCallExpression) featureCall
0689: .parent().parent());
0690: // if the result of an arrow feature (collection operation) has a
0691: // navigational
0692: // path, retrieve it and wrap the current expression with an OCL
0693: // introspector call
0694: boolean resultNavigationalPath = StringUtils
0695: .isNotBlank(navigationalPath);
0696: if (resultNavigationalPath) {
0697: write(OCL_INTROSPECTOR_INVOKE_PREFIX);
0698: }
0699: write(OCL_TRANSLATOR_PACKAGE);
0700: write(".OCLCollections.");
0701: inAFeatureCall(featureCall);
0702: if (featureCall.getPathName() != null) {
0703: featureCall.getPathName().apply(this );
0704: }
0705: String featureCallName = TranslationUtils
0706: .trimToEmpty(featureCall.getPathName());
0707: AFeatureCallParameters parameters = (AFeatureCallParameters) featureCall
0708: .getFeatureCallParameters();
0709: if (parameters != null) {
0710: if (parameters.getLParen() != null) {
0711: parameters.getLParen().apply(this );
0712: }
0713: mergeTranslationLayerBefore();
0714: AActualParameterList parameterList = (AActualParameterList) parameters
0715: .getActualParameterList();
0716: if (parameterList != null) {
0717: List expressions = parameterList
0718: .getCommaExpression();
0719:
0720: if (parameterList.getExpression() != null) {
0721: if (arrow) {
0722: write(",");
0723: write(features.getProperty(featureCallName));
0724: write(" ");
0725: if (OCLPredicateFeatures
0726: .isPredicateFeature(featureCallName)) {
0727: write(BOOLEAN_WRAP_PREFIX);
0728: }
0729: }
0730: parameterList.getExpression().apply(this );
0731: }
0732: for (int ctr = 0; ctr < expressions.size(); ctr++) {
0733: Node expression = (Node) expressions.get(ctr);
0734: if (expression != null) {
0735: write(",");
0736: expression.apply(this );
0737: }
0738: }
0739: if (parameterList.getExpression() != null) {
0740: if (OCLPredicateFeatures
0741: .isPredicateFeature(featureCallName)) {
0742: write(BOOLEAN_WRAP_SUFFIX);
0743: }
0744: if (arrow)
0745: write(";}}");
0746: }
0747: }
0748: if (parameters.getRParen() != null) {
0749: parameters.getRParen().apply(this );
0750: }
0751: }
0752: // now since we have a navigational path off of the
0753: // result we need to write the path and close off
0754: // the call to the OCL introspector.
0755: if (resultNavigationalPath) {
0756: write(",\"");
0757: write(navigationalPath);
0758: write("\"");
0759: write(")");
0760: }
0761: this .outAFeatureCall(featureCall);
0762: }
0763: }
0764:
0765: public void caseALetExp(ALetExp node) {
0766: inALetExp(node);
0767: if (node.getLet() != null) {
0768: node.getLet().apply(this );
0769: }
0770: if (node.getLetVariableDeclaration() != null) {
0771: node.getLetVariableDeclaration().apply(this );
0772: }
0773: if (node.getLetExpSub() != null) {
0774: node.getLetExpSub().apply(this );
0775: }
0776: outALetExp(node);
0777: }
0778:
0779: /**
0780: * We are ready to store a new context of variables
0781: */
0782: public void inALetExp(ALetExp node) {
0783: newLetVariableContext();
0784: }
0785:
0786: /**
0787: * The variables are out of scope, we need to purge their context.
0788: */
0789: public void outALetExp(ALetExp node) {
0790: dropLetVariableContext();
0791: }
0792:
0793: public void caseAVariableDeclarationLetExpSub(
0794: AVariableDeclarationLetExpSub node) {
0795: node.getComma().apply(this );
0796: node.getLetVariableDeclaration().apply(this );
0797: node.getLetExpSub().apply(this );
0798: }
0799:
0800: public void caseALogicalExp(ALogicalExp node) {
0801: newTranslationLayer();
0802: if (node.getRelationalExpression() != null) {
0803: node.getRelationalExpression().apply(this );
0804: }
0805: Object tails[] = node.getLogicalExpressionTail().toArray();
0806: for (int ctr = 0; ctr < tails.length; ctr++) {
0807: ((ALogicalExpressionTail) tails[ctr]).apply(this );
0808: }
0809: mergeTranslationLayerAfter();
0810: }
0811:
0812: public void caseALogicalExpressionTail(ALogicalExpressionTail node) {
0813: node.getLogicalOperator().apply(this );
0814: if (node.getLogicalOperator() instanceof AImpliesLogicalOperator) {
0815: prependToTranslationLayer("(");
0816: }
0817: if (node.getRelationalExpression() != null) {
0818: node.getRelationalExpression().apply(this );
0819: }
0820: if (node.getLogicalOperator() instanceof AImpliesLogicalOperator) {
0821: write(":true)");
0822: }
0823: }
0824:
0825: public void caseARelationalExpressionTail(
0826: ARelationalExpressionTail node) {
0827: inARelationalExpressionTail(node);
0828:
0829: newTranslationLayer();
0830: write(OCL_TRANSLATOR_PACKAGE);
0831: write(".OCLExpressions.");
0832: node.getRelationalOperator().apply(this );
0833: write("(");
0834: mergeTranslationLayerBefore();
0835: if (node.getAdditiveExpression() != null) {
0836: write(",");
0837: node.getAdditiveExpression().apply(this );
0838: }
0839: write(")");
0840: outARelationalExpressionTail(node);
0841: }
0842:
0843: /**
0844: * A flag indicating if the expression needs to be converted/wrapped in a Java Boolean instance.
0845: */
0846: private boolean requiresBooleanConversion = false;
0847:
0848: public void inARelationalExpression(ARelationalExpression node) {
0849: // in this block of code, we determine whether or not
0850: // the next appended expression needs to be
0851: // converted/wrapped by a Java Boolean
0852: if (node.getRelationalExpressionTail() == null) {
0853: Object parent = node.parent();
0854: Object expression = null;
0855: if (parent instanceof ALogicalExp) {
0856: List tails = ((ALogicalExp) parent)
0857: .getLogicalExpressionTail();
0858: if (tails != null && !tails.isEmpty()) {
0859: expression = tails.get(0);
0860: // if it's an AND or OR expression set the expression
0861: // to the node
0862: if (OCLPatterns.isAndOrOrExpression(expression)) {
0863: expression = node;
0864: }
0865: }
0866: } else if (parent instanceof ALogicalExpressionTail) {
0867: expression = node;
0868: }
0869: requiresBooleanConversion = expression != null
0870: && OCLPatterns.isNavigationalPath(expression)
0871: && !OCLPatterns.isOperation(node);
0872: if (this .requiresBooleanConversion) {
0873: this .write(BOOLEAN_WRAP_PREFIX);
0874: }
0875: }
0876: newTranslationLayer();
0877: }
0878:
0879: public void outARelationalExpression(ARelationalExpression node) {
0880: mergeTranslationLayerAfter();
0881: }
0882:
0883: public void caseTName(TName node) {
0884: write(node.getText());
0885: }
0886:
0887: public void caseTAnd(TAnd tAnd) {
0888: write("&&");
0889: }
0890:
0891: public void caseTOr(TOr tOr) {
0892: write("||");
0893: }
0894:
0895: public void caseTXor(TXor tXor) {
0896: write("^");
0897: }
0898:
0899: public void caseTImplies(TImplies tImplies) {
0900: // convert any non boolean's to boolean
0901: this .prependToTranslationLayer(BOOLEAN_WRAP_PREFIX);
0902: this .appendToTranslationLayer(BOOLEAN_WRAP_SUFFIX);
0903: write("?");
0904: }
0905:
0906: public void caseTNot(TNot tNot) {
0907: write("!");
0908: }
0909:
0910: public void caseTPlus(TPlus tPlus) {
0911: write("+");
0912: }
0913:
0914: public void caseTMinus(TMinus tMinus) {
0915: write("-");
0916: }
0917:
0918: public void caseTMult(TMult tMult) {
0919: write("*");
0920: }
0921:
0922: public void caseTDiv(TDiv tDiv) {
0923: write("/");
0924: }
0925:
0926: public void caseTEqual(TEqual tEqual) {
0927: write("equal");
0928: }
0929:
0930: public void caseTNotEqual(TNotEqual tNotEqual) {
0931: write("notEqual");
0932: }
0933:
0934: public void caseTLt(TLt tLt) {
0935: write("less");
0936: }
0937:
0938: public void caseTLteq(TLteq tLteq) {
0939: write("lessOrEqual");
0940: }
0941:
0942: public void caseTGt(TGt tGt) {
0943: write("greater");
0944: }
0945:
0946: public void caseTGteq(TGteq tGteq) {
0947: write("greaterOrEqual");
0948: }
0949:
0950: public void caseTInv(TInv tInv) {
0951: }
0952:
0953: public void caseTDef(TDef tDef) {
0954: }
0955:
0956: public void caseTLet(TLet tLet) {
0957: }
0958:
0959: public void caseTColon(TColon tColon) {
0960: }
0961:
0962: public void caseTLBrace(TLBrace tlBrace) {
0963: write("{");
0964: }
0965:
0966: public void caseTLBracket(TLBracket tlBracket) {
0967: write("[");
0968: }
0969:
0970: public void caseTLParen(TLParen tlParen) {
0971: write("(");
0972: }
0973:
0974: public void caseTRBrace(TRBrace trBrace) {
0975: write("}");
0976: }
0977:
0978: public void caseTRBracket(TRBracket trBracket) {
0979: write("]");
0980: }
0981:
0982: public void caseTRParen(TRParen trParen) {
0983: write(")");
0984: }
0985:
0986: public void caseTContext(TContext tContext) {
0987: }
0988:
0989: public void caseTBoolean(TBoolean tBoolean) {
0990: write(tBoolean.getText());
0991: }
0992:
0993: public void caseTApostrophe(TApostrophe tApostrophe) {
0994: write("\'");
0995: }
0996:
0997: public void caseTBlank(TBlank tBlank) {
0998: write(" ");
0999: }
1000:
1001: public void caseTCollection(TCollection tCollection) {
1002: write("java.util.Collection ");
1003: }
1004:
1005: public void caseTComment(TSingleLineComment tSingleLineComment) {
1006: write("// ");
1007: }
1008:
1009: public void caseTEndif(TEndif tEndif) {
1010: }
1011:
1012: public void caseTAttr(TAttr tAttr) {
1013: }
1014:
1015: public void caseTBag(TBag tBag) {
1016: }
1017:
1018: public void caseTBar(TBar tBar) {
1019: }
1020:
1021: public void caseTBody(TBody tBody) {
1022: }
1023:
1024: public void caseTCommercialAt(TCommercialAt tCommercialAt) {
1025: }
1026:
1027: public void caseTDerive(TDerive tDerive) {
1028: }
1029:
1030: public void caseTEndpackage(TEndpackage tEndpackage) {
1031: }
1032:
1033: public void caseTEnum(TEnum tEnum) {
1034: }
1035:
1036: public void caseTIn(TIn tIn) {
1037: }
1038:
1039: public void caseTInit(TInit tInit) {
1040: }
1041:
1042: public void caseTInt(TInt tInt) {
1043: write(tInt.getText());
1044: }
1045:
1046: public void caseTIsSentOperator(TIsSentOperator tIsSentOperator) {
1047: }
1048:
1049: public void caseTMessageOperator(TMessageOperator tMessageOperator) {
1050: }
1051:
1052: public void caseTNewLine(TNewLine tNewLine) {
1053: }
1054:
1055: public void caseTOper(TOper tOper) {
1056: }
1057:
1058: public void caseTOrderedset(TOrderedset tOrderedset) {
1059: }
1060:
1061: public void caseTPackage(TPackage tPackage) {
1062: }
1063:
1064: public void caseTPost(TPost tPost) {
1065: }
1066:
1067: public void caseTPre(TPre tPre) {
1068: }
1069:
1070: public void caseTArrow(TArrow tArrow) {
1071: }
1072:
1073: public void caseTIf(TIf tIf) {
1074: write("if");
1075: }
1076:
1077: public void caseTElse(TElse tElse) {
1078: write("else");
1079: }
1080:
1081: public void caseTThen(TThen tThen) {
1082: }
1083:
1084: public void caseTRange(TRange tRange) {
1085: }
1086:
1087: public void caseTReal(TReal tReal) {
1088: write(tReal.getText());
1089: }
1090:
1091: public void caseTComma(TComma tComma) {
1092: write(", ");
1093: }
1094:
1095: public void caseTDot(TDot tDot) {
1096: write(".");
1097: }
1098:
1099: public void caseTSemicolon(TSemicolon tSemicolon) {
1100: }
1101:
1102: public void caseTUnknown(TUnknown tUnknown) {
1103: }
1104:
1105: public void caseTScopeOperator(TScopeOperator tScopeOperator) {
1106: write(".");
1107: }
1108:
1109: public void caseTSequence(TSequence tSequence) {
1110: }
1111:
1112: public void caseTSet(TSet tSet) {
1113: }
1114:
1115: /**
1116: * @todo: this method very naively replaces every single quote by a double quote, this should be updated
1117: */
1118: public void caseTStringLit(TStringLit tStringLit) {
1119: final StringBuffer buffer = new StringBuffer(tStringLit
1120: .getText().replace('\'', '\"'));
1121: write(buffer);
1122: }
1123:
1124: public void caseTTab(TTab tTab) {
1125: }
1126:
1127: public void caseTTuple(TTuple tTuple) {
1128: }
1129:
1130: public void caseTTupletype(TTupletype tTupletype) {
1131: }
1132:
1133: private final Stack translationLayers = new Stack();
1134:
1135: /**
1136: * Contains Boolean.TRUE on the top when the most recent property call was an arrow property call, contains
1137: * Boolean.FALSE otherwise.
1138: */
1139: private final Stack arrowPropertyCallStack = new Stack();
1140:
1141: /**
1142: * This stack contains elements implementing the Map interface. For each definition of variables a new Map element
1143: * will be pushed onto the stack. This element contains the variables defined in the definition.
1144: * <p/>
1145: * The keys and values contained in the Map are the names of the variables only (String instances).
1146: */
1147: private final Stack letVariableStack = new Stack();
1148:
1149: private void write(Object object) {
1150: appendToTranslationLayer(String.valueOf(object));
1151: }
1152:
1153: private StringBuffer newTranslationLayer() {
1154: return (StringBuffer) translationLayers
1155: .push(new StringBuffer());
1156: }
1157:
1158: private StringBuffer appendToTranslationLayer(Object appendix) {
1159: return ((StringBuffer) translationLayers.peek())
1160: .append(appendix);
1161: }
1162:
1163: private StringBuffer prependToTranslationLayer(Object appendix) {
1164: return ((StringBuffer) translationLayers.peek()).insert(0,
1165: appendix);
1166: }
1167:
1168: private StringBuffer mergeTranslationLayerAfter() {
1169: StringBuffer newTop = null;
1170:
1171: if (translationLayers.size() > 1) {
1172: newTop = appendToTranslationLayer(translationLayers.pop());
1173: }
1174:
1175: return newTop;
1176: }
1177:
1178: private StringBuffer mergeTranslationLayerBefore() {
1179: StringBuffer newTop = null;
1180:
1181: if (translationLayers.size() > 1) {
1182: newTop = prependToTranslationLayer(translationLayers.pop());
1183: }
1184:
1185: return newTop;
1186: }
1187:
1188: private StringBuffer mergeTranslationLayers() {
1189: while (mergeTranslationLayerAfter() != null)
1190: ;
1191: return (StringBuffer) translationLayers.peek();
1192: }
1193:
1194: private String getDeclaredLetVariableValue(String variableName) {
1195: for (final Iterator iterator = letVariableStack.iterator(); iterator
1196: .hasNext();) {
1197: Map variableMap = (Map) iterator.next();
1198: if (variableMap.containsKey(variableName)) {
1199: return (String) variableMap.get(variableName);
1200: }
1201: }
1202: return null;
1203: }
1204:
1205: private void newLetVariableContext() {
1206: letVariableStack.push(new HashMap(4));
1207: }
1208:
1209: private void dropLetVariableContext() {
1210: if (!letVariableStack.isEmpty())
1211: letVariableStack.pop();
1212: }
1213:
1214: private void addLetVariableToContext(String variableName,
1215: String variableValue) {
1216: ((Map) letVariableStack.peek())
1217: .put(variableName, variableValue);
1218: }
1219:
1220: /**
1221: * Gets the current context element as a {@link org.andromda.uml.metafacades.ModelElementFacade}.
1222: *
1223: * @return the context element as a model element facade
1224: */
1225: private ModelElementFacade getModelElement() {
1226: return (ModelElementFacade) this .getContextElement();
1227: }
1228:
1229: public ValidationJavaTranslator() {
1230: arrowPropertyCallStack.push(Boolean.FALSE);
1231: }
1232:
1233: /**
1234: * The name of the context element within the translated expression.
1235: */
1236: private static final String CONTEXT_ELEMENT_NAME = "contextElement";
1237:
1238: /**
1239: * The prefix for calling the OCLIntrospector to invoke a property or method on an element.
1240: */
1241: private static final String OCL_INTROSPECTOR_INVOKE_PREFIX = OCL_TRANSLATOR_PACKAGE
1242: + ".OCLIntrospector.invoke(";
1243:
1244: /**
1245: * The prefix for converting expressions to boolean expressions
1246: */
1247: private static final String BOOLEAN_WRAP_PREFIX = "Boolean.valueOf(String.valueOf(";
1248:
1249: /**
1250: * The suffix for converting expressions to boolean expressions;
1251: */
1252: private static final String BOOLEAN_WRAP_SUFFIX = ")).booleanValue()";
1253:
1254: /**
1255: * We need to wrap every expression with a converter so that any expressions that return just objects are converted
1256: * to boolean values.
1257: *
1258: * @see org.andromda.translation.ocl.BaseTranslator#postProcess()
1259: */
1260: public void postProcess() {
1261: this .getExpression().insertInTranslatedExpression(0,
1262: OCL_TRANSLATOR_PACKAGE + ".OCLResultEnsurer.ensure(");
1263: this .getExpression().insertInTranslatedExpression(0,
1264: "boolean constraintValid = ");
1265: this .getExpression().insertInTranslatedExpression(
1266: 0,
1267: "final java.lang.Object " + CONTEXT_ELEMENT_NAME
1268: + " = this; ");
1269: this .getExpression().appendToTranslatedExpression(");");
1270: }
1271: }
|