Source code: javax/ide/menu/spi/PositionMap.java
1 package javax.ide.menu.spi;
2
3 import java.util.ArrayList;
4 import java.util.HashMap;
5 import java.util.Iterator;
6 import java.util.LinkedHashMap;
7 import java.util.List;
8 import java.util.ListIterator;
9 import java.util.Map;
10
11
12
13 /**
14 * A map that tracks Positionable items. Provides utilities for getting the
15 * items in their "resolved" order.
16 */
17 final class PositionMap
18 {
19 // Ordering of items is essentially a graph problem. We build a graph of items
20 // where the (directed) edges are their required positions in relation to each
21 // other. The correct order of the items is a topological sort of this graph.
22
23 private Map _idsToVertices = new LinkedHashMap();
24 private Vertex _currentAncestor;
25
26 private List _sortedList;
27
28 /**
29 * Adds a positionable to the map.
30 *
31 * @param p the positionable to add.
32 */
33 public void add( Positionable p )
34 {
35 // Invalidate the sorted list if required.
36 _sortedList = null;
37
38 Vertex thisVertex = (Vertex) _idsToVertices.get( p.getID() );
39 if ( thisVertex == null )
40 {
41 thisVertex = new Vertex( p );
42 _idsToVertices.put( p.getID(), thisVertex );
43 }
44 }
45
46 private void addToGraph( Vertex thisVertex )
47 {
48 Positionable p = thisVertex.getPositionable();
49
50 String before = p.getBefore();
51 String after = p.getAfter();
52
53 if ( after == null && before == null && _currentAncestor != null )
54 {
55 after = _currentAncestor.getName();
56 }
57
58 if ( after != null )
59 {
60 Vertex afterVertex = (Vertex) _idsToVertices.get( after );
61 if ( afterVertex == null )
62 {
63 afterVertex = new Vertex( after );
64 _idsToVertices.put( after, afterVertex );
65 }
66
67 // Anything after the afterVertex must also be after this vertex.
68 thisVertex.attachAfter( afterVertex.getToEdges() );
69
70 afterVertex.attachAfter( thisVertex );
71
72 if ( thisVertex.isUndefinedReference() )
73 {
74 // Anything before thisVertex (except afterVertex itself)
75 // must also be after afterVertex.
76 for ( Iterator i = thisVertex.getFromEdges(); i.hasNext(); )
77 {
78 Vertex v = (Vertex) i.next();
79 if ( afterVertex != v )
80 {
81 afterVertex.attachAfter( v );
82 }
83 }
84 thisVertex.setPositionable( p );
85 }
86
87 }
88
89 if ( before != null )
90 {
91 Vertex beforeVertex = (Vertex) _idsToVertices.get( before );
92 if ( beforeVertex == null )
93 {
94 beforeVertex = new Vertex( before );
95 _idsToVertices.put( before, beforeVertex );
96 }
97
98 // Anything before the beforeVertex must be before this vertex.
99 thisVertex.attachBefore( beforeVertex.getFromEdges() );
100
101 beforeVertex.attachBefore( thisVertex );
102
103 if ( thisVertex.isUndefinedReference() )
104 {
105 // Anything after thisVertex (except beforeVertex itself)
106 // must also be before beforeVertex.
107 for ( Iterator i = thisVertex.getToEdges(); i.hasNext(); )
108 {
109 Vertex v = (Vertex) i.next();
110 if ( beforeVertex != v )
111 {
112 beforeVertex.attachBefore( v );
113 }
114 }
115 thisVertex.setPositionable( p );
116 }
117 }
118 _currentAncestor = thisVertex;
119 }
120
121 /**
122 * Gets items in the correct, sorted order.
123 *
124 * @return the items in the correct, sorted order.
125 */
126 public List getSortedItems()
127 {
128 if ( _sortedList == null )
129 {
130 _sortedList = sort();
131 }
132 return _sortedList;
133 }
134
135 /**
136 * Get a positionable by its id.
137 *
138 * @param id the id of a positionable
139 * @return the Positionable with the specified id, or null if no such
140 * Positionable has been added.
141 */
142 public Positionable get( String id )
143 {
144 Vertex vertex = (Vertex) _idsToVertices.get( id );
145 if ( vertex == null || vertex.isUndefinedReference() )
146 {
147 return null;
148 }
149 return vertex.getPositionable();
150 }
151
152 /**
153 * Build a sorted list of all items.
154 *
155 * @return a sorted list of all items.
156 */
157 private List sort()
158 {
159 // Build the graph.
160 for ( Iterator i = _idsToVertices.values().iterator(); i.hasNext(); )
161 {
162 Vertex vertex = (Vertex) i.next();
163 addToGraph( vertex );
164 }
165
166 Map colorings = new HashMap();
167 List topo = new ArrayList();
168
169 // Now visit remaining items.
170 for ( Iterator i = _idsToVertices.values().iterator(); i.hasNext(); )
171 {
172 Vertex vertex = (Vertex) i.next();
173 Object color = colorings.get( vertex );
174 if ( color == null )
175 {
176 visit( colorings, vertex, topo );
177 }
178 }
179
180
181 // Reverse the list, removing any unresolved items.
182 ArrayList sorted = new ArrayList( topo.size() );
183 for ( ListIterator li = topo.listIterator( topo.size() ); li.hasPrevious(); )
184 {
185 Vertex v = (Vertex) li.previous();
186 if ( !v.isUndefinedReference() )
187 {
188 sorted.add( v.getPositionable() );
189 }
190 }
191 return sorted;
192 }
193
194 private void visit( Map colorings, Vertex vertex, List topo )
195 {
196 colorings.put( vertex, COLOR_VISITING );
197
198 for ( Iterator children = vertex.getToEdges(); children.hasNext(); )
199 {
200 Vertex child = (Vertex) children.next();
201 Object color = colorings.get( child );
202 if ( color == COLOR_VISITING )
203 {
204 // Cycle detected. Skip this child.
205 continue;
206 }
207 else if ( color != COLOR_VISITED )
208 {
209 visit( colorings, child, topo );
210 }
211 }
212
213 // Have visited all children, now add to the topo list.
214 topo.add( vertex );
215 colorings.put( vertex, COLOR_VISITED );
216 }
217
218
219 private final static Object COLOR_VISITING = "visiting";
220 private final static Object COLOR_VISITED = "visited";
221
222
223
224 /**
225 * A vertex in the graph. Each vertex represents a Positionable and its back
226 * and forward references to other vertices. An undefined reference is a
227 * vertex for which the positionable has not yet been encountered (such cases
228 * occur when an item defines itself to be before or after another item which
229 * has not yet been defined)
230 */
231 private class Vertex
232 {
233 private List _toEdges = new ArrayList();
234 private List _fromEdges = new ArrayList();
235 private final String _name;
236 private Positionable _positionable;
237
238 public Vertex( String name )
239 {
240 _name = name;
241 }
242
243 public Vertex( Positionable positionable )
244 {
245 _name = positionable.getID();
246 _positionable = positionable;
247 }
248
249
250 public void setPositionable( Positionable p )
251 {
252 _positionable = p;
253 }
254
255 public Positionable getPositionable()
256 {
257 return _positionable;
258 }
259
260 public boolean isUndefinedReference()
261 {
262 return _positionable == null;
263 }
264
265 public String getName()
266 {
267 return _name;
268 }
269
270 public Iterator getFromEdges()
271 {
272 return _fromEdges.iterator();
273 }
274
275 public Iterator getToEdges()
276 {
277 return _toEdges.iterator();
278 }
279
280 public void attachBefore( Vertex otherVertex )
281 {
282 if ( otherVertex == this )
283 {
284 throw new IllegalArgumentException( "Cannot connect a vertex to itself" );
285 }
286 _fromEdges.add( otherVertex );
287 otherVertex._toEdges.add( this );
288 }
289
290 public void attachAfter( Vertex otherVertex )
291 {
292 if ( otherVertex == this )
293 {
294 throw new IllegalArgumentException( "Cannot connect a vertex to itself" );
295 }
296 _toEdges.add( otherVertex );
297 otherVertex._fromEdges.add( this );
298 }
299
300 public void attachAfter( Iterator vertices )
301 {
302 while ( vertices.hasNext() )
303 {
304 Vertex v = (Vertex) vertices.next();
305 attachAfter( v );
306 }
307 }
308
309 public void attachBefore( Iterator vertices )
310 {
311 while ( vertices.hasNext() )
312 {
313 Vertex v = (Vertex) vertices.next();
314 attachBefore( v );
315 }
316 }
317 }
318 }