| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package org.openide.filesystems; |
| |
| import java.beans.PropertyChangeEvent; |
| import java.beans.PropertyChangeListener; |
| import java.beans.PropertyVetoException; |
| import java.beans.VetoableChangeListener; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.ObjectInput; |
| import java.io.ObjectInputStream; |
| import java.io.ObjectOutput; |
| import java.io.ObjectOutputStream; |
| import java.io.Serializable; |
| import java.lang.ref.Reference; |
| import java.lang.ref.WeakReference; |
| import java.net.URL; |
| import java.util.*; |
| import java.util.concurrent.Callable; |
| import java.util.jar.Manifest; |
| import java.util.logging.Level; |
| import static org.openide.filesystems.FileSystem.LOG; |
| import org.openide.util.Exceptions; |
| import org.openide.util.Lookup; |
| import org.openide.util.LookupEvent; |
| import org.openide.util.LookupListener; |
| import org.openide.util.NbCollections; |
| import org.openide.util.io.NbMarshalledObject; |
| import org.openide.util.lookup.ServiceProvider; |
| import org.openide.util.lookup.ServiceProviders; |
| |
| /** |
| * Holder for NetBeans default (system, configuration) filesystem, used for most |
| * of NetBeans' runtime configuration. The default implementation is a merge |
| * of all of the XML layer files provided by enabled modules, and |
| * the {@code config} subfolder of the userdir on the user's disk. |
| * If you just want to modify configuration data use e.g. |
| * <pre> |
| * FileObject menus = FileUtil.getConfigFile("Menu"); |
| * // ... |
| * </pre> |
| * Formerly (NB 3.x) contained a list of mounted filesystems. This functionality |
| * is no longer used and is now deprecated. |
| */ |
| public class Repository implements Serializable { |
| /** @GuardedBy(Repository.class) */ |
| private static Repository repository; |
| |
| /** Contributes to content of {@link #getDefaultFileSystem() system file system} |
| * (which influences structure under {@link FileUtil#getConfigRoot()}). The |
| * method {@link #registerLayers(java.util.Collection)} |
| * is called during initialization of {@link Repository} and |
| * implementors (registered via {@link ServiceProvider} annotation) may |
| * add their <em>layers</em> (later processed via {@link XMLFileSystem}) into |
| * the general collection of existing providers. |
| * <p class="nonnormative"> |
| * The list of <em>layers</em> as well as their content may be cached. |
| * In a typical NetBeans Platform application, the cache remains until |
| * list of <a href="@org-openide-modules@/org/openide/modules/ModuleInfo.html">modules</a> |
| * and their enabled state remain the same. While it does, the {@link LayerProvider}s |
| * are not queried again. |
| * </p> |
| * <p>You can show a dialog letting the user log in to some server, you can call |
| * {@code LoginProvider.injectLayer(...)} with the URL to an XML layer. |
| * The contents of the layer will become available only after login. |
| * <pre> |
| * {@code @}{@link ServiceProviders}({ |
| * {@code @}{@link ServiceProvider}(service=LoginProvider.class), |
| * {@code @}{@link ServiceProvider}(service=LayerProvider.class) |
| * }) |
| * public final class LoginProvider extends LayerProvider { |
| * private URL layer; |
| * |
| * public void registerLayers(Collection{@code <? super URL>} arr) { |
| * if (layer != null) { |
| * arr.add(layer); |
| * } |
| * } |
| * |
| * public static void injectLayer(URL u) { |
| * LoginProvider lp = {@link Lookup}.getDefault().lookup(LoginProvider.class); |
| * lp.url = u; |
| * lp.refresh(); |
| * } |
| * } |
| * </pre> |
| * The additional layers are inserted below traditional layers provided |
| * by modules in so called {@link FileSystem fallback mode} - e.g. modules |
| * can override and redefine layers contributed by layer providers. |
| * |
| * @since 7.59 |
| */ |
| public static abstract class LayerProvider { |
| /** Allows providers to add their additions to the structure |
| * beneath {@link FileUtil#getConfigRoot()}. The method is |
| * supposed to collect all additional layers and {@link Collection#add(java.lang.Object) add} |
| * them into the <code>context</code> collection. The provided |
| * layers will be processed by {@link XMLFileSystem}-like manner later. |
| * |
| * @param context the context where to register the additions |
| */ |
| protected abstract void registerLayers(Collection<? super URL> context); |
| |
| /** Method to call when set of URLs returned from the {@link #layers()} |
| * method changed and there is a need to refresh it. Refresh is very likely |
| * a time consuming task - consider invoking it on a background thread and |
| * don't hold any locks while calling the method |
| */ |
| protected final void refresh() { |
| Repository.getDefault().refreshAdditionalLayers(); |
| } |
| } |
| |
| /** Methods that tells {@link Repository} subclasses to refresh list of |
| * URLs provided by {@link LayerProvider}s. |
| * @since 7.59 |
| */ |
| protected void refreshAdditionalLayers() { |
| if (getDefaultFileSystem() instanceof MainFS) { |
| ((MainFS)getDefaultFileSystem()).refreshLayers(); |
| } |
| } |
| |
| /** Allows subclasses registered as {@link Repository#getDefault()} to |
| * find out list of URLs for a given provider. The method just calls |
| * {@link LayerProvider#registerLayers(java.util.Collection)}. |
| * |
| * @param p the provider. |
| * @return ordered list of URLs |
| * @since 7.59 |
| */ |
| protected final List<? extends URL> findLayers(LayerProvider p) { |
| if (this != Repository.getDefault()) { |
| return Collections.emptyList(); |
| } |
| List<URL> urls = new ArrayList<URL>(); |
| p.registerLayers(urls); |
| return urls; |
| } |
| |
| /** |
| * Allows subclasses to accept layer contributions from {@link Repository.LayerProvider}s. |
| * Layer XMLs will be collected from locations specified in manifests accessible by the |
| * passed ClassLoader, including generated layers. |
| * Finally {@link LayerProvider}s will be consulted to append their |
| * URLs to the list. If the passed classloader is {@code null}, the classloader which |
| * loaded the Repository class will be used. |
| * @param layerUrls out: collection which receives the URLs. |
| * @throws IOException propagated if some I/O error occurs. |
| * |
| * @since 9.5 |
| */ |
| protected static final void provideLayers(ClassLoader l, List<URL> layerUrls) throws IOException { |
| if (l == null) { |
| l = Repository.class.getClassLoader(); |
| } |
| for (URL manifest : NbCollections.iterable(l.getResources("META-INF/MANIFEST.MF"))) { // NOI18N |
| InputStream is = manifest.openStream(); |
| try { |
| Manifest mani = new Manifest(is); |
| String layerLoc = mani.getMainAttributes().getValue("OpenIDE-Module-Layer"); // NOI18N |
| if (layerLoc != null) { |
| URL layer = l.getResource(layerLoc); |
| if (layer != null) { |
| layerUrls.add(layer); |
| } else { |
| LOG.warning("No such layer: " + layerLoc); |
| } |
| } |
| } finally { |
| is.close(); |
| } |
| } |
| for (URL generatedLayer : NbCollections.iterable(l.getResources("META-INF/generated-layer.xml"))) { // NOI18N |
| layerUrls.add(generatedLayer); |
| } |
| for (LayerProvider p : Lookup.getDefault().lookupAll(LayerProvider.class)) { |
| List<URL> newURLs = new ArrayList<URL>(); |
| p.registerLayers(newURLs); |
| layerUrls.addAll(newURLs); |
| } |
| } |
| |
| private static final class MainFS extends MultiFileSystem implements LookupListener { |
| private static final Lookup.Result<FileSystem> ALL = Lookup.getDefault().lookupResult(FileSystem.class); |
| private static final FileSystem MEMORY = FileUtil.createMemoryFileSystem(); |
| private static final XMLFileSystem layers = new XMLFileSystem(); |
| |
| public MainFS() { |
| ALL.addLookupListener(this); |
| refreshLayers(); |
| } |
| |
| final void refreshLayers() { |
| List<URL> layerUrls = new ArrayList<URL>(); |
| try { |
| provideLayers(Thread.currentThread().getContextClassLoader(), layerUrls); |
| layers.setXmlUrls(layerUrls.toArray(new URL[layerUrls.size()])); |
| LOG.log(Level.FINE, "Loading classpath layers: {0}", layerUrls); |
| } catch (Exception x) { |
| LOG.log(Level.WARNING, "Setting layer URLs: " + layerUrls, x); |
| } |
| resultChanged(null); // run after add listener - see PN1 in #26338 |
| } |
| |
| private static FileSystem[] computeDelegates() { |
| List<FileSystem> arr = new ArrayList<FileSystem>(); |
| arr.add(MEMORY); |
| for (FileSystem f : ALL.allInstances()) { |
| if (Boolean.TRUE.equals(f.getRoot().getAttribute("fallback"))) { // NOI18N |
| continue; |
| } |
| arr.add(f); |
| } |
| arr.add(layers); |
| for (FileSystem f : ALL.allInstances()) { |
| if (Boolean.TRUE.equals(f.getRoot().getAttribute("fallback"))) { // NOI18N |
| arr.add(f); |
| } |
| } |
| return arr.toArray(new FileSystem[0]); |
| } |
| |
| |
| public void resultChanged(LookupEvent ev) { |
| synchronized (Repository.class) { |
| setDelegates(computeDelegates()); |
| } |
| } |
| } // end of MainFS |
| |
| static final long serialVersionUID = -6344768369160069704L; |
| |
| /** list of filesystems (FileSystem) */ |
| private ArrayList<FileSystem> fileSystems; |
| private transient List<FileSystem> fileSystemsClone = Collections.emptyList(); |
| |
| /** the system filesystem */ |
| private FileSystem system; |
| |
| /** hashtable that maps system names to FileSystems */ |
| private Hashtable<String, FileSystem> names; |
| private transient FCLSupport fclSupport; |
| |
| // [PENDING] access to this hashtable is apparently not propertly synched |
| // should use e.g. Collections.synchronizedSet, or just synch methods using it |
| |
| /** hashtable for listeners on changes in the filesystem. |
| * Its elements are of type (RepositoryListener, RepositoryListener) |
| */ |
| private Hashtable<RepositoryListener,RepositoryListener> listeners = |
| new Hashtable<RepositoryListener,RepositoryListener>(); |
| |
| /** vetoable listener on systemName property of filesystem */ |
| private VetoableChangeListener vetoListener = new VetoableChangeListener() { |
| /** @param ev event with changes */ |
| public void vetoableChange(PropertyChangeEvent ev) |
| throws PropertyVetoException { |
| if (ev.getPropertyName().equals("systemName")) { |
| final String ov = (String) ev.getOldValue(); |
| final String nv = (String) ev.getNewValue(); |
| |
| if (names.get(nv) != null) { |
| throw new PropertyVetoException("system name already exists: " + ov + " -> " + nv, ev); // NOI18N |
| } |
| } |
| } |
| }; |
| |
| /** property listener on systemName property of filesystem */ |
| private PropertyChangeListener propListener = new PropertyChangeListener() { |
| /** @param ev event with changes */ |
| public void propertyChange(PropertyChangeEvent ev) { |
| if (ev.getPropertyName().equals("systemName")) { |
| // assign the property to new name |
| String ov = (String) ev.getOldValue(); |
| String nv = (String) ev.getNewValue(); |
| FileSystem fs = (FileSystem) ev.getSource(); |
| |
| if (fs.isValid()) { |
| // when a filesystem is valid then it is attached to a name |
| names.remove(ov); |
| } |
| |
| // register name of the filesystem |
| names.put(nv, fs); |
| |
| // the filesystem becomes valid |
| fs.setValid(true); |
| } |
| } |
| }; |
| |
| /** Creates new instance of filesystem pool and |
| * registers it as the default one. Also registers the default filesystem. |
| * |
| * @param def the default filesystem |
| */ |
| public Repository(FileSystem def) { |
| this.system = def; |
| init(); |
| } |
| |
| /** Initialazes the pool. |
| */ |
| private void init() { |
| // empties the pool |
| fileSystems = new ArrayList<FileSystem>(); |
| names = new Hashtable<String, FileSystem>(); |
| if (addFileSystemDelayed(system)) { |
| addFileSystem(system); |
| } |
| } |
| |
| /** Access method to get default instance of repository in the system. |
| * The instance is either taken as a result of |
| * <CODE>org.openide.util.Lookup.getDefault ().lookup (Repository.class)</CODE> |
| * or (if the lookup query returns null) a default instance is created. |
| * <p/> |
| * In a contextual environment, the method remembers the result of the default Lookup |
| * query, and will return the same instance as a system-wide Repository instance. |
| * Instances provided by <code>Lookup.getDefault().lookup(Repository.class)</code> may vary |
| * depending on the Lookup's implementation and context - be aware that multiple Repository |
| * instances may exist, possibly one for each contextual Lookup craeted. |
| * <p/> |
| * |
| * @return default repository for the system |
| * @since 9.5 support for multiple contexts |
| */ |
| public static Repository getDefault() { |
| final Lookup lkp = Lookup.getDefault(); |
| |
| synchronized (Repository.class) { |
| if (repository != null) { |
| return repository; |
| } |
| } |
| try { |
| Repository newRepo = delayFileSystemAttachImpl(new Callable<Repository>() { |
| public Repository call() throws Exception { |
| Repository r = lkp.lookup(Repository.class); |
| if (r == null) { |
| // if not provided use default one |
| r = new Repository(new MainFS()); |
| } |
| return r; |
| } |
| }); |
| synchronized (Repository.class) { |
| if (repository == null) { |
| repository = newRepo; |
| } |
| return repository; |
| } |
| } catch (IOException ex) { |
| throw new IllegalStateException(ex); |
| } |
| } |
| |
| /** |
| * Last known Lookup instance, for which the {@link #lastLocalProvider} holds a value. |
| */ |
| private static Reference<Lookup> lastDefLookup = new WeakReference<>(null); |
| |
| /** |
| * LocalProvider associated with the {@link #lastDefLookup}. |
| * The Provider is cached rather than the Repository instance, to avoid |
| * several locations where Repository is cached - testsuites may recycle instances |
| * and would have to be updated if another cache is created. |
| */ |
| private static LocalProvider lastLocalProvider = null; |
| |
| /** |
| * Provides a safe initialization for repository in a context Lookup. |
| * Because of delayed FS initialization guarded by {@link #ADD_FS}, it's not safe |
| * to directly lookup Repository.class in the default Lookup; if an instance does not |
| * exist yet and will be created within the lookup() call, it would obscure possible |
| * existing ADD_FS value. In fact, the Repository instance <b>is usually instantiated twice</b>, |
| * because ctor initialization indirectly invokes Repository.getDefault(), resulting in another |
| * initialization. |
| * <p/> |
| * <b>Performance note:</b> during startup, the default Lookup changes frequently because of modules |
| * activations. The <code>lkp.lookup(LocalProvider.class)</code> query blocks on Lookup computation |
| * locks, adding up to 20% to startup time. The last discovered value of LocalProvider is therefore cached |
| * ({@link #lastLocalProvider}) and the default Lookup in effect is remembered in {@link #lastDefLookup}. |
| * If {@code getLocalRepository} is called again with the default Lookup instance unchanged (= the same execution context), |
| * no {@code lookup()} query will be executed and the cached {@code LocalProvider} instance will be |
| * used to return the repository instance. |
| * <p/> |
| * In multi-user/execution environment, this cache is beneficial during system startup; after that, the |
| * default Lookup changes with the execution context frequently, but contents of individual Lookups are rather |
| * stable, so performance should be acceptable. |
| * |
| * @return Repository instance |
| */ |
| /* package private */ static Repository getLocalRepository() { |
| Lookup lkp = Lookup.getDefault(); |
| Reference<Lookup> c; |
| LocalProvider p = null; |
| LocalProvider q; |
| synchronized (Repository.class) { |
| c = lastDefLookup; |
| Lookup l = c.get(); |
| if (lkp == l) { |
| p = lastLocalProvider; |
| } |
| q = p; |
| } |
| if (p == null) { |
| q = lkp.lookup(LocalProvider.class); |
| if (q == null) { |
| q = NO_PROVIDER; |
| } |
| } |
| Repository inst = null; |
| try { |
| inst = q.getRepository(); |
| } catch (IOException ex) { |
| Exceptions.printStackTrace(ex); |
| return null; |
| } |
| if (p == null) { |
| synchronized (Repository.class) { |
| if (lastDefLookup == c) { |
| lastLocalProvider = q; |
| lastDefLookup = new WeakReference(lkp); |
| } |
| } |
| } |
| return inst; |
| } |
| |
| private static final LocalProvider NO_PROVIDER = new LocalProvider() { |
| @Override |
| public Repository getRepository() throws IOException { |
| return getDefault(); |
| } |
| }; |
| |
| |
| static synchronized void reset() { |
| repository = null; |
| lastLocalProvider = null; |
| lastDefLookup = new WeakReference(null); |
| } |
| private static final ThreadLocal<FileSystem[]> ADD_FS = new ThreadLocal<FileSystem[]>(); |
| private static boolean addFileSystemDelayed(FileSystem fs) { |
| FileSystem[] store = ADD_FS.get(); |
| if (store != null) { |
| assert store[0] == null; |
| store[0] = fs; |
| return false; |
| } else { |
| return true; |
| } |
| } |
| |
| private boolean addFileSystemImpl(FileSystem fs) { |
| Thread.holdsLock(this); |
| |
| // if the filesystem is not assigned yet |
| if (!fs.assigned && !fileSystems.contains(fs)) { |
| // new filesystem |
| fs.setRepository(this); |
| fileSystems.add(fs); |
| fileSystemsClone = new ArrayList<FileSystem>(fileSystems); |
| |
| String systemName = fs.getSystemName(); |
| |
| boolean isReg = names.get(systemName) == null; |
| |
| if (isReg && !systemName.equals("")) { // NOI18N |
| |
| // filesystem with the same name is not there => then it is valid |
| names.put(systemName, fs); |
| fs.setValid(true); |
| } else { |
| // there is another filesystem with the same name => it is invalid |
| fs.setValid(false); |
| } |
| |
| // mark the filesystem as being assigned |
| fs.assigned = true; |
| |
| // mark as a listener on changes in the filesystem |
| fs.addPropertyChangeListener(propListener); |
| fs.addVetoableChangeListener(vetoListener); |
| |
| // notify filesystem itself that it has been added |
| fs.addNotify(); |
| |
| // fire info about new filesystem |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| /** |
| * Gets the NetBeans default (system, configuration) filesystem. |
| * @return the default filesystem |
| * @deprecated Please use {@link FileUtil#getConfigFile(String)} or |
| * {@link FileUtil#getConfigRoot()} instead. |
| */ |
| @Deprecated |
| public final FileSystem getDefaultFileSystem() { |
| return system; |
| } |
| |
| /** Adds new filesystem to the pool. |
| * <em>Note</em> that a filesystem cannot be assigned to more than one file |
| * system pool at one time (though currently there is only one pool anyway). |
| * At any given time, no two filesystems in the pool may share the same {@link FileSystem#getSystemName name} |
| * (unless all but one are {@link FileSystem#isValid invalid}). To be sure, that |
| * filesystem was really added in Repository, then test that <code>FileSystem</code> |
| * is valid. |
| * @param fs filesystem to add |
| * @deprecated Please use the <a href="@org-netbeans-api-java-classpath@/org/netbeans/api/java/classpath/ClassPath.html">ClassPath API</a> instead. |
| */ |
| @Deprecated |
| public final void addFileSystem(FileSystem fs) { |
| boolean fireIt; |
| synchronized (this) { |
| fireIt = addFileSystemImpl(fs); |
| } |
| // postponed firing after synchronized block to prevent deadlock |
| if (fireIt) { |
| fireFileSystem(fs, true); |
| } |
| } |
| |
| /** Removes a filesystem from the pool. |
| * @param fs filesystem to remove |
| * @deprecated Please use the <a href="@org-netbeans-api-java-classpath@/org/netbeans/api/java/classpath/ClassPath.html">ClassPath API</a> instead. |
| */ |
| @Deprecated |
| public final void removeFileSystem(FileSystem fs) { |
| boolean fireIt = false; |
| |
| synchronized (this) { |
| if (fs.isDefault()) { |
| return; |
| } |
| |
| if (fireIt = fileSystems.remove(fs)) { |
| fs.setRepository(null); |
| fileSystemsClone = new ArrayList<FileSystem>(fileSystems); |
| |
| // the filesystem realy was here |
| if (fs.isValid()) { |
| // if the filesystem is valid then is in names hashtable |
| names.remove(fs.getSystemName()); |
| fs.setValid(false); |
| } |
| |
| // in all cases remove it from listeners |
| fs.removePropertyChangeListener(propListener); |
| fs.removeVetoableChangeListener(vetoListener); |
| |
| // notify filesystem itself that it has been removed |
| fs.removeNotify(); |
| } |
| |
| // unassign the filesystem |
| fs.assigned = false; |
| } |
| |
| // postponed firing after synchronized block to prevent deadlock |
| if (fireIt) { |
| fireFileSystem(fs, false); |
| } |
| } |
| |
| /** Reorders {@link FileSystem}s by given permutation. |
| * For example, if there are three filesystems, <code>new int[] {2, 0, 1}</code> cycles the filesystems forwards. |
| * @param perm an array of integers |
| * @throws IllegalArgumentException if the array is not a permutation, or is not the same length as the current number of filesystems in the pool |
| * @deprecated Please use the <a href="@org-netbeans-api-java-classpath@/org/netbeans/api/java/classpath/ClassPath.html">ClassPath API</a> instead. |
| */ |
| @Deprecated |
| public final void reorder(int[] perm) { |
| synchronized (this) { |
| if (perm == null) { |
| throw new IllegalArgumentException("null permutation"); // NOI18N |
| } else if (perm.length != fileSystems.size()) { |
| throw new IllegalArgumentException( |
| "permutation is wrong size: " + perm.length + " elements but should be " + fileSystems.size() |
| ); // NOI18N |
| } else if (!isPermutation(perm)) { |
| StringBuffer message = new StringBuffer("permutation is not really a permutation:"); // NOI18N |
| |
| for (int i = 0; i < perm.length; i++) { |
| message.append(' '); |
| message.append(perm[i]); |
| } |
| |
| throw new IllegalArgumentException(message.toString()); |
| } |
| |
| ArrayList<FileSystem> newList = new ArrayList<FileSystem>(fileSystems.size()); |
| int len = perm.length; |
| |
| for (int i = 0; i < len; i++) { |
| newList.add(fileSystems.get(perm[i])); |
| } |
| |
| fileSystems = newList; |
| fileSystemsClone = new ArrayList<FileSystem>(fileSystems); |
| } |
| |
| fireFileSystemReordered(perm); |
| } |
| |
| /** @return true if the parameter describes a permutation */ |
| private static boolean isPermutation(int[] perm) { |
| final int len = perm.length; |
| boolean[] bool = new boolean[len]; |
| |
| try { |
| for (int i = 0; i < len; i++) { |
| if (bool[perm[i]]) { |
| return false; |
| } else { |
| bool[perm[i]] = true; |
| } |
| } |
| |
| return true; |
| } catch (IndexOutOfBoundsException e) { |
| return false; |
| } |
| } |
| |
| /** Returns enumeration of all filesystems. |
| * @return enumeration of type {@link FileSystem} |
| * @deprecated Please use the <a href="@org-netbeans-api-java-classpath@/org/netbeans/api/java/classpath/ClassPath.html">ClassPath API</a> instead. |
| */ |
| @Deprecated |
| public final Enumeration<? extends FileSystem> getFileSystems() { |
| return Collections.enumeration(fileSystemsClone); |
| } |
| |
| /** Returns enumeration of all filesystems. |
| * @return enumeration of type {@link FileSystem} |
| * @deprecated Please use the <a href="@org-netbeans-api-java-classpath@/org/netbeans/api/java/classpath/ClassPath.html">ClassPath API</a> instead. |
| */ |
| @Deprecated |
| public final Enumeration<? extends FileSystem> fileSystems() { |
| return getFileSystems(); |
| } |
| |
| /** |
| * Returns a sorted array of filesystems. |
| * @return a sorted array of filesystems |
| * @deprecated Please use the <a href="@org-netbeans-api-java-classpath@/org/netbeans/api/java/classpath/ClassPath.html">ClassPath API</a> instead. |
| */ |
| @Deprecated |
| public final FileSystem[] toArray() { |
| List<FileSystem> tempFileSystems = fileSystemsClone; |
| |
| FileSystem[] fss = new FileSystem[tempFileSystems.size()]; |
| tempFileSystems.toArray(fss); |
| |
| return fss; |
| } |
| |
| /** Finds filesystem when only its system name is known. |
| * @param systemName {@link FileSystem#getSystemName name} of the filesystem |
| * @return the filesystem or <CODE>null</CODE> if there is no such |
| * filesystem |
| * @deprecated Please use the <a href="@org-netbeans-api-java-classpath@/org/netbeans/api/java/classpath/ClassPath.html">ClassPath API</a> instead. |
| */ |
| @Deprecated |
| public final FileSystem findFileSystem(String systemName) { |
| FileSystem fs = names.get(systemName); |
| |
| return fs; |
| } |
| |
| /** Saves pool to stream by saving all filesystems. |
| * The default (system) filesystem, or any persistent filesystems, are skipped. |
| * |
| * @param oos object output stream |
| * @exception IOException if an error occures |
| * @deprecated Unused. |
| */ |
| @Deprecated |
| public final synchronized void writeExternal(ObjectOutput oos) |
| throws IOException { |
| Iterator iter = fileSystems.iterator(); |
| |
| while (iter.hasNext()) { |
| FileSystem fs = (FileSystem) iter.next(); |
| |
| if (!fs.isDefault()) { |
| oos.writeObject(new NbMarshalledObject(fs)); |
| } |
| } |
| |
| oos.writeObject(null); |
| } |
| |
| /** Reads object from stream. |
| * Reads all filesystems. Persistent and system filesystems are untouched; all others are removed and possibly reread. |
| * @param ois object input stream |
| * @exception IOException if an error occures |
| * @exception ClassNotFoundException if read class is not found |
| * @deprecated Unused. |
| */ |
| @Deprecated |
| public final synchronized void readExternal(ObjectInput ois) |
| throws IOException, ClassNotFoundException { |
| ArrayList<FileSystem> temp = new ArrayList<FileSystem>(10); |
| |
| for (;;) { |
| Object obj = ois.readObject(); |
| |
| if (obj == null) { |
| // all system has been read in |
| break; |
| } |
| |
| FileSystem fs; |
| |
| if (obj instanceof FileSystem) { |
| fs = (FileSystem) obj; |
| } else { |
| try { |
| NbMarshalledObject mar = (NbMarshalledObject) obj; |
| fs = (FileSystem) mar.get(); |
| } catch (IOException ex) { |
| ExternalUtil.exception(ex); |
| fs = null; |
| } catch (ClassNotFoundException ex) { |
| ExternalUtil.exception(ex); |
| fs = null; |
| } |
| } |
| |
| if (fs != null) { |
| // add the new filesystem |
| temp.add(fs); |
| } |
| } |
| |
| Enumeration<? extends FileSystem> ee = getFileSystems(); |
| FileSystem fs; |
| |
| while (ee.hasMoreElements()) { |
| fs = ee.nextElement(); |
| |
| if (!fs.isDefault()) { |
| removeFileSystem(fs); |
| } |
| } |
| |
| // in init assigned is checked and we force 'system' to be added again |
| system.assigned = false; |
| init(); |
| |
| // all is successfuly read |
| for (Iterator iter = temp.iterator(); iter.hasNext();) |
| addFileSystem((FileSystem) iter.next()); |
| } |
| |
| /** Finds file when its name is provided. It scans in the list of |
| * filesystems and asks them for the specified file by a call to |
| * {@link FileSystem#find find}. The first object that is found is returned or <CODE>null</CODE> |
| * if none of the filesystems contain such a file. |
| * |
| * @param aPackage package name where each package is separated by a dot |
| * @param name name of the file (without dots) or <CODE>null</CODE> if |
| * one wants to obtain the name of a package and not a file in it |
| * @param ext extension of the file or <CODE>null</CODE> if one needs |
| * a package and not a file name |
| * |
| * @return {@link FileObject} that represents file with given name or |
| * <CODE>null</CODE> if the file does not exist |
| * @deprecated Please use the <a href="@org-netbeans-api-java-classpath@/org/netbeans/api/java/classpath/ClassPath.html">ClassPath API</a> instead. |
| */ |
| @Deprecated |
| public final FileObject find(String aPackage, String name, String ext) { |
| assert false : "Deprecated."; |
| |
| Enumeration<? extends FileSystem> en = getFileSystems(); |
| |
| while (en.hasMoreElements()) { |
| FileSystem fs = en.nextElement(); |
| FileObject fo = fs.find(aPackage, name, ext); |
| |
| if (fo != null) { |
| // object found |
| return fo; |
| } |
| } |
| |
| return null; |
| } |
| |
| /** Searches for the given resource among all filesystems. |
| * @see FileSystem#findResource |
| * @param name a name of the resource |
| * @return file object or <code>null</code> if the resource can not be found |
| * @deprecated Please use the <a href="@org-netbeans-api-java-classpath@/org/netbeans/api/java/classpath/ClassPath.html">ClassPath API</a> instead. |
| */ |
| @Deprecated |
| public final FileObject findResource(String name) { |
| assert false : "Deprecated."; |
| |
| Enumeration<? extends FileSystem> en = getFileSystems(); |
| |
| while (en.hasMoreElements()) { |
| FileSystem fs = en.nextElement(); |
| FileObject fo = fs.findResource(name); |
| |
| if (fo != null) { |
| // object found |
| return fo; |
| } |
| } |
| |
| return null; |
| } |
| |
| /** Searches for the given resource among all filesystems, returning all matches. |
| * @param name name of the resource |
| * @return enumeration of {@link FileObject}s |
| * @deprecated Please use the <a href="@org-netbeans-api-java-classpath@/org/netbeans/api/java/classpath/ClassPath.html">ClassPath API</a> instead. |
| */ |
| @Deprecated |
| public final Enumeration<? extends FileObject> findAllResources(String name) { |
| assert false : "Deprecated."; |
| |
| Vector<FileObject> v = new Vector<FileObject>(8); |
| Enumeration<? extends FileSystem> en = getFileSystems(); |
| |
| while (en.hasMoreElements()) { |
| FileSystem fs = en.nextElement(); |
| FileObject fo = fs.findResource(name); |
| |
| if (fo != null) { |
| v.addElement(fo); |
| } |
| } |
| |
| return v.elements(); |
| } |
| |
| /** Finds all files among all filesystems matching a given name, returning all matches. |
| * All filesystems are queried with {@link FileSystem#find}. |
| * |
| * @param aPackage package name where each package is separated by a dot |
| * @param name name of the file (without dots) or <CODE>null</CODE> if |
| * one wants to obtain the name of a package and not a file in it |
| * @param ext extension of the file or <CODE>null</CODE> if one needs |
| * a package and not a file name |
| * |
| * @return enumeration of {@link FileObject}s |
| * @deprecated Please use the <a href="@org-netbeans-api-java-classpath@/org/netbeans/api/java/classpath/ClassPath.html">ClassPath API</a> instead. |
| */ |
| @Deprecated |
| public final Enumeration<? extends FileObject> findAll(String aPackage, String name, String ext) { |
| assert false : "Deprecated."; |
| |
| Enumeration<? extends FileSystem> en = getFileSystems(); |
| Vector<FileObject> ret = new Vector<FileObject>(); |
| |
| while (en.hasMoreElements()) { |
| FileSystem fs = (FileSystem) en.nextElement(); |
| FileObject fo = fs.find(aPackage, name, ext); |
| |
| if (fo != null) { |
| ret.addElement(fo); |
| } |
| } |
| |
| return ret.elements(); |
| } |
| |
| /** Fire info about changes in the filesystem pool. |
| * @param fs filesystem |
| * @param add <CODE>true</CODE> if the filesystem is added, |
| * <CODE>false</CODE> if it is removed |
| */ |
| private void fireFileSystem(FileSystem fs, boolean add) { |
| RepositoryEvent ev = new RepositoryEvent(this, fs, add); |
| for (RepositoryListener list : new HashSet<RepositoryListener>(listeners.values())) { |
| if (add) { |
| list.fileSystemAdded(ev); |
| } else { |
| list.fileSystemRemoved(ev); |
| } |
| } |
| } |
| |
| /** Fires info about reordering |
| * @param perm |
| */ |
| private void fireFileSystemReordered(int[] perm) { |
| RepositoryReorderedEvent ev = new RepositoryReorderedEvent(this, perm); |
| for (RepositoryListener list : new HashSet<RepositoryListener>(listeners.values())) { |
| list.fileSystemPoolReordered(ev); |
| } |
| } |
| |
| /** Adds new listener. |
| * @param list the listener |
| * @deprecated Please use the <a href="@org-netbeans-api-java-classpath@/org/netbeans/api/java/classpath/ClassPath.html">ClassPath API</a> instead. |
| */ |
| @Deprecated |
| public final void addRepositoryListener(RepositoryListener list) { |
| listeners.put(list, list); |
| } |
| |
| /** Removes listener. |
| * @param list the listener |
| * @deprecated Please use the <a href="@org-netbeans-api-java-classpath@/org/netbeans/api/java/classpath/ClassPath.html">ClassPath API</a> instead. |
| */ |
| @Deprecated |
| public final void removeRepositoryListener(RepositoryListener list) { |
| listeners.remove(list); |
| } |
| |
| /** Writes the object to the stream. |
| */ |
| private Object writeReplace() { |
| return new Replacer(); |
| } |
| |
| final FCLSupport getFCLSupport() { |
| synchronized (FCLSupport.class) { |
| if (fclSupport == null) { |
| fclSupport = new FCLSupport(); |
| } |
| } |
| |
| return fclSupport; |
| } |
| |
| /** Add new listener to this object. |
| * @param fcl the listener |
| * @since 2.8 |
| * @deprecated useless because there is no filesystem but only |
| * default filesystem in Repository. Add new listener directly to |
| * default filesystem {@link #getDefaultFileSystem}. |
| */ |
| @Deprecated |
| public final void addFileChangeListener(FileChangeListener fcl) { |
| getFCLSupport().addFileChangeListener(fcl); |
| } |
| |
| /** Remove listener from this object. |
| * @param fcl the listener |
| * @since 2.8 |
| * @deprecated useless because there is no filesystem but only |
| * default filesystem in Repository. Add new listener directly to |
| * default filesystem {@link #getDefaultFileSystem}. |
| */ |
| @Deprecated |
| public final void removeFileChangeListener(FileChangeListener fcl) { |
| getFCLSupport().removeFileChangeListener(fcl); |
| } |
| |
| private static class Replacer implements java.io.Serializable { |
| /** serial version UID */ |
| static final long serialVersionUID = -3814531276726840241L; |
| |
| Replacer() { |
| } |
| |
| private void writeObject(ObjectOutputStream oos) |
| throws IOException { |
| getDefault().writeExternal(oos); |
| } |
| |
| private void readObject(ObjectInputStream ois) |
| throws IOException, ClassNotFoundException { |
| getDefault().readExternal(ois); |
| } |
| |
| /** @return the default pool */ |
| public Object readResolve() { |
| return getDefault(); |
| } |
| } |
| |
| private static Repository delayFileSystemAttachImpl(Callable<Repository> init) throws IOException { |
| FileSystem[] previous = ADD_FS.get(); |
| try { |
| FileSystem[] addLater = new FileSystem[1]; |
| ADD_FS.set(addLater); |
| Repository newRepo = init.call(); |
| if (newRepo == null) { |
| return null; |
| } |
| synchronized (newRepo) { |
| if (addLater[0] instanceof FileSystem) { |
| newRepo.addFileSystemImpl(addLater[0]); |
| } |
| } |
| return newRepo; |
| } catch (IOException ex) { |
| throw ex; |
| } catch (Exception ex) { |
| throw new IOException(ex); |
| } finally { |
| ADD_FS.set(previous); |
| } |
| } |
| |
| /** |
| * Provides local repositories, depending on the current execution environment. |
| * The caller may use {@link Lookup#executeWith} to temporarily modify the default |
| * Lookup contents and route Repository requests to a Repository private to some task. The |
| * {@code LocalProvider} is responsible for caching and lifecycle of the Repository instance; |
| * it can expire the Repository if the Provider's execution context terminates. Implementations |
| * must be prepared to handle requests for Repository already shutdown etc. |
| * <p/> |
| * @since 9.5 |
| */ |
| public static abstract class LocalProvider { |
| /** |
| * Returns the local repository instance. The method can return {@code null} to indicate |
| * the main Repository should be used. The method <b>must</b> return the very same instance |
| * of Repository for the whole duration of the execution context. It must return the same |
| * Repository instance even after the execution context shuts down; the contents of Repository and |
| * its FileSystems may be limited in that case. |
| * <p/> |
| * Implementations must be reentrant (a call to {@link #getLocalRepository} can be made during |
| * construction of the Repository instance) and must not recurse infinitely. |
| * |
| * @return local repository instance. |
| */ |
| public abstract Repository getRepository() throws IOException; |
| |
| /** |
| * Allows to delay attaching filesystems to the Repository. For backwards compatibility, the Repository |
| * constructor still takes the default FileSystem instance. However it is better to actually publish the |
| * FileSystem in the repository only after it is registered, and can be obtained using {@link #getDefault()} or |
| * {@link #getLocalRepository()}. |
| * <p/> |
| * This method wraps the Repository creation so that the default FileSystem is attached after the repository |
| * is fully constructed. The `init' callable must create a new Repository instance. |
| * This method will then add the default FS for newly created repository instances. |
| * |
| * @param init Callable which produces a new Repository instance or {@code null} to abort creation |
| * @return new Repository instance or {@code null}, if no instance was created. |
| * @throws IOException if the Callable fails. All exceptions are wrapped into {@link IOException}. |
| */ |
| protected final Repository delayFilesystemAttach(Callable<Repository> init) throws IOException { |
| // just a trampoline to the real implementation |
| return delayFileSystemAttachImpl(init); |
| } |
| } |
| } |