package gov.nasa.jpl.mbee.mdk.ocl; import com.nomagic.magicdraw.core.Application; import com.nomagic.magicdraw.uml.BaseElement; import com.nomagic.uml2.ext.jmi.helpers.StereotypesHelper; import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Class; import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.*; import gov.nasa.jpl.mbee.mdk.api.ElementFinder; import gov.nasa.jpl.mbee.mdk.api.incubating.convert.Converters; import gov.nasa.jpl.mbee.mdk.docgen.DocGenProfile; import gov.nasa.jpl.mbee.mdk.docgen.DocGenUtils; import gov.nasa.jpl.mbee.mdk.generator.DocumentGenerator; import gov.nasa.jpl.mbee.mdk.generator.DocumentValidator; import gov.nasa.jpl.mbee.mdk.generator.ViewParser; import gov.nasa.jpl.mbee.mdk.util.Debug; import gov.nasa.jpl.mbee.mdk.util.GeneratorUtils; import gov.nasa.jpl.mbee.mdk.util.Utils; import gov.nasa.jpl.mbee.mdk.util.Utils2; import gov.nasa.jpl.mbee.mdk.ocl.GetCallOperation.CallReturnType; import lpg.runtime.ParseTable; import org.eclipse.emf.common.util.BasicDiagnostic; import org.eclipse.emf.common.util.Diagnostic; import org.eclipse.emf.ecore.*; import org.eclipse.ocl.OCL; import org.eclipse.ocl.ParserException; import org.eclipse.ocl.Query; import org.eclipse.ocl.ecore.Constraint; import org.eclipse.ocl.ecore.internal.OCLStandardLibraryImpl; import org.eclipse.ocl.expressions.OCLExpression; import org.eclipse.ocl.helper.Choice; import org.eclipse.ocl.helper.ConstraintKind; import org.eclipse.ocl.helper.OCLHelper; import org.eclipse.ocl.lpg.AbstractLexer; import org.eclipse.ocl.lpg.AbstractParser; import org.eclipse.ocl.lpg.ProblemHandler; import org.eclipse.ocl.util.OCLUtil; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Utility class for encapsulating the OCL query and constraint evaluations. * Note that OCL query subsumes constraints, so evaluateQuery can always be * called in place of checkConstraint. * <p> * Here's an example of how to use OclEvaluator, setting up the environment as * well // create custom environment facto DocGenEnvironmentFactory envFactory = new * DocGenEnvironmentFactory(); * <p> * // create custom operation DocGenOperationInstance doi = new * DocGenOperationInstance(); doi.setName("regexMatch"); * doi.setAnnotationName("DocGenEnvironment"); EParameter parm = * EcoreFactory.eINSTANCE.createEParameter(); parm.setName("pattern"); * doi.addParameter(parm); * <p> * // essentially set the actual operation as function pointer * doi.setOperation(new CallOperation() { * * @author cinyoung * @Override public Object callOperation(Object source, Object[] args) { Pattern * pattern = Pattern.compile((String) args[0]); Matcher matcher = * pattern.matcher((String) source); * <p> * return matcher.matches() ? matcher.group() : null; } }); * <p> * // add custom operation to environment and evaluation environment * envFactory.getDgEnvironment().addDgOperation(doi); * envFactory.getDgEvaluationEnvironment().addDgOperation(doi); * <p> * // create the ocl evaluator * OclEvaluator.createOclInstance(envFactory); * <p> * // create query and evaluate String oclquery = * "name.regexMatch('DocGen Templating') <> null"; Object result = * OclEvaluator.evaluateQuery(rootEObject, oclquery, verbose); * <p> * <p> * TODO: Need to expand so it can handle multiple contexts */ public class OclEvaluator { public static OclEvaluator instance = null; public enum QueryStatus { PARSE_EXCEPTION, VALID_OCL, INVALID_OCL, NO_QUERY } private OCL<?, EClassifier, ?, ?, ?, ?, ?, ?, ?, Constraint, EClass, EObject> ocl; private QueryStatus queryStatus = QueryStatus.NO_QUERY; public static boolean isVerboseDefault = Debug.isOn(); protected BasicDiagnostic basicDiagnostic = null; protected OCLHelper<EClassifier, ?, ?, Constraint> helper = null; private ProblemHandler problemHandler = null; protected DocGenEnvironmentFactory environmentFactory = new DocGenEnvironmentFactory(); public String errorMessage = ""; // public static Set< DocGenOperationInstance > opsCache = null; // public static boolean useCachedOps = true; public OclEvaluator() { // make sure order is correct getEnvironmentFactory(); getEnvironment(); getOcl(); } public void createOclInstance(DocGenEnvironmentFactory envFactory) { ocl = OCL.newInstance(envFactory); } protected static boolean notNullOrEndInQuestion(String expr) { return (!Utils2.isNullOrEmpty(expr) && expr.trim().lastIndexOf("?") < expr.trim().length() - 1); } public static String queryElementToStringExpression(Element query) { String expr = null; Object o = GeneratorUtils.getObjectProperty(query, DocGenProfile.expressionChoosable, "expression", null); expr = queryObjectToStringExpression(o); if (notNullOrEndInQuestion(expr)) { return expr; } if (query instanceof com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Constraint) { com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Constraint c = (com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Constraint) query; ValueSpecification v = c.getSpecification(); if (v != null) { expr = DocGenUtils.fixString(v); if (notNullOrEndInQuestion(expr)) { return expr; } expr = v.get_representationText(); if (notNullOrEndInQuestion(expr)) { return expr; } expr = v.toString(); if (notNullOrEndInQuestion(expr)) { return expr; } if (v instanceof OpaqueExpression) { OpaqueExpression oe = (OpaqueExpression) v; List<String> list = oe.getBody(); if (!Utils2.isNullOrEmpty(list)) { expr = queryCollectionToStringExpression(list); if (notNullOrEndInQuestion(expr)) { return expr; } } Expression x = oe.getExpression(); if (x != null) { expr = x.get_representationText(); if (notNullOrEndInQuestion(expr)) { return expr; } expr = x.toString(); if (notNullOrEndInQuestion(expr)) { return expr; } } } } } else { // Assume it's a LiteralString or some other value expression. expr = DocGenUtils.fixString(query, false); } return expr; } public static String queryCollectionToStringExpression(Collection<?> queryColl) { StringBuffer sb = new StringBuffer(); boolean first = true; for (Object q : queryColl) { if (first) { first = false; } else { sb.append(" and "); } String expr = queryObjectToStringExpression(q); sb.append("(" + expr + ")"); // REVIEW -- Do parentheses work??! } String exprString = sb.toString(); return exprString; } /** * @param query * @return */ public static String queryObjectToStringExpression(Object query) { if (query == null) { return null; } String exprString = null; if (query instanceof Element) { Element element = (Element) query; exprString = queryElementToStringExpression(element); } else if (query instanceof String) { exprString = (String) query; } else if (query instanceof Collection) { Collection<?> queryColl = (Collection<?>) query; exprString = queryCollectionToStringExpression(queryColl); } else if (query != null) { exprString = query.toString(); } return exprString; } /** * Evaluates the specified query given a particular context * * @param context EObject of the context that the query should be run against * (e.g., self) * @param query object to convert to a valid OCL string to be evaluated in the * context * @return Object of the result whose type should be known by the caller * @throws ParserException */ public static Object evaluateQuery(Object context, Object query) throws ParserException { // public static Object evaluateQuery(EObject context, Object query) { return evaluateQuery(context, queryObjectToStringExpression(query)); } /** * Evaluates the specified query given a particular context * * @param context EObject of the context that the query should be run against * (e.g., self) * @param queryString Valid OCL string that to be evaluated in the context * @return Object of the result whose type should be known by the caller * @throws ParserException */ public static Object evaluateQuery(Object context, String queryString) throws ParserException { // public static Object evaluateQuery(EObject context, String // queryString) { return evaluateQuery(context, queryString, isVerboseDefault); } public Object evaluateQueryNoSetup(Object context, String queryString, boolean verbose) throws ParserException { Object result = null; OCLExpression<EClassifier> query = null; try { query = getHelper().createQuery(queryString); } catch (ParserException e) { queryStatus = QueryStatus.PARSE_EXCEPTION; if (verbose) { e.printStackTrace(); Debug.outln("my diag = " + getBasicDiagnostic()); //Object analyzer = getBasicDiagnostic().getData().get(0); //Debug.outln("analyzer = " + analyzer); Debug.outln("ProblemHandler = " + getProblemHandler()); if (getProblemHandler() != null) { int offset = getProblemHandler().getErrorReportLineOffset(); Debug.outln("getErrorReportLineOffset() = " + offset); this.errorMessage = Utils2.toString(ProblemHandler.ERROR_MESSAGES); Debug.outln("Error messages = " + errorMessage); AbstractParser parser = getProblemHandler().getParser(); Debug.outln("parser = " + parser); if (parser != null) { ParseTable pt = parser.getParseTable(); Debug.outln("ParseTable = " + pt); AbstractLexer lexer = parser.getLexer(); Debug.outln("lexer = " + lexer); if (lexer != null) { pt = lexer.getParseTable(); Debug.outln("lexer ParseTable = " + pt); } } } } // if ( !this.errorMessage.matches( ".*[A-Za-z]" ) ) { this.errorMessage = e.getLocalizedMessage(); // } throw e;// new ParserException( getBasicDiagnostic() ); } catch (NullPointerException ignored) { } result = getOcl().evaluate(context, query); System.out.println(query.toString()); if (getOcl().isInvalid(result)) { queryStatus = QueryStatus.INVALID_OCL; } return result; } /** * Evaluates the specified query given a particular context * * @param context EObject of the context that the query should be run against * (e.g., self) * @param queryString Valid OCL string that to be evaluated in the context * @param verbose Turns on OCL debugging if true, off if false * @return Object of the result whose type should be known by the caller * @throws ParserException */ public static Object evaluateQuery(Object context, String queryString, boolean verbose) throws ParserException { OclEvaluator ev = new OclEvaluator(); instance = ev; ev.setupEnvironment(); if (queryString == null) { return null; } // create the ocl evaluator // boolean wasOn = Debug.isOn(); Debug.turnOn(); verbose = true; ev.setOclTracingEnabled(verbose); ev.queryStatus = QueryStatus.VALID_OCL; if (context == null) { ev.getHelper().setContext(OCLStandardLibraryImpl.INSTANCE.getOclVoid()); } else if (context instanceof EObject) { ev.getHelper().setContext(((EObject) context).eClass()); } else if (context instanceof Collection) { ev.getHelper().setContext(OCLStandardLibraryImpl.INSTANCE.getSequence()); } Object result = null; ev.basicDiagnostic = null; ev.problemHandler = null; result = ev.evaluateQueryNoSetup(context, queryString, verbose); Debug.outln("evaluateQuery(context=" + DocGenUtils.fixString(context) + ", queryString=" + queryString + ", verbose=" + verbose + ") = " + DocGenUtils.fixString(result)); // if ( !wasOn ) Debug.turnOff(); return result; } public static List<GetCallOperation> addOperation(String[] names, EClassifier callerType, EClassifier returnType, EClassifier parmType, String parmName, boolean zeroArgToo, boolean singularNameReturnsOnlyOne, CallReturnType opType, DocGenEnvironmentFactory envFactory) { // GetCallOperation op = new GetCallOperation(); // op.resultType = opType; ArrayList<GetCallOperation> ops = new ArrayList<GetCallOperation>(); GetCallOperation op = null; boolean someEndWithS = false; boolean notAllEndWithS = false; if (singularNameReturnsOnlyOne) { for (String name : names) { if (!Utils2.isNullOrEmpty(name)) { if (name.trim().substring(name.length() - 1).toLowerCase().equals("s")) { someEndWithS = true; } else { notAllEndWithS = true; } } } } for (String name : names) { op = new GetCallOperation(); op.resultType = opType; boolean endsWithS = false; boolean oneChar = false; // Create the one parameter for the operation EParameter parm = EcoreFactory.eINSTANCE.createEParameter(); parm.setName(parmName); parm.setEType(parmType); if (someEndWithS && notAllEndWithS) { oneChar = name.trim().length() == 1; endsWithS = name.trim().substring(name.length() - 1).toLowerCase().equals("s"); if (endsWithS || oneChar) { op.onlyOneForAll = false; op.onlyOnePer = false; } else { op.onlyOneForAll = false; op.onlyOnePer = true; } } // Create the one-parameter operation DocGenOperationInstance.addOperation(name, "DocGenEnvironment", envFactory, callerType, returnType, op, parm); ops.add(op); if (zeroArgToo) { // Create the zero-parameter operation op = new GetCallOperation(); op.resultType = opType; if (singularNameReturnsOnlyOne && someEndWithS && notAllEndWithS) { if (endsWithS || oneChar) { op.onlyOneForAll = false; op.onlyOnePer = false; } else { op.onlyOneForAll = false; op.onlyOnePer = true; } } DocGenOperationInstance.addOperation(name, "DocGenEnvironment", envFactory, callerType, returnType, op); ops.add(op); } } return ops; } protected static void addRegexMatchOperation(DocGenEnvironmentFactory envFactory) { // create custom operation DocGenOperationInstance doi = new DocGenOperationInstance(); doi.setName("regexMatch"); doi.setAnnotationName("DocGenEnvironment"); EParameter parm = EcoreFactory.eINSTANCE.createEParameter(); parm.setName("pattern"); doi.addStringParameter(parm); doi.setCallerType(OCLStandardLibraryImpl.INSTANCE.getString()); doi.setReturnType(OCLStandardLibraryImpl.INSTANCE.getString()); // essentially set the actual operation as function pointer doi.setOperation(new CallOperation() { @Override public Object callOperation(Object source, Object[] args) { Pattern pattern = Pattern.compile((String) args[0], Pattern.MULTILINE | Pattern.DOTALL); Matcher matcher = pattern.matcher((String) source); return matcher.matches() ? matcher.group() : null; } }); // add custom operation to environment and evaluation environment envFactory.getDgEnvironment().addDgOperation(doi); envFactory.getDgEvaluationEnvironment().addDgOperation(doi); } protected static void addLogOperation(DocGenEnvironmentFactory envFactory, boolean addArg, boolean addColorArg) { addLogOperation(envFactory, addArg, addColorArg, false, false); addLogOperation(envFactory, addArg, addColorArg, true, false); addLogOperation(envFactory, addArg, addColorArg, false, true); addLogOperation(envFactory, addArg, addColorArg, true, true); } protected static void addLogOperation(DocGenEnvironmentFactory envFactory, boolean addArg, boolean addColorArg, boolean asSequence, boolean fromSequence) { // Are these last two args helpful??? // create custom operation DocGenOperationInstance doi = new DocGenOperationInstance(); doi.setName("log");// + (asSequence ? "S" : "" ) + (fromSequence ? "F" : "") ); doi.setAnnotationName("DocGenEnvironment"); if (addArg) { EParameter parm = EcoreFactory.eINSTANCE.createEParameter(); parm.setName("toLog"); if (asSequence) { doi.addParameter(parm, OCLStandardLibraryImpl.INSTANCE.getSequence()); } else { doi.addParameter(parm, OCLStandardLibraryImpl.INSTANCE.getOclAny()); } if (addColorArg) { parm = EcoreFactory.eINSTANCE.createEParameter(); parm.setName("color"); doi.addParameter(parm, OCLStandardLibraryImpl.INSTANCE.getOclAny()); } } if (fromSequence) { doi.setCallerType(OCLStandardLibraryImpl.INSTANCE.getOclAny()); } else { doi.setCallerType(OCLStandardLibraryImpl.INSTANCE.getSequence()); } doi.setReturnType(OCLStandardLibraryImpl.INSTANCE.getOclAny()); // essentially set the actual operation as function pointer doi.setOperation(new CallOperation() { @Override public Object callOperation(Object source, Object[] args) { if (args != null && args.length > 0) { Object o = args[0]; Object colorObj = null; if (args.length >= 2) { colorObj = args[1]; } else if (source != null && Utils.isColor(o)) { colorObj = args[0]; o = source; } Utils.log(o, colorObj); } else if (source != null) { Utils.log(source); } return source; } }); // add custom operation to environment and evaluation environment envFactory.getDgEnvironment().addDgOperation(doi); envFactory.getDgEvaluationEnvironment().addDgOperation(doi); } protected static void addGetOperation(DocGenEnvironmentFactory envFactory) { // create custom operation DocGenOperationInstance doi = new DocGenOperationInstance(); doi.setName("get"); doi.setAnnotationName("DocGenEnvironment"); EParameter parm = EcoreFactory.eINSTANCE.createEParameter(); parm.setName("nameOrId"); doi.addStringParameter(parm); doi.setCallerType(OCLStandardLibraryImpl.INSTANCE.getOclAny()); doi.setReturnType(OCLStandardLibraryImpl.INSTANCE.getOclAny()); // essentially set the actual operation as function pointer doi.setOperation(new CallOperation() { @Override public Object callOperation(Object source, Object[] args) { if (args.length < 1) { return null; } if (args.length > 1) { return null; } String nameOrId = (String) args[0]; // try id BaseElement e = Converters.getIdToElementConverter() .apply(nameOrId, Application.getInstance().getProject()); if (e != null) { return e; } // try child if (source != null && source instanceof Element) { e = ElementFinder.findOwnedElementByName((Element) source, nameOrId); if (e != null) { return e; } } // try qualified name //TODO @donbot verify that this usage can be removed, or that it's harmless to restrict search to only primary model e = ElementFinder.getElementByQualifiedName(nameOrId, Application.getInstance().getProject()); if (e != null) { return e; } // try searching everything List<Element> results = Utils.findByName(Application.getInstance().getProject(), nameOrId, true); if (!Utils2.isNullOrEmpty(results)) { return results.get(0); } return null; } }); // add custom operation to environment and evaluation environment envFactory.getDgEnvironment().addDgOperation(doi); envFactory.getDgEvaluationEnvironment().addDgOperation(doi); } protected static void addRunOperation(DocGenEnvironmentFactory envFactory, EClassifier argType) { // create custom operation DocGenOperationInstance doi = new DocGenOperationInstance(); doi.setName("run"); doi.setAnnotationName("DocGenEnvironment"); EParameter parm = EcoreFactory.eINSTANCE.createEParameter(); parm.setName("input"); if (argType != null) { doi.addParameter(parm, argType); doi.setCallerType(OCLStandardLibraryImpl.INSTANCE.getOclAny()); doi.setReturnType(OCLStandardLibraryImpl.INSTANCE.getOclAny()); } // essentially set the actual operation as function pointer doi.setOperation(new CallOperation() { boolean runItemsInCollectionIndividually = true; @Override public Object callOperation(Object source, Object[] args) { if (!(source instanceof Element)) { return null; } Element sourceElement = (Element) source; // If the source is a view, parse it. if (sourceElement instanceof Class && StereotypesHelper.hasStereotypeOrDerived(sourceElement, DocGenProfile.viewStereotype)) { DocumentValidator dv = new DocumentValidator(sourceElement); DocumentGenerator dg = new DocumentGenerator(sourceElement, dv, null); ViewParser vp = new ViewParser(dg, true, true, dg.getDocument(), sourceElement); return vp.parse(); } // Need to parse the behavior of the Viewpoint, not the // Viewpoint itself. if (sourceElement instanceof Class && StereotypesHelper.hasStereotypeOrDerived(sourceElement, DocGenProfile.viewpointStereotype)) { sourceElement = ((Class) sourceElement).getClassifierBehavior(); } Object input = args[0]; // Allow for activity and target input to be reversed. // For example, run may be called as // viewpoint1.run(Sequence{element1, element2}) or as // Sequence{element1, element2}.run(viewpoint1) if (GeneratorUtils.findInitialNode(sourceElement) == null && input instanceof Element && GeneratorUtils.findInitialNode((Element) input) != null) { // Change to run Collection of items together as a single // input since user can use "." or "->" to specify to the Ocl // parser which way to handle it. runItemsInCollectionIndividually = false; // Call with swapped source/input, using the original source // since sourceElement may have been reassigned. return callOperation(input, new Object[]{source}); } List<Object> inputs = new ArrayList<Object>(); if (runItemsInCollectionIndividually && input instanceof Collection) { inputs.addAll((Collection<?>) input); } else { inputs.add(input); } DocumentValidator dv = new DocumentValidator(sourceElement); DocumentGenerator dg = new DocumentGenerator(sourceElement, dv, null); dg.getContext().pushTargets(inputs); Object result = dg.parseActivityOrStructuredNode(sourceElement, dg.getDocument()); return result; } }); // add custom operation to environment and evaluation environment envFactory.getDgEnvironment().addDgOperation(doi); envFactory.getDgEvaluationEnvironment().addDgOperation(doi); } protected static void addEvalOperation(DocGenEnvironmentFactory envFactory, String opName) { // create custom operation DocGenOperationInstance doi = new DocGenOperationInstance(); doi.setName(opName); doi.setAnnotationName("DocGenEnvironment"); EParameter parm = EcoreFactory.eINSTANCE.createEParameter(); parm.setName("expression"); doi.addStringParameter(parm); doi.setCallerType(OCLStandardLibraryImpl.INSTANCE.getOclAny()); doi.setReturnType(OCLStandardLibraryImpl.INSTANCE.getOclAny()); // essentially set the actual operation as function pointer doi.setOperation(new CallOperation() { @Override public Object callOperation(Object source, Object[] args) { String expression = (String) args[0]; Object result = null; try { result = evaluateQuery(source, expression, isVerboseDefault()); } catch (Throwable e) { Debug.error(true, false, e.getLocalizedMessage()); } return result; } }); // add custom operation to environment and evaluation environment envFactory.getDgEnvironment().addDgOperation(doi); envFactory.getDgEvaluationEnvironment().addDgOperation(doi); } protected static void addExpressionOperation(final String opName, final String expression, DocGenEnvironmentFactory envFactory) { // create custom operation DocGenOperationInstance doi = new DocGenOperationInstance(); doi.setName(opName); doi.setAnnotationName("DocGenEnvironment"); // REVIEW -- Can we do better than OclAny? Would it help avoid needing // to add oclAsType() before and after? doi.setCallerType(OCLStandardLibraryImpl.INSTANCE.getOclAny()); doi.setReturnType(OCLStandardLibraryImpl.INSTANCE.getOclAny()); // essentially set the actual operation as function pointer doi.setOperation(new CallOperation() { @Override public Object callOperation(Object source, Object[] args) { Object result = null; try { result = evaluateQuery(source, expression, isVerboseDefault()); } catch (Throwable e) { Debug.error(true, false, e.getLocalizedMessage()); } return result; } }); // add custom operation to environment and evaluation environment envFactory.getDgEnvironment().addDgOperation(doi); envFactory.getDgEvaluationEnvironment().addDgOperation(doi); } // /** // * @param result // * @return an OclCollection // */ // public static Object makeCollectionOcl( Object result ) { // if ( result instanceof Bag ) return result; // if ( result instanceof LinkedHashSet ) return result; // if ( result instanceof ArrayList ) { // result = CollectionUtil.asSequence( (Collection< ? >)result ); // } else if ( result instanceof HashSet ) { // result = CollectionUtil.asSet( (Collection< ? >)result ); // } else if ( result instanceof Collection ) { // result = CollectionUtil.asBag( (Collection< ? >)result ); // } // return result; // } static EClassifier getGenericCallerType() { return OCLStandardLibraryImpl.INSTANCE.getOclAny(); } protected static void addROperation(DocGenEnvironmentFactory envFactory) { EClassifier callerType = getGenericCallerType(); EClassifier returnType = OCLStandardLibraryImpl.INSTANCE.getSequence(); EClassifier stringType = OCLStandardLibraryImpl.INSTANCE.getString(); addOperation(new String[]{"relationship", "relationships", "r"}, callerType, returnType, stringType, "relationship", true, true, CallReturnType.RELATIONSHIP, envFactory); } protected static void addMOperation(DocGenEnvironmentFactory envFactory) { EClassifier callerType = getGenericCallerType(); EClassifier returnType = OCLStandardLibraryImpl.INSTANCE.getSequence(); EClassifier stringType = OCLStandardLibraryImpl.INSTANCE.getString(); addOperation(new String[]{"member", "members", "m"}, callerType, returnType, stringType, "member", true, false, CallReturnType.MEMBER, envFactory); } protected static void addTOperation(DocGenEnvironmentFactory envFactory) { EClassifier callerType = getGenericCallerType(); EClassifier returnType = OCLStandardLibraryImpl.INSTANCE.getSequence(); EClassifier stringType = OCLStandardLibraryImpl.INSTANCE.getString(); addOperation(new String[]{"type", "types", "t"}, callerType, returnType, stringType, "type", true, true, CallReturnType.TYPE, envFactory); } protected static void addVOperation(DocGenEnvironmentFactory envFactory) { EClassifier callerType = getGenericCallerType(); EClassifier returnType = OCLStandardLibraryImpl.INSTANCE.getOclAny(); EClassifier stringType = OCLStandardLibraryImpl.INSTANCE.getString(); addOperation(new String[]{"value", "values", "v"}, callerType, returnType, stringType, "value", true, true, CallReturnType.VALUE, envFactory); } protected static void addDefaultOperation(DocGenEnvironmentFactory envFactory) { EClassifier callerType = getGenericCallerType(); EClassifier returnType = OCLStandardLibraryImpl.INSTANCE.getString(); EClassifier stringType = OCLStandardLibraryImpl.INSTANCE.getString(); addOperation(new String[]{"default"}, callerType, returnType, stringType, "default", true, true, CallReturnType.DEFAULT, envFactory); } protected static void addSOperation(DocGenEnvironmentFactory envFactory) { EClassifier callerType = getGenericCallerType(); EClassifier returnType = OCLStandardLibraryImpl.INSTANCE.getSequence(); EClassifier stringType = OCLStandardLibraryImpl.INSTANCE.getString(); List<GetCallOperation> ops = addOperation(new String[]{"stereotype", "stereotypes", "s"}, callerType, returnType, stringType, "stereotype", true, true, CallReturnType.TYPE, envFactory); for (GetCallOperation op : ops) { op.alwaysFilter = new Object[]{"Stereotype"}; } } /** * Add n(), name(), and names() OCL shortcuts with and without arguments. * * @param envFactory */ protected static void addNOperation(DocGenEnvironmentFactory envFactory) { EClassifier callerType = getGenericCallerType(); EClassifier returnType = OCLStandardLibraryImpl.INSTANCE.getString(); EClassifier stringType = OCLStandardLibraryImpl.INSTANCE.getString(); addOperation(new String[]{"name", "n"}, callerType, returnType, stringType, "name", true, true, CallReturnType.NAME, envFactory); } /** * Add n(), name(), and names() OCL shortcuts with and without arguments. * * @param envFactory */ protected static void addOOperation(DocGenEnvironmentFactory envFactory) { EClassifier callerType = getGenericCallerType(); EClassifier returnType = OCLStandardLibraryImpl.INSTANCE.getSequence(); EClassifier stringType = OCLStandardLibraryImpl.INSTANCE.getString(); addOperation(new String[]{"owner", "owners", "o"}, callerType, returnType, stringType, "owner", true, true, CallReturnType.OWNER, envFactory); } /** * @param exprString an OCL expression * @return an error message if the parse failed; otherwise return null */ public String checkParsable(String exprString) { try { OCLExpression<EClassifier> query = getHelper().createQuery(exprString); if (query == null) { throw new Exception(); } } catch (ParserException e) { return e.getLocalizedMessage(); } catch (Throwable e) { return "query is null: \"" + exprString + "\""; } return null; } /** * Find Expressions in model and add them as blackbox shortcuts. * ExpressionLibraries are retained * * @param envFactory */ protected static void addExpressionOperations(DocGenEnvironmentFactory envFactory) { // add each of the elements with the Expression stereotype as shortcut/blackbox functions List<Element> expressions = StereotypesHelper.getExtendedElements(Utils.getExpressionStereotype(Application.getInstance().getProject())); for (Element expression : expressions) { // function name can't have spaces and strange characters; e.g. the name "four+five" would be parsed as a sum operation. String name = Utils.getName(expression); name = name.replaceAll("[^A-Za-z0-9_]+", ""); String exprString = queryElementToStringExpression(expression); if (!Utils2.isNullOrEmpty(name)) { try { addExpressionOperation(name, exprString, envFactory); } catch (Throwable e) { Debug.error(true, false, "Could not add " + name + " OCL shortcut with expression \"" + exprString + "\". " + e.getLocalizedMessage()); } } } } public DocGenEnvironmentFactory getEnvironmentFactory() { if (environmentFactory == null) { environmentFactory = new DocGenEnvironmentFactory(); } return environmentFactory; } public DocGenEvaluationEnvironment getEvaluationEnvironment() { return getEnvironmentFactory().getDgEvaluationEnvironment(); } public DocGenEnvironment getEnvironment() { return getEnvironmentFactory().getDgEnvironment(); } protected DocGenEnvironmentFactory setupEnvironment() { // set up the customized environment // add custom OCL functions addRegexMatchOperation(getEnvironmentFactory()); addEvalOperation(getEnvironmentFactory(), "eval"); addEvalOperation(getEnvironmentFactory(), "evaluate"); addEvalOperation(getEnvironmentFactory(), "e"); addRunOperation(getEnvironmentFactory(), OCLStandardLibraryImpl.INSTANCE.getOclAny()); addRunOperation(getEnvironmentFactory(), OCLStandardLibraryImpl.INSTANCE.getSequence()); addRunOperation(getEnvironmentFactory(), null); addGetOperation(getEnvironmentFactory()); addLogOperation(getEnvironmentFactory(), true, false); addLogOperation(getEnvironmentFactory(), true, true); addLogOperation(getEnvironmentFactory(), false, false); // add one-letter custom OCL operations addROperation(getEnvironmentFactory()); addMOperation(getEnvironmentFactory()); addTOperation(getEnvironmentFactory()); addSOperation(getEnvironmentFactory()); addNOperation(getEnvironmentFactory()); addOOperation(getEnvironmentFactory()); addVOperation(getEnvironmentFactory()); addDefaultOperation(getEnvironmentFactory()); addExpressionOperations(getEnvironmentFactory()); return getEnvironmentFactory(); } public List<Choice> commandCompletionChoices(OCLHelper<EClassifier, ?, ?, Constraint> helper, Object context, String oclInput) { EClassifier helperContext = null; if (context instanceof EObject) { helperContext = ((EObject) context).eClass(); } else if (context instanceof Collection) { helperContext = OCLStandardLibraryImpl.INSTANCE.getSequence(); } getHelper().setContext(helperContext != null ? helperContext : OCLStandardLibraryImpl.INSTANCE.getOclVoid()); List<Choice> choices = getHelper().getSyntaxHelp(ConstraintKind.INVARIANT, oclInput); Debug.outln("Completion choices for OCL expression \"" + oclInput + "\" = " + choices); return choices; } public List<String> commandCompletionChoiceStrings(OCLHelper<EClassifier, ?, ?, Constraint> helper, Object context, String oclInput, int depth) { Object result = null; try { result = evaluateQuery(context, oclInput, Debug.isOn()); } catch (ParserException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (result == null) { return Collections.emptyList(); } List<Choice> choiceList = commandCompletionChoices(helper, context, oclInput); List<String> newChoiceStringList = new ArrayList<String>(); boolean canExtend = depth > 0; for (Choice c : choiceList) { String newChoiceString = oclInput; if (canExtend) { newChoiceString += "." + c.getName(); } newChoiceStringList.add(newChoiceString); List<String> extensions = null; if (depth > 1) { extensions = commandCompletionChoiceStrings(helper, context, newChoiceString, depth - 1); } canExtend = !Utils2.isNullOrEmpty(extensions); if (!canExtend) { newChoiceStringList.add(newChoiceString); } else { newChoiceStringList.addAll(extensions); } } return newChoiceStringList; } public List<String> commandCompletionChoiceStrings(OCLHelper<EClassifier, ?, ?, Constraint> helper, Object context, String oclInput) { // boolean wasOn = Debug.isOn(); // Debug.turnOn(); List<String> choiceList = new ArrayList<String>(); List<Choice> choices = commandCompletionChoices(helper, context, oclInput); for (Choice next : choices) { choiceList.add(next.getName() + " : " + next.getDescription()); switch (next.getKind()) { case OPERATION: case SIGNAL: // the description is already complete // Debug.outln( next.getDescription() ); // break; case PROPERTY: case ENUMERATION_LITERAL: case VARIABLE: Debug.outln(next.getName() + " : " + next.getDescription()); // choiceList.add( next.getName() ); break; default: // choiceList.add( next.getName() ); Debug.outln(next.getName()); break; } } // if (!wasOn) // Debug.turnOff(); Debug.outln("choices = " + choiceList.toString()); return choiceList; } /** * Evaluates the specified invariant (constraint given a particular context) * <p> * Note that the evaluateQuery is more generic and can handle invariants as * well * * @param context EObject of the context that the constraint should be checked * against * @param constraintString Valid OCL constraint string to be checked * @param verbose Turns on OCL debugging if true, off if false * @return true if constraint is satisfied, false otherwise */ public boolean checkConstraint(EObject context, String constraintString, boolean verbose) { setOclTracingEnabled(verbose); queryStatus = QueryStatus.VALID_OCL; OCLHelper<EClassifier, ?, ?, Constraint> helper = getOcl().createOCLHelper(); helper.setContext(context.eClass()); boolean ok = false; Constraint constraint = null; try { constraint = helper.createInvariant(constraintString); } catch (ParserException e) { queryStatus = QueryStatus.PARSE_EXCEPTION; e.printStackTrace(); return ok; } if (constraint != null) { Query<EClassifier, EClass, EObject> eval = getOcl().createQuery(constraint); ok = eval.check(context); } return ok; } /** * Utility method for toggling OCL tracing/debugging information * * @param verbose true if tracing should be enabled, false otherwise */ private void setOclTracingEnabled(boolean verbose) { getOcl().setEvaluationTracingEnabled(verbose); getOcl().setParseTracingEnabled(verbose); } /** * Returns the query status. * * @return */ public QueryStatus getQueryStatus() { return queryStatus; } /** * Simple rollup function that determines whether a query was executed * properly or not * * @return */ public boolean isValid() { switch (getQueryStatus()) { case VALID_OCL: return true; default: return false; } } public OCL<?, EClassifier, ?, ?, ?, ?, ?, ?, ?, Constraint, EClass, EObject> getOcl() { if (ocl == null) { createOclInstance(getEnvironmentFactory()); } return ocl; } public void setOcl(OCL<?, EClassifier, ?, ?, ?, ?, ?, ?, ?, Constraint, EClass, EObject> ocl) { this.ocl = ocl; } public static boolean isVerboseDefault() { return isVerboseDefault; } public static void setVerboseDefault(boolean isVerboseDefault) { OclEvaluator.isVerboseDefault = isVerboseDefault; } public BasicDiagnostic getBasicDiagnostic() { if (basicDiagnostic == null) { if (getHelper() != null) { Diagnostic diag = getHelper().getProblems(); if (diag instanceof BasicDiagnostic) { setBasicDiagnostic((BasicDiagnostic) diag); } } } return basicDiagnostic; } public void setBasicDiagnostic(BasicDiagnostic basicDiagnostic) { this.basicDiagnostic = basicDiagnostic; } /** * @return */ public ProblemHandler getProblemHandler() { if (problemHandler == null) { if (getOcl() != null) { setProblemHandler(OCLUtil.getAdapter(getOcl().getEnvironment(), ProblemHandler.class)); } } return problemHandler; } /** * @param problemHandler */ public void setProblemHandler(ProblemHandler problemHandler) { this.problemHandler = problemHandler; } public void setQueryStatus(QueryStatus queryStatus) { this.queryStatus = queryStatus; } public OCLHelper<EClassifier, ?, ?, Constraint> getHelper() { if (helper == null) { if (getOcl() != null) { setHelper(getOcl().createOCLHelper()); } } return helper; } public void setHelper(OCLHelper<EClassifier, ?, ?, Constraint> helper) { this.helper = helper; } }