package com.horstmann.violet.product.diagram.classes.node;
import java.awt.Color;
import java.awt.Shape;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import com.horstmann.violet.framework.graphics.content.*;
import com.horstmann.violet.framework.graphics.shape.ContentInsideCustomShape;
import com.horstmann.violet.product.diagram.abstracts.Direction;
import com.horstmann.violet.product.diagram.abstracts.edge.IEdge;
import com.horstmann.violet.product.diagram.abstracts.node.AbstractNode;
import com.horstmann.violet.product.diagram.common.node.ColorableNode;
import com.horstmann.violet.product.diagram.abstracts.node.INode;
import com.horstmann.violet.product.diagram.classes.ClassDiagramConstant;
import com.horstmann.violet.product.diagram.property.text.LineText;
import com.horstmann.violet.product.diagram.property.text.MultiLineText;
import com.horstmann.violet.product.diagram.property.text.SingleLineText;
/**
* A package node in a class diagram.
*/
public class PackageNode extends ColorableNode
{
public PackageNode()
{
name = new SingleLineText();
name.setAlignment(LineText.CENTER);
context = new MultiLineText();
createContentStructure();
}
protected PackageNode(PackageNode node) throws CloneNotSupportedException
{
super(node);
name = node.name.clone();
context = node.context.clone();
createContentStructure();
}
@Override
protected void beforeReconstruction()
{
super.beforeReconstruction();
if(null == name)
{
name = new SingleLineText();
}
if(null == context)
{
context = new MultiLineText();
}
name.reconstruction();
context.reconstruction();
}
@Override
protected void afterReconstruction()
{
for(INode child : getChildren())
{
if (isSupportedNode(child))
{
nodesGroup.add(((ColorableNode) child).getContent(), getChildRelativeLocation(child));
}
}
super.afterReconstruction();
}
@Override
protected INode copy() throws CloneNotSupportedException
{
return new PackageNode(this);
}
@Override
protected void createContentStructure()
{
nameContent = new TextContent(name);
nameContent.setMinHeight(DEFAULT_TOP_HEIGHT);
nameContent.setMinWidth(DEFAULT_TOP_WIDTH);
TextContent textContent = new TextContent(context);
nodesGroup = new RelativeLayout();
nodesGroup.setMinHeight(DEFAULT_HEIGHT);
nodesGroup.setMinWidth(DEFAULT_WIDTH);
RelativeLayout relativeGroupContent = new RelativeLayout();
EmptyContent emptyContent = new EmptyContent();
emptyContent.setMinWidth(NAME_GAP);
HorizontalLayout horizontalName = new HorizontalLayout();
horizontalName.add(nameContent);
horizontalName.add(emptyContent);
relativeGroupContent.add(
new PaddingContent(nodesGroup, CHILD_GAP), new Point2D.Double(0, DEFAULT_TOP_HEIGHT)
);
relativeGroupContent.add(textContent, new Point2D.Double(0, DEFAULT_TOP_HEIGHT));
relativeGroupContent.add(horizontalName);
ContentInsideShape contentInsideShape = new ContentInsideCustomShape(
relativeGroupContent, new PackageShape(nameContent)
);
setBorder(new ContentBorder(contentInsideShape, getBorderColor()));
setBackground(new ContentBackground(getBorder(), getBackgroundColor()));
setContent(getBackground());
}
@Override
public void setTextColor(Color textColor)
{
name.setTextColor(textColor);
context.setTextColor(textColor);
super.setTextColor(textColor);
}
@Override
public String getToolTip()
{
return ClassDiagramConstant.CLASS_DIAGRAM_RESOURCE.getString("tooltip.package_node");
}
@Override
public Point2D getConnectionPoint(IEdge edge)
{
Point2D connectionPoint = super.getConnectionPoint(edge);
Direction direction = edge.getDirection(this).getNearestCardinalDirection();
if (Direction.SOUTH.equals(direction))
{
if(connectionPoint.getX() >= getLocation().getX() + nameContent.getWidth())
{
return new Point2D.Double(connectionPoint.getX(), connectionPoint.getY() + nameContent.getHeight());
}
}
else if(Direction.WEST.equals(direction))
{
if(connectionPoint.getY() < getLocation().getY() + nameContent.getHeight())
{
return new Point2D.Double(connectionPoint.getX(), connectionPoint.getY() + nameContent.getHeight());
}
}
return connectionPoint;
}
@Override
public void removeChild(INode node)
{
nodesGroup.remove(((AbstractNode) node).getContent());
super.removeChild(node);
}
@Override
public boolean addChild(INode node, Point2D point)
{
if(DEFAULT_TOP_HEIGHT > point.getY())
{
return false;
}
if (isSupportedNode(node))
{
node.setParent(this);
node.setGraph(this.getGraph());
node.setLocation(point);
addChild(node, getChildren().size());
ColorableNode colorableNode = (ColorableNode) node;
colorableNode.setTextColor(getTextColor());
colorableNode.setBackgroundColor(getBackgroundColor());
colorableNode.setBorderColor(getBorderColor());
nodesGroup.add(colorableNode.getContent(), getChildRelativeLocation(node));
return true;
}
return false;
}
protected Point2D getChildRelativeLocation(INode child)
{
Point2D childLocation = child.getLocation();
if(DEFAULT_TOP_HEIGHT+CHILD_GAP > childLocation.getY() || CHILD_GAP > childLocation.getX())
{
childLocation.setLocation(Math.max(childLocation.getX(), CHILD_GAP), Math.max(childLocation.getY(), DEFAULT_TOP_HEIGHT+CHILD_GAP));
child.setLocation(childLocation);
}
return new Point2D.Double(childLocation.getX()-CHILD_GAP, childLocation.getY()-DEFAULT_TOP_HEIGHT-CHILD_GAP);
}
/**
* Ensure that child node_old respects the minimum gap with package borders
*
* @param child
*/
protected void onChildChangeLocation(INode child)
{
nodesGroup.setPosition(((AbstractNode) child).getContent(), getChildRelativeLocation(child));
}
private boolean isSupportedNode(INode node)
{
return (node instanceof ClassNode || node instanceof InterfaceNode || node instanceof PackageNode || node instanceof EnumNode || node instanceof BallAndSocketNode);
}
/**
* Sets the name property value.
*
* @param newValue the class name
*/
public void setName(LineText newValue)
{
name.setText(newValue);
}
/**
* Gets the name property value.
*
* @return the class name
*/
public LineText getName()
{
return name;
}
/**
* Sets the contents property value.
*
* @param newValue the contents of this class
*/
public void setContext(LineText newValue)
{
context.setText(newValue);
}
/**
* Gets the contents property value.
*
* @return the contents of this class
*/
public LineText getContext()
{
return context;
}
private SingleLineText name;
private MultiLineText context;
private transient TextContent nameContent;
private transient RelativeLayout nodesGroup;
private static int DEFAULT_TOP_WIDTH = 60;
private static int DEFAULT_TOP_HEIGHT = 25;
private static int DEFAULT_WIDTH = 100;
private static int DEFAULT_HEIGHT = 60;
private static final int NAME_GAP = 30;
private static final int CHILD_GAP = 20;
static protected class PackageShape implements ContentInsideCustomShape.ShapeCreator
{
PackageShape(TextContent nameContent)
{
this.nameContent = nameContent;
}
@Override
public Shape createShape(double contentWidth, double contentHeight)
{
GeneralPath path = new GeneralPath();
path.moveTo(nameContent.getWidth(), nameContent.getHeight());
path.lineTo(nameContent.getWidth(), 0);
path.lineTo(0, 0);
path.lineTo(0, contentHeight);
path.lineTo(contentWidth, contentHeight);
path.lineTo(contentWidth, nameContent.getHeight());
path.lineTo(0, nameContent.getHeight());
path.closePath();
return path;
}
private TextContent nameContent;
}
}