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-2007 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.
41
package org.netbeans.modules.editor.lib;
43
import java.beans.PropertyChangeListener;
44
import java.beans.PropertyChangeSupport;
45
import java.lang.reflect.Field;
46
import java.util.Collection;
47
import java.util.Collections;
48
import java.util.HashMap;
49
import java.util.List;
51
import java.util.WeakHashMap;
52
import java.util.logging.Logger;
53
import javax.swing.text.AttributeSet;
54
import javax.swing.text.EditorKit;
55
import org.netbeans.api.editor.mimelookup.MimeLookup;
56
import org.netbeans.api.editor.mimelookup.MimePath;
57
import org.netbeans.api.editor.settings.FontColorNames;
58
import org.netbeans.api.editor.settings.FontColorSettings;
59
import org.netbeans.api.lexer.Language;
60
import org.netbeans.api.lexer.TokenId;
61
import org.netbeans.editor.Coloring;
62
import org.netbeans.editor.SettingsNames;
63
import org.netbeans.editor.SettingsUtil;
64
import org.netbeans.editor.TokenCategory;
65
import org.netbeans.editor.TokenContext;
66
import org.netbeans.editor.TokenContextPath;
67
import org.netbeans.editor.TokenID;
68
import org.openide.util.Lookup;
69
import org.openide.util.LookupEvent;
70
import org.openide.util.LookupListener;
71
import org.openide.util.WeakListeners;
75
* @author Vita Stejskal
77
public final class ColoringMap {
79
private static final Logger LOG = Logger.getLogger(ColoringMap.class.getName());
81
public static final String PROP_COLORING_MAP = "ColoringMap.PROP_COLORING_MAP"; //NOI18N
83
public static ColoringMap get(String mimeType) {
87
return getInternal(mimeType);
96
public Map<String, Coloring> getMap() {
100
legacyNonTokenColoringNames,
103
lookupResult.allInstances()
111
public void addPropertyChangeListener(PropertyChangeListener l) {
112
PCS.addPropertyChangeListener(l);
115
public void removePropertyChangeListener(PropertyChangeListener l) {
116
PCS.removePropertyChangeListener(l);
119
// ---------------------------------------------------------
120
// Private implementation
121
// ---------------------------------------------------------
123
private static final Map<MimePath, ColoringMap> CACHE = new WeakHashMap<MimePath, ColoringMap>();
124
private static final ColoringMap EMPTY = new ColoringMap();
125
private static final ThreadLocal<Boolean> IN_GET = new ThreadLocal<Boolean>() {
127
protected Boolean initialValue() {
128
return Boolean.FALSE;
132
private final List<String> legacyNonTokenColoringNames;
133
private final Language<?> lexerLanguage;
134
private final List<? extends TokenContext> syntaxLanguages;
135
private final Lookup.Result<FontColorSettings> lookupResult;
136
private final LookupListener lookupListener = new LookupListener() {
137
public void resultChanged(LookupEvent ev) {
138
synchronized (LOCK) {
142
PCS.firePropertyChange(PROP_COLORING_MAP, null, null);
146
private final PropertyChangeSupport PCS = new PropertyChangeSupport(this);
148
private final String LOCK = new String("ColoringMap.LOCK"); //NOI18N
150
private Map<String, Coloring> map = null;
152
private ColoringMap() {
153
this.legacyNonTokenColoringNames = null;
154
this.lexerLanguage = null;
155
this.syntaxLanguages = null;
156
this.lookupResult = null;
157
this.map = Collections.<String, Coloring>emptyMap();
161
List<String> legacyNonTokenColoringNames,
162
Language<?> lexerLanguage,
163
List<? extends TokenContext> syntaxLanguages,
164
Lookup.Result<FontColorSettings> lookupResult
166
this.legacyNonTokenColoringNames = legacyNonTokenColoringNames;
167
this.lexerLanguage = lexerLanguage;
168
this.syntaxLanguages = syntaxLanguages;
169
this.lookupResult = lookupResult;
171
this.map = loadTheMap(
172
legacyNonTokenColoringNames,
175
lookupResult.allInstances()
178
this.lookupResult.addLookupListener(WeakListeners.create(LookupListener.class, lookupListener, this.lookupResult));
181
private static ColoringMap getInternal(String mimeType) {
182
MimePath mimePath = mimeType == null || mimeType.length() == 0 ?
183
MimePath.EMPTY : MimePath.parse(mimeType);
185
synchronized (CACHE) {
186
ColoringMap cm = CACHE.get(mimePath);
193
List<String> legacyNonTokenColoringNames = findLegacyNonTokenColoringNames(mimePath);
194
Lookup.Result<FontColorSettings> lookupResult =
195
MimeLookup.getLookup(mimePath).lookupResult(FontColorSettings.class);
197
Language<?> lexerLanguage = null;
198
List<? extends TokenContext> syntaxLanguage = null;
200
if (mimePath.size() > 0) {
201
lexerLanguage = Language.find(mimePath.getPath());
202
syntaxLanguage = findSyntaxLanguage(mimePath);
205
LOG.fine("Creating ColoringMap for '" + mimeType + "' ---------------------------"); //NOI18N
206
ColoringMap myCm = new ColoringMap(
207
legacyNonTokenColoringNames,
212
LOG.fine("----------------------------------------------------------------------"); //NOI18N
214
synchronized (CACHE) {
215
ColoringMap cm = CACHE.get(mimePath);
219
CACHE.put(mimePath, cm);
226
private static Map<String, Coloring> loadTheMap(
227
List<String> legacyNonTokenColoringNames,
228
Language<?> lexerLanguage,
229
List<? extends TokenContext> syntaxLanguages,
230
Collection<? extends FontColorSettings> fontsColors
232
HashMap<String, Coloring> coloringMap = new HashMap<String, Coloring>();
234
if (!fontsColors.isEmpty()) {
235
FontColorSettings fcs = fontsColors.iterator().next();
237
if (legacyNonTokenColoringNames != null) {
238
collectLegacyNonTokenColorings(coloringMap, legacyNonTokenColoringNames, fcs);
241
collectNonTokenColorings(coloringMap, fcs);
243
if (syntaxLanguages != null) {
244
collectLegacyTokenColorings(coloringMap, syntaxLanguages, fcs);
247
if (lexerLanguage != null) {
248
collectTokenColorings(coloringMap, lexerLanguage, fcs);
252
return Collections.unmodifiableMap(coloringMap);
255
private static void collectNonTokenColorings(
256
HashMap<String, Coloring> coloringMap,
257
FontColorSettings fcs
259
// Introspect the fields in FontColorNames class
260
for(Field field : FontColorNames.class.getDeclaredFields()) {
261
Object fieldValue = null;
264
fieldValue = field.get(null);
265
} catch (IllegalAccessException e) {
269
if (fieldValue instanceof String) {
270
String coloringName = (String) fieldValue;
271
AttributeSet attribs = fcs.getFontColors(coloringName);
272
if (attribs != null) {
273
LOG.fine("Loading coloring '" + coloringName + "'"); //NOI18N
274
coloringMap.put(coloringName, Coloring.fromAttributeSet(attribs));
280
private static void collectLegacyNonTokenColorings(
281
HashMap<String, Coloring> coloringMap,
282
List<String> legacyNonTokenColoringNames,
283
FontColorSettings fcs
285
for (int i = legacyNonTokenColoringNames.size() - 1; i >= 0; i--) {
286
String coloringName = legacyNonTokenColoringNames.get(i);
287
AttributeSet attribs = fcs.getFontColors(coloringName);
288
if (attribs != null) {
289
LOG.fine("Loading legacy coloring '" + coloringName + "'"); //NOI18N
290
coloringMap.put(coloringName, Coloring.fromAttributeSet(attribs));
295
private static void collectTokenColorings(
296
HashMap<String, Coloring> coloringMap,
297
Language<?> lexerLanguage,
298
FontColorSettings fcs
300
// Add token-categories colorings
301
for (String category : lexerLanguage.tokenCategories()) {
302
AttributeSet attribs = fcs.getTokenFontColors(category);
303
if (attribs != null) {
304
LOG.fine("Loading token coloring '" + category + "'"); //NOI18N
305
coloringMap.put(category, Coloring.fromAttributeSet(attribs));
309
// Add token-ids colorings
310
for (TokenId tokenId : lexerLanguage.tokenIds()) {
311
AttributeSet attribs = fcs.getTokenFontColors(tokenId.name());
312
if (attribs != null) {
313
LOG.fine("Loading token coloring '" + tokenId.name() + "'"); //NOI18N
314
coloringMap.put(tokenId.name(), Coloring.fromAttributeSet(attribs));
319
private static void collectLegacyTokenColorings(
320
HashMap<String, Coloring> coloringMap,
321
List<? extends TokenContext> tokenContextList,
322
FontColorSettings fcs
324
for (int i = tokenContextList.size() - 1; i >= 0; i--) {
325
TokenContext tc = tokenContextList.get(i);
326
TokenContextPath[] allPaths = tc.getAllContextPaths();
327
for (int j = 0; j < allPaths.length; j++) {
328
TokenContext firstContext = allPaths[j].getContexts()[0];
330
// Add token-categories colorings
331
TokenCategory[] tokenCategories = firstContext.getTokenCategories();
332
for (int k = 0; k < tokenCategories.length; k++) {
333
String fullName = allPaths[j].getFullTokenName(tokenCategories[k]);
334
AttributeSet attribs = fcs.getTokenFontColors(fullName);
335
if (attribs != null) {
336
LOG.fine("Loading legacy token coloring '" + fullName + "'"); //NOI18N
337
coloringMap.put(fullName, Coloring.fromAttributeSet(attribs));
341
// Add token-ids colorings
342
TokenID[] tokenIDs = firstContext.getTokenIDs();
343
for (int k = 0; k < tokenIDs.length; k++) {
344
String fullName = allPaths[j].getFullTokenName(tokenIDs[k]);
345
AttributeSet attribs = fcs.getTokenFontColors(fullName);
346
if (attribs != null) {
347
LOG.fine("Loading legacy token coloring '" + fullName + "'"); //NOI18N
348
coloringMap.put(fullName, Coloring.fromAttributeSet(attribs));
355
private static List<String> findLegacyNonTokenColoringNames(MimePath mimePath) {
356
EditorKit kit = MimeLookup.getLookup(mimePath).lookup(EditorKit.class);
357
List<String> legacyNonTokenColoringNames = null;
360
List list = SettingsUtil.getCumulativeList(kit.getClass(), SettingsNames.COLORING_NAME_LIST, null);
361
legacyNonTokenColoringNames = list;
364
return legacyNonTokenColoringNames;
367
private static List<? extends TokenContext> findSyntaxLanguage(MimePath mimePath) {
368
EditorKit kit = MimeLookup.getLookup(mimePath).lookup(EditorKit.class);
369
List<? extends TokenContext> languages = null;
372
List tcl = SettingsUtil.getList(kit.getClass(), SettingsNames.TOKEN_CONTEXT_LIST, null);