1 19 20 package org.netbeans.modules.editor.impl.highlighting; 21 22 import java.util.Collection ; 23 import java.util.ConcurrentModificationException ; 24 import java.util.NoSuchElementException ; 25 import java.util.WeakHashMap ; 26 import java.util.logging.Level ; 27 import java.util.logging.Logger ; 28 import javax.swing.event.DocumentEvent ; 29 import javax.swing.event.DocumentListener ; 30 import javax.swing.text.AttributeSet ; 31 import javax.swing.text.BadLocationException ; 32 import javax.swing.text.Document ; 33 import javax.swing.text.SimpleAttributeSet ; 34 import org.netbeans.api.editor.mimelookup.MimeLookup; 35 import org.netbeans.api.editor.mimelookup.MimePath; 36 import org.netbeans.api.editor.settings.FontColorSettings; 37 import org.netbeans.editor.BaseDocument; 38 import org.netbeans.editor.TokenCategory; 39 import org.netbeans.editor.TokenContextPath; 40 import org.netbeans.editor.TokenID; 41 import org.netbeans.editor.TokenItem; 42 import org.netbeans.editor.ext.ExtSyntaxSupport; 43 import org.netbeans.spi.editor.highlighting.HighlightsSequence; 44 import org.netbeans.spi.editor.highlighting.support.AbstractHighlightsContainer; 45 import org.openide.util.Lookup; 46 import org.openide.util.LookupEvent; 47 import org.openide.util.LookupListener; 48 import org.openide.util.WeakListeners; 49 50 54 public final class NonLexerSyntaxHighlighting extends AbstractHighlightsContainer implements DocumentListener , LookupListener { 55 56 private static final Logger LOG = Logger.getLogger(NonLexerSyntaxHighlighting.class.getName()); 57 public static final String LAYER_TYPE_ID = "org.netbeans.modules.editor.oldlibbridge.NonLexerSyntaxHighlighting"; 59 private final Document document; 60 private long version = 0; 61 62 private final MimePath mimePath; 63 private final Lookup.Result<FontColorSettings> lookupResult; 64 private final WeakHashMap <TokenID, AttributeSet > attribsCache = new WeakHashMap <TokenID, AttributeSet >(); 65 66 67 public NonLexerSyntaxHighlighting(Document document, String mimeType) { 68 this.mimePath = MimePath.parse(mimeType); 69 this.lookupResult = MimeLookup.getLookup(mimePath).lookup(new Lookup.Template<FontColorSettings>(FontColorSettings.class)); 70 this.lookupResult.addLookupListener(this); 71 72 this.document = document; 73 this.document.addDocumentListener(WeakListeners.document(this, document)); 74 } 75 76 public HighlightsSequence getHighlights(int startOffset, int endOffset) { 77 synchronized (this) { 78 if (document instanceof BaseDocument) { 79 return new HSImpl(version, (BaseDocument) document, startOffset, endOffset); 80 } else { 81 return HighlightsSequence.EMPTY; 82 } 83 } 84 } 85 86 90 public void insertUpdate(DocumentEvent e) { 91 documentChanged(e.getOffset(), e.getLength()); 92 } 93 94 public void removeUpdate(DocumentEvent e) { 95 documentChanged(e.getOffset(), e.getLength()); 96 } 97 98 public void changedUpdate(DocumentEvent e) { 99 documentChanged(e.getOffset(), e.getLength()); 100 } 101 102 106 public void resultChanged(LookupEvent ev) { 107 synchronized (this) { 108 attribsCache.clear(); 109 version++; 110 } 111 112 document.render(new Runnable () { 113 public void run() { 114 fireHighlightsChange(0, Integer.MAX_VALUE); 115 } 116 }); 117 } 118 119 123 private AttributeSet findAttribs(TokenItem tokenItem) { 124 synchronized (this) { 125 AttributeSet attribs = attribsCache.get(tokenItem.getTokenID()); 126 127 if (attribs == null) { 128 Collection <? extends FontColorSettings> allFcs = lookupResult.allInstances(); 129 if (!allFcs.isEmpty()) { 130 FontColorSettings fcs = allFcs.iterator().next(); 131 132 attribs = findFontAndColors(fcs, tokenItem); 133 if (attribs == null) { 134 attribs = SimpleAttributeSet.EMPTY; 135 } 136 137 attribsCache.put(tokenItem.getTokenID(), attribs); 138 } else { 139 LOG.warning("Can't find FCS for mime path: '" + mimePath.getPath() + "'"); } 141 } 142 143 return attribs == null ? SimpleAttributeSet.EMPTY : attribs; 144 } 145 } 146 147 private static AttributeSet findFontAndColors(FontColorSettings fcs, TokenItem tokenItem) { 148 AttributeSet attribs = null; 149 TokenContextPath tokenContextPath = tokenItem.getTokenContextPath(); 150 151 { 153 String name = tokenContextPath.getFullTokenName(tokenItem.getTokenID()); 154 if (name != null) { 155 attribs = fcs.getTokenFontColors(name); 156 } 157 } 158 159 if (attribs == null) { 161 TokenCategory category = tokenItem.getTokenID().getCategory(); 162 if (category != null) { 163 String categoryName = tokenContextPath.getFullTokenName(category); 164 if (categoryName != null) { 165 attribs = fcs.getTokenFontColors(categoryName); 166 } 167 } 168 } 169 170 return attribs; 171 } 172 173 private void documentChanged(int offset, int lenght) { 174 synchronized (this) { 175 version++; 176 } 177 178 if (LOG.isLoggable(Level.FINE)) { 179 LOG.fine("Document changed: changeStart = " + offset + ", changeEnd = " + (offset + lenght)); } 181 182 if (offset < 0 || offset > document.getLength()) { 183 offset = 0; 184 } 185 186 if (lenght <= 0 || offset + lenght > document.getLength()) { 187 lenght = document.getLength() - offset; 188 } 189 190 fireHighlightsChange(offset, offset + lenght); 191 } 192 193 private final class HSImpl implements HighlightsSequence { 194 195 private final long version; 196 private final BaseDocument baseDocument; 197 private final int startOffset; 198 private final int endOffset; 199 200 private boolean init = false; 201 private TokenItem tokenItem; 202 203 public HSImpl(long version, BaseDocument baseDocument, int startOffset, int endOffset) { 204 this.version = version; 205 this.baseDocument = baseDocument; 206 this.startOffset = startOffset; 207 this.endOffset = endOffset; 208 } 209 210 public boolean moveNext() { 211 if (!init) { 212 init = true; 213 214 try { 215 ExtSyntaxSupport ess = new ExtSyntaxSupport(baseDocument); 216 tokenItem = ess.getTokenChain(startOffset, endOffset); 217 } catch (BadLocationException e) { 218 LOG.log(Level.WARNING, "Can't get token sequence: document " + baseDocument + ", startOffset = " + startOffset + ", endOffset = " + endOffset, e); tokenItem = null; 221 } 222 223 while(null != tokenItem) { 224 if (tokenItem.getOffset() + tokenItem.getImage().length() > startOffset) { 225 break; 226 } 227 228 if (LOG.isLoggable(Level.FINE)) { 229 LOG.fine("Skipping tokenId: " + tokenItem.getTokenID() + ", tokenStart = " + tokenItem.getOffset() + ", tokenEnd = " + (tokenItem.getOffset() + tokenItem.getImage().length()) + ", startOffset = " + startOffset + ", endOffset = " + endOffset ); 235 } 236 237 tokenItem = tokenItem.getNext(); 238 } 239 } else if (tokenItem != null) { 240 tokenItem = tokenItem.getNext(); 241 } 242 243 if (tokenItem != null && tokenItem.getOffset() > endOffset) { 244 tokenItem = null; 245 } 246 247 if (LOG.isLoggable(Level.FINE)) { 248 if (tokenItem != null) { 249 LOG.fine("Next tokenId: " + tokenItem.getTokenID() + ", tokenStart = " + tokenItem.getOffset() + ", tokenEnd = " + (tokenItem.getOffset() + tokenItem.getImage().length()) + ", startOffset = " + startOffset + ", endOffset = " + endOffset ); 255 } else { 256 LOG.fine("Next tokenId: null"); } 258 } 259 260 return tokenItem != null; 261 } 262 263 public int getStartOffset() { 264 synchronized (NonLexerSyntaxHighlighting.this) { 265 checkVersion(); 266 267 if (!init) { 268 throw new NoSuchElementException ("Call moveNext() first."); } else if (tokenItem == null) { 270 throw new NoSuchElementException (); 271 } 272 273 return Math.max(tokenItem.getOffset(), startOffset); 274 } 275 } 276 277 public int getEndOffset() { 278 synchronized (NonLexerSyntaxHighlighting.this) { 279 checkVersion(); 280 281 if (!init) { 282 throw new NoSuchElementException ("Call moveNext() first."); } else if (tokenItem == null) { 284 throw new NoSuchElementException (); 285 } 286 287 return Math.min(tokenItem.getOffset() + tokenItem.getImage().length(), endOffset); 288 } 289 } 290 291 public AttributeSet getAttributes() { 292 synchronized (NonLexerSyntaxHighlighting.this) { 293 checkVersion(); 294 295 if (!init) { 296 throw new NoSuchElementException ("Call moveNext() first."); } else if (tokenItem == null) { 298 throw new NoSuchElementException (); 299 } 300 301 return findAttribs(tokenItem); 302 } 303 } 304 305 private void checkVersion() { 306 if (this.version != NonLexerSyntaxHighlighting.this.version) { 307 throw new ConcurrentModificationException (); 308 } 309 } 310 } 312 } 313 | Popular Tags |