package gov.nasa.jpl.mbee.mdk.generator; import com.nomagic.uml2.ext.jmi.helpers.StereotypesHelper; import com.nomagic.uml2.ext.magicdraw.actions.mdbasicactions.CallBehaviorAction; import com.nomagic.uml2.ext.magicdraw.actions.mdbasicactions.CallOperationAction; import com.nomagic.uml2.ext.magicdraw.activities.mdbasicactivities.ActivityEdge; import com.nomagic.uml2.ext.magicdraw.activities.mdbasicactivities.InitialNode; import com.nomagic.uml2.ext.magicdraw.activities.mdfundamentalactivities.Activity; import com.nomagic.uml2.ext.magicdraw.activities.mdfundamentalactivities.ActivityNode; import com.nomagic.uml2.ext.magicdraw.activities.mdintermediateactivities.DecisionNode; import com.nomagic.uml2.ext.magicdraw.activities.mdintermediateactivities.ForkNode; import com.nomagic.uml2.ext.magicdraw.activities.mdintermediateactivities.JoinNode; import com.nomagic.uml2.ext.magicdraw.activities.mdintermediateactivities.MergeNode; import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.*; import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Class; import com.nomagic.uml2.ext.magicdraw.mdprofiles.Stereotype; import gov.nasa.jpl.mbee.mdk.docgen.DocGenProfile; import gov.nasa.jpl.mbee.mdk.generator.graphs.DirectedEdgeVector; import gov.nasa.jpl.mbee.mdk.generator.graphs.DirectedGraphHashSet; import gov.nasa.jpl.mbee.mdk.generator.graphs.algorithms.TopologicalSort; import gov.nasa.jpl.mbee.mdk.util.GeneratorUtils; import gov.nasa.jpl.mbee.mdk.util.ScriptRunner; import gov.nasa.jpl.mbee.mdk.util.Utils; import gov.nasa.jpl.mbee.mdk.util.Utils2; import org.apache.log4j.Logger; import javax.script.ScriptException; import java.io.PrintWriter; import java.io.StringWriter; import java.util.*; public class CollectFilterParser { private static GenerationContext context; private static Logger log = Logger.getLogger(CollectFilterParser.class); public static void setContext(GenerationContext gc) { context = gc; } public static GenerationContext getContext() { return context; } public static DocumentValidator getValidator() { return context == null ? null : context.getValidator(); } /** * gets a graph of the collect/filter actions that starts from a, evaluates * and executes actions topologically and return result * * @param a * @param in * @param context TODO * @return */ public static List<Element> startCollectAndFilterSequence(ActivityNode a, List<Element> in) { DirectedGraphHashSet<CollectFilterNode, DirectedEdgeVector<CollectFilterNode>> graph = new DirectedGraphHashSet<CollectFilterNode, DirectedEdgeVector<CollectFilterNode>>(); getCollectFilterGraph(a, new HashSet<ActivityNode>(), graph, new HashMap<ActivityNode, CollectFilterNode>()); SortedSet<CollectFilterNode> reverse = (new TopologicalSort()).topological_sort(graph); List<CollectFilterNode> toposort = new ArrayList<CollectFilterNode>(reverse); Collections.reverse(toposort); List<Element> res = in; for (CollectFilterNode node : toposort) { Set<CollectFilterNode> incomings = new HashSet<CollectFilterNode>(); for (DirectedEdgeVector<CollectFilterNode> edge : graph.findEdgesWithTargetVertex(node)) { incomings.add(edge.getSourceVertex()); } Set<List<Element>> ins = new HashSet<List<Element>>(); if (incomings.isEmpty()) { if (in == null) { if (!context.targetsEmpty()) { ins.add(Utils2.asList(context.peekTargets(), Element.class)); } } else { ins.add(in); } } else { for (CollectFilterNode i : incomings) { ins.add(i.getResult()); } } if (node.getNode() instanceof CallBehaviorAction) { if (ins.size() == 1) { res = collectAndFilter((CallBehaviorAction) node.getNode(), ins.iterator().next()); /*System.out.println( "collectAndFilter(): returned (after removing duplicates) res[" + res.size() + "]=" + MoreToString.Helper.toLongString( res ) );*/ node.setResult(res); } else { // ??? res = new ArrayList<Element>(); node.setResult(res); } } else if (node.getNode() instanceof ForkNode) { if (ins.size() == 1) { res = ins.iterator().next(); node.setResult(res); } else { res = new ArrayList<Element>(); node.setResult(res); // ??? } } else if (node.getNode() instanceof MergeNode) { res = Utils.unionOfCollections(ins); node.setResult(res); } else if (node.getNode() instanceof JoinNode) { res = Utils.intersectionOfCollections(ins); node.setResult(res); } else if (node.getNode() instanceof DecisionNode) { res = Utils.xorOfCollections(ins); node.setResult(res); } context.setCurrentNode(node.getNode()); DocumentValidator.evaluateConstraints(node.getNode(), res, context, true, true); } return res; } private static void getCollectFilterGraph(ActivityNode cur, Set<ActivityNode> done, DirectedGraphHashSet<CollectFilterNode, DirectedEdgeVector<CollectFilterNode>> graph, Map<ActivityNode, CollectFilterNode> mapping) { if (done.contains(cur)) { return; } done.add(cur); CollectFilterNode source = mapping.get(cur); if (source == null) { source = new CollectFilterNode(cur); mapping.put(cur, source); } graph.addVertex(source); for (ActivityEdge e : cur.getOutgoing()) { ActivityNode n = e.getTarget(); if (GeneratorUtils.hasStereotypeByString(n, DocGenProfile.collectFilterStereotype, true)) { CollectFilterNode target = mapping.get(n); if (target == null) { target = new CollectFilterNode(n); mapping.put(n, target); } graph.addEdge(source, target); getCollectFilterGraph(n, done, graph, mapping); } } } /** * given in as input, execute collect/filter action and return result * * @param cba * @param in * @return */ @SuppressWarnings("unchecked") private static List<Element> collectAndFilter(CallBehaviorAction cba, List<Element> in) { //System.out.println("collectAndFilter(): cba=" + MoreToString.Helper.toLongString( cba ) ); //System.out.println("collectAndFilter(): in[" + in.size() + "]=" + MoreToString.Helper.toLongString( in ) ); Integer depth = (Integer) GeneratorUtils.getObjectProperty(cba, DocGenProfile.depthChoosable, "depth", 0); int direction = ((Boolean) GeneratorUtils.getObjectProperty(cba, DocGenProfile.directionChoosable, "directionOut", true)) ? 1 : 2; List<Stereotype> stereotypes = (List<Stereotype>) GeneratorUtils.getListProperty(cba, DocGenProfile.stereotypeChoosable, "stereotypes", new ArrayList<Stereotype>()); List<Class> metaclasses = (List<Class>) GeneratorUtils.getListProperty(cba, DocGenProfile.metaclassChoosable, "metaclasses", new ArrayList<Class>()); Boolean derived = (Boolean) GeneratorUtils.getObjectProperty(cba, DocGenProfile.derivedChoosable, "considerDerived", true); List<String> names = (List<String>) GeneratorUtils.getListProperty(cba, DocGenProfile.nameChoosable, "names", new ArrayList<String>()); List<String> diagramTypes = Utils.getElementNames((List<NamedElement>) GeneratorUtils.getListProperty( cba, DocGenProfile.diagramTypeChoosable, "diagramTypes", new ArrayList<NamedElement>())); Boolean include = (Boolean) GeneratorUtils.getObjectProperty(cba, DocGenProfile.includeChoosable, "include", true); List<Property> stereotypeProperties = (List<Property>) GeneratorUtils .getListProperty(cba, DocGenProfile.stereotypePropertyChoosable, "stereotypeProperties", new ArrayList<Property>()); Boolean inherited = (Boolean) GeneratorUtils.getObjectProperty(cba, DocGenProfile.inheritedChoosable, "includeInherited", false); EnumerationLiteral asso = (EnumerationLiteral) GeneratorUtils.getObjectProperty(cba, DocGenProfile.associationChoosable, "associationType", null); String expression = (String) GeneratorUtils.getObjectProperty(cba, DocGenProfile.expressionChoosable, "expression", null); Boolean iterate = (Boolean) GeneratorUtils.getObjectProperty(cba, DocGenProfile.expressionChoosable, "iterate", true); AggregationKind associationType = null; if (asso != null) { if (asso.getName().equals("composite")) { associationType = AggregationKindEnum.COMPOSITE; } else if (asso.getName().equals("none")) { associationType = AggregationKindEnum.NONE; } else { associationType = AggregationKindEnum.SHARED; } } if (associationType == null) { associationType = AggregationKindEnum.COMPOSITE; } List<Element> res = new ArrayList<Element>(); if (GeneratorUtils.hasStereotypeByString(cba, DocGenProfile.collectDiagram)) { for (Element e : in) { if (e instanceof Diagram) { res.addAll(Utils.getElementsOnDiagram((Diagram) e)); } } } else if (GeneratorUtils.hasStereotypeByString(cba, DocGenProfile.collectAssociationStereotype)) { for (Element e : in) { res.addAll(Utils.collectAssociatedElements(e, depth, associationType)); } } else if (GeneratorUtils.hasStereotypeByString(cba, DocGenProfile.collectOwnedElementStereotype)) { for (Element e : in) { res.addAll(Utils.collectOwnedElements(e, depth)); } } else if (GeneratorUtils.hasStereotypeByString(cba, DocGenProfile.collectOwnerStereotype)) { for (Element e : in) { res.addAll(Utils.collectOwners(e, depth)); } } else if (GeneratorUtils.hasStereotypeByString(cba, DocGenProfile.collectRelMetaclassStereotype)) { for (Element e : in) { res.addAll(Utils.collectDirectedRelatedElementsByRelationshipMetaclasses(e, metaclasses, direction, depth)); } } else if (GeneratorUtils.hasStereotypeByString(cba, DocGenProfile.collectRelStereotypeStereotype)) { for (Element e : in) { res.addAll(Utils.collectDirectedRelatedElementsByRelationshipStereotypes(e, stereotypes, direction, derived, depth)); } } else if (GeneratorUtils.hasStereotypeByString(cba, DocGenProfile.collectStereotypePropStereotype)) { List<Object> blah = new ArrayList<Object>(); for (Element e : in) { for (Property p : stereotypeProperties) //blah.addAll(StereotypesHelper.getStereotypePropertyValue(e, (Stereotype)p.getOwner(), p)); { blah.addAll(Utils.collectByStereotypeProperty(e, p)); } } for (Object b : blah) { if (b instanceof Element) { res.add((Element) b); } } } else if (GeneratorUtils.hasStereotypeByString(cba, DocGenProfile.collectTypeStereotype)) { for (Element e : in) { if (e instanceof TypedElement) { if (((TypedElement) e).getType() != null) { res.add(((TypedElement) e).getType()); } } else if (e instanceof CallBehaviorAction && ((CallBehaviorAction) e).getBehavior() != null) { res.add(((CallBehaviorAction) e).getBehavior()); } else if (e instanceof CallOperationAction && ((CallOperationAction) e).getOperation() != null) { res.add(((CallOperationAction) e).getOperation()); } } } else if (GeneratorUtils.hasStereotypeByString(cba, DocGenProfile.collectClassifierAttributes)) { for (Element e : in) { res.addAll(Utils.getAttributes(e, inherited)); } } else if (GeneratorUtils.hasStereotypeByString(cba, DocGenProfile.collectExpressionStereotype)) { res.addAll(Utils.collectByExpression(in, expression, iterate)); } else if (GeneratorUtils.hasStereotypeByString(cba, DocGenProfile.filterDiagramTypeStereotype)) { res.addAll(Utils.filterDiagramsByDiagramTypes(in, diagramTypes, include)); } else if (GeneratorUtils.hasStereotypeByString(cba, DocGenProfile.filterMetaclassStereotype)) { res.addAll(Utils.filterElementsByMetaclasses(in, metaclasses, include)); } else if (GeneratorUtils.hasStereotypeByString(cba, DocGenProfile.filterNameStereotype)) { res.addAll(Utils.filterElementsByNameRegex(in, names, include)); } else if (GeneratorUtils.hasStereotypeByString(cba, DocGenProfile.filterStereotypeStereotype)) { res.addAll(Utils.filterElementsByStereotypes(in, stereotypes, include, derived)); } else if (GeneratorUtils.hasStereotypeByString(cba, DocGenProfile.filterExpressionStereotype)) { res.addAll(Utils.filterElementsByExpression(in, expression, include, iterate)); } else if (GeneratorUtils.hasStereotypeByString(cba, DocGenProfile.collectionStereotype) && cba.getBehavior() != null) { res.addAll(collectAndFilterGroup((Activity) cba.getBehavior(), in)); } else if (GeneratorUtils.hasStereotypeByString(cba, DocGenProfile.removeDuplicates)) { res.addAll(Utils.removeDuplicates(in)); } else if (GeneratorUtils.hasStereotypeByString(cba, DocGenProfile.userScriptCFStereotype, true)) { res.addAll(getUserScriptCF(in, cba)); } else if (GeneratorUtils.hasStereotypeByString(cba, DocGenProfile.sortByName)) { res.addAll(sortElements(in, DocGenProfile.sortByName, cba)); } else if (GeneratorUtils.hasStereotypeByString(cba, DocGenProfile.sortByAttribute)) { res.addAll(sortElements(in, DocGenProfile.sortByAttribute, cba)); } else if (GeneratorUtils.hasStereotypeByString(cba, DocGenProfile.sortByProperty)) { res.addAll(sortElements(in, DocGenProfile.sortByProperty, cba)); } else if (GeneratorUtils.hasStereotypeByString(cba, DocGenProfile.sortByExpression)) { res.addAll(sortElements(in, DocGenProfile.sortByExpression, cba)); } // TODO -- duplicates should probably not be removed if just sorting! /*System.out.println( "collectAndFilter(): returning (before removing duplicates) res[" + res.size() + "]=" + MoreToString.Helper.toLongString( res ) );*/ return Utils.removeDuplicates(res); } /** * Sorts elements by property, attribute, or name after applying call * behavior. * * @param in elements to be sorted * @param sortStereotype the kind of sort based on sort stereotype name. This may be * sortByProperty, sortByAttribute, or sortByName as found in * DocGenProfile. * @param cba call behavior to be applied before getting the specified * @return */ public static List<Element> sortElements(Collection<? extends Element> in, String sortStereotype, Element cba) { List<Element> ordered = new ArrayList<Element>(in); boolean isProp = sortStereotype.equals(DocGenProfile.sortByProperty); boolean isAttr = sortStereotype.equals(DocGenProfile.sortByAttribute); boolean isExpr = sortStereotype.equals(DocGenProfile.sortByExpression); boolean isName = sortStereotype.equals(DocGenProfile.sortByName); if (!isProp && !isAttr && !isName && !isExpr) { log.error("Error! Trying to sort by unknown sort type: " + sortStereotype); return ordered; } String stereotypeProperty = null; if (isProp) { stereotypeProperty = "desiredProperty"; } else if (isAttr) { stereotypeProperty = "desiredAttribute"; } else if (isExpr) { stereotypeProperty = "expression"; } Object o = GeneratorUtils.getObjectProperty(cba, sortStereotype, stereotypeProperty, null); if (o instanceof Property && isProp) { ordered = Utils.sortByProperty(in, (Property) o); } else if (o instanceof EnumerationLiteral && isAttr) { ordered = Utils.sortByAttribute(in, o); } else if (isExpr) { ordered = Utils.sortByExpression(in, o); } else if (isName) { ordered = Utils2.asList(Utils.sortByName(in), Element.class); } else { log.error("Error! Trying to sort as " + sortStereotype + ", but the property/attribute is the wrong type: " + o); return ordered; } o = GeneratorUtils.getObjectProperty(cba, sortStereotype, "reverse", false); if (o == null) { o = GeneratorUtils.getObjectProperty(cba, sortStereotype, "invertOrder", false); } Boolean b = null; try { b = (Boolean) o; } catch (ClassCastException e) { // ignore } if (b != null && b) { Collections.reverse(ordered); } return ordered; } /** * an activity that should only has collect/filter actions in it * * @param a * @param in * @return */ private static List<Element> collectAndFilterGroup(Activity a, List<Element> in) { InitialNode initial = GeneratorUtils.findInitialNode(a); Collection<ActivityEdge> outs = initial.getOutgoing(); List<Element> res = in; if (outs != null && outs.size() == 1) { ActivityNode n = outs.iterator().next().getTarget(); if (StereotypesHelper.hasStereotypeOrDerived(n, DocGenProfile.collectFilterStereotype) || n instanceof CallBehaviorAction && ((CallBehaviorAction) n).getBehavior() != null && StereotypesHelper.hasStereotypeOrDerived(((CallBehaviorAction) n).getBehavior(), DocGenProfile.collectFilterStereotype)) { res = startCollectAndFilterSequence(n, in); } } return res; } /** * collect/filter action can be a userscript - input and output are both * collection of elements * * @param in * @param cba * @return */ @SuppressWarnings("rawtypes") private static List<Element> getUserScriptCF(List<Element> in, CallBehaviorAction cba) { List<Element> res = new ArrayList<Element>(); try { Map<String, Object> inputs = new HashMap<String, Object>(); inputs.put("DocGenTargets", in); Element e = cba; if (!StereotypesHelper.hasStereotypeOrDerived(cba, DocGenProfile.userScriptCFStereotype)) { if (cba.getBehavior() != null && StereotypesHelper.hasStereotypeOrDerived(cba.getBehavior(), DocGenProfile.userScriptCFStereotype)) { e = ((CallBehaviorAction) e).getBehavior(); } } Object o = ScriptRunner.runScriptFromStereotype(e, StereotypesHelper.checkForDerivedStereotype(e, DocGenProfile.userScriptCFStereotype), inputs); if (o != null && o instanceof Map && ((Map) o).containsKey("DocGenOutput")) { Object l = ((Map) o).get("DocGenOutput"); if (l instanceof List) { for (Object oo : (List) l) { if (oo instanceof Element) { res.add((Element) oo); } } } } } catch (ScriptException ex) { ex.printStackTrace(); StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); ex.printStackTrace(pw); context.log(sw.toString()); // stack trace as a string } return res; } }