diff --git a/cnd/nbproject/project.xml b/cnd/nbproject/project.xml --- a/cnd/nbproject/project.xml +++ b/cnd/nbproject/project.xml @@ -190,7 +190,7 @@ - 6.4.1 + 7.13 diff --git a/cnd/src/org/netbeans/modules/cnd/loaders/CndMIMEResolver.java b/cnd/src/org/netbeans/modules/cnd/loaders/CndMIMEResolver.java --- a/cnd/src/org/netbeans/modules/cnd/loaders/CndMIMEResolver.java +++ b/cnd/src/org/netbeans/modules/cnd/loaders/CndMIMEResolver.java @@ -51,8 +51,12 @@ * declarative mime resolver. */ public class CndMIMEResolver extends MIMEResolver { + public CndMIMEResolver() { //System.err.println("called CndMIMEResolver.CndMIMEResolver()"); + super(MIMENames.C_MIME_TYPE, MIMENames.CPLUSPLUS_MIME_TYPE, + MIMENames.MAKEFILE_MIME_TYPE, MIMENames.SHELL_MIME_TYPE, + MIMENames.FORTRAN_MIME_TYPE, MIMENames.ASM_MIME_TYPE); } public static boolean isHeaderExtension(String ext){ @@ -67,7 +71,7 @@ } return false; } - + /** * Resolves FileObject and returns recognized MIME type * @param fo is FileObject which should be resolved diff --git a/cnd/src/org/netbeans/modules/cnd/loaders/CndSniffyMIMEResolver.java b/cnd/src/org/netbeans/modules/cnd/loaders/CndSniffyMIMEResolver.java --- a/cnd/src/org/netbeans/modules/cnd/loaders/CndSniffyMIMEResolver.java +++ b/cnd/src/org/netbeans/modules/cnd/loaders/CndSniffyMIMEResolver.java @@ -29,6 +29,10 @@ * @author Vladimir Voskresensky */ public class CndSniffyMIMEResolver extends MIMEResolver { + + public CndSniffyMIMEResolver() { + super(MIMENames.CPLUSPLUS_MIME_TYPE, MIMENames.SHELL_MIME_TYPE); + } /** * Resolves FileObject and returns recognized MIME type diff --git a/languages.sh/nbproject/project.properties b/languages.sh/nbproject/project.properties --- a/languages.sh/nbproject/project.properties +++ b/languages.sh/nbproject/project.properties @@ -0,0 +1,1 @@ +javac.source=1.5 diff --git a/languages.sh/nbproject/project.xml b/languages.sh/nbproject/project.xml --- a/languages.sh/nbproject/project.xml +++ b/languages.sh/nbproject/project.xml @@ -19,7 +19,7 @@ - 7.3 + 7.13 diff --git a/languages.sh/src/org/netbeans/modules/languages/sh/ShellScriptResolver.java b/languages.sh/src/org/netbeans/modules/languages/sh/ShellScriptResolver.java --- a/languages.sh/src/org/netbeans/modules/languages/sh/ShellScriptResolver.java +++ b/languages.sh/src/org/netbeans/modules/languages/sh/ShellScriptResolver.java @@ -39,7 +39,9 @@ public class ShellScriptResolver extends MIMEResolver { /** Default constructor for lookup. */ - public ShellScriptResolver() {} + public ShellScriptResolver() { + super("text/sh"); //NOI18N + } public String findMIMEType(FileObject fo) { if (fo.hasExt("sh")) { //NOI18N @@ -62,7 +64,7 @@ } return null; } - + /** Checks whether byte array starts with given string. * @return true if byte array starts with given prefix, false otherwise */ 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,31 @@ Filesystems API + + + FileUtil.getMIMEType() can be restricted by listed MIME types + + + + + +

+ To speed up MIME type recognition it is added an extra parameter + to method FileUtil.getMIMEType(FileObject, String...). + We can supply one or more MIME types which we are only interested in. + Module writers have to override default constructor and call + super(String...), e.g.: +

+
+                    public MyResolver() {
+                        super("text/plain", "text/sh");
+                    }
+                
+
+ + + +
XMLFileSystem attributes can be queried for instance class 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 @@ -43,4 +43,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.12.0 +spec.version.base=7.13.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 @@ -78,6 +78,17 @@ return new Impl(fo); } + /** Check whether given resolver is declarative. */ + public static boolean isDeclarative(MIMEResolver resolver) { + return resolver instanceof Impl; + } + + /** Returns resolvable MIME Types for given declarative resolver. */ + 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. * @param fo MIMEResolver FileObject @@ -120,6 +131,7 @@ public @Override void fileChanged(FileEvent fe) { synchronized (Impl.this) { state = DescParser.INIT; + implResolvableMIMETypes = null; } } }; @@ -128,33 +140,24 @@ private FileElement[] smell = null; private short state = DescParser.INIT; - + + private String[] implResolvableMIMETypes = null; + Impl(FileObject obj) { if (ERR.isLoggable(Level.FINE)) ERR.fine("MIMEResolverImpl.Impl.(" + obj + ")"); // NOI18N data = obj; data.addFileChangeListener(FileUtil.weakFileChangeListener(listener, data)); } - - /** - * Resolves FileObject and returns recognized MIME type - * @param fo is FileObject which should be resolved - * @return recognized MIME type or null if not recognized - */ + public String findMIMEType(FileObject fo) { if (fo.hasExt("xml") && fo.getPath().startsWith("Services/MIMEResolver")) { // NOI18N // do not try to check ourselves! return null; } - synchronized (this) { // lazy init - - if (state == DescParser.INIT) { - state = parseDesc(); - } - - if (state == DescParser.ERROR) { - return null; - } + init(); + if (state == DescParser.ERROR) { + return null; } // smell is filled in reverse order @@ -168,6 +171,14 @@ } return null; + } + + private void init() { + synchronized (this) { // lazy init + if (state == DescParser.INIT) { + state = parseDesc(); + } + } } // description document is parsed in the same thread @@ -187,6 +198,15 @@ ERR.fine(buf.toString()); } } + // fill resolvableMIMETypes array with available MIME types + if(parser.state != DescParser.ERROR) { + for (int i = 0; i < smell.length; i++) { + String mimeType = smell[i].getMimeType(); + if(mimeType != null) { + implResolvableMIMETypes = Util.addString(implResolvableMIMETypes, mimeType); + } + } + } return parser.state; } @@ -195,7 +215,7 @@ return "MIMEResolverImpl.Impl[" + data + ", " + smell + "]"; // NOI18N } - + } 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 @@ -1096,8 +1096,8 @@ * to a case-insensitive match. * @param ext the extension: "jar", "zip", etc. * @return the MIME type for the extension, or null if the extension is unrecognized - * @deprecated use {@link #getMIMEType(FileObject) getMIMEType(FileObject)} as MIME cannot - * be generally detected by file object extension. + * @deprecated use {@link #getMIMEType(FileObject)} or {@link #getMIMEType(FileObject, String[])} + * as MIME cannot be generally detected by file object extension. */ @Deprecated public static String getMIMEType(String ext) { @@ -1118,6 +1118,27 @@ */ public static String getMIMEType(FileObject fo) { String retVal = MIMESupport.findMIMEType(fo, null); + + if (retVal == null) { + retVal = getMIMEType(fo.getExt()); + } + + return retVal; + } + + /** 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. + * @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 + */ + 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()); diff --git a/openide.filesystems/src/org/openide/filesystems/MIMEResolver.java b/openide.filesystems/src/org/openide/filesystems/MIMEResolver.java --- a/openide.filesystems/src/org/openide/filesystems/MIMEResolver.java +++ b/openide.filesystems/src/org/openide/filesystems/MIMEResolver.java @@ -40,6 +40,8 @@ */ package org.openide.filesystems; +import org.openide.util.Parameters; + /** * This class is intended as superclass for individual resolvers. * All registered subclasses of MIMEResolver are looked up and asked one by one @@ -57,6 +59,35 @@ * @author rmatous */ public abstract class MIMEResolver { + + private String[] resolvableMIMETypes = null; + + /** Creates a new MIMEResolver. + * @param mimeTypes an array of MIME types which can be resolved by this resolver. + * It should contain all MIME types which {@link #findMIMEType} can return. + * If something is missing, this resolver can be ignored, when searching for that + * missing MIME type (see {@link FileUtil#getMIMEType(FileObject, String...)}). + */ + public MIMEResolver(String... mimeTypes) { + Parameters.notNull("mimeTypes", mimeTypes); //NOI18N + if(mimeTypes.length == 0) { + throw new IllegalArgumentException("The mimeTypes parameter cannot be empty array."); //NOI18N + } + for (String mimeType : mimeTypes) { + if(mimeType == null || mimeType.length() == 0) { + throw new IllegalArgumentException("The item in mimeTypes parameter cannot be null nor empty String."); //NOI18N + } + } + resolvableMIMETypes = mimeTypes; + + //the MIMEResolver(String...) constructor does not check for a null or + //empty array, nor for nulls in the array. + } + + /** Creates a new MIMEResolver. */ + public MIMEResolver() { + } + /** * Resolves FileObject and returns recognized MIME type * @param fo is FileObject which should be resolved (This FileObject is not @@ -64,4 +95,11 @@ * @return recognized MIME type or null if not recognized */ public abstract String findMIMEType(FileObject fo); + + /** Returns an array of MIME types which can be resolved by this resolver. + * @return a non-empty array of MIME types + */ + String[] getMIMETypes() { + return resolvableMIMETypes; + } } 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 @@ -61,6 +61,7 @@ import org.openide.util.LookupEvent; import org.openide.util.LookupListener; import org.openide.util.Union2; +import org.openide.util.WeakSet; /** * This class is intended to enhance MIME resolving. This class offers @@ -97,8 +98,9 @@ /** 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) { + static String findMIMEType(FileObject fo, String def, String... withinMIMETypes) { if (!fo.isValid() || fo.isFolder()) { return null; } @@ -121,7 +123,7 @@ lastCfo = EMPTY; } - return cfo.getMIMEType(def); + return cfo.getMIMEType(def, withinMIMETypes); } finally { synchronized (lock) { lastFo = new WeakReference(fo); @@ -129,7 +131,7 @@ } } } - + /** Testing purposes. */ static MIMEResolver[] getResolvers() { @@ -141,7 +143,9 @@ private static Union2> resolvers; // call getResolvers instead /** resolvers that were here before we cleaned them */ private static MIMEResolver[] previousResolvers; - + /** Set used to print just one warning per resolver. */ + private static final WeakSet warningPrinted = new WeakSet(); + String mimeType; java.util.Date lastModified; Long size; @@ -275,22 +279,59 @@ return getMIMEType(null); } - public String getMIMEType(String def) { + public String getMIMEType(String def, String... withinMIMETypes) { if (mimeType == null) { - mimeType = resolveMIME(def); + mimeType = resolveMIME(def, withinMIMETypes); } return mimeType; } - private String resolveMIME(String def) { + /** Decides whether given MIMEResolver is capable to resolve at least + * one of given MIME types. + * @param resolver MIMEResolver to be examined + * @param desiredMIMETypes an array of MIME types + * @return true if at least one of given MIME types can be resolved by + * given resolver or if array is empty or resolver.getMIMETypes() doesn't + * return non empty array, false otherwise. + */ + private boolean canResolveMIMETypes(MIMEResolver resolver, String... desiredMIMETypes) { + if(desiredMIMETypes.length == 0) { + return true; + } + String[] resolvableMIMETypes = null; + if (MIMEResolverImpl.isDeclarative(resolver)) { + resolvableMIMETypes = MIMEResolverImpl.getMIMETypes(resolver); + } else { + resolvableMIMETypes = resolver.getMIMETypes(); + } + if(resolvableMIMETypes == null || resolvableMIMETypes.length == 0) { + if(warningPrinted.add(resolver)) { + ERR.warning(resolver + " constructor should call super(String...) with list of resolvable MIME types."); //NOI18N + } + return true; + } + assert resolvableMIMETypes != null && resolvableMIMETypes.length > 0 : "Resolver " + resolver + " should resolve at least one MIME type."; //NOI18N + for (int i = 0; i < desiredMIMETypes.length; i++) { + for (int j = 0; j < resolvableMIMETypes.length; j++) { + if(resolvableMIMETypes[j].equals(desiredMIMETypes[i])) { + return true; + } + } + } + return false; + } + + private String resolveMIME(String def, String... withinMIMETypes) { String retVal = null; MIMEResolver[] local = getResolvers(); try { for (int i = 0; i < local.length; i++) { - retVal = local[i].findMIMEType(this); - + MIMEResolver resolver = local[i]; + if(canResolveMIMETypes(resolver, withinMIMETypes)) { + retVal = resolver.findMIMEType(this); + } if (retVal != null) { return retVal; } 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 @@ -42,9 +42,11 @@ package org.openide.filesystems; import java.io.File; +import java.io.IOException; import java.net.URL; import org.netbeans.junit.NbTestCase; import org.openide.filesystems.test.TestFileUtils; +import org.openide.util.Lookup; import org.openide.util.Utilities; import org.openide.util.test.MockLookup; @@ -166,4 +168,55 @@ File file = new File("."); assertTrue("java.io.File(\".\") not normalized.", FileUtil.normalizeFile(FileUtil.normalizeFile(file)).equals(FileUtil.normalizeFile(file))); } + + /** Tests that only resolvers are queried which supply at least one of + * MIME types given in array in FileUtil.getMIMEType(fo, String[]). + * See issue 137734. + */ + public void testGetMIMETypeConstrained() throws IOException { + MyResolver resolver = new MyResolver(); + MockLookup.setInstances(resolver); + assertNotNull(Lookup.getDefault().lookup(MyResolver.class)); + FileObject testFolder = FileUtil.createMemoryFileSystem().getRoot(); + + FileObject fo = FileUtil.createData(testFolder, "fo1.mime1"); + String[] withinMIMETypes = null; + try { + FileUtil.getMIMEType(fo, withinMIMETypes); + fail("FileUtil.getMIMEType(fo, null) should throw IllegalArgumentException."); + } catch (NullPointerException npe) { + // exception correctly thrown + } + + 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))); + + 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))); + + 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))); + + 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))); + } + + /** 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. */ + public String findMIMEType(FileObject fo) { + return QUERIED; + } + } } diff --git a/php.project/nbproject/project.xml b/php.project/nbproject/project.xml --- a/php.project/nbproject/project.xml +++ b/php.project/nbproject/project.xml @@ -190,7 +190,7 @@ - 7.0 + 7.13 diff --git a/php.project/src/org/netbeans/modules/php/project/PhpMimeResolver.java b/php.project/src/org/netbeans/modules/php/project/PhpMimeResolver.java --- a/php.project/src/org/netbeans/modules/php/project/PhpMimeResolver.java +++ b/php.project/src/org/netbeans/modules/php/project/PhpMimeResolver.java @@ -71,6 +71,10 @@ //html signs private static final Set resolvedExt = new HashSet(); + + public PhpMimeResolver() { + super(MIME_TYPE); + } @Override public String findMIMEType(FileObject fo) { diff --git a/ruby/nbproject/project.xml b/ruby/nbproject/project.xml --- a/ruby/nbproject/project.xml +++ b/ruby/nbproject/project.xml @@ -5,15 +5,6 @@ org.netbeans.modules.ruby - - org.netbeans.libs.bytelist - - - - 1 - 0.1 - - org.jdesktop.layout @@ -30,6 +21,15 @@ 1 0.111 + + + + org.netbeans.libs.bytelist + + + + 1 + 0.1 @@ -179,7 +179,7 @@ - 7.0 + 7.13 diff --git a/ruby/src/org/netbeans/modules/ruby/RubyMimeResolver.java b/ruby/src/org/netbeans/modules/ruby/RubyMimeResolver.java --- a/ruby/src/org/netbeans/modules/ruby/RubyMimeResolver.java +++ b/ruby/src/org/netbeans/modules/ruby/RubyMimeResolver.java @@ -58,11 +58,12 @@ * that cannot reference this value directly. */ public static final String RUBY_MIME_TYPE = "text/x-ruby"; // application/x-ruby is also used a fair bit. - + /** Number of bytes to sniff from the file headers */ static final int HEADER_LENGTH = 80; public RubyMimeResolver() { + super(RUBY_MIME_TYPE); } public String findMIMEType(FileObject fo) {