2
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
6
* The contents of this file are subject to the terms of either the GNU
7
* General Public License Version 2 only ("GPL") or the Common
8
* Development and Distribution License("CDDL") (collectively, the
9
* "License"). You may not use this file except in compliance with the
10
* License. You can obtain a copy of the License at
11
* http://www.netbeans.org/cddl-gplv2.html
12
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
* specific language governing permissions and limitations under the
14
* License. When distributing the software, include this License Header
15
* Notice in each file and include the License file at
16
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
17
* particular file as subject to the "Classpath" exception as provided
18
* by Sun in the GPL Version 2 section of the License file that
19
* accompanied this code. If applicable, add the following below the
20
* License Header, with the fields enclosed by brackets [] replaced by
21
* your own identifying information:
22
* "Portions Copyrighted [year] [name of copyright owner]"
26
* The Original Software is NetBeans. The Initial Developer of the Original
27
* Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
28
* Microsystems, Inc. All Rights Reserved.
30
* If you wish your version of this file to be governed by only the CDDL
31
* or only the GPL Version 2, indicate your decision by adding
32
* "[Contributor] elects to include this software in this distribution
33
* under the [CDDL or GPL Version 2] license." If you do not indicate a
34
* single choice of license, a recipient has the option to distribute
35
* your version of this file under either the CDDL, the GPL Version 2 or
36
* to extend the choice of license to its licensees as provided above.
37
* However, if you add GPL Version 2 code and therefore, elected the GPL
38
* Version 2 license, then the option applies only if the new code is
39
* made subject to such option by the copyright holder.
42
package org.netbeans.modules.editor.impl.highlighting;
44
import java.util.ConcurrentModificationException;
45
import java.util.NoSuchElementException;
46
import java.util.WeakHashMap;
47
import java.util.logging.Level;
48
import java.util.logging.Logger;
49
import javax.swing.event.DocumentEvent;
50
import javax.swing.event.DocumentListener;
51
import javax.swing.text.AttributeSet;
52
import javax.swing.text.BadLocationException;
53
import javax.swing.text.Document;
54
import javax.swing.text.SimpleAttributeSet;
55
import org.netbeans.api.editor.mimelookup.MimeLookup;
56
import org.netbeans.api.editor.mimelookup.MimePath;
57
import org.netbeans.api.editor.settings.FontColorSettings;
58
import org.netbeans.editor.BaseDocument;
59
import org.netbeans.editor.TokenCategory;
60
import org.netbeans.editor.TokenContextPath;
61
import org.netbeans.editor.TokenID;
62
import org.netbeans.editor.TokenItem;
63
import org.netbeans.editor.ext.ExtSyntaxSupport;
64
import org.netbeans.spi.editor.highlighting.HighlightsSequence;
65
import org.netbeans.spi.editor.highlighting.support.AbstractHighlightsContainer;
66
import org.openide.util.WeakListeners;
70
* @author Vita Stejskal
72
public final class NonLexerSyntaxHighlighting extends AbstractHighlightsContainer implements DocumentListener {
74
private static final Logger LOG = Logger.getLogger(NonLexerSyntaxHighlighting.class.getName());
75
public static final String LAYER_TYPE_ID = "org.netbeans.modules.editor.oldlibbridge.NonLexerSyntaxHighlighting"; //NOI18N
77
private final Document document;
78
private long version = 0;
80
private final MimePath mimePath;
81
private final WeakHashMap<TokenID, AttributeSet> attribsCache = new WeakHashMap<TokenID, AttributeSet>();
83
/** Creates a new instance of NonLexerSytaxHighlighting */
84
public NonLexerSyntaxHighlighting(Document document, String mimeType) {
85
this.mimePath = MimePath.parse(mimeType);
87
this.document = document;
88
this.document.addDocumentListener(WeakListeners.document(this, document));
91
public HighlightsSequence getHighlights(int startOffset, int endOffset) {
93
if (document instanceof BaseDocument) {
94
return new HSImpl(version, (BaseDocument) document, startOffset, endOffset);
96
return HighlightsSequence.EMPTY;
101
// ----------------------------------------------------------------------
102
// DocumentListener implementation
103
// ----------------------------------------------------------------------
105
public void insertUpdate(DocumentEvent e) {
106
documentChanged(e.getOffset(), e.getLength());
109
public void removeUpdate(DocumentEvent e) {
110
documentChanged(e.getOffset(), e.getLength());
113
public void changedUpdate(DocumentEvent e) {
114
documentChanged(e.getOffset(), e.getLength());
117
// ----------------------------------------------------------------------
118
// Private implementation
119
// ----------------------------------------------------------------------
121
private AttributeSet findAttribs(TokenItem tokenItem) {
122
synchronized (this) {
123
AttributeSet attribs = attribsCache.get(tokenItem.getTokenID());
125
if (attribs == null) {
126
FontColorSettings fcs = MimeLookup.getLookup(mimePath).lookup(FontColorSettings.class);
128
attribs = findFontAndColors(fcs, tokenItem);
129
if (attribs == null) {
130
attribs = SimpleAttributeSet.EMPTY;
133
attribsCache.put(tokenItem.getTokenID(), attribs);
135
LOG.warning("Can't find FCS for mime path: '" + mimePath.getPath() + "'"); //NOI18N
139
return attribs == null ? SimpleAttributeSet.EMPTY : attribs;
143
private static AttributeSet findFontAndColors(FontColorSettings fcs, TokenItem tokenItem) {
144
AttributeSet attribs = null;
145
TokenContextPath tokenContextPath = tokenItem.getTokenContextPath();
147
// First try the token's name
149
String name = tokenContextPath.getFullTokenName(tokenItem.getTokenID());
151
attribs = fcs.getTokenFontColors(name);
155
// Then try the category
156
if (attribs == null) {
157
TokenCategory category = tokenItem.getTokenID().getCategory();
158
if (category != null) {
159
String categoryName = tokenContextPath.getFullTokenName(category);
160
if (categoryName != null) {
161
attribs = fcs.getTokenFontColors(categoryName);
169
private void documentChanged(int offset, int lenght) {
170
synchronized (this) {
174
if (LOG.isLoggable(Level.FINE)) {
175
LOG.fine("Document changed: changeStart = " + offset + ", changeEnd = " + (offset + lenght)); //NOI18N
178
if (offset < 0 || offset > document.getLength()) {
182
if (lenght <= 0 || offset + lenght > document.getLength()) {
183
lenght = document.getLength() - offset;
186
fireHighlightsChange(offset, offset + lenght);
189
private final class HSImpl implements HighlightsSequence {
191
private final long version;
192
private final BaseDocument baseDocument;
193
private final int startOffset;
194
private final int endOffset;
196
private boolean init = false;
197
private TokenItem tokenItem;
199
public HSImpl(long version, BaseDocument baseDocument, int startOffset, int endOffset) {
200
this.version = version;
201
this.baseDocument = baseDocument;
202
this.startOffset = startOffset;
203
this.endOffset = endOffset;
206
public boolean moveNext() {
211
ExtSyntaxSupport ess = (ExtSyntaxSupport)baseDocument.getSyntaxSupport();
212
tokenItem = ess.getTokenChain(startOffset, endOffset);
213
} catch (BadLocationException e) {
214
LOG.log(Level.WARNING, "Can't get token sequence: document " + baseDocument + //NOI18N
215
", startOffset = " + startOffset + ", endOffset = " + endOffset, e); //NOI18N
219
while(null != tokenItem) {
220
if (tokenItem.getOffset() + tokenItem.getImage().length() > startOffset) {
224
if (LOG.isLoggable(Level.FINE)) {
225
LOG.fine("Skipping tokenId: " + tokenItem.getTokenID() + //NOI18N
226
", tokenStart = " + tokenItem.getOffset() + //NOI18N
227
", tokenEnd = " + (tokenItem.getOffset() + tokenItem.getImage().length()) + //NOI18N
228
", startOffset = " + startOffset + //NOI18N
229
", endOffset = " + endOffset //NOI18N
233
tokenItem = tokenItem.getNext();
235
} else if (tokenItem != null) {
236
tokenItem = tokenItem.getNext();
239
if (tokenItem != null && tokenItem.getOffset() > endOffset) {
243
if (LOG.isLoggable(Level.FINE)) {
244
if (tokenItem != null) {
245
LOG.fine("Next tokenId: " + tokenItem.getTokenID() + //NOI18N
246
", tokenStart = " + tokenItem.getOffset() + //NOI18N
247
", tokenEnd = " + (tokenItem.getOffset() + tokenItem.getImage().length()) + //NOI18N
248
", startOffset = " + startOffset + //NOI18N
249
", endOffset = " + endOffset //NOI18N
252
LOG.fine("Next tokenId: null"); //NOI18N
256
return tokenItem != null;
259
public int getStartOffset() {
260
synchronized (NonLexerSyntaxHighlighting.this) {
264
throw new NoSuchElementException("Call moveNext() first."); //NOI18N
265
} else if (tokenItem == null) {
266
throw new NoSuchElementException();
269
return Math.max(tokenItem.getOffset(), startOffset);
273
public int getEndOffset() {
274
synchronized (NonLexerSyntaxHighlighting.this) {
278
throw new NoSuchElementException("Call moveNext() first."); //NOI18N
279
} else if (tokenItem == null) {
280
throw new NoSuchElementException();
283
return Math.min(tokenItem.getOffset() + tokenItem.getImage().length(), endOffset);
287
public AttributeSet getAttributes() {
288
synchronized (NonLexerSyntaxHighlighting.this) {
292
throw new NoSuchElementException("Call moveNext() first."); //NOI18N
293
} else if (tokenItem == null) {
294
throw new NoSuchElementException();
297
return findAttribs(tokenItem);
301
private void checkVersion() {
302
if (this.version != NonLexerSyntaxHighlighting.this.version) {
303
throw new ConcurrentModificationException();
306
} // End of HSImpl class