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: // <RAVE> Copy from projects/projectui/src/org/netbeans/modules/project/ui
043: package org.netbeans.modules.visualweb.project.jsf.ui;
044:
045: import java.beans.PropertyChangeEvent;
046: import java.beans.PropertyChangeListener;
047: import java.text.MessageFormat;
048: import java.util.ArrayList;
049: import java.util.Collections;
050: import java.util.StringTokenizer;
051: import javax.swing.Action;
052: import javax.swing.event.ChangeEvent;
053: import javax.swing.event.ChangeListener;
054: import javax.swing.event.EventListenerList;
055: import org.netbeans.api.project.Project;
056: import org.netbeans.api.project.ProjectUtils;
057: import org.netbeans.api.project.ProjectInformation;
058: import org.netbeans.api.project.SourceGroup;
059: import org.netbeans.api.project.Sources;
060: import org.netbeans.api.queries.VisibilityQuery;
061: import org.netbeans.spi.project.ui.support.CommonProjectActions;
062: import org.openide.ErrorManager;
063: import org.openide.filesystems.FileObject;
064: import org.openide.filesystems.FileUtil;
065: import org.openide.loaders.ChangeableDataFilter;
066: import org.openide.loaders.DataFilter;
067: import org.openide.loaders.DataFolder;
068: import org.openide.loaders.DataObject;
069: import org.openide.loaders.DataObjectNotFoundException;
070: import org.openide.nodes.FilterNode;
071: import org.openide.nodes.Node;
072: import org.openide.nodes.NodeNotFoundException;
073: import org.openide.nodes.NodeOp;
074: import org.openide.util.NbBundle;
075: import org.openide.util.Lookup;
076: import org.openide.util.WeakListeners;
077: import org.openide.util.lookup.Lookups;
078: import org.openide.util.lookup.ProxyLookup;
079:
080: /**
081: * Support for creating logical views.
082: * @author Jesse Glick, Petr Hrebejk
083: */
084: public class PhysicalView {
085:
086: public static boolean isProjectDirNode(Node n) {
087: return n instanceof GroupNode && ((GroupNode) n).isProjectDir;
088: }
089:
090: public static Node[] createNodesForProject(Project p) {
091: Sources s = ProjectUtils.getSources(p);
092: SourceGroup[] groups = s.getSourceGroups(Sources.TYPE_GENERIC);
093:
094: FileObject projectDirectory = p.getProjectDirectory();
095: SourceGroup projectDirGroup = null;
096:
097: // First find the source group which will represent the project
098: for (int i = 0; i < groups.length; i++) {
099: FileObject groupRoot = groups[i].getRootFolder();
100: if (projectDirectory.equals(groupRoot)
101: || FileUtil.isParentOf(groupRoot, projectDirectory)) {
102: if (projectDirGroup != null) {
103: // more than once => Illegal
104: projectDirGroup = null;
105: break;
106: } else {
107: projectDirGroup = groups[i];
108: }
109: }
110: }
111:
112: if (projectDirGroup == null) {
113: // Illegal project
114: ErrorManager.getDefault().log(
115: ErrorManager.WARNING,
116: "Project "
117: + p
118: + // NOI18N
119: "either does not contain it's project directory under the "
120: + // NOI18N
121: "Generic source groups or the project directory is under "
122: + // NOI18N
123: "more than one source group"); // NOI18N
124: return new Node[0];
125: }
126:
127: // Create the nodes
128: ArrayList nodesList = new ArrayList(groups.length);
129: nodesList
130: .add(/*new GroupContainmentFilterNode(*/new GroupNode(
131: p, projectDirGroup, true, DataFolder
132: .findFolder(projectDirGroup
133: .getRootFolder()))/*, projectDirGroup)*/);
134:
135: for (int i = 0; i < groups.length; i++) {
136:
137: if (groups[i] == projectDirGroup) {
138: continue;
139: }
140:
141: nodesList
142: .add(/*new GroupContainmentFilterNode(*/new GroupNode(
143: p, groups[i], false, DataFolder
144: .findFolder(groups[i]
145: .getRootFolder()))/*, groups[i])*/);
146: }
147:
148: Node nodes[] = new Node[nodesList.size()];
149: nodesList.toArray(nodes);
150: return nodes;
151: }
152:
153: static final class VisibilityQueryDataFilter implements
154: ChangeListener, ChangeableDataFilter {
155:
156: EventListenerList ell = new EventListenerList();
157:
158: public VisibilityQueryDataFilter() {
159: VisibilityQuery.getDefault().addChangeListener(this );
160: }
161:
162: public boolean acceptDataObject(DataObject obj) {
163: FileObject fo = obj.getPrimaryFile();
164: return VisibilityQuery.getDefault().isVisible(fo);
165: }
166:
167: public void stateChanged(ChangeEvent e) {
168: Object[] listeners = ell.getListenerList();
169: ChangeEvent event = null;
170: for (int i = listeners.length - 2; i >= 0; i -= 2) {
171: if (listeners[i] == ChangeListener.class) {
172: if (event == null) {
173: event = new ChangeEvent(this );
174: }
175: ((ChangeListener) listeners[i + 1])
176: .stateChanged(event);
177: }
178: }
179: }
180:
181: public void addChangeListener(ChangeListener listener) {
182: ell.add(ChangeListener.class, listener);
183: }
184:
185: public void removeChangeListener(ChangeListener listener) {
186: ell.remove(ChangeListener.class, listener);
187: }
188:
189: }
190:
191: static final class GroupNode extends FilterNode implements
192: PropertyChangeListener {
193:
194: private static final DataFilter VISIBILITY_QUERY_FILTER = new VisibilityQueryDataFilter();
195:
196: static final String GROUP_NAME_PATTERN = NbBundle.getMessage(
197: PhysicalView.class, "FMT_PhysicalView_GroupName"); // NOI18N
198:
199: private Project project;
200: private ProjectInformation pi;
201: private SourceGroup group;
202: private boolean isProjectDir;
203:
204: public GroupNode(Project project, SourceGroup group,
205: boolean isProjectDir, DataFolder dataFolder) {
206: super (dataFolder.getNodeDelegate(), dataFolder
207: .createNodeChildren(VISIBILITY_QUERY_FILTER),
208: createLookup(project, group, dataFolder));
209:
210: this .project = project;
211: this .pi = ProjectUtils.getInformation(project);
212: this .group = group;
213: this .isProjectDir = isProjectDir;
214: pi.addPropertyChangeListener(WeakListeners.propertyChange(
215: this , pi));
216: group.addPropertyChangeListener(WeakListeners
217: .propertyChange(this , group));
218: }
219:
220: // XXX May need to change icons as well
221:
222: public String getName() {
223: if (isProjectDir) {
224: return pi.getName();
225: } else {
226: String n = group.getName();
227: if (n == null) {
228: n = "???"; // NOI18N
229: ErrorManager
230: .getDefault()
231: .log(
232: ErrorManager.WARNING,
233: "SourceGroup impl of type "
234: + group.getClass()
235: .getName()
236: + " specified a null getName(); this is illegal");
237: }
238: return n;
239: }
240: }
241:
242: public String getDisplayName() {
243: if (isProjectDir) {
244: return pi.getDisplayName();
245: } else {
246: return MessageFormat.format(GROUP_NAME_PATTERN,
247: new Object[] { group.getDisplayName(),
248: pi.getDisplayName(),
249: getOriginal().getDisplayName() });
250: }
251: }
252:
253: public String getShortDescription() {
254: FileObject gdir = group.getRootFolder();
255: String dir = FileUtil.getFileDisplayName(gdir);
256: return NbBundle.getMessage(PhysicalView.class,
257: isProjectDir ? "HINT_project" : "HINT_group", // NOI18N
258: dir);
259: }
260:
261: public boolean canRename() {
262: return false;
263: }
264:
265: public boolean canCut() {
266: return false;
267: }
268:
269: public boolean canCopy() {
270: // At least for now.
271: return false;
272: }
273:
274: public boolean canDestroy() {
275: return false;
276: }
277:
278: public Action[] getActions(boolean context) {
279:
280: if (context) {
281: return super .getActions(true);
282: } else {
283: Action[] folderActions = super .getActions(false);
284: Action[] projectActions;
285:
286: if (isProjectDir) {
287: // If this is project dir then the properties action
288: // has to be replaced to invoke project customizer
289: projectActions = new Action[folderActions.length];
290: for (int i = 0; i < folderActions.length; i++) {
291: if (folderActions[i] instanceof org.openide.actions.PropertiesAction) {
292: projectActions[i] = CommonProjectActions
293: .customizeProjectAction();
294: } else {
295: projectActions[i] = folderActions[i];
296: }
297: }
298: } else {
299: projectActions = folderActions;
300: }
301:
302: return projectActions;
303: }
304: }
305:
306: // Private methods -------------------------------------------------
307:
308: public void propertyChange(PropertyChangeEvent evt) {
309: String prop = evt.getPropertyName();
310: if (ProjectInformation.PROP_DISPLAY_NAME.equals(prop)) {
311: fireDisplayNameChange(null, null);
312: } else if (ProjectInformation.PROP_NAME.equals(prop)) {
313: fireNameChange(null, null);
314: } else if (ProjectInformation.PROP_ICON.equals(prop)) {
315: // OK, ignore
316: } else if ("name".equals(prop)) { // NOI18N
317: fireNameChange(null, null);
318: } else if ("displayName".equals(prop)) { // NOI18N
319: fireDisplayNameChange(null, null);
320: } else if ("icon".equals(prop)) { // NOI18N
321: // OK, ignore
322: } else if ("rootFolder".equals(prop)) { // NOI18N
323: // XXX Do something to children and lookup
324: fireNameChange(null, null);
325: fireDisplayNameChange(null, null);
326: fireShortDescriptionChange(null, null);
327: } else {
328: assert false : "Attempt to fire an unsupported property change event from "
329: + pi.getClass().getName() + ": " + prop;
330: }
331: }
332:
333: private static Lookup createLookup(Project p,
334: SourceGroup group, DataFolder dataFolder) {
335: return new ProxyLookup(new Lookup[] {
336: dataFolder.getNodeDelegate().getLookup(),
337: Lookups.fixed(new Object[] { p,
338: new PathFinder(group) }), p.getLookup(), });
339: }
340:
341: }
342:
343: /* XXX disabled for now pending resolution of interaction with planned VCS annotations (color only):
344: /**
345: * Specially displays nodes corresponding to files which are not contained in this source group.
346: * /
347: private static final class GroupContainmentFilterNode extends FilterNode {
348:
349: private final SourceGroup g;
350:
351: public GroupContainmentFilterNode(Node orig, SourceGroup g) {
352: super(orig, orig.isLeaf() ? Children.LEAF : new GroupContainmentFilterChildren(orig, g));
353: this.g = g;
354: }
355:
356: public String getHtmlDisplayName() {
357: Node orig = getOriginal();
358: DataObject d = (DataObject) orig.getCookie(DataObject.class);
359: assert d != null : orig;
360: FileObject f = d.getPrimaryFile();
361: String barename = orig.getHtmlDisplayName();
362: if (!FileUtil.isParentOf(g.getRootFolder(), f) || g.contains(f)) {
363: // Leave it alone.
364: return barename;
365: }
366: // Try to grey it out.
367: if (barename == null) {
368: try {
369: barename = XMLUtil.toElementContent(orig.getDisplayName());
370: } catch (CharConversionException e) {
371: // Never mind.
372: return null;
373: }
374: }
375: return "<font color='!Label.disabledForeground'>" + barename + "</font>"; // NOI18N
376: }
377:
378: private static final class GroupContainmentFilterChildren extends FilterNode.Children {
379:
380: private final SourceGroup g;
381:
382: public GroupContainmentFilterChildren(Node orig, SourceGroup g) {
383: super(orig);
384: this.g = g;
385: }
386:
387: protected Node copyNode(Node node) {
388: if (original.getCookie(DataFolder.class) != null && node.getCookie(DataObject.class) != null) {
389: return new GroupContainmentFilterNode(node, g);
390: } else {
391: return super.copyNode(node);
392: }
393: }
394:
395: }
396:
397: }
398: */
399:
400: public static class PathFinder {
401:
402: private SourceGroup group;
403:
404: public PathFinder(SourceGroup group) {
405: this .group = group;
406: }
407:
408: public Node findPath(Node root, Object object) {
409:
410: if (!(object instanceof FileObject)) {
411: return null;
412: }
413:
414: FileObject fo = (FileObject) object;
415: FileObject groupRoot = group.getRootFolder();
416: if (FileUtil.isParentOf(groupRoot, fo) /* && group.contains( fo ) */) {
417: // The group contains the object
418:
419: String relPath = FileUtil
420: .getRelativePath(groupRoot, fo);
421:
422: ArrayList path = new ArrayList();
423: StringTokenizer strtok = new StringTokenizer(relPath,
424: "/");
425: while (strtok.hasMoreTokens()) {
426: path.add(strtok.nextToken());
427: }
428:
429: String name = fo.getName();
430:
431: try {
432: DataObject dobj = DataObject.find(fo);
433: name = dobj.getNodeDelegate().getName();
434: } catch (DataObjectNotFoundException e) {
435: }
436:
437: path.set(path.size() - 1, name);
438:
439: try {
440: return NodeOp.findPath(root, Collections
441: .enumeration(path));
442: } catch (NodeNotFoundException e) {
443: return null;
444: }
445: } else if (groupRoot.equals(fo)) {
446: return root;
447: }
448:
449: return null;
450: }
451:
452: }
453:
454: }
|