package gov.nasa.jpl.mbee.mdk.ocl.actions; import com.nomagic.magicdraw.actions.MDAction; import com.nomagic.magicdraw.core.Application; import com.nomagic.magicdraw.uml.BaseElement; import com.nomagic.uml2.ext.magicdraw.base.ModelObject; import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Element; import gov.nasa.jpl.mbee.mdk.emf.EmfUtils; import gov.nasa.jpl.mbee.mdk.ocl.OclEvaluator; import gov.nasa.jpl.mbee.mdk.ocl.ui.OclQueryDialog; import gov.nasa.jpl.mbee.mdk.ocl.ui.RepeatInputComboBoxDialog; import gov.nasa.jpl.mbee.mdk.util.Debug; import gov.nasa.jpl.mbee.mdk.util.MoreToString; import gov.nasa.jpl.mbee.mdk.util.Utils2; import org.eclipse.emf.ecore.EObject; import org.eclipse.ocl.ParserException; import java.awt.*; import java.awt.event.ActionEvent; import java.util.*; import java.util.List; public class OclQueryAction extends MDAction { public static final String DEFAULT_ID = "OclQuery"; public static final String actionText = "Run OCL Query"; protected String lastInput = ""; protected LinkedList<String> inputHistory = new LinkedList<String>(); protected TreeSet<String> pastInputs = new TreeSet<String>(); protected LinkedList<String> choices = new LinkedList<String>(); //protected static boolean selectionInDiagram = true; public static OclQueryDialog dialog = null; public static boolean useNewOclEvaluator = true; public OclQueryAction() { super(DEFAULT_ID, actionText, null, null); } public static class ProcessOclQuery implements RepeatInputComboBoxDialog.Processor { private Object context; public List<String> choiceStrings = new LinkedList<String>(); protected Object completionSource; public ProcessOclQuery(Object context) { super(); setContext(context); } @Deprecated public Object parseAndProcess(Object input, Object context) { setContext(context); return parseAndProcess(input); } public Object getContext() { return context; } public void setContext(final Object context) { this.context = context; } /*public void setContext(Collection<Element> context) { getContext().clear(); getContext().addAll(context); }*/ /** * Parse and evaluate OCL and additional helper syntax: * <ol> * <li>Parse as OCL. * <li>If the parse succeeds, return the result. * <li>Get the evaluation result up to the point where parse failed. * <li>Try to parse using additional syntax where the parse failed in * the context of the result from the previous step. * <li>If the parse fails, return with failure. * <li>If there is more to parse, continue at step 1 in the context of * the new result with the remaining unparsed input string. * <li>TODO -- How does this work when expressions are nested and not * just chained? * </ol> * * @param input * @return the result of the evaluation of the input expression */ @Deprecated public Object parseAndProcess(Object input) { final String oclString = input != null ? input.toString() : null; Object result = null; OclEvaluator evaluator = null; // OCLSyntaxHelper syntaxHelper = null; try { //result = OclEvaluator.evaluateQuery(contextEObject, oclString, true); result = OclEvaluator.evaluateQuery(input, oclString, true); evaluator = OclEvaluator.instance; } catch (ParserException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (evaluator == null || !evaluator.isValid() || Utils2.isNullOrEmpty(result)) { result = null; } // If the parse succeeds, return the result. /*if (evaluator != null && ( evaluator.isValid() || !Utils2.isNullOrEmpty( result ) ) ) { return result; } else { outputList.add(null); }*/ // Get the evaluation result up to the point where parse // failed. //syntaxHelper = new OCLSyntaxHelper(evaluator.getOcl().getEnvironment()); //List completions = syntaxHelper.getSyntaxHelp(ConstraintKind.INVARIANT, oclString); //Debug.outln("completions = " + completions); return result; } /*@SuppressWarnings("rawtypes") public Object parseAndProcess(Object input) { String oclString = input == null ? null : input.toString(); //ArrayList<Object> outputList = new ArrayList<Object>(); //ArrayList<Object> localContext = new ArrayList<Object>(); //localContext.addAll(getContext()); OCLSyntaxHelper syntaxHelper = null; // // TODO -- How does this work when expressions are nested and not // just chained? // // Parse as OCL. EObject contextEObject = null; // for ( Object o : localContext ) { // if ( o instanceof Element ) { // contextEObject = (EObject)o; // break; // } // } // if ( contextEObject == null ) { if (context instanceof Collection) { final List<Object> outputList = new ArrayList<Object>(); for (Object o: (Collection) context) { //if (o instanceof EObject) { //contextEObject = (EObject) o; // break; // } // } // } Object result = null; OclEvaluator evaluator = null; try { //result = OclEvaluator.evaluateQuery(contextEObject, oclString, true); result = OclEvaluator.evaluateQuery(o, oclString, true); evaluator = OclEvaluator.instance; } catch (ParserException e) { // TODO Auto-generated catch block e.printStackTrace(); } // If the parse succeeds, return the result. if (evaluator != null && ( evaluator.isValid() || !Utils2.isNullOrEmpty( result ) ) ) { // return result; outputList.add(result); } else { outputList.add(null); } // Get the evaluation result up to the point where parse // failed. syntaxHelper = new OCLSyntaxHelper(evaluator.getOcl().getEnvironment()); List completions = syntaxHelper.getSyntaxHelp(ConstraintKind.INVARIANT, oclString); Debug.outln("completions = " + completions); // Try to parse using additional syntax where the parse // failed in the // context of the result from the previous step. // TODO // If the parse fails, return with failure. // TODO // If there is more to parse, continue at step 1 in the // context of the // new result with the remaining unparsed input string. // TODO // try to parse as OCL // TODO } } return OclEvaluator.evaluateQuery(input, oclString, true); }*/ // ALTERNATIVE APPROACH // Try to parse in pieces using "." and "->" as delimiters. /*if (syntaxHelper == null) { int pos = 0; boolean found = true; while (!found) { int nextPos1 = oclString.indexOf('.', pos); int nextPos2 = oclString.indexOf("->", pos); // TODO -- HERE!! } }*/ // String[] split = oclString.split( "[.]|([-][>])" ); // ArrayList<String> tokens = new ArrayList< String >(); // int insideExpression = 0; // for ( String s : split ) { // if (Utils2.count("\"", s) % 2 != insideExpression ) { // // expression is still open // } // } //return outputList; //} public static String getTypeName(Object o) { if (o == null) { return null; } EObject eo = (EObject) (o instanceof EObject ? o : null); Class<?> c = (eo != null ? EmfUtils.getType(eo) : o.getClass()); if (c == null) { return null; } String type = c.getSimpleName(); return type; } public ArrayList<Object> process(Element elem, String oclString) { ArrayList<Object> outputList = new ArrayList<Object>(); Object result = null; String output = null; OclEvaluator evaluator = null; try { if (elem == null) { return null; } completionSource = elem; result = OclEvaluator.evaluateQuery(elem, oclString, true); if (result instanceof EObject) { completionSource = result; // TODO -- what if the result is a collection? } evaluator = OclEvaluator.instance; output = toString(result); if (!evaluator.isValid() && Utils2.isNullOrEmpty(result)) { output = output + "\nOclInvalid\nThis may be the result of a problem with a shortcut/blackbox function."; } output = output + " : " + getTypeName(result); Debug.outln("evaluated \"" + oclString + "\" for element " + toString(elem) + "\n" + " result = " + output + "\n"); outputList.add(output); } catch (Exception ex) { String errorMsg = ex.getLocalizedMessage(); outputList.add("Error: " + errorMsg); errorMsg = errorMsg + " for OCL query \"" + OclEvaluator.queryObjectToStringExpression(oclString) + "\" on " + EmfUtils.toString(elem); Debug.error(false, false, errorMsg); } choiceStrings.clear(); if (evaluator != null) { choiceStrings.addAll(evaluator.commandCompletionChoiceStrings(OclEvaluator.instance.getHelper(), completionSource, oclString)); Collections.sort(choiceStrings, new Comparator<String>() { @Override public int compare(String o1, String o2) { if (o1 == o2) { return 0; } if (o1 == null) { return -1; } if (o2 == null) { return 1; } o1 = o1.replaceFirst("[^A-Za-z0-9]*", ""); o2 = o2.replaceFirst("[^A-Za-z0-9]*", ""); return o1.compareTo(o2); } }); System.out.println("completionSource: " + completionSource); Debug.outln("CS: " + choiceStrings.toString()); } return outputList; } @Override public Object process(Object input) { final String oclString = input != null ? input.toString() : null; Object result = null; try { result = OclEvaluator.evaluateQuery(getContext(), oclString, true); } catch (Exception e) { String errorMsg = e.getLocalizedMessage(); //outputList.add("Error: " + errorMsg); errorMsg = "Error: \"" + errorMsg + "\" for OCL query \"" + OclEvaluator.queryObjectToStringExpression(oclString) + "\" on " + EmfUtils.toString(context); Debug.error(false, false, errorMsg); // TODO Auto-generated catch block //e.printStackTrace(); choiceStrings.clear(); choiceStrings.add(errorMsg); return null; } //System.out.println("GETTING HERE"); completionSource = result; choiceStrings.clear(); if (OclEvaluator.instance != null) { choiceStrings.addAll(OclEvaluator.instance.commandCompletionChoiceStrings(OclEvaluator.instance.getHelper(), completionSource, oclString)); Collections.sort(choiceStrings, new Comparator<String>() { @Override public int compare(String o1, String o2) { if (o1 == o2) { return 0; } if (o1 == null) { return -1; } if (o2 == null) { return 1; } o1 = o1.replaceFirst("[^A-Za-z0-9]*", ""); o2 = o2.replaceFirst("[^A-Za-z0-9]*", ""); return o1.compareTo(o2); } }); //System.out.println("CS: " + choiceStrings); Debug.outln(choiceStrings.toString()); } return result; /*final String oclString = input == null ? null : input.toString(); //ArrayList<Object> outputList = new ArrayList<Object>(); // outputList = parseAndProcess( input ); // if ( Utils2.isNullOrEmpty( outputList ) ) { // outputList = new ArrayList< Object >(); // } else { // return outputList; // } // Ensure user-defined shortcut functions are updated OclEvaluator.resetEnvironment(); if (Utils2.isNullOrEmpty(getContext())) { outputList = process(null, oclString); } else for (Element elem: getContext()) { ArrayList<Object> results = process(elem, oclString); if (results != null) outputList.addAll(results); } if (outputList != null && outputList.size() == 1) return outputList.get(0); return outputList;*/ } public static String toString(Object result) { String s = null; if (result instanceof Collection) { StringBuffer sb = new StringBuffer(); sb.append(MoreToString.Helper.toString(result)); s = sb.toString(); } if (Utils2.isNullOrEmpty(s) && result instanceof BaseElement) { BaseElement be = ((BaseElement) result); s = be.getHumanName(); } if (Utils2.isNullOrEmpty(s) && result instanceof ModelObject) { s = ((ModelObject) result).get_representationText(); } if (Utils2.isNullOrEmpty(s) && result != null) { s = result.toString(); } if (s == null) { s = "null"; } return s; } @Override public List<String> getCompletionChoices() { return choiceStrings; } @Override public Object getSourceOfCompletion() { // TODO Auto-generated method stub return completionSource; } } @Override public void actionPerformed(ActionEvent e) { // Ensure user-defined shortcut functions are updated Window owner = null; try { owner = Application.getInstance().getMainFrame(); } catch (Throwable t) { t.printStackTrace(); } try { if (dialog == null) { dialog = new OclQueryDialog(owner, "OCL Evaluation"); } dialog.diagramCB.setSelected(true); dialog.browserCB.setSelected(true); dialog.getEditableListPanel().setResult(""); dialog.setVisible(true); } catch (Throwable t) { t.printStackTrace(); } } }