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-2006 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: package org.netbeans.core.projects;
043:
044: import org.openide.NotifyDescriptor;
045: import org.openide.cookies.InstanceCookie;
046: import org.openide.loaders.DataObject;
047: import org.openide.loaders.DataFolder;
048: import org.openide.loaders.DataShadow;
049: import org.openide.filesystems.FileObject;
050: import org.openide.filesystems.FileSystem;
051: import org.openide.filesystems.FileStateInvalidException;
052: import org.openide.filesystems.Repository;
053: import org.openide.util.NbBundle;
054: import org.openide.util.Utilities;
055: import org.netbeans.beaninfo.editors.ListImageEditor;
056: import java.awt.Image;
057: import java.beans.PropertyEditor;
058: import java.lang.reflect.InvocationTargetException;
059: import java.lang.ref.WeakReference;
060: import java.util.ArrayList;
061: import java.util.Arrays;
062: import java.util.List;
063: import javax.swing.Action;
064: import org.openide.actions.ToolsAction;
065: import org.openide.nodes.FilterNode;
066: import org.openide.nodes.Node;
067: import org.openide.nodes.PropertySupport;
068: import org.openide.util.Exceptions;
069: import org.openide.util.actions.SystemAction;
070:
071: /** Filters nodes under the session node (displayed in Options dialog), adds special
072: * properties to Nodes of particular settings to show/edit positions wher the
073: * setting is defined on DefaultFileSystem.
074: *
075: * @author Vitezslav Stejskal
076: */
077: public final class SettingChildren extends FilterNode.Children {
078:
079: /** Name of Node.Property showing status of Session layer according to the setting */
080: public static final String PROP_LAYER_SESSION = "Session-Layer"; // NOI18N
081: /** Name of Node.Property showing status of Modules layer according to the setting */
082: public static final String PROP_LAYER_MODULES = "Modules-Layer"; // NOI18N
083:
084: public SettingChildren(Node original) {
085: super (original);
086: }
087:
088: protected Node copyNode(Node node) {
089: boolean filter = false;
090: try {
091: DataObject d = (DataObject) node
092: .getCookie(DataObject.class);
093: if (d != null) {
094: InstanceCookie.Of inst = (InstanceCookie.Of) d
095: .getCookie(InstanceCookie.Of.class);
096: if (inst != null
097: && (inst.instanceOf(Node.class) || inst
098: .instanceOf(Node.Handle.class))) {
099: // This is just a node, not a real setting. E.g. ModuleNode, LoaderPoolNode. As such,
100: // it itself should not display any origin information, it would make no sense. However
101: // its children might have a legitimate DataObject cookie from the SFS.
102: d = null;
103: }
104: }
105: DataFolder folder = (DataFolder) node
106: .getCookie(DataFolder.class);
107: FileSystem fs = d == null || folder != null ? null : d
108: .getPrimaryFile().getFileSystem();
109: filter = fs == null ? false : fs.equals(Repository
110: .getDefault().getDefaultFileSystem());
111: } catch (FileStateInvalidException e) {
112: // ignore
113: }
114:
115: return filter ? new SettingFilterNode(node)
116: : node.isLeaf() ? node.cloneNode()
117: : new TrivialFilterNode(node);
118: }
119:
120: private static Action[] removeActions(Action[] allActions,
121: Action[] toDeleteActions) {
122: Action[] retVal = allActions;
123: List<Action> actions = new ArrayList<Action>(Arrays
124: .asList(allActions)); // to be mutable
125: for (int i = 0; i < toDeleteActions.length; i++) {
126: Action a = toDeleteActions[i];
127: if (actions.contains(a)) {
128: actions.remove(a);
129: retVal = actions.toArray(new Action[actions.size()]);
130: }
131: }
132: return retVal;
133: }
134:
135: private static final class TrivialFilterNode extends FilterNode {
136: public TrivialFilterNode(Node n) {
137: super (n, new SettingChildren(n));
138: }
139:
140: // #17920: Index cookie works only when equality works
141: public boolean equals(Object o) {
142: return this == o || getOriginal().equals(o)
143: || (o != null && o.equals(getOriginal()));
144: }
145:
146: public int hashCode() {
147: return getOriginal().hashCode();
148: }
149:
150: public Action[] getActions(boolean context) {
151: return removeActions(
152: super .getActions(context),
153: new Action[] { SystemAction.get(ToolsAction.class) });
154: }
155:
156: public String getHtmlDisplayName() {
157: return null;
158: }
159: }
160:
161: /** Property allowing display/manipulation of setting status for one specific layer. */
162: public static class FileStateProperty extends
163: PropertySupport<Integer> {
164: static final int ACTION_DEFINE = 1;
165: static final int ACTION_REVERT = 2;
166: static final int ACTION_DELETE = 3;
167:
168: private FileObject primaryFile = null;
169: private int layer;
170:
171: public FileStateProperty(String name) {
172: this (null, 0, name, true);
173: }
174:
175: public FileStateProperty(FileObject primaryFile, int layer,
176: String name, boolean readonly) {
177: super (name, Integer.class, NbBundle.getMessage(
178: FileStateProperty.class, "LBL_FSP_" + name), // NOI18N
179: NbBundle.getMessage(FileStateProperty.class,
180: "LBL_FSP_Desc_" + name), // NOI18N
181: true, !readonly);
182:
183: this .primaryFile = primaryFile;
184: this .layer = layer;
185:
186: setValue(ListImageEditor.PROP_VALUES, new Integer[] {
187: FileStateManager.FSTATE_DEFINED,
188: FileStateManager.FSTATE_IGNORED,
189: FileStateManager.FSTATE_INHERITED,
190: FileStateManager.FSTATE_UNDEFINED, });
191:
192: setValue(
193: ListImageEditor.PROP_IMAGES,
194: new Image[] {
195: Utilities
196: .loadImage("org/netbeans/core/resources/setting-defined.gif"), // NOI18N
197: Utilities
198: .loadImage("org/netbeans/core/resources/setting-ignored.gif"), // NOI18N
199: Utilities
200: .loadImage("org/netbeans/core/resources/setting-inherited.gif"), // NOI18N
201: Utilities
202: .loadImage("org/openide/resources/actions/empty.gif") // NOI18N
203: });
204: }
205:
206: public boolean canWrite() {
207: if (!super .canWrite())
208: return false;
209:
210: Integer val = null;
211: try {
212: val = getValue();
213: } catch (Exception e) {
214: // ignore it, will be handled later
215: }
216:
217: return val != null
218: && val != FileStateManager.FSTATE_DEFINED
219: && (layer != FileStateManager.LAYER_MODULES || val != FileStateManager.FSTATE_UNDEFINED);
220: }
221:
222: public Integer getValue() throws IllegalAccessException,
223: InvocationTargetException {
224: return FileStateManager.getDefault().getFileState(
225: primaryFile, layer);
226: }
227:
228: public void setValue(Integer val)
229: throws IllegalAccessException,
230: IllegalArgumentException, InvocationTargetException {
231: FileStateManager fsm = FileStateManager.getDefault();
232: int action = val;
233:
234: try {
235: switch (action) {
236: case ACTION_DEFINE:
237: case ACTION_REVERT:
238: boolean go = true;
239:
240: for (int i = 0; i < layer; i++) {
241: int state = fsm.getFileState(primaryFile, i);
242: if (state == FileStateManager.FSTATE_DEFINED) {
243: // warn user, that above defined files will be removed
244:
245: NotifyDescriptor nd = new NotifyDescriptor.Confirmation(
246: NbBundle
247: .getMessage(
248: SettingChildren.class,
249: "MSG_ask_remove_above_defined_files"), // NOI18N
250: NotifyDescriptor.YES_NO_OPTION);
251:
252: Object answer = org.openide.DialogDisplayer
253: .getDefault().notify(nd);
254: if (answer
255: .equals(NotifyDescriptor.NO_OPTION))
256: go = false;
257:
258: break;
259: }
260: }
261:
262: if (go)
263: fsm.define(primaryFile, layer,
264: action == ACTION_REVERT);
265:
266: break;
267:
268: case ACTION_DELETE:
269: fsm.delete(primaryFile, layer);
270: break;
271:
272: default:
273: throw new IllegalArgumentException(
274: "Required file state change isn't allowed. Action="
275: + action); // NOI18N
276: }
277: } catch (java.io.IOException e) {
278: Exceptions.printStackTrace(e);
279: }
280: }
281:
282: public PropertyEditor getPropertyEditor() {
283: return new FileStateEditor();
284: }
285:
286: public String getShortDescription() {
287: Integer val = null;
288: String s = null;
289:
290: if (primaryFile != null) {
291: try {
292: val = getValue();
293: } catch (Exception e) {
294: // ignore it, will be handled later
295: }
296:
297: switch (val == null ? -1 : val) {
298: case FileStateManager.FSTATE_DEFINED:
299: s = NbBundle.getMessage(SettingChildren.class,
300: "LBL_fstate_defined");
301: break;
302: case FileStateManager.FSTATE_IGNORED:
303: s = NbBundle.getMessage(SettingChildren.class,
304: "LBL_fstate_ignored");
305: break;
306: case FileStateManager.FSTATE_INHERITED:
307: s = NbBundle.getMessage(SettingChildren.class,
308: "LBL_fstate_inherited");
309: break;
310: case FileStateManager.FSTATE_UNDEFINED:
311: s = NbBundle.getMessage(SettingChildren.class,
312: "LBL_fstate_undefined");
313: break;
314: }
315: } else {
316: s = super .getShortDescription();
317: }
318: return s == null || s.length() == 0 ? null : s;
319: }
320: }
321:
322: /** Filter node used for adding special status related properties to setting nodes. */
323: private static final class SettingFilterNode extends FilterNode {
324: private FSL weakL = null;
325:
326: public SettingFilterNode(Node original) {
327: super (original);
328: // need to keep the values in this FilterNode, not delegates
329: disableDelegation(DELEGATE_SET_VALUE | DELEGATE_GET_VALUE);
330:
331: FileObject pf = ((DataObject) getCookie(DataObject.class))
332: .getPrimaryFile();
333: weakL = new FSL(this );
334: FileStateManager.getDefault().addFileStatusListener(weakL,
335: pf);
336:
337: specialProp(new FileStateProperty(pf,
338: FileStateManager.LAYER_SESSION, PROP_LAYER_SESSION,
339: false));
340: specialProp(new FileStateProperty(pf,
341: FileStateManager.LAYER_MODULES, PROP_LAYER_MODULES,
342: false));
343: }
344:
345: /* @return the display name of the original node
346: */
347: public String getDisplayName() {
348: String retVal = null;
349: DataObject dobj = (DataObject) getCookie(DataObject.class);
350: if (dobj != null && dobj instanceof DataShadow) {
351: DataShadow dsh = (DataShadow) dobj;
352: Node origNode = dsh.getOriginal().getNodeDelegate();
353: if (origNode != null) {
354: retVal = origNode.getDisplayName();
355: }
356: }
357: return (retVal != null) ? retVal : super .getDisplayName();
358: }
359:
360: /** Registers special property.
361: */
362: private void specialProp(Node.Property p) {
363: setValue(p.getName(), p);
364: }
365:
366: // #17920: Index cookie works only when equality works
367: public boolean equals(Object o) {
368: return this == o || getOriginal().equals(o)
369: || (o != null && o.equals(getOriginal()));
370: }
371:
372: public int hashCode() {
373: return getOriginal().hashCode();
374: }
375:
376: // #24766 Exclude Customize Bean action.
377: /** Overrides superclass method, excludes the ToolsAction from the node. */
378: public Action[] getActions(boolean context) {
379: return removeActions(
380: super .getActions(context),
381: new Action[] { SystemAction.get(ToolsAction.class) });
382: }
383:
384: private static class FSL implements
385: FileStateManager.FileStatusListener {
386: WeakReference<SettingFilterNode> node = null;
387:
388: public FSL(SettingFilterNode sfn) {
389: node = new WeakReference<SettingFilterNode>(sfn);
390: }
391:
392: public void fileStatusChanged(FileObject mfo) {
393: SettingFilterNode n = node.get();
394: if (n == null) {
395: FileStateManager.getDefault()
396: .removeFileStatusListener(this, null);
397: return;
398: }
399:
400: n.firePropertyChange(PROP_LAYER_SESSION, null, null);
401: n.firePropertyChange(PROP_LAYER_MODULES, null, null);
402: }
403: }
404: }
405: }
|