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: /*
021: *
022: * Copyright 2005 Sun Microsystems, Inc.
023: *
024: * Licensed under the Apache License, Version 2.0 (the "License");
025: * you may not use this file except in compliance with the License.
026: * You may obtain a copy of the License at
027: *
028: * http://www.apache.org/licenses/LICENSE-2.0
029: *
030: * Unless required by applicable law or agreed to in writing, software
031: * distributed under the License is distributed on an "AS IS" BASIS,
032: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
033: * See the License for the specific language governing permissions and
034: * limitations under the License.
035: *
036: */
037:
038: package org.netbeans.modules.jdbcwizard.wizards;
039:
040: import org.netbeans.api.project.Project;
041: import org.netbeans.api.project.ProjectInformation;
042: import org.netbeans.api.project.ProjectUtils;
043: import org.netbeans.api.project.SourceGroup;
044: import org.netbeans.api.project.Sources;
045: import org.netbeans.api.queries.VisibilityQuery;
046:
047: import org.netbeans.spi.project.ui.support.CommonProjectActions;
048:
049: import org.openide.ErrorManager;
050:
051: import org.openide.filesystems.FileObject;
052: import org.openide.filesystems.FileUtil;
053:
054: import org.openide.loaders.ChangeableDataFilter;
055: import org.openide.loaders.DataFilter;
056: import org.openide.loaders.DataFolder;
057: import org.openide.loaders.DataObject;
058: import org.openide.loaders.DataObjectNotFoundException;
059:
060: import org.openide.nodes.FilterNode;
061: import org.openide.nodes.Node;
062: import org.openide.nodes.NodeNotFoundException;
063: import org.openide.nodes.NodeOp;
064:
065: import org.openide.util.Lookup;
066: import org.openide.util.NbBundle;
067: import org.openide.util.WeakListeners;
068: import org.openide.util.lookup.Lookups;
069: import org.openide.util.lookup.ProxyLookup;
070:
071: import java.beans.PropertyChangeEvent;
072: import java.beans.PropertyChangeListener;
073:
074: import java.text.MessageFormat;
075:
076: import java.util.ArrayList;
077: import java.util.Collections;
078: import java.util.StringTokenizer;
079:
080: import javax.swing.Action;
081: import javax.swing.event.ChangeEvent;
082: import javax.swing.event.ChangeListener;
083: import javax.swing.event.EventListenerList;
084:
085: /**
086: * Support for creating logical views.
087: *
088: * @author
089: */
090: public class PhysicalView {
091: /**
092: * DOCUMENT ME!
093: *
094: * @param n DOCUMENT ME!
095: * @return DOCUMENT ME!
096: */
097: public static boolean isProjectDirNode(Node n) {
098: return n instanceof GroupNode && ((GroupNode) n).isProjectDir;
099: }
100:
101: /**
102: * DOCUMENT ME!
103: *
104: * @param p DOCUMENT ME!
105: * @return DOCUMENT ME!
106: */
107: public static Node[] createNodesForProject(Project p) {
108: Sources s = ProjectUtils.getSources(p);
109: SourceGroup[] groups = s.getSourceGroups(Sources.TYPE_GENERIC);
110:
111: if (groups == null || groups.length < 1) {
112: groups = s.getSourceGroups(Sources.TYPE_GENERIC);
113: }
114:
115: FileObject projectDirectory = p.getProjectDirectory();
116: SourceGroup projectDirGroup = null;
117:
118: // First find the source group which will represent the project
119: for (int i = 0; i < groups.length; i++) {
120: FileObject groupRoot = groups[i].getRootFolder();
121:
122: if (projectDirectory.equals(groupRoot)
123: || FileUtil.isParentOf(groupRoot, projectDirectory)) {
124: if (projectDirGroup != null) {
125: // more than once => Illegal
126: projectDirGroup = null;
127:
128: break;
129: } else {
130: projectDirGroup = groups[i];
131: }
132: }
133: }
134:
135: if (projectDirGroup == null) {
136: // Illegal project
137: ErrorManager.getDefault().notify(
138: ErrorManager.INFORMATIONAL,
139: new IllegalStateException("Project "
140: + p
141: + // NOI18N
142: "either does not contain it's project directory under the "
143: + // NOI18N
144: "Generic source groups or the project directory is under"
145: + // NOI18N
146: "more than one source group")); // NOI18N
147:
148: return new Node[0];
149: }
150:
151: // Create the nodes
152: final ArrayList nodesList = new ArrayList(groups.length);
153: nodesList
154: .add(new GroupNode(p, projectDirGroup, true, DataFolder
155: .findFolder(projectDirGroup.getRootFolder())));
156:
157: for (int i = 0; i < groups.length; i++) {
158: if (groups[i] == projectDirGroup) {
159: continue;
160: }
161:
162: nodesList.add(new GroupNode(p, groups[i], false, DataFolder
163: .findFolder(groups[i].getRootFolder())));
164: }
165:
166: Node[] nodes = new Node[nodesList.size()];
167: nodesList.toArray(nodes);
168:
169: return nodes;
170: }
171:
172: /**
173: * DOCUMENT ME!
174: *
175: * @author
176: * @version
177: */
178: static final class VisibilityQueryDataFilter implements
179: ChangeListener, ChangeableDataFilter {
180: /**
181: * DOCUMENT ME!
182: */
183: EventListenerList ell = new EventListenerList();
184:
185: /**
186: * Creates a new VisibilityQueryDataFilter object.
187: */
188: public VisibilityQueryDataFilter() {
189: VisibilityQuery.getDefault().addChangeListener(this );
190: }
191:
192: /**
193: * DOCUMENT ME!
194: *
195: * @param obj DOCUMENT ME!
196: * @return DOCUMENT ME!
197: */
198: public boolean acceptDataObject(DataObject obj) {
199: FileObject fo = obj.getPrimaryFile();
200:
201: return VisibilityQuery.getDefault().isVisible(fo);
202: }
203:
204: /**
205: * DOCUMENT ME!
206: *
207: * @param e DOCUMENT ME!
208: */
209: public void stateChanged(final ChangeEvent e) {
210: final Object[] listeners = this .ell.getListenerList();
211: ChangeEvent event = null;
212:
213: for (int i = listeners.length - 2; i >= 0; i -= 2) {
214: if (listeners[i] == ChangeListener.class) {
215: if (event == null) {
216: event = new ChangeEvent(this );
217: }
218:
219: ((ChangeListener) listeners[i + 1])
220: .stateChanged(event);
221: }
222: }
223: }
224:
225: /**
226: * DOCUMENT ME!
227: *
228: * @param listener DOCUMENT ME!
229: */
230: public void addChangeListener(final ChangeListener listener) {
231: this .ell.add(ChangeListener.class, listener);
232: }
233:
234: /**
235: * DOCUMENT ME!
236: *
237: * @param listener DOCUMENT ME!
238: */
239: public void removeChangeListener(final ChangeListener listener) {
240: this .ell.remove(ChangeListener.class, listener);
241: }
242: }
243:
244: /**
245: * DOCUMENT ME!
246: *
247: * @author
248: * @version
249: */
250: static final class GroupNode extends FilterNode implements
251: PropertyChangeListener {
252: private static final DataFilter VISIBILITY_QUERY_FILTER = new VisibilityQueryDataFilter();
253:
254: /**
255: * DOCUMENT ME!
256: */
257: static final String GROUP_NAME_PATTERN = NbBundle.getMessage(
258: PhysicalView.class, "FMT_PhysicalView_GroupName"); // NOI18N
259:
260: private Project project;
261:
262: private ProjectInformation pi;
263:
264: private SourceGroup group;
265:
266: private boolean isProjectDir;
267:
268: /**
269: * Creates a new GroupNode object.
270: *
271: * @param project DOCUMENT ME!
272: * @param group DOCUMENT ME!
273: * @param isProjectDir DOCUMENT ME!
274: * @param dataFolder DOCUMENT ME!
275: */
276: public GroupNode(Project project, SourceGroup group,
277: boolean isProjectDir, DataFolder dataFolder) {
278: super (dataFolder.getNodeDelegate(), dataFolder
279: .createNodeChildren(VISIBILITY_QUERY_FILTER),
280: createLookup(project, group, dataFolder));
281:
282: this .project = project;
283: this .pi = ProjectUtils.getInformation(project);
284: this .group = group;
285: this .isProjectDir = isProjectDir;
286: pi.addPropertyChangeListener(WeakListeners.propertyChange(
287: this , pi));
288: group.addPropertyChangeListener(WeakListeners
289: .propertyChange(this , group));
290: }
291:
292: // XXX May need to change icons as well
293: public String getName() {
294: if (this .isProjectDir) {
295: return pi.getName();
296: } else {
297: return group.getName();
298: }
299: }
300:
301: /**
302: * DOCUMENT ME!
303: *
304: * @return DOCUMENT ME!
305: */
306: public String getDisplayName() {
307: if (this .isProjectDir) {
308: return pi.getDisplayName();
309: } else {
310: return MessageFormat.format(
311: GroupNode.GROUP_NAME_PATTERN, new Object[] {
312: group.getDisplayName(),
313: pi.getDisplayName(),
314: getOriginal().getDisplayName() });
315: }
316: }
317:
318: /**
319: * DOCUMENT ME!
320: *
321: * @return DOCUMENT ME!
322: */
323: public String getShortDescription() {
324: FileObject gdir = group.getRootFolder();
325: final String dir = FileUtil.getFileDisplayName(gdir);
326:
327: return NbBundle.getMessage(PhysicalView.class,
328: this .isProjectDir ? "HINT_project" : "HINT_group", // NOI18N
329: dir);
330: }
331:
332: /**
333: * DOCUMENT ME!
334: *
335: * @return DOCUMENT ME!
336: */
337: public boolean canRename() {
338: return false;
339: }
340:
341: /**
342: * DOCUMENT ME!
343: *
344: * @return DOCUMENT ME!
345: */
346: public boolean canCut() {
347: return false;
348: }
349:
350: /**
351: * DOCUMENT ME!
352: *
353: * @return DOCUMENT ME!
354: */
355: public boolean canCopy() {
356: // At least for now.
357: return false;
358: }
359:
360: /**
361: * DOCUMENT ME!
362: *
363: * @return DOCUMENT ME!
364: */
365: public boolean canDestroy() {
366: return false;
367: }
368:
369: /**
370: * DOCUMENT ME!
371: *
372: * @param context DOCUMENT ME!
373: * @return DOCUMENT ME!
374: */
375: public Action[] getActions(final boolean context) {
376: if (context) {
377: return super .getActions(true);
378: } else {
379: final Action[] folderActions = super .getActions(false);
380: Action[] projectActions;
381:
382: if (this .isProjectDir) {
383: // If this is project dir then the properties action
384: // has to be replaced to invoke project customizer
385: projectActions = new Action[folderActions.length];
386:
387: for (int i = 0; i < folderActions.length; i++) {
388: if (folderActions[i] instanceof org.openide.actions.PropertiesAction) {
389: projectActions[i] = CommonProjectActions
390: .customizeProjectAction();
391: } else {
392: projectActions[i] = folderActions[i];
393: }
394: }
395: } else {
396: projectActions = folderActions;
397: }
398:
399: return projectActions;
400: }
401: }
402:
403: // Private methods -------------------------------------------------
404: public void propertyChange(final PropertyChangeEvent evt) {
405: String prop = evt.getPropertyName();
406:
407: if (ProjectInformation.PROP_DISPLAY_NAME.equals(prop)) {
408: fireDisplayNameChange(null, null);
409: } else if (ProjectInformation.PROP_NAME.equals(prop)) {
410: fireNameChange(null, null);
411: } else if (ProjectInformation.PROP_ICON.equals(prop)) {
412: // OK, ignore
413: } else if ("name".equals(prop)) { // NOI18N
414: fireNameChange(null, null);
415: } else if ("displayName".equals(prop)) { // NOI18N
416: fireDisplayNameChange(null, null);
417: } else if ("icon".equals(prop)) { // NOI18N
418:
419: // OK, ignore
420: } else if ("rootFolder".equals(prop)) { // NOI18N
421:
422: // XXX Do something to children and lookup
423: fireNameChange(null, null);
424: fireDisplayNameChange(null, null);
425: fireShortDescriptionChange(null, null);
426: } else {
427: assert false : "Attempt to fire an unsupported property change event from "
428: + pi.getClass().getName() + ": " + prop;
429: }
430: }
431:
432: private static Lookup createLookup(Project p,
433: SourceGroup group, DataFolder dataFolder) {
434: return new ProxyLookup(new Lookup[] {
435: dataFolder.getNodeDelegate().getLookup(),
436: Lookups.fixed(new Object[] { p,
437: new PathFinder(group) }), p.getLookup(), });
438: }
439: }
440:
441: /**
442: * DOCUMENT ME!
443: *
444: * @author
445: * @version
446: */
447: public static class PathFinder {
448: private SourceGroup group;
449:
450: /**
451: * Creates a new PathFinder object.
452: *
453: * @param group DOCUMENT ME!
454: */
455: public PathFinder(SourceGroup group) {
456: this .group = group;
457: }
458:
459: /**
460: * DOCUMENT ME!
461: *
462: * @param root DOCUMENT ME!
463: * @param object DOCUMENT ME!
464: * @return DOCUMENT ME!
465: */
466: public Node findPath(Node root, Object object) {
467: if (!(object instanceof FileObject)) {
468: return null;
469: }
470:
471: FileObject fo = (FileObject) object;
472: FileObject groupRoot = group.getRootFolder();
473:
474: if (FileUtil.isParentOf(groupRoot, fo) /* && group.contains( fo ) */) {
475: // The group contains the object
476: final String relPath = FileUtil.getRelativePath(
477: groupRoot, fo);
478:
479: final ArrayList path = new ArrayList();
480: final StringTokenizer strtok = new StringTokenizer(
481: relPath, "/");
482:
483: while (strtok.hasMoreTokens()) {
484: path.add(strtok.nextToken());
485: }
486:
487: String name = fo.getName();
488:
489: try {
490: DataObject dobj = DataObject.find(fo);
491: name = dobj.getNodeDelegate().getName();
492: } catch (DataObjectNotFoundException e) {
493: }
494:
495: path.set(path.size() - 1, name);
496:
497: try {
498: return NodeOp.findPath(root, Collections
499: .enumeration(path));
500: } catch (NodeNotFoundException e) {
501: return null;
502: }
503: } else if (groupRoot.equals(fo)) {
504: return root;
505: }
506:
507: return null;
508: }
509: }
510: }
|