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,21 @@
+ It is now possible to write declarive MIME resolvers checking
+ file names (element name) and file content (element pattern).
+
+ * Pattern <?php in first 255 bytes
+ *
+ * <pattern value="<?php" range="255"/>
+ *
+ *
+ * Pattern <HTML>> or <html> in first 255 bytes and pattern <?php in first 4000 bytes. + *
+ * <pattern value="<HTML>" range="255" ignorecase="true"> + * <pattern value="<?php" range="4000"/> + * </pattern> + *+ * + */ + private class FilePattern { + // case sensitive by default + private static final boolean DEFAULT_IGNORE_CASE = false; + private final String value; + private final int range; + private final boolean ignoreCase; + private FilePattern inner; + private int pointer = 0; + private final byte[] bytes; + private final int valueLength; + + public FilePattern(String value, int range, boolean ignoreCase) { + this.value = value; + this.valueLength = value.length(); + if (ignoreCase) { + this.bytes = value.toLowerCase().getBytes(); + } else { + this.bytes = value.getBytes(); + } + this.range = range; + this.ignoreCase = ignoreCase; + } + + public void setInner(FilePattern inner) { + this.inner = inner; + } + + private boolean match(byte b) { + if (pointer < valueLength) { + if (b == bytes[pointer]) { + pointer++; + } else { + pointer = 0; + return false; + } + } + return pointer >= valueLength; + } + + /** Read from given file and compare byte-by-byte if pattern + * appers in given range. + */ + public boolean match(FileObject fo) throws IOException { + pointer = 0; + InputStream is = null; + boolean matched = false; + try { + is = fo.getInputStream(); // it is CachedInputStream, so you can call getInputStream and read more times without performance penalty + byte[] byteRange = new byte[range]; + int read = is.read(byteRange); + for (int i = 0; i < read; i++) { + byte b = byteRange[i]; + if (ignoreCase) { + b = (byte) Character.toLowerCase(b); + } + if (match(b)) { + matched = true; + break; + } + } + } finally { + try { + if (is != null) { + is.close(); + } + } catch (IOException ioe) { + // already closed + } + } + if (matched) { + if (inner == null) { + return true; + } else { + return inner.match(fo); + } + } + return false; + } + + @Override + public String toString() { + return "[" + value + ", " + range + ", " + ignoreCase + (inner != null ? ", " + inner : "") + "]"; + } + } + + /** Used to compare filename with given name. + * For example: + *
+ * Filename matches makefile, Makefile, MaKeFiLe, mymakefile, gnumakefile, makefile1, .... + *
+ * <name name="makefile" substring="true"/> + *+ * + *
+ * Filename exactly matches rakefile or Rakefile. + *
+ * <name name="rakefile" ignorecase="false"/> + * <name name="Rakefile" ignorecase="false"/> + *+ * + */ + private class FileName { + + // case insensitive by default + private static final boolean DEFAULT_IGNORE_CASE = true; + private static final boolean DEFAULT_SUBSTRING = false; + private final String name; + private final boolean substring; + private final boolean ignoreCase; + + public FileName(String name, boolean substring, boolean ignoreCase) { + if (ignoreCase) { + this.name = name.toLowerCase(); + } else { + this.name = name; + } + this.substring = substring; + this.ignoreCase = ignoreCase; + } + + public boolean match(FileObject fo) { + String nameAndExt = fo.getNameExt(); + if (ignoreCase) { + nameAndExt = nameAndExt.toLowerCase(); + } + if (substring) { + return nameAndExt.indexOf(name) != -1; + } else { + return nameAndExt.equals(name); + } + } + + @Override + public String toString() { + return "[" + name + ", " + substring + ", " + ignoreCase + "]"; + } + } + /** * For debug purposes only. */ + @Override public String toString() { int i = 0; StringBuffer buf = new StringBuffer(); @@ -529,6 +777,20 @@ buf.append("file-attributes:"); for (i = 0; i
this.getMIMEType()
.
-->
-
+
@@ -123,6 +124,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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
@@ -662,21 +662,21 @@
private boolean ensureBufferLength(int newLen) throws IOException {
if (!eof && newLen > len) {
- byte[] tmpBuffer = new byte[len + newLen];
+ byte[] tmpBuffer = new byte[newLen];
if (len > 0) {
System.arraycopy(buffer, 0, tmpBuffer, 0, len);
}
- int readLen = inputStream.read(tmpBuffer, len, newLen);
+ int readLen = inputStream.read(tmpBuffer, len, newLen - len);
if ((readLen > 0)) {
buffer = tmpBuffer;
- len += readLen;
+ len += readLen;
} else {
eof = true;
}
}
return len >= newLen;
}
-
+
@Override
public int read(byte[] b, int off, int blen) throws IOException {
ensureBufferLength(pos + blen);
@@ -688,7 +688,7 @@
}
return retval;
}
-
+
public int read() throws IOException {
int retval = -1;
@@ -699,7 +699,7 @@
}
return retval;
}
-
+
void cacheToStart() {
pos = 0;
eof = false;
diff --git a/openide.filesystems/src/org/openide/filesystems/doc-files/HOWTO-MIME.html b/openide.filesystems/src/org/openide/filesystems/doc-files/HOWTO-MIME.html
--- a/openide.filesystems/src/org/openide/filesystems/doc-files/HOWTO-MIME.html
+++ b/openide.filesystems/src/org/openide/filesystems/doc-files/HOWTO-MIME.html
@@ -105,7 +105,7 @@
To solve both of the above problems a developer can use a +
To solve both of the above problems a developer should use a declarative MIME type resolver. The semantics of the MIME resolution may be specified in a simple manner and an implementation is available that will interpret the declaration as efficiently as possible.
@@ -186,8 +186,6 @@ url="mime-resolver.xml"> <attr name="SystemFileSystem.localizingBundle" stringvalue="org.nb.modules.ant.Bundle"/> - <attr name="SystemFileSystem.icon" - urlvalue="nbresloc:/org/nb/modules/ant/resolver-icon.gif"/> <attr name="position" intvalue="1234"/> </file> </folder> @@ -197,17 +195,14 @@The resolver is registered as Services/MIMEResolver/org-nb-modules-ant-mime-resolver.xml. -It is given a pleasant display name and icon since the user may see -the resolver presented in NetBeans settings. The bundle file +It is given a pleasant display name since the user may see +the resolver presented under XML Layer node. The bundle file org/nb/modules/ant/Bundle.properties would contain a matching key:
Services/MIMEResolver/org-nb-modules-ant-mime-resolver.xml=Ant project files- -
and org/nb/modules/ant/resolver-icon.gif should hold a -suitable icon.
Note that an open position @@ -217,31 +212,6 @@ over more generic resolvers.
-Now whenever matching files are encountered, they will be treated
-as having the MIME type text/x-ant+xml
(in this example).
-The data loader should be made to be sensitive to the resolved MIME
-type; this is most easily accomplished when subclassing
-UniFileLoader
:
-protected void initialize() { - super.initialize(); - getExtensions().addMimeType("text/x-ant+xml"); -} -- -
Note that in this case JEditorPane
will probably not
-recognize such files as being a variant of text/xml
(the
-text/*+xml
syntax is a pending standard), so you should
-remember when using a DataEditorSupport
or similar to
-explicitly set the editor MIME type to text/xml
(the
-default is taken from the file MIME type). In the future it is planned
-for editor supports to check an OpenIDE-specific type registry which
-would automatically associate a general XML editor kit with MIME types
-matching text/*+xml
unless an exact match was found.
MIME resolver description root element. +
PUBLIC "-//NetBeans//DTD MIME Resolver 1.0//EN" -
+Element Content Model
-(file)+ +(file)+
Referenced by
+- - -
MIME component checking XML resource header, permitting matches against +
PUBLIC "-//NetBeans//DTD MIME Resolver XML Rules 1.0//EN" @@ -98,235 +102,281 @@
Element Content Model
-(pi*, doctype?, element?) +Referenced by
-resolver + - -Processing instructions can be tested on target and pseudo-attributes. +
It should be used for in-band tagging that is specified by some
accepted specification. For NetBeans-specific tagging of third party standards it is
recommended to use out-of-band tagging such as FileObject
attributes.
-
Declared Attributes
-Element Content Model
-(attr*) +(attr*)
Referenced by
-xml-rule + - -Match one of the DOCTYPE's public IDs.
- Use of public-id
subelements is mutually exclusive with the attribute.
public-id
subelements is mutually exclusive with the attribute.Declared Attributes
-Element Content Model
-(public-id*) +(public-id*)
Referenced by
-xml-rule + - -A public ID is checked for an exact match.
+Declared Attributes
-Element Content Model
-EMPTY +EMPTY
Referenced by
-doctype + - -Matches a root element by specified name, attributes or namespace. +
- The namespace ns
attribute or the presence of any ns
subelements
+ The namespace ns
attribute or the presence of any ns
subelements
implies that the name
attribute represents a local element name,
otherwise the name
attribute represents a full element name ("QName").
One of the namespaces must match.
-
Declared Attributes
-Element Content Model
-(ns*, attr*) +Referenced by
-xml-rule + - -Test namespace equality (exact match).
+Declared Attributes
-Element Content Model
-EMPTY +EMPTY
Referenced by
-element + - -Element attribute template, also used for pseudo-attributes in processing instructions. +
The name
attribute contains the full attribute name (QName).
This is inteded to be used for documents that cannot be indentified by public ID nor
root element namespace.
text
is specified then the attribute's normalized text must
- exactly match the specified text, otherwise the attribute just needs to be present.
+ exactly match the specified text, otherwise the attribute just needs to be present.Declared Attributes
-Element Content Model
-EMPTY +EMPTY
Referenced by
-pi, element + - -A file (resource) represents the MIME resolver input. +
FileObject
attributes.
Some of them must match to proceed to the resolver
element.
- Implementation Note:
- Lower level MIME type is obtained by FileUtil.getMIMEType()
. It may not be
+ Implementation Note:
+ Lower level MIME type is obtained by FileUtil.getMIMEType()
. It may not be
retrieved by a call to FileObject.getMIMEType
to avoid recursion. A better way
to determine MIME type as assigned by the OS may be introduced in the future.
- Implementation Note:
+ Implementation Note:
All other tests are performed by calling appropriate methods on FileObject
so there is a danger of recursion if these call this.getMIMEType()
.
-
Element Content Model
-((ext | mime | magic | fattr)+, (resolver | exit)) +((ext | mime | magic | fattr | pattern | name)+, (resolver | exit))
Referenced by
-MIME-resolver + - -Tests resource extension for full equality.
+Declared Attributes
-Element Content Model
-EMPTY +EMPTY
Referenced by
-file + - -Tests resource MIME type for equality (RFC2045) or suffix equality if it - starts with '+' (RFC 3023).
+Declared Attributes
-Element Content Model
-EMPTY +EMPTY
Referenced by
-file + - -Look at initial bytes of the file and test for a complete match of masked +
hex
attribute is long.
- E.g. <magic hex="0a0001" mask="FF00FF"/>
+ E.g. <magic hex="0a0001" mask="FF00FF"/>Declared Attributes
-Element Content Model
-EMPTY +EMPTY
Referenced by
-file + - -Test on FileObject
attributes. Matching attributes are converted
+
FileObject
attributes. Matching attributes are converted
to strings via Object.toString()
and compared to the text
attribute.
FileObject
attributes can be used for out-of-band tagging of standard documents.
-
Declared Attributes
-Element Content Model
-EMPTY +EMPTY
Referenced by
-file + - -You may apply additional rules based on resource content.
- The mime
element value is returned. Use the reserved value of null
- to indicate you are not interested in such files (same as <exit/>).
+ Pattern <?php in first 255 bytes +
+ <pattern value="<?php" range="255"/> ++ +
+ Pattern <HTML> or <html> in first 255 bytes and pattern <?php in first 4000 bytes. +
+ <pattern value="<HTML>" range="255" ignorecase="true"> + <pattern value="<?php" range="4000"/> + </pattern> ++
Declared Attributes
-Element Content Model
-(xml-rule)? +(pattern?)
Referenced by
-file + - -Declares that this file is not recognized by this resolver. - A shortcut for <resolver mime="null"/>.
++ Filename matches makefile, Makefile, MaKeFiLe, mymakefile, gnumakefile, makefile1, .... +
+ <name name="makefile" substring="true"/> ++ +
+ Filename exactly matches rakefile or Rakefile. +
+ <name name="rakefile" ignorecase="false"/> + <name name="Rakefile" ignorecase="false"/> ++
Declared Attributes
+Element Content Model
-EMPTY +EMPTY
Referenced by
-file + + +mime
element value is returned. Use the reserved value of null
+ to indicate you are not interested in such files (same as <exit/>).Declared Attributes
+Element Content Model
+(xml-rule)?
+Referenced by
+ + + ++ Do not resolve *.txt files and do time consuming magic recognition only for not-txt files. +
+ <file> + <ext name="txt"/> + <exit/> + </file> + <file> + <magic hex="0a0001" mask="FF00FF"/> + <resolver mime="text/plain"/> + </file> ++
Element Content Model
+EMPTY
+Referenced by
+ \ No newline at end of file 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 @@ -44,7 +44,13 @@ import java.net.URL; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.logging.Handler; +import java.util.logging.LogRecord; +import java.util.logging.Logger; +import junit.framework.Test; import org.netbeans.junit.NbTestCase; +import org.netbeans.junit.NbTestSuite; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileSystem; import org.openide.filesystems.FileUtil; @@ -54,21 +60,31 @@ public class MIMEResolverImplTest extends NbTestCase { List