[103768] update resolver to distinguish between physical and logical resolutin results
diff --git a/plugins/org.eclipse.wst.common.uriresolver/src/org/eclipse/wst/common/uriresolver/internal/ExtensibleURIResolver.java b/plugins/org.eclipse.wst.common.uriresolver/src/org/eclipse/wst/common/uriresolver/internal/ExtensibleURIResolver.java
index dff7bba..d50c258 100644
--- a/plugins/org.eclipse.wst.common.uriresolver/src/org/eclipse/wst/common/uriresolver/internal/ExtensibleURIResolver.java
+++ b/plugins/org.eclipse.wst.common.uriresolver/src/org/eclipse/wst/common/uriresolver/internal/ExtensibleURIResolver.java
@@ -94,6 +94,34 @@
return result;
}
+
+ public String resolvePhysicalLocation(String baseLocation, String publicId, String logicalLocation)
+ {
+ String result = logicalLocation;
+ URIResolverExtensionRegistry resolverRegistry = URIResolverExtensionRegistry.getIntance();
+ IFile file = computeFile(baseLocation);
+
+ // compute the project that holds the resource
+ //
+ IProject project = file != null ? file.getProject() : null;
+ List list = resolverRegistry.getExtensionDescriptors(project);
+ for (Iterator i = resolverRegistry.getMatchingURIResolvers(list, URIResolverExtensionRegistry.STAGE_PHYSICAL).iterator(); i.hasNext(); )
+ {
+ // get the list of applicable physical resolvers from the extension registry
+ //
+ while (i.hasNext())
+ {
+ URIResolverExtension resolver = (URIResolverExtension) i.next();
+ String tempresult = resolver.resolve(file, baseLocation, publicId, result);
+ if(tempresult != null)
+ {
+ result = tempresult;
+ }
+ }
+ }
+ return result;
+ }
+
protected String normalize(String baseLocation, String systemId)
{
diff --git a/plugins/org.eclipse.wst.common.uriresolver/src/org/eclipse/wst/common/uriresolver/internal/URI.java b/plugins/org.eclipse.wst.common.uriresolver/src/org/eclipse/wst/common/uriresolver/internal/URI.java
index 3aac823..6003437 100644
--- a/plugins/org.eclipse.wst.common.uriresolver/src/org/eclipse/wst/common/uriresolver/internal/URI.java
+++ b/plugins/org.eclipse.wst.common.uriresolver/src/org/eclipse/wst/common/uriresolver/internal/URI.java
@@ -1,23 +1,26 @@
/*
-* Copyright (c) 2002 IBM Corporation and others.
-* All rights reserved. This program and the accompanying materials
-* are made available under the terms of the Common Public License v1.0
-* which accompanies this distribution, and is available at
-* http://www.eclipse.org/legal/cpl-v10.html
-*
-* Contributors:
-* IBM - Initial API and implementation
-* Jens Lukowski/Innoopract - initial renaming/restructuring
-*
-*/
+ * Copyright (c) 2002 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM - Initial API and implementation
+ * Jens Lukowski/Innoopract - initial renaming/restructuring
+ *
+ */
package org.eclipse.wst.common.uriresolver.internal;
import java.io.File;
-
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
/**
* A representation of a Uniform Resource Identifier (URI), as specified by
@@ -42,9 +45,9 @@
* locate resources via common protocols such as HTTP, FTP, and Gopher, and
* to identify files on a local file system. Acordingly, most of this
* class's functionality is for handling such URIs, which can be identified
- * via {@link #isHierarchical()}.
+ * via {@link #isHierarchical isHierarchical}.
*
- * <p><a name="device_explaination">
+ * <p><a name="device_explanation">
* The primary enhancement beyond the RFC description is an optional
* device component. Instead of treating the device as just another segment
* in the path, it can be stored as a separate component (almost a
@@ -55,22 +58,59 @@
* <code>file:///c:/</code> would not yield <code>file:///</code>, as you
* might expect. This feature is useful when working with file-scheme
* URIs, as devices do not typically occur in protocol-based ones. A
- * device-enabled <code>URI</code> can be created by parsing a string with
- * {@link #createURI} or by specifying a non-null <code>device</code>
- * paramter for either the {@link #createHierarchicalURI(String, String,
- * String, String, String) no-path} or the {@link
- * #createHierarchicalURI(String, String, String, String[], String, String)
- * absolute-path} form of <code>createHierarchicalURI()</code>.
+ * device-enabled <code>URI</code> is created by parsing a string with
+ * {@link #createURI(String) createURI}; if the first segment of the path
+ * ends with the <code>:</code> character, it is stored (including the colon)
+ * as the device, instead. Alternately, either the {@link
+ * #createHierarchicalURI(String, String, String, String, String) no-path}
+ * or the {@link #createHierarchicalURI(String, String, String, String[],
+ * String, String) absolute-path} form of <code>createHierarchicalURI()</code>
+ * can be used, in which a non-null <code>device</code> parameter can be
+ * specified.
*
- * <p>Compared to the RFC description, this implementation is quite relaxed
- * about validity. Static methods whose names begin with "valid" test
- * whether a given string is a valid value for the various URI components.
- * Presently, these tests place no restrictions beyond what would have been
- * required in order for {@link #createURI} to
- * have parsed them correctly from a single URI string. Note that all of
- * the static factory methods invoke the appropriate validation methods and
- * throw exceptions in response to a negative result, ensuring that
- * invalid URIs are never created.
+ * <p><a name="archive_explanation">
+ * The other enhancement provides support for the almost-hierarchical
+ * form used for files within archives, such as the JAR scheme, defined
+ * for the Java Platform in the documentation for {@link
+ * java.net.JarURLConnection}. By default, this support is enabled for
+ * absolute URIs with scheme equal to "jar", "zip", or "archive" (ignoring case), and
+ * is implemented by a hierarchical URI, whose authority includes the
+ * entire URI of the archive, up to and including the <code>!</code>
+ * character. The URI of the archive must have no fragment. The whole
+ * archive URI must have no device and an absolute path. Special handling
+ * is supported for {@link #createURI creating}, {@link
+ * #validArchiveAuthority validating}, {@link #devicePath getting the path}
+ * from, and {@link #toString displaying} archive URIs. In all other
+ * operations, including {@link #resolve(URI) resolving} and {@link
+ * #deresolve(URI) deresolving}, they are handled like any ordinary URI.
+ *
+ * <p>This implementation does not impose the all of the restrictions on
+ * character validity that are specified in the RFC. Static methods whose
+ * names begin with "valid" are used to test whether a given string is valid
+ * value for the various URI components. Presently, these tests place no
+ * restrictions beyond what would have been required in order for {@link
+ * createURI(String) createURI} to have parsed them correctly from a single
+ * URI string. If necessary in the future, these tests may be made more
+ * strict, to better coform to the RFC.
+ *
+ * <p>Another group of static methods, whose names begin with "encode", use
+ * percent escaping to encode any characters that are not permitted in the
+ * various URI components. Another static method is provided to {@link
+ * #decode decode} encoded strings. An escaped character is represented as
+ * a percent sybol (<code>%</code>), followed by two hex digits that specify
+ * the character code. These encoding methods are more strict than the
+ * validation methods described above. They ensure validity according to the
+ * RFC, with one exception: non-ASCII characters.
+ *
+ * <p>The RFC allows only characters that can be mapped to 7-bit US-ASCII
+ * representations. Non-ASCII, single-byte characters can be used only via
+ * percent escaping, as described above. This implementation uses Java's
+ * Unicode <code>char</code> and <code>String</code> representations, and
+ * makes no attempt to encode characters 0xA0 and above. Characters in the
+ * range 0x80-0x9F are still escaped. In this respect, this notion of a URI
+ * is actually more like an IRI (Internationalized Resource Identifier), for
+ * which an RFC is now in <href="http://www.w3.org/International/iri-edit/draft-duerst-iri-09.txt">draft
+ * form</a>.
*
* <p>Finally, note the difference between a <code>null</code> parameter to
* the static factory methods and an empty string. The former signifies the
@@ -94,6 +134,8 @@
private final String fragment;
private URI cachedTrimFragment;
private String cachedToString;
+ //private final boolean iri;
+ //private URI cachedASCIIURI;
// Applicable only to a hierarchical URI.
private final String device;
@@ -101,9 +143,20 @@
private final String[] segments; // empty last segment -> trailing separator
private final String query;
+ // A cache of URIs, keyed by the strings from which they were created.
+ // The fragment of any URI is removed before caching it here, to minimize
+ // the size of the cache in the usual case where most URIs only differ by
+ // the fragment.
+ private static final Map uriCache = Collections.synchronizedMap(new HashMap());
+
+ // The lower-cased schemes that will be used to identify archive URIs.
+ private static final Set archiveSchemes;
+
// Identifies a file-type absolute URI.
private static final String SCHEME_FILE = "file";
private static final String SCHEME_JAR = "jar";
+ private static final String SCHEME_ZIP = "zip";
+ private static final String SCHEME_ARCHIVE = "archive";
// Special segment values interpreted at resolve and resolve time.
private static final String SEGMENT_EMPTY = "";
@@ -111,7 +164,7 @@
private static final String SEGMENT_PARENT = "..";
private static final String[] NO_SEGMENTS = new String[0];
- // Separators for parsing a URI string
+ // Separators for parsing a URI string.
private static final char SCHEME_SEPARATOR = ':';
private static final String AUTHORITY_SEPARATOR = "//";
private static final char DEVICE_IDENTIFIER = ':';
@@ -121,10 +174,146 @@
private static final char USER_INFO_SEPARATOR = '@';
private static final char PORT_SEPARATOR = ':';
private static final char FILE_EXTENSION_SEPARATOR = '.';
- private static final char[] MAJOR_SEPARATORS = {
- SCHEME_SEPARATOR, SEGMENT_SEPARATOR, QUERY_SEPARATOR, FRAGMENT_SEPARATOR };
- private static final char[] SEGMENT_END = {
- SEGMENT_SEPARATOR, QUERY_SEPARATOR, FRAGMENT_SEPARATOR };
+ private static final char ARCHIVE_IDENTIFIER = '!';
+ private static final String ARCHIVE_SEPARATOR = "!/";
+
+ // Characters to use in escaping.
+ private static final char ESCAPE = '%';
+ private static final char[] HEX_DIGITS = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+
+ // Some character classes, as defined in RFC 2396's BNF for URI.
+ // These are 128-bit bitmasks, stored as two longs, where the Nth bit is set
+ // iff the ASCII character with value N is included in the set. These are
+ // created with the highBitmask() and lowBitmask() methods defined below,
+ // and a character is tested against them using matches().
+ //
+ private static final long ALPHA_HI = highBitmask('a', 'z') | highBitmask('A', 'Z');
+ private static final long ALPHA_LO = lowBitmask('a', 'z') | lowBitmask('A', 'Z');
+ private static final long DIGIT_HI = highBitmask('0', '9');
+ private static final long DIGIT_LO = lowBitmask('0', '9');
+ private static final long ALPHANUM_HI = ALPHA_HI | DIGIT_HI;
+ private static final long ALPHANUM_LO = ALPHA_LO | DIGIT_LO;
+ private static final long HEX_HI = DIGIT_HI | highBitmask('A', 'F') | highBitmask('a', 'f');
+ private static final long HEX_LO = DIGIT_LO | lowBitmask('A', 'F') | lowBitmask('a', 'f');
+ private static final long UNRESERVED_HI = ALPHANUM_HI | highBitmask("-_.!~*'()");
+ private static final long UNRESERVED_LO = ALPHANUM_LO | lowBitmask("-_.!~*'()");
+ private static final long RESERVED_HI = highBitmask(";/?:@&=+$,");
+ private static final long RESERVED_LO = lowBitmask(";/?:@&=+$,");
+ private static final long URIC_HI = RESERVED_HI | UNRESERVED_HI; // | ucschar | escaped
+ private static final long URIC_LO = RESERVED_LO | UNRESERVED_LO;
+
+ // Additional useful character classes, including characters valid in certain
+ // URI components and separators used in parsing them out of a string.
+ //
+ private static final long SEGMENT_CHAR_HI = UNRESERVED_HI | highBitmask(";:@&=+$,"); // | ucschar | escaped
+ private static final long SEGMENT_CHAR_LO = UNRESERVED_LO | lowBitmask(";:@&=+$,");
+ private static final long PATH_CHAR_HI = SEGMENT_CHAR_HI | highBitmask('/'); // | ucschar | escaped
+ private static final long PATH_CHAR_LO = SEGMENT_CHAR_LO | lowBitmask('/');
+// private static final long SCHEME_CHAR_HI = ALPHANUM_HI | highBitmask("+-.");
+// private static final long SCHEME_CHAR_LO = ALPHANUM_LO | lowBitmask("+-.");
+ private static final long MAJOR_SEPARATOR_HI = highBitmask(":/?#");
+ private static final long MAJOR_SEPARATOR_LO = lowBitmask(":/?#");
+ private static final long SEGMENT_END_HI = highBitmask("/?#");
+ private static final long SEGMENT_END_LO = lowBitmask("/?#");
+
+ // Static initializer for archiveSchemes.
+ static
+ {
+ Set set = new HashSet();
+ set.add(SCHEME_JAR);
+ set.add(SCHEME_ZIP);
+ set.add(SCHEME_ARCHIVE);
+
+
+ archiveSchemes = Collections.unmodifiableSet(set);
+ }
+
+ // Returns the lower half bitmask for the given ASCII character.
+ private static long lowBitmask(char c)
+ {
+ return c < 64 ? 1L << c : 0L;
+ }
+
+ // Returns the upper half bitmask for the given ACSII character.
+ private static long highBitmask(char c)
+ {
+ return c >= 64 && c < 128 ? 1L << (c - 64) : 0L;
+ }
+
+ // Returns the lower half bitmask for all ASCII characters between the two
+ // given characters, inclusive.
+ private static long lowBitmask(char from, char to)
+ {
+ long result = 0L;
+ if (from < 64 && from <= to)
+ {
+ to = to < 64 ? to : 63;
+ for (char c = from; c <= to; c++)
+ {
+ result |= (1L << c);
+ }
+ }
+ return result;
+ }
+
+ // Returns the upper half bitmask for all AsCII characters between the two
+ // given characters, inclusive.
+ private static long highBitmask(char from, char to)
+ {
+ return to < 64 ? 0 : lowBitmask((char)(from < 64 ? 0 : from - 64), (char)(to - 64));
+ }
+
+ // Returns the lower half bitmask for all the ASCII characters in the given
+ // string.
+ private static long lowBitmask(String chars)
+ {
+ long result = 0L;
+ for (int i = 0, len = chars.length(); i < len; i++)
+ {
+ char c = chars.charAt(i);
+ if (c < 64) result |= (1L << c);
+ }
+ return result;
+ }
+
+ // Returns the upper half bitmask for all the ASCII characters in the given
+ // string.
+ private static long highBitmask(String chars)
+ {
+ long result = 0L;
+ for (int i = 0, len = chars.length(); i < len; i++)
+ {
+ char c = chars.charAt(i);
+ if (c >= 64 && c < 128) result |= (1L << (c - 64));
+ }
+ return result;
+ }
+
+ // Returns whether the given character is in the set specified by the given
+ // bitmask.
+ private static boolean matches(char c, long highBitmask, long lowBitmask)
+ {
+ if (c >= 128) return false;
+ return c < 64 ?
+ ((1L << c) & lowBitmask) != 0 :
+ ((1L << (c - 64)) & highBitmask) != 0;
+ }
+
+ // Debugging method: converts the given long to a string of binary digits.
+/*
+ private static String toBits(long l)
+ {
+ StringBuffer result = new StringBuffer();
+ for (int i = 0; i < 64; i++)
+ {
+ boolean b = (l & 1L) != 0;
+ result.insert(0, b ? '1' : '0');
+ l >>= 1;
+ }
+ return result.toString();
+ }
+*/
/**
* Static factory method for a generic, non-hierarchical URI. There is no
@@ -132,9 +321,11 @@
* created.
*
* @exception java.lang.IllegalArgumentException if <code>scheme</code> is
- * null, or if <code>scheme</code>, <code>opaquePart</code>, or
- * <code>fragment</code> is not valid according to {@link #validScheme},
- * {@link #validOpaquePart}, or {@link #validFragment}, respectively.
+ * null, if <code>scheme</code> is an <a href="#archive_explanation">archive
+ * URI</a> scheme, or if <code>scheme</code>, <code>opaquePart</code>, or
+ * <code>fragment</code> is not valid according to {@link #validScheme
+ * validScheme}, {@link #validOpaquePart validOpaquePart}, or {@link
+ * #validFragment validFragment}, respectively.
*/
public static URI createGenericURI(String scheme, String opaquePart,
String fragment)
@@ -144,8 +335,13 @@
throw new IllegalArgumentException("relative non-hierarchical URI");
}
- return new URI(false, scheme, opaquePart, null, false, NO_SEGMENTS,
- null, fragment);
+ if (isArchiveScheme(scheme))
+ {
+ throw new IllegalArgumentException("non-hierarchical archive URI");
+ }
+
+ validateURI(false, scheme, opaquePart, null, false, NO_SEGMENTS, null, fragment);
+ return new URI(false, scheme, opaquePart, null, false, NO_SEGMENTS, null, fragment);
}
/**
@@ -156,10 +352,13 @@
*
* @exception java.lang.IllegalArgumentException if <code>scheme</code> is
* non-null while <code>authority</code> and <code>device</code> are null,
- * or if <code>scheme</code>, <code>authority</code>, <code>device</code>,
- * <code>query</code>, or <code>fragment</code> is not valid according to
- * {@link #validScheme}, {@link #validAuthority}, {@link #validDevice},
- * {@link #validQuery}, or {@link #validFragment}, respectively.
+ * if <code>scheme</code> is an <a href="#archive_explanation">archive
+ * URI</a> scheme, or if <code>scheme</code>, <code>authority</code>,
+ * <code>device</code>, <code>query</code>, or <code>fragment</code> is not
+ * valid according to {@link #validScheme validSheme}, {@link
+ * #validAuthority validAuthority}, {@link #validDevice validDevice},
+ * {@link #validQuery validQuery}, or {@link #validFragment validFragment},
+ * respectively.
*/
public static URI createHierarchicalURI(String scheme, String authority,
String device, String query,
@@ -171,8 +370,13 @@
"absolute hierarchical URI without authority, device, path");
}
- return new URI(true, scheme, authority, device, false, NO_SEGMENTS,
- query, fragment);
+ if (isArchiveScheme(scheme))
+ {
+ throw new IllegalArgumentException("archive URI with no path");
+ }
+
+ validateURI(true, scheme, authority, device, false, NO_SEGMENTS, query, fragment);
+ return new URI(true, scheme, authority, device, false, NO_SEGMENTS, query, fragment);
}
/**
@@ -186,19 +390,29 @@
* separator should be represented by an empty-string segment as the last
* element of the array.
*
- * @exception java.lang.IllegalArgumentException if <code>scheme</code>,
+ * @exception java.lang.IllegalArgumentException if <code>scheme</code> is
+ * an <a href="#archive_explanation">archive URI</a> scheme and
+ * <code>device</code> is non-null, or if <code>scheme</code>,
* <code>authority</code>, <code>device</code>, <code>segments</code>,
* <code>query</code>, or <code>fragment</code> is not valid according to
- * {@link #validScheme}, {@link #validAuthority}, {@link #validDevice},
- * {@link #validSegments}, {@link #validQuery}, or {@link #validFragment},
- * respectively.
+ * {@link #validScheme validScheme}, {@link #validAuthority validAuthority}
+ * or {@link #validArchiveAuthority validArchiveAuthority}, {@link
+ * #validDevice validDevice}, {@link #validSegments validSegments}, {@link
+ * #validQuery validQuery}, or {@link #validFragment validFragment}, as
+ * appropriate.
*/
public static URI createHierarchicalURI(String scheme, String authority,
String device, String[] segments,
String query, String fragment)
{
- return new URI(true, scheme, authority, device, true, fix(segments),
- query, fragment);
+ if (isArchiveScheme(scheme) && device != null)
+ {
+ throw new IllegalArgumentException("archive URI with device");
+ }
+
+ segments = fix(segments);
+ validateURI(true, scheme, authority, device, true, segments, query, fragment);
+ return new URI(true, scheme, authority, device, true, segments, query, fragment);
}
/**
@@ -211,14 +425,15 @@
*
* @exception java.lang.IllegalArgumentException if <code>segments</code>,
* <code>query</code>, or <code>fragment</code> is not valid according to
- * {@link #validSegments}, {@link #validQuery}, or {@link #validFragment},
- * respectively.
+ * {@link #validSegments validSegments}, {@link #validQuery validQuery}, or
+ * {@link #validFragment validFragment}, respectively.
*/
public static URI createHierarchicalURI(String[] segments, String query,
String fragment)
{
- return new URI(true, null, null, null, false, fix(segments), query,
- fragment);
+ segments = fix(segments);
+ validateURI(true, null, null, null, false, segments, query, fragment);
+ return new URI(true, null, null, null, false, segments, query, fragment);
}
// Converts null to length-zero array, and clones array to ensure
@@ -230,8 +445,9 @@
/**
* Static factory method based on parsing a URI string, with
- * <a href="#device_explaination">explicit device support</a> enabled.
- * The specified string is parsed as described in <a
+ * <a href="#device_explanation">explicit device support</a> and handling
+ * for <a href="#archive_explanation">archive URIs</a> enabled. The
+ * specified string is parsed as described in <a
* href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>, and an
* appropriate <code>URI</code> is created and returned. Note that
* validity testing is not as strict as in the RFC; essentially, only
@@ -240,33 +456,98 @@
* error.
*
* @exception java.lang.IllegalArgumentException if any component parsed
- * from <code>uri</code> is not valid according to {@link #validScheme},
- * {@link #validOpaquePart}, {@link #validAuthority}, {@link
- * #validDevice}, {@link #validSegments}, {@link #validQuery}, or {@link
- * #validFragment}, as appropriate.
+ * from <code>uri</code> is not valid according to {@link #validScheme
+ * validScheme}, {@link #validOpaquePart validOpaquePart}, {@link
+ * #validAuthority validAuthority}, {@link #validArchiveAuthority
+ * validArchiveAuthority}, {@link #validDevice validDevice}, {@link
+ * #validSegments validSegments}, {@link #validQuery validQuery}, or {@link
+ * #validFragment validFragment}, as appropriate.
*/
public static URI createURI(String uri)
{
- return parseIntoURI(uri);
+ return createURIWithCache(uri);
+ }
+
+ /**
+ * Static factory method that encodes and parses the given URI string.
+ * Appropriate encoding is performed for each component of the URI.
+ * If more than one <code>#</code> is in the string, the last one is
+ * assumed to be the fragment's separator, and any others are encoded.
+ *
+ * @param ignoreEscaped <code>true</code> to leave <code>%</code> characters
+ * unescaped if they already begin a valid three-character escape sequence;
+ * <code>false</code> to encode all <code>%</code> characters. Note that
+ * if a <code>%</code> is not followed by 2 hex digits, it will always be
+ * escaped.
+ *
+ * @exception java.lang.IllegalArgumentException if any component parsed
+ * from <code>uri</code> is not valid according to {@link #validScheme
+ * validScheme}, {@link #validOpaquePart validOpaquePart}, {@link
+ * #validAuthority validAuthority}, {@link #validArchiveAuthority
+ * validArchiveAuthority}, {@link #validDevice validDevice}, {@link
+ * #validSegments validSegments}, {@link #validQuery validQuery}, or {@link
+ * #validFragment validFragment}, as appropriate.
+ */
+ public static URI createURI(String uri, boolean ignoreEscaped)
+ {
+ return createURIWithCache(encodeURI(uri, ignoreEscaped));
}
/**
* Static factory method based on parsing a URI string, with
- * <a href="#device_explaination">explicit device support</a> enabled.
+ * <a href="#device_explanation">explicit device support</a> enabled.
* Note that validity testing is not a strict as in the RFC; essentially,
* only separator characters are considered. So, for example, non-Latin
* alphabet characters appearing in the scheme would not be considered an
* error.
+ *
* @exception java.lang.IllegalArgumentException if any component parsed
- * from <code>uri</code> is not valid according to {@link #validScheme},
- * {@link #validOpaquePart}, {@link #validAuthority}, {@link
- * #validDevice}, {@link #validSegments}, {@link #validQuery}, or {@link
- * #validFragment}, as appropriate.
- * @deprecated
+ * from <code>uri</code> is not valid according to {@link #validScheme
+ * validScheme}, {@link #validOpaquePart validOpaquePart}, {@link
+ * #validAuthority validAuthority}, {@link #validArchiveAuthority
+ * validArchiveAuthority}, {@link #validDevice validDevice}, {@link
+ * #validSegments validSegments}, {@link #validQuery validQuery}, or {@link
+ * #validFragment validFragment}, as appropriate.
+ *
+ * @deprecated Use {@link #createURI createURI}, which now has explicit
+ * device support enabled. The two methods now operate identically.
*/
public static URI createDeviceURI(String uri)
{
- return parseIntoURI(uri);
+ return createURIWithCache(uri);
+ }
+
+ // Uses a cache to speed up creation of a URI from a string. The cache
+ // is consulted to see if the URI, less any fragment, has already been
+ // created. If needed, the fragment is re-appended to the cached URI,
+ // which is considerably more efficient than creating the whole URI from
+ // scratch. If the URI wasn't found in the cache, it is created using
+ // parseIntoURI() and then cached. This method should always be used
+ // by string-parsing factory methods, instead of parseIntoURI() directly.
+ /**
+ * This method was included in the public API by mistake.
+ *
+ * @deprecated Please use {@link #createURI createURI} instead.
+ */
+ public static URI createURIWithCache(String uri)
+ {
+ int i = uri.indexOf(FRAGMENT_SEPARATOR);
+ String base = i == -1 ? uri : uri.substring(0, i);
+ String fragment = i == -1 ? null : uri.substring(i + 1);
+
+ URI result = (URI)uriCache.get(base);
+
+ if (result == null)
+ {
+ result = parseIntoURI(base);
+ uriCache.put(base, result);
+ }
+
+ if (fragment != null)
+ {
+ result = result.appendFragment(fragment);
+ }
+ return result;
}
// String-parsing implementation.
@@ -282,7 +563,7 @@
String fragment = null;
int i = 0;
- int j = findSeparator(uri, i, MAJOR_SEPARATORS);
+ int j = find(uri, i, MAJOR_SEPARATOR_HI, MAJOR_SEPARATOR_LO);
if (j < uri.length() && uri.charAt(j) == SCHEME_SEPARATOR)
{
@@ -290,28 +571,38 @@
i = j + 1;
}
- if (uri.startsWith(AUTHORITY_SEPARATOR, i))
+ boolean archiveScheme = isArchiveScheme(scheme);
+ if (archiveScheme)
+ {
+ j = uri.lastIndexOf(ARCHIVE_SEPARATOR);
+ if (j == -1)
+ {
+ throw new IllegalArgumentException("no archive separator");
+ }
+ hierarchical = true;
+ authority = uri.substring(i, ++j);
+ i = j;
+ }
+ else if (uri.startsWith(AUTHORITY_SEPARATOR, i))
{
i += AUTHORITY_SEPARATOR.length();
- j = findSeparator(uri, i, SEGMENT_END);
+ j = find(uri, i, SEGMENT_END_HI, SEGMENT_END_LO);
authority = uri.substring(i, j);
i = j;
}
else if (scheme != null &&
- (i == uri.length() ||
- uri.charAt(i) != SEGMENT_SEPARATOR &&
- !(scheme.equalsIgnoreCase(SCHEME_FILE) || scheme.equalsIgnoreCase(SCHEME_JAR))))
+ (i == uri.length() || uri.charAt(i) != SEGMENT_SEPARATOR))
{
hierarchical = false;
- j = findSeparator(uri, i, new char[] { FRAGMENT_SEPARATOR });
+ j = uri.indexOf(FRAGMENT_SEPARATOR, i);
+ if (j == -1) j = uri.length();
authority = uri.substring(i, j);
i = j;
}
- if (i < uri.length() &&
- uri.charAt(i) == SEGMENT_SEPARATOR)
+ if (!archiveScheme && i < uri.length() && uri.charAt(i) == SEGMENT_SEPARATOR)
{
- j = findSeparator(uri, i + 1, SEGMENT_END);
+ j = find(uri, i + 1, SEGMENT_END_HI, SEGMENT_END_LO);
String s = uri.substring(i + 1, j);
if (s.length() > 0 && s.charAt(s.length() - 1) == DEVICE_IDENTIFIER)
@@ -333,7 +624,7 @@
while (segmentsRemain(uri, i))
{
- j = findSeparator(uri, i, SEGMENT_END);
+ j = find(uri, i, SEGMENT_END_HI, SEGMENT_END_LO);
segmentList.add(uri.substring(i, j));
i = j;
@@ -348,7 +639,8 @@
if (i < uri.length() && uri.charAt(i) == QUERY_SEPARATOR)
{
- j = findSeparator(uri, ++i, new char[] { FRAGMENT_SEPARATOR });
+ j = uri.indexOf(FRAGMENT_SEPARATOR, ++i);
+ if (j == -1) j = uri.length();
query = uri.substring(i, j);
i = j;
}
@@ -358,66 +650,8 @@
fragment = uri.substring(++i);
}
- return new URI(hierarchical, scheme, authority, device, absolutePath,
- segments, query, fragment);
- }
-
- /**
- * Static factory method based on parsing File path string, with
- * <a href="#device_explaination">explicit device support</a> enabled.
- * Note that validity testing is not a strict as in the RFC; essentially,
- * only separator characters are considered. So, for example, non-Latin
- * alphabet characters appearing in a path segment would not be considered an
- * error.
- * @exception java.lang.IllegalArgumentException if any component parsed
- * from <code>uri</code> is not valid according to {@link #validScheme},
- * {@link #validOpaquePart}, {@link #validAuthority}, {@link
- * #validDevice}, {@link #validSegments}, {@link #validQuery}, or {@link
- * #validFragment}, as appropriate.
- */
- public static URI createFileURI(String pathName)
- {
- File file = new File(pathName);
- String uri = File.separatorChar != '/' ? pathName.replace(File.separatorChar, SEGMENT_SEPARATOR) : pathName;
- if (file.isAbsolute())
- {
- URI result = parseIntoURI((uri.charAt(0) == SEGMENT_SEPARATOR ? "file:" : "file:/") + uri);
- return result;
- }
- else
- {
- URI result = parseIntoURI(uri);
- if (result.scheme() != null)
- {
- throw new IllegalArgumentException("invalid relative pathName: " + pathName);
- }
- return result;
- }
- }
-
- /**
- * Static factory method based on parsing a platform-relative path string.
- * The <code>pathName</code> must be of the form
- *<pre>
- * /project-name/path
- *</pre>
- * and the result will be of the form
- *<pre>
- * platform:/resource/project-name/path
- *</pre>
- * The leading separator of the path will be provided if not present.
- * This scheme supports relocatable projects in Eclipse and in stand-alone EMF.
- * @exception java.lang.IllegalArgumentException if any component parsed
- * from <code>uri</code> is not valid according to {@link #validScheme},
- * {@link #validOpaquePart}, {@link #validAuthority}, {@link
- * #validDevice}, {@link #validSegments}, {@link #validQuery}, or {@link
- * #validFragment}, as appropriate.
- * @see org.eclipse.core.runtime.Platform#resolve
- */
- public static URI createPlatformResourceURI(String pathName)
- {
- URI result = parseIntoURI((pathName.charAt(0) == SEGMENT_SEPARATOR ? "platform:/resource" : "platform:/resource/") + pathName);
- return result;
+ validateURI(hierarchical, scheme, authority, device, absolutePath, segments, query, fragment);
+ return new URI(hierarchical, scheme, authority, device, absolutePath, segments, query, fragment);
}
// Checks whether the string contains any more segments after the one that
@@ -428,36 +662,212 @@
uri.charAt(i) != FRAGMENT_SEPARATOR;
}
- // Finds the next occurance of one of the characters specified in
- // separators in the given URI, beginning at index i. The index of the
- // first separator, or uri.length() if there is no such character, is
- // returned. Before searching, i is limited to be in the range
- // [0, uri.length()].
- private static int findSeparator(String uri, int i, char[] separators)
+ // Finds the next occurance of one of the characters in the set represented
+ // by the given bitmask in the given string, beginning at index i. The index
+ // of the first found character, or s.length() if there is none, is
+ // returned. Before searching, i is limited to the range [0, s.length()].
+ //
+ private static int find(String s, int i, long highBitmask, long lowBitmask)
{
- int len = uri.length();
+ int len = s.length();
if (i >= len) return len;
- outerLoop: for (i = i > 0 ? i : 0; i < len; i++)
+ for (i = i > 0 ? i : 0; i < len; i++)
{
- for (int j = 0, slen = separators.length; j < slen; j++)
- {
- if (uri.charAt(i) == separators[j]) break outerLoop;
- }
+ if (matches(s.charAt(i), highBitmask, lowBitmask)) break;
}
return i;
}
- // Private constructor for use of static factory methods. Does validation
- // of each component, but assumes that the inter-component requirements
- // described in the factory doc-comments are all satisfied.
+ /**
+ * Static factory method based on parsing a {@link java.io.File} path
+ * string. The <code>pathName</code> is converted into an appropriate
+ * form, as follows: platform specific path separators are converted to
+ * <code>/<code>; the path is encoded; and a "file" scheme and, if missing,
+ * a leading <code>/</code>, are added to an absolute path. The result
+ * is then parsed using {@link #createURI(String) createURI}.
+ *
+ * <p>The encoding step escapes all spaces, <code>#</code> characters, and
+ * other characters disallowed in URIs, as well as <code>?</code>, which
+ * would delimit a path from a query. Decoding is automatically performed
+ * by {@link #toFileString toFileString}, and can be applied to the values
+ * returned by other accessors by via the static {@link #decode(String)
+ * decode} method.
+ *
+ * <p>A relative path with a specified device (something like
+ * <code>C:myfile.txt</code>) cannot be expressed as a valid URI.
+ *
+ * @exception java.lang.IllegalArgumentException if <code>pathName</code>
+ * specifies a device and a relative path, or if any component of the path
+ * is not valid according to {@link #validAuthority validAuthority}, {@link
+ * #validDevice validDevice}, or {@link #validSegments validSegments},
+ * {@link #validQuery validQuery}, or {@link #validFragment validFragment}.
+ */
+ public static URI createFileURI(String pathName)
+ {
+ File file = new File(pathName);
+ String uri = File.separatorChar != '/' ? pathName.replace(File.separatorChar, SEGMENT_SEPARATOR) : pathName;
+ uri = encode(uri, PATH_CHAR_HI, PATH_CHAR_LO, false);
+ if (file.isAbsolute())
+ {
+ URI result = createURI((uri.charAt(0) == SEGMENT_SEPARATOR ? "file:" : "file:/") + uri);
+ return result;
+ }
+ else
+ {
+ URI result = createURI(uri);
+ if (result.scheme() != null)
+ {
+ throw new IllegalArgumentException("invalid relative pathName: " + pathName);
+ }
+ return result;
+ }
+ }
+
+ /**
+ * Static factory method based on parsing a platform-relative path string.
+ *
+ * <p>The <code>pathName</code> must be of the form:
+ * <pre>
+ * /project-name/path</pre>
+ *
+ * <p>Platform-specific path separators will be converterted to slashes.
+ * If not included, the leading path separator will be added. The
+ * result will be of this form, which is parsed using {@link #createURI
+ * createURI}:
+ * <pre>
+ * platform:/resource/project-name/path</pre>
+ *
+ *
+ * @exception java.lang.IllegalArgumentException if any component parsed
+ * from the path is not valid according to {@link #validDevice validDevice},
+ * {@link #validSegments validSegments}, {@link #validQuery validQuery}, or
+ * {@link #validFragment validFragment}.
+ *
+ * @see org.eclipse.core.runtime.Platform#resolve
+ * @see #createPlatformResourceURI(String, boolean)
+ */
+ public static URI createPlatformResourceURI(String pathName)
+ {
+ return createPlatformResourceURI(pathName, false);
+ }
+
+ /**
+ * Static factory method based on parsing a platform-relative path string,
+ * with an option to encode the created URI.
+ *
+ * <p>The <code>pathName</code> must be of the form:
+ * <pre>
+ * /project-name/path</pre>
+ *
+ * <p>Platform-specific path separators will be converterted to slashes.
+ * If not included, the leading path separator will be added. The
+ * result will be of this form, which is parsed using {@link #createURI
+ * createURI}:
+ * <pre>
+ * platform:/resource/project-name/path</pre>
+ *
+ * <p>This scheme supports relocatable projects in Eclipse and in
+ * stand-alone .
+ *
+ * <p>Depending on the <code>encode</code> argument, the path may be
+ * automatically encoded to escape all spaces, <code>#</code> characters,
+ * and other characters disallowed in URIs, as well as <code>?</code>,
+ * which would delimit a path from a query. Decoding can be performed with
+ * the static {@link #decode(String) decode} method.
+ *
+ * @exception java.lang.IllegalArgumentException if any component parsed
+ * from the path is not valid according to {@link #validDevice validDevice},
+ * {@link #validSegments validSegments}, {@link #validQuery validQuery}, or
+ * {@link #validFragment validFragment}.
+ *
+ * @see org.eclipse.core.runtime.Platform#resolve
+ */
+ public static URI createPlatformResourceURI(String pathName, boolean encode)
+ {
+ if (File.separatorChar != SEGMENT_SEPARATOR)
+ {
+ pathName = pathName.replace(File.separatorChar, SEGMENT_SEPARATOR);
+ }
+
+ if (encode)
+ {
+ pathName = encode(pathName, PATH_CHAR_HI, PATH_CHAR_LO, false);
+ }
+ URI result = createURI((pathName.charAt(0) == SEGMENT_SEPARATOR ? "platform:/resource" : "platform:/resource/") + pathName);
+ return result;
+ }
+
+ // Private constructor for use of static factory methods.
private URI(boolean hierarchical, String scheme, String authority,
String device, boolean absolutePath, String[] segments,
String query, String fragment)
{
- String name = null;
- String value = null;
+ int hashCode = 0;
+ //boolean iri = false;
+ if (hierarchical)
+ {
+ ++hashCode;
+ }
+ if (absolutePath)
+ {
+ hashCode += 2;
+ }
+ if (scheme != null)
+ {
+ hashCode ^= scheme.toLowerCase().hashCode();
+ }
+ if (authority != null)
+ {
+ hashCode ^= authority.hashCode();
+ //iri = iri || containsNonASCII(authority);
+ }
+ if (device != null)
+ {
+ hashCode ^= device.hashCode();
+ //iri = iri || containsNonASCII(device);
+ }
+ if (query != null)
+ {
+ hashCode ^= query.hashCode();
+ //iri = iri || containsNonASCII(query);
+ }
+ if (fragment != null)
+ {
+ hashCode ^= fragment.hashCode();
+ //iri = iri || containsNonASCII(fragment);
+ }
+
+ for (int i = 0, len = segments.length; i < len; i++)
+ {
+ hashCode ^= segments[i].hashCode();
+ //iri = iri || containsNonASCII(segments[i]);
+ }
+
+ this.hashCode = hashCode;
+ //this.iri = iri;
+ this.hierarchical = hierarchical;
+ this.scheme = scheme == null ? null : scheme.intern();
+ this.authority = authority;
+ this.device = device;
+ this.absolutePath = absolutePath;
+ this.segments = segments;
+ this.query = query;
+ this.fragment = fragment;
+ }
+
+ // Validates all of the URI components. Factory methods should call this
+ // before using the constructor, though they must ensure that the
+ // inter-component requirements described in their own Javadocs are all
+ // satisfied, themselves. If a new URI is being constructed out of
+ // an existing URI, this need not be called. Instead, just the new
+ // components may be validated individually.
+ private static void validateURI(boolean hierarchical, String scheme,
+ String authority, String device,
+ boolean absolutePath, String[] segments,
+ String query, String fragment)
+ {
if (!validScheme(scheme))
{
throw new IllegalArgumentException("invalid scheme: " + scheme);
@@ -466,7 +876,11 @@
{
throw new IllegalArgumentException("invalid opaquePart: " + authority);
}
- if (hierarchical && !validAuthority(authority))
+ if (hierarchical && !isArchiveScheme(scheme) && !validAuthority(authority))
+ {
+ throw new IllegalArgumentException("invalid authority: " + authority);
+ }
+ if (hierarchical && isArchiveScheme(scheme) && !validArchiveAuthority(authority))
{
throw new IllegalArgumentException("invalid authority: " + authority);
}
@@ -488,66 +902,10 @@
{
throw new IllegalArgumentException("invalid fragment: " + fragment);
}
-
- int hashCode = 0;
- if (hierarchical)
- {
- ++hashCode;
- }
- if (absolutePath)
- {
- hashCode += 2;
- }
- if (scheme != null)
- {
- hashCode ^= scheme.hashCode();
- }
- if (authority != null)
- {
- hashCode ^= authority.hashCode();
- }
- if (device != null)
- {
- hashCode ^= device.hashCode();
- }
- if (query != null)
- {
- hashCode ^= query.hashCode();
- }
- if (fragment != null)
- {
- hashCode ^= fragment.hashCode();
- }
-
- for (int i = 0, len = segments.length; i < len; i++)
- {
- hashCode ^= segments[i].hashCode();
- }
-
- this.hashCode = hashCode;
- this.hierarchical = hierarchical;
- this.scheme = scheme;
- this.authority = authority;
- this.device = device;
- this.absolutePath = absolutePath;
- this.segments = segments;
- this.query = query;
- this.fragment = fragment;
}
- // Searches the specified string for any of the specified target
- // characters, and if any occur, returns true; false otherwise.
- private static boolean contains(String s, char[] targets)
- {
- for (int i = 0, len = s.length(); i < len; i++)
- {
- for (int j = 0, tlen = targets.length; j < tlen; j++)
- {
- if (s.charAt(i) == targets[j]) return true;
- }
- }
- return false;
- }
+ // Alternate, stricter implementations of the following validation methods
+ // are provided, commented out, for possible future use...
/**
* Returns <code>true</code> if the specified <code>value</code> would be
@@ -558,7 +916,16 @@
*/
public static boolean validScheme(String value)
{
- return value == null || !contains(value, MAJOR_SEPARATORS);
+ return value == null || !contains(value, MAJOR_SEPARATOR_HI, MAJOR_SEPARATOR_LO);
+
+ // <p>A valid scheme may be null, or consist of a single letter followed
+ // by any number of letters, numbers, and the following characters:
+ // <code>+ - .</code>
+
+ //if (value == null) return true;
+ //return value.length() != 0 &&
+ // matches(value.charAt(0), ALPHA_HI, ALPHA_LO) &&
+ // validate(value, SCHEME_CHAR_HI, SCHEME_CHAR_LO, false, false);
}
/**
@@ -573,7 +940,15 @@
public static boolean validOpaquePart(String value)
{
return value != null && value.indexOf(FRAGMENT_SEPARATOR) == -1 &&
- value.length() > 0 && value.charAt(0) != SEGMENT_SEPARATOR;
+ value.length() > 0 && value.charAt(0) != SEGMENT_SEPARATOR;
+
+ // <p>A valid opaque part must be non-null and non-empty. It may contain
+ // any allowed URI characters, but its first character may not be
+ // <code>/</code>
+
+ //return value != null && value.length() != 0 &&
+ // value.charAt(0) != SEGMENT_SEPARATOR &&
+ // validate(value, URIC_HI, URIC_LO, true, true);
}
/**
@@ -585,11 +960,43 @@
*/
public static boolean validAuthority(String value)
{
- return value == null || !contains(value, SEGMENT_END);
+ return value == null || !contains(value, SEGMENT_END_HI, SEGMENT_END_LO);
+
+ // A valid authority may be null or contain any allowed URI characters except
+ // for the following: <code>/ ?</code>
+
+ //return value == null || validate(value, SEGMENT_CHAR_HI, SEGMENT_CHAR_LO, true, true);
}
/**
* Returns <code>true</code> if the specified <code>value</code> would be
+ * valid as the authority component of an <a
+ * href="#archive_explanation">archive URI</a>; <code>false</code>
+ * otherwise.
+ *
+ * <p>To be valid, the authority, itself, must be a URI with no fragment,
+ * followed by the character <code>!</code>.
+ */
+ public static boolean validArchiveAuthority(String value)
+ {
+ if (value != null && value.length() > 0 &&
+ value.charAt(value.length() - 1) == ARCHIVE_IDENTIFIER)
+ {
+ try
+ {
+ URI archiveURI = createURI(value.substring(0, value.length() - 1));
+ return !archiveURI.hasFragment();
+ }
+ catch (IllegalArgumentException e)
+ {
+ }
+ }
+ return false;
+ }
+
+
+ /**
+ * Returns <code>true</code> if the specified <code>value</code> would be
* valid as the device component of a URI; <code>false</code> otherwise.
*
* <p>A valid device may be null or non-empty, containing any characters
@@ -597,11 +1004,20 @@
* character must be <code>:</code>
*/
public static boolean validDevice(String value)
- {
+ {
if (value == null) return true;
int len = value.length();
return len > 0 && value.charAt(len - 1) == DEVICE_IDENTIFIER &&
- !contains(value, SEGMENT_END);
+ !contains(value, SEGMENT_END_HI, SEGMENT_END_LO);
+
+ // <p>A valid device may be null or non-empty, containing any allowed URI
+ // characters except for the following: <code>/ ?</code> In addition, its
+ // last character must be <code>:</code>
+
+ //if (value == null) return true;
+ //int len = value.length();
+ //return len > 0 && validate(value, SEGMENT_CHAR_HI, SEGMENT_CHAR_LO, true, true) &&
+ // value.charAt(len - 1) == DEVICE_IDENTIFIER;
}
/**
@@ -613,7 +1029,12 @@
*/
public static boolean validSegment(String value)
{
- return value != null && !contains(value, SEGMENT_END);
+ return value != null && !contains(value, SEGMENT_END_HI, SEGMENT_END_LO);
+
+ // <p>A valid path segment must be non-null and may contain any allowed URI
+ // characters except for the following: <code>/ ?</code>
+
+ //return value != null && validate(value, SEGMENT_CHAR_HI, SEGMENT_CHAR_LO, true, true);
}
/**
@@ -621,7 +1042,7 @@
* a valid path segment array of a URI; <code>false</code> otherwise.
*
* <p>A valid path segment array must be non-null and contain only path
- * segements that are valid, according to {@link #validSegment}.
+ * segements that are valid according to {@link #validSegment validSegment}.
*/
public static boolean validSegments(String[] value)
{
@@ -656,7 +1077,11 @@
public static boolean validQuery(String value)
{
return value == null || value.indexOf(FRAGMENT_SEPARATOR) == -1;
- }
+
+ // <p>A valid query may be null or contain any allowed URI characters.
+
+ //return value == null || validate(value, URIC_HI, URIC_LO, true, true);
+}
/**
* Returns <code>true</code> if the specified <code>value</code> would be
@@ -667,8 +1092,50 @@
public static boolean validFragment(String value)
{
return true;
+
+ // <p>A valid fragment may be null or contain any allowed URI characters.
+
+ //return value == null || validate(value, URIC_HI, URIC_LO, true, true);
}
+ // Searches the specified string for any characters in the set represnted
+ // by the 128-bit bitmask. Returns true if any occur, or false otherwise.
+ private static boolean contains(String s, long highBitmask, long lowBitmask)
+ {
+ for (int i = 0, len = s.length(); i < len; i++)
+ {
+ if (matches(s.charAt(i), highBitmask, lowBitmask)) return true;
+ }
+ return false;
+ }
+
+ // Tests the non-null string value to see if it contains only ASCII
+ // characters in the set represented by the specified 128-bit bitmask,
+ // as well as, optionally, non-ASCII characters 0xA0 and above, and,
+ // also optionally, escape sequences of % followed by two hex digits.
+ // This method is used for the new, strict URI validation that is not
+ // not currently in place.
+/*
+ private static boolean validate(String value, long highBitmask, long lowBitmask,
+ boolean allowNonASCII, boolean allowEscaped)
+ {
+ for (int i = 0, len = value.length(); i < len; i++)
+ {
+ char c = value.charAt(i);
+
+ if (matches(c, highBitmask, lowBitmask)) continue;
+ if (allowNonASCII && c >= 160) continue;
+ if (allowEscaped && isEscaped(value, i))
+ {
+ i += 2;
+ continue;
+ }
+ return false;
+ }
+ return true;
+ }
+*/
+
/**
* Returns <code>true</code> if this is a relative URI, or
* <code>false</code> if it is an absolute URI.
@@ -826,6 +1293,25 @@
((isRelative() && !hasQuery()) || SCHEME_FILE.equalsIgnoreCase(scheme));
}
+ // Returns true if this is an archive URI. If so, we should expect that
+ // it is also hierarchical, with an authority (consisting of an absolute
+ // URI followed by "!"), no device, and an absolute path.
+ private boolean isArchive()
+ {
+ return isArchiveScheme(scheme);
+ }
+
+ /**
+ * Returns <code>true</code> if the specified <code>value</code> would be
+ * valid as the scheme of an <a
+ * href="#archive_explanation">archive URI</a>; <code>false</code>
+ * otherwise.
+ */
+ public static boolean isArchiveScheme(String value)
+ {
+ return value != null && archiveSchemes.contains(value.toLowerCase());
+ }
+
/**
* Returns the hash code.
*/
@@ -839,7 +1325,8 @@
* <code>URI</code> equal to this one; <code>false</code> otherwise.
*
* <p>Equality is determined strictly by comparing components, not by
- * attempting to interpret what resource is being identified.
+ * attempting to interpret what resource is being identified. The
+ * comparison of schemes is case-insensitive.
*/
public boolean equals(Object obj)
{
@@ -850,7 +1337,7 @@
return hashCode == uri.hashCode() &&
hierarchical == uri.isHierarchical() &&
absolutePath == uri.hasAbsolutePath() &&
- equals(scheme, uri.scheme()) &&
+ equals(scheme, uri.scheme(), true) &&
equals(authority, hierarchical ? uri.authority() : uri.opaquePart()) &&
equals(device, uri.device()) &&
equals(query, uri.query()) &&
@@ -877,6 +1364,14 @@
return o1 == null ? o2 == null : o1.equals(o2);
}
+ // Tests two strings for equality, tolerating nulls and optionally
+ // ignoring case.
+ private static boolean equals(String s1, String s2, boolean ignoreCase)
+ {
+ return s1 == null ? s2 == null :
+ ignoreCase ? s1.equalsIgnoreCase(s2) : s1.equals(s2);
+ }
+
/**
* If this is an absolute URI, returns the scheme component;
* <code>null</code> otherwise.
@@ -964,7 +1459,7 @@
/**
* Returns an unmodifiable list containing the same segments as the array
- * returned by {@link #segments()}.
+ * returned by {@link #segments segments}.
*/
public List segmentsList()
{
@@ -973,7 +1468,7 @@
/**
* Returns the number of elements in the segment array that would be
- * returned by {@link #segments()}.
+ * returned by {@link #segments segments}.
*/
public int segmentCount()
{
@@ -1007,9 +1502,8 @@
* representation of the path; <code>null</code> otherwise. The path
* consists of a leading segment separator character (a slash), if the
* path is absolute, followed by the slash-separated path segments. If
- * this URI has a separate <a href="#device_explaination">device
- * component</a>, it is <em>not</em> included in the path. If it has a
- * device stored as a path segment, it is included.
+ * this URI has a separate <a href="#device_explanation">device
+ * component</a>, it is <em>not</em> included in the path.
*/
public String path()
{
@@ -1029,22 +1523,30 @@
/**
* If this is a hierarchical URI with a path, returns a string
* representation of the path, including the authority and the
- * <a href="#device_explaination">device component</a>;
+ * <a href="#device_explanation">device component</a>;
* <code>null</code> otherwise.
- * <p>The format of this string is
+ *
+ * <p>If there is no authority, the format of this string is:
* <pre>
- * //authority/device/pathSegment1/pathSegment2...
- *</pre>
+ * device/pathSegment1/pathSegment2...</pre>
+ *
+ * <p>If there is an authority, it is:
+ * <pre>
+ * //authority/device/pathSegment1/pathSegment2...</pre>
+ *
+ * <p>For an <a href="#archive_explanation">archive URI</a>, it's just:
+ * <pre>
+ * authority/pathSegment1/pathSegment2...</pre>
*/
public String devicePath()
{
if (!hasPath()) return null;
StringBuffer result = new StringBuffer();
+
if (hasAuthority())
{
- result.append(SEGMENT_SEPARATOR);
- result.append(SEGMENT_SEPARATOR);
+ if (!isArchive()) result.append(AUTHORITY_SEPARATOR);
result.append(authority);
if (hasDevice()) result.append(SEGMENT_SEPARATOR);
@@ -1076,7 +1578,7 @@
*
* @exception java.lang.IllegalArgumentException if
* <code>query</code> is not a valid query (portion) according
- * to {@link #validQuery}.
+ * to {@link #validQuery validQuery}.
*/
public URI appendQuery(String query)
{
@@ -1089,7 +1591,7 @@
}
/**
- * If this URI has a non-null {@link #query}, returns the URI
+ * If this URI has a non-null {@link #query query}, returns the URI
* formed by removing it; this URI unchanged, otherwise.
*/
public URI trimQuery()
@@ -1118,7 +1620,7 @@
*
* @exception java.lang.IllegalArgumentException if
* <code>fragment</code> is not a valid fragment (portion) according
- * to {@link #validFragment}.
+ * to {@link #validFragment validFragment}.
*/
public URI appendFragment(String fragment)
{
@@ -1128,12 +1630,16 @@
"invalid fragment portion: " + fragment);
}
URI result = new URI(hierarchical, scheme, authority, device, absolutePath, segments, query, fragment);
- result.cachedTrimFragment = this;
+
+ if (!hasFragment())
+ {
+ result.cachedTrimFragment = this;
+ }
return result;
}
/**
- * If this URI has a non-null {@link #fragment}, returns the URI
+ * If this URI has a non-null {@link #fragment fragment}, returns the URI
* formed by removing it; this URI unchanged, otherwise.
*/
public URI trimFragment()
@@ -1162,7 +1668,8 @@
* path. Step 6(g) gives a choice of how to handle the case where parent
* references point to a path above the root: the offending segments can
* be preserved or discarded. This method preserves them. To have them
- * discarded, please use the {@link #resolve(URI, boolean)} method.
+ * discarded, please use the two-parameter form of {@link
+ * #resolve(URI, boolean) resolve}.
*
* @exception java.lang.IllegalArgumentException if <code>base</code> is
* non-hierarchical or is relative.
@@ -1185,7 +1692,7 @@
* references point to a path above the root: the offending segments can
* be preserved or discarded. This method can do either.
*
- * @param preserveRootParent <code>true</code> if segments refering to the
+ * @param preserveRootParents <code>true</code> if segments refering to the
* parent of the root path are to be preserved; <code>false</code> if they
* are to be discarded.
*
@@ -1245,7 +1752,8 @@
}
// else keep authority, device, path, and query
- // always keep fragment, even if null, and use scheme from base
+ // always keep fragment, even if null, and use scheme from base;
+ // no validation needed since all components are from existing URIs
return new URI(true, base.scheme(), newAuthority, newDevice,
newAbsolutePath, newSegments, newQuery, fragment);
}
@@ -1331,7 +1839,7 @@
/**
* Finds the shortest relative or, if necessary, the absolute URI that,
* when resolved against the given <code>base</code> absolute hierarchical
- * URI using {@link #resolve(URI)}, will yield this absolute URI.
+ * URI using {@link #resolve(URI) resolve}, will yield this absolute URI.
*
* @exception java.lang.IllegalArgumentException if <code>base</code> is
* non-hierarchical or is relative.
@@ -1345,8 +1853,8 @@
/**
* Finds an absolute URI that, when resolved against the given
- * <code>base</code> absolute hierarchical URI using {@link #resolve(URI,
- * boolean)}, will yield this absolute URI.
+ * <code>base</code> absolute hierarchical URI using {@link
+ * #resolve(URI, boolean) resolve}, will yield this absolute URI.
*
* @param preserveRootParents the boolean argument to <code>resolve(URI,
* boolean)</code> for which the returned URI should resolve to this URI.
@@ -1381,7 +1889,7 @@
// relative path; thus, both have either an absolute path or no path
// different scheme: need complete, absolute URI
- if (!scheme.equals(base.scheme())) return this;
+ if (!scheme.equalsIgnoreCase(base.scheme())) return this;
// since base must be hierarchical, and since a non-hierarchical URI
// must have both scheme and opaque part, the complete absolute URI is
@@ -1447,7 +1955,8 @@
}
// else keep authority, device, path, and query
- // always include fragment, even if null
+ // always include fragment, even if null;
+ // no validation needed since all components are from existing URIs
return new URI(true, null, newAuthority, newDevice, newAbsolutePath,
newSegments, newQuery, fragment);
}
@@ -1581,14 +2090,16 @@
/**
* Returns the string representation of this URI. For a generic,
* non-hierarchical URI, this looks like:
- *
* <pre>
* scheme:opaquePart#fragment</pre>
*
* <p>For a hierarchical URI, it looks like:
* <pre>
* scheme://authority/device/pathSegment1/pathSegment2...?query#fragment</pre>
- *
+ *
+ * <p>For an <a href="#archive_explanation">archive URI</a>, it's just:
+ * <pre>
+ * scheme:authority/pathSegment1/pathSegment2...?query#fragment</pre>
* <p>Of course, absent components and their separators will be omitted.
*/
public String toString()
@@ -1606,7 +2117,7 @@
{
if (hasAuthority())
{
- result.append(AUTHORITY_SEPARATOR);
+ if (!isArchive()) result.append(AUTHORITY_SEPARATOR);
result.append(authority);
}
@@ -1677,10 +2188,14 @@
/**
* If this URI may refer directly to a locally accessible file, as
- * determined by {@link #isFile()}, returns the URI formatted as a
- * pathname to that file; null otherwise.
+ * determined by {@link #isFile isFile}, {@link decode decodes} and formats
+ * the URI as a pathname to that file; returns null otherwise.
*
- * <p>The format of this string is
+ * <p>If there is no authority, the format of this string is:
+ * <pre>
+ * device/pathSegment1/pathSegment2...</pre>
+ *
+ * <p>If there is an authority, it is:
* <pre>
* //authority/device/pathSegment1/pathSegment2...</pre>
*
@@ -1711,7 +2226,8 @@
if (i != 0) result.append(separator);
result.append(segments[i]);
}
- return result.toString();
+
+ return decode(result.toString());
}
/**
@@ -1884,8 +2400,8 @@
}
/**
- * If this URI has a non-null {@link #fileExtension}, returns the URI
- * formed by removing it; this URI unchanged, otherwise.
+ * If this URI has a non-null {@link #fileExtension fileExtension},
+ * returns the URI formed by removing it; this URI unchanged, otherwise.
*/
public URI trimFileExtension()
{
@@ -1924,9 +2440,9 @@
* <code>newPrefix</code>; <code>null</code> otherwise.
*
* <p>In order to be a prefix, the <code>oldPrefix</code>'s
- * {@link #isPrefix} must return <code>true</code>, and it must match this
- * URI's scheme, authority, and device. Also, the paths must match, up to
- * prefix's end.
+ * {@link #isPrefix isPrefix} must return <code>true</code>, and it must
+ * match this URI's scheme, authority, and device. Also, the paths must
+ * match, up to prefix's end.
*
* @exception java.lang.IllegalArgumentException if either
* <code>oldPrefix</code> or <code>newPrefix</code> is not a prefix URI
@@ -1962,6 +2478,7 @@
}
}
+ // no validation needed since all components are from existing URIs
return new URI(true, newPrefix.scheme(), newPrefix.authority(),
newPrefix.device(), newPrefix.hasAbsolutePath(),
mergedSegments, query, fragment);
@@ -1980,7 +2497,7 @@
// Don't even consider it unless this is hierarchical and has scheme,
// authority, device and path absoluteness equal to those of the prefix.
if (!hierarchical ||
- !equals(scheme, prefix.scheme()) ||
+ !equals(scheme, prefix.scheme(), true) ||
!equals(authority, prefix.authority()) ||
!equals(device, prefix.device()) ||
absolutePath != prefix.hasAbsolutePath())
@@ -2016,4 +2533,344 @@
System.arraycopy(segments, i, newSegments, 0, newSegments.length);
return newSegments;
}
+
+ /**
+ * Encodes a string so as to produce a valid opaque part value, as defined
+ * by the RFC. All excluded characters, such as space and <code>#</code>,
+ * are escaped, as is <code>/</code> if it is the first character.
+ *
+ * @param ignoreEscaped <code>true</code> to leave <code>%</code> characters
+ * unescaped if they already begin a valid three-character escape sequence;
+ * <code>false</code> to encode all <code>%</code> characters. Note that
+ * if a <code>%</code> is not followed by 2 hex digits, it will always be
+ * escaped.
+ */
+ public static String encodeOpaquePart(String value, boolean ignoreEscaped)
+ {
+ String result = encode(value, URIC_HI, URIC_LO, ignoreEscaped);
+ return result != null && result.length() > 0 && result.charAt(0) == SEGMENT_SEPARATOR ?
+ "%2F" + result.substring(1) :
+ result;
+ }
+
+ /**
+ * Encodes a string so as to produce a valid authority, as defined by the
+ * RFC. All excluded characters, such as space and <code>#</code>,
+ * are escaped, as are <code>/</code> and <code>?</code>
+ *
+ * @param ignoreEscaped <code>true</code> to leave <code>%</code> characters
+ * unescaped if they already begin a valid three-character escape sequence;
+ * <code>false</code> to encode all <code>%</code> characters. Note that
+ * if a <code>%</code> is not followed by 2 hex digits, it will always be
+ * escaped.
+ */
+ public static String encodeAuthority(String value, boolean ignoreEscaped)
+ {
+ return encode(value, SEGMENT_CHAR_HI, SEGMENT_CHAR_LO, ignoreEscaped);
+ }
+
+ /**
+ * Encodes a string so as to produce a valid segment, as defined by the
+ * RFC. All excluded characters, such as space and <code>#</code>,
+ * are escaped, as are <code>/</code> and <code>?</code>
+ *
+ * @param ignoreEscaped <code>true</code> to leave <code>%</code> characters
+ * unescaped if they already begin a valid three-character escape sequence;
+ * <code>false</code> to encode all <code>%</code> characters. Note that
+ * if a <code>%</code> is not followed by 2 hex digits, it will always be
+ * escaped.
+ */
+ public static String encodeSegment(String value, boolean ignoreEscaped)
+ {
+ return encode(value, SEGMENT_CHAR_HI, SEGMENT_CHAR_LO, ignoreEscaped);
+ }
+
+ /**
+ * Encodes a string so as to produce a valid query, as defined by the RFC.
+ * Only excluded characters, such as space and <code>#</code>, are escaped.
+ *
+ * @param ignoreEscaped <code>true</code> to leave <code>%</code> characters
+ * unescaped if they already begin a valid three-character escape sequence;
+ * <code>false</code> to encode all <code>%</code> characters. Note that
+ * if a <code>%</code> is not followed by 2 hex digits, it will always be
+ * escaped.
+ */
+ public static String encodeQuery(String value, boolean ignoreEscaped)
+ {
+ return encode(value, URIC_HI, URIC_LO, ignoreEscaped);
+ }
+
+ /**
+ * Encodes a string so as to produce a valid fragment, as defined by the
+ * RFC. Only excluded characters, such as space and <code>#</code>, are
+ * escaped.
+ *
+ * @param ignoreEscaped <code>true</code> to leave <code>%</code> characters
+ * unescaped if they already begin a valid three-character escape sequence;
+ * <code>false</code> to encode all <code>%</code> characters. Note that
+ * if a <code>%</code> is not followed by 2 hex digits, it will always be
+ * escaped.
+ */
+ public static String encodeFragment(String value, boolean ignoreEscaped)
+ {
+ return encode(value, URIC_HI, URIC_LO, ignoreEscaped);
+ }
+
+ // Encodes a complete URI, optionally leaving % characters unescaped when
+ // beginning a valid three-character escape sequence. We assume that the
+ // last # begins the fragment.
+ private static String encodeURI(String uri, boolean ignoreEscaped)
+ {
+ if (uri == null) return null;
+
+ StringBuffer result = new StringBuffer();
+
+ int i = uri.indexOf(SCHEME_SEPARATOR);
+ if (i != -1)
+ {
+ String scheme = uri.substring(0, i);
+ result.append(scheme);
+ result.append(SCHEME_SEPARATOR);
+ }
+
+ int j = uri.lastIndexOf(FRAGMENT_SEPARATOR);
+ if (j != -1)
+ {
+ String sspart = uri.substring(++i, j);
+ result.append(encode(sspart, URIC_HI, URIC_LO, ignoreEscaped));
+ result.append(FRAGMENT_SEPARATOR);
+
+ String fragment = uri.substring(++j);
+ result.append(encode(fragment, URIC_HI, URIC_LO, ignoreEscaped));
+ }
+ else
+ {
+ String sspart = uri.substring(++i);
+ result.append(encode(sspart, URIC_HI, URIC_LO, ignoreEscaped));
+ }
+
+ return result.toString();
+ }
+
+ // Encodes the given string, replacing each ASCII character that is not in
+ // the set specified by the 128-bit bitmask and each non-ASCII character
+ // below 0xA0 by an escape sequence of % followed by two hex digits. If
+ // % is not in the set but ignoreEscaped is true, then % will not be encoded
+ // iff it already begins a valid escape sequence.
+ private static String encode(String value, long highBitmask, long lowBitmask, boolean ignoreEscaped)
+ {
+ if (value == null) return null;
+
+ StringBuffer result = null;
+
+ for (int i = 0, len = value.length(); i < len; i++)
+ {
+ char c = value.charAt(i);
+
+ if (!matches(c, highBitmask, lowBitmask) && c < 160 &&
+ (!ignoreEscaped || !isEscaped(value, i)))
+ {
+ if (result == null)
+ {
+ result = new StringBuffer(value.substring(0, i));
+ }
+ appendEscaped(result, (byte)c);
+ }
+ else if (result != null)
+ {
+ result.append(c);
+ }
+ }
+ return result == null ? value : result.toString();
+ }
+
+ // Tests whether an escape occurs in the given string, starting at index i.
+ // An escape sequence is a % followed by two hex digits.
+ private static boolean isEscaped(String s, int i)
+ {
+ return s.charAt(i) == ESCAPE && s.length() > i + 2 &&
+ matches(s.charAt(i + 1), HEX_HI, HEX_LO) &&
+ matches(s.charAt(i + 2), HEX_HI, HEX_LO);
+ }
+
+ // Computes a three-character escape sequence for the byte, appending
+ // it to the StringBuffer. Only characters up to 0xFF should be escaped;
+ // all but the least significant byte will be ignored.
+ private static void appendEscaped(StringBuffer result, byte b)
+ {
+ result.append(ESCAPE);
+
+ // The byte is automatically widened into an int, with sign extension,
+ // for shifting. This can introduce 1's to the left of the byte, which
+ // must be cleared by masking before looking up the hex digit.
+ //
+ result.append(HEX_DIGITS[(b >> 4) & 0x0F]);
+ result.append(HEX_DIGITS[b & 0x0F]);
+ }
+
+ /**
+ * Decodes the given string, replacing each three-digit escape sequence by
+ * the character that it represents. Incomplete escape sequences are
+ * ignored.
+ */
+ public static String decode(String value)
+ {
+ if (value == null) return null;
+
+ StringBuffer result = null;
+
+ for (int i = 0, len = value.length(); i < len; i++)
+ {
+ if (isEscaped(value, i))
+ {
+ if (result == null)
+ {
+ result = new StringBuffer(value.substring(0, i));
+ }
+ result.append(unescape(value.charAt(i + 1), value.charAt(i + 2)));
+ i += 2;
+ }
+ else if (result != null)
+ {
+ result.append(value.charAt(i));
+ }
+ }
+ return result == null ? value : result.toString();
+ }
+
+ // Returns the character encoded by % followed by the two given hex digits,
+ // which is always 0xFF or less, so can safely be casted to a byte. If
+ // either character is not a hex digit, a bogus result will be returned.
+ private static char unescape(char highHexDigit, char lowHexDigit)
+ {
+ return (char)((valueOf(highHexDigit) << 4) | valueOf(lowHexDigit));
+ }
+
+ // Returns the int value of the given hex digit.
+ private static int valueOf(char hexDigit)
+ {
+ if (hexDigit >= 'A' && hexDigit <= 'F')
+ {
+ return hexDigit - 'A' + 10;
+ }
+ if (hexDigit >= 'a' && hexDigit <= 'f')
+ {
+ return hexDigit - 'a' + 10;
+ }
+ if (hexDigit >= '0' && hexDigit <= '9')
+ {
+ return hexDigit - '0';
+ }
+ return 0;
+ }
+
+ /*
+ * Returns <code>true</code> if this URI contains non-ASCII characters;
+ * <code>false</code> otherwise.
+ *
+ * This unused code is included for possible future use...
+ */
+/*
+ public boolean isIRI()
+ {
+ return iri;
+ }
+
+ // Returns true if the given string contains any non-ASCII characters;
+ // false otherwise.
+ private static boolean containsNonASCII(String value)
+ {
+ for (int i = 0, len = value.length(); i < len; i++)
+ {
+ if (value.charAt(i) > 127) return true;
+ }
+ return false;
+ }
+*/
+
+ /*
+ * If this is an {@link #isIRI IRI}, converts it to a strict ASCII URI,
+ * using the procedure described in Section 3.1 of the
+ * <a href="http://www.w3.org/International/iri-edit/draft-duerst-iri-09.txt">IRI
+ * Draft RFC</a>. Otherwise, this URI, itself, is returned.
+ *
+ * This unused code is included for possible future use...
+ */
+/*
+ public URI toASCIIURI()
+ {
+ if (!iri) return this;
+
+ if (cachedASCIIURI == null)
+ {
+ String eAuthority = encodeAsASCII(authority);
+ String eDevice = encodeAsASCII(device);
+ String eQuery = encodeAsASCII(query);
+ String eFragment = encodeAsASCII(fragment);
+ String[] eSegments = new String[segments.length];
+ for (int i = 0; i < segments.length; i++)
+ {
+ eSegments[i] = encodeAsASCII(segments[i]);
+ }
+ cachedASCIIURI = new URI(hierarchical, scheme, eAuthority, eDevice, absolutePath, eSegments, eQuery, eFragment);
+
+ }
+ return cachedASCIIURI;
+ }
+
+ // Returns a strict ASCII encoding of the given value. Each non-ASCII
+ // character is converted to bytes using UTF-8 encoding, which are then
+ // represnted using % escaping.
+ private String encodeAsASCII(String value)
+ {
+ if (value == null) return null;
+
+ StringBuffer result = null;
+
+ for (int i = 0, len = value.length(); i < len; i++)
+ {
+ char c = value.charAt(i);
+
+ if (c >= 128)
+ {
+ if (result == null)
+ {
+ result = new StringBuffer(value.substring(0, i));
+ }
+
+ try
+ {
+ byte[] encoded = (new String(new char[] { c })).getBytes("UTF-8");
+ for (int j = 0, encLen = encoded.length; j < encLen; j++)
+ {
+ appendEscaped(result, encoded[j]);
+ }
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ throw new WrappedException(e);
+ }
+ }
+ else if (result != null)
+ {
+ result.append(c);
+ }
+
+ }
+ return result == null ? value : result.toString();
+ }
+
+ // Returns the number of valid, consecutive, three-character escape
+ // sequences in the given string, starting at index i.
+ private static int countEscaped(String s, int i)
+ {
+ int result = 0;
+
+ for (int len = s.length(); i < len; i += 3)
+ {
+ if (isEscaped(s, i)) result++;
+ }
+ return result;
+ }
+*/
}
diff --git a/plugins/org.eclipse.wst.common.uriresolver/src/org/eclipse/wst/common/uriresolver/internal/URIResolverExtensionRegistry.java b/plugins/org.eclipse.wst.common.uriresolver/src/org/eclipse/wst/common/uriresolver/internal/URIResolverExtensionRegistry.java
index 48c0827..926229e 100644
--- a/plugins/org.eclipse.wst.common.uriresolver/src/org/eclipse/wst/common/uriresolver/internal/URIResolverExtensionRegistry.java
+++ b/plugins/org.eclipse.wst.common.uriresolver/src/org/eclipse/wst/common/uriresolver/internal/URIResolverExtensionRegistry.java
@@ -26,6 +26,7 @@
protected HashMap map = new HashMap();
public static final int STAGE_PRENORMALIZATION = 1;
public static final int STAGE_POSTNORMALIZATION = 2;
+ public static final int STAGE_PHYSICAL = 3;
public static final String PRIORITY_LOW = "low";
public static final String PRIORITY_MEDIUM = "medium";
public static final String PRIORITY_HIGH = "high";
diff --git a/plugins/org.eclipse.wst.common.uriresolver/src/org/eclipse/wst/common/uriresolver/internal/URIResolverExtensionRegistryReader.java b/plugins/org.eclipse.wst.common.uriresolver/src/org/eclipse/wst/common/uriresolver/internal/URIResolverExtensionRegistryReader.java
index e7de355..35b3847 100644
--- a/plugins/org.eclipse.wst.common.uriresolver/src/org/eclipse/wst/common/uriresolver/internal/URIResolverExtensionRegistryReader.java
+++ b/plugins/org.eclipse.wst.common.uriresolver/src/org/eclipse/wst/common/uriresolver/internal/URIResolverExtensionRegistryReader.java
@@ -36,6 +36,7 @@
protected static final String ATT_STAGE = "stage";
protected static final String VAL_STAGE_PRE = "prenormalization";
protected static final String VAL_STAGE_POST = "postnormalization";
+ protected static final String VAL_STAGE_PHYSICAL = "physical";
protected static final String ATT_VALUE = "value";
protected static final String ATT_PRIORITY = "priority";
@@ -99,6 +100,10 @@
{
stageint = URIResolverExtensionRegistry.STAGE_PRENORMALIZATION;
}
+ else if (stage.equalsIgnoreCase(VAL_STAGE_PHYSICAL))
+ {
+ stageint = URIResolverExtensionRegistry.STAGE_PHYSICAL;
+ }
registry.put(className, classLoader, projectNatureIds, resourceType, stageint, priority);
} catch (Exception e) {
}
diff --git a/plugins/org.eclipse.wst.common.uriresolver/src/org/eclipse/wst/common/uriresolver/internal/provisional/URIResolver.java b/plugins/org.eclipse.wst.common.uriresolver/src/org/eclipse/wst/common/uriresolver/internal/provisional/URIResolver.java
index ed7dab9..dff7f90 100644
--- a/plugins/org.eclipse.wst.common.uriresolver/src/org/eclipse/wst/common/uriresolver/internal/provisional/URIResolver.java
+++ b/plugins/org.eclipse.wst.common.uriresolver/src/org/eclipse/wst/common/uriresolver/internal/provisional/URIResolver.java
@@ -14,7 +14,6 @@
/**
* A URIResolver is used to resolve URI references to resources.
- *
*/
public interface URIResolver {
@@ -22,7 +21,15 @@
* @param baseLocation - the location of the resource that contains the uri
* @param publicId - an optional public identifier (i.e. namespace name), or null if none
* @param systemId - an absolute or relative URI, or null if none
- * @return an absolute URI
+ * @return an absolute URI represention the 'logical' location of the resource
*/
public String resolve(String baseLocation, String publicId, String systemId);
+
+ /**
+ * @param baseLocation - the location of the resource that contains the uri
+ * @param publicId - an optional public identifier (i.e. namespace name), or null if none
+ * @param systemId - an absolute or relative URI, or null if none
+ * @return an absolute URI represention the 'physical' location of the resource
+ */
+ public String resolvePhysicalLocation(String baseLocation, String publicId, String logicalLocation);
}