KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > editor > lib2 > highlighting > ProxyHighlightsContainer


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.editor.lib2.highlighting;
21
22 import java.lang.ref.WeakReference JavaDoc;
23 import java.util.ArrayList JavaDoc;
24 import java.util.ConcurrentModificationException JavaDoc;
25 import java.util.NoSuchElementException JavaDoc;
26 import java.util.logging.Logger JavaDoc;
27 import javax.swing.text.AttributeSet JavaDoc;
28 import org.netbeans.api.editor.settings.AttributesUtilities;
29 import org.netbeans.spi.editor.highlighting.HighlightsChangeEvent;
30 import org.netbeans.spi.editor.highlighting.HighlightsChangeListener;
31 import org.netbeans.spi.editor.highlighting.HighlightsContainer;
32 import org.netbeans.spi.editor.highlighting.HighlightsSequence;
33 import org.netbeans.spi.editor.highlighting.support.AbstractHighlightsContainer;
34
35 /**
36  *
37  * @author Vita Stejskal, Miloslav Metelka
38  */

39 public final class ProxyHighlightsContainer extends AbstractHighlightsContainer {
40
41     private static final Logger JavaDoc LOG = Logger.getLogger(ProxyHighlightsContainer.class.getName());
42     
43     private HighlightsContainer[] layers;
44     private long version = 0;
45
46     private final String JavaDoc LOCK = new String JavaDoc("ProxyHighlightsContainer.LOCK"); //NOI18N
47
private final LayerListener listener = new LayerListener(this);
48
49     public ProxyHighlightsContainer() {
50         this(null);
51     }
52     
53     public ProxyHighlightsContainer(HighlightsContainer[] layers) {
54         setLayers(layers);
55     }
56     
57     /**
58      * Gets the list of <code>Highlight</code>s from this layer in the specified
59      * area. The highlights are obtained as a merge of the highlights from all the
60      * delegate layers. The following rules must hold true for the parameters
61      * passed in:
62      *
63      * <ul>
64      * <li>0 <= <code>startOffset</code> <= <code>endOffset</code></li>
65      * <li>0 <= <code>endOffset</code> <= <code>document.getLength() - 1<code></li>
66      * <li>Optionally, <code>endOffset</code> can be equal to Integer.MAX_VALUE
67      * in which case all available highlights will be returned.</li>
68      * </ul>
69      *
70      * @param startOffset The beginning of the area.
71      * @param endOffset The end of the area.
72      *
73      * @return The <code>Highlight</code>s in the area between <code>startOffset</code>
74      * and <code>endOffset</code>.
75      */

76     public HighlightsSequence getHighlights(int startOffset, int endOffset) {
77         assert 0 <= startOffset : "offsets must be greater than or equal to zero"; //NOI18N
78
assert startOffset <= endOffset : "startOffset must be less than or equal to endOffset; " + //NOI18N
79
"startOffset = " + startOffset + " endOffset = " + endOffset; //NOI18N
80

81         synchronized (LOCK) {
82             if (layers == null || layers.length == 0 || startOffset == endOffset) {
83                 return HighlightsSequence.EMPTY;
84             }
85         
86             HighlightsSequence seq [] = new HighlightsSequence[layers.length];
87
88             for(int i = 0; i < layers.length; i++) {
89                 seq[i] = layers[layers.length - i - 1].getHighlights(startOffset, endOffset);
90             }
91
92             return new ProxySeq(version, seq, startOffset, endOffset);
93         }
94     }
95
96     /**
97      * Gets the delegate layers.
98      *
99      * @return The layers, which this proxy layer delegates to.
100      */

101     public HighlightsContainer[] getLayers() {
102         synchronized (LOCK) {
103             return layers;
104         }
105     }
106     
107     /**
108      * Sets the delegate layers. The layers are merged in the same order in which
109      * they appear in the array passed into this method. That means that the first
110      * layer in the array is the less important (i.e. the bottom of the z-order) and
111      * the last layer in the array is the most visible one (i.e. the top of the z-order).
112      *
113      * <p>If you want the layers to be merged according to their real z-order sort
114      * the array first by using <code>ZOrder.sort()</code>.
115      *
116      * @param layers The new delegate layers. Can be <code>null</code>.
117      * @see org.netbeans.api.editor.view.ZOrder#sort(HighlightLayer [])
118      */

119     public void setLayers(HighlightsContainer[] layers) {
120         synchronized (LOCK) {
121             // Remove the listener from the current layers
122
if (this.layers != null) {
123                 for (int i = 0; i < this.layers.length; i++) {
124                     this.layers[i].removeHighlightsChangeListener(listener);
125                 }
126             }
127     
128             this.layers = layers;
129             this.version++;
130
131             // Add the listener to the new layers
132
if (this.layers != null) {
133                 for (int i = 0; i < this.layers.length; i++) {
134                     this.layers[i].addHighlightsChangeListener(listener);
135                 }
136             }
137         }
138         
139         fireHighlightsChange(0, Integer.MAX_VALUE);
140     }
141
142     // ----------------------------------------------------------------------
143
// Private implementation
144
// ----------------------------------------------------------------------
145

146     private void layerChanged(HighlightsContainer layer, int changeStartOffset, int changeEndOffset) {
147         synchronized (LOCK) {
148             version++;
149         }
150         
151         // Fire an event
152
fireHighlightsChange(changeStartOffset, changeEndOffset);
153     }
154
155     private static final class LayerListener implements HighlightsChangeListener {
156         
157         private WeakReference JavaDoc<ProxyHighlightsContainer> ref;
158         
159         public LayerListener(ProxyHighlightsContainer container) {
160             ref = new WeakReference JavaDoc<ProxyHighlightsContainer>(container);
161         }
162         
163         public void highlightChanged(HighlightsChangeEvent event) {
164             ProxyHighlightsContainer container = ref.get();
165             if (container != null) {
166                 container.layerChanged(
167                     (HighlightsContainer)event.getSource(),
168                     event.getStartOffset(),
169                     event.getEndOffset());
170             }
171         }
172     } // End of Listener class
173

174     private final class ProxySeq implements HighlightsSequence {
175         
176         private Sequence2Marks [] marks;
177         private int index1 = -1;
178         private int index2 = -1;
179         private AttributeSet JavaDoc compositeAttributes = null;
180         private long version;
181         
182         public ProxySeq(long version, HighlightsSequence [] seq, int startOffset, int endOffset) {
183             this.version = version;
184             
185             // Initialize marks
186
marks = new Sequence2Marks [seq.length];
187             for (int i = 0; i < seq.length; i++) {
188                 marks[i] = new Sequence2Marks(seq[i], startOffset, endOffset);
189                 marks[i].moveNext();
190             }
191             
192             this.index2 = findLowest();
193         }
194
195         public boolean moveNext() {
196             synchronized (ProxyHighlightsContainer.this.LOCK) {
197                 checkVersion();
198
199                 do {
200                     // Move to the next mark
201
index1 = index2;
202                     if (index2 != -1) {
203                         marks[index2].moveNext();
204                         index2 = findLowest();
205                     }
206
207                     if (index1 == -1 || index2 == -1) {
208                         break;
209                     }
210
211                     compositeAttributes = findAttributes();
212                     
213                 } while (compositeAttributes == null);
214                 
215                 return index1 != -1 && index2 != -1;
216             }
217         }
218
219         public int getStartOffset() {
220             synchronized (ProxyHighlightsContainer.this.LOCK) {
221                 checkVersion();
222                 
223                 if (index1 == -1 || index2 == -1) {
224                     throw new NoSuchElementException JavaDoc();
225                 }
226
227                 return marks[index1].getPreviousMarkOffset();
228             }
229         }
230
231         public int getEndOffset() {
232             synchronized (ProxyHighlightsContainer.this.LOCK) {
233                 checkVersion();
234                 
235                 if (index1 == -1 || index2 == -1) {
236                     throw new NoSuchElementException JavaDoc();
237                 }
238
239                 return marks[index2].getMarkOffset();
240             }
241         }
242
243         public AttributeSet JavaDoc getAttributes() {
244             synchronized (ProxyHighlightsContainer.this.LOCK) {
245                 checkVersion();
246                 
247                 if (index1 == -1 || index2 == -1) {
248                     throw new NoSuchElementException JavaDoc();
249                 }
250
251                 return compositeAttributes;
252             }
253         }
254         
255         private int findLowest() {
256             int lowest = Integer.MAX_VALUE;
257             int idx = -1;
258             
259             for(int i = 0; i < marks.length; i++) {
260                 if (marks[i].isFinished()) {
261                     continue;
262                 }
263                 
264                 int offset = marks[i].getMarkOffset();
265                 if (offset < lowest) {
266                     lowest = offset;
267                     idx = i;
268                 }
269             }
270             
271             return idx;
272         }
273
274         private AttributeSet JavaDoc findAttributes() {
275             ArrayList JavaDoc<AttributeSet JavaDoc> list = new ArrayList JavaDoc<AttributeSet JavaDoc>();
276
277             for(int i = 0; i < marks.length; i++) {
278                 if (marks[i].getPreviousMarkAttributes() != null) {
279                     list.add(marks[i].getPreviousMarkAttributes());
280                 }
281             }
282
283             if (!list.isEmpty()) {
284                 return AttributesUtilities.createComposite(list.toArray(new AttributeSet JavaDoc[list.size()]));
285             } else {
286                 return null;
287             }
288         }
289         
290         private void checkVersion() {
291             if (this.version != ProxyHighlightsContainer.this.version) {
292                 throw new ConcurrentModificationException JavaDoc();
293             }
294         }
295     } // End of ProxySeq class
296

297     /* package */ static final class Sequence2Marks {
298         
299         private HighlightsSequence seq;
300         private int startOffset;
301         private int endOffset;
302         
303         private boolean hasNext = false;
304         private boolean useStartOffset = true;
305         private boolean finished = true;
306
307         private int lastEndOffset = -1;
308         
309         private int previousMarkOffset = -1;
310         private AttributeSet JavaDoc previousMarkAttributes = null;
311         
312         public Sequence2Marks(HighlightsSequence seq, int startOffset, int endOffset) {
313             this.seq = seq;
314             this.startOffset = startOffset;
315             this.endOffset = endOffset;
316         }
317         
318         public boolean isFinished() {
319             return finished;
320         }
321         
322         public boolean moveNext() {
323             if (!useStartOffset || hasNext) {
324                 previousMarkOffset = getMarkOffset();
325                 previousMarkAttributes = getMarkAttributes();
326             }
327             
328             if (useStartOffset) {
329                 // Move to the next highlighted area
330
while(true == (hasNext = seq.moveNext())) {
331                     if (seq.getEndOffset() > startOffset) {
332                         break;
333                     }
334                 }
335                 
336                 if (hasNext && seq.getStartOffset() > endOffset) {
337                     hasNext = false;
338                 }
339                 
340                 if (hasNext) {
341                     if (lastEndOffset != -1 && lastEndOffset < seq.getStartOffset()) {
342                         useStartOffset = false;
343                     } else {
344                         lastEndOffset = seq.getEndOffset();
345                     }
346                 } else {
347                     if (lastEndOffset != -1) {
348                         useStartOffset = false;
349                     }
350                 }
351             } else {
352                 if (hasNext) {
353                     lastEndOffset = seq.getEndOffset();
354                 }
355                 useStartOffset = true;
356             }
357             
358             finished = useStartOffset && !hasNext;
359             return !finished;
360         }
361         
362         public int getMarkOffset() {
363             if (finished) {
364                 throw new NoSuchElementException JavaDoc();
365             }
366             
367             return useStartOffset ?
368                 Math.max(startOffset, seq.getStartOffset()) :
369                 Math.min(endOffset, lastEndOffset);
370         }
371         
372         public AttributeSet JavaDoc getMarkAttributes() {
373             if (finished) {
374                 throw new NoSuchElementException JavaDoc();
375             }
376             
377             return useStartOffset ? seq.getAttributes() : null;
378         }
379         
380         public int getPreviousMarkOffset() {
381             return previousMarkOffset;
382         }
383         
384         public AttributeSet JavaDoc getPreviousMarkAttributes() {
385             return previousMarkAttributes;
386         }
387     } // End of Sequence2Marks class
388
}
389
Popular Tags