package gov.nasa.jpl.mbee.mdk.generator;
import com.nomagic.magicdraw.core.Application;
import com.nomagic.magicdraw.core.GUILog;
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.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.Activity;
import com.nomagic.uml2.ext.magicdraw.activities.mdfundamentalactivities.ActivityNode;
import com.nomagic.uml2.ext.magicdraw.activities.mdintermediateactivities.*;
import com.nomagic.uml2.ext.magicdraw.activities.mdstructuredactivities.StructuredActivityNode;
import com.nomagic.uml2.ext.magicdraw.classes.mddependencies.Dependency;
import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Class;
import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.*;
import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Package;
import com.nomagic.uml2.ext.magicdraw.commonbehaviors.mdbasicbehaviors.Behavior;
import com.nomagic.uml2.ext.magicdraw.mdprofiles.Stereotype;
import gov.nasa.jpl.mbee.mdk.api.incubating.convert.Converters;
import gov.nasa.jpl.mbee.mdk.constraint.BasicConstraint;
import gov.nasa.jpl.mbee.mdk.constraint.Constraint;
import gov.nasa.jpl.mbee.mdk.docgen.DocGenProfile;
import gov.nasa.jpl.mbee.mdk.docgen.validation.ConstraintValidationRule;
import gov.nasa.jpl.mbee.mdk.util.Debug;
import gov.nasa.jpl.mbee.mdk.util.MoreToString;
import gov.nasa.jpl.mbee.mdk.util.Utils;
import gov.nasa.jpl.mbee.mdk.util.Utils2;
import gov.nasa.jpl.mbee.mdk.ocl.OclEvaluator;
import gov.nasa.jpl.mbee.mdk.validation.ValidationRule;
import gov.nasa.jpl.mbee.mdk.validation.ValidationRuleViolation;
import gov.nasa.jpl.mbee.mdk.validation.ValidationSuite;
import gov.nasa.jpl.mbee.mdk.validation.ViolationSeverity;
import org.eclipse.ocl.ParserException;
import org.jgrapht.DirectedGraph;
import org.jgrapht.EdgeFactory;
import org.jgrapht.alg.StrongConnectivityInspector;
import org.jgrapht.graph.DefaultDirectedGraph;
import java.io.PrintWriter;
import java.util.*;
/**
* validates docgen 3 document uses jgrapht to detect cycles in the document and
* various other potential errors this only checks for static model structure
* and does not actually try to execute the document
*
* @author dlam Changelog: Document Validator updated to use Validationsuite.
*/
public class DocumentValidator {
public ValidationRule getViewpointConstraintRule() {
return viewpointConstraintRule;
}
private Element start;
private Set<Behavior> done;
private ValidationSuite validationui = new ValidationSuite(
"Validationui");
private ValidationSuite dynamicExpressionValidation = new ValidationSuite(
"ExpressionValidation");
private static final Map<String, String> requiredTags = new HashMap<String, String>() {
private static final long serialVersionUID = -5391825454091546000L;
{
put(DocGenProfile.metaclassChoosable, "metaclasses");
put(DocGenProfile.stereotypeChoosable, "stereotypes");
put(DocGenProfile.nameChoosable, "names");
put(DocGenProfile.diagramTypeChoosable, "diagramTypes");
put(DocGenProfile.expressionChoosable, "expression");
put(DocGenProfile.propertyChoosable, "desiredProperty");
put(DocGenProfile.attributeChoosable, "desiredAttribute");
}
};
/*
* Statuses possible, currently error, warning, and fatal error.
*/
private ViolationSeverity error = ViolationSeverity.ERROR;
private ViolationSeverity warn = ViolationSeverity.WARNING;
private ViolationSeverity fatalerror = ViolationSeverity.FATAL;
/*
* Current List of validation flags.
*/
private ValidationRule multipleFirstErrors = new ValidationRule(
"Multiple First Errors",
"Has multiple firsts!",
error);
private ValidationRule multipleNextErrors = new ValidationRule(
"Multiple Next Errors",
"Has multiple nexts!",
error);
private ValidationRule multipleContentErrors = new ValidationRule(
"Multiple Content Errors",
"has multiple (unstereotyped) dependencies!",
error);
private ValidationRule multipleViewpoints = new ValidationRule(
"MultipleViewpoints",
"Conforms to multiple vewpoints!",
error);
private ValidationRule missingViewpointErrors = new ValidationRule(
"Missing Viewpoint Errors",
"Doesn't conform to any viewpoint!",
warn);
private ValidationRule missingImportErrors = new ValidationRule(
"Missing Import Errors",
"Is missing imports!",
warn);
private ValidationRule missingViewpointBehavior = new ValidationRule(
"Missing Viewpoint Behavior",
"Is missng the viewpoint behavior",
error);
private ValidationRule nonView2View = new ValidationRule(
"Nonsection With Dependencies",
"Is a nonsection but has first or next dependencies!",
warn);
private ValidationRule shouldBeSection = new ValidationRule(
"Should be a Section",
"Is a nonsection but should be a section (because it's the target of a First or Next",
warn);
private ValidationRule shouldNotBeSection = new ValidationRule(
"Should not be a section",
"Is a section but should not be (because it's the target of a vanilla dependency",
warn);
private ValidationRule multipleInitialNode = new ValidationRule(
"Muliple Initial Nodes",
"Has multiple initial nodes!",
error);
private ValidationRule multipleOutgoingFlows = new ValidationRule(
"Multiple Outgoing Flows",
"Has multiple outgoing flows!",
error);
private ValidationRule multipleIncomingFlows = new ValidationRule(
"Multple Incoming Flows",
"Has multiple incoming flows!",
warn);
private ValidationRule missingInitialNode = new ValidationRule(
"Missing Initial Node",
"Is missing an initial node!",
warn);
private ValidationRule multipleStereotypes = new ValidationRule(
"Multiple Stereotypes",
"Element and/or its behavior has multiple stereotypes!",
error);
private ValidationRule mismatchStereotypeErrors = new ValidationRule(
"Mismatched Stereotypes",
"Element and its behavior have mismatched sterotypes!",
error);
private ValidationRule missingStereotype = new ValidationRule(
"Missing stereotype",
"Element and its behavior (if present) is missing a document stereotype!",
error);
private ValidationRule missingOutgoingFlow = new ValidationRule(
"Missing outgoing flow",
"Non-final node is missing outgoing flow!",
warn);
private ValidationRule cycleError = new ValidationRule(
"Cycles in model",
"There are loops in this document! Do not generate document!",
fatalerror);
private ValidationRule activityNodeCycleError = new ValidationRule(
"Activity Node Cycles in Model",
"There are loops in this document! Do not generate document!",
fatalerror);
private ValidationRule missingTagValue = new ValidationRule(
"Missing tag",
"An action is missing required tag value",
error);
private ValidationRule viewpointConstraintRule = new ConstraintValidationRule();
/*
* Needed to use the utils.displayvalidationwindow
*/
private Collection<ValidationSuite> validationOutput = new ArrayList<ValidationSuite>();
private GUILog log;
private DirectedGraph<NamedElement, Element> dg; // graph for viewpoints
private List<Set<ActivityNode>> cycles; // cycles for activities and structured nodes
private ActivityEdgeFactory aef;
private boolean fatal;
private Stereotype sysmlview;
private Stereotype conforms;
private Stereotype conforms14;
private Stereotype md18expose;
private Stereotype ourExpose;
private Project project;
public DocumentValidator(Element e) {
start = e;
project = Project.getProject(e);
sysmlview = Utils.getViewStereotype(project);
conforms = Utils.getConformsStereotype(project);
conforms14 = Utils.getSysML14ConformsStereotype(project);
md18expose = Utils.get18ExposeStereotype(project);
ourExpose = Utils.getExposeStereotype(project);
log = Application.getInstance().getGUILog();
cycles = new ArrayList<>();
fatal = false;
done = new HashSet<>();
aef = new ActivityEdgeFactory();
dg = new DefaultDirectedGraph<>(Element.class);
// List of Validation Rules
validationui.addValidationRule(multipleFirstErrors);
validationui.addValidationRule(multipleNextErrors);
validationui.addValidationRule(multipleContentErrors);
validationui.addValidationRule(multipleViewpoints);
validationui.addValidationRule(multipleOutgoingFlows);
validationui.addValidationRule(mismatchStereotypeErrors);
validationui.addValidationRule(missingViewpointErrors);
validationui.addValidationRule(missingImportErrors);
validationui.addValidationRule(multipleInitialNode);
validationui.addValidationRule(multipleIncomingFlows);
validationui.addValidationRule(missingInitialNode);
validationui.addValidationRule(missingViewpointBehavior);
validationui.addValidationRule(missingStereotype);
validationui.addValidationRule(missingOutgoingFlow);
validationui.addValidationRule(multipleStereotypes);
validationui.addValidationRule(nonView2View);
validationui.addValidationRule(shouldBeSection);
validationui.addValidationRule(shouldNotBeSection);
validationui.addValidationRule(cycleError);
validationui.addValidationRule(activityNodeCycleError);
validationui.addValidationRule(missingTagValue);
dynamicExpressionValidation.addValidationRule(viewpointConstraintRule);
// Need Collection to use the utils.DisplayValidationWindow method
validationOutput.add(validationui);
validationOutput.add(dynamicExpressionValidation);
}
public boolean isFatal() {
return fatal;
}
public void validateDocument() {
if (StereotypesHelper.hasStereotypeOrDerived(start, sysmlview)) {
validateView((NamedElement) start, true);
}
else if (StereotypesHelper.hasStereotypeOrDerived(start, DocGenProfile.documentStereotype)
&& start instanceof Activity) {
this.done.add((Activity) start);
validateActivity((NamedElement) start);
}
else {
log.log("This is not a starting docgen 3 document!");
}
}
class ViewDependencyEdgeFactory implements EdgeFactory<NamedElement, DirectedRelationship> {
@Override
public DirectedRelationship createEdge(NamedElement sourceVertex, NamedElement targetVertex) {
List<DirectedRelationship> dep = Utils.findDirectedRelationshipsBetween(sourceVertex,
targetVertex);
if (!dep.isEmpty()) {
return dep.get(0);
}
return null;
}
}
class ActivityEdgeFactory implements EdgeFactory<ActivityNode, ActivityEdge> {
@Override
public ActivityEdge createEdge(ActivityNode sourceVertex, ActivityNode targetVertex) {
for (ActivityEdge ae : sourceVertex.getOutgoing()) {
if (ae.getTarget() == targetVertex) {
return ae;
}
}
return null;
}
}
private void validateView(NamedElement view, boolean section) {
if (dg.containsVertex(view)) {
return;
}
dg.addVertex(view);
List<Element> viewpoints = Utils.collectDirectedRelatedElementsByRelationshipStereotype(view,
conforms, 1, false, 1);
if (viewpoints.isEmpty()) {
viewpoints = Utils.collectDirectedRelatedElementsByRelationshipStereotype(view, conforms14, 1, false, 1);
}
if (viewpoints.size() > 1) {
multipleViewpoints.addViolation(view, multipleViewpoints.getDescription());
}
for (Element viewpoint : viewpoints) {
if (viewpoint != null && viewpoint instanceof Class) {
Collection<Behavior> viewpointBehavior = ((Class) viewpoint).getOwnedBehavior();
Behavior b = null;
if (viewpointBehavior.size() > 0) {
b = viewpointBehavior.iterator().next();
}
else {
Class now = (Class) viewpoint;
while (now != null) {
if (!now.getSuperClass().isEmpty()) {
now = now.getSuperClass().iterator().next();
if (now.getOwnedBehavior().size() > 0) {
b = now.getOwnedBehavior().iterator().next();
break;
}
}
else {
now = null;
}
}
}
if (b == null) {
missingViewpointBehavior.addViolation(viewpoint,
missingViewpointBehavior.getDescription());
}
else {
if (b instanceof Activity) {
if (!this.done.contains(b)) {
this.done.add(b);
validateActivity(b);
}
}
}
}
}
if (!viewpoints.isEmpty()) {
List<Element> elementImports = Utils.collectDirectedRelatedElementsByRelationshipJavaClass(view,
ElementImport.class, 1, 1);
List<Element> packageImports = Utils.collectDirectedRelatedElementsByRelationshipJavaClass(view,
PackageImport.class, 1, 1);
List<Element> queries = Utils.collectDirectedRelatedElementsByRelationshipStereotype(view,
ourExpose, 1, false, 1);
if (md18expose != null) {
queries.addAll(Utils.collectDirectedRelatedElementsByRelationshipStereotype(view,
md18expose, 1, false, 1));
}
elementImports.addAll(packageImports);
elementImports.addAll(queries);
if (elementImports.isEmpty()) {
missingImportErrors.addViolation(view, missingImportErrors.getDescription());
}
}
else if (!(view instanceof Diagram)) {
missingViewpointErrors.addViolation(view, missingViewpointErrors.getDescription());
}
if (view instanceof Package) {
List<Dependency> firsts = getOutgoingDependencies(view, DocGenProfile.firstStereotype);//Utils.collectDirectedRelatedElementsByRelationshipStereotypeString(view,
//DocGenProfile.firstStereotype, 1, false, 1);
List<Dependency> nexts = getOutgoingDependencies(view, DocGenProfile.nextStereotype);//Utils.collectDirectedRelatedElementsByRelationshipStereotypeString(view,
//DocGenProfile.nextStereotype, 1, false, 1);
List<Dependency> contents = getOutgoingDependencies(view, DocGenProfile.nosectionStereotype);//Utils.collectDirectedRelatedElementsByRelationshipStereotypeString(view,
//DocGenProfile.nosectionStereotype, 1, false, 1);
if (contents.size() > 1) {
multipleContentErrors.addViolation(view, multipleContentErrors.getDescription());
}
if (!section && (!firsts.isEmpty() || !nexts.isEmpty())) {
nonView2View.addViolation(view, nonView2View.getDescription());
}
if (firsts.size() > 1) {
multipleFirstErrors.addViolation(view, multipleFirstErrors.getDescription());
}
if (nexts.size() > 1) {
multipleNextErrors.addViolation(view, multipleNextErrors.getDescription());
}
for (Dependency c : contents) {
Element nosection = ModelHelper.getSupplierElement(c);
validateView((NamedElement) nosection, false);
dg.addEdge(view, (NamedElement) nosection, c);
}
for (Dependency f : firsts) {
Element first = ModelHelper.getSupplierElement(f);
validateView((NamedElement) first, true);
dg.addEdge(view, (NamedElement) first, f);
}
for (Dependency n : nexts) {
Element next = ModelHelper.getSupplierElement(n);
validateView((NamedElement) next, true);
dg.addEdge(view, (NamedElement) next, n);
}
}
else if (view instanceof Class) {
for (Property p : ((Class) view).getOwnedAttribute()) {
if (p.getType() != null && StereotypesHelper.hasStereotypeOrDerived(p.getType(), sysmlview)) {
validateView(p.getType(), true);
dg.addEdge(view, p.getType(), p);
}
}
}
}
private void validateActivity(NamedElement activity) {
DirectedGraph<ActivityNode, ActivityEdge> graph = new DefaultDirectedGraph<ActivityNode, ActivityEdge>(
aef);
List<InitialNode> inodes = findInitialNodes(activity);
if (inodes.size() > 1) {
multipleInitialNode.addViolation(activity, multipleInitialNode.getDescription());
}
if (inodes.isEmpty()) {
missingInitialNode.addViolation(activity, missingInitialNode.getDescription());
}
for (InitialNode n : inodes) {
graph.addVertex(n);
validateNode(n, graph);
}
StrongConnectivityInspector<ActivityNode, ActivityEdge> sci = new StrongConnectivityInspector<ActivityNode, ActivityEdge>(
graph);
List<Set<ActivityNode>> cycles = sci.stronglyConnectedSets();
if (!cycles.isEmpty()) {
for (Set<ActivityNode> cycle : cycles) {
if (cycle.size() > 1) {
this.cycles.add(cycle);
}
}
}
}
private void validateNode(ActivityNode n, DirectedGraph<ActivityNode, ActivityEdge> graph) {
Collection<ActivityEdge> outs = n.getOutgoing();
if (!(n instanceof ForkNode) && outs.size() > 1) {
multipleOutgoingFlows.addViolation(n, multipleOutgoingFlows.getDescription());
}
if (!(n instanceof FinalNode) && outs.isEmpty()) {
missingOutgoingFlow.addViolation(n, missingOutgoingFlow.getDescription());
}
if (!(n instanceof MergeNode) && !(n instanceof JoinNode) && !(n instanceof DecisionNode)
&& n.getIncoming().size() > 1) {
multipleIncomingFlows.addViolation(n, multipleIncomingFlows.getDescription());
}
if (n instanceof CallBehaviorAction) {
Behavior b = n instanceof CallBehaviorAction ? ((CallBehaviorAction) n).getBehavior() : null;
Collection<Stereotype> napplied = new HashSet<Stereotype>(
StereotypesHelper
.checkForAllDerivedStereotypes(n, DocGenProfile.collectFilterStereotype));
napplied.addAll(StereotypesHelper.checkForAllDerivedStereotypes(n,
DocGenProfile.ignorableStereotype));
napplied.addAll(StereotypesHelper.checkForAllDerivedStereotypes(n,
DocGenProfile.tableColumnStereotype));
if (b == null) {
if (napplied.isEmpty()) {
missingStereotype.addViolation(n, missingStereotype.getDescription());
}
else if (napplied.size() > 1) {
multipleStereotypes.addViolation(n, multipleStereotypes.getDescription());
}
}
else {
Collection<Stereotype> bapplied = new HashSet<Stereotype>(
StereotypesHelper.checkForAllDerivedStereotypes(b,
DocGenProfile.collectFilterStereotype));
bapplied.addAll(StereotypesHelper.checkForAllDerivedStereotypes(b,
DocGenProfile.ignorableStereotype));
bapplied.addAll(StereotypesHelper.checkForAllDerivedStereotypes(b,
DocGenProfile.tableColumnStereotype));
if (napplied.isEmpty() && bapplied.isEmpty()) {
missingStereotype.addViolation(n, missingStereotype.getDescription());
}
// else if (bapplied.isEmpty())
// mismatchStereotypeErrors.addViolation(n,
// mismatchStereotypeErrors.getDescription());
else if (napplied.size() > 1 || bapplied.size() > 1) {
multipleStereotypes.addViolation(n, multipleStereotypes.getDescription());
}
else if (!napplied.isEmpty() && !bapplied.isEmpty()
&& napplied.iterator().next() != bapplied.iterator().next()) {
Stereotype ns = napplied.iterator().next();
if (!ns.getName().equals(DocGenProfile.tableAttributeColumnStereotype) &&
!ns.getName().equals(DocGenProfile.tableColumnStereotype) &&
!ns.getName().equals(DocGenProfile.tableExpressionColumnStereotype) &&
!ns.getName().equals(DocGenProfile.tablePropertyColumnStereotype)) {
mismatchStereotypeErrors.addViolation(n, mismatchStereotypeErrors.getDescription());
}
}
/*
* if (StereotypesHelper.hasStereotype(b,
* DocGenProfile.sectionStereotype) ||
* StereotypesHelper.hasStereotype(b,
* DocGenProfile.structuredQueryStereotype) ||
* StereotypesHelper.hasStereotypeOrDerived(b,
* DocGenProfile.collectionStereotype) ||
* StereotypesHelper.hasStereotype(b,
* DocGenProfile.tableStructureStereotype)) {
*/
if (!this.done.contains(b)) {
this.done.add(b);
validateActivity(b);
}
// }
}
validateTags(n, b);
}
else if (n instanceof StructuredActivityNode) {
if (!StereotypesHelper.hasStereotype(n, DocGenProfile.structuredQueryStereotype)
&& !StereotypesHelper.hasStereotype(n, DocGenProfile.tableStructureStereotype)
&& StereotypesHelper.checkForAllDerivedStereotypes(n,
DocGenProfile.tableColumnStereotype).isEmpty()) {
missingStereotype.addViolation(n, missingStereotype.getDescription());
}
validateActivity(n);
validateTags(n, null);
}
for (ActivityEdge out : outs) {
ActivityNode next = out.getTarget();
if (graph.containsVertex(next)) {
graph.addEdge(n, next);
continue;
}
else {
graph.addVertex(next);
graph.addEdge(n, next);
validateNode(next, graph);
}
}
}
private void validateTags(ActivityNode node, Behavior b) {
for (String stereotype : requiredTags.keySet()) {
String tag = requiredTags.get(stereotype);
if (StereotypesHelper.hasStereotypeOrDerived(node, stereotype)
&& StereotypesHelper.getStereotypePropertyFirst(node, stereotype, tag) == null) {
if (b == null) {
missingTagValue.addViolation(node, missingTagValue.getDescription());
return;
}
else {
if (StereotypesHelper.hasStereotypeOrDerived(b, stereotype)
&& StereotypesHelper.getStereotypePropertyFirst(b, stereotype, tag) == null) {
missingTagValue.addViolation(b, missingTagValue.getDescription());
return;
}
}
}
}
}
private List<InitialNode> findInitialNodes(NamedElement e) {
List<InitialNode> res = new ArrayList<InitialNode>();
for (Element ee : e.getOwnedElement()) {
if (ee instanceof InitialNode) {
res.add((InitialNode) ee);
}
}
return res;
}
public void printErrors() {
printErrors(true);
}
// the 2 print errors should be consolidated...
public void printErrors(boolean showWindow) {
String fatal = "[FATAL] DocGen: ";
StrongConnectivityInspector<NamedElement, Element> sci = new StrongConnectivityInspector<NamedElement, Element>(
dg);
List<Set<NamedElement>> cycles = sci.stronglyConnectedSets();
if (!cycles.isEmpty()) {
for (Set<NamedElement> cycle : cycles) {
if (cycle.size() > 1) {
this.fatal = true;
log.log("\tView Cycle Set:");
for (NamedElement ne : cycle) {
cycleError.addViolation(ne, cycleError.getDescription());
log.log("\t\t" + ne.getQualifiedName());
}
}
}
}
if (!this.cycles.isEmpty()) {
for (Set<ActivityNode> cycle : this.cycles) {
if (cycle.size() > 1) {
this.fatal = true;
log.log("\tActivityNode Cycle Set:");
for (NamedElement ne : cycle) {
log.log("\t\t" + ne.getQualifiedName());
activityNodeCycleError.addViolation(ne,
activityNodeCycleError.getDescription());
}
}
}
}
if (this.fatal) {
log.log(fatal + "There are loops in this document! Do not generate document!");
}
else if (showWindow) {
log.log("Validation done.");
}
if (showWindow) {
Utils.displayValidationWindow(project, validationOutput, "Document Validation Results");
}
}
public void printErrors(PrintWriter pw) {
String warning = "[WARNING] DocGen: ";
String fatal = "[FATAL] DocGen: ";
String error = "[ERROR] DocGen: ";
/*
* In the following code, I cast the element that violated the rule to a
* NamedElement so that I could get the qualified name that was used in
* the printout pre-Validation Suite (to preserve the printout). This
* could also be done by modifying the ValidationRuleViolation class,
* but it's not necessary. -Peter
*/
for (ValidationRuleViolation e : multipleFirstErrors.getViolations()) {
pw.println(error + ((NamedElement) e.getElement()).getQualifiedName() + " has multiple firsts!");
}
for (ValidationRuleViolation e : multipleNextErrors.getViolations()) {
pw.println(error + ((NamedElement) e.getElement()).getQualifiedName() + " has multiple nexts!");
}
for (ValidationRuleViolation e : multipleContentErrors.getViolations()) {
pw.println(error + ((NamedElement) e.getElement()).getQualifiedName()
+ " has multiple (unstereotyped) dependencies!");
}
for (ValidationRuleViolation e : multipleViewpoints.getViolations()) {
pw.println(error + ((NamedElement) e.getElement()).getQualifiedName()
+ " conforms to multiple viewpoints!");
}
for (ValidationRuleViolation e : missingViewpointErrors.getViolations()) {
pw.println(warning + ((NamedElement) e.getElement()).getQualifiedName()
+ " doesn't conform to any viewpoint!");
}
for (ValidationRuleViolation e : missingImportErrors.getViolations()) {
pw.println(warning + ((NamedElement) e.getElement()).getQualifiedName() + " is missing imports!");
}
for (ValidationRuleViolation e : missingViewpointBehavior.getViolations()) {
pw.println(error + ((NamedElement) e.getElement()).getQualifiedName()
+ " is missing the viewpoint behavior!");
}
for (ValidationRuleViolation e : nonView2View.getViolations()) {
pw.println(warning + ((NamedElement) e.getElement()).getQualifiedName()
+ " is a nonsection but has first or next dependencies!");
}
for (ValidationRuleViolation e : shouldBeSection.getViolations()) {
pw.println(warning + ((NamedElement) e.getElement()).getQualifiedName()
+ " is a nonsection but should be a section (because it's the target of First or Next");
}
for (ValidationRuleViolation e : shouldNotBeSection.getViolations()) {
pw.println(warning + ((NamedElement) e.getElement()).getQualifiedName()
+ " is a section but should not be (because it's the target of a vanilla dependency");
}
for (ValidationRuleViolation e : multipleInitialNode.getViolations()) {
pw.println(error + ((NamedElement) e.getElement()).getQualifiedName()
+ " has multiple initial nodes!");
}
for (ValidationRuleViolation e : multipleOutgoingFlows.getViolations()) {
pw.println(error + ((NamedElement) e.getElement()).getQualifiedName()
+ " has multiple outgoing flows!");
}
for (ValidationRuleViolation e : multipleIncomingFlows.getViolations()) {
pw.println(warning + ((NamedElement) e.getElement()).getQualifiedName()
+ " has multiple incoming flows!");
}
for (ValidationRuleViolation e : missingInitialNode.getViolations()) {
pw.println(error + ((NamedElement) e.getElement()).getQualifiedName()
+ " is missing an initial node!");
}
for (ValidationRuleViolation e : multipleStereotypes.getViolations()) {
pw.println(error + ((NamedElement) e.getElement()).getQualifiedName()
+ " and/or its behavior has multiple stereotypes!");
}
for (ValidationRuleViolation e : mismatchStereotypeErrors.getViolations()) {
pw.println(error + ((NamedElement) e.getElement()).getQualifiedName()
+ " and its behavior have mismatched stereotypes!");
}
for (ValidationRuleViolation e : missingStereotype.getViolations()) {
pw.println(warning + ((NamedElement) e.getElement()).getQualifiedName()
+ " and its behavior (if present) is missing a document stereotype!");
}
StrongConnectivityInspector<NamedElement, Element> sci = new StrongConnectivityInspector<NamedElement, Element>(
dg);
List<Set<NamedElement>> cycles = sci.stronglyConnectedSets();
if (!cycles.isEmpty()) {
for (Set<NamedElement> cycle : cycles) {
if (cycle.size() > 1) {
this.fatal = true;
pw.println("\tView Cycle Set:");
for (NamedElement ne : cycle) {
pw.println("\t\t" + ne.getQualifiedName());
}
}
}
}
if (!this.cycles.isEmpty()) {
for (Set<ActivityNode> cycle : this.cycles) {
if (cycle.size() > 1) {
this.fatal = true;
pw.println("\tActivityNode Cycle Set:");
for (NamedElement ne : cycle) {
pw.println("\t\t" + ne.getQualifiedName());
}
}
}
}
if (this.fatal) {
pw.println(fatal + "There are loops in this document! Do not generate document!");
}
else {
pw.println("Validation done.");
}
}
// REVIEW -- should this function always be called instead of
// ValidationRule.addViolation()? Consider making all rules a subclass of
// ValidationRule that uses a subclass of ValidationRuleViolation that
// implements Comparable so that set inclusion is efficient/elegant.
/**
* Add a violation for the rule only if none of the rules existing
* violations have the same element and comment.
*
* @param rule
* @param element
* @param comment
* @return whether a violation was added
*/
public static boolean addViolationIfUnique(ValidationRule rule, Element element, String comment,
boolean reported) {
if (rule == null) {
return false;
}
List<ValidationRuleViolation> violations = rule.getViolations();
boolean alreadyAdded = false;
if (violations != null) {
for (ValidationRuleViolation v : violations) {
if (Utils2.valuesEqual(v.getElement(), element)
&& Utils2.valuesEqual(v.getComment(), comment)) {
alreadyAdded = true;
break;
}
}
}
if (alreadyAdded) {
return false;
}
rule.addViolation(element, comment, reported);
return true;
}
/**
* Evaluate the expression and, if the violationIfConsistent flag is true
* and the validator is not null, add a validation rule violation if the
* expression is inconsistent.
*
* @param expression
* @param context
* @param validator
* @param violationIfInconsistent
* @return the result of the evaluation
*/
public static Object evaluate(Object expression, Object context, DocumentValidator validator,
boolean violationIfInconsistent) {
ValidationRule rule = validator == null ? null : validator.getViewpointConstraintRule();
return evaluate(expression, context, rule, violationIfInconsistent);
}
/**
* Evaluate the expression and, if the violationIfConsistent flag is true
* and the validator is not null, add a validation rule violation if the
* expression is inconsistent.
*
* @param expression
* @param context
* @param rule
* @param violationIfInconsistent
* @return the result of the evaluation
*/
public static Object evaluate(Object expression, Object context, ValidationRule rule,
boolean violationIfInconsistent) {
if (expression == null) {
return null;
}
Object result = null;
try {
result = OclEvaluator.evaluateQuery(context, expression);
} catch (ParserException e) {
if (violationIfInconsistent) {
String id = context instanceof Element ? Converters.getElementToIdConverter().apply((Element) context) : context.toString();
String errorMessage = e.getLocalizedMessage() + " for OCL query \"" + expression + "\" on "
+ Utils.getName(context) + (showElementIds ? "[" + id + "]" : "");
if (rule != null && context instanceof Element) {
// need fixes to allow context be a collection
addViolationIfUnique(rule, (Element) context, errorMessage, false);
}
Debug.error(violationIfInconsistent, false, errorMessage);
}
}
return result;
}
/**
* Evaluate the constraint and, if the constraint is inconsistent and the
* violatedIfConsistent flag is true and the validator is not null, add a
* validation rule violation. If the constraint evaluates to false, and the
* validator is not null, add a violation.
*
* @param constraint
* @param validator
* @param violatedIfInconsistent
* @return the result of the evaluation
*/
public static Boolean evaluateConstraint(Constraint constraint, DocumentValidator validator,
boolean violatedIfInconsistent) {
ValidationRule rule = validator.getViewpointConstraintRule();
return evaluateConstraint(constraint, rule, violatedIfInconsistent);
}
public static int maxNumberOfViolatingElementsToShow = Integer.MAX_VALUE;
public static boolean showElementIds = true;
protected static boolean loggingResults = true;
/**
* Evaluate the constraint and, if the constraint is inconsistent and the
* violatedIfConsistent flag is true and the validation rule is not null,
* add a rule violation. If the constraint evaluates to false, and the rule
* is not null, add a violation.
*
* @param constraint
* @param rule
* @param violatedIfInconsistent
* @return the result of the evaluation
*/
public static Boolean evaluateConstraint(Constraint constraint, ValidationRule rule,
boolean violatedIfInconsistent) {
if (constraint == null) {
return null;
}
Boolean satisfied = null;
if (constraint instanceof BasicConstraint) {
satisfied = ((BasicConstraint) constraint).evaluate(false);
}
else {
satisfied = constraint.evaluate();
}
if (rule == null) {
return satisfied;
}
// check if constraint is violated
if (satisfied != null && satisfied.equals(Boolean.FALSE)) {
Element violatedElement = constraint.getViolatedConstraintElement();
String comment;
if (constraint instanceof BasicConstraint) {
comment = ((BasicConstraint) constraint).toStringViolated(maxNumberOfViolatingElementsToShow,
showElementIds);
}
else {
comment = constraint.toString();
}
addViolationIfUnique(rule, violatedElement, comment, constraint.isReported());
}
else if (violatedIfInconsistent) {
// check if inconsistent
// TODO -- not yet checking if the constraint is self-contradictory
// (always false independent of context)
Element constrainingElement = (Utils2.isNullOrEmpty(constraint.getConstrainingElements()) ? null
: constraint.getConstrainingElements().iterator().next());
if (!constraint.isConsistent()) {
String msg = ((BasicConstraint) constraint).getErrorMessage();
if (Utils2.isNullOrEmpty(msg)) {
msg = "inconsistent ";
}
else {
msg = msg + " for ";
}
if (constraint instanceof BasicConstraint) {
msg = msg
+ ((BasicConstraint) constraint).toString(maxNumberOfViolatingElementsToShow,
showElementIds);
}
else {
msg = msg + "constraint " + constraint;
}
addViolationIfUnique(rule, constrainingElement, msg, constraint.isReported());
}
}
return satisfied;
}
/**
* Evaluate all constraints on the execution of the constrainedObject. For
* each constraint, add validation rule violations if evaluated to false and
* addViolations is true or if inconsistent (because malformed or
* self-contradictory) and addViolationForInconsistency is true.
*
* @param constrainedObject
* @param actionOutput the result of executing the constrainedObject as an action, to
* which the constraints may applied
* @param context the execution context, providing target elements that passed
* through, to which the constraints may be applied
* @param addViolations
* @param addViolationForInconsistency
* @return the conjunction of the constraint evaluations (false if any are
* false; otherwise null if any are null, else true)
*/
public static Boolean evaluateConstraints(Object constrainedObject, Object actionOutput,
GenerationContext context, boolean addViolations, boolean addViolationForInconsistency) {
Boolean result = true; // false if any false; else, null if any null,
// else true
if (context.getValidator() == null) {
return result;
}
result = true;
List<Constraint> constraints = getConstraints(constrainedObject, actionOutput, context);
if (constrainedObject instanceof Element) {
Element e = (Element) constrainedObject;
Debug.outln("constraints for " + e.getHumanName() + ", " + Converters.getElementToIdConverter().apply(e) + ": "
+ MoreToString.Helper.toString(constraints));
}
else {
Debug.outln("constraints for " + constrainedObject + ": "
+ MoreToString.Helper.toString(constraints));
}
DocumentValidator dv = addViolations ? context.getValidator() : null;
// If generating validation rule violations, evaluate all.
// Result is false if any false; else, null if any null, else true.
// MdDebug.logForce(
// "*** Starting MDK Validate Viewpoint Constraints ***" );
for (Constraint constraint : constraints) {
Debug.outln("found constraint: " + MoreToString.Helper.toString(constraint));
if (Utils2.isNullOrEmpty(constraint.getExpression())) {
continue;
}
Boolean satisfied = evaluateConstraint(constraint, dv, addViolationForInconsistency);
if (loggingResults) {
ConstraintValidationRule.logResults(satisfied, constraint);
}
if (satisfied != null && satisfied.equals(Boolean.FALSE)) {
result = false;
if (dv == null) {
break;
}
}
else if (satisfied == null && Boolean.TRUE.equals(result)) {
result = null;
}
}
// MdDebug.logForce(
// "*** Finished MDK Validate Viewpoint Constraints ***" );
return result;
}
/**
* Gather all constraints applicable to the output or input of the
* constrainedObject or the constrainedObject itself.
*
* @param constrainedObject
* @param actionOutput the result of executing the constrainedObject as an action, to
* which the constraints may applied
* @param context the execution context, providing target elements that passed
* through, to which the constraints may be applied
* @return a list of constraints
*/
public static List<Constraint> getConstraints(Object constrainedObject, Object actionOutput,
GenerationContext context) {
List<Constraint> constraints = new ArrayList<Constraint>();
List<Object> targets = DocumentGenerator.getTargets(constrainedObject, context);
// targets = (List< Element >)BasicConstraint.fixTargets( targets );
List<Element> constraintElements = BasicConstraint.getConstraintElements(constrainedObject,
BasicConstraint.Type.DYNAMIC);
Object[] alternativeContexts = new Object[]{actionOutput, targets, constrainedObject};
// Object[] vpcAlternativeContexts = new Object[] { targets };
Object[] contexts = null;
// constrained = fixTargets( constrained );
for (Element constraintElement : constraintElements) {
List<Object> separatelyConstrained = Utils2.newList();
boolean isVpConstraint = BasicConstraint.elementIsViewpointConstraint(constraintElement);
boolean isExpressionChoosable = StereotypesHelper.hasStereotypeOrDerived(constraintElement,
DocGenProfile.expressionChoosable);
// if ( isVpConstraint) {
Element vpConstraint = constraintElement;
// contexts = vpcAlternativeContexts;
if (!isExpressionChoosable || BasicConstraint.iterateViewpointConstrraint(vpConstraint)) {
separatelyConstrained.addAll(targets);
}
else {
separatelyConstrained.add(targets);
}
// } else {
// separatelyConstrained.add( constrainedObject );
// contexts = alternativeContexts;
// }
for (Object constrained : separatelyConstrained) {
if (isVpConstraint) {
contexts = new Object[]{constrained};
}
else {
contexts = alternativeContexts;
}
Constraint c = BasicConstraint.makeConstraintFromAlternativeContexts(constraintElement,
contexts);
constraints.add(c);
}
}
return constraints;
}
public static List<Dependency> getOutgoingDependencies(Element source, String s) {
List<Dependency> result = new ArrayList<Dependency>();
for (DirectedRelationship dr : source.get_directedRelationshipOfSource()) {
if (StereotypesHelper.hasStereotype(dr, s) && dr instanceof Dependency) {
result.add((Dependency) dr);
}
}
return result;
}
}