001: /*
002: * Sun Public License Notice
003: *
004: * The contents of this file are subject to the Sun Public License
005: * Version 1.0 (the "License"). You may not use this file except in
006: * compliance with the License. A copy of the License is available at
007: * http://www.sun.com/
008: *
009: * The Original Code is NetBeans. The Initial Developer of the Original
010: * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
011: * Microsystems, Inc. All Rights Reserved.
012: */
013:
014: package org.netbeans.editor;
015:
016: import java.util.ArrayList;
017: import java.util.HashMap;
018: import java.util.Iterator;
019: import java.util.List;
020: import java.util.Map;
021:
022: /**
023: * Configurable settings that editor uses. All the methods are static The editor
024: * is configurable mainly by using the following static method in Settings
025: * class:
026: *
027: * org.netbeans.editor.Settings.setValue(Class kitClass, String settingName,
028: * Object newValue);
029: *
030: * kitClass - this is the class of the editor kit for which the setting is
031: * changed. The current hierarchy of editor kits starts with the
032: * <tt>org.netbeans.editor.BaseKit</tt> kit, the begining of the whole kit
033: * hierarchy. There should be a different editor kit for each mime-type.
034: *
035: * When the particular setting is not set for a given kit, then the superclass
036: * of the given kit class is retrieved and the search for the setting value is
037: * performed. Example: If the java document calls Settings.getValue() to
038: * retrieve the value for TAB_SIZE setting and it passes JavaKit.class as the
039: * kitClass parameter and the setting has no value on this level, then the super
040: * class of the JavaKit is retrieved (by using Class.getSuperclass() call) which
041: * is BaseKit in this case and the search for the value of TAB_SIZE setting is
042: * performed again. It is finished by reaching the null value for the kitClass.
043: * The null value can be also used as the kitClass parameter value. In a more
044: * general look not only the kit-class hierarchy could be used in
045: * <tt>Settings</tt>. Any class inheritance hierarchy could be used here
046: * having the null as the common root.
047: *
048: * This way the inheritance of the setting values is guaranteed. By changing the
049: * setting value on the BaseKit level (or even on the null level), all the kit
050: * classes that don't override the particular setting are affected.
051: *
052: * settingName - name of the setting to change. The base setting names are
053: * defined as public String constants in <tt>SettingsNames</tt> class. The
054: * additional packages that extend the basic editor functionality can define
055: * additional setting names.
056: *
057: * newValue - new value for the setting. It must be always an object even if the
058: * setting is logicaly the basic datatype such as int (java.lang.Integer would
059: * be used in this case). A particular class types that can be used for the
060: * value of the settings are documented for each setting.
061: *
062: * WARNING! Please read carefully the description for each option you're going
063: * to change as you can make the editor stop working if you'll change the
064: * setting in a wrong way.
065: *
066: * @author Miloslav Metelka
067: * @version 1.00
068: */
069:
070: public class Settings {
071:
072: /**
073: * Core level used by the settings initializers. This is the level used for
074: * the base and ext editor packages initializers only.
075: */
076: public static final int CORE_LEVEL = 0;
077:
078: /**
079: * System level used by the settings initializers. This is the (default)
080: * first level. It should be used by all the modules that install the new
081: * kits into the editor.
082: */
083: public static final int SYSTEM_LEVEL = 1;
084:
085: /**
086: * Extension level used by the settings initializers. This is the second
087: * level. It should be used by all the modules that want to extend or modify
088: * the settings but they don't install their own kits. The example can be a
089: * module extending the popup menu of an existing kit.
090: */
091: public static final int EXTENSION_LEVEL = 2;
092:
093: /**
094: * Option level used by the settings initializers. This is the third level.
095: * It should be used by the visual options created by the IDE.
096: */
097: public static final int OPTION_LEVEL = 3;
098:
099: /**
100: * User level used by the settings initializers. This is the fourth level.
101: * All the initializers with this level will be called AFTER all the
102: * initializers at the system level. All the user custom initializers should
103: * be added at this level to guarantee they will overwrite the settings
104: * added by the system.
105: */
106: public static final int USER_LEVEL = 4;
107:
108: /** List of Initializers */
109: private static final ArrayList initializerLists = new ArrayList();
110:
111: /** Current initializer sorter. */
112: private static InitializerSorter currentInitializerSorter;
113:
114: /** List of Filters */
115: private static final ArrayList filterList = new ArrayList();
116:
117: /** [kit-class, map-of-settings] pairs */
118: private static final Map kit2Maps = new HashMap();
119:
120: /** Support for firing change events */
121: private static final WeakEventListenerList listenerList = new WeakEventListenerList();
122:
123: /**
124: * Internal map instance signing that initializer returned null map for
125: * particular kit. To sign this fact and not query initializer again, this
126: * simple map is used.
127: */
128: private static final Map NULL_MAP = new HashMap(1);
129:
130: private static boolean firingEnabled = true;
131:
132: /** Save repetitive creation of the empty maps using this variable */
133: private static HashMap emptyMap = null;
134:
135: private Settings() {
136: // no instances allowed
137: }
138:
139: /** Add the initializer at the system level and perform reset. */
140: public static synchronized void addInitializer(Initializer i) {
141: addInitializer(i, SYSTEM_LEVEL);
142: reset();
143: }
144:
145: /**
146: * Add initializer instance to the list of current initializers. You can
147: * call reset() after adding the initializer to make sure it will update the
148: * current settings with its values. However all the changes that were made
149: * explicitly by calling setValue() will be lost in this case.
150: *
151: * @param i
152: * initializer to add to the current list of initializers
153: * @param level
154: * initializer level. It defines in which order the initializers
155: * will be called. There are currently three levels
156: * <tt>CORE_LEVEL</tt>, <tt>SYSTEM_LEVEL</tt> and
157: * <tt>USER_LEVEL</tt>. It's guaranteed that initializers with
158: * the particular level will be called in the order shown above.
159: * The order of the initializers at the same level is given by
160: * the order of their addition.
161: */
162: public static synchronized void addInitializer(Initializer i,
163: int level) {
164: int size = initializerLists.size();
165: for (int j = size; j <= level; j++) {
166: initializerLists.add(new ArrayList());
167: }
168: ((List) initializerLists.get(level)).add(i);
169:
170: // Sort the initializers if there's a valid sorter
171: if (currentInitializerSorter != null) {
172: currentInitializerSorter.sort(initializerLists);
173: }
174: }
175:
176: /**
177: * Remove the initializer of the given name from all the levels where it
178: * occurs.
179: *
180: * @param name
181: * name of the initializer sorter to remove.
182: */
183: public static synchronized void removeInitializer(String name) {
184: Iterator itit = initializerLists.iterator();
185: while (itit.hasNext()) {
186: Iterator it = ((List) itit.next()).iterator();
187: while (it.hasNext()) {
188: if (name.equals(((Initializer) it.next()).getName())) {
189: it.remove();
190: }
191: }
192: }
193:
194: // Sort the initializers if there's a valid sorter
195: if (currentInitializerSorter != null) {
196: currentInitializerSorter.sort(initializerLists);
197: }
198: }
199:
200: /** Get the current initializer sorter. */
201: public static synchronized InitializerSorter getInitializerSorter() {
202: return currentInitializerSorter;
203: }
204:
205: /** Set the current initializer sorter. */
206: public static synchronized void setInitializerSorter(
207: InitializerSorter initializerSorter) {
208: currentInitializerSorter = initializerSorter;
209: }
210:
211: /**
212: * Add filter instance to the list of current filters. If there are already
213: * existing editor components, and you want to apply the changes that this
214: * filter makes to these existing components, you can call reset(). However
215: * all the changes that were made explicitly by calling setValue() will be
216: * lost in this case.
217: *
218: * @param f
219: * filter to add to the list of the filters
220: */
221: public static synchronized void addFilter(Filter f) {
222: filterList.add(f);
223: }
224:
225: public static synchronized void removeFilter(Filter f) {
226: Iterator it = filterList.iterator();
227: while (it.hasNext()) {
228: if (it.next() == f) {
229: it.remove();
230: }
231: }
232: }
233:
234: /** Get the value and evaluate the evaluators. */
235: public static Object getValue(Class kitClass, String settingName) {
236: return getValue(kitClass, settingName, true);
237: }
238:
239: /**
240: * Get the property by searching the given kit class settings and if not
241: * found then the settings for super class and so on.
242: *
243: * @param kitClass
244: * editor kit class for which the value of setting should be
245: * retrieved. The null can be used as the root of the whole
246: * hierarchy. (which is top of kit class hierarchy) before the
247: * processing begins.
248: * @param settingName
249: * name of the setting for which the value should be retrieved
250: * @return the value of the setting
251: */
252: public static synchronized Object getValue(Class kitClass,
253: String settingName, boolean evaluateEvaluators) {
254: Object value = null;
255: Class kc = kitClass;
256: while (true) {
257: Map map = getKitMap(kc, false);
258: if (map != null) {
259: value = map.get(settingName);
260: if (evaluateEvaluators && value instanceof Evaluator) {
261: value = ((Evaluator) value).getValue(kitClass,
262: settingName);
263: }
264: if (value != null) {
265: break;
266: }
267: }
268: if (kc == null) {
269: break;
270: }
271: kc = kc.getSuperclass();
272: }
273:
274: // filter the value if necessary
275: int cnt = filterList.size();
276: for (int i = 0; i < cnt; i++) {
277: value = ((Filter) filterList.get(i)).filterValue(kitClass,
278: settingName, value);
279: }
280:
281: return value;
282: }
283:
284: /** Get the value hierarchy and evaluate the evaluators */
285: public static KitAndValue[] getValueHierarchy(Class kitClass,
286: String settingName) {
287: return getValueHierarchy(kitClass, settingName, true);
288: }
289:
290: /**
291: * Get array of KitAndValue objects sorted from the given kit class to its
292: * deepest superclass and the last member can be filled whether there is
293: * global setting (kit class of that member would be null). This method is
294: * useful for objects like keymaps that need to create all the parent
295: * keymaps to work properly. The method can either evaluate evaluators or
296: * leave them untouched which can become handy in some cases.
297: *
298: * @param kitClass
299: * editor kit class for which the value of setting should be
300: * retrieved. The null can be used as the root of the whole
301: * hierarchy.
302: * @param settingName
303: * name of the setting for which the value should be retrieved
304: * @param evaluateEvaluators
305: * whether the evaluators should be evaluated or not
306: * @return the array containing KitAndValue instances describing the
307: * particular setting's value on the specific kit level.
308: */
309: public static synchronized KitAndValue[] getValueHierarchy(
310: Class kitClass, String settingName,
311: boolean evaluateEvaluators) {
312: ArrayList kavList = new ArrayList();
313: Class kc = kitClass;
314: while (true) {
315: Map map = getKitMap(kc, false);
316: if (map != null) {
317: Object value = map.get(settingName);
318: if (evaluateEvaluators && value instanceof Evaluator) {
319: value = ((Evaluator) value).getValue(kitClass,
320: settingName);
321: }
322: if (value != null) {
323: kavList.add(new KitAndValue(kc, value));
324: }
325: }
326: if (kc == null) {
327: break;
328: }
329: kc = kc.getSuperclass();
330: }
331: KitAndValue[] kavArray = (KitAndValue[]) kavList
332: .toArray(new KitAndValue[kavList.size()]);
333:
334: // filter the value if necessary
335: int cnt = filterList.size();
336: for (int i = 0; i < cnt; i++) {
337: kavArray = ((Filter) filterList.get(i))
338: .filterValueHierarchy(kitClass, settingName,
339: kavArray);
340: }
341:
342: return kavArray;
343: }
344:
345: /**
346: * Set the new value for property on kit level. The old and new values are
347: * compared and if they are equal the setting is not changed and nothing is
348: * fired.
349: *
350: * @param kitClass
351: * editor kit class for which the value of setting should be set.
352: * The null can be used as the root of the whole hierarchy.
353: * @param settingName
354: * the string used for searching the value
355: * @param newValue
356: * new value to set for the property; the value can be null to
357: * clear the value for the specified kit
358: */
359: public static synchronized void setValue(Class kitClass,
360: String settingName, Object newValue) {
361: Map map = getKitMap(kitClass, true);
362: Object oldValue = map.get(settingName);
363: if (oldValue == null && newValue == null
364: || (oldValue != null && oldValue.equals(newValue))) {
365: return; // no change
366: }
367: if (newValue != null) {
368: map.put(settingName, newValue);
369: } else {
370: map.remove(settingName);
371: }
372: fireSettingsChange(kitClass, settingName, oldValue, newValue);
373: }
374:
375: /**
376: * Don't change the value of the setting, but fire change event. This is
377: * useful when there's internal change in the value object of some setting.
378: */
379: public static synchronized void touchValue(Class kitClass,
380: String settingName) {
381: fireSettingsChange(kitClass, settingName, null, null); // kit class
382: // currently not
383: // used
384: }
385:
386: /**
387: * Set the value for the current kit and propagate it to all the children of
388: * the given kit by removing the possible values for the setting from the
389: * children kit setting maps. Note: This call only affects the settings for
390: * the kit classes for which the kit setting map with the setting values
391: * currently exists, i.e. when there was at least one getValue() or
392: * setValue() call performed for any setting on that particular kit class
393: * level. Other kit classes maps will be initialized by the particular
394: * initializer(s) as soon as the first getValue() or setValue() will be
395: * performed for them. However that future process will not be affected by
396: * the current propagateValue() call. This method is useful for the visual
397: * options that always set the value on all the kit levels without regard
398: * whether it's necessary or not. If the value is then changed for the base
399: * kit, it's not propagated naturally as there's a special setting This
400: * method enables
401: *
402: * The current implementation always fires the change regardless whether
403: * there was real change in setting value or not.
404: *
405: * @param kitClass
406: * editor kit class for which the value of setting should be set.
407: * When given null, it's automatically set to BaseKit.class
408: * (which is top of kit class hierarchy) before the processing
409: * begins. Therefore you get exactly the same results for
410: * kitClass == null and kitClass == BaseKit.class.
411: * @param settingName
412: * the string used for searching the value
413: * @param newValue
414: * new value to set for the property; the value can be null to
415: * clear the value for the specified kit
416: */
417: public static synchronized void propagateValue(Class kitClass,
418: String settingName, Object newValue) {
419: Map map = getKitMap(kitClass, true);
420: if (newValue != null) {
421: map.put(settingName, newValue);
422: } else {
423: map.remove(settingName);
424: }
425: // resolve kits
426: Iterator it = kit2Maps.entrySet().iterator();
427: while (it.hasNext()) {
428: Map.Entry me = (Map.Entry) it.next();
429: Class kc = (Class) me.getKey();
430: if (kitClass != kc
431: && (kitClass == null || kitClass
432: .isAssignableFrom(kc))) {
433: ((Map) me.getValue()).remove(settingName);
434: }
435: }
436: fireSettingsChange(null, settingName, null, null);
437: }
438:
439: /**
440: * Run the given runnable. All the changes in the settings are not fired
441: * until the whole runnable completes. Nesting of <tt>update()</tt> call
442: * is allowed. Only one firing is performed after the whole runnable
443: * completes using the 'null triple'.
444: */
445: public static synchronized void update(Runnable r) {
446: boolean turnedOff = firingEnabled;
447: firingEnabled = false;
448: try {
449: r.run();
450: } finally {
451: if (turnedOff) {
452: firingEnabled = true;
453: fireSettingsChange(null, null, null, null);
454: }
455: }
456: }
457:
458: /**
459: * Reset all the settings and fire the change of the settings so that all
460: * the listeners will be notified and will reload the settings. The settings
461: * that were changed using setValue() and propagateValue() are lost.
462: * Initializers will be asked for the settings values when necessary.
463: */
464: public static synchronized void reset() {
465: kit2Maps.clear();
466: fireSettingsChange(null, null, null, null);
467: }
468:
469: /** Debug the current initializers */
470: public static synchronized String initializersToString() {
471: StringBuffer sb = new StringBuffer();
472: for (int i = 0; i < initializerLists.size(); i++) {
473: // debug the level
474: switch (i) {
475: case CORE_LEVEL:
476: sb.append("CORE_LEVEL");
477: break;
478:
479: case SYSTEM_LEVEL:
480: sb.append("SYSTEM_LEVEL");
481: break;
482:
483: case EXTENSION_LEVEL:
484: sb.append("EXTENSION_LEVEL");
485: break;
486:
487: case OPTION_LEVEL:
488: sb.append("OPTION_LEVEL");
489: break;
490:
491: case USER_LEVEL:
492: sb.append("USER_LEVEL");
493: break;
494:
495: default:
496: sb.append("level " + i);
497: break;
498: }
499: sb.append(":\n");
500:
501: // debug the initializers
502: sb.append(EditorDebug.debugList((List) initializerLists
503: .get(i)));
504: sb.append('\n');
505: }
506:
507: return sb.toString();
508: }
509:
510: /**
511: * Add weak listener to listen to change of any property. The caller must
512: * hold the listener object in some instance variable to prevent it from
513: * being garbage collected.
514: */
515: public static void addSettingsChangeListener(
516: SettingsChangeListener l) {
517: listenerList.add(SettingsChangeListener.class, l);
518: }
519:
520: /** Remove listener for changes in properties */
521: public static void removeSettingsChangeListener(
522: SettingsChangeListener l) {
523: listenerList.remove(SettingsChangeListener.class, l);
524: }
525:
526: private static void fireSettingsChange(Class kitClass,
527: String settingName, Object oldValue, Object newValue) {
528: if (firingEnabled) {
529: SettingsChangeListener[] listeners = (SettingsChangeListener[]) listenerList
530: .getListeners(SettingsChangeListener.class);
531: SettingsChangeEvent evt = new SettingsChangeEvent(
532: Settings.class, kitClass, settingName, oldValue,
533: newValue);
534: for (int i = 0; i < listeners.length; i++) {
535: listeners[i].settingsChange(evt);
536: }
537: }
538: }
539:
540: /** Get (and possibly create) kit map for particular kit */
541: private static Map getKitMap(Class kitClass, boolean forceCreation) {
542: Map kitMap = (Map) kit2Maps.get(kitClass);
543: if (kitMap == null) {
544: if (emptyMap == null) {
545: emptyMap = new HashMap();
546: }
547:
548: // Go through all the initializers
549: Iterator itit = initializerLists.iterator();
550: while (itit.hasNext()) {
551: Iterator it = ((List) itit.next()).iterator();
552: while (it.hasNext()) {
553: Initializer i = (Initializer) it.next();
554:
555: // A call to initializer shouldn't break the whole updating
556: try {
557: i.updateSettingsMap(kitClass, emptyMap);
558: } catch (Throwable t) {
559: if (System
560: .getProperty("netbeans.debug.exceptions") != null) { // NOI18N
561: t.printStackTrace();
562: }
563: }
564: }
565: }
566:
567: if (emptyMap.size() > 0) {
568: kitMap = emptyMap;
569: emptyMap = null;
570: }
571:
572: if (kitMap == null) { // no initialization done for this kit
573: kitMap = NULL_MAP; // initializer will not be called again
574: }
575: kit2Maps.put(kitClass, kitMap);
576: }
577:
578: if (kitMap == NULL_MAP) {
579: if (!forceCreation) {
580: return null;
581: } else {
582: kitMap = new HashMap(); // create empty map
583: kit2Maps.put(kitClass, kitMap);
584: }
585: }
586: return kitMap;
587: }
588:
589: /** Kit class and value pair */
590: public static class KitAndValue {
591:
592: public Class kitClass;
593:
594: public Object value;
595:
596: public KitAndValue(Class kitClass, Object value) {
597: this .kitClass = kitClass;
598: this .value = value;
599: }
600:
601: }
602:
603: /**
604: * Initializer of the settings updates the map filled with settings for the
605: * particular kit class when asked. If the settings are being initialized
606: * all the initializers registered by the <tt>Settings.addInitializer()</tt>
607: * are being asked to update the settings-map through calling their
608: * <tt>updateSettingsMap()</tt>.
609: */
610: public static interface Initializer {
611:
612: /**
613: * Each initializer must have a name. The name should be unique. The
614: * name is used for identifying the initializer during removal and sort
615: * operations and for debuging purposes.
616: */
617: public String getName();
618:
619: /**
620: * Update map filled with the settings.
621: *
622: * @param kitClass
623: * kit class for which the settings are being updated. It is
624: * always non-null value.
625: * @param settingsMap
626: * map holding [setting-name, setting-value] pairs. The map
627: * can be empty if this is the first initializer that updates
628: * it or if no previous initializers updated it.
629: */
630: public void updateSettingsMap(Class kitClass, Map settingsMap);
631:
632: }
633:
634: /** Abstract implementation of the initializer dealing with the name. */
635: public static abstract class AbstractInitializer implements
636: Initializer {
637:
638: private String name;
639:
640: public AbstractInitializer(String name) {
641: this .name = name;
642: }
643:
644: public String getName() {
645: return name;
646: }
647:
648: public String toString() {
649: return getName();
650: }
651:
652: }
653:
654: /**
655: * Sort the settings initializers that were added to the settings. There can
656: * be only one sorter for the Settings, but it can delegate to previously
657: * registered sorter.
658: */
659: public static interface InitializerSorter {
660:
661: public void sort(List initializersList);
662:
663: }
664:
665: /** Initializer sorter that delegates to another sorter. */
666: public static abstract class FilterInitializerSorter {
667:
668: private InitializerSorter delegate;
669:
670: public FilterInitializerSorter(InitializerSorter delegate) {
671: this .delegate = delegate;
672: }
673:
674: public void sort(List initializersList) {
675: if (delegate != null) {
676: delegate.sort(initializersList);
677: }
678: }
679:
680: }
681:
682: /**
683: * Evaluator can be used in cases when value of some setting depends on the
684: * value for other setting and it allows to compute the value dynamically
685: * based on the other setting(s) value. The <tt>Evaluator</tt> instance
686: * can be used as the value in the <tt>Settings.setValue()</tt> call. In
687: * that case the call to the <tt>Settings.getValue()</tt> call will
688: * 'evaluate' the Evaluator by calling its <tt>getValue()</tt>.
689: */
690: public static interface Evaluator {
691:
692: /**
693: * Compute the particular setting's value.
694: *
695: * @param kitClass
696: * kit class for which the setting is being retrieved.
697: * @param settingName
698: * name of the setting to retrieve. Although the Evaluator
699: * are usually constructed only for the concrete setting,
700: * this parameter allows creation of the Evaluator for
701: * multiple settings.
702: * @return the value for the requested setting. The substitution is not
703: * attempted again, so the return value cannot be another
704: * Evaluator instance. If the returned value is null, the same
705: * action is taken as if there would no value set on the
706: * particular kit level.
707: *
708: */
709: public Object getValue(Class kitClass, String settingName);
710:
711: }
712:
713: /**
714: * Filter is applied on every value or KitAndValue pairs returned from
715: * getValue(). The filter can be registered by calling
716: * <tt>Settings.addFilter()</tt>. Each call to
717: * <tt>Settings.getValue()</tt> will first retrieve the value and then
718: * call the <tt>Filter.filterValue()</tt> to get the final value. Each
719: * call to <tt>Settings.getValueHierarchy()</tt> will first retrieve the
720: * kit-and-value array and then call the
721: * <tt>Filter.filterValueHierarchy()</tt>. If more filters are registered
722: * they are all used in the order they were added.
723: */
724: public static interface Filter {
725:
726: /**
727: * Filter single value. The value can be substituted here.
728: *
729: * @param kitClass
730: * class of the kit for which the value is retrieved
731: * @param settingName
732: * name of the retrieved setting
733: * @param value
734: * value to be optionally filtered
735: */
736: public Object filterValue(Class kitClass, String settingName,
737: Object value);
738:
739: /**
740: * Filter array of kit-class and value pairs. The pairs can be
741: * completely substituted with an array with different length and
742: * different members.
743: *
744: * @param kitClass
745: * class of the kit for which the value is retrieved
746: * @param settingName
747: * name of the retrieved setting
748: * @param kavArray
749: * kit-class and value array to be filtered
750: */
751: public KitAndValue[] filterValueHierarchy(Class kitClass,
752: String settingName, KitAndValue[] kavArray);
753:
754: }
755:
756: }
|