1 19 20 package org.netbeans.modules.editor.lib2.highlighting; 21 22 import java.lang.ref.WeakReference ; 23 import java.util.ConcurrentModificationException ; 24 import java.util.logging.Level ; 25 import java.util.logging.Logger ; 26 import javax.swing.text.AttributeSet ; 27 import javax.swing.text.BadLocationException ; 28 import javax.swing.text.Document ; 29 import javax.swing.text.Position ; 30 import org.netbeans.spi.editor.highlighting.HighlightsChangeEvent; 31 import org.netbeans.spi.editor.highlighting.HighlightsChangeListener; 32 import org.netbeans.spi.editor.highlighting.HighlightsContainer; 33 import org.netbeans.spi.editor.highlighting.HighlightsSequence; 34 import org.netbeans.spi.editor.highlighting.support.AbstractHighlightsContainer; 35 import org.netbeans.spi.editor.highlighting.support.OffsetsBag; 36 37 41 public final class CompoundHighlightsContainer extends AbstractHighlightsContainer { 42 43 private static final Logger LOG = Logger.getLogger(CompoundHighlightsContainer.class.getName()); 44 45 private static final Position MAX_POSITION = new Position () { 46 public int getOffset() { 47 return Integer.MAX_VALUE; 48 } 49 }; 50 51 private static final int MIN_CACHE_SIZE = 128; 52 53 private Document doc; 54 private HighlightsContainer[] layers; 55 private long version = 0; 56 57 private final String LOCK = new String ("CompoundHighlightsContainer.LOCK"); private final LayerListener listener = new LayerListener(this); 59 60 private OffsetsBag cache; 61 private Position cacheLowestPos; 62 private Position cacheHighestPos; 63 64 public CompoundHighlightsContainer() { 65 this(null, null); 66 } 67 68 public CompoundHighlightsContainer(Document doc, HighlightsContainer[] layers) { 69 setLayers(doc, layers); 70 } 71 72 91 public HighlightsSequence getHighlights(int startOffset, int endOffset) { 92 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; 96 synchronized (LOCK) { 97 if (doc == null || layers == null || layers.length == 0 || startOffset == endOffset) { 98 return HighlightsSequence.EMPTY; 99 } 100 101 int [] update = null; 102 103 int lowest = cacheLowestPos == null ? -1 : cacheLowestPos.getOffset(); 104 int highest = cacheHighestPos == null ? -1 : cacheHighestPos.getOffset(); 105 106 if (lowest == -1 || highest == -1) { 107 cache = null; 109 } else { 110 int maxDistance = Math.max(MIN_CACHE_SIZE, highest - lowest); 111 if (endOffset > lowest - maxDistance && endOffset <= highest && startOffset < lowest) { 112 update = new int [] { startOffset, lowest }; 114 } else if (startOffset < highest + maxDistance && startOffset >= lowest && endOffset > highest) { 115 update = new int [] { highest, endOffset }; 117 } else if (startOffset < lowest && endOffset > highest) { 118 update = new int [] { startOffset, lowest, highest, endOffset }; 120 } else if (startOffset >= lowest && endOffset <= highest) { 121 } else { 123 cache = null; 125 } 126 } 127 128 if (cache == null) { 129 cache = new OffsetsBag(doc, true); 130 lowest = highest = -1; 131 update = new int [] { startOffset, endOffset }; 132 } 133 134 if (update != null) { 135 for (int i = 0; i < update.length / 2; i++) { 136 if (update[2 * i + 1] - update[2 * i] < MIN_CACHE_SIZE) { 137 update[2 * i + 1] = update[2 * i] + MIN_CACHE_SIZE; 138 if (update[2 * i + 1] >= doc.getLength()) { 139 update[2 * i + 1] = Integer.MAX_VALUE; 140 } 141 } 142 143 updateCache(update[2 * i], update[2 * i + 1]); 144 145 if (update[2 * i + 1] == Integer.MAX_VALUE) { 146 break; 147 } 148 } 149 150 if (lowest == -1 || highest == -1) { 151 cacheLowestPos = createPosition(update[0]); 152 cacheHighestPos = createPosition(update[update.length - 1]); 153 } else { 154 cacheLowestPos = createPosition(Math.min(lowest, update[0])); 155 cacheHighestPos = createPosition(Math.max(highest, update[update.length - 1])); 156 } 157 158 if (LOG.isLoggable(Level.FINE)) { 159 LOG.fine("Cache boundaries: " + "<" + (cacheLowestPos == null ? "-" : cacheLowestPos.getOffset()) + ", " + (cacheHighestPos == null ? "-" : cacheHighestPos.getOffset()) + "> " + "when asked for <" + startOffset + ", " + endOffset + ">"); } 164 } 165 166 return new Seq(version, cache.getHighlights(startOffset, endOffset)); 167 } 168 } 169 170 175 public HighlightsContainer[] getLayers() { 176 synchronized (LOCK) { 177 return layers; 178 } 179 } 180 181 193 public void setLayers(Document doc, HighlightsContainer[] layers) { 194 Document docForEvents = null; 195 196 synchronized (LOCK) { 197 if (doc == null) { 198 assert layers == null : "If doc is null the layers must be null too."; } 200 201 docForEvents = doc != null ? doc : this.doc; 202 203 if (this.layers != null) { 205 for (int i = 0; i < this.layers.length; i++) { 206 this.layers[i].removeHighlightsChangeListener(listener); 207 } 208 } 209 210 this.doc = doc; 211 this.layers = layers; 212 cache = null; 213 version++; 214 215 if (this.layers != null) { 217 for (int i = 0; i < this.layers.length; i++) { 218 this.layers[i].addHighlightsChangeListener(listener); 219 } 220 } 221 } 222 223 if (docForEvents != null) { 224 docForEvents.render(new Runnable () { 225 public void run() { 226 fireHighlightsChange(0, Integer.MAX_VALUE); 227 } 228 }); 229 } 230 } 231 232 public void resetCache() { 233 layerChanged(null, 0, Integer.MAX_VALUE); 234 } 235 236 240 private void layerChanged(HighlightsContainer layer, final int changeStartOffset, final int changeEndOffset) { 241 Document docForEvents = null; 242 243 synchronized (LOCK) { 244 cache = null; 246 version++; 247 248 docForEvents = doc; 249 } 250 251 if (docForEvents != null) { 253 docForEvents.render(new Runnable () { 254 public void run() { 255 fireHighlightsChange(changeStartOffset, changeEndOffset); 256 } 257 }); 258 } 259 } 260 261 private void updateCache(int startOffset, int endOffset) { 262 if (LOG.isLoggable(Level.FINE)) { 263 LOG.fine("Updating cache: <" + startOffset + ", " + endOffset + ">"); } 265 266 for (HighlightsContainer layer : layers) { 267 HighlightsSequence seq = layer.getHighlights(startOffset, endOffset); 268 cache.addAllHighlights(seq); 269 } 270 } 271 272 private Position createPosition(int offset) { 273 try { 274 if (offset == Integer.MAX_VALUE) { 275 return MAX_POSITION; 276 } else { 277 return doc.createPosition(offset); 278 } 279 } catch (BadLocationException e) { 280 LOG.log(Level.WARNING, "Can't create document position: offset = " + offset + ", document.lenght = " + doc.getLength(), e); return null; 283 } 284 } 285 286 private static final class LayerListener implements HighlightsChangeListener { 287 288 private WeakReference <CompoundHighlightsContainer> ref; 289 290 public LayerListener(CompoundHighlightsContainer container) { 291 ref = new WeakReference <CompoundHighlightsContainer>(container); 292 } 293 294 public void highlightChanged(HighlightsChangeEvent event) { 295 CompoundHighlightsContainer container = ref.get(); 296 if (container != null) { 297 container.layerChanged( 298 (HighlightsContainer)event.getSource(), 299 event.getStartOffset(), 300 event.getEndOffset()); 301 } 302 } 303 } 305 private final class Seq implements HighlightsSequence { 306 307 private HighlightsSequence seq; 308 private long version; 309 310 public Seq(long version, HighlightsSequence seq) { 311 this.version = version; 312 this.seq = seq; 313 } 314 315 public boolean moveNext() { 316 synchronized (CompoundHighlightsContainer.this.LOCK) { 317 checkVersion(); 318 319 return seq.moveNext(); 320 } 321 } 322 323 public int getStartOffset() { 324 synchronized (CompoundHighlightsContainer.this.LOCK) { 325 checkVersion(); 326 327 return seq.getStartOffset(); 328 } 329 } 330 331 public int getEndOffset() { 332 synchronized (CompoundHighlightsContainer.this.LOCK) { 333 checkVersion(); 334 335 return seq.getEndOffset(); 336 } 337 } 338 339 public AttributeSet getAttributes() { 340 synchronized (CompoundHighlightsContainer.this.LOCK) { 341 checkVersion(); 342 343 return seq.getAttributes(); 344 } 345 } 346 347 private void checkVersion() { 348 if (this.version != CompoundHighlightsContainer.this.version) { 349 throw new ConcurrentModificationException (); 350 } 351 } 352 } } 354 | Popular Tags |