001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: /*
043: * File : Preferences.java
044: * Version : 1.00
045: * Description : Describe preference manager for preferences affecting
046: * integrations.
047: * Author : Gautam Sabba
048: */
049: package org.netbeans.modules.uml.integration.ide;
050:
051: import java.io.File;
052: import java.util.ArrayList;
053: import java.util.Collection;
054: import java.util.HashMap;
055: import java.util.Iterator;
056:
057: import org.netbeans.modules.uml.core.coreapplication.IPreferenceManager2;
058: import org.netbeans.modules.uml.core.support.umlsupport.Log;
059: import org.netbeans.modules.uml.core.support.umlutils.IPropertyElement;
060: import org.netbeans.modules.uml.util.DummyCorePreference;
061: import org.openide.util.NbBundle;
062: import org.openide.util.NbPreferences;
063:
064: /**
065: * Describe preference manager for preferences affecting integrations.
066: */
067: public class Preferences {
068: /**
069: * Interface for classes that want to be notified of when a preference
070: * changes.
071: *
072: * @author darshans
073: */
074: public static interface PreferenceWatcher {
075: /**
076: * Notification that the preference specified by the given preference
077: * key has changed from oldV to newV.
078: *
079: * @param preferencePath The preference key as its absolute path.
080: * @param oldV The old value of the preference. This is not
081: * guaranteed to be accurate; if not available,
082: * <code>null</code> is passed.
083: * @param newV The new value of the preference.
084: */
085: public void preferenceChanged(String preferencePath,
086: String oldV, String newV);
087: }
088:
089: public static final String PSK_YES = "PSK_YES";
090: public static final String PSK_NO = "PSK_NO";
091: public static final String PSK_ASK = "PSK_ASK";
092: public static final String PSK_NEVER = "PSK_NEVER";
093: public static final String PSK_ALWAYS = "PSK_ALWAYS";
094:
095: public static final String COLLECTION_OVERRIDE = "UML_COLLECTION_OVERRIDE_DEFAULT";
096: public static final String USE_GENERICS_DEFAULT = "UML_USE_GENERICS_DEFAULT";
097: public static final String RECONNECT_LINKS = "UML_Reconnect to Presentation Boundary";
098:
099: public static final String CONFIRM_SOURCE_DELETE = "ArtifactDeleteDeletesFile";
100:
101: /**
102: * Adds a preference watcher for the given preference. Watchers are notified
103: * whenever their associated preference changes (or if their associated
104: * preference is <code>null</code>, whenever any <em>watched</em> preference
105: * changes). Explicitly specifying a preference to this function
106: * automatically adds it to the list of watched preferences; if you specify
107: * <code>null</code>, you'll have to add the list of preferences you're
108: * interested in to the watch list separately.
109: * <br/>
110: * The list of watched preferences is independent of whether we're
111: * connected to Describe (i.e., disconnect/reconnect will not affect the
112: * watch list).
113: * <br/>
114: * Preference change notifications are fired only when readPreferences()
115: * is called, i.e., there is no magic behind the scenes to fire events as
116: * soon as the user/code changes a preference. However, integrations are
117: * expected to call readPreferences() each time the Describe preferences
118: * dialog is dismissed, so that should be academic.
119: *
120: * @param pref The preference to watch. If not <code>null</code>, this
121: * preference is added to the watch list.
122: * @param watcher The watcher to be notified when the associated preference
123: * changes.
124: * @param fireInitial If <code>true</code>, immediately fire a
125: * preferenceChanged() to the watcher with the current
126: * preference value.
127: */
128: synchronized public static void addPreferenceWatcher(String pref,
129: PreferenceWatcher watcher, boolean fireInitial) {
130: if (watcher == null) {
131: throw new IllegalArgumentException("Can't add null watcher");
132: }
133: addWatch(pref);
134:
135: if (pref == null)
136: pref = "";
137: if (watchers == null)
138: watchers = new HashMap();
139:
140: ArrayList iwatchers = null;
141: if (watchers.containsKey(pref))
142: iwatchers = (ArrayList) watchers.get(pref);
143: else {
144: iwatchers = new ArrayList();
145: watchers.put(pref, iwatchers);
146: }
147:
148: if (!iwatchers.contains(watcher))
149: iwatchers.add(watcher);
150:
151: if (fireInitial && pref.length() > 0) {
152: Log
153: .out("Preferences.addPreferenceWatcher: Firing initial event "
154: + "for " + pref + " to " + watcher);
155: watcher.preferenceChanged(pref, null, getPreference(pref));
156: }
157: }
158:
159: /**
160: * Removes a property watcher. Any keys which were exclusively watched by
161: * this watcher will also be removed from the watch list.
162: *
163: * @param watcher The watcher that's no longer wanted.
164: */
165: synchronized public static void removePreferenceWatcher(
166: PreferenceWatcher watcher) {
167: if (watcher == null)
168: throw new IllegalArgumentException(
169: "Can't remove null watcher");
170: if (watchers == null)
171: return;
172:
173: Iterator ikey = watchers.keySet().iterator();
174: while (ikey.hasNext()) {
175: String pref = ikey.next().toString();
176:
177: ArrayList a = (ArrayList) watchers.get(pref);
178: if (a == null)
179: continue;
180:
181: if (a.remove(watcher) && a.size() == 0) {
182: try {
183: ikey.remove();
184: removeWatch(pref);
185: } catch (Exception e) {
186: Log.stackTrace(e);
187: }
188: }
189: }
190: }
191:
192: /**
193: * Adds the given preference to the list of preferences that are being
194: * watched. Changes to watched preferences fire events to their
195: * corresponding watchers.
196: *
197: * @param pref The preference to watch. If <code>null</code>, this function
198: * returns silently.
199: */
200: synchronized public static void addWatch(String pref) {
201: if (pref == null)
202: return;
203: if (watches == null)
204: watches = new HashMap();
205: if (!watches.containsKey(pref))
206: watches.put(pref, null);
207: }
208:
209: /**
210: * Removes the given preference from the list of preferences that are being
211: * watched.
212: *
213: * @param pref The preference that needn't be watched any longer. Silently
214: * ignores error conditions such as a <code>null</code> pref, or
215: * attempts to remove a preference that's not being watched.
216: */
217: synchronized public static void removeWatch(String pref) {
218: if (watches != null && pref != null)
219: watches.remove(pref);
220: }
221:
222: /**
223: * Reads all Describe preferences relevant to IDE integrations from the
224: * Describe preference manager, if the integration is currently connected
225: * to Describe. If not connected to Describe, this is a silent no-op. If
226: * the default workspace location is unset, will call the integration
227: * using IIDEManager.getDefaultWorkspaceDirectory() to get a default path.
228: */
229: public static void readPreferences() {
230: if (!initPreferenceManager())
231: return;
232:
233: try {
234: clearCache();
235:
236: java.util.prefs.Preferences prefs = NbPreferences
237: .forModule(DummyCorePreference.class);
238:
239: reconnectLinks = prefs.getBoolean(RECONNECT_LINKS, true);
240: promptSaveWorkspace = true;
241:
242: defaultElementName = NbBundle.getMessage(Preferences.class,
243: "UNNAMED"); // NOI18N
244:
245: collectionOverride = prefs.get(COLLECTION_OVERRIDE,
246: "java.util.ArrayList"); // NOI18N
247: confirmSourceDelete = getPreference(CONFIRM_SOURCE_DELETE);
248:
249: readWatchedPreferences();
250: } catch (ClobberedException ex) {
251: Log.stackTrace(ex);
252: UMLSupport.reviveDescribe();
253: readPreferences();
254: }
255: }
256:
257: /**
258: * Clears all cached preference values and forces requerying the Describe
259: * preference manager the next time any preference is asked for. This is
260: * called automatically by readPreferences().
261: */
262: public static void clearCache() {
263: preferenceCache.clear();
264: }
265:
266: /**
267: * Reads all preferences that are being watched, and fires events for those
268: * that have changed.
269: */
270: synchronized private static void readWatchedPreferences() {
271: if (watches == null || watchers == null)
272: return;
273:
274: Iterator prefs = watches.keySet().iterator();
275: while (prefs.hasNext()) {
276: String pref = (String) prefs.next();
277: if (pref == null) {
278: try {
279: prefs.remove();
280: } catch (Exception ignored) {
281: }
282: continue;
283: }
284:
285: String newVal = getPreference(pref);
286: String oldVal = (String) watches.get(pref);
287:
288: if ((newVal == null && oldVal == null)
289: || (newVal != null && newVal.equals(oldVal))
290: || (oldVal != null && oldVal.equals(newVal))) {
291: continue;
292: }
293:
294: watches.put(pref, newVal);
295: firePreferenceChanged(pref, oldVal, newVal);
296: }
297: }
298:
299: private static void firePreferenceChanged(String pref, String oldV,
300: String newV) {
301: if (watchers == null)
302: return;
303: Collection ws = getWatchers(pref);
304: firePreferenceChanged(ws, pref, oldV, newV);
305: ws = getWatchers("");
306: firePreferenceChanged(ws, pref, oldV, newV);
307: }
308:
309: private static void firePreferenceChanged(Collection coll,
310: String pref, String oldV, String newV) {
311: if (coll == null)
312: return;
313: Iterator iter = coll.iterator();
314: while (iter.hasNext()) {
315: PreferenceWatcher watcher = (PreferenceWatcher) iter.next();
316: if (watcher != null) {
317: try {
318: watcher.preferenceChanged(pref, oldV, newV);
319: } catch (Exception e) {
320: Log.stackTrace(e);
321: }
322: }
323: }
324: }
325:
326: private static Collection getWatchers(String pref) {
327: return (Collection) watchers.get(pref);
328: }
329:
330: /**
331: * Forgets the cached Describe preference manager. This should before
332: * disconnecting from Describe (so that the preference manager proxy can be
333: * garbage collected), and after a clobber-revive.
334: */
335: public static void reset() {
336: prefMan = null;
337: }
338:
339: /**
340: * Returns whether integrations should prompt for the location to create a
341: * new Describe project, when creating a Describe project for a newly
342: * created IDE project.
343: * @return <code>true</code> if the integration should prompt the user to
344: * choose a location for the new Describe project.
345: */
346: public static boolean isPromptProjectLocation() {
347: throw new UnsupportedOperationException(
348: "This pref is no long valid.");
349: }
350:
351: /**
352: * Returns whether integrations should prompt to save the current Describe
353: * workspace/project when switching between IDE projects.
354: * @return <code>true</code> if the integration should prompt to save
355: * Describe metadata.
356: */
357: public static boolean isPromptSaveWorkspace() {
358: return promptSaveWorkspace;
359: }
360:
361: /**
362: * Returns whether integrations should prompt for the location to create a
363: * new Describe workspace, when creating or opening a Describe workspace for
364: * a newly created IDE project.
365: * @return <code>true</code> if the integration should prompt the user to
366: * choose a location for the new Describe workspace.
367: */
368: public static boolean isPromptWksLocation() {
369: throw new UnsupportedOperationException(
370: "This pref is no long valid.");
371: }
372:
373: /**
374: * Returns whether links are reconnected to presentations element
375: * boundaries.
376: *
377: * @return <code>true</code> if the reconnect links preference is set.
378: */
379: public static boolean isReconnectLinks() {
380: return reconnectLinks;
381: }
382:
383: /**
384: * Returns whether integrations should prompt the user to create a new
385: * Describe diagram when connecting a new IDE project to Describe.
386: * @return <code>true</code> if the integration should prompt the user to
387: * create a new diagram.
388: */
389: public static boolean isCreateNewDiagram() {
390: //return createNewDiagram;
391: // I wasn't sure how the whole watched preferences was working, but it
392: // doesn't seem to be working in this case. Not sure how this variable
393: // would ever be updated. So, changed to get the preference value whenever
394: // this method is called
395: //kris richards - "QueryForNewDiagram" pref expunged. Set to true.
396: return true;
397: }
398:
399: public static boolean getUseGenericsDefault() {
400: //kris richards - changing to use NbPreferences
401: //preferenceCache.remove(USE_GENERICS_DEFAULT);
402: return NbPreferences.forModule(DummyCorePreference.class)
403: .getBoolean("UML_USE_GENERICS_DEFAULT", true);
404: }
405:
406: /**
407: * Returns the absolute path to the Describe workspace to be used by default
408: * when connecting to new IDE projects.
409: *
410: * @return <code>String</code> - the absolute path to the default Describe
411: * workspace .etw file.
412: */
413: public static String getDefaultWorkspacePath() {
414: // Point to the location specified (i.e. <workspaceFolder>/Default.etw)
415: // else if workspaceFolder == null
416: // Point at default workspace:
417: // ${DESCRIBEHOME}/Workspaces/Default/Default.etw
418:
419: if (defaultWorkspaceLocation == null
420: || defaultWorkspaceLocation.trim().length() == 0) {
421: UMLSupport gps = UMLSupport.getUMLSupport();
422:
423: String workspaceFolder = UMLSupport.getUMLSupport()
424: .getIDEManager().getDefaultWorkspaceDirectory();
425: Log.out("readPreferences: IDE's workspace folder : "
426: + workspaceFolder);
427: try {
428: File workspaceLocation = null;
429:
430: if (workspaceFolder != null
431: && workspaceFolder.trim().length() > 0)
432: workspaceLocation = new File(new File(
433: workspaceFolder), DEF_WKS_FILE);
434: else
435: workspaceLocation = new File(new File(gps
436: .getApplication().getInstallLocation())
437: .getParentFile().getParentFile(),
438: REL_DEF_WKS_LOC);
439:
440: return workspaceLocation.toString();
441: } catch (Exception ignored) {
442: }
443: }
444:
445: if (defaultWorkspaceLocation != null
446: && defaultWorkspaceLocation.trim().length() == 0)
447: defaultWorkspaceLocation = null;
448: return defaultWorkspaceLocation;
449: }
450:
451: /**
452: * Checks if the default location preference is set. *
453: */
454: public static boolean isDefaultWorkspacePathEmpty() {
455: return (defaultWorkspaceLocation == null || defaultWorkspaceLocation
456: .trim().length() == 0);
457: }
458:
459: /**
460: * Sets the absolute path to the Describe workspace to be used as the
461: * default when connecting to new IDE projects.
462: *
463: * @param path A <code>String</code> with the absolute path to the workspace
464: * .etw file.
465: */
466: public static void setDefaultWorkspacePath(String path) {
467: //kris richards - this pref does not really exist.
468: throw new UnsupportedOperationException(
469: "DefaultWSLocation pref is no long valid.");
470: }
471:
472: /**
473: * Sets the preference state of "Prompt for workspace path".
474: *
475: * @param path A <code>true</code> for PSK_YES, <code>false</code> for
476: * PSK_NO.
477: */
478: public static void setPromptWksLocation(boolean state) {
479: throw new UnsupportedOperationException(
480: "This pref is no long valid.");
481: }
482:
483: /**
484: * Sets the preference state of "Delete file with artifacts".
485: *
486: * @param val PSK_ASK, PSK_NEVER and PSK_ALWAYS.
487: */
488: public static void setDeleteFileWithArtifact(String val) {
489: confirmSourceDelete = val;
490: setPreference(CONFIRM_SOURCE_DELETE, val);
491: }
492:
493: public static String getDeleteFileWithArtifact() {
494: return confirmSourceDelete;
495: }
496:
497: /**
498: * Sets whether links are reconnected to presentation element boundaries.
499: *
500: * @param reconnectLinks <code>true</code> to reconnect links.
501: */
502: public static void setReconnectLinks(boolean reconnectLinks) {
503: Preferences.reconnectLinks = reconnectLinks;
504: setPreference(RECONNECT_LINKS, reconnectLinks ? PSK_YES
505: : PSK_NO);
506: }
507:
508: /**
509: * Returns the default name for unnamed elements in the Describe model.
510: *
511: * @return A <code>String</code> of the default name for unnamed elements.
512: */
513: public static String getDefaultElementName() {
514: //kris richards - "DefaultElementName" pref expunged. Set to "Unnamed".
515: return NbBundle.getMessage(Preferences.class, "UNNAMED");
516: }
517:
518: /**
519: * Returns the type to be used when substituting collection types for
520: * array-type attributes.
521: * @return <code>String</code> The name of the collection class to be used
522: * instead of arrays.
523: */
524: public static String getCollectionOverride() {
525: if (collectionOverride == null)
526: collectionOverride = getPreference(COLLECTION_OVERRIDE);
527:
528: return collectionOverride;
529: }
530:
531: public static void removePreference(String prefKey) {
532: if (prefKey == null || !initPreferenceManager())
533: return;
534:
535: int pathDelimPos = prefKey.lastIndexOf("|");
536: String path = (pathDelimPos != -1 ? prefKey.substring(0,
537: pathDelimPos) : ""), key = prefKey
538: .substring(pathDelimPos + 1);
539:
540: IPropertyElement pe = prefMan.getPreferenceElement(path, key);
541: if (pe != null) {
542: Log.out("removePreference(): Removing preference - "
543: + prefKey);
544: prefMan.removePreference(pe);
545: } else
546: Log.out("removePreference(): Could not locate preference"
547: + " to delete - " + prefKey);
548: }
549:
550: private static boolean getBooleanPreference(String prefKey) {
551: String val = getPreference(prefKey);
552: if (val == null && UMLSupport.isClobbered())
553: throw new ClobberedException();
554: return PSK_YES.equals(getPreference(prefKey));
555: }
556:
557: private static String getPreference(String prefKey) {
558: if (prefKey == null || !initPreferenceManager())
559: return null;
560:
561: if (preferenceCache.containsKey(prefKey))
562: return (String) preferenceCache.get(prefKey);
563:
564: int pathDelimPos = prefKey.lastIndexOf("|");
565: String path = (pathDelimPos != -1 ? prefKey.substring(0,
566: pathDelimPos) : ""), key = prefKey
567: .substring(pathDelimPos + 1);
568: try {
569: String val = prefMan.getPreferenceValue(path, key);
570: if (val != null && val.trim().length() == 0)
571: val = null;
572:
573: preferenceCache.put(prefKey, val);
574: return val;
575: } catch (Exception ex) {
576: Log.stackTrace(ex);
577: return null;
578: }
579: }
580:
581: private static void setPreference(String prefKey, String prefValue) {
582: if (prefKey == null || !initPreferenceManager())
583: return;
584: if (prefValue == null)
585: prefValue = "";
586:
587: int pathDelimPos = prefKey.lastIndexOf("|");
588: String path = (pathDelimPos != -1 ? prefKey.substring(0,
589: pathDelimPos) : ""), key = prefKey
590: .substring(pathDelimPos + 1);
591: try {
592: prefMan.setPreferenceValue(path, key, prefValue);
593: preferenceCache.put(prefKey, prefValue);
594: } catch (Exception ex) {
595: Log.stackTrace(ex);
596: }
597: }
598:
599: private static boolean initPreferenceManager() {
600: //if (prefMan != null) return true;
601: if (!UMLSupport.getUMLSupport().isConnected())
602: return false;
603:
604: prefMan = null;
605: try {
606: prefMan = UMLSupport.getUMLSupport().getProduct()
607: .getPreferenceManager();
608: } catch (Exception ex) {
609: Log.stackTrace(ex);
610: }
611: boolean retVal = prefMan != null;
612: return retVal;
613: }
614:
615: // Individual preference properties
616: private static boolean promptSaveWorkspace;
617: private static boolean reconnectLinks;
618: // private static boolean useGenericsForCollections;
619:
620: private static String defaultWorkspaceLocation;
621: private static String defaultElementName;
622: private static String collectionOverride;
623: private static String confirmSourceDelete;
624: private static IPreferenceManager2 prefMan = null;
625: private static String REL_DEF_WKS_LOC = "Workspaces/Default/Default.etw";
626: private static String DEF_WKS_FILE = "Default.etw";
627:
628: private static HashMap watches;
629: private static HashMap watchers;
630:
631: private static HashMap preferenceCache = new HashMap();
632:
633: static class ClobberedException extends RuntimeException {
634:
635: }
636: }
|