mirror of https://github.com/eitch/first-wave.git
mim.canvas initial commit.
This commit is contained in:
parent
edb21f1319
commit
6af1753c0c
|
@ -0,0 +1,4 @@
|
|||
/target
|
||||
/.settings
|
||||
/.project
|
||||
/.classpath
|
|
@ -0,0 +1,22 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>ch.bitwave</groupId>
|
||||
<artifactId>maven.platform.parent</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>mim.canvas</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>ch.bitwave</groupId>
|
||||
<artifactId>mim.meta</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.bitwave</groupId>
|
||||
<artifactId>ui.swing</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -0,0 +1,62 @@
|
|||
package ch.bitwave.mim.canvas.builders;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import ch.bitwave.mim.canvas.features.HorizontalGroupFeature;
|
||||
import ch.bitwave.mim.canvas.features.HorizontalRankFeature;
|
||||
import ch.bitwave.mim.canvas.features.NodeFeature;
|
||||
import ch.bitwave.mim.canvas.generators.TreeNode;
|
||||
import ch.bitwave.mim.canvas.strategies.FitTextSizingStrategy;
|
||||
import ch.bitwave.mim.canvas.strategies.HorizontalFannedConnectorLayoutStrategy;
|
||||
import ch.parametrix.common.util.ui.swing.clap.DelegatedLayoutGoal;
|
||||
|
||||
/**
|
||||
* Renders a tree of nodes in a left-to-right orientation.
|
||||
*/
|
||||
public class HorizontalTreeRenderer extends TreeRenderer {
|
||||
public HorizontalTreeRenderer() {
|
||||
// In a horizontal tree we size the nodes by their labels by default and
|
||||
// fan connectors vertically.
|
||||
setSizingStrategy(new FitTextSizingStrategy());
|
||||
setConnectorLayoutStrategy(new HorizontalFannedConnectorLayoutStrategy());
|
||||
}
|
||||
|
||||
@Override
|
||||
public HorizontalGroupFeature renderTree(final List<TreeNode> rootNodes) {
|
||||
HorizontalGroupFeature root = createNode(null);
|
||||
for (TreeNode node : rootNodes) {
|
||||
renderNode(root, node);
|
||||
}
|
||||
getLayoutProcessor().addGoal(new DelegatedLayoutGoal(root));
|
||||
return root;
|
||||
}
|
||||
|
||||
private void renderNode(final HorizontalGroupFeature parentGroup, final TreeNode node) {
|
||||
NodeFeature nodeFeature = (NodeFeature) getFeatureFactory().createFeatureForElement(
|
||||
node.getElement());
|
||||
configureNode(nodeFeature);
|
||||
addFeature(nodeFeature);
|
||||
HorizontalGroupFeature detailFeature = createNode(nodeFeature);
|
||||
parentGroup.addDetailGroup(detailFeature);
|
||||
renderConnection(parentGroup.getNode(), detailFeature.getNode());
|
||||
for (TreeNode detailNode : node.getDetails()) {
|
||||
renderNode(detailFeature, detailNode);
|
||||
}
|
||||
}
|
||||
|
||||
protected HorizontalGroupFeature createNode(@Nullable final NodeFeature feature) {
|
||||
Object model = null;
|
||||
if (feature != null)
|
||||
model = feature.getElement();
|
||||
HorizontalGroupFeature group = new HorizontalGroupFeature(model);
|
||||
HorizontalRankFeature rank = new HorizontalRankFeature(model);
|
||||
group.setRank(rank);
|
||||
group.setNode(feature);
|
||||
addFeature(group);
|
||||
addFeature(rank);
|
||||
return group;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
package ch.bitwave.mim.canvas.builders;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import ch.bitwave.mim.canvas.features.ClassFeature;
|
||||
import ch.bitwave.mim.canvas.features.ConnectionFeature;
|
||||
import ch.bitwave.mim.canvas.features.GroupFeature;
|
||||
import ch.bitwave.mim.canvas.features.NodeFeature;
|
||||
import ch.bitwave.mim.canvas.features.PackageFeature;
|
||||
import ch.bitwave.mim.canvas.generators.TreeNode;
|
||||
import ch.bitwave.mim.canvas.strategies.ColoringStrategy;
|
||||
import ch.bitwave.mim.canvas.strategies.ConnectorLayoutStrategy;
|
||||
import ch.bitwave.mim.canvas.strategies.SizingStrategy;
|
||||
import ch.bitwave.mim.m2.core.Generalization;
|
||||
import ch.bitwave.mim.m2.core.MimClass;
|
||||
import ch.bitwave.mim.m2.core.MimPackage;
|
||||
import ch.bitwave.mim.m2.core.StubClass;
|
||||
import ch.bitwave.mim.m2.core.StubPackage;
|
||||
import ch.parametrix.common.util.ui.swing.canvas.FeatureFactory;
|
||||
import ch.parametrix.common.util.ui.swing.clap.LayoutProcessor;
|
||||
import ch.parametrix.common.util.ui.swing.contracts.Feature;
|
||||
import ch.parametrix.common.util.ui.swing.contracts.ICanvasFeatureManager;
|
||||
|
||||
public abstract class TreeRenderer {
|
||||
|
||||
private ICanvasFeatureManager featureManager;
|
||||
private FeatureFactory featureFactory;
|
||||
private ColoringStrategy coloringStrategy;
|
||||
private SizingStrategy sizingStrategy;
|
||||
private ConnectorLayoutStrategy connectorLayoutStrategy;
|
||||
private LayoutProcessor clap;
|
||||
|
||||
public TreeRenderer() {
|
||||
this.featureFactory = new FeatureFactory();
|
||||
configureFeatureFactory(this.featureFactory);
|
||||
}
|
||||
|
||||
public LayoutProcessor getLayoutProcessor() {
|
||||
return this.clap;
|
||||
}
|
||||
|
||||
public ConnectorLayoutStrategy getConnectorLayoutStrategy() {
|
||||
return this.connectorLayoutStrategy;
|
||||
}
|
||||
|
||||
public void setConnectorLayoutStrategy(final ConnectorLayoutStrategy connectorLayoutStrategy) {
|
||||
this.connectorLayoutStrategy = connectorLayoutStrategy;
|
||||
}
|
||||
|
||||
public ColoringStrategy getColoringStrategy() {
|
||||
return this.coloringStrategy;
|
||||
}
|
||||
|
||||
public void setColoringStrategy(final ColoringStrategy coloringStrategy) {
|
||||
this.coloringStrategy = coloringStrategy;
|
||||
}
|
||||
|
||||
public SizingStrategy getSizingStrategy() {
|
||||
return this.sizingStrategy;
|
||||
}
|
||||
|
||||
public void setSizingStrategy(final SizingStrategy sizingStrategy) {
|
||||
this.sizingStrategy = sizingStrategy;
|
||||
}
|
||||
|
||||
public FeatureFactory getFeatureFactory() {
|
||||
return this.featureFactory;
|
||||
}
|
||||
|
||||
protected void configureFeatureFactory(final FeatureFactory factory) {
|
||||
factory.put(StubPackage.class, PackageFeature.class);
|
||||
factory.put(MimPackage.class, PackageFeature.class);
|
||||
factory.put(Generalization.class, ConnectionFeature.class);
|
||||
factory.put(StubClass.class, ClassFeature.class);
|
||||
factory.put(MimClass.class, ClassFeature.class);
|
||||
}
|
||||
|
||||
public void configure(final ICanvasFeatureManager featureManager, final LayoutProcessor clap) {
|
||||
this.featureManager = featureManager;
|
||||
this.clap = clap;
|
||||
}
|
||||
|
||||
protected void addFeature(final Feature feature) {
|
||||
this.featureManager.addFeature(feature);
|
||||
}
|
||||
|
||||
public abstract GroupFeature renderTree(final List<TreeNode> rootNodes);
|
||||
|
||||
protected void configureNode(final NodeFeature nodeFeature) {
|
||||
nodeFeature.setConnectorLayoutStrategy(this.getConnectorLayoutStrategy());
|
||||
nodeFeature.setColoringStrategy(this.getColoringStrategy());
|
||||
nodeFeature.setSizingStrategy(this.getSizingStrategy());
|
||||
}
|
||||
|
||||
protected void renderConnection(final NodeFeature master, final NodeFeature detail) {
|
||||
if (master != null) {
|
||||
ConnectionFeature con = new ConnectionFeature(master.getElement());
|
||||
master.addOutboundConnector(con.getSourceFeature());
|
||||
detail.addInboundConnector(con.getTargetFeature());
|
||||
addFeature(con.getSourceFeature());
|
||||
addFeature(con.getTargetFeature());
|
||||
addFeature(con);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package ch.bitwave.mim.canvas.builders;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import ch.bitwave.mim.canvas.features.GroupFeature;
|
||||
import ch.bitwave.mim.canvas.features.NodeFeature;
|
||||
import ch.bitwave.mim.canvas.features.VerticalGroupFeature;
|
||||
import ch.bitwave.mim.canvas.features.VerticalRankFeature;
|
||||
import ch.bitwave.mim.canvas.generators.TreeNode;
|
||||
import ch.bitwave.mim.canvas.strategies.VerticalFannedConnectorLayoutStrategy;
|
||||
import ch.parametrix.common.util.ui.swing.clap.DelegatedLayoutGoal;
|
||||
|
||||
/**
|
||||
* Renders a tree of nodes in a top-to-bottom orientation.
|
||||
*/
|
||||
public class VerticalTreeRenderer extends TreeRenderer {
|
||||
|
||||
public VerticalTreeRenderer() {
|
||||
setConnectorLayoutStrategy(new VerticalFannedConnectorLayoutStrategy());
|
||||
}
|
||||
|
||||
@Override
|
||||
public GroupFeature renderTree(final List<TreeNode> rootNodes) {
|
||||
VerticalGroupFeature root = createNode(null);
|
||||
for (TreeNode node : rootNodes) {
|
||||
renderNode(root, node);
|
||||
}
|
||||
getLayoutProcessor().addGoal(new DelegatedLayoutGoal(root));
|
||||
return root;
|
||||
}
|
||||
|
||||
private void renderNode(final VerticalGroupFeature parentGroup, final TreeNode node) {
|
||||
NodeFeature nodeFeature = (NodeFeature) getFeatureFactory().createFeatureForElement(
|
||||
node.getElement());
|
||||
configureNode(nodeFeature);
|
||||
addFeature(nodeFeature);
|
||||
VerticalGroupFeature detailFeature = createNode(nodeFeature);
|
||||
parentGroup.addDetailGroup(detailFeature);
|
||||
renderConnection(parentGroup.getNode(), detailFeature.getNode());
|
||||
for (TreeNode detailNode : node.getDetails()) {
|
||||
renderNode(detailFeature, detailNode);
|
||||
}
|
||||
}
|
||||
|
||||
protected VerticalGroupFeature createNode(@Nullable final NodeFeature feature) {
|
||||
Object model = null;
|
||||
if (feature != null)
|
||||
model = feature.getElement();
|
||||
VerticalGroupFeature group = new VerticalGroupFeature(model);
|
||||
VerticalRankFeature rank = new VerticalRankFeature(model);
|
||||
group.setRank(rank);
|
||||
group.setNode(feature);
|
||||
addFeature(group);
|
||||
addFeature(rank);
|
||||
return group;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
package ch.bitwave.mim.canvas.features;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics2D;
|
||||
|
||||
import ch.bitwave.mim.m2.core.MimClass;
|
||||
import ch.bitwave.mim.m2.core.MimElement;
|
||||
import ch.bitwave.mim.m2.core.NamedElement;
|
||||
import ch.parametrix.common.util.ui.swing.TextRenderer;
|
||||
import ch.parametrix.common.util.ui.swing.contracts.ICanvasFeatureProvider;
|
||||
import ch.parametrix.common.util.ui.swing.contracts.WorldDistance;
|
||||
|
||||
public class ClassFeature extends NodeFeature {
|
||||
|
||||
public ClassFeature(final Object element) {
|
||||
super(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIdentifier() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WorldDistance getInitialSize() {
|
||||
return new WorldDistance(1000, 600);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderNode(final ICanvasFeatureProvider provider, final Graphics2D g,
|
||||
final int ax, final int ay, final Color featureColor, final boolean isSelected) {
|
||||
MimElement elem = (MimElement) getElement();
|
||||
int w = getWidth();
|
||||
int h = getHeight();
|
||||
g.setColor(featureColor);
|
||||
g.fillRect(ax, ay, w, h);
|
||||
TextRenderer tr = new TextRenderer(g, ax + 50, ay + 150, w - 100, false);
|
||||
renderLabel(elem, g, tr);
|
||||
g.setColor(Color.BLACK);
|
||||
g.setStroke(NODE_OUTLINE_STROKE);
|
||||
g.drawRect(ax, ay, w, h);
|
||||
setBounds(ax, ay, w, h);
|
||||
}
|
||||
|
||||
private void renderLabel(final MimElement elem, final Graphics2D g, final TextRenderer tr) {
|
||||
if (elem instanceof NamedElement) {
|
||||
g.setColor(Color.BLACK);
|
||||
if (isAbstract(elem)) {
|
||||
tr.setFontStyle(Font.BOLD | Font.ITALIC);
|
||||
} else {
|
||||
tr.setFontStyle(Font.BOLD);
|
||||
}
|
||||
String name = ((NamedElement) elem).getName();
|
||||
tr.renderTextCentered(name);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAbstract(final MimElement elem) {
|
||||
return elem instanceof MimClass && ((MimClass) elem).isAbstract();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package ch.bitwave.mim.canvas.features;
|
||||
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Stroke;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
|
||||
import ch.parametrix.common.util.ui.swing.contracts.Feature;
|
||||
import ch.parametrix.common.util.ui.swing.contracts.ICanvasFeatureProvider;
|
||||
import ch.parametrix.common.util.ui.swing.contracts.WorldDistance;
|
||||
|
||||
public class ConnectionFeature extends Feature {
|
||||
|
||||
private ConnectionNodeFeature sourceFeature;
|
||||
private ConnectionNodeFeature targetFeature;
|
||||
protected static final Stroke CONNECTION_STROKE = new BasicStroke(2.5f);
|
||||
|
||||
public ConnectionFeature(final Object element) {
|
||||
super(element);
|
||||
this.sourceFeature = new ConnectionNodeFeature(element);
|
||||
this.targetFeature = new ConnectionNodeFeature(element);
|
||||
}
|
||||
|
||||
public ConnectionNodeFeature getSourceFeature() {
|
||||
return this.sourceFeature;
|
||||
}
|
||||
|
||||
public ConnectionNodeFeature getTargetFeature() {
|
||||
return this.targetFeature;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIdentifier() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WorldDistance getInitialSize() {
|
||||
return new WorldDistance(100, 100);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void render(final ICanvasFeatureProvider provider, final Graphics2D g, final int ax,
|
||||
final int ay, final Color featureColor, final boolean isSelected) {
|
||||
Rectangle2D bounds1 = this.sourceFeature.getBounds();
|
||||
int x1 = (int) bounds1.getCenterX();
|
||||
int y1 = (int) bounds1.getCenterY();
|
||||
Rectangle2D bounds2 = this.targetFeature.getBounds();
|
||||
int x2 = (int) bounds2.getCenterX();
|
||||
int y2 = (int) bounds2.getCenterY();
|
||||
g.setStroke(CONNECTION_STROKE);
|
||||
g.drawLine(x1, y1, x2, y2);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package ch.bitwave.mim.canvas.features;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
|
||||
import ch.parametrix.common.util.ui.swing.contracts.Feature;
|
||||
import ch.parametrix.common.util.ui.swing.contracts.ICanvasFeatureProvider;
|
||||
import ch.parametrix.common.util.ui.swing.contracts.WorldDistance;
|
||||
|
||||
public class ConnectionNodeFeature extends Feature {
|
||||
|
||||
public ConnectionNodeFeature(final Object element) {
|
||||
super(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIdentifier() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WorldDistance getInitialSize() {
|
||||
return new WorldDistance(0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void render(final ICanvasFeatureProvider provider, final Graphics2D g, final int ax,
|
||||
final int ay, final Color featureColor, final boolean isSelected) {
|
||||
// Connector nodes are not visible themselves.
|
||||
setBounds(ax, ay, getWidth(), getHeight());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
package ch.bitwave.mim.canvas.features;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
|
||||
import ch.parametrix.common.util.ui.swing.contracts.Feature;
|
||||
import ch.parametrix.common.util.ui.swing.contracts.ICanvasFeatureProvider;
|
||||
import ch.parametrix.common.util.ui.swing.contracts.ILayoutCapable;
|
||||
import ch.parametrix.common.util.ui.swing.contracts.WorldDistance;
|
||||
|
||||
public abstract class GroupFeature extends Feature implements ILayoutCapable {
|
||||
private NodeFeature node;
|
||||
|
||||
public GroupFeature(final Object element) {
|
||||
super(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIdentifier() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void render(final ICanvasFeatureProvider provider, final Graphics2D g, final int ax,
|
||||
final int ay, final Color featureColor, final boolean isSelected) {
|
||||
setBounds(ax, ay, getWidth(), getHeight());
|
||||
}
|
||||
|
||||
public void setNode(final NodeFeature node) {
|
||||
this.node = node;
|
||||
if (node != null) {
|
||||
this.node.setParent(this);
|
||||
}
|
||||
}
|
||||
|
||||
public NodeFeature getNode() {
|
||||
return this.node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean layout(final Graphics2D g) {
|
||||
resize(g);
|
||||
reposition();
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract void resize(final Graphics2D g);
|
||||
|
||||
public abstract void reposition();
|
||||
|
||||
protected WorldDistance getNodeSize() {
|
||||
WorldDistance nodeSize = null;
|
||||
if (this.node != null) {
|
||||
nodeSize = this.node.getSize();
|
||||
} else {
|
||||
nodeSize = new WorldDistance(0, 0);
|
||||
}
|
||||
return nodeSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WorldDistance getInitialSize() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSelectable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
package ch.bitwave.mim.canvas.features;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
|
||||
import ch.parametrix.common.util.ui.swing.contracts.WorldDistance;
|
||||
|
||||
/**
|
||||
* Centers a node at the left of a single vertical rank of details where the
|
||||
* details flow from the top to the bottom.
|
||||
*/
|
||||
public class HorizontalGroupFeature extends GroupFeature {
|
||||
private final static double RANK_TO_NODE_SPACING = 500;
|
||||
private HorizontalRankFeature rank;
|
||||
|
||||
public HorizontalGroupFeature(final Object element) {
|
||||
super(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WorldDistance getInitialSize() {
|
||||
return new WorldDistance(1000, 3000);
|
||||
}
|
||||
|
||||
public void setRank(final HorizontalRankFeature rank) {
|
||||
this.rank = rank;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reposition() {
|
||||
WorldDistance nodeSize = getNodeSize();
|
||||
double y = this.getTop();
|
||||
double x = this.getLeft();
|
||||
NodeFeature node = getNode();
|
||||
if (node != null) {
|
||||
// The node is centered vertically within the group
|
||||
double centerY = y + (this.getSize().getDy() - nodeSize.getDy()) / 2;
|
||||
node.place(x, centerY);
|
||||
node.reposition();
|
||||
this.rank.placeX(node.getRight() + RANK_TO_NODE_SPACING);
|
||||
} else {
|
||||
// If there is no feature, the rank just hugs the right of the
|
||||
// group.
|
||||
this.rank.placeX(x);
|
||||
}
|
||||
this.rank.placeY(y);
|
||||
this.rank.reposition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resize(final Graphics2D g) {
|
||||
NodeFeature node = getNode();
|
||||
if (node != null) {
|
||||
node.resize(g);
|
||||
}
|
||||
WorldDistance nodeSize = getNodeSize();
|
||||
this.rank.resize(g);
|
||||
double w = this.rank.getWidth();
|
||||
double h = Math.max(nodeSize.getDy(), this.rank.getSize().getDy());
|
||||
if (node != null) {
|
||||
w += nodeSize.getDx() + RANK_TO_NODE_SPACING;
|
||||
}
|
||||
setSize(w, h);
|
||||
}
|
||||
|
||||
public void addDetailGroup(final HorizontalGroupFeature detail) {
|
||||
this.rank.addDetailGroup(detail);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package ch.bitwave.mim.canvas.features;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ch.parametrix.common.util.ui.swing.contracts.Feature;
|
||||
import ch.parametrix.common.util.ui.swing.contracts.ICanvasFeatureProvider;
|
||||
import ch.parametrix.common.util.ui.swing.contracts.WorldDistance;
|
||||
|
||||
public class HorizontalRankFeature extends Feature {
|
||||
private final double GROUP_SPACING = 100;
|
||||
|
||||
private List<HorizontalGroupFeature> detailGroups;
|
||||
|
||||
public HorizontalRankFeature(final Object element) {
|
||||
super(element);
|
||||
this.detailGroups = new ArrayList<HorizontalGroupFeature>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIdentifier() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WorldDistance getInitialSize() {
|
||||
return new WorldDistance(1000, 100);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void render(final ICanvasFeatureProvider provider, final Graphics2D g, final int ax,
|
||||
final int ay, final Color featureColor, final boolean isSelected) {
|
||||
setBounds(ax, ay, getWidth(), getHeight());
|
||||
}
|
||||
|
||||
public void reposition() {
|
||||
double x = getLeft();
|
||||
double y = getTop();
|
||||
for (int i = 0; i < this.detailGroups.size(); i++) {
|
||||
if (i > 0) {
|
||||
y += this.GROUP_SPACING;
|
||||
}
|
||||
HorizontalGroupFeature detail = this.detailGroups.get(i);
|
||||
detail.place(x, y);
|
||||
detail.reposition();
|
||||
y += detail.getSize().getDy();
|
||||
}
|
||||
}
|
||||
|
||||
public void resize(final Graphics2D g) {
|
||||
double h = 0;
|
||||
double w = 0;
|
||||
for (int i = 0; i < this.detailGroups.size(); i++) {
|
||||
if (i > 0) {
|
||||
h += this.GROUP_SPACING;
|
||||
}
|
||||
HorizontalGroupFeature detail = this.detailGroups.get(i);
|
||||
detail.resize(g);
|
||||
h += detail.getSize().getDy();
|
||||
double detailW = detail.getSize().getDx();
|
||||
if (w < detailW) {
|
||||
w = detailW;
|
||||
}
|
||||
}
|
||||
setSize(w, h);
|
||||
}
|
||||
|
||||
public void addDetailGroup(final HorizontalGroupFeature detail) {
|
||||
this.detailGroups.add(detail);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSelectable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
package ch.bitwave.mim.canvas.features;
|
||||
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Stroke;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ch.bitwave.mim.canvas.strategies.ColoringStrategy;
|
||||
import ch.bitwave.mim.canvas.strategies.ConnectorLayoutStrategy;
|
||||
import ch.bitwave.mim.canvas.strategies.SizingStrategy;
|
||||
import ch.parametrix.common.util.ui.swing.contracts.Feature;
|
||||
import ch.parametrix.common.util.ui.swing.contracts.ICanvasFeatureProvider;
|
||||
import ch.parametrix.common.util.ui.swing.contracts.WorldDistance;
|
||||
|
||||
/**
|
||||
* A feature for representing a node within a structure, e.g. a tree.
|
||||
*/
|
||||
public abstract class NodeFeature extends Feature {
|
||||
|
||||
private SizingStrategy sizingStrategy;
|
||||
private ColoringStrategy coloringStrategy;
|
||||
private ConnectorLayoutStrategy connectorLayoutStrategy;
|
||||
private List<ConnectionNodeFeature> outboundConnectors;
|
||||
private List<ConnectionNodeFeature> inboundConnectors;
|
||||
protected static final Stroke NODE_OUTLINE_STROKE = new BasicStroke(5f);
|
||||
|
||||
public NodeFeature(final Object element) {
|
||||
super(element);
|
||||
this.outboundConnectors = new ArrayList<ConnectionNodeFeature>();
|
||||
this.inboundConnectors = new ArrayList<ConnectionNodeFeature>();
|
||||
}
|
||||
|
||||
public ConnectorLayoutStrategy getConnectorLayoutStrategy() {
|
||||
return this.connectorLayoutStrategy;
|
||||
}
|
||||
|
||||
public void setConnectorLayoutStrategy(final ConnectorLayoutStrategy connectorLayoutStrategy) {
|
||||
this.connectorLayoutStrategy = connectorLayoutStrategy;
|
||||
}
|
||||
|
||||
public SizingStrategy getSizingStrategy() {
|
||||
return this.sizingStrategy;
|
||||
}
|
||||
|
||||
public void setSizingStrategy(final SizingStrategy sizingStrategy) {
|
||||
this.sizingStrategy = sizingStrategy;
|
||||
}
|
||||
|
||||
public ColoringStrategy getColoringStrategy() {
|
||||
return this.coloringStrategy;
|
||||
}
|
||||
|
||||
public void setColoringStrategy(final ColoringStrategy coloringStrategy) {
|
||||
this.coloringStrategy = coloringStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void render(final ICanvasFeatureProvider provider, final Graphics2D g,
|
||||
final int ax, final int ay, final Color featureColor, final boolean isSelected) {
|
||||
Color color = featureColor;
|
||||
if (this.coloringStrategy != null) {
|
||||
color = this.coloringStrategy.getColor(getElement());
|
||||
}
|
||||
renderNode(provider, g, ax, ay, color, isSelected);
|
||||
}
|
||||
|
||||
protected abstract void renderNode(ICanvasFeatureProvider provider, Graphics2D g, int ax,
|
||||
int ay, Color color, boolean isSelected);
|
||||
|
||||
public void reposition() {
|
||||
if (this.connectorLayoutStrategy != null) {
|
||||
this.connectorLayoutStrategy.layoutConnectorNodes(this, this.outboundConnectors,
|
||||
this.inboundConnectors);
|
||||
}
|
||||
}
|
||||
|
||||
public void resize(final Graphics2D g) {
|
||||
if (this.sizingStrategy != null) {
|
||||
Object element = getElement();
|
||||
WorldDistance size = this.sizingStrategy.getSize(g, element);
|
||||
setSize(size);
|
||||
}
|
||||
}
|
||||
|
||||
public void addOutboundConnector(final ConnectionNodeFeature out) {
|
||||
this.outboundConnectors.add(out);
|
||||
}
|
||||
|
||||
public void addInboundConnector(final ConnectionNodeFeature out) {
|
||||
this.inboundConnectors.add(out);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package ch.bitwave.mim.canvas.features;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics2D;
|
||||
|
||||
import ch.bitwave.mim.m2.core.MimElement;
|
||||
import ch.bitwave.mim.m2.core.NamedElement;
|
||||
import ch.parametrix.common.util.ui.swing.TextRenderer;
|
||||
import ch.parametrix.common.util.ui.swing.contracts.ICanvasFeatureProvider;
|
||||
import ch.parametrix.common.util.ui.swing.contracts.WorldDistance;
|
||||
|
||||
public class PackageFeature extends NodeFeature {
|
||||
|
||||
private static final Color STUB_BG = new Color(1.0f, 0.8f, 0.8f);
|
||||
private static final Color PACKAGE_BG = new Color(0.8f, 0.85f, 1.0f);
|
||||
|
||||
public PackageFeature(final Object element) {
|
||||
super(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIdentifier() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WorldDistance getInitialSize() {
|
||||
return new WorldDistance(1000, 600);
|
||||
}
|
||||
|
||||
private void renderLabel(final MimElement elem, final Graphics2D g, final TextRenderer tr) {
|
||||
if (elem instanceof NamedElement) {
|
||||
g.setColor(Color.BLACK);
|
||||
tr.setFontStyle(Font.BOLD);
|
||||
String name = ((NamedElement) elem).getName();
|
||||
tr.renderText(name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderNode(final ICanvasFeatureProvider provider, final Graphics2D g,
|
||||
final int ax, final int ay, final Color color, final boolean isSelected) {
|
||||
MimElement elem = (MimElement) getElement();
|
||||
int w = getWidth();
|
||||
int h = getHeight();
|
||||
if (elem.isStub()) {
|
||||
g.setColor(STUB_BG);
|
||||
} else {
|
||||
g.setColor(PACKAGE_BG);
|
||||
}
|
||||
g.fillRect(ax, ay, w, h);
|
||||
TextRenderer tr = new TextRenderer(g, ax + 50, ay + 200, w - 100, false);
|
||||
renderLabel(elem, g, tr);
|
||||
g.setColor(Color.BLACK);
|
||||
g.drawRect(ax, ay, w, h);
|
||||
setBounds(ax, ay, w, h);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package ch.bitwave.mim.canvas.features;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
|
||||
import ch.parametrix.common.util.ui.swing.contracts.WorldDistance;
|
||||
|
||||
/**
|
||||
* Centers a node at the top of a single horizontal rank of details where the
|
||||
* details flow from the left to the right.
|
||||
*/
|
||||
public class VerticalGroupFeature extends GroupFeature {
|
||||
private final static double RANK_TO_NODE_SPACING = 200;
|
||||
private VerticalRankFeature rank;
|
||||
|
||||
public VerticalGroupFeature(final Object element) {
|
||||
super(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WorldDistance getInitialSize() {
|
||||
return new WorldDistance(3000, 1000);
|
||||
}
|
||||
|
||||
public void setRank(final VerticalRankFeature rank) {
|
||||
this.rank = rank;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reposition() {
|
||||
WorldDistance nodeSize = getNodeSize();
|
||||
double y = this.getTop();
|
||||
double x = this.getLeft();
|
||||
NodeFeature node = getNode();
|
||||
if (node != null) {
|
||||
// The node is offset vertically within the group
|
||||
double centerX = x + (this.getSize().getDx() - nodeSize.getDx()) / 2;
|
||||
node.place(centerX, y);
|
||||
node.reposition();
|
||||
this.rank.placeY(y + node.getBottom() + RANK_TO_NODE_SPACING);
|
||||
} else {
|
||||
// If there is no feature, the rank just hugs the top of the group.
|
||||
this.rank.placeY(y);
|
||||
}
|
||||
this.rank.placeX(x);
|
||||
this.rank.reposition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resize(final Graphics2D g) {
|
||||
NodeFeature node = getNode();
|
||||
if (node != null) {
|
||||
node.resize(g);
|
||||
}
|
||||
WorldDistance nodeSize = getNodeSize();
|
||||
this.rank.resize(g);
|
||||
double w = Math.max(nodeSize.getDx(), this.rank.getSize().getDx());
|
||||
double h = this.rank.getHeight();
|
||||
if (getNode() != null) {
|
||||
h += nodeSize.getDy() + RANK_TO_NODE_SPACING;
|
||||
}
|
||||
setSize(w, h);
|
||||
}
|
||||
|
||||
public void addDetailGroup(final VerticalGroupFeature detail) {
|
||||
this.rank.addDetailGroup(detail);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package ch.bitwave.mim.canvas.features;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ch.parametrix.common.util.ui.swing.contracts.Feature;
|
||||
import ch.parametrix.common.util.ui.swing.contracts.ICanvasFeatureProvider;
|
||||
import ch.parametrix.common.util.ui.swing.contracts.WorldDistance;
|
||||
|
||||
public class VerticalRankFeature extends Feature {
|
||||
private final double GROUP_SPACING = 200;
|
||||
|
||||
private List<VerticalGroupFeature> detailGroups;
|
||||
|
||||
public VerticalRankFeature(final Object element) {
|
||||
super(element);
|
||||
this.detailGroups = new ArrayList<VerticalGroupFeature>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIdentifier() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WorldDistance getInitialSize() {
|
||||
return new WorldDistance(1000, 100);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void render(final ICanvasFeatureProvider provider, final Graphics2D g, final int ax,
|
||||
final int ay, final Color featureColor, final boolean isSelected) {
|
||||
setBounds(ax, ay, getWidth(), getHeight());
|
||||
}
|
||||
|
||||
public void reposition() {
|
||||
double x = getLeft();
|
||||
double y = getTop();
|
||||
for (int i = 0; i < this.detailGroups.size(); i++) {
|
||||
if (i > 0) {
|
||||
x += this.GROUP_SPACING;
|
||||
}
|
||||
VerticalGroupFeature detail = this.detailGroups.get(i);
|
||||
detail.place(x, y);
|
||||
detail.reposition();
|
||||
x += detail.getSize().getDx();
|
||||
}
|
||||
}
|
||||
|
||||
public void resize(final Graphics2D g) {
|
||||
double h = 0;
|
||||
double w = 0;
|
||||
for (int i = 0; i < this.detailGroups.size(); i++) {
|
||||
if (i > 0) {
|
||||
w += this.GROUP_SPACING;
|
||||
}
|
||||
VerticalGroupFeature detail = this.detailGroups.get(i);
|
||||
detail.resize(g);
|
||||
w += detail.getSize().getDx();
|
||||
double detailH = detail.getSize().getDy();
|
||||
if (h < detailH) {
|
||||
h = detailH;
|
||||
}
|
||||
}
|
||||
setSize(w, h);
|
||||
}
|
||||
|
||||
public void addDetailGroup(final VerticalGroupFeature detail) {
|
||||
this.detailGroups.add(detail);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSelectable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package ch.bitwave.mim.canvas.generators;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import ch.bitwave.mim.m2.core.Classifier;
|
||||
import ch.bitwave.mim.m2.core.Generalization;
|
||||
import ch.bitwave.mim.m2.core.MimClass;
|
||||
import ch.bitwave.mim.m2.core.MimPackage;
|
||||
|
||||
public class ClassDependencyTreeGenerator extends TreeGenerator {
|
||||
|
||||
private ElementFilter rootFilter;
|
||||
|
||||
public ClassDependencyTreeGenerator(@Nonnull final ElementFilter rootFilter) {
|
||||
super();
|
||||
this.rootFilter = rootFilter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TreeNode> build(final MimPackage root) {
|
||||
List<TreeNode> result = new ArrayList<TreeNode>();
|
||||
Set<MimClass> elementSet = root.getAllOwnedClasses();
|
||||
List<MimClass> treeRoot = getRootElements(elementSet, this.rootFilter);
|
||||
for (MimClass mimClass : treeRoot) {
|
||||
TreeNode rootNode = new TreeNode(mimClass);
|
||||
result.add(rootNode);
|
||||
buildNode(rootNode, mimClass);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void buildNode(final TreeNode node, final Classifier generalClass) {
|
||||
List<Generalization> specializations = generalClass.getSpecializations();
|
||||
for (Generalization gen : specializations) {
|
||||
Classifier specific = gen.getSpecific();
|
||||
TreeNode detailNode = new TreeNode(gen, specific);
|
||||
node.addDetail(detailNode);
|
||||
buildNode(detailNode, specific);
|
||||
}
|
||||
}
|
||||
|
||||
private List<MimClass> getRootElements(final Set<MimClass> elementSet,
|
||||
final ElementFilter rootFilter) {
|
||||
List<MimClass> result = new ArrayList<MimClass>();
|
||||
for (MimClass mimClass : elementSet) {
|
||||
if (rootFilter.accepts(mimClass)) {
|
||||
result.add(mimClass);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package ch.bitwave.mim.canvas.generators;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import ch.bitwave.mim.m2.core.MimElement;
|
||||
import ch.bitwave.mim.m2.core.NamedElement;
|
||||
|
||||
public class ElementByNameFilter extends ElementFilter {
|
||||
private Pattern pattern;
|
||||
private Matcher matcher;
|
||||
|
||||
public ElementByNameFilter(final String selectionPattern, final boolean caseSensitive) {
|
||||
int flags = caseSensitive ? 0 : Pattern.CASE_INSENSITIVE;
|
||||
this.pattern = Pattern.compile(selectionPattern, flags);
|
||||
this.matcher = this.pattern.matcher("");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accepts(final MimElement element) {
|
||||
if (!(element instanceof NamedElement)) {
|
||||
return false;
|
||||
}
|
||||
NamedElement ne = (NamedElement) element;
|
||||
this.matcher.reset(ne.getName());
|
||||
return this.matcher.matches();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package ch.bitwave.mim.canvas.generators;
|
||||
|
||||
import ch.bitwave.mim.m2.core.MimElement;
|
||||
|
||||
public abstract class ElementFilter {
|
||||
|
||||
public abstract boolean accepts(final MimElement element);
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package ch.bitwave.mim.canvas.generators;
|
||||
|
||||
import ch.bitwave.mim.m2.core.MimElement;
|
||||
|
||||
public class ElementPassthroughFilter extends ElementFilter {
|
||||
|
||||
public ElementPassthroughFilter() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accepts(final MimElement element) {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package ch.bitwave.mim.canvas.generators;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ch.bitwave.mim.m2.core.MimPackage;
|
||||
import ch.bitwave.mim.m2.core.Namespace;
|
||||
import ch.bitwave.mim.m2.core.PackageImport;
|
||||
|
||||
public class PackageDependencyTreeGenerator extends TreeGenerator {
|
||||
|
||||
private boolean hideStubs;
|
||||
|
||||
public PackageDependencyTreeGenerator(final boolean hideStubs) {
|
||||
super();
|
||||
this.hideStubs = hideStubs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TreeNode> build(final MimPackage root) {
|
||||
List<MimPackage> elementSet = root.getAllOwnedPackages();
|
||||
List<TreeNode> result = new ArrayList<TreeNode>();
|
||||
for (MimPackage mimPackage : elementSet) {
|
||||
result.add(buildNode(mimPackage));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private TreeNode buildNode(final Namespace pack) {
|
||||
List<PackageImport> importers = pack.getImportingPackages();
|
||||
TreeNode master = new TreeNode(pack);
|
||||
for (PackageImport imp : importers) {
|
||||
Namespace importing = imp.getImportingNamespace();
|
||||
if (!(this.hideStubs && importing.isStub())) {
|
||||
TreeNode detail = buildNode(importing);
|
||||
master.addDetail(detail);
|
||||
}
|
||||
}
|
||||
return master;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package ch.bitwave.mim.canvas.generators;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import ch.bitwave.mim.m2.core.MimPackage;
|
||||
|
||||
public abstract class TreeGenerator {
|
||||
|
||||
public abstract List<TreeNode> build(MimPackage root);
|
||||
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
package ch.bitwave.mim.canvas.generators;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import ch.bitwave.mim.m2.core.MimElement;
|
||||
import ch.bitwave.mim.m2.core.NamedElement;
|
||||
import ch.bitwave.mim.m2.core.Relationship;
|
||||
|
||||
public class TreeNode {
|
||||
|
||||
private Relationship masterRelationship;
|
||||
private MimElement element;
|
||||
private List<TreeNode> details;
|
||||
private TreeNode parent;
|
||||
|
||||
public TreeNode(final MimElement element) {
|
||||
this.element = element;
|
||||
this.details = new ArrayList<TreeNode>();
|
||||
}
|
||||
|
||||
public TreeNode(final Relationship masterRelationship, final MimElement element) {
|
||||
this(element);
|
||||
this.masterRelationship = masterRelationship;
|
||||
}
|
||||
|
||||
public MimElement getElement() {
|
||||
return this.element;
|
||||
}
|
||||
|
||||
public void setElement(final MimElement element) {
|
||||
this.element = element;
|
||||
}
|
||||
|
||||
public Relationship getMasterRelationship() {
|
||||
return this.masterRelationship;
|
||||
}
|
||||
|
||||
public void setMasterRelationship(final Relationship masterRelationship) {
|
||||
this.masterRelationship = masterRelationship;
|
||||
}
|
||||
|
||||
public List<TreeNode> getDetails() {
|
||||
return this.details;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("TreeNode [masterRelationship=").append(this.masterRelationship)
|
||||
.append(", element=").append(this.element).append(", details=")
|
||||
.append(this.details).append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public void addDetail(@Nonnull final TreeNode detail) {
|
||||
this.details.add(detail);
|
||||
detail.setParent(this);
|
||||
}
|
||||
|
||||
protected void setParent(final TreeNode treeNode) {
|
||||
this.parent = treeNode;
|
||||
}
|
||||
|
||||
public TreeNode getParent() {
|
||||
return this.parent;
|
||||
}
|
||||
|
||||
public boolean hasParent() {
|
||||
return this.parent != null;
|
||||
}
|
||||
|
||||
public boolean isRoot() {
|
||||
return this.parent == null;
|
||||
}
|
||||
|
||||
public void sortRecursively(final Comparator<TreeNode> comparator) {
|
||||
Collections.sort(this.details, comparator);
|
||||
for (TreeNode detail : this.details) {
|
||||
detail.sortRecursively(comparator);
|
||||
}
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
if (this.element instanceof NamedElement) {
|
||||
return ((NamedElement) this.element).getName();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package ch.bitwave.mim.canvas.generators;
|
||||
|
||||
import java.text.Collator;
|
||||
import java.util.Comparator;
|
||||
|
||||
public class TreeNodeAlphabetizer implements Comparator<TreeNode> {
|
||||
|
||||
@Override
|
||||
public int compare(final TreeNode arg0, final TreeNode arg1) {
|
||||
return Collator.getInstance().compare(arg0.getLabel(), arg1.getLabel());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package ch.bitwave.mim.canvas.strategies;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import ch.bitwave.mim.m2.core.MimClass;
|
||||
|
||||
/**
|
||||
* Draws classes introducing features matching a given pattern in a different
|
||||
* color.
|
||||
*/
|
||||
public class ColoringByFeatureIntroductionStrategy extends ColoringByPatternStrategy {
|
||||
|
||||
public ColoringByFeatureIntroductionStrategy(final Color matchColor, final Pattern namePattern) {
|
||||
super(matchColor, namePattern);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isMatchingElement(final Matcher matcher, final Object element) {
|
||||
if (element instanceof MimClass) {
|
||||
MimClass mc = (MimClass) element;
|
||||
if (mc.hasFeatureMatching(matcher)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package ch.bitwave.mim.canvas.strategies;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import ch.bitwave.mim.m2.core.MimClass;
|
||||
|
||||
/**
|
||||
* Draws classes with features matching a given pattern in a different color.
|
||||
*/
|
||||
public class ColoringByFeaturePresenceStrategy extends ColoringByPatternStrategy {
|
||||
|
||||
public ColoringByFeaturePresenceStrategy(final Color matchColor, final Pattern namePattern) {
|
||||
super(matchColor, namePattern);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isMatchingElement(final Matcher matcher, final Object element) {
|
||||
if (element instanceof MimClass) {
|
||||
MimClass mc = (MimClass) element;
|
||||
if (mc.introducesFeatureMatching(matcher)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package ch.bitwave.mim.canvas.strategies;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import ch.bitwave.mim.m2.core.MimClass;
|
||||
|
||||
/**
|
||||
* Colors a node in a different background color if its label matches the name
|
||||
* pattern.
|
||||
*/
|
||||
public class ColoringByNameStrategy extends ColoringByPatternStrategy {
|
||||
|
||||
public ColoringByNameStrategy(final Color matchColor, final Pattern namePattern) {
|
||||
super(matchColor, namePattern);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isMatchingElement(final Matcher matcher, final Object element) {
|
||||
if (element instanceof MimClass) {
|
||||
MimClass mc = (MimClass) element;
|
||||
matcher.reset(mc.getName());
|
||||
if (matcher.matches()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package ch.bitwave.mim.canvas.strategies;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public abstract class ColoringByPatternStrategy extends ColoringStrategy {
|
||||
|
||||
private Color matchColor;
|
||||
private Matcher memberNameMatcher;
|
||||
|
||||
public ColoringByPatternStrategy(final Color matchColor, final Pattern namePattern) {
|
||||
this.matchColor = matchColor;
|
||||
this.memberNameMatcher = namePattern.matcher("");
|
||||
}
|
||||
|
||||
public Color getMatchColor() {
|
||||
return this.matchColor;
|
||||
}
|
||||
|
||||
public void setMatchColor(final Color matchColor) {
|
||||
this.matchColor = matchColor;
|
||||
}
|
||||
|
||||
public Matcher getMemberNameMatcher() {
|
||||
return this.memberNameMatcher;
|
||||
}
|
||||
|
||||
public void setMemberNameMatcher(final Matcher memberNameMatcher) {
|
||||
this.memberNameMatcher = memberNameMatcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Color getColor(final Object element) {
|
||||
if (isMatchingElement(this.memberNameMatcher, element)) {
|
||||
return this.matchColor;
|
||||
}
|
||||
return Color.WHITE;
|
||||
}
|
||||
|
||||
protected abstract boolean isMatchingElement(final Matcher matcher, final Object element);
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package ch.bitwave.mim.canvas.strategies;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
public abstract class ColoringStrategy {
|
||||
public abstract Color getColor(Object element);
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package ch.bitwave.mim.canvas.strategies;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import ch.bitwave.mim.m2.core.MimElement;
|
||||
|
||||
/**
|
||||
* Draws stubs in light red, regular elements in white instead of the regular
|
||||
* color.
|
||||
*/
|
||||
public class ColoringStubsStrategy extends ColoringStrategy {
|
||||
private static final Color STUB_BG = new Color(1.0f, 0.8f, 0.8f);
|
||||
|
||||
@Override
|
||||
public Color getColor(final Object element) {
|
||||
if (element instanceof MimElement) {
|
||||
MimElement me = (MimElement) element;
|
||||
if (me.isStub()) {
|
||||
return STUB_BG;
|
||||
}
|
||||
}
|
||||
return Color.WHITE;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package ch.bitwave.mim.canvas.strategies;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import ch.bitwave.mim.canvas.features.ConnectionNodeFeature;
|
||||
import ch.bitwave.mim.canvas.features.NodeFeature;
|
||||
|
||||
public abstract class ConnectorLayoutStrategy {
|
||||
|
||||
public abstract void layoutConnectorNodes(final NodeFeature node,
|
||||
List<ConnectionNodeFeature> outboundConnectors,
|
||||
List<ConnectionNodeFeature> inboundConnectors);
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package ch.bitwave.mim.canvas.strategies;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
|
||||
import ch.bitwave.mim.m2.core.NamedElement;
|
||||
import ch.parametrix.common.util.ui.swing.TextRenderer;
|
||||
import ch.parametrix.common.util.ui.swing.contracts.WorldDistance;
|
||||
|
||||
public class FitTextSizingStrategy extends SizingStrategy {
|
||||
|
||||
@Override
|
||||
public WorldDistance getSize(final Graphics2D g, final Object element) {
|
||||
TextRenderer tr = new TextRenderer(g, 0, 0, 2000, false);
|
||||
if (element instanceof NamedElement) {
|
||||
NamedElement ne = (NamedElement) element;
|
||||
int width = tr.measureWidth(g, ne.getName());
|
||||
return new WorldDistance(Math.max(500, width * 2), 300);
|
||||
}
|
||||
return new WorldDistance(1500, 300);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
package ch.bitwave.mim.canvas.strategies;
|
||||
|
||||
import java.awt.geom.Point2D;
|
||||
import java.util.List;
|
||||
|
||||
import ch.bitwave.mim.canvas.features.ConnectionNodeFeature;
|
||||
import ch.bitwave.mim.canvas.features.NodeFeature;
|
||||
|
||||
/**
|
||||
* Arranges the inbound and outbound connectors of a node in a left-to-right
|
||||
* layout. The inbound and outbound connectors are fanned out along a certain
|
||||
* percentage of the node height to make them more easily discernible.
|
||||
*/
|
||||
public class HorizontalFannedConnectorLayoutStrategy extends ConnectorLayoutStrategy {
|
||||
|
||||
/**
|
||||
* Specifies the factor of the total height of the node occupied by the
|
||||
* connector fan.
|
||||
*/
|
||||
private static final double FAN_EXTENT_FACTOR = 0.6;
|
||||
private static final double NON_FAN = 1 - FAN_EXTENT_FACTOR;
|
||||
|
||||
@Override
|
||||
public void layoutConnectorNodes(final NodeFeature node,
|
||||
final List<ConnectionNodeFeature> outboundConnectors,
|
||||
final List<ConnectionNodeFeature> inboundConnectors) {
|
||||
Point2D anchor = node.getAnchor();
|
||||
double h = node.getSize().getDy();
|
||||
double x = anchor.getX();
|
||||
layoutConnectors(inboundConnectors, anchor, h, x);
|
||||
x += node.getSize().getDx();
|
||||
layoutConnectors(outboundConnectors, anchor, h, x);
|
||||
}
|
||||
|
||||
private void layoutConnectors(final List<ConnectionNodeFeature> connectors,
|
||||
final Point2D anchor, final double h, final double x) {
|
||||
int numConnectors = connectors.size();
|
||||
if (numConnectors > 0) {
|
||||
double y = calcFanStart(anchor.getY(), h, numConnectors);
|
||||
double dy = (h * FAN_EXTENT_FACTOR) / (numConnectors - 1);
|
||||
for (ConnectionNodeFeature cn : connectors) {
|
||||
cn.place(x, y);
|
||||
y += dy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private double calcFanStart(final double t, final double d, final int size) {
|
||||
if (size == 1) {
|
||||
return t + d / 2.0;
|
||||
}
|
||||
return t + (NON_FAN * d) / 2;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package ch.bitwave.mim.canvas.strategies;
|
||||
|
||||
import java.awt.geom.Point2D;
|
||||
import java.util.List;
|
||||
|
||||
import ch.bitwave.mim.canvas.features.ConnectionNodeFeature;
|
||||
import ch.bitwave.mim.canvas.features.NodeFeature;
|
||||
|
||||
/**
|
||||
* Arranges the inbound and outbound connectors of a node in a left-to-right
|
||||
* layout. All inbound connections originate in the same point, as do all
|
||||
* outbound connections.
|
||||
*/
|
||||
public class HorizontalPointConnectorLayoutStrategy extends ConnectorLayoutStrategy {
|
||||
|
||||
@Override
|
||||
public void layoutConnectorNodes(final NodeFeature node,
|
||||
final List<ConnectionNodeFeature> outboundConnectors,
|
||||
final List<ConnectionNodeFeature> inboundConnectors) {
|
||||
Point2D anchor = node.getAnchor();
|
||||
double x = anchor.getX();
|
||||
double y = anchor.getY() + node.getSize().getDy() / 2;
|
||||
for (ConnectionNodeFeature cn : inboundConnectors) {
|
||||
cn.place(x, y);
|
||||
}
|
||||
x += node.getSize().getDx();
|
||||
for (ConnectionNodeFeature cn : outboundConnectors) {
|
||||
cn.place(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package ch.bitwave.mim.canvas.strategies;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
|
||||
import ch.parametrix.common.util.ui.swing.contracts.WorldDistance;
|
||||
|
||||
public abstract class SizingStrategy {
|
||||
|
||||
public abstract WorldDistance getSize(final Graphics2D g, final Object element);
|
||||
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
package ch.bitwave.mim.canvas.strategies;
|
||||
|
||||
import java.awt.geom.Point2D;
|
||||
import java.util.List;
|
||||
|
||||
import ch.bitwave.mim.canvas.features.ConnectionNodeFeature;
|
||||
import ch.bitwave.mim.canvas.features.NodeFeature;
|
||||
|
||||
/**
|
||||
* Arranges the inbound and outbound connectors of a node in a top-to-bottom
|
||||
* layout. The inbound and outbound connectors are fanned out along a certain
|
||||
* percentage of the node width to make them more easily discernible.
|
||||
*/
|
||||
public class VerticalFannedConnectorLayoutStrategy extends ConnectorLayoutStrategy {
|
||||
|
||||
/**
|
||||
* Specifies the factor of the total height of the node occupied by the
|
||||
* connector fan.
|
||||
*/
|
||||
private static final double FAN_EXTENT_FACTOR = 0.6;
|
||||
private static final double NON_FAN = 1 - FAN_EXTENT_FACTOR;
|
||||
|
||||
@Override
|
||||
public void layoutConnectorNodes(final NodeFeature node,
|
||||
final List<ConnectionNodeFeature> outboundConnectors,
|
||||
final List<ConnectionNodeFeature> inboundConnectors) {
|
||||
Point2D anchor = node.getAnchor();
|
||||
double w = node.getSize().getDx();
|
||||
double y = anchor.getY();
|
||||
layoutConnectors(inboundConnectors, anchor, w, y);
|
||||
y += node.getSize().getDy();
|
||||
layoutConnectors(outboundConnectors, anchor, w, y);
|
||||
}
|
||||
|
||||
private void layoutConnectors(final List<ConnectionNodeFeature> connectors,
|
||||
final Point2D anchor, final double w, final double y) {
|
||||
int numConnectors = connectors.size();
|
||||
if (numConnectors > 0) {
|
||||
double x = calcFanStart(anchor.getX(), w, numConnectors);
|
||||
double dx = (w * FAN_EXTENT_FACTOR) / (numConnectors - 1);
|
||||
for (ConnectionNodeFeature cn : connectors) {
|
||||
cn.place(x, y);
|
||||
x += dx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private double calcFanStart(final double t, final double d, final int size) {
|
||||
if (size == 1) {
|
||||
return t + d / 2.0;
|
||||
}
|
||||
return t + (NON_FAN * d) / 2;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package ch.bitwave.mim.canvas.strategies;
|
||||
|
||||
import java.awt.geom.Point2D;
|
||||
import java.util.List;
|
||||
|
||||
import ch.bitwave.mim.canvas.features.ConnectionNodeFeature;
|
||||
import ch.bitwave.mim.canvas.features.NodeFeature;
|
||||
|
||||
/**
|
||||
* Arranges the inbound and outbound connectors of a node in a top-to-bottom
|
||||
* layout. All inbound connections originate in the same point, as do all
|
||||
* outbound connections.
|
||||
*/
|
||||
public class VerticalPointConnectorLayoutStrategy extends ConnectorLayoutStrategy {
|
||||
|
||||
@Override
|
||||
public void layoutConnectorNodes(final NodeFeature node,
|
||||
final List<ConnectionNodeFeature> outboundConnectors,
|
||||
final List<ConnectionNodeFeature> inboundConnectors) {
|
||||
Point2D anchor = node.getAnchor();
|
||||
double x = anchor.getX() + node.getSize().getDx() / 2;
|
||||
double y = anchor.getY();
|
||||
for (ConnectionNodeFeature cn : inboundConnectors) {
|
||||
cn.place(x, y);
|
||||
}
|
||||
y += node.getSize().getDy();
|
||||
for (ConnectionNodeFeature cn : outboundConnectors) {
|
||||
cn.place(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package ch.bitwave.mim.canvas.views;
|
||||
|
||||
import java.awt.geom.AffineTransform;
|
||||
|
||||
import ch.parametrix.common.util.ui.swing.canvas.CanvasPanel;
|
||||
|
||||
public class MimCanvas extends CanvasPanel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
protected void initViewTransform() {
|
||||
super.initViewTransform();
|
||||
scaleCanvas(AffineTransform.getScaleInstance(0.05, 0.05));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package ch.bitwave.mim.canvas.generators;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import ch.bitwave.mim.m2.core.MimClass;
|
||||
import ch.bitwave.mim.m2.core.MimElement;
|
||||
import ch.bitwave.mim.m2.core.NamedElement;
|
||||
|
||||
public class ElementByNameFilterTest {
|
||||
|
||||
@Test
|
||||
public void shouldAcceptMatchingCaseInsensitive() {
|
||||
ElementByNameFilter filter = new ElementByNameFilter("tpomcontroller", false);
|
||||
assertTrue(filter.accepts(createElement("TPOMController")));
|
||||
assertFalse(filter.accepts(createElement("TFooController")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAcceptMatchingCaseSensitive() {
|
||||
ElementByNameFilter filter = new ElementByNameFilter("TPOMController", true);
|
||||
assertTrue(filter.accepts(createElement("TPOMController")));
|
||||
assertFalse(filter.accepts(createElement("TpomController")));
|
||||
}
|
||||
|
||||
private MimElement createElement(final String name) {
|
||||
NamedElement result = new MimClass();
|
||||
result.setName(name);
|
||||
return result;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue