001: /*
002: * The contents of this file are subject to the terms of the Common Development
003: * and Distribution License (the License). You may not use this file except in
004: * compliance with the License.
005: *
006: * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
007: * or http://www.netbeans.org/cddl.txt.
008: *
009: * When distributing Covered Code, include this CDDL Header Notice in each file
010: * and include the License file at http://www.netbeans.org/cddl.txt.
011: * If applicable, add the following below the CDDL Header, with the fields
012: * enclosed by brackets [] replaced by your own identifying information:
013: * "Portions Copyrighted [year] [name of copyright owner]"
014: *
015: * The Original Software is NetBeans. The Initial Developer of the Original
016: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
017: * Microsystems, Inc. All Rights Reserved.
018: */
019:
020: package org.netbeans.modules.compapp.projects.wizard;
021:
022: import java.beans.PropertyChangeEvent;
023: import java.beans.PropertyChangeListener;
024: import java.io.File;
025: import java.text.MessageFormat;
026: import java.util.ArrayList;
027: import java.util.Collections;
028: import java.util.StringTokenizer;
029: import javax.swing.Action;
030: import javax.swing.event.ChangeEvent;
031: import javax.swing.event.ChangeListener;
032: import javax.swing.event.EventListenerList;
033: import org.netbeans.api.project.Project;
034: import org.netbeans.api.project.ProjectUtils;
035: import org.netbeans.api.queries.VisibilityQuery;
036: import org.netbeans.api.project.ProjectInformation;
037: import org.netbeans.api.project.SourceGroup;
038: import org.netbeans.api.project.Sources;
039: import org.netbeans.spi.project.ui.support.CommonProjectActions;
040: import org.openide.ErrorManager;
041: import org.openide.loaders.DataObject;
042: import org.openide.loaders.DataObjectNotFoundException;
043: import org.openide.nodes.FilterNode;
044: import org.openide.nodes.Node;
045: import org.openide.util.Lookup;
046: import org.openide.util.WeakListeners;
047: import org.openide.util.lookup.Lookups;
048: import org.openide.util.lookup.ProxyLookup;
049: import org.openide.filesystems.FileObject;
050: import org.openide.filesystems.FileUtil;
051: import org.openide.loaders.ChangeableDataFilter;
052: import org.openide.loaders.DataFilter;
053: import org.openide.loaders.DataFolder;
054: import org.openide.nodes.NodeNotFoundException;
055: import org.openide.nodes.NodeOp;
056: import org.openide.util.NbBundle;
057:
058: /**
059: * Support for creating logical views.
060: *
061: * @author Tientien Li
062: */
063: public class PhysicalView {
064:
065: public static boolean isProjectDirNode(Node n) {
066: return n instanceof GroupNode && ((GroupNode) n).isProjectDir;
067: }
068:
069: public static Node[] createNodesForProject(Project p) {
070: Sources s = ProjectUtils.getSources(p);
071: SourceGroup[] groups = s
072: .getSourceGroups(NewFileIterator.SOURCE_TYPE_BIZPRO /* Sources.TYPE_GENERIC */);
073: if ((groups == null) || (groups.length < 1)) {
074: groups = s.getSourceGroups(Sources.TYPE_GENERIC);
075: }
076:
077: FileObject projectDirectory = p.getProjectDirectory();
078: SourceGroup projectDirGroup = null;
079:
080: // First find the source group which will represent the project
081: for (int i = 0; i < groups.length; i++) {
082: FileObject groupRoot = groups[i].getRootFolder();
083: if (projectDirectory.equals(groupRoot)
084: || FileUtil.isParentOf(groupRoot, projectDirectory)) {
085: if (projectDirGroup != null) {
086: // more than once => Illegal
087: projectDirGroup = null;
088: break;
089: } else {
090: projectDirGroup = groups[i];
091: }
092: }
093: }
094:
095: if (projectDirGroup == null) {
096: // Illegal project
097: ErrorManager.getDefault().notify(
098: ErrorManager.INFORMATIONAL,
099: new IllegalStateException("Project "
100: + p
101: + // NOI18N
102: "either does not contain it's project directory under the "
103: + // NOI18N
104: "Generic source groups or the project directory is under"
105: + // NOI18N
106: "more than one source group")); // NOI18N
107: return new Node[0];
108: }
109:
110: // Create the nodes
111: ArrayList nodesList = new ArrayList(groups.length);
112: nodesList
113: .add(new GroupNode(p, projectDirGroup, true, DataFolder
114: .findFolder(projectDirGroup.getRootFolder())));
115:
116: for (int i = 0; i < groups.length; i++) {
117:
118: if (groups[i] == projectDirGroup) {
119: continue;
120: }
121:
122: nodesList.add(new GroupNode(p, groups[i], false, DataFolder
123: .findFolder(groups[i].getRootFolder())));
124: }
125:
126: Node nodes[] = new Node[nodesList.size()];
127: nodesList.toArray(nodes);
128: return nodes;
129: }
130:
131: static final class VisibilityQueryDataFilter implements
132: ChangeListener, ChangeableDataFilter {
133:
134: EventListenerList ell = new EventListenerList();
135:
136: public VisibilityQueryDataFilter() {
137: VisibilityQuery.getDefault().addChangeListener(this );
138: }
139:
140: public boolean acceptDataObject(DataObject obj) {
141: FileObject fo = obj.getPrimaryFile();
142: return VisibilityQuery.getDefault().isVisible(fo);
143: }
144:
145: public void stateChanged(ChangeEvent e) {
146: Object[] listeners = ell.getListenerList();
147: ChangeEvent event = null;
148: for (int i = listeners.length - 2; i >= 0; i -= 2) {
149: if (listeners[i] == ChangeListener.class) {
150: if (event == null) {
151: event = new ChangeEvent(this );
152: }
153: ((ChangeListener) listeners[i + 1])
154: .stateChanged(event);
155: }
156: }
157: }
158:
159: public void addChangeListener(ChangeListener listener) {
160: ell.add(ChangeListener.class, listener);
161: }
162:
163: public void removeChangeListener(ChangeListener listener) {
164: ell.remove(ChangeListener.class, listener);
165: }
166:
167: }
168:
169: static final class GroupNode extends FilterNode implements
170: PropertyChangeListener {
171:
172: private static final DataFilter VISIBILITY_QUERY_FILTER = new VisibilityQueryDataFilter();
173:
174: static final String GROUP_NAME_PATTERN = NbBundle.getMessage(
175: PhysicalView.class, "FMT_PhysicalView_GroupName"); // NOI18N
176:
177: private Project project;
178: private ProjectInformation pi;
179: private SourceGroup group;
180: private boolean isProjectDir;
181:
182: public GroupNode(Project project, SourceGroup group,
183: boolean isProjectDir, DataFolder dataFolder) {
184: super (dataFolder.getNodeDelegate(), dataFolder
185: .createNodeChildren(VISIBILITY_QUERY_FILTER),
186: createLookup(project, group, dataFolder));
187:
188: this .project = project;
189: this .pi = ProjectUtils.getInformation(project);
190: this .group = group;
191: this .isProjectDir = isProjectDir;
192: pi.addPropertyChangeListener(WeakListeners.propertyChange(
193: this , pi));
194: group.addPropertyChangeListener(WeakListeners
195: .propertyChange(this , group));
196: }
197:
198: // XXX May need to change icons as well
199:
200: public String getName() {
201: if (isProjectDir) {
202: return pi.getName();
203: } else {
204: return group.getName();
205: }
206: }
207:
208: public String getDisplayName() {
209: if (isProjectDir) {
210: return pi.getDisplayName();
211: } else {
212: return MessageFormat.format(GROUP_NAME_PATTERN,
213: new Object[] { group.getDisplayName(),
214: pi.getDisplayName(),
215: getOriginal().getDisplayName() });
216: }
217: }
218:
219: public String getShortDescription() {
220: FileObject gdir = group.getRootFolder();
221: String dir = FileUtil.getFileDisplayName(gdir);
222: return NbBundle.getMessage(PhysicalView.class,
223: isProjectDir ? "HINT_project" : "HINT_group", // NOI18N
224: dir);
225: }
226:
227: public boolean canRename() {
228: return false;
229: }
230:
231: public boolean canCut() {
232: return false;
233: }
234:
235: public boolean canCopy() {
236: // At least for now.
237: return false;
238: }
239:
240: public boolean canDestroy() {
241: return false;
242: }
243:
244: public Action[] getActions(boolean context) {
245:
246: if (context) {
247: return super .getActions(true);
248: } else {
249: Action[] folderActions = super .getActions(false);
250: Action[] projectActions;
251:
252: if (isProjectDir) {
253: // If this is project dir then the properties action
254: // has to be replaced to invoke project customizer
255: projectActions = new Action[folderActions.length];
256: for (int i = 0; i < folderActions.length; i++) {
257: if (folderActions[i] instanceof org.openide.actions.PropertiesAction) {
258: projectActions[i] = CommonProjectActions
259: .customizeProjectAction();
260: } else {
261: projectActions[i] = folderActions[i];
262: }
263: }
264: } else {
265: projectActions = folderActions;
266: }
267:
268: return projectActions;
269: }
270: }
271:
272: // Private methods -------------------------------------------------
273:
274: public void propertyChange(PropertyChangeEvent evt) {
275: String prop = evt.getPropertyName();
276: if (ProjectInformation.PROP_DISPLAY_NAME.equals(prop)) {
277: fireDisplayNameChange(null, null);
278: } else if (ProjectInformation.PROP_NAME.equals(prop)) {
279: fireNameChange(null, null);
280: } else if (ProjectInformation.PROP_ICON.equals(prop)) {
281: // OK, ignore
282: } else if ("name".equals(prop)) { // NOI18N
283: fireNameChange(null, null);
284: } else if ("displayName".equals(prop)) { // NOI18N
285: fireDisplayNameChange(null, null);
286: } else if ("icon".equals(prop)) { // NOI18N
287: // OK, ignore
288: } else if ("rootFolder".equals(prop)) { // NOI18N
289: // XXX Do something to children and lookup
290: fireNameChange(null, null);
291: fireDisplayNameChange(null, null);
292: fireShortDescriptionChange(null, null);
293: } else {
294: assert false : "Attempt to fire an unsupported property change event from "
295: + pi.getClass().getName() + ": " + prop;
296: }
297: }
298:
299: private static Lookup createLookup(Project p,
300: SourceGroup group, DataFolder dataFolder) {
301: return new ProxyLookup(new Lookup[] {
302: dataFolder.getNodeDelegate().getLookup(),
303: Lookups.fixed(new Object[] { p,
304: new PathFinder(group) }), p.getLookup(), });
305: }
306:
307: }
308:
309: public static class PathFinder {
310:
311: private SourceGroup group;
312:
313: public PathFinder(SourceGroup group) {
314: this .group = group;
315: }
316:
317: public Node findPath(Node root, Object object) {
318:
319: if (!(object instanceof FileObject)) {
320: return null;
321: }
322:
323: FileObject fo = (FileObject) object;
324: FileObject groupRoot = group.getRootFolder();
325: if (FileUtil.isParentOf(groupRoot, fo) /* && group.contains( fo ) */) {
326: // The group contains the object
327:
328: String relPath = FileUtil
329: .getRelativePath(groupRoot, fo);
330:
331: ArrayList path = new ArrayList();
332: StringTokenizer strtok = new StringTokenizer(relPath,
333: "/");
334: while (strtok.hasMoreTokens()) {
335: path.add(strtok.nextToken());
336: }
337:
338: String name = fo.getName();
339:
340: try {
341: DataObject dobj = DataObject.find(fo);
342: name = dobj.getNodeDelegate().getName();
343: } catch (DataObjectNotFoundException e) {
344: }
345:
346: path.set(path.size() - 1, name);
347:
348: try {
349: return NodeOp.findPath(root, Collections
350: .enumeration(path));
351: } catch (NodeNotFoundException e) {
352: return null;
353: }
354: } else if (groupRoot.equals(fo)) {
355: return root;
356: }
357:
358: return null;
359: }
360:
361: }
362:
363: }
|