diff --git a/src/jcckit/GraphicsPlotCanvas.java b/src/jcckit/GraphicsPlotCanvas.java new file mode 100644 index 000000000..5d2b41b4a --- /dev/null +++ b/src/jcckit/GraphicsPlotCanvas.java @@ -0,0 +1,228 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; + +import jcckit.graphic.GraphicalElement; +import jcckit.graphic.Renderer; +import jcckit.plot.Plot; +import jcckit.plot.PlotCanvas; +import jcckit.renderer.Graphics2DRenderer; +import jcckit.renderer.Transformation; +import jcckit.util.ConfigParameters; +import jcckit.util.Factory; + +/** + * Class which handles plotting into a Graphics context based on the + * {@link jcckit.renderer.GraphicsRenderer}. This class is not a subclass of + * java.awt.Component. The actual AWT component presenting the plot + * is an innerclass. Its instance wrapped by GraphicsPlotCanvas can + * be obtained with {@link #getGraphicsCanvas}. + *

+ * The plot is painted by using double-buffering and pre-rendered view of the + * coordinate system. That is, the coordinate system is drawn into an off-screen + * image. It will be redrawn only if the size of the embedding AWT component is + * changed. + * + * @author Franz-Josef Elmer + */ +public class GraphicsPlotCanvas extends PlotCanvas { + /** Key of a configuration parameter. */ + public static final String BACKGROUND_KEY = "background"; + public static final String FOREGROUND_KEY = "foreground"; + public static final String DOUBLE_BUFFERING_KEY = "doubleBuffering"; + + /** + * Class which does the actual painting. Needs the Component into + * which the plot is painted for some resources like size, background color, + * etc. + * + * @author Franz-Josef Elmer + */ + private final BufferedImage img3; + + private final Graphics2D g3; + + private Transformation _transformation; + private String _renderer = "jcckit.renderer.GraphicsRenderer"; + + private GraphicalElement _marker; + + /** + * Creates an instance from the specified configuration parameters. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Key & Default ValueTypeMandatoryDescription
background = default background color of the wrapped + * AWT componentColornoBackground color of the wrapped AWT component.
foreground = default foreground color of the wrapped + * AWT componentColornoForeground color of the wrapped AWT component.
doubleBuffering = truebooleannoIf true the plot will be painted by using + * double-buffering and pre-rendered view of the coordinate system. + *
+ * In addition the configuration parameters of the + * + * constructor of the superclass {@link jcckit.plot.PlotCanvas} apply. + */ + public GraphicsPlotCanvas(ConfigParameters config, BufferedImage img3) { + super(config); + this.img3 = img3; + setRenderer("jcckit.renderer.Graphics2DRenderer"); + + g3 = img3.createGraphics(); + g3.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + // _doubleBuffering = config.getBoolean(DOUBLE_BUFFERING_KEY, true); + background = config.getColor(BACKGROUND_KEY, Color.WHITE); + foreground = config.getColor(FOREGROUND_KEY, Color.BLACK); + } + + private final Color background; + private final Color foreground; + + /** + * Paints the plot. If {@link GraphicsPlotCanvas#_doubleBuffering} is set + * double-buffering and pre-rendered view of the coordinate system is used. + */ + public void paint() { + Dimension size = new Dimension(img3.getWidth(), img3.getHeight()); + g3.setColor(background); + g3.fillRect(0, 0, size.width + 1, size.height + 1); + + init(size); + + _transformation.apply(g3); + Plot plot = getPlot(); + drawCoordinateSystem(size, plot); + drawPlot(plot); + if (_marker != null) { + _marker.renderWith(createRenderer()); + } + } + + private void drawPlot(Plot plot) { + prepare(); + Renderer renderer = createRenderer(); + GraphicalElement[] curves = plot.getCurves(); + for (int i = 0; i < curves.length; i++) { + curves[i].renderWith(renderer); + } + GraphicalElement annotation = plot.getAnnotation(); + if (annotation != null) { + annotation.renderWith(renderer); + } + if (plot.isLegendVisible()) { + plot.getLegend().renderWith(renderer); + } + } + + private void init(Dimension size) { + calculateTransformation(size); + } + + private void drawCoordinateSystem(Dimension size, Plot plot) { + g3.setColor(foreground); + plot.getCoordinateSystem().renderWith(createRenderer()); + } + + /** + * Prepare graphics context before drawing the pre-rendered view of the + * coordinate system. Does nothing but will be used in subclasses. + */ + protected void prepare() { + } + + /** + * Calculate the transformation form device-independent coordinates into + * device-dependent coordinates according to the specified canvas size. + */ + protected void calculateTransformation(Dimension size) { + _transformation = new Transformation(size.width, size.height, getPaper(), getHorizontalAnchor(), + getVerticalAnchor()); + } + + /** + * Creates an appropriated {@link Renderer} for the specified + * Graphics context. + */ + protected Renderer createRenderer() { + return ((Graphics2DRenderer) Factory.create(_renderer)).init(g3); + // return ((GraphicsRenderer) Factory.create(_renderer)).init(g, null, + // _transformation); + } + + /** + * Sets the renderer used to render the plot. The default value is + * {@link GraphicsRenderer}. + * + * @param className + * Fully qualified name of the renderer class. + */ + public void setRenderer(String className) { + _renderer = className; + } + + // /** + // * Maps the cursor position onto a point in device-independent + // coordinates. + // * + // * @param x + // * X-coordinate of the cursor. + // * @param y + // * Y-coordinate of the cursor. + // */ + // public GraphPoint mapCursorPosition(int x, int y) { + // return _transformation.transformBack(x, y); + // } + + /** + * Defines a graphical marker which will be drawn on top of the plot. To + * remove the marker call this method with argument null. + * + * @param marker + * Marker element. Can be null. + */ + public void setMarker(GraphicalElement marker) { + _marker = marker; + } + +} diff --git a/src/jcckit/data/DataContainer.java b/src/jcckit/data/DataContainer.java new file mode 100644 index 000000000..831ad0b32 --- /dev/null +++ b/src/jcckit/data/DataContainer.java @@ -0,0 +1,182 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.data; + +import java.text.MessageFormat; +import java.util.Vector; + +/** + * Abstract superclass of all data containers. A data container holds an + * ordered list of {@link DataElement DataElements} of the same type. + *

+ * Data elements can be added, inserted, removed, or replaced. + * Such an action leads to a {@link DataEvent} which will be delivered to + * all {@link DataListener DataListeners} observing this + * DataContainer. If this data container also implements + * {@link DataEvent} (as {@link DataCurve} does) also the listeners + * registrated at the data container containg this container will be notified. + * As a consequence a DataListener must only be registered at the + * {@link DataPlot} instance and it will automatically also received events + * caused by manipulating one of its DataCurves. + *

+ * Concrete subclasses have to implement {@link #isValid} which + * checks whether the added or inserted DataElement is of the right + * type. This is an application of the Template Method Design Pattern. + * + * @author Franz-Josef Elmer + */ +public abstract class DataContainer { + private final static String TEMPLATE + = "Invalid operation: {0}, Element: {1}, Container: {2}"; + final static String ADD = "add", + REPLACE = "replace", + INSERT = "insert"; + + private final Vector _listeners = new Vector(); + private final Vector _container = new Vector(); + + /** Adds a {@link DataListener}. Does nothing if already added. */ + public void addDataListener(DataListener listener) { + if (!_listeners.contains(listener)) { + _listeners.addElement(listener); + } + } + + /** Removes a {@link DataListener}. Does nothing if already removed. */ + public void removeDataListener(DataListener listener) { + _listeners.removeElement(listener); + } + + private void notifyListeners(DataEvent event) { + for (int i = 0, n = _listeners.size(); i < n; i++) { + ((DataListener) _listeners.elementAt(i)).dataChanged(event); + } + // Notifies also parent container + if (this instanceof DataElement) { + DataContainer container = ((DataElement) this).getContainer(); + if (container != null) { + container.notifyListeners(event); + } + } + } + + /** Returns the number of elements of this container. */ + public int getNumberOfElements() { + return _container.size(); + } + + /** Returns the element for the specified index. */ + public DataElement getElement(int index) { + return (DataElement) _container.elementAt(index); + } + + /** + * Returns the index of the specified element. + * @param element Element to be looked for. + * @return -1 if not found. + */ + public int getIndexOf(DataElement element) { + return _container.indexOf(element); + } + + /** + * Adds a {@link DataElement}. After the element has been successfully + * added all {@link DataListener DataListeners} will be informed. + * @param element DataElement to be added. + * @throws IllegalArgumentException if element is not of the correct + * type which will be checked by the method {@link #isValid}. + */ + public void addElement(DataElement element) { + if (isValid(element)) { + _container.addElement(element); + element.setContainer(this); + notifyListeners(DataEvent.createAddEvent(this)); + } else { + throwException(ADD, element); + } + } + + /** + * Inserts a {@link DataElement} at the specified index. + * After the element has been successfully inserted + * all {@link DataListener DataListeners} will be informed. + * @param index Index at which element will be inserted. + * All elements with an index >= index will be shifted. + * @param element DataElement to be added. + * @throws IllegalArgumentException if element is not of the correct + * type which will be checked by the method {@link #isValid}. + */ + public void insertElementAt(int index, DataElement element) { + if (isValid(element)) { + _container.insertElementAt(element, index); + element.setContainer(this); + notifyListeners(DataEvent.createInsertEvent(this, index)); + } else { + throwException(INSERT, element); + } + } + + /** + * Removes a {@link DataElement} at the specified index. + * After the element has been successfully removed + * all {@link DataListener DataListeners} will be informed. + * @param index Index of the element which will be removed. + * All elements with an index > index will be shifted. + */ + public void removeElementAt(int index) { + DataElement element = (DataElement) _container.elementAt(index); + element.setContainer(null); + _container.removeElementAt(index); + notifyListeners(DataEvent.createRemoveEvent(this, index, element)); + } + + /** + * Replaces the {@link DataElement} at the specified index. + * After the element has been successfully replaced + * all {@link DataListener DataListeners} will be informed. + * @param index Index of the element which will be replaced by + * element. + * @param element The new DataElement. + * @throws IllegalArgumentException if element is not of the correct + * type which will be checked by the method {@link #isValid}. + */ + public void replaceElementAt(int index, DataElement element) { + if (isValid(element)) { + DataElement oldElement = (DataElement) _container.elementAt(index); + oldElement.setContainer(null); + _container.setElementAt(element, index); + element.setContainer(this); + notifyListeners(DataEvent.createReplaceEvent(this, index, oldElement)); + } else { + throwException(REPLACE, element); + } + } + + private void throwException(String operation, DataElement element) { + throw new IllegalArgumentException(MessageFormat.format(TEMPLATE, + new Object[] {operation, element, this.getClass().getName()})); + } + + /** + * Returns true if the specified {@link DataElement} has the + * correct type. Concrete subclasses have to implement this method. + * @param element DataElement to be checked. + */ + protected abstract boolean isValid(DataElement element); +} diff --git a/src/jcckit/data/DataCurve.java b/src/jcckit/data/DataCurve.java new file mode 100644 index 000000000..caf94f3fc --- /dev/null +++ b/src/jcckit/data/DataCurve.java @@ -0,0 +1,93 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.data; + +import jcckit.util.ConfigParameters; + +/** + * A curve is a {@link DataContainer} of {@link DataPoint DataPoints}. + * + * @author Franz-Josef Elmer + */ +public class DataCurve extends DataContainer implements DataElement { + /** Config parameter key. */ + public static final String X_KEY = "x", + Y_KEY = "y", + TITLE_KEY = "title"; + + private final String _title; + private DataContainer _container; + + /** Creates an empty instance with the specified title. */ + public DataCurve(String title) { + _title = title; + } + + /** + * Creates an instance from the specified config parameters. + * + * + * + * + * + * + * + * + * + * + *
Key & Default ValueTypeMandatoryDescription
title = empty stringStringnoCurve title.
xdouble[]yesx-coordinates of the curve points.
ydouble[]yesy-coordinates of the curve points.
+ */ + public DataCurve(ConfigParameters config) { + this(config.get(TITLE_KEY, "")); + double[] xPoints = config.getDoubleArray(X_KEY); + double[] yPoints = config.getDoubleArray(Y_KEY); + int n = Math.min(xPoints.length, yPoints.length); + for (int i = 0; i < n; i++) { + addElement(new DataPoint(xPoints[i], yPoints[i])); + } + } + + /** + * Returns the {@link DataPlot} containing this curve. + */ + public DataContainer getContainer() { + return _container; + } + + + /** + * Sets the {@link DataPlot} where this is a curve of. + */ + public void setContainer(DataContainer container) { + _container = container; + } + + /** Returns the title of this curve. */ + public String getTitle() { + return _title; + } + + /** + * Returns true if element is an instance of + * {@link DataPoint}. + */ + protected boolean isValid(DataElement element) { + return element instanceof DataPoint; + } +} diff --git a/src/jcckit/data/DataElement.java b/src/jcckit/data/DataElement.java new file mode 100644 index 000000000..a66ce4993 --- /dev/null +++ b/src/jcckit/data/DataElement.java @@ -0,0 +1,40 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.data; + +/** + * Interface for all kinds of data elements. + * + * @author Franz-Josef Elmer + */ +public interface DataElement { + /** + * Returns the container containing this element. + * @return null if this element is not an element of a container. + */ + public DataContainer getContainer(); + + /** + * Sets the container which should contain this element. + * This method should not used outside {@link DataContainer}.. + * @param container Container which should contains this element. Cann be + * null if this element does not belong to a container. + */ + public void setContainer(DataContainer container); +} diff --git a/src/jcckit/data/DataEvent.java b/src/jcckit/data/DataEvent.java new file mode 100644 index 000000000..8839f1350 --- /dev/null +++ b/src/jcckit/data/DataEvent.java @@ -0,0 +1,128 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.data; + +/** + * Event to be sent to a {@link DataListener}. + * + * @author Franz-Josef Elmer + */ +public class DataEvent { + private final DataContainer _container; + private final DataEventType _type; + private final int _index; + private final DataElement _deletedElement; + + /** + * Creates an instance for the specified parameters. + * @param container The container which has been changed. + * @param type Type of change. + * @param index Index of the element which has been added, inserted, + * replaced, or removed. + * @param deletedElement Element which has been replaced or removed. + */ + private DataEvent(DataContainer container, DataEventType type, int index, + DataElement deletedElement) { + _container = container; + _type = type; + _index = index; + _deletedElement = deletedElement; + } + + /** + * Creates an event of type {@link DataEventType#ELEMENT_ADDED} for the + * specified container. + * @param container Container where an element has been added. + * @return ELEMENT_ADDED event. + */ + public static final DataEvent createAddEvent(DataContainer container) { + return new DataEvent(container, DataEventType.ELEMENT_ADDED, + container.getNumberOfElements() - 1, null); + } + + /** + * Creates an event of type {@link DataEventType#ELEMENT_INSERTED} for the + * specified container. + * @param container Container where an element has been inserted. + * @param index Index at which an element has been inserted. + * @return ELEMENT_INSERTED event. + */ + public static final DataEvent createInsertEvent(DataContainer container, + int index) { + return new DataEvent(container, DataEventType.ELEMENT_INSERTED, index, + null); + } + + /** + * Creates an event of type {@link DataEventType#ELEMENT_REPLACED} for the + * specified container. + * @param container Container where an element has been replaced. + * @param index Index of the replaced element. + * @param replacedElement The previous element at index. + * @return ELEMENT_REPLACED event. + */ + public static final DataEvent createReplaceEvent(DataContainer container, + int index, DataElement replacedElement) { + return new DataEvent(container, DataEventType.ELEMENT_REPLACED, index, + replacedElement); + } + + /** + * Creates an event of type {@link DataEventType#ELEMENT_REMOVED} for the + * specified container. + * @param container Container where an element has been removed. + * @param index Index of the removed element. + * @param removedElement The previous element at index. + * @return ELEMENT_REMOVED event. + */ + public static final DataEvent createRemoveEvent(DataContainer container, + int index, DataElement removedElement) { + return new DataEvent(container, DataEventType.ELEMENT_REMOVED, index, + removedElement); + } + + /** Returns the container. */ + public DataContainer getContainer() { + return _container; + } + + /** + * Returns the event type. Will be one of the constants + * {@link DataEventType#ELEMENT_ADDED}, + * {@link DataEventType#ELEMENT_INSERTED}, + * {@link DataEventType#ELEMENT_REMOVED}, or + * {@link DataEventType#ELEMENT_REPLACED}. + */ + public DataEventType getType() { + return _type; + } + + /** Returns the index. */ + public int getIndex() { + return _index; + } + + /** + * Returns the deleted element. + * @return null if either an element has been added or inserted. + */ + public DataElement getDeletedElement() { + return _deletedElement; + } +} diff --git a/src/jcckit/data/DataEventType.java b/src/jcckit/data/DataEventType.java new file mode 100644 index 000000000..116139b33 --- /dev/null +++ b/src/jcckit/data/DataEventType.java @@ -0,0 +1,35 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.data; + +/** + * Type of a {@link DataEvent}. Implements the typesafe enumeration pattern. + * + * @author Franz-Josef Elmer + */ +public class DataEventType { + private DataEventType() {} + + /** Event type. */ + public static final DataEventType ELEMENT_ADDED = new DataEventType(), + ELEMENT_INSERTED = new DataEventType(), + ELEMENT_REPLACED = new DataEventType(), + ELEMENT_REMOVED = new DataEventType(); + +} diff --git a/src/jcckit/data/DataListener.java b/src/jcckit/data/DataListener.java new file mode 100644 index 000000000..cd2c70841 --- /dev/null +++ b/src/jcckit/data/DataListener.java @@ -0,0 +1,33 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.data; + +/** + * An interface all observers of {@link DataEvent DataEvents} + * have to implement. + * + * @author Franz-Josef Elmer + */ +public interface DataListener { + /** + * Sends the specified data event to this object. + * @param event Data event informing where and what happened. + */ + public void dataChanged(DataEvent event); +} diff --git a/src/jcckit/data/DataPlot.java b/src/jcckit/data/DataPlot.java new file mode 100644 index 000000000..4ebf14f94 --- /dev/null +++ b/src/jcckit/data/DataPlot.java @@ -0,0 +1,74 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.data; + +import java.util.StringTokenizer; + +import jcckit.util.ConfigParameters; + +/** + * A plot is a {@link DataContainer} of {@link DataCurve DataCurves}. + * + * @author Franz-Josef Elmer + */ +public class DataPlot extends DataContainer { + /** Config parameter key. */ + public static final String CURVES_KEY = "curves", + DATA_KEY = "data"; + + /** Creates an empty instance. */ + public DataPlot() {} + + /** + * Creates an instance from the specified config parameters. + * + * + * + * + * + *
Key & Default ValueTypeMandatoryDescription
curvesString[]yesList of keys denoting data curves. Each key refers to + * config parameters used in the + * + * constructor of {@link DataCurve}.
+ */ + public DataPlot(ConfigParameters config) { + StringTokenizer tokenizer = new StringTokenizer(config.get(CURVES_KEY)); + while (tokenizer.hasMoreTokens()) { + addElement(new DataCurve(config.getNode(tokenizer.nextToken()))); + } + } + + /** + * Convenient method to create a DataPlot based on the specified + * config parameters. It is a short-cut of + * new DataPlot(config.getNode("data")). + */ + public static DataPlot create(ConfigParameters config) { + return new DataPlot(config.getNode(DATA_KEY)); + } + + /** + * Returns true if element is an instance of + * {@link DataCurve}. + */ + protected boolean isValid(DataElement element) { + return element instanceof DataCurve; + } +} + diff --git a/src/jcckit/data/DataPoint.java b/src/jcckit/data/DataPoint.java new file mode 100644 index 000000000..38810ebf3 --- /dev/null +++ b/src/jcckit/data/DataPoint.java @@ -0,0 +1,40 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.data; + +import jcckit.util.Point; + +/** + * Immutable two-dimensional point in data coordinates. + * + * @author Franz-Josef Elmer + */ +public class DataPoint extends Point implements DataElement { + public DataPoint(double x, double y) { + super(x, y); + } + + /** Returns always null. */ + public DataContainer getContainer() { + return null; + } + + /** Does nothing. */ + public void setContainer(DataContainer container) {} +} diff --git a/src/jcckit/graphic/Anchor.java b/src/jcckit/graphic/Anchor.java new file mode 100644 index 000000000..71ad63638 --- /dev/null +++ b/src/jcckit/graphic/Anchor.java @@ -0,0 +1,129 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.graphic; + +import jcckit.util.ConfigParameters; +import jcckit.util.FactoryException; + +/** + * Anchor of a graphical element. There exist only the three + * instances {@link #LEFT_BOTTOM}, {@link #CENTER}, and + * {@link #RIGHT_TOP}. + *

+ * The anchor factor can be used in a position formular. Its value + * for the three instances reads: + *

+ *

+ * + * + * + * + * + *
InstanceFactor
LEFT_BOTTOM0
CENTER1
RIGHT_TOP2
+ *
+ * + * @author Franz-Josef Elmer + */ +public class Anchor { + /** Anchor constant. */ + public static final Anchor LEFT_BOTTOM = new Anchor(0), + CENTER = new Anchor(1), + RIGHT_TOP = new Anchor(2); + private static final String LEFT_VALUE = "left", + RIGHT_VALUE = "right", + CENTER_VALUE = "center", + TOP_VALUE = "top", + BOTTOM_VALUE = "bottom"; + + /** + * Returns form the specified configuration parameters the + * horizontal anchor defined by the specified key or the + * specified default value. + * @param config Configuration parameters. + * @param key The key of the anchor. null is not allowed. + * @param defaultValue The default value. + * @return one of the three instances of Anchor. + * @throws FactoryException if the value of key is + * neither left, center, + * nor right. + * Note, that {@link FactoryException#getClassName()} + * returns the invalid value. + */ + public static Anchor getHorizontalAnchor(ConfigParameters config, String key, + Anchor defaultValue) { + Anchor result = defaultValue; + String anchor = config.get(key, null); + if (anchor != null) { + if (anchor.equals(LEFT_VALUE)) { + result = Anchor.LEFT_BOTTOM; + } else if (anchor.equals(CENTER_VALUE)) { + result = Anchor.CENTER; + } else if (anchor.equals(RIGHT_VALUE)) { + result = Anchor.RIGHT_TOP; + } else { + throw new FactoryException(config, key, "Invalid horizontal anchor."); + } + } + return result; + } + + /** + * Returns form the specified configuration parameters the + * vertical anchor defined by the specified key or the + * specified default value. + * @param config Configuration parameters. + * @param key The key of the anchor. null is not allowed. + * @param defaultValue The default value. + * @return one of the three instances of Anchor. + * @throws FactoryException if the value of key is + * neither top, center, + * nor bottom. + * Note, that {@link FactoryException#getClassName()} + * returns the invalid value. + */ + public static Anchor getVerticalAnchor(ConfigParameters config, String key, + Anchor defaultValue) { + Anchor result = defaultValue; + String anchor = config.get(key, null); + if (anchor != null) { + if (anchor.equals(BOTTOM_VALUE)) { + result = Anchor.LEFT_BOTTOM; + } else if (anchor.equals(CENTER_VALUE)) { + result = Anchor.CENTER; + } else if (anchor.equals(TOP_VALUE)) { + result = Anchor.RIGHT_TOP; + } else { + throw new FactoryException(config, key, "Invalid vertcal anchor."); + } + } + return result; + } + + private final int _factor; + + private Anchor(int factor) { + _factor = factor; + } + + /** Returns the factor. */ + public int getFactor() { + return _factor; + } +} + diff --git a/src/jcckit/graphic/BasicGraphicAttributes.java b/src/jcckit/graphic/BasicGraphicAttributes.java new file mode 100644 index 000000000..5b63ff161 --- /dev/null +++ b/src/jcckit/graphic/BasicGraphicAttributes.java @@ -0,0 +1,202 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.graphic; + +import java.awt.Color; + +import jcckit.util.ConfigParameters; + +/** + * The basic attributes of any {@link BasicGraphicalElement}. This is an + * extension of {@link ShapeAttributes} implementing {@link TextAttributes}. + * + * @author Franz-Josef Elmer + */ +public class BasicGraphicAttributes extends ShapeAttributes + implements TextAttributes { + /** Configuration parameter key. */ + public static final String TEXT_COLOR_KEY = "textColor", + FONT_NAME_KEY = "fontName", + FONT_STYLE_KEY = "fontStyle", + FONT_SIZE_KEY = "fontSize", + HORIZONTAL_ANCHOR_KEY = "horizontalAnchor", + VERTICAL_ANCHOR_KEY = "verticalAnchor", + ORIENTATION_ANGLE_KEY = "orientationAngle"; + + private final Color _textColor; + private final String _fontName; + private final FontStyle _fontStyle; + private final double _fontSize; + private final double _orientationAngle; + private final Anchor _horizontalAnchor; + private final Anchor _verticalAnchor; + + /** + * Creates a new instance based on the specified configuration + * parameters. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Key & Default ValueTypeMandatoryDescription
textColor = default foreground color of the + * rendererColornoThe text color.
fontName = default font name of the + * rendererStringnoThe name of the text font. The standard Java font name + * "Serif", "SansSerif", and "Monospaced" can be used. + * Other font names depend on the actual {@link Renderer} + * rendering the corresponding {@link BasicGraphicalElement}. + *
fontStyle = normalString + * noThe font style. Possible values are: + *
  • normal
  • bold
  • italic + *
  • bold italic
+ *
fontSize = default font size of the + * rendererdoublenoThe font size in units of the device-independent + * coordinates.
orientationAngle = 0doublenoThe orientation angle of the text (in degree). + * Zero means normal orientation whereas a positive value means + * a rotation in counter-clockweise direction.
horizontalAnchor = leftString + * noAnchor for horizontal text position. Possible values are + * left, center, and right.
verticalAnchor = centerString + * noAnchor for vertical text position. Possible values are + * top, center, and bottom.
+ * Additional configuration parameters are explained in the + * {@link ShapeAttributes#ShapeAttributes constructor} + * of the superclass {@link ShapeAttributes}. + */ + public BasicGraphicAttributes(ConfigParameters config) { + super(config); + _textColor = config.getColor(TEXT_COLOR_KEY, null); + _fontName = config.get(FONT_NAME_KEY, null); + _fontStyle = FontStyle.getFontStyle(config, FONT_STYLE_KEY, + FontStyle.NORMAL); + _fontSize = config.getDouble(FONT_SIZE_KEY, 0); + _orientationAngle = config.getDouble(ORIENTATION_ANGLE_KEY, 0); + + _horizontalAnchor = Anchor.getHorizontalAnchor(config, + HORIZONTAL_ANCHOR_KEY, Anchor.LEFT_BOTTOM); + _verticalAnchor = Anchor.getVerticalAnchor(config, + VERTICAL_ANCHOR_KEY, Anchor.CENTER); + } + + /** + * Creates a new instance. + * @param fillColor The fill color. May be null. + * @param lineColor The line color. May be null. + * @param lineThickness Thickness of the line. + * Negative numbers will be trimmed to zero. + * @param linePattern Line pattern. May be null. + * @param textColor The text color. May be null. + * @param fontName The font name. May be null. + * @param fontStyle The font style. May be null. + * @param fontSize The font size in units of the device-independent + * coordinates. May be null. + * @param orientationAngle Orientation angle of the text. + * @param horizontalAnchor Horizontal text anchor. + * @param verticalAnchor Vertical text anchor. + */ + public BasicGraphicAttributes(Color fillColor, Color lineColor, + double lineThickness, + double[] linePattern, Color textColor, + String fontName, FontStyle fontStyle, + double fontSize, double orientationAngle, + Anchor horizontalAnchor, + Anchor verticalAnchor) { + super(fillColor, lineColor, lineThickness, linePattern); + _textColor = textColor; + _fontName = fontName; + _fontStyle = fontStyle; + _fontSize = fontSize; + _orientationAngle = orientationAngle; + _horizontalAnchor = horizontalAnchor; + _verticalAnchor = verticalAnchor; + } + + /** + * Returns the text color. + * @return null means default color of the renderer. + */ + public Color getTextColor() { + return _textColor; + } + + /** + * Returns the font name. + * @return null means default font name of the renderer. + */ + public String getFontName() { + return _fontName; + } + + /** + * Returns the font style. + * @return null means default font style of the renderer. + */ + public FontStyle getFontStyle() { + return _fontStyle; + } + + /** + * Returns the font size in units of the device-independent coordinates. + */ + public double getFontSize() { + return _fontSize; + } + + /** + * Returns the orientation angle in degree. Zero means + * normal text orientation. Any positive angle means a + * counter-clockwise rotation of the text. + */ + public double getOrientationAngle() { + return _orientationAngle; + } + + /** + * Returns the anchor for horizontal position of the text. + * Note, that the anchor is related to the text before + * it is rotated by the orientation angle. + * @return one of the three instances of Anchor. + */ + public Anchor getHorizontalAnchor() { + return _horizontalAnchor; + } + + /** + * Returns the anchor for vertical position of the text. + * Note, that the anchor is related to the text before + * it is rotated by the orientation angle. + * @return one of the three instances of Anchor. + */ + public Anchor getVerticalAnchor() { + return _verticalAnchor; + } +} + diff --git a/src/jcckit/graphic/BasicGraphicalElement.java b/src/jcckit/graphic/BasicGraphicalElement.java new file mode 100644 index 000000000..37ed247ff --- /dev/null +++ b/src/jcckit/graphic/BasicGraphicalElement.java @@ -0,0 +1,58 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.graphic; + +/** + * Abstract superclass of all basic {@link GraphicalElement + * GraphicalElements}. Concrete subclasses have to implement + * the method {@link GraphicalElement#renderWith}. + * + * @author Franz-Josef Elmer + */ +public abstract class BasicGraphicalElement implements GraphicalElement { + private final GraphicAttributes _attributes; + + /** + * Creates an instance with the specified drawing attributes. + * Note, that a {@link Renderer} should use default attributes + * in the case no attributes are defined. + * @param attributes Drawing attributes or null if undefined. + */ + public BasicGraphicalElement(GraphicAttributes attributes) { + _attributes = attributes; + } + + /** + * Returns the drawing attributes. + * @return null if undefined. + */ + public GraphicAttributes getGraphicAttributes() { + return _attributes; + } + + /** + * Returns whether this basic graphical element has a closed shape + * or not. By default always true. Subclasses may override + * this behaviour. + * @return true if the shape is closed. + */ + public boolean isClosed() { + return true; + } +} diff --git a/src/jcckit/graphic/ClippingRectangle.java b/src/jcckit/graphic/ClippingRectangle.java new file mode 100644 index 000000000..3c56241c2 --- /dev/null +++ b/src/jcckit/graphic/ClippingRectangle.java @@ -0,0 +1,81 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.graphic; + +/** + * Immutable class of a rectangular clipping area. + * + * @author Franz-Josef Elmer + */ +public class ClippingRectangle implements ClippingShape { + private final double _minX, _minY, _maxX, _maxY; + + /** + * Creates an instance for the specified coordinates of + * two opposite corner points. + */ + public ClippingRectangle(double x1, double y1, double x2, double y2) { + _minX = Math.min(x1, x2); + _minY = Math.min(y1, y2); + _maxX = Math.max(x1, x2); + _maxY = Math.max(y1, y2); + } + + /** + * Returns true if the specified point is inside this + * rectangle. + */ + public boolean isInside(GraphPoint point) { + double x = point.getX(); + double y = point.getY(); + return _minX <= x && x <= _maxX && _minY <= y && y <= _maxY; + } + + /** Returns the minimum x value. */ + public double getMinX() { + return _minX; + } + + /** Returns the maximum x value. */ + public double getMaxX() { + return _maxX; + } + + /** Returns the minimum y value. */ + public double getMinY() { + return _minY; + } + + /** Returns the maximum y value. */ + public double getMaxY() { + return _maxY; + } + + /** Returns this instance. */ + public ClippingRectangle getBoundingBox() { + return this; + } + + /** Returns a {@link Rectangle}. */ + public BasicGraphicalElement getGraphicalElement() { + return new Rectangle(new GraphPoint(0.5 * (_minX + _maxX), + 0.5 * (_minY + _maxY)), + _maxX - _minX, _maxY - _minY, null); + } +} diff --git a/src/jcckit/graphic/ClippingShape.java b/src/jcckit/graphic/ClippingShape.java new file mode 100644 index 000000000..8559a9e8b --- /dev/null +++ b/src/jcckit/graphic/ClippingShape.java @@ -0,0 +1,47 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.graphic; + +/** + * Defining a clipping shape applied to all {@link GraphicalElement + * GraphicalElements} of a {@link GraphicalComposite}. + * + * @author Franz-Josef Elmer + */ +public interface ClippingShape { + /** + * Returns true if the specified point is inside this + * clipping shape. + */ + public boolean isInside(GraphPoint point); + + /** + * Returns the bounding box of this clipping shape. + * This method will be used by renderers who supports only + * rectangular clipping shapes. + */ + public ClippingRectangle getBoundingBox(); + + /** + * Returns a basic graphical element (such as {@link Rectangle} + * or {@link Polygon}) which may be used by renderers to + * define the clipping shape for the output device. + */ + public BasicGraphicalElement getGraphicalElement(); +} diff --git a/src/jcckit/graphic/FillAttributes.java b/src/jcckit/graphic/FillAttributes.java new file mode 100644 index 000000000..7af1b56f2 --- /dev/null +++ b/src/jcckit/graphic/FillAttributes.java @@ -0,0 +1,35 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.graphic; + +import java.awt.Color; + +/** + * Interface for fill attributes. + * + * @author Franz-Josef Elmer + */ +public interface FillAttributes extends GraphicAttributes { + /** + * Returns the fill color. + * @return null means no filling. + */ + public Color getFillColor(); +} + diff --git a/src/jcckit/graphic/FontStyle.java b/src/jcckit/graphic/FontStyle.java new file mode 100644 index 000000000..7c037d9c3 --- /dev/null +++ b/src/jcckit/graphic/FontStyle.java @@ -0,0 +1,82 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.graphic; + +import java.util.Hashtable; + +import jcckit.util.ConfigParameters; +import jcckit.util.FactoryException; + +/** + * Font style constants. + * This class is based on the typesafe enumeration pattern. + * + * @author Franz-Josef Elmer + */ +public class FontStyle { + private static final Hashtable REPOSITORY = new Hashtable(); + static final String NORMAL_TXT = "normal", + BOLD_TXT = "bold", + ITALIC_TXT = "italic", + BOLD_ITALIC_TXT = "bold italic"; + /** Font style constant. */ + public static final FontStyle NORMAL = new FontStyle(NORMAL_TXT), + BOLD = new FontStyle(BOLD_TXT), + ITALIC = new FontStyle(ITALIC_TXT), + BOLD_ITALIC = new FontStyle(BOLD_ITALIC_TXT); + + private final String _description; + + /** Non-public constructor to control the number of instances. */ + private FontStyle(String description) { + _description = description; + REPOSITORY.put(description, this); + } + + /** + * Returns from the specified configuration parameters the font style + * defined by the specified key or the specified default value. + * @param config Configuration parameters. + * @param key The key of the font style. + * @param defaultValue The default value. + * @return one of the four instances of FontStyle. + * @throws FactoryException if the value of the key-value pair denoted + * by key is neither normal, bold, + * italic, nor bold italic, + * Note, that {@link FactoryException#getClassName()} + * returns the invalid value. + */ + public static FontStyle getFontStyle(ConfigParameters config, String key, + FontStyle defaultValue) { + FontStyle result = defaultValue; + String value = config.get(key, null); + if (value != null) { + result = (FontStyle) REPOSITORY.get(value); + if (result == null) { + throw new FactoryException(config, key, "Invalid font style."); + } + } + return result; + } + + /** Returns a human readable description for pretty printing. */ + public String toString() { + return _description; + } +} diff --git a/src/jcckit/graphic/GraphPoint.java b/src/jcckit/graphic/GraphPoint.java new file mode 100644 index 000000000..a85de78a3 --- /dev/null +++ b/src/jcckit/graphic/GraphPoint.java @@ -0,0 +1,43 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.graphic; + +import jcckit.util.Point; + +/** + * Immutable class of a two-dimensional point in the device-independent + * coordinate system. + * + * @author Franz-Josef Elmer + */ +public class GraphPoint extends Point { + /** + * Creates an instance for the specified vector. + * If vector is null or not long enough the + * default value 0 will be used instead. + */ + public GraphPoint(double[] vector) { + super(vector); + } + + /** Creates an instance for the specified coordinates. */ + public GraphPoint(double x, double y) { + super(x, y); + } +} diff --git a/src/jcckit/graphic/GraphicAttributes.java b/src/jcckit/graphic/GraphicAttributes.java new file mode 100644 index 000000000..a418c1cd1 --- /dev/null +++ b/src/jcckit/graphic/GraphicAttributes.java @@ -0,0 +1,36 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.graphic; + +/** + * Marker interface for all types of graphic attributes + * of a {@link BasicGraphicalElement}. Graphic attributes are only + * hints for {@link Renderer Renderers} how to render a + * BasicGraphicalElement. Whether they are used and how + * they are interpreted depends on the concrete Renderer. + *

+ * This is only a marker interface. There are several subinterfaces + * specifying various attributes grouped by the type of element to + * be rendered. + * + * @author Franz-Josef Elmer + */ +public interface GraphicAttributes { +} + diff --git a/src/jcckit/graphic/GraphicalComposite.java b/src/jcckit/graphic/GraphicalComposite.java new file mode 100644 index 000000000..b981343d7 --- /dev/null +++ b/src/jcckit/graphic/GraphicalComposite.java @@ -0,0 +1,106 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.graphic; + +import java.util.Vector; + +/** + * Container for {@link GraphicalElement GraphicalElements}. + * + * @author Franz-Josef Elmer + */ +public class GraphicalComposite implements GraphicalElement { + private final Vector _elements = new Vector(); + private final ClippingShape _clippingShape; + + /** + * Creates an instance with the specified clipping shape. + * @param clippingShape Clipping shape or null if no clipping. + */ + public GraphicalComposite(ClippingShape clippingShape) { + _clippingShape = clippingShape; + } + + /** + * Returns the clipping shape. + * @return null if no clipping should be applied. + */ + public ClippingShape getClippingShape() { + return _clippingShape; + } + + /** + * Adds the specified element at the end of the list of elements. + * @param element Element to be added. null is not allowed. + * @throws NullPointerException if element == null + */ + public void addElement(GraphicalElement element) { + if (element == null) { + throwNullPointerException(); + } else { + _elements.addElement(element); + } + } + + /** Remove all elements. */ + public void removeAllElements() { + _elements.removeAllElements(); + } + + /** + * Replaces the specified element at the specified index of + * the list of elements. + * @param element New element. null is not allowed. + * @throws NullPointerException if element == null + */ + public void replaceElementAt(int index, GraphicalElement element) { + if (element == null) { + throwNullPointerException(); + } else { + _elements.setElementAt(element, index); + } + } + + private void throwNullPointerException() { + throw new NullPointerException( + "A null as an GraphicalElement is not allowed"); + } + + /** + * Renders all {@link GraphicalElement GraphicalElements} in the sequence + * they have been added. + * @param renderer Renderer which implements all renderer interfaces + * necessary to render the child elements. + * @throws IllegalArgumentException if renderer is not + * an instance of GraphicalCompositeRenderer. + */ + public void renderWith(Renderer renderer) { + if (renderer instanceof GraphicalCompositeRenderer) { + GraphicalCompositeRenderer r = (GraphicalCompositeRenderer) renderer; + r.startRendering(this); + for (int i = 0, n = _elements.size(); i < n; i++) { + ((GraphicalElement) _elements.elementAt(i)).renderWith(r); + } + r.finishRendering(this); + } else { + throw new IllegalArgumentException(renderer + + " does not implements GraphicalCompositeRenderer."); + } + } +} diff --git a/src/jcckit/graphic/GraphicalCompositeRenderer.java b/src/jcckit/graphic/GraphicalCompositeRenderer.java new file mode 100644 index 000000000..f3c7ba402 --- /dev/null +++ b/src/jcckit/graphic/GraphicalCompositeRenderer.java @@ -0,0 +1,40 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.graphic; + +/** + * Interface of all {@link Renderer Renderers} who render a + * {@link GraphicalComposite}. Note, that a + * GraphicalCompositeRenderer does not + * render the element of a GraphicalComposite + * + * @author Franz-Josef Elmer + */ +public interface GraphicalCompositeRenderer extends Renderer { + /** + * Starts rendering of the specified composite before its + * elements are rendererd. Implementations of this method + * usually obtain the {@link ClippingShape} from + * composite. + */ + public void startRendering(GraphicalComposite composite); + + /** Finishes rendering of the specified composite. */ + public void finishRendering(GraphicalComposite composite); +} diff --git a/src/jcckit/graphic/GraphicalElement.java b/src/jcckit/graphic/GraphicalElement.java new file mode 100644 index 000000000..2b28fa40a --- /dev/null +++ b/src/jcckit/graphic/GraphicalElement.java @@ -0,0 +1,41 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.graphic; + +/** + * Interface all graphical elements have to implement. + * Together with the marker interface {@link Renderer} it + * realizes the Anticyclic Visitor Pattern, a variant of the + * GoF Visitor Pattern. This allows not only to extend JCCKit with + * new renderers but also with new types of GraphicalElements + * without touching existing code. + * + * @author Franz-Josef Elmer + */ +public interface GraphicalElement { + /** + * Renders this element according to the type of renderer. + * Concrete GraphicalElements who are not instances of + * {@link GraphicalComposite} dynamically cast renderer. + * If it does not implement the type of renderer specific for + * the concrete GraphicalElement it should throw an + * IllegalArgumentException. + */ + public abstract void renderWith(Renderer renderer); +} diff --git a/src/jcckit/graphic/LineAttributes.java b/src/jcckit/graphic/LineAttributes.java new file mode 100644 index 000000000..2d8f7a178 --- /dev/null +++ b/src/jcckit/graphic/LineAttributes.java @@ -0,0 +1,52 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.graphic; + +import java.awt.Color; + +/** + * Interface for line attributes. + * + * @author Franz-Josef Elmer + */ +public interface LineAttributes extends GraphicAttributes { + /** + * Returns the line color. + * @return null means default color of the renderer. + */ + public Color getLineColor(); + + /** + * Returns the line tickness. 0 means that the line thickness is + * chosen as thin as possible. + * Implementations have to guarantee that the returned value is + * never negative. + */ + public double getLineThickness(); + + /** + * Returns the line pattern. This is a sequence of length where the + * pen is down or up. The first element is the length where the + * pen is down. The next element is the length where the pen is up. + * The pattern is cyclically repeated. + * @return null means solid line. + */ + public double[] getLinePattern(); +} + diff --git a/src/jcckit/graphic/Oval.java b/src/jcckit/graphic/Oval.java new file mode 100644 index 000000000..9d49621a6 --- /dev/null +++ b/src/jcckit/graphic/Oval.java @@ -0,0 +1,54 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.graphic; + +/** + * An oval (i.e. an ellipse). + * + * @author Franz-Josef Elmer + */ +public class Oval extends Rectangle { + /** + * Creates a new instance. + * @param center The position of the center of this element. + * @param width The width. + * @param height The height. + * @param attributes Drawing attributes. Can be null. + */ + public Oval(GraphPoint center, double width, double height, + GraphicAttributes attributes) { + super(center, width, height, attributes); + } + + /** + * Renders this oval with the specified {@link Renderer}. + * @param renderer An instance of {@link OvalRenderer}. + * @throws IllegalArgumentException if renderer is not + * an instance of OvalRenderer. + */ + public void renderWith(Renderer renderer) { + if (renderer instanceof OvalRenderer) { + ((OvalRenderer) renderer).render(this); + } else { + throw new IllegalArgumentException(renderer + + " does not implements OvalRenderer."); + } + } +} + diff --git a/src/jcckit/graphic/OvalRenderer.java b/src/jcckit/graphic/OvalRenderer.java new file mode 100644 index 000000000..97108eb2e --- /dev/null +++ b/src/jcckit/graphic/OvalRenderer.java @@ -0,0 +1,30 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.graphic; + +/** + * Interface of all {@link Renderer Renderers} who render a + * {@link Oval}. + * + * @author Franz-Josef Elmer + */ +public interface OvalRenderer extends Renderer { + /** Renders the specified oval. */ + public void render(Oval oval); +} diff --git a/src/jcckit/graphic/Polygon.java b/src/jcckit/graphic/Polygon.java new file mode 100644 index 000000000..45245b4d8 --- /dev/null +++ b/src/jcckit/graphic/Polygon.java @@ -0,0 +1,85 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.graphic; + +import java.util.Vector; + +/** + * A polygon or polyline. + * + * @author Franz-Josef Elmer + */ +public class Polygon extends BasicGraphicalElement { + private final Vector _points = new Vector(); + private final boolean _closed; + + /** + * Creates an instance of the specified graphic attributes. + * @param closed true if this polygon is closed. + */ + public Polygon(GraphicAttributes attributes, boolean closed) { + super(attributes); + _closed = closed; + } + + /** Returns true if this polygon is closed. */ + public boolean isClosed() { + return _closed; + } + + /** Returns the number points. */ + public int getNumberOfPoints() { + return _points.size(); + } + + /** Returns the point for the specified index. */ + public GraphPoint getPoint(int index) { + return (GraphPoint) _points.elementAt(index); + } + + /** Adds a new point to the end of the list of points. */ + public void addPoint(GraphPoint point) { + _points.addElement(point); + } + + /** Removes all points. */ + public void removeAllPoints() { + _points.removeAllElements(); + } + + /** Replaces the point at the specified index by a new one. */ + public void replacePointAt(int index, GraphPoint point) { + _points.setElementAt(point, index); + } + + /** + * Renders this line with the specified {@link Renderer}. + * @param renderer An instance of {@link PolygonRenderer}. + * @throws IllegalArgumentException if renderer is not + * an instance of PolygonRenderer. + */ + public void renderWith(Renderer renderer) { + if (renderer instanceof PolygonRenderer) { + ((PolygonRenderer) renderer).render(this); + } else { + throw new IllegalArgumentException(renderer + + " does not implements PolygonRenderer."); + } + } +} diff --git a/src/jcckit/graphic/PolygonRenderer.java b/src/jcckit/graphic/PolygonRenderer.java new file mode 100644 index 000000000..0c07f905d --- /dev/null +++ b/src/jcckit/graphic/PolygonRenderer.java @@ -0,0 +1,30 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.graphic; + +/** + * Interface of all {@link Renderer Renderers} who render an + * instance of {@link Polygon}. + * + * @author Franz-Josef Elmer + */ +public interface PolygonRenderer extends Renderer { + /** Renders the specified Polygon instance. */ + public void render(Polygon polygon); +} diff --git a/src/jcckit/graphic/Rectangle.java b/src/jcckit/graphic/Rectangle.java new file mode 100644 index 000000000..f66a3c5f1 --- /dev/null +++ b/src/jcckit/graphic/Rectangle.java @@ -0,0 +1,76 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.graphic; + +/** + * A rectangle. + * + * @author Franz-Josef Elmer + */ +public class Rectangle extends BasicGraphicalElement { + private final GraphPoint _center; + private final double _width; + private final double _height; + + /** + * Creates a new instance. + * @param center The position of the center of this element. + * @param width The width. + * @param height The height. + * @param attributes Drawing attributes. Can be null. + */ + public Rectangle(GraphPoint center, double width, double height, + GraphicAttributes attributes) { + super(attributes); + _center = center; + _width = width; + _height = height; + } + + /** Returns the center of this element. */ + public GraphPoint getCenter() { + return _center; + } + + /** Returns the width of this element. */ + public double getWidth() { + return _width; + } + + /** Returns the height of this element. */ + public double getHeight() { + return _height; + } + + /** + * Renders this rectangle with the specified {@link Renderer}. + * @param renderer An instance of {@link RectangleRenderer}. + * @throws IllegalArgumentException if renderer is not + * an instance of RectangleRenderer. + */ + public void renderWith(Renderer renderer) { + if (renderer instanceof RectangleRenderer) { + ((RectangleRenderer) renderer).render(this); + } else { + throw new IllegalArgumentException(renderer + + " does not implements RectangleRenderer."); + } + } +} + diff --git a/src/jcckit/graphic/RectangleRenderer.java b/src/jcckit/graphic/RectangleRenderer.java new file mode 100644 index 000000000..6a1d01c66 --- /dev/null +++ b/src/jcckit/graphic/RectangleRenderer.java @@ -0,0 +1,30 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.graphic; + +/** + * Interface of all {@link Renderer Renderers} who render a + * {@link Rectangle}. + * + * @author Franz-Josef Elmer + */ +public interface RectangleRenderer extends Renderer { + /** Renders the specified rectangle. */ + public void render(Rectangle rectangle); +} diff --git a/src/jcckit/graphic/Renderer.java b/src/jcckit/graphic/Renderer.java new file mode 100644 index 000000000..b3d722645 --- /dev/null +++ b/src/jcckit/graphic/Renderer.java @@ -0,0 +1,27 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.graphic; + +/** + * Marker interface. Each subclass is an interface for a specific + * type of {@link GraphicalElement}. + * + * @author Franz-Josef Elmer + */ +public interface Renderer {} diff --git a/src/jcckit/graphic/ShapeAttributes.java b/src/jcckit/graphic/ShapeAttributes.java new file mode 100644 index 000000000..362f3037d --- /dev/null +++ b/src/jcckit/graphic/ShapeAttributes.java @@ -0,0 +1,104 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.graphic; + +import java.awt.Color; + +import jcckit.util.ConfigParameters; + +/** + * Basic attributes for shapes. + * + * @author Franz-Josef Elmer + */ +public class ShapeAttributes implements LineAttributes, FillAttributes { + /** Configuration parameter key. */ + public static final String FILL_COLOR_KEY = "fillColor", + LINE_COLOR_KEY = "lineColor", + LINE_THICKNESS_KEY = "lineThickness", + LINE_PATTERN_KEY = "linePattern"; + + private final Color _fillColor; + private final Color _lineColor; + private final double _lineThickness; + private final double[] _linePattern; + + /** + * Creates a new instance based on the specified configuration + * parameters. + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Key & Default ValueTypeMandatoryDescription
fillColor = no fillingColornoThe fill color of the shape.
lineColor = no lineColornoThe color of a line, a polygon, or the border of a shape.
lineThickness = 0doublenoThe thickness of a line. A thickness of zero means that + * the renderer will draw the thinest line possible.
linePattern = solid linedouble[]noA sequence of lengths where the pen is alternatively + * down or up. For example, 0.1 0.1 will lead to a dashed + * line whereas 0.02 0.02 is the pattern of a dotted + * line and 0.02 0.02 0.1 0.02 of a dashed-dotted + * line.
+ */ + public ShapeAttributes(ConfigParameters config) { + this(config.getColor(FILL_COLOR_KEY, null), + config.getColor(LINE_COLOR_KEY, null), + config.getDouble(LINE_THICKNESS_KEY, 0), + config.getDoubleArray(LINE_PATTERN_KEY, null)); + } + + /** + * Creates a new instance. + * @param fillColor The fill color. May be null. + * @param lineColor The line color. May be null. + * @param lineThickness Thickness of the line. + * Negative numbers will be trimmed to zero. + * @param linePattern Line pattern. May be null. + */ + public ShapeAttributes(Color fillColor, Color lineColor, + double lineThickness, double[] linePattern) { + _fillColor = fillColor; + _lineColor = lineColor; + _lineThickness = Math.max(0, lineThickness); + _linePattern = linePattern; + } + + public Color getFillColor() { + return _fillColor; + } + + public Color getLineColor() { + return _lineColor; + } + + public double getLineThickness() { + return _lineThickness; + } + + public double[] getLinePattern() { + return _linePattern; + } +} + diff --git a/src/jcckit/graphic/Text.java b/src/jcckit/graphic/Text.java new file mode 100644 index 000000000..23133c260 --- /dev/null +++ b/src/jcckit/graphic/Text.java @@ -0,0 +1,67 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.graphic; + +/** + * A single line of text. + * + * @author Franz-Josef Elmer + */ +public class Text extends BasicGraphicalElement { + private final GraphPoint _position; + private final String _text; + + + /** + * Creates an instance with the specified parameters. + * @param position Position of the text. + * @param text Text. + * @param attributes Drawing attributes. Can be null. + */ + public Text(GraphPoint position, String text, GraphicAttributes attributes) { + super(attributes); + _position = position; + _text = text; + } + + /** Returns the position. */ + public GraphPoint getPosition() { + return _position; + } + + /** Returns the text string. */ + public String getText() { + return _text; + } + + /** + * Renders this line with the specified {@link Renderer}. + * @param renderer An instance of {@link TextRenderer}. + * @throws IllegalArgumentException if renderer is not + * an instance of TextRenderer. + */ + public void renderWith(Renderer renderer) { + if (renderer instanceof TextRenderer) { + ((TextRenderer) renderer).render(this); + } else { + throw new IllegalArgumentException(renderer + + " does not implements TextRenderer."); + } + } +} diff --git a/src/jcckit/graphic/TextAttributes.java b/src/jcckit/graphic/TextAttributes.java new file mode 100644 index 000000000..3e6080d85 --- /dev/null +++ b/src/jcckit/graphic/TextAttributes.java @@ -0,0 +1,75 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.graphic; + +import java.awt.Color; + +/** + * Interface for text attributes. + * + * @author Franz-Josef Elmer + */ +public interface TextAttributes extends GraphicAttributes { + /** + * Returns the text color. + * @return null means default color of the renderer. + */ + public Color getTextColor(); + + /** + * Returns the font name. + * @return null means default font name of the renderer. + */ + public String getFontName(); + + /** + * Returns the font style. + * @return null means default font style of the renderer. + */ + public FontStyle getFontStyle(); + + /** + * Returns the font size in units of the device-independent coordinates. + */ + public double getFontSize(); + + /** + * Returns the orientation angle in degree. Zero means + * normal text orientation. Any positive angle means a + * counter-clockwise rotation of the text. + */ + public double getOrientationAngle(); + + /** + * Returns the anchor for horizontal position of the text. + * Note, that the anchor is related to the text before + * it is rotated by the orientation angle. + * @return one of the three instances of Anchor. + */ + public Anchor getHorizontalAnchor(); + + /** + * Returns the anchor for vertical position of the text. + * Note, that the anchor is related to the text before + * it is rotated by the orientation angle. + * @return one of the three instances of Anchor. + */ + public Anchor getVerticalAnchor(); +} + diff --git a/src/jcckit/graphic/TextRenderer.java b/src/jcckit/graphic/TextRenderer.java new file mode 100644 index 000000000..98f2124ee --- /dev/null +++ b/src/jcckit/graphic/TextRenderer.java @@ -0,0 +1,30 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.graphic; + +/** + * Interface of all {@link Renderer Renderers} who render an + * instance of {@link Text}. + * + * @author Franz-Josef Elmer + */ +public interface TextRenderer extends Renderer { + /** Renders the specified Text instance. */ + public void render(Text text); +} diff --git a/src/jcckit/plot/AbstractSymbolFactory.java b/src/jcckit/plot/AbstractSymbolFactory.java new file mode 100644 index 000000000..f88f18abd --- /dev/null +++ b/src/jcckit/plot/AbstractSymbolFactory.java @@ -0,0 +1,120 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.plot; + +import jcckit.graphic.GraphPoint; +import jcckit.graphic.GraphicAttributes; +import jcckit.graphic.GraphicalElement; +import jcckit.util.ConfigParameters; +import jcckit.util.Factory; + +/** + * Abstract superclass of all {@link SymbolFactory SymbolFactories}. + * Subclasses have to implement {@link #createPlainSymbol createPlainSymbol()}. + * + * @author Franz-Josef Elmer + */ +public abstract class AbstractSymbolFactory implements SymbolFactory { + /** Size of all symbols. */ + protected final double _size; + + /** Attributes of all symbols. */ + protected final GraphicAttributes _attributes; + + /** + * Creates an instance from the specified configuration parameters. + * + * + * + * + * + * + * + * + * + *
Key & Default ValueTypeMandatoryDescription
size = 0.01doublenoSize of the symbol in device-independent units.
attributesConfigParametersnoConfiguration parameters for the attributes of the symbol. + * className has to be a class which is an instance of + * {@link GraphicAttributes}.
+ */ + public AbstractSymbolFactory(ConfigParameters config) { + _size = config.getDouble(SIZE_KEY, DEFAULT_SIZE); + _attributes = (GraphicAttributes) Factory.createOrGet( + config.getNode(ATTRIBUTES_KEY), null); + } + + /** + * Creates a symbol. + * Evaluate hintFromPreviousPoint if it is a {@link AttributesHint}. + * Calls {@link #createSymbol(GraphPoint, GraphicAttributes, Hint, Hint)}. + * @param point Symbol position. + * @param hintFromPreviousPoint Hint from the previous point. + * @param hintFromPreviousCurve Hint from the previous curve. + */ + public Symbol createSymbol(GraphPoint point, Hint hintFromPreviousPoint, + Hint hintFromPreviousCurve) { + GraphicAttributes attributes = _attributes; + Hint hintForNextPoint = hintFromPreviousPoint; + if (hintFromPreviousPoint instanceof AttributesHint) { + attributes = ((AttributesHint) hintFromPreviousPoint).getAttributes(); + hintForNextPoint + = ((AttributesHint) hintFromPreviousPoint).getNextHint(); + } + return createSymbol(point, attributes, hintForNextPoint, + hintFromPreviousCurve); + } + + /** + * Creates a symbol. + * Uses {@link #createPlainSymbol createPlainSymbol()}. + * @param point Symbol position. + * @param attributes Symbol attributes. + * @param hintForNextPoint Hint for the next point. Will be delivered + * unchanged in the return Symbol object. + * @param hintFromPreviousCurve Hint from the previous curve. + * Will be delivered unchanged in the return Symbol object. + * Subclasses may override this behavior. + */ + protected Symbol createSymbol(GraphPoint point, GraphicAttributes attributes, + Hint hintForNextPoint, + Hint hintFromPreviousCurve) { + return new Symbol(createPlainSymbol(point, _size, attributes), + hintForNextPoint, hintFromPreviousCurve); + } + + /** + * Creates a symbol for the legend at the specified position. + * Uses {@link #createPlainSymbol createPlainSymbol()} + * @param centerPosition Center position of the symbol. + * @param size The size of the symbol. Will be ignored because the value + * given in the constructor will be used. + */ + public GraphicalElement createLegendSymbol(GraphPoint centerPosition, + double size) { + return createPlainSymbol(centerPosition, _size, _attributes); + } + + /** + * Creates the graphical element of the plain symbol. + * @param centerPosition Center position of the symbol. + * @param size The size of the symbol. + * @param attributes The attributes of the symbol. + */ + protected abstract GraphicalElement createPlainSymbol( + GraphPoint centerPosition, double size, GraphicAttributes attributes); +} diff --git a/src/jcckit/plot/AttributesHint.java b/src/jcckit/plot/AttributesHint.java new file mode 100644 index 000000000..0ab44166f --- /dev/null +++ b/src/jcckit/plot/AttributesHint.java @@ -0,0 +1,39 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.plot; + +import jcckit.graphic.GraphicAttributes; + +/** + * A {@link Hint} which wraps a {@link GraphicAttributes} instance. + * In addition the method {@link #getNextHint()} creates a new instance + * with different attributes derivated from the wrapped attributes. + * + * @author Franz-Josef Elmer + */ +public interface AttributesHint extends Hint { + /** + * Returns the hint for the next {@link Symbol} of a {@link Curve}. + * The new hint has a different {@link GraphicAttributes}. + */ + public AttributesHint getNextHint(); + + /** Returns the attributes value. */ + public GraphicAttributes getAttributes(); +} diff --git a/src/jcckit/plot/AxisParameters.java b/src/jcckit/plot/AxisParameters.java new file mode 100644 index 000000000..3fd2aa089 --- /dev/null +++ b/src/jcckit/plot/AxisParameters.java @@ -0,0 +1,496 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.plot; + +import java.util.Properties; + +import jcckit.graphic.BasicGraphicAttributes; +import jcckit.graphic.GraphPoint; +import jcckit.graphic.LineAttributes; +import jcckit.graphic.ShapeAttributes; +import jcckit.graphic.TextAttributes; +import jcckit.util.ConfigData; +import jcckit.util.ConfigParameters; +import jcckit.util.ConfigParametersBasedConfigData; +import jcckit.util.Factory; +import jcckit.util.Format; +import jcckit.util.PropertiesBasedConfigData; +import jcckit.util.TicLabelFormat; +import jcckit.util.Util; + +/** + * Helper class with various parameters defining an axis. + * This helper class is used by {@link CartesianCoordinateSystem} + * to set up a coordinate systems. + *

+ * This class holds more than a dozen parameters. There are two factory + * methods creating instances for x- and y-axis based on + * {@link ConfigParameters}. They differ in their default parameters for + * those axes. + *

+ * Note, that there is a direct access of these parameters without getters + * and setters but only for classes in the package jcckit.plot. + * + * @author Franz-Josef Elmer + */ +public class AxisParameters { + /** Configuration parameter key. */ + public static final String LOG_SCALE_KEY = "logScale", + MINIMUM_KEY = "minimum", + MAXIMUM_KEY = "maximum", + AXIS_LENGTH_KEY = "axisLength", + AXIS_ATTRIBUTES_KEY = "axisAttributes", + AXIS_LABEL_KEY = "axisLabel", + AXIS_LABEL_POSITION_KEY = "axisLabelPosition", + AXIS_LABEL_ATTRIBUTES_KEY = "axisLabelAttributes", + AUTOMATIC_TIC_CALCULATION_KEY + = "automaticTicCalculation", + MINIMUM_TIC_KEY = "minimumTic", + MAXIMUM_TIC_KEY = "maximumTic", + NUMBER_OF_TICS_KEY = "numberOfTics", + TIC_LENGTH_KEY = "ticLength", + TIC_ATTRIBUTES_KEY = "ticAttributes", + TIC_LABEL_FORMAT_KEY = "ticLabelFormat", + TIC_LABEL_POSITION_KEY = "ticLabelPosition", + TIC_LABEL_ATTRIBUTES_KEY = "ticLabelAttributes", + GRID_KEY = "grid", + GRID_ATTRIBUTES_KEY = "gridAttributes"; + + private static final double LN10 = Math.log(10); + + /** If true the scale is logarithmic otherwise linear. */ + boolean logScale; + /** Minimum data value represented by the axis. */ + double minimum; + /** Maximum data value represented by the axis. */ + double maximum; + /** Length of the axis in device-independent graphical units. */ + double axisLength; + /** + * Line attributes of the axis. + * Can be null which means default attributes. + */ + LineAttributes axisAttributes; + + boolean automaticTicCalculation; + double minimumTic; + double maximumTic; + int numberOfTics; + /** + * Length of the tics in device-independent graphical units. + * If 0 no tics and tics label will be drawn. + */ + double ticLength; + /** + * Attributes of the tics. + * Can be null which means default attributes. + */ + LineAttributes ticAttributes; + /** Tic label formatter. */ + TicLabelFormat ticLabelFormat; + /** Position of the tic label relative to the tic. */ + GraphPoint ticLabelPosition; + /** Text attributes of the tic labels. */ + TextAttributes ticLabelAttributes; + + /** If true grid lines are drawn. */ + boolean grid; + /** + * Attributes of the grid lines. + * Can be null which means default attributes. + */ + LineAttributes gridAttributes; + + /** Axis label. */ + String axisLabel; + /** Position of the axis label relative to the center of the axis. */ + GraphPoint axisLabelPosition; + /** Text attributes of the axis label. */ + TextAttributes axisLabelAttributes; + + /** + * Calculate the tics based on minimumTic, maximumTic, + * and numberOfTics. If automaticTicCalculation == true + * appropriated values for these fields are calculated. + */ + double[] calculateTics() { + if (automaticTicCalculation) { + calculateTicsParameters(); + } + double[] result = new double[numberOfTics]; + if (numberOfTics > 0) { + double b = Util.log(minimumTic, logScale); + double a = Util.log(maximumTic, logScale); + a = numberOfTics > 1 ? (a - b) / (numberOfTics - 1) : 0; + for (int i = 0; i < result.length; i++) { + result[i] = Util.exp(a * i + b, logScale); + } + result[0] = adjust(minimum, result[0]); + result[numberOfTics - 1] = adjust(maximum, result[numberOfTics - 1]); + } + return result; + } + + private void calculateTicsParameters() { + double min = Math.min(minimum, maximum); + double max = Math.max(minimum, maximum); + if (logScale) { + int minExponent = (int) (199.9999 + Math.log(min) / LN10) - 199; + int maxExponent = (int) (200.0001 + Math.log(max) / LN10) - 200; + minimumTic = Math.exp(LN10 * minExponent); + maximumTic = Math.exp(LN10 * maxExponent); + numberOfTics = maxExponent - minExponent + 1; + } else { + int baseExponent = (int) (199.69 + Math.log(max - min) / LN10) - 200; + double base = 0.2 * Math.exp(LN10 * baseExponent); + do + { + base *= 5; + int minInt = (int) (999999.999999 + min / base) - 999999; + int maxInt = (int) (1000000.000001 + max / base) - 1000000; + minimumTic = minInt * base; + maximumTic = maxInt * base; + numberOfTics = maxInt - minInt + 1; + } while (numberOfTics > 11); + } + } + + /** + * Returns adjustingValue if value is very close + * to adjustingValue. Otherwise value is returned. + */ + private static double adjust(double adjustingValue, double value) { + return value != 0 && Math.abs(adjustingValue / value - 1) < 1e-11 + ? adjustingValue : value; + } + + /** + * Returns a Properties object with those default parameters + * which are common for x- and y-axis. + */ + private static Properties createDefaultAxisProperties() { + Properties p = new Properties(); + p.put(LOG_SCALE_KEY, "false"); + p.put(MINIMUM_KEY, "0"); + p.put(MAXIMUM_KEY, "1"); + p.put(AXIS_LENGTH_KEY, "0.8"); + p.put(AXIS_ATTRIBUTES_KEY + '/' + Factory.CLASS_NAME_KEY, + ShapeAttributes.class.getName()); + p.put(AXIS_LABEL_KEY, "x"); + p.put(AXIS_LABEL_POSITION_KEY, "0 -0.05"); + p.put(AXIS_LABEL_ATTRIBUTES_KEY + '/' + Factory.CLASS_NAME_KEY, + BasicGraphicAttributes.class.getName()); + p.put(AXIS_LABEL_ATTRIBUTES_KEY + '/' + + BasicGraphicAttributes.HORIZONTAL_ANCHOR_KEY, "center"); + p.put(AUTOMATIC_TIC_CALCULATION_KEY, "true"); + p.put(TIC_LENGTH_KEY, "0.01"); + p.put(TIC_ATTRIBUTES_KEY + '/' + Factory.CLASS_NAME_KEY, + ShapeAttributes.class.getName()); + p.put(TIC_LABEL_POSITION_KEY, "0 -0.01"); + p.put(TIC_LABEL_FORMAT_KEY, "%1.1f"); + p.put(TIC_LABEL_ATTRIBUTES_KEY + '/' + Factory.CLASS_NAME_KEY, + BasicGraphicAttributes.class.getName()); + p.put(TIC_LABEL_ATTRIBUTES_KEY + '/' + + BasicGraphicAttributes.HORIZONTAL_ANCHOR_KEY, "center"); + p.put(TIC_LABEL_ATTRIBUTES_KEY + '/' + + BasicGraphicAttributes.VERTICAL_ANCHOR_KEY, "top"); + p.put(GRID_KEY, "false"); + p.put(GRID_ATTRIBUTES_KEY + '/' + Factory.CLASS_NAME_KEY, + ShapeAttributes.class.getName()); + return p; + } + + /** + * Returns a Properties object of the default parameters for + * an x-axis. + */ + private static Properties createDefaultXAxisProperties() { + Properties p = createDefaultAxisProperties(); + p.put(AXIS_LABEL_KEY, "x"); + p.put(AXIS_LABEL_POSITION_KEY, "0 -0.05"); + p.put(AXIS_LABEL_ATTRIBUTES_KEY + '/' + + BasicGraphicAttributes.VERTICAL_ANCHOR_KEY, "top"); + p.put(TIC_LABEL_POSITION_KEY, "0 -0.01"); + p.put(TIC_LABEL_ATTRIBUTES_KEY + '/' + + BasicGraphicAttributes.HORIZONTAL_ANCHOR_KEY, "center"); + p.put(TIC_LABEL_ATTRIBUTES_KEY + '/' + + BasicGraphicAttributes.VERTICAL_ANCHOR_KEY, "top"); + return p; + } + + /** + * Creates an x axis based on the specified configuration parameters. + * All numbers (lengths, fontsizes, linethicknesses, etc.) are in + * device-independent units. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Key & Default ValueTypeMandatoryDescription
automaticTicCalculation = truebooleannoHas to be true if the tics should be calculated + * automatically.
axisAttributes = default values of + * {@link ShapeAttributes}ConfigParametersnoAttributes of the axis box.
axisLabel = xStringnoAxis label.
axisLabelAttributes = default values of + * {@link BasicGraphicAttributes} with a text anchor CENTER + * TOP.ConfigParametersnoText attributes of axis label.
axisLabelPosition = 0 -0.05double[]noPosition of the anchor of the axis + * label relative to the center of the x-axis line.
axisLength = 0.8doublenoLength of the x-axis.
grid = falsebooleannoIf true grid lines will be drawn through the axis + * tics.
gridAttributes = default values of + * {@link ShapeAttributes}ConfigParametersnoAttributes of the grid lines.
logScale = falsebooleannoIf true the axis will be logarithmic. Otherwise + * the axis is linear.
maximum = 1doublenoThe corresponding data value of one end of the axis.
maximumTic = result from automatic calculationdoublenoThe corresponding data value of the tic nearest the maximum end + * of the axis.
minimum = 0doublenoThe corresponding data value of one end of the axis.
minimumTic = result from automatic calculationdoublenoThe corresponding data value of the tic nearest the minimum end + * of the axis.
numberOfTics = result from automatic calculationintnoNumber of tics. The tics between the minimum and maximum tic + * are spaced equidistantly.
ticAttributes = default values of + * {@link ShapeAttributes}ConfigParametersnoAttributes of the tics.
ticLabelAttributes = default values of + * {@link BasicGraphicAttributes} with a text anchor CENTER + * TOP.ConfigParametersnoText attributes of tic labels.
ticLabelFormat = %1.1fString or ConfigParametersnoDefines rendering of the tic label. By default a + * printf-like format string is given (see {@link Format}). + * Note, that an empty string means that tic labels are dropped. + *

+ * For non-numerical rendering an implementation of a + * {@link TicLabelFormat} can be specified (e.g. + * {@link TicLabelMap}). Note, that a configuration sub tree with + * a className key-value pair overwrites any string + * definition.

ticLabelPosition = 0 -0.01double[]noPosition of the anchor of the tic label relative to the + * tic position on the axis.
ticLength = 0.01doublenoLength of the tics. Negative/positive values mean tics + * inside/outside the box.
+ */ + public static AxisParameters createXAxis(ConfigParameters config) { + return createAxis(config, createDefaultXAxisProperties()); + } + + /** + * Returns a Properties object of the default parameters for + * an x-axis. + */ + private static Properties createDefaultYAxisProperties() { + Properties p = createDefaultAxisProperties(); + p.put(AXIS_LENGTH_KEY, "0.45"); + p.put(AXIS_LABEL_KEY, "y"); + p.put(AXIS_LABEL_POSITION_KEY, "-0.1 0"); + p.put(AXIS_LABEL_ATTRIBUTES_KEY + '/' + + BasicGraphicAttributes.VERTICAL_ANCHOR_KEY, "bottom"); + p.put(AXIS_LABEL_ATTRIBUTES_KEY + '/' + + BasicGraphicAttributes.ORIENTATION_ANGLE_KEY, "90"); + p.put(TIC_LABEL_POSITION_KEY, "-0.01 0"); + p.put(TIC_LABEL_ATTRIBUTES_KEY + '/' + + BasicGraphicAttributes.HORIZONTAL_ANCHOR_KEY, "right"); + p.put(TIC_LABEL_ATTRIBUTES_KEY + '/' + + BasicGraphicAttributes.VERTICAL_ANCHOR_KEY, "center"); + return p; + } + + /** + * Creates an y axis based on the specified configuration parameters. + * All numbers (lengths, fontsizes, linethicknesses, etc.) are in + * device-independent units. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Key & Default ValueTypeMandatoryDescription
automaticTicCalculation = truebooleannoHas to be true if the tics should be calculated + * automatically.
axisAttributes = default values of + * {@link ShapeAttributes}ConfigParametersnoAttributes of the axis box.
axisLabel = yStringnoAxis label.
axisLabelAttributes = default values of + * {@link BasicGraphicAttributes} with a text anchor CENTER + * BOTTOM and the text rotated by 90 degree.ConfigParametersnoText attributes of axis label.
axisLabelPosition = -0.1 0double[]noPosition of the anchor of the axis + * label relative to the center of the y-axis line.
axisLength = 0.45doublenoLength of the y-axis.
grid = falsebooleannoIf true grid lines will be drawn through the axis + * tics.
gridAttributes = default values of + * {@link ShapeAttributes}ConfigParametersnoAttributes of the grid lines.
logScale = falsebooleannoIf true the axis will be logarithmic. Otherwise + * the axis is linear.
maximum = 1doublenoThe corresponding data value of one end of the axis.
maximumTic = result from automatic calculationdoublenoThe corresponding data value of the tic nearest the maximum end + * of the axis.
minimum = 0doublenoThe corresponding data value of one end of the axis.
minimumTic = result from automatic calculationdoublenoThe corresponding data value of the tic nearest the minimum end + * of the axis.
numberOfTics = result from automatic calculationintnoNumber of tics. The tics between the minimum and maximum tic + * are spaced equidistantly.
ticAttributes = default values of + * {@link ShapeAttributes}ConfigParametersnoAttributes of the tics.
ticLabelAttributes = default values of + * {@link BasicGraphicAttributes} with a text anchor RIGHT CENTER. + * ConfigParametersnoText attributes of tic labels.
ticLabelFormat = %1.1fStringnoDefines rendering of the tic label. By default a + * printf-like format string is given (see {@link Format}). + * Note, that an empty string means that tic labels are dropped. + *

+ * For non-numerical rendering an implementation of a + * {@link TicLabelFormat} can be specified (e.g. + * {@link TicLabelMap}). Note, that a configuration sub tree with + * a className key-value pair overwrites any string + * definition.

ticLabelPosition = -0.01 0double[]noPosition of the anchor of the tic label relative to the + * tic position on the axis.
ticLength = 0.01doublenoLength of the tics. Negative/positive values mean tics + * inside/outside the box.
+ */ + public static AxisParameters createYAxis(ConfigParameters config) { + return createAxis(config, createDefaultYAxisProperties()); + } + + private static AxisParameters createAxis(ConfigParameters config, + Properties p) { + ConfigData cd = new PropertiesBasedConfigData(p); + ConfigParameters c = new ConfigParameters(cd); + cd = new ConfigParametersBasedConfigData(config, c); + c = new ConfigParameters(cd); + + AxisParameters a = new AxisParameters(); + a.logScale = c.getBoolean(LOG_SCALE_KEY); + a.minimum = c.getDouble(MINIMUM_KEY); + a.maximum = c.getDouble(MAXIMUM_KEY); + a.axisLength = c.getDouble(AXIS_LENGTH_KEY); + a.axisAttributes + = (LineAttributes) Factory.create(c.getNode(AXIS_ATTRIBUTES_KEY)); + a.axisLabel = c.get(AXIS_LABEL_KEY); + a.axisLabelPosition + = new GraphPoint(c.getDoubleArray(AXIS_LABEL_POSITION_KEY)); + a.axisLabelAttributes = (TextAttributes) Factory.create( + c.getNode(AXIS_LABEL_ATTRIBUTES_KEY)); + a.ticLength = c.getDouble(TIC_LENGTH_KEY); + a.automaticTicCalculation + = c.getBoolean(AUTOMATIC_TIC_CALCULATION_KEY); + if (!a.automaticTicCalculation) { + a.calculateTicsParameters(); // calculate default parameters + a.minimumTic = c.getDouble(MINIMUM_TIC_KEY, a.minimumTic); + a.maximumTic = c.getDouble(MAXIMUM_TIC_KEY, a.maximumTic); + a.numberOfTics = c.getInt(NUMBER_OF_TICS_KEY, a.numberOfTics); + } + a.ticAttributes + = (LineAttributes) Factory.create(c.getNode(TIC_ATTRIBUTES_KEY)); + a.ticLabelFormat = createTicLabelFormat(c); + a.ticLabelPosition + = new GraphPoint(c.getDoubleArray(TIC_LABEL_POSITION_KEY)); + a.ticLabelAttributes = (TextAttributes) Factory.create( + c.getNode(TIC_LABEL_ATTRIBUTES_KEY)); + a.grid = c.getBoolean(GRID_KEY); + a.gridAttributes + = (LineAttributes) Factory.create(c.getNode(GRID_ATTRIBUTES_KEY)); + return a; + } + + private static TicLabelFormat createTicLabelFormat(ConfigParameters c) + { + TicLabelFormat result = Format.create(c, TIC_LABEL_FORMAT_KEY); + ConfigParameters node = c.getNode(TIC_LABEL_FORMAT_KEY); + if (node.get(Factory.CLASS_NAME_KEY, null) != null) { + result = (TicLabelFormat) Factory.create(node); + } + return result; + } +} diff --git a/src/jcckit/plot/BarFactory.java b/src/jcckit/plot/BarFactory.java new file mode 100644 index 000000000..6a452a1e6 --- /dev/null +++ b/src/jcckit/plot/BarFactory.java @@ -0,0 +1,131 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.plot; + +import jcckit.graphic.ClippingShape; +import jcckit.graphic.GraphPoint; +import jcckit.graphic.GraphicAttributes; +import jcckit.graphic.GraphicalElement; +import jcckit.graphic.Rectangle; +import jcckit.util.ConfigParameters; + +/** + * A factory of bars. The bars are {@link Rectangle Rectangles}. + * Depending on the configuration parameters the bars can be + * horizontal or vertical. Bars of several curves can be side by side or + * stacked. The bar length is determined by the x or y value of the + * curve point in device-independent coordinates. If the value is negative + * the bar goes into the negative direction. For stacked bars the values + * should always be positive. + *

+ * When used inside a {@link SimpleCurve} soft clipping should always be + * switched off (see + * {@link SimpleCurve#SimpleCurve(ConfigParameters, int, int, ClippingShape, Legend)}). + * + * @author Franz-Josef Elmer + */ +public class BarFactory extends AbstractSymbolFactory { + /** Configuration parameter key. */ + public static final String STACKED_KEY = "stacked", + HORIZONTAL_BARS_KEY = "horizontalBars"; + + private final boolean _stacked; + private final boolean _horizontalBars; + + /** + * Creates an instance from the specified configuration parameters. + * + * + * + * + * + * + * + * + * + *
Key & Default ValueTypeMandatoryDescription
horizontalBars = falsebooleannoIf true horizontal bars will be drawn. Otherwise + * vertical bars are drawn.
stacked = falsebooleannoIf true the bars of several curves will be + * stacked.
+ * In addition the configuration parameters of the + * + * constructor of the superclass {@link AbstractSymbolFactory} apply. + */ + public BarFactory(ConfigParameters config) { + super(config); + _horizontalBars = config.getBoolean(HORIZONTAL_BARS_KEY, false); + _stacked = config.getBoolean(STACKED_KEY, false); + } + + /** + * Creates a bar at the specified point. + * If hintFromPreviousCurve + * is not an instance of {@link PositionHint} the values of + * origin and position will be (0,0). + * @param hintFromPreviousCurve Hint from previous curve. Will be used + * to calculate symbol shape and hint for the next curve. + */ + protected Symbol createSymbol(GraphPoint point, GraphicAttributes attributes, + Hint hintForNextPoint, + Hint hintFromPreviousCurve) { + GraphPoint origin = new GraphPoint(null); + GraphPoint position = origin; + if (hintFromPreviousCurve instanceof PositionHint) { + origin = ((PositionHint) hintFromPreviousCurve).getOrigin(); + position = ((PositionHint) hintFromPreviousCurve).getPosition(); + } + double px = position.getX(); + double py = position.getY(); + double x = point.getX() - origin.getX(); + double y = point.getY() - origin.getY(); + if (_horizontalBars) { + y = _size; + position = new GraphPoint(px + 0.5 * x, point.getY() + py); + px += _stacked ? x : 0; + py += _stacked ? 0 : _size; + } else { + x = _size; + position = new GraphPoint(point.getX() + px, py + 0.5 * y); + px += _stacked ? 0 : _size; + py += _stacked ? y : 0; + } + Hint hintForNextCurve = new PositionHint(origin, new GraphPoint(px, py)); + return new Symbol(new Rectangle(position, Math.abs(x), Math.abs(y), + attributes), + hintForNextPoint, hintForNextCurve); + } + + /** + * Creates a symbol for the legend at the specified position. + * @param centerPosition Center position of the symbol. + * @param size The size of the symbol. + */ + public GraphicalElement createLegendSymbol(GraphPoint centerPosition, + double size) { + return new Rectangle(centerPosition, size, size, _attributes); + } + + /** + * Returns null because this method isn't needed but has to be + * implemented. + */ + protected GraphicalElement createPlainSymbol( + GraphPoint centerPosition, double size, GraphicAttributes attributes) { + return null; + } +} diff --git a/src/jcckit/plot/CartesianCoordinateSystem.java b/src/jcckit/plot/CartesianCoordinateSystem.java new file mode 100644 index 000000000..dbfaae8db --- /dev/null +++ b/src/jcckit/plot/CartesianCoordinateSystem.java @@ -0,0 +1,206 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.plot; + +import jcckit.data.DataPoint; +import jcckit.graphic.ClippingRectangle; +import jcckit.graphic.ClippingShape; +import jcckit.graphic.GraphPoint; +import jcckit.graphic.GraphicAttributes; +import jcckit.graphic.GraphicalComposite; +import jcckit.graphic.GraphicalElement; +import jcckit.graphic.LineAttributes; +import jcckit.graphic.Polygon; +import jcckit.graphic.Text; +import jcckit.transformation.CartesianTransformation; +import jcckit.transformation.Transformation; +import jcckit.util.ConfigParameters; + +/** + * A Cartesian coordinate system. One or both axes can be logarithmic. + * + * @author Franz-Josef Elmer + */ +public class CartesianCoordinateSystem implements CoordinateSystem { + /** Configuration parameter key. */ + public static final String ORIGIN_KEY = "origin", + X_AXIS_KEY = "xAxis", + Y_AXIS_KEY = "yAxis"; + + private final CartesianTransformation _transformation; + private final GraphicalComposite _view; + private final ClippingRectangle _clippingRectangle; + + /** + * Creates an instance from the specified configuration parameters. + * + * + * + * + * + * + * + * + * + * + * + * + *
Key & Default ValueTypeMandatoryDescription
origin = 0.15, 0.1double[]noPosition (in device-independent coordinates) of the lower-left + * corner of the axis box.
xAxisConfigParametersnoParameters defining the x-axis. For definitions and default + * values see {@link AxisParameters#createXAxis + * AxisParameters.createXAxis()}.
yAxisConfigParametersnoParameters defining the y-axis. For definitions and default + * values see {@link AxisParameters#createYAxis + * AxisParameters.createYAxis()}.
+ */ + public CartesianCoordinateSystem(ConfigParameters config) { + this(new GraphPoint(config.getDoubleArray(ORIGIN_KEY, + new double[] {0.15, 0.1})), + AxisParameters.createXAxis(config.getNode(X_AXIS_KEY)), + AxisParameters.createYAxis(config.getNode(Y_AXIS_KEY))); + } + + /** + * Creates an instance for the specified origin and parameters + * of both axes. + * @param origin Position (in device-independent coordinates) of the + * lower-left corner of the axis box. + * @param xAxisParameters Parameters of the x-axis. + * @param yAxisParameters Parameters of the y-axis. + */ + public CartesianCoordinateSystem(GraphPoint origin, + AxisParameters xAxisParameters, + AxisParameters yAxisParameters) { + double x = origin.getX(); + double y = origin.getY(); + _transformation = new CartesianTransformation(xAxisParameters.logScale, + yAxisParameters.logScale, + new DataPoint(xAxisParameters.minimum, yAxisParameters.minimum), + new GraphPoint(x, y), + new DataPoint(xAxisParameters.maximum, yAxisParameters.maximum), + new GraphPoint(x + xAxisParameters.axisLength, + y + yAxisParameters.axisLength)); + _clippingRectangle = new ClippingRectangle(x, y, + x + xAxisParameters.axisLength, + y + yAxisParameters.axisLength); + _view = new GraphicalComposite(null); + createView(origin, xAxisParameters, yAxisParameters); + } + + /** Creates the graphical representation of this coordinate system. */ + private void createView(GraphPoint origin, + AxisParameters xAxisParameters, + AxisParameters yAxisParameters) { + double x0 = origin.getX(); + double x1 = x0 + xAxisParameters.axisLength; + double y0 = origin.getY(); + double y1 = y0 + yAxisParameters.axisLength; + GraphPoint lowerLeftCorner = new GraphPoint(x0, y0); + GraphPoint upperLeftCorner = new GraphPoint(x0, y1); + GraphPoint lowerRightCorner = new GraphPoint(x1, y0); + GraphPoint upperRightCorner = new GraphPoint(x1, y1); + LineAttributes xLineAttributes = xAxisParameters.axisAttributes; + LineAttributes yLineAttributes = yAxisParameters.axisAttributes; + createTicsAndGrid(true, y0, y1, xAxisParameters); + createTicsAndGrid(false, x0, x1, yAxisParameters); + addLine(lowerLeftCorner, lowerRightCorner, xLineAttributes); + addLine(lowerLeftCorner, upperLeftCorner, yLineAttributes); + addLine(upperLeftCorner, upperRightCorner, xLineAttributes); + addLine(lowerRightCorner, upperRightCorner, yLineAttributes); + createLabel(0.5 * (x0 + x1), y0, xAxisParameters); + createLabel(x0, 0.5 * (y0 + y1), yAxisParameters); + } + + private void createLabel(double x, double y, AxisParameters parameters) { + if (parameters.axisLabel.length() > 0) { + _view.addElement(new Text( + new GraphPoint(x + parameters.axisLabelPosition.getX(), + y + parameters.axisLabelPosition.getY()), + parameters.axisLabel, parameters.axisLabelAttributes)); + } + } + + private void createTicsAndGrid(boolean isXAxis, double low, double high, + AxisParameters parameters) { + double[] tics = parameters.calculateTics(); + int offIndex = isXAxis ? 1 : 0; + double[] point = new double[2]; // helper array + for (int i = 0; i < tics.length; i++) { + point[1 - offIndex] = tics[i]; + point[offIndex] = 1; + GraphPoint gPoint1 = + _transformation.transformToGraph(new DataPoint(point[0], point[1])); + point[0] = gPoint1.getX(); + point[1] = gPoint1.getY(); + point[offIndex] = high; + gPoint1 = new GraphPoint(point[0], point[1]); + point[offIndex] += parameters.ticLength; + addLine(gPoint1, new GraphPoint(point[0], point[1]), + parameters.ticAttributes); + point[offIndex] = low; + GraphPoint gPoint2 = new GraphPoint(point[0], point[1]); + if (parameters.grid) { + addLine(gPoint1, gPoint2, parameters.gridAttributes); + } + point[offIndex] -= parameters.ticLength; + addLine(gPoint2, new GraphPoint(point[0], point[1]), + parameters.ticAttributes); + if (parameters.ticLabelFormat != null) { + point[offIndex] += parameters.ticLength; + point[0] += parameters.ticLabelPosition.getX(); + point[1] += parameters.ticLabelPosition.getY(); + _view.addElement(new Text(new GraphPoint(point[0], point[1]), + parameters.ticLabelFormat.form(tics[i]), + parameters.ticLabelAttributes)); + } + } + } + + private void addLine(GraphPoint point1, GraphPoint point2, + GraphicAttributes attributes) { + Polygon line = new Polygon(attributes, false); + line.addPoint(point1); + line.addPoint(point2); + _view.addElement(line); + } + + /** + * Returns the graphical representation of the coordinate system. + * In each call the same instance is returned. + */ + public GraphicalElement getView() { + return _view; + } + + /** + * Returns the clipping rectangle of specified by the axis. + * In each call the same instance is returned. + */ + public ClippingShape getClippingShape() { + return _clippingRectangle; + } + + /** + * Returns the transformation of data coordinates into the device-independent + * coordinates of the axis box. + * In each call the same instance is returned. + */ + public Transformation getTransformation() { + return _transformation; + } +} diff --git a/src/jcckit/plot/CircleSymbolFactory.java b/src/jcckit/plot/CircleSymbolFactory.java new file mode 100644 index 000000000..b9eb9a6b9 --- /dev/null +++ b/src/jcckit/plot/CircleSymbolFactory.java @@ -0,0 +1,54 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.plot; + +import jcckit.graphic.GraphPoint; +import jcckit.graphic.GraphicAttributes; +import jcckit.graphic.GraphicalElement; +import jcckit.graphic.Oval; +import jcckit.util.ConfigParameters; + +/** + * A factory of circle symbols. + * + * @author Franz-Josef Elmer + */ +public class CircleSymbolFactory extends AbstractSymbolFactory { + /** + * Creates an instance from the specified configuration parameters. + * For the configuration parameters see the + * + * constructor of the superclass {@link AbstractSymbolFactory}. + */ + public CircleSymbolFactory(ConfigParameters config) { + super(config); + } + + /** + * Creates a circle. + * @param centerPosition Position of the center of the circle. + * @param size Diameter of the circle. + * @param attributes Circle attributes. + */ + protected GraphicalElement createPlainSymbol(GraphPoint centerPosition, + double size, + GraphicAttributes attributes) { + return new Oval(centerPosition, size, size, attributes); + } +} diff --git a/src/jcckit/plot/CoordinateSystem.java b/src/jcckit/plot/CoordinateSystem.java new file mode 100644 index 000000000..11ac62838 --- /dev/null +++ b/src/jcckit/plot/CoordinateSystem.java @@ -0,0 +1,55 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.plot; + +import jcckit.graphic.ClippingShape; +import jcckit.graphic.GraphicalElement; +import jcckit.transformation.Transformation; + +/** + * Interface for all generators of coordinate systems. A + * CoordinateSystem creates a + * {@link jcckit.graphic.GraphicalComposite} which contains all the + * {@link GraphicalElement GraphicalElements} defining axes, labels, grid, etc. + * + * @author Franz-Josef Elmer + */ +public interface CoordinateSystem { + /** + * Returns the graphical representation of a coordinate + * system. Different invocations of this method may return + * different coordinate systems, e.g., due to changes in the + * transformation or clipping shapes. + */ + public GraphicalElement getView(); + + /** + * Returns the clipping chape of {@link Curve Curves} drawn on top + * of the coordinates system. Different invocations of + * this method may return different clipping shapes. + */ + public ClippingShape getClippingShape(); + + /** + * Returns the transformation between data coordinates and + * device-independent graphcial coordinates. Different invocations + * of this method may return different transformations. + */ + public Transformation getTransformation(); +} diff --git a/src/jcckit/plot/Curve.java b/src/jcckit/plot/Curve.java new file mode 100644 index 000000000..03cb3cb78 --- /dev/null +++ b/src/jcckit/plot/Curve.java @@ -0,0 +1,64 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.plot; + +import jcckit.graphic.GraphPoint; +import jcckit.graphic.GraphicalElement; + +/** + * A curve is defined by a sequence of points in device-independent + * coordinates. The points can be decorated by symbols and/or + * connected by lines.{@link Hint Hints} are used to determine additional + * properties of the symbol. This is especially important for + * charts with bars. + *

+ * In accordance with the Factory Method Pattern + * the symbols are created by a {@link SymbolFactory}. + * + * @author Franz-Josef Elmer + */ +public interface Curve { + /** + * Returns the graphical representation of a curve. + * Different invocations of this method might return + * different instances. + * This is especially true after adding, inserting, removing, or + * repplacing a point of the curve. + */ + public GraphicalElement getView(); + + /** + * Returns a symbol which can be used to create the legend for the curve. + * For example, it should return a horizontal line with the symbol + * in the middle if the curve is a line with points decorated by symbols. + */ + public GraphicalElement getLegendSymbol(); + + /** + * Appends a new point to the curve. + * @param point Position in device-independent coordinates. + * @param hintFromPreviousCurve Hint which may be used to calculate + * the corresponding {@link GraphicalElement}. + * @return hint for next curve. + */ + public Hint addPoint(GraphPoint point, Hint hintFromPreviousCurve); + + /** Removes all points from the curve. */ + public void removeAllPoints(); +} diff --git a/src/jcckit/plot/CurveFactory.java b/src/jcckit/plot/CurveFactory.java new file mode 100644 index 000000000..0e6ee53fa --- /dev/null +++ b/src/jcckit/plot/CurveFactory.java @@ -0,0 +1,42 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.plot; + +import jcckit.graphic.ClippingShape; + +/** + * Interface of a curve factory. A curve factory creates a new instance + * of a {@link Curve}. + * + * @author Franz-Josef Elmer + */ +public interface CurveFactory { + /** + * Creates a new curve instance. + * @param curveIndex The index of the curve in the {@link Plot} to which + * it should belong. + * @param numberOfCurves Number of curves. Will be needed to calculate the + * y-coordinate of the legend symbol. + * @param clippingShape Clipping shape applied to the curve. + * @param legend The legend which will show the curve symbol. + * @return an empty instance. + */ + public Curve create(int curveIndex, int numberOfCurves, + ClippingShape clippingShape, Legend legend); +} diff --git a/src/jcckit/plot/ErrorBarFactory.java b/src/jcckit/plot/ErrorBarFactory.java new file mode 100644 index 000000000..2c55462d3 --- /dev/null +++ b/src/jcckit/plot/ErrorBarFactory.java @@ -0,0 +1,136 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.plot; + +import jcckit.graphic.GraphPoint; +import jcckit.graphic.GraphicAttributes; +import jcckit.graphic.GraphicalComposite; +import jcckit.graphic.GraphicalElement; +import jcckit.graphic.Rectangle; +import jcckit.util.ConfigParameters; +import jcckit.util.Factory; + +/** + * Symbol factory for creating symbols with error bars. It wraps + * a {@link SymbolFactory} for creating the symbol. The error bars + * are {@link Rectangle Rectangles}. + *

+ * Curves with error bars are based on two + * {@link jcckit.data.DataCurve DataCurves}: + *

  1. The plain curve. + *
  2. An instance which stores the errors in x and y. + * It is assumed that the errors are positive values defining + * the error symmetrically around the curve points. + *
+ *

+ * The ErrorBarFactory needs an instance of {@link PositionHint} + * as initial {@link Hint} for the next curve. Its origin must be + * the origin of the data coordinate system in device-independent coordinates. + * The position of PositionHint must be undefined. + * + * @author Franz-Josef Elmer + */ +public class ErrorBarFactory implements SymbolFactory { + /** Configuration parameter key. */ + public static final String SYMBOL_FACTORY_KEY = "symbolFactory"; + + private final SymbolFactory _symbolFactory; + private final GraphicAttributes _attributes; + private final double _size; + + /** + * Creates an instance from the specified configuration parameters. + * + * + * + * + * + * + * + * + * + * + * + * + *
Key & Default ValueTypeMandatoryDescription
symbolFactory = nullConfigParametersnoDefinition of the wrapped {@link SymbolFactory} which generates + * the curve symbol without bars. By default an empty + * {@link GraphicalComposite} will be created.
size = 0doublenoWidth of the error bars.
attributes = nullConfigParametersnoDefinition of the {@link GraphicAttributes} of the error + * bars.
+ */ + public ErrorBarFactory(ConfigParameters config) { + _symbolFactory = (SymbolFactory) Factory.createOrGet( + config.getNode(SYMBOL_FACTORY_KEY), null); + _size = config.getDouble(SIZE_KEY, 0); + _attributes = (GraphicAttributes) Factory.createOrGet( + config.getNode(ATTRIBUTES_KEY), null); + } + + /** + * Creates the legend symbol. Calls the wrapped {@link SymbolFactory} + * or returns an empty instance of {@link GraphicalComposite} if undefined. + */ + public GraphicalElement createLegendSymbol(GraphPoint centerPosition, + double size) { + return _symbolFactory == null ? new GraphicalComposite(null) + : _symbolFactory.createLegendSymbol(centerPosition, size); + } + + /** + * Creates either the curve symbol or the error bars. Error bars are + * created when hintFromPreviousCurve is an instance of + * {@link PositionHint} and its position attribute is not null. + * Otherwise the curve symbol is created. The position attributes stores + * the curve point (in device-independent coordinates). The origin is + * always as set in the initial PositionHint. The hint for + * the next curve wrapped by the returned Symbol is always + * a PositionHint. + */ + public Symbol createSymbol(GraphPoint point, Hint hintFromPreviousPoint, + Hint hintFromPreviousCurve) { + GraphPoint origin = new GraphPoint(null); + GraphPoint position = null; + if (hintFromPreviousCurve instanceof PositionHint) { + origin = ((PositionHint) hintFromPreviousCurve).getOrigin(); + position = ((PositionHint) hintFromPreviousCurve).getPosition(); + } + if (position == null) { + if (_symbolFactory == null) { + return new Symbol(new GraphicalComposite(null), hintFromPreviousPoint, + new PositionHint(origin, point)); + } else { + return _symbolFactory.createSymbol(point, hintFromPreviousPoint, + new PositionHint(origin, point)); + } + } else { + double xError = point.getX() - origin.getX(); + double yError = point.getY() - origin.getY(); + GraphicalComposite errorBars = new GraphicalComposite(null); + if (xError > 0) { + errorBars.addElement(new Rectangle(position, 2 * xError, _size, + _attributes)); + } + if (yError > 0) { + errorBars.addElement(new Rectangle(position, _size, 2 * yError, + _attributes)); + } + return new Symbol(errorBars, hintFromPreviousPoint, + new PositionHint(origin, null)); + } + } +} diff --git a/src/jcckit/plot/Hint.java b/src/jcckit/plot/Hint.java new file mode 100644 index 000000000..ff565b8f4 --- /dev/null +++ b/src/jcckit/plot/Hint.java @@ -0,0 +1,32 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.plot; + + +/** + * Marker interface of all types of hints. Hints are used to calculate + * {@link jcckit.graphic.GraphicalElement} representing a point in a {@link + * Curve}. For example, in a chart with stacked + * bars the data determines the height of a bar but the foot of + * a bar is determined by the height of the bar below. Its value will be + * stored in a {@link PositionHint}. + * + * @author Franz-Josef Elmer + */ +public interface Hint {} diff --git a/src/jcckit/plot/Legend.java b/src/jcckit/plot/Legend.java new file mode 100644 index 000000000..3feea40c8 --- /dev/null +++ b/src/jcckit/plot/Legend.java @@ -0,0 +1,250 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.plot; + +import java.util.Properties; + +import jcckit.graphic.BasicGraphicAttributes; +import jcckit.graphic.GraphPoint; +import jcckit.graphic.GraphicAttributes; +import jcckit.graphic.GraphicalComposite; +import jcckit.graphic.GraphicalElement; +import jcckit.graphic.Polygon; +import jcckit.graphic.Rectangle; +import jcckit.graphic.ShapeAttributes; +import jcckit.graphic.Text; +import jcckit.graphic.TextAttributes; +import jcckit.util.ConfigData; +import jcckit.util.ConfigParameters; +import jcckit.util.ConfigParametersBasedConfigData; +import jcckit.util.Factory; +import jcckit.util.PropertiesBasedConfigData; + + +/** + * Helper class for creating the legend of a {@link Plot}. + * + * @author Franz-Josef Elmer + */ +public class Legend { + /** Configuration parameter key. */ + public static final String UPPER_RIGHT_CORNER_KEY = "upperRightCorner", + BOX_WIDTH_KEY = "boxWidth", + BOX_HEIGHT_KEY = "boxHeight", + BOX_ATTRIBUTES_KEY = "boxAttributes", + TITLE_KEY = "title", + TITLE_DISTANCE_KEY = "titleDistance", + TITLE_ATTRIBUTES_KEY = "titleAttributes", + LEFT_DISTANCE_KEY = "leftDistance", + BOTTOM_DISTANCE_KEY = "bottomDistance", + TOP_DISTANCE_KEY = "topDistance", + LINE_LENGTH_KEY = "lineLength", + SYMBOL_SIZE_KEY = "symbolSize", + CURVE_TITLE_DISTANCE_KEY = "curveTitleDistance", + CURVE_TITLE_ATTRIBUTES_KEY + = "curveTitleAttributes"; + + private final GraphicalComposite _box; + private final TextAttributes _curveTitleAttributes; + private final double _xSymbol; + private final double _xText; + private final double _yBase; + private final double _yLastRow; + private final double _length; + private final double _size; + + /** + * Creates an instance from the specified configuration parameters. + * All numbers (lengths, fontsizes, linethicknesses, etc.) are in + * device-independent units. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Key & Default ValueTypeMandatoryDescription
bottomDistance = 0.02doublenoDistance between the last row and the bottom of the legend box. + *
boxAttributes = default values of + * {@link ShapeAttributes} with a white fill color.ConfigParametersnoAttributes of the legend box.
boxHeight = 0.1doublenoHeight of the legend box.
boxWidth = 0.2doublenoWidth of the legend box.
curveTitleAttributes = default values of + * {@link BasicGraphicAttributes}ConfigParametersnoText attributes of curve titles printed in the legend.
curveTitleDistance = 0.005doublenoHorizontal distance between the line part of the legend symbol + * and the curve title.
leftDistance = 0.01doublenoHorizontal distance between the line part of the legend symbol + * and the left border of the legend box.
lineLength = 0.035doublenoLength of the line part of the legend symbol.
symbolSize = 0.01doublenoSize of the symbol part of the legend symbol. Will be the + * size argument of {@link SymbolFactory#createLegendSymbol + * createLegendSymbol} in a {@link SymbolFactory}.
titleAttributes = default values of + * {@link BasicGraphicAttributes} with a text anchor CENTER + * TOP.ConfigParametersnoText attributes of the title of the legend box.
title = LegendStringnoTitle of the legend box.
titleDistance = 0.005doublenoDistance between the center of the upper line of the legend box + * and the anchor of the legend title.
topDistance = 0.04doublenoDistance between the first row and the top of the legend box. + *
upperRightCorner = 0.94, 0.54double[]noPosition of the upper-right corner of the legend box.
+ */ + public Legend(ConfigParameters config) { + config = mergeWithDefaultConfig(config); + GraphPoint corner + = new GraphPoint(config.getDoubleArray(UPPER_RIGHT_CORNER_KEY, + new double[] {0.94, 0.54})); + double width = config.getDouble(BOX_WIDTH_KEY, 0.2); + double height = config.getDouble(BOX_HEIGHT_KEY, 0.1); + _curveTitleAttributes = (TextAttributes) Factory.create( + config.getNode(CURVE_TITLE_ATTRIBUTES_KEY)); + _xSymbol = corner.getX() - width + + config.getDouble(LEFT_DISTANCE_KEY, 0.01); + _yBase = corner.getY() - config.getDouble(TOP_DISTANCE_KEY, 0.04); + _yLastRow = corner.getY() - height + + config.getDouble(BOTTOM_DISTANCE_KEY, 0.02); + _length = config.getDouble(LINE_LENGTH_KEY, 0.035); + _size = config.getDouble(SYMBOL_SIZE_KEY, 0.01); + _xText = _xSymbol + _length + + config.getDouble(CURVE_TITLE_DISTANCE_KEY, 0.005); + + _box = new GraphicalComposite(null); + double xCenter = corner.getX() - width / 2; + _box.addElement(new Rectangle( + new GraphPoint(xCenter, corner.getY() - height / 2), width, height, + (GraphicAttributes) Factory.create( + config.getNode(BOX_ATTRIBUTES_KEY)))); + _box.addElement(new Text( + new GraphPoint(xCenter, corner.getY() + - config.getDouble(TITLE_DISTANCE_KEY, 0.005)), + config.get(TITLE_KEY, "Legend"), + (TextAttributes) Factory.create( + config.getNode(TITLE_ATTRIBUTES_KEY)))); + } + + private ConfigParameters mergeWithDefaultConfig(ConfigParameters config) { + Properties p = new Properties(); + p.put(BOX_ATTRIBUTES_KEY + '/' + Factory.CLASS_NAME_KEY, + ShapeAttributes.class.getName()); + p.put(BOX_ATTRIBUTES_KEY + '/' + + ShapeAttributes.FILL_COLOR_KEY, "0xffffff"); + p.put(BOX_ATTRIBUTES_KEY + '/' + + ShapeAttributes.LINE_COLOR_KEY, "0"); + p.put(TITLE_ATTRIBUTES_KEY + '/' + Factory.CLASS_NAME_KEY, + BasicGraphicAttributes.class.getName()); + p.put(TITLE_ATTRIBUTES_KEY + '/' + + BasicGraphicAttributes.HORIZONTAL_ANCHOR_KEY, "center"); + p.put(TITLE_ATTRIBUTES_KEY + '/' + + BasicGraphicAttributes.VERTICAL_ANCHOR_KEY, "top"); + p.put(CURVE_TITLE_ATTRIBUTES_KEY + '/' + Factory.CLASS_NAME_KEY, + BasicGraphicAttributes.class.getName()); + ConfigData cd = new PropertiesBasedConfigData(p); + cd = new ConfigParametersBasedConfigData(config, new ConfigParameters(cd)); + return new ConfigParameters(cd); + } + + /** + * Returns the legend box with title but without legend symbols and curve + * titles. + */ + public GraphicalElement getBox() { + return _box; + } + + /** + * Creates the symbol part of a legend symbol. + * @param curveIndex Index of the curve. Will be needed to calculate the + * y-coordinate of the symbol. + * @param numberOfCurves Number of curves. Will be needed to calculate the + * y-coordinate of the symbol. + * @param factory Factory for the symbol part of the legend symbol. + * Can be null. + * @param withLine true if the line part of the legend symbol + * should be created. + * @param lineAttributes Attributes of the line part. + */ + public GraphicalElement createSymbol(int curveIndex, int numberOfCurves, + SymbolFactory factory, + boolean withLine, + GraphicAttributes lineAttributes) { + GraphicalComposite result = new GraphicalComposite(null); + double y = calculateBaseLine(curveIndex, numberOfCurves); + if (withLine) { + Polygon line = new Polygon(lineAttributes, false); + line.addPoint(new GraphPoint(_xSymbol, y)); + line.addPoint(new GraphPoint(_xSymbol + _length, y)); + result.addElement(line); + } + if (factory != null) { + result.addElement(factory.createLegendSymbol( + new GraphPoint(_xSymbol + _length / 2, y), _size)); + } + return result; + } + + private double calculateBaseLine(int curveIndex, int numberOfCurves) { + if (numberOfCurves > 1) { + return _yBase + ((_yLastRow - _yBase) / (numberOfCurves - 1)) + * curveIndex; + } else { + return 0.5 * (_yBase + _yLastRow); + } + } + + /** + * Creates the title part of a legend symbol. + * @param curveIndex Index of the curve. Will be needed to calculate the + * y-coordinate of the title. + * @param numberOfCurves Number of curves. Will be needed to calculate the + * y-coordinate of the symbol. + * @param title Title text. + */ + public GraphicalElement createCurveTitle(int curveIndex, int numberOfCurves, + String title) { + return new Text(new GraphPoint(_xText, calculateBaseLine(curveIndex, + numberOfCurves)), + title, _curveTitleAttributes); + } +} diff --git a/src/jcckit/plot/Plot.java b/src/jcckit/plot/Plot.java new file mode 100644 index 000000000..0a2c7d274 --- /dev/null +++ b/src/jcckit/plot/Plot.java @@ -0,0 +1,377 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.plot; + +import java.util.Vector; + +import jcckit.data.DataCurve; +import jcckit.data.DataEvent; +import jcckit.data.DataListener; +import jcckit.data.DataPlot; +import jcckit.data.DataPoint; +import jcckit.graphic.ClippingShape; +import jcckit.graphic.GraphPoint; +import jcckit.graphic.GraphicalComposite; +import jcckit.graphic.GraphicalElement; +import jcckit.transformation.Transformation; +import jcckit.util.ConfigParameters; +import jcckit.util.Factory; + +/** + * A plot is determined by a {@link CoordinateSystem}, {@link Curve Curves}, + * an optional annotation layer and an optional {@link Legend}. When rendered + * these components are draw in this order. + *

+ * Registrated {@link PlotListener PlotListeners} will be informed + * when the plot changes. + *

+ * A {@link DataPlot} can be connected with a Plot instance. + * This is done with the method {@link #connect connect()} which registrates + * this Plot instance as + * a {@link DataListener} at the connected DataPlot. + * After an received {@link DataEvent DataEvents} has been handled + * the registrated PlotListeners will receive a + * {@link PlotEvent} of the type {@link PlotEventType#DATA_PLOT_CHANGED}. + * + * @author Franz-Josef Elmer + */ +public class Plot implements DataListener { + /** Configuration parameter key. */ + public static final String COORDINATE_SYSTEM_KEY = "coordinateSystem", + CURVE_FACTORY_KEY = "curveFactory", + LEGEND_VISIBLE_KEY = "legendVisible", + LEGEND_KEY = "legend", + INITIAL_HINT_FOR_NEXT_CURVE_KEY + = "initialHintForNextCurve"; + private final Vector _plotListeners = new Vector(); + private DataPlot _dataPlot; + private final CurveFactory _curveFactory; + private final Vector _curves = new Vector(); + private final Vector _nextCurveHints = new Vector(); + private final Hint _initialHintForNextCurve; + private final Legend _legend; + private final boolean _legendVisibility; + + private GraphicalElement _coordinateSystemView; + private ClippingShape _clippingShape; + private Transformation _transformation; + private GraphicalElement _annotation; + private GraphicalComposite _legendView = new GraphicalComposite(null); + + /** + * Creates an instance from the specified configuration parameters. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Key & Default ValueTypeMandatoryDescription
coordinateSystem = {@link CartesianCoordinateSystem}ConfigParametersnoDefinition of the {@link CoordinateSystem}.
curveFactory = {@link SimpleCurveFactory}ConfigParametersnoDefinition of the {@link CurveFactory}.
initialHintForNextCurve = nullConfigParametersnoDefinition of the initial {@link Hint} which is needed by some + * {@link SymbolFactory SymbolFactories} like {@link BarFactory}. + *
legend = default values of {@link Legend}ConfigParametersnoConfiguration parameters of a {@link Legend}.
legendVisible = truebooleannoIf true the {@link Legend} will be created.
+ */ + public Plot(ConfigParameters config) { + CoordinateSystem coordinateSystem = (CoordinateSystem) Factory.create( + config.getNode(COORDINATE_SYSTEM_KEY), + CartesianCoordinateSystem.class.getName()); + setCoordinateSystem(coordinateSystem); + _curveFactory = (CurveFactory) Factory.create( + config.getNode(CURVE_FACTORY_KEY), + SimpleCurveFactory.class.getName()); + _initialHintForNextCurve = (Hint) Factory.createOrGet( + config.getNode(INITIAL_HINT_FOR_NEXT_CURVE_KEY), null); + _legend = new Legend(config.getNode(LEGEND_KEY)); + _legendVisibility = config.getBoolean(LEGEND_VISIBLE_KEY, true); + } + + /** + * Sets the coordinate system. All curves will be regenerated and a + * {@link PlotEvent} of type {@link PlotEventType#COODINATE_SYSTEM_CHANGED} + * will be fired. + * + * @param coordinateSystem New coordinate system. + */ + public void setCoordinateSystem(CoordinateSystem coordinateSystem) + { + _coordinateSystemView = coordinateSystem.getView(); + _clippingShape = coordinateSystem.getClippingShape(); + _transformation = coordinateSystem.getTransformation(); + if (_dataPlot != null) + { + generateCurves(_dataPlot); + } + notifyListeners( + new PlotEvent(this, PlotEventType.COODINATE_SYSTEM_CHANGED, null)); + } + + /** + * Adds the specified {@link PlotListener}. Does nothing if + * already added. + */ + public void addPlotListener(PlotListener listener) { + if (!_plotListeners.contains(listener)) { + _plotListeners.addElement(listener); + } + } + + /** + * Removes the specfied {@link PlotListener}. Does nothing if + * already removed. + */ + public void removePlotListener(PlotListener listener) { + _plotListeners.removeElement(listener); + } + + /** + * Sends all registrated {@link PlotListener PlotListeners} + * the specified event. + */ + protected void notifyListeners(PlotEvent event) { + for (int i = 0, n = _plotListeners.size(); i < n; i++) { + ((PlotListener) _plotListeners.elementAt(i)).plotChanged(event); + } + } + + /** + * Connect the specified {@link DataPlot} with this instance. + *

+ * If this Plot instance is already connected with a + * DataPlot the connection will be released and a + * {@link PlotEvent} of the type {@link PlotEventType#DATA_PLOT_DISCONNECTED} + * will be sent to all registrated {@link PlotListener PlotListeners}. + *

+ * It registers itself at dataPlot and + * all its {@link DataCurve DataCurves}. + *

+ * Finally all curves will be generated and a PlotEvent + * of the type {@link PlotEventType#DATA_PLOT_CONNECTED} will be transmitted. + * @param dataPlot Data to be connected with this plot instance. + * Can be null in order to disconnect this instance from + * any DataPlot. + */ + public void connect(DataPlot dataPlot) { + if (_dataPlot != null) { + _dataPlot.removeDataListener(this); + notifyListeners(new PlotEvent(this, PlotEventType.DATA_PLOT_DISCONNECTED, + _dataPlot)); + } + _dataPlot = dataPlot; + if (_dataPlot != null) + { + _dataPlot.addDataListener(this); + generateCurves(_dataPlot); + notifyListeners(new PlotEvent(this, PlotEventType.DATA_PLOT_CONNECTED, + _dataPlot)); + } + } + + /** + * Transforms a point from device-independent coordinates into + * data coordinates. + * @param point Point in device-independent coordinates. + * @return transform point. + */ + public DataPoint transform(GraphPoint point) { + return _transformation.transformToData(point); + } + + /** + * Creates a graphical representation of the complete plot. + * @return GraphicalComposite containing the views of the + * coordinate system, the curves, and optionally the legend (in this order). + */ + public GraphicalComposite getCompletePlot() { + GraphicalComposite result = new GraphicalComposite(null); + result.addElement(_coordinateSystemView); + GraphicalElement[] curves = getCurves(); + for (int i = 0; i < curves.length; i++) { + result.addElement(curves[i]); + } + if (_annotation != null) { + result.addElement(_annotation); + } + if (_legendVisibility) { + result.addElement(getLegend()); + } + return result; + } + + /** Returns the view of the coordinate system. */ + public GraphicalElement getCoordinateSystem() { + return _coordinateSystemView; + } + + /** Returns the graphical representations of all curves. */ + public GraphicalElement[] getCurves() { + synchronized (_curves) { + GraphicalElement[] curves = new GraphicalElement[_curves.size()]; + for (int i = 0; i < curves.length; i++) { + curves[i] = ((Curve) _curves.elementAt(i)).getView(); + } + return curves; + } + } + + /** + * Returns the annotation layer. + * @return null if no annotation layer. + */ + public GraphicalElement getAnnotation() + { + return _annotation; + } + + /** + * Sets the annotation layer. + * @param annotation Any kind of graphics which will be drawn on the + * top of the curves but may be covered by the legend. + * Can be null. + */ + public void setAnnotation(GraphicalElement annotation) + { + _annotation = annotation; + } + + /** Returns true if the legend is visible. */ + public boolean isLegendVisible() { + return _legendVisibility; + } + + /** Returns the graphical representations of the legend. */ + public GraphicalElement getLegend() { + return _legendView; + } + + /** + * Handles the received {@link DataEvent} and notifies + * {@link PlotListener PlotListeners} by an event of the type + * {@link PlotEventType#DATA_CURVE_CHANGED} or + * {@link PlotEventType#DATA_PLOT_CHANGED}. The following table shows what + * this method does: + * + * + * + * + * + * + * + *
Source of eventAll hints for the next curve are null?ActionType of sent {@link PlotEvent}
{@link DataCurve}YesRecreate changed curve. + * DATA_CURVE_CHANGED
{@link DataCurve}NoRecreate changed curve + * and all curves with large curve index. + * DATA_PLOT_CHANGED
{@link DataPlot}-Recreate all curves + * and {@link Legend} view. + * DATA_PLOT_CHANGED
+ */ + public void dataChanged(DataEvent event) { + Integer index = new Integer(0); + PlotEventType type = PlotEventType.DATA_PLOT_CHANGED; + synchronized (_curves) { + int numberOfCurves = _curves.size(); + if (event.getContainer() instanceof DataCurve + && numberOfCurves == _dataPlot.getNumberOfElements()) { + DataCurve curve = (DataCurve) event.getContainer(); + index = new Integer(curve.getContainer().getIndexOf(curve)); + type = PlotEventType.DATA_CURVE_CHANGED; + fillCurve(index.intValue(), curve); + if (index.intValue() < numberOfCurves - 1) { + Vector curveHints + = (Vector) _nextCurveHints.elementAt(index.intValue()); + for (int i = 0, n = curveHints.size(); i < n; i++) { + if (curveHints.elementAt(i) != null) { + type = PlotEventType.DATA_PLOT_CHANGED; + for (int j = index.intValue()+1; j < numberOfCurves; j++) { + fillCurve(j, (DataCurve) _dataPlot.getElement(j)); + } + break; + } + } + } + } else { + generateCurves(_dataPlot); + } + } + notifyListeners(new PlotEvent(Plot.this, type, index)); + } + + /** + * Generates all curves based on the specified data. + * In addition the legend view is created. + */ + private void generateCurves(DataPlot dataPlot) { + synchronized (_curves) { + _legendView = new GraphicalComposite(null); + _legendView.addElement(_legend.getBox()); + _curves.setSize(0); + _nextCurveHints.setSize(0); + for (int i = 0, n = dataPlot.getNumberOfElements(); i < n; i++) { + Curve curve = _curveFactory.create(i, n, _clippingShape, _legend); + _curves.addElement(curve); + _nextCurveHints.addElement(new Vector()); + DataCurve dataCurve = (DataCurve) dataPlot.getElement(i); + _legendView.addElement(curve.getLegendSymbol()); + _legendView.addElement( + _legend.createCurveTitle(i, n, dataCurve.getTitle())); + fillCurve(i, dataCurve); + } + } + } + + private void fillCurve(int curveIndex, DataCurve dataCurve) { + Vector curveHints = (Vector) _nextCurveHints.elementAt(curveIndex); + Curve curve = (Curve) _curves.elementAt(curveIndex); + curve.removeAllPoints(); + for (int i = 0, n = dataCurve.getNumberOfElements(); i < n; i++) { + setHintForNextCurve(curveHints, i, + curve.addPoint(_transformation.transformToGraph( + (DataPoint) dataCurve.getElement(i)), + getHintForNextCurve(curveIndex - 1, i))); + } + } + + private Hint getHintForNextCurve(int curveIndex, int pointIndex) { + Hint result = _initialHintForNextCurve; + if (curveIndex >= 0) { + Vector curveHints = (Vector) _nextCurveHints.elementAt(curveIndex); + result = pointIndex < curveHints.size() ? + (Hint) curveHints.elementAt(pointIndex) + : getHintForNextCurve(curveIndex - 1, pointIndex); + } + return result; + } + + private void setHintForNextCurve(Vector curveHints, int pointIndex, + Hint hint) { + while (curveHints.size() <= pointIndex) { + curveHints.addElement(_initialHintForNextCurve); + } + curveHints.setElementAt(hint, pointIndex); + } +} diff --git a/src/jcckit/plot/PlotCanvas.java b/src/jcckit/plot/PlotCanvas.java new file mode 100644 index 000000000..9c5480d5b --- /dev/null +++ b/src/jcckit/plot/PlotCanvas.java @@ -0,0 +1,135 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.plot; + +import jcckit.data.DataPlot; +import jcckit.graphic.Anchor; +import jcckit.graphic.ClippingRectangle; +import jcckit.util.ConfigParameters; + +/** + * An abstract canvas containg a single {@link Plot}. The canvas is specified + * by a {@link ClippingRectangle}, called paper. A horizontal and + * vertical {@link Anchor} determine the position of the paper on the actual + * device. + * + * @author Franz-Josef Elmer + */ +public class PlotCanvas implements PlotListener { + /** Configuration parameter key. */ + public static final String PAPER_KEY = "paper", HORIZONTAL_ANCHOR_KEY = "horizontalAnchor", + VERTICAL_ANCHOR_KEY = "verticalAnchor", PLOT_KEY = "plot"; + private final ClippingRectangle _paper; + private final Anchor _horizontalAnchor; + private final Anchor _verticalAnchor; + private final Plot _plot; + + /** + * Creates an instance from the specified configuration parameters. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Key & Default ValueTypeMandatoryDescription
horizontalAnchor = centerStringnoHorizontal position of the paper relative to the device border. + * Possible values are left, center, and + * right.
paper = 0, 0, 1, 0.6double[]noRectangle defining the paper. The first two values determine the x- + * and y- coordinates (in device-independent units) of the lower-left + * corner. The last two values determine the upper-right corner.
plot = default values of {@link Plot}ConfigParametersnoDefinition of the {@link Plot}.
verticalAnchor = centerStringnoVertical position of the paper relative to the device border. + * Possible values are top, center, and + * bottom.
+ *

+ * Note, that this instance registers itself at the wrapped {@link Plot} + * instance. + */ + public PlotCanvas(ConfigParameters config) { + double[] paper = config.getDoubleArray(PAPER_KEY, new double[] { 0, 0, 1, 0.6 }); + _paper = new ClippingRectangle(paper[0], paper[1], paper[2], paper[3]); + _horizontalAnchor = Anchor.getHorizontalAnchor(config, HORIZONTAL_ANCHOR_KEY, Anchor.CENTER); + _verticalAnchor = Anchor.getVerticalAnchor(config, VERTICAL_ANCHOR_KEY, Anchor.CENTER); + _plot = new Plot(config.getNode(PLOT_KEY)); + _plot.addPlotListener(this); + } + + /** Returns the paper definition. */ + public ClippingRectangle getPaper() { + return _paper; + } + + /** Returns the horizontal anchor. */ + public Anchor getHorizontalAnchor() { + return _horizontalAnchor; + } + + /** Returns the vertical anchor. */ + public Anchor getVerticalAnchor() { + return _verticalAnchor; + } + + /** Returns the plot. */ + public Plot getPlot() { + return _plot; + } + + /** + * Connects the wrapped {@link Plot} instance with the specified + * {@link DataPlot}. + * + * @param dataPlot + * Data to be connected with this plot canvas. Can be + * null in order to disconnect this instance from a + * DataPlot. + */ + public void connect(DataPlot dataPlot) { + _plot.connect(dataPlot); + } + + /** + * Handles the spcified event. Here nothing is done. But subclass may + * override this method. + */ + public void plotChanged(PlotEvent event) { + } +} diff --git a/src/jcckit/plot/PlotEvent.java b/src/jcckit/plot/PlotEvent.java new file mode 100644 index 000000000..b1fecc382 --- /dev/null +++ b/src/jcckit/plot/PlotEvent.java @@ -0,0 +1,88 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.plot; + +/** + * A plot event signales some changes of a {@link Plot}. + * It has three attributes: + *

+ * + * + * @author Franz-Josef Elmer + */ +public class PlotEvent { + private final Plot _source; + private final PlotEventType _type; + private final Object _message; + + /** + * Creates a new event for the specified source, type, and message. + * @param source Plot causing this event. + * @param type Type of the event. Possible values are + * {@link PlotEventType#DATA_PLOT_CHANGED}, + * {@link PlotEventType#DATA_CURVE_CHANGED}, + * {@link PlotEventType#DATA_PLOT_CONNECTED}, and + * {@link PlotEventType#DATA_PLOT_DISCONNECTED}. + * @param message Message object. Can be null + */ + public PlotEvent(Plot source, PlotEventType type, Object message) { + _source = source; + _type = type; + _message = message; + } + + /** Returns the source of this event. */ + public Plot getSource() { + return _source; + } + + /** + * Returns the event type. + * @return either {@link PlotEventType#DATA_PLOT_CHANGED}, + * {@link PlotEventType#DATA_CURVE_CHANGED}, + * {@link PlotEventType#DATA_PLOT_CONNECTED}, or + * {@link PlotEventType#DATA_PLOT_DISCONNECTED}. + */ + public PlotEventType getType() { + return _type; + } + + /** Returns the message object. */ + public Object getMessage() { + return _message; + } +} diff --git a/src/jcckit/plot/PlotEventType.java b/src/jcckit/plot/PlotEventType.java new file mode 100644 index 000000000..24a2a3cfc --- /dev/null +++ b/src/jcckit/plot/PlotEventType.java @@ -0,0 +1,36 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.plot; + +/** + * Types of {@link PlotEvent PlotEvents}. Using the typesafe enumeration + * pattern. + * + * @author Franz-Josef Elmer + */ +public class PlotEventType { + /** Event type. */ + public static final PlotEventType DATA_PLOT_CONNECTED = new PlotEventType(), + DATA_PLOT_DISCONNECTED = new PlotEventType(), + COODINATE_SYSTEM_CHANGED = new PlotEventType(), + DATA_CURVE_CHANGED = new PlotEventType(), + DATA_PLOT_CHANGED = new PlotEventType(); + + private PlotEventType() {} +} diff --git a/src/jcckit/plot/PlotListener.java b/src/jcckit/plot/PlotListener.java new file mode 100644 index 000000000..ed37113f8 --- /dev/null +++ b/src/jcckit/plot/PlotListener.java @@ -0,0 +1,29 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.plot; + +/** + * Listener for changes of plots, diagrams, and charts. + * + * @author Franz-Josef Elmer + */ +public interface PlotListener { + /** Receives the specified plot event.*/ + public void plotChanged(PlotEvent event); +} diff --git a/src/jcckit/plot/PositionHint.java b/src/jcckit/plot/PositionHint.java new file mode 100644 index 000000000..d068e2970 --- /dev/null +++ b/src/jcckit/plot/PositionHint.java @@ -0,0 +1,75 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.plot; + +import jcckit.graphic.GraphPoint; +import jcckit.util.ConfigParameters; + +/** + * An immutable {@link Hint} capsulating two {@link GraphPoint GraphPoints}. + * + * @author Franz-Josef Elmer + */ +public class PositionHint implements Hint { + /** Configuration parameter key. */ + public static final String POSITION_KEY = "position", + ORIGIN_KEY = "origin"; + private final GraphPoint _position; + private final GraphPoint _origin; + + /** + * Creates an instance from the specified configuration parameters. + * + * + * + * + * + * + * + * + * + *
Key & Default ValueTypeMandatoryDescription
position = nulldouble[]noDefinition of position.
origin = position or (0,0) if position + * undefineddouble[]noDefinition of origin.
+ */ + public PositionHint(ConfigParameters config) { + double[] point = config.getDoubleArray(POSITION_KEY, null); + _position = point == null ? null : new GraphPoint(point); + _origin = new GraphPoint(config.getDoubleArray(ORIGIN_KEY, point)); + } + + /** + * Creates an instance based on two points. + * @param origin The origin. + * @param position The position. + */ + public PositionHint(GraphPoint origin, GraphPoint position) { + _origin = origin; + _position = position; + } + + /** Returns the position. */ + public GraphPoint getPosition() { + return _position; + } + + /** Returns the origin. */ + public GraphPoint getOrigin() { + return _origin; + } +} diff --git a/src/jcckit/plot/ShapeAttributesHint.java b/src/jcckit/plot/ShapeAttributesHint.java new file mode 100644 index 000000000..4c71835af --- /dev/null +++ b/src/jcckit/plot/ShapeAttributesHint.java @@ -0,0 +1,140 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.plot; + +import java.awt.Color; + +import jcckit.graphic.GraphicAttributes; +import jcckit.graphic.ShapeAttributes; +import jcckit.util.ConfigParameters; + +/** + * An {@link AttributesHint} which wraps {@link ShapeAttributes}. + * Each call of {@link #getNextHint()} returns a new instance of + * ShapeAttributes where fill color, line color and/or + * line thickness has been increased by a constant amount. + * + * @author Franz-Josef Elmer + */ +public class ShapeAttributesHint implements AttributesHint, Cloneable { + /** Configuration parameter key. */ + public static final String INITIAL_ATTRIBUTES_KEY = "initialAttributes", + FILL_COLOR_HSB_INCREMENT_KEY + = "fillColorHSBIncrement", + LINE_COLOR_HSB_INCREMENT_KEY + = "lineColorHSBIncrement", + LINE_THICKNESS_INCREMENT_KEY + = "lineThicknessIncrement"; + private static float[] extractHSB(Color color) { + return color == null ? null + : Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), + null); + } + + private static Color createColor(float[] colorHSB) { + return colorHSB == null ? null + : Color.getHSBColor(colorHSB[0], colorHSB[1], colorHSB[2]); + } + + private static float[] incrementColor(float[] colorHSB, + double[] increments) { + float[] result = null; + if (colorHSB != null) { + result = (float[]) colorHSB.clone(); + for (int i = 0; i < 3; i++) { + result[i] += increments[i]; + } + } + return result; + } + + private float[] _fillColorHSB; + private float[] _lineColorHSB; + private double _lineThickness; + private double[] _linePattern; + private double[] _fillColorHSBIncrement; + private double[] _lineColorHSBIncrement; + private double _lineThicknessIncrement; + + /** + * Creates an instance from the specified configuration parameters. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Key & Default ValueTypeMandatoryDescription
initialAttributes = default values of + * {@link ShapeAttributes}ConfigParametersnoInitial values of shape attributes. Note, that default + * fill and line colors are undefined (they depend on the + * Renderer). In this case color increments have no effects. + *
fillColorHSBIncrement = 0 0 0double[]noHue, saturation, and brightness increments of the fill color. + *
lineColorHSBIncrement = 0 0 0double[]noHue, saturation, and brightness increments of the line color. + *
lineThicknessIncrement = 0doublenoLine thickness increment.
+ */ + public ShapeAttributesHint(ConfigParameters config) { + ShapeAttributes attributes + = new ShapeAttributes(config.getNode(INITIAL_ATTRIBUTES_KEY)); + _fillColorHSB = extractHSB(attributes.getFillColor()); + _lineColorHSB = extractHSB(attributes.getLineColor()); + _lineThickness = attributes.getLineThickness(); + _linePattern = attributes.getLinePattern(); + + _fillColorHSBIncrement + = config.getDoubleArray(FILL_COLOR_HSB_INCREMENT_KEY, new double[3]); + _lineColorHSBIncrement + = config.getDoubleArray(LINE_COLOR_HSB_INCREMENT_KEY, new double[3]); + _lineThicknessIncrement + = config.getDouble(LINE_THICKNESS_INCREMENT_KEY, 0); + } + + /** + * Creates a new ShapeAttributesHint where all attributes has been + * incremented. + */ + public AttributesHint getNextHint() { + ShapeAttributesHint nextHint = null; + try { + nextHint = (ShapeAttributesHint) clone(); + } catch (CloneNotSupportedException e) {} + nextHint._fillColorHSB + = incrementColor(_fillColorHSB, _fillColorHSBIncrement); + nextHint._lineColorHSB + = incrementColor(_lineColorHSB, _lineColorHSBIncrement); + nextHint._lineThickness + = Math.max(0, _lineThickness + _lineThicknessIncrement); + return nextHint; + } + + /** Returns the wrapped {@link ShapeAttributes} instance. */ + public GraphicAttributes getAttributes() { + return new ShapeAttributes(createColor(_fillColorHSB), + createColor(_lineColorHSB), + _lineThickness, _linePattern); + } +} diff --git a/src/jcckit/plot/SimpleCurve.java b/src/jcckit/plot/SimpleCurve.java new file mode 100644 index 000000000..7477d2c24 --- /dev/null +++ b/src/jcckit/plot/SimpleCurve.java @@ -0,0 +1,185 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.plot; + +import java.awt.Color; + +import jcckit.graphic.ClippingShape; +import jcckit.graphic.GraphPoint; +import jcckit.graphic.GraphicalComposite; +import jcckit.graphic.GraphicalElement; +import jcckit.graphic.LineAttributes; +import jcckit.graphic.Polygon; +import jcckit.graphic.ShapeAttributes; +import jcckit.util.ConfigParameters; +import jcckit.util.Factory; + +/** + * A simple curve is the basic implementation of the {@link Curve} interface. + * + * @author Franz-Josef Elmer + */ +public class SimpleCurve implements Curve { + /** Configuration parameter key. */ + public static final String SYMBOL_FACTORY_KEY = "symbolFactory", + WITH_LINE_KEY = "withLine", + SOFT_CLIPPING_KEY = "softClipping", + LINE_ATTRIBUTES_KEY = "lineAttributes", + INITIAL_HINT_FOR_NEXT_POINT_KEY + = "initialHintForNextPoint"; + private final ClippingShape _clippingShape; + private final SymbolFactory _symbolFactory; + private final GraphicalComposite _symbols; + private final GraphicalComposite _completeCurve; + private final GraphicalElement _legendSymbol; + private final Hint _initialHintForNextPoint; + private final Polygon _curve; + private final boolean _softClipping; + private Hint _hintForNextPoint; + + /** + * Creates a new curve. The parameter config contains: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Key & Default ValueTypeMandatoryDescription
initialHintForNextPoint = nullConfigParametersnoDefinition of an initial {@link Hint} for the first curve point. + *
lineAttributes = a {@link ShapeAttributes} + * instances with default values and line colors based on + * the formula Color.getHSBColor(curveIndex/6,1,0.8)ConfigParametersnoConfiguration parameters of an instances of + * {@link jcckit.graphic.GraphicAttributes} for the + * {@link Polygon Polygons} connecting curve points.
symbolFactory = nullConfigParametersnoConfiguration parameters defining an instances of + * {@link SymbolFactory} for the {@link Symbol Symbols} + * decorating curve points.
softClipping = truebooleannoIf true no explicit clipping takes + * place but the symbol is not drawn if the corresponding curve + * point is outside the axis box.
+ * If false the symbol is + * drawn in any case but it may be clipped by the axis box. + * Soft-clipping should be set to false if the + * symbols are not located around the curve point (like for bars). + *
withLine = truebooleannoIf true curve points are connected by a + * {@link jcckit.graphic.Polygon}.
+ * @param config Configuration parameters described above. + * @param curveIndex Index of this curve in the collection of curves + * defining a {@link Plot}. + * @param numberOfCurves Number of curves in this collection. + * @param clippingShape Clipping shape. Can be null. + * @param legend Legend. Will be used to calculate the legend symbol. + * @throws IllegalArgumentException if symbolFactory == null and + * withLine == false. + * + */ + public SimpleCurve(ConfigParameters config, int curveIndex, + int numberOfCurves, ClippingShape clippingShape, + Legend legend) { + _symbolFactory = (SymbolFactory) Factory.createOrGet( + config.getNode(SYMBOL_FACTORY_KEY), null); + boolean withLine = config.getBoolean(WITH_LINE_KEY, true); + LineAttributes lineAttributes = (LineAttributes) Factory.createOrGet( + config.getNode(LINE_ATTRIBUTES_KEY), + new ShapeAttributes(null, Color.getHSBColor((curveIndex % 6) / 6f, + 1f, 0.8f), + 0, null)); + if (_symbolFactory != null || withLine) { + _clippingShape = clippingShape; + _completeCurve = new GraphicalComposite(null); + if (withLine) { + GraphicalComposite container = new GraphicalComposite(clippingShape); + _curve = new Polygon(lineAttributes, false); + container.addElement(_curve); + _completeCurve.addElement(container); + } else { + _curve = null; + } + _softClipping = config.getBoolean(SOFT_CLIPPING_KEY, true); + if (_symbolFactory != null) { + _symbols = new GraphicalComposite(_softClipping ? null + : clippingShape); + _completeCurve.addElement(_symbols); + } else { + _symbols = null; + } + } else { + throw new IllegalArgumentException( + "Either a SymbolFactory must exist or withLines == true."); + } + _hintForNextPoint = _initialHintForNextPoint + = (Hint) Factory.createOrGet( + config.getNode(INITIAL_HINT_FOR_NEXT_POINT_KEY), null); + _legendSymbol = legend.createSymbol(curveIndex, numberOfCurves, + _symbolFactory, withLine, + lineAttributes); + } + + /** + * Returns the graphical representation of a curve. + * @return always the same instance. + */ + public GraphicalElement getView() { + return _completeCurve; + } + + /** Returns the legend symbol. */ + public GraphicalElement getLegendSymbol() { + return _legendSymbol; + } + + /** Appends a new point to the curve if inside the clipping shape. */ + public Hint addPoint(GraphPoint point, Hint hintFromPreviousCurve) { + if (_curve != null) { + _curve.addPoint(point); + } + Hint hintForNextCurve = hintFromPreviousCurve; + if (_symbolFactory != null) { + Symbol symbol = _symbolFactory.createSymbol(point, _hintForNextPoint, + hintFromPreviousCurve); + if (_clippingShape == null || !_softClipping + || _clippingShape.isInside(point)) { + _symbols.addElement(symbol.getSymbol()); + } + _hintForNextPoint = symbol.getHintForNextPoint(); + hintForNextCurve = symbol.getHintForNextCurve(); + } + return hintForNextCurve; + } + + public void removeAllPoints() { + if (_curve != null) { + _curve.removeAllPoints(); + } + if (_symbols != null) { + _symbols.removeAllElements(); + } + _hintForNextPoint = _initialHintForNextPoint; + } +} diff --git a/src/jcckit/plot/SimpleCurveFactory.java b/src/jcckit/plot/SimpleCurveFactory.java new file mode 100644 index 000000000..4d7b2bc02 --- /dev/null +++ b/src/jcckit/plot/SimpleCurveFactory.java @@ -0,0 +1,80 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.plot; + +import java.util.Properties; +import java.util.StringTokenizer; + +import jcckit.graphic.ClippingShape; +import jcckit.util.ConfigParameters; +import jcckit.util.PropertiesBasedConfigData; + +/** + * Factory for {@link SimpleCurve SimpleCurves}. + * + * @author Franz-Josef Elmer + */ +public class SimpleCurveFactory implements CurveFactory { + /** Configuration parameter key. */ + public static final String DEFINITIONS_KEY = "definitions"; + + private ConfigParameters[] _configs = new ConfigParameters[] + {new ConfigParameters(new PropertiesBasedConfigData(new Properties()))}; + + /** + * Creates an instance from the specified configuration parameter. + * + * + * + * + * + * + *
Key & Default ValueTypeMandatoryDescription
definitions = one empty ConfigParameters + * instanceString[]noKeys of subtrees defining {@link ConfigParameters} + * used by the {@link SimpleCurve#SimpleCurve constructor} of + * {@link SimpleCurve}.
+ */ + public SimpleCurveFactory(ConfigParameters config) { + String value = config.get(DEFINITIONS_KEY, null); + if (value != null) { + StringTokenizer tokenizer = new StringTokenizer(value); + _configs = new ConfigParameters[tokenizer.countTokens()]; + for (int i = 0; i < _configs.length; i++) { + _configs[i] = config.getNode(tokenizer.nextToken()); + } + } + } + + /** + * Creates an instance of {@link SimpleCurve}. + * @param curveIndex Index of the curve. Will be used to select the + * {@link ConfigParameters} object and the line attributes. + * In addition it will be used to calculate the y-coordinate + * of the legend symbol. + * @param numberOfCurves Number of curves. Will be needed to calculate + * the y-coordinate of the legend symbol. + * @param clippingShape The clipping shape. + * @param legend The legend. Will be needed to create the legend symbol. + */ + public Curve create(int curveIndex, int numberOfCurves, + ClippingShape clippingShape, Legend legend) { + return new SimpleCurve(_configs[curveIndex % _configs.length], curveIndex, + numberOfCurves, clippingShape, legend); + } +} diff --git a/src/jcckit/plot/SquareSymbolFactory.java b/src/jcckit/plot/SquareSymbolFactory.java new file mode 100644 index 000000000..4dc377268 --- /dev/null +++ b/src/jcckit/plot/SquareSymbolFactory.java @@ -0,0 +1,54 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.plot; + +import jcckit.graphic.GraphPoint; +import jcckit.graphic.GraphicAttributes; +import jcckit.graphic.GraphicalElement; +import jcckit.graphic.Rectangle; +import jcckit.util.ConfigParameters; + +/** + * A factory of square symbols. + * + * @author Franz-Josef Elmer + */ +public class SquareSymbolFactory extends AbstractSymbolFactory { + /** + * Creates an instance from the specified configuration parameters. + * For the configuration parameters see the + * + * constructor of the superclass {@link AbstractSymbolFactory}. + */ + public SquareSymbolFactory(ConfigParameters config) { + super(config); + } + + /** + * Creates a {@link Rectangle}. + * @param centerPosition Position of the center of the rectangle. + * @param size Diameter of the rectangle. + * @param attributes Rectangle attributes. + */ + protected GraphicalElement createPlainSymbol(GraphPoint centerPosition, + double size, + GraphicAttributes attributes) { + return new Rectangle(centerPosition, size, size, attributes); + } +} diff --git a/src/jcckit/plot/Symbol.java b/src/jcckit/plot/Symbol.java new file mode 100644 index 000000000..69a2aa5db --- /dev/null +++ b/src/jcckit/plot/Symbol.java @@ -0,0 +1,56 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.plot; + +import jcckit.graphic.GraphicalElement; + +/** + * Immutable class holding the graphical represention of the symbol and + * two {@link Hint Hints}. + * + * @author Franz-Josef Elmer + */ +public class Symbol { + private final GraphicalElement _symbol; + private final Hint _hintForNextPoint; + private final Hint _hintForNextCurve; + + /** Creates an instance for the specified symbol and hints. */ + public Symbol(GraphicalElement symbol, Hint hintForNextPoint, + Hint hintForNextCurve) { + _symbol = symbol; + _hintForNextPoint = hintForNextPoint; + _hintForNextCurve = hintForNextCurve; + } + + /** Returns the graphical symbol. */ + public GraphicalElement getSymbol() { + return _symbol; + } + + /** Returns the hint for the next point. */ + public Hint getHintForNextPoint() { + return _hintForNextPoint; + } + + /** Returns the hint for the next curve. */ + public Hint getHintForNextCurve() { + return _hintForNextCurve; + } +} diff --git a/src/jcckit/plot/SymbolFactory.java b/src/jcckit/plot/SymbolFactory.java new file mode 100644 index 000000000..d354bc095 --- /dev/null +++ b/src/jcckit/plot/SymbolFactory.java @@ -0,0 +1,65 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.plot; + +import jcckit.graphic.GraphPoint; +import jcckit.graphic.GraphicalElement; + +/** + * Interface of a symbol factory. A symbol is a {@link GraphicalElement} + * or {@link jcckit.graphic.GraphicalComposite}. A symbol factory creates + * the same type of symbols. In general, they have all the same size. + * But they are distinguished from each other by their positions. + * In addition they may also differ in other properties which will + * be determined by {@link Hint Hints}. + * + * @author Franz-Josef Elmer + */ +public interface SymbolFactory { + /** Common configuration parameter key need by implementing classes. */ + public static final String SIZE_KEY = "size", + ATTRIBUTES_KEY = "attributes"; + + /** Default size of a symbol = 0.01. */ + public static final double DEFAULT_SIZE = 0.01; + + /** + * Creates a symbol for the specified point taking into account + * the specified hints. + * @param point The position of the symbol. In general it is a transformation + * of a corresponding {@link jcckit.data.DataPoint} into a + * {@link GraphPoint}. + * @param hintFromPreviousPoint Hint from the previous point of the same + * {@link Curve} or null. + * @param hintFromPreviousCurve Hint from the previous + * {@link Curve} or null. + */ + public Symbol createSymbol(GraphPoint point, Hint hintFromPreviousPoint, + Hint hintFromPreviousCurve); + + /** + * Creates a symbol for the legend at the specified position. + * @param centerPosition Center position of the symbol. + * @param size The size of the symbol. Will not be used if the symbol + * of the curve points have all the same size. In this case + * the symbol for the legend has the size of the curve symbols. + */ + public GraphicalElement createLegendSymbol(GraphPoint centerPosition, + double size); +} diff --git a/src/jcckit/plot/TicLabelMap.java b/src/jcckit/plot/TicLabelMap.java new file mode 100644 index 000000000..d2989e481 --- /dev/null +++ b/src/jcckit/plot/TicLabelMap.java @@ -0,0 +1,136 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.plot; + +import java.util.StringTokenizer; + +import jcckit.util.ConfigParameters; +import jcckit.util.TicLabelFormat; + +/** + * Map of number intervals onto a text label. The map is defined by a + * map description string provided by configuration data. + *

+ * The map description is a list + * of conditions separated by ';'. The conditions are tested from left to + * right until a condition is fulfilled for the tic value. If no condition + * is fullfilled a '?' will be returned. + *

+ * A condition description has one of the following forms: + *

<label>
+ *
<number>=<label>
+ *
<number1>:<number2>=<label>
+ *

+ * The first type of condition is always fulfilled. It will return + * <label>. This is a kind of else condtion + * which is put at the end of the condition list. + *

+ * The second form maps a particular number onto a label. In order to be + * equal with the sepcified number the tic value should not deviate more + * than 1 ppm (part per millions) from <number>. + *

+ * The third form maps an interval onto a label. The condition reads + *

+ * <number1> <= tic label < <number2> + *

+ * Examples: + *


+ * 1=monday;2=tuesday;3=wednesday;4=thursday;5=friday;6=saturday;7=sunday 
+ * 0.5:1.5=I; 1.5:2.5 = II; 2.5:3.5 = III; the rest 
+ * 
+ * + * @author Franz-Josef Elmer + */ +public class TicLabelMap implements TicLabelFormat { + public static final String MAP_KEY = "map"; + + private static class MapItem { + private double _min = Double.MIN_VALUE; + private double _max = Double.MAX_VALUE; + private final String label; + public MapItem(String item) { + int index = item.indexOf('='); + if (index < 0) { + label = item; + } else { + label = item.substring(index + 1).trim(); + item = item.substring(0, index).trim(); + index = item.indexOf(':'); + if (index < 0) { + _min = new Double(item).doubleValue(); + _max = _min == 0 ? Double.MIN_VALUE : _min * 1.000001d; + _min = _min * 0.999999d; + if (_min > _max) { + double z = _min; + _min = _max; + _max = z; + } + } else { + _min = new Double(item.substring(0, index)).doubleValue(); + _max = new Double(item.substring(index + 1)).doubleValue(); + } + } + } + public boolean isInside(double value) { + return value >= _min && value < _max; + } + } + + private final MapItem[] _map; + + /** + * Creates an instance from the specified configuration parameters. + * + * + * + * + * + * + *
Key & Default ValueTypeMandatoryDescription
mapStringyesMap description as explained above.
+ */ + public TicLabelMap(ConfigParameters config) { + StringTokenizer tokenizer = new StringTokenizer(config.get(MAP_KEY), ";"); + _map = new MapItem[tokenizer.countTokens()]; + for (int i = 0; i < _map.length; i++) + { + String item = tokenizer.nextToken(); + try { + _map[i] = new MapItem(item.trim()); + } catch (NumberFormatException e) { + throw new NumberFormatException("Item '" + item + "' of " + + config.getFullKey(MAP_KEY) + " has an invalid number."); + } + } + } + + /** + * Maps the specified tic value onto a text label in accordance + * with the map description. + */ + public String form(double ticValue) { + String result = "?"; + for (int i = 0; i < _map.length; i++) { + if (_map[i].isInside(ticValue)) { + result = _map[i].label; + break; + } + } + return result; + } +} diff --git a/src/jcckit/renderer/Graphics2DRenderer.java b/src/jcckit/renderer/Graphics2DRenderer.java new file mode 100644 index 000000000..9b229ebf9 --- /dev/null +++ b/src/jcckit/renderer/Graphics2DRenderer.java @@ -0,0 +1,313 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.renderer; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.Ellipse2D; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +import jcckit.graphic.BasicGraphicalElement; +import jcckit.graphic.ClippingRectangle; +import jcckit.graphic.ClippingShape; +import jcckit.graphic.FillAttributes; +import jcckit.graphic.FontStyle; +import jcckit.graphic.GraphPoint; +import jcckit.graphic.GraphicAttributes; +import jcckit.graphic.GraphicalComposite; +import jcckit.graphic.GraphicalCompositeRenderer; +import jcckit.graphic.LineAttributes; +import jcckit.graphic.Oval; +import jcckit.graphic.OvalRenderer; +import jcckit.graphic.Polygon; +import jcckit.graphic.PolygonRenderer; +import jcckit.graphic.Rectangle; +import jcckit.graphic.RectangleRenderer; +import jcckit.graphic.Text; +import jcckit.graphic.TextAttributes; +import jcckit.graphic.TextRenderer; + +/** + * Renderer who draws the {@link jcckit.graphic.GraphicalElement + * GraphicalElements} into a java.awt.Graphics2D context. + *

+ * The default color for lines and texts is determined by the current color of + * the Graphics2D context when a new instance of + * Graphics2DRenderer is created. + *

+ * The default font is SansSerif-12. + * + * @author Franz-Josef Elmer + */ +public class Graphics2DRenderer implements GraphicalCompositeRenderer, PolygonRenderer, OvalRenderer, TextRenderer, + RectangleRenderer { + private static final int FS = 1; + private static final String DEFAULT_FONT_NAME = "SansSerif"; + private static final FontStyle DEFAULT_FONT_STYLE = FontStyle.NORMAL; + private static final int DEFAULT_FONT_SIZE = 12; + + private Color _defaultColor; + private Graphics2D _graphics; + + /** + * Initializes this instance. During renderering the current transformation + * will be leaved unchanged. But the current Clip may be cleared. + * + * @param graphics + * Graphics2D context into which the + * {@link BasicGraphicalElement BaiscGraphicalElements} are + * painted. + * @return this instance. + */ + public Graphics2DRenderer init(Graphics2D graphics) { + _graphics = graphics; + _defaultColor = graphics.getColor(); // the foreground color + return this; + } + + /** + * Starts rendering of the specified composite. Does nothing except if + * composite has a {@link ClippingShape}. In this case the Clip + * of the Graphics2D context becomes the clipping rectangle + * determined by the bounding box of the ClippingShape. + */ + public void startRendering(GraphicalComposite composite) { + ClippingShape shape = composite.getClippingShape(); + if (shape != null) { + ClippingRectangle rect = shape.getBoundingBox(); + _graphics.clip(new Rectangle2D.Double(rect.getMinX(), rect.getMinY(), rect.getMaxX() - rect.getMinX(), rect + .getMaxY() + - rect.getMinY())); + } + } + + /** + * Finishes rendering of the specified composite. Does nothing except if + * composite has a {@link ClippingShape}. In this case the Clip + * of the Graphics2D context will be cleared. + */ + public void finishRendering(GraphicalComposite composite) { + _graphics.setClip(null); + } + + /** Paints the specified polygon into the Graphics2D context. */ + public void render(Polygon polygon) { + int numberOfPoints = polygon.getNumberOfPoints(); + if (numberOfPoints > 0) { + Color currentColor = _graphics.getColor(); + GeneralPath p = new GeneralPath(GeneralPath.WIND_EVEN_ODD, numberOfPoints); + p.moveTo((float) polygon.getPoint(0).getX(), (float) polygon.getPoint(0).getY()); + for (int i = 1; i < numberOfPoints; i++) { + p.lineTo((float) polygon.getPoint(i).getX(), (float) polygon.getPoint(i).getY()); + } + if (polygon.isClosed()) { + p.closePath(); + } + drawShape(p, polygon, currentColor); + } + } + + /** + * Paints the specified rectangle into the current Graphics + * context. + */ + public void render(Rectangle rectangle) { + Color currentColor = _graphics.getColor(); + GraphPoint center = rectangle.getCenter(); + double width = rectangle.getWidth(); + double height = rectangle.getHeight(); + Rectangle2D rect = new Rectangle2D.Double(center.getX() - 0.5 * width, center.getY() - 0.5 * height, width, + height); + drawShape(rect, rectangle, currentColor); + } + + /** + * Paints the specified oval into the current Graphics context. + */ + public void render(Oval oval) { + Color currentColor = _graphics.getColor(); + GraphPoint center = oval.getCenter(); + double width = oval.getWidth(); + double height = oval.getHeight(); + Ellipse2D ellipse = new Ellipse2D.Double(center.getX() - 0.5 * width, center.getY() - 0.5 * height, width, + height); + drawShape(ellipse, oval, currentColor); + } + + private void drawShape(Shape shape, BasicGraphicalElement element, Color backupColor) { + GraphicAttributes attributes = element.getGraphicAttributes(); + Color fillColor = null; + if (element.isClosed() && attributes instanceof FillAttributes) { + fillColor = ((FillAttributes) attributes).getFillColor(); + } + if (fillColor != null) { + _graphics.setColor(fillColor); + _graphics.fill(shape); + } + Color lineColor = _defaultColor; + if (attributes instanceof LineAttributes) { + LineAttributes la = (LineAttributes) attributes; + BasicStroke stroke = new BasicStroke((float) la.getLineThickness()); + double[] linePattern = la.getLinePattern(); + if (linePattern != null) { + float[] dash = new float[linePattern.length]; + for (int i = 0; i < dash.length; i++) { + dash[i] = (float) la.getLinePattern()[i]; + } + stroke = new BasicStroke(stroke.getLineWidth(), BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10f, + dash, 0f); + } + _graphics.setStroke(stroke); + if (la.getLineColor() != null || fillColor != null) { + lineColor = la.getLineColor(); + } + } + if (lineColor != null) { + _graphics.setColor(lineColor); + _graphics.draw(shape); + } + _graphics.setColor(backupColor); + } + + /** + * Paints the specified text into the current Graphics context. + *

+ * If the font size is zero the default font size will be used. + *

+ * If the orientation angle is unequal zero the text will first be painted + * into an off-screen image and rotated. Finally, it will be drawn into the + * current Graphics context. Note, that only integer multiples of + * 90 degree rotation are performed. Other orientation angles will be + * adjusted to the nearest integer multiple of 90 degree. + */ + public void render(Text text) { + final GraphicAttributes ga = text.getGraphicAttributes(); + if (ga instanceof TextAttributes) { + final TextAttributes ta = (TextAttributes) ga; + final Color currentColor = _graphics.getColor(); + Color fontColor = ta.getTextColor(); + if (fontColor == null) { + fontColor = _defaultColor; + } + _graphics.setColor(fontColor); + + final double scale = _graphics.getTransform().getScaleX(); + final String str = text.getText(); + + AffineTransform before = _graphics.getTransform(); + _graphics.setTransform(new AffineTransform()); + + double fs = ta.getFontSize(); + fs = fs == 0 ? 1 : fs * scale / DEFAULT_FONT_SIZE; + + Font font = createFont(ta, 0); + + AffineTransform fontTransform = new AffineTransform(); + fontTransform.scale(fs, fs); + fontTransform.rotate(-ta.getOrientationAngle() * Math.PI / 180); + font = font.deriveFont(fontTransform); + _graphics.setFont(font); + Rectangle2D bounds = _graphics.getFontMetrics().getStringBounds(str, _graphics); + + fontTransform.rotate(-ta.getOrientationAngle() * Math.PI / 180); + + final double yy = bounds.getHeight() + bounds.getY(); + + Point2D.Double pos = new Point2D.Double(text.getPosition().getX(), text.getPosition().getY()); + before.transform(pos, pos); + + double x = 0; + double y = 0; + if (ta.getOrientationAngle() == 0) { + x = -0.5 * ta.getHorizontalAnchor().getFactor() * bounds.getWidth(); + y = 0.5 * ta.getVerticalAnchor().getFactor() * bounds.getHeight() - yy; + x = pos.x + x; + y = pos.y + y; + } else { + x = 0.5 * ta.getVerticalAnchor().getFactor() * bounds.getHeight(); + y = 0.5 * ta.getHorizontalAnchor().getFactor() * bounds.getWidth(); + // System.err.println("yy="+y+" dx="+x+" dy="+y); + // x = 0; + // y = 0; + x = pos.x + x; + y = pos.y + y; + } + +// if (ta.getOrientationAngle() == 0) { +//// System.err.println("x0=" + x); +//// System.err.println("y0=" + y); +// } else { +// System.err.println("bounds=" + bounds + " y=" + bounds.getY() + " h=" + bounds.getHeight() + " vert=" +// + ta.getVerticalAnchor().getFactor()+" horz="+ta.getHorizontalAnchor().getFactor()); +// System.err.println("x1=" + x); +// System.err.println("y1=" + y); +// } + + + _graphics.drawString(str, (float) x, (float) y); + // _graphics.fillRect((int)x, (int)y, 5, 5); + _graphics.setTransform(before); + _graphics.setColor(currentColor); + } + } + + /** + * Creates a font instance based on the specified text attributes and font + * size. + * + * @param attributes + * Text attributes (font name and style). + * @param size + * Font size in pixel. If 0 {@link #DEFAULT_FONT_SIZE} will be + * used. + * @return new font instance. + */ + static Font createFont(TextAttributes attributes, int size) { + String fontName = attributes.getFontName(); + if (fontName == null) { + fontName = DEFAULT_FONT_NAME; + } + + FontStyle fontStyle = attributes.getFontStyle(); + if (fontStyle == null) { + fontStyle = DEFAULT_FONT_STYLE; + } + int style = Font.PLAIN; + if (fontStyle == FontStyle.BOLD) { + style = Font.BOLD; + } else if (fontStyle == FontStyle.ITALIC) { + style = Font.ITALIC; + } else if (fontStyle == FontStyle.BOLD_ITALIC) { + style = Font.BOLD + Font.ITALIC; + } + + if (size == 0) { + size = DEFAULT_FONT_SIZE; + } + + return new Font(fontName, style, size); + } + +} diff --git a/src/jcckit/renderer/Transformation.java b/src/jcckit/renderer/Transformation.java new file mode 100644 index 000000000..1277c2e7e --- /dev/null +++ b/src/jcckit/renderer/Transformation.java @@ -0,0 +1,107 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.renderer; + +import java.awt.Graphics2D; + +import jcckit.graphic.Anchor; +import jcckit.graphic.ClippingRectangle; +import jcckit.graphic.GraphPoint; + +/** + * Transformation between device-independent coordinates + * and standard Java coordinates. The aspect-ratio will + * be the same. The position in the canvas is determined by a + * {@link jcckit.graphic.Rectangle Rectangle} defining a (virtual) + * paper which is placed in the canvas according to an anchor point. + * Depending on the aspect ratio of the canvas the paper width or + * height occupies the canvas width or height. + * + * @author Franz-Josef Elmer + */ +public class Transformation { + private final double _scale, _x0, _y0; + + public String toString() { + return "_scale=" + _scale + " _x0=" + _x0 + " _y0=" + _y0; + } + + /** + * Creates an instance for the specified canvas size, paper size, + * and anchor points of the paper. + * @param width Width of the canvas. + * @param height Height of the canvas. + * @param paper Rectangle defining the paper in device-independent + * coordinates. + * @param horizontalAnchor Horizontal anchor of the paper in the canvas. + * @param verticalAnchor Vertical anchor of the paper in the canvas. + */ + public Transformation(int width, int height, ClippingRectangle paper, + Anchor horizontalAnchor, Anchor verticalAnchor) { + double pWidth = paper.getMaxX() - paper.getMinX(); + double pHeight = paper.getMaxY() - paper.getMinY(); + _scale = Math.min(width / pWidth, height / pHeight); + _x0 = 0.5 * horizontalAnchor.getFactor() * (width - _scale * pWidth) + - _scale * paper.getMinX(); + _y0 = 0.5 * verticalAnchor.getFactor() * (_scale * pHeight - height) + + height + _scale * + paper.getMinY(); + } + + /** Transforms the device-independent x coordinate into Java coordinates. */ + public int transformX(double x) { + return trim(_scale * x + _x0); + } + + /** Transforms the device-independent y coordinate into Java coordinates. */ + public int transformY(double y) { + return trim(_y0 - _scale * y); + } + + /** Transforms the device-independent width into Java width. */ + public int transformWidth(double width) { + return trim(_scale * width + 0.5); + } + + /** Transforms the device-independent height into Java height. */ + public int transformHeight(double height) { + return trim(_scale * height + 0.5); + } + + private static int trim(double number) + { + return number > Short.MAX_VALUE + ? Short.MAX_VALUE + : (number < Short.MIN_VALUE ? Short.MIN_VALUE : (int) number); + } + + /** + * Transforms a point in Java coordinates back into device-independent + * coordinates. + */ + public GraphPoint transformBack(int x, int y) { + return new GraphPoint((x - _x0) / _scale, (_y0 - y) / _scale); + } + +public void apply(Graphics2D g) { + g.translate(_x0, _y0); + g.scale(_scale, -_scale); + +} +} + diff --git a/src/jcckit/transformation/CartesianTransformation.java b/src/jcckit/transformation/CartesianTransformation.java new file mode 100644 index 000000000..d2c30da49 --- /dev/null +++ b/src/jcckit/transformation/CartesianTransformation.java @@ -0,0 +1,96 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.transformation; + +import jcckit.data.DataPoint; +import jcckit.graphic.GraphPoint; +import jcckit.util.Util; + +/** + * Two-dimensional Cartesian transformation. The two independent + * transformations for the x-axis and the y-axis can be logarithmic + * from data coordinates to device-independent coordinates in order to + * realize diagrams with logarithmic scales. + * + * @author Franz-Josef Elmer + */ +public class CartesianTransformation implements Transformation { + private final boolean _xLogScale; + private final double _xOffset; + private final double _xScale; + private final boolean _yLogScale; + private final double _yOffset; + private final double _yScale; + + /** + * Creates an instance from the specified reference points. + * Note, that the reference points must differ in x and y coordinates + * otherwise a transformation would not be possible. + * @param xLogScale true if logarithmic x axis. + * @param yLogScale true if logarithmic y axis. + * @param dataPoint1 First reference point in data coordinates. + * @param graphPoint1 First reference point in device-independent + * coordinates. + * @param dataPoint2 Second reference point in data coordinates. + * @param graphPoint2 Second reference point in device-independent + * coordinates. + * @throws IllegalArgumentException if transformation in at least + * one of both directions is not possible. + */ + public CartesianTransformation(boolean xLogScale, boolean yLogScale, + DataPoint dataPoint1, GraphPoint graphPoint1, + DataPoint dataPoint2, GraphPoint graphPoint2) { + _xLogScale = xLogScale; + double d1 = Util.log(dataPoint1.getX(), xLogScale); + double dd = Util.log(dataPoint2.getX(), xLogScale) - d1; + check(dd, "data", "x", d1); + _xScale = (graphPoint2.getX() - graphPoint1.getX()) / dd; + check(_xScale, "graphical", "x", graphPoint1.getX()); + _xOffset = graphPoint1.getX() - d1 * _xScale; + + _yLogScale = yLogScale; + d1 = Util.log(dataPoint1.getY(), yLogScale); + dd = Util.log(dataPoint2.getY(), yLogScale) - d1; + check(dd, "data", "y", d1); + _yScale = (graphPoint2.getY() - graphPoint1.getY()) / dd; + check(_yScale, "graphical", "y", graphPoint1.getY()); + _yOffset = graphPoint1.getY() - d1 * _yScale; + } + + private void check(double valueToCheck, String type, String axis, + double value) { + if (valueToCheck == 0) { + throw new IllegalArgumentException("The " + type + + " reference points in " + axis + " must be different; both are " + + value); + } + } + + public GraphPoint transformToGraph(DataPoint point) { + return new GraphPoint( + _xOffset + Util.log(point.getX(), _xLogScale) * _xScale, + _yOffset + Util.log(point.getY(), _yLogScale) * _yScale); + } + + public DataPoint transformToData(GraphPoint point) { + return new DataPoint( + Util.exp((point.getX() - _xOffset) / _xScale, _xLogScale), + Util.exp((point.getY() - _yOffset) / _yScale, _yLogScale)); + } +} diff --git a/src/jcckit/transformation/Transformation.java b/src/jcckit/transformation/Transformation.java new file mode 100644 index 000000000..46b3d16a9 --- /dev/null +++ b/src/jcckit/transformation/Transformation.java @@ -0,0 +1,44 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.transformation; + +import jcckit.data.DataPoint; +import jcckit.graphic.GraphPoint; + +/** + * Interface for transformations between data coordinates + * and device-independent coordinates. + * + * @author Franz-Josef Elmer + */ +public interface Transformation { + /** + * Transforms a {@link DataPoint} into a {@link GraphPoint}. + * @param point A point in data coordinates. + * @return point tranformed into device-independent coordinates.. + */ + public GraphPoint transformToGraph(DataPoint point); + + /** + * Transforms a {@link GraphPoint} into a {@link DataPoint}. + * @param point A point in device-independent coordinates.. + * @return point tranformed into data coordinates. + */ + public DataPoint transformToData(GraphPoint point); +} diff --git a/src/jcckit/util/AppletBasedConfigData.java b/src/jcckit/util/AppletBasedConfigData.java new file mode 100644 index 000000000..e46b7208d --- /dev/null +++ b/src/jcckit/util/AppletBasedConfigData.java @@ -0,0 +1,53 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.util; + +import java.applet.Applet; + +/** + * Implementation of {@link FlatConfigData} based on + * java.applet.Applet. + * + * @author Franz-Josef Elmer + */ +public class AppletBasedConfigData extends FlatConfigData { + private final Applet _applet; + + /** + * Creates an instance based on the specified applet. + * The path is undefined. + */ + public AppletBasedConfigData(Applet applet) { + this(applet, null); + } + + /** Creates an instance based on the specified properties and path. */ + private AppletBasedConfigData(Applet applet, String path) { + super(path); + _applet = applet; + } + + protected String getValue(String fullKey) { + return _applet.getParameter(fullKey); + } + + protected ConfigData createConfigData(String path) { + return new AppletBasedConfigData(_applet, path); + } +} diff --git a/src/jcckit/util/ConfigData.java b/src/jcckit/util/ConfigData.java new file mode 100644 index 000000000..7a48bb395 --- /dev/null +++ b/src/jcckit/util/ConfigData.java @@ -0,0 +1,55 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.util; + +/** + * Interface for hierarchically managed key-value pairs. The key is + * always a string which contains any kind of printable character except + * '/', '=', ':', and whitespace characters like ' ' and '\t'. + * The value is either a string or a ConfigData object. + *

+ * This interface will be used by {@link ConfigParameters} in accordance + * with the Strategy design pattern. + * + * @author Franz-Josef Elmer + */ +public interface ConfigData { + /** + * Returns the full key. + * @param key A (relative) key. null is not allowed. + * @return the full key including path. + */ + public String getFullKey(String key); + + /** + * Returns the value associated with this key. + * @param key The relative key. null is not allowed. + * @return the associated value. Will be null if no value exists + * for key. + */ + public String get(String key); + + /** + * Returns the ConfigData object associated with this key. + * @param key The relative key. null is not allowed. + * @return the associated value. Will never return null. + * Instead an empty ConfigData is returned. + */ + public ConfigData getNode(String key); +} diff --git a/src/jcckit/util/ConfigParameters.java b/src/jcckit/util/ConfigParameters.java new file mode 100644 index 000000000..7d28374b3 --- /dev/null +++ b/src/jcckit/util/ConfigParameters.java @@ -0,0 +1,320 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.util; + +import java.awt.Color; +import java.util.StringTokenizer; + +import net.sourceforge.plantuml.ugraphic.color.ColorMapperIdentity; +import net.sourceforge.plantuml.ugraphic.color.HColorSet; + + +/** + * Read-only class for hierarchically organized key-value pairs. + * The key is always a string. The following value types are + * supported: + *

+ *

+ * In accordance with the Strategy design pattern the retrieval of + * a key-value pair is delegated to an instance of + * {@link ConfigData}. + * + * @author Franz-Josef Elmer + */ +public class ConfigParameters { + private final ConfigData _configData; + + /** Creates an instance from the specified ConfigData object. */ + public ConfigParameters(ConfigData configData) { + _configData = configData; + } + + /** + * Returns the full key. + * @return the path concatenated with key. + * @see ConfigData#getFullKey + */ + public String getFullKey(String key) { + return _configData.getFullKey(key); + } + + /** + * Returns the string value associated with the specified key. + * @param key The (relative) key. null is not allowed. + * @return the corresponding value. Will always be not null. + * @throws IllegalArgumentException if no value exists for key. + * The exception message is the full key. + */ + public String get(String key) { + String result = _configData.get(key); + if (result == null) { + throw new IllegalArgumentException(getFullKey(key)); + } + return result; + } + + /** + * Returns the string value associated with the specified key or + * defaultValue if undefined. + * @param key The (relative) key. null is not allowed. + * @param defaultValue The default value. Can be null. + * @return the corresponding value or defaultValue. + */ + public String get(String key, String defaultValue) { + String result = _configData.get(key); + if (result == null) { + result = defaultValue; + } + return result; + } + + /** + * Returns the boolean associated with the specified key. + * @param key The (relative) key. null is not allowed. + * @return true if the value is "true" otherwise false. + * @throws IllegalArgumentException if no value exists for key. + * The exception message is the full key. + * @throws NumberFormatException if the value is neither "true" nor "false". + */ + public boolean getBoolean(String key) { + return parseBoolean(get(key), key); + } + + /** + * Returns the boolean associated with the specified key. + * @param key The (relative) key. null is not allowed. + * @param defaultValue The default value. Can be null. + * @return true if the value is "true" otherwise false. + * @throws NumberFormatException if the value is neither "true" nor "false". + */ + public boolean getBoolean(String key, boolean defaultValue) { + String value = _configData.get(key); + return value == null ? defaultValue : parseBoolean(value, key); + } + + private boolean parseBoolean(String value, String key) { + if (value.equals("true")) { + return true; + } else if (value.equals("false")) { + return false; + } else { + throw createNumberFormatException("boolean", value, key); + } + } + + private NumberFormatException createNumberFormatException(String text, + String value, + String key) { + return new NumberFormatException("Not a " + text + ": " + getFullKey(key) + + " = " + value); + } + + /** + * Returns the integer associated with the specified key. + * The value can be either + *

+ * @param key The (relative) key. null is not allowed. + * @return the integer value. + * @throws IllegalArgumentException if no value exists for key. + * The exception message is the full key. + * @throws NumberFormatException if the value is not a number. + * The exception message contains the full key and the invalid value. + */ + public int getInt(String key) { + return parseInt(get(key), key); + } + + /** + * Returns the integer associated with the specified key or + * defaultValue if no key-value pair exists for the specified key. + * The value can be either + * + * @param key The (relative) key. null is not allowed. + * @param defaultValue The default value. Can be null. + * @return the integer value. + * @throws NumberFormatException if the value exists but is not a number. + * The exception message contains the full key and the invalid value. + */ + public int getInt(String key, int defaultValue) { + String value = _configData.get(key); + return value == null ? defaultValue : parseInt(value, key); + } + + private int parseInt(String value, String key) { + try { + return Integer.decode(value).intValue(); + } catch (NumberFormatException e) { + throw createNumberFormatException("number", value, key); + } + } + + /** + * Returns the double associated with the specified key. + * @param key The (relative) key. null is not allowed. + * @return the double value. + * @throws IllegalArgumentException if no value exists for key. + * The exception message is the full key. + * @throws NumberFormatException if the value is not a valid number. + * The exception message contains the full key and the invalid value. + */ + public double getDouble(String key) { + return parseDouble(get(key), key); + } + + /** + * Returns the double associated with the specified key or + * defaultValue if no key-value pair exists for the specified key. + * @param key The (relative) key. null is not allowed. + * @param defaultValue The default value. Can be null. + * @return the double value. + * @throws NumberFormatException if the value exists but is not a valid + * number. + * The exception message contains the full key and the invalid value. + */ + public double getDouble(String key, double defaultValue) { + String value = _configData.get(key); + return value == null ? defaultValue : parseDouble(value, key); + } + + private double parseDouble(String value, String key) { + try { + return new Double(value).doubleValue(); + } catch (NumberFormatException e) { + throw createNumberFormatException("number", value, key); + } + } + + /** + * Returns the array of doubles associated with the specified key. + * The numbers are separated by whitespaces. + * @param key The (relative) key. null is not allowed. + * @return the array of double values. + * @throws IllegalArgumentException if no value exists for key. + * The exception message is the full key. + * @throws NumberFormatException if the value exists but is not a + * sequence of number. The exception message contains + * the full key and the invalid value. + */ + public double[] getDoubleArray(String key) { + return parseDoubleArray(get(key), key); + } + + /** + * Returns the array of doubles associated with the specified key + * or defaultValue if no key-value pair exists for + * the specified key. The numbers are separated by whitespaces. + * @param key The (relative) key. null is not allowed. + * @param defaultValue The default value. Can be null. + * @return the array of double values. + * @throws NumberFormatException if the value exists but is not a + * sequence of number. The exception message contains + * the full key and the invalid value. + */ + public double[] getDoubleArray(String key, double[] defaultValue) { + String value = _configData.get(key); + return value == null ? defaultValue : parseDoubleArray(value, key); + } + + private double[] parseDoubleArray(String value, String key) { + try { + StringTokenizer tokenizer = new StringTokenizer(value); + double[] result = new double[tokenizer.countTokens()]; + for (int i = 0; i < result.length; i++) { + result[i] = new Double(tokenizer.nextToken()).doubleValue(); + } + return result; + } catch (NumberFormatException e) { + throw createNumberFormatException("sequence of numbers", value, key); + } + } + + /** + * Returns the color associated with the specified key. + * The color is coded as + * + * @param key The (relative) key. null is not allowed. + * @return the color. + * @throws NumberFormatException if the value exists but is not a number. + * The exception message contains the full key and the invalid value. + */ + public Color getColor(String key) { + return parseColor(get(key), key); + } + + /** + * Returns the color associated with the specified key or the specified + * default value if no key-value pair exists for the specified key. + * The color is coded as + * + * @param key The (relative) key. null is not allowed. + * @param defaultValue The default value. Can be null. + * @return the color or null if the value is an empty string. + * @throws NumberFormatException if the value exists but is not a number. + * The exception message contains the full key and the invalid value. + */ + public Color getColor(String key, Color defaultValue) { + String value = _configData.get(key); + return value == null ? defaultValue : parseColor(value, key); + } + + private Color parseColor(String value, String key) { + try { + return value.length() == 0 ? null : decodeInternal(value); + } catch (NumberFormatException e) { + throw createNumberFormatException("number", value, key); + } + } + +static private HColorSet colors = HColorSet.instance(); +private Color decodeInternal(String value) { + if (colors.getColorIfValid(value)!=null) { + return new ColorMapperIdentity().getMappedColor(colors.getColorIfValid(value)); + } + return Color.decode(value); +} + + /** + * Returns the child node associated with the specified key. + * This method returns in any case a non-null result. + * @param key The (relative) key. null is not allowed. + * @return the corresponding child node which may be empty. + */ + public ConfigParameters getNode(String key) { + return new ConfigParameters(_configData.getNode(key)); + } +} diff --git a/src/jcckit/util/ConfigParametersBasedConfigData.java b/src/jcckit/util/ConfigParametersBasedConfigData.java new file mode 100644 index 000000000..77138a7ea --- /dev/null +++ b/src/jcckit/util/ConfigParametersBasedConfigData.java @@ -0,0 +1,74 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.util; + +/** + * An implementation of {@link ConfigData} based on two instances of + * {@link ConfigParameters}. The second one serves as a set of + * default parameters. It will be used if the first one has not the requested + * key-value pair. + * + * @author Franz-Josef Elmer + */ +public class ConfigParametersBasedConfigData implements ConfigData { + private ConfigParameters _config; + private ConfigParameters _defaultConfig; + + /** + * Creates an instance. + * @param config A set of key-value pairs. + * @param defaultConfig The default set of key-value pairs. + */ + public ConfigParametersBasedConfigData(ConfigParameters config, + ConfigParameters defaultConfig) { + _config = config; + _defaultConfig = defaultConfig; + } + + /** + * Returns the full key. + * @param key A (relative) key. null is not allowed. + * @return the full key including path. + */ + public String getFullKey(String key) { + return _config.getFullKey(key); + } + + /** + * Returns the value associated with this key. + * @param key The relative key. null is not allowed. + * @return the associated value. Will be null if no value exists + * for key. + */ + public String get(String key) { + String value = _config.get(key, null); + return value == null ? _defaultConfig.get(key, null) : value; + } + + /** + * Returns the ConfigData object associated with this key. + * @param key The relative key. null is not allowed. + * @return the associated value. Will never return null. + * Instead an empty ConfigData is returned. + */ + public ConfigData getNode(String key) { + return new ConfigParametersBasedConfigData(_config.getNode(key), + _defaultConfig.getNode(key)); + } +} diff --git a/src/jcckit/util/Factory.java b/src/jcckit/util/Factory.java new file mode 100644 index 000000000..240f8befe --- /dev/null +++ b/src/jcckit/util/Factory.java @@ -0,0 +1,120 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.util; + +import java.lang.reflect.Constructor; + +/** + * General purpose factory method based on {@link ConfigParameters} + * and Java's Reflection API. + * + * @author Franz-Josef Elmer + */ +public class Factory { + /** The constant defining the key className. */ + public static final String CLASS_NAME_KEY = "className"; + + /** No public constructor necessary. */ + private Factory() {} + + /** + * Creates an instance of the specified class. + * @param className Fully-qualified name of a class with a default + * constructor. + * @return a new instance. + * @throws IllegalArgumentException if the instance could be created. + */ + public static Object create(String className) { + try { + return Class.forName(className).newInstance(); + } catch (Throwable t) { + throw new IllegalArgumentException("Could not create an instance of " + + className + " because of " + t); + } + } + + /** + * Creates an object based on the specified configuration + * parameters. The class of the object is determined by the + * parameter with the key {@link #CLASS_NAME_KEY}. + * The constructor with a single argument of the type + * ConfigParameter is invoked with the argument + * configParameters. If such a constructor + * does not exists the default constructor is invoked. If + * neither of these constructors exist a {@link FactoryException} + * is thrown. + * @param configParameters Configuration parameters. + * @return the newly created object. + * @throws IllegalArgumentException if key className is missing. + * @throws FactoryException wrapping any kind of exception or error occured. + */ + public static Object create(ConfigParameters configParameters) { + String className = configParameters.get(CLASS_NAME_KEY); + return createObject(configParameters, className); + } + + /** + * Creates an object based on the specified configuration + * parameters and default class name. If the + * parameter with the key {@link #CLASS_NAME_KEY} is missed in + * configParameters defaultClassName is used. + * Otherwise it works as {@link #create(jcckit.util.ConfigParameters)}. + * @param configParameters Configuration parameters. + * @param defaultClassName Default class name. + * @return the newly created object. + * @throws FactoryException wrapping any kind of exception or error occured. + */ + public static Object create(ConfigParameters configParameters, + String defaultClassName) { + String className = configParameters.get(CLASS_NAME_KEY, defaultClassName); + return createObject(configParameters, className); + } + + /** + * Creates an object based on the specified configuration + * parameters or returns the default object. This method behaves + * as {@link #create(jcckit.util.ConfigParameters)}, except that is does + * not throw an IllegalArgumentException if key className + * is missing. Instead defaultObject is returned. + */ + public static Object createOrGet(ConfigParameters configParameters, + Object defaultObject) { + String className = configParameters.get(CLASS_NAME_KEY, null); + return className == null ? defaultObject + : createObject(configParameters, className); + } + + private static Object createObject(ConfigParameters configParameters, + String className) { + try { + Class c = Class.forName(className); + Object result = null; + Constructor constructor = null; + try { + constructor = c.getConstructor(new Class[] {ConfigParameters.class}); + result = constructor.newInstance(new Object[] {configParameters}); + } catch (NoSuchMethodException e) { + result = c.newInstance(); + } + return result; + } catch (Throwable t) { + throw new FactoryException(configParameters, CLASS_NAME_KEY, t); + } + } +} diff --git a/src/jcckit/util/FactoryException.java b/src/jcckit/util/FactoryException.java new file mode 100644 index 000000000..28d0f39e9 --- /dev/null +++ b/src/jcckit/util/FactoryException.java @@ -0,0 +1,79 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.util; + +import java.lang.reflect.InvocationTargetException; + +/** + * Exception thrown in the case of an error during creation of a new + * object by {@link Factory#create}. + * + * @author Franz-Josef Elmer + */ +public class FactoryException extends RuntimeException { + private final String _fullKey; + private final String _className; + private final Object _reason; + + /** + * Creates a new instance based on the specified configuration parameters + * and reason object. + *

+ * If reason is an instance of InvocationTargetException + * it will be replaced by the wrapped Throwable. + * @param configParameters Configuration parameters from which the + * className will be extracted (if it exists, otherwise + * null will be taken). + * @param reason The reason causing this exception. Most often an + * an exception. + */ + public FactoryException(ConfigParameters configParameters, String key, + Object reason) { + _fullKey = configParameters.getFullKey(key); + _className = configParameters.get(key, null); + if (reason instanceof InvocationTargetException) { + reason = ((InvocationTargetException) reason).getTargetException(); + } + _reason = reason; + } + + /** Returns the full class name key. */ + public String getFullKey() { + return _fullKey; + } + + /** Returns the fully qualified class name. */ + public String getClassName() { + return _className; + } + + /** Returns the reason object causing this exception. */ + public Object getReason() { + return _reason; + } + + /** + * Renders this instance as follows: jcckit.util.FactoryException: + * full key = class name: reason. + */ + public String toString() { + return getClass().getName() + ": " + _fullKey + " = " + _className + + ": " + _reason; + } +} diff --git a/src/jcckit/util/FlatConfigData.java b/src/jcckit/util/FlatConfigData.java new file mode 100644 index 000000000..1384e5ca1 --- /dev/null +++ b/src/jcckit/util/FlatConfigData.java @@ -0,0 +1,187 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.util; + +/** + * An implementation of ConfigData based on a flat + * representation of the hierachically organized key-value pairs. + * Concrete subclasses must implement the methods + * {@link #getValue} and {@link #createConfigData} in accordance + * with the Template Method pattern and Factory Method pattern, + * respectively. + *

+ * In a flat representation of hierachically organized key-value + * pairs all key-value pairs are stored in a single Hashtable. + * Its key is the full key of the configuration data (i.e. the key + * including its path). + *

+ * Example (using the notation for a .properties file): + *

+ *  title = example
+ *  symbolAttributes/className = jcckit.graphic.BasicDrawingAttributes
+ *  symbolAttributes/fillColor = 0xcaffee
+ *  symbolAttributes/lineColor = 0xff0000
+ *  
+ * The following table shows the result of some method calls at a + * FlatConfigData instance prepared with + * this example: + *

+ *

+ * + * + * + * + *
Method callResult
get("title")example
getNode("symbolAttributes").get("fillColor") + * 0xcaffee
+ *
+ *

+ * In addition FlatConfigData implements inheritance + * of key-value pairs. + * Basically a node in the tree of key-value pairs + * may extend another node in the tree. + * The extended node inherit all key-value pairs from the extending + * one including the key-value pairs of all descendants. + * The value of a inherited key-value pair may be overridden. + * Also new key-value pairs may be placed in the inherited node or + * anywhere in the subtree. + * Note, that the extending node has to be a node which is not a + * descendant of the extended node (otherwise a circulary chain + * of references occurs). As a consequence not more than 20 inheritance + * levels are allowed. + *

+ * The implementation of this kind of inheritance in a flat hashtable + * is done by an additional key-value pair of the form + *

+ *    extending-node/ = extended-node/
+ *  
+ * Example: + *
+ *  A/a/priority = high
+ *  A/a/alpha/hello = universe
+ *  A/a/alpha/answer = 42
+ *  A/b/1/ = A/a/
+ *  A/b/1/alpha/hello = world
+ *  A/b/1/alpha/question = 6 * 7
+ *  
+ * The following table shows the result of various method calls + * applied at the node A/b/1/ of a FlatConfigData + * instance prepared with this example: + *

+ *

+ * + * + * + * + * + * + *
Method callResultComment
get("priority")highinherited
getNode("alpha").get("hello") + * worldoverridden
getNode("alpha").get("question") + * 6 * 7added
getNode("alpha").get("answer") + * 42inherited
+ *
+ * + * @author Franz-Josef Elmer + */ +public abstract class FlatConfigData implements ConfigData { + private final String _path; + + /** Creates a new instance for the specified path. */ + public FlatConfigData(String path) { + _path = path; + } + + /** + * Returns the full key. + * @param key A (relative) key. null is not allowed. + * @return the path concatenated with key or key + * if the path is undefined. + */ + public String getFullKey(String key) { + return _path == null ? key : _path + key; + } + + /** + * Returns the value associated with this key. + * @param key The relative key. null is not allowed. + * @return the associated value. Will be null if no value exists + * for key. + */ + public String get(String key) { + return get(_path, key, 0); + } + + /** + * Obtains a value in accordance with hierarchy (path) and + * inheritance (recursive calls of this routine). + */ + private String get(String path, String key, int numberOfLevels) { + String result = null; + if (numberOfLevels < 20) { + String fullKey = path == null ? key : path + key; + result = getValue(fullKey); + if (result == null) { + // posAfterDelim is the index in path just after '/' + int posAfterDelim = path == null ? -1 : path.length(); + String replacement; + while (posAfterDelim > 0) { + // look for a sub-tree + replacement = getValue(path.substring(0, posAfterDelim)); + if (replacement != null) { + // sub-tree found, add last part of the original path + result = get(replacement + path.substring(posAfterDelim), key, + numberOfLevels + 1); + // break whether result is null or not. + break; + } + // remove last element from the path + posAfterDelim = path.lastIndexOf('/', posAfterDelim - 2) + 1; + } + } + } + return result; + } + + /** + * Returns the ConfigData object associated with this key. + * @param key The relative key. + * @return the associated value. Will never return null. + * Instead an empty ConfigData is returned. + */ + public ConfigData getNode(String key) { + String path = (_path == null ? key : _path + key) + '/'; + return createConfigData(path); + } + + /** + * Returns the value for the specified full key from the flat + * representation of the hierarchically organized key-value pairs. + * @param fullKey The full key including path. null is not allowed. + * @return the value or null if not found. + */ + protected abstract String getValue(String fullKey); + + /** + * Returns the FlatConfigData object for the specified full path. + * In general path will be used in the constructor with + * path argument. + * @param path The full path. + * @return a new instance in any case. + */ + protected abstract ConfigData createConfigData(String path); +} diff --git a/src/jcckit/util/Format.java b/src/jcckit/util/Format.java new file mode 100644 index 000000000..c4e04e2b4 --- /dev/null +++ b/src/jcckit/util/Format.java @@ -0,0 +1,208 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.util; + +import java.util.Vector; + +/** + * A helper class for formatting numbers according to + * a printf-like format string. Each instance of + * this class is initialized by a format string for a + * single number. + * + * @author Franz-Josef Elmer + */ +public class Format implements TicLabelFormat { + /** + * Creates a new instance based of specified key-value pair of the + * specified configuration parameters. + * @param config Config parameters. + * @param key The key of the key-value pair in config containing + * the format string. + * @return null if undefined key-value pair or format string + * is an empty string. + * @throws FactoryException if the format string is invalid. + */ + public static Format create(ConfigParameters config, String key) { + Format result = null; + String format = config.get(key, null); + if (format != null && format.length() > 0) { + try { + result = new Format(format); + } catch (Exception e) { + throw new FactoryException(config, key, e); + } + } + return result; + } + + private final FormatElement[] _formatElements; + private final Vector _staticParts; + + /** + * Creates an instance for the specified format string. + * The format string is an alternation of some static texts and + * format elements. + * A format element has to start with '%' and it must end with + * one of the following format descriptors: + * + * + * + * + * + * + * + * + * + * + * + * + * + *
ddecimal integer
ooctal integer
xhex integer
ffloating point number with a fixed decimal point
e, Efloating point number in logarithmic format
g, Gfloating point number rendered either in fixed-decimal + * format of logarithmic format depending on the size of + * the mantissa.
+ * The characters between '%' and the decriptor are optional. + * They can be grouped into + * + * A plain '%' is coded as '%%'. + * @param formatString The format string. + * @exception IllegalArgumentException if invalid format string. + */ + public Format(String formatString) { + _staticParts = new Vector(); + Vector formatElements = new Vector(); + StringBuffer part = new StringBuffer(); + boolean insideFormatElement = false; + boolean atPercentSymbol = false; + for (int i = 0, n = formatString.length(); i < n; i++) { + char c = formatString.charAt(i); + if (insideFormatElement) { + part.append(c); + if (FormatElement.DESCRIPTORS.indexOf(c) >= 0) { + formatElements.addElement(new String(part)); + part.setLength(0); + insideFormatElement = false; + } + } else if (atPercentSymbol) { + atPercentSymbol = false; + if (c != '%') { + _staticParts.addElement(new String(part)); + part.setLength(0); + insideFormatElement = true; + } + part.append(c); + if (FormatElement.DESCRIPTORS.indexOf(c) >= 0) { + formatElements.addElement(new String(part)); + part.setLength(0); + insideFormatElement = false; + } + } else { + if (c == '%') { + atPercentSymbol = true; + } else { + part.append(c); + } + } + } + if (insideFormatElement) { + formatElements.addElement(new String(part)); + } else { + _staticParts.addElement(new String(part)); + } + + _formatElements = new FormatElement[formatElements.size()]; + for (int i = 0; i < _formatElements.length; i++) { + _formatElements[i] + = new FormatElement((String) formatElements.elementAt(i)); + } + } + + /** + * Format a number. + * If there are no format elements the numbers will be ignored. + * If there are more than one format elements the + * additional format elements will be ignored and only the static parts + * are taken. + * @param number Number to be formated. + * @return Formated number. + */ + public String form(long number) { + StringBuffer result = new StringBuffer(); + result.append(_staticParts.elementAt(0)); + if (_formatElements.length > 0) { + _formatElements[0].form(result, number); + } + return appendRest(result); + } + + /** + * Format a number. + * If there are no format elements the numbers will be ignored. + * If there are more than one format elements the + * additional format elements will be ignored and only the static parts + * are taken. + * @param number Number to be formated. + * @return Formated number. + */ + public String form(double number) { + StringBuffer result = new StringBuffer(); + result.append(_staticParts.elementAt(0)); + if (_formatElements.length > 0) { + _formatElements[0].form(result, number); + } + return appendRest(result); + } + + private String appendRest(StringBuffer buffer) { + for (int i = 1, n = _staticParts.size(); i < n; i++) { + buffer.append(_staticParts.elementAt(i)); + } + return new String(buffer); + } + + /** + * Format an array of double numbers. + * If there are less format elements than numbers the additional numbers + * will be ignored. If there are less numbers than format elements the + * additional format elements will be ignored and only the static parts + * are taken. + * @param numbers Numbers to be formated. + * @return Formated numbers. + */ + public String form(double[] numbers) { + StringBuffer result = new StringBuffer(); + for (int i = 0, n = _staticParts.size(); i < n; i++) { + result.append(_staticParts.elementAt(i)); + if (i < _formatElements.length && i < numbers.length) { + _formatElements[i].form(result, numbers[i]); + } + } + return new String(result); + } +} diff --git a/src/jcckit/util/FormatElement.java b/src/jcckit/util/FormatElement.java new file mode 100644 index 000000000..84f629a99 --- /dev/null +++ b/src/jcckit/util/FormatElement.java @@ -0,0 +1,242 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.util; + +/** + * + * + * @author Franz-Josef Elmer + */ +class FormatElement { + /** All descriptor characters. */ + static final String DESCRIPTORS = "doxfeEgG"; + private static final String INT_DESCRIPTORS = "dox"; + private static final int INT_DESCRIPTOR = 0; + private static final int FLOAT_DESCRIPTOR = 1; + /** + * Calculate the integer power of a floating point number. + * @param n Exponent. + * @param x Number. + * @return x^n. + */ + private static final double power(double x, int n) { + return n < 0 ? 1.0 / power2(x, -n) : power2(x, n); + } + + /** Calculate x^n recursively assuming n > 0. */ + private static final double power2(double x, int n) { + switch (n) { + case 0: return 1; + case 1: return x; + default: + double p = power2(x, n / 2); + return p * p * power2(x, n % 2); + } + } + + private final char _descriptor; + private final int _descriptorType; + private final double _tenToPrecision; + private boolean _decimalPoint; + private boolean _flushLeft; + private boolean _leadingZeros; + private boolean _alwaysSign; + private int _width; + private int _precision; + + /** Creates an instance for the specified format string. */ + FormatElement(String formatString) { + int len = formatString.length() - 1; + _descriptor = formatString.charAt(len); + if (DESCRIPTORS.indexOf(_descriptor) < 0) { + throw new IllegalArgumentException("Format element '" + formatString + + "' does not ends with one of the following characters: " + + DESCRIPTORS); + } + _descriptorType = INT_DESCRIPTORS.indexOf(_descriptor) >= 0 + ? INT_DESCRIPTOR : FLOAT_DESCRIPTOR; + if (formatString.length() > 1) { + switch (formatString.charAt(0)) { + case '-': + _flushLeft = true; + formatString = formatString.substring(1); + break; + case '0': + _leadingZeros = true; + formatString = formatString.substring(1); + break; + case '+': + _alwaysSign = true; + formatString = formatString.substring(1); + break; + } + len = formatString.length() - 1; + int index = formatString.indexOf('.'); + _decimalPoint = index >= 0; + int last = _decimalPoint ? index : len; + if (last > 0) { + _width = Integer.parseInt(formatString.substring(0, last)); + } + if (_decimalPoint) { + index++; + if (index < len) { + _precision = Integer.parseInt(formatString.substring(index, len)); + } + } + } + _tenToPrecision = power(10, _precision); + } + + /** + * Format a number in accordance of the format string + * given at the initialisation of this instance. + * @param buffer Buffer to which the formated output will be appended. + * @param number Number to be formated. + */ + public void form(StringBuffer buffer, long number) { + if (_descriptorType == FLOAT_DESCRIPTOR) { + form(buffer, (double) number); + } else { + // Format absolut value in the right base + buffer.append(form(number < 0, + Long.toString(Math.abs(number), + _descriptor == 'o' ? 8 + : (_descriptor == 'x' ? 16 : 10)), + "")); + } + } + + /** + * Format a number in accordance of the format string + * given at the initialisation of this instance. + * @param buffer Buffer to which the formated output will be appended. + * @param number Number to be formated. + */ + public void form(StringBuffer buffer, double number) { + if (_descriptorType == INT_DESCRIPTOR) { + form(buffer, (long) Math.floor(number + 0.5)); + } else if (_descriptor == 'f') { + buffer.append(formF(number)); + } else if (_descriptor == 'e' || _descriptor == 'E') { + buffer.append(formE(number)); + } else if (_descriptor == 'g' || _descriptor == 'G') { + String formF = formF(number); + String formE = formE(number); + buffer.append(formF.length() > formE.length() ? formE : formF); + } + } + + private String form(boolean negativeValue, String intPart, String fracPart) { + int len = intPart.length() + fracPart.length(); + + // Buffer holding the result + StringBuffer result = new StringBuffer(); + int count = 0; + + // add sign if necessary + if (_alwaysSign || negativeValue) { + result.append(negativeValue ? '-' : '+'); + count++; + } + + // add zeros if necessary + if (_leadingZeros) { + for (int i = count + len; i < _width; i++) { + result.append('0'); + count++; + } + } + + // add number + result.append(intPart).append(fracPart); + count += len; + + // add spaces if necessary + if (_flushLeft) { + for (; count < _width; count++) { + result.append(' '); + } + } else { + for (; count < _width; count++) { + result.insert(0, ' '); + } + } + + return new String(result); + } + + /** Format floating point number with exponent. */ + private String formE(double number) { + // format absolute mantisse + int exponent = 0; + String zeros = "00000000000000000000000".substring(0, _precision + 1); + if (number != 0) { + exponent = (int) Math.floor(Math.log(Math.abs(number)) / Math.log(10)); + double mantisse = Math.floor(Math.abs(number * power(10.0, + _precision - exponent)) + 0.5); + if (mantisse >= 10 * _tenToPrecision) { + exponent++; + mantisse = Math.floor(Math.abs(number * power(10.0, + _precision - exponent)) + 0.5); + } + zeros = Long.toString((long) mantisse); + } + + // make fractional part + StringBuffer fracPart = new StringBuffer(); + if (_decimalPoint) { + fracPart.append('.').append(zeros.substring(1)); + } + + // make exponent + fracPart.append(Character.isLowerCase(_descriptor) ? 'e': 'E') + .append(exponent < 0 ? '-' : '+'); + exponent = Math.abs(exponent); + for (int i = 0, n = fracPart.length(); i < 3; i++) { + fracPart.insert(n, Character.forDigit(exponent % 10, 10)); + exponent /= 10; + } + + return form(number < 0, zeros.substring(0, 1), new String(fracPart)); + } + + /** Format floating point number. */ + private String formF(double number) { + // Format absolut value + double multiplier = number < 0 ? - _tenToPrecision : _tenToPrecision; + String digits + = Long.toString((long) Math.floor(number * multiplier + 0.5)); + String intPart = digits; + StringBuffer fracPart = new StringBuffer(); + if (_decimalPoint) { + int len = digits.length() - _precision; + fracPart.append('.').append(digits.substring(Math.max(0, len))); + if (len > 0) { + intPart = digits.substring(0, len); + } else { + intPart = "0"; + for (; len < 0; len++) { + fracPart.insert(1, '0'); + } + } + } + + return form(number < 0, intPart, new String(fracPart)); + } +} diff --git a/src/jcckit/util/Point.java b/src/jcckit/util/Point.java new file mode 100644 index 000000000..623c78fc1 --- /dev/null +++ b/src/jcckit/util/Point.java @@ -0,0 +1,65 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.util; + +/** + * Immutable class of a two-dimensional point with floating point + * coordinates. + * + * @author Franz-Josef Elmer + */ +public class Point { + private final double _x; + private final double _y; + + /** + * Creates an instance for the specified vector. The value of the + * first/second element of vector denotes the x/y value. + * If vector is null or not long enough 0 will be used + * as default values. + */ + public Point(double[] vector) { + double x = 0; + double y = 0; + if (vector != null && vector.length > 0) { + x = vector[0]; + if (vector.length > 1) { + y = vector[1]; + } + } + _x = x; + _y = y; + } + + /** Creates an instance for the specified coordinates. */ + public Point(double x, double y) { + _x = x; + _y = y; + } + + /** Returns the x-coordinate of the point. */ + public double getX() { + return _x; + } + + /** Returns the y-coordinate of the point. */ + public double getY() { + return _y; + } +} diff --git a/src/jcckit/util/PropertiesBasedConfigData.java b/src/jcckit/util/PropertiesBasedConfigData.java new file mode 100644 index 000000000..1d5bcc436 --- /dev/null +++ b/src/jcckit/util/PropertiesBasedConfigData.java @@ -0,0 +1,81 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.util; + +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Properties; + +/** + * Implementation of {@link FlatConfigData} based on + * java.util.Properties. + * + * @author Franz-Josef Elmer + */ +public class PropertiesBasedConfigData extends FlatConfigData { + private final Properties _properties; + + /** + * Creates an instance from the specified .properties file. + * @param fileName File name of the .properties file relative + * to the working directory or absolute. + * @throws IOException if the .properties does not exist or could + * not be read. + */ + public PropertiesBasedConfigData(String fileName) throws IOException { + super(null); + _properties = new Properties(); + _properties.load(new FileInputStream(fileName)); + } + + /** + * Creates an instance based on the specified properties. + * The path is undefined. + */ + public PropertiesBasedConfigData(Properties properties) { + this(properties, null); + } + + /** Creates an instance based on the specified properties and path. */ + private PropertiesBasedConfigData(Properties properties, String path) { + super(path); + _properties = properties; + } + + /** + * Returns the value for the specified full key. The call will be delegated + * to the wrapped java.util.properties object. + * @param fullKey The full key including path. null is not allowed. + * @return the value or null if not found. + */ + protected String getValue(String fullKey) { + return _properties.getProperty(fullKey); + } + + /** + * Returns a new instance of PropertiesBasedConfigData + * for the specified full path. The wrapped java.util.Properties + * will be the same as of this instance. + * @param path The full path. + * @return a new instance. + */ + protected ConfigData createConfigData(String path) { + return new PropertiesBasedConfigData(_properties, path); + } +} diff --git a/src/jcckit/util/TicLabelFormat.java b/src/jcckit/util/TicLabelFormat.java new file mode 100644 index 000000000..61d924c58 --- /dev/null +++ b/src/jcckit/util/TicLabelFormat.java @@ -0,0 +1,33 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.util; + +/** + * Format interface for tic labels. Maps a numerical tic value onto a string. + * + * @author Franz-Josef Elmer + */ +public interface TicLabelFormat +{ + /** + * Forms the specified tic value to a string. Note, the numerical + * ticValue may be mapped onto a non-numerical one. + */ + public String form(double ticValue); +} diff --git a/src/jcckit/util/Util.java b/src/jcckit/util/Util.java new file mode 100644 index 000000000..5715d5a46 --- /dev/null +++ b/src/jcckit/util/Util.java @@ -0,0 +1,47 @@ +/* + * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details + * (http://www.gnu.org/copyleft/lesser.html). + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package jcckit.util; + +/** + * Collection of static utility methods. + * + * @author Franz-Josef Elmer + */ +public class Util { + /** Private constructor to prevent instanciation of this class. */ + private Util() {} + + /** + * Returns the natural logarithm of the specified number if + * logScale is true. + * @return x if logScale == false. + */ + public static double log(double x, boolean logScale) { + return logScale ? Math.log(x) : x; + } + + /** + * Returns the exponential function of the specified number if + * logScale is true. + * @return x if logScale == false. + */ + public static double exp(double x, boolean logScale) { + return logScale ? Math.exp(x) : x; + } +} diff --git a/src/net/sourceforge/plantuml/LineBreakStrategy.java b/src/net/sourceforge/plantuml/LineBreakStrategy.java index 238891468..03ba93e10 100644 --- a/src/net/sourceforge/plantuml/LineBreakStrategy.java +++ b/src/net/sourceforge/plantuml/LineBreakStrategy.java @@ -44,6 +44,11 @@ public class LineBreakStrategy { public LineBreakStrategy(String value) { this.value = value; } + + @Override + public String toString() { + return value; + } public boolean isAuto() { return "auto".equalsIgnoreCase(value); diff --git a/src/net/sourceforge/plantuml/PSystemBuilder.java b/src/net/sourceforge/plantuml/PSystemBuilder.java index 3fbbe0459..98daca766 100644 --- a/src/net/sourceforge/plantuml/PSystemBuilder.java +++ b/src/net/sourceforge/plantuml/PSystemBuilder.java @@ -68,6 +68,7 @@ import net.sourceforge.plantuml.error.PSystemErrorUtils; import net.sourceforge.plantuml.flowdiagram.FlowDiagramFactory; import net.sourceforge.plantuml.font.PSystemListFontsFactory; import net.sourceforge.plantuml.help.HelpFactory; +import net.sourceforge.plantuml.jcckit.PSystemJcckitFactory; import net.sourceforge.plantuml.math.PSystemLatexFactory; import net.sourceforge.plantuml.math.PSystemMathFactory; import net.sourceforge.plantuml.mindmap.MindMapDiagramFactory; @@ -79,8 +80,8 @@ import net.sourceforge.plantuml.project.GanttDiagramFactory; import net.sourceforge.plantuml.salt.PSystemSaltFactory; import net.sourceforge.plantuml.sequencediagram.SequenceDiagramFactory; import net.sourceforge.plantuml.sprite.ListSpriteDiagramFactory; -import net.sourceforge.plantuml.sprite.StdlibDiagramFactory; import net.sourceforge.plantuml.sprite.PSystemListInternalSpritesFactory; +import net.sourceforge.plantuml.sprite.StdlibDiagramFactory; import net.sourceforge.plantuml.statediagram.StateDiagramFactory; import net.sourceforge.plantuml.stats.StatsUtilsIncrement; import net.sourceforge.plantuml.sudoku.PSystemSudokuFactory; @@ -109,9 +110,10 @@ public class PSystemBuilder { // Dead code : should not append Log.error("Preprocessor Error: " + s.getPreprocessorError()); final ErrorUml err = new ErrorUml(ErrorUmlType.SYNTAX_ERROR, s.getPreprocessorError(), /* cpt */ - s.getLocation()); - // return PSystemErrorUtils.buildV1(umlSource, err, Collections. emptyList()); - return PSystemErrorUtils.buildV2(umlSource, err, Collections. emptyList(), strings2); + s.getLocation()); + // return PSystemErrorUtils.buildV1(umlSource, err, Collections. + // emptyList()); + return PSystemErrorUtils.buildV2(umlSource, err, Collections.emptyList(), strings2); } } @@ -173,8 +175,8 @@ public class PSystemBuilder { factories.add(new PSystemDitaaFactory(DiagramType.DITAA)); factories.add(new PSystemDitaaFactory(DiagramType.UML)); if (License.getCurrent() == License.GPL || License.getCurrent() == License.GPLV2) { - // factories.add(new PSystemJcckitFactory(DiagramType.JCCKIT)); - // factories.add(new PSystemJcckitFactory(DiagramType.UML)); + factories.add(new PSystemJcckitFactory(DiagramType.JCCKIT)); + factories.add(new PSystemJcckitFactory(DiagramType.UML)); // factories.add(new PSystemLogoFactory()); factories.add(new PSystemSudokuFactory()); } diff --git a/src/net/sourceforge/plantuml/Run.java b/src/net/sourceforge/plantuml/Run.java index 4964007ec..6bc6d4960 100644 --- a/src/net/sourceforge/plantuml/Run.java +++ b/src/net/sourceforge/plantuml/Run.java @@ -62,7 +62,6 @@ import net.sourceforge.plantuml.code.TranscoderUtil; import net.sourceforge.plantuml.command.UmlDiagramFactory; import net.sourceforge.plantuml.descdiagram.DescriptionDiagramFactory; import net.sourceforge.plantuml.ftp.FtpServer; -import net.sourceforge.plantuml.objectdiagram.ObjectDiagramFactory; import net.sourceforge.plantuml.png.MetadataTag; import net.sourceforge.plantuml.preproc.Stdlib; import net.sourceforge.plantuml.sequencediagram.SequenceDiagramFactory; @@ -342,7 +341,7 @@ public class Run { printPattern(new DescriptionDiagramFactory(null)); // printPattern(new ComponentDiagramFactory()); printPattern(new StateDiagramFactory(null)); - printPattern(new ObjectDiagramFactory(null)); + // printPattern(new ObjectDiagramFactory(null)); } private static void printPattern(UmlDiagramFactory factory) { diff --git a/src/net/sourceforge/plantuml/activitydiagram3/ActivityDiagram3.java b/src/net/sourceforge/plantuml/activitydiagram3/ActivityDiagram3.java index ca4695275..d8924cb9e 100644 --- a/src/net/sourceforge/plantuml/activitydiagram3/ActivityDiagram3.java +++ b/src/net/sourceforge/plantuml/activitydiagram3/ActivityDiagram3.java @@ -49,6 +49,8 @@ import net.sourceforge.plantuml.UmlDiagram; import net.sourceforge.plantuml.UmlDiagramType; import net.sourceforge.plantuml.Url; import net.sourceforge.plantuml.activitydiagram3.ftile.BoxStyle; +import net.sourceforge.plantuml.activitydiagram3.ftile.ISwimlanesA; +import net.sourceforge.plantuml.activitydiagram3.ftile.SwimlanesAAA; import net.sourceforge.plantuml.activitydiagram3.ftile.SwimlanesC; import net.sourceforge.plantuml.command.CommandExecutionResult; import net.sourceforge.plantuml.core.DiagramDescription; @@ -64,7 +66,7 @@ import net.sourceforge.plantuml.sequencediagram.NoteType; import net.sourceforge.plantuml.ugraphic.ImageBuilder; import net.sourceforge.plantuml.ugraphic.color.HColor; import net.sourceforge.plantuml.ugraphic.comp.CompressionMode; -import net.sourceforge.plantuml.ugraphic.comp.TextBlockCompressedOnXorY; +import net.sourceforge.plantuml.ugraphic.comp.CompressionXorYBuilder; public class ActivityDiagram3 extends UmlDiagram { @@ -74,7 +76,36 @@ public class ActivityDiagram3 extends UmlDiagram { private SwimlaneStrategy swimlaneStrategy; - private final SwimlanesC swinlanes = new SwimlanesC(getSkinParam(), getPragma()); + private final ISwimlanesA swinlanes = new SwimlanesAAA(getSkinParam(), getPragma()); + // private final ISwimlanesA swinlanes = new SwimlanesC(getSkinParam(), + // getPragma()); + + private ImageData exportDiagramInternalAAA(OutputStream os, int index, FileFormatOption fileFormatOption) + throws IOException { + // BUG42 + // COMPRESSION + swinlanes.computeSize(fileFormatOption.getDefaultStringBounder()); + TextBlock result = swinlanes; + + result = CompressionXorYBuilder.build(CompressionMode.ON_X, result, fileFormatOption.getDefaultStringBounder()); + result = CompressionXorYBuilder.build(CompressionMode.ON_Y, result, fileFormatOption.getDefaultStringBounder()); + + result = new TextBlockRecentred(result); + final ISkinParam skinParam = getSkinParam(); + result = new AnnotatedWorker(this, skinParam, fileFormatOption.getDefaultStringBounder()).addAdd(result); + + final Dimension2D dim = result.getMinMax(fileFormatOption.getDefaultStringBounder()).getDimension(); + final double margin = 10; + final double dpiFactor = getDpiFactor(fileFormatOption, Dimension2DDouble.delta(dim, 2 * margin, 0)); + + final ImageBuilder imageBuilder = new ImageBuilder(getSkinParam(), dpiFactor, + fileFormatOption.isWithMetadata() ? getMetadata() : null, getWarningOrError(), margin, margin, + getAnimation()); + imageBuilder.setUDrawable(result); + + return imageBuilder.writeImageTOBEMOVED(fileFormatOption, seed(), os); + + } public ActivityDiagram3(ISkinSimple skinParam) { super(skinParam); @@ -123,7 +154,8 @@ public class ActivityDiagram3 extends UmlDiagram { } public void addSpot(String spot, HColor color) { - final InstructionSpot ins = new InstructionSpot(spot, color, nextLinkRenderer(), swinlanes.getCurrentSwimlane()); + final InstructionSpot ins = new InstructionSpot(spot, color, nextLinkRenderer(), + swinlanes.getCurrentSwimlane()); current().add(ins); setNextLinkRendererInternal(LinkRendering.none()); manageSwimlaneStrategy(); @@ -201,17 +233,21 @@ public class ActivityDiagram3 extends UmlDiagram { @Override protected ImageData exportDiagramInternal(OutputStream os, int index, FileFormatOption fileFormatOption) throws IOException { + if (swinlanes instanceof SwimlanesC == false || swinlanes instanceof SwimlanesAAA) { + return exportDiagramInternalAAA(os, index, fileFormatOption); + } // BUG42 // COMPRESSION + swinlanes.computeSize(fileFormatOption.getDefaultStringBounder()); TextBlock result = swinlanes; - // result = new TextBlockCompressedOnY(CompressionMode.ON_Y, result); - result = new TextBlockCompressedOnXorY(CompressionMode.ON_X, result); - result = new TextBlockCompressedOnXorY(CompressionMode.ON_Y, result); + + result = CompressionXorYBuilder.build(CompressionMode.ON_X, result, fileFormatOption.getDefaultStringBounder()); + result = CompressionXorYBuilder.build(CompressionMode.ON_Y, result, fileFormatOption.getDefaultStringBounder()); + result = new TextBlockRecentred(result); final ISkinParam skinParam = getSkinParam(); result = new AnnotatedWorker(this, skinParam, fileFormatOption.getDefaultStringBounder()).addAdd(result); - // final Dimension2D dim = TextBlockUtils.getMinMax(result, fileFormatOption.getDefaultStringBounder()) - // .getDimension(); + final Dimension2D dim = result.getMinMax(fileFormatOption.getDefaultStringBounder()).getDimension(); final double margin = 10; final double dpiFactor = getDpiFactor(fileFormatOption, Dimension2DDouble.delta(dim, 2 * margin, 0)); @@ -329,8 +365,8 @@ public class ActivityDiagram3 extends UmlDiagram { public void startIf(Display test, Display whenThen, HColor color, Url url) { manageSwimlaneStrategy(); - final InstructionIf instructionIf = new InstructionIf(swinlanes.getCurrentSwimlane(), current(), test, - whenThen, nextLinkRenderer(), color, getSkinParam(), url); + final InstructionIf instructionIf = new InstructionIf(swinlanes.getCurrentSwimlane(), current(), test, whenThen, + nextLinkRenderer(), color, getSkinParam(), url); current().add(instructionIf); setNextLinkRendererInternal(LinkRendering.none()); setCurrent(instructionIf); @@ -396,12 +432,13 @@ public class ActivityDiagram3 extends UmlDiagram { } - public CommandExecutionResult backwardWhile(Display label) { + public CommandExecutionResult backwardWhile(Display label, BoxStyle boxStyle) { manageSwimlaneStrategy(); if (current() instanceof InstructionRepeat) { final InstructionRepeat instructionRepeat = (InstructionRepeat) current(); - // final LinkRendering back = new LinkRendering(linkColor).withDisplay(linkLabel); - instructionRepeat.setBackward(label, swinlanes.getCurrentSwimlane()); + // final LinkRendering back = new + // LinkRendering(linkColor).withDisplay(linkLabel); + instructionRepeat.setBackward(label, swinlanes.getCurrentSwimlane(), boxStyle); // setCurrent(instructionRepeat.getParent()); // this.setNextLinkRendererInternal(LinkRendering.none()); return CommandExecutionResult.ok(); @@ -435,8 +472,8 @@ public class ActivityDiagram3 extends UmlDiagram { return CommandExecutionResult.ok(); } - public void startGroup(Display name, HColor backColor, HColor titleColor, HColor borderColor, - USymbol type, double roundCorner) { + public void startGroup(Display name, HColor backColor, HColor titleColor, HColor borderColor, USymbol type, + double roundCorner) { manageSwimlaneStrategy(); final InstructionGroup instructionGroup = new InstructionGroup(current(), name, backColor, titleColor, swinlanes.getCurrentSwimlane(), borderColor, nextLinkRenderer(), type, roundCorner); diff --git a/src/net/sourceforge/plantuml/activitydiagram3/InstructionRepeat.java b/src/net/sourceforge/plantuml/activitydiagram3/InstructionRepeat.java index 5ff0153ae..63516a3ba 100644 --- a/src/net/sourceforge/plantuml/activitydiagram3/InstructionRepeat.java +++ b/src/net/sourceforge/plantuml/activitydiagram3/InstructionRepeat.java @@ -55,6 +55,7 @@ public class InstructionRepeat implements Instruction { private final LinkRendering nextLinkRenderer; private final Swimlane swimlane; private Swimlane swimlaneOut; + private BoxStyle boxStyle; // private final HtmlColor color; private boolean killed = false; private final BoxStyle boxStyleIn; @@ -93,9 +94,10 @@ public class InstructionRepeat implements Instruction { return false; } - public void setBackward(Display label, Swimlane swimlaneOut) { + public void setBackward(Display label, Swimlane swimlaneOut, BoxStyle boxStyle) { this.backward = label; this.swimlaneOut = swimlaneOut; + this.boxStyle = boxStyle; } public void add(Instruction ins) { @@ -104,7 +106,7 @@ public class InstructionRepeat implements Instruction { public Ftile createFtile(FtileFactory factory) { final Ftile back = Display.isNull(backward) ? null - : factory.activity(backward, swimlane, BoxStyle.PLAIN, Colors.empty()); + : factory.activity(backward, swimlane, boxStyle, Colors.empty()); final Ftile decorateOut = factory.decorateOut(repeatList.createFtile(factory), endRepeatLinkRendering); final Ftile result = factory.repeat(boxStyleIn, swimlane, swimlaneOut, startLabel, decorateOut, test, yes, out, colors, backRepeatLinkRendering, back, isLastOfTheParent()); diff --git a/src/net/sourceforge/plantuml/activitydiagram3/command/CommandBackward3.java b/src/net/sourceforge/plantuml/activitydiagram3/command/CommandBackward3.java index 42bb41326..566c9bca9 100644 --- a/src/net/sourceforge/plantuml/activitydiagram3/command/CommandBackward3.java +++ b/src/net/sourceforge/plantuml/activitydiagram3/command/CommandBackward3.java @@ -37,6 +37,7 @@ package net.sourceforge.plantuml.activitydiagram3.command; import net.sourceforge.plantuml.LineLocation; import net.sourceforge.plantuml.activitydiagram3.ActivityDiagram3; +import net.sourceforge.plantuml.activitydiagram3.ftile.BoxStyle; import net.sourceforge.plantuml.command.CommandExecutionResult; import net.sourceforge.plantuml.command.SingleLineCommand2; import net.sourceforge.plantuml.command.regex.IRegex; @@ -57,14 +58,22 @@ public class CommandBackward3 extends SingleLineCommand2 { RegexLeaf.spaceZeroOrMore(), // new RegexLeaf(":"), // new RegexLeaf("LABEL", "(.*)"), // - new RegexLeaf(";"), // + new RegexLeaf("STYLE", CommandActivity3.ENDING_GROUP), // RegexLeaf.end()); } @Override protected CommandExecutionResult executeArg(ActivityDiagram3 diagram, LineLocation location, RegexResult arg) { + final BoxStyle boxStyle; + final String styleString = arg.get("STYLE", 0); + if (styleString == null) { + boxStyle = BoxStyle.PLAIN; + } else { + boxStyle = BoxStyle.fromChar(styleString.charAt(0)); + } + final Display label = Display.getWithNewlines(arg.get("LABEL", 0)); - return diagram.backwardWhile(label); + return diagram.backwardWhile(label, boxStyle); } } diff --git a/src/net/sourceforge/plantuml/activitydiagram3/ftile/CenteredText.java b/src/net/sourceforge/plantuml/activitydiagram3/ftile/CenteredText.java new file mode 100644 index 000000000..863a4253a --- /dev/null +++ b/src/net/sourceforge/plantuml/activitydiagram3/ftile/CenteredText.java @@ -0,0 +1,59 @@ +/* ======================================================================== + * PlantUML : a free UML diagram generator + * ======================================================================== + * + * (C) Copyright 2009-2020, Arnaud Roques + * + * Project Info: http://plantuml.com + * + * If you like this project or if you find it useful, you can support us at: + * + * http://plantuml.com/patreon (only 1$ per month!) + * http://plantuml.com/paypal + * + * This file is part of PlantUML. + * + * PlantUML is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PlantUML distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * + * Original Author: Arnaud Roques + * + * + */ +package net.sourceforge.plantuml.activitydiagram3.ftile; + +import net.sourceforge.plantuml.graphic.TextBlock; +import net.sourceforge.plantuml.ugraphic.UShape; + +public class CenteredText implements UShape { + + private final TextBlock text; + private final double totalWidth; + + public CenteredText(TextBlock text, double totalWidth) { + this.text = text; + this.totalWidth = totalWidth; + } + + public TextBlock getText() { + return text; + } + + public double getTotalWidth() { + return totalWidth; + } + +} diff --git a/src/net/sourceforge/plantuml/activitydiagram3/ftile/FtileFactoryDelegator.java b/src/net/sourceforge/plantuml/activitydiagram3/ftile/FtileFactoryDelegator.java index 927b5e9ca..2f20a3ae6 100644 --- a/src/net/sourceforge/plantuml/activitydiagram3/ftile/FtileFactoryDelegator.java +++ b/src/net/sourceforge/plantuml/activitydiagram3/ftile/FtileFactoryDelegator.java @@ -118,7 +118,7 @@ public class FtileFactoryDelegator implements FtileFactory { } else { fontConfiguration = new FontConfiguration(skinParam(), FontParam.ARROW, null); } - return display.create(fontConfiguration, HorizontalAlignment.LEFT, skinParam(), CreoleMode.SIMPLE_LINE); + return display.create7(fontConfiguration, HorizontalAlignment.LEFT, skinParam(), CreoleMode.SIMPLE_LINE); } protected Display getInLinkRenderingDisplay(Ftile tile) { diff --git a/src/net/sourceforge/plantuml/activitydiagram3/ftile/ISwimlanesA.java b/src/net/sourceforge/plantuml/activitydiagram3/ftile/ISwimlanesA.java new file mode 100644 index 000000000..eb58f1e82 --- /dev/null +++ b/src/net/sourceforge/plantuml/activitydiagram3/ftile/ISwimlanesA.java @@ -0,0 +1,62 @@ +/* ======================================================================== + * PlantUML : a free UML diagram generator + * ======================================================================== + * + * (C) Copyright 2009-2020, Arnaud Roques + * + * Project Info: http://plantuml.com + * + * If you like this project or if you find it useful, you can support us at: + * + * http://plantuml.com/patreon (only 1$ per month!) + * http://plantuml.com/paypal + * + * This file is part of PlantUML. + * + * PlantUML is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PlantUML distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * + * Original Author: Arnaud Roques + * + * + */ +package net.sourceforge.plantuml.activitydiagram3.ftile; + +import net.sourceforge.plantuml.activitydiagram3.Instruction; +import net.sourceforge.plantuml.activitydiagram3.LinkRendering; +import net.sourceforge.plantuml.cucadiagram.Display; +import net.sourceforge.plantuml.graphic.StringBounder; +import net.sourceforge.plantuml.graphic.TextBlock; +import net.sourceforge.plantuml.style.Styleable; +import net.sourceforge.plantuml.ugraphic.color.HColor; + +public interface ISwimlanesA extends TextBlock, Styleable { + + public void computeSize(StringBounder stringBounder); + + public void swimlane(String name, HColor color, Display label); + + public void setCurrent(Instruction ins); + + public Instruction getCurrent(); + + public LinkRendering nextLinkRenderer(); + + public Swimlane getCurrentSwimlane(); + + public void setNextLinkRenderer(LinkRendering link); + +} diff --git a/src/net/sourceforge/plantuml/activitydiagram3/ftile/LaneDivider.java b/src/net/sourceforge/plantuml/activitydiagram3/ftile/LaneDivider.java new file mode 100644 index 000000000..d8621011e --- /dev/null +++ b/src/net/sourceforge/plantuml/activitydiagram3/ftile/LaneDivider.java @@ -0,0 +1,123 @@ +/* ======================================================================== + * PlantUML : a free UML diagram generator + * ======================================================================== + * + * (C) Copyright 2009-2020, Arnaud Roques + * + * Project Info: http://plantuml.com + * + * If you like this project or if you find it useful, you can support us at: + * + * http://plantuml.com/patreon (only 1$ per month!) + * http://plantuml.com/paypal + * + * This file is part of PlantUML. + * + * PlantUML is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PlantUML distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * + * Original Author: Arnaud Roques + * + * + */ +package net.sourceforge.plantuml.activitydiagram3.ftile; + +import java.awt.geom.Dimension2D; + +import net.sourceforge.plantuml.ColorParam; +import net.sourceforge.plantuml.Dimension2DDouble; +import net.sourceforge.plantuml.ISkinParam; +import net.sourceforge.plantuml.LineParam; +import net.sourceforge.plantuml.SkinParam; +import net.sourceforge.plantuml.graphic.AbstractTextBlock; +import net.sourceforge.plantuml.graphic.StringBounder; +import net.sourceforge.plantuml.skin.rose.Rose; +import net.sourceforge.plantuml.style.PName; +import net.sourceforge.plantuml.style.SName; +import net.sourceforge.plantuml.style.Style; +import net.sourceforge.plantuml.style.StyleSignature; +import net.sourceforge.plantuml.ugraphic.UChangeColor; +import net.sourceforge.plantuml.ugraphic.UEmpty; +import net.sourceforge.plantuml.ugraphic.UGraphic; +import net.sourceforge.plantuml.ugraphic.ULine; +import net.sourceforge.plantuml.ugraphic.UShape; +import net.sourceforge.plantuml.ugraphic.UStroke; +import net.sourceforge.plantuml.ugraphic.UTranslate; +import net.sourceforge.plantuml.ugraphic.color.HColor; + +public class LaneDivider extends AbstractTextBlock { + + private final ISkinParam skinParam;; + + private final double x1; + private final double x2; + private final double height; + private Style style; + + public LaneDivider(ISkinParam skinParam, double x1, double x2, double height) { + this.skinParam = skinParam; + this.x1 = x1; + this.x2 = x2; + this.height = height; + } + + public StyleSignature getDefaultStyleDefinition() { + return StyleSignature.of(SName.root, SName.element, SName.classDiagram, SName.swimlane); + } + + private Style getStyle() { + if (style == null) { + this.style = getDefaultStyleDefinition().getMergedStyle(skinParam.getCurrentStyleBuilder()); + } + return style; + } + + public Dimension2D calculateDimension(StringBounder stringBounder) { + return new Dimension2DDouble(x1 + x2, height); + } + + public void drawU(UGraphic ug) { +// final UShape back = new URectangle(x1 + x2, height).ignoreForCompressionOnY(); +// ug.apply(new UChangeColor(HColorUtils.BLUE)).draw(back); + final UShape back = new UEmpty(x1 + x2, 1); + ug.draw(back); + + HColor color = skinParam.getHtmlColor(ColorParam.swimlaneBorder, null, false); + if (color == null) { + color = ColorParam.swimlaneBorder.getDefaultValue(); + } + UStroke thickness = Rose.getStroke(skinParam, LineParam.swimlaneBorder, 2); + if (SkinParam.USE_STYLES()) { + color = getStyle().value(PName.LineColor).asColor(skinParam.getIHtmlColorSet()); + thickness = getStyle().getStroke(); + } + ug.apply(UTranslate.dx(x1)).apply(thickness).apply(new UChangeColor(color)).draw(ULine.vline(height)); + + } + + public double getWidth() { + return x1 + x2; + } + + public final double getX1() { + return x1; + } + + public final double getX2() { + return x2; + } + +} diff --git a/src/net/sourceforge/plantuml/activitydiagram3/ftile/Snake.java b/src/net/sourceforge/plantuml/activitydiagram3/ftile/Snake.java index bc6079179..ff1420083 100644 --- a/src/net/sourceforge/plantuml/activitydiagram3/ftile/Snake.java +++ b/src/net/sourceforge/plantuml/activitydiagram3/ftile/Snake.java @@ -54,7 +54,7 @@ import net.sourceforge.plantuml.ugraphic.UGraphic; import net.sourceforge.plantuml.ugraphic.UPolygon; import net.sourceforge.plantuml.ugraphic.UShape; import net.sourceforge.plantuml.ugraphic.UTranslate; -import net.sourceforge.plantuml.ugraphic.comp.CompressionTransform; +import net.sourceforge.plantuml.ugraphic.comp.PiecewiseAffineTransform; public class Snake implements UShape { @@ -72,7 +72,7 @@ public class Snake implements UShape { this.worm.setIgnoreForCompression(); } - public Snake transformX(CompressionTransform compressionTransform) { + public Snake transformX(PiecewiseAffineTransform compressionTransform) { final Snake result = new Snake(startDecoration, horizontalAlignment, color, endDecoration); result.textBlock = this.textBlock; result.mergeable = this.mergeable; diff --git a/src/net/sourceforge/plantuml/activitydiagram3/ftile/Swimlane.java b/src/net/sourceforge/plantuml/activitydiagram3/ftile/Swimlane.java index ab0e6297c..09de94ec7 100644 --- a/src/net/sourceforge/plantuml/activitydiagram3/ftile/Swimlane.java +++ b/src/net/sourceforge/plantuml/activitydiagram3/ftile/Swimlane.java @@ -79,8 +79,11 @@ public class Swimlane implements SpecificBackcolorable { return translate; } - public final void setTranslateAndWidth(UTranslate translate, double actualWidth) { + public final void setTranslate(UTranslate translate) { this.translate = translate; + } + + public final void setWidth(double actualWidth) { this.actualWidth = actualWidth; } diff --git a/src/net/sourceforge/plantuml/activitydiagram3/ftile/SwimlanesA.java b/src/net/sourceforge/plantuml/activitydiagram3/ftile/SwimlanesA.java index 710362303..b3d243073 100644 --- a/src/net/sourceforge/plantuml/activitydiagram3/ftile/SwimlanesA.java +++ b/src/net/sourceforge/plantuml/activitydiagram3/ftile/SwimlanesA.java @@ -77,10 +77,12 @@ import net.sourceforge.plantuml.ugraphic.URectangle; import net.sourceforge.plantuml.ugraphic.UShape; import net.sourceforge.plantuml.ugraphic.UTranslate; import net.sourceforge.plantuml.ugraphic.color.HColor; +import net.sourceforge.plantuml.ugraphic.comp.CompressionMode; +import net.sourceforge.plantuml.ugraphic.comp.SlotFinder; import net.sourceforge.plantuml.ugraphic.comp.SlotSet; import net.sourceforge.plantuml.utils.MathUtils; -public class SwimlanesA extends AbstractTextBlock implements TextBlock, Styleable { +public class SwimlanesA extends AbstractTextBlock implements ISwimlanesA, TextBlock, Styleable { protected final ISkinParam skinParam;; private final Pragma pragma; @@ -179,28 +181,33 @@ public class SwimlanesA extends AbstractTextBlock implements TextBlock, Styleabl } - static protected final double separationMargin = 10; + protected double separationMargin() { + return 10; + } - private TextBlock full; + // private TextBlock full; - public void drawU(UGraphic ug) { - if (full == null) { - final FtileFactory factory = getFtileFactory(ug.getStringBounder()); - full = root.createFtile(factory); - if (swimlanes.size() <= 1) { - // BUG42 - full = new TextBlockInterceptorUDrawable(full); - } + public final void computeSize(StringBounder stringBounder) { + final SlotFinder ug = new SlotFinder(CompressionMode.ON_Y, stringBounder); + if (swimlanes.size() > 1) { + TextBlock full = root.createFtile(getFtileFactory(stringBounder)); + computeSizeInternal(ug, full); } + } + + public final void drawU(UGraphic ug) { + TextBlock full = root.createFtile(getFtileFactory(ug.getStringBounder())); + ug = new UGraphicForSnake(ug); - if (swimlanes.size() <= 1) { + if (swimlanes.size() > 1) { + drawWhenSwimlanes(ug, full); + } else { + // BUG42 + full = new TextBlockInterceptorUDrawable(full); full.drawU(ug); ug.flushUg(); - return; } - - drawWhenSwimlanes(ug, full); } static private void printDebug(UGraphic ug, SlotSet slot, HColor col, TextBlock full) { @@ -213,30 +220,32 @@ public class SwimlanesA extends AbstractTextBlock implements TextBlock, Styleabl final StringBounder stringBounder = ug.getStringBounder(); final Dimension2D dimensionFull = full.calculateDimension(stringBounder); - computeSize(ug, full); - final UTranslate titleHeightTranslate = getTitleHeightTranslate(stringBounder); - double x2 = 0; for (Swimlane swimlane : swimlanes) { final HColor back = swimlane.getColors(skinParam).getColor(ColorType.BACK); if (back != null) { - final UGraphic background = ug.apply(new UChangeBackColor(back)).apply(new UChangeColor(back)) - .apply(UTranslate.dx(x2)); - final URectangle rectangle = new URectangle(swimlane.getActualWidth(), dimensionFull.getHeight() - + titleHeightTranslate.getDy()).ignoreForCompression(); - background.draw(rectangle); + UGraphic background = ug.apply(new UChangeBackColor(back)).apply(new UChangeColor(back)); + background = background.apply(UTranslate.dx(x2)); + drawBackColor(background, swimlane, dimensionFull); } - full.drawU(new UGraphicInterceptorOneSwimlane(ug, swimlane).apply(swimlane.getTranslate()).apply( - titleHeightTranslate)); + full.drawU(new UGraphicInterceptorOneSwimlane(ug, swimlane).apply(swimlane.getTranslate()) + .apply(getTitleHeightTranslate(stringBounder))); x2 += swimlane.getActualWidth(); } - final Cross cross = new Cross(ug.apply(titleHeightTranslate)); + final Cross cross = new Cross(ug.apply(getTitleHeightTranslate(stringBounder))); full.drawU(cross); cross.flushUg(); } + protected void drawBackColor(UGraphic ug, Swimlane swimlane, Dimension2D dimensionFull) { + final StringBounder stringBounder = ug.getStringBounder(); + final double height = dimensionFull.getHeight() + getTitleHeightTranslate(stringBounder).getDy(); + final URectangle rectangle = new URectangle(swimlane.getActualWidth(), height).ignoreForCompressionOnX().ignoreForCompressionOnY(); + ug.draw(rectangle); + } + protected UTranslate getTitleHeightTranslate(final StringBounder stringBounder) { return new UTranslate(); } @@ -245,8 +254,8 @@ public class SwimlanesA extends AbstractTextBlock implements TextBlock, Styleabl final StringBounder stringBounder = ug.getStringBounder(); for (Swimlane swimlane : swimlanes) { final LimitFinder limitFinder = new LimitFinder(stringBounder, false); - final UGraphicInterceptorOneSwimlane interceptor = new UGraphicInterceptorOneSwimlane(new UGraphicForSnake( - limitFinder), swimlane); + final UGraphicInterceptorOneSwimlane interceptor = new UGraphicInterceptorOneSwimlane( + new UGraphicForSnake(limitFinder), swimlane); full.drawU(interceptor); interceptor.flushUg(); final MinMax minMax = limitFinder.getMinMax(); @@ -254,7 +263,7 @@ public class SwimlanesA extends AbstractTextBlock implements TextBlock, Styleabl } } - private void computeSize(UGraphic ug, TextBlock full) { + private void computeSizeInternal(UGraphic ug, TextBlock full) { computeDrawingWidths(ug, full); double x1 = 0; @@ -269,9 +278,10 @@ public class SwimlanesA extends AbstractTextBlock implements TextBlock, Styleabl for (Swimlane swimlane : swimlanes) { final double swimlaneActualWidth = swimlaneActualWidth(ug.getStringBounder(), swimlaneWidth, swimlane); - final UTranslate translate = UTranslate.dx(x1 - swimlane.getMinMax().getMinX() + separationMargin - + (swimlaneActualWidth - rawDrawingWidth(swimlane)) / 2.0); - swimlane.setTranslateAndWidth(translate, swimlaneActualWidth); + final UTranslate translate = UTranslate.dx(x1 - swimlane.getMinMax().getMinX() + separationMargin() + + (swimlaneActualWidth - rawDrawingWidth(swimlane)) / 2.0); + swimlane.setTranslate(translate); + swimlane.setWidth(swimlaneActualWidth); x1 += swimlaneActualWidth; } @@ -282,7 +292,7 @@ public class SwimlanesA extends AbstractTextBlock implements TextBlock, Styleabl } private double rawDrawingWidth(Swimlane swimlane) { - return swimlane.getMinMax().getWidth() + 2 * separationMargin; + return swimlane.getMinMax().getWidth() + 2 * separationMargin(); } public Dimension2D calculateDimension(StringBounder stringBounder) { diff --git a/src/net/sourceforge/plantuml/activitydiagram3/ftile/SwimlanesAAA.java b/src/net/sourceforge/plantuml/activitydiagram3/ftile/SwimlanesAAA.java new file mode 100644 index 000000000..53e616346 --- /dev/null +++ b/src/net/sourceforge/plantuml/activitydiagram3/ftile/SwimlanesAAA.java @@ -0,0 +1,436 @@ +/* ======================================================================== + * PlantUML : a free UML diagram generator + * ======================================================================== + * + * (C) Copyright 2009-2020, Arnaud Roques + * + * Project Info: http://plantuml.com + * + * If you like this project or if you find it useful, you can support us at: + * + * http://plantuml.com/patreon (only 1$ per month!) + * http://plantuml.com/paypal + * + * This file is part of PlantUML. + * + * PlantUML is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PlantUML distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * + * Original Author: Arnaud Roques + * + * + */ +package net.sourceforge.plantuml.activitydiagram3.ftile; + +import java.awt.geom.Dimension2D; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import net.sourceforge.plantuml.ColorParam; +import net.sourceforge.plantuml.FontParam; +import net.sourceforge.plantuml.ISkinParam; +import net.sourceforge.plantuml.LineBreakStrategy; +import net.sourceforge.plantuml.Pragma; +import net.sourceforge.plantuml.SkinParam; +import net.sourceforge.plantuml.activitydiagram3.Instruction; +import net.sourceforge.plantuml.activitydiagram3.InstructionList; +import net.sourceforge.plantuml.activitydiagram3.LinkRendering; +import net.sourceforge.plantuml.activitydiagram3.ftile.vcompact.FtileFactoryDelegatorAddNote; +import net.sourceforge.plantuml.activitydiagram3.ftile.vcompact.FtileFactoryDelegatorAddUrl; +import net.sourceforge.plantuml.activitydiagram3.ftile.vcompact.FtileFactoryDelegatorAssembly; +import net.sourceforge.plantuml.activitydiagram3.ftile.vcompact.FtileFactoryDelegatorCreateGroup; +import net.sourceforge.plantuml.activitydiagram3.ftile.vcompact.FtileFactoryDelegatorCreateParallel; +import net.sourceforge.plantuml.activitydiagram3.ftile.vcompact.FtileFactoryDelegatorIf; +import net.sourceforge.plantuml.activitydiagram3.ftile.vcompact.FtileFactoryDelegatorRepeat; +import net.sourceforge.plantuml.activitydiagram3.ftile.vcompact.FtileFactoryDelegatorSwitch; +import net.sourceforge.plantuml.activitydiagram3.ftile.vcompact.FtileFactoryDelegatorWhile; +import net.sourceforge.plantuml.activitydiagram3.ftile.vcompact.UGraphicInterceptorOneSwimlane; +import net.sourceforge.plantuml.activitydiagram3.ftile.vcompact.VCompactFactory; +import net.sourceforge.plantuml.cucadiagram.Display; +import net.sourceforge.plantuml.graphic.AbstractTextBlock; +import net.sourceforge.plantuml.graphic.FontConfiguration; +import net.sourceforge.plantuml.graphic.HorizontalAlignment; +import net.sourceforge.plantuml.graphic.StringBounder; +import net.sourceforge.plantuml.graphic.TextBlock; +import net.sourceforge.plantuml.graphic.TextBlockUtils; +import net.sourceforge.plantuml.graphic.UGraphicDelegator; +import net.sourceforge.plantuml.graphic.color.ColorType; +import net.sourceforge.plantuml.style.PName; +import net.sourceforge.plantuml.style.SName; +import net.sourceforge.plantuml.style.Style; +import net.sourceforge.plantuml.style.StyleSignature; +import net.sourceforge.plantuml.style.Styleable; +import net.sourceforge.plantuml.svek.UGraphicForSnake; +import net.sourceforge.plantuml.ugraphic.LimitFinder; +import net.sourceforge.plantuml.ugraphic.MinMax; +import net.sourceforge.plantuml.ugraphic.UChange; +import net.sourceforge.plantuml.ugraphic.UChangeBackColor; +import net.sourceforge.plantuml.ugraphic.UChangeColor; +import net.sourceforge.plantuml.ugraphic.UGraphic; +import net.sourceforge.plantuml.ugraphic.URectangle; +import net.sourceforge.plantuml.ugraphic.UShape; +import net.sourceforge.plantuml.ugraphic.UTranslate; +import net.sourceforge.plantuml.ugraphic.color.HColor; +import net.sourceforge.plantuml.ugraphic.comp.CompressionMode; +import net.sourceforge.plantuml.ugraphic.comp.SlotFinder; +import net.sourceforge.plantuml.utils.MathUtils; + +public class SwimlanesAAA extends AbstractTextBlock implements ISwimlanesA, TextBlock, Styleable { + + private final ISkinParam skinParam;; + private final Pragma pragma; + + private final List swimlanesRaw = new ArrayList(); + private final List swimlanesSpecial = new ArrayList(); + private final List dividers = new ArrayList(); + private Swimlane currentSwimlane = null; + + private final Instruction root = new InstructionList(); + private Instruction currentInstruction = root; + + private LinkRendering nextLinkRenderer = LinkRendering.none(); + private Style style; + + private List swimlanes() { + return Collections.unmodifiableList(swimlanesRaw); + } + + private List swimlanesSpecial() { + if (swimlanesSpecial.size() == 0) { + swimlanesSpecial.addAll(swimlanesRaw); + final Swimlane last = new Swimlane(""); + last.setMinMax(MinMax.getEmpty(true)); + swimlanesSpecial.add(last); + } + return Collections.unmodifiableList(swimlanesSpecial); + } + + public StyleSignature getDefaultStyleDefinition() { + return StyleSignature.of(SName.root, SName.element, SName.classDiagram, SName.swimlane); + } + + public SwimlanesAAA(ISkinParam skinParam, Pragma pragma) { + this.skinParam = skinParam; + this.pragma = pragma; + } + + protected Style getStyle() { + if (style == null) { + this.style = getDefaultStyleDefinition().getMergedStyle(skinParam.getCurrentStyleBuilder()); + } + return style; + } + + private FtileFactory getFtileFactory(StringBounder stringBounder) { + FtileFactory factory = new VCompactFactory(skinParam, stringBounder); + factory = new FtileFactoryDelegatorAddUrl(factory); + factory = new FtileFactoryDelegatorAssembly(factory); + factory = new FtileFactoryDelegatorIf(factory, pragma); + factory = new FtileFactoryDelegatorSwitch(factory); + factory = new FtileFactoryDelegatorWhile(factory); + factory = new FtileFactoryDelegatorRepeat(factory); + factory = new FtileFactoryDelegatorCreateParallel(factory); + // factory = new FtileFactoryDelegatorCreateParallelAddingMargin(new + // FtileFactoryDelegatorCreateParallel1(factory)); + factory = new FtileFactoryDelegatorAddNote(factory); + factory = new FtileFactoryDelegatorCreateGroup(factory); + return factory; + } + + public void swimlane(String name, HColor color, Display label) { + currentSwimlane = getOrCreate(name); + if (color != null) { + currentSwimlane.setSpecificColorTOBEREMOVED(ColorType.BACK, color); + } + if (Display.isNull(label) == false) { + currentSwimlane.setDisplay(label); + } + } + + private Swimlane getOrCreate(String name) { + for (Swimlane s : swimlanes()) { + if (s.getName().equals(name)) { + return s; + } + } + final Swimlane result = new Swimlane(name); + swimlanesRaw.add(result); + return result; + } + + class Cross extends UGraphicDelegator { + + private Cross(UGraphic ug) { + super(ug); + } + + @Override + public void draw(UShape shape) { + if (shape instanceof Ftile) { + final Ftile tile = (Ftile) shape; + tile.drawU(this); + } else if (shape instanceof Connection) { + final Connection connection = (Connection) shape; + final Ftile tile1 = connection.getFtile1(); + final Ftile tile2 = connection.getFtile2(); + + if (tile1 == null || tile2 == null) { + return; + } + if (tile1.getSwimlaneOut() != tile2.getSwimlaneIn()) { + final ConnectionCross connectionCross = new ConnectionCross(connection); + connectionCross.drawU(getUg()); + } + } + } + + public UGraphic apply(UChange change) { + return new Cross(getUg().apply(change)); + } + + } + + public final void computeSize(StringBounder stringBounder) { + final SlotFinder ug = new SlotFinder(CompressionMode.ON_Y, stringBounder); + if (swimlanes().size() > 1) { + TextBlock full = root.createFtile(getFtileFactory(stringBounder)); + computeSizeInternal(ug, full); + } + + } + + public final void drawU(UGraphic ug) { + TextBlock full = root.createFtile(getFtileFactory(ug.getStringBounder())); + + ug = new UGraphicForSnake(ug); + if (swimlanes().size() > 1) { + drawWhenSwimlanes(ug, full); + } else { + // BUG42 + full = new TextBlockInterceptorUDrawable(full); + full.drawU(ug); + ug.flushUg(); + } + } + + private TextBlock getTitle(Swimlane swimlane) { + final HorizontalAlignment horizontalAlignment = HorizontalAlignment.LEFT; + FontConfiguration fontConfiguration = new FontConfiguration(skinParam, FontParam.SWIMLANE_TITLE, null); + if (SkinParam.USE_STYLES()) { + fontConfiguration = getStyle().getFontConfiguration(skinParam.getIHtmlColorSet()); + } + LineBreakStrategy wrap = getWrap(); + if (wrap.isAuto()) { + wrap = new LineBreakStrategy("" + ((int) swimlane.getActualWidth())); + } + + return swimlane.getDisplay().create9(fontConfiguration, horizontalAlignment, skinParam, wrap); + } + + private LineBreakStrategy getWrap() { + LineBreakStrategy wrap = skinParam.swimlaneWrapTitleWidth(); + if (wrap == LineBreakStrategy.NONE) { + wrap = skinParam.wrapWidth(); + } + return wrap; + } + + private UTranslate getTitleHeightTranslate(final StringBounder stringBounder) { + double titlesHeight = getTitlesHeight(stringBounder); + return UTranslate.dy(titlesHeight > 0 ? titlesHeight + 5 : 0); + } + + private double getTitlesHeight(StringBounder stringBounder) { + double titlesHeight = 0; + for (Swimlane swimlane : swimlanes()) { + final TextBlock swTitle = getTitle(swimlane); + titlesHeight = Math.max(titlesHeight, swTitle.calculateDimension(stringBounder).getHeight()); + } + return titlesHeight; + } + + private void drawWhenSwimlanes(UGraphic ug, TextBlock full) { + final StringBounder stringBounder = ug.getStringBounder(); + final UTranslate titleHeightTranslate = getTitleHeightTranslate(stringBounder); + + drawTitlesBackground(ug); + + final Dimension2D dimensionFull = full.calculateDimension(stringBounder); + int i = 0; + assert dividers.size() == swimlanes().size() + 1; + for (Swimlane swimlane : swimlanesSpecial()) { + final LaneDivider divider1 = dividers.get(i); + + final double xpos = swimlane.getTranslate().getDx() + swimlane.getMinMax().getMinX(); + final HColor back = swimlane.getColors(skinParam).getColor(ColorType.BACK); + if (back != null) { + final LaneDivider divider2 = dividers.get(i + 1); + final UGraphic background = ug.apply(new UChangeBackColor(back)).apply(new UChangeColor(back)) + .apply(UTranslate.dx(xpos - divider1.getX2())); + final double width = swimlane.getActualWidth() + divider1.getX2() + divider2.getX1(); + final double height = dimensionFull.getHeight() + titleHeightTranslate.getDy(); + background.draw(new URectangle(width, height).ignoreForCompressionOnX().ignoreForCompressionOnY()); + } + + full.drawU(new UGraphicInterceptorOneSwimlane(ug, swimlane).apply(swimlane.getTranslate()) + .apply(getTitleHeightTranslate(stringBounder))); + + final double dividerWith = divider1.calculateDimension(stringBounder).getWidth(); + divider1.drawU(ug.apply(UTranslate.dx(xpos - dividerWith))); + i++; + } + + final Cross cross = new Cross(ug.apply(getTitleHeightTranslate(stringBounder))); + full.drawU(cross); + cross.flushUg(); + + drawTitles(ug); + + } + + private void drawTitlesBackground(UGraphic ug) { + HColor color = skinParam.getHtmlColor(ColorParam.swimlaneTitleBackground, null, false); + if (SkinParam.USE_STYLES()) { + color = getStyle().value(PName.BackGroundColor).asColor(skinParam.getIHtmlColorSet()); + } + if (color != null) { + final double titleHeight = getTitlesHeight(ug.getStringBounder()); + double fullWidth = swimlanesSpecial().get(swimlanesSpecial().size() - 1).getTranslate().getDx() - 2 * 5 - 1; + final URectangle back = new URectangle(fullWidth, titleHeight).ignoreForCompressionOnX() + .ignoreForCompressionOnY(); + ug.apply(UTranslate.dx(5)).apply(new UChangeBackColor(color)).apply(new UChangeColor(color)).draw(back); + } + } + + private void drawTitles(UGraphic ug) { + for (Swimlane swimlane : swimlanes()) { + final TextBlock swTitle = getTitle(swimlane); + final double x2 = swimlane.getTranslate().getDx() + swimlane.getMinMax().getMinX(); + final CenteredText centeredText = new CenteredText(swTitle, getWidthWithoutTitle(swimlane)); + ug.apply(UTranslate.dx(x2)).draw(centeredText); + } + } + + private void computeDrawingWidths(UGraphic ug, TextBlock full) { + final StringBounder stringBounder = ug.getStringBounder(); + for (Swimlane swimlane : swimlanes()) { + final LimitFinder limitFinder = new LimitFinder(stringBounder, false); + final UGraphicInterceptorOneSwimlane interceptor = new UGraphicInterceptorOneSwimlane( + new UGraphicForSnake(limitFinder), swimlane); + full.drawU(interceptor); + interceptor.flushUg(); + final MinMax minMax = limitFinder.getMinMax(); + swimlane.setMinMax(minMax); + } + } + + private void computeSizeInternal(UGraphic ug, TextBlock full) { + computeDrawingWidths(ug, full); + + double min = skinParam.swimlaneWidth(); + + if (min == ISkinParam.SWIMLANE_WIDTH_SAME) { + for (Swimlane swimlane : swimlanes()) { + min = Math.max(min, getWidthWithoutTitle(swimlane)); + } + } + final StringBounder stringBounder = ug.getStringBounder(); + + for (int i = 0; i < swimlanesSpecial().size(); i++) { + final Swimlane swimlane = swimlanesSpecial().get(i); + final double swimlaneActualWidth = MathUtils.max(min, getWidthWithoutTitle(swimlane)); + swimlane.setWidth(swimlaneActualWidth); + } + + final UTranslate titleHeightTranslate = getTitleHeightTranslate(stringBounder); + final Dimension2D dimensionFull = full.calculateDimension(stringBounder); + + dividers.clear(); + double xpos = 0; + for (int i = 0; i < swimlanesSpecial().size(); i++) { + final Swimlane swimlane = swimlanesSpecial().get(i); + double x1 = getHalfMissingSpace(stringBounder, i, min); + double x2 = getHalfMissingSpace(stringBounder, i + 1, min); + final LaneDivider laneDivider = new LaneDivider(skinParam, x1, x2, + dimensionFull.getHeight() + titleHeightTranslate.getDy()); + dividers.add(laneDivider); + + final double xx = xpos + laneDivider.getWidth() - swimlane.getMinMax().getMinX() + + (swimlane.getActualWidth() - getWidthWithoutTitle(swimlane)) / 2.0; + swimlane.setTranslate(UTranslate.dx(xx)); + + xpos += swimlane.getActualWidth() + laneDivider.getWidth(); + } + assert dividers.size() == swimlanes().size() + 1; + } + + public double getHalfMissingSpace(StringBounder stringBounder, int i, double min) { + if (i == 0 || i > swimlanesSpecial().size()) { + return 5; + } + final Swimlane swimlane = swimlanesSpecial().get(i - 1); + final double swimlaneActualWidth = Math.max(min, getWidthWithoutTitle(swimlane)); + final double titleWidth = getTitle(swimlane).calculateDimension(stringBounder).getWidth(); + if (titleWidth <= swimlaneActualWidth) { + return 5; + } + assert titleWidth > swimlaneActualWidth; + return Math.max(5, 5 + (titleWidth - swimlaneActualWidth) / 2); + } + + private double getWidthWithoutTitle(Swimlane swimlane) { + return swimlane.getMinMax().getWidth(); + } + + public Dimension2D calculateDimension(StringBounder stringBounder) { + return getMinMax(stringBounder).getDimension(); + } + + public Instruction getCurrent() { + return currentInstruction; + } + + public void setCurrent(Instruction current) { + this.currentInstruction = current; + } + + public LinkRendering nextLinkRenderer() { + return nextLinkRenderer; + } + + public void setNextLinkRenderer(LinkRendering link) { + if (link == null) { + throw new IllegalArgumentException(); + } + this.nextLinkRenderer = link; + } + + public Swimlane getCurrentSwimlane() { + return currentSwimlane; + } + + private MinMax cachedMinMax; + + @Override + public MinMax getMinMax(StringBounder stringBounder) { + if (cachedMinMax == null) { + cachedMinMax = TextBlockUtils.getMinMax(this, stringBounder); + } + return cachedMinMax; + } + +} diff --git a/src/net/sourceforge/plantuml/activitydiagram3/ftile/SwimlanesB.java b/src/net/sourceforge/plantuml/activitydiagram3/ftile/SwimlanesB.java index d973307b3..6802fcb86 100644 --- a/src/net/sourceforge/plantuml/activitydiagram3/ftile/SwimlanesB.java +++ b/src/net/sourceforge/plantuml/activitydiagram3/ftile/SwimlanesB.java @@ -73,7 +73,7 @@ public class SwimlanesB extends SwimlanesA { } if (color != null) { final double titleHeight = getTitlesHeight(stringBounder); - final URectangle back = new URectangle(getTitlesWidth(stringBounder), titleHeight).ignoreForCompression(); + final URectangle back = new URectangle(getTitlesWidth(stringBounder), titleHeight).ignoreForCompressionOnX().ignoreForCompressionOnY(); ug.apply(new UChangeBackColor(color)).apply(new UChangeColor(color)).draw(back); } for (Swimlane swimlane : swimlanes) { @@ -105,7 +105,7 @@ public class SwimlanesB extends SwimlanesA { wrap = new LineBreakStrategy("" + ((int) swimlane.getActualWidth())); } - return swimlane.getDisplay().create(fontConfiguration, horizontalAlignment, skinParam, wrap); + return swimlane.getDisplay().create9(fontConfiguration, horizontalAlignment, skinParam, wrap); } private LineBreakStrategy getWrap() { @@ -124,7 +124,7 @@ public class SwimlanesB extends SwimlanesA { } final double titleWidth = getTitle(swimlane).calculateDimension(stringBounder).getWidth(); - return MathUtils.max(m1, titleWidth + 2 * separationMargin); + return MathUtils.max(m1, titleWidth + 2 * separationMargin()); } diff --git a/src/net/sourceforge/plantuml/activitydiagram3/ftile/SwimlanesC.java b/src/net/sourceforge/plantuml/activitydiagram3/ftile/SwimlanesC.java index 679519bad..97c98f2e6 100644 --- a/src/net/sourceforge/plantuml/activitydiagram3/ftile/SwimlanesC.java +++ b/src/net/sourceforge/plantuml/activitydiagram3/ftile/SwimlanesC.java @@ -91,5 +91,4 @@ public class SwimlanesC extends SwimlanesB { } ug.apply(thickness).apply(new UChangeColor(color)).draw(ULine.vline(height)); } - } diff --git a/src/net/sourceforge/plantuml/activitydiagram3/ftile/Worm.java b/src/net/sourceforge/plantuml/activitydiagram3/ftile/Worm.java index 76a6a6fbd..30a39f2d5 100644 --- a/src/net/sourceforge/plantuml/activitydiagram3/ftile/Worm.java +++ b/src/net/sourceforge/plantuml/activitydiagram3/ftile/Worm.java @@ -104,7 +104,7 @@ public class Worm implements Iterable { ug = ug.apply(new UStroke(1.5)); final Point2D start = points.get(0); if (ignoreForCompression) { - startDecoration.setIgnoreForCompression(CompressionMode.ON_X); + startDecoration.setCompressionMode(CompressionMode.ON_X); } ug.apply(new UTranslate(start)).apply(new UStroke()).draw(startDecoration); } @@ -112,7 +112,7 @@ public class Worm implements Iterable { ug = ug.apply(new UStroke(1.5)); final Point2D end = points.get(points.size() - 1); if (ignoreForCompression) { - endDecoration.setIgnoreForCompression(CompressionMode.ON_X); + endDecoration.setCompressionMode(CompressionMode.ON_X); } ug.apply(new UTranslate(end)).apply(new UStroke()).draw(endDecoration); } diff --git a/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/AbstractParallelFtilesBuilder.java b/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/AbstractParallelFtilesBuilder.java index d2aa032a8..cf787e45e 100644 --- a/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/AbstractParallelFtilesBuilder.java +++ b/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/AbstractParallelFtilesBuilder.java @@ -119,7 +119,7 @@ public abstract class AbstractParallelFtilesBuilder { } else { fontConfiguration = new FontConfiguration(skinParam(), FontParam.ARROW, null); } - return display.create(fontConfiguration, HorizontalAlignment.LEFT, skinParam(), CreoleMode.SIMPLE_LINE); + return display.create7(fontConfiguration, HorizontalAlignment.LEFT, skinParam(), CreoleMode.SIMPLE_LINE); } protected TextBlock getTextBlock(LinkRendering linkRendering) { diff --git a/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/FtileGroup.java b/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/FtileGroup.java index d21e36fa8..01cc8ae7f 100644 --- a/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/FtileGroup.java +++ b/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/FtileGroup.java @@ -173,8 +173,8 @@ public class FtileGroup extends AbstractFtile { final FtileGeometry orig = getInnerDimension(stringBounder); final Dimension2D dimTitle = name.calculateDimension(stringBounder); final Dimension2D dimHeaderNote = headerNote.calculateDimension(stringBounder); - final double suppWidth = MathUtils - .max(orig.getWidth(), dimTitle.getWidth() + 20, dimHeaderNote.getWidth() + 20) - orig.getWidth(); + final double suppWidth = MathUtils.max(orig.getWidth(), dimTitle.getWidth() + 20, dimHeaderNote.getWidth() + 20) + - orig.getWidth(); return suppWidth; } @@ -207,11 +207,11 @@ public class FtileGroup extends AbstractFtile { + headerNoteHeight(stringBounder); final double titleAndHeaderNoteHeight = diffHeightTitle(stringBounder) + headerNoteHeight(stringBounder); if (orig.hasPointOut()) { - return new FtileGeometry(width, height, orig.getLeft() + suppWidth / 2, orig.getInY() - + titleAndHeaderNoteHeight, orig.getOutY() + titleAndHeaderNoteHeight); + return new FtileGeometry(width, height, orig.getLeft() + suppWidth / 2, + orig.getInY() + titleAndHeaderNoteHeight, orig.getOutY() + titleAndHeaderNoteHeight); } - return new FtileGeometry(width, height, orig.getLeft() + suppWidth / 2, orig.getInY() - + titleAndHeaderNoteHeight); + return new FtileGeometry(width, height, orig.getLeft() + suppWidth / 2, + orig.getInY() + titleAndHeaderNoteHeight); } private double headerNoteHeight(StringBounder stringBounder) { @@ -222,12 +222,14 @@ public class FtileGroup extends AbstractFtile { final StringBounder stringBounder = ug.getStringBounder(); final Dimension2D dimTotal = calculateDimension(stringBounder); - // final double roundCorner = type.getSkinParameter().getRoundCorner(skinParam(), null); + // final double roundCorner = + // type.getSkinParameter().getRoundCorner(skinParam(), null); final SymbolContext symbolContext = new SymbolContext(backColor, borderColor).withShadow(shadowing) .withStroke(stroke).withCorner(roundCorner, 0); - type.asBig(name, inner.skinParam().getHorizontalAlignment(AlignmentParam.packageTitleAlignment, null, false), - TextBlockUtils.empty(0, 0), dimTotal.getWidth(), dimTotal.getHeight(), symbolContext, + final HorizontalAlignment align = inner.skinParam().getHorizontalAlignment(AlignmentParam.packageTitleAlignment, + null, false); + type.asBig(name, align, TextBlockUtils.empty(0, 0), dimTotal.getWidth(), dimTotal.getHeight(), symbolContext, skinParam().getStereotypeAlignment()).drawU(ug); final Dimension2D dimHeaderNote = headerNote.calculateDimension(stringBounder); diff --git a/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/FtileRepeat.java b/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/FtileRepeat.java index dbc07e722..45ca924c9 100644 --- a/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/FtileRepeat.java +++ b/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/FtileRepeat.java @@ -154,12 +154,12 @@ class FtileRepeat extends AbstractFtile { final List conns = new ArrayList(); final Display in1 = repeat.getInLinkRendering().getDisplay(); - final TextBlock tbin1 = in1 == null ? null : in1.create(fcArrow, HorizontalAlignment.LEFT, spriteContainer, + final TextBlock tbin1 = in1 == null ? null : in1.create7(fcArrow, HorizontalAlignment.LEFT, spriteContainer, CreoleMode.SIMPLE_LINE); conns.add(result.new ConnectionIn(repeat.getInLinkRendering().getRainbow(arrowColor), tbin1)); final Display backLink1 = backRepeatLinkRendering.getDisplay(); - final TextBlock tbbackLink1 = backLink1 == null ? null : backLink1.create(fcArrow, HorizontalAlignment.LEFT, + final TextBlock tbbackLink1 = backLink1 == null ? null : backLink1.create7(fcArrow, HorizontalAlignment.LEFT, spriteContainer, CreoleMode.SIMPLE_LINE); if (repeat.getSwimlaneIn() == swimlaneOut) { if (backward == null) { @@ -176,7 +176,7 @@ class FtileRepeat extends AbstractFtile { } final Display out1 = repeat.getOutLinkRendering().getDisplay(); - final TextBlock tbout1 = out1 == null ? null : out1.create(fcArrow, HorizontalAlignment.LEFT, spriteContainer, + final TextBlock tbout1 = out1 == null ? null : out1.create7(fcArrow, HorizontalAlignment.LEFT, spriteContainer, CreoleMode.SIMPLE_LINE); final Rainbow tmpColor = endRepeatLinkColor.withDefault(arrowColor); diff --git a/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/ParallelBuilderSplit.java b/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/ParallelBuilderSplit.java index d56e794b0..cbf0530b1 100644 --- a/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/ParallelBuilderSplit.java +++ b/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/ParallelBuilderSplit.java @@ -52,7 +52,6 @@ import net.sourceforge.plantuml.activitydiagram3.ftile.FtileGeometry; import net.sourceforge.plantuml.activitydiagram3.ftile.FtileKilled; import net.sourceforge.plantuml.activitydiagram3.ftile.FtileUtils; import net.sourceforge.plantuml.activitydiagram3.ftile.Snake; -import net.sourceforge.plantuml.activitydiagram3.ftile.Swimlane; import net.sourceforge.plantuml.activitydiagram3.ftile.vertical.FtileThinSplit; import net.sourceforge.plantuml.cucadiagram.Display; import net.sourceforge.plantuml.graphic.Rainbow; diff --git a/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/cond/ConditionalBuilder.java b/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/cond/ConditionalBuilder.java index 01d80f3bc..a244eab08 100644 --- a/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/cond/ConditionalBuilder.java +++ b/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/cond/ConditionalBuilder.java @@ -267,7 +267,7 @@ public class ConditionalBuilder { } private TextBlock getLabelPositive(Branch branch) { - return branch.getLabelPositive().create(fontArrow, HorizontalAlignment.LEFT, ftileFactory.skinParam(), + return branch.getLabelPositive().create7(fontArrow, HorizontalAlignment.LEFT, ftileFactory.skinParam(), CreoleMode.SIMPLE_LINE); } @@ -279,10 +279,10 @@ public class ConditionalBuilder { // else use default ConditionEndStyle.DIAMOND if (hasTwoBranches()) { final Display out1 = branch1.getFtile().getOutLinkRendering().getDisplay(); - final TextBlock tbout1 = out1 == null ? null : out1.create(fontArrow, HorizontalAlignment.LEFT, + final TextBlock tbout1 = out1 == null ? null : out1.create7(fontArrow, HorizontalAlignment.LEFT, ftileFactory.skinParam(), CreoleMode.SIMPLE_LINE); final Display out2 = branch2.getFtile().getOutLinkRendering().getDisplay(); - final TextBlock tbout2 = out2 == null ? null : out2.create(fontArrow, HorizontalAlignment.LEFT, + final TextBlock tbout2 = out2 == null ? null : out2.create7(fontArrow, HorizontalAlignment.LEFT, ftileFactory.skinParam(), CreoleMode.SIMPLE_LINE); FtileDiamond tmp = new FtileDiamond(tile1.skinParam(), backColor, borderColor, swimlane); tmp = useNorth ? tmp.withNorth(tbout1) : tmp.withWest(tbout1); diff --git a/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/cond/FtileSwitchWithDiamonds.java b/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/cond/FtileSwitchWithDiamonds.java index c12357381..23241fde0 100644 --- a/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/cond/FtileSwitchWithDiamonds.java +++ b/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/cond/FtileSwitchWithDiamonds.java @@ -194,7 +194,7 @@ public class FtileSwitchWithDiamonds extends FtileSwitchNude { protected TextBlock getLabelPositive(Branch branch) { final FontConfiguration fcArrow = new FontConfiguration(skinParam(), FontParam.ARROW, null); - return branch.getLabelPositive().create(fcArrow, HorizontalAlignment.LEFT, skinParam(), CreoleMode.SIMPLE_LINE); + return branch.getLabelPositive().create7(fcArrow, HorizontalAlignment.LEFT, skinParam(), CreoleMode.SIMPLE_LINE); } } diff --git a/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/cond/FtileSwitchWithOneLink.java b/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/cond/FtileSwitchWithOneLink.java index eaec448b3..32d521e08 100644 --- a/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/cond/FtileSwitchWithOneLink.java +++ b/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/cond/FtileSwitchWithOneLink.java @@ -39,7 +39,6 @@ import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.List; -import net.sourceforge.plantuml.FontParam; import net.sourceforge.plantuml.activitydiagram3.Branch; import net.sourceforge.plantuml.activitydiagram3.ftile.AbstractConnection; import net.sourceforge.plantuml.activitydiagram3.ftile.Arrows; @@ -49,12 +48,8 @@ import net.sourceforge.plantuml.activitydiagram3.ftile.FtileGeometry; import net.sourceforge.plantuml.activitydiagram3.ftile.FtileUtils; import net.sourceforge.plantuml.activitydiagram3.ftile.Snake; import net.sourceforge.plantuml.activitydiagram3.ftile.Swimlane; -import net.sourceforge.plantuml.creole.CreoleMode; -import net.sourceforge.plantuml.graphic.FontConfiguration; -import net.sourceforge.plantuml.graphic.HorizontalAlignment; import net.sourceforge.plantuml.graphic.Rainbow; import net.sourceforge.plantuml.graphic.StringBounder; -import net.sourceforge.plantuml.graphic.TextBlock; import net.sourceforge.plantuml.ugraphic.UGraphic; public class FtileSwitchWithOneLink extends FtileSwitchWithDiamonds { diff --git a/src/net/sourceforge/plantuml/activitydiagram3/ftile/vertical/FtileBlackBlock.java b/src/net/sourceforge/plantuml/activitydiagram3/ftile/vertical/FtileBlackBlock.java index f925a61b7..fcbdbd497 100644 --- a/src/net/sourceforge/plantuml/activitydiagram3/ftile/vertical/FtileBlackBlock.java +++ b/src/net/sourceforge/plantuml/activitydiagram3/ftile/vertical/FtileBlackBlock.java @@ -91,7 +91,7 @@ public class FtileBlackBlock extends AbstractFtile { } public void drawU(UGraphic ug) { - final URectangle rect = new URectangle(width, height).rounded(5).ignoreForCompression(); + final URectangle rect = new URectangle(width, height).rounded(5).ignoreForCompressionOnX(); if (skinParam().shadowing(null)) { rect.setDeltaShadow(3); } diff --git a/src/net/sourceforge/plantuml/activitydiagram3/ftile/vertical/FtileBox.java b/src/net/sourceforge/plantuml/activitydiagram3/ftile/vertical/FtileBox.java index 3993ff281..120c557b6 100644 --- a/src/net/sourceforge/plantuml/activitydiagram3/ftile/vertical/FtileBox.java +++ b/src/net/sourceforge/plantuml/activitydiagram3/ftile/vertical/FtileBox.java @@ -67,7 +67,6 @@ import net.sourceforge.plantuml.graphic.Rainbow; import net.sourceforge.plantuml.graphic.StringBounder; import net.sourceforge.plantuml.graphic.TextBlock; import net.sourceforge.plantuml.graphic.UDrawable; -import net.sourceforge.plantuml.graphic.color.ColorType; import net.sourceforge.plantuml.graphic.color.Colors; import net.sourceforge.plantuml.style.PName; import net.sourceforge.plantuml.style.SName; diff --git a/src/net/sourceforge/plantuml/classdiagram/ClassDiagramFactory.java b/src/net/sourceforge/plantuml/classdiagram/ClassDiagramFactory.java index 068293331..dad321503 100644 --- a/src/net/sourceforge/plantuml/classdiagram/ClassDiagramFactory.java +++ b/src/net/sourceforge/plantuml/classdiagram/ClassDiagramFactory.java @@ -79,6 +79,7 @@ import net.sourceforge.plantuml.descdiagram.command.CommandNewpage; import net.sourceforge.plantuml.descdiagram.command.CommandPackageWithUSymbol; import net.sourceforge.plantuml.objectdiagram.command.CommandCreateEntityObject; import net.sourceforge.plantuml.objectdiagram.command.CommandCreateEntityObjectMultilines; +import net.sourceforge.plantuml.objectdiagram.command.CommandCreateMap; public class ClassDiagramFactory extends UmlDiagramFactory { @@ -110,6 +111,7 @@ public class ClassDiagramFactory extends UmlDiagramFactory { cmds.add(new CommandRemoveRestore()); cmds.add(new CommandCreateClassMultilines()); cmds.add(new CommandCreateEntityObjectMultilines()); + cmds.add(new CommandCreateMap()); cmds.add(new CommandCreateClass()); cmds.add(new CommandCreateEntityObject()); diff --git a/src/net/sourceforge/plantuml/classdiagram/command/CommandAddMethod.java b/src/net/sourceforge/plantuml/classdiagram/command/CommandAddMethod.java index 663c6c7c0..fe53e6fcf 100644 --- a/src/net/sourceforge/plantuml/classdiagram/command/CommandAddMethod.java +++ b/src/net/sourceforge/plantuml/classdiagram/command/CommandAddMethod.java @@ -84,7 +84,7 @@ public class CommandAddMethod extends SingleLineCommand2 { if (field.length() > 0 && VisibilityModifier.isVisibilityCharacter(field)) { diagram.setVisibilityModifierPresent(true); } - entity.getBodier().addFieldOrMethod(field, entity); + entity.getBodier().addFieldOrMethod(field); return CommandExecutionResult.ok(); } } diff --git a/src/net/sourceforge/plantuml/classdiagram/command/CommandCreateClassMultilines.java b/src/net/sourceforge/plantuml/classdiagram/command/CommandCreateClassMultilines.java index 04285202f..16432539c 100644 --- a/src/net/sourceforge/plantuml/classdiagram/command/CommandCreateClassMultilines.java +++ b/src/net/sourceforge/plantuml/classdiagram/command/CommandCreateClassMultilines.java @@ -109,8 +109,8 @@ public class CommandCreateClassMultilines extends CommandMultilines2"))), // + new RegexOptional(new RegexConcat(RegexLeaf.spaceZeroOrMore(), + new RegexLeaf("GENERIC", "\\<(" + GenericRegexProducer.PATTERN + ")\\>"))), // RegexLeaf.spaceZeroOrMore(), // new RegexLeaf("STEREO", "(\\<\\<.+\\>\\>)?"), // RegexLeaf.spaceZeroOrMore(), // @@ -120,17 +120,17 @@ public class CommandCreateClassMultilines extends CommandMultilines2 1) { lines = lines.subExtract(1, 1); - final Url url = null; + // final Url url = null; // if (lines.size() > 0) { - // final UrlBuilder urlBuilder = new UrlBuilder(diagram.getSkinParam().getValue("topurl"), ModeUrl.STRICT); + // final UrlBuilder urlBuilder = new + // UrlBuilder(diagram.getSkinParam().getValue("topurl"), ModeUrl.STRICT); // url = urlBuilder.getUrl(lines.getFirst499().toString()); // } else { // url = null; @@ -166,15 +167,11 @@ public class CommandCreateClassMultilines extends CommandMultilines2 0 && VisibilityModifier.isVisibilityCharacter(s.getString())) { diagram.setVisibilityModifierPresent(true); } - if (s instanceof StringLocated) { - entity.getBodier().addFieldOrMethod(((StringLocated) s).getString(), entity); - } else { - entity.getBodier().addFieldOrMethod(s.toString(), entity); - } - } - if (url != null) { - entity.addUrl(url); + entity.getBodier().addFieldOrMethod(s.getString()); } +// if (url != null) { +// entity.addUrl(url); +// } } manageExtends("EXTENDS", diagram, line0, entity); @@ -216,8 +213,8 @@ public class CommandCreateClassMultilines extends CommandMultilines2 splitInTwo(StringBounder stringBounder, double width) { throw new UnsupportedOperationException(getClass().toString()); } - + private final Sheet sheet; private List stripes; private Map heights; @@ -77,7 +77,7 @@ public class SheetBlock1 extends AbstractTextBlock implements TextBlock, Atom, S throw new IllegalArgumentException(); } } - + @Override public String toString() { return sheet.toString(); diff --git a/src/net/sourceforge/plantuml/creole/atom/AtomText.java b/src/net/sourceforge/plantuml/creole/atom/AtomText.java index 872c95ada..e1119ae53 100644 --- a/src/net/sourceforge/plantuml/creole/atom/AtomText.java +++ b/src/net/sourceforge/plantuml/creole/atom/AtomText.java @@ -92,11 +92,6 @@ public class AtomText extends AbstractAtom implements Atom { return new AtomText(text, fontConfiguration, null, ZERO, ZERO); } -// public static AtomText createHeading(String text, FontConfiguration fontConfiguration, int order) { -// fontConfiguration = FOO(fontConfiguration, order); -// return new AtomText(text, fontConfiguration, null, ZERO, ZERO); -// } - public static Atom createUrl(Url url, FontConfiguration fontConfiguration, ISkinSimple skinSimple) { fontConfiguration = fontConfiguration.hyperlink(); final Display display = Display.getWithNewlines(url.getLabel()); diff --git a/src/net/sourceforge/plantuml/cucadiagram/Bodier.java b/src/net/sourceforge/plantuml/cucadiagram/Bodier.java index c2e881429..c91716f93 100644 --- a/src/net/sourceforge/plantuml/cucadiagram/Bodier.java +++ b/src/net/sourceforge/plantuml/cucadiagram/Bodier.java @@ -35,206 +35,28 @@ */ package net.sourceforge.plantuml.cucadiagram; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; -import java.util.Set; import net.sourceforge.plantuml.FontParam; import net.sourceforge.plantuml.ISkinParam; -import net.sourceforge.plantuml.StringUtils; -import net.sourceforge.plantuml.graphic.HorizontalAlignment; import net.sourceforge.plantuml.graphic.TextBlock; -import net.sourceforge.plantuml.graphic.TextBlockLineBefore; -import net.sourceforge.plantuml.graphic.TextBlockUtils; -import net.sourceforge.plantuml.skin.VisibilityModifier; -public class Bodier { +public interface Bodier { - private final List rawBody = new ArrayList(); - private final Set hides; - private LeafType type; - private List methodsToDisplay; - private List fieldsToDisplay; - private final boolean manageModifier; - private ILeaf leaf; + public void setLeaf(ILeaf leaf); - public void muteClassToObject() { - methodsToDisplay = null; - fieldsToDisplay = null; - type = LeafType.OBJECT; - } + public List getFieldsToDisplay(); - public Bodier(LeafType type, Set hides) { - this.hides = hides; - this.type = type; - this.manageModifier = type == null ? false : type.manageModifier(); - } + public List getMethodsToDisplay(); - public void setLeaf(ILeaf leaf) { - if (leaf == null) { - throw new IllegalArgumentException(); - } - this.leaf = leaf; + public void addFieldOrMethod(String s); - } + public TextBlock getBody(FontParam fontParam, ISkinParam skinParam, boolean showMethods, boolean showFields, + Stereotype stereotype); - public void addFieldOrMethod(String s, IEntity leaf) { - if (leaf == null) { - throw new IllegalArgumentException(); - } - // Empty cache - methodsToDisplay = null; - fieldsToDisplay = null; - rawBody.add(s); - if (leaf instanceof ILeaf) { - if (this.leaf != null && this.leaf != leaf) { - throw new IllegalArgumentException(); - } - this.leaf = (ILeaf) leaf; - } - } + public List getRawBody(); - private boolean isBodyEnhanced() { - for (String s : rawBody) { - if (BodyEnhanced.isBlockSeparator(s)) { - return true; - } - } - return false; - } - - private boolean isMethod(String s) { - if (type == LeafType.ANNOTATION || type == LeafType.ABSTRACT_CLASS || type == LeafType.CLASS - || type == LeafType.INTERFACE || type == LeafType.ENUM) { - return Member.isMethod(s); - } - return false; - } - - public List getMethodsToDisplay() { - if (methodsToDisplay == null) { - methodsToDisplay = new ArrayList(); - for (int i = 0; i < rawBody.size(); i++) { - final String s = rawBody.get(i); - if (isMethod(i, rawBody) == false) { - continue; - } - if (s.length() == 0 && methodsToDisplay.size() == 0) { - continue; - } - final Member m = new Member(s, true, manageModifier); - if (hides == null || hides.contains(m.getVisibilityModifier()) == false) { - methodsToDisplay.add(m); - } - } - removeFinalEmptyMembers(methodsToDisplay); - } - return Collections.unmodifiableList(methodsToDisplay); - } - - private boolean isMethod(int i, List rawBody) { - if (i > 0 && i < rawBody.size() - 1 && rawBody.get(i).length() == 0 && isMethod(rawBody.get(i - 1)) - && isMethod(rawBody.get(i + 1))) { - return true; - } - return isMethod(rawBody.get(i)); - } - - public List getFieldsToDisplay() { - if (fieldsToDisplay == null) { - fieldsToDisplay = new ArrayList(); - for (String s : rawBody) { - if (isMethod(s) == true) { - continue; - } - if (s.length() == 0 && fieldsToDisplay.size() == 0) { - continue; - } - final Member m = new Member(s, false, manageModifier); - if (hides == null || hides.contains(m.getVisibilityModifier()) == false) { - fieldsToDisplay.add(m); - } - } - removeFinalEmptyMembers(fieldsToDisplay); - } - return Collections.unmodifiableList(fieldsToDisplay); - } - - private void removeFinalEmptyMembers(List result) { - while (result.size() > 0 && StringUtils.trin(result.get(result.size() - 1).getDisplay(false)).length() == 0) { - result.remove(result.size() - 1); - } - } - - public boolean hasUrl() { - for (Member m : getFieldsToDisplay()) { - if (m.hasUrl()) { - return true; - } - } - for (Member m : getMethodsToDisplay()) { - if (m.hasUrl()) { - return true; - } - } - return false; - } - - private List rawBodyWithoutHidden() { - if (hides == null || hides.size() == 0) { - return rawBody; - } - final List result = new ArrayList(); - for (String s : rawBody) { - final Member m = new Member(s, isMethod(s), manageModifier); - if (hides.contains(m.getVisibilityModifier()) == false) { - result.add(s); - } - - } - return result; - } - - public TextBlock getBody(final FontParam fontParam, final ISkinParam skinParam, final boolean showMethods, - final boolean showFields, Stereotype stereotype) { - if (type.isLikeClass() && isBodyEnhanced()) { - if (showMethods || showFields) { - return new BodyEnhanced(rawBodyWithoutHidden(), fontParam, skinParam, manageModifier, stereotype, leaf); - } - return null; - } - if (leaf == null) { - throw new IllegalStateException(); - } - final MethodsOrFieldsArea fields = new MethodsOrFieldsArea(getFieldsToDisplay(), fontParam, skinParam, - stereotype, leaf); - if (type == LeafType.OBJECT) { - if (showFields == false) { - return new TextBlockLineBefore(TextBlockUtils.empty(0, 0)); - } - return fields.asBlockMemberImpl(); - } - if (type.isLikeClass() == false) { - throw new UnsupportedOperationException(); - } - final MethodsOrFieldsArea methods = new MethodsOrFieldsArea(getMethodsToDisplay(), fontParam, skinParam, - stereotype, leaf); - if (showFields && showMethods == false) { - return fields.asBlockMemberImpl(); - } else if (showMethods && showFields == false) { - return methods.asBlockMemberImpl(); - } else if (showFields == false && showMethods == false) { - return TextBlockUtils.empty(0, 0); - } - - final TextBlock bb1 = fields.asBlockMemberImpl(); - final TextBlock bb2 = methods.asBlockMemberImpl(); - return TextBlockUtils.mergeTB(bb1, bb2, HorizontalAlignment.LEFT); - } - - public List getRawBody() { - return Collections.unmodifiableList(rawBody); - } + public void muteClassToObject(); + public boolean hasUrl(); } diff --git a/src/net/sourceforge/plantuml/cucadiagram/BodierImpl.java b/src/net/sourceforge/plantuml/cucadiagram/BodierImpl.java new file mode 100644 index 000000000..fec9b01ce --- /dev/null +++ b/src/net/sourceforge/plantuml/cucadiagram/BodierImpl.java @@ -0,0 +1,243 @@ +/* ======================================================================== + * PlantUML : a free UML diagram generator + * ======================================================================== + * + * (C) Copyright 2009-2020, Arnaud Roques + * + * Project Info: http://plantuml.com + * + * If you like this project or if you find it useful, you can support us at: + * + * http://plantuml.com/patreon (only 1$ per month!) + * http://plantuml.com/paypal + * + * This file is part of PlantUML. + * + * PlantUML is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PlantUML distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * + * Original Author: Arnaud Roques + * + * + */ +package net.sourceforge.plantuml.cucadiagram; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import net.sourceforge.plantuml.FontParam; +import net.sourceforge.plantuml.ISkinParam; +import net.sourceforge.plantuml.StringUtils; +import net.sourceforge.plantuml.graphic.HorizontalAlignment; +import net.sourceforge.plantuml.graphic.TextBlock; +import net.sourceforge.plantuml.graphic.TextBlockLineBefore; +import net.sourceforge.plantuml.graphic.TextBlockUtils; +import net.sourceforge.plantuml.skin.VisibilityModifier; + +public class BodierImpl implements Bodier { + + private final List rawBody = new ArrayList(); + private final Set hides; + private LeafType type; + private List methodsToDisplay; + private List fieldsToDisplay; + private final boolean manageModifier; + private ILeaf leaf; + + public void muteClassToObject() { + methodsToDisplay = null; + fieldsToDisplay = null; + type = LeafType.OBJECT; + } + + public BodierImpl(LeafType type, Set hides) { + if (type == LeafType.MAP) { + throw new IllegalArgumentException(); + } + this.hides = hides; + this.type = type; + this.manageModifier = type == null ? false : type.manageModifier(); + } + + public void setLeaf(ILeaf leaf) { + if (leaf == null) { + throw new IllegalArgumentException(); + } + this.leaf = leaf; + + } + + public void addFieldOrMethod(String s) { +// if (leaf == null) { +// throw new IllegalArgumentException(); +// } + // Empty cache + methodsToDisplay = null; + fieldsToDisplay = null; + rawBody.add(s); +// if (leaf instanceof ILeaf) { +// if (this.leaf != null && this.leaf != leaf) { +// throw new IllegalArgumentException(); +// } +// this.leaf = (ILeaf) leaf; +// } + } + + private boolean isBodyEnhanced() { + for (String s : rawBody) { + if (BodyEnhanced.isBlockSeparator(s)) { + return true; + } + } + return false; + } + + private boolean isMethod(String s) { + if (type == LeafType.ANNOTATION || type == LeafType.ABSTRACT_CLASS || type == LeafType.CLASS + || type == LeafType.INTERFACE || type == LeafType.ENUM) { + return Member.isMethod(s); + } + return false; + } + + public List getMethodsToDisplay() { + if (methodsToDisplay == null) { + methodsToDisplay = new ArrayList(); + for (int i = 0; i < rawBody.size(); i++) { + final String s = rawBody.get(i); + if (isMethod(i, rawBody) == false) { + continue; + } + if (s.length() == 0 && methodsToDisplay.size() == 0) { + continue; + } + final Member m = new Member(s, true, manageModifier); + if (hides == null || hides.contains(m.getVisibilityModifier()) == false) { + methodsToDisplay.add(m); + } + } + removeFinalEmptyMembers(methodsToDisplay); + } + return Collections.unmodifiableList(methodsToDisplay); + } + + private boolean isMethod(int i, List rawBody) { + if (i > 0 && i < rawBody.size() - 1 && rawBody.get(i).length() == 0 && isMethod(rawBody.get(i - 1)) + && isMethod(rawBody.get(i + 1))) { + return true; + } + return isMethod(rawBody.get(i)); + } + + public List getFieldsToDisplay() { + if (fieldsToDisplay == null) { + fieldsToDisplay = new ArrayList(); + for (String s : rawBody) { + if (isMethod(s) == true) { + continue; + } + if (s.length() == 0 && fieldsToDisplay.size() == 0) { + continue; + } + final Member m = new Member(s, false, manageModifier); + if (hides == null || hides.contains(m.getVisibilityModifier()) == false) { + fieldsToDisplay.add(m); + } + } + removeFinalEmptyMembers(fieldsToDisplay); + } + return Collections.unmodifiableList(fieldsToDisplay); + } + + private void removeFinalEmptyMembers(List result) { + while (result.size() > 0 && StringUtils.trin(result.get(result.size() - 1).getDisplay(false)).length() == 0) { + result.remove(result.size() - 1); + } + } + + public boolean hasUrl() { + for (Member m : getFieldsToDisplay()) { + if (m.hasUrl()) { + return true; + } + } + for (Member m : getMethodsToDisplay()) { + if (m.hasUrl()) { + return true; + } + } + return false; + } + + private List rawBodyWithoutHidden() { + if (hides == null || hides.size() == 0) { + return rawBody; + } + final List result = new ArrayList(); + for (String s : rawBody) { + final Member m = new Member(s, isMethod(s), manageModifier); + if (hides.contains(m.getVisibilityModifier()) == false) { + result.add(s); + } + + } + return result; + } + + public TextBlock getBody(final FontParam fontParam, final ISkinParam skinParam, final boolean showMethods, + final boolean showFields, Stereotype stereotype) { + if (type.isLikeClass() && isBodyEnhanced()) { + if (showMethods || showFields) { + return new BodyEnhanced(rawBodyWithoutHidden(), fontParam, skinParam, manageModifier, stereotype, leaf); + } + return null; + } + if (leaf == null) { + throw new IllegalStateException(); + } + final MethodsOrFieldsArea fields = new MethodsOrFieldsArea(getFieldsToDisplay(), fontParam, skinParam, + stereotype, leaf); + if (type == LeafType.OBJECT) { + if (showFields == false) { + return new TextBlockLineBefore(TextBlockUtils.empty(0, 0)); + } + return fields.asBlockMemberImpl(); + } + if (type.isLikeClass() == false) { + throw new UnsupportedOperationException(); + } + final MethodsOrFieldsArea methods = new MethodsOrFieldsArea(getMethodsToDisplay(), fontParam, skinParam, + stereotype, leaf); + if (showFields && showMethods == false) { + return fields.asBlockMemberImpl(); + } else if (showMethods && showFields == false) { + return methods.asBlockMemberImpl(); + } else if (showFields == false && showMethods == false) { + return TextBlockUtils.empty(0, 0); + } + + final TextBlock bb1 = fields.asBlockMemberImpl(); + final TextBlock bb2 = methods.asBlockMemberImpl(); + return TextBlockUtils.mergeTB(bb1, bb2, HorizontalAlignment.LEFT); + } + + public List getRawBody() { + return Collections.unmodifiableList(rawBody); + } + +} diff --git a/src/net/sourceforge/plantuml/cucadiagram/BodierMap.java b/src/net/sourceforge/plantuml/cucadiagram/BodierMap.java new file mode 100644 index 000000000..af4d5139a --- /dev/null +++ b/src/net/sourceforge/plantuml/cucadiagram/BodierMap.java @@ -0,0 +1,112 @@ +/* ======================================================================== + * PlantUML : a free UML diagram generator + * ======================================================================== + * + * (C) Copyright 2009-2020, Arnaud Roques + * + * Project Info: http://plantuml.com + * + * If you like this project or if you find it useful, you can support us at: + * + * http://plantuml.com/patreon (only 1$ per month!) + * http://plantuml.com/paypal + * + * This file is part of PlantUML. + * + * PlantUML is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PlantUML distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * + * Original Author: Arnaud Roques + * + * + */ +package net.sourceforge.plantuml.cucadiagram; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import net.sourceforge.plantuml.FontParam; +import net.sourceforge.plantuml.ISkinParam; +import net.sourceforge.plantuml.graphic.TextBlock; + +public class BodierMap implements Bodier { + + private final List rawBody = new ArrayList(); + private final Map map = new LinkedHashMap(); + private ILeaf leaf; + + public void muteClassToObject() { + throw new UnsupportedOperationException(); + } + + public BodierMap() { + } + + public void setLeaf(ILeaf leaf) { + if (leaf == null) { + throw new IllegalArgumentException(); + } + this.leaf = leaf; + + } + + public static String getLinkedEntry(String s) { + final Pattern p = Pattern.compile("(\\*-+\\>)"); + final Matcher m = p.matcher(s); + if (m.find()) { + return m.group(1); + } + return null; + } + + public void addFieldOrMethod(String s) { + if (s.contains("=>")) { + final int x = s.indexOf("=>"); + map.put(s.substring(0, x).trim(), s.substring(x + 2).trim()); + } else if (getLinkedEntry(s) != null) { + final String link = getLinkedEntry(s); + final int x = s.indexOf(link); + map.put(s.substring(0, x).trim(), "\0"); + } + } + + public List getMethodsToDisplay() { + throw new UnsupportedOperationException(); + } + + public List getFieldsToDisplay() { + throw new UnsupportedOperationException(); + } + + public boolean hasUrl() { + return false; + } + + public TextBlock getBody(FontParam fontParam, ISkinParam skinParam, final boolean showMethods, + final boolean showFields, Stereotype stereotype) { + return new TextBlockMap(fontParam, skinParam, map); + } + + public List getRawBody() { + return Collections.unmodifiableList(rawBody); + } + +} diff --git a/src/net/sourceforge/plantuml/cucadiagram/BodyEnhanced.java b/src/net/sourceforge/plantuml/cucadiagram/BodyEnhanced.java index a1fef7abd..032642187 100644 --- a/src/net/sourceforge/plantuml/cucadiagram/BodyEnhanced.java +++ b/src/net/sourceforge/plantuml/cucadiagram/BodyEnhanced.java @@ -166,7 +166,7 @@ public class BodyEnhanced extends AbstractTextBlock implements TextBlock, WithPo } members = new ArrayList(); final List allTree = buildAllTree(s, it); - final TextBlock bloc = Display.create(allTree).create(fontParam.getFontConfiguration(skinParam), + final TextBlock bloc = Display.create(allTree).create7(fontParam.getFontConfiguration(skinParam), align, skinParam, CreoleMode.FULL); blocks.add(bloc); } else { diff --git a/src/net/sourceforge/plantuml/cucadiagram/BodyEnhanced2.java b/src/net/sourceforge/plantuml/cucadiagram/BodyEnhanced2.java index e13d4e21b..4199147c4 100644 --- a/src/net/sourceforge/plantuml/cucadiagram/BodyEnhanced2.java +++ b/src/net/sourceforge/plantuml/cucadiagram/BodyEnhanced2.java @@ -124,7 +124,7 @@ public class BodyEnhanced2 extends AbstractTextBlock implements TextBlock { } private TextBlock getTextBlock(Display members2, StringBounder stringBounder) { - final TextBlock result = members2.create(titleConfig, align, spriteContainer, lineBreakStrategy); + final TextBlock result = members2.create9(titleConfig, align, spriteContainer, lineBreakStrategy); return result; } diff --git a/src/net/sourceforge/plantuml/cucadiagram/Display.java b/src/net/sourceforge/plantuml/cucadiagram/Display.java index ef1fa872a..0e235f473 100644 --- a/src/net/sourceforge/plantuml/cucadiagram/Display.java +++ b/src/net/sourceforge/plantuml/cucadiagram/Display.java @@ -446,33 +446,33 @@ public class Display implements Iterable { public TextBlock create(FontConfiguration fontConfiguration, HorizontalAlignment horizontalAlignment, ISkinSimple spriteContainer) { - return create(fontConfiguration, horizontalAlignment, spriteContainer, CreoleMode.FULL); + return create7(fontConfiguration, horizontalAlignment, spriteContainer, CreoleMode.FULL); } public TextBlock createWithNiceCreoleMode(FontConfiguration fontConfiguration, HorizontalAlignment horizontalAlignment, ISkinSimple spriteContainer) { - return create(fontConfiguration, horizontalAlignment, spriteContainer, defaultCreoleMode); + return create7(fontConfiguration, horizontalAlignment, spriteContainer, defaultCreoleMode); } - public TextBlock create(FontConfiguration fontConfiguration, HorizontalAlignment horizontalAlignment, + public TextBlock create7(FontConfiguration fontConfiguration, HorizontalAlignment horizontalAlignment, ISkinSimple spriteContainer, CreoleMode creoleMode) { - return create(fontConfiguration, horizontalAlignment, spriteContainer, LineBreakStrategy.NONE, creoleMode, null, - null); + return create0(fontConfiguration, horizontalAlignment, spriteContainer, LineBreakStrategy.NONE, creoleMode, + null, null); } - public TextBlock create(FontConfiguration fontConfiguration, HorizontalAlignment horizontalAlignment, + public TextBlock create8(FontConfiguration fontConfiguration, HorizontalAlignment horizontalAlignment, ISkinSimple spriteContainer, CreoleMode modeSimpleLine, LineBreakStrategy maxMessageSize) { - return create(fontConfiguration, horizontalAlignment, spriteContainer, maxMessageSize, modeSimpleLine, null, + return create0(fontConfiguration, horizontalAlignment, spriteContainer, maxMessageSize, modeSimpleLine, null, null); } - public TextBlock create(FontConfiguration fontConfiguration, HorizontalAlignment horizontalAlignment, + public TextBlock create9(FontConfiguration fontConfiguration, HorizontalAlignment horizontalAlignment, ISkinSimple spriteContainer, LineBreakStrategy maxMessageSize) { - return create(fontConfiguration, horizontalAlignment, spriteContainer, maxMessageSize, defaultCreoleMode, null, + return create0(fontConfiguration, horizontalAlignment, spriteContainer, maxMessageSize, defaultCreoleMode, null, null); } - public TextBlock create(FontConfiguration fontConfiguration, HorizontalAlignment horizontalAlignment, + public TextBlock create0(FontConfiguration fontConfiguration, HorizontalAlignment horizontalAlignment, ISkinSimple spriteContainer, LineBreakStrategy maxMessageSize, CreoleMode creoleMode, UFont fontForStereotype, HColor htmlColorForStereotype) { if (maxMessageSize == null) { @@ -528,8 +528,8 @@ public class Display implements Iterable { FontConfiguration stereotypeConfiguration) { final Sheet sheet = new CreoleParser(fontConfiguration, horizontalAlignment, spriteContainer, creoleMode, stereotypeConfiguration).createSheet(this); - final SheetBlock1 sheetBlock1 = new SheetBlock1(sheet, maxMessageSize, - spriteContainer == null ? 0 : spriteContainer.getPadding()); + final double padding = spriteContainer == null ? 0 : spriteContainer.getPadding(); + final SheetBlock1 sheetBlock1 = new SheetBlock1(sheet, maxMessageSize, padding); return new SheetBlock2(sheetBlock1, sheetBlock1, new UStroke(1.5)); } diff --git a/src/net/sourceforge/plantuml/cucadiagram/GroupRoot.java b/src/net/sourceforge/plantuml/cucadiagram/GroupRoot.java index b76d83e91..92fee6cd1 100644 --- a/src/net/sourceforge/plantuml/cucadiagram/GroupRoot.java +++ b/src/net/sourceforge/plantuml/cucadiagram/GroupRoot.java @@ -282,4 +282,8 @@ public class GroupRoot implements IGroup { public Ident getIdent() { return Ident.empty(); } + + public boolean isAloneAndUnlinked() { + throw new UnsupportedOperationException(); + } } diff --git a/src/net/sourceforge/plantuml/cucadiagram/HideOrShow2.java b/src/net/sourceforge/plantuml/cucadiagram/HideOrShow2.java index 066412306..f18d1097c 100644 --- a/src/net/sourceforge/plantuml/cucadiagram/HideOrShow2.java +++ b/src/net/sourceforge/plantuml/cucadiagram/HideOrShow2.java @@ -40,6 +40,11 @@ public class HideOrShow2 { private final String what; private final boolean show; + @Override + public String toString() { + return what + " (" + show + ")"; + } + private boolean isApplyable(ILeaf leaf) { if (what.startsWith("$")) { return isApplyableTag(leaf, what.substring(1)); @@ -47,11 +52,21 @@ public class HideOrShow2 { if (what.startsWith("<<") && what.endsWith(">>")) { return isApplyableStereotype(leaf, what.substring(2, what.length() - 2).trim()); } + if (what.equalsIgnoreCase("@unlinked")) { + return isApplyableUnlinked(leaf); + } final String fullName = leaf.getCodeGetName(); // System.err.println("fullName=" + fullName); return match(fullName, what); } + private boolean isApplyableUnlinked(ILeaf leaf) { + if (leaf.isAloneAndUnlinked()) { + return true; + } + return false; + } + private boolean isApplyableStereotype(ILeaf leaf, String pattern) { final Stereotype stereotype = leaf.getStereotype(); if (stereotype == null) { diff --git a/src/net/sourceforge/plantuml/cucadiagram/IEntity.java b/src/net/sourceforge/plantuml/cucadiagram/IEntity.java index 253dc29c8..19c9d82c0 100644 --- a/src/net/sourceforge/plantuml/cucadiagram/IEntity.java +++ b/src/net/sourceforge/plantuml/cucadiagram/IEntity.java @@ -94,5 +94,8 @@ public interface IEntity extends SpecificBackcolorable, Hideable, Removeable, Li public void addStereotag(Stereotag tag); public Set stereotags(); + + public boolean isAloneAndUnlinked(); + } diff --git a/src/net/sourceforge/plantuml/cucadiagram/LeafType.java b/src/net/sourceforge/plantuml/cucadiagram/LeafType.java index 53a149c01..f37e10ff4 100644 --- a/src/net/sourceforge/plantuml/cucadiagram/LeafType.java +++ b/src/net/sourceforge/plantuml/cucadiagram/LeafType.java @@ -41,7 +41,7 @@ public enum LeafType { EMPTY_PACKAGE, - ABSTRACT_CLASS, CLASS, INTERFACE, ANNOTATION, LOLLIPOP_FULL, LOLLIPOP_HALF, NOTE, TIPS, OBJECT, ASSOCIATION, ENUM, CIRCLE, + ABSTRACT_CLASS, CLASS, INTERFACE, ANNOTATION, LOLLIPOP_FULL, LOLLIPOP_HALF, NOTE, TIPS, OBJECT, MAP, ASSOCIATION, ENUM, CIRCLE, USECASE, diff --git a/src/net/sourceforge/plantuml/cucadiagram/MethodsOrFieldsArea.java b/src/net/sourceforge/plantuml/cucadiagram/MethodsOrFieldsArea.java index 0503a7b31..3a1a21978 100644 --- a/src/net/sourceforge/plantuml/cucadiagram/MethodsOrFieldsArea.java +++ b/src/net/sourceforge/plantuml/cucadiagram/MethodsOrFieldsArea.java @@ -166,7 +166,7 @@ public class MethodsOrFieldsArea extends AbstractTextBlock implements TextBlockW if (m.isStatic()) { config = config.underline(); } - TextBlock bloc = Display.getWithNewlines(s).create(config, align, skinParam, CreoleMode.SIMPLE_LINE, + TextBlock bloc = Display.getWithNewlines(s).create8(config, align, skinParam, CreoleMode.SIMPLE_LINE, skinParam.wrapWidth()); bloc = TextBlockUtils.fullInnerPosition(bloc, m.getDisplay(false)); return new TextBlockTracer(m, bloc); diff --git a/src/net/sourceforge/plantuml/cucadiagram/TextBlockMap.java b/src/net/sourceforge/plantuml/cucadiagram/TextBlockMap.java new file mode 100644 index 000000000..4e0fea1f0 --- /dev/null +++ b/src/net/sourceforge/plantuml/cucadiagram/TextBlockMap.java @@ -0,0 +1,210 @@ +/* ======================================================================== + * PlantUML : a free UML diagram generator + * ======================================================================== + * + * (C) Copyright 2009-2020, Arnaud Roques + * + * Project Info: http://plantuml.com + * + * If you like this project or if you find it useful, you can support us at: + * + * http://plantuml.com/patreon (only 1$ per month!) + * http://plantuml.com/paypal + * + * This file is part of PlantUML. + * + * PlantUML is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PlantUML distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * + * Original Author: Arnaud Roques + * + * + */ +package net.sourceforge.plantuml.cucadiagram; + +import java.awt.geom.Dimension2D; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import net.sourceforge.plantuml.Dimension2DDouble; +import net.sourceforge.plantuml.FontParam; +import net.sourceforge.plantuml.ISkinParam; +import net.sourceforge.plantuml.graphic.AbstractTextBlock; +import net.sourceforge.plantuml.graphic.FontConfiguration; +import net.sourceforge.plantuml.graphic.HorizontalAlignment; +import net.sourceforge.plantuml.graphic.StringBounder; +import net.sourceforge.plantuml.graphic.TextBlock; +import net.sourceforge.plantuml.graphic.TextBlockUtils; +import net.sourceforge.plantuml.svek.Ports; +import net.sourceforge.plantuml.svek.WithPorts; +import net.sourceforge.plantuml.ugraphic.UChangeBackColor; +import net.sourceforge.plantuml.ugraphic.UChangeColor; +import net.sourceforge.plantuml.ugraphic.UEllipse; +import net.sourceforge.plantuml.ugraphic.UGraphic; +import net.sourceforge.plantuml.ugraphic.ULine; +import net.sourceforge.plantuml.ugraphic.UShape; +import net.sourceforge.plantuml.ugraphic.UTranslate; +import net.sourceforge.plantuml.ugraphic.color.HColor; + +public class TextBlockMap extends AbstractTextBlock implements WithPorts { + + private final FontParam fontParam; + private final ISkinParam skinParam; + private final Map blocksMap = new LinkedHashMap(); + private final List keys = new ArrayList(); + private double totalWidth; + + public TextBlockMap(FontParam fontParam, ISkinParam skinParam, Map map) { + this.fontParam = fontParam; + this.skinParam = skinParam; + for (Map.Entry ent : map.entrySet()) { + final String key = ent.getKey(); + this.keys.add(key); + final String value = ent.getValue(); + final TextBlock block1 = getTextBlock(key); + final TextBlock block2 = getTextBlock(value); + this.blocksMap.put(block1, block2); + } + } + + public Ports getPorts(StringBounder stringBounder) { + final Ports ports = new Ports(); + int i = 0; + double y = 0; + for (Map.Entry ent : blocksMap.entrySet()) { + final TextBlock key = ent.getKey(); + final TextBlock value = ent.getValue(); + final double height = getHeightOfRow(stringBounder, key, value); + ports.add(keys.get(i), y, height); + y += height; + i++; + } + return ports; + } + + public Dimension2D calculateDimension(StringBounder stringBounder) { + return new Dimension2DDouble(getWidthColA(stringBounder) + getWidthColB(stringBounder), + getTotalHeight(stringBounder)); + } + + private double getWidthColA(StringBounder stringBounder) { + return getMaxWidth(stringBounder, blocksMap.keySet()); + } + + private double getWidthColB(StringBounder stringBounder) { + return getMaxWidth(stringBounder, blocksMap.values()); + } + + private double getMaxWidth(StringBounder stringBounder, Collection blocks) { + double width = 0; + for (TextBlock block : blocks) { + width = Math.max(width, block.calculateDimension(stringBounder).getWidth()); + } + return width; + } + + public void drawU(final UGraphic ug) { + final StringBounder stringBounder = ug.getStringBounder(); + final Dimension2D fullDim = calculateDimension(stringBounder); + final double trueWidth = Math.max(fullDim.getWidth(), totalWidth); + final double widthColA = getWidthColA(stringBounder); + final double widthColB = getWidthColB(stringBounder); + double y = 0; + for (Map.Entry ent : blocksMap.entrySet()) { + final TextBlock key = ent.getKey(); + final TextBlock value = ent.getValue(); + final UGraphic ugline = ug.apply(UTranslate.dy(y)); + ugline.draw(ULine.hline(trueWidth)); + final double heightOfRow = getHeightOfRow(stringBounder, key, value); + if (value instanceof Point) { +// final Dimension2D dimPoint = value.calculateDimension(stringBounder); +// final double xp = widthColA + (widthColB - dimPoint.getWidth()) / 2; +// final double yp = (heightOfRow - dimPoint.getHeight()) / 2; +// value.drawU(ugline.apply(new UTranslate(xp, yp))); + final double posColA = (trueWidth - key.calculateDimension(stringBounder).getWidth()) / 2; + key.drawU(ugline.apply(UTranslate.dx(posColA))); + } else { + final double posColA = (widthColA - key.calculateDimension(stringBounder).getWidth()) / 2; + key.drawU(ugline.apply(UTranslate.dx(posColA))); + value.drawU(ugline.apply(UTranslate.dx(widthColA))); + ugline.apply(UTranslate.dx(widthColA)).draw(ULine.vline(heightOfRow)); + } + y += heightOfRow; + } + // ug.apply(UTranslate.dx(widthColA)).draw(ULine.vline(fullDim.getHeight())); + } + + private double getTotalHeight(StringBounder stringBounder) { + double height = 0; + for (Map.Entry ent : blocksMap.entrySet()) { + final TextBlock key = ent.getKey(); + final TextBlock value = ent.getValue(); + height += getHeightOfRow(stringBounder, key, value); + } + return height; + } + + private double getHeightOfRow(StringBounder stringBounder, TextBlock key, TextBlock value) { + return Math.max(key.calculateDimension(stringBounder).getHeight(), + value.calculateDimension(stringBounder).getHeight()); + } + + private TextBlock getTextBlock(String key) { + if (key.equals("\0")) { + return new Point(getFontConfiguration().getColor()); + } + final Display display = Display.getWithNewlines(key); + TextBlock result = display.create(getFontConfiguration(), HorizontalAlignment.LEFT, skinParam); + result = TextBlockUtils.withMargin(result, 5, 2); + return result; + } + + static class Point extends AbstractTextBlock { + + private final HColor color; + + public Point(HColor color) { + this.color = color; + } + + public Dimension2D calculateDimension(StringBounder stringBounder) { + return new Dimension2DDouble(getDiameter(), getDiameter()); + } + + public void drawU(UGraphic ug) { + final UShape point = new UEllipse(getDiameter(), getDiameter()); + ug = ug.apply(new UChangeColor(color)).apply(new UChangeBackColor(color)); + ug.draw(point); + } + + private double getDiameter() { + return 7; + } + + } + + private FontConfiguration getFontConfiguration() { + return new FontConfiguration(skinParam, fontParam, null); + } + + public void setTotalWidth(double totalWidth) { + this.totalWidth = totalWidth; + } + +} diff --git a/src/net/sourceforge/plantuml/cucadiagram/dot/GraphvizUtils.java b/src/net/sourceforge/plantuml/cucadiagram/dot/GraphvizUtils.java index 6f5a99c25..863121799 100644 --- a/src/net/sourceforge/plantuml/cucadiagram/dot/GraphvizUtils.java +++ b/src/net/sourceforge/plantuml/cucadiagram/dot/GraphvizUtils.java @@ -46,6 +46,7 @@ import java.util.regex.Pattern; import net.sourceforge.plantuml.ISkinParam; import net.sourceforge.plantuml.Log; +import net.sourceforge.plantuml.OptionFlags; import net.sourceforge.plantuml.StringUtils; import net.sourceforge.plantuml.vizjs.GraphvizJs; import net.sourceforge.plantuml.vizjs.VizJsEngine; @@ -139,6 +140,14 @@ public class GraphvizUtils { return 4096; } + public static boolean getJavascriptUnsecure() { + final String env = getenv("PLANTUML_JAVASCRIPT_UNSECURE"); + if ("true".equalsIgnoreCase(env)) { + return true; + } + return OptionFlags.ALLOW_INCLUDE; + } + public static String getenvDefaultConfigFilename() { return getenv("PLANTUML_DEFAULT_CONFIG_FILENAME"); } diff --git a/src/net/sourceforge/plantuml/cucadiagram/entity/EntityFactory.java b/src/net/sourceforge/plantuml/cucadiagram/entity/EntityFactory.java index cf8794179..0e86ea839 100644 --- a/src/net/sourceforge/plantuml/cucadiagram/entity/EntityFactory.java +++ b/src/net/sourceforge/plantuml/cucadiagram/entity/EntityFactory.java @@ -50,6 +50,8 @@ import net.sourceforge.plantuml.ColorParam; import net.sourceforge.plantuml.ISkinParam; import net.sourceforge.plantuml.creole.CreoleMode; import net.sourceforge.plantuml.cucadiagram.Bodier; +import net.sourceforge.plantuml.cucadiagram.BodierImpl; +import net.sourceforge.plantuml.cucadiagram.BodierMap; import net.sourceforge.plantuml.cucadiagram.Code; import net.sourceforge.plantuml.cucadiagram.CucaDiagram; import net.sourceforge.plantuml.cucadiagram.Display; @@ -82,7 +84,7 @@ public final class EntityFactory { private final IGroup rootGroup = new GroupRoot(this); private final SuperGroup rootSuperGroup = new SuperGroup(rootGroup); - + private final List hides2; private final List removed; /* private */ final public CucaDiagram namespaceSeparator; @@ -216,7 +218,7 @@ public final class EntityFactory { if (entityType == null) { throw new IllegalArgumentException(); } - final Bodier bodier = new Bodier(entityType, hides); + final Bodier bodier = entityType == LeafType.MAP ? new BodierMap() : new BodierImpl(entityType, hides); final EntityImpl result = new EntityImpl(ident, code, this, bodier, parentContainer, entityType, namespaceSeparator, rawLayout); bodier.setLeaf(result); @@ -229,7 +231,7 @@ public final class EntityFactory { if (groupType == null) { throw new IllegalArgumentException(); } - final Bodier bodier = new Bodier(null, hides); + final Bodier bodier = new BodierImpl(null, hides); final EntityImpl result = new EntityImpl(ident, code, this, bodier, parentContainer, groupType, namespace, namespaceSeparator, rawLayout); if (Display.isNull(display) == false) { @@ -511,5 +513,4 @@ public final class EntityFactory { return parentContainer; } - } diff --git a/src/net/sourceforge/plantuml/cucadiagram/entity/EntityImpl.java b/src/net/sourceforge/plantuml/cucadiagram/entity/EntityImpl.java index fb8dd611e..0381f6085 100644 --- a/src/net/sourceforge/plantuml/cucadiagram/entity/EntityImpl.java +++ b/src/net/sourceforge/plantuml/cucadiagram/entity/EntityImpl.java @@ -661,6 +661,18 @@ final public class EntityImpl implements ILeaf, IGroup { return entityFactory.isRemoved(this); } + public boolean isAloneAndUnlinked() { + if (isGroup()) { + return false; + } + for (Link link : entityFactory.getLinks()) { + if (link.contains(this) && link.getType().isInvisible() == false) { + return false; + } + } + return true; + } + private int layer; public int getHectorLayer() { diff --git a/src/net/sourceforge/plantuml/descdiagram/command/CommandCreateElementFull.java b/src/net/sourceforge/plantuml/descdiagram/command/CommandCreateElementFull.java index 94608d321..f9a80895a 100644 --- a/src/net/sourceforge/plantuml/descdiagram/command/CommandCreateElementFull.java +++ b/src/net/sourceforge/plantuml/descdiagram/command/CommandCreateElementFull.java @@ -43,6 +43,7 @@ import net.sourceforge.plantuml.Url; import net.sourceforge.plantuml.UrlBuilder; import net.sourceforge.plantuml.UrlBuilder.ModeUrl; import net.sourceforge.plantuml.classdiagram.AbstractEntityDiagram; +import net.sourceforge.plantuml.classdiagram.command.CommandCreateClassMultilines; import net.sourceforge.plantuml.command.CommandExecutionResult; import net.sourceforge.plantuml.command.SingleLineCommand2; import net.sourceforge.plantuml.command.regex.IRegex; @@ -57,6 +58,7 @@ import net.sourceforge.plantuml.cucadiagram.IEntity; import net.sourceforge.plantuml.cucadiagram.ILeaf; import net.sourceforge.plantuml.cucadiagram.Ident; import net.sourceforge.plantuml.cucadiagram.LeafType; +import net.sourceforge.plantuml.cucadiagram.Stereotag; import net.sourceforge.plantuml.cucadiagram.Stereotype; import net.sourceforge.plantuml.descdiagram.DescriptionDiagram; import net.sourceforge.plantuml.graphic.USymbol; @@ -120,6 +122,8 @@ public class CommandCreateElementFull extends SingleLineCommand2\\>)") // )), // RegexLeaf.spaceZeroOrMore(), // + new RegexLeaf("TAGS", Stereotag.pattern() + "?"), // + RegexLeaf.spaceZeroOrMore(), // new RegexLeaf("URL", "(" + UrlBuilder.getRegexp() + ")?"), // RegexLeaf.spaceZeroOrMore(), // color().getRegex(), RegexLeaf.end()); @@ -215,6 +219,7 @@ public class CommandCreateElementFull extends SingleLineCommand2Coronavirus: Why You Must Act Now", " ", - "Please use the flashcode or go to", "https://plantuml/coronavirus", " ", - "If you live in UK, Europe, North & South America, Iran, Japan, Korea...", - " please apply social distancing right now!", " ", "The coronavirus is coming to you.", - "It’s coming at an exponential speed: gradually, and then suddenly.", - "It’s a matter of days. Maybe a week or two.", - "When it does, your healthcare system will be overwhelmed.", - "Your fellow citizens will be treated in the hallways.", - "Exhausted healthcare workers will break down. Some will die.", - "They will have to decide which patient gets the oxygen and which one dies.", - "The only way to prevent this is social distancing today. Not tomorrow. Today.", - "That means keeping as many people home as possible, starting now.", " "); - - final UFont font = UFont.sansSerif(14); - final FontConfiguration fc = new FontConfiguration(font, HColorUtils.BLACK, HColorUtils.BLACK, false); - final TextBlock text = TextBlockUtils - .withMargin(disp.create(fc, HorizontalAlignment.LEFT, new SpriteContainerEmpty()), 10, 0); - final TextBlock result; - if (qrcode == null) { - result = text; - } else { - final UImage qr = new UImage(qrcode).scaleNearestNeighbor(3); - result = TextBlockUtils.mergeLR(text, TextBlockUtils.fromUImage(qr), VerticalAlignment.CENTER); - } - return TextBlockUtils.addBackcolor(result, backColor); - - } - private TextBlockBackcolored getMessagePatreon() { final UImage message = new UImage(PSystemVersion.getTime01()); final Color back = new Color(message.getImage().getRGB(0, 0)); diff --git a/src/net/sourceforge/plantuml/graphic/GraphicStrings.java b/src/net/sourceforge/plantuml/graphic/GraphicStrings.java index fd8946f9f..82fbec933 100644 --- a/src/net/sourceforge/plantuml/graphic/GraphicStrings.java +++ b/src/net/sourceforge/plantuml/graphic/GraphicStrings.java @@ -145,7 +145,7 @@ public class GraphicStrings extends AbstractTextBlock implements IEntityImage { return new TextBlockRaw(strings, fontConfiguration); } else { - return display.create(fontConfiguration, HorizontalAlignment.LEFT, new SpriteContainerEmpty(), mode); + return display.create7(fontConfiguration, HorizontalAlignment.LEFT, new SpriteContainerEmpty(), mode); } } diff --git a/src/net/sourceforge/plantuml/graphic/QuoteUtils.java b/src/net/sourceforge/plantuml/graphic/QuoteUtils.java index 7188b733d..582853597 100644 --- a/src/net/sourceforge/plantuml/graphic/QuoteUtils.java +++ b/src/net/sourceforge/plantuml/graphic/QuoteUtils.java @@ -248,7 +248,15 @@ public class QuoteUtils { "Guvf vf gur jnl", "Cngvrapr lbh zhfg unir zl lbhat cnqnjna", "V gubhtug lbh jrer ba zl fvqr", "Qba'g cnavp. Abar bs lbh cnavp. Nofbyhgryl ab ernfba gb cnavp.", "Gung jnf hacyrnfnag. V'z fbeel lbh unq gb frr gung.", "Gur neg bs fvzcyvpvgl vf n chmmyr bs pbzcyrkvgl.", - "Xrrc pnyz naq fgnl ng ubzr"); + "Xrrc pnyz naq fgnl ng ubzr", + "Gur terngrfg fubegpbzvat bs gur uhzna enpr vf bhe vanovyvgl gb haqrefgnaq gur rkcbaragvny shapgvba", + "Vs gur Znegvnaf vainqrq rnegu, bhe svefg erfcbafr jbhyq or gb ybjre vagrerfg engrf.", + "Rnfl gvzrf qba'g sbetr punenpgre. Vg'f gur gbhtu gvzrf gung sbetr punenpgre", + "Fbzrgvzrf va beqre gb qb gur evtug guvat, lbh unir gb qb gur jebat guvat.", + "Gur fgeratgu bs WninFpevcg vf gung lbh pna qb nalguvat. Gur jrnxarff vf gung lbh jvyy.", + "Vg'f tbvat gb qvfnccrne. Bar qnl vg'f yvxr n zvenpyr, vg jvyy qvfnccrne. Cebonoyl ol Rnfgre, BX?", + "V'ir sryg vg jnf n oht ybat orsber vg jnf pnyyrq n oht", + "Yrg'f frr guvf nf n pbyyngreny qnzntr sebz fbzr ureq vzzhavgl pybja'f gurbel."); private QuoteUtils() { } diff --git a/src/net/sourceforge/plantuml/graphic/SpecialText.java b/src/net/sourceforge/plantuml/graphic/SpecialText.java new file mode 100644 index 000000000..eb59bd65d --- /dev/null +++ b/src/net/sourceforge/plantuml/graphic/SpecialText.java @@ -0,0 +1,67 @@ +/* ======================================================================== + * PlantUML : a free UML diagram generator + * ======================================================================== + * + * (C) Copyright 2009-2020, Arnaud Roques + * + * Project Info: http://plantuml.com + * + * If you like this project or if you find it useful, you can support us at: + * + * http://plantuml.com/patreon (only 1$ per month!) + * http://plantuml.com/paypal + * + * This file is part of PlantUML. + * + * PlantUML is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PlantUML distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * + * Original Author: Arnaud Roques + * + * + */ +package net.sourceforge.plantuml.graphic; + +import java.awt.geom.Dimension2D; + +import net.sourceforge.plantuml.ugraphic.UEmpty; +import net.sourceforge.plantuml.ugraphic.UGraphic; +import net.sourceforge.plantuml.ugraphic.UShapeIgnorableForCompression; +import net.sourceforge.plantuml.ugraphic.UTranslate; +import net.sourceforge.plantuml.ugraphic.comp.CompressionMode; + +public class SpecialText implements UShapeIgnorableForCompression { + + private final TextBlock title; + + public SpecialText(TextBlock title) { + this.title = title; + } + + public boolean isIgnoreForCompressionOn(CompressionMode mode) { + return true; + } + + public void drawWhenCompressed(UGraphic ug, CompressionMode mode) { + final Dimension2D dim = title.calculateDimension(ug.getStringBounder()); + ug.apply(UTranslate.dx(dim.getWidth())).draw(new UEmpty(1, 1)); + } + + public final TextBlock getTitle() { + return title; + } + +} \ No newline at end of file diff --git a/src/net/sourceforge/plantuml/graphic/TextBlockTitle.java b/src/net/sourceforge/plantuml/graphic/TextBlockTitle.java index 6926d9a2e..d141d6464 100644 --- a/src/net/sourceforge/plantuml/graphic/TextBlockTitle.java +++ b/src/net/sourceforge/plantuml/graphic/TextBlockTitle.java @@ -58,7 +58,7 @@ public class TextBlockTitle implements TextBlock { throw new IllegalArgumentException(); } final LineBreakStrategy lineBreak = LineBreakStrategy.NONE; - textBlock = stringsToDisplay.create(font, HorizontalAlignment.CENTER, spriteContainer, lineBreak, + textBlock = stringsToDisplay.create0(font, HorizontalAlignment.CENTER, spriteContainer, lineBreak, CreoleMode.FULL, null, null); } diff --git a/src/net/sourceforge/plantuml/graphic/USymbolFrame.java b/src/net/sourceforge/plantuml/graphic/USymbolFrame.java index 5577c1ebf..d4fa5c5cb 100644 --- a/src/net/sourceforge/plantuml/graphic/USymbolFrame.java +++ b/src/net/sourceforge/plantuml/graphic/USymbolFrame.java @@ -38,6 +38,7 @@ package net.sourceforge.plantuml.graphic; import java.awt.geom.Dimension2D; import net.sourceforge.plantuml.Dimension2DDouble; +import net.sourceforge.plantuml.ugraphic.Shadowable; import net.sourceforge.plantuml.ugraphic.UChangeBackColor; import net.sourceforge.plantuml.ugraphic.UGraphic; import net.sourceforge.plantuml.ugraphic.UGraphicStencil; @@ -55,7 +56,8 @@ class USymbolFrame extends USymbol { private void drawFrame(UGraphic ug, double width, double height, Dimension2D dimTitle, boolean shadowing, double roundCorner) { - final URectangle shape = new URectangle(width, height).rounded(roundCorner).ignoreForCompression(); + final Shadowable shape = new URectangle(width, height).rounded(roundCorner).ignoreForCompressionOnX() + .ignoreForCompressionOnY(); if (shadowing) { shape.setDeltaShadow(3.0); } @@ -74,6 +76,7 @@ class USymbolFrame extends USymbol { final double textHeight = getYpos(dimTitle); final UPath polygon = new UPath(); + polygon.setIgnoreForCompressionOnX(); polygon.moveTo(textWidth, 1); polygon.lineTo(textWidth, textHeight - cornersize); @@ -104,8 +107,8 @@ class USymbolFrame extends USymbol { final Dimension2D dim = calculateDimension(ug.getStringBounder()); ug = UGraphicStencil.create(ug, getRectangleStencil(dim), new UStroke()); ug = symbolContext.apply(ug); - drawFrame(ug, dim.getWidth(), dim.getHeight(), new Dimension2DDouble(0, 0), - symbolContext.isShadowing(), symbolContext.getRoundCorner()); + drawFrame(ug, dim.getWidth(), dim.getHeight(), new Dimension2DDouble(0, 0), symbolContext.isShadowing(), + symbolContext.getRoundCorner()); final Margin margin = getMargin(); final TextBlock tb = TextBlockUtils.mergeTB(stereotype, label, HorizontalAlignment.CENTER); tb.drawU(ug.apply(new UTranslate(margin.getX1(), margin.getY1()))); @@ -121,7 +124,8 @@ class USymbolFrame extends USymbol { @Override public TextBlock asBig(final TextBlock title, HorizontalAlignment labelAlignment, final TextBlock stereotype, - final double width, final double height, final SymbolContext symbolContext, final HorizontalAlignment stereoAlignment) { + final double width, final double height, final SymbolContext symbolContext, + final HorizontalAlignment stereoAlignment) { return new AbstractTextBlock() { public void drawU(UGraphic ug) { @@ -129,9 +133,17 @@ class USymbolFrame extends USymbol { final Dimension2D dim = calculateDimension(stringBounder); ug = symbolContext.apply(ug); final Dimension2D dimTitle = title.calculateDimension(stringBounder); - drawFrame(ug, dim.getWidth(), dim.getHeight(), dimTitle, symbolContext.isShadowing(), + final double widthFull = dim.getWidth(); + drawFrame(ug, widthFull, dim.getHeight(), dimTitle, symbolContext.isShadowing(), symbolContext.getRoundCorner()); - title.drawU(ug.apply(new UTranslate(3, 1))); + final double widthTitle = title.calculateDimension(stringBounder).getWidth(); + + // Temporary hack... + if (widthFull - widthTitle < 25) { + title.drawU(ug.apply(new UTranslate(3, 1))); + } else { + ug.apply(new UTranslate(3, 1)).draw(new SpecialText(title)); + } final Dimension2D dimStereo = stereotype.calculateDimension(stringBounder); final double posStereo = (width - dimStereo.getWidth()) / 2; @@ -145,6 +157,29 @@ class USymbolFrame extends USymbol { }; } +// static class Interceptor extends UGraphicDelegator { +// +// public Interceptor(UGraphic ug) { +// super(ug); +// } +// +// @Override +// public void draw(UShape shape) { +// if (shape instanceof SpecialText) { +// final SpecialText specialText = (SpecialText) shape; +// specialText.title.drawU(getUg()); +// // System.err.println("getug=" + getUg()); +// return; +// } +// super.draw(shape); +// } +// +// public UGraphic apply(UChange change) { +// return new Interceptor(getUg().apply(change)); +// } +// +// } + @Override public boolean manageHorizontalLine() { return true; diff --git a/src/net/sourceforge/plantuml/jcckit/PSystemJcckit.java b/src/net/sourceforge/plantuml/jcckit/PSystemJcckit.java new file mode 100644 index 000000000..e2872adbf --- /dev/null +++ b/src/net/sourceforge/plantuml/jcckit/PSystemJcckit.java @@ -0,0 +1,88 @@ +/* ======================================================================== + * PlantUML : a free UML diagram generator + * ======================================================================== + * + * (C) Copyright 2009-2020, Arnaud Roques + * + * Project Info: http://plantuml.com + * + * If you like this project or if you find it useful, you can support us at: + * + * http://plantuml.com/patreon (only 1$ per month!) + * http://plantuml.com/paypal + * + * This file is part of PlantUML. + * + * PlantUML is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PlantUML distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * + * Original Author: Arnaud Roques + * + * + */ +package net.sourceforge.plantuml.jcckit; + +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Properties; + +import javax.imageio.ImageIO; + +import jcckit.GraphicsPlotCanvas; +import jcckit.data.DataPlot; +import jcckit.util.ConfigParameters; +import jcckit.util.PropertiesBasedConfigData; +import net.sourceforge.plantuml.AbstractPSystem; +import net.sourceforge.plantuml.FileFormatOption; +import net.sourceforge.plantuml.api.ImageDataSimple; +import net.sourceforge.plantuml.core.DiagramDescription; +import net.sourceforge.plantuml.core.ImageData; + +public class PSystemJcckit extends AbstractPSystem { + + private final PropertiesBasedConfigData prop; + private final int width; + private final int height; + + public PSystemJcckit(Properties p, int width, int height) { + this.width = width; + this.height = height; + prop = new PropertiesBasedConfigData(p); + } + + @Override + final protected ImageData exportDiagramNow(OutputStream os, int num, FileFormatOption fileFormat, long seed) + throws IOException { + BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + + // Sets up a Graphics2DPlotCanvas + ConfigParameters config = new ConfigParameters(prop); + GraphicsPlotCanvas plotCanvas = new GraphicsPlotCanvas(config, image); + plotCanvas.connect(DataPlot.create(config)); + plotCanvas.paint(); + + // Writes the off-screen image into a PNG file + ImageIO.write(image, "png", os); + + return new ImageDataSimple(width, height); + } + + public DiagramDescription getDescription() { + return new DiagramDescription("(JCCKit)"); + } + +} diff --git a/src/net/sourceforge/plantuml/jcckit/PSystemJcckitFactory.java b/src/net/sourceforge/plantuml/jcckit/PSystemJcckitFactory.java new file mode 100644 index 000000000..8adae1979 --- /dev/null +++ b/src/net/sourceforge/plantuml/jcckit/PSystemJcckitFactory.java @@ -0,0 +1,120 @@ +/* ======================================================================== + * PlantUML : a free UML diagram generator + * ======================================================================== + * + * (C) Copyright 2009-2020, Arnaud Roques + * + * Project Info: http://plantuml.com + * + * If you like this project or if you find it useful, you can support us at: + * + * http://plantuml.com/patreon (only 1$ per month!) + * http://plantuml.com/paypal + * + * This file is part of PlantUML. + * + * PlantUML is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PlantUML distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * + * Original Author: Arnaud Roques + * + * + */ +package net.sourceforge.plantuml.jcckit; + +import java.io.IOException; +import java.io.StringReader; +import java.util.Properties; + +import net.sourceforge.plantuml.BackSlash; +import net.sourceforge.plantuml.Log; +import net.sourceforge.plantuml.StringUtils; +import net.sourceforge.plantuml.command.PSystemBasicFactory; +import net.sourceforge.plantuml.command.regex.Matcher2; +import net.sourceforge.plantuml.command.regex.MyPattern; +import net.sourceforge.plantuml.command.regex.Pattern2; +import net.sourceforge.plantuml.core.DiagramType; + +public class PSystemJcckitFactory extends PSystemBasicFactory { + + private StringBuilder data; + private int width; + private int height; + + public PSystemJcckitFactory(DiagramType diagramType) { + super(diagramType); + } + + public PSystemJcckit init(String startLine) { + this.data = null; + this.width = 640; + this.height = 400; + if (getDiagramType() == DiagramType.UML) { + return null; + } else if (getDiagramType() == DiagramType.JCCKIT) { + extractDimension(startLine); + data = new StringBuilder(); + return createSystem(); + } else { + throw new IllegalStateException(getDiagramType().name()); + } + + } + + private void extractDimension(String startLine) { + final Pattern2 p = MyPattern.cmpile("\\((\\d+),(\\d+)\\)"); + final Matcher2 m = p.matcher(startLine); + final boolean ok = m.find(); + if (ok) { + width = Integer.parseInt(m.group(1)); + height = Integer.parseInt(m.group(2)); + } + } + + String getDimension() { + return "" + width + "-" + height; + } + + private PSystemJcckit createSystem() { + final Properties p = new Properties(); + try { + p.load(new StringReader(data.toString())); + // For Java 1.5 + // p.load(new ByteArrayInputStream(data.toString().getBytes("ISO-8859-1"))); + } catch (IOException e) { + Log.error("Error " + e); + e.printStackTrace(); + return null; + } + return new PSystemJcckit(p, width, height); + } + + @Override + public PSystemJcckit executeLine(PSystemJcckit system, String line) { + if (system == null && line.startsWith("jcckit")) { + data = new StringBuilder(); + extractDimension(line); + return createSystem(); + } + if (data == null) { + return null; + } + data.append(StringUtils.trin(line)); + data.append(BackSlash.NEWLINE); + return createSystem(); + } + +} diff --git a/src/net/sourceforge/plantuml/objectdiagram/ObjectDiagramFactory.java b/src/net/sourceforge/plantuml/objectdiagram/ObjectDiagramFactory.java deleted file mode 100644 index e0ccf25a6..000000000 --- a/src/net/sourceforge/plantuml/objectdiagram/ObjectDiagramFactory.java +++ /dev/null @@ -1,115 +0,0 @@ -/* ======================================================================== - * PlantUML : a free UML diagram generator - * ======================================================================== - * - * (C) Copyright 2009-2020, Arnaud Roques - * - * Project Info: http://plantuml.com - * - * If you like this project or if you find it useful, you can support us at: - * - * http://plantuml.com/patreon (only 1$ per month!) - * http://plantuml.com/paypal - * - * This file is part of PlantUML. - * - * PlantUML is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * PlantUML distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public - * License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. - * - * - * Original Author: Arnaud Roques - * - * - */ -package net.sourceforge.plantuml.objectdiagram; - -import java.util.ArrayList; -import java.util.List; - -import net.sourceforge.plantuml.ISkinSimple; -import net.sourceforge.plantuml.UmlDiagramType; -import net.sourceforge.plantuml.classdiagram.command.CommandLinkClass; -import net.sourceforge.plantuml.classdiagram.command.CommandUrl; -import net.sourceforge.plantuml.command.Command; -import net.sourceforge.plantuml.command.CommandEndPackage; -import net.sourceforge.plantuml.command.CommandFootboxIgnored; -import net.sourceforge.plantuml.command.CommandPackage; -import net.sourceforge.plantuml.command.CommandPage; -import net.sourceforge.plantuml.command.CommandRankDir; -import net.sourceforge.plantuml.command.UmlDiagramFactory; -import net.sourceforge.plantuml.command.note.CommandFactoryNote; -import net.sourceforge.plantuml.command.note.CommandFactoryNoteOnEntity; -import net.sourceforge.plantuml.command.note.CommandFactoryNoteOnLink; -import net.sourceforge.plantuml.command.regex.RegexLeaf; -import net.sourceforge.plantuml.objectdiagram.command.CommandAddData; -import net.sourceforge.plantuml.objectdiagram.command.CommandCreateEntityObject; -import net.sourceforge.plantuml.objectdiagram.command.CommandCreateEntityObjectMultilines; - -public class ObjectDiagramFactory extends UmlDiagramFactory { - - private final ISkinSimple skinParam; - - public ObjectDiagramFactory(ISkinSimple skinParam) { - this.skinParam = skinParam; - } - - @Override - protected List createCommands() { - - final List cmds = new ArrayList(); - cmds.add(new CommandFootboxIgnored()); - - addCommonCommands1(cmds); - cmds.add(new CommandRankDir()); - cmds.add(new CommandPage()); - cmds.add(new CommandAddData()); - cmds.add(new CommandLinkClass(UmlDiagramType.OBJECT)); - // - cmds.add(new CommandCreateEntityObject()); - final CommandFactoryNote factoryNoteCommand = new CommandFactoryNote(); - - cmds.add(factoryNoteCommand.createSingleLine()); - cmds.add(new CommandPackage()); - cmds.add(new CommandEndPackage()); - // addCommand(new CommandNamespace()); - // addCommand(new CommandEndNamespace()); - // addCommand(new CommandStereotype()); - // - // addCommand(new CommandImport()); - final CommandFactoryNoteOnEntity factoryNoteOnEntityCommand = new CommandFactoryNoteOnEntity("object", - new RegexLeaf("ENTITY", "([\\p{L}0-9_.]+|[%g][^%g]+[%g])")); - cmds.add(factoryNoteOnEntityCommand.createSingleLine()); - - cmds.add(new CommandUrl()); - - cmds.add(factoryNoteCommand.createMultiLine(false)); - cmds.add(factoryNoteOnEntityCommand.createMultiLine(true)); - cmds.add(factoryNoteOnEntityCommand.createMultiLine(false)); - cmds.add(new CommandCreateEntityObjectMultilines()); - - final CommandFactoryNoteOnLink factoryNoteOnLinkCommand = new CommandFactoryNoteOnLink(); - cmds.add(factoryNoteOnLinkCommand.createSingleLine()); - cmds.add(factoryNoteOnLinkCommand.createMultiLine(false)); - - // addCommand(new CommandNoopClass()); - return cmds; - - } - - @Override - public ObjectDiagram createEmptyDiagram() { - return new ObjectDiagram(skinParam); - } -} diff --git a/src/net/sourceforge/plantuml/objectdiagram/command/CommandAddData.java b/src/net/sourceforge/plantuml/objectdiagram/command/CommandAddData.java index b9f3f8d28..05eafc91a 100644 --- a/src/net/sourceforge/plantuml/objectdiagram/command/CommandAddData.java +++ b/src/net/sourceforge/plantuml/objectdiagram/command/CommandAddData.java @@ -73,7 +73,7 @@ public class CommandAddData extends SingleLineCommand2 0 && VisibilityModifier.isVisibilityCharacter(field)) { diagram.setVisibilityModifierPresent(true); } - entity.getBodier().addFieldOrMethod(field, entity); + entity.getBodier().addFieldOrMethod(field); return CommandExecutionResult.ok(); } } diff --git a/src/net/sourceforge/plantuml/objectdiagram/command/CommandCreateEntityObjectMultilines.java b/src/net/sourceforge/plantuml/objectdiagram/command/CommandCreateEntityObjectMultilines.java index 98d0f9625..3295b3a82 100644 --- a/src/net/sourceforge/plantuml/objectdiagram/command/CommandCreateEntityObjectMultilines.java +++ b/src/net/sourceforge/plantuml/objectdiagram/command/CommandCreateEntityObjectMultilines.java @@ -99,7 +99,7 @@ public class CommandCreateEntityObjectMultilines extends CommandMultilines2 { + + public CommandCreateMap() { + super(getRegexConcat(), MultilinesStrategy.REMOVE_STARTING_QUOTE); + } + + private static IRegex getRegexConcat() { + return RegexConcat.build(CommandCreateMap.class.getName(), RegexLeaf.start(), // + new RegexLeaf("TYPE", "map"), // + RegexLeaf.spaceOneOrMore(), // + new RegexLeaf("NAME", "(?:[%g]([^%g]+)[%g][%s]+as[%s]+)?([\\p{L}0-9_.]+)"), // + RegexLeaf.spaceZeroOrMore(), // + new RegexLeaf("STEREO", "(\\<\\<.+\\>\\>)?"), // + RegexLeaf.spaceZeroOrMore(), // + new RegexLeaf("URL", "(" + UrlBuilder.getRegexp() + ")?"), // + RegexLeaf.spaceZeroOrMore(), // + ColorParser.exp1(), // + RegexLeaf.spaceZeroOrMore(), // + new RegexLeaf("\\{"), // + RegexLeaf.end()); + } + + @Override + public String getPatternEnd() { + return "(?i)^[%s]*\\}[%s]*$"; + } + + @Override + protected CommandExecutionResult executeNow(AbstractClassOrObjectDiagram diagram, BlocLines lines) { + lines = lines.trim(true); + final RegexResult line0 = getStartingPattern().matcher(lines.getFirst499().getTrimmed().getString()); + final IEntity entity1 = executeArg0(diagram, line0); + if (entity1 == null) { + return CommandExecutionResult.error("No such entity"); + } + lines = lines.subExtract(1, 1); + for (StringLocated sl : lines) { + final String line = sl.getString(); + assert line.length() > 0; + entity1.getBodier().addFieldOrMethod(line); + if (BodierMap.getLinkedEntry(line) != null) { + final String linkStr = BodierMap.getLinkedEntry(line); + final int x = line.indexOf(linkStr); + final String key = line.substring(0, x).trim(); + final String dest = line.substring(x + linkStr.length()).trim(); + final Ident ident2 = diagram.buildLeafIdentSpecial(dest); + final ILeaf entity2 = diagram.getEntityFactory().getLeafStrict(ident2); + final LinkType linkType = new LinkType(LinkDecor.ARROW, LinkDecor.NONE); + final int length = linkStr.length() - 2; + final Link link = new Link(entity1, entity2, linkType, Display.NULL, length, + diagram.getSkinParam().getCurrentStyleBuilder()); + link.setPortMembers(key, null); + diagram.addLink(link); + } + } + return CommandExecutionResult.ok(); + } + + private IEntity executeArg0(AbstractClassOrObjectDiagram diagram, RegexResult line0) { + final String name = line0.get("NAME", 1); + final Ident ident = diagram.buildLeafIdent(name); + final Code code = diagram.V1972() ? ident : diagram.buildCode(name); + final String display = line0.get("NAME", 0); + final String stereotype = line0.get("STEREO", 0); + final boolean leafExist = diagram.V1972() ? diagram.leafExistSmart(ident) : diagram.leafExist(code); + if (leafExist) { + return diagram.getOrCreateLeaf(diagram.buildLeafIdent(name), code, LeafType.MAP, null); + } + final IEntity entity = diagram.createLeaf(ident, code, Display.getWithNewlines(display), LeafType.MAP, null); + if (stereotype != null) { + entity.setStereotype(new Stereotype(stereotype, diagram.getSkinParam().getCircledCharacterRadius(), + diagram.getSkinParam().getFont(null, false, FontParam.CIRCLED_CHARACTER), + diagram.getSkinParam().getIHtmlColorSet())); + } + entity.setSpecificColorTOBEREMOVED(ColorType.BACK, + diagram.getSkinParam().getIHtmlColorSet().getColorIfValid(line0.get("COLOR", 0))); + return entity; + } + +} diff --git a/src/net/sourceforge/plantuml/project/GanttDiagram.java b/src/net/sourceforge/plantuml/project/GanttDiagram.java index adc235108..840f8d568 100644 --- a/src/net/sourceforge/plantuml/project/GanttDiagram.java +++ b/src/net/sourceforge/plantuml/project/GanttDiagram.java @@ -181,7 +181,7 @@ public class GanttDiagram extends TitledDiagram implements Subject { } private boolean isHidden(Task task) { - if (printStart == null) { + if (printStart == null || task instanceof TaskSeparator) { return false; } if (task.getEnd().compareTo(min) < 0) { diff --git a/src/net/sourceforge/plantuml/sequencediagram/teoz/AbstractTile.java b/src/net/sourceforge/plantuml/sequencediagram/teoz/AbstractTile.java index ac13d3570..72f1e4b58 100644 --- a/src/net/sourceforge/plantuml/sequencediagram/teoz/AbstractTile.java +++ b/src/net/sourceforge/plantuml/sequencediagram/teoz/AbstractTile.java @@ -51,15 +51,15 @@ public abstract class AbstractTile implements Tile { return result; } - public Tile matchAnchorV1(String anchor) { + public boolean matchAnchorV1(String anchor) { final Event event = this.getEvent(); if (event instanceof AbstractMessage) { final AbstractMessage msg = (AbstractMessage) event; if (anchor.equals(msg.getAnchor())) { - return this; + return true; } } - return null; + return false; } diff --git a/src/net/sourceforge/plantuml/sequencediagram/teoz/GroupingTile.java b/src/net/sourceforge/plantuml/sequencediagram/teoz/GroupingTile.java index 6b7be4fb9..a1d74c4c8 100644 --- a/src/net/sourceforge/plantuml/sequencediagram/teoz/GroupingTile.java +++ b/src/net/sourceforge/plantuml/sequencediagram/teoz/GroupingTile.java @@ -87,17 +87,6 @@ public class GroupingTile extends AbstractTile implements TileWithCallbackY { return 0; } - @Override - public Tile matchAnchorV1(String anchor) { - for (Tile tile : tiles) { - final Tile result = tile.matchAnchorV1(anchor); - if (result != null) { - return result; - } - } - return super.matchAnchorV1(anchor); - } - public GroupingTile(Iterator it, GroupingStart start, TileArguments tileArgumentsBachColorChanged, TileArguments tileArgumentsOriginal) { final StringBounder stringBounder = tileArgumentsOriginal.getStringBounder(); @@ -236,14 +225,16 @@ public class GroupingTile extends AbstractTile implements TileWithCallbackY { } public static double fillPositionelTiles(StringBounder stringBounder, double y, List tiles, - final List positionedTiles) { + final List local, List full) { for (Tile tile : mergeParallel(tiles)) { - positionedTiles.add(new YPositionedTile(tile, y)); + final YPositionedTile ytile = new YPositionedTile(tile, y); + local.add(ytile); + full.add(ytile); if (tile instanceof GroupingTile) { final GroupingTile groupingTile = (GroupingTile) tile; final double headerHeight = groupingTile.getHeaderHeight(stringBounder); - fillPositionelTiles(stringBounder, y + headerHeight, groupingTile.tiles, - new ArrayList()); + final ArrayList local2 = new ArrayList(); + fillPositionelTiles(stringBounder, y + headerHeight, groupingTile.tiles, local2, full); } y += tile.getPreferredHeight(stringBounder); } diff --git a/src/net/sourceforge/plantuml/sequencediagram/teoz/PlayingSpace.java b/src/net/sourceforge/plantuml/sequencediagram/teoz/PlayingSpace.java index 9215b0c2b..8986a1bc7 100644 --- a/src/net/sourceforge/plantuml/sequencediagram/teoz/PlayingSpace.java +++ b/src/net/sourceforge/plantuml/sequencediagram/teoz/PlayingSpace.java @@ -110,16 +110,17 @@ public class PlayingSpace implements Bordered { private double drawUInternal(UGraphic ug, boolean trace) { final StringBounder stringBounder = ug.getStringBounder(); - final List positionedTiles = new ArrayList(); - final double y = GroupingTile.fillPositionelTiles(stringBounder, startingY, tiles, positionedTiles); - for (YPositionedTile tile : positionedTiles) { + final List local = new ArrayList(); + final List full = new ArrayList(); + final double y = GroupingTile.fillPositionelTiles(stringBounder, startingY, tiles, local, full); + for (YPositionedTile tile : local) { tile.drawInArea(ug); } for (LinkAnchor linkAnchor : linkAnchors) { - final YPositionedTile tile1 = getFromAnchor(positionedTiles, linkAnchor.getAnchor1()); - final YPositionedTile tile2 = getFromAnchor(positionedTiles, linkAnchor.getAnchor2()); - if (tile1 != null && tile2 != null) { - linkAnchor.drawAnchor(ug, tile1, tile2, skinParam); + final YPositionedTile ytile1 = getFromAnchor(full, linkAnchor.getAnchor1()); + final YPositionedTile ytile2 = getFromAnchor(full, linkAnchor.getAnchor2()); + if (ytile1 != null && ytile2 != null) { + linkAnchor.drawAnchor(ug, ytile1, ytile2, skinParam); } } // System.err.println("MainTile::drawUInternal finalY=" + y); @@ -127,9 +128,10 @@ public class PlayingSpace implements Bordered { } private YPositionedTile getFromAnchor(List positionedTiles, String anchor) { - for (YPositionedTile tile : positionedTiles) { - if (tile.matchAnchorV2(anchor)) { - return tile; + for (YPositionedTile ytile : positionedTiles) { + final boolean matchAnchorV2 = ytile.matchAnchorV2(anchor); + if (matchAnchorV2) { + return ytile; } } return null; diff --git a/src/net/sourceforge/plantuml/sequencediagram/teoz/Tile.java b/src/net/sourceforge/plantuml/sequencediagram/teoz/Tile.java index 5d354ca4b..447d71c49 100644 --- a/src/net/sourceforge/plantuml/sequencediagram/teoz/Tile.java +++ b/src/net/sourceforge/plantuml/sequencediagram/teoz/Tile.java @@ -57,7 +57,7 @@ public interface Tile extends UDrawable, UShape { public double getZ(StringBounder stringBounder); - public Tile matchAnchorV1(String anchor); + public boolean matchAnchorV1(String anchor); } diff --git a/src/net/sourceforge/plantuml/sequencediagram/teoz/TileParallel.java b/src/net/sourceforge/plantuml/sequencediagram/teoz/TileParallel.java index f0852eb4b..1f52d07bf 100644 --- a/src/net/sourceforge/plantuml/sequencediagram/teoz/TileParallel.java +++ b/src/net/sourceforge/plantuml/sequencediagram/teoz/TileParallel.java @@ -165,14 +165,13 @@ public class TileParallel implements Tile, TileWithUpdateStairs, TileWithCallbac return null; } - public Tile matchAnchorV1(String anchor) { + public boolean matchAnchorV1(String anchor) { for (Tile tile : tiles) { - final Tile result = tile.matchAnchorV1(anchor); - if (result != null) { - return result; + if (tile.matchAnchorV1(anchor)) { + return true; } } - return null; + return false; } } diff --git a/src/net/sourceforge/plantuml/sequencediagram/teoz/YPositionedTile.java b/src/net/sourceforge/plantuml/sequencediagram/teoz/YPositionedTile.java index f8d6bec97..391939abb 100644 --- a/src/net/sourceforge/plantuml/sequencediagram/teoz/YPositionedTile.java +++ b/src/net/sourceforge/plantuml/sequencediagram/teoz/YPositionedTile.java @@ -67,8 +67,8 @@ public class YPositionedTile { } public boolean matchAnchorV2(String anchor) { - final Tile result = tile.matchAnchorV1(anchor); - return result != null; + final boolean result = tile.matchAnchorV1(anchor); + return result; } public final double getY(StringBounder stringBounder) { diff --git a/src/net/sourceforge/plantuml/skin/AbstractTextualComponent.java b/src/net/sourceforge/plantuml/skin/AbstractTextualComponent.java index c43d49873..d0883b5b4 100644 --- a/src/net/sourceforge/plantuml/skin/AbstractTextualComponent.java +++ b/src/net/sourceforge/plantuml/skin/AbstractTextualComponent.java @@ -114,7 +114,7 @@ public abstract class AbstractTextualComponent extends AbstractComponent { textBlock = new BodyEnhanced2(this.display, FontParam.NOTE, spriteContainer, horizontalAlignment, fc, maxMessageSize); } else { - textBlock = this.display.create(fc, horizontalAlignment, spriteContainer, maxMessageSize, CreoleMode.FULL, + textBlock = this.display.create0(fc, horizontalAlignment, spriteContainer, maxMessageSize, CreoleMode.FULL, fontForStereotype, htmlColorForStereotype); } } diff --git a/src/net/sourceforge/plantuml/skin/rose/ComponentRoseActiveLine.java b/src/net/sourceforge/plantuml/skin/rose/ComponentRoseActiveLine.java index 39d77018c..98ef41c3e 100644 --- a/src/net/sourceforge/plantuml/skin/rose/ComponentRoseActiveLine.java +++ b/src/net/sourceforge/plantuml/skin/rose/ComponentRoseActiveLine.java @@ -73,6 +73,10 @@ public class ComponentRoseActiveLine extends AbstractComponent { final StringBounder stringBounder = ug.getStringBounder(); final int x = (int) (dimensionToUse.getWidth() - getPreferredWidth(stringBounder)) / 2; + if (dimensionToUse.getHeight() == 0) { + return; + } + final URectangle rect = new URectangle(getPreferredWidth(stringBounder), dimensionToUse.getHeight()); if (symbolContext.isShadowing()) { rect.setDeltaShadow(1); diff --git a/src/net/sourceforge/plantuml/statediagram/command/CommandAddField.java b/src/net/sourceforge/plantuml/statediagram/command/CommandAddField.java index 435880721..6e5a50459 100644 --- a/src/net/sourceforge/plantuml/statediagram/command/CommandAddField.java +++ b/src/net/sourceforge/plantuml/statediagram/command/CommandAddField.java @@ -81,7 +81,7 @@ public class CommandAddField extends SingleLineCommand2 { final Code code = diagram.V1972() ? ident : diagram.buildCode(codeString); final IEntity entity = diagram.getOrCreateLeaf(ident, code, null, null); - entity.getBodier().addFieldOrMethod(field, entity); + entity.getBodier().addFieldOrMethod(field); return CommandExecutionResult.ok(); } diff --git a/src/net/sourceforge/plantuml/statediagram/command/CommandCreateState.java b/src/net/sourceforge/plantuml/statediagram/command/CommandCreateState.java index 29f9da5cc..5e60796d5 100644 --- a/src/net/sourceforge/plantuml/statediagram/command/CommandCreateState.java +++ b/src/net/sourceforge/plantuml/statediagram/command/CommandCreateState.java @@ -150,7 +150,7 @@ public class CommandCreateState extends SingleLineCommand2 { final String addFields = arg.get("ADDFIELD", 0); if (addFields != null) { - ent.getBodier().addFieldOrMethod(addFields, ent); + ent.getBodier().addFieldOrMethod(addFields); } return CommandExecutionResult.ok(); } diff --git a/src/net/sourceforge/plantuml/svek/GeneralImageBuilder.java b/src/net/sourceforge/plantuml/svek/GeneralImageBuilder.java index 608fe88bf..3d8406370 100644 --- a/src/net/sourceforge/plantuml/svek/GeneralImageBuilder.java +++ b/src/net/sourceforge/plantuml/svek/GeneralImageBuilder.java @@ -84,7 +84,6 @@ import net.sourceforge.plantuml.cucadiagram.dot.ExeState; import net.sourceforge.plantuml.cucadiagram.dot.GraphvizVersion; import net.sourceforge.plantuml.cucadiagram.dot.Neighborhood; import net.sourceforge.plantuml.cucadiagram.entity.EntityFactory; -import net.sourceforge.plantuml.cucadiagram.entity.EntityImpl; import net.sourceforge.plantuml.descdiagram.EntityImageDesignedDomain; import net.sourceforge.plantuml.descdiagram.EntityImageDomain; import net.sourceforge.plantuml.descdiagram.EntityImageMachine; @@ -100,7 +99,6 @@ import net.sourceforge.plantuml.graphic.TextBlockUtils; import net.sourceforge.plantuml.graphic.TextBlockWidth; import net.sourceforge.plantuml.graphic.USymbol; import net.sourceforge.plantuml.graphic.USymbolInterface; -import net.sourceforge.plantuml.graphic.color.ColorType; import net.sourceforge.plantuml.style.SName; import net.sourceforge.plantuml.style.Style; import net.sourceforge.plantuml.style.StyleSignature; @@ -118,6 +116,7 @@ import net.sourceforge.plantuml.svek.image.EntityImageGroup; import net.sourceforge.plantuml.svek.image.EntityImageLollipopInterface; import net.sourceforge.plantuml.svek.image.EntityImageLollipopInterfaceEye1; import net.sourceforge.plantuml.svek.image.EntityImageLollipopInterfaceEye2; +import net.sourceforge.plantuml.svek.image.EntityImageMap; import net.sourceforge.plantuml.svek.image.EntityImageNote; import net.sourceforge.plantuml.svek.image.EntityImageObject; import net.sourceforge.plantuml.svek.image.EntityImagePseudoState; @@ -212,6 +211,9 @@ public final class GeneralImageBuilder { if (leaf.getLeafType() == LeafType.OBJECT) { return new EntityImageObject(leaf, skinParam, portionShower); } + if (leaf.getLeafType() == LeafType.MAP) { + return new EntityImageMap(leaf, skinParam, portionShower); + } if (leaf.getLeafType() == LeafType.SYNCHRO_BAR || leaf.getLeafType() == LeafType.STATE_FORK_JOIN) { return new EntityImageSynchroBar(leaf, skinParam); } diff --git a/src/net/sourceforge/plantuml/svek/Line.java b/src/net/sourceforge/plantuml/svek/Line.java index 949e75340..0490d3722 100644 --- a/src/net/sourceforge/plantuml/svek/Line.java +++ b/src/net/sourceforge/plantuml/svek/Line.java @@ -342,7 +342,7 @@ public class Line implements Moveable, Hideable { private TextBlock getLineLabel(Link link, ISkinParam skinParam, FontConfiguration labelFont) { final double marginLabel = startUid.equalsId(endUid) ? 6 : 1; final HorizontalAlignment alignment = getMessageTextAlignment(link.getUmlDiagramType(), skinParam); - TextBlock label = link.getLabel().create(labelFont, alignment, skinParam, skinParam.maxMessageSize()); + TextBlock label = link.getLabel().create9(labelFont, alignment, skinParam, skinParam.maxMessageSize()); final VisibilityModifier visibilityModifier = link.getVisibilityModifier(); if (visibilityModifier != null) { final Rose rose = new Rose(); diff --git a/src/net/sourceforge/plantuml/svek/UGraphicForSnake.java b/src/net/sourceforge/plantuml/svek/UGraphicForSnake.java index b3cea8c14..b79634e27 100644 --- a/src/net/sourceforge/plantuml/svek/UGraphicForSnake.java +++ b/src/net/sourceforge/plantuml/svek/UGraphicForSnake.java @@ -53,7 +53,7 @@ public class UGraphicForSnake extends UGraphicDelegator { @Override public String toString() { - return super.toString() + " " + getUg(); + return "UGraphicForSnake " + getUg(); } public UTranslate getTranslation() { diff --git a/src/net/sourceforge/plantuml/svek/image/EntityImageMap.java b/src/net/sourceforge/plantuml/svek/image/EntityImageMap.java new file mode 100644 index 000000000..0def72bef --- /dev/null +++ b/src/net/sourceforge/plantuml/svek/image/EntityImageMap.java @@ -0,0 +1,222 @@ +/* ======================================================================== + * PlantUML : a free UML diagram generator + * ======================================================================== + * + * (C) Copyright 2009-2020, Arnaud Roques + * + * Project Info: http://plantuml.com + * + * If you like this project or if you find it useful, you can support us at: + * + * http://plantuml.com/patreon (only 1$ per month!) + * http://plantuml.com/paypal + * + * This file is part of PlantUML. + * + * PlantUML is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PlantUML distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * + * Original Author: Arnaud Roques + * + * + */ +package net.sourceforge.plantuml.svek.image; + +import java.awt.geom.Dimension2D; + +import net.sourceforge.plantuml.ColorParam; +import net.sourceforge.plantuml.CornerParam; +import net.sourceforge.plantuml.Dimension2DDouble; +import net.sourceforge.plantuml.FontParam; +import net.sourceforge.plantuml.Guillemet; +import net.sourceforge.plantuml.ISkinParam; +import net.sourceforge.plantuml.LineConfigurable; +import net.sourceforge.plantuml.LineParam; +import net.sourceforge.plantuml.SkinParamUtils; +import net.sourceforge.plantuml.Url; +import net.sourceforge.plantuml.creole.Stencil; +import net.sourceforge.plantuml.cucadiagram.Display; +import net.sourceforge.plantuml.cucadiagram.EntityPortion; +import net.sourceforge.plantuml.cucadiagram.ILeaf; +import net.sourceforge.plantuml.cucadiagram.LeafType; +import net.sourceforge.plantuml.cucadiagram.PortionShower; +import net.sourceforge.plantuml.cucadiagram.Stereotype; +import net.sourceforge.plantuml.cucadiagram.TextBlockMap; +import net.sourceforge.plantuml.graphic.FontConfiguration; +import net.sourceforge.plantuml.graphic.HorizontalAlignment; +import net.sourceforge.plantuml.graphic.StringBounder; +import net.sourceforge.plantuml.graphic.TextBlock; +import net.sourceforge.plantuml.graphic.TextBlockEmpty; +import net.sourceforge.plantuml.graphic.TextBlockLineBefore; +import net.sourceforge.plantuml.graphic.TextBlockUtils; +import net.sourceforge.plantuml.graphic.color.ColorType; +import net.sourceforge.plantuml.svek.AbstractEntityImage; +import net.sourceforge.plantuml.svek.Ports; +import net.sourceforge.plantuml.svek.ShapeType; +import net.sourceforge.plantuml.svek.WithPorts; +import net.sourceforge.plantuml.ugraphic.PlacementStrategyY1Y2; +import net.sourceforge.plantuml.ugraphic.Shadowable; +import net.sourceforge.plantuml.ugraphic.UChangeBackColor; +import net.sourceforge.plantuml.ugraphic.UChangeColor; +import net.sourceforge.plantuml.ugraphic.UGraphic; +import net.sourceforge.plantuml.ugraphic.UGraphicStencil; +import net.sourceforge.plantuml.ugraphic.ULayoutGroup; +import net.sourceforge.plantuml.ugraphic.URectangle; +import net.sourceforge.plantuml.ugraphic.UStroke; +import net.sourceforge.plantuml.ugraphic.UTranslate; +import net.sourceforge.plantuml.ugraphic.color.HColor; + +public class EntityImageMap extends AbstractEntityImage implements Stencil, WithPorts { + + final private TextBlock name; + final private TextBlock stereo; + final private TextBlock entries; + final private Url url; + final private double roundCorner; + + final private LineConfigurable lineConfig; + + public EntityImageMap(ILeaf entity, ISkinParam skinParam, PortionShower portionShower) { + super(entity, skinParam); + this.lineConfig = entity; + final Stereotype stereotype = entity.getStereotype(); + this.roundCorner = skinParam.getRoundCorner(CornerParam.DEFAULT, null); + this.name = TextBlockUtils.withMargin( + entity.getDisplay().create(new FontConfiguration(getSkinParam(), FontParam.OBJECT, stereotype), + HorizontalAlignment.CENTER, skinParam), + 2, 2); + if (stereotype == null || stereotype.getLabel(Guillemet.DOUBLE_COMPARATOR) == null + || portionShower.showPortion(EntityPortion.STEREOTYPE, entity) == false) { + this.stereo = null; + } else { + this.stereo = Display.create(stereotype.getLabels(skinParam.guillemet())).create( + new FontConfiguration(getSkinParam(), FontParam.OBJECT_STEREOTYPE, stereotype), + HorizontalAlignment.CENTER, skinParam); + } + + this.entries = entity.getBodier().getBody(FontParam.OBJECT_ATTRIBUTE, skinParam, false, false, + entity.getStereotype()); + this.url = entity.getUrl99(); + + } + + public Ports getPorts(StringBounder stringBounder) { + final Dimension2D dimTitle = getTitleDimension(stringBounder); + return ((WithPorts) entries).getPorts(stringBounder).translateY(dimTitle.getHeight()); + } + + private int marginEmptyFieldsOrMethod = 13; + + public Dimension2D calculateDimension(StringBounder stringBounder) { + final Dimension2D dimTitle = getTitleDimension(stringBounder); + final Dimension2D dimFields = entries.calculateDimension(stringBounder); + double width = Math.max(dimFields.getWidth(), dimTitle.getWidth() + 2 * xMarginCircle); + if (width < getSkinParam().minClassWidth()) { + width = getSkinParam().minClassWidth(); + } + + final double height = getMethodOrFieldHeight(dimFields) + dimTitle.getHeight(); + return new Dimension2DDouble(width, height); + } + + final public void drawU(UGraphic ug) { + final StringBounder stringBounder = ug.getStringBounder(); + final Dimension2D dimTotal = calculateDimension(stringBounder); + final Dimension2D dimTitle = getTitleDimension(stringBounder); + + final double widthTotal = dimTotal.getWidth(); + final double heightTotal = dimTotal.getHeight(); + final Shadowable rect = new URectangle(widthTotal, heightTotal).rounded(roundCorner); + if (getSkinParam().shadowing(getEntity().getStereotype())) { + rect.setDeltaShadow(4); + } + + ug = ug.apply(new UChangeColor(SkinParamUtils.getColor(getSkinParam(), getStereo(), ColorParam.objectBorder))); + HColor backcolor = getEntity().getColors(getSkinParam()).getColor(ColorType.BACK); + if (backcolor == null) { + backcolor = SkinParamUtils.getColor(getSkinParam(), getStereo(), ColorParam.objectBackground); + } + ug = ug.apply(new UChangeBackColor(backcolor)); + if (url != null) { + ug.startUrl(url); + } + + final UStroke stroke = getStroke(); + ug.apply(stroke).draw(rect); + + final ULayoutGroup header = new ULayoutGroup(new PlacementStrategyY1Y2(ug.getStringBounder())); + if (stereo != null) { + header.add(stereo); + } + header.add(name); + header.drawU(ug, dimTotal.getWidth(), dimTitle.getHeight()); + + final UGraphic ug2 = UGraphicStencil.create(ug, this, stroke); + ((TextBlockMap) entries).setTotalWidth(dimTotal.getWidth()); + entries.drawU(ug2.apply(UTranslate.dy(dimTitle.getHeight()))); + + if (url != null) { + ug.closeAction(); + } + } + + private UStroke getStroke() { + UStroke stroke = lineConfig.getColors(getSkinParam()).getSpecificLineStroke(); + if (stroke == null) { + stroke = getSkinParam().getThickness(LineParam.objectBorder, getStereo()); + } + if (stroke == null) { + stroke = new UStroke(1.5); + } + return stroke; + } + + private double getMethodOrFieldHeight(final Dimension2D dim) { + final double fieldsHeight = dim.getHeight(); + if (fieldsHeight == 0 && this.getEntity().getLeafType() != LeafType.MAP) { + return marginEmptyFieldsOrMethod; + } + return fieldsHeight; + } + + private int xMarginCircle = 5; + + private Dimension2D getTitleDimension(StringBounder stringBounder) { + return getNameAndSteretypeDimension(stringBounder); + } + + private Dimension2D getNameAndSteretypeDimension(StringBounder stringBounder) { + final Dimension2D nameDim = name.calculateDimension(stringBounder); + final Dimension2D stereoDim = stereo == null ? new Dimension2DDouble(0, 0) + : stereo.calculateDimension(stringBounder); + final Dimension2D nameAndStereo = new Dimension2DDouble(Math.max(nameDim.getWidth(), stereoDim.getWidth()), + nameDim.getHeight() + stereoDim.getHeight()); + return nameAndStereo; + } + + public ShapeType getShapeType() { + return ShapeType.RECTANGLE_HTML_FOR_PORTS; + } + + public double getStartingX(StringBounder stringBounder, double y) { + return 0; + } + + public double getEndingX(StringBounder stringBounder, double y) { + return calculateDimension(stringBounder).getWidth(); + } + +} diff --git a/src/net/sourceforge/plantuml/svek/image/EntityImageState.java b/src/net/sourceforge/plantuml/svek/image/EntityImageState.java index 502e609be..77390d4f9 100644 --- a/src/net/sourceforge/plantuml/svek/image/EntityImageState.java +++ b/src/net/sourceforge/plantuml/svek/image/EntityImageState.java @@ -91,7 +91,7 @@ public class EntityImageState extends AbstractEntityImage { final Stereotype stereotype = entity.getStereotype(); this.withSymbol = stereotype != null && stereotype.isWithOOSymbol(); - this.desc = entity.getDisplay().create(new FontConfiguration(getSkinParam(), FontParam.STATE, stereotype), + this.desc = entity.getDisplay().create8(new FontConfiguration(getSkinParam(), FontParam.STATE, stereotype), HorizontalAlignment.CENTER, skinParam, CreoleMode.FULL, skinParam.wrapWidth()); Display list = Display.empty(); @@ -101,7 +101,7 @@ public class EntityImageState extends AbstractEntityImage { this.url = entity.getUrl99(); - this.fields = list.create(new FontConfiguration(getSkinParam(), FontParam.STATE_ATTRIBUTE, stereotype), + this.fields = list.create8(new FontConfiguration(getSkinParam(), FontParam.STATE_ATTRIBUTE, stereotype), HorizontalAlignment.LEFT, skinParam, CreoleMode.FULL, skinParam.wrapWidth()); } diff --git a/src/net/sourceforge/plantuml/svek/image/EntityImageStateEmptyDescription.java b/src/net/sourceforge/plantuml/svek/image/EntityImageStateEmptyDescription.java index 6fd453b82..77e37c0f2 100644 --- a/src/net/sourceforge/plantuml/svek/image/EntityImageStateEmptyDescription.java +++ b/src/net/sourceforge/plantuml/svek/image/EntityImageStateEmptyDescription.java @@ -76,7 +76,7 @@ public class EntityImageStateEmptyDescription extends AbstractEntityImage { super(entity, skinParam); final Stereotype stereotype = entity.getStereotype(); - this.desc = entity.getDisplay().create(new FontConfiguration(getSkinParam(), FontParam.STATE, stereotype), + this.desc = entity.getDisplay().create8(new FontConfiguration(getSkinParam(), FontParam.STATE, stereotype), HorizontalAlignment.CENTER, skinParam, CreoleMode.FULL, skinParam.wrapWidth()); Display list = Display.empty(); diff --git a/src/net/sourceforge/plantuml/svg/SvgGraphics.java b/src/net/sourceforge/plantuml/svg/SvgGraphics.java index 04fe14999..dcd36a725 100644 --- a/src/net/sourceforge/plantuml/svg/SvgGraphics.java +++ b/src/net/sourceforge/plantuml/svg/SvgGraphics.java @@ -63,6 +63,7 @@ import net.sourceforge.plantuml.SignatureUtils; import net.sourceforge.plantuml.StringUtils; import net.sourceforge.plantuml.SvgString; import net.sourceforge.plantuml.code.Base64Coder; +import net.sourceforge.plantuml.cucadiagram.dot.GraphvizUtils; import net.sourceforge.plantuml.tikz.TikzGraphics; import net.sourceforge.plantuml.ugraphic.UPath; import net.sourceforge.plantuml.ugraphic.USegment; @@ -326,7 +327,8 @@ public class SvgGraphics { if (url == null) { throw new IllegalArgumentException(); } - if (OptionFlags.ALLOW_INCLUDE == false && url.toLowerCase().startsWith("javascript")) { + // javascript: security issue + if (GraphvizUtils.getJavascriptUnsecure() == false && url.toLowerCase().startsWith("javascript")) { return; } diff --git a/src/net/sourceforge/plantuml/ugraphic/AbstractUGraphic.java b/src/net/sourceforge/plantuml/ugraphic/AbstractUGraphic.java index 044598cae..e130983e1 100644 --- a/src/net/sourceforge/plantuml/ugraphic/AbstractUGraphic.java +++ b/src/net/sourceforge/plantuml/ugraphic/AbstractUGraphic.java @@ -38,6 +38,7 @@ package net.sourceforge.plantuml.ugraphic; import java.util.HashMap; import java.util.Map; +import net.sourceforge.plantuml.graphic.SpecialText; import net.sourceforge.plantuml.ugraphic.color.ColorMapper; public abstract class AbstractUGraphic extends AbstractCommonUGraphic { @@ -70,6 +71,10 @@ public abstract class AbstractUGraphic extends AbstractCommonUGraphic { } public final void draw(UShape shape) { + if (shape instanceof SpecialText) { + ((SpecialText) shape).getTitle().drawU(this); + return; + } if (shape instanceof UEmpty) { return; } diff --git a/src/net/sourceforge/plantuml/ugraphic/LimitFinder.java b/src/net/sourceforge/plantuml/ugraphic/LimitFinder.java index 7762a7efa..bd1c55c39 100644 --- a/src/net/sourceforge/plantuml/ugraphic/LimitFinder.java +++ b/src/net/sourceforge/plantuml/ugraphic/LimitFinder.java @@ -38,6 +38,8 @@ package net.sourceforge.plantuml.ugraphic; import java.awt.geom.Dimension2D; import net.sourceforge.plantuml.Url; +import net.sourceforge.plantuml.activitydiagram3.ftile.CenteredText; +import net.sourceforge.plantuml.graphic.SpecialText; import net.sourceforge.plantuml.graphic.StringBounder; import net.sourceforge.plantuml.graphic.TextBlock; import net.sourceforge.plantuml.posimo.DotPath; @@ -49,7 +51,7 @@ public class LimitFinder implements UGraphic { public boolean matchesProperty(String propertyName) { return false; } - + public double dpiFactor() { return 1; } @@ -141,6 +143,10 @@ public class LimitFinder implements UGraphic { tb.drawU(this); } else if (shape instanceof UCenteredCharacter) { // To be done + } else if (shape instanceof CenteredText) { + // Ignored + } else if (shape instanceof SpecialText) { + // Ignored } else if (shape instanceof UPixel) { addPoint(x, y); } else { diff --git a/src/net/sourceforge/plantuml/ugraphic/UPath.java b/src/net/sourceforge/plantuml/ugraphic/UPath.java index baf112ced..79a8d5ad2 100644 --- a/src/net/sourceforge/plantuml/ugraphic/UPath.java +++ b/src/net/sourceforge/plantuml/ugraphic/UPath.java @@ -40,13 +40,17 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; -public class UPath extends AbstractShadowable implements Iterable { +import net.sourceforge.plantuml.ugraphic.comp.CompressionMode; + +public class UPath extends AbstractShadowable implements Iterable, UShapeIgnorableForCompression { private final String comment; private final List segments = new ArrayList(); private MinMax minmax = MinMax.getEmpty(false); private boolean isOpenIconic; + private boolean ignoreForCompressionOnX; + private boolean ignoreForCompressionOnY; public UPath(String comment) { this.comment = comment; @@ -179,6 +183,27 @@ public class UPath extends AbstractShadowable implements Iterable { return comment; } + public void setIgnoreForCompressionOnX() { + this.ignoreForCompressionOnX = true; + } + + public void setIgnoreForCompressionOnY() { + this.ignoreForCompressionOnY = true; + } + + public void drawWhenCompressed(UGraphic ug, CompressionMode mode) { + } + + public boolean isIgnoreForCompressionOn(CompressionMode mode) { + if (mode == CompressionMode.ON_X) { + return ignoreForCompressionOnX; + } + if (mode == CompressionMode.ON_Y) { + return ignoreForCompressionOnY; + } + throw new IllegalArgumentException(); + } + // public boolean isEmpty() { // return segments.size() == 0; // } diff --git a/src/net/sourceforge/plantuml/ugraphic/UPolygon.java b/src/net/sourceforge/plantuml/ugraphic/UPolygon.java index 15cbd734d..268ec1a9f 100644 --- a/src/net/sourceforge/plantuml/ugraphic/UPolygon.java +++ b/src/net/sourceforge/plantuml/ugraphic/UPolygon.java @@ -159,14 +159,14 @@ public class UPolygon extends AbstractShadowable { return points; } - private CompressionMode ignoreForCompression; + private CompressionMode compressionMode; - public final CompressionMode isIgnoreForCompression() { - return ignoreForCompression; + public final CompressionMode getCompressionMode() { + return compressionMode; } - public final void setIgnoreForCompression(CompressionMode ignoreForCompression) { - this.ignoreForCompression = ignoreForCompression; + public final void setCompressionMode(CompressionMode compressionMode) { + this.compressionMode = compressionMode; } diff --git a/src/net/sourceforge/plantuml/ugraphic/URectangle.java b/src/net/sourceforge/plantuml/ugraphic/URectangle.java index d16cd23c9..aaab07f1b 100644 --- a/src/net/sourceforge/plantuml/ugraphic/URectangle.java +++ b/src/net/sourceforge/plantuml/ugraphic/URectangle.java @@ -36,39 +36,42 @@ package net.sourceforge.plantuml.ugraphic; import java.awt.geom.Dimension2D; -public class URectangle extends AbstractShadowable implements Scalable, UShapeSized { +import net.sourceforge.plantuml.ugraphic.comp.CompressionMode; + +public class URectangle extends AbstractShadowable implements Scalable, UShapeSized, UShapeIgnorableForCompression { private final double width; private final double height; private final double rx; private final double ry; private final String comment; - private boolean ignoreForCompression; + private final boolean ignoreForCompressionOnX; + private final boolean ignoreForCompressionOnY; public URectangle withHeight(double newHeight) { - final URectangle result = new URectangle(width, newHeight, rx, ry, comment, ignoreForCompression); - result.ignoreForCompression = this.ignoreForCompression; + final URectangle result = new URectangle(width, newHeight, rx, ry, comment, ignoreForCompressionOnX, + ignoreForCompressionOnY); result.setDeltaShadow(this.getDeltaShadow()); return result; } public URectangle withWidth(double newWidth) { - final URectangle result = new URectangle(newWidth, height, rx, ry, comment, ignoreForCompression); - result.ignoreForCompression = this.ignoreForCompression; + final URectangle result = new URectangle(newWidth, height, rx, ry, comment, ignoreForCompressionOnX, + ignoreForCompressionOnY); result.setDeltaShadow(this.getDeltaShadow()); return result; } public URectangle withComment(String comment) { - return new URectangle(width, height, rx, ry, comment, ignoreForCompression); + return new URectangle(width, height, rx, ry, comment, ignoreForCompressionOnX, ignoreForCompressionOnY); } public URectangle rounded(double round) { - return new URectangle(width, height, round, round, comment, ignoreForCompression); + return new URectangle(width, height, round, round, comment, ignoreForCompressionOnX, ignoreForCompressionOnY); } public Shadowable diagonalCorner(double diagonalCorner) { - if (ignoreForCompression) { + if (ignoreForCompressionOnX || ignoreForCompressionOnY) { throw new IllegalStateException(); } if (diagonalCorner == 0) { @@ -87,8 +90,12 @@ public class URectangle extends AbstractShadowable implements Scalable, UShapeSi return result; } - public final URectangle ignoreForCompression() { - return new URectangle(width, height, rx, ry, comment, true); + public final URectangle ignoreForCompressionOnX() { + return new URectangle(width, height, rx, ry, comment, true, ignoreForCompressionOnY); + } + + public final URectangle ignoreForCompressionOnY() { + return new URectangle(width, height, rx, ry, comment, ignoreForCompressionOnX, true); } public UShape getScaled(double scale) { @@ -96,13 +103,13 @@ public class URectangle extends AbstractShadowable implements Scalable, UShapeSi return this; } final AbstractShadowable result = new URectangle(width * scale, height * scale, rx * scale, ry * scale, comment, - ignoreForCompression); + ignoreForCompressionOnX, ignoreForCompressionOnY); result.setDeltaShadow(this.getDeltaShadow()); return result; } public URectangle(double width, double height) { - this(width, height, 0, 0, null, false); + this(width, height, 0, 0, null, false, false); } public URectangle(Dimension2D dim) { @@ -110,14 +117,15 @@ public class URectangle extends AbstractShadowable implements Scalable, UShapeSi } private URectangle(double width, double height, double rx, double ry, String comment, - boolean ignoreForCompression) { + boolean ignoreForCompressionOnX, boolean ignoreForCompressionOnY) { if (height == 0) { throw new IllegalArgumentException("height=" + height); } if (width == 0) { throw new IllegalArgumentException("width=" + width); } - this.ignoreForCompression = ignoreForCompression; + this.ignoreForCompressionOnX = ignoreForCompressionOnX; + this.ignoreForCompressionOnY = ignoreForCompressionOnY; this.comment = comment; this.width = width; this.height = height; @@ -158,8 +166,30 @@ public class URectangle extends AbstractShadowable implements Scalable, UShapeSi return comment; } - public final boolean isIgnoreForCompression() { - return ignoreForCompression; + public void drawWhenCompressed(UGraphic ug, CompressionMode mode) { + if (mode == CompressionMode.ON_X) { + ug.draw(new UEmpty(2, getHeight())); + ug.apply(UTranslate.dx(getWidth() - 2)).draw(new UEmpty(2, getHeight())); +// drawEmpty(x, y, new UEmpty(2, shape.getHeight())); +// drawEmpty(x + shape.getWidth() - 2, y, new UEmpty(2, shape.getHeight())); + } + if (mode == CompressionMode.ON_Y) { + ug.draw(new UEmpty(getWidth(), 2)); + ug.apply(UTranslate.dy(getHeight() - 2)).draw(new UEmpty(getWidth(), 2)); +// drawEmpty(x, y, new UEmpty(shape.getWidth(), 2)); +// drawEmpty(x, y + shape.getHeight() - 2, new UEmpty(shape.getWidth(), 2)); + } + + } + + public boolean isIgnoreForCompressionOn(CompressionMode mode) { + if (mode == CompressionMode.ON_X) { + return ignoreForCompressionOnX; + } + if (mode == CompressionMode.ON_Y) { + return ignoreForCompressionOnY; + } + throw new IllegalArgumentException(); } } diff --git a/src/net/sourceforge/plantuml/ugraphic/UShapeIgnorableForCompression.java b/src/net/sourceforge/plantuml/ugraphic/UShapeIgnorableForCompression.java new file mode 100644 index 000000000..11c9084b4 --- /dev/null +++ b/src/net/sourceforge/plantuml/ugraphic/UShapeIgnorableForCompression.java @@ -0,0 +1,46 @@ +/* ======================================================================== + * PlantUML : a free UML diagram generator + * ======================================================================== + * + * (C) Copyright 2009-2020, Arnaud Roques + * + * Project Info: http://plantuml.com + * + * If you like this project or if you find it useful, you can support us at: + * + * http://plantuml.com/patreon (only 1$ per month!) + * http://plantuml.com/paypal + * + * This file is part of PlantUML. + * + * PlantUML is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PlantUML distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * + * Original Author: Arnaud Roques + * + * + */ +package net.sourceforge.plantuml.ugraphic; + +import net.sourceforge.plantuml.ugraphic.comp.CompressionMode; + +public interface UShapeIgnorableForCompression extends UShape { + + public boolean isIgnoreForCompressionOn(CompressionMode mode); + + public void drawWhenCompressed(UGraphic ug, CompressionMode mode); + +} diff --git a/src/net/sourceforge/plantuml/ugraphic/comp/CompressionTransform.java b/src/net/sourceforge/plantuml/ugraphic/comp/CompressionTransform.java index add8d6cf4..7f5383e47 100644 --- a/src/net/sourceforge/plantuml/ugraphic/comp/CompressionTransform.java +++ b/src/net/sourceforge/plantuml/ugraphic/comp/CompressionTransform.java @@ -37,7 +37,7 @@ package net.sourceforge.plantuml.ugraphic.comp; import java.util.List; -public class CompressionTransform { +public class CompressionTransform implements PiecewiseAffineTransform { private final List all; diff --git a/src/net/sourceforge/plantuml/ugraphic/comp/CompressionXorYBuilder.java b/src/net/sourceforge/plantuml/ugraphic/comp/CompressionXorYBuilder.java new file mode 100644 index 000000000..cdaf039b3 --- /dev/null +++ b/src/net/sourceforge/plantuml/ugraphic/comp/CompressionXorYBuilder.java @@ -0,0 +1,56 @@ +/* ======================================================================== + * PlantUML : a free UML diagram generator + * ======================================================================== + * + * (C) Copyright 2009-2020, Arnaud Roques + * + * Project Info: http://plantuml.com + * + * If you like this project or if you find it useful, you can support us at: + * + * http://plantuml.com/patreon (only 1$ per month!) + * http://plantuml.com/paypal + * + * This file is part of PlantUML. + * + * PlantUML is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PlantUML distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * + * Original Author: Arnaud Roques + * + * + */ +package net.sourceforge.plantuml.ugraphic.comp; + +import net.sourceforge.plantuml.graphic.StringBounder; +import net.sourceforge.plantuml.graphic.TextBlock; + +public class CompressionXorYBuilder { + + public static TextBlock build(CompressionMode mode, TextBlock textBlock, StringBounder stringBounder) { + final PiecewiseAffineTransform affine = getPiecewiseAffineTransform(mode, textBlock, stringBounder); + return PiecewiseAffineOnXorYBuilder.build(mode, textBlock, affine); + } + + private static PiecewiseAffineTransform getPiecewiseAffineTransform(CompressionMode mode, TextBlock textBlock, + StringBounder stringBounder) { + final SlotFinder slotFinder = new SlotFinder(mode, stringBounder); + textBlock.drawU(slotFinder); + final SlotSet ysSlotSet = slotFinder.getSlotSet().reverse().smaller(5.0); + return new CompressionTransform(ysSlotSet); + } + +} \ No newline at end of file diff --git a/src/net/sourceforge/plantuml/ugraphic/comp/Expand.java b/src/net/sourceforge/plantuml/ugraphic/comp/Expand.java new file mode 100644 index 000000000..1a02241c5 --- /dev/null +++ b/src/net/sourceforge/plantuml/ugraphic/comp/Expand.java @@ -0,0 +1,80 @@ +/* ======================================================================== + * PlantUML : a free UML diagram generator + * ======================================================================== + * + * (C) Copyright 2009-2020, Arnaud Roques + * + * Project Info: http://plantuml.com + * + * If you like this project or if you find it useful, you can support us at: + * + * http://plantuml.com/patreon (only 1$ per month!) + * http://plantuml.com/paypal + * + * This file is part of PlantUML. + * + * PlantUML is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PlantUML distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * + * Original Author: Arnaud Roques + * + * + */ +package net.sourceforge.plantuml.ugraphic.comp; + +public class Expand implements Comparable { + + private final double position; + private final double extend; + private final ExpandType type; + + public Expand(ExpandType type, double position, double extend) { + if (extend <= 0) { + throw new IllegalArgumentException(); + } + this.type = type; + this.position = position; + this.extend = extend; + } + + @Override + public String toString() { + return "(" + position + "==>+" + extend + " " + type + ")"; + } + + public int compareTo(Expand other) { + if (this.position < other.position) { + return -1; + } + if (this.position > other.position) { + return 1; + } + return 0; + } + + public final double getPosition() { + return position; + } + + public final double getExtend() { + return extend; + } + + public final ExpandType getType() { + return type; + } + +} diff --git a/src/net/sourceforge/plantuml/ugraphic/comp/ExpandTransform.java b/src/net/sourceforge/plantuml/ugraphic/comp/ExpandTransform.java new file mode 100644 index 000000000..892e19ed3 --- /dev/null +++ b/src/net/sourceforge/plantuml/ugraphic/comp/ExpandTransform.java @@ -0,0 +1,71 @@ +/* ======================================================================== + * PlantUML : a free UML diagram generator + * ======================================================================== + * + * (C) Copyright 2009-2020, Arnaud Roques + * + * Project Info: http://plantuml.com + * + * If you like this project or if you find it useful, you can support us at: + * + * http://plantuml.com/patreon (only 1$ per month!) + * http://plantuml.com/paypal + * + * This file is part of PlantUML. + * + * PlantUML is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PlantUML distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * + * Original Author: Arnaud Roques + * + * + */ +package net.sourceforge.plantuml.ugraphic.comp; + +import java.util.Set; +import java.util.TreeSet; + +public class ExpandTransform implements PiecewiseAffineTransform { + + private final Set all = new TreeSet(); + + @Override + public String toString() { + return all.toString(); + } + + public void addExpandIncludingLimit(double position, double extend) { + this.all.add(new Expand(ExpandType.INCLUDING_LIMIT, position, extend)); + } + + public void addExpandExcludingLimit(double position, double extend) { + this.all.add(new Expand(ExpandType.EXCLUDING_LIMIT, position, extend)); + } + + public double transform(final double init) { + double result = init; + for (Expand expand : all) { + if (ExpandType.INCLUDING_LIMIT == expand.getType() && init >= expand.getPosition()) { + result += expand.getExtend(); + } + if (ExpandType.EXCLUDING_LIMIT == expand.getType() && init > expand.getPosition()) { + result += expand.getExtend(); + } + } + return result; + } + +} diff --git a/src/net/sourceforge/plantuml/ugraphic/comp/ExpandType.java b/src/net/sourceforge/plantuml/ugraphic/comp/ExpandType.java new file mode 100644 index 000000000..1d5d67abb --- /dev/null +++ b/src/net/sourceforge/plantuml/ugraphic/comp/ExpandType.java @@ -0,0 +1,42 @@ +/* ======================================================================== + * PlantUML : a free UML diagram generator + * ======================================================================== + * + * (C) Copyright 2009-2020, Arnaud Roques + * + * Project Info: http://plantuml.com + * + * If you like this project or if you find it useful, you can support us at: + * + * http://plantuml.com/patreon (only 1$ per month!) + * http://plantuml.com/paypal + * + * This file is part of PlantUML. + * + * PlantUML is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PlantUML distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * + * Original Author: Arnaud Roques + * + * + */ +package net.sourceforge.plantuml.ugraphic.comp; + +public enum ExpandType { + + INCLUDING_LIMIT, EXCLUDING_LIMIT + +} diff --git a/src/net/sourceforge/plantuml/ugraphic/comp/PiecewiseAffineComposition.java b/src/net/sourceforge/plantuml/ugraphic/comp/PiecewiseAffineComposition.java new file mode 100644 index 000000000..e86b28820 --- /dev/null +++ b/src/net/sourceforge/plantuml/ugraphic/comp/PiecewiseAffineComposition.java @@ -0,0 +1,52 @@ +/* ======================================================================== + * PlantUML : a free UML diagram generator + * ======================================================================== + * + * (C) Copyright 2009-2020, Arnaud Roques + * + * Project Info: http://plantuml.com + * + * If you like this project or if you find it useful, you can support us at: + * + * http://plantuml.com/patreon (only 1$ per month!) + * http://plantuml.com/paypal + * + * This file is part of PlantUML. + * + * PlantUML is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PlantUML distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * + * Original Author: Arnaud Roques + * + * + */ +package net.sourceforge.plantuml.ugraphic.comp; + +public class PiecewiseAffineComposition implements PiecewiseAffineTransform { + + private final PiecewiseAffineComposition first; + private final PiecewiseAffineComposition second; + + public PiecewiseAffineComposition(PiecewiseAffineComposition first, PiecewiseAffineComposition second) { + this.first = first; + this.second = second; + } + + public double transform(double v) { + return second.transform(first.transform(v)); + } + +} diff --git a/src/net/sourceforge/plantuml/ugraphic/comp/TextBlockCompressedOnXorY.java b/src/net/sourceforge/plantuml/ugraphic/comp/PiecewiseAffineOnXorYBuilder.java similarity index 62% rename from src/net/sourceforge/plantuml/ugraphic/comp/TextBlockCompressedOnXorY.java rename to src/net/sourceforge/plantuml/ugraphic/comp/PiecewiseAffineOnXorYBuilder.java index ba680ee44..2697a2fb6 100644 --- a/src/net/sourceforge/plantuml/ugraphic/comp/TextBlockCompressedOnXorY.java +++ b/src/net/sourceforge/plantuml/ugraphic/comp/PiecewiseAffineOnXorYBuilder.java @@ -42,23 +42,31 @@ import net.sourceforge.plantuml.graphic.AbstractTextBlock; import net.sourceforge.plantuml.graphic.StringBounder; import net.sourceforge.plantuml.graphic.TextBlock; import net.sourceforge.plantuml.graphic.TextBlockUtils; +import net.sourceforge.plantuml.svek.TextBlockBackcolored; import net.sourceforge.plantuml.ugraphic.MinMax; import net.sourceforge.plantuml.ugraphic.UGraphic; +import net.sourceforge.plantuml.ugraphic.color.HColor; -public class TextBlockCompressedOnXorY extends AbstractTextBlock implements TextBlock { +public class PiecewiseAffineOnXorYBuilder extends AbstractTextBlock implements TextBlock, TextBlockBackcolored { private final TextBlock textBlock; private final CompressionMode mode; + private final PiecewiseAffineTransform piecewiseAffineTransform; - public TextBlockCompressedOnXorY(CompressionMode mode, TextBlock textBlock) { + public static TextBlock build(CompressionMode mode, TextBlock textBlock, + PiecewiseAffineTransform piecewiseAffineTransform) { + return new PiecewiseAffineOnXorYBuilder(mode, textBlock, piecewiseAffineTransform); + } + + private PiecewiseAffineOnXorYBuilder(CompressionMode mode, TextBlock textBlock, + PiecewiseAffineTransform piecewiseAffineTransform) { this.textBlock = textBlock; this.mode = mode; + this.piecewiseAffineTransform = piecewiseAffineTransform; } public void drawU(final UGraphic ug) { - final StringBounder stringBounder = ug.getStringBounder(); - final CompressionTransform compressionTransform = getCompressionTransform(stringBounder); - textBlock.drawU(new UGraphicCompressOnXorY(mode, ug, compressionTransform)); + textBlock.drawU(new UGraphicCompressOnXorY(mode, ug, piecewiseAffineTransform)); } private MinMax cachedMinMax; @@ -71,30 +79,17 @@ public class TextBlockCompressedOnXorY extends AbstractTextBlock implements Text return cachedMinMax; } - private CompressionTransform cachedCompressionTransform; - - private CompressionTransform getCompressionTransform(final StringBounder stringBounder) { - if (cachedCompressionTransform == null) { - cachedCompressionTransform = getCompressionTransformSlow(stringBounder); - } - return cachedCompressionTransform; - } - - private CompressionTransform getCompressionTransformSlow(final StringBounder stringBounder) { - final SlotFinder slotFinder = new SlotFinder(mode, stringBounder); - textBlock.drawU(slotFinder); - final SlotSet ysSlotSet = slotFinder.getSlotSet().reverse().smaller(5.0); - final CompressionTransform compressionTransform = new CompressionTransform(ysSlotSet); - return compressionTransform; - } - public Dimension2D calculateDimension(StringBounder stringBounder) { - final CompressionTransform compressionTransform = getCompressionTransform(stringBounder); final Dimension2D dim = textBlock.calculateDimension(stringBounder); if (mode == CompressionMode.ON_X) { - return new Dimension2DDouble(compressionTransform.transform(dim.getWidth()), dim.getHeight()); + return new Dimension2DDouble(piecewiseAffineTransform.transform(dim.getWidth()), dim.getHeight()); } else { - return new Dimension2DDouble(dim.getWidth(), compressionTransform.transform(dim.getHeight())); + return new Dimension2DDouble(dim.getWidth(), piecewiseAffineTransform.transform(dim.getHeight())); } } + + public HColor getBackcolor() { + return null; + } + } \ No newline at end of file diff --git a/src/net/sourceforge/plantuml/ugraphic/comp/PiecewiseAffineTransform.java b/src/net/sourceforge/plantuml/ugraphic/comp/PiecewiseAffineTransform.java new file mode 100644 index 000000000..65ab00659 --- /dev/null +++ b/src/net/sourceforge/plantuml/ugraphic/comp/PiecewiseAffineTransform.java @@ -0,0 +1,41 @@ +/* ======================================================================== + * PlantUML : a free UML diagram generator + * ======================================================================== + * + * (C) Copyright 2009-2020, Arnaud Roques + * + * Project Info: http://plantuml.com + * + * If you like this project or if you find it useful, you can support us at: + * + * http://plantuml.com/patreon (only 1$ per month!) + * http://plantuml.com/paypal + * + * This file is part of PlantUML. + * + * PlantUML is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PlantUML distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * + * Original Author: Arnaud Roques + * + * + */ +package net.sourceforge.plantuml.ugraphic.comp; + +public interface PiecewiseAffineTransform { + + public double transform(double v); +} diff --git a/src/net/sourceforge/plantuml/ugraphic/comp/SlotFinder.java b/src/net/sourceforge/plantuml/ugraphic/comp/SlotFinder.java index f2352a8fb..5d3cf5c79 100644 --- a/src/net/sourceforge/plantuml/ugraphic/comp/SlotFinder.java +++ b/src/net/sourceforge/plantuml/ugraphic/comp/SlotFinder.java @@ -50,6 +50,7 @@ import net.sourceforge.plantuml.ugraphic.UPath; import net.sourceforge.plantuml.ugraphic.UPolygon; import net.sourceforge.plantuml.ugraphic.URectangle; import net.sourceforge.plantuml.ugraphic.UShape; +import net.sourceforge.plantuml.ugraphic.UShapeIgnorableForCompression; import net.sourceforge.plantuml.ugraphic.UStroke; import net.sourceforge.plantuml.ugraphic.UText; import net.sourceforge.plantuml.ugraphic.UTranslate; @@ -108,32 +109,30 @@ public class SlotFinder implements UGraphic { return new UParamNull(); } - public void draw(UShape shape) { + public void draw(UShape sh) { final double x = translate.getDx(); final double y = translate.getDy(); - if (shape instanceof URectangle) { - final URectangle rect = (URectangle) shape; - if (mode == CompressionMode.ON_X && rect.isIgnoreForCompression()) { - drawRectangle(x, y, new URectangle(2, rect.getHeight())); - drawRectangle(x + rect.getWidth() - 2, y, new URectangle(2, rect.getHeight())); + if (sh instanceof UShapeIgnorableForCompression) { + final UShapeIgnorableForCompression shape = (UShapeIgnorableForCompression) sh; + if (shape.isIgnoreForCompressionOn(mode)) { + shape.drawWhenCompressed(this, mode); return; } - if (mode == CompressionMode.ON_Y && rect.isIgnoreForCompression()) { - drawRectangle(x, y, new URectangle(rect.getWidth(), 2)); - drawRectangle(x, y + rect.getHeight() - 2, new URectangle(rect.getWidth(), 2)); - return; - } - drawRectangle(x, y, (URectangle) shape); - } else if (shape instanceof UPath) { - drawPath(x, y, (UPath) shape); - } else if (shape instanceof UPolygon) { - drawPolygon(x, y, (UPolygon) shape); - } else if (shape instanceof UEllipse) { - drawEllipse(x, y, (UEllipse) shape); - } else if (shape instanceof UText) { - drawText(x, y, (UText) shape); - } else if (shape instanceof UEmpty) { - drawEmpty(x, y, (UEmpty) shape); + + } + if (sh instanceof URectangle) { + drawRectangle(x, y, (URectangle) sh); + } else if (sh instanceof UPath) { + drawPath(x, y, (UPath) sh); + } else if (sh instanceof UPolygon) { + drawPolygon(x, y, (UPolygon) sh); + } else if (sh instanceof UEllipse) { + drawEllipse(x, y, (UEllipse) sh); + } else if (sh instanceof UText) { + final UText text = (UText) sh; + drawText(x, y, text); + } else if (sh instanceof UEmpty) { + drawEmpty(x, y, (UEmpty) sh); } } @@ -173,7 +172,7 @@ public class SlotFinder implements UGraphic { } private void drawPolygon(double x, double y, UPolygon shape) { - if (mode == shape.isIgnoreForCompression()) { + if (mode == shape.getCompressionMode()) { return; } if (mode == CompressionMode.ON_X) { diff --git a/src/net/sourceforge/plantuml/ugraphic/comp/UGraphicCompressOnXorY.java b/src/net/sourceforge/plantuml/ugraphic/comp/UGraphicCompressOnXorY.java index ea01395eb..94dc6654b 100644 --- a/src/net/sourceforge/plantuml/ugraphic/comp/UGraphicCompressOnXorY.java +++ b/src/net/sourceforge/plantuml/ugraphic/comp/UGraphicCompressOnXorY.java @@ -35,6 +35,8 @@ */ package net.sourceforge.plantuml.ugraphic.comp; +import net.sourceforge.plantuml.activitydiagram3.ftile.CenteredText; +import net.sourceforge.plantuml.graphic.TextBlock; import net.sourceforge.plantuml.graphic.UGraphicDelegator; import net.sourceforge.plantuml.ugraphic.UChange; import net.sourceforge.plantuml.ugraphic.UChangeBackColor; @@ -50,7 +52,8 @@ public class UGraphicCompressOnXorY extends UGraphicDelegator { public UGraphic apply(UChange change) { if (change instanceof UTranslate) { - return new UGraphicCompressOnXorY(mode, getUg(), compressionTransform, translate.compose((UTranslate) change)); + return new UGraphicCompressOnXorY(mode, getUg(), compressionTransform, + translate.compose((UTranslate) change)); } else if (change instanceof UStroke || change instanceof UChangeBackColor || change instanceof UChangeColor) { return new UGraphicCompressOnXorY(mode, getUg().apply(change), compressionTransform, translate); } @@ -58,14 +61,19 @@ public class UGraphicCompressOnXorY extends UGraphicDelegator { } private final CompressionMode mode; - private final CompressionTransform compressionTransform; + private final PiecewiseAffineTransform compressionTransform; private final UTranslate translate; - public UGraphicCompressOnXorY(CompressionMode mode, UGraphic ug, CompressionTransform compressionTransform) { + @Override + public String toString() { + return "UGraphicCompressOnXorY " + mode; + } + + public UGraphicCompressOnXorY(CompressionMode mode, UGraphic ug, PiecewiseAffineTransform compressionTransform) { this(mode, ug, compressionTransform, new UTranslate()); } - private UGraphicCompressOnXorY(CompressionMode mode, UGraphic ug, CompressionTransform compressionTransform, + private UGraphicCompressOnXorY(CompressionMode mode, UGraphic ug, PiecewiseAffineTransform compressionTransform, UTranslate translate) { super(ug); this.mode = mode; @@ -78,24 +86,41 @@ public class UGraphicCompressOnXorY extends UGraphicDelegator { final double y = translate.getDy(); if (shape instanceof URectangle) { final URectangle rect = (URectangle) shape; - if (rect.isIgnoreForCompression()) { - if (mode == CompressionMode.ON_X) { - final double x2 = ct(x + rect.getWidth()); - shape = rect.withWidth(x2 - ct(x)); - } else { - final double y2 = ct(y + rect.getHeight()); - shape = rect.withHeight(y2 - ct(y)); - } + if (mode == CompressionMode.ON_X) { + final double x2 = ct(x + rect.getWidth()); + shape = rect.withWidth(x2 - ct(x)); + } else { + final double y2 = ct(y + rect.getHeight()); + shape = rect.withHeight(y2 - ct(y)); } } + if (shape instanceof CenteredText) { + final CenteredText centeredText = (CenteredText) shape; + final TextBlock text = centeredText.getText(); + final double totalWidth = centeredText.getTotalWidth(); + final double realSpaceWidth; + if (mode == CompressionMode.ON_X) { + realSpaceWidth = ct(x + totalWidth) - ct(x); + } else { + realSpaceWidth = totalWidth; + } + final double textWidth = text.calculateDimension(getStringBounder()).getWidth(); + final double pos = (realSpaceWidth - textWidth) / 2; + text.drawU(getUg().apply(getTranslate(x, y)).apply(UTranslate.dx(pos))); + return; + } if (shape instanceof ULine) { drawLine(x, y, (ULine) shape); } else { - if (mode == CompressionMode.ON_X) { - getUg().apply(new UTranslate(ct(x), y)).draw(shape); - } else { - getUg().apply(new UTranslate(x, ct(y))).draw(shape); - } + getUg().apply(getTranslate(x, y)).draw(shape); + } + } + + private UTranslate getTranslate(final double x, final double y) { + if (mode == CompressionMode.ON_X) { + return new UTranslate(ct(x), y); + } else { + return new UTranslate(x, ct(y)); } } diff --git a/src/net/sourceforge/plantuml/version/Version.java b/src/net/sourceforge/plantuml/version/Version.java index 10a8b319e..cab610f7f 100644 --- a/src/net/sourceforge/plantuml/version/Version.java +++ b/src/net/sourceforge/plantuml/version/Version.java @@ -43,7 +43,7 @@ public class Version { private static final int MAJOR_SEPARATOR = 1000000; public static int version() { - return 1202004; + return 1202006; } public static int versionPatched() { @@ -92,7 +92,7 @@ public class Version { } public static long compileTime() { - return 1584613009502L; + return 1586090312827L; } public static String compileTimeString() { diff --git a/stdlib/logos-abx.repx b/stdlib/logos-abx.repx index 1daaef512..58a01eb95 100644 Binary files a/stdlib/logos-abx.repx and b/stdlib/logos-abx.repx differ diff --git a/stdlib/logos-dex.repx b/stdlib/logos-dex.repx index 02e926f9e..b419225b6 100644 Binary files a/stdlib/logos-dex.repx and b/stdlib/logos-dex.repx differ