KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > java > codegen > ContainerSupport


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.java.codegen;
21
22 import java.util.*;
23 import javax.swing.text.Position JavaDoc;
24
25 import org.openide.src.*;
26 import org.openide.text.*;
27
28 import org.netbeans.modules.java.bridge.Binding;
29
30 /**
31  *
32  * @author sdedic
33  * @version
34  */

35 class ContainerSupport implements TextBinding.Container, ContainerImpl {
36     /** Sorted set of children Bindings, sorted by their text position.
37      */

38     TreeSet children;
39
40     ElementBinding parent;
41
42     SourceText source;
43
44     /**
45      * True, if the container should attempt to separate its members using blank newlines.
46      */

47     boolean separateMembers;
48     
49     private static final Comparator CHILD_COMPARATOR = new PositionComparator();
50     
51     private static final boolean DEBUG = false;
52
53     public ContainerSupport(SourceText s, ElementBinding parent) {
54         this.source = s;
55         this.parent = parent;
56         children = createSet(null);
57         // standard behaviour: separate member elements by newlines.
58
separateMembers = true;
59     }
60     
61     private Element getElement() {
62         return parent.getElement();
63     }
64     
65     public boolean isEmpty() {
66         return children == null || children.isEmpty();
67     }
68
69     /**
70      * Returns the first position occupied by the container, or null
71      * if the container is empty.
72      */

73     public PositionRef getFirstPosition() {
74         ElementBinding first;
75         synchronized (this) {
76             if (children.isEmpty())
77                 return null;
78
79             first = (ElementBinding)children.first();
80         }
81         return first.wholeBounds.getBegin();
82     }
83     
84     /**
85      * Returns the last position occupied by the container, or null,
86      * if the container is empty.
87      */

88     public PositionRef getLastPosition() {
89         ElementBinding last;
90         synchronized (this) {
91             if (children.isEmpty())
92                 return null;
93
94             last = (ElementBinding)children.last();
95         }
96         return last.wholeBounds.getEnd();
97     }
98     
99     protected PositionBounds getContainerBounds() {
100         return parent.findContainerBounds(this);
101     }
102     
103     protected TreeSet createSet(Collection c) {
104         TreeSet s = new TreeSet(CHILD_COMPARATOR);
105         if (c != null)
106             s.addAll(c);
107         return s;
108     }
109     
110     public void updateChildren(Collection c) {
111         ElementBinding last = null;
112         ElementBinding first = null;
113         
114         for (Iterator it = c.iterator(); it.hasNext(); ) {
115             ElementBinding b = ((ElementBinding)it.next());
116             if (first == null) {
117                 first = b;
118             }
119             b.containerRef = this;
120             last = b;
121         }
122         children = createSet(c);
123     }
124
125     /**
126      * Finds the next binding, given some anchor one.
127      * @return binding, that follows b, or the first one if b is null.
128      */

129     public synchronized ElementBinding findNext(ElementBinding b) {
130         SortedSet s;
131
132         if (b == null) {
133             if (children.isEmpty())
134                 return null;
135             return (ElementBinding)children.first();
136         }
137         synchronized (source.getTreeLock()) {
138             s = children.tailSet(b);
139         }
140         if (s.size() < 2)
141             return null;
142         Iterator it = s.iterator();
143         it.next();
144         return (ElementBinding)it.next();
145     }
146     
147     public synchronized ElementBinding findPrevious(ElementBinding b) {
148         SortedSet s;
149
150         if (b == null) {
151             if (children.isEmpty())
152                 return null;
153             return (ElementBinding)children.last();
154         }
155         synchronized (source.getTreeLock()) {
156             s = children.headSet(b);
157         }
158         if (s.size() < 1)
159             return null;
160         return (ElementBinding)s.last();
161     }
162     
163     /**
164      * Attempts to find a binding at the given location.
165      */

166     public ElementBinding findBindingAt(int pos) {
167         Integer JavaDoc posObj = new Integer JavaDoc(pos);
168         SortedSet s = children.tailSet(posObj);
169         if (s == null || s.isEmpty())
170             return null;
171         return ((ElementBinding)s.iterator().next()).findBindingAt(pos);
172     }
173     
174     public ElementBinding findParent() {
175         return parent;
176     }
177
178     /** Determines, if the executing code is allowed to insert after the specified
179      * binding.
180      */

181     public boolean canInsertAfter(Binding b) {
182         if (!source.isAtomicAsUser())
183             return true;
184         
185         ElementBinding previous = (ElementBinding)b;
186         ElementBinding next;
187         PositionRef first;
188         PositionRef last;
189         PositionBounds cb = getContainerBounds();
190
191         if (!source.isGeneratorEnabled() || cb == null)
192             return true;
193         
194         if (previous == null) {
195             // b is the first member of the class (?) --> begin is the class body start.
196
first = source.createPos(cb.getBegin().getOffset(),
197                 Position.Bias.Forward);
198             if (children.isEmpty())
199                 next = null;
200             else
201                 next = (ElementBinding)children.first();
202         } else {
203             // element binding can override the default (textual) behaviour.
204
if (!previous.canInsertAfter())
205                 return false;
206             first = source.createPos(previous.wholeBounds.getEnd().getOffset(),
207                 Position.Bias.Forward);
208             SortedSet s = children.tailSet(previous);
209             Iterator it = s.iterator();
210             it.next();
211             if (it.hasNext()) {
212                 next = (ElementBinding)it.next();
213             } else {
214                 next = null;
215             }
216         }
217         
218         if (next != null) {
219             last = source.createPos(next.wholeBounds.getBegin().getOffset() - 1,
220                 Position.Bias.Forward);
221         } else {
222             int endOffset = cb.getEnd().getOffset();
223             
224             if (endOffset != 0)
225                 endOffset--;
226             last = source.createPos(endOffset,Position.Bias.Forward);
227         }
228         
229         return source.canWriteInside(new PositionBounds(first, last));
230     }
231
232     public void reorder(Map fromToMap) throws SourceException {
233         // disabled
234
}
235     public void replace(Binding oldBinding, Binding newBinding) throws SourceException {
236         // disabled
237
}
238     public void insert(Binding toInitialize, Binding previous) throws SourceException {
239         // disabled
240
}
241
242     /**
243      * Attempts to insert an element between after binding "p"
244      */

245     public void insertChild(ElementBinding n, ElementBinding previous, ElementBinding next,
246         PositionBounds bounds) throws SourceException {
247         boolean emptyBefore, emptyAfter;
248         
249         if (separateMembers) {
250             emptyBefore = emptyAfter = true;
251         } else {
252             emptyBefore = previous == null;
253             emptyAfter = next == null;
254         }
255         if (DEBUG) {
256             System.err.println("Trying to insert " + n + " after " + previous + ", before " + next + " container bounds = " + // NOI18N
257
bounds);
258         }
259         n.create(this, previous, next, bounds, emptyBefore, emptyAfter);
260     }
261     
262     public void insertChild(ElementBinding n, ElementBinding previous, ElementBinding next)
263     throws SourceException {
264         PositionBounds cb = getContainerBounds();
265         if (cb == null) {
266             PositionRef upperBound = parent.getEndPosition();
267             cb = new PositionBounds(upperBound, upperBound);
268         }
269         insertChild(n, previous, next, cb);
270     }
271     
272     private void doReorder(Map reorderMap) throws SourceException {
273         Collection refPositions = new ArrayList(reorderMap.size());
274         
275         for (Iterator it = reorderMap.keySet().iterator(); it.hasNext(); ) {
276             ElementBinding impl = (ElementBinding)it.next();
277             refPositions.add(impl.prepareInsert(null, false));
278         }
279         
280         Iterator it2 = refPositions.iterator();
281         for (Iterator it = reorderMap.entrySet().iterator(); it.hasNext(); ) {
282             Map.Entry en = (Map.Entry)it.next();
283             ElementBinding b2 = (ElementBinding)en.getValue();
284             
285             PositionRef refPos = (PositionRef)it2.next();
286             //b2.moveTo(refPos);
287
}
288     }
289     
290     private void performRemove(MultiPropertyChangeEvent evt)
291     throws SourceException {
292         if (evt == null)
293             return;
294         Collection elems = evt.getAffectedItems();
295         int[] removeIndices = evt.getIndices();
296         boolean empty = ((Object JavaDoc[])evt.getNewValue()).length == 0;
297         int pos = 0;
298         for (Iterator it = elems.iterator(); it.hasNext(); pos++) {
299             Element e = (Element)it.next();
300             ElementBinding victim = source.findBinding(e);
301             boolean collapse;
302             
303             if (this.separateMembers)
304                 collapse = true;
305             else
306                 collapse = empty;
307             victim.remove(collapse, collapse);
308         }
309     }
310     
311     private void performReorder(MultiPropertyChangeEvent evt, int[] offsets)
312     throws SourceException {
313         if (evt == null)
314             return;
315
316         Element[] oldEls = (Element[])evt.getOldValue();
317         Element[] newEls = (Element[])evt.getNewValue();
318         int[] indices = evt.getIndices();
319         Collection refPositions = new ArrayList(oldEls.length);
320         Collection items = new ArrayList(oldEls.length);
321         
322         ElementBinding[] toMove = new ElementBinding[newEls.length];
323         int count = 0;
324         
325         ElementBinding refBinding = null;
326         for (int i = 0; i < indices.length; i++) {
327             if (indices[i] == -1)
328                 // no change in relative pos, or the element was removed.
329
continue;
330             toMove[indices[i]] = source.findBinding(oldEls[i]);
331             /*
332             System.err.println("moving " + oldEls[i] + " from index " + i + // NOI18N
333                 " to index " + indices[i]); // NOI18N
334              */

335             count++;
336         }
337
338         //System.err.println("Moving elements..."); // NOI18N
339
for (int i = 0; i < newEls.length; i++) {
340             if (toMove[i] == null)
341                 continue;
342             
343             ElementBinding previous, next;
344             ElementBinding moving = toMove[i];
345             if (i > 0)
346                 previous = source.findBinding(newEls[i - 1]);
347             else
348                 previous = null;
349             if (i < newEls.length - 1)
350                 next = source.findBinding(newEls[i + 1]);
351             else
352                 next = null;
353             
354             moving.moveTo(previous, next);
355         }
356     }
357
358     private void performInsert(MultiPropertyChangeEvent evt)
359     throws SourceException {
360         if (evt == null)
361             return;
362         Collection inserted = evt.getAffectedItems();
363         int[] indexes = evt.getIndices();
364         Iterator it = inserted.iterator();
365         Element[] newEls = (Element[])evt.getNewValue();
366         int indexPos = 0;
367         int existingPos = -1;
368         Element[] allElems = (Element[])evt.getNewValue();
369         PositionRef upperBound;
370         ElementBinding nextBinding;
371        
372         for (int i = 0; i < inserted.size(); i++) {
373             Element n = (Element)it.next();
374             ElementBinding b = source.findBinding(n);
375             ElementBinding previous;
376             int idx = indexes[indexPos++];
377             
378             if (existingPos < idx) {
379                 int tempIndex = indexPos;
380                 existingPos = idx + 1;
381                 while (existingPos < allElems.length &&
382                        tempIndex < indexes.length &&
383                        existingPos == indexes[tempIndex]) {
384                        existingPos++;
385                        tempIndex++;
386                 }
387             }
388             if (existingPos >= allElems.length) {
389                 nextBinding = null;
390             }
391             else {
392                 nextBinding = source.findBinding(allElems[existingPos]);
393                 upperBound = nextBinding.wholeBounds.getBegin();
394             }
395             if (idx == 0)
396                 previous = null;
397             else {
398                 Element ref = newEls[idx - 1];
399                 previous = source.findBinding(ref);
400             }
401             insertChild(b, previous, nextBinding);
402         }
403     }
404     
405     public static final int OP_REORDER = 0;
406     public static final int OP_INSERT = 1;
407     public static final int OP_REPLACE = 2;
408     public static final int OP_REMOVE = 3;
409
410     public void changeMembers(final MultiPropertyChangeEvent evt)
411     throws SourceException {
412         if (!source.isGeneratorEnabled())
413             // no changes -- code generation is disabled.
414
return;
415         source.runAtomic(parent.getElement(), new ExceptionRunnable() {
416             public void run() throws Exception JavaDoc {
417                 performChangeMembers(evt);
418             }
419         });
420     }
421     
422     /**
423      * Inserts, removes, reorders or replaces individual items as ordered in the
424      * property change event.
425      * If the operation fails, it *should* rollback the changes.
426      */

427     private void performChangeMembers(MultiPropertyChangeEvent evt)
428         throws SourceException {
429
430         // assumption: a head of an "insertion run" either starts right from the beginning
431
// of the container, or follows another (non-inserted) element. We need to perform
432
// all other operations BEFORE the insert.
433
MultiPropertyChangeEvent removeEv, reorderEv, insertEv;
434
435         Collection evs;
436         int[] offsets;
437         
438         if (evt.getEventType() == evt.TYPE_COMPOUND) {
439             evs = evt.getComponents();
440             offsets = evt.getIndices();
441         } else {
442             evs = Collections.singletonList(evt);
443             offsets = null;
444         }
445         removeEv = reorderEv = insertEv = null;
446         for (Iterator it = evs.iterator(); it.hasNext(); ) {
447             MultiPropertyChangeEvent tmp;
448             
449             tmp = (MultiPropertyChangeEvent)it.next();
450             switch (tmp.getEventType()) {
451                 case MultiPropertyChangeEvent.TYPE_ADD:
452                     insertEv = tmp;
453                     break;
454                 case MultiPropertyChangeEvent.TYPE_REMOVE:
455                     removeEv = tmp;
456                     break;
457                 case MultiPropertyChangeEvent.TYPE_REORDER:
458                     reorderEv = tmp;
459                     break;
460                 default:
461                     throw new IllegalArgumentException JavaDoc("Unknown operation " + // NOI18N
462
tmp.getEventType());
463             }
464         }
465         // remove & insert adjust slot positions to the new state.
466
performRemove(removeEv);
467         performInsert(insertEv);
468         performReorder(reorderEv, offsets);
469         computeChildren((Element[])evt.getNewValue());
470     }
471     
472     private void computeChildren(Element[] els) {
473         ElementBinding[] bindings = new ElementBinding[els.length];
474         
475         for (int i = 0; i < els.length; i++) {
476             bindings[i] = source.findBinding(els[i]);
477         }
478         updateChildren(Arrays.asList(bindings));
479     }
480             
481     
482     private class Writer implements ExceptionRunnable {
483         int operation;
484         Map reorderMap;
485         ElementBinding previous, current;
486         
487         Writer(Map reorder) {
488             this.operation = OP_REORDER;
489             this.reorderMap = reorder;
490         }
491         
492         Writer(ElementBinding prev, ElementBinding toinsert) {
493             this.previous = prev;
494             this.current = toinsert;
495             this.operation = OP_INSERT;
496         }
497         
498         public void run() throws Exception JavaDoc {
499             if (!source.isGeneratorEnabled())
500                 return;
501             switch (operation) {
502                 case OP_INSERT:
503                     /*
504                     doInsert(current, previous);
505                     break;
506                      */

507                 case OP_REORDER:
508                     doReorder(reorderMap);
509                     break;
510                 default:
511                     throw new UnsupportedOperationException JavaDoc("Unknown operation :" + operation); // NOI18N
512
}
513         }
514     }
515 }
516
Popular Tags