KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > antlr > works > visualization > graphics > GRenderer


1 /*
2
3 [The "BSD licence"]
4 Copyright (c) 2005 Jean Bovet
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions
9 are met:
10
11 1. Redistributions of source code must retain the above copyright
12 notice, this list of conditions and the following disclaimer.
13 2. Redistributions in binary form must reproduce the above copyright
14 notice, this list of conditions and the following disclaimer in the
15 documentation and/or other materials provided with the distribution.
16 3. The name of the author may not be used to endorse or promote products
17 derived from this software without specific prior written permission.
18
19 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 */

31
32 package org.antlr.works.visualization.graphics;
33
34 import org.antlr.works.visualization.fa.FAAnalysis;
35 import org.antlr.works.visualization.fa.FAState;
36 import org.antlr.works.visualization.fa.FATransition;
37 import org.antlr.works.visualization.graphics.graph.GGraph;
38 import org.antlr.works.visualization.graphics.primitive.GDimension;
39 import org.antlr.works.visualization.graphics.primitive.GPoint;
40 import org.antlr.works.visualization.graphics.shape.GLink;
41 import org.antlr.works.visualization.graphics.shape.GNode;
42
43 import java.util.ArrayList JavaDoc;
44 import java.util.HashMap JavaDoc;
45 import java.util.List JavaDoc;
46 import java.util.Map JavaDoc;
47
48 public class GRenderer {
49
50     protected List JavaDoc<GNode> graphicNodes = new ArrayList JavaDoc<GNode>();
51
52     protected FAAnalysis analysis = new FAAnalysis();
53     protected Map JavaDoc<FAState,GNode> nodes = new HashMap JavaDoc<FAState, GNode>();
54     protected Map JavaDoc<FAState, EOAInfo> endOfAlternativeInfoMap = new HashMap JavaDoc<FAState, EOAInfo>();
55
56     public GRenderer() {
57     }
58
59     /** This method is the entry point of this class. It computes the size and position
60      * of each node and link. The position are literal and are actually evaluated later, when
61      * displaying the syntax diagram.
62      *
63      */

64
65     public synchronized GGraph render(FAState state) {
66         GGraph graph = new GGraph();
67         graph.setDimension(renderSize(state));
68         renderPosition(state);
69
70         /** Mark the last node in order to draw an arrow at the end of the syntax diagram
71          */

72         GNode lastNode = graphicNodes.get(graphicNodes.size()-1);
73         if(lastNode != null)
74             lastNode.lastNodeOfRule = true;
75
76         graph.setNodes((ArrayList JavaDoc<GNode>)((ArrayList JavaDoc)graphicNodes).clone());
77         return graph;
78     }
79
80     public void renderPosition(FAState state) {
81         recursiveRenderPositionNode(state, null, new GPoint());
82     }
83
84     public void recursiveRenderPositionNode(FAState state, FAState endState, GPoint basePoint) {
85         while(state != endState) {
86             GNode node = getNode(state);
87             if(node == null) {
88                 System.err.println("Cannot find SDNode associated with state \""+state+"\"");
89                 return;
90             }
91
92             node.setPosition(basePoint);
93
94             if(state.isAlternative()) {
95                 state = recursiveRenderPositionAlternative(state, basePoint);
96                 basePoint.addX(node.nodeDimension.width+node.linkDimension.width);
97             } else if(state.isSingle()) {
98                 basePoint.addX(node.nodeDimension.width+node.linkDimension.width);
99                 state = state.getNextFirstState();
100             } else {
101                 state = null;
102             }
103         }
104     }
105
106     public FAState recursiveRenderPositionAlternative(FAState state, GPoint basePoint) {
107         FAState alternativeEndState = alternativeEndState(state);
108
109         // This point is used to position each transition
110
GPoint point = new GPoint(basePoint);
111         point.addX(GContext.NODE_WIDTH+GContext.EPSILON_WIDTH);
112
113         GDimension firstAlternativeDimension = null;
114
115         for(int t=0; t<state.getNumberOfTransitions(); t++) {
116             FATransition transition = state.transition(t);
117             GLink link = getNode(state).getLink(transition);
118
119             if(t == 0) {
120                 // We remember here the size of the first transition because if we find later
121
// a "loop" transition, we will have to offset this "loop" by the size of the first
122
// transition (because the "loop" is always drawed above the first transition).
123
firstAlternativeDimension = link.branchDim;
124             }
125
126             if(t > 0 && !transition.loop) {
127                 // Offset the current point for each transition (except for a "loop" because it is
128
// displayed above the first transition)
129
point.addY(GContext.LINE_SPACE);
130                 point.addY(link.branchDim.up);
131             }
132
133             if(transition.target == alternativeEndState) {
134                 // The transition is simply a single transition (epsilon normally).
135
if(transition.loop) {
136                     // If this is a "loop", draw it above the first transition
137
GPoint vp = new GPoint(basePoint);
138                     vp.subY(firstAlternativeDimension.up);
139                     vp.subY(link.branchDim.down);
140
141                     // The "virtual position" is used by the link to know where to display itself
142
// when it has to "curve" (because both start and end point are on the same y-axis value)
143
getNode(state).getLink(transition).setVirtualPosition(vp);
144                 } else {
145                     getNode(state).getLink(transition).setVirtualPosition(point);
146                     point.addY(link.branchDim.down);
147                 }
148             } else {
149                 // The transition is more than a single transition, continue recursively...
150
recursiveRenderPositionNode(transition.target, alternativeEndState, new GPoint(point));
151                 point.addY(link.branchDim.down);
152             }
153         }
154         return alternativeEndState;
155     }
156
157     public GDimension renderSize(FAState state) {
158         graphicNodes.clear();
159         nodes.clear();
160         endOfAlternativeInfoMap.clear();
161         analysis.analyze(state);
162         return recursiveRenderSizeSingle(state, null);
163     }
164
165     public GDimension recursiveRenderSizeSingle(FAState state, FAState endState) {
166         GDimension dimension = new GDimension();
167
168         dimension.addUp(GContext.NODE_UP);
169         dimension.addDown(GContext.NODE_DOWN);
170
171         while(state != endState) {
172             if(state.isAlternative()) {
173                 GDimension altDim = recursiveRenderSizeAlternative(state);
174                 dimension.addWidth(GContext.NODE_WIDTH+altDim.width);
175                 dimension.maxUp(altDim.up);
176                 dimension.maxDown(altDim.down);
177                 state = alternativeEndState(state);
178             } else if(state.isSingle()) {
179                 // Create the first node...
180
GNode n1 = createNode(state);
181
182                 // ... and compute the size of the transition...
183
FATransition transition = state.getFirstTransition();
184                 if(transition.isEpsilon()) {
185                     n1.linkDimension.width = GContext.EPSILON_WIDTH;
186                     n1.linkDimension.up = GContext.EPSILON_UP;
187                     n1.linkDimension.down = GContext.EPSILON_DOWN;
188                 } else {
189                     n1.linkDimension.width = GContext.getBoxWidth(transition.label);
190                     n1.linkDimension.up = GContext.BOX_UP;
191                     n1.linkDimension.down = GContext.BOX_DOWN;
192                 }
193
194                 dimension.addWidth(GContext.NODE_WIDTH+n1.linkDimension.width);
195                 dimension.maxUp(n1.linkDimension.up);
196                 dimension.maxDown(n1.linkDimension.down);
197
198                 // ... then create the target node...
199
state = transition.target;
200                 GNode n2 = createNode(state);
201
202                 // ... and create the link between these two states
203
GLink link = new GLink();
204                 link.transition = transition;
205                 link.target = n2;
206                 n1.addLink(link);
207
208                 if(state == endState) {
209                     // If we have reached the end of an alternative, we must set the "last" flag
210
// to the SDLink in order for it to be correctly rendered on screen.
211
EOAInfo eoa = endOfAlternativeInfoMap.get(state);
212                     if(eoa != null) {
213                         link.setLast(eoa.last);
214                     }
215                 }
216             } else {
217                 dimension.addWidth(GContext.NODE_WIDTH);
218                 state = null;
219             }
220         }
221         return dimension;
222     }
223
224     public GDimension recursiveRenderSizeAlternative(FAState state) {
225         FAState alternativeEndState = alternativeEndState(state);
226
227         GNode norigin = createNode(state);
228
229         GDimension dimension = norigin.linkDimension;
230         dimension.addWidth(GContext.EPSILON_WIDTH);
231
232         GDimension firstTransitionDimension = null;
233
234         for(int t=0; t<state.getNumberOfTransitions(); t++) {
235             FATransition transition = state.transition(t);
236
237             GLink link = new GLink();
238             link.transition = transition;
239             link.target = createNode(transition.target);
240             norigin.addLink(link);
241
242             boolean last = t == state.getNumberOfTransitions()-1;
243             if(t == state.getNumberOfTransitions()-2 && state.transition(t+1).loop) {
244                 // If the last alternative is a loop, consider the last-1 alternative as the last one:
245
// the loop will be displayed above the first transition (up) in order to see it easily
246
// from the other transition(s).
247
last = true;
248             }
249
250             link.setLast(last);
251
252             if(transition.target == alternativeEndState) {
253                 GDimension transitionDimension = new GDimension();
254                 transitionDimension.addUp(GContext.EPSILON_UP);
255                 transitionDimension.addDown(GContext.EPSILON_DOWN);
256                 if(transition.loop)
257                     transitionDimension.addDown(GContext.LINE_SPACE);
258
259                 if(transition.loop) {
260                     link.setBranchDimension(transitionDimension);
261                     dimension.maxUp(firstTransitionDimension.up+transitionDimension.up+transitionDimension.down);
262                 } else {
263                     link.setBranchDimension(transitionDimension);
264                     if(t == 0)
265                         firstTransitionDimension = transitionDimension;
266                     dimension.addDown(transitionDimension.up);
267                     dimension.addDown(transitionDimension.down);
268                 }
269             } else {
270                 endOfAlternativeInfoMap.put(alternativeEndState, new EOAInfo(last));
271                 GDimension transitionDimension = recursiveRenderSizeSingle(transition.target, alternativeEndState);
272
273                 if(((t > 0) || (t == 0 && !state.transition(1).loop)) && !last)
274                     dimension.addDown(GContext.LINE_SPACE);
275
276                 link.setBranchDimension(transitionDimension);
277
278                 transitionDimension.addWidth(GContext.EPSILON_WIDTH);
279                 dimension.maxWidth(transitionDimension.width);
280                 if(t == 0) {
281                     // Remember the size of the first transition
282
firstTransitionDimension = transitionDimension;
283                     // Add its "up" size to the transition "up" size
284
dimension.maxUp(transitionDimension.up);
285                     dimension.addDown(transitionDimension.down);
286                 } else {
287                     dimension.addDown(transitionDimension.up);
288                     dimension.addDown(transitionDimension.down);
289                 }
290             }
291         }
292         return dimension;
293     }
294
295     public GNode createNode(FAState state) {
296         GNode node = getNode(state);
297         if(node == null) {
298             node = new GNode();
299             node.setState(state);
300             graphicNodes.add(node);
301             nodes.put(state, node);
302         }
303         return node;
304     }
305
306     public GNode getNode(FAState state) {
307         return nodes.get(state);
308     }
309
310     public FAState alternativeEndState(FAState alt) {
311         int counter = 1;
312         FAState state = alt;
313         while(state != null) {
314             FATransition transition = state.getFirstTransition();
315             if(transition == null)
316                 break;
317             else
318                 state = transition.target;
319
320             // Note: a state can be both an end-of-alternative and an alternative itself ;-)
321

322             if(analysis.numberOfIncomingTransition(state)>1) {
323                 counter--;
324                 if(counter == 0)
325                     break;
326             }
327
328             if(state.isAlternative())
329                 counter++;
330         }
331         return state;
332     }
333
334     private class EOAInfo {
335         public boolean last = false;
336
337         public EOAInfo(boolean last) {
338             this.last = last;
339         }
340     }
341 }
342
Popular Tags