1 19 20 26 27 package org.netbeans.modules.editor.lib2.highlighting; 28 29 import java.lang.reflect.InvocationTargetException ; 30 import java.lang.reflect.Method ; 31 import java.util.ArrayList ; 32 import java.util.ConcurrentModificationException ; 33 import java.util.Enumeration ; 34 import java.util.Random ; 35 import javax.swing.text.AttributeSet ; 36 import javax.swing.text.Document ; 37 import javax.swing.text.PlainDocument ; 38 import javax.swing.text.Position ; 39 import javax.swing.text.SimpleAttributeSet ; 40 import org.netbeans.api.editor.settings.AttributesUtilities; 41 import org.netbeans.spi.editor.highlighting.support.PositionsBag; 42 import org.netbeans.spi.editor.highlighting.HighlightsContainer; 43 import org.netbeans.spi.editor.highlighting.HighlightsChangeEvent; 44 import org.netbeans.spi.editor.highlighting.HighlightsChangeListener; 45 import org.netbeans.spi.editor.highlighting.HighlightsSequence; 46 import org.netbeans.junit.NbTestCase; 47 48 52 public class CompoundHighlightsContainerTest extends NbTestCase { 53 54 public CompoundHighlightsContainerTest(String testName) { 55 super(testName); 56 } 57 58 public void testSimple() { 59 PlainDocument doc = new PlainDocument (); 60 HighlightsContainer layer = createRandomBag(doc, "layer"); 61 HighlightsSequence highlights = layer.getHighlights(0, 100); 62 63 CompoundHighlightsContainer proxyLayer = new CompoundHighlightsContainer(doc, new HighlightsContainer [] { layer }); 64 HighlightsSequence proxyHighlights = proxyLayer.getHighlights(0, 100); 65 66 for ( ; highlights.moveNext(); ) { 67 if (highlights.getStartOffset() == highlights.getEndOffset()) { 69 continue; 70 } 71 72 assertTrue("Wrong number of proxy highlights", proxyHighlights.moveNext()); 73 74 assertEquals("Start offset does not match", highlights.getStartOffset(), proxyHighlights.getStartOffset()); 75 assertEquals("End offset does not match", highlights.getEndOffset(), proxyHighlights.getEndOffset()); 76 assertTrue("Attributes do not match", highlights.getAttributes().isEqual(proxyHighlights.getAttributes())); 77 } 78 } 79 80 public void testOrdering() { 81 PlainDocument doc = new PlainDocument (); 82 PositionsBag hsA = new PositionsBag(doc); 83 PositionsBag hsB = new PositionsBag(doc); 84 85 SimpleAttributeSet attribsA = new SimpleAttributeSet (); 86 SimpleAttributeSet attribsB = new SimpleAttributeSet (); 87 88 attribsA.addAttribute("attribute", "value-A"); 89 attribsB.addAttribute("attribute", "value-B"); 90 91 hsA.addHighlight(new SimplePosition(5), new SimplePosition(15), attribsA); 92 hsB.addHighlight(new SimplePosition(10), new SimplePosition(20), attribsB); 93 94 CompoundHighlightsContainer chc = new CompoundHighlightsContainer(doc, new HighlightsContainer [] { hsA, hsB }); 95 HighlightsSequence highlights = chc.getHighlights(0, Integer.MAX_VALUE); 96 97 assertTrue("Wrong number of highlights", highlights.moveNext()); 98 assertEquals("1. highlight - wrong attribs", "value-A", highlights.getAttributes().getAttribute("attribute")); 99 100 assertTrue("Wrong number of highlights", highlights.moveNext()); 101 assertEquals("2. highlight - wrong attribs", "value-B", highlights.getAttributes().getAttribute("attribute")); 102 103 assertTrue("Wrong number of highlights", highlights.moveNext()); 104 assertEquals("3. highlight - wrong attribs", "value-B", highlights.getAttributes().getAttribute("attribute")); 105 } 106 107 public void testConcurrentModification() throws Exception { 108 checkConcurrentModificationOnMethod("moveNext"); 109 checkConcurrentModificationOnMethod("getStartOffset"); 110 checkConcurrentModificationOnMethod("getEndOffset"); 111 checkConcurrentModificationOnMethod("getAttributes"); 112 } 113 114 private void checkConcurrentModificationOnMethod(String methodName) throws Exception { 115 PlainDocument doc = new PlainDocument (); 116 PositionsBag bag = createRandomBag(doc, "layer"); 117 HighlightsContainer [] layers = new HighlightsContainer [] { bag }; 118 119 { 120 CompoundHighlightsContainer hb = new CompoundHighlightsContainer(doc, layers); 121 HighlightsSequence hs = hb.getHighlights(0, Integer.MAX_VALUE); 122 123 hb.setLayers(doc, layers); 125 126 Throwable exc = null; 127 try { 128 Method m = hs.getClass().getMethod(methodName); 129 m.invoke(hs); 130 } catch (InvocationTargetException e) { 131 exc = e.getCause(); 132 } 133 134 assertTrue("ConcurrentModificationException has not been thrown from " + 135 methodName + "() after setLayers", exc instanceof ConcurrentModificationException ); 136 } 137 { 138 CompoundHighlightsContainer hb = new CompoundHighlightsContainer(doc, layers); 139 HighlightsSequence hs = hb.getHighlights(0, Integer.MAX_VALUE); 140 141 bag.addHighlight(new SimplePosition(20), new SimplePosition(30), SimpleAttributeSet.EMPTY); 143 144 Throwable exc = null; 145 try { 146 Method m = hs.getClass().getMethod(methodName); 147 m.invoke(hs); 148 } catch (InvocationTargetException e) { 149 exc = e.getCause(); 150 } 151 152 assertTrue("ConcurrentModificationException has not been thrown from " + 153 methodName + "() after changing the original bag", 154 exc instanceof ConcurrentModificationException ); 155 } 156 } 157 158 public void testRandomMerging() { 159 String [] layerNames = new String [] { 160 "layer-1", 161 "layer-2", 162 "layer-3", 163 }; 164 165 PlainDocument doc = new PlainDocument (); 166 HighlightsContainer [] layers = new HighlightsContainer [layerNames.length]; 167 for(int i = 0; i < layers.length; i++) { 168 layers[i] = createRandomBag(doc, layerNames[i]); 169 }; 170 171 CompoundHighlightsContainer proxyLayer = new CompoundHighlightsContainer(doc, layers); 172 173 for (int pointer = 0; pointer <= 100; pointer++) { 174 175 String failMsg = null; 177 Highlight [] highestPair = new Highlight [] { null, null }; 178 Highlight [] proxyPair = new Highlight [] { null, null }; 179 180 181 try { 182 highestPair = new Highlight [] { null, null }; 183 proxyPair = new Highlight [] { null, null }; 184 185 ArrayList <AttributeSet > leftHighlights = new ArrayList <AttributeSet >(); 187 ArrayList <AttributeSet > rightHighlights = new ArrayList <AttributeSet >(); 188 for (int i = 0; i < layers.length; i++) { 189 Highlight [] layerPair = findPair(pointer, layers[i].getHighlights(0, 100)); 190 if (layerPair[0] != null) { 191 leftHighlights.add(layerPair[0].getAttributes()); 192 } 193 if (layerPair[1] != null) { 194 rightHighlights.add(layerPair[1].getAttributes()); 195 } 196 } 197 198 if (!leftHighlights.isEmpty()) { 199 highestPair[0] = new Highlight(pointer, pointer, AttributesUtilities.createComposite( 200 leftHighlights.toArray(new AttributeSet [leftHighlights.size()]))); 201 } 202 if (!rightHighlights.isEmpty()) { 203 highestPair[1] = new Highlight(pointer, pointer, AttributesUtilities.createComposite( 204 rightHighlights.toArray(new AttributeSet [rightHighlights.size()]))); 205 } 206 207 proxyPair = findPair(pointer, proxyLayer.getHighlights(0, 100)); 209 210 for (int i = 0; i < 2; i++) { 211 if (highestPair[i] != null && proxyPair[i] != null) { 212 if (!highestPair[i].getAttributes().isEqual(proxyPair[i].getAttributes())) { 214 failMsg = (i == 0 ? "Left" : "Right") + "pair attributes do not match"; 215 } 216 } else if (highestPair[i] != null || proxyPair[i] != null) { 217 failMsg = (i == 0 ? "Left" : "Right") + " highlight doesn't match"; 219 } 220 } 221 } catch (Throwable e) { 222 failMsg = e.getMessage(); 223 } 224 225 if (failMsg != null) { 226 System.out.println("Dumping layers:"); 228 for (int i = 0; i < layers.length; i++) { 229 System.out.println(" layer[" + i + "] = " + layerNames[i] + "{"); 230 for (HighlightsSequence highlights = layers[i].getHighlights(0, 100); highlights.moveNext(); ) { 231 Highlight h = copyCurrentHighlight(highlights); 232 System.out.println(" " + dumpHighlight(h)); 233 } 234 System.out.println(" } End of layer[" + i + "] -----------------"); 235 } 236 System.out.println("Dumping proxy layer: {"); 237 for (HighlightsSequence proxyHighlights = proxyLayer.getHighlights(0, 100); proxyHighlights.moveNext(); ) { 238 Highlight h = copyCurrentHighlight(proxyHighlights); 239 System.out.println(" " + dumpHighlight(h)); 240 } 241 System.out.println("} End of proxy layer -----------------------"); 242 243 System.out.println("highest pair (pos = " + pointer + ") : " + dumpHighlight(highestPair[0]) + ", " + dumpHighlight(highestPair[1])); 245 System.out.println(" proxy pair (pos = " + pointer + ") : " + dumpHighlight(proxyPair[0]) + ", " + dumpHighlight(proxyPair[1])); 246 247 fail(failMsg + " (position = " + pointer + ")"); 248 } 249 } 250 } 251 252 public void testEvents() { 253 PlainDocument doc = new PlainDocument (); 254 PositionsBag hsA = new PositionsBag(doc); 255 PositionsBag hsB = new PositionsBag(doc); 256 257 CompoundHighlightsContainer chc = new CompoundHighlightsContainer(doc, new HighlightsContainer [] { hsA, hsB }); 258 Listener listener = new Listener(); 259 chc.addHighlightsChangeListener(listener); 260 261 hsA.addHighlight(new SimplePosition(10), new SimplePosition(20), new SimpleAttributeSet ()); 262 assertEquals("Wrong number of events", 1, listener.eventsCnt); 263 assertEquals("Wrong change start offset", 10, listener.lastEventStartOffset); 264 assertEquals("Wrong change end offset", 20, listener.lastEventEndOffset); 265 266 listener.reset(); 267 hsB.addHighlight(new SimplePosition(11), new SimplePosition(12), new SimpleAttributeSet ()); 268 assertEquals("Wrong number of events", 1, listener.eventsCnt); 269 assertEquals("Wrong change start offset", 11, listener.lastEventStartOffset); 270 assertEquals("Wrong change end offset", 12, listener.lastEventEndOffset); 271 } 272 273 public void testEvents2() { 274 PlainDocument doc = new PlainDocument (); 275 PositionsBag hsA = new PositionsBag(doc); 276 PositionsBag hsB = new PositionsBag(doc); 277 278 hsA.addHighlight(new SimplePosition(10), new SimplePosition(20), new SimpleAttributeSet ()); 279 hsB.addHighlight(new SimplePosition(11), new SimplePosition(12), new SimpleAttributeSet ()); 280 281 CompoundHighlightsContainer chc = new CompoundHighlightsContainer(); 282 Listener listener = new Listener(); 283 chc.addHighlightsChangeListener(listener); 284 285 chc.setLayers(doc, new HighlightsContainer [] { hsA, hsB }); 287 assertEquals("Wrong number of events", 1, listener.eventsCnt); 288 assertEquals("Wrong change start offset", 0, listener.lastEventStartOffset); 289 assertEquals("Wrong change end offset", Integer.MAX_VALUE, listener.lastEventEndOffset); 290 } 291 292 private Highlight [] findPair(int offset, HighlightsSequence highlights) { 293 Highlight left = null; 294 Highlight right = null; 295 296 for ( ; highlights.moveNext(); ) { 297 if (highlights.getStartOffset() == highlights.getEndOffset()) { 298 continue; 300 } 301 302 if (offset > highlights.getStartOffset() && offset < highlights.getEndOffset()) { 303 left = right = copyCurrentHighlight(highlights); 304 } else if (offset == highlights.getEndOffset()) { 305 left = copyCurrentHighlight(highlights); 306 } else if (offset == highlights.getStartOffset()) { 307 right = copyCurrentHighlight(highlights); 308 } 309 } 310 311 return new Highlight [] { left, right }; 312 } 313 314 private Highlight copyCurrentHighlight(HighlightsSequence iterator) { 315 return new Highlight( 316 iterator.getStartOffset(), 317 iterator.getEndOffset(), 318 iterator.getAttributes() 319 ); 320 } 321 322 private String dumpHighlight(Highlight h) { 323 if (h == null) { 324 return "< , , >"; 325 } else { 326 StringBuilder sb = new StringBuilder (); 327 328 sb.append("<"); 329 sb.append(h.getStartOffset()); 330 sb.append(","); 331 sb.append(h.getEndOffset()); 332 sb.append(","); 333 334 Enumeration en = h.getAttributes().getAttributeNames(); 335 while (en.hasMoreElements()) { 336 Object attrName = en.nextElement(); 337 Object attrValue = h.getAttributes().getAttribute(attrName); 338 339 sb.append("'"); 340 sb.append(attrName.toString()); 341 sb.append("' = '"); 342 sb.append(attrValue == null ? "null" : attrValue.toString()); 343 sb.append("'"); 344 if (en.hasMoreElements()) { 345 sb.append(", "); 346 } 347 } 348 349 sb.append(">"); 350 351 return sb.toString(); 352 } 353 } 354 355 private PositionsBag createRandomBag(Document doc, String bagId) { 356 357 PositionsBag bag = new PositionsBag(doc, false); 358 359 Random rand = new Random (System.currentTimeMillis()); 360 int attrIdx = 0; 361 int startOffset = 0; 362 int endOffset = 100; 363 364 int maxGapSize = Math.max((int) (endOffset - startOffset) / 10, 1); 365 int maxHighlightSize = Math.max((int) (endOffset - startOffset) / 2, 1); 366 367 for (int pointer = startOffset + rand.nextInt(maxGapSize); pointer <= endOffset; ) { 368 int highlightSize = rand.nextInt(maxHighlightSize); 369 SimpleAttributeSet attributes = new SimpleAttributeSet (); 370 attributes.addAttribute("AttrName-" + bagId + "-" + attrIdx, "AttrValue"); 371 attrIdx++; 372 373 if (pointer + highlightSize < endOffset) { 374 bag.addHighlight( 375 new SimplePosition(pointer), new SimplePosition(pointer + highlightSize), attributes); 376 } else { 377 bag.addHighlight( 378 new SimplePosition(pointer), new SimplePosition(endOffset), attributes); 379 } 380 381 pointer += highlightSize + rand.nextInt(maxGapSize); 383 } 384 385 return bag; 386 } 387 388 private static final class Highlight { 389 private int startOffset; 390 private int endOffset; 391 private AttributeSet attributes; 392 393 public Highlight(int startOffset, int endOffset, AttributeSet attributes) { 394 this.startOffset = startOffset; 395 this.endOffset = endOffset; 396 this.attributes = attributes; 397 } 398 399 public int getStartOffset() { 400 return startOffset; 401 } 402 403 public void setStartOffset(int startOffset) { 404 this.startOffset = startOffset; 405 } 406 407 public int getEndOffset() { 408 return endOffset; 409 } 410 411 public void setEndOffset(int endOffset) { 412 this.endOffset = endOffset; 413 } 414 415 public AttributeSet getAttributes() { 416 return attributes; 417 } 418 419 public void setAttributes(AttributeSet attributes) { 420 this.attributes = attributes; 421 } 422 423 } 425 private static final class Listener implements HighlightsChangeListener { 426 public int eventsCnt = 0; 427 public int lastEventStartOffset = 0; 428 public int lastEventEndOffset = 0; 429 430 public void highlightChanged(HighlightsChangeEvent event) { 431 eventsCnt++; 432 lastEventStartOffset = event.getStartOffset(); 433 lastEventEndOffset = event.getEndOffset(); 434 } 435 436 public void reset() { 437 eventsCnt = 0; 438 lastEventStartOffset = 0; 439 lastEventEndOffset = 0; 440 } 441 } 443 private static final class SimplePosition implements Position { 444 private int offset; 445 446 public SimplePosition(int offset) { 447 this.offset = offset; 448 } 449 450 public int getOffset() { 451 return offset; 452 } 453 } 455 private void dumpHighlights(HighlightsSequence seq) { 456 System.out.println("Dumping highlights from: " + seq + "{"); 457 while(seq.moveNext()) { 458 System.out.println("<" + seq.getStartOffset() + ", " + seq.getEndOffset() + ", " + seq.getAttributes() + ">"); 459 } 460 System.out.println("} --- End of Dumping highlights from: " + seq + " ---------------------"); 461 } 462 } 463 | Popular Tags |