diff --git a/openide.filesystems/apichanges.xml b/openide.filesystems/apichanges.xml --- a/openide.filesystems/apichanges.xml +++ b/openide.filesystems/apichanges.xml @@ -46,6 +46,27 @@ Filesystems API + + + Persisted registration of file extension to MIME type + + + + + +

+ Method + FileUtil.setMIMEType(String extension, String mimeType) + resurrected to register file extension for specified MIME type. + It is persisted in userdir contrary to previous implementation. + Added method + FileUtil.getMIMETypeExtensions(String mimeType) + to get list of file extensions associated with specified MIME type. +

+
+ + +
Added new elements to MIME resolver DTD diff --git a/openide.filesystems/nbproject/project.properties b/openide.filesystems/nbproject/project.properties --- a/openide.filesystems/nbproject/project.properties +++ b/openide.filesystems/nbproject/project.properties @@ -44,4 +44,4 @@ javadoc.main.page=org/openide/filesystems/doc-files/api.html javadoc.arch=${basedir}/arch.xml javadoc.apichanges=${basedir}/apichanges.xml -spec.version.base=7.16.0 +spec.version.base=7.17.0 diff --git a/openide.filesystems/src/org/netbeans/modules/openide/filesystems/declmime/MIMEResolverImpl.java b/openide.filesystems/src/org/netbeans/modules/openide/filesystems/declmime/MIMEResolverImpl.java --- a/openide.filesystems/src/org/netbeans/modules/openide/filesystems/declmime/MIMEResolverImpl.java +++ b/openide.filesystems/src/org/netbeans/modules/openide/filesystems/declmime/MIMEResolverImpl.java @@ -44,21 +44,30 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.TreeMap; import java.util.logging.Level; import java.util.logging.Logger; import org.openide.filesystems.FileChangeAdapter; import org.openide.filesystems.FileChangeListener; import org.openide.filesystems.FileEvent; import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileSystem; import org.openide.filesystems.FileUtil; import org.openide.filesystems.MIMEResolver; +import org.openide.filesystems.Repository; +import org.openide.util.Parameters; import org.openide.util.Utilities; import org.openide.xml.XMLUtil; +import org.w3c.dom.Document; +import org.w3c.dom.Element; import org.xml.sax.Attributes; import org.xml.sax.SAXException; @@ -80,7 +89,12 @@ private static final int READ_LIMIT = 4000; private static Set readLimitReported = new HashSet(); - + // constants for user defined declarative MIME resolver + private static final String MIME_RESOLVERS_PATH = "Services/MIMEResolver"; //NOI18N + private static final String USER_DEFINED_MIME_RESOLVER = "user-defined-mime-resolver"; //NOI18N + /** Position of user-defined mime resolver. Need to very low to override all other resolvers. */ + private static final int USER_DEFINED_MIME_RESOLVER_POSITION = 10; + public static MIMEResolver forDescriptor(FileObject fo) { return new Impl(fo); } @@ -94,43 +108,168 @@ public static String[] getMIMETypes(MIMEResolver resolver) { return ((Impl)resolver).implResolvableMIMETypes; } - - /** Returns list of extension and MIME type pairs for given MIMEResolver - * FileObject. The list can contain duplicates and also [null, MIME] pairs. + /** Check whether given resolver's FileObject is user defined. + * @param mimeResolverFO resolver's FileObject + * @return true if specified FileObject is user defined MIME resolver, false otherwise + */ + public static boolean isUserDefined(FileObject mimeResolverFO) { + return mimeResolverFO.getAttribute(USER_DEFINED_MIME_RESOLVER) != null; + } + + /** Returns mapping of MIME type to set of extensions. It never returns null, + * it can return empty set of extensions. * @param fo MIMEResolver FileObject - * @return list of extension and MIME type pairs. The list can contain - * duplicates and also [null, MIME] pairs. + * @return mapping of MIME type to set of extensions like + * {@literal {image/jpeg=[jpg, jpeg], image/gif=[]}}. */ - public static List getExtensionsAndMIMETypes(FileObject fo) { - assert fo.getPath().startsWith("Services/MIMEResolver"); //NOI18N + public static Map> getMIMEToExtensions(FileObject fo) { if (!fo.hasExt("xml")) { // NOI18N - return Collections.emptyList(); + return Collections.emptyMap(); } - ArrayList result = new ArrayList(); + Map> result = new HashMap>(); Impl impl = new Impl(fo); impl.parseDesc(); FileElement[] elements = impl.smell; - if(elements != null) { + if (elements != null) { for (FileElement fileElement : elements) { String mimeType = fileElement.getMimeType(); if (mimeType != null) { // can be null if element is used String[] extensions = fileElement.getExtensions(); + Set extensionsSet = new HashSet(); if (extensions != null) { for (String extension : extensions) { if (extension.length() > 0) { // ignore empty extension - result.add(new String[] {extension, mimeType}); + extensionsSet.add(extension); } } - } else { - result.add(new String[] {null, mimeType}); } + Set previous = result.get(mimeType); + if (previous != null) { + extensionsSet.addAll(previous); + } + result.put(mimeType, extensionsSet); } } } return result; } - + + /** Returns FileObject representing declarative user defined MIME resolver + * or null if not yet created. + * @return FileObject representing declarative user defined MIME resolver + * or null if not yet created. + */ + public static FileObject getUserDefinedResolver() { + FileObject resolversFolder = Repository.getDefault().getDefaultFileSystem().findResource(MIME_RESOLVERS_PATH); + if (resolversFolder != null) { + FileObject[] resolvers = resolversFolder.getChildren(); + for (FileObject resolverFO : resolvers) { + if (resolverFO.getAttribute(USER_DEFINED_MIME_RESOLVER) != null) { + return resolverFO; + } + } + } + return null; + } + + /** Stores declarative resolver corresponding to specified mapping of MIME type + * and set of extensions. This resolver has the highest priority. Usually + * it resides in userdir/config/Servicer/MIMEResolver. + * @param mimeToExtensions mapping of MIME type to set of extensions like + * {@literal {image/jpeg=[jpg, jpeg], image/gif=[]}}. + */ + public static void storeUserDefinedResolver(final Map> mimeToExtensions) { + Parameters.notNull("mimeToExtensions", mimeToExtensions); //NOI18N + FileObject userDefinedResolverFO = getUserDefinedResolver(); + if (userDefinedResolverFO != null) { + try { + // delete previous resolver because we need to refresh MIMEResolvers + userDefinedResolverFO.delete(); + } catch (IOException e) { + ERR.log(Level.SEVERE, "Cannot delete resolver " + FileUtil.toFile(userDefinedResolverFO), e); //NOI18N + return; + } + } + if (mimeToExtensions.isEmpty()) { + // nothing to write + return; + } + FileUtil.runAtomicAction(new Runnable() { + + public void run() { + Document document = XMLUtil.createDocument("MIME-resolver", null, "-//NetBeans//DTD MIME Resolver 1.1//EN", "http://www.netbeans.org/dtds/mime-resolver-1_1.dtd"); //NOI18N + for (String mimeType : mimeToExtensions.keySet()) { + Set extensions = mimeToExtensions.get(mimeType); + if (!extensions.isEmpty()) { + Element fileElement = document.createElement("file"); //NOI18N + for (String extension : mimeToExtensions.get(mimeType)) { + Element extElement = document.createElement("ext"); //NOI18N + extElement.setAttribute("name", extension); //NOI18N + fileElement.appendChild(extElement); + } + Element resolverElement = document.createElement("resolver"); //NOI18N + resolverElement.setAttribute("mime", mimeType); //NOI18N + fileElement.appendChild(resolverElement); + document.getDocumentElement().appendChild(fileElement); + } + } + if (!document.getDocumentElement().hasChildNodes()) { + // nothing to write + return; + } + OutputStream os = null; + FileObject userDefinedResolverFO = null; + try { + FileSystem defaultFS = Repository.getDefault().getDefaultFileSystem(); + FileObject resolversFolder = defaultFS.findResource(MIME_RESOLVERS_PATH); + if (resolversFolder == null) { + resolversFolder = FileUtil.createFolder(defaultFS.getRoot(), MIME_RESOLVERS_PATH); + } + userDefinedResolverFO = resolversFolder.createData(USER_DEFINED_MIME_RESOLVER, "xml"); //NOI18N + userDefinedResolverFO.setAttribute(USER_DEFINED_MIME_RESOLVER, Boolean.TRUE); + userDefinedResolverFO.setAttribute("position", USER_DEFINED_MIME_RESOLVER_POSITION); //NOI18N + os = userDefinedResolverFO.getOutputStream(); + XMLUtil.write(document, os, "UTF-8"); //NOI18N + } catch (IOException e) { + ERR.log(Level.SEVERE, "Cannot write resolver " + (userDefinedResolverFO == null ? "" : FileUtil.toFile(userDefinedResolverFO)), e); //NOI18N + } finally { + if (os != null) { + try { + os.close(); + } catch (IOException e) { + ERR.log(Level.SEVERE, "Cannot close OutputStream of file " + (userDefinedResolverFO == null ? "" : FileUtil.toFile(userDefinedResolverFO)), e); //NOI18N + } + } + } + } + }); + } + + /** Returns map of all registered MIMEResolver instances in revers order, + * i.e. first are ones with lower priority (position attribute higher) + * and last are ones with highest prority (position attribute lower). + * @return map of all registered MIMEResolver instances in revers order + * (highest priority last) + */ + public static Map getOrderedResolvers() { + // scan resolvers and order them to be able to assign extension to mime type from resolver with the lowest position + FileSystem defaultFS = Repository.getDefault().getDefaultFileSystem(); + FileObject[] resolvers = defaultFS.findResource(MIME_RESOLVERS_PATH).getChildren(); + TreeMap orderedResolvers = new TreeMap(Collections.reverseOrder()); + for (FileObject mimeResolverFO : resolvers) { + Integer position = (Integer) mimeResolverFO.getAttribute("position"); //NOI18N + if (position == null) { + position = Integer.MAX_VALUE; + } + while (orderedResolvers.containsKey(position)) { + position--; + } + orderedResolvers.put(position, mimeResolverFO); + } + return orderedResolvers; + } + // MIMEResolver ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ private static class Impl extends MIMEResolver { @@ -161,7 +300,7 @@ } public String findMIMEType(FileObject fo) { - if (fo.hasExt("xml") && fo.getPath().startsWith("Services/MIMEResolver")) { // NOI18N + if (fo.hasExt("xml") && fo.getPath().startsWith(MIME_RESOLVERS_PATH)) { // NOI18N // do not try to check ourselves! return null; } @@ -890,7 +1029,7 @@ if (mimes != null) { boolean match = false; - String s = getMIMEType(fo.getExt()); + String s = getMIMEType(fo.getExt()); //from the very first implementation there is still question "how to obtain resource MIME type as classified by lower layers?" if (s == null) return false; // RFC2045; remove content type paramaters and ignore case diff --git a/openide.filesystems/src/org/openide/filesystems/AbstractFileObject.java b/openide.filesystems/src/org/openide/filesystems/AbstractFileObject.java --- a/openide.filesystems/src/org/openide/filesystems/AbstractFileObject.java +++ b/openide.filesystems/src/org/openide/filesystems/AbstractFileObject.java @@ -183,11 +183,12 @@ return fs.isReadOnly() || fs.info.readOnly(getPath()); } + @Override public String getMIMEType() { String retVal = getAbstractFileSystem().info.mimeType(getPath()); if (retVal == null) { - retVal = FileUtil.getMIMETypeOrDefault(this); + retVal = super.getMIMEType(); } return retVal; diff --git a/openide.filesystems/src/org/openide/filesystems/FileObject.java b/openide.filesystems/src/org/openide/filesystems/FileObject.java --- a/openide.filesystems/src/org/openide/filesystems/FileObject.java +++ b/openide.filesystems/src/org/openide/filesystems/FileObject.java @@ -486,7 +486,8 @@ * @return the MIME type textual representation, e.g. "text/plain"; never null */ public String getMIMEType() { - return FileUtil.getMIMETypeOrDefault(this); + String mimeType = FileUtil.getMIMEType(this); + return mimeType == null ? "content/unknown" : mimeType; //NOI18N } /** Get the size of the file. diff --git a/openide.filesystems/src/org/openide/filesystems/FileUtil.java b/openide.filesystems/src/org/openide/filesystems/FileUtil.java --- a/openide.filesystems/src/org/openide/filesystems/FileUtil.java +++ b/openide.filesystems/src/org/openide/filesystems/FileUtil.java @@ -52,13 +52,13 @@ import java.net.URI; import java.net.URL; import java.net.URLStreamHandler; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Dictionary; +import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; -import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -73,6 +73,7 @@ import javax.swing.Icon; import javax.swing.JFileChooser; import javax.swing.filechooser.FileSystemView; +import org.netbeans.modules.openide.filesystems.declmime.MIMEResolverImpl; import org.openide.filesystems.FileSystem.AtomicAction; import org.openide.util.Exceptions; import org.openide.util.NbBundle; @@ -108,16 +109,6 @@ transientAttributes.add("SystemFileSystem.icon"); // NOI18N transientAttributes.add("SystemFileSystem.icon32"); // NOI18N transientAttributes.add("position"); // NOI18N - } - - /* mapping of file extensions to content-types */ - private static Dictionary map = new Hashtable(); - - static { - // Set up at least this one statically, because it is so basic; - // we do not want to rely on Lookup, MIMEResolver, declarative resolvers, - // XML layers, etc. just to find this. - setMIMEType("xml", "text/xml"); // NOI18N } /** Cache for {@link #isArchiveFile(FileObject)}. */ @@ -1080,102 +1071,103 @@ */ @Deprecated public static String getMIMEType(String ext) { - String s = map.get(ext); - - if (s != null) { - return s; - } else { - return map.get(ext.toLowerCase()); + assert false : "FileUtil.getMIMEType(String extension) is deprecated. Please, use FileUtil.getMIMEType(FileObject)."; //NOI18N + if (ext.toLowerCase().equals("xml")) { //NOI18N + return "text/xml"; // NOI18N } + return null; } /** Resolves MIME type. Registered resolvers are invoked and used to achieve this goal. - * Resolvers must subclass MIMEResolver. If resolvers don't recognize MIME type then - * MIME type is obtained for a well-known extension. + * Resolvers must subclass MIMEResolver. * @param fo whose MIME type should be recognized - * @return the MIME type for the FileObject, or null if the FileObject is unrecognized + * @return the MIME type for the FileObject, or {@code null} if the FileObject is unrecognized. + * It may return {@code content/unknown} instead of {@code null}. */ public static String getMIMEType(FileObject fo) { - String retVal = MIMESupport.findMIMEType(fo, null); - - if (retVal == null) { - retVal = getMIMEType(fo.getExt()); - } - - return retVal; + return MIMESupport.findMIMEType(fo); } /** Resolves MIME type. Registered resolvers are invoked and used to achieve this goal. - * Resolvers must subclass MIMEResolver. If resolvers don't recognize MIME type then - * MIME type is obtained for a well-known extension. + * Resolvers must subclass MIMEResolver. * @param fo whose MIME type should be recognized * @param withinMIMETypes an array of MIME types. Only resolvers whose * {@link MIMEResolver#getMIMETypes} contain one or more of the requested * MIME types will be asked if they recognize the file. It is possible for * the resulting MIME type to not be a member of this list. * @return the MIME type for the FileObject, or null if - * the FileObject is unrecognized. It is possible for the resulting MIME type - * to not be a member of given list. + * the FileObject is unrecognized. It may return {@code content/unknown} instead of {@code null}. + * It is possible for the resulting MIME type to not be a member of given list. * @since 7.13 */ public static String getMIMEType(FileObject fo, String... withinMIMETypes) { Parameters.notNull("withinMIMETypes", withinMIMETypes); //NOI18N - String retVal = MIMESupport.findMIMEType(fo, null, withinMIMETypes); - - if (retVal == null) { - retVal = getMIMEType(fo.getExt()); - } - - return retVal; + return MIMESupport.findMIMEType(fo, withinMIMETypes); } - /** Finds mime type by calling getMIMEType, but - * instead of returning null it fallbacks to default type - * either text/plain or content/unknown (even for folders) + /** Registers specified extension to be recognized as specified MIME type. + * If MIME type parameter is null, it cancels previous registration. + * Note that you may register a case-sensitive extension if that is + * relevant (for example {@literal *.C} for C++) but if you register + * a lowercase extension it will by default apply to uppercase extensions + * too on Windows. + * @param extension the file extension to be registered + * @param mimeType the MIME type to be registered for the extension or {@code null} to deregister + * @see #getMIMEType(FileObject) + * @see #getMIMETypeExtensions(String) */ - static String getMIMETypeOrDefault(FileObject fo) { - String def = getMIMEType(fo.getExt()); - String t = MIMESupport.findMIMEType(fo, def); - - if (t == null) { - // #42965: never allowed - t = "content/unknown"; // NOI18N + public static void setMIMEType(String extension, String mimeType) { + Parameters.notEmpty("extension", extension); //NOI18N + final Map> mimeToExtensions = new HashMap>(); + FileObject userDefinedResolverFO = MIMEResolverImpl.getUserDefinedResolver(); + if (userDefinedResolverFO != null) { + // add all previous content + mimeToExtensions.putAll(MIMEResolverImpl.getMIMEToExtensions(userDefinedResolverFO)); + // exclude extension possibly registered for other MIME types + for (Set extensions : mimeToExtensions.values()) { + extensions.remove(extension); + } } - - return t; + if (mimeType != null) { + // add specified extension to our structure + Set previousExtensions = mimeToExtensions.get(mimeType); + if (previousExtensions != null) { + previousExtensions.add(extension); + } else { + mimeToExtensions.put(mimeType, Collections.singleton(extension)); + } + } + MIMEResolverImpl.storeUserDefinedResolver(mimeToExtensions); } - /** - * Register MIME type for a new extension. - * Note that you may register a case-sensitive extension if that is - * relevant (for example *.C for C++) but if you register - * a lowercase extension it will by default apply to uppercase extensions - * too (for use on Windows or generally for situations where filenames - * become accidentally uppercase). - * @param ext the file extension (should be lowercase unless you specifically care about case) - * @param mimeType the new MIME type - * @throws IllegalArgumentException if this extension was already registered with a different MIME type - * @see #getMIMEType - * @deprecated You should instead use the more general {@link MIMEResolver} system. + /** Returns list of file extensions associated with specified MIME type. In + * other words files with those extensions are recognized as specified MIME type + * in NetBeans' filesystem. It never returns {@code null}. + * @param mimeType the MIME type (e.g. image/gif) + * @return list of file extensions associated with specified MIME type, never {@code null} + * @see #setMIMEType(String, String) + * @since 7.17 */ - @Deprecated - public static void setMIMEType(String ext, String mimeType) { - synchronized (map) { - String old = map.get(ext); - - if (old == null) { - map.put(ext, mimeType); - } else { - if (!old.equals(mimeType)) { - throw new IllegalArgumentException( - "Cannot overwrite existing MIME type mapping for extension `" + // NOI18N - ext + "' with " + mimeType + " (was " + old + ")" - ); // NOI18N + public static List getMIMETypeExtensions(String mimeType) { + Parameters.notEmpty("mimeType", mimeType); //NOI18N + HashMap extensionToMime = new HashMap(); + for (FileObject mimeResolverFO : MIMEResolverImpl.getOrderedResolvers().values()) { + Map> mimeToExtensions = MIMEResolverImpl.getMIMEToExtensions(mimeResolverFO); + for (Map.Entry> entry : mimeToExtensions.entrySet()) { + String mimeKey = entry.getKey(); + Set extensions = entry.getValue(); + for (String extension : extensions) { + extensionToMime.put(extension, mimeKey); } - - // else do nothing } } + List registeredExtensions = new ArrayList(); + for (Map.Entry entry : extensionToMime.entrySet()) { + if (entry.getValue().equals(mimeType)) { + registeredExtensions.add(entry.getKey()); + } + } + return registeredExtensions; } /** diff --git a/openide.filesystems/src/org/openide/filesystems/MIMESupport.java b/openide.filesystems/src/org/openide/filesystems/MIMESupport.java --- a/openide.filesystems/src/org/openide/filesystems/MIMESupport.java +++ b/openide.filesystems/src/org/openide/filesystems/MIMESupport.java @@ -96,16 +96,11 @@ /** Asks all registered subclasses of MIMEResolver to resolve FileObject passed as parameter. * @param fo is FileObject, whose MIME should be resolved - * @param def the default value to return or null * @param withinMIMETypes an array of MIME types which only should be considered * @return MIME type or null if not resolved*/ - static String findMIMEType(FileObject fo, String def, String... withinMIMETypes) { + static String findMIMEType(FileObject fo, String... withinMIMETypes) { if (!fo.isValid() || fo.isFolder()) { return null; - } - - if ((def != null) && !CachedFileObject.isAnyResolver()) { - return def; } CachedFileObject cfo = null; @@ -122,7 +117,7 @@ lastCfo = EMPTY; } - return cfo.getMIMEType(def, withinMIMETypes); + return cfo.getMIMEType(withinMIMETypes); } finally { synchronized (lock) { lastFo = new WeakReference(fo); @@ -241,9 +236,14 @@ }; private static final FileChangeListener weakDeclarativeFolderListener = FileUtil.weakFileChangeListener(declarativeFolderListener, null); private static final Map declarativeResolverCache = new WeakHashMap(); + // holds reference to not loose FileChangeListener + private static FileObject declarativeFolder = null; + private static synchronized List declarativeResolvers() { List declmimes = new ArrayList(); - FileObject declarativeFolder = Repository.getDefault().getDefaultFileSystem().findResource("Services/MIMEResolver"); // NOI18N + if (declarativeFolder == null) { + declarativeFolder = Repository.getDefault().getDefaultFileSystem().findResource("Services/MIMEResolver"); // NOI18N + } if (declarativeFolder != null) { for (FileObject f : Ordering.getOrder(Arrays.asList(declarativeFolder.getChildren()), true)) { if (f.hasExt("xml")) { // NOI18N @@ -275,12 +275,12 @@ @Override public String getMIMEType() { - return getMIMEType(null); + return getMIMEType((String[]) null); } - public String getMIMEType(String def, String... withinMIMETypes) { + public String getMIMEType(String... withinMIMETypes) { if (mimeType == null) { - mimeType = resolveMIME(def, withinMIMETypes); + mimeType = resolveMIME(withinMIMETypes); } return mimeType; @@ -320,7 +320,7 @@ return false; } - private String resolveMIME(String def, String... withinMIMETypes) { + private String resolveMIME(String... withinMIMETypes) { String retVal = null; MIMEResolver[] local = getResolvers(); @@ -334,12 +334,6 @@ return retVal; } } - - if (def != null) { - return def; - } - - return "content/unknown"; // NOI18N } finally { if (fixIt != null) { fixIt.internalClose(); @@ -347,6 +341,11 @@ fixIt = null; } + // fallback for xml files to be recognized e.g. in platform without any MIME resolver registered + if (getExt().toLowerCase().equals("xml")) { + return "text/xml"; // NOI18N + } + return "content/unknown"; // NOI18N } public java.util.Date lastModified() { diff --git a/openide.filesystems/test/unit/src/org/netbeans/modules/openide/filesystems/declmime/MIMEResolverImplTest.java b/openide.filesystems/test/unit/src/org/netbeans/modules/openide/filesystems/declmime/MIMEResolverImplTest.java --- a/openide.filesystems/test/unit/src/org/netbeans/modules/openide/filesystems/declmime/MIMEResolverImplTest.java +++ b/openide.filesystems/test/unit/src/org/netbeans/modules/openide/filesystems/declmime/MIMEResolverImplTest.java @@ -100,8 +100,7 @@ fs = new XMLFileSystem(u); root = fs.getRoot().getFileObject("root"); - root.refresh(); - FileUtil.setMIMEType("txt2", "text/plain; charset=us-ascii"); + root.refresh(); } private static MIMEResolver createResolver(FileObject fo) throws Exception { @@ -125,8 +124,19 @@ TestThread t1 = new TestThread(tl1); TestThread t2 = new TestThread(tl2); + Thread.UncaughtExceptionHandler exceptionHandler = new Thread.UncaughtExceptionHandler() { + + public void uncaughtException(Thread t, Throwable e) { + e.printStackTrace(); + ((TestThread) t).fail = e.getMessage(); + } + }; + + t1.setUncaughtExceptionHandler(exceptionHandler); + t2.setUncaughtExceptionHandler(exceptionHandler); + // call resolver from two threads - + t1.start(); t2.start(); Thread.currentThread().join(100); @@ -136,7 +146,7 @@ t1.join(5000); t2.join(5000); - + if (t1.fail != null) fail(t1.fail); if (t2.fail != null) fail(t2.fail); @@ -156,14 +166,6 @@ String s; FileObject fo = null; - fo = root.getFileObject("test","txt2"); - s = resolve(fo); - if ("mime.xml".equals(s) == false) fail = "mime rule failure: " + fo + " => " + s; - - fo = root.getFileObject("test","txt3"); - s = resolve(fo); - if (s != null) fail = "and-mime rule failure: " + fo + " => " + s; - fo = root.getFileObject("test","elf"); s = resolve(fo); if ("magic-mask.xml".equals(s) == false) fail = "magic-mask rule failure: " + fo + " => " + s; diff --git a/openide.filesystems/test/unit/src/org/netbeans/modules/openide/filesystems/declmime/code-fs.xml b/openide.filesystems/test/unit/src/org/netbeans/modules/openide/filesystems/declmime/code-fs.xml --- a/openide.filesystems/test/unit/src/org/netbeans/modules/openide/filesystems/declmime/code-fs.xml +++ b/openide.filesystems/test/unit/src/org/netbeans/modules/openide/filesystems/declmime/code-fs.xml @@ -58,18 +58,6 @@ ]]> - - - - - - - - - - ]]> - - diff --git a/openide.filesystems/test/unit/src/org/openide/filesystems/FileObjectTestHid.java b/openide.filesystems/test/unit/src/org/openide/filesystems/FileObjectTestHid.java --- a/openide.filesystems/test/unit/src/org/openide/filesystems/FileObjectTestHid.java +++ b/openide.filesystems/test/unit/src/org/openide/filesystems/FileObjectTestHid.java @@ -1011,6 +1011,8 @@ FileUtil.setMIMEType(fo.getExt(),mimeType); String actualMT = fo.getMIMEType(); + // deregister + FileUtil.setMIMEType(fo.getExt(), null); fsAssert("mimeType for this fo was registered; was really " + actualMT, actualMT.equals(mimeType)); } diff --git a/openide.filesystems/test/unit/src/org/openide/filesystems/FileUtilTest.java b/openide.filesystems/test/unit/src/org/openide/filesystems/FileUtilTest.java --- a/openide.filesystems/test/unit/src/org/openide/filesystems/FileUtilTest.java +++ b/openide.filesystems/test/unit/src/org/openide/filesystems/FileUtilTest.java @@ -44,6 +44,8 @@ import java.io.File; import java.io.IOException; import java.net.URL; +import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.logging.Handler; @@ -74,6 +76,12 @@ suite = new NbTestSuite(FileUtilTest.class); } return suite; + } + + @Override + public void setUp() throws IOException { + // folder of declarative resolvers must exist before MIME resolvers tests + FileUtil.createFolder(Repository.getDefault().getDefaultFileSystem().getRoot(), "Services/MIMEResolver"); } public void testToFileObjectSlash() throws Exception { // #98388 @@ -182,7 +190,6 @@ /** Tests normalizeFile() method. */ public void testNormalizeFile() throws IOException { - System.out.println("java.version="+System.getProperty("java.version")); // pairs of path before and after normalization Map paths = new HashMap(); if (Utilities.isWindows()) { @@ -251,33 +258,43 @@ fo = FileUtil.createData(testFolder, "fo2.mime1"); withinMIMETypes = new String[0]; - assertTrue("Resolver should be queried if array of desired MIME types is empty.", MyResolver.QUERIED.equals(FileUtil.getMIMEType(fo, withinMIMETypes))); + FileUtil.getMIMEType(fo, withinMIMETypes); + assertTrue("Resolver should be queried if array of desired MIME types is empty.", MyResolver.wasQueried()); fo = FileUtil.createData(testFolder, "fo3.mime1"); withinMIMETypes = new String[]{"mime3", "mime4"}; - assertFalse("Resolver should not be queried if array of desired MIME types doesn't match MIMEResolver.getMIMETypes.", MyResolver.QUERIED.equals(FileUtil.getMIMEType(fo, withinMIMETypes))); + FileUtil.getMIMEType(fo, withinMIMETypes); + assertFalse("Resolver should not be queried if array of desired MIME types doesn't match MIMEResolver.getMIMETypes.", MyResolver.wasQueried()); fo = FileUtil.createData(testFolder, "fo4.mime1"); withinMIMETypes = new String[]{"mime1", "mime4"}; - assertTrue("Resolver should be queried if one item in array of desired MIME types matches MIMEResolver.getMIMETypes.", MyResolver.QUERIED.equals(FileUtil.getMIMEType(fo, withinMIMETypes))); + FileUtil.getMIMEType(fo, withinMIMETypes); + assertTrue("Resolver should be queried if one item in array of desired MIME types matches MIMEResolver.getMIMETypes.", MyResolver.wasQueried()); fo = FileUtil.createData(testFolder, "fo5.mime1"); withinMIMETypes = new String[]{"mime1", "mime2"}; - assertTrue("Resolver should be queried if both items in array of desired MIME types matches MIMEResolver.getMIMETypes.", MyResolver.QUERIED.equals(FileUtil.getMIMEType(fo, withinMIMETypes))); + FileUtil.getMIMEType(fo, withinMIMETypes); + assertTrue("Resolver should be queried if both items in array of desired MIME types matches MIMEResolver.getMIMETypes.", MyResolver.wasQueried()); } /** MIMEResolver used in testGetMIMETypeConstrained. */ public static final class MyResolver extends MIMEResolver { - public static final String QUERIED = "QUERIED"; - public MyResolver() { super("mime1", "mime2"); } - - /** Always returns the same just to signal it's been queried. */ + + /** Always returns null and change value to signal it's been queried. */ public String findMIMEType(FileObject fo) { - return QUERIED; + queried = true; + return null; + } + private static boolean queried = false; + + public static boolean wasQueried() { + boolean wasQueried = queried; + queried = false; + return wasQueried; } } @@ -319,4 +336,41 @@ logger.removeHandler(handler); } } + + /** Tests FileUtil.setMIMEType method (see #153202). */ + public void testSetMIMEType() throws IOException { + FileObject testRoot = FileUtil.createMemoryFileSystem().getRoot(); + FileObject g1FO = testRoot.createData("a", "g1"); + FileObject g2FO = testRoot.createData("a", "g2"); + FileObject xmlFO = testRoot.createData("a", "xml"); + String gifMIMEType = "image/gif"; + String bmpMIMEType = "image/bmp"; + String xmlMIMEType = "text/xml"; + String unknownMIMEType = "content/unknown"; + + assertEquals("Wrong MIME type.", unknownMIMEType, g1FO.getMIMEType()); + assertEquals("Wrong list of extensions.", Collections.EMPTY_LIST, FileUtil.getMIMETypeExtensions(bmpMIMEType)); + assertEquals("Wrong list of extensions.", Collections.EMPTY_LIST, FileUtil.getMIMETypeExtensions(gifMIMEType)); + // xml registered to text/xml as fallback + assertEquals("Wrong MIME type.", xmlMIMEType, xmlFO.getMIMEType()); + + // {image/bmp=[g1]} + FileUtil.setMIMEType("g1", bmpMIMEType); + assertEquals("Wrong list of extensions.", Collections.singletonList("g1"), FileUtil.getMIMETypeExtensions(bmpMIMEType)); + // {image/bmp=[g1, g2]} + FileUtil.setMIMEType("g2", bmpMIMEType); + assertEquals("Wrong MIME type.", bmpMIMEType, g1FO.getMIMEType()); + assertEquals("Wrong MIME type.", bmpMIMEType, g2FO.getMIMEType()); + assertEquals("Wrong list of extensions.", Arrays.asList("g1", "g2"), FileUtil.getMIMETypeExtensions(bmpMIMEType)); + // {image/bmp=[g2], image/gif=[g1]} + FileUtil.setMIMEType("g1", gifMIMEType); + assertEquals("Wrong MIME type.", gifMIMEType, g1FO.getMIMEType()); + assertEquals("Wrong list of extensions.", Arrays.asList("g1"), FileUtil.getMIMETypeExtensions(gifMIMEType)); + assertEquals("Wrong list of extensions.", Arrays.asList("g2"), FileUtil.getMIMETypeExtensions(bmpMIMEType)); + // {image/gif=[g1]} + FileUtil.setMIMEType("g2", null); + assertEquals("Wrong MIME type.", unknownMIMEType, g2FO.getMIMEType()); + assertEquals("Wrong list of extensions.", Arrays.asList("g1"), FileUtil.getMIMETypeExtensions(gifMIMEType)); + assertEquals("Wrong list of extensions.", Collections.EMPTY_LIST, FileUtil.getMIMETypeExtensions(bmpMIMEType)); + } }