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.openide.filesystems;
043:
044: import java.beans.PropertyChangeEvent;
045: import java.beans.PropertyChangeListener;
046: import java.beans.PropertyVetoException;
047: import java.beans.VetoableChangeListener;
048: import java.io.IOException;
049: import java.io.ObjectInput;
050: import java.io.ObjectInputStream;
051: import java.io.ObjectOutput;
052: import java.io.ObjectOutputStream;
053: import java.io.Serializable;
054: import java.util.ArrayList;
055: import java.util.Enumeration;
056: import java.util.HashSet;
057: import java.util.Hashtable;
058: import java.util.Iterator;
059: import java.util.Vector;
060: import org.openide.util.io.NbMarshalledObject;
061:
062: /**
063: * Holder for system filesystem, used for most of NetBeans' runtime configuration.
064: * There is only one useful thing to do with this class:
065: * <pre>
066: * FileSystem sfs = Repository.getDefault().getDefaultFileSystem();
067: * // now use somehow, e.g.
068: * FileObject menus = sfs.findResource("Menu");
069: * // ...
070: * </pre>
071: * Formerly (NB 3.x) contained a list of mounted filesystems. This functionality
072: * is no longer used and is now deprecated.
073: */
074: public class Repository implements Serializable {
075: static final long serialVersionUID = -6344768369160069704L;
076:
077: /** list of filesystems (FileSystem) */
078: private ArrayList<FileSystem> fileSystems;
079: private transient ArrayList<FileSystem> fileSystemsClone;
080:
081: /** the system filesystem */
082: private FileSystem system;
083:
084: /** hashtable that maps system names to FileSystems */
085: private Hashtable<String, FileSystem> names;
086: private transient FCLSupport fclSupport;
087:
088: // [PENDING] access to this hashtable is apparently not propertly synched
089: // should use e.g. Collections.synchronizedSet, or just synch methods using it
090:
091: /** hashtable for listeners on changes in the filesystem.
092: * Its elements are of type (RepositoryListener, RepositoryListener)
093: */
094: private Hashtable<RepositoryListener, RepositoryListener> listeners = new Hashtable<RepositoryListener, RepositoryListener>();
095:
096: /** vetoable listener on systemName property of filesystem */
097: private VetoableChangeListener vetoListener = new VetoableChangeListener() {
098: /** @param ev event with changes */
099: public void vetoableChange(PropertyChangeEvent ev)
100: throws PropertyVetoException {
101: if (ev.getPropertyName().equals("systemName")) {
102: final String ov = (String) ev.getOldValue();
103: final String nv = (String) ev.getNewValue();
104:
105: if (names.get(nv) != null) {
106: throw new PropertyVetoException(
107: "system name already exists: " + ov
108: + " -> " + nv, ev); // NOI18N
109: }
110: }
111: }
112: };
113:
114: /** property listener on systemName property of filesystem */
115: private PropertyChangeListener propListener = new PropertyChangeListener() {
116: /** @param ev event with changes */
117: public void propertyChange(PropertyChangeEvent ev) {
118: if (ev.getPropertyName().equals("systemName")) {
119: // assign the property to new name
120: String ov = (String) ev.getOldValue();
121: String nv = (String) ev.getNewValue();
122: FileSystem fs = (FileSystem) ev.getSource();
123:
124: if (fs.isValid()) {
125: // when a filesystem is valid then it is attached to a name
126: names.remove(ov);
127: }
128:
129: // register name of the filesystem
130: names.put(nv, fs);
131:
132: // the filesystem becomes valid
133: fs.setValid(true);
134: }
135: }
136: };
137:
138: /** Creates new instance of filesystem pool and
139: * registers it as the default one. Also registers the default filesystem.
140: *
141: * @param def the default filesystem
142: */
143: public Repository(FileSystem def) {
144: this .system = def;
145: init();
146: }
147:
148: /** Access method to get default instance of repository in the system.
149: * The instance is either taken as a result of
150: * <CODE>org.openide.util.Lookup.getDefault ().lookup (Repository.class)</CODE>
151: * or (if the lookup query returns null) a default instance is created.
152: *
153: * @return default repository for the system
154: */
155: public static Repository getDefault() {
156: return ExternalUtil.getRepository();
157: }
158:
159: /** Initialazes the pool.
160: */
161: private void init() {
162: // empties the pool
163: fileSystems = new ArrayList<FileSystem>();
164: names = new Hashtable<String, FileSystem>();
165: if (ExternalUtil.addFileSystemDelayed(system)) {
166: addFileSystem(system);
167: }
168: }
169:
170: /**
171: * Gets the NetBeans default (system, configuration) filesystem.
172: * @return the default filesystem
173: */
174: public final FileSystem getDefaultFileSystem() {
175: return system;
176: }
177:
178: /** Adds new filesystem to the pool.
179: * <em>Note</em> that a filesystem cannot be assigned to more than one file
180: * system pool at one time (though currently there is only one pool anyway).
181: * At any given time, no two filesystems in the pool may share the same {@link FileSystem#getSystemName name}
182: * (unless all but one are {@link FileSystem#isValid invalid}). To be sure, that
183: * filesystem was really added in Repository, then test that <code>FileSystem</code>
184: * is valid.
185: * @param fs filesystem to add
186: * @deprecated Please use the <a href="@org-netbeans-api-java@/org/netbeans/api/java/classpath/ClassPath.html">ClassPath API</a> instead.
187: */
188: @Deprecated
189: public final void addFileSystem(FileSystem fs) {
190:
191: boolean fireIt = false;
192:
193: synchronized (this ) {
194: // if the filesystem is not assigned yet
195: if (!fs.assigned && !fileSystems.contains(fs)) {
196: // new filesystem
197: fs.setRepository(this );
198: fileSystems.add(fs);
199: fileSystemsClone = new ArrayList<FileSystem>(
200: fileSystems);
201:
202: String systemName = fs.getSystemName();
203:
204: boolean isReg = names.get(systemName) == null;
205:
206: if (isReg && !systemName.equals("")) { // NOI18N
207:
208: // filesystem with the same name is not there => then it is valid
209: names.put(systemName, fs);
210: fs.setValid(true);
211: } else {
212: // there is another filesystem with the same name => it is invalid
213: fs.setValid(false);
214: }
215:
216: // mark the filesystem as being assigned
217: fs.assigned = true;
218:
219: // mark as a listener on changes in the filesystem
220: fs.addPropertyChangeListener(propListener);
221: fs.addVetoableChangeListener(vetoListener);
222:
223: // notify filesystem itself that it has been added
224: fs.addNotify();
225:
226: // fire info about new filesystem
227: fireIt = true;
228: }
229: }
230:
231: // postponed firing after synchronized block to prevent deadlock
232: if (fireIt) {
233: fireFileSystem(fs, true);
234: }
235: }
236:
237: /** Removes a filesystem from the pool.
238: * @param fs filesystem to remove
239: * @deprecated Please use the <a href="@org-netbeans-api-java@/org/netbeans/api/java/classpath/ClassPath.html">ClassPath API</a> instead.
240: */
241: @Deprecated
242: public final void removeFileSystem(FileSystem fs) {
243: boolean fireIt = false;
244:
245: synchronized (this ) {
246: if (fs.isDefault()) {
247: return;
248: }
249:
250: if (fireIt = fileSystems.remove(fs)) {
251: fs.setRepository(null);
252: fileSystemsClone = new ArrayList<FileSystem>(
253: fileSystems);
254:
255: // the filesystem realy was here
256: if (fs.isValid()) {
257: // if the filesystem is valid then is in names hashtable
258: names.remove(fs.getSystemName());
259: fs.setValid(false);
260: }
261:
262: // in all cases remove it from listeners
263: fs.removePropertyChangeListener(propListener);
264: fs.removeVetoableChangeListener(vetoListener);
265:
266: // notify filesystem itself that it has been removed
267: fs.removeNotify();
268: }
269:
270: // unassign the filesystem
271: fs.assigned = false;
272: }
273:
274: // postponed firing after synchronized block to prevent deadlock
275: if (fireIt) {
276: fireFileSystem(fs, false);
277: }
278: }
279:
280: /** Reorders {@link FileSystem}s by given permutation.
281: * For example, if there are three filesystems, <code>new int[] {2, 0, 1}</code> cycles the filesystems forwards.
282: * @param perm an array of integers
283: * @throws IllegalArgumentException if the array is not a permutation, or is not the same length as the current number of filesystems in the pool
284: * @deprecated Please use the <a href="@org-netbeans-api-java@/org/netbeans/api/java/classpath/ClassPath.html">ClassPath API</a> instead.
285: */
286: @Deprecated
287: public final void reorder(int[] perm) {
288: synchronized (this ) {
289: if (perm == null) {
290: throw new IllegalArgumentException("null permutation"); // NOI18N
291: } else if (perm.length != fileSystems.size()) {
292: throw new IllegalArgumentException(
293: "permutation is wrong size: " + perm.length
294: + " elements but should be "
295: + fileSystems.size()); // NOI18N
296: } else if (!isPermutation(perm)) {
297: StringBuffer message = new StringBuffer(
298: "permutation is not really a permutation:"); // NOI18N
299:
300: for (int i = 0; i < perm.length; i++) {
301: message.append(' ');
302: message.append(perm[i]);
303: }
304:
305: throw new IllegalArgumentException(message.toString());
306: }
307:
308: ArrayList<FileSystem> newList = new ArrayList<FileSystem>(
309: fileSystems.size());
310: int len = perm.length;
311:
312: for (int i = 0; i < len; i++) {
313: newList.add(fileSystems.get(perm[i]));
314: }
315:
316: fileSystems = newList;
317: fileSystemsClone = new ArrayList<FileSystem>(fileSystems);
318: }
319:
320: fireFileSystemReordered(perm);
321: }
322:
323: /** @return true if the parameter describes a permutation */
324: private static boolean isPermutation(int[] perm) {
325: final int len = perm.length;
326: boolean[] bool = new boolean[len];
327:
328: try {
329: for (int i = 0; i < len; i++) {
330: if (bool[perm[i]]) {
331: return false;
332: } else {
333: bool[perm[i]] = true;
334: }
335: }
336:
337: return true;
338: } catch (IndexOutOfBoundsException e) {
339: return false;
340: }
341: }
342:
343: /** Returns enumeration of all filesystems.
344: * @return enumeration of type {@link FileSystem}
345: * @deprecated Please use the <a href="@org-netbeans-api-java@/org/netbeans/api/java/classpath/ClassPath.html">ClassPath API</a> instead.
346: */
347: @Deprecated
348: public final Enumeration<? extends FileSystem> getFileSystems() {
349: ArrayList<FileSystem> tempFileSystems = fileSystemsClone;
350:
351: return java.util.Collections.enumeration(tempFileSystems);
352: }
353:
354: /** Returns enumeration of all filesystems.
355: * @return enumeration of type {@link FileSystem}
356: * @deprecated Please use the <a href="@org-netbeans-api-java@/org/netbeans/api/java/classpath/ClassPath.html">ClassPath API</a> instead.
357: */
358: @Deprecated
359: public final Enumeration<? extends FileSystem> fileSystems() {
360: return getFileSystems();
361: }
362:
363: /**
364: * Returns a sorted array of filesystems.
365: * @return a sorted array of filesystems
366: * @deprecated Please use the <a href="@org-netbeans-api-java@/org/netbeans/api/java/classpath/ClassPath.html">ClassPath API</a> instead.
367: */
368: @Deprecated
369: public final FileSystem[] toArray() {
370: ArrayList<FileSystem> tempFileSystems = fileSystemsClone;
371:
372: FileSystem[] fss = new FileSystem[tempFileSystems.size()];
373: tempFileSystems.toArray(fss);
374:
375: return fss;
376: }
377:
378: /** Finds filesystem when only its system name is known.
379: * @param systemName {@link FileSystem#getSystemName name} of the filesystem
380: * @return the filesystem or <CODE>null</CODE> if there is no such
381: * filesystem
382: * @deprecated Please use the <a href="@org-netbeans-api-java@/org/netbeans/api/java/classpath/ClassPath.html">ClassPath API</a> instead.
383: */
384: @Deprecated
385: public final FileSystem findFileSystem(String systemName) {
386: FileSystem fs = names.get(systemName);
387:
388: return fs;
389: }
390:
391: /** Saves pool to stream by saving all filesystems.
392: * The default (system) filesystem, or any persistent filesystems, are skipped.
393: *
394: * @param oos object output stream
395: * @exception IOException if an error occures
396: * @deprecated Unused.
397: */
398: @Deprecated
399: public final synchronized void writeExternal(ObjectOutput oos)
400: throws IOException {
401: Iterator iter = fileSystems.iterator();
402:
403: while (iter.hasNext()) {
404: FileSystem fs = (FileSystem) iter.next();
405:
406: if (!fs.isDefault() && !fs.isPersistent()) {
407: oos.writeObject(new NbMarshalledObject(fs));
408: }
409: }
410:
411: oos.writeObject(null);
412: }
413:
414: /** Reads object from stream.
415: * Reads all filesystems. Persistent and system filesystems are untouched; all others are removed and possibly reread.
416: * @param ois object input stream
417: * @exception IOException if an error occures
418: * @exception ClassNotFoundException if read class is not found
419: * @deprecated Unused.
420: */
421: @Deprecated
422: public final synchronized void readExternal(ObjectInput ois)
423: throws IOException, ClassNotFoundException {
424: ArrayList<FileSystem> temp = new ArrayList<FileSystem>(10);
425:
426: for (;;) {
427: Object obj = ois.readObject();
428:
429: if (obj == null) {
430: // all system has been read in
431: break;
432: }
433:
434: FileSystem fs;
435:
436: if (obj instanceof FileSystem) {
437: fs = (FileSystem) obj;
438: } else {
439: try {
440: NbMarshalledObject mar = (NbMarshalledObject) obj;
441: fs = (FileSystem) mar.get();
442: } catch (IOException ex) {
443: ExternalUtil.exception(ex);
444: fs = null;
445: } catch (ClassNotFoundException ex) {
446: ExternalUtil.exception(ex);
447: fs = null;
448: }
449: }
450:
451: if (fs != null) {
452: // add the new filesystem
453: temp.add(fs);
454: }
455: }
456:
457: Enumeration<? extends FileSystem> ee = getFileSystems();
458: FileSystem fs;
459:
460: while (ee.hasMoreElements()) {
461: fs = ee.nextElement();
462:
463: if (!fs.isPersistent()) {
464: removeFileSystem(fs);
465: }
466: }
467:
468: // in init assigned is checked and we force 'system' to be added again
469: system.assigned = false;
470: init();
471:
472: // all is successfuly read
473: for (Iterator iter = temp.iterator(); iter.hasNext();)
474: addFileSystem((FileSystem) iter.next());
475: }
476:
477: /** Finds file when its name is provided. It scans in the list of
478: * filesystems and asks them for the specified file by a call to
479: * {@link FileSystem#find find}. The first object that is found is returned or <CODE>null</CODE>
480: * if none of the filesystems contain such a file.
481: *
482: * @param aPackage package name where each package is separated by a dot
483: * @param name name of the file (without dots) or <CODE>null</CODE> if
484: * one wants to obtain the name of a package and not a file in it
485: * @param ext extension of the file or <CODE>null</CODE> if one needs
486: * a package and not a file name
487: *
488: * @return {@link FileObject} that represents file with given name or
489: * <CODE>null</CODE> if the file does not exist
490: * @deprecated Please use the <a href="@org-netbeans-api-java@/org/netbeans/api/java/classpath/ClassPath.html">ClassPath API</a> instead.
491: */
492: @Deprecated
493: public final FileObject find(String aPackage, String name,
494: String ext) {
495: assert FileUtil.assertDeprecatedMethod();
496:
497: Enumeration<? extends FileSystem> en = getFileSystems();
498:
499: while (en.hasMoreElements()) {
500: FileSystem fs = en.nextElement();
501: FileObject fo = fs.find(aPackage, name, ext);
502:
503: if (fo != null) {
504: // object found
505: return fo;
506: }
507: }
508:
509: return null;
510: }
511:
512: /** Searches for the given resource among all filesystems.
513: * @see FileSystem#findResource
514: * @param name a name of the resource
515: * @return file object or <code>null</code> if the resource can not be found
516: * @deprecated Please use the <a href="@org-netbeans-api-java@/org/netbeans/api/java/classpath/ClassPath.html">ClassPath API</a> instead.
517: */
518: @Deprecated
519: public final FileObject findResource(String name) {
520: assert FileUtil.assertDeprecatedMethod();
521:
522: Enumeration<? extends FileSystem> en = getFileSystems();
523:
524: while (en.hasMoreElements()) {
525: FileSystem fs = en.nextElement();
526: FileObject fo = fs.findResource(name);
527:
528: if (fo != null) {
529: // object found
530: return fo;
531: }
532: }
533:
534: return null;
535: }
536:
537: /** Searches for the given resource among all filesystems, returning all matches.
538: * @param name name of the resource
539: * @return enumeration of {@link FileObject}s
540: * @deprecated Please use the <a href="@org-netbeans-api-java@/org/netbeans/api/java/classpath/ClassPath.html">ClassPath API</a> instead.
541: */
542: @Deprecated
543: public final Enumeration<? extends FileObject> findAllResources(
544: String name) {
545: assert FileUtil.assertDeprecatedMethod();
546:
547: Vector<FileObject> v = new Vector<FileObject>(8);
548: Enumeration<? extends FileSystem> en = getFileSystems();
549:
550: while (en.hasMoreElements()) {
551: FileSystem fs = en.nextElement();
552: FileObject fo = fs.findResource(name);
553:
554: if (fo != null) {
555: v.addElement(fo);
556: }
557: }
558:
559: return v.elements();
560: }
561:
562: /** Finds all files among all filesystems matching a given name, returning all matches.
563: * All filesystems are queried with {@link FileSystem#find}.
564: *
565: * @param aPackage package name where each package is separated by a dot
566: * @param name name of the file (without dots) or <CODE>null</CODE> if
567: * one wants to obtain the name of a package and not a file in it
568: * @param ext extension of the file or <CODE>null</CODE> if one needs
569: * a package and not a file name
570: *
571: * @return enumeration of {@link FileObject}s
572: * @deprecated Please use the <a href="@org-netbeans-api-java@/org/netbeans/api/java/classpath/ClassPath.html">ClassPath API</a> instead.
573: */
574: @Deprecated
575: public final Enumeration<? extends FileObject> findAll(
576: String aPackage, String name, String ext) {
577: assert FileUtil.assertDeprecatedMethod();
578:
579: Enumeration<? extends FileSystem> en = getFileSystems();
580: Vector<FileObject> ret = new Vector<FileObject>();
581:
582: while (en.hasMoreElements()) {
583: FileSystem fs = (FileSystem) en.nextElement();
584: FileObject fo = fs.find(aPackage, name, ext);
585:
586: if (fo != null) {
587: ret.addElement(fo);
588: }
589: }
590:
591: return ret.elements();
592: }
593:
594: /** Fire info about changes in the filesystem pool.
595: * @param fs filesystem
596: * @param add <CODE>true</CODE> if the filesystem is added,
597: * <CODE>false</CODE> if it is removed
598: */
599: private void fireFileSystem(FileSystem fs, boolean add) {
600: RepositoryEvent ev = new RepositoryEvent(this , fs, add);
601: for (RepositoryListener list : new HashSet<RepositoryListener>(
602: listeners.values())) {
603: if (add) {
604: list.fileSystemAdded(ev);
605: } else {
606: list.fileSystemRemoved(ev);
607: }
608: }
609: }
610:
611: /** Fires info about reordering
612: * @param perm
613: */
614: private void fireFileSystemReordered(int[] perm) {
615: RepositoryReorderedEvent ev = new RepositoryReorderedEvent(
616: this , perm);
617: for (RepositoryListener list : new HashSet<RepositoryListener>(
618: listeners.values())) {
619: list.fileSystemPoolReordered(ev);
620: }
621: }
622:
623: /** Adds new listener.
624: * @param list the listener
625: * @deprecated Please use the <a href="@org-netbeans-api-java@/org/netbeans/api/java/classpath/ClassPath.html">ClassPath API</a> instead.
626: */
627: @Deprecated
628: public final void addRepositoryListener(RepositoryListener list) {
629: listeners.put(list, list);
630: }
631:
632: /** Removes listener.
633: * @param list the listener
634: * @deprecated Please use the <a href="@org-netbeans-api-java@/org/netbeans/api/java/classpath/ClassPath.html">ClassPath API</a> instead.
635: */
636: @Deprecated
637: public final void removeRepositoryListener(RepositoryListener list) {
638: listeners.remove(list);
639: }
640:
641: /** Writes the object to the stream.
642: */
643: private Object writeReplace() {
644: return new Replacer();
645: }
646:
647: final FCLSupport getFCLSupport() {
648: synchronized (FCLSupport.class) {
649: if (fclSupport == null) {
650: fclSupport = new FCLSupport();
651: }
652: }
653:
654: return fclSupport;
655: }
656:
657: /** Add new listener to this object.
658: * @param fcl the listener
659: * @since 2.8
660: * @deprecated useless because there is no filesystem but only
661: * default filesystem in Repository. Add new listener directly to
662: * default filesystem {@link #getDefaultFileSystem}.
663: */
664: @Deprecated
665: public final void addFileChangeListener(FileChangeListener fcl) {
666: getFCLSupport().addFileChangeListener(fcl);
667: }
668:
669: /** Remove listener from this object.
670: * @param fcl the listener
671: * @since 2.8
672: * @deprecated useless because there is no filesystem but only
673: * default filesystem in Repository. Add new listener directly to
674: * default filesystem {@link #getDefaultFileSystem}.
675: */
676: @Deprecated
677: public final void removeFileChangeListener(FileChangeListener fcl) {
678: getFCLSupport().removeFileChangeListener(fcl);
679: }
680:
681: private static class Replacer implements java.io.Serializable {
682: /** serial version UID */
683: static final long serialVersionUID = -3814531276726840241L;
684:
685: Replacer() {
686: }
687:
688: private void writeObject(ObjectOutputStream oos)
689: throws IOException {
690: ExternalUtil.getRepository().writeExternal(oos);
691: }
692:
693: private void readObject(ObjectInputStream ois)
694: throws IOException, ClassNotFoundException {
695: ExternalUtil.getRepository().readExternal(ois);
696: }
697:
698: /** @return the default pool */
699: public Object readResolve() {
700: return ExternalUtil.getRepository();
701: }
702: }
703: }
|