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;
043:
044: import java.beans.PropertyChangeEvent;
045: import java.beans.PropertyChangeListener;
046: import java.io.File;
047: import java.net.MalformedURLException;
048: import java.net.URL;
049: import java.util.ArrayList;
050: import java.util.Collection;
051: import java.util.Enumeration;
052: import java.util.Iterator;
053: import java.util.LinkedHashSet;
054: import java.util.Set;
055: import java.util.logging.Level;
056: import java.util.logging.Logger;
057: import javax.swing.Action;
058: import org.openide.filesystems.FileObject;
059: import org.openide.filesystems.FileStateInvalidException;
060: import org.openide.filesystems.FileSystem;
061: import org.openide.filesystems.Repository;
062: import org.openide.filesystems.RepositoryEvent;
063: import org.openide.filesystems.RepositoryListener;
064: import org.openide.filesystems.RepositoryReorderedEvent;
065: import org.openide.filesystems.URLMapper;
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.loaders.InstanceSupport;
071: import org.openide.loaders.RepositoryNodeFactory;
072: import org.openide.nodes.AbstractNode;
073: import org.openide.nodes.Children;
074: import org.openide.nodes.FilterNode;
075: import org.openide.nodes.Node;
076: import org.openide.util.HelpCtx;
077: import org.openide.util.Lookup;
078: import org.openide.util.NbBundle;
079: import org.openide.util.WeakListeners;
080: import org.openide.util.actions.SystemAction;
081:
082: /** Data system encapsulates logical structure of more file systems.
083: * It also allows filtering of content of DataFolders
084: *
085: * @author Jaroslav Tulach, Petr Hamernik
086: */
087: public final class DataSystem extends AbstractNode implements
088: RepositoryListener {
089: /** default instance */
090: private static DataSystem def;
091:
092: /** the file system pool to work with */
093: private transient Repository fileSystemPool;
094:
095: /** filter for the data system */
096: DataFilter filter;
097:
098: /** Constructor.
099: * @param fsp file system pool
100: * @param filter the filter for filtering files
101: */
102: private DataSystem(Children ch, Repository fsp, DataFilter filter) {
103: super (ch);
104: fileSystemPool = fsp;
105: this .filter = filter;
106: initialize();
107: setIconBaseWithExtension("org/netbeans/core/resources/repository.gif"); // NOI18N
108: setName(NbBundle.getBundle(DataSystem.class).getString(
109: "dataSystemName"));
110: setShortDescription(NbBundle.getBundle(DataSystem.class)
111: .getString("CTL_Repository_Hint"));
112: getCookieSet().add(new InstanceSupport.Instance(fsp));
113: }
114:
115: /** Constructor. Uses default file system pool.
116: * @param filter the filter to use
117: */
118: private DataSystem(Children ch, DataFilter filter) {
119: this (ch, Repository.getDefault(), filter);
120: }
121:
122: public HelpCtx getHelpCtx() {
123: return new HelpCtx(DataSystem.class);
124: }
125:
126: /** Factory for DataSystem instances */
127: public static Node getDataSystem(DataFilter filter) {
128: if (filter == null) {
129: if (def != null) {
130: return def;
131: }
132: return def = new DataSystem(new DSMap(), DataFilter.ALL);
133: } else {
134: return new DataSystem(new DSMap(), filter);
135: }
136: }
137:
138: /** Gets a DataSystem */
139: public static Node getDataSystem() {
140: return getDataSystem(null);
141: }
142:
143: void initialize() {
144: fileSystemPool.addRepositoryListener(WeakListeners.create(
145: RepositoryListener.class, this , fileSystemPool));
146: Enumeration en = fileSystemPool.getFileSystems();
147: while (en.hasMoreElements()) {
148: FileSystem fs = (FileSystem) en.nextElement();
149: fs.addPropertyChangeListener(org.openide.util.WeakListeners
150: .propertyChange((DSMap) getChildren(), fs));
151: }
152: refresh();
153: }
154:
155: /** writes this node to ObjectOutputStream and its display name
156: */
157: public Node.Handle getHandle() {
158: return filter == DataFilter.ALL ? new DSHandle(null)
159: : new DSHandle(filter);
160: }
161:
162: public Action[] getActions(boolean context) {
163: return new Action[] {
164: SystemAction.get(org.openide.actions.FindAction.class),
165: //Problem with ToolsAction as last item and separator. When ToolsAction
166: //is empty separator is displayed as last item.
167: //null,
168: SystemAction.get(org.openide.actions.ToolsAction.class),
169: //SystemAction.get (org.openide.actions.PropertiesAction.class), // #12072
170: //SystemAction.get (org.openide.actions.CustomizeAction.class),
171: };
172: }
173:
174: /** Called when new file system is added to the pool.
175: * @param ev event describing the action
176: */
177: public void fileSystemAdded(RepositoryEvent ev) {
178: ev.getFileSystem().addPropertyChangeListener(
179: org.openide.util.WeakListeners.propertyChange(
180: (DSMap) getChildren(), ev.getFileSystem()));
181: refresh();
182: }
183:
184: /** Called when a file system is deleted from the pool.
185: * @param ev event describing the action
186: */
187: public void fileSystemRemoved(RepositoryEvent ev) {
188: refresh();
189: }
190:
191: /** Called when the fsp is reordered */
192: public void fileSystemPoolReordered(RepositoryReorderedEvent ev) {
193: refresh();
194: }
195:
196: /** Refreshes the pool.
197: */
198: void refresh() {
199: refresh(null);
200: }
201:
202: /** Refreshes the pool.
203: * @param fs file system to remove
204: */
205: void refresh(FileSystem fs) {
206: // XXX hack to show only masterfs and no other filesystems
207: // should later be solved better
208: // XXX should check if fs.root.url.protocol is not 'file' or 'jar', and if so, show it also
209: // (to display network mounts)
210: URLMapper mapper = getMasterFsURLMapper();
211: if (mapper == null) {
212: //original solution based on Repository
213: ((DSMap) getChildren()).refresh(fileSystemPool, fs);
214: } else {
215: ((DSMap) getChildren()).refreshListRoots(mapper);
216: }
217: }
218:
219: private static URLMapper getMasterFsURLMapper() {
220: URLMapper retVal = null;
221: Lookup.Result result = Lookup.getDefault().lookupResult(
222: URLMapper.class);
223: Collection c = result.allInstances();
224: for (Iterator iterator = c.iterator(); iterator.hasNext();) {
225: URLMapper mapper = (URLMapper) iterator.next();
226: if (mapper != null
227: && "org.netbeans.modules.masterfs.MasterURLMapper"
228: .equals(mapper.getClass().getName())) {//NOI18N
229: retVal = mapper;
230: break;
231: }
232: }
233: return retVal;
234: }
235:
236: /** We have customizer */
237: public boolean hasCustomizer() {
238: return true;
239: }
240:
241: /** Children that listens to changes in filesystem pool.
242: */
243: static class DSMap extends Children.Keys implements
244: PropertyChangeListener {
245:
246: public void propertyChange(PropertyChangeEvent ev) {
247: //System.out.println ("Property change"); // NOI18N
248: DataSystem ds = getDS();
249: if (ds == null)
250: return;
251:
252: if ("root".equals(ev.getPropertyName())) {
253: FileSystem fs = (FileSystem) ev.getSource();
254: ds.refresh(fs);
255: ds.refresh();
256: }
257: }
258:
259: /** The node */
260: private DataSystem getDS() {
261: return (DataSystem) getNode();
262: }
263:
264: protected Node[] createNodes(Object key) {
265: DataFolder df = (DataFolder) key;
266: Node n = new FilterNode(df.getNodeDelegate(), df
267: .createNodeChildren(getDS().filter));
268: return new Node[] { n };
269: }
270:
271: /** Refreshes the pool.
272: * @param fileSystemPool the pool
273: * @param fs file system to remove
274: */
275: public void refresh(Repository fileSystemPool, FileSystem fs) {
276: @SuppressWarnings("unchecked")
277: Enumeration<FileSystem> en = (Enumeration<FileSystem>) fileSystemPool
278: .getFileSystems();
279: ArrayList<DataFolder> list = new ArrayList<DataFolder>();
280: while (en.hasMoreElements()) {
281: FileSystem fsystem = en.nextElement();
282: DataObject root = null;
283: try {
284: root = DataObject.find(fsystem.getRoot());
285: } catch (DataObjectNotFoundException e) {
286: Logger.getLogger(DataSystem.class.getName()).log(
287: Level.WARNING, null, e);
288: // root will remain null and will be accepted
289: // (as that seems safer than not accepting it)
290: }
291: if ((root instanceof DataFolder)
292: && getDS().filter.acceptDataObject(root)) {
293: list.add((DataFolder) root);
294: }
295: }
296: setKeys(list);
297: }
298:
299: private void refreshListRoots(URLMapper mapper) {
300: File[] files = File.listRoots();
301: Set<DataFolder> rootSet = new LinkedHashSet<DataFolder>();
302:
303: for (int i = 0; i < files.length; i++) {
304: File file = files[i];
305: FileObject fo = fetchFileObject(file, mapper);
306:
307: if (fo != null) {
308: try {
309: fo = fo.getFileSystem().getRoot();
310: } catch (FileStateInvalidException e) {
311: continue;
312: }
313: DataObject root = null;
314:
315: try {
316: root = DataObject.find(fo);
317: } catch (DataObjectNotFoundException e) {
318: Logger.getLogger(DataSystem.class.getName())
319: .log(Level.WARNING, null, e);
320: }
321: if ((root instanceof DataFolder)
322: && getDS().filter.acceptDataObject(root)) {
323: rootSet.add((DataFolder) root);
324: }
325: }
326: }
327: setKeys(rootSet);
328: }
329:
330: private FileObject fetchFileObject(File file, URLMapper mapper) {
331: /*intentiionally isn't used FileUtil.toFileObject because here can't be
332: called method normalizeFile which causes problems with removeable drives
333: on Windows*/
334: FileObject retVal = null;
335: try {
336: FileObject[] all = mapper.getFileObjects(toUrl(file));//NOI18N
337: if (all != null && all.length > 0) {
338: retVal = all[0];
339: }
340: } catch (MalformedURLException e) {
341: retVal = null;
342: }
343: return retVal;
344: }
345:
346: private URL toUrl(File file) throws MalformedURLException {
347: return (org.openide.util.Utilities.isWindows()) ? new URL(
348: "file:/" + file.getAbsolutePath()) : file.toURI()
349: .toURL();//NOI18N
350: }
351:
352: }
353:
354: /** Serialization. */
355: private static class DSHandle implements Node.Handle {
356: DataFilter filter;
357:
358: static final long serialVersionUID = -2266375092419944364L;
359:
360: public DSHandle(DataFilter f) {
361: filter = f;
362: }
363:
364: public Node getNode() {
365: return getDataSystem(filter);
366: }
367: }
368:
369: /** @deprecated No longer useful in the UI. */
370: public static final class NbRepositoryNodeFactory extends
371: RepositoryNodeFactory {
372:
373: public Node repository(DataFilter f) {
374: return DataSystem.getDataSystem(f == DataFilter.ALL ? null
375: : f);
376: }
377:
378: }
379:
380: }
|