001/*
002 * Copyright 2006 - 2013
003 *     Stefan Balev     <stefan.balev@graphstream-project.org>
004 *     Julien Baudry    <julien.baudry@graphstream-project.org>
005 *     Antoine Dutot    <antoine.dutot@graphstream-project.org>
006 *     Yoann Pigné      <yoann.pigne@graphstream-project.org>
007 *     Guilhelm Savin   <guilhelm.savin@graphstream-project.org>
008 * 
009 * This file is part of GraphStream <http://graphstream-project.org>.
010 * 
011 * GraphStream is a library whose purpose is to handle static or dynamic
012 * graph, create them from scratch, file or any source and display them.
013 * 
014 * This program is free software distributed under the terms of two licenses, the
015 * CeCILL-C license that fits European law, and the GNU Lesser General Public
016 * License. You can  use, modify and/ or redistribute the software under the terms
017 * of the CeCILL-C license as circulated by CEA, CNRS and INRIA at the following
018 * URL <http://www.cecill.info> or under the terms of the GNU LGPL as published by
019 * the Free Software Foundation, either version 3 of the License, or (at your
020 * option) any later version.
021 * 
022 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
023 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
024 * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
025 * 
026 * You should have received a copy of the GNU Lesser General Public License
027 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
028 * 
029 * The fact that you are presently reading this means that you have had
030 * knowledge of the CeCILL-C and LGPL licenses and that you accept their terms.
031 */
032package org.graphstream.ui.graphicGraph;
033
034import java.util.regex.Matcher;
035import java.util.regex.Pattern;
036
037import org.graphstream.graph.implementations.AbstractElement;
038import org.graphstream.ui.graphicGraph.stylesheet.Selector;
039import org.graphstream.ui.graphicGraph.stylesheet.StyleConstants;
040
041/**
042 * Super class of all graphic node, edge, and sprite elements.
043 * 
044 * <p>
045 * Each graphic element references a style, a graphic graph and has a label.
046 * </p>
047 * 
048 * <p>
049 * The element also defines the basic behaviour to reload the style when needed,
050 * defines abstract methods to set and get the position and bounds in spaces of
051 * the element, and to do appropriate actions when specific predefined
052 * attributes change (most of them starting with the prefix "ui.").
053 * </p>
054 * 
055 * <p>
056 * The graphic element has the ability to store attributes like any other graph
057 * element, however the attributes stored by the graphic element are restricted.
058 * There is a filter on the attribute adding methods that let pass only :
059 * <ul>
060 * <li>All attributes starting with "ui.".</li>
061 * <li>The "x", "y", "z", "xy" and "xyz" attributes.</li>
062 * <li>The "stylesheet" attribute.</li>
063 * <li>The "label" attribute.</li>
064 * </ul>
065 * All other attributes are filtered and not stored. The result is that if the
066 * graphic graph is used as an input (a source of graph events) some attributes
067 * will not pass through the filter.
068 * </p>
069 */
070public abstract class GraphicElement extends AbstractElement {
071        /**
072         * Interface for renderers registered in each style group.
073         */
074        public interface SwingElementRenderer {
075        }
076
077        /**
078         * Graph containing this element.
079         */
080        protected GraphicGraph mygraph;
081
082        /**
083         * The label or null if not specified.
084         */
085        public String label;
086
087        /**
088         * The node style.
089         */
090        public StyleGroup style;
091
092        /**
093         * Associated GUI component.
094         */
095        public Object component;
096
097        /**
098         * Do not show.
099         */
100        public boolean hidden = false;
101
102        /**
103         * New element.
104         */
105        public GraphicElement(String id, GraphicGraph graph) {
106                super(id);
107                this.mygraph = graph;
108        }
109
110        public GraphicGraph myGraph() {
111                return mygraph;
112        }
113
114        @Override
115        protected boolean nullAttributesAreErrors() {
116                return mygraph.nullAttributesAreErrors();
117        }
118
119        /**
120         * Type of selector for the graphic element (Node, Edge, Sprite ?).
121         */
122        public abstract Selector.Type getSelectorType();
123
124        /**
125         * Style group. An style group may reference several elements.
126         * 
127         * @return The style group corresponding to this element.
128         */
129        public StyleGroup getStyle() {
130                return style;
131        }
132
133        /**
134         * Label or null if not set.
135         */
136        public String getLabel() {
137                return label;
138        }
139
140        /**
141         * Abscissa of the element, always in GU (graph units). For edges this is
142         * the X of the "from" node.
143         */
144        public abstract double getX();
145
146        /**
147         * Ordinate of the element, always in GU (graph units). For edges this is
148         * the Y of the "from" node.
149         */
150        public abstract double getY();
151
152        /**
153         * Depth of the element, always in GU (graph units). For edges this is the Z
154         * of the "from" node.
155         */
156        public abstract double getZ();
157
158        /**
159         * The associated GUI component.
160         * 
161         * @return An object.
162         */
163        public Object getComponent() {
164                return component;
165        }
166
167        // Commands
168
169        /**
170         * The graphic element was removed from the graphic graph, clean up.
171         */
172        protected abstract void removed();
173
174        /**
175         * Try to force the element to move at the give location in graph units
176         * (GU). For edges, this may move the two attached nodes.
177         * 
178         * @param x
179         *            The new X.
180         * @param y
181         *            The new Y.
182         * @param z
183         *            the new Z.
184         */
185        public abstract void move(double x, double y, double z);
186
187        /**
188         * Set the GUI component of this element.
189         * 
190         * @param component
191         *            The component.
192         */
193        public void setComponent(Object component) {
194                this.component = component;
195        }
196
197        /**
198         * Handle the "ui.class", "label", "ui.style", etc. attributes.
199         */
200        @Override
201        protected void attributeChanged(AttributeChangeEvent event,
202                        String attribute, Object oldValue, Object newValue) {
203                if (event == AttributeChangeEvent.ADD
204                                || event == AttributeChangeEvent.CHANGE) {
205                        if (attribute.charAt(0) == 'u' && attribute.charAt(1) == 'i') {
206                                if (attribute.equals("ui.class")) {
207                                        mygraph.styleGroups.checkElementStyleGroup(this);
208                                        // mygraph.styleGroups.removeElement( tis );
209                                        // mygraph.styleGroups.addElement( this );
210                                        mygraph.graphChanged = true;
211                                } else if (attribute.equals("ui.label")) {
212                                        label = StyleConstants.convertLabel(newValue);
213                                        mygraph.graphChanged = true;
214                                } else if (attribute.equals("ui.style")) {
215                                        // Cascade the new style in the style sheet.
216
217                                        if (newValue instanceof String) {
218                                                try {
219                                                        mygraph.styleSheet.parseStyleFromString(
220                                                                        new Selector(getSelectorType(), getId(),
221                                                                                        null), (String) newValue);
222                                                } catch (java.io.IOException e) {
223                                                        System.err.printf(
224                                                                        "Error while parsing style for %S '%s' :",
225                                                                        getSelectorType(), getId());
226                                                        System.err.printf("    %s%n", e.getMessage());
227                                                        System.err.printf("    The style was ignored");
228                                                }
229
230                                                mygraph.graphChanged = true;
231                                        } else {
232                                                System.err.printf("ERROR !!%n");
233                                        }
234                                } else if (attribute.equals("ui.hide")) {
235                                        hidden = true;
236                                        mygraph.graphChanged = true;
237                                } else if (attribute.equals("ui.clicked")) {
238                                        style.pushEventFor(this, "clicked");
239                                        mygraph.graphChanged = true;
240                                } else if (attribute.equals("ui.selected")) {
241                                        style.pushEventFor(this, "selected");
242                                        mygraph.graphChanged = true;
243                                } else if (attribute.equals("ui.color")) {
244                                        style.pushElementAsDynamic(this);
245                                        mygraph.graphChanged = true;
246                                } else if (attribute.equals("ui.size")) {
247                                        style.pushElementAsDynamic(this);
248                                        mygraph.graphChanged = true;
249                                } else if (attribute.equals("ui.icon")) {
250                                        mygraph.graphChanged = true;
251                                }
252                                // else if( attribute.equals( "ui.state" ) )
253                                // {
254                                // if( newValue == null )
255                                // state = null;
256                                // else if( newValue instanceof String )
257                                // state = (String) newValue;
258                                // }
259                        } else if (attribute.equals("label")) {
260                                label = StyleConstants.convertLabel(newValue);
261                                mygraph.graphChanged = true;
262                        }
263                } else // REMOVE
264                {
265                        if (attribute.charAt(0) == 'u' && attribute.charAt(1) == 'i') {
266                                if (attribute.equals("ui.class")) {
267                                        Object o = attributes.remove("ui.class"); // Not yet removed
268                                                                                                                                // at
269                                                                                                                                // this point !
270                                        mygraph.styleGroups.checkElementStyleGroup(this);
271                                        attributes.put("ui.class", o);
272                                        mygraph.graphChanged = true;
273                                } else if (attribute.equals("ui.label")) {
274                                        label = "";
275                                        mygraph.graphChanged = true;
276                                } else if (attribute.equals("ui.hide")) {
277                                        hidden = false;
278                                        mygraph.graphChanged = true;
279                                } else if (attribute.equals("ui.clicked")) {
280                                        style.popEventFor(this, "clicked");
281                                        mygraph.graphChanged = true;
282                                } else if (attribute.equals("ui.selected")) {
283                                        style.popEventFor(this, "selected");
284                                        mygraph.graphChanged = true;
285                                } else if (attribute.equals("ui.color")) {
286                                        style.popElementAsDynamic(this);
287                                        mygraph.graphChanged = true;
288                                } else if (attribute.equals("ui.size")) {
289                                        style.popElementAsDynamic(this);
290                                        mygraph.graphChanged = true;
291                                }
292                        } else if (attribute.equals("label")) {
293                                label = "";
294                                mygraph.graphChanged = true;
295                        }
296                }
297        }
298
299        // Overriding of standard attribute changing to filter them.
300
301        protected static Pattern acceptedAttribute;
302
303        static {
304                acceptedAttribute = Pattern
305                                .compile("(ui[.].*)|(layout[.].*)|x|y|z|xy|xyz|label|stylesheet");
306        }
307
308        @Override
309        public void addAttribute(String attribute, Object... values) {
310                Matcher matcher = acceptedAttribute.matcher(attribute);
311
312                if (matcher.matches())
313                        super.addAttribute(attribute, values);
314        }
315}