All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openide.filesystems.URLMapper Maven / Gradle / Ivy

There is a newer version: RELEASE240
Show newest version
/*
 * 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.io.File;
import java.io.FileNotFoundException;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.netbeans.modules.openide.filesystems.DefaultURLMapperProxy;
import org.openide.util.Lookup;
import org.openide.util.LookupEvent;
import org.openide.util.LookupListener;
import org.openide.util.BaseUtilities;

/** Mapper from FileObject -> URL.
 * Should be registered in default lookup. For details see {@link Lookup#getDefault()}.
 * For all methods, if the passed-in file object is the root folder
 * of some filesystem, then it is assumed that any valid file object
 * in that filesystem may also have a URL constructed for it by means
 * of appending the file object's resource path to the URL of the root
 * folder. If this cannot work for all file objects on the filesystem,
 * the root folder must not be assigned a URL of that type. nbfs: URLs
 * of course always work correctly in this regard.
 * @since 2.16
 */
public abstract class URLMapper {
    /**
     * URL which works inside this VM.
     * Not guaranteed to work outside the VM (though it may).
     */
    public static final int INTERNAL = 0;

    /**
     * URL which works inside this machine.
     * Not guaranteed to work from other machines (though it may).
     * 
* Typical protocols used: file for disk files (see {@link File#toURI}); * jar to wrap other URLs (e.g. jar:file:/some/thing.jar!/some/entry). *
*/ public static final int EXTERNAL = 1; /** URL which works from networked machines.*/ public static final int NETWORK = 2; /** results with URLMapper instances*/ private static Lookup.Result result; private static final List CACHE_JUST_COMPUTING = new ArrayList(); private static final ThreadLocal> threadCache = new ThreadLocal>(); static { DefaultURLMapperProxy.setDefault(new DefaultURLMapper()); reset(); } /** Cache of all available URLMapper instances. */ private static List cache; /** Reset cache, for use from unit tests. */ static void reset() { cache = null; result = Lookup.getDefault().lookupResult(URLMapper.class); result.addLookupListener( new LookupListener() { public void resultChanged(LookupEvent ev) { synchronized (URLMapper.class) { cache = null; } } } ); } /** Find a good URL for this file object which works according to type: *
    *
  • inside this VM *
  • inside this machine *
  • from networked machines *
* @return a suitable URL, or (only if not {@link #INTERNAL}) null */ public static URL findURL(FileObject fo, int type) { /** secondly registered URLMappers are asked to resolve URL */ for (URLMapper mapper : getInstances()) { URL retVal = mapper.getURL(fo, type); if (retVal != null) { return retVal; } } // if not resolved yet then internal URL with nbfs protocol is returned // XXX this would be better handled by making DefaultURLMapper just return nbfs for INTERNAL when necessary! if (type == INTERNAL) { return FileURL.encodeFileObject(fo); } return null; } /** Get a good URL for this file object which works according to type: *
    *
  • inside this VM *
  • inside this machine *
  • from networked machines *
* The implementation can't use neither {@link FileUtil#toFile} nor {@link FileUtil#toFileObject} * otherwise StackOverflowError maybe thrown. * @return a suitable URL, or null */ public abstract URL getURL(FileObject fo, int type); /** Find an array of FileObjects for this URL. * Zero or more FOs may be returned. * * For each returned FO, it must be true that FO -> URL gives the * exact URL which was passed in, but depends on appropriate type * findURL(FileObject fo, int type) . * @param url to wanted FileObjects * @return a suitable array of FileObjects, or empty array if not successful * @since 2.22 * @deprecated Use {@link #findFileObject} instead. */ @Deprecated public static FileObject[] findFileObjects(URL url) { Set retSet = new LinkedHashSet(); for (URLMapper mapper: getInstances()) { FileObject[] retVal = mapper.getFileObjects(url); if (retVal != null) { retSet.addAll(Arrays.asList(retVal)); } } return retSet.toArray(new FileObject[retSet.size()]); } /** Find an appropriate instance of FileObject that addresses this URL * * @param url URL to be converted to file object * @return file object corresponding to URL or null if no one was found * @since 4.29 */ public static FileObject findFileObject(URL url) { if (url == null) { throw new NullPointerException("Cannot pass null URL to URLMapper.findFileObject"); // NOI18N } /** first basic implementation */ FileObject[] results = null; Iterator instances = getInstances().iterator(); while (instances.hasNext() && ((results == null) || (results.length == 0))) { URLMapper mapper = instances.next(); results = mapper.getFileObjects(url); } return ((results != null) && (results.length > 0)) ? results[0] : null; } /** * Get an array of FileObjects for this URL. * There is no reason to return array * with size greater than one because method {@link #findFileObject findFileObject} * uses just the first element (subsequent elements will not be accepted anyway). * The implementation cannot use either {@link FileUtil#toFile} nor {@link FileUtil#toFileObject} * or StackOverflowError may be thrown. *

The only reason to return an array here * is for backward compatibility.

* @param url to wanted FileObjects * @return an array of FileObjects with size no greater than one, or null * @since 2.22 */ public abstract FileObject[] getFileObjects(URL url); /** Returns all available instances of URLMapper. * @return list of URLMapper instances */ private static List getInstances() { synchronized (URLMapper.class) { if (cache != null) { if ((cache != CACHE_JUST_COMPUTING) || (threadCache.get() == CACHE_JUST_COMPUTING)) { return cache; } } // Set cache to empty array here to prevent infinite loop. // See issue #41358, #43359 cache = CACHE_JUST_COMPUTING; threadCache.set(CACHE_JUST_COMPUTING); } ArrayList res = null; try { res = new ArrayList(result.allInstances()); { // XXX hack to put default last, since we cannot easily adjust META-INF/services/o.o.f.URLM order to our tastes // (would need to ask *all other* impls to be earlier somewhere) URLMapper def = null; Iterator it = res.iterator(); while (it.hasNext()) { URLMapper m = it.next(); if (m instanceof DefaultURLMapperProxy) { def = m; it.remove(); break; } } if (def != null) { res.add(def); } } return res; } finally { synchronized (URLMapper.class) { if (cache == CACHE_JUST_COMPUTING) { cache = res; } threadCache.set(null); } } } /*** Basic impl. for JarFileSystem, LocalFileSystem, MultiFileSystem */ private static class DefaultURLMapper extends URLMapper { DefaultURLMapper() { } // implements URLMapper.getFileObjects(URL url) public FileObject[] getFileObjects(URL url) { String prot = url.getProtocol(); if (prot.equals(FileURL.PROTOCOL)) { //// NOI18N FileObject retVal = FileURL.decodeURL(url); return (retVal == null) ? null : new FileObject[] { retVal }; } if (prot.equals("jar")) { //// NOI18N return getFileObjectsForJarProtocol(url); } if (prot.equals("file")) { //// NOI18N File f = toFile(url); if (f != null) { FileObject[] foRes = findFileObjectsInRepository(f); if ((foRes != null) && (foRes.length > 0)) { return foRes; } } } return null; } private FileObject[] findFileObjectsInRepository(File f) { if (!f.equals(FileUtil.normalizeFile(f))) { throw new IllegalArgumentException( "Parameter file was not " + // NOI18N "normalized. Was " + f + " instead of " + FileUtil.normalizeFile(f) ); // NOI18N } @SuppressWarnings("deprecation") // keep for backward compatibility w/ NB 3.x Enumeration en = Repository.getDefault().getFileSystems(); LinkedList list = new LinkedList(); String fileName = f.getAbsolutePath(); while (en.hasMoreElements()) { FileSystem fs = en.nextElement(); String rootName = null; FileObject fsRoot = fs.getRoot(); File root = findFileInRepository(fsRoot); if (root == null) { Object rootPath = fsRoot.getAttribute("FileSystem.rootPath"); //NOI18N if ((rootPath != null) && (rootPath instanceof String)) { rootName = (String) rootPath; } else { continue; } } if (rootName == null) { rootName = root.getAbsolutePath(); } /**root is parent of file*/ if (fileName.indexOf(rootName) == 0) { String res = fileName.substring(rootName.length()).replace(File.separatorChar, '/'); FileObject fo = fs.findResource(res); File file2Fo = (fo != null) ? findFileInRepository(fo) : null; if ((fo != null) && (file2Fo != null) && f.equals(file2Fo)) { if (fo.getClass().toString().indexOf("org.netbeans.modules.masterfs.MasterFileObject") != -1) { //NOI18N list.addFirst(fo); } else { list.addLast(fo); } } } } FileObject[] results = new FileObject[list.size()]; list.toArray(results); return results; } // implements URLMapper.getURL(FileObject fo, int type) public URL getURL(FileObject fo, int type) { if (fo == null) { return null; } if (type == NETWORK) { return null; } if (fo instanceof MultiFileObject && (type == INTERNAL)) { // Stick to nbfs protocol, otherwise URL calculations // get messed up. See #39613. return null; } File fFile = findFileInRepository(fo); if (fFile != null) { try { return toURL(fFile, fo); } catch (MalformedURLException mfx) { assert false : mfx; return null; } } URL retURL = null; FileSystem fs = null; try { fs = fo.getFileSystem(); } catch (FileStateInvalidException fsex) { return null; } if (fs instanceof JarFileSystem) { JarFileSystem jfs = (JarFileSystem) fs; File f = jfs.getJarFile(); if (f == null) { return null; } try { // XXX clumsy; see ArchiveURLMapper for possible cleaner style String toReplace = "__EXCLAMATION_REPLACEMENT__";//NOI18N retURL = new URL( "jar:" + BaseUtilities.toURI(new File(f,toReplace + fo.getPath())).toString().replaceFirst("/"+toReplace,"!/") + // NOI18N ((fo.isFolder() && !fo.isRoot()) ? "/" : "") ); // NOI18N } catch (MalformedURLException mfx) { mfx.printStackTrace(); return null; } } else if (fs instanceof XMLFileSystem) { URL retVal = null; try { retVal = ((XMLFileSystem) fs).getURL(fo.getPath()); if (retVal == null) { return null; } if (type == INTERNAL) { return retVal; } boolean isInternal = retVal.getProtocol().startsWith("nbres"); //NOI18N if ((type == EXTERNAL) && !isInternal) { return retVal; } return null; } catch (FileNotFoundException fnx) { return null; } } return retURL; } private static URL toURL(File fFile, FileObject fo) throws MalformedURLException { URL retVal = BaseUtilities.toURI(fFile).toURL(); if (retVal != null && fo.isFolder()) { // #155742,160333 - URL for folder must always end with slash final String urlDef = retVal.toExternalForm(); final String pathSeparator = "/";//NOI18N if (!urlDef.endsWith(pathSeparator)) { retVal = new URL(urlDef + pathSeparator); } } return retVal; } private static File findFileInRepository(FileObject fo) { File f = (File) fo.getAttribute("java.io.File"); // NOI18N return (f != null) ? FileUtil.normalizeFile(f) : null; } private static FileObject[] getFileObjectsForJarProtocol(URL url) { FileObject retVal = null; JarURLParser jarUrlParser = new JarURLParser(url); File file = jarUrlParser.getJarFile(); String entryName = jarUrlParser.getEntryName(); if (file != null) { JarFileSystem fs = findJarFileSystem(file); if (fs != null) { if (entryName == null) { entryName = ""; // #39190: root of JAR } retVal = fs.findResource(entryName); } } return (retVal == null) ? null : new FileObject[] { retVal }; } private static JarFileSystem findJarFileSystem(File jarFile) { JarFileSystem retVal = null; @SuppressWarnings("deprecation") // keep for backward compatibility w/ NB 3.x Enumeration en = Repository.getDefault().getFileSystems(); while (en.hasMoreElements()) { FileSystem fs = en.nextElement(); if (fs instanceof JarFileSystem) { File fsJarFile = ((JarFileSystem) fs).getJarFile(); if (fsJarFile.equals(jarFile)) { retVal = (JarFileSystem) fs; break; } } } return retVal; } private static File toFile(URL u) { if (u == null) { throw new NullPointerException(); } try { URI uri = new URI(u.toExternalForm()); return FileUtil.normalizeFile(BaseUtilities.toFile(uri)); } catch (URISyntaxException use) { // malformed URL return null; } catch (IllegalArgumentException iae) { // not a file: URL return null; } } private static class JarURLParser { private File jarFile; private String entryName; JarURLParser(URL originalURL) { parse(originalURL); } /** copy & pasted from JarURLConnection.parse*/ void parse(URL originalURL) { String spec = originalURL.getFile(); int separator = spec.indexOf('!'); if (separator != -1) { try { jarFile = toFile(new URL(spec.substring(0, separator++))); entryName = null; } catch (MalformedURLException e) { return; } /* if ! is the last letter of the innerURL, entryName is null */ if (++separator != spec.length()) { try { // XXX new URI("substring").getPath() might be better? entryName = URLDecoder.decode(spec.substring(separator, spec.length()),"UTF-8"); } catch (UnsupportedEncodingException ex) { return; } } } } File getJarFile() { return jarFile; } String getEntryName() { return entryName; } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy