~ubuntu-branches/ubuntu/quantal/netbeans/quantal

« back to all changes in this revision

Viewing changes to editor/libsrc/org/netbeans/modules/editor/lib/ColoringMap.java

  • Committer: Bazaar Package Importer
  • Author(s): Marek Slama
  • Date: 2008-01-29 14:11:22 UTC
  • Revision ID: james.westby@ubuntu.com-20080129141122-fnzjbo11ntghxfu7
Tags: upstream-6.0.1
ImportĀ upstreamĀ versionĀ 6.0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 
3
 *
 
4
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 
5
 *
 
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]"
 
23
 *
 
24
 * Contributor(s):
 
25
 *
 
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.
 
29
 *
 
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.
 
40
 */
 
41
package org.netbeans.modules.editor.lib;
 
42
 
 
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;
 
50
import java.util.Map;
 
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;
 
72
 
 
73
/**
 
74
 *
 
75
 * @author Vita Stejskal
 
76
 */
 
77
public final class ColoringMap {
 
78
 
 
79
    private static final Logger LOG = Logger.getLogger(ColoringMap.class.getName());
 
80
    
 
81
    public static final String PROP_COLORING_MAP = "ColoringMap.PROP_COLORING_MAP"; //NOI18N
 
82
 
 
83
    public static ColoringMap get(String mimeType) {
 
84
        if (!IN_GET.get()) {
 
85
            IN_GET.set(true);
 
86
            try {
 
87
                return getInternal(mimeType);
 
88
            } finally {
 
89
                IN_GET.set(false);
 
90
            }
 
91
        } else {
 
92
            return EMPTY;
 
93
        }
 
94
    }
 
95
    
 
96
    public Map<String, Coloring> getMap() {
 
97
        synchronized (LOCK) {
 
98
            if (map == null) {
 
99
                map = loadTheMap(
 
100
                    legacyNonTokenColoringNames, 
 
101
                    lexerLanguage, 
 
102
                    syntaxLanguages, 
 
103
                    lookupResult.allInstances()
 
104
                );
 
105
            }
 
106
            
 
107
            return map;
 
108
        }
 
109
    }
 
110
    
 
111
    public void addPropertyChangeListener(PropertyChangeListener l) {
 
112
        PCS.addPropertyChangeListener(l);
 
113
    }
 
114
 
 
115
    public void removePropertyChangeListener(PropertyChangeListener l) {
 
116
        PCS.removePropertyChangeListener(l);
 
117
    }
 
118
    
 
119
    // ---------------------------------------------------------
 
120
    // Private implementation
 
121
    // ---------------------------------------------------------
 
122
 
 
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>() {
 
126
        @Override
 
127
        protected Boolean initialValue() {
 
128
            return Boolean.FALSE;
 
129
        }
 
130
    };
 
131
 
 
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) {
 
139
                map = null;
 
140
            }
 
141
            
 
142
            PCS.firePropertyChange(PROP_COLORING_MAP, null, null);
 
143
        }
 
144
    };
 
145
    
 
146
    private final PropertyChangeSupport PCS = new PropertyChangeSupport(this);
 
147
    
 
148
    private final String LOCK = new String("ColoringMap.LOCK"); //NOI18N
 
149
    
 
150
    private Map<String, Coloring> map = null;
 
151
    
 
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();
 
158
    }
 
159
    
 
160
    private ColoringMap(
 
161
        List<String> legacyNonTokenColoringNames,
 
162
        Language<?> lexerLanguage, 
 
163
        List<? extends TokenContext> syntaxLanguages, 
 
164
        Lookup.Result<FontColorSettings> lookupResult
 
165
    ) {
 
166
        this.legacyNonTokenColoringNames = legacyNonTokenColoringNames;
 
167
        this.lexerLanguage = lexerLanguage;
 
168
        this.syntaxLanguages = syntaxLanguages;
 
169
        this.lookupResult = lookupResult;
 
170
        
 
171
        this.map = loadTheMap(
 
172
            legacyNonTokenColoringNames, 
 
173
            lexerLanguage, 
 
174
            syntaxLanguages, 
 
175
            lookupResult.allInstances()
 
176
        );
 
177
        
 
178
        this.lookupResult.addLookupListener(WeakListeners.create(LookupListener.class, lookupListener, this.lookupResult));
 
179
    }
 
180
 
 
181
    private static ColoringMap getInternal(String mimeType) {
 
182
        MimePath mimePath = mimeType == null || mimeType.length() == 0 ? 
 
183
            MimePath.EMPTY : MimePath.parse(mimeType);
 
184
        
 
185
        synchronized (CACHE) {
 
186
            ColoringMap cm = CACHE.get(mimePath);
 
187
            
 
188
            if (cm != null) {
 
189
                return cm;
 
190
            }
 
191
        }
 
192
 
 
193
        List<String> legacyNonTokenColoringNames = findLegacyNonTokenColoringNames(mimePath);
 
194
        Lookup.Result<FontColorSettings> lookupResult = 
 
195
            MimeLookup.getLookup(mimePath).lookupResult(FontColorSettings.class);
 
196
 
 
197
        Language<?> lexerLanguage = null;
 
198
        List<? extends TokenContext> syntaxLanguage = null;
 
199
 
 
200
        if (mimePath.size() > 0) {
 
201
            lexerLanguage = Language.find(mimePath.getPath());
 
202
            syntaxLanguage = findSyntaxLanguage(mimePath);
 
203
        }
 
204
 
 
205
        LOG.fine("Creating ColoringMap for '" + mimeType + "' ---------------------------"); //NOI18N
 
206
        ColoringMap myCm = new ColoringMap(
 
207
            legacyNonTokenColoringNames, 
 
208
            lexerLanguage, 
 
209
            syntaxLanguage, 
 
210
            lookupResult
 
211
        );
 
212
        LOG.fine("----------------------------------------------------------------------"); //NOI18N
 
213
                
 
214
        synchronized (CACHE) {
 
215
            ColoringMap cm = CACHE.get(mimePath);
 
216
            
 
217
            if (cm == null) {
 
218
                cm = myCm;
 
219
                CACHE.put(mimePath, cm);
 
220
            }
 
221
            
 
222
            return cm;
 
223
        }
 
224
    }
 
225
    
 
226
    private static Map<String, Coloring> loadTheMap(
 
227
        List<String> legacyNonTokenColoringNames,
 
228
        Language<?> lexerLanguage, 
 
229
        List<? extends TokenContext> syntaxLanguages, 
 
230
        Collection<? extends FontColorSettings> fontsColors
 
231
    ) {
 
232
        HashMap<String, Coloring> coloringMap = new HashMap<String, Coloring>();
 
233
        
 
234
        if (!fontsColors.isEmpty()) {
 
235
            FontColorSettings fcs = fontsColors.iterator().next();
 
236
        
 
237
            if (legacyNonTokenColoringNames != null) {
 
238
                collectLegacyNonTokenColorings(coloringMap, legacyNonTokenColoringNames, fcs);
 
239
            }
 
240
            
 
241
            collectNonTokenColorings(coloringMap, fcs);
 
242
            
 
243
            if (syntaxLanguages != null) {
 
244
                collectLegacyTokenColorings(coloringMap, syntaxLanguages, fcs);
 
245
            }
 
246
            
 
247
            if (lexerLanguage != null) {
 
248
                collectTokenColorings(coloringMap, lexerLanguage, fcs);
 
249
            }
 
250
        }
 
251
        
 
252
        return Collections.unmodifiableMap(coloringMap);
 
253
    }
 
254
 
 
255
    private static void collectNonTokenColorings(
 
256
        HashMap<String, Coloring> coloringMap, 
 
257
        FontColorSettings fcs
 
258
    ) {
 
259
        // Introspect the fields in FontColorNames class
 
260
        for(Field field : FontColorNames.class.getDeclaredFields()) {
 
261
            Object fieldValue = null;
 
262
            
 
263
            try {
 
264
                fieldValue = field.get(null);
 
265
            } catch (IllegalAccessException e) {
 
266
                // ignore
 
267
            }
 
268
            
 
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));
 
275
                }
 
276
            }
 
277
        }
 
278
    }
 
279
 
 
280
    private static void collectLegacyNonTokenColorings(
 
281
        HashMap<String, Coloring> coloringMap, 
 
282
        List<String> legacyNonTokenColoringNames,
 
283
        FontColorSettings fcs
 
284
    ) {
 
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));
 
291
            }
 
292
        }
 
293
    }
 
294
    
 
295
    private static void collectTokenColorings(
 
296
        HashMap<String, Coloring> coloringMap, 
 
297
        Language<?> lexerLanguage,
 
298
        FontColorSettings fcs
 
299
    ) {
 
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));
 
306
            }
 
307
        }
 
308
 
 
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));
 
315
            }
 
316
        }
 
317
    }
 
318
    
 
319
    private static void collectLegacyTokenColorings(
 
320
        HashMap<String, Coloring> coloringMap, 
 
321
        List<? extends TokenContext> tokenContextList, 
 
322
        FontColorSettings fcs
 
323
    ) {
 
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];
 
329
 
 
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));
 
338
                    }
 
339
                }
 
340
 
 
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));
 
349
                    }
 
350
                }
 
351
            }
 
352
        }        
 
353
    }
 
354
 
 
355
    private static List<String> findLegacyNonTokenColoringNames(MimePath mimePath) {
 
356
        EditorKit kit = MimeLookup.getLookup(mimePath).lookup(EditorKit.class);
 
357
        List<String> legacyNonTokenColoringNames = null;
 
358
 
 
359
        if (kit != null) {
 
360
            List list = SettingsUtil.getCumulativeList(kit.getClass(), SettingsNames.COLORING_NAME_LIST, null);
 
361
            legacyNonTokenColoringNames = list;
 
362
        }
 
363
        
 
364
        return legacyNonTokenColoringNames;
 
365
    }
 
366
    
 
367
    private static List<? extends TokenContext> findSyntaxLanguage(MimePath mimePath) {
 
368
        EditorKit kit = MimeLookup.getLookup(mimePath).lookup(EditorKit.class);
 
369
        List<? extends TokenContext> languages = null;
 
370
        
 
371
        if (kit != null) {
 
372
            List tcl = SettingsUtil.getList(kit.getClass(), SettingsNames.TOKEN_CONTEXT_LIST, null);
 
373
            languages = tcl;
 
374
        }
 
375
        
 
376
        return languages;
 
377
    }
 
378
}