package gov.nasa.jpl.mbee.mdk.model; import com.nomagic.magicdraw.core.Application; import com.nomagic.uml2.ext.magicdraw.actions.mdbasicactions.CallBehaviorAction; 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.ActivityNode; import com.nomagic.uml2.ext.magicdraw.activities.mdstructuredactivities.StructuredActivityNode; import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Element; import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.EnumerationLiteral; import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.NamedElement; import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Property; import gov.nasa.jpl.mbee.mdk.docgen.DocGenProfile; import gov.nasa.jpl.mbee.mdk.docgen.docbook.*; import gov.nasa.jpl.mbee.mdk.generator.CollectFilterParser; import gov.nasa.jpl.mbee.mdk.generator.DocumentGenerator; import gov.nasa.jpl.mbee.mdk.generator.DocumentValidator; import gov.nasa.jpl.mbee.mdk.generator.GenerationContext; import gov.nasa.jpl.mbee.mdk.util.*; import gov.nasa.jpl.mbee.mdk.ocl.OclEvaluator; import gov.nasa.jpl.mbee.mdk.util.Pair; import java.util.*; /** * This class contains methods for parsing and visiting TableStructures and * their contents. * * @author bcompane */ public class TableStructure extends Table { private abstract class TableColumn { public InitialNode bnode; public ActivityNode activityNode; public GenerationContext context = null; public String name = ""; public boolean editable = true; public GenerationContext makeContext() { ActivityNode n = null; if (bnode != null && bnode.getOutgoing().iterator().hasNext()) { // should // check // next // node // is // collect/filter // node n = bnode.getOutgoing().iterator().next().getTarget(); } Stack<List<Object>> in = new Stack<List<Object>>(); // in.add( targets ); context = new GenerationContext(in, n, getValidator(), Application.getInstance().getGUILog()); return context; } } private class TableColumnGroup extends TableColumn { public List<TableColumn> childColumns = new ArrayList<TableColumn>(); } private class TableAttributeColumn extends TableColumn { public Utils.AvailableAttribute attribute; } private class TablePropertyColumn extends TableColumn { public Property property; } private class TableExpressionColumn extends TableColumn { public String expression; public Boolean iterate; } private class TableStructuredColumn extends TableColumn { //public Generatable generatable; } //private List<String> headers = new ArrayList<String>(); private List<TableColumn> headers = new ArrayList<TableColumn>(); private int headerDepth = 0; //1 based private List<TableColumn> columns = new ArrayList<TableColumn>(); private Element ts; private InitialNode bnode; private String title; private List<List<List<Pair<Reference, Boolean>>>> tableContent = new ArrayList<>(); private Map<TableColumn, Integer> columnIndex = new HashMap<TableStructure.TableColumn, Integer>(); //0 based protected DocumentValidator validator = null; public TableStructure(DocumentValidator validator) { super(); this.validator = validator; } @Override public void initialize() { super.initialize(); if (dgElement instanceof StructuredActivityNode) { ts = dgElement; } else if (dgElement instanceof CallBehaviorAction) { ts = ((CallBehaviorAction) dgElement).getBehavior(); } else { ts = null; } if (ts != null) { bnode = GeneratorUtils.findInitialNode(ts); } title = ((NamedElement) dgElement).getName(); } @Override public void parse() { if (bnode == null) { return; } parseColumns(bnode, null, 1); } private void parseColumns(ActivityNode inNode, TableColumnGroup parent, int curDepth) { if (inNode == null) { return; } ActivityNode curNode = inNode; Collection<ActivityEdge> outs = curNode.getOutgoing(); while (outs != null && outs.size() == 1) { curNode = outs.iterator().next().getTarget(); TableColumn col = null; if (GeneratorUtils.hasStereotypeByString(curNode, DocGenProfile.tableAttributeColumnStereotype)) { col = new TableAttributeColumn(); Object attr = GeneratorUtils.getObjectProperty(curNode, DocGenProfile.tableAttributeColumnStereotype, "desiredAttribute", null); ((TableAttributeColumn) col).attribute = (attr instanceof EnumerationLiteral) ? Utils.AvailableAttribute.valueOf(((EnumerationLiteral) attr).getName()) : null; } else if (GeneratorUtils.hasStereotypeByString(curNode, DocGenProfile.tableExpressionColumnStereotype)) { col = new TableExpressionColumn(); ((TableExpressionColumn) col).expression = (String) GeneratorUtils.getObjectProperty(curNode, DocGenProfile.tableExpressionColumnStereotype, "expression", null); ((TableExpressionColumn) col).iterate = (Boolean) GeneratorUtils.getObjectProperty(curNode, DocGenProfile.tableExpressionColumnStereotype, "iterate", true); } else if (GeneratorUtils.hasStereotypeByString(curNode, DocGenProfile.tablePropertyColumnStereotype)) { col = new TablePropertyColumn(); ((TablePropertyColumn) col).property = (Property) GeneratorUtils.getObjectProperty(curNode, DocGenProfile.tablePropertyColumnStereotype, "desiredProperty", null); } else if (GeneratorUtils.hasStereotypeByString(curNode, "TableColumnGroup")) { col = new TableColumnGroup(); } else if (GeneratorUtils.hasStereotypeByString(curNode, "StructuredQuery") || curNode instanceof CallBehaviorAction) { col = new TableStructuredColumn(); //((TableStructuredColumn)col).structuredNode = curNode; } else { outs = curNode.getOutgoing(); continue; } col.editable = (Boolean) GeneratorUtils.getObjectProperty(curNode, DocGenProfile.editableChoosable, "editable", true); col.activityNode = curNode; if (curNode instanceof CallBehaviorAction && ((CallBehaviorAction) curNode).getBehavior() != null) { col.bnode = GeneratorUtils.findInitialNode(((CallBehaviorAction) curNode).getBehavior()); } else if (curNode instanceof StructuredActivityNode) { col.bnode = GeneratorUtils.findInitialNode(curNode); } col.name = curNode.getName(); //headers.add(curNode.getName()); if (!(col instanceof TableColumnGroup)) { columns.add(col); columnIndex.put(col, columnIndex.size()); } else { parseColumns(col.bnode, (TableColumnGroup) col, curDepth + 1); } if (parent != null) { parent.childColumns.add(col); } else { headers.add(col); } outs = curNode.getOutgoing(); if (headerDepth < curDepth) { headerDepth = curDepth; } } } private void buildTableReferences(boolean forViewEditor, String outputDir) { Set<Object> warnedError = new HashSet<Object>(); for (Object e : targets) { List<List<Pair<Reference, Boolean>>> row = new ArrayList<>(); List<Object> startElements = new ArrayList<Object>(); startElements.add(e); for (TableColumn tc : columns) { List<Element> resultElements; GenerationContext context = tc.makeContext(); if (!(tc instanceof TableStructuredColumn) && context.getCurrentNode() != null) { // should check next // node is // collect/filter node CollectFilterParser.setContext(context); resultElements = CollectFilterParser.startCollectAndFilterSequence( context.getCurrentNode(), Utils2.asList(startElements, Element.class)); } else { resultElements = Utils2.asList(startElements, Element.class); } List<Pair<Reference, Boolean>> cell = new ArrayList<>(); if (tc instanceof TableExpressionColumn && !((TableExpressionColumn) tc).iterate) { String expr = ((TableExpressionColumn) tc).expression; if (expr != null) { Object result = DocumentValidator .evaluate(expr, resultElements, getValidator(), true); OclEvaluator evaluator = OclEvaluator.instance; if (evaluator.isValid() && result != null) { Debug.outln("valid result = " + result + " for expression " + expr + " on " + MoreToString.Helper.toLongString(resultElements)); cell.add(new Pair<>(new Reference(result), tc.editable)); } else { Debug.outln("invalid evaluation of expression " + expr + " on " + MoreToString.Helper.toLongString(resultElements)); } } else { Debug.outln("attempted to evaluate null expression on " + MoreToString.Helper.toLongString(resultElements)); } } else { /*for (final Element ee : resultElements) { Application.getInstance().getGUILog().log(e instanceof NamedElement ? ((NamedElement) ee).getQualifiedName() : ee.getHumanName()); } Application.getInstance().getGUILog().log(resultElements.toString());*/ for (Element re : resultElements) { if (tc instanceof TableAttributeColumn) { Utils.AvailableAttribute at = ((TableAttributeColumn) tc).attribute; if (at == null) { continue; } Object attr = Utils.getElementAttribute(re, at); // attr can be string, value spec, or list of value spec if (attr == null && tc.editable && at == Utils.AvailableAttribute.Value && re instanceof Property) { cell.add(new Pair<>(new Reference(re, Utils.getFromAttribute(at), ""), tc.editable)); } else if (attr != null) { cell.add(new Pair<>(new Reference(re, Utils.getFromAttribute(((TableAttributeColumn) tc).attribute), attr), tc.editable)); } } else if (tc instanceof TablePropertyColumn) { Property prop = ((TablePropertyColumn) tc).property; if (prop == null) { continue; } Element slotOrProperty = Utils.getElementProperty(re, prop); List<Object> values = Utils.getElementPropertyValues(re, prop, true); if (slotOrProperty != null) { cell.add(new Pair<>(new Reference(slotOrProperty, From.DVALUE, values), tc.editable)); } else { cell.add(new Pair<>(new Reference(values), tc.editable)); } } else if (tc instanceof TableExpressionColumn) { String expr = ((TableExpressionColumn) tc).expression; if (expr == null) { // cell.add(new Reference(empty)); Debug.outln("attempted to evaluate null expression on " + MoreToString.Helper.toLongString(re)); continue; } Object result = DocumentValidator.evaluate(expr, re, getValidator(), true); OclEvaluator evaluator = OclEvaluator.instance; if (evaluator.isValid() || result != null) { Debug.outln("valid result = " + result + " for expression " + expr + " on " + MoreToString.Helper.toLongString(re)); cell.add(new Pair<>(new Reference(result), false)); } else { Debug.outln("invalid evaluation of expression " + expr + " on " + MoreToString.Helper.toLongString(re)); } } else if (tc instanceof TableStructuredColumn) { final Container con = new Section(); final DocumentGenerator dg = new DocumentGenerator(tc.activityNode, getValidator(), null); if (tc.bnode == null) { if (tc.activityNode != null && !warnedError.contains(tc)) { Utils.guilog("[WARN] Table structure column is missing initial node, skipping: " + tc.activityNode.getQualifiedName()); warnedError.add(tc); } break; } final Element a = tc.bnode.getOwner(); final GenerationContext nestedContext = new GenerationContext(new Stack<List<Object>>(), tc.activityNode, getValidator(), Application.getInstance().getGUILog()); //Application.getInstance().getGUILog().log(re instanceof NamedElement ? ((NamedElement) re).getQualifiedName() : re.getHumanName()); List<Object> newl = new ArrayList<Object>(); newl.add(re); nestedContext.pushTargets(newl); //context.pushTargets(new ArrayList<Object>(startElements)); dg.setContext(nestedContext); dg.parseActivityOrStructuredNode(a, con); //Application.getInstance().getGUILog().log(re instanceof NamedElement ? ((NamedElement) re).getQualifiedName() : re.getHumanName()); for (DocGenElement dge : con.getChildren()) { cell.add(new Pair<>(new Reference(dge), tc.editable)); } } } } row.add(cell); // check constraints DocumentValidator.evaluateConstraints(tc.activityNode, getCellData(row, tc), context, true, true); } Debug.outln("adding " + row.size() + " cells in row to table."); tableContent.add(row); } } @Override public List<DocumentElement> visit(boolean forViewEditor, String outputDir) { List<DocumentElement> res = new ArrayList<DocumentElement>(); if (ignore) { return res; } buildTableReferences(forViewEditor, outputDir); DBTable table = new DBTable(); List<List<DocumentElement>> tableheaders = makeTableHeaders(); table.setHeaders(tableheaders); if (headerDepth > 1) { List<DBColSpec> colspecs = new ArrayList<DBColSpec>(); for (int i = 0; i < columns.size(); i++) { DBColSpec cs = new DBColSpec(i + 1); colspecs.add(cs); } table.setColspecs(colspecs); } List<List<DocumentElement>> body = new ArrayList<List<DocumentElement>>(); for (List<List<Pair<Reference, Boolean>>> row : tableContent) { List<DocumentElement> tableRow = new ArrayList<DocumentElement>(); for (List<Pair<Reference, Boolean>> cell : row) { DBTableEntry entry = new DBTableEntry(); for (Pair<Reference, Boolean> pair : cell) { Reference cellPart = pair.getKey(); //Common.addReferenceToDBHasContent(cellPart, entry); if (cellPart.result instanceof DocGenElement) { DocBookOutputVisitor nested = new DocBookOutputVisitor(forViewEditor, outputDir); nested.getParent().push(entry); ((DocGenElement) cellPart.result).accept(nested); } else { Common.addReferenceToDBHasContent(cellPart, entry, pair.getValue()); } } tableRow.add(entry); } body.add(tableRow); } table.setBody(body); if (title != null && !title.isEmpty() && titles.isEmpty()) { titles.add(title); } setTableThings(table); res.add(table); return res; } public List<Pair<Reference, Boolean>> getCellReferences(List<List<Pair<Reference, Boolean>>> row, TableColumn col) { List<Pair<Reference, Boolean>> colData = row.get(getColumnIndex(col)); return colData; } public List<Object> getCellData(List<List<Pair<Reference, Boolean>>> row, TableColumn col) { List<Pair<Reference, Boolean>> colRefs = getCellReferences(row, col); List<Object> colData = new ArrayList<Object>(); for (Pair<Reference, Boolean> pair : colRefs) { colData.add(pair.getKey().result); } return colData; } protected int getColumnIndex(TableColumn col) { return columnIndex.get(col); } /* * @SuppressWarnings("unchecked") public void addSumRow() { List<Object> * sumRow = new ArrayList<Object>(); double f; boolean foundSumable = false; * for (List<Object> c: table) { f = 0; for (Object l: c) { if (l instanceof * List<?>) { for (Object item: (List<Object>)l) { if (item instanceof Float * || item instanceof Double || item instanceof Integer) { foundSumable = * true; f += (Double)item; } else if * (Utils2.toDouble(DocGenUtils.fixString(item, false)) != null) { * foundSumable = true; f += new * Double(ModelHelper.getValueString((ValueSpecification)item)); } } } } * List<Object> bucket = new ArrayList<Object>(); if (foundSumable) { * bucket.add(f); } else { bucket.add(irrelevantEntry); } * sumRow.add(bucket); foundSumable = false; } addRow(sumRow); } */ public DocumentValidator getValidator() { return validator; } public void setValidator(DocumentValidator validator) { this.validator = validator; } private List<List<DocumentElement>> makeTableHeaders() { List<List<DocumentElement>> result = new ArrayList<List<DocumentElement>>(); for (int i = 0; i < headerDepth; i++) { //add in rows of the headers result.add(new ArrayList<DocumentElement>()); } int start = 0; for (TableColumn tc : headers) { start = addHeader(tc, result, 1, start + 1); } return result; } private int addHeader(TableColumn tc, List<List<DocumentElement>> header, int curDepth, int startIndex) { if (tc instanceof TableColumnGroup) { int start = startIndex; DBTableEntry entry = new DBTableEntry(); entry.addElement(new DBText(tc.name)); int i = 0; for (TableColumn ctc : ((TableColumnGroup) tc).childColumns) { if (i == 0) { start = addHeader(ctc, header, curDepth + 1, start); } else { start = addHeader(ctc, header, curDepth + 1, start + 1); } i++; } entry.setNamest(Integer.toString(startIndex)); entry.setNameend(Integer.toString(start)); header.get(curDepth - 1).add(entry); return start; } else { DBTableEntry entry = new DBTableEntry(); entry.addElement(new DBText(tc.name)); header.get(curDepth - 1).add(entry); if (curDepth < headerDepth) { entry.setMorerows(headerDepth - curDepth); } return columnIndex.get(tc) + 1; } } }