/*******************************************************************************
* Copyright (c) 2006-2012
* Software Technology Group, Dresden University of Technology
* DevBoost GmbH, Berlin, Amtsgericht Charlottenburg, HRB 140026
*
* 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:
* Software Technology Group - TU Dresden, Germany;
* DevBoost GmbH - Berlin, Germany
* - initial API and implementation
******************************************************************************/
/*
* @(#)AbstractAttributedFigure.java 4.1 2007-05-18
*
* Copyright (c) 1996-2007 by the original authors of JHotDraw
* and all its contributors.
* All rights reserved.
*
* The copyright of this software is owned by the authors and
* contributors of the JHotDraw project ("the copyright holders").
* You may not use, copy or modify this software, except in
* accordance with the license agreement you entered into with
* the copyright holders. For details see accompanying license terms.
*/
package org.jhotdraw.draw;
import java.awt.*;
import java.awt.geom.*;
import java.util.*;
import java.io.*;
import static org.jhotdraw.draw.AttributeKeys.*;
import org.jhotdraw.geom.*;
import org.jhotdraw.xml.DOMInput;
import org.jhotdraw.xml.DOMOutput;
/**
* A figure that can keep track of an open ended set of attributes.
* <p>
*
* @author Werner Randelshofer
* @version 4.1 2007-05-18 Changed due to changes in Figure interface.
* <br>4.0 2007-01-03 Renamed from AttributedFigure to
* AbstractAttributedFigure.
* <br>3.2 2006-12-07 Changed method setAttributes(HashMap)
* to setAttributes(Map);
* <br>3.1 2006-06-17 Method chop(Point2D.Double) added.
* <br>3.0 2006-06-07 Reworked.
* <br>2.1 2006-02-20 Support for DoubleStroke added.
* <br>2.0 2006-01-14 Changed to support double precision coordinates.
* <br>1.0 2003-12-01 Derived from JHotDraw 5.4b1.
*/
public abstract class AbstractAttributedFigure extends AbstractFigure {
/**
* Holds the attributes of the figure.
*/
private HashMap<AttributeKey, Object> attributes = new HashMap<AttributeKey,Object>();
/**
* Forbidden attributes can't be set by the setAttribute() operation.
* They can only be changed by setAttribute().
*/
private HashSet<AttributeKey> forbiddenAttributes;
/** Creates a new instance. */
public AbstractAttributedFigure() {
}
public void setAttributeEnabled(AttributeKey key, boolean b) {
if (forbiddenAttributes == null) {
forbiddenAttributes = new HashSet<AttributeKey>();
}
if (b) {
forbiddenAttributes.remove(key);
} else {
forbiddenAttributes.add(key);
}
}
public boolean isAttributeEnabled(AttributeKey key) {
return forbiddenAttributes == null || ! forbiddenAttributes.contains(key);
}
@SuppressWarnings("unchecked")
public void setAttributes(Map<AttributeKey, Object> map) {
for (Map.Entry<AttributeKey, Object> entry : map.entrySet()) {
setAttribute(entry.getKey(), entry.getValue());
}
}
public Map<AttributeKey, Object> getAttributes() {
return new HashMap<AttributeKey,Object>(attributes);
}
public Object getAttributesRestoreData() {
return getAttributes();
}
@SuppressWarnings("unchecked")
public void restoreAttributesTo(Object restoreData) {
attributes.clear();
setAttributes((HashMap<AttributeKey,Object>) restoreData);
}
/**
* Sets an attribute of the figure.
* AttributeKey name and semantics are defined by the class implementing
* the figure interface.
*/
public <T> void setAttribute(AttributeKey<T> key, T newValue) {
if (forbiddenAttributes == null
|| ! forbiddenAttributes.contains(key)) {
T oldValue = (T) key.put(attributes, newValue);
fireAttributeChanged(key, oldValue, newValue);
}
}
/**
* Gets an attribute from the figure.
*/
public <T> T getAttribute(AttributeKey<T> key) {
return hasAttribute(key) ? key.get(attributes) : key.getDefaultValue();
}
public void draw(Graphics2D g) {
if (AttributeKeys.FILL_COLOR.get(this) != null) {
g.setColor(AttributeKeys.FILL_COLOR.get(this));
drawFill(g);
}
if (STROKE_COLOR.get(this) != null && STROKE_WIDTH.get(this) > 0d) {
g.setStroke(AttributeKeys.getStroke(this));
g.setColor(STROKE_COLOR.get(this));
drawStroke(g);
}
if (TEXT_COLOR.get(this) != null) {
if (TEXT_SHADOW_COLOR.get(this) != null &&
TEXT_SHADOW_OFFSET.get(this) != null) {
Dimension2DDouble d = TEXT_SHADOW_OFFSET.get(this);
g.translate(d.width, d.height);
g.setColor(TEXT_SHADOW_COLOR.get(this));
drawText(g);
g.translate(-d.width,-d.height);
}
g.setColor(TEXT_COLOR.get(this));
drawText(g);
}
}
public Stroke getStroke() {
return AttributeKeys.getStroke(this);
}
public double getStrokeMiterLimitFactor() {
Number value = (Number) getAttribute(AttributeKeys.STROKE_MITER_LIMIT);
return (value != null) ? value.doubleValue() : 10f;
}
public Rectangle2D.Double getDrawingArea() {
double strokeTotalWidth = AttributeKeys.getStrokeTotalWidth(this);
double width = strokeTotalWidth / 2d;
if (STROKE_JOIN.get(this) == BasicStroke.JOIN_MITER) {
width *= STROKE_MITER_LIMIT.get(this);
} else if (STROKE_CAP.get(this) != BasicStroke.CAP_BUTT) {
width += strokeTotalWidth * 2;
}
width++;
Rectangle2D.Double r = getBounds();
Geom.grow(r, width, width);
return r;
}
/**
* This method is called by method draw() to draw the fill
* area of the figure. AbstractAttributedFigure configures the Graphics2D
* object with the FILL_COLOR attribute before calling this method.
* If the FILL_COLOR attribute is null, this method is not called.
*/
protected abstract void drawFill(java.awt.Graphics2D g);
/**
* This method is called by method draw() to draw the lines of the figure
*. AttributedFigure configures the Graphics2D object with
* the STROKE_COLOR attribute before calling this method.
* If the STROKE_COLOR attribute is null, this method is not called.
*/
/**
* This method is called by method draw() to draw the text of the figure
* . AbstractAttributedFigure configures the Graphics2D object with
* the TEXT_COLOR attribute before calling this method.
* If the TEXT_COLOR attribute is null, this method is not called.
*/
protected abstract void drawStroke(java.awt.Graphics2D g);
protected void drawText(java.awt.Graphics2D g) {
}
public AbstractAttributedFigure clone() {
AbstractAttributedFigure that = (AbstractAttributedFigure) super.clone();
that.attributes = new HashMap<AttributeKey,Object>(this.attributes);
if (this.forbiddenAttributes != null) {
that.forbiddenAttributes = new HashSet<AttributeKey>(this.forbiddenAttributes);
}
return that;
}
protected void writeAttributes(DOMOutput out) throws IOException {
Figure prototype = (Figure) out.getPrototype();
boolean isElementOpen = false;
for (Map.Entry<AttributeKey, Object> entry : attributes.entrySet()) {
AttributeKey key = entry.getKey();
if (forbiddenAttributes == null
|| ! forbiddenAttributes.contains(key)) {
Object prototypeValue = key.get(prototype);
Object attributeValue = key.get(this);
if (prototypeValue != attributeValue ||
(prototypeValue != null && attributeValue != null &&
! prototypeValue.equals(attributeValue))) {
if (! isElementOpen) {
out.openElement("a");
isElementOpen = true;
}
out.openElement(key.getKey());
out.writeObject(entry.getValue());
out.closeElement();
}
}
}
if (isElementOpen) {
out.closeElement();
}
}
@SuppressWarnings("unchecked")
protected void readAttributes(DOMInput in) throws IOException {
if (in.getElementCount("a") > 0) {
in.openElement("a");
for (int i=in.getElementCount() - 1; i >= 0; i-- ) {
in.openElement(i);
String name = in.getTagName();
Object value = in.readObject();
AttributeKey key = getAttributeKey(name);
if (key != null && key.isAssignable(value)) {
if (forbiddenAttributes == null
|| ! forbiddenAttributes.contains(key)) {
setAttribute(key, value);
}
}
in.closeElement();
}
in.closeElement();
}
}
protected AttributeKey getAttributeKey(String name) {
return AttributeKeys.supportedAttributeMap.get(name);
}
/**
* Applies all attributes of this figure to that figure.
*/
@SuppressWarnings("unchecked")
protected void applyAttributesTo(Figure that) {
for (Map.Entry<AttributeKey, Object> entry : attributes.entrySet()) {
entry.getKey().basicSet(that, entry.getValue());
}
}
public void write(DOMOutput out) throws IOException {
Rectangle2D.Double r = getBounds();
out.addAttribute("x", r.x);
out.addAttribute("y", r.y);
out.addAttribute("w", r.width);
out.addAttribute("h", r.height);
writeAttributes(out);
}
public void read(DOMInput in) throws IOException {
double x = in.getAttribute("x", 0d);
double y = in.getAttribute("y", 0d);
double w = in.getAttribute("w", 0d);
double h = in.getAttribute("h", 0d);
setBounds(new Point2D.Double(x,y), new Point2D.Double(x+w,y+h));
readAttributes(in);
}
public <T> void removeAttribute(AttributeKey<T> key) {
if (hasAttribute(key)) {
T oldValue = key.get(this);
attributes.remove(key);
fireAttributeChanged(key, oldValue, key.getDefaultValue());
}
}
public boolean hasAttribute(AttributeKey key) {
return attributes.containsKey(key);
}
}