Index: editor/codetemplates/src/org/netbeans/lib/editor/codetemplates/storage/CodeTemplatesStorage.java =================================================================== RCS file: /cvs/editor/codetemplates/src/org/netbeans/lib/editor/codetemplates/storage/CodeTemplatesStorage.java,v retrieving revision 1.2 diff -u -r1.2 CodeTemplatesStorage.java --- editor/codetemplates/src/org/netbeans/lib/editor/codetemplates/storage/CodeTemplatesStorage.java 29 Nov 2007 10:19:44 -0000 1.2 +++ editor/codetemplates/src/org/netbeans/lib/editor/codetemplates/storage/CodeTemplatesStorage.java 10 Jan 2008 09:45:19 -0000 @@ -171,16 +171,17 @@ } else if (qName.equals(E_CODETEMPLATE)) { boolean removed = Boolean.valueOf(attributes.getValue(A_REMOVE)); + abbreviation = null; + description = null; + contexts = null; + uuid = null; + text = null; + cdataText = null; + if (removed) { String abbrev = attributes.getValue(A_ABBREV); removedTemplates.add(abbrev); - abbreviation = null; - description = null; - contexts = null; - uuid = null; - text = null; - cdataText = null; } else { // Read the abbreviation abbreviation = attributes.getValue(A_ABBREV); Index: editor/lib/src/org/netbeans/editor/Settings.java =================================================================== RCS file: /cvs/editor/lib/src/org/netbeans/editor/Settings.java,v retrieving revision 1.2 diff -u -r1.2 Settings.java --- editor/lib/src/org/netbeans/editor/Settings.java 11 Dec 2007 11:49:34 -0000 1.2 +++ editor/lib/src/org/netbeans/editor/Settings.java 10 Jan 2008 09:45:19 -0000 @@ -42,6 +42,8 @@ package org.netbeans.editor; import java.awt.Color; +import java.awt.Dimension; +import java.awt.Insets; import java.lang.reflect.Method; import java.util.Map; import java.util.List; @@ -53,9 +55,13 @@ import java.util.Collections; import java.util.HashSet; import java.util.Set; +import java.util.StringTokenizer; import java.util.WeakHashMap; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.prefs.PreferenceChangeEvent; +import java.util.prefs.PreferenceChangeListener; +import java.util.prefs.Preferences; import javax.swing.KeyStroke; import javax.swing.text.AttributeSet; import javax.swing.text.StyleConstants; @@ -360,27 +366,108 @@ * @return the value of the setting */ public static Object getValue(Class kitClass, String settingName, boolean evaluateEvaluators) { - + String mimeType = BaseKit.kitsTracker_FindMimeType(kitClass); + MimePath mimePath = mimeType == null ? MimePath.EMPTY : MimePath.parse(mimeType); + + // Get the value + Object value = getValueEx(mimePath, kitClass, settingName, evaluateEvaluators); + + // filter the value if necessary + Filter [] currentFilters = filters; + for (int i = 0; i < currentFilters.length; i++) { + value = currentFilters[i].filterValue(kitClass, settingName, value); + } + + return value; + } + + private static Object getValueEx(MimePath mimePath, Class kitClass, String settingName, boolean evaluateEvaluators) { Object value = null; boolean hasValue = false; - // Check if the requested setting is a coloring - if (!hasValue) { + // read the value according to the guessed type of the setting + if (settingName != null && SettingsNames.ABBREV_MAP.equals(settingName)) { + value = findCodeTemplates(mimePath); + hasValue = true; + } else if (settingName != null && SettingsNames.KEY_BINDING_LIST.equals(settingName)) { + value = findKeyBindings(mimePath); + hasValue = true; + } else if (settingName != null && SettingsNames.MACRO_MAP.equals(settingName)) { + value = findMacros(mimePath); + hasValue = true; + } else { + // Check if the requested setting is a coloring if (settingName != null && HIGHLIGHT_COLOR_NAMES.contains(settingName)) { - value = findColor(settingName, kitClass); + value = findColor(settingName, mimePath); hasValue = true; } else if (settingName != null && HIGHLIGHT_COLORING_NAMES.contains(settingName)) { - value = findColoring(settingName, kitClass, false, true); + value = findColoring(settingName, mimePath, false, true); hasValue = true; } else { String coloringName = translateOldTokenColoringName(settingName); if (coloringName != null) { - value = findColoring(coloringName, kitClass, true, true); + value = findColoring(coloringName, mimePath, true, true); hasValue = true; } } + + // Try getting it from editor Preferences + if (!hasValue) { + Preferences prefs = findPreferences(mimePath); + + // check if there is actually some value + if (prefs != null && null != prefs.get(settingName, null)) { + // try guessing the type + Class type = null; + String javaType = prefs.get(JAVATYPE_KEY_PREFIX + settingName, null); + if (javaType != null) { + type = typeFromString(javaType); + } + + if (type != null) { + if (type.equals(Boolean.class)) { + value = prefs.getBoolean(settingName, false); + hasValue = true; + } else if (type.equals(Integer.class)) { + value = prefs.getInt(settingName, 0); + hasValue = true; + } else if (type.equals(Long.class)) { + value = prefs.getLong(settingName, 0L); + hasValue = true; + } else if (type.equals(Float.class)) { + value = prefs.getFloat(settingName, 0.0F); + hasValue = true; + } else if (type.equals(Double.class)) { + value = prefs.getDouble(settingName, 0.0D); + hasValue = true; + } else if (type.equals(Insets.class)) { + value = parseInsets(prefs.get(settingName, null)); + hasValue = true; + } else if (type.equals(Dimension.class)) { + value = parseDimension(prefs.get(settingName, null)); + hasValue = true; + } else if (type.equals(Color.class)) { + value = parseColor(prefs.get(settingName, null)); + hasValue = true; + } else if (type.equals(String.class)) { + value = prefs.get(settingName, null); + hasValue = true; + } else { + LOG.log(Level.WARNING, "Can't load setting '" + settingName + "' with value '" + prefs.get(settingName, null) //NOI18N + + "' through org.netbeans.editor.Settings! Unsupported value conversion to " + type, new Throwable("Stacktrace")); //NOI18N + } + } else { + // unknown setting type, treat it as String + LOG.warning("Can't determine type of '" + settingName + "' editor setting. If you supplied this setting" //NOI18N + + " through the editor implementation of java.util.prefs.Preferences you should use the 'javaType'" //NOI18N + + " attribute and specify the class representing values of this setting. There seem to be legacy" //NOI18N + + " clients accessing your setting through the old org.netbeans.editor.Settings."); //NOI18N + } + } + } } + // Fallback on to the kitmaps if (!hasValue) { List allKitMaps = getAllKitMaps(kitClass); assert allKitMaps.size() % 2 == 0 : "allKitMaps should contain pairs of [kitClass, settingsMap]."; //NOI18N @@ -398,18 +485,11 @@ } } - // filter the value if necessary - Filter [] currentFilters = filters; - for (int i = 0; i < currentFilters.length; i++) { - value = currentFilters[i].filterValue(kitClass, settingName, value); - } - return value; } /** Get the value hierarchy and evaluate the evaluators */ - public static KitAndValue[] getValueHierarchy(Class kitClass, - String settingName) { + public static KitAndValue[] getValueHierarchy(Class kitClass, String settingName) { return getValueHierarchy(kitClass, settingName, true); } @@ -429,25 +509,23 @@ * setting's value on the specific kit level. */ public static KitAndValue[] getValueHierarchy(Class kitClass, String settingName, boolean evaluateEvaluators) { - ArrayList kavList = new ArrayList(); - List allKitMaps = getAllKitMaps(kitClass); - - assert allKitMaps.size() % 2 == 0 : "allKitMaps should contain pairs of [kitClass, settingsMap]."; //NOI18N + ArrayList kavList = new ArrayList(); - for(int i = 0; i < allKitMaps.size() / 2; i++) { - Class kc = (Class) allKitMaps.get(2 * i); - Map map = (Map) allKitMaps.get(2 * i + 1); - - Object value = map.get(settingName); - if (evaluateEvaluators && value instanceof Evaluator) { - value = ((Evaluator)value).getValue(kc, settingName); - } + for (Class kc = kitClass; kc != null; kc = kc.getSuperclass()) { + String mimeType = BaseKit.kitsTracker_FindMimeType(kitClass); + MimePath mimePath = mimeType == null ? MimePath.EMPTY : MimePath.parse(mimeType); + + Object value = getValueEx(mimePath, kc, settingName, evaluateEvaluators); if (value != null) { kavList.add(new KitAndValue(kc, value)); } + + if (mimePath == MimePath.EMPTY) { + break; + } } - KitAndValue[] kavArray = (KitAndValue[])kavList.toArray(new KitAndValue[kavList.size()]); + KitAndValue[] kavArray = kavList.toArray(new KitAndValue[kavList.size()]); // filter the value if necessary Filter [] currentFilters = filters; @@ -469,24 +547,89 @@ * be null to clear the value for the specified kit */ public static void setValue(Class kitClass, String settingName, Object newValue) { - Object oldValue; - - synchronized (Settings.class) { - Map map = getKitMap(kitClass, true); - oldValue = map.get(settingName); - if (oldValue == null && newValue == null - || (oldValue != null && oldValue.equals(newValue)) - ) { - return; // no change + if (settingName != null && SettingsNames.ABBREV_MAP.equals(settingName)) { + LOG.log(Level.WARNING, "Can't save 'SettingsNames.ABBREV_MAP' setting through org.netbeans.editor.Settings!", new Throwable("Stacktrace")); //NOI18N + } else if (settingName != null && SettingsNames.KEY_BINDING_LIST.equals(settingName)) { + LOG.log(Level.WARNING, "Can't save 'SettingsNames.KEY_BINDING_LIST' setting through org.netbeans.editor.Settings!", new Throwable("Stacktrace")); //NOI18N + } else if (settingName != null && SettingsNames.MACRO_MAP.equals(settingName)) { + LOG.log(Level.WARNING, "Can't save 'SettingsNames.MACRO_MAP' setting through org.netbeans.editor.Settings!", new Throwable("Stacktrace")); //NOI18N + } else { + boolean coloring = false; + + // Check if the requested setting is a coloring + if (settingName != null && HIGHLIGHT_COLOR_NAMES.contains(settingName)) { + coloring = true; + } else if (settingName != null && HIGHLIGHT_COLORING_NAMES.contains(settingName)) { + coloring = true; + } else { + String coloringName = translateOldTokenColoringName(settingName); + if (coloringName != null) { + coloring = true; + } } - if (newValue != null) { - map.put(settingName, newValue); + + if (coloring) { + LOG.log(Level.WARNING, "Can't save coloring '" + settingName + "' through org.netbeans.editor.Settings!", new Throwable("Stacktrace")); //NOI18N } else { - map.remove(settingName); + boolean useKitMaps = false; + String mimeType = BaseKit.kitsTracker_FindMimeType(kitClass); + MimePath mimePath = mimeType == null ? MimePath.EMPTY : MimePath.parse(mimeType); + Preferences prefs = findPreferences(mimePath); + + if (prefs != null) { + if (newValue != null) { + if (newValue instanceof Boolean) { + prefs.putBoolean(settingName, (Boolean) newValue); + } else if (newValue instanceof Integer) { + prefs.putInt(settingName, (Integer) newValue); + } else if (newValue instanceof Long) { + prefs.putLong(settingName,(Long) newValue); + } else if (newValue instanceof Float) { + prefs.putFloat(settingName, (Float) newValue); + } else if (newValue instanceof Double) { + prefs.putDouble(settingName, (Double) newValue); + } else if (newValue instanceof Insets) { + prefs.put(settingName, insetsToString((Insets) newValue)); + prefs.put(JAVATYPE_KEY_PREFIX + settingName, Insets.class.getName()); + } else if (newValue instanceof Dimension) { + prefs.put(settingName, dimensionToString((Dimension) newValue)); + prefs.put(JAVATYPE_KEY_PREFIX + settingName, Dimension.class.getName()); + } else if (newValue instanceof Color) { + prefs.put(settingName, color2String((Color) newValue)); + prefs.put(JAVATYPE_KEY_PREFIX + settingName, Color.class.getName()); + } else if (newValue instanceof String) { + prefs.put(settingName, (String) newValue); + } else { + LOG.log(Level.FINE, "Can't save setting '" + settingName + "' with value '" + newValue //NOI18N + + "' through org.netbeans.editor.Settings; unsupported value conversion!", new Throwable("Stacktrace")); //NOI18N + useKitMaps = true; + } + } else { + prefs.remove(settingName); + } + } else { + useKitMaps = true; + } + + if (useKitMaps) { + // no prefs implementation, fall back on to the kit maps + synchronized (Settings.class) { + Map map = getKitMap(kitClass, true); + Object oldValue = map.get(settingName); + if (oldValue == null && newValue == null || (oldValue != null && oldValue.equals(newValue))) { + return; // no change + } + if (newValue != null) { + map.put(settingName, newValue); + } else { + map.remove(settingName); + } + } + } } } - - fireSettingsChange(kitClass, settingName, oldValue, newValue); + + fireSettingsChange(kitClass, settingName, null, newValue); } /** Don't change the value of the setting, but fire change @@ -764,8 +907,8 @@ return list; } } - -/** Kit class and value pair */ + + /** Kit class and value pair */ public static class KitAndValue { public Class kitClass; @@ -779,8 +922,7 @@ } - -/** Initializer of the settings updates the map filled + /** Initializer of the settings updates the map filled * with settings for the particular kit class when asked. * If the settings are being initialized all the initializers registered * by the Settings.addInitializer() are being asked to update @@ -805,7 +947,7 @@ } -/** Abstract implementation of the initializer dealing with the name. */ + /** Abstract implementation of the initializer dealing with the name. */ public static abstract class AbstractInitializer implements Initializer { private String name; @@ -824,7 +966,7 @@ } // End of AbstractInitializer class -/** Sort the settings initializers that were added to the settings. + /** Sort the settings initializers that were added to the settings. * There can be only one sorter for the Settings, but it can delegate * to previously registered sorter. */ @@ -834,7 +976,7 @@ } -/** Initializer sorter that delegates to another sorter. */ + /** Initializer sorter that delegates to another sorter. */ public static abstract class FilterInitializerSorter { private InitializerSorter delegate; @@ -853,7 +995,7 @@ -/** Evaluator can be used in cases when value of some setting + /** Evaluator can be used in cases when value of some setting * depends on the value for other setting and it allows to compute * the value dynamically based on the other setting(s) value. * The Evaluator instance can be used as the value @@ -880,7 +1022,7 @@ } -/** Filter is applied on every value or KitAndValue pairs returned from getValue(). + /** Filter is applied on every value or KitAndValue pairs returned from getValue(). * The filter can be registered by calling Settings.addFilter(). * Each call to Settings.getValue() will first retrieve the value and * then call the Filter.filterValue() to get the final value. Each call @@ -908,7 +1050,7 @@ } -// This is just for debugging and should not normally be used. + // This is just for debugging and should not normally be used. private static final class LoggingMap extends HashMap { private Class kitClass; @@ -1017,21 +1159,18 @@ HIGHLIGHT_COLORING_NAMES.add(SettingsNames.STATUS_BAR_BOLD_COLORING); } - private static Coloring findColoring(String coloringName, Class kitClass, boolean token, boolean highlight) { - AttributeSet attribs = findAttribs(coloringName, kitClass, token, highlight); + private static Coloring findColoring(String coloringName, MimePath mimePath, boolean token, boolean highlight) { + AttributeSet attribs = findAttribs(coloringName, mimePath, token, highlight); return attribs == null ? null : Coloring.fromAttributeSet(attribs); } - private static Color findColor(String coloringName, Class kitClass) { - AttributeSet attribs = findAttribs(coloringName, kitClass, false, true); + private static Color findColor(String coloringName, MimePath mimePath) { + AttributeSet attribs = findAttribs(coloringName, mimePath, false, true); return attribs == null ? null : (Color) attribs.getAttribute(StyleConstants.Foreground); } - private static AttributeSet findAttribs(String coloringName, Class kitClass, boolean token, boolean highlight) { - String mimeType = BaseKit.kitsTracker_FindMimeType(kitClass); - + private static AttributeSet findAttribs(String coloringName, MimePath mimePath, boolean token, boolean highlight) { synchronized (FCS_CACHE) { - MimePath mimePath = mimeType == null ? MimePath.EMPTY : MimePath.parse(mimeType); ChangesTrackingLookupResult ctlr = FCS_CACHE.get(mimePath); if (ctlr == null) { @@ -1082,11 +1221,8 @@ private static final Map> KBS_CACHE = new WeakHashMap>(); - private static List findKeyBindings(Class kitClass) { - String mimeType = BaseKit.kitsTracker_FindMimeType(kitClass); - + private static List findKeyBindings(MimePath mimePath) { synchronized (KBS_CACHE) { - MimePath mimePath = mimeType == null ? MimePath.EMPTY : MimePath.parse(mimeType); ChangesTrackingLookupResult ctlr = KBS_CACHE.get(mimePath); if (ctlr == null) { @@ -1127,11 +1263,8 @@ private static final Map> CTS_CACHE = new WeakHashMap>(); - private static Map findCodeTemplates(Class kitClass) { - String mimeType = BaseKit.kitsTracker_FindMimeType(kitClass); - + private static Map findCodeTemplates(MimePath mimePath) { synchronized (CTS_CACHE) { - MimePath mimePath = mimeType == null ? MimePath.EMPTY : MimePath.parse(mimeType); ChangesTrackingLookupResult ctlr = CTS_CACHE.get(mimePath); if (ctlr == null) { @@ -1192,45 +1325,84 @@ } // End of TrackingResult class // ---------------------------------------------------------- + // Preferences bridge to Editor Settings API + // ---------------------------------------------------------- + + private static final Map PREFS_CACHE = + new WeakHashMap(); + + private static Preferences findPreferences(MimePath mimePath) { + synchronized (PREFS_CACHE) { + PreferenceChangesTracker tracker = PREFS_CACHE.get(mimePath); + + if (tracker == null) { + Preferences prefs = MimeLookup.getLookup(mimePath).lookup(Preferences.class); + + // in some tests there is no MimeLookup at all + if (prefs != null) { + tracker = new PreferenceChangesTracker(prefs); + PREFS_CACHE.put(mimePath, tracker); + } + } + + return tracker == null ? null : tracker.getPreferences(); + } + } + + private static final class PreferenceChangesTracker implements PreferenceChangeListener { + private final Preferences prefs; + + public PreferenceChangesTracker(Preferences prefs) { + this.prefs = prefs; + this.prefs.addPreferenceChangeListener(WeakListeners.create(PreferenceChangeListener.class, this, this.prefs)); + } + + public Preferences getPreferences() { + return prefs; + } + + public void preferenceChange(PreferenceChangeEvent evt) { + fireSettingsChange(null, evt.getKey(), null, null); + } + } // End of TrackingResult class + + // ---------------------------------------------------------- // Macros to Editor Settings API // ---------------------------------------------------------- // XXX: rewrite this to not use reflection. It will require dependency on editor/macros - // and editor/settings/storage. It should also liste on changes fired from EditorSettingsStorage + // and editor/settings/storage. It should also listen on changes fired from EditorSettingsStorage // and call fireSettingsChange(null, SettingsName.MACRO_MAP, null, null). Should be done // after the Settings & co. is factored out to its own deprecated module so that we don't // introduce additional dependencies in editor/lib. - private static Map findMacros(Class kitClass) { + private static Map findMacros(MimePath mimePath) { Map macros = new HashMap(); - String mimeType = BaseKit.kitsTracker_FindMimeType(kitClass); - if (mimeType != null) { - ClassLoader classLoader = Lookup.getDefault().lookup(ClassLoader.class); - try { - Class essClass = classLoader.loadClass("org.netbeans.modules.editor.settings.storage.api.EditorSettingsStorage"); //NOI18N - Method findMethod = essClass.getDeclaredMethod("find", String.class); //NOI18N - Object macrosEss = findMethod.invoke(null, "Macros"); //NOI18N - - if (macrosEss != null) { - Class mdClass = classLoader.loadClass("org.netbeans.modules.editor.macros.storage.MacroDescription"); //NOI18N - Method getCodeMethod = mdClass.getDeclaredMethod("getCode"); //NOI18N - - Method loadMethod = essClass.getDeclaredMethod("load", MimePath.class, String.class, Boolean.TYPE); //NOI18N - Map macroDescriptions = (Map) loadMethod.invoke(macrosEss, MimePath.parse(mimeType), null, false); - for(Object key : macroDescriptions.keySet()) { - String macroName = (String) key; - Object macroDescription = macroDescriptions.get(key); + ClassLoader classLoader = Lookup.getDefault().lookup(ClassLoader.class); + try { + Class essClass = classLoader.loadClass("org.netbeans.modules.editor.settings.storage.api.EditorSettingsStorage"); //NOI18N + Method findMethod = essClass.getDeclaredMethod("find", String.class); //NOI18N + Object macrosEss = findMethod.invoke(null, "Macros"); //NOI18N + + if (macrosEss != null) { + Class mdClass = classLoader.loadClass("org.netbeans.modules.editor.macros.storage.MacroDescription"); //NOI18N + Method getCodeMethod = mdClass.getDeclaredMethod("getCode"); //NOI18N + + Method loadMethod = essClass.getDeclaredMethod("load", MimePath.class, String.class, Boolean.TYPE); //NOI18N + Map macroDescriptions = (Map) loadMethod.invoke(macrosEss, mimePath, null, false); + for(Object key : macroDescriptions.keySet()) { + String macroName = (String) key; + Object macroDescription = macroDescriptions.get(key); - String macroCode = (String) getCodeMethod.invoke(macroDescription); - macros.put(macroName, macroCode); - } + String macroCode = (String) getCodeMethod.invoke(macroDescription); + macros.put(macroName, macroCode); } - } catch (Exception e) { - // ignore } + } catch (Exception e) { + // ignore } - macros.put(null, findKeyBindings(kitClass)); + macros.put(null, findKeyBindings(mimePath)); return macros; } @@ -1238,16 +1410,165 @@ private static final class SettingsToMimeLookupBridge implements Evaluator { public Object getValue(Class kitClass, String settingName) { + String mimeType = BaseKit.kitsTracker_FindMimeType(kitClass); + MimePath mimePath = mimeType == null ? MimePath.EMPTY : MimePath.parse(mimeType); + if (SettingsNames.ABBREV_MAP.equals(settingName)) { - return findCodeTemplates(kitClass); + return findCodeTemplates(mimePath); } if (SettingsNames.KEY_BINDING_LIST.equals(settingName)) { - return findKeyBindings(kitClass); + return findKeyBindings(mimePath); } if (SettingsNames.MACRO_MAP.equals(settingName)) { - return findMacros(kitClass); + return findMacros(mimePath); } + return null; } } // End of SettingsToMimeLookupBridge class + + /** Coverts Insets to String representation */ + private static String insetsToString(Insets ins) { + StringBuilder sb = new StringBuilder(); + sb.append(ins.top); + sb.append(','); //NOI18N + + sb.append(ins.left); + sb.append(','); //NOI18N + + sb.append(ins.bottom); + sb.append(','); //NOI18N + + sb.append(ins.right); + + return sb.toString(); + } + + /** Converts textual representation of Insets */ + private static Insets parseInsets(String s) { + StringTokenizer st = new StringTokenizer(s, ","); //NOI18N + + int arr[] = new int[4]; + int i = 0; + while (st.hasMoreElements()) { + if (i > 3) { + return null; + } + try { + arr[i] = Integer.parseInt(st.nextToken()); + } catch (NumberFormatException nfe) { + LOG.log(Level.WARNING, null, nfe); + return null; + } + i++; + } + if (i != 4) { + return null; + } else { + return new Insets(arr[0], arr[1], arr[2], arr[3]); + } + } + + private static String dimensionToString(Dimension dim) { + StringBuilder sb = new StringBuilder(); + sb.append(dim.width); + sb.append(','); //NOI18N + sb.append(dim.height); + return sb.toString(); + } + + private static Dimension parseDimension(String s) { + StringTokenizer st = new StringTokenizer(s, ","); // NOI18N + + int arr[] = new int[2]; + int i = 0; + while (st.hasMoreElements()) { + if (i > 1) { + return null; + } + try { + arr[i] = Integer.parseInt(st.nextToken()); + } catch (NumberFormatException nfe) { + LOG.log(Level.WARNING, null, nfe); + return null; + } + i++; + } + if (i != 2) { + return null; + } else { + return new Dimension(arr[0], arr[1]); + } + } + + private static String wrap(String s) { + return (s.length() == 1) ? "0" + s : s; // NOI18N + } + + /** Converts Color to hexadecimal String representation */ + private static String color2String(Color c) { + StringBuilder sb = new StringBuilder(); + sb.append('#'); // NOI18N + sb.append(wrap(Integer.toHexString(c.getRed()).toUpperCase())); + sb.append(wrap(Integer.toHexString(c.getGreen()).toUpperCase())); + sb.append(wrap(Integer.toHexString(c.getBlue()).toUpperCase())); + return sb.toString(); + } + + /** Converts a String to an integer and returns the specified opaque Color. */ + private static Color parseColor(String s) { + try { + return Color.decode(s); + } catch (NumberFormatException nfe) { + LOG.log(Level.WARNING, null, nfe); + return null; + } + } + +// private static final Map typesCache = new HashMap(); +// private static final class NO_TYPE {}; +// private static Class typeFromName(String settingName) { +// synchronized (typesCache) { +// Class settingType = typesCache.get(settingName); +// +// if (settingType == null) { +// EditorSettingClass esc = null; +// +// try { +// for (Field f : SettingsNames.class.getDeclaredFields()) { +// Object value; +// try { +// if ((f.getModifiers() & Modifier.STATIC) == Modifier.STATIC && +// null != (value = f.get(null)) && +// value.equals(settingName) +// ) { +// esc = f.getAnnotation(EditorSettingClass.class); +// break; +// } +// } catch (Exception e) { +// LOG.log(Level.FINE, null, e); +// } +// } +// } catch (SecurityException se) { +// LOG.log(Level.FINE, null, se); +// } +// +// settingType = esc == null ? NO_TYPE.class : esc.value(); +// typesCache.put(settingName, settingType); +// } +// +// return settingType == NO_TYPE.class ? null : settingType; +// } +// } + + private static final String JAVATYPE_KEY_PREFIX = "nbeditor-javaType-for-legacy-setting_"; //NOI18N + private static Class typeFromString(String javaType) { + try { + ClassLoader classLoader = Lookup.getDefault().lookup(ClassLoader.class); + return classLoader == null ? null : classLoader.loadClass(javaType); + } catch (ClassNotFoundException cnfe) { + LOG.log(Level.WARNING, null, cnfe); + return null; + } + } } Index: editor/lib/test/unit/src/org/netbeans/editor/SettingsTest.java =================================================================== RCS file: /cvs/editor/lib/test/unit/src/org/netbeans/editor/SettingsTest.java,v retrieving revision 1.6 diff -u -r1.6 SettingsTest.java --- editor/lib/test/unit/src/org/netbeans/editor/SettingsTest.java 11 Dec 2007 11:49:35 -0000 1.6 +++ editor/lib/test/unit/src/org/netbeans/editor/SettingsTest.java 10 Jan 2008 09:45:29 -0000 @@ -31,7 +31,9 @@ import java.net.URL; import java.util.List; import java.util.Map; +import java.util.prefs.Preferences; import javax.swing.KeyStroke; +import org.netbeans.api.editor.mimelookup.MimeLookup; import org.netbeans.core.startup.Main; import org.netbeans.junit.NbTestCase; import org.netbeans.modules.editor.settings.storage.EditorTestLookup; @@ -135,9 +137,10 @@ } public void testNoEventsWhenInitializing() { - Settings.addInitializer(new MyInitializer()); + MyInitializer init = new MyInitializer(); + Settings.addInitializer(init); try { - Thread.sleep(1000); + Thread.sleep(2000); } catch (InterruptedException ie) { } @@ -145,9 +148,14 @@ MyListener listener = new MyListener(); Settings.addSettingsChangeListener(listener); try { - Object value = Settings.getValue(BaseKit.class, MyInitializer.TEST_SETTING_NAME); - assertEquals("Wrong test setting value", Boolean.TRUE, value); + Settings.getValue(BaseKit.class, MyInitializer.TEST_SETTING_NAME); + assertTrue("Initializer not called at all", init.updateCalled > 0); assertEquals("There should be no events", 0, listener.eventsCnt); + + // MyInitializer does not set the value correct way, so the value is + // not returned from the first call + Object value = Settings.getValue(BaseKit.class, MyInitializer.TEST_SETTING_NAME); + assertEquals("Wrong test setting value", Boolean.TRUE, value); } finally { Settings.removeSettingsChangeListener(listener); } @@ -159,6 +167,11 @@ public void testEventsWhenNotInitializing() { Settings.addInitializer(new MyInitializer()); try { + Thread.sleep(2000); + } catch (InterruptedException ie) { + + } + try { MyListener listener = new MyListener(); Settings.addSettingsChangeListener(listener); try { @@ -191,6 +204,30 @@ } } + public void testSettingsFromPrefs() { + Settings.addInitializer(new MyInitializer3()); + try { + // settings supplied from preferences.xml + checkSetting("test-prop-A", Integer.class, 123); + checkSetting("test-prop-B", String.class, "Hello"); + checkSetting("test-prop-C", Boolean.class, true); + checkSetting("test-prop-D", Double.class, 3.1415927D); + + // settings supplied from MyInitializer3 + checkSetting(MyInitializer3.PROP_A, String.class, MyInitializer3.PROP_A + "_value"); + + // settings supplied from both + checkSetting(MyInitializer3.CLASH_PROP, String.class, "value_from_preferences.xml"); + + // writing + Settings.setValue(MyKit2.class, "newly-written-setting", 0.1f); + Preferences prefs = MimeLookup.getLookup(new MyKit2().getContentType()).lookup(Preferences.class); + assertEquals("Value was not written", 0.1f, prefs.getFloat("newly-written-setting", -1f)); + } finally { + Settings.removeInitializer(MyInitializer3.NAME); + } + } + public static final class MyKit extends BaseKit { public MyKit() { super(); @@ -207,11 +244,14 @@ public static final String NAME = "TestInitializer"; public static final String TEST_SETTING_NAME = "TestSetting"; + public int updateCalled = 0; + public MyInitializer() { super(NAME); } public void updateSettingsMap(Class kitClass, Map settingsMap) { + updateCalled++; Settings.setValue(BaseKit.class, TEST_SETTING_NAME, Boolean.TRUE); } } // End of MyInitializer class @@ -238,7 +278,7 @@ } } else { if (kitClass == MyKit.class) { - Settings.setValue(BaseKit.class, TEST_SETTING_NAME, Boolean.TRUE); + settingsMap.put(TEST_SETTING_NAME, Boolean.TRUE); } } } @@ -252,4 +292,38 @@ eventsCnt++; } } // End of MyListener class + + public static final class MyKit2 extends BaseKit { + public MyKit2() { + super(); + } + + @Override + public String getContentType() { + return "text/x-type-B"; + } + } // End of MyKit2 class + + private static final class MyInitializer3 extends Settings.AbstractInitializer { + + public static final String NAME = "MyInitializer3"; + public static final String PROP_A = "test-old-prop-A"; + public static final String CLASH_PROP = "test-prop-clash"; + + public MyInitializer3() { + super(NAME); + } + + public void updateSettingsMap(Class kitClass, Map settingsMap) { + settingsMap.put(PROP_A, PROP_A + "_value"); + settingsMap.put(CLASH_PROP, "value_from_MyInitializer3"); + } + } // End of MyInitializer class + + private static void checkSetting(String name, Class javaType, Object expectedValue) { + Object value = Settings.getValue(MyKit2.class, name); + assertNotNull("No '" + name + "'", value); + assertTrue("Wrong type of '" + name + "'", javaType.isAssignableFrom(value.getClass())); + assertEquals("Wrong '" + name + "' value", expectedValue, value); + } } Index: editor/lib/test/unit/src/org/netbeans/editor/preferences.xml =================================================================== RCS file: editor/lib/test/unit/src/org/netbeans/editor/preferences.xml diff -N editor/lib/test/unit/src/org/netbeans/editor/preferences.xml --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ editor/lib/test/unit/src/org/netbeans/editor/preferences.xml 10 Jan 2008 09:45:29 -0000 @@ -0,0 +1,11 @@ + + + + + + + + + + + Index: editor/lib/test/unit/src/org/netbeans/editor/test-layer.xml =================================================================== RCS file: /cvs/editor/lib/test/unit/src/org/netbeans/editor/test-layer.xml,v retrieving revision 1.3 diff -u -r1.3 test-layer.xml --- editor/lib/test/unit/src/org/netbeans/editor/test-layer.xml 11 Dec 2007 11:49:35 -0000 1.3 +++ editor/lib/test/unit/src/org/netbeans/editor/test-layer.xml 10 Jan 2008 09:45:29 -0000 @@ -65,6 +65,15 @@ + + + + + + + + + Index: editor/macros/src/org/netbeans/modules/editor/macros/storage/MacrosStorage.java =================================================================== RCS file: /cvs/editor/macros/src/org/netbeans/modules/editor/macros/storage/MacrosStorage.java,v retrieving revision 1.2 diff -u -r1.2 MacrosStorage.java --- editor/macros/src/org/netbeans/modules/editor/macros/storage/MacrosStorage.java 11 Dec 2007 11:49:39 -0000 1.2 +++ editor/macros/src/org/netbeans/modules/editor/macros/storage/MacrosStorage.java 10 Jan 2008 09:45:29 -0000 @@ -174,15 +174,16 @@ } else if (qName.equals(E_MACRO)) { boolean removed = Boolean.valueOf(attributes.getValue(A_REMOVE)); + name = null; + description = null; + shortcuts = null; + text = null; + cdataText = null; + if (removed) { String macroName = attributes.getValue(A_NAME); removedMacros.add(macroName); - name = null; - description = null; - shortcuts = null; - text = null; - cdataText = null; } else { // Read the name name = attributes.getValue(A_NAME); Index: editor/settings/branch-info.txt =================================================================== RCS file: /cvs/editor/settings/branch-info.txt,v retrieving revision 1.3 diff -u -r1.3 branch-info.txt --- editor/settings/branch-info.txt 11 Dec 2007 11:49:45 -0000 1.3 +++ editor/settings/branch-info.txt 10 Jan 2008 09:45:29 -0000 @@ -1,5 +1,5 @@ Branch created from: editor_settings_90403_base -Last update from trunk (rebased to): editor_settings_90403_base_3 +Last update from trunk (rebased to): editor_settings_90403_base_4 Last merge to trunk (delivered from): editor_settings_90403_delivery_2 Additional modules branched off from the editor_settings_90403_base_2 tag: editor/codetemplates, editor/options Index: editor/settings/storage/src/META-INF/services/org.netbeans.modules.editor.settings.storage.spi.StorageDescription =================================================================== RCS file: /cvs/editor/settings/storage/src/META-INF/services/org.netbeans.modules.editor.settings.storage.spi.StorageDescription,v retrieving revision 1.2 diff -u -r1.2 org.netbeans.modules.editor.settings.storage.spi.StorageDescription --- editor/settings/storage/src/META-INF/services/org.netbeans.modules.editor.settings.storage.spi.StorageDescription 29 Nov 2007 10:19:55 -0000 1.2 +++ editor/settings/storage/src/META-INF/services/org.netbeans.modules.editor.settings.storage.spi.StorageDescription 10 Jan 2008 09:45:29 -0000 @@ -1,2 +1,3 @@ org.netbeans.modules.editor.settings.storage.fontscolors.ColoringStorage -org.netbeans.modules.editor.settings.storage.keybindings.KeyMapsStorage \ No newline at end of file +org.netbeans.modules.editor.settings.storage.keybindings.KeyMapsStorage +org.netbeans.modules.editor.settings.storage.preferences.PreferencesStorage \ No newline at end of file Index: editor/settings/storage/src/org/netbeans/modules/editor/settings/storage/SettingsProvider.java =================================================================== RCS file: /cvs/editor/settings/storage/src/org/netbeans/modules/editor/settings/storage/SettingsProvider.java,v retrieving revision 1.13 diff -u -r1.13 SettingsProvider.java --- editor/settings/storage/src/org/netbeans/modules/editor/settings/storage/SettingsProvider.java 29 Nov 2007 10:19:58 -0000 1.13 +++ editor/settings/storage/src/org/netbeans/modules/editor/settings/storage/SettingsProvider.java 10 Jan 2008 09:45:29 -0000 @@ -46,7 +46,9 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.lang.ref.WeakReference; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Map; import java.util.WeakHashMap; import java.util.logging.Level; @@ -57,6 +59,7 @@ import org.netbeans.api.editor.settings.KeyBindingSettings; import org.netbeans.modules.editor.settings.storage.api.EditorSettings; import org.netbeans.modules.editor.settings.storage.fontscolors.FontColorSettingsImpl; +import org.netbeans.modules.editor.settings.storage.preferences.PreferencesImpl; import org.netbeans.spi.editor.mimelookup.MimeDataProvider; import org.openide.util.Lookup; import org.openide.util.WeakListeners; @@ -138,6 +141,7 @@ private final InstanceContent ic; private CompositeFCS fontColorSettings = null; private Object keyBindingSettings = null; + private Object preferences = null; private KeyBindingSettingsImpl kbsi; @@ -177,10 +181,12 @@ synchronized (this) { fontColorSettings = new CompositeFCS(mimePath, fcsProfile); keyBindingSettings = this.kbsi.createInstanceForLookup(); + preferences = PreferencesImpl.get(mimePath); ic.set(Arrays.asList(new Object [] { fontColorSettings, keyBindingSettings, + preferences }), null); } } @@ -243,10 +249,17 @@ } if (updateContents) { - ic.set(Arrays.asList(new Object [] { - fontColorSettings, - keyBindingSettings, - }), null); + List list = new ArrayList(); + if (fontColorSettings != null) { + list.add(fontColorSettings); + } + if (keyBindingSettings != null) { + list.add(keyBindingSettings); + } + if (preferences != null) { + list.add(preferences); + } + ic.set(list, null); } } } Index: editor/settings/storage/src/org/netbeans/modules/editor/settings/storage/layer.xml =================================================================== RCS file: /cvs/editor/settings/storage/src/org/netbeans/modules/editor/settings/storage/layer.xml,v retrieving revision 1.7 diff -u -r1.7 layer.xml --- editor/settings/storage/src/org/netbeans/modules/editor/settings/storage/layer.xml 29 Nov 2007 10:20:00 -0000 1.7 +++ editor/settings/storage/src/org/netbeans/modules/editor/settings/storage/layer.xml 10 Jan 2008 09:45:29 -0000 @@ -64,6 +64,9 @@ + + + Index: editor/settings/storage/src/org/netbeans/modules/editor/settings/storage/mime-resolvers.xml =================================================================== RCS file: /cvs/editor/settings/storage/src/org/netbeans/modules/editor/settings/storage/mime-resolvers.xml,v retrieving revision 1.6 diff -u -r1.6 mime-resolvers.xml --- editor/settings/storage/src/org/netbeans/modules/editor/settings/storage/mime-resolvers.xml 29 Nov 2007 10:20:00 -0000 1.6 +++ editor/settings/storage/src/org/netbeans/modules/editor/settings/storage/mime-resolvers.xml 10 Jan 2008 09:45:29 -0000 @@ -80,4 +80,22 @@ + + + + + + + + + + + + + + + + + + Index: editor/settings/storage/src/org/netbeans/modules/editor/settings/storage/preferences/EditorPreferences-1_0.dtd =================================================================== RCS file: editor/settings/storage/src/org/netbeans/modules/editor/settings/storage/preferences/EditorPreferences-1_0.dtd diff -N editor/settings/storage/src/org/netbeans/modules/editor/settings/storage/preferences/EditorPreferences-1_0.dtd --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ editor/settings/storage/src/org/netbeans/modules/editor/settings/storage/preferences/EditorPreferences-1_0.dtd 10 Jan 2008 09:45:29 -0000 @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + Index: editor/settings/storage/src/org/netbeans/modules/editor/settings/storage/preferences/PreferencesImpl.java =================================================================== RCS file: editor/settings/storage/src/org/netbeans/modules/editor/settings/storage/preferences/PreferencesImpl.java diff -N editor/settings/storage/src/org/netbeans/modules/editor/settings/storage/preferences/PreferencesImpl.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ editor/settings/storage/src/org/netbeans/modules/editor/settings/storage/preferences/PreferencesImpl.java 10 Jan 2008 09:45:30 -0000 @@ -0,0 +1,454 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2007 Sun Microsystems, Inc. + */ + +package org.netbeans.modules.editor.settings.storage.preferences; + +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.WeakHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.prefs.AbstractPreferences; +import java.util.prefs.BackingStoreException; +import java.util.prefs.PreferenceChangeEvent; +import java.util.prefs.PreferenceChangeListener; +import java.util.prefs.Preferences; +import org.netbeans.api.editor.mimelookup.MimePath; +import org.netbeans.modules.editor.settings.storage.api.EditorSettingsStorage; +import org.openide.util.RequestProcessor; +import org.openide.util.WeakListeners; + +/** + * + * @author vita + */ +public final class PreferencesImpl extends AbstractPreferences implements PreferenceChangeListener { + + // the constant bellow is used in o.n.e.Settings!! + private static final String JAVATYPE_KEY_PREFIX = "nbeditor-javaType-for-legacy-setting_"; //NOI18N + + public static synchronized PreferencesImpl get(MimePath mimePath) { + PreferencesImpl prefs = INSTANCES.get(mimePath); + + if (prefs == null) { + prefs = new PreferencesImpl(mimePath.getPath()); + INSTANCES.put(mimePath, prefs); + } + + return prefs; + } + + public static final class TypedValue { + + private final String value; + private String javaType; + + public TypedValue(String value, String javaType) { + this.value = value; + this.javaType = javaType; + } + + public String getJavaType() { + return javaType; + } + + public void setJavaType(String javaType) { + this.javaType = javaType; + } + + public String getValue() { + return value; + } + } // End of TypedValue class + + // --------------------------------------------------------------------- + // Preferences API + // --------------------------------------------------------------------- + + public @Override String absolutePath() { + return SLASH; + } + + public @Override String[] childrenNames() throws BackingStoreException { + return EMPTY_ARRAY; + } + + public @Override boolean isUserNode() { + return true; + } + + public @Override String name() { + return EMPTY; + } + + public @Override Preferences node(String path) { + if (path.length() == 0 || path.equals(SLASH)) { + return this; + } else { + throw new IllegalStateException("Editor Preferences does not support children nodes."); //NOI18N + } + } + + public @Override boolean nodeExists(String path) throws BackingStoreException { + if (path.length() == 0 || path.equals(SLASH)) { + return true; + } else { + return false; + } + } + + public @Override Preferences parent() { + return null; + } + + public @Override void removeNode() throws BackingStoreException { + throw new IllegalStateException("Can't remove the root!"); //NOI18N + } + + public @Override final void sync() throws BackingStoreException { + flushTask.waitFinished(); + super.sync(); + } + + public @Override void put(String key, String value) { + if (putValueJavaType.get() == null) { + putValueJavaType.set(String.class.getName()); + } + try { + super.put(key, value); + } finally { + if (putValueJavaType.get().equals(String.class.getName())) { + putValueJavaType.remove(); + } + } + } + + public @Override void putInt(String key, int value) { + putValueJavaType.set(Integer.class.getName()); + try { + super.putInt(key, value); + } finally { + putValueJavaType.remove(); + } + } + + public @Override void putLong(String key, long value) { + putValueJavaType.set(Long.class.getName()); + try { + super.putLong(key, value); + } finally { + putValueJavaType.remove(); + } + } + + public @Override void putBoolean(String key, boolean value) { + putValueJavaType.set(Boolean.class.getName()); + try { + super.putBoolean(key, value); + } finally { + putValueJavaType.remove(); + } + } + + public @Override void putFloat(String key, float value) { + putValueJavaType.set(Float.class.getName()); + try { + super.putFloat(key, value); + } finally { + putValueJavaType.remove(); + } + } + + public @Override void putDouble(String key, double value) { + putValueJavaType.set(Double.class.getName()); + try { + super.putDouble(key, value); + } finally { + putValueJavaType.remove(); + } + } + + public @Override void putByteArray(String key, byte[] value) { + putValueJavaType.set(value.getClass().getName()); + try { + super.putByteArray(key, value); + } finally { + putValueJavaType.remove(); + } + } + + // --------------------------------------------------------------------- + // AbstractPreferences SPI + // --------------------------------------------------------------------- + + @Override + protected AbstractPreferences getChild(String nodeName) throws BackingStoreException { + throw new IllegalStateException("Should never be called."); //NOI18N + } + + @Override + protected boolean isRemoved() { + boolean superRemoved = super.isRemoved(); + assert superRemoved == false : "super.isRemoved() should always == false"; //NOI18N + return superRemoved; + } + + protected @Override void removeNodeSpi() throws BackingStoreException { + throw new IllegalStateException("Should never be called."); //NOI18N + } + + protected @Override String[] childrenNamesSpi() throws BackingStoreException { + throw new IllegalStateException("Should never be called."); //NOI18N + } + + protected @Override AbstractPreferences childSpi(String name) { + throw new IllegalStateException("Should never be called."); //NOI18N + } + + protected @Override void putSpi(String key, String value) { + // This hack is here for refiring preferenceChange events from 'inherited'. + // Comparing the key strings by == is ok here. Please see firePreferenceChange() + // for details. + if (refiringChangeKey.get() != key) { + if (!key.startsWith(JAVATYPE_KEY_PREFIX)) { + getLocal().put(key, new TypedValue(value, putValueJavaType.get())); + asyncInvocationOfFlushSpi(); + } else { + String bareKey = key.substring(JAVATYPE_KEY_PREFIX.length()); + if (getLocal().containsKey(bareKey)) { + getLocal().get(bareKey).setJavaType(value); + asyncInvocationOfFlushSpi(); + } else { + getInherited().put(key, value); + } + } + } + } + + protected @Override String getSpi(String key) { + String bareKey; + boolean returnValue; + + if (key.startsWith(JAVATYPE_KEY_PREFIX)) { + bareKey = key.substring(JAVATYPE_KEY_PREFIX.length()); + returnValue = false; + } else { + bareKey = key; + returnValue = true; + } + + if (getLocal().containsKey(bareKey)) { + TypedValue typedValue = getLocal().get(bareKey); + return returnValue ? typedValue.getValue() : typedValue.getJavaType(); + } else { + return getInherited().get(key, null); + } + } + + protected @Override void removeSpi(String key) { + String bareKey; + boolean removeValue; + + if (key.startsWith(JAVATYPE_KEY_PREFIX)) { + bareKey = key.substring(JAVATYPE_KEY_PREFIX.length()); + removeValue = false; + } else { + bareKey = key; + removeValue = true; + } + + if (getLocal().containsKey(bareKey)) { + if (removeValue) { + getLocal().remove(bareKey); + } else { + getLocal().get(bareKey).setJavaType(null); + } + + asyncInvocationOfFlushSpi(); + } + } + + protected @Override String[] keysSpi() throws BackingStoreException { + Set keys; + Preferences prefs = getInherited(); + + if (prefs != null) { + keys = new HashSet(); + keys.addAll(Arrays.asList(prefs.keys())); + keys.addAll(getLocal().keySet()); + } else { + keys = getLocal().keySet(); + } + + return keys.toArray(new String [keys.size()]); + } + + protected @Override void syncSpi() throws BackingStoreException { + local = null; + } + + protected @Override void flushSpi() throws BackingStoreException { + // This should normally be true when invoked from flushTask, but + // clients can generally get this node and call flush() on it without + // changing anything. + if (local != null) { + EditorSettingsStorage ess = EditorSettingsStorage.get(PreferencesStorage.ID); + try { + ess.save(MimePath.parse(mimePath), null, false, local); + } catch (IOException ioe) { + LOG.log(Level.WARNING, "Can't save editor preferences for '" + mimePath + "'", ioe); //NOI18N + } + } + } + + // --------------------------------------------------------------------- + // PreferenceChangeListener implementation + // --------------------------------------------------------------------- + + public void preferenceChange(PreferenceChangeEvent evt) { + synchronized (lock) { + if (local != null && local.containsKey(evt.getKey())) { + // ignore + return; + } + } + + firePreferenceChange(evt.getKey(), evt.getNewValue()); + } + + // --------------------------------------------------------------------- + // private implementation + // --------------------------------------------------------------------- + + private static final Logger LOG = Logger.getLogger(PreferencesImpl.class.getName()); + + private static final Map INSTANCES = + new WeakHashMap(); + + private static final String SLASH = "/"; //NOI18N + private static final String EMPTY = ""; //NOI18N + private static final String [] EMPTY_ARRAY = new String [0]; + + private static final RequestProcessor RP = new RequestProcessor(); + private final RequestProcessor.Task flushTask = RP.create( + new Runnable() { + public void run() { + synchronized(lock) { + try { + flushSpi(); + } catch (BackingStoreException ex) { + LOG.log(Level.WARNING, null, ex); + } + } + } + }, + true // initially finished + ); + + private final ThreadLocal refiringChangeKey = new ThreadLocal(); + private final ThreadLocal putValueJavaType = new ThreadLocal(); + + private final String mimePath; + private Map local = null; + private Preferences inherited = null; + + private PreferencesImpl(String mimePath) { + super(null, EMPTY); + + this.mimePath = mimePath; + } + + private Map getLocal() { + if (local == null) { + EditorSettingsStorage ess = EditorSettingsStorage.get(PreferencesStorage.ID); + try { + local = new HashMap(ess.load(MimePath.parse(mimePath), null, false)); + } catch (IOException ioe) { + LOG.log(Level.WARNING, "Can't load editor preferences for '" + mimePath + "'", ioe); //NOI18N + local = new HashMap(); + } + } + return local; + } + + private Preferences getInherited() { + if (inherited == null && mimePath.length() > 0) { + List paths = null; + try { + Method m = MimePath.class.getDeclaredMethod("getInheritedPaths", String.class, String.class); //NOI18N + m.setAccessible(true); + @SuppressWarnings("unchecked") + List ret = (List) m.invoke(MimePath.parse(mimePath), null, null); + paths = ret; + } catch (Exception e) { + LOG.log(Level.WARNING, "Can't call org.netbeans.api.editor.mimelookup.MimePath.getInheritedPaths method.", e); //NOI18N + } + + if (paths != null) { + assert paths.size() > 1 : "Wrong getInheritedPaths result size: " + paths.size(); //NOI18N + inherited = get(MimePath.parse(paths.get(1))); + } else { + inherited = get(MimePath.EMPTY); + } + + inherited.addPreferenceChangeListener(WeakListeners.create(PreferenceChangeListener.class, this, inherited)); + } + + return inherited; + } + + private void asyncInvocationOfFlushSpi() { + flushTask.schedule(200); + } + + private void firePreferenceChange(String key, String newValue) { + refiringChangeKey.set(key); + try { + put(key, newValue); + } finally { + refiringChangeKey.remove(); + } + } +} Index: editor/settings/storage/src/org/netbeans/modules/editor/settings/storage/preferences/PreferencesStorage.java =================================================================== RCS file: editor/settings/storage/src/org/netbeans/modules/editor/settings/storage/preferences/PreferencesStorage.java diff -N editor/settings/storage/src/org/netbeans/modules/editor/settings/storage/preferences/PreferencesStorage.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ editor/settings/storage/src/org/netbeans/modules/editor/settings/storage/preferences/PreferencesStorage.java 10 Jan 2008 09:45:30 -0000 @@ -0,0 +1,349 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun + * Microsystems, Inc. All Rights Reserved. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ +package org.netbeans.modules.editor.settings.storage.preferences; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.logging.Logger; +import org.netbeans.lib.editor.util.CharacterConversions; +import org.netbeans.modules.editor.settings.storage.preferences.PreferencesImpl.TypedValue; +import org.netbeans.modules.editor.settings.storage.spi.StorageDescription; +import org.netbeans.modules.editor.settings.storage.spi.StorageReader; +import org.netbeans.modules.editor.settings.storage.spi.StorageWriter; +import org.netbeans.modules.editor.settings.storage.spi.support.StorageSupport; +import org.openide.filesystems.FileObject; +import org.openide.xml.XMLUtil; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; + +/** + * + * @author Vita Stejskal + */ +public final class PreferencesStorage implements StorageDescription { + + private static final Logger LOG = Logger.getLogger(PreferencesStorage.class.getName()); + + public static final String ID = "Preferences"; //NOI18N + + // --------------------------------------------------------- + // StorageDescription implementation + // --------------------------------------------------------- + + public PreferencesStorage() { + } + + public String getId() { + return ID; + } + + public boolean isUsingProfiles() { + return false; + } + + public String getMimeType() { + return MIME_TYPE; + } + + public String getLegacyFileName() { + return "properties.xml"; //NOI18N + } + + public StorageReader createReader(FileObject f) { + if (MIME_TYPE.equals(f.getMIMEType())) { + return new Reader(); + } else { + // assume legacy file + return new LegacyReader(); + } + } + + public StorageWriter createWriter(FileObject f) { + return new Writer(); + } + + // --------------------------------------------------------- + // Private implementation + // --------------------------------------------------------- + + private static final String E_ROOT = "editor-preferences"; //NOI18N + private static final String E_ENTRY = "entry"; //NOI18N + private static final String E_VALUE = "value"; //NOI18N + private static final String A_NAME = "name"; //NOI18N + private static final String A_VALUE = "value"; //NOI18N + private static final String A_VALUE_ID = "valueId"; //NOI18N + private static final String A_JAVA_TYPE = "javaType"; //NOI18N + private static final String A_REMOVE = "remove"; //NOI18N + private static final String A_XML_SPACE = "xml:space"; //NOI18N + private static final String V_PRESERVE = "preserve"; //NOI18N + + private static final String PUBLIC_ID = "-//NetBeans//DTD Editor Preferences 1.0//EN"; //NOI18N + private static final String SYSTEM_ID = "http://www.netbeans.org/dtds/EditorPreferences-1_0.dtd"; //NOI18N + + private static final String MIME_TYPE = "text/x-nbeditor-preferences"; //NOI18N + + private static abstract class PreferencesReader extends StorageReader { + public abstract Map getAdded(); + public abstract Set getRemoved(); + } + + private static final class Reader extends PreferencesReader { + + private Map entriesMap = new HashMap(); + private Set removedEntries = new HashSet(); + + // The entry being processed + private String name = null; + private String value = null; + private String javaType = null; + + private StringBuilder text = null; + private StringBuilder cdataText = null; + private boolean insideCdata = false; + + public Map getAdded() { + return entriesMap; + } + + public Set getRemoved() { + return removedEntries; + } + + public @Override void characters(char[] ch, int start, int length) throws SAXException { + if (text != null) { + text.append(ch, start, length); + + if (insideCdata) { + cdataText.append(ch, start, length); + } + } + } + + public @Override void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + if (qName.equals(E_ROOT)) { + // We don't read anything from the root + } else if (qName.equals(E_ENTRY)) { + boolean removed = Boolean.valueOf(attributes.getValue(A_REMOVE)); + + name = null; + value = null; + javaType = null; + text = null; + cdataText = null; + + if (removed) { + String entryName = attributes.getValue(A_NAME); + removedEntries.add(entryName); + + } else { + // Read the name + name = attributes.getValue(A_NAME); + + // Read the value and localize it + String valueId = attributes.getValue(A_VALUE_ID); + if (valueId != null) { + String localizedValue = StorageSupport.getLocalizingBundleMessage( + getProcessedFile(), valueId, null); + if (localizedValue != null) { + value = localizedValue; + } + } + + String valueValue = attributes.getValue(A_VALUE); + if (valueValue != null) { + if (value == null) { + value = valueValue; + } else { + LOG.warning("The 'valueId' attribute specified valid resource bundle key, ignoring the 'value' attribute!"); //NOI18N + } + } + + // Read the type of the value + javaType = attributes.getValue(A_JAVA_TYPE); + } + } else if (name != null && qName.equals(E_VALUE)) { + // Initiate the new builder for the entry value + if (value == null) { + text = new StringBuilder(); + cdataText = new StringBuilder(); + insideCdata = false; + } else { + LOG.warning("The 'value' or 'valueId' attribute was specified, ignoring the element!"); //NOI18N + } + } + } + + public @Override void endElement(String uri, String localName, String qName) throws SAXException { + if (qName.equals(E_ROOT)) { + // We don't read anything from the root + } else if (qName.equals(E_ENTRY)) { + if (name != null) { + if (value != null) { + if (!entriesMap.containsKey(name)) { + entriesMap.put(name, new TypedValue(CharacterConversions.lineSeparatorToLineFeed(value), javaType)); + } else { + LOG.warning("Ignoring duplicate editor preferences entry '" + name + "'!"); //NOI18N + } + } else { + LOG.warning("Ignoring editor preferences entry '" + name + "' that does not specify any value!"); //NOI18N + } + } + } else if (qName.equals(E_VALUE)) { + if (text != null) { + value = cdataText.length() > 0 ? cdataText.toString() : text.toString(); + } + } + } + + public @Override void startCDATA() throws SAXException { + if (cdataText != null) { + insideCdata = true; + } + } + + public @Override void endCDATA() throws SAXException { + if (cdataText != null) { + insideCdata = false; + } + } + } // End of Reader class + + private static final class LegacyReader extends PreferencesReader { + + private static final String EL_ROOT = "properties"; //NOI18N + private static final String EL_PROPERTY = "property"; //NOI18N + private static final String AL_NAME = "name"; //NOI18N + private static final String AL_CLASS = "class"; //NOI18N we can safely ignore this + private static final String AL_VALUE = "value"; //NOI18N + + private Map entriesMap = new HashMap(); + + // The entry being processed + private String name = null; + private String value = null; + private String javaType = null; + + public Map getAdded() { + return entriesMap; + } + + public Set getRemoved() { + return Collections.emptySet(); + } + + public @Override void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + if (qName.equals(EL_ROOT)) { + // We don't read anything from the root + } else if (qName.equals(EL_PROPERTY)) { + name = attributes.getValue(AL_NAME); + value = attributes.getValue(AL_VALUE); + javaType = attributes.getValue(AL_CLASS); + } + } + + public @Override void endElement(String uri, String localName, String qName) throws SAXException { + if (qName.equals(EL_ROOT)) { + // We don't read anything from the root + } else if (qName.equals(EL_PROPERTY)) { + if (name != null && value != null) { + entriesMap.put(name, new TypedValue(value, javaType)); + } else { + LOG.warning("Ignoring editor preferences legacy entry {'" + name + "', '" + value + "'}!"); //NOI18N + } + } + } + } // End of LegacyReader class + + private static final class Writer extends StorageWriter { + + public Writer() { + } + + public Document getDocument() { + Document doc = XMLUtil.createDocument(E_ROOT, null, PUBLIC_ID, SYSTEM_ID); + Node root = doc.getElementsByTagName(E_ROOT).item(0); + + for(String name : getAdded().keySet()) { + Element element = doc.createElement(E_ENTRY); + root.appendChild(element); + + // Store entry's name + element.setAttribute(A_NAME, name); + + // Store entry's value + String value = getAdded().get(name).getValue(); + if (value.length() > 0) { + Element valueElement = doc.createElement(E_VALUE); + valueElement.appendChild(doc.createCDATASection( + CharacterConversions.lineFeedToLineSeparator(value))); + element.appendChild(valueElement); + } else { + element.setAttribute(name, value); + } + + // Store entry's value type (if any) + String javaType = getAdded().get(name).getJavaType(); + if (javaType != null && javaType.length() > 0) { + element.setAttribute(A_JAVA_TYPE, javaType); + } + + // Just some XML crap to preserve whitespace + element.setAttribute(A_XML_SPACE, V_PRESERVE); + } + + for(String name : getRemoved()) { + Element element = doc.createElement(E_ENTRY); + root.appendChild(element); + + element.setAttribute(A_NAME, name); + element.setAttribute(A_REMOVE, Boolean.TRUE.toString()); + } + + return doc; + } + } // End of Writer class +} Index: editor/settings/storage/test/unit/src/org/netbeans/modules/editor/settings/storage/preferences/Bundle.properties =================================================================== RCS file: editor/settings/storage/test/unit/src/org/netbeans/modules/editor/settings/storage/preferences/Bundle.properties diff -N editor/settings/storage/test/unit/src/org/netbeans/modules/editor/settings/storage/preferences/Bundle.properties --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ editor/settings/storage/test/unit/src/org/netbeans/modules/editor/settings/storage/preferences/Bundle.properties 10 Jan 2008 09:45:30 -0000 @@ -0,0 +1,38 @@ +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. +# +# Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. +# +# The contents of this file are subject to the terms of either the GNU +# General Public License Version 2 only ("GPL") or the Common +# Development and Distribution License("CDDL") (collectively, the +# "License"). You may not use this file except in compliance with the +# License. You can obtain a copy of the License at +# http://www.netbeans.org/cddl-gplv2.html +# or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the +# specific language governing permissions and limitations under the +# License. When distributing the software, include this License Header +# Notice in each file and include the License file at +# nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this +# particular file as subject to the "Classpath" exception as provided +# by Sun in the GPL Version 2 section of the License file that +# accompanied this code. If applicable, add the following below the +# License Header, with the fields enclosed by brackets [] replaced by +# your own identifying information: +# "Portions Copyrighted [year] [name of copyright owner]" +# +# If you wish your version of this file to be governed by only the CDDL +# or only the GPL Version 2, indicate your decision by adding +# "[Contributor] elects to include this software in this distribution +# under the [CDDL or GPL Version 2] license." If you do not indicate a +# single choice of license, a recipient has the option to distribute +# your version of this file under either the CDDL, the GPL Version 2 or +# to extend the choice of license to its licensees as provided above. +# However, if you add GPL Version 2 code and therefore, elected the GPL +# Version 2 license, then the option applies only if the new code is +# made subject to such option by the copyright holder. +# +# Contributor(s): +# +# Portions Copyrighted 2007 Sun Microsystems, Inc. + +localized-setting-bundle-key=Hey! This is the value from Bundle.properties!!! Index: editor/settings/storage/test/unit/src/org/netbeans/modules/editor/settings/storage/preferences/PreferencesStorageTest.java =================================================================== RCS file: editor/settings/storage/test/unit/src/org/netbeans/modules/editor/settings/storage/preferences/PreferencesStorageTest.java diff -N editor/settings/storage/test/unit/src/org/netbeans/modules/editor/settings/storage/preferences/PreferencesStorageTest.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ editor/settings/storage/test/unit/src/org/netbeans/modules/editor/settings/storage/preferences/PreferencesStorageTest.java 10 Jan 2008 09:45:30 -0000 @@ -0,0 +1,173 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2007 Sun Microsystems, Inc. + */ + +package org.netbeans.modules.editor.settings.storage.preferences; + +import java.io.IOException; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import org.netbeans.api.editor.mimelookup.MimePath; +import org.netbeans.core.startup.Main; +import org.netbeans.junit.NbTestCase; +import org.netbeans.modules.editor.settings.storage.EditorTestLookup; +import org.netbeans.modules.editor.settings.storage.StorageImpl; +import org.netbeans.modules.editor.settings.storage.preferences.PreferencesImpl.TypedValue; + +/** + * + * @author Vita Stejskal + */ +public class PreferencesStorageTest extends NbTestCase { + + public PreferencesStorageTest(String name) { + super(name); + } + + protected @Override void setUp() throws Exception { + super.setUp(); + + EditorTestLookup.setLookup( + new URL[] { + getClass().getClassLoader().getResource( + "org/netbeans/modules/editor/settings/storage/preferences/test-layer-PreferencesStorageTest.xml"), + getClass().getClassLoader().getResource( + "org/netbeans/modules/editor/settings/storage/layer.xml"), + getClass().getClassLoader().getResource( + "org/netbeans/core/resources/mf-layer.xml"), // for MIMEResolverImpl to work + }, + getWorkDir(), + new Object[] {}, + getClass().getClassLoader() + ); + + // This is here to initialize Nb URL factory (org.netbeans.core.startup), + // which is needed by Nb EntityCatalog (org.netbeans.core). + // Also see the test dependencies in project.xml + Main.initializeURLFactory(); + } + + public void testSimple() throws IOException { + StorageImpl storage = new StorageImpl(new PreferencesStorage()); + Map map = storage.load(MimePath.EMPTY, null, false); + assertNotNull("Preferences map should not be null", map); + assertEquals("Wrong value for 'simple-value-setting-A'", "value-A", map.get("simple-value-setting-A").getValue()); + } + + public void testWriting() throws IOException { + StorageImpl storage = new StorageImpl(new PreferencesStorage()); + Map map = storage.load(MimePath.EMPTY, null, false); + assertNotNull("Preferences map should not be null", map); + assertEquals("Wrong value for 'simple-value-setting-A'", "value-A", map.get("simple-value-setting-A").getValue()); + + HashMap mm = new HashMap(); + mm.put("simple-value-setting-A", typedValue("123")); + mm.put("simple-value-setting-B", typedValue("xyz")); + + storage.save(MimePath.EMPTY, null, false, mm); + + // use fresh new StorageImpl + storage = new StorageImpl(new PreferencesStorage()); + map = storage.load(MimePath.EMPTY, null, false); + assertNotNull("Preferences map should not be null", map); + assertEquals("Wrong number of settings", 2, map.size()); + assertEquals("Wrong value for 'simple-value-setting-A'", "123", map.get("simple-value-setting-A").getValue()); + assertEquals("Wrong value for 'simple-value-setting-B'", "xyz", map.get("simple-value-setting-B").getValue()); + } + + // test localization through valueId + + public void testLocalizedValues() throws IOException { + StorageImpl storage = new StorageImpl(new PreferencesStorage()); + Map map = storage.load(MimePath.EMPTY, null, false); + assertNotNull("Preferences map should not be null", map); + assertEquals("Wrong value for 'localized-setting'", "Hey! This is the value from Bundle.properties!!!", map.get("localized-setting").getValue()); + } + + // test reading from multiple files (including remove="true") + + public void testMultiplePreferencesFiles() throws IOException { + StorageImpl storage = new StorageImpl(new PreferencesStorage()); + Map map = storage.load(MimePath.parse("text/x-testA"), null, false); + assertNotNull("Preferences map should not be null", map); + + check("testA-1-setting-1", map); + assertFalse("testA-1-setting-2 should be removed", map.containsKey("testA-1-setting-2")); + check("testA-1-setting-3", "value-of-testA-1-setting-3-from-testA-2", map); + check("testA-2-setting-1", map); + } + + // test removing settings + + public void testRemovingSettings() throws IOException { + StorageImpl storage = new StorageImpl(new PreferencesStorage()); + Map map = storage.load(MimePath.EMPTY, null, false); + assertNotNull("Preferences map should not be null", map); + assertEquals("Wrong value for 'simple-value-setting-A'", "value-A", map.get("simple-value-setting-A").getValue()); + + HashMap mm = new HashMap(); + // no simple-value-setting-A in the mm map + mm.put("simple-value-setting-B", typedValue("ABC-123")); + mm.put("simple-value-setting-C", typedValue("HowHowHow")); + mm.put("simple-value-setting-D", typedValue("NowNowNow")); + + storage.save(MimePath.EMPTY, null, false, mm); + + // use fresh new StorageImpl + storage = new StorageImpl(new PreferencesStorage()); + map = storage.load(MimePath.EMPTY, null, false); + assertNotNull("Preferences map should not be null", map); + assertEquals("Wrong number of settings", 3, map.size()); + check("simple-value-setting-B", "ABC-123", map); + check("simple-value-setting-C", "HowHowHow", map); + check("simple-value-setting-D", "NowNowNow", map); + } + + private void check(String settingName, Map settings) { + check(settingName, "value-of-" + settingName, settings); + } + + private void check(String settingName, String settingValue, Map settings) { + assertEquals("Wrong value for '" + settingName + "'", settingValue, settings.get(settingName).getValue()); + } + + private TypedValue typedValue(Object value) { + return new TypedValue(value.toString(), value.getClass().getName()); + } +} Index: editor/settings/storage/test/unit/src/org/netbeans/modules/editor/settings/storage/preferences/PreferencesTest.java =================================================================== RCS file: editor/settings/storage/test/unit/src/org/netbeans/modules/editor/settings/storage/preferences/PreferencesTest.java diff -N editor/settings/storage/test/unit/src/org/netbeans/modules/editor/settings/storage/preferences/PreferencesTest.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ editor/settings/storage/test/unit/src/org/netbeans/modules/editor/settings/storage/preferences/PreferencesTest.java 10 Jan 2008 09:45:30 -0000 @@ -0,0 +1,212 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2007 Sun Microsystems, Inc. + */ + +package org.netbeans.modules.editor.settings.storage.preferences; + +import java.net.URL; +import java.util.Map; +import java.util.prefs.PreferenceChangeEvent; +import java.util.prefs.PreferenceChangeListener; +import java.util.prefs.Preferences; +import org.netbeans.api.editor.mimelookup.MimeLookup; +import org.netbeans.api.editor.mimelookup.MimePath; +import org.netbeans.core.startup.Main; +import org.netbeans.junit.NbTestCase; +import org.netbeans.modules.editor.settings.storage.EditorTestLookup; +import org.netbeans.modules.editor.settings.storage.StorageImpl; +import org.netbeans.modules.editor.settings.storage.preferences.PreferencesImpl.TypedValue; + +/** + * + * @author Vita Stejskal + */ +public class PreferencesTest extends NbTestCase { + + public PreferencesTest(String name) { + super(name); + } + + protected @Override void setUp() throws Exception { + super.setUp(); + + EditorTestLookup.setLookup( + new URL[] { + getClass().getClassLoader().getResource( + "org/netbeans/modules/editor/settings/storage/preferences/test-layer-PreferencesStorageTest.xml"), + getClass().getClassLoader().getResource( + "org/netbeans/modules/editor/settings/storage/layer.xml"), + getClass().getClassLoader().getResource( + "org/netbeans/core/resources/mf-layer.xml"), // for MIMEResolverImpl to work + }, + getWorkDir(), + new Object[] {}, + getClass().getClassLoader() + ); + + // This is here to initialize Nb URL factory (org.netbeans.core.startup), + // which is needed by Nb EntityCatalog (org.netbeans.core). + // Also see the test dependencies in project.xml + Main.initializeURLFactory(); + } + + public void testSimple() { + Preferences prefs = MimeLookup.getLookup(MimePath.EMPTY).lookup(Preferences.class); + assertEquals("Wrong value for 'simple-value-setting-A'", "value-A", prefs.get("simple-value-setting-A", null)); + assertEquals("Wrong value for 'localized-setting'", "Hey! This is the value from Bundle.properties!!!", prefs.get("localized-setting", null)); + + prefs = MimeLookup.getLookup(MimePath.parse("text/x-testA")).lookup(Preferences.class); + // inherited from MimePath.EMPTY + assertEquals("Wrong value for 'simple-value-setting-A'", "value-A", prefs.get("simple-value-setting-A", null)); + assertEquals("Wrong value for 'localized-setting'", "Hey! This is the value from Bundle.properties!!!", prefs.get("localized-setting", null)); + // from text/x-testA + assertEquals("Wrong value for 'testA-1-setting-1'", "value-of-testA-1-setting-1", prefs.get("testA-1-setting-1", null)); + assertEquals("Wrong value for 'testA-1-setting-2'", "The-Default-Value", prefs.get("testA-1-setting-2", "The-Default-Value")); + assertEquals("Wrong value for 'testA-1-setting-3'", "value-of-testA-1-setting-3-from-testA-2", prefs.get("testA-1-setting-3", null)); + assertEquals("Wrong value for 'testA-2-setting-1'", "value-of-testA-2-setting-1", prefs.get("testA-2-setting-1", null)); + } + + public void testWriting() throws Exception { + { + Preferences prefs = MimeLookup.getLookup(MimePath.EMPTY).lookup(Preferences.class); + assertEquals("Wrong value for 'simple-value-setting-A'", "value-A", prefs.get("simple-value-setting-A", null)); + + prefs.put("simple-value-setting-A", "New-Written-Value"); + assertEquals("Wrong value for 'simple-value-setting-A'", "New-Written-Value", prefs.get("simple-value-setting-A", null)); + } + + Thread.sleep(500); + + { + // read the settings right from the file + StorageImpl storage = new StorageImpl(new PreferencesStorage()); + Map map = storage.load(MimePath.EMPTY, null, false); + assertEquals("Wrong value for 'simple-value-setting-A'", "New-Written-Value", map.get("simple-value-setting-A").getValue()); + } + } + + public void testEvents1() throws Exception { + Preferences prefsA = MimeLookup.getLookup(MimePath.EMPTY).lookup(Preferences.class); + L listenerA = new L(); + prefsA.addPreferenceChangeListener(listenerA); + + Preferences prefsB = MimeLookup.getLookup(MimePath.parse("text/x-testA")).lookup(Preferences.class); + L listenerB = new L(); + prefsB.addPreferenceChangeListener(listenerB); + + assertNotNull("'simple-value-setting-A' should not be null", prefsA.get("simple-value-setting-A", null)); + assertNotNull("'simple-value-setting-A' should not be null", prefsB.get("simple-value-setting-A", null)); + assertEquals("Wrong value for 'testA-1-setting-1'", "value-of-testA-1-setting-1", prefsB.get("testA-1-setting-1", null)); + + assertEquals("There should be no A events", 0, listenerA.count); + assertEquals("There should be no B events", 0, listenerB.count); + + prefsA.put("simple-value-setting-A", "new-value"); + assertEquals("Wrong value for 'simple-value-setting-A'", "new-value", prefsA.get("simple-value-setting-A", null)); + assertEquals("The value for 'simple-value-setting-A' was not propagated", "new-value", prefsB.get("simple-value-setting-A", null)); + + Thread.sleep(500); + + assertEquals("Wrong number of A events", 1, listenerA.count); + assertEquals("Wrong setting name in the A event", "simple-value-setting-A", listenerA.lastEvent.getKey()); + assertEquals("Wrong setting value in the A event", "new-value", listenerA.lastEvent.getNewValue()); + assertSame("Wrong Preferences instance in the A event", prefsA, listenerA.lastEvent.getNode()); + assertEquals("Wrong number of B events", 1, listenerB.count); + assertEquals("Wrong setting name in the B event", "simple-value-setting-A", listenerB.lastEvent.getKey()); + assertEquals("Wrong setting value in the B event", "new-value", listenerB.lastEvent.getNewValue()); + assertSame("Wrong Preferences instance in the B event", prefsB, listenerB.lastEvent.getNode()); + } + + public void testEvents2() throws Exception { + Preferences prefsA = MimeLookup.getLookup(MimePath.EMPTY).lookup(Preferences.class); + L listenerA = new L(); + prefsA.addPreferenceChangeListener(listenerA); + + Preferences prefsB = MimeLookup.getLookup(MimePath.parse("text/x-testA")).lookup(Preferences.class); + L listenerB = new L(); + prefsB.addPreferenceChangeListener(listenerB); + + String origValue = prefsA.get("simple-value-setting-A", null); + assertNotNull("'simple-value-setting-A' should not be null", origValue); + assertNotNull("'simple-value-setting-A' should not be null", prefsB.get("simple-value-setting-A", null)); + assertEquals("Wrong value for 'testA-1-setting-1'", "value-of-testA-1-setting-1", prefsB.get("testA-1-setting-1", null)); + + assertEquals("There should be no A events", 0, listenerA.count); + assertEquals("There should be no B events", 0, listenerB.count); + + prefsB.put("simple-value-setting-A", "another-value-for-testA"); + assertEquals("Wrong value for 'simple-value-setting-A'", origValue, prefsA.get("simple-value-setting-A", null)); + assertEquals("Wrong value for 'simple-value-setting-A' in 'text/x-testA'", "another-value-for-testA", prefsB.get("simple-value-setting-A", null)); + + Thread.sleep(500); + + assertEquals("Wrong number of A events", 0, listenerA.count); + assertEquals("Wrong number of B events", 1, listenerB.count); + assertEquals("Wrong setting name in the B event", "simple-value-setting-A", listenerB.lastEvent.getKey()); + assertEquals("Wrong setting value in the B event", "another-value-for-testA", listenerB.lastEvent.getNewValue()); + assertSame("Wrong Preferences instance in the B event", prefsB, listenerB.lastEvent.getNode()); + + listenerA.count = 0; listenerA.lastEvent = null; + listenerB.count = 0; listenerB.lastEvent = null; + + // now change the value in MimeType.EMPTY + + prefsA.put("simple-value-setting-A", "another-value-for-MimeType.EMPTY"); + assertEquals("Wrong value for 'simple-value-setting-A'", "another-value-for-MimeType.EMPTY", prefsA.get("simple-value-setting-A", null)); + assertEquals("Wrong value for 'simple-value-setting-A' in 'text/x-testA'", "another-value-for-testA", prefsB.get("simple-value-setting-A", null)); + + Thread.sleep(500); + + assertEquals("Wrong number of A events", 1, listenerA.count); + assertEquals("Wrong setting name in the A event", "simple-value-setting-A", listenerA.lastEvent.getKey()); + assertEquals("Wrong setting value in the A event", "another-value-for-MimeType.EMPTY", listenerA.lastEvent.getNewValue()); + assertSame("Wrong Preferences instance in the A event", prefsA, listenerA.lastEvent.getNode()); + assertEquals("Wrong number of B events", 0, listenerB.count); + } + + private static final class L implements PreferenceChangeListener { + public int count = 0; + public PreferenceChangeEvent lastEvent = null; + + public void preferenceChange(PreferenceChangeEvent evt) { + count++; + lastEvent = evt; + } + } // End of L class +} + Index: editor/settings/storage/test/unit/src/org/netbeans/modules/editor/settings/storage/preferences/preferences-all-languages.xml =================================================================== RCS file: editor/settings/storage/test/unit/src/org/netbeans/modules/editor/settings/storage/preferences/preferences-all-languages.xml diff -N editor/settings/storage/test/unit/src/org/netbeans/modules/editor/settings/storage/preferences/preferences-all-languages.xml --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ editor/settings/storage/test/unit/src/org/netbeans/modules/editor/settings/storage/preferences/preferences-all-languages.xml 10 Jan 2008 09:45:30 -0000 @@ -0,0 +1,7 @@ + + + + + + + Index: editor/settings/storage/test/unit/src/org/netbeans/modules/editor/settings/storage/preferences/preferences-testA-1.xml =================================================================== RCS file: editor/settings/storage/test/unit/src/org/netbeans/modules/editor/settings/storage/preferences/preferences-testA-1.xml diff -N editor/settings/storage/test/unit/src/org/netbeans/modules/editor/settings/storage/preferences/preferences-testA-1.xml --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ editor/settings/storage/test/unit/src/org/netbeans/modules/editor/settings/storage/preferences/preferences-testA-1.xml 10 Jan 2008 09:45:30 -0000 @@ -0,0 +1,8 @@ + + + + + + + + Index: editor/settings/storage/test/unit/src/org/netbeans/modules/editor/settings/storage/preferences/preferences-testA-2.xml =================================================================== RCS file: editor/settings/storage/test/unit/src/org/netbeans/modules/editor/settings/storage/preferences/preferences-testA-2.xml diff -N editor/settings/storage/test/unit/src/org/netbeans/modules/editor/settings/storage/preferences/preferences-testA-2.xml --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ editor/settings/storage/test/unit/src/org/netbeans/modules/editor/settings/storage/preferences/preferences-testA-2.xml 10 Jan 2008 09:45:30 -0000 @@ -0,0 +1,11 @@ + + + + + + + + + + + Index: editor/settings/storage/test/unit/src/org/netbeans/modules/editor/settings/storage/preferences/test-layer-PreferencesStorageTest.xml =================================================================== RCS file: editor/settings/storage/test/unit/src/org/netbeans/modules/editor/settings/storage/preferences/test-layer-PreferencesStorageTest.xml diff -N editor/settings/storage/test/unit/src/org/netbeans/modules/editor/settings/storage/preferences/test-layer-PreferencesStorageTest.xml --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ editor/settings/storage/test/unit/src/org/netbeans/modules/editor/settings/storage/preferences/test-layer-PreferencesStorageTest.xml 10 Jan 2008 09:45:30 -0000 @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + Index: editor/src/org/netbeans/modules/editor/options/BaseOptions.java =================================================================== RCS file: /cvs/editor/src/org/netbeans/modules/editor/options/BaseOptions.java,v retrieving revision 1.159 diff -u -r1.159 BaseOptions.java --- editor/src/org/netbeans/modules/editor/options/BaseOptions.java 11 Dec 2007 11:49:48 -0000 1.159 +++ editor/src/org/netbeans/modules/editor/options/BaseOptions.java 10 Jan 2008 09:45:37 -0000 @@ -54,7 +54,6 @@ import java.util.logging.Logger; import java.awt.Toolkit; import org.netbeans.api.editor.mimelookup.MimeLookup; -import org.netbeans.api.editor.settings.KeyBindingSettings; import org.netbeans.editor.Settings; import org.netbeans.editor.SettingsNames; import org.netbeans.editor.SettingsDefaults; @@ -72,20 +71,20 @@ import java.beans.IntrospectionException; import org.openide.loaders.DataObject; import java.io.ObjectOutput; -import org.openide.util.LookupListener; import org.openide.loaders.DataFolder; import org.openide.filesystems.FileObject; import javax.swing.KeyStroke; import java.awt.event.KeyEvent; import org.openide.util.Lookup; -import java.util.StringTokenizer; import org.netbeans.modules.editor.NbEditorUtilities; import java.awt.RenderingHints; import java.util.logging.Level; +import java.util.prefs.Preferences; import org.netbeans.api.editor.mimelookup.MimePath; import org.netbeans.modules.editor.NbEditorKit; import org.netbeans.modules.editor.impl.KitsTracker; import org.netbeans.modules.editor.lib.ColoringMap; +import org.openide.filesystems.FileUtil; import org.openide.filesystems.Repository; import org.openide.util.Utilities; @@ -227,25 +226,24 @@ //private static final String HELP_ID = "editing.global"; // !!! NOI18N private static final String NO_INDENT_ENGINE = "NO_INDENT_ENGINE"; // NOI18N - private transient Settings.Initializer coloringMapInitializer; +// private transient Settings.Initializer coloringMapInitializer; /** Version of the options. It's used for patching the options. */ private transient int optionsVersion; - /* Indent engine available during readExternal() */ - private transient IndentEngine readExternalIndentEngine; - private transient boolean inReadExternal; +// /* Indent engine available during readExternal() */ +// private transient IndentEngine readExternalIndentEngine; +// private transient boolean inReadExternal; private transient MIMEOptionNode mimeNode; - private transient Map defaultMacrosMap; - private transient Map defaultKeyBindingsMap; +// private transient Map defaultMacrosMap; +// private transient Map defaultKeyBindingsMap; private transient MIMEOptionFolder settingsFolder; private transient boolean settingsFolderInitialization; - private transient Boolean usingNewOptions = null; - private transient KeyBindingSettings keyBindingsSettings; - - private transient boolean keybindingsInitialized = false; - private transient boolean loaded = false; +// private transient KeyBindingSettings keyBindingsSettings; +// +// private transient boolean keybindingsInitialized = false; +// private transient boolean loaded = false; /** Map of Kit to Options */ private static final HashMap kitClass2Options = new HashMap(); @@ -253,9 +251,13 @@ /** Code template expand key setting name */ public static final String CODE_TEMPLATE_EXPAND_KEY = "code-template-expand-key"; // NOI18N - private Lookup.Result resultKB; - private LookupListener weakLookupListenerKB; - private LookupListener lookupListenerKB; +// private Lookup.Result resultKB; +// private LookupListener weakLookupListenerKB; +// private LookupListener lookupListenerKB; + + // ------------------------------------------------------------------------ + // BaseOptions creation + // ------------------------------------------------------------------------ public BaseOptions() { this(BaseKit.class, BASE); @@ -267,53 +269,7 @@ kitClass2Options.put(kitClass, this); // new Throwable("BaseOptions: " + getClass() + "; kitClass=" + kitClass + "; typeName=" + typeName).printStackTrace(); } - -// public boolean usesNewOptionsDialog() { -// if (usingNewOptions == null) { -// boolean b = false; -// if (!BASE.equals(getTypeName())) { -// String mime = getCTImpl(); -// Lookup lookup = MimeLookup.getLookup(MimePath.parse(mime)); -// FontColorSettings fcs = lookup.lookup(FontColorSettings.class); -// if (fcs != null){ -// AttributeSet as = fcs.getTokenFontColors(FontColorNames.DEFAULT_COLORING); -// if (as !=null) { -// b = true; -// } -// } -// } -// -// usingNewOptions = b ? Boolean.TRUE : Boolean.FALSE; -// } -// -// return usingNewOptions.booleanValue(); -// } - protected String getContentType(){ - BaseKit kit = BaseKit.getKit(getKitClass()); - return kit.getContentType(); - } - - // diagnostics for #101078 - private String getCTImpl() { - String mimeType = getContentType(); - if (mimeType == null) { - String msg = "Can't determine mime type for " + simpleToString(this) + "; kitClass = " + getKitClass(); //NOI18N - LOG.log(Level.WARNING, null, new Throwable(msg)); - - mimeType="text/plain"; //NOI18N - } - return mimeType; - } - - private static String simpleToString(Object o) { - if (o == null) { - return null; - } else { - return o.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(o)); //NOI18N - } - } - /** * Gets an instance of BaseOptions for an editir kit implementation * class. Please see description of BaseKit.getKit(Class) for @@ -361,98 +317,42 @@ return options; } - /** Listening for Settings.settings creation.*/ + // ------------------------------------------------------------------------ + // Mime type + // ------------------------------------------------------------------------ - /** Lazy initialization of the MIME specific settings folder. The folder should be created - * via XML layers, if not, it will be created. - * Instances of all XML file in this folder will be created. + /** + * ALWAYS OVERWRITE THIS AND PROVIDE VALID MIME TYPE! + * @return The mime type for this BaseOptions instance. */ - protected MIMEOptionFolder getMIMEFolder() { - /* #25541 - Instead of synchronized getMIMEFolder() the kit - * is first obtained and then the synchronization is done - * to avoid the deadlock caused by locking in opposite order. - */ - String name = getCTImpl(); - if (name == null) { - return null; - } - - synchronized (Settings.class) { - if (settingsFolderInitialization) return null; - settingsFolderInitialization = true; - try { - // return already initialized folder - if (settingsFolder!=null) return settingsFolder; - - FileObject f = Repository.getDefault().getDefaultFileSystem(). - findResource(AllOptionsFolder.FOLDER+"/"+name); //NOI18N - - // MIME folder doesn't exist, let's create it - if (f==null){ - FileObject fo = Repository.getDefault().getDefaultFileSystem(). - findResource(AllOptionsFolder.FOLDER); - - if (fo != null){ - try{ - StringTokenizer stok = new StringTokenizer(name,"/"); // NOI18N - while (stok.hasMoreElements()) { - String newFolder = stok.nextToken(); - if (fo.getFileObject(newFolder) == null){ - fo = fo.createFolder(newFolder); - } - else - fo = fo.getFileObject(newFolder); - } - }catch(IOException ioe){ - ioe.printStackTrace(); - } - - f = Repository.getDefault().getDefaultFileSystem(). - findResource(AllOptionsFolder.FOLDER+"/"+name); // NOI18N - } - } - - if (f != null) { - try { - DataObject d = DataObject.find(f); - DataFolder df = (DataFolder)d.getCookie(DataFolder.class); - if (df != null) { - settingsFolder = new MIMEOptionFolder(df, this); - return settingsFolder; - } - } catch (org.openide.loaders.DataObjectNotFoundException ex) { - ex.printStackTrace(); - } - } + protected String getContentType() { + BaseKit kit = BaseKit.getKit(getKitClass()); + return kit.getContentType(); + } - return null; - } finally { - settingsFolderInitialization = false; - } + // diagnostics for #101078 + private String getCTImpl() { + String mimeType = getContentType(); + if (mimeType == null) { + String msg = "Can't determine mime type for " + simpleToString(this) + "; kitClass = " + getKitClass(); //NOI18N + LOG.log(Level.WARNING, null, new Throwable(msg)); + + mimeType="text/plain"; //NOI18N } + return mimeType; } - /** Gets MIMEOptionNode that belongs to this bean */ - public MIMEOptionNode getMimeNode() { - synchronized (Settings.class) { - if (mimeNode == null) { - createMIMENode(getTypeName()); - } - return mimeNode; + private static String simpleToString(Object o) { + if (o == null) { + return null; + } else { + return o.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(o)); //NOI18N } } - /** Creates Node in global options for appropriate MIME type */ - private void createMIMENode(String typeName){ - if (typeName.equals(BASE)){ - return; - } - try{ - mimeNode = new MIMEOptionNode(this); - }catch(IntrospectionException ie){ - ie.printStackTrace(); - } - } + // ------------------------------------------------------------------------ + // o.n.e.Settings initialization + // ------------------------------------------------------------------------ protected @Override void updateSettingsMap(Class kitClass, Map settingsMap) { // final String id = "kitClass=" + kitClass + "; getKitClass()=" + getKitClass() + "; this=" + this; @@ -492,17 +392,67 @@ } } ); +// XXX: remove +// if (coloringMapInitializer != null) { +// coloringMapInitializer.updateSettingsMap(kitClass, settingsMap); +// } + } + +// XXX: remove +// if (kitClass == BaseKit.class && coloringMapInitializer != null) { +// coloringMapInitializer.updateSettingsMap(BaseKit.class, settingsMap); +// } + } + + // ------------------------------------------------------------------------ + // Antialiasing + // ------------------------------------------------------------------------ + + public boolean isTextAntialiasing() { + // artificial setting -> evaluator used (at begining of this class) + Boolean val = (Boolean)getSettingValue(TEXT_ANTIALIASING_PROP); + if (val != null) { + return val.booleanValue(); + } else { + //XXX this prop is not used anymore, but anyway, I believe + //it should have been swing.aatext - I've never seen + //javax.aatext. -Tim + // #56234: Check -Djavax.aatext=true + if (Boolean.getBoolean("javax.aatext")) { + return true; - if (coloringMapInitializer != null) { - coloringMapInitializer.updateSettingsMap(kitClass, settingsMap); + } else { + // fix of #31758 + if (Utilities.isMac()) { + // On OSX, default to true + return true; + } else { + return isSystemAntialias(); + } } } - - if (kitClass == BaseKit.class && coloringMapInitializer != null) { - coloringMapInitializer.updateSettingsMap(BaseKit.class, settingsMap); + } + + private static boolean isSystemAntialias() { + Map systemHints = (Map)(Toolkit.getDefaultToolkit().getDesktopProperty( + "awt.font.desktophints")); //NOI18N + if (systemHints != null) { + Object o = systemHints.get(RenderingHints.KEY_TEXT_ANTIALIASING); + boolean result = o != null && + o != RenderingHints.VALUE_TEXT_ANTIALIAS_OFF && + o != RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT; + return result; + } else { + return false; } } + public void setTextAntialiasing(boolean textAntialiasing) { + setSettingBoolean(TEXT_ANTIALIASING_PROP, textAntialiasing, TEXT_ANTIALIASING_PROP); + // Cause refresh or renderingHints variable in EditorUI + Settings.touchValue(getKitClass(), SettingsNames.RENDERING_HINTS); + } + private Map computeTextAntialiasingMap( boolean aaSetting ) { Map result; @@ -558,45 +508,27 @@ return systemSetting == gaspConst; } - /* #54893 - public HelpCtx getHelpCtx() { - return new HelpCtx(HELP_ID); - } - */ - - public int getTabSize() { - return getSettingInteger(SettingsNames.TAB_SIZE); - } - public void setTabSize(int tabSize) { - if (tabSize < 0) { - NbEditorUtilities.invalidArgument("MSG_NegativeValue"); // NOI18N - return; - } - setSettingInteger(SettingsNames.TAB_SIZE, tabSize, TAB_SIZE_PROP); - } - -/* public boolean getExpandTabs() { - return getSettingBoolean(SettingsNames.EXPAND_TABS); - } - [Mila] Moved to IndentEngine; Setter must stay here - */ + // ------------------------------------------------------------------------ + // Code Templates (formerly abbreviations) related getters & setters + // ------------------------------------------------------------------------ - public void setExpandTabs(boolean expandTabs) { - setSettingBoolean(SettingsNames.EXPAND_TABS, expandTabs, EXPAND_TABS_PROP); + /** Saves the keystroke of code tamplate expansion into properties.xml file under Editors/text/base */ + public static void setCodeTemplateExpandKey(KeyStroke ks) { + Settings.setValue(BaseKit.class, CODE_TEMPLATE_EXPAND_KEY, Utilities.keyToString(ks)); } -/* public int getSpacesPerTab() { - return getSettingInteger(SettingsNames.SPACES_PER_TAB); - } - [Mila] Moved to IndentEngine; Setter must stay here - */ - public void setSpacesPerTab(int i){ - if (i > 0) - setSettingInteger(SettingsNames.SPACES_PER_TAB, i, SPACES_PER_TAB_PROP); - else - Toolkit.getDefaultToolkit().beep(); + /** Gets Code Template Expand Key. Can return null if there is no key in the settings file */ + public static KeyStroke getCodeTemplateExpandKey(){ + String ks = (String) Settings.getValue(BaseKit.class, CODE_TEMPLATE_EXPAND_KEY); + if (ks != null) { + KeyStroke keyStroke = Utilities.stringToKey(ks); + if (keyStroke != null) { + return keyStroke; + } + } + return KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0); } - + /** * @return The same as getAbbrevMap. * @deprecated Use Editor Settings API instead. @@ -651,149 +583,9 @@ setAbbrevMap(map, true); } - public String getCaretTypeInsertMode() { - return (String)getSettingValue(SettingsNames.CARET_TYPE_INSERT_MODE); - } - public void setCaretTypeInsertMode(String type) { - setSettingValue(SettingsNames.CARET_TYPE_INSERT_MODE, type, - CARET_TYPE_INSERT_MODE_PROP); - } - - public String getCaretTypeOverwriteMode() { - return (String)getSettingValue(SettingsNames.CARET_TYPE_OVERWRITE_MODE); - } - public void setCaretTypeOverwriteMode(String type) { - setSettingValue(SettingsNames.CARET_TYPE_OVERWRITE_MODE, type, - CARET_TYPE_OVERWRITE_MODE_PROP); - } - - /** - *@deprecated since adaptation to new view implementation - the option is not supported - */ - public boolean getCaretItalicInsertMode() { - return false;//getSettingBoolean(SettingsNames.CARET_ITALIC_INSERT_MODE); - } - /** - *@deprecated since adaptation to new view implementation - the option is not supported - */ - public void setCaretItalicInsertMode(boolean b) { - setSettingBoolean(SettingsNames.CARET_ITALIC_INSERT_MODE, b, - CARET_ITALIC_INSERT_MODE_PROP); - } - - /** - *@deprecated since adaptation to new view implementation - the option is not supported - */ - public boolean getCaretItalicOverwriteMode() { - return false;//getSettingBoolean(SettingsNames.CARET_ITALIC_OVERWRITE_MODE); - } - /** - *@deprecated since adaptation to new view implementation - the option is not supported - */ - public void setCaretItalicOverwriteMode(boolean b) { - setSettingBoolean(SettingsNames.CARET_ITALIC_OVERWRITE_MODE, b, - CARET_ITALIC_OVERWRITE_MODE_PROP); - } - - public Color getCaretColorInsertMode() { - loadSettings(FontsColorsMIMEProcessor.class); - return (Color)super.getSettingValue(SettingsNames.CARET_COLOR_INSERT_MODE); - } - - /** Sets new CaretColorInsertMode property to initializer map and save the - * changes to XML file */ - public void setCaretColorInsertMode(Color color) { - setCaretColorInsertMode(color, true); - } - - /** Sets new CaretColorInsertMode property to initializer map and if saveToXML is true, - * then new settings will be saved to XML file (fontsColors.xml). */ - public void setCaretColorInsertMode(Color color, boolean saveToXML) { - if (saveToXML){ - if (!getCaretColorInsertMode().equals(color) && (color!=null)){ - // settings has changed, write changed settings to XML file - Map map = new HashMap(); - map.put(SettingsNames.CARET_COLOR_INSERT_MODE,color); - if (map!=null){ - updateSettings(FontsColorsMIMEProcessor.class, - map); - } - } - } - - super.setSettingValue(SettingsNames.CARET_COLOR_INSERT_MODE, color, - CARET_COLOR_INSERT_MODE_PROP); - } - - public Color getCaretColorOverwriteMode() { - loadSettings(FontsColorsMIMEProcessor.class); - return (Color)super.getSettingValue(SettingsNames.CARET_COLOR_OVERWRITE_MODE); - } - - /** Sets new CaretColorOverwriteMode property to initializer map and save the - * changes to XML file */ - public void setCaretColorOverwriteMode(Color color) { - setCaretColorOverwriteMode(color, true); - } - - /** Sets new CaretColorOverwriteMode property to initializer map and if saveToXML is true, - * then new settings will be saved to XML file (fontsColors.xml). */ - public void setCaretColorOverwriteMode(Color color, boolean saveToXML) { - if (saveToXML){ - if (!getCaretColorOverwriteMode().equals(color) && (color!=null)){ - // settings has changed, write changed settings to XML file - Map map = new HashMap(); - map.put(SettingsNames.CARET_COLOR_OVERWRITE_MODE,color); - if (map!=null){ - updateSettings(FontsColorsMIMEProcessor.class, - map); - } - } - } - - super.setSettingValue(SettingsNames.CARET_COLOR_OVERWRITE_MODE, color, - CARET_COLOR_OVERWRITE_MODE_PROP); - } - - public int getCaretBlinkRate() { - return getSettingInteger(SettingsNames.CARET_BLINK_RATE); - } - public void setCaretBlinkRate(int rate) { - if (rate < 0) { - NbEditorUtilities.invalidArgument("MSG_NegativeValue"); // NOI18N - return; - } - setSettingInteger(SettingsNames.CARET_BLINK_RATE, rate, - CARET_BLINK_RATE_PROP); - } - - public boolean getLineNumberVisible() { - return getSettingBoolean(SettingsNames.LINE_NUMBER_VISIBLE); - } - public void setLineNumberVisible(boolean b) { - setSettingBoolean(SettingsNames.LINE_NUMBER_VISIBLE, b, - LINE_NUMBER_VISIBLE_PROP); - } - - public Insets getScrollJumpInsets() { - return (Insets)getSettingValue(SettingsNames.SCROLL_JUMP_INSETS); - } - public void setScrollJumpInsets(Insets i) { - setSettingValue(SettingsNames.SCROLL_JUMP_INSETS, i, - SCROLL_JUMP_INSETS_PROP); - } - - public Insets getScrollFindInsets() { - return (Insets)getSettingValue(SettingsNames.SCROLL_FIND_INSETS); - } - public void setScrollFindInsets(Insets i) { - setSettingValue(SettingsNames.SCROLL_FIND_INSETS, i, - SCROLL_FIND_INSETS_PROP); - } + // ------------------------------------------------------------------------ + // Keybindings related getters/setters + // ------------------------------------------------------------------------ /** * @return The same keybindings as getKeyBindingList, but stored @@ -890,6 +682,10 @@ // super.setSettingValue(SettingsNames.KEY_BINDING_LIST, list, KEY_BINDING_LIST_PROP); } + // ------------------------------------------------------------------------ + // Fonts & Colors related getters/setters + // ------------------------------------------------------------------------ + /** * Tries to gather all colorings defined for the mime type of this instance. * @@ -1029,6 +825,10 @@ LINE_HEIGHT_CORRECTION_PROP); } + // ------------------------------------------------------------------------ + // Editor macros settings + // ------------------------------------------------------------------------ + /** * The same as getMacroMap(). * @deprecated Without any replacement. @@ -1171,15 +971,167 @@ // super.setSettingValue(SettingsNames.MACRO_MAP, map); } - /** - * Sets new macros. This method calls setMacroMap(map, true). - * @param macros The map with macro names mapped to macro code. - * @deprecated Without any replacement. - */ - public void setMacroMap(Map macros) { - setMacroMap(macros, true); + /** + * Sets new macros. This method calls setMacroMap(map, true). + * @param macros The map with macro names mapped to macro code. + * @deprecated Without any replacement. + */ + public void setMacroMap(Map macros) { + setMacroMap(macros, true); + } + + // ------------------------------------------------------------------------ + // Miscellaneous getters & setters + // ------------------------------------------------------------------------ + + public int getTabSize() { + return getSettingInteger(SettingsNames.TAB_SIZE); + } + public void setTabSize(int tabSize) { + if (tabSize < 0) { + NbEditorUtilities.invalidArgument("MSG_NegativeValue"); // NOI18N + return; + } + setSettingInteger(SettingsNames.TAB_SIZE, tabSize, TAB_SIZE_PROP); + } + +/* public boolean getExpandTabs() { + return getSettingBoolean(SettingsNames.EXPAND_TABS); + } + [Mila] Moved to IndentEngine; Setter must stay here + */ + + public void setExpandTabs(boolean expandTabs) { + setSettingBoolean(SettingsNames.EXPAND_TABS, expandTabs, EXPAND_TABS_PROP); + } + +/* public int getSpacesPerTab() { + return getSettingInteger(SettingsNames.SPACES_PER_TAB); + } + [Mila] Moved to IndentEngine; Setter must stay here + */ + public void setSpacesPerTab(int i){ + if (i > 0) + setSettingInteger(SettingsNames.SPACES_PER_TAB, i, SPACES_PER_TAB_PROP); + else + Toolkit.getDefaultToolkit().beep(); + } + + public String getCaretTypeInsertMode() { + return (String)getSettingValue(SettingsNames.CARET_TYPE_INSERT_MODE); + } + public void setCaretTypeInsertMode(String type) { + setSettingValue(SettingsNames.CARET_TYPE_INSERT_MODE, type, + CARET_TYPE_INSERT_MODE_PROP); + } + + public String getCaretTypeOverwriteMode() { + return (String)getSettingValue(SettingsNames.CARET_TYPE_OVERWRITE_MODE); + } + public void setCaretTypeOverwriteMode(String type) { + setSettingValue(SettingsNames.CARET_TYPE_OVERWRITE_MODE, type, + CARET_TYPE_OVERWRITE_MODE_PROP); + } + + /** + *@deprecated since adaptation to new view implementation + the option is not supported + */ + public boolean getCaretItalicInsertMode() { + return false;//getSettingBoolean(SettingsNames.CARET_ITALIC_INSERT_MODE); + } + /** + *@deprecated since adaptation to new view implementation + the option is not supported + */ + public void setCaretItalicInsertMode(boolean b) { + setSettingBoolean(SettingsNames.CARET_ITALIC_INSERT_MODE, b, + CARET_ITALIC_INSERT_MODE_PROP); + } + + /** + *@deprecated since adaptation to new view implementation + the option is not supported + */ + public boolean getCaretItalicOverwriteMode() { + return false;//getSettingBoolean(SettingsNames.CARET_ITALIC_OVERWRITE_MODE); + } + /** + *@deprecated since adaptation to new view implementation + the option is not supported + */ + public void setCaretItalicOverwriteMode(boolean b) { + setSettingBoolean(SettingsNames.CARET_ITALIC_OVERWRITE_MODE, b, + CARET_ITALIC_OVERWRITE_MODE_PROP); + } + + public Color getCaretColorInsertMode() { + return (Color)super.getSettingValue(SettingsNames.CARET_COLOR_INSERT_MODE); + } + + /** Sets new CaretColorInsertMode property to initializer map and save the + * changes to XML file */ + public void setCaretColorInsertMode(Color color) { + setCaretColorInsertMode(color, true); + } + + /** Sets new CaretColorInsertMode property to initializer map and if saveToXML is true, + * then new settings will be saved to XML file (fontsColors.xml). */ + public void setCaretColorInsertMode(Color color, boolean saveToXML) { + super.setSettingValue(SettingsNames.CARET_COLOR_INSERT_MODE, color, CARET_COLOR_INSERT_MODE_PROP); + } + + public Color getCaretColorOverwriteMode() { + return (Color)super.getSettingValue(SettingsNames.CARET_COLOR_OVERWRITE_MODE); + } + + /** Sets new CaretColorOverwriteMode property to initializer map and save the + * changes to XML file */ + public void setCaretColorOverwriteMode(Color color) { + setCaretColorOverwriteMode(color, true); + } + + /** Sets new CaretColorOverwriteMode property to initializer map and if saveToXML is true, + * then new settings will be saved to XML file (fontsColors.xml). */ + public void setCaretColorOverwriteMode(Color color, boolean saveToXML) { + super.setSettingValue(SettingsNames.CARET_COLOR_OVERWRITE_MODE, color, CARET_COLOR_OVERWRITE_MODE_PROP); + } + + public int getCaretBlinkRate() { + return getSettingInteger(SettingsNames.CARET_BLINK_RATE); + } + public void setCaretBlinkRate(int rate) { + if (rate < 0) { + NbEditorUtilities.invalidArgument("MSG_NegativeValue"); // NOI18N + return; + } + setSettingInteger(SettingsNames.CARET_BLINK_RATE, rate, CARET_BLINK_RATE_PROP); + } + + public boolean getLineNumberVisible() { + return getSettingBoolean(SettingsNames.LINE_NUMBER_VISIBLE); + } + public void setLineNumberVisible(boolean b) { + setSettingBoolean(SettingsNames.LINE_NUMBER_VISIBLE, b, + LINE_NUMBER_VISIBLE_PROP); + } + + public Insets getScrollJumpInsets() { + return (Insets)getSettingValue(SettingsNames.SCROLL_JUMP_INSETS); + } + public void setScrollJumpInsets(Insets i) { + setSettingValue(SettingsNames.SCROLL_JUMP_INSETS, i, + SCROLL_JUMP_INSETS_PROP); + } + + public Insets getScrollFindInsets() { + return (Insets)getSettingValue(SettingsNames.SCROLL_FIND_INSETS); } - + public void setScrollFindInsets(Insets i) { + setSettingValue(SettingsNames.SCROLL_FIND_INSETS, i, + SCROLL_FIND_INSETS_PROP); + } + public Insets getMargin() { return (Insets)getSettingValue(SettingsNames.MARGIN); } @@ -1287,9 +1239,7 @@ PAIR_CHARACTERS_COMPLETION); } - public Color getTextLimitLineColor() { - loadSettings(FontsColorsMIMEProcessor.class); return (Color)super.getSettingValue(SettingsNames.TEXT_LIMIT_LINE_COLOR); } @@ -1310,20 +1260,7 @@ * @deprecated Use Editor Settings Storage API instead. */ public void setTextLimitLineColor(Color color , boolean saveToXML) { - if (saveToXML){ - if (!getTextLimitLineColor().equals(color) && (color!=null)){ - // settings has changed, write changed settings to XML file - Map map = new HashMap(); - map.put(SettingsNames.TEXT_LIMIT_LINE_COLOR,color); - if (map!=null){ - updateSettings(FontsColorsMIMEProcessor.class, - map); - } - } - } - - super.setSettingValue(SettingsNames.TEXT_LIMIT_LINE_COLOR, color, - TEXT_LIMIT_LINE_COLOR_PROP); + super.setSettingValue(SettingsNames.TEXT_LIMIT_LINE_COLOR, color, TEXT_LIMIT_LINE_COLOR_PROP); } public int getTextLimitWidth() { @@ -1374,51 +1311,6 @@ setSettingBoolean(TOOLBAR_VISIBLE_PROP, toolbarVisible, TOOLBAR_VISIBLE_PROP); } - public boolean isTextAntialiasing() { - // artificial setting -> evaluator used (at begining of this class) - Boolean val = (Boolean)getSettingValue(TEXT_ANTIALIASING_PROP); - if (val != null) { - return val.booleanValue(); - } else { - //XXX this prop is not used anymore, but anyway, I believe - //it should have been swing.aatext - I've never seen - //javax.aatext. -Tim - // #56234: Check -Djavax.aatext=true - if (Boolean.getBoolean("javax.aatext")) { - return true; - - } else { - // fix of #31758 - if (Utilities.isMac()) { - // On OSX, default to true - return true; - } else { - return isSystemAntialias(); - } - } - } - } - - private static boolean isSystemAntialias() { - Map systemHints = (Map)(Toolkit.getDefaultToolkit().getDesktopProperty( - "awt.font.desktophints")); //NOI18N - if (systemHints != null) { - Object o = systemHints.get(RenderingHints.KEY_TEXT_ANTIALIASING); - boolean result = o != null && - o != RenderingHints.VALUE_TEXT_ANTIALIAS_OFF && - o != RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT; - return result; - } else { - return false; - } - } - - public void setTextAntialiasing(boolean textAntialiasing) { - setSettingBoolean(TEXT_ANTIALIASING_PROP, textAntialiasing, TEXT_ANTIALIASING_PROP); - // Cause refresh or renderingHints variable in EditorUI - Settings.touchValue(getKitClass(), SettingsNames.RENDERING_HINTS); - } - public Map getCodeFoldingProps(){ Map map = new HashMap(); @@ -1433,52 +1325,50 @@ setSettingValue(name, props.get(name)); } - /** Retrieves the actions from XML file */ + /** + * Retrieves the actions from XML file. + * @deprecated Use the layers, ie. Editors//Popup. + */ public void initPopupMenuItems(){ - //List orderedPopupFiles = getOrderedMultiPropertyFolderFiles("Popup"); //NOI18N - //if (orderedPopupFiles.size() >0){ - // super.setSettingValue(ExtSettingsNames.POPUP_MENU_ACTION_NAME_LIST, - // OptionUtilities.getPopupStrings(orderedPopupFiles)); //NOI18N - //} +// XXX: remove +// List orderedPopupFiles = getOrderedMultiPropertyFolderFiles("Popup"); //NOI18N +// if (orderedPopupFiles.size() >0){ +// super.setSettingValue(ExtSettingsNames.POPUP_MENU_ACTION_NAME_LIST, +// OptionUtilities.getPopupStrings(orderedPopupFiles)); //NOI18N +// } } + // ------------------------------------------------------------------------ + // IndentEngines related stuff + // ------------------------------------------------------------------------ + public IndentEngine getIndentEngine() { - // Due to #11212 - if (inReadExternal) { - return readExternalIndentEngine; - } - - if (!BASE.equals(getTypeName())){ - loadSettings(PropertiesMIMEProcessor.class); - - MIMEOptionFile file; - MIMEOptionFolder mimeFolder = getMIMEFolder(); - if (mimeFolder != null){ - file= mimeFolder.getFile(PropertiesMIMEProcessor.class, false); - if (file != null) { - Map setMap = file.getAllProperties(); - Object handle = setMap.get(INDENT_ENGINE_PROP); - if (handle instanceof String){ - Object instance = null; - String handleString = (String) handle; - - if (handleString.equals(NO_INDENT_ENGINE)){ - return IndentEngine.getDefault(); - } - - Lookup.Template tmp = new Lookup.Template(null, handleString, null); - Lookup.Item item = Lookup.getDefault().lookupItem(tmp); - if (item != null) { - instance = item.getInstance(); - if(instance instanceof IndentEngine){ - return (IndentEngine) instance; - } - } +// XXX: remove +// // Due to #11212 +// if (inReadExternal) { +// return readExternalIndentEngine; +// } +// + if (!BASE.equals(getTypeName())) { + Preferences prefs = MimeLookup.getLookup(MimePath.parse(getContentType())).lookup(Preferences.class); + String handle = prefs.get(INDENT_ENGINE_PROP, null); + if (handle != null) { + Object instance = null; + String handleString = (String) handle; + + if (handleString.equals(NO_INDENT_ENGINE)) { + return IndentEngine.getDefault(); + } + Lookup.Template tmp = new Lookup.Template(null, handleString, null); + Lookup.Item item = Lookup.getDefault().lookupItem(tmp); + if (item != null) { + instance = item.getInstance(); + if (instance instanceof IndentEngine) { + return (IndentEngine) instance; } } } - } // [BACKWARD-COMPATIBILITY-START] @@ -1510,28 +1400,31 @@ } public void setIndentEngine(IndentEngine eng) { - /* Disabled direct setting of the engine - * during project deserialization to avoid doubled - * indent engine as described in #9687 - */ - if (!inReadExternal) { +// XXX: remove +// /* Disabled direct setting of the engine +// * during project deserialization to avoid doubled +// * indent engine as described in #9687 +// */ +// if (!inReadExternal) { String id = null; if (eng != null) { Lookup.Template tmp = new Lookup.Template(null, null, eng); Lookup.Item item = Lookup.getDefault().lookupItem(tmp); - if (item != null) id = item.getId(); - + if (item != null) { + id = item.getId(); + } } if (!BASE.equals(getTypeName())){ - Map map = new HashMap(); - if (id == null) id = NO_INDENT_ENGINE; - map.put(INDENT_ENGINE_PROP, id); - updateSettings(PropertiesMIMEProcessor.class, map, false); + Preferences prefs = MimeLookup.getLookup(MimePath.parse(getContentType())).lookup(Preferences.class); + if (id == null) { + id = NO_INDENT_ENGINE; + } + prefs.put(INDENT_ENGINE_PROP, id); } refreshIndentEngineSettings(); - } +// } } private void refreshIndentEngineSettings() { @@ -1560,246 +1453,337 @@ return null; } + // ------------------------------------------------------------------------ + // Options versioning + // ------------------------------------------------------------------------ + + /** + * @deprecated Without any replacement. + */ public void setOptionsVersion(int optionsVersion) { int oldOptionsVersion = this.optionsVersion; this.optionsVersion = optionsVersion; if (optionsVersion != oldOptionsVersion) { - firePropertyChange(OPTIONS_VERSION_PROP, - new Integer(oldOptionsVersion), new Integer(optionsVersion)); + firePropertyChange(OPTIONS_VERSION_PROP, new Integer(oldOptionsVersion), new Integer(optionsVersion)); } - } - + + /** + * @deprecated Without any replacement. + */ public int getOptionsVersion() { return optionsVersion; } - public @Override void readExternal(ObjectInput in) - throws IOException, ClassNotFoundException { - - /** Hold the current indent engine due to #11212 */ - readExternalIndentEngine = getIndentEngine(); - inReadExternal = true; - - /* Make the current options version to be zero - * temporarily to distinguish whether the options - * imported were old and the setOptionsVersion() - * was not called or whether the options - * were new so the options version was set - * to the LATEST_OPTIONS_VERSION value. - */ - optionsVersion = 0; - - try { - // Read the serialized options - super.readExternal(in); - }catch(java.io.OptionalDataException ode){ - // #17385. It occurs during reading Settings.settings, that is unimportant - } finally { - - // Make sure the indent engine settings are propagated - // (SharedClassObject.putProperty() is final) - refreshIndentEngineSettings(); - - // Possibly upgrade the options - //if (optionsVersion < LATEST_OPTIONS_VERSION) { - // upgradeOptions(optionsVersion, LATEST_OPTIONS_VERSION); - //} - - optionsVersion = LATEST_OPTIONS_VERSION; - - /** Release temp indent engine - #11212 */ - inReadExternal = false; - readExternalIndentEngine = null; - } - } - - /** Upgrade the deserialized options. + /** + * Upgrade the deserialized options. * @param version deserialized version of the options * @param latestVersion latest version of the options * that will be set to them after they are upgraded + * @deprecated Without any replacement. This method is never called. */ protected void upgradeOptions(int version, int latestVersion) { // Upgrade in separate class to avoid messing up BaseOptions //UpgradeOptions.upgradeOptions(this, version, latestVersion); } - /** Load settings from XML files and initialize changes */ - private void loadSettings(Class processor){ - MIMEOptionFile file; - if (BASE.equals(getTypeName())){ - MIMEOptionFolder mimeFolder = AllOptionsFolder.getDefault().getMIMEFolder(); - if (mimeFolder == null) return; - file= mimeFolder.getFile(processor, false); - }else{ - MIMEOptionFolder mimeFolder = getMIMEFolder(); - if (mimeFolder == null) return; - file= mimeFolder.getFile(processor, false); - } - if ((file!=null) && (!file.isLoaded())) { - file.loadSettings(); - } - } + // ------------------------------------------------------------------------ + // XML-ization & MIME* stuff + // ------------------------------------------------------------------------ - /** Save changes to XML files. - * @see #updateSettings(java.lang.Class, java.util.Map, boolean ) - */ - private void updateSettings(Class processor, Map settings){ - updateSettings(processor, settings, true); - } - - /** Save changes to XML files - * @param processor MIMEProcessor class - * @param settings the settings map - * @param useRequestProcessorForSaving if true settings will be saved in RequestProcessor thread. - */ - private void updateSettings(Class processor, Map settings, boolean useRequestProcessorForSaving){ - if (processor == FontsColorsMIMEProcessor.class || - processor == KeyBindingsMIMEProcessor.class || - processor == AbbrevsMIMEProcessor.class || - processor == MacrosMIMEProcessor.class - ) { - return; + /** + * @return The MIMEOptionFolder instance for the mime type of + * this BaseOptions instance. + * @deprecated Use Editor Settings Storage API. + */ + protected MIMEOptionFolder getMIMEFolder() { + /* #25541 - Instead of synchronized getMIMEFolder() the kit + * is first obtained and then the synchronization is done + * to avoid the deadlock caused by locking in opposite order. + */ + String name = getCTImpl(); + if (name == null) { + return null; } - - MIMEOptionFile fileX; - MIMEOptionFolder mimeFolder; - if (BASE.equals(getTypeName())){ - mimeFolder = AllOptionsFolder.getDefault().getMIMEFolder(); - if (mimeFolder == null) return; - fileX = mimeFolder.getFile(processor, true); - }else{ - mimeFolder = getMIMEFolder(); - if (mimeFolder == null) return; - fileX = mimeFolder.getFile(processor, true); - } - final Map finalSettings = settings; - final MIMEOptionFile file = fileX; - if (file!=null){ - if (useRequestProcessorForSaving){ - Settings.update(new Runnable() { - public void run() { - file.setAllProperties(finalSettings); + + synchronized (Settings.class) { + if (settingsFolderInitialization) { + return null; + } + settingsFolderInitialization = true; + try { + // return already initialized folder + if (settingsFolder != null) { + return settingsFolder; + } + + FileObject f = Repository.getDefault().getDefaultFileSystem().findResource(AllOptionsFolder.FOLDER + "/" + name); //NOI18N + + // MIME folder doesn't exist, let's create it + if (f == null) { + FileObject fo = Repository.getDefault().getDefaultFileSystem().findResource(AllOptionsFolder.FOLDER); + if (fo != null) { + try { + FileUtil.createFolder(fo, name); + } catch (IOException ioe) { + LOG.log(Level.WARNING, null, ioe); + } + + f = Repository.getDefault().getDefaultFileSystem().findResource(AllOptionsFolder.FOLDER + "/" + name); // NOI18N } - public boolean asynchronous() { - return true; + } + + if (f != null) { + try { + DataObject d = DataObject.find(f); + DataFolder df = (DataFolder) d.getCookie(DataFolder.class); + if (df != null) { + settingsFolder = new MIMEOptionFolder(df, this); + return settingsFolder; + } + } catch (org.openide.loaders.DataObjectNotFoundException ex) { + LOG.log(Level.WARNING, null, ex); } - }); - }else{ - file.setAllProperties(finalSettings); + } + + return null; + } finally { + settingsFolderInitialization = false; } - - } else { - LOG.info("A settings file for " + processor + " does not exist in " + mimeFolder.getDataFolder()); //NOI18N } } - public @Override void setSettingValue(String settingName, Object newValue) { - setSettingValue(settingName, newValue, settingName); - } - - private boolean isTheSame(String settingName, Object newValue){ - if (settingName == null || - settingName.equals(NbEditorDocument.INDENT_ENGINE) || - settingName.equals(NbEditorDocument.FORMATTER) - ) { - return true; - } - Object oldValue = getSettingValue(settingName); - if ((oldValue == null && newValue == null) - || (oldValue != null && oldValue.equals(newValue)) - ) { - return true; // the same object + /** Gets MIMEOptionNode that belongs to this bean */ + public MIMEOptionNode getMimeNode() { + synchronized (Settings.class) { + if (mimeNode == null) { + createMIMENode(getTypeName()); + } + return mimeNode; } - return false; } - /** Sets setting value to initializer Map and save the changes to XML file - * (properties.xml) */ - public @Override void setSettingValue(String settingName, Object newValue, String propertyName) { - if (!isTheSame(settingName, newValue)){ - super.setSettingValue(settingName,newValue,propertyName); - - // Save it - Map map = new HashMap(); - map.put(settingName, newValue); - updateSettings(PropertiesMIMEProcessor.class, map); + /** Creates Node in global options for appropriate MIME type */ + private void createMIMENode(String typeName) { + if (typeName.equals(BASE)) { + return; + } + try { + mimeNode = new MIMEOptionNode(this); + } catch (IntrospectionException ie) { + LOG.log(Level.WARNING, null, ie); } } - public @Override Object getSettingValue(String settingName) { - loadSettings(PropertiesMIMEProcessor.class); - return super.getSettingValue(settingName); - } +// XXX: remove +// /** Load settings from XML files and initialize changes */ +// private void loadSettings(Class processor){ +// MIMEOptionFile file; +// if (BASE.equals(getTypeName())){ +// MIMEOptionFolder mimeFolder = AllOptionsFolder.getDefault().getMIMEFolder(); +// if (mimeFolder == null) return; +// file= mimeFolder.getFile(processor, false); +// }else{ +// MIMEOptionFolder mimeFolder = getMIMEFolder(); +// if (mimeFolder == null) return; +// file= mimeFolder.getFile(processor, false); +// } +// if ((file!=null) && (!file.isLoaded())) { +// file.loadSettings(); +// } +// } +// +// /** Save changes to XML files. +// * @see #updateSettings(java.lang.Class, java.util.Map, boolean ) +// */ +// private void updateSettings(Class processor, Map settings){ +// updateSettings(processor, settings, true); +// } +// +// /** Save changes to XML files +// * @param processor MIMEProcessor class +// * @param settings the settings map +// * @param useRequestProcessorForSaving if true settings will be saved in RequestProcessor thread. +// */ +// private void updateSettings(Class processor, Map settings, boolean useRequestProcessorForSaving){ +// if (processor == FontsColorsMIMEProcessor.class || +// processor == KeyBindingsMIMEProcessor.class || +// processor == AbbrevsMIMEProcessor.class || +// processor == MacrosMIMEProcessor.class +// ) { +// return; +// } +// +// MIMEOptionFile fileX; +// MIMEOptionFolder mimeFolder; +// if (BASE.equals(getTypeName())){ +// mimeFolder = AllOptionsFolder.getDefault().getMIMEFolder(); +// if (mimeFolder == null) return; +// fileX = mimeFolder.getFile(processor, true); +// }else{ +// mimeFolder = getMIMEFolder(); +// if (mimeFolder == null) return; +// fileX = mimeFolder.getFile(processor, true); +// } +// final Map finalSettings = settings; +// final MIMEOptionFile file = fileX; +// if (file!=null){ +// if (useRequestProcessorForSaving){ +// Settings.update(new Runnable() { +// public void run() { +// file.setAllProperties(finalSettings); +// } +// public boolean asynchronous() { +// return true; +// } +// }); +// }else{ +// file.setAllProperties(finalSettings); +// } +// +// } else { +// LOG.info("A settings file for " + processor + " does not exist in " + mimeFolder.getDataFolder()); //NOI18N +// } +// } +// +// public @Override void setSettingValue(String settingName, Object newValue) { +// setSettingValue(settingName, newValue, settingName); +// } +// +// private boolean isTheSame(String settingName, Object newValue){ +// if (settingName == null || +// settingName.equals(NbEditorDocument.INDENT_ENGINE) || +// settingName.equals(NbEditorDocument.FORMATTER) +// ) { +// return true; +// } +// Object oldValue = getSettingValue(settingName); +// if ((oldValue == null && newValue == null) +// || (oldValue != null && oldValue.equals(newValue)) +// ) { +// return true; // the same object +// } +// return false; +// } +// +// +// /** Sets setting value to initializer Map and save the changes to XML file +// * (properties.xml) */ +// public @Override void setSettingValue(String settingName, Object newValue, String propertyName) { +// if (!isTheSame(settingName, newValue)){ +// super.setSettingValue(settingName,newValue,propertyName); +// +// // Save it +// Map map = new HashMap(); +// map.put(settingName, newValue); +// updateSettings(PropertiesMIMEProcessor.class, map); +// } +// } +// +// public @Override Object getSettingValue(String settingName) { +// loadSettings(PropertiesMIMEProcessor.class); +// return super.getSettingValue(settingName); +// } +// +// protected final @Override void setSettingBoolean(String settingName, boolean newValue, String propertyName) { +// setSettingValue(settingName, newValue ? Boolean.TRUE : Boolean.FALSE); +// } +// +// protected final @Override void setSettingInteger(String settingName, int newValue, String propertyName) { +// setSettingValue(settingName, new Integer(newValue)); +// } - protected final @Override void setSettingBoolean(String settingName, boolean newValue, String propertyName) { - setSettingValue(settingName, newValue ? Boolean.TRUE : Boolean.FALSE); + /** + * Load all available settings from XML files and initialize them. + * @deprecated Use Editor Settings Storage API instead. This method is never called. + */ + protected void loadXMLSettings() { +// XXX: remove +// if (!loaded) { +// loaded = true; +// if (LOG.isLoggable(Level.FINE)) { +// LOG.fine("Loading " + getClass() + "; mimeType='" + getCTImpl() + "'"); //NOI18N +// } +// +// loadSettings(PropertiesMIMEProcessor.class); +// +// if (LOG.isLoggable(Level.FINE)) { +// LOG.fine("Loaded! " + getClass() + "; mimeType='" + getCTImpl() + "'"); //NOI18N +// } +// } else { +// if (LOG.isLoggable(Level.FINE)) { +// LOG.fine("Already loaded! " + getClass() + "; mimeType='" + getCTImpl() + "'"); //NOI18N +// } +// } } + + // ------------------------------------------------------------------------ + // Serialization, Externalization, etc + // ------------------------------------------------------------------------ - protected final @Override void setSettingInteger(String settingName, int newValue, String propertyName) { - setSettingValue(settingName, new Integer(newValue)); + /** + * @deprecated Use Editor Settings Storage API instead. BaseOptions are no longer serialized. + */ + public @Override void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { +// XXX: remove +// /** Hold the current indent engine due to #11212 */ +// readExternalIndentEngine = getIndentEngine(); +// inReadExternal = true; +// +// /* Make the current options version to be zero +// * temporarily to distinguish whether the options +// * imported were old and the setOptionsVersion() +// * was not called or whether the options +// * were new so the options version was set +// * to the LATEST_OPTIONS_VERSION value. +// */ +// optionsVersion = 0; +// +// try { +// // Read the serialized options +// super.readExternal(in); +// }catch(java.io.OptionalDataException ode){ +// // #17385. It occurs during reading Settings.settings, that is unimportant +// } finally { +// +// // Make sure the indent engine settings are propagated +// // (SharedClassObject.putProperty() is final) +// refreshIndentEngineSettings(); +// +// // Possibly upgrade the options +// //if (optionsVersion < LATEST_OPTIONS_VERSION) { +// // upgradeOptions(optionsVersion, LATEST_OPTIONS_VERSION); +// //} +// +// optionsVersion = LATEST_OPTIONS_VERSION; +// +// /** Release temp indent engine - #11212 */ +// inReadExternal = false; +// readExternalIndentEngine = null; +// } } - /** Load all available settings from XML files and initialize them */ - protected void loadXMLSettings() { - if (!loaded) { - loaded = true; - if (LOG.isLoggable(Level.FINE)) { - LOG.fine("Loading " + getClass() + "; mimeType='" + getCTImpl() + "'"); //NOI18N - } - - loadSettings(PropertiesMIMEProcessor.class); - - if (LOG.isLoggable(Level.FINE)) { - LOG.fine("Loaded! " + getClass() + "; mimeType='" + getCTImpl() + "'"); //NOI18N - } - } else { - if (LOG.isLoggable(Level.FINE)) { - LOG.fine("Already loaded! " + getClass() + "; mimeType='" + getCTImpl() + "'"); //NOI18N - } - } - } - - /** Overriden writeExternal method. BaseOptions are no longer serialized. */ + /** + * Overriden writeExternal method. + * @deprecated Use Editor Settings Storage API instead. BaseOptions are no longer serialized. + */ public void writeExternal() throws IOException{ } - /** Overriden writeExternal method. BaseOptions are no longer serialized. */ + /** + * Overriden writeExternal method. + * @deprecated Use Editor Settings Storage API instead. BaseOptions are no longer serialized. + */ public @Override void writeExternal(ObjectOutput out) throws IOException{ } + /** + * @deprecated Use Editor Settings Storage API instead. BaseOptions are no longer serialized. + */ protected @Override void firePropertyChange(String name, Object oldValue, Object newValue){ // ignore firing... Quick fix of #47261. // BaseOptions should be rewritten to not extend SystemOption ... // there is no need to be compatile with NB 3.2 and deserialize its options... - } - - /** Saves the keystroke of code tamplate expansion into properties.xml file under Editors/text/base */ - public static void setCodeTemplateExpandKey(KeyStroke ks){ - String s = Utilities.keyToString(ks); - BaseOptions base = getOptions(BaseKit.class); - Map map = new HashMap(); - map.put(CODE_TEMPLATE_EXPAND_KEY, s); - base.updateSettings(PropertiesMIMEProcessor.class, map); - } - - /** Gets Code Template Expand Key. Can return null if there is no key in the settings file */ - public static KeyStroke getCodeTemplateExpandKey(){ - MIMEOptionFolder mimeFolder = AllOptionsFolder.getDefault().getMIMEFolder(); - if (mimeFolder != null){ - MIMEOptionFile file = mimeFolder.getFile(PropertiesMIMEProcessor.class, false); - if (file != null){ - if (!file.isLoaded()) { - file.loadSettings(false); - } - Map properties = file.getAllProperties(); - String s = (String) properties.get(CODE_TEMPLATE_EXPAND_KEY); - if (s != null){ - return Utilities.stringToKey(s); - } - } - } - return KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0); } } Index: editor/src/org/netbeans/modules/editor/options/OptionSupport.java =================================================================== RCS file: /cvs/editor/src/org/netbeans/modules/editor/options/OptionSupport.java,v retrieving revision 1.37 diff -u -r1.37 OptionSupport.java --- editor/src/org/netbeans/modules/editor/options/OptionSupport.java 25 Oct 2007 18:27:31 -0000 1.37 +++ editor/src/org/netbeans/modules/editor/options/OptionSupport.java 10 Jan 2008 09:45:37 -0000 @@ -159,13 +159,9 @@ * fire the property change. * @param settingName name of the setting to change * @param newValue new value of the setting - * @param propertyName if non-null it means that the property change - * should be fired if the newValue is differernt from the old one. - * Firing is performed using the given property name. Nothing is fired - * when it's set to null. + * @param propertyName Ignored. */ - public void setSettingValue(String settingName, Object newValue, - String propertyName) { + public void setSettingValue(String settingName, Object newValue, String propertyName) { initializerValuesMap.put(settingName, newValue); @@ -177,15 +173,10 @@ } Settings.setValue(kitClass, settingName, newValue); - -// if (propertyName != null) { - //firePropertyChange(propertyName, oldValue, newValue);[PENDING] -// } } - public void doSetSettingValue(String settingName, Object newValue, - String propertyName) { + public void doSetSettingValue(String settingName, Object newValue, String propertyName) { initializerValuesMap.put(settingName, newValue); Settings.setValue(kitClass, settingName, newValue); } @@ -194,10 +185,7 @@ /** Enables easier handling of the boolean settings. * @param settingName name of the setting to change * @param newValue new boolean value of the setting - * @param propertyName if non-null it means that the property change - * should be fired if the newValue is differernt from the old one. - * Firing is performed using the given property name. Nothing is fired - * when it's set to null. + * @param propertyName Ignored. */ protected void setSettingBoolean(String settingName, boolean newValue, String propertyName) { setSettingValue(settingName, newValue ? Boolean.TRUE : Boolean.FALSE, propertyName); @@ -206,10 +194,7 @@ /** Enables easier handling of the integer settings. * @param settingName name of the setting to change * @param newValue new integer value of the setting - * @param propertyName if non-null it means that the property change - * should be fired if the newValue is differernt from the old one. - * Firing is performed using the given property name. Nothing is fired - * when it's set to null. + * @param propertyName Ignored. */ protected void setSettingInteger(String settingName, int newValue, String propertyName) { setSettingValue(settingName, new Integer(newValue)); Index: editor/src/org/netbeans/modules/editor/options/PropertiesMIMEOptionFile.java =================================================================== RCS file: /cvs/editor/src/org/netbeans/modules/editor/options/PropertiesMIMEOptionFile.java,v retrieving revision 1.10 diff -u -r1.10 PropertiesMIMEOptionFile.java --- editor/src/org/netbeans/modules/editor/options/PropertiesMIMEOptionFile.java 26 Oct 2007 09:13:39 -0000 1.10 +++ editor/src/org/netbeans/modules/editor/options/PropertiesMIMEOptionFile.java 10 Jan 2008 09:45:37 -0000 @@ -64,6 +64,7 @@ * * @author Martin Roskanin * @since 08/2001 + * @deprecated Use Editor Settings Storage API instead. */ public class PropertiesMIMEOptionFile extends MIMEOptionFile{ @@ -86,6 +87,10 @@ /** Loads settings from XML file. * @param propagate if true - propagates the loaded settings to Editor UI */ protected void loadSettings(boolean propagate){ + assert false : "PropertiesMIMEOptionFile should not be used anymore. " + //NOI18N + "Please file a bug (http://www.netbeans.org/community/issues.html) " + //NOI18N + "for editor/settings and attach this stacktrace to it."; //NOI18N + synchronized (Settings.class) { Document doc = dom; Element rootElement = doc.getDocumentElement(); @@ -174,6 +179,10 @@ /** Save settings to XML file * @param changedProp the Map of settings to save */ protected void updateSettings(Map changedProp){ + assert false : "PropertiesMIMEOptionFile should not be used anymore. " + //NOI18N + "Please file a bug (http://www.netbeans.org/community/issues.html) " + //NOI18N + "for editor/settings and attach this stacktrace to it."; //NOI18N + synchronized (Settings.class) { boolean save = false; Index: editor/src/org/netbeans/modules/editor/options/PropertiesMIMEProcessor.java =================================================================== RCS file: /cvs/editor/src/org/netbeans/modules/editor/options/PropertiesMIMEProcessor.java,v retrieving revision 1.5 diff -u -r1.5 PropertiesMIMEProcessor.java --- editor/src/org/netbeans/modules/editor/options/PropertiesMIMEProcessor.java 1 Oct 2007 15:50:09 -0000 1.5 +++ editor/src/org/netbeans/modules/editor/options/PropertiesMIMEProcessor.java 10 Jan 2008 09:45:37 -0000 @@ -41,12 +41,11 @@ package org.netbeans.modules.editor.options; -import org.openide.loaders.XMLDataObject; - /** XML Processor for properties settings * * @author Martin Roskanin * @since 08/2001 + * @deprecated Use Editor Settings Storage API instead. */ public class PropertiesMIMEProcessor extends MIMEProcessor{ Index: editor/test/unit/src/org/netbeans/modules/editor/openide/InheritedNotifyModifiedTest.java =================================================================== RCS file: /cvs/editor/test/unit/src/org/netbeans/modules/editor/openide/InheritedNotifyModifiedTest.java,v retrieving revision 1.6 diff -u -r1.6 InheritedNotifyModifiedTest.java --- editor/test/unit/src/org/netbeans/modules/editor/openide/InheritedNotifyModifiedTest.java 1 Oct 2007 15:50:57 -0000 1.6 +++ editor/test/unit/src/org/netbeans/modules/editor/openide/InheritedNotifyModifiedTest.java 10 Jan 2008 09:45:37 -0000 @@ -42,7 +42,9 @@ package org.netbeans.modules.editor.openide; import javax.swing.text.EditorKit; +import org.openide.modules.ModuleInfo; import org.openide.text.*; +import org.openide.util.Lookup; /** * @@ -55,8 +57,14 @@ super(methodName); } - protected EditorKit createEditorKit() { + protected @Override EditorKit createEditorKit() { return new org.netbeans.modules.editor.NbEditorKit(); + } + + protected @Override void setUp() { + super.setUp(); + + Lookup.getDefault().lookup(ModuleInfo.class); } }