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.stream;
033
034import org.graphstream.stream.SourceBase.ElementType;
035
036import java.lang.annotation.Documented;
037import java.lang.annotation.Retention;
038import java.lang.annotation.RetentionPolicy;
039import java.lang.annotation.Target;
040import java.lang.reflect.InvocationTargetException;
041import java.lang.reflect.Method;
042import java.util.EnumMap;
043import java.util.HashMap;
044
045/**
046 * A sink easily allowing a bind between attribute modifications and method
047 * calls.
048 * 
049 * <pre>
050 * public class MyObject extends AnnotatedSink {
051 *      String a1;
052 *      double a2;
053 * 
054 *      &#064;Bind(&quot;myobject.set.a1&quot;)
055 *      public void setA1(String eventId, Object value) {
056 *              a1 = (String) value;
057 *      }
058 * 
059 *      &#064;Bind(&quot;myobject.set.a2&quot;)
060 *      public void setA2(String eventId, Object value) {
061 *              a2 = (Double) value;
062 *      }
063 * 
064 *      public static void main(String ... args) {
065 *                      Graph g = ...;
066 *                      MyObject obj = new MyObject();
067 * 
068 *                      g.addSink(obj);
069 *                      
070 *                      g.addAttribute("myobject.set.a1", "MyObject A1");
071 *                      g.addAttribute("myobject.set.a2", 100.0);
072 *              }
073 * }
074 * </pre>
075 */
076public abstract class AnnotatedSink implements Sink {
077        /**
078         * Annotation used to bind an event to a method. This bind is composed of a
079         * name (the attribute key) and an element type. For example, the annotation
080         * 
081         * <pre>
082         * @Bind(value = &quot;test&quot;, type = ElementType.NODE)
083         * </pre>
084         * 
085         * will be triggered the annotated method when receiving
086         * 'nodeAttributeXXX()' methods.
087         */
088        @Documented
089        @Retention(RetentionPolicy.RUNTIME)
090        @Target(java.lang.annotation.ElementType.METHOD)
091        public static @interface Bind {
092                /**
093                 * Name of the attribute key that triggered the annotated method.
094                 * 
095                 * @return an attribute key
096                 */
097                String value();
098
099                /**
100                 * Type of element that triggered the annotated method. Default is
101                 * GRAPH.
102                 * 
103                 * @return type of element in GRAPH, NODE or EDGE
104                 */
105                ElementType type() default ElementType.GRAPH;
106        }
107
108        private final EnumMap<ElementType, MethodMap> methods;
109
110        protected AnnotatedSink() {
111                methods = new EnumMap<ElementType, MethodMap>(ElementType.class);
112                methods.put(ElementType.GRAPH, new MethodMap());
113                methods.put(ElementType.EDGE, new MethodMap());
114                methods.put(ElementType.NODE, new MethodMap());
115
116                Method[] ms = getClass().getMethods();
117
118                if (ms != null) {
119                        for (int i = 0; i < ms.length; i++) {
120                                Method m = ms[i];
121                                Bind b = m.getAnnotation(Bind.class);
122                                
123                                if (b != null)
124                                        methods.get(b.type()).put(b.value(), m);
125                        }
126                }
127        }
128
129        private void invoke(Method m, Object... args) {
130                try {
131                        m.invoke(this, args);
132                } catch (IllegalArgumentException e) {
133                        e.printStackTrace();
134                } catch (IllegalAccessException e) {
135                        e.printStackTrace();
136                } catch (InvocationTargetException e) {
137                        e.printStackTrace();
138                }
139        }
140
141        /*
142         * (non-Javadoc)
143         * @see org.graphstream.stream.AttributeSink#edgeAttributeAdded(java.lang.String, long, java.lang.String, java.lang.String, java.lang.Object)
144         */
145        public void edgeAttributeAdded(String sourceId, long timeId, String edgeId,
146                        String attribute, Object value) {
147                Method m = methods.get(ElementType.EDGE).get(attribute);
148
149                if (m != null)
150                        invoke(m, edgeId, attribute, value);
151        }
152
153        /*
154         * (non-Javadoc)
155         * @see org.graphstream.stream.AttributeSink#edgeAttributeChanged(java.lang.String, long, java.lang.String, java.lang.String, java.lang.Object, java.lang.Object)
156         */
157        public void edgeAttributeChanged(String sourceId, long timeId,
158                        String edgeId, String attribute, Object oldValue, Object newValue) {
159                Method m = methods.get(ElementType.EDGE).get(attribute);
160
161                if (m != null)
162                        invoke(m, edgeId, attribute, newValue);
163        }
164
165        /*
166         * (non-Javadoc)
167         * @see org.graphstream.stream.AttributeSink#edgeAttributeRemoved(java.lang.String, long, java.lang.String, java.lang.String)
168         */
169        public void edgeAttributeRemoved(String sourceId, long timeId,
170                        String edgeId, String attribute) {
171                Method m = methods.get(ElementType.EDGE).get(attribute);
172
173                if (m != null)
174                        invoke(m, edgeId, attribute, null);
175        }
176
177        /*
178         * (non-Javadoc)
179         * @see org.graphstream.stream.AttributeSink#graphAttributeAdded(java.lang.String, long, java.lang.String, java.lang.Object)
180         */
181        public void graphAttributeAdded(String sourceId, long timeId,
182                        String attribute, Object value) {
183                Method m = methods.get(ElementType.GRAPH).get(attribute);
184
185                if (m != null)
186                        invoke(m, attribute, value);
187        }
188
189        /*
190         * (non-Javadoc)
191         * @see org.graphstream.stream.AttributeSink#graphAttributeChanged(java.lang.String, long, java.lang.String, java.lang.Object, java.lang.Object)
192         */
193        public void graphAttributeChanged(String sourceId, long timeId,
194                        String attribute, Object oldValue, Object newValue) {
195                Method m = methods.get(ElementType.GRAPH).get(attribute);
196
197                if (m != null)
198                        invoke(m, attribute, newValue);
199        }
200
201        /*
202         * (non-Javadoc)
203         * @see org.graphstream.stream.AttributeSink#graphAttributeRemoved(java.lang.String, long, java.lang.String)
204         */
205        public void graphAttributeRemoved(String sourceId, long timeId,
206                        String attribute) {
207                Method m = methods.get(ElementType.GRAPH).get(attribute);
208
209                if (m != null)
210                        invoke(m, attribute, null);
211        }
212
213        /*
214         * (non-Javadoc)
215         * @see org.graphstream.stream.AttributeSink#nodeAttributeAdded(java.lang.String, long, java.lang.String, java.lang.String, java.lang.Object)
216         */
217        public void nodeAttributeAdded(String sourceId, long timeId, String nodeId,
218                        String attribute, Object value) {
219                Method m = methods.get(ElementType.NODE).get(attribute);
220
221                if (m != null)
222                        invoke(m, nodeId, attribute, value);
223        }
224
225        /*
226         * (non-Javadoc)
227         * @see org.graphstream.stream.AttributeSink#nodeAttributeChanged(java.lang.String, long, java.lang.String, java.lang.String, java.lang.Object, java.lang.Object)
228         */
229        public void nodeAttributeChanged(String sourceId, long timeId,
230                        String nodeId, String attribute, Object oldValue, Object newValue) {
231                Method m = methods.get(ElementType.NODE).get(attribute);
232
233                if (m != null)
234                        invoke(m, nodeId, attribute, newValue);
235        }
236
237        /*
238         * (non-Javadoc)
239         * @see org.graphstream.stream.AttributeSink#nodeAttributeRemoved(java.lang.String, long, java.lang.String, java.lang.String)
240         */
241        public void nodeAttributeRemoved(String sourceId, long timeId,
242                        String nodeId, String attribute) {
243                Method m = methods.get(ElementType.NODE).get(attribute);
244
245                if (m != null)
246                        invoke(m, nodeId, attribute, null);
247        }
248
249        /*
250         * (non-Javadoc)
251         * @see org.graphstream.stream.ElementSink#edgeAdded(java.lang.String, long, java.lang.String, java.lang.String, java.lang.String, boolean)
252         */
253        public void edgeAdded(String sourceId, long timeId, String edgeId,
254                        String fromNodeId, String toNodeId, boolean directed) {
255        }
256
257        /*
258         * (non-Javadoc)
259         * @see org.graphstream.stream.ElementSink#edgeRemoved(java.lang.String, long, java.lang.String)
260         */
261        public void edgeRemoved(String sourceId, long timeId, String edgeId) {
262        }
263
264        /*
265         * (non-Javadoc)
266         * @see org.graphstream.stream.ElementSink#graphCleared(java.lang.String, long)
267         */
268        public void graphCleared(String sourceId, long timeId) {
269        }
270
271        /*
272         * (non-Javadoc)
273         * @see org.graphstream.stream.ElementSink#nodeAdded(java.lang.String, long, java.lang.String)
274         */
275        public void nodeAdded(String sourceId, long timeId, String nodeId) {
276        }
277
278        /*
279         * (non-Javadoc)
280         * @see org.graphstream.stream.ElementSink#nodeRemoved(java.lang.String, long, java.lang.String)
281         */
282        public void nodeRemoved(String sourceId, long timeId, String nodeId) {
283        }
284
285        /*
286         * (non-Javadoc)
287         * @see org.graphstream.stream.ElementSink#stepBegins(java.lang.String, long, double)
288         */
289        public void stepBegins(String sourceId, long timeId, double step) {
290        }
291
292        private static class MethodMap extends HashMap<String, Method> {
293                private static final long serialVersionUID = 1664854698109523697L;
294        }
295}