package gov.nasa.jpl.mbee.mdk.generator; import com.nomagic.magicdraw.core.Application; import com.nomagic.magicdraw.core.Project; import com.nomagic.uml2.ext.jmi.helpers.ModelHelper; import com.nomagic.uml2.ext.jmi.helpers.StereotypesHelper; import com.nomagic.uml2.ext.magicdraw.classes.mddependencies.Dependency; import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.*; import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Class; import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Package; import com.nomagic.uml2.ext.magicdraw.mdprofiles.Stereotype; import com.nomagic.uml2.impl.ElementsFactory; import gov.nasa.jpl.mbee.mdk.api.docgen.presentation_elements.PresentationElementEnum; import gov.nasa.jpl.mbee.mdk.api.incubating.MDKConstants; import gov.nasa.jpl.mbee.mdk.api.incubating.convert.Converters; import gov.nasa.jpl.mbee.mdk.util.Utils; import org.json.simple.JSONObject; import org.json.simple.JSONValue; import org.json.simple.parser.JSONParser; import java.util.*; //@donbot update json simple to jackson public class PresentationElementUtils { public static final String ID_SUFFIX = "_pei"; private Project project; private Classifier paraC, tparaC, tableC, listC, imageC, sectionC, tsectionC; private Stereotype presentsStereotype, productStereotype, viewClassStereotype; private Property generatedFromView, generatedFromElement; private ElementsFactory ef; { this.project = Application.getInstance().getProject(); this.paraC = PresentationElementEnum.OPAQUE_PARAGRAPH.get().apply(project); this.tparaC = PresentationElementEnum.PARAGRAPH.get().apply(project); this.tableC = PresentationElementEnum.OPAQUE_TABLE.get().apply(project); this.listC = PresentationElementEnum.OPAQUE_LIST.get().apply(project); this.imageC = PresentationElementEnum.OPAQUE_IMAGE.get().apply(project); this.sectionC = PresentationElementEnum.OPAQUE_SECTION.get().apply(project); this.tsectionC = PresentationElementEnum.SECTION.get().apply(project); this.presentsStereotype = Utils.getPresentsStereotype(project); this.productStereotype = Utils.getProductStereotype(project); this.viewClassStereotype = Utils.getViewClassStereotype(project); this.generatedFromView = Utils.getGeneratedFromViewProperty(project); this.generatedFromElement = Utils.getGeneratedFromElementProperty(project); this.ef = project.getElementsFactory(); } public static Expression getViewOrSectionExpression(Element viewOrSection) { if (viewOrSection instanceof InstanceSpecification) { if (((InstanceSpecification) viewOrSection).getSpecification() instanceof Expression) { return (Expression) ((InstanceSpecification) viewOrSection).getSpecification(); } } else if (viewOrSection instanceof Class) { Constraint c = Utils.getViewConstraint(viewOrSection); if (c != null && c.getSpecification() instanceof Expression) { return (Expression) c.getSpecification(); } } return null; } public PresentationElementInfo getCurrentInstances(Element viewOrSection, Element view) { List<InstanceSpecification> tables = new ArrayList<InstanceSpecification>(); List<InstanceSpecification> lists = new ArrayList<InstanceSpecification>(); List<InstanceSpecification> sections = new ArrayList<InstanceSpecification>(); List<InstanceSpecification> paras = new ArrayList<InstanceSpecification>(); List<InstanceSpecification> images = new ArrayList<InstanceSpecification>(); List<InstanceSpecification> manuals = new ArrayList<InstanceSpecification>(); List<InstanceSpecification> all = new ArrayList<InstanceSpecification>(); List<InstanceSpecification> extraRef = new ArrayList<InstanceSpecification>(); List<InstanceSpecification> unused = new ArrayList<InstanceSpecification>(); List<InstanceSpecification> opaque = new ArrayList<InstanceSpecification>(); List<InstanceSpecification> extraManualRef = new ArrayList<InstanceSpecification>(); PresentationElementInfo res = new PresentationElementInfo(all, images, tables, lists, paras, sections, manuals, extraRef, extraManualRef, unused, opaque); Expression e = getViewOrSectionExpression(viewOrSection); boolean isView = !(viewOrSection instanceof InstanceSpecification); if (e == null) { return res; } for (ValueSpecification vs : e.getOperand()) { if (vs instanceof InstanceValue) { InstanceSpecification is = ((InstanceValue) vs).getInstance(); if (is == null) { continue; } if (!is.getClassifier().isEmpty()) { List<Classifier> iscs = is.getClassifier(); boolean viewinstance = false; if (iscs.contains(paraC) || iscs.contains(tableC) || iscs.contains(listC) || iscs.contains(imageC) || iscs.contains(sectionC)) { for (Element el : is.getOwnedElement()) { if (el instanceof Slot && ((Slot) el).getDefiningFeature() != null && ((Slot) el).getDefiningFeature().getName() != null && ((Slot) el).getDefiningFeature().getName().equals("generatedFromView") && !((Slot) el).getValue().isEmpty() && ((Slot) el).getValue().get(0) instanceof ElementValue && ((ElementValue) ((Slot) el).getValue().get(0)).getElement() == view) { viewinstance = true; } } if (!viewinstance) { for (InstanceValue iv : is.get_instanceValueOfInstance()) { if (iv != vs && iv.getOwner() != null) { //an opaque instance that's referenced from somewhere else extraRef.add(is); break; } } } } if ((iscs.contains(paraC) || iscs.contains(tparaC)) && isView && is.getSpecification() instanceof LiteralString) { try { JSONObject ob = (JSONObject) (new JSONParser()).parse(((LiteralString) is.getSpecification()).getValue()); //TODO sourceProperty json key migration? @donbot if (Converters.getElementToIdConverter().apply(view).equals(ob.get("sourceId")) && "documentation".equals(ob.get("sourceProperty"))) { viewinstance = false; //a view doc instance res.setViewDocHack(is); } } catch (Exception x) { } } if (viewinstance) {//instance generated by current view if (iscs.contains(paraC)) { paras.add(is); } else if (iscs.contains(tableC)) { tables.add(is); } else if (iscs.contains(listC)) { lists.add(is); } else if (iscs.contains(imageC)) { images.add(is); } else if (iscs.contains(sectionC)) { sections.add(is); } opaque.add(is); } else { manuals.add(is); for (InstanceValue iv : is.get_instanceValueOfInstance()) { if (iv != vs && iv.getOwner() != null) { //a non opaque instance being referenced from somewhere else extraManualRef.add(is); break; } } } all.add(is); } } } if (isView) { Package viewp = findViewInstancePackage(view); if (viewp != null) { for (Element el : viewp.getOwnedElement()) { if (el instanceof InstanceSpecification && ((InstanceSpecification) el).get_instanceValueOfInstance().isEmpty()) { unused.add((InstanceSpecification) el); //but this might be a manual instance that's referenced by higher project? } } } } return res; } public boolean isSection(InstanceSpecification is) { return is.getClassifier().contains(sectionC) || is.getClassifier().contains(tsectionC); } public boolean isInSomeViewPackage(InstanceSpecification is) { Element owner = is.getOwner(); if (owner instanceof Package) { for (Element e : Utils.collectDirectedRelatedElementsByRelationshipStereotype(owner, presentsStereotype, 2, false, 1)) { if (StereotypesHelper.hasStereotypeOrDerived(e, viewClassStereotype)) { return true; } } } return false; } public Package findViewInstancePackage(Element view) { List<Element> results = Utils.collectDirectedRelatedElementsByRelationshipStereotype(view, presentsStereotype, 1, false, 1); if (!results.isEmpty() && results.get(0) instanceof Package) { return (Package) results.get(0); } return null; } public List<Package> findCorrectViewInstancePackageOwners(Element view) { Type viewt = (Type) view; List<Package> parentPack = new ArrayList<Package>(); if (StereotypesHelper.hasStereotypeOrDerived(view, productStereotype)) { Element owner = view.getOwner(); while (!(owner instanceof Package)) { owner = owner.getOwner(); } parentPack.add((Package) owner); } else { for (TypedElement t : viewt.get_typedElementOfType()) { if (t instanceof Property && ((Property) t).getAggregation().equals(AggregationKindEnum.COMPOSITE) && StereotypesHelper.hasStereotypeOrDerived(t.getOwner(), viewClassStereotype)) { Package parent = findViewInstancePackage(t.getOwner()); if (parent != null) { parentPack.add(parent); } } } if (parentPack.isEmpty()) { Element owner = view.getOwner(); while (!(owner instanceof Package)) { owner = owner.getOwner(); } parentPack.add((Package) owner); } } return parentPack; } public Package createViewInstancePackage(Element view, Package owner) { Package viewPackage = ef.createPackageInstance(); viewPackage.setName(((NamedElement) view).getName() + " Instances"); viewPackage.setOwner(owner); Dependency d = ef.createDependencyInstance(); d.setOwner(viewPackage); ModelHelper.setSupplierElement(d, viewPackage); ModelHelper.setClientElement(d, view); StereotypesHelper.addStereotype(d, presentsStereotype); return viewPackage; } public boolean needLockForEdit(PresentationElementInstance pe) { InstanceSpecification is = pe.getInstance(); if (is == null || (pe.isManual() && !pe.isViewDocHack())) { return false; } if (pe.isViewDocHack()) { return true; } ValueSpecification oldvs = is.getSpecification(); //check classifier if (pe.getNewspec() != null && !pe.getNewspec().get("type").equals("Section")) { if (oldvs instanceof LiteralString && ((LiteralString) oldvs).getValue() != null) { try { JSONObject oldob = (JSONObject) JSONValue.parse(((LiteralString) oldvs).getValue()); if (oldob == null || !oldob.equals(pe.getNewspec())) { return true; } } catch (Exception ex) { return true; } } else { return true; } } else if (pe.getType().equals(PresentationElementEnum.SECTION)) { if (!(is.getSpecification() instanceof Expression)) { return true; } List<InstanceSpecification> list = new ArrayList<InstanceSpecification>(); for (PresentationElementInstance cpe : pe.getChildren()) { if (cpe.getInstance() == null) { return true; } list.add(cpe.getInstance()); } List<ValueSpecification> model = ((Expression) is.getSpecification()).getOperand(); if (model.size() != list.size()) { return true; } for (int i = 0; i < model.size(); i++) { ValueSpecification modelvs = model.get(i); if (!(modelvs instanceof InstanceValue) || ((InstanceValue) modelvs).getInstance() != list.get(i)) { return true; } } } return false; } public InstanceSpecification updateOrCreateInstance(PresentationElementInstance pe, Package owner) { InstanceSpecification is = pe.getInstance(); if (is != null && pe.isManual() && !pe.isViewDocHack()) { return is; } if (is == null) { is = ef.createInstanceSpecificationInstance(); Application.getInstance().getProject().getCounter().setCanResetIDForObject(true); is.setID(MDKConstants.HIDDEN_ID_PREFIX + Converters.getElementToIdConverter().apply(is) + ID_SUFFIX); if (!pe.isViewDocHack()) { Slot s = ef.createSlotInstance(); s.setOwner(is); s.setOwningInstance(is); s.setDefiningFeature(generatedFromView); ElementValue ev = ef.createElementValueInstance(); ev.setElement(pe.getView()); s.getValue().add(ev); if (pe.getType() == PresentationElementEnum.SECTION && pe.getLoopElement() != null) { Slot ss = ef.createSlotInstance(); ss.setOwner(is); ss.setOwningInstance(is); ss.setDefiningFeature(generatedFromElement); ElementValue ev2 = ef.createElementValueInstance(); ev2.setElement(pe.getLoopElement()); ss.getValue().add(ev2); } } } JSONObject newspec = pe.getNewspec(); Classifier classifier = null; String name; if (pe.isViewDocHack()) { newspec = new JSONObject(); newspec.put("source", Converters.getElementToIdConverter().apply(is)); newspec.put("type", "Paragraph"); newspec.put("sourceProperty", "documentation"); String transclude = "<p> </p><p><mms-transclude-doc data-mms-eid=\"" + Converters.getElementToIdConverter().apply(pe.getView()) + "\">[cf." + ((NamedElement) pe.getView()).getName() + ".doc]</mms-transclude-doc></p><p> </p>"; ModelHelper.setComment(is, transclude); name = "View Documentation"; classifier = tparaC; } else { if (pe.getType() == PresentationElementEnum.PARAGRAPH) { classifier = paraC; } else if (pe.getType() == PresentationElementEnum.TABLE) { classifier = tableC; } else if (pe.getType() == PresentationElementEnum.LIST) { classifier = listC; } else if (pe.getType() == PresentationElementEnum.IMAGE) { classifier = imageC; } else if (pe.getType() == PresentationElementEnum.SECTION) { classifier = sectionC; } name = pe.getName(); if (name == null || name.isEmpty()) { name = "<>"; } } is.setName(name); is.getClassifier().clear(); is.getClassifier().add(classifier); if (pe.getType() == PresentationElementEnum.SECTION) { //assume all children pe have instance, caller should walk bottom up Expression expression = is.getSpecification() instanceof Expression ? (Expression) is.getSpecification() : ef.createExpressionInstance(); expression.setOwner(is); List<InstanceValue> ivs = new ArrayList<>(pe.getChildren().size()); for (int i = 0; i < pe.getChildren().size(); i++) { InstanceValue iv = i < expression.getOperand().size() && expression.getOperand().get(i) instanceof InstanceValue ? (InstanceValue) expression.getOperand().get(i) : ef.createInstanceValueInstance(); iv.setInstance(pe.getChildren().get(i).getInstance()); ivs.add(iv); } expression.getOperand().clear(); expression.getOperand().addAll(ivs); } else if (newspec != null) { ValueSpecification string = is.getSpecification(); if (!(string instanceof LiteralString)) { string = ef.createLiteralStringInstance(); } string.setOwner(is); ((LiteralString) string).setValue(newspec.toJSONString()); is.setSpecification(string); } is.setOwner(owner); pe.setInstance(is); return is; } //return bfs view order public List<Element> getViewProcessOrder(Element start, Map<Element, List<Element>> view2view) { List<Element> res = new ArrayList<>(); Queue<Element> toProcess = new LinkedList<>(); HashSet<Element> processed = new HashSet<>(); toProcess.add(start); while (!toProcess.isEmpty()) { Element next = toProcess.remove(); res.add(next); processed.add(next); if (view2view.containsKey(next)) { for (Element element : view2view.get(next)) { if (!processed.contains(element)) { toProcess.add(element); } else { Application.getInstance().getGUILog().log("[WARNING] Circular view order detected for " + element.getHumanName() + ". View will not be added to the ordering an additional time."); } } } } return res; } public Constraint getOrCreateViewConstraint(Element view) { Constraint c = Utils.getViewConstraint(view); if (c != null) { return c; } c = ef.createConstraintInstance(); Application.getInstance().getProject().getCounter().setCanResetIDForObject(true); c.setID(Converters.getElementToIdConverter().apply(view) + "_vc"); c.setOwner(view); c.getConstrainedElement().add(view); return c; } public void updateOrCreateConstraintFromInstanceSpecifications(Element view, List<InstanceSpecification> instanceSpecifications) { Constraint c = getOrCreateViewConstraint(view); Expression expression = c.getSpecification() instanceof Expression ? (Expression) c.getSpecification() : ef.createExpressionInstance(); Application.getInstance().getProject().getCounter().setCanResetIDForObject(true); expression.setID(Converters.getElementToIdConverter().apply(view) + "_vc_expression"); expression.setOwner(c); List<InstanceValue> instanceValues = new ArrayList<>(instanceSpecifications.size()); Iterator<ValueSpecification> operandIterator = expression.getOperand().iterator(); while (operandIterator.hasNext()) { ValueSpecification valueSpecification = operandIterator.next(); if (!(valueSpecification instanceof InstanceValue)) { operandIterator.remove(); continue; } instanceValues.add((InstanceValue) valueSpecification); } for (int i = 0; i < instanceSpecifications.size(); i++) { InstanceValue instanceValue = i < instanceValues.size() ? instanceValues.get(i) : ef.createInstanceValueInstance(); instanceValue.setInstance(instanceSpecifications.get(i)); instanceValues.add(instanceValue); } expression.getOperand().clear(); expression.getOperand().addAll(instanceValues); } public void updateOrCreateConstraintFromPresentationElements(Element view, List<PresentationElementInstance> presentationElementInstances) { List<InstanceSpecification> instanceSpecifications = new ArrayList<>(presentationElementInstances.size()); for (PresentationElementInstance presentationElementInstance : presentationElementInstances) { instanceSpecifications.add(presentationElementInstance.getInstance()); } updateOrCreateConstraintFromInstanceSpecifications(view, instanceSpecifications); } }