1 19 20 package org.netbeans.modules.editor.lib2.highlighting; 21 22 import java.lang.ref.WeakReference ; 23 import java.util.ArrayList ; 24 import java.util.ConcurrentModificationException ; 25 import java.util.NoSuchElementException ; 26 import java.util.logging.Logger ; 27 import javax.swing.text.AttributeSet ; 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 39 public final class ProxyHighlightsContainer extends AbstractHighlightsContainer { 40 41 private static final Logger LOG = Logger.getLogger(ProxyHighlightsContainer.class.getName()); 42 43 private HighlightsContainer[] layers; 44 private long version = 0; 45 46 private final String LOCK = new String ("ProxyHighlightsContainer.LOCK"); 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 76 public HighlightsSequence getHighlights(int startOffset, int endOffset) { 77 assert 0 <= startOffset : "offsets must be greater than or equal to zero"; assert startOffset <= endOffset : "startOffset must be less than or equal to endOffset; " + "startOffset = " + startOffset + " endOffset = " + endOffset; 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 101 public HighlightsContainer[] getLayers() { 102 synchronized (LOCK) { 103 return layers; 104 } 105 } 106 107 119 public void setLayers(HighlightsContainer[] layers) { 120 synchronized (LOCK) { 121 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 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 146 private void layerChanged(HighlightsContainer layer, int changeStartOffset, int changeEndOffset) { 147 synchronized (LOCK) { 148 version++; 149 } 150 151 fireHighlightsChange(changeStartOffset, changeEndOffset); 153 } 154 155 private static final class LayerListener implements HighlightsChangeListener { 156 157 private WeakReference <ProxyHighlightsContainer> ref; 158 159 public LayerListener(ProxyHighlightsContainer container) { 160 ref = new WeakReference <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 } 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 compositeAttributes = null; 180 private long version; 181 182 public ProxySeq(long version, HighlightsSequence [] seq, int startOffset, int endOffset) { 183 this.version = version; 184 185 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 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 (); 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 (); 237 } 238 239 return marks[index2].getMarkOffset(); 240 } 241 } 242 243 public AttributeSet getAttributes() { 244 synchronized (ProxyHighlightsContainer.this.LOCK) { 245 checkVersion(); 246 247 if (index1 == -1 || index2 == -1) { 248 throw new NoSuchElementException (); 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 findAttributes() { 275 ArrayList <AttributeSet > list = new ArrayList <AttributeSet >(); 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 [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 (); 293 } 294 } 295 } 297 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 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 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 (); 365 } 366 367 return useStartOffset ? 368 Math.max(startOffset, seq.getStartOffset()) : 369 Math.min(endOffset, lastEndOffset); 370 } 371 372 public AttributeSet getMarkAttributes() { 373 if (finished) { 374 throw new NoSuchElementException (); 375 } 376 377 return useStartOffset ? seq.getAttributes() : null; 378 } 379 380 public int getPreviousMarkOffset() { 381 return previousMarkOffset; 382 } 383 384 public AttributeSet getPreviousMarkAttributes() { 385 return previousMarkAttributes; 386 } 387 } } 389 | Popular Tags |