blob: bb20e421f01e506007a487955b0bedf30b8e87bb [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015 Willink Transformations and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* E.D.Willink - initial API and implementation
*******************************************************************************/
package org.eclipse.qvtd.pivot.schedule.utilities;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.pivot.NamedElement;
import org.eclipse.qvtd.pivot.qvtcorebase.CoreDomain;
import org.eclipse.qvtd.pivot.qvtimperative.utilities.GraphBuilder;
import org.eclipse.qvtd.pivot.qvtimperative.utilities.GraphMLBuilder.ArrowType;
import org.eclipse.qvtd.pivot.qvtimperative.utilities.GraphMLBuilder.LineType;
import org.eclipse.qvtd.pivot.qvtimperative.utilities.GraphMLBuilder.ShapeType;
import org.eclipse.qvtd.pivot.schedule.AbstractAction;
import org.eclipse.qvtd.pivot.schedule.AbstractDatum;
import org.eclipse.qvtd.pivot.schedule.ClassDatum;
import org.eclipse.qvtd.pivot.schedule.DataParameter;
import org.eclipse.qvtd.pivot.schedule.MappingAction;
import org.eclipse.qvtd.pivot.schedule.PropertyDatum;
import org.eclipse.qvtd.pivot.schedule.Schedule;
import org.eclipse.qvtd.pivot.schedule.ScheduleElement;
import org.eclipse.qvtd.pivot.schedule.util.AbstractExtendingScheduleVisitor;
public class ScheduleToDependencyGraphVisitor extends AbstractExtendingScheduleVisitor<String, GraphBuilder> {
private final List<String> SOL_BASE = Arrays.asList("#fdf6e3", "#eee8d5", "#93a1a1", "#839496", "#657b83", "#586e75", "#073642", "#002b36");
@NonNull private final int SOL_BACKROUND = 0;
@NonNull private final int SOL_BACKROUND_HL = 1;
@NonNull private final int SOL_SECONDARY = 2;
@NonNull private final int SOL_NONE = 3;
@NonNull private final int SOL_PRIMARY = 4;
@NonNull private final int SOL_OPTIONAL = 5;
@NonNull private final String SOL_YELLOW = "#b58900";
@NonNull private final String SOL_ORANGE = "#cb4b16";
@NonNull private final String SOL_RED = "#dc322f";
@NonNull private final String SOL_MAGENTA = "#d33682";
@NonNull private final String SOL_VIOLET = "#6c71c4";
@NonNull private final String SOL_BLUE = "#268bd2";
@NonNull private final String SOL_CYAN = "#2aa198";
@NonNull private final String SOL_GREEN = "#859900";
@NonNull private final String NODE_FILL_COLOR;
@NonNull private final String MAPPING_ACTION_COLOR = SOL_ORANGE;
@NonNull private final String MAPPING_ACTION_SHAPE = ShapeType.hexagon.name();
@NonNull private final String DATUM_INPUT_COLOR = SOL_BLUE;
@NonNull private final String DATUM_MIDDLE_COLOR = SOL_VIOLET;
@NonNull private final String DATUM_TARGET_COLOR = SOL_MAGENTA;
@NonNull private final String DATUM_SHAPE = ShapeType.rectangle.name();
@NonNull private final String SUPER_EDGE_COLOR;
@NonNull private final String PRODUCTION_EDGE_COLOR;
@NonNull private final String REQUISITE_EDGE_COLOR = SOL_GREEN;
@NonNull private final String REQUISITE_MULTIPLE_EDGE_COLOR = SOL_RED;
@NonNull private final String DEPENDENCY_ARROW_END = ArrowType.standard.name();
@NonNull private final String DEPENDENCY_LOOP_ARROW_END = ArrowType.transparent_circle.name();
@NonNull private final String SUPER_ARROW_END = ArrowType.delta.name();
private Map<MutiNamedElementKeyImpl, Integer> nodeOrder = new HashMap<MutiNamedElementKeyImpl, Integer>();
//private Map<CoreDomain, String> domainColor = new HashMap<CoreDomain, String>();
private List<String> outputDirection;
private String middleDirection;
private String inputDirection;
private boolean onlyClassDatums;
public ScheduleToDependencyGraphVisitor(GraphBuilder context) {
this(context, false);
}
public ScheduleToDependencyGraphVisitor(GraphBuilder context, boolean darkTheme) {
super(context);
if (darkTheme) {
Collections.reverse(SOL_BASE);
}
NODE_FILL_COLOR = SOL_BASE.get(SOL_BACKROUND_HL);
SUPER_EDGE_COLOR = SOL_BASE.get(SOL_SECONDARY);
PRODUCTION_EDGE_COLOR = SOL_BASE.get(SOL_PRIMARY);
}
private String getClassId(ClassDatum object) {
String id = //object.getDomain().getName() + "\n" +
object.getType().getName();
return id;
}
private String getDomainColor(CoreDomain domain) {
if (domain != null) {
if (domain.getName().equals(this.inputDirection)) {
return DATUM_INPUT_COLOR;
} else if (domain.getName().equals(this.middleDirection)) {
return DATUM_MIDDLE_COLOR;
} else if (this.outputDirection.contains(domain.getName())) {
return DATUM_TARGET_COLOR;
}
}
return SOL_BASE.get(SOL_OPTIONAL);
}
protected @NonNull String getMappingLabel(@NonNull MappingAction object) {
String id = object.getMapping().getName() + "\n" + "(" + object.getOrder() + ")";
return id;
}
/**
* @param eo
* @return
*/
private Integer getNodeOrder(@NonNull NamedElement... elements) {
MutiNamedElementKeyImpl key = new MutiNamedElementKeyImpl(elements);
Integer order = 0;
if (nodeOrder.containsKey(key)) {
order = nodeOrder.get(key);
} else {
order = nodeOrder.size();
nodeOrder.put(key, order);
}
return order;
}
public List<String> getOutputDirection() {
if (this.outputDirection == null)
this.outputDirection = new ArrayList<String>();
return this.outputDirection;
}
private String getPropertyId(PropertyDatum object) {
String id = //object.getDomain().getName() + "\n" +
object.getClassDatum().getType().getName() + "\n." + object.getProperty().getName();
return id;
}
private boolean nodeExists(@NonNull NamedElement... elements) {
MutiNamedElementKeyImpl key = new MutiNamedElementKeyImpl(elements);
return nodeOrder.containsKey(key);
}
public void setInputDirection(String name) {
this.inputDirection = name;
}
public void setMiddleDirection(String name) {
this.middleDirection = name;
}
@Override
public @Nullable String visitClassDatum(ClassDatum object) {
String datumLabel = getClassId(object);
String order = getNodeOrder(object.getType(), object.getDomain()).toString();
context.appendNode(order, DATUM_SHAPE, NODE_FILL_COLOR, datumLabel, getDomainColor(object.getDomain()));
for (PropertyDatum pd : object.getPropertyDatums()) {
visitPropertyDatum(pd);
}
for (AbstractDatum sd : object.getSuper()) {
if (!nodeExists(((ClassDatum) sd).getType(), sd.getDomain()))
visitClassDatum((ClassDatum) sd);
String sdOrder = getNodeOrder(((ClassDatum) sd).getType(), sd.getDomain()).toString();
context.appendEdge(order,
sdOrder,
SUPER_EDGE_COLOR,
LineType.dotted.name(),
ArrowType.none.name(),
SUPER_ARROW_END);
}
return null;
}
@Override
@Nullable
public String visiting(ScheduleElement visitable) {
throw new IllegalArgumentException("Unsupported " + visitable.eClass().getName() + " for " + getClass().getSimpleName());
}
@Override
public @Nullable String visitMappingAction(MappingAction object) {
String mappingLabel = getMappingLabel(object);
String order = getNodeOrder(object.getMapping()).toString();
context.appendNode(order, MAPPING_ACTION_SHAPE, NODE_FILL_COLOR, mappingLabel, MAPPING_ACTION_COLOR);
String lineType = LineType.line.name();
for(DataParameter out : object.getResults()) {
String targetId = null;
targetId = getNodeOrder(((ClassDatum) out.getDatum()).getType(), ((ClassDatum) out.getDatum()).getDomain()).toString();
assert targetId != null;
context.appendEdge(order,
targetId,
PRODUCTION_EDGE_COLOR,
LineType.line.name(),
ArrowType.none.name(),
DEPENDENCY_ARROW_END);
}
for (AbstractDatum ad : object.getProductions()) {
String targetId = null;
lineType = LineType.line.name();
if (ad instanceof PropertyDatum) {
targetId = getNodeOrder(((PropertyDatum) ad).getProperty(), ((PropertyDatum) ad).getClassDatum().getType(), ad.getDomain()).toString();
assert targetId != null;
if (((PropertyDatum) ad).getProperty().isIsImplicit())
lineType = LineType.dashed.name();
context.appendEdge(order,
targetId,
PRODUCTION_EDGE_COLOR,
lineType,
ArrowType.none.name(),
DEPENDENCY_ARROW_END);
}
}
// One edge per data parameter of the ClassDatum
List<DataParameter> visited = new ArrayList<DataParameter>();
String edgeColor;
String targetArrow;
String targetId = null;
for (DataParameter in : object.getParameters()) {
visited.add(in);
lineType = LineType.line.name();
edgeColor = REQUISITE_EDGE_COLOR;
targetArrow = DEPENDENCY_ARROW_END;
targetId = null;
targetId = getNodeOrder(((ClassDatum) in.getDatum()).getType(), ((ClassDatum) in.getDatum()).getDomain()).toString();
assert targetId != null;
// If two or more DataParameters have this Type, its a MULTIPLE Type dependency
int dpCount = 0;
for (DataParameter dp2 : object.getParameters()) {
if (!visited.contains(dp2) && !dp2.equals(in) && dp2.getDatum().equals(in.getDatum())) {
dpCount++;
}
}
if (dpCount > 0)
edgeColor = REQUISITE_MULTIPLE_EDGE_COLOR;
// If the DataParameter can be derived, the line is dotted
/*
for (ParameterDerivation pd : object.getParameterDerivations()) {
if (pd.getSecondaryParameter().getDataParameter().getDatum().equals(ad)) {
if (pd.getPrimaryParameter() != null)
lineType = LineType.dotted.name(); // FIXME Might not work for multiple derived parameters
if (pd.getSecondaryParameter().isIsLoop()) {
targetArrow = DEPENDENCY_LOOP_ARROW_END;
}
}
}
*/
context.appendEdge(targetId,
order,
edgeColor,
lineType,
ArrowType.none.name(),
targetArrow);
}
for (AbstractDatum ad : object.getRequisites()) {
// One edge per data parameter of the ClassDatum
edgeColor = REQUISITE_EDGE_COLOR;
targetArrow = DEPENDENCY_ARROW_END;
targetId = null;
lineType = LineType.line.name();
if (ad instanceof PropertyDatum) {
targetId = getNodeOrder(((PropertyDatum) ad).getProperty(), ((PropertyDatum) ad).getClassDatum().getType(), ad.getDomain()).toString();
assert targetId != null;
if (((PropertyDatum) ad).getProperty().isIsImplicit())
lineType = LineType.dashed.name();
// Primary and Secondary Information
context.appendEdge(targetId,
order,
edgeColor,
lineType,
ArrowType.none.name(),
targetArrow);
}
}
return null;
}
@Override
public @Nullable String visitPropertyDatum(PropertyDatum object) {
String datumLabel = getPropertyId(object);
String order = getNodeOrder(object.getProperty(), object.getClassDatum().getType(), object.getDomain()).toString();
context.appendNode(order, DATUM_SHAPE, NODE_FILL_COLOR, datumLabel, getDomainColor(object.getDomain()));
for (AbstractDatum sd : object.getSuper()) {
if (!nodeExists(object.getProperty(), object.getClassDatum().getType(), object.getDomain()))
visitPropertyDatum((PropertyDatum) sd);
String sdOrder = getNodeOrder(((PropertyDatum) sd).getProperty(), ((PropertyDatum) sd).getClassDatum().getType(), sd.getDomain()).toString();
context.appendEdge(order,
sdOrder,
SUPER_EDGE_COLOR,
LineType.dotted.name(),
ArrowType.none.name(),
SUPER_ARROW_END);
}
return null;
}
@Override
public @Nullable String visitSchedule(Schedule object) {
context.open();
// First the datums so the nodes exist
for (AbstractDatum ad : object.getDatums()) {
if (ad instanceof ClassDatum) {
visitClassDatum((ClassDatum) ad);
}
}
for (AbstractAction aa : object.getActions()) {
if (aa instanceof MappingAction) {
MappingAction ma = (MappingAction) aa;
if (ma.getMapping() != null)
visitMappingAction(ma);
}
}
context.close();
return null;
}
public boolean isOnlyClassDatums() {
return onlyClassDatums;
}
public void setOnlyClassDatums(boolean onlyClassDatums) {
this.onlyClassDatums = onlyClassDatums;
}
}