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 @@ Filesystems API + + + Added new elements to MIME resolver DTD + + + + + +

+ It is now possible to write declarive MIME resolvers checking + file names (element name) and file content (element pattern). +

+
+ +
Support for annotation processors generating XML layer fragments 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.15.0 +spec.version.base=7.16.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 @@ -46,7 +46,9 @@ import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import org.openide.filesystems.FileChangeAdapter; @@ -73,6 +75,11 @@ private static final boolean CASE_INSENSITIVE = Utilities.isWindows() || Utilities.getOperatingSystem() == Utilities.OS_VMS; + + // notification limit in bytes for reading file content. It should not exceed 4192 (4kB) because it is read in one disk touch. + private static final int READ_LIMIT = 4000; + private static Set readLimitReported = new HashSet(); + public static MIMEResolver forDescriptor(FileObject fo) { return new Impl(fo); @@ -166,6 +173,10 @@ for (int i = smell.length-1; i>=0; i--) { String s = smell[i].resolve(fo); if (s != null) { + if (s.equals(FileElement.EXIT_MIME_TYPE)) { + // if file matches conditions and exit element is present, do not continue in loop and return null + return null; + } if (ERR.isLoggable(Level.FINE)) ERR.fine("MIMEResolverImpl.findMIMEType(" + fo + ")=" + s); // NOI18N return s; } @@ -212,8 +223,9 @@ } /** For debug purposes. */ + @Override public String toString() { - return "MIMEResolverImpl.Impl[" + data + ", " + smell + "]"; // NOI18N + return "MIMEResolverImpl.Impl[" + data.getPath() + "]"; // NOI18N } @@ -236,6 +248,10 @@ // references active resolver component private MIMEComponent component = null; private String componentDelimiter = null; + // holds level of pattern element + private int patternLevel = 0; + // used to prohibit more pattern elements on the same level + Set patternLevelSet; DescParser(FileObject fo) { @@ -247,6 +263,7 @@ private static final short IN_FILE = 2; private static final short IN_RESOLVER = 3; private static final short IN_COMPONENT = 4; + private static final short IN_PATTERN = 5; // second state dimension private static final short IN_EXIT = INIT + 1; @@ -259,13 +276,19 @@ private static final String RESOLVER = "resolver"; // NOI18N private static final String FATTR = "fattr"; // NOI18N private static final String NAME = "name"; // NOI18N + private static final String PATTERN = "pattern"; // NOI18N + private static final String VALUE = "value"; // NOI18N + private static final String RANGE = "range"; // NOI18N + private static final String IGNORE_CASE = "ignorecase"; // NOI18N + private static final String SUBSTRING = "substring"; // NOI18N private static final String MAGIC = "magic"; // NOI18N private static final String HEX = "hex"; // NOI18N private static final String MASK = "mask"; // NOI18N - private static final String VALUE = "text"; // NOI18N + private static final String TEXT = "text"; // NOI18N private static final String EXIT = "exit"; // NOI18N private static final String XML_RULE_COMPONENT = "xml-rule"; // NOI18N + @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { String s; @@ -337,14 +360,57 @@ } else if (FATTR.equals(qName)) { s = atts.getValue(NAME); if (s == null) error(); - String val = atts.getValue(VALUE); + String val = atts.getValue(TEXT); template[0].fileCheck.addAttr(s, val); + + } else if (PATTERN.equals(qName)) { + + s = atts.getValue(VALUE); if (s == null) error(); + int range = Integer.valueOf(atts.getValue(RANGE)); + assert range <= READ_LIMIT || !readLimitReported.add(fo.getPath()): "MIME resolver " + fo.getPath() + " should not exceed " + READ_LIMIT + " bytes limit for files content check."; //NOI18N + boolean ignoreCase = Type.FilePattern.DEFAULT_IGNORE_CASE; + String ignoreCaseAttr = atts.getValue(IGNORE_CASE); + if (ignoreCaseAttr != null) { + ignoreCase = Boolean.valueOf(ignoreCaseAttr); + } + if (file_state == IN_PATTERN) { + if (patternLevelSet == null) { + patternLevelSet = new HashSet(); + } + if (!patternLevelSet.add(patternLevel)) { + error("Second pattern element on the same level not allowed"); //NOI18N + } + template[0].fileCheck.addInnerPattern(s, range, ignoreCase); + } else { + template[0].fileCheck.addPattern(s, range, ignoreCase); + file_state = IN_PATTERN; + } + patternLevel++; + break; + + } else if (NAME.equals(qName)) { + + s = atts.getValue(NAME); if (s == null) error(); + String substringAttr = atts.getValue(SUBSTRING); + boolean substring = Type.FileName.DEFAULT_SUBSTRING; + if (substringAttr != null) { + substring = Boolean.valueOf(substringAttr); + } + boolean ignoreCase = Type.FileName.DEFAULT_IGNORE_CASE; + String ignoreCaseAttr = atts.getValue(IGNORE_CASE); + if (ignoreCaseAttr != null) { + ignoreCase = Boolean.valueOf(ignoreCaseAttr); + } + template[0].fileCheck.addName(s, substring, ignoreCase); + break; } else if (RESOLVER.equals(qName)) { if (template[0].fileCheck.exts == null && template[0].fileCheck.mimes == null - && template[0].fileCheck.fatts == null + && template[0].fileCheck.fatts == null + && template[0].fileCheck.patterns == null + && template[0].fileCheck.names == null && template[0].fileCheck.magic == null) { error(); // at least one must be specified } @@ -357,7 +423,7 @@ break; } else if (EXIT.equals(qName)) { - + template[0].fileCheck.setExit(); file_state = IN_EXIT; break; @@ -387,7 +453,7 @@ component.startElement(namespaceURI, localName, qName, atts); break; - + default: } @@ -402,12 +468,19 @@ state = IN_COMPONENT; } + @Override public void endElement(String namespaceURI, String localName, String qName) throws SAXException { switch (state) { case IN_FILE: if (FILE.equals(qName)) { state = IN_ROOT; file_state = INIT; + } + if (PATTERN.equals(qName)) { + if (--patternLevel == 0) { + patternLevelSet = null; + file_state = INIT; + } } break; @@ -427,6 +500,7 @@ } } + @Override public void characters(char[] data, int offset, int len) throws SAXException { if (state == IN_COMPONENT) component.characters(data, offset, len); } @@ -444,6 +518,8 @@ private Type fileCheck = new Type(); private String mime = null; private MIMEComponent rule = null; + // unique string to mark exit condition + private static final String EXIT_MIME_TYPE = "mime-type-to-exit"; //NOI18N private String[] getExtensions() { return fileCheck.exts; @@ -453,6 +529,10 @@ return mime; } + private boolean isExit() { + return fileCheck.exit; + } + private void setMIME(String mime) { if ("null".equals(mime)) return; // NOI18N this.mime = mime; @@ -462,9 +542,15 @@ try { if (fileCheck.accept(file)) { - if (mime == null) return null; - if (rule == null) return mime; - if (rule.acceptFileObject(file)) return mime; + if (rule != null && !rule.acceptFileObject(file)) { + return null; + } + if (isExit() || mime == null) { + // all matched but exit element was found or mime attribute of resolver element is null => escape this resolver + return EXIT_MIME_TYPE; + } + // all matched + return mime; } } catch (IOException io) { Logger.getLogger(MIMEResolverImpl.class.getName()).log(Level.INFO, null, io); @@ -475,6 +561,7 @@ /** * For debug puroses only. */ + @Override public String toString() { StringBuffer buf = new StringBuffer(); buf.append("FileElement("); @@ -497,16 +584,177 @@ private static class Type { Type() {} private String[] exts; + private static final String EMPTY_EXTENSION = ""; //NOI18N private String[] mimes; private String[] fatts; + private List patterns; + private FilePattern lastAddedPattern = null; + private List names; private String[] vals; // contains null or value of attribute at the same index private byte[] magic; private byte[] mask; + private boolean exit = false; - + /** Used to search in the file for given pattern in given range. If there is an inner + * pattern element, it is used only if outer is fulfilled. Searching starts + * always from the beginning of the file. For example: + *

+ * 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(); + } + lastAddedPattern = new FilePattern(value, range, ignoreCase); + patterns.add(lastAddedPattern); + } + + private void addInnerPattern(String value, int range, boolean ignoreCase) { + FilePattern inner = new FilePattern(value, range, ignoreCase); + lastAddedPattern.setInner(inner); + lastAddedPattern = inner; + } + + private void addName(String name, boolean substring, boolean ignoreCase) { + if (names == null) { + names = new ArrayList(); + } + names.add(new FileName(name, substring, ignoreCase)); + } + private boolean setMagic(byte[] magic, byte[] mask) { if (magic == null) return true; if (mask != null && magic.length != mask.length) return false; @@ -570,15 +853,34 @@ return true; } + private void setExit() { + exit = true; + } + @SuppressWarnings("deprecation") private static String getMIMEType(String extension) { return FileUtil.getMIMEType(extension); } + + /** #26521, 114976 - ignore not readable and windows' locked files. */ + private static void handleIOException(FileObject fo, IOException ioe) throws IOException { + if (fo.canRead()) { + if (!Utilities.isWindows() || !(ioe instanceof FileNotFoundException) || !fo.isValid() || fo.getName().toLowerCase().indexOf("ntuser") == -1) {//NOI18N + throw ioe; + } + } + } + private boolean accept(FileObject fo) throws IOException { // check for resource extension if (exts != null) { - if (fo.getExt() == null) return false; - if (!Util.contains(exts, fo.getExt(), CASE_INSENSITIVE)) return false; + String ext = fo.getExt(); + if (ext == null) { + ext = EMPTY_EXTENSION; + } + if (!Util.contains(exts, ext, CASE_INSENSITIVE)) { + return false; + } } // check for resource mime type @@ -613,63 +915,36 @@ if (magic != null) { byte[] header = new byte[magic.length]; -// System.err.println("FO" + fo); - -// String m = mask == null ? "" : " mask " + XMLUtil.toHex(mask, 0, mask.length); -// System.err.println("Magic test " + XMLUtil.toHex(magic, 0, magic.length) + m); - // fetch header InputStream in = null; - boolean unexpectedEnd = false; try { in = fo.getInputStream(); - for (int i = 0; i escape loop, otherwise continue + matched = true; + break; + } + } + if (!matched) { + return false; + } + } catch (IOException ioe) { + handleIOException(fo, ioe); + return false; + } + } + + // check file name + if (names != null) { + boolean matched = false; + for (FileName name : names) { + if(name.match(fo)) { + // at least one matched => escape loop, otherwise continue + matched = true; + break; + } + } + if (!matched) { + return false; + } + } + // all templates matched return true; } - } } diff --git a/openide.filesystems/src/org/netbeans/modules/openide/filesystems/declmime/resolver.dtd b/openide.filesystems/src/org/netbeans/modules/openide/filesystems/declmime/resolver.dtd --- a/openide.filesystems/src/org/netbeans/modules/openide/filesystems/declmime/resolver.dtd +++ b/openide.filesystems/src/org/netbeans/modules/openide/filesystems/declmime/resolver.dtd @@ -27,7 +27,7 @@ Contributor(s): The Original Software is NetBeans. The Initial Developer of the Original -Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun +Software is Sun Microsystems, Inc. Portions Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. If you wish your version of this file to be governed by only the CDDL @@ -85,10 +85,11 @@ so there is a danger of recursion if these call 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 @@

Declarative MIME Type Resolvers

-

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.

-

Using the MIME Resolver

- -

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.

-

Links

RFC 3023: XML Media Types diff --git a/openide.filesystems/src/org/openide/filesystems/doc-files/resolverDocumentation.html b/openide.filesystems/src/org/openide/filesystems/doc-files/resolverDocumentation.html --- a/openide.filesystems/src/org/openide/filesystems/doc-files/resolverDocumentation.html +++ b/openide.filesystems/src/org/openide/filesystems/doc-files/resolverDocumentation.html @@ -42,50 +42,54 @@ --> -DTD grammar documentation + +DTD Grammar Documentation + + - + +

Element Index

-
  • MIME-resolver
  • -
  • attr
  • -
  • doctype
  • -
  • element
  • -
  • exit
  • -
  • ext
  • -
  • fattr
  • -
  • file
  • -
  • magic
  • -
  • mime
  • -
  • ns
  • -
  • pi
  • -
  • public-id
  • -
  • resolver
  • -
  • xml-rule
  • + +

    Element Details

    -
    -

    MIME-resolver

    -

    MIME resolver description root element. +

    MIME-resolver

    +
    MIME resolver description root element.

    PUBLIC "-//NetBeans//DTD MIME Resolver 1.0//EN" -

    +

    Element Content Model

    -(file)+ +

    (file)+

    Referenced by

    +

    - - -
    -

    xml-rule

    -

    MIME component checking XML resource header, permitting matches against +

    xml-rule

    +
    MIME component checking XML resource header, permitting matches against XML constructs: processing instructions, document type declarations, and root element (start tag).

    PUBLIC "-//NetBeans//DTD MIME Resolver XML Rules 1.0//EN" @@ -98,235 +102,281 @@

    Using: PIs and DOCTYPE public IDs.
    NS-constrained documents
    Using: PIs and root element namespaces.
    -

    +

    Element Content Model

    -(pi*, doctype?, element?) +

    (pi*, doctype?, element?)

    Referenced by

    -resolver +

    resolver

    - -
    -

    pi

    -

    Processing instructions can be tested on target and pseudo-attributes. +

    pi

    +
    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

    -
    • #REQUIRED CDATA target
    • -
    +
    • #REQUIRED CDATA target
    • +

    Element Content Model

    -(attr*) +

    (attr*)

    Referenced by

    -xml-rule +

    xml-rule

    - -
    -

    doctype

    -

    Match one of the DOCTYPE's public IDs. - Use of public-id subelements is mutually exclusive with the attribute.

    +

    doctype

    +
    Match one of the DOCTYPE's public IDs. + Use of public-id subelements is mutually exclusive with the attribute.

    Declared Attributes

    -
    • #IMPLIED CDATA public-id
    • -
    +
    • #IMPLIED CDATA public-id
    • +

    Element Content Model

    -(public-id*) +

    (public-id*)

    Referenced by

    -xml-rule +

    xml-rule

    - -
    -

    public-id

    -

    A public ID is checked for an exact match.

    +

    public-id

    +
    A public ID is checked for an exact match.

    Declared Attributes

    -
    • #REQUIRED CDATA id
    • -
    +
    • #REQUIRED CDATA id
    • +

    Element Content Model

    -EMPTY +

    EMPTY

    Referenced by

    -doctype +

    doctype

    - -
    -

    element

    -

    Matches a root element by specified name, attributes or namespace. +

    element

    +
    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

    -
    • #IMPLIED CDATA ns
    • -
    • #IMPLIED CDATA name
    • -
    +
    • #IMPLIED CDATA ns
    • +
    • #IMPLIED CDATA name
    • +

    Element Content Model

    -(ns*, attr*) +

    (ns*, attr*)

    Referenced by

    -xml-rule +

    xml-rule

    - -
    -

    ns

    -

    Test namespace equality (exact match).

    +

    ns

    +
    Test namespace equality (exact match).

    Declared Attributes

    -
    • #REQUIRED CDATA ns
    • -
    +
    • #REQUIRED CDATA ns
    • +

    Element Content Model

    -EMPTY +

    EMPTY

    Referenced by

    -element +

    element

    - -
    -

    attr

    -

    Element attribute template, also used for pseudo-attributes in processing instructions. +

    attr

    +
    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.

    If 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

    -
    • #REQUIRED CDATA name
    • -
    • #IMPLIED CDATA text
    • -
    +
    • #REQUIRED CDATA name
    • +
    • #IMPLIED CDATA text
    • +

    Element Content Model

    -EMPTY +

    EMPTY

    Referenced by

    -pi, element +

    pi, element

    - -
    -

    file

    -

    A file (resource) represents the MIME resolver input. +

    file

    +
    A file (resource) represents the MIME resolver input. The resource is tested on attributes obtained from lower layers (OS) such as extension, header bytes and wrapping 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 +

    MIME-resolver

    - -
    -

    ext

    -

    Tests resource extension for full equality.

    +

    ext

    +
    Tests resource extension for full equality. If name attribute is empty string + (name=""), it matches all files without extension.

    Declared Attributes

    -
    • #REQUIRED CDATA name
    • -
    +
    • #REQUIRED CDATA name
    • +

    Element Content Model

    -EMPTY +

    EMPTY

    Referenced by

    -file +

    file

    - -
    -

    mime

    -

    Tests resource MIME type for equality (RFC2045) or suffix equality if it - starts with '+' (RFC 3023).

    +

    mime

    +
    Tests resource MIME type for equality (RFC2045) or suffix equality if it + starts with '+' (RFC 3023).

    Declared Attributes

    -
    • #REQUIRED CDATA name
    • -
    +
    • #REQUIRED CDATA name
    • +

    Element Content Model

    -EMPTY +

    EMPTY

    Referenced by

    -file +

    file

    - -
    -

    magic

    -

    Look at initial bytes of the file and test for a complete match of masked +

    magic

    +
    Look at initial bytes of the file and test for a complete match of masked bits. The default mask is the hexadecimal byte FF repeated as many times as the hex attribute is long. - E.g. <magic hex="0a0001" mask="FF00FF"/>

    + E.g. <magic hex="0a0001" mask="FF00FF"/>

    Declared Attributes

    -
    • #REQUIRED CDATA hex
    • -
    • #IMPLIED CDATA mask
    • -
    +
    • #REQUIRED CDATA hex
    • +
    • #IMPLIED CDATA mask
    • +

    Element Content Model

    -EMPTY +

    EMPTY

    Referenced by

    -file +

    file

    - -
    -

    fattr

    -

    Test on FileObject attributes. Matching attributes are converted +

    fattr

    +
    Test on 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

    -
    • #REQUIRED CDATA name
    • -
    • #REQUIRED CDATA text
    • -
    +
    • #REQUIRED CDATA name
    • +
    • #REQUIRED CDATA text
    • +

    Element Content Model

    -EMPTY +

    EMPTY

    Referenced by

    -file +

    file

    - -
    -

    resolver

    -

    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

    +
    Search in the file for given pattern in given range. If there is an inner + pattern element, it is used only if outer is fulfilled. Searching starts + always from the beginning of the file. For example: +

    + 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

    -
    • #REQUIRED CDATA mime
    • -
    +
    • #REQUIRED CDATA value
    • +
    • #REQUIRED CDATA range
    • +
    • #IMPLIED CDATA ignorecase
    • +

    Element Content Model

    -(xml-rule)? +

    (pattern?)

    Referenced by

    -file +

    file, pattern

    - -
    -

    exit

    -

    Declares that this file is not recognized by this resolver. - A shortcut for <resolver mime="null"/>.

    +

    name

    +
    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"/>
    +  
    +

    +

    Declared Attributes

    +
    • #REQUIRED CDATA name
    • +
    • #IMPLIED CDATA substring
    • +
    • #IMPLIED CDATA ignorecase
    • +

    Element Content Model

    -EMPTY +

    EMPTY

    Referenced by

    -file +

    file

    + +
    +

    resolver

    +
    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/>).
    +

    Declared Attributes

    +
    • #REQUIRED CDATA mime
    • +
    +

    Element Content Model

    +

    (xml-rule)?

    +

    Referenced by

    +

    file

    + + +
    +

    exit

    +
    Declares that this file is not recognized by this resolver. + A shortcut for <resolver mime="null"/>. + For example: +

    + 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

    +

    file

    \ 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 resolvers; FileObject root; + FileObject resolversRoot; public MIMEResolverImplTest(String testName) { super(testName); } - + + public static Test suite() { + Test suite = null; + //suite = new MIMEResolverImplTest("testPatternElement"); + if (suite == null) { + suite = new NbTestSuite(MIMEResolverImplTest.class); + } + return suite; + } + @SuppressWarnings("deprecation") @Override protected void setUp() throws Exception { URL u = this.getClass().getResource ("code-fs.xml"); FileSystem fs = new XMLFileSystem(u); - FileObject coderoot = fs.getRoot().getFileObject("root"); - coderoot.refresh(); + resolversRoot = fs.getRoot().getFileObject("root"); + resolversRoot.refresh(); - FileObject fos[] = coderoot.getChildren(); + FileObject fos[] = resolversRoot.getChildren(); resolvers = new ArrayList(); for (int i = 0; i + + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + + ]]> + + + + + + + + + + + + + ]]> + + + + + + + + + + + ]]> + + + + + + + + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + + ]]> + + + + + + + + + ]]> + + + + + + + + + + ]]> + + + + + + + + ]]> + + + + + + + + + + + + + + ]]> + + + + + + + + + + + + + + ]]> + diff --git a/openide.filesystems/test/unit/src/org/netbeans/modules/openide/filesystems/declmime/data-fs.xml b/openide.filesystems/test/unit/src/org/netbeans/modules/openide/filesystems/declmime/data-fs.xml --- a/openide.filesystems/test/unit/src/org/netbeans/modules/openide/filesystems/declmime/data-fs.xml +++ b/openide.filesystems/test/unit/src/org/netbeans/modules/openide/filesystems/declmime/data-fs.xml @@ -37,5 +37,44 @@ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> ]]> + + + + + +0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 + + + + + + + + + + + + + + + + + + +