You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2017/05/29 22:21:59 UTC
[2/5] incubator-juneau git commit: Improved support for resolution of
URIs.
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c4952d2c/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerBuilder.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerBuilder.java b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerBuilder.java
index fe9858b..79d6a29 100644
--- a/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerBuilder.java
+++ b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerBuilder.java
@@ -378,97 +378,111 @@ public class SerializerBuilder extends CoreObjectBuilder {
}
/**
- * <b>Configuration property:</b> URI base for relative URIs.
+ * <b>Configuration property:</b> URI context bean.
* <p>
* <ul>
- * <li><b>Name:</b> <js>"Serializer.relativeUriBase"</js>
- * <li><b>Data type:</b> <code>String</code>
- * <li><b>Default:</b> <js>""</js>
+ * <li><b>Name:</b> <js>"Serializer.uriContext"</js>
+ * <li><b>Data type:</b> {@link UriContext}
+ * <li><b>Default:</b> {@link UriContext#DEFAULT}
* <li><b>Session-overridable:</b> <jk>true</jk>
* </ul>
* <p>
- * Prepended to relative URIs during serialization (along with the {@link SerializerContext#SERIALIZER_absolutePathUriBase} if specified.
- * (i.e. URIs not containing a schema and not starting with <js>'/'</js>).
- * (e.g. <js>"foo/bar"</js>)
- *
- * <h5 class='section'>Example:</h5>
- * <table class='styled'>
- * <tr><th>SERIALIZER_relativeUriBase</th><th>URI</th><th>Serialized URI</th></tr>
- * <tr>
- * <td><code>http://foo:9080/bar/baz</code></td>
- * <td><code>mywebapp</code></td>
- * <td><code>http://foo:9080/bar/baz/mywebapp</code></td>
- * </tr>
- * <tr>
- * <td><code>http://foo:9080/bar/baz</code></td>
- * <td><code>/mywebapp</code></td>
- * <td><code>/mywebapp</code></td>
- * </tr>
- * <tr>
- * <td><code>http://foo:9080/bar/baz</code></td>
- * <td><code>http://mywebapp</code></td>
- * <td><code>http://mywebapp</code></td>
- * </tr>
- * </table>
+ * Bean used for resolution of URIs to absolute or root-relative form.
+ * <p>
+ * <h6 class='figure'>Example:</h6>
+ * <p class='bcode'>
+ * <js>"{authority:'http://localhost:10000',contextRoot:'/myContext',servletPath:'/myServlet',pathInfo:'/foo'}"</js>
+ * </p>
* <p>
* <h5 class='section'>Notes:</h5>
* <ul>
- * <li>This is equivalent to calling <code>property(<jsf>SERIALIZER_relativeUriBase</jsf>, value)</code>.
+ * <li>This is equivalent to calling <code>property(<jsf>SERIALIZER_uriContext</jsf>, value)</code>.
* </ul>
*
* @param value The new value for this property.
* @return This object (for method chaining).
- * @see SerializerContext#SERIALIZER_relativeUriBase
+ * @see SerializerContext#SERIALIZER_uriContext
*/
- public SerializerBuilder relativeUriBase(String value) {
- return property(SERIALIZER_relativeUriBase, value);
+ public SerializerBuilder uriContext(UriContext value) {
+ return property(SERIALIZER_uriContext, value);
}
/**
- * <b>Configuration property:</b> URI base for relative URIs with absolute paths.
+ * <b>Configuration property:</b> URI resolution.
* <p>
* <ul>
- * <li><b>Name:</b> <js>"Serializer.absolutePathUriBase"</js>
- * <li><b>Data type:</b> <code>String</code>
- * <li><b>Default:</b> <js>""</js>
+ * <li><b>Name:</b> <js>"Serializer.uriResolution"</js>
+ * <li><b>Data type:</b> {@link UriResolution}
+ * <li><b>Default:</b> {@link UriResolution#ROOT_RELATIVE}
* <li><b>Session-overridable:</b> <jk>true</jk>
* </ul>
* <p>
- * Prepended to relative absolute-path URIs during serialization.
- * (i.e. URIs starting with <js>'/'</js>).
- * (e.g. <js>"/foo/bar"</js>)
+ * Defines the resolution level for URIs when serializing any of the following:
+ * <ul>
+ * <li>{@link java.net.URI}
+ * <li>{@link java.net.URL}
+ * <li>Properties annotated with {@link org.apache.juneau.annotation.URI @URI}
+ * </ul>
+ * <p>
+ * Possible values are:
+ * <ul>
+ * <li>{@link UriResolution#ABSOLUTE}
+ * - Resolve to an absolute URL (e.g. <js>"http://host:port/context-root/servlet-path/path-info"</js>).
+ * <li>{@link UriResolution#ROOT_RELATIVE}
+ * - Resolve to a root-relative URL (e.g. <js>"/context-root/servlet-path/path-info"</js>).
+ * <li>{@link UriResolution#NONE}
+ * - Don't do any URL resolution.
+ * </ul>
+ * <p>
+ * <h5 class='section'>Notes:</h5>
+ * <ul>
+ * <li>This is equivalent to calling <code>property(<jsf>SERIALIZER_uriResolution</jsf>, value)</code>.
+ * </ul>
*
- * <h5 class='section'>Examples:</h5>
- * <table class='styled'>
- * <tr><th>SERIALIZER_absolutePathUriBase</th><th>URI</th><th>Serialized URI</th></tr>
- * <tr>
- * <td><code>http://foo:9080/bar/baz</code></td>
- * <td><code>mywebapp</code></td>
- * <td><code>mywebapp</code></td>
- * </tr>
- * <tr>
- * <td><code>http://foo:9080/bar/baz</code></td>
- * <td><code>/mywebapp</code></td>
- * <td><code>http://foo:9080/bar/baz/mywebapp</code></td>
- * </tr>
- * <tr>
- * <td><code>http://foo:9080/bar/baz</code></td>
- * <td><code>http://mywebapp</code></td>
- * <td><code>http://mywebapp</code></td>
- * </tr>
- * </table>
+ * @param value The new value for this property.
+ * @return This object (for method chaining).
+ * @see SerializerContext#SERIALIZER_uriResolution
+ */
+ public SerializerBuilder uriResolution(UriResolution value) {
+ return property(SERIALIZER_uriResolution, value);
+ }
+
+ /**
+ * <b>Configuration property:</b> URI relativity.
+ * <p>
+ * <ul>
+ * <li><b>Name:</b> <js>"Serializer.uriRelativity"</js>
+ * <li><b>Data type:</b> {@link UriRelativity}
+ * <li><b>Default:</b> {@link UriRelativity#RESOURCE}
+ * <li><b>Session-overridable:</b> <jk>true</jk>
+ * </ul>
+ * <p>
+ * Defines what relative URIs are relative to when serializing any of the following:
+ * <ul>
+ * <li>{@link java.net.URI}
+ * <li>{@link java.net.URL}
+ * <li>Properties annotated with {@link org.apache.juneau.annotation.URI @URI}
+ * </ul>
+ * <p>
+ * Possible values are:
+ * <ul>
+ * <li>{@link UriRelativity#RESOURCE}
+ * - Relative URIs should be considered relative to the servlet URI.
+ * <li>{@link UriRelativity#PATH_INFO}
+ * - Relative URIs should be considered relative to the request URI.
+ * </ul>
* <p>
* <h5 class='section'>Notes:</h5>
* <ul>
- * <li>This is equivalent to calling <code>property(<jsf>SERIALIZER_absolutePathUriBase</jsf>, value)</code>.
+ * <li>This is equivalent to calling <code>property(<jsf>SERIALIZER_uriRelativity</jsf>, value)</code>.
* </ul>
*
* @param value The new value for this property.
* @return This object (for method chaining).
- * @see SerializerContext#SERIALIZER_absolutePathUriBase
+ * @see SerializerContext#SERIALIZER_uriRelativity
*/
- public SerializerBuilder absolutePathUriBase(String value) {
- return property(SERIALIZER_absolutePathUriBase, value);
+ public SerializerBuilder uriRelativity(UriRelativity value) {
+ return property(SERIALIZER_uriRelativity, value);
}
/**
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c4952d2c/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerContext.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerContext.java b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerContext.java
index fd890f9..372e8e0 100644
--- a/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerContext.java
+++ b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerContext.java
@@ -14,7 +14,6 @@ package org.apache.juneau.serializer;
import org.apache.juneau.*;
import org.apache.juneau.annotation.*;
-import org.apache.juneau.internal.*;
/**
* Configurable properties common to all serializers.
@@ -215,96 +214,79 @@ public class SerializerContext extends BeanContext {
public static final String SERIALIZER_trimStrings = "Serializer.trimStrings";
/**
- * <b>Configuration property:</b> URI base for relative URIs.
+ * <b>Configuration property:</b> URI context bean.
* <p>
* <ul>
- * <li><b>Name:</b> <js>"Serializer.relativeUriBase"</js>
- * <li><b>Data type:</b> <code>String</code>
- * <li><b>Default:</b> <js>""</js>
+ * <li><b>Name:</b> <js>"Serializer.uriContext"</js>
+ * <li><b>Data type:</b> {@link UriContext}
+ * <li><b>Default:</b> {@link UriContext#DEFAULT}
* <li><b>Session-overridable:</b> <jk>true</jk>
* </ul>
* <p>
- * Prepended to relative URIs during serialization (along with the {@link #SERIALIZER_absolutePathUriBase} if specified.
- * (i.e. URIs not containing a schema and not starting with <js>'/'</js>).
- * (e.g. <js>"foo/bar"</js>)
- *
- * <h5 class='section'>Example:</h5>
- * <table class='styled'>
- * <tr><th>SERIALIZER_relativeUriBase</th><th>URI</th><th>Serialized URI</th></tr>
- * <tr>
- * <td><code>http://foo:9080/bar/baz</code></td>
- * <td><code>mywebapp</code></td>
- * <td><code>http://foo:9080/bar/baz/mywebapp</code></td>
- * </tr>
- * <tr>
- * <td><code>http://foo:9080/bar/baz</code></td>
- * <td><code>/mywebapp</code></td>
- * <td><code>/mywebapp</code></td>
- * </tr>
- * <tr>
- * <td><code>http://foo:9080/bar/baz</code></td>
- * <td><code>http://mywebapp</code></td>
- * <td><code>http://mywebapp</code></td>
- * </tr>
- * </table>
+ * Bean used for resolution of URIs to absolute or root-relative form.
+ * <p>
+ * <h6 class='figure'>Example:</h6>
+ * <p class='bcode'>
+ * <js>"{authority:'http://localhost:10000',contextRoot:'/myContext',servletPath:'/myServlet',pathInfo:'/foo'}"</js>
+ * </p>
*/
- public static final String SERIALIZER_relativeUriBase = "Serializer.relativeUriBase";
+ public static final String SERIALIZER_uriContext = "Serializer.uriContext";
/**
- * <b>Configuration property:</b> URI base for relative URIs with absolute paths.
+ * <b>Configuration property:</b> URI resolution.
* <p>
* <ul>
- * <li><b>Name:</b> <js>"Serializer.absolutePathUriBase"</js>
- * <li><b>Data type:</b> <code>String</code>
- * <li><b>Default:</b> <js>""</js>
+ * <li><b>Name:</b> <js>"Serializer.uriResolution"</js>
+ * <li><b>Data type:</b> {@link UriResolution}
+ * <li><b>Default:</b> {@link UriResolution#ROOT_RELATIVE}
* <li><b>Session-overridable:</b> <jk>true</jk>
* </ul>
* <p>
- * Prepended to relative absolute-path URIs during serialization.
- * (i.e. URIs starting with <js>'/'</js>).
- * (e.g. <js>"/foo/bar"</js>)
- *
- * <h5 class='section'>Examples:</h5>
- * <table class='styled'>
- * <tr><th>SERIALIZER_absolutePathUriBase</th><th>URI</th><th>Serialized URI</th></tr>
- * <tr>
- * <td><code>http://foo:9080/bar/baz</code></td>
- * <td><code>mywebapp</code></td>
- * <td><code>mywebapp</code></td>
- * </tr>
- * <tr>
- * <td><code>http://foo:9080/bar/baz</code></td>
- * <td><code>/mywebapp</code></td>
- * <td><code>http://foo:9080/bar/baz/mywebapp</code></td>
- * </tr>
- * <tr>
- * <td><code>http://foo:9080/bar/baz</code></td>
- * <td><code>http://mywebapp</code></td>
- * <td><code>http://mywebapp</code></td>
- * </tr>
- * </table>
+ * Defines the resolution level for URIs when serializing any of the following:
+ * <ul>
+ * <li>{@link java.net.URI}
+ * <li>{@link java.net.URL}
+ * <li>Properties annotated with {@link org.apache.juneau.annotation.URI @URI}
+ * </ul>
+ * <p>
+ * Possible values are:
+ * <ul>
+ * <li>{@link UriResolution#ABSOLUTE}
+ * - Resolve to an absolute URL (e.g. <js>"http://host:port/context-root/servlet-path/path-info"</js>).
+ * <li>{@link UriResolution#ROOT_RELATIVE}
+ * - Resolve to a root-relative URL (e.g. <js>"/context-root/servlet-path/path-info"</js>).
+ * <li>{@link UriResolution#NONE}
+ * - Don't do any URL resolution.
+ * </ul>
*/
- public static final String SERIALIZER_absolutePathUriBase = "Serializer.absolutePathUriBase";
+ public static final String SERIALIZER_uriResolution = "Serializer.uriResolution";
/**
- * <b>Configuration property:</b> URI context bean.
+ * <b>Configuration property:</b> URI relativity.
* <p>
* <ul>
- * <li><b>Name:</b> <js>"Serializer.uriContext"</js>
- * <li><b>Data type:</b> {@link UriContext}
- * <li><b>Default:</b> {@link UriContext#DEFAULT}
+ * <li><b>Name:</b> <js>"Serializer.uriRelativity"</js>
+ * <li><b>Data type:</b> {@link UriRelativity}
+ * <li><b>Default:</b> {@link UriRelativity#RESOURCE}
* <li><b>Session-overridable:</b> <jk>true</jk>
* </ul>
* <p>
- * Bean used for resolution of URIs to absolute or root-relative form.
+ * Defines what relative URIs are relative to when serializing any of the following:
+ * <ul>
+ * <li>{@link java.net.URI}
+ * <li>{@link java.net.URL}
+ * <li>Properties annotated with {@link org.apache.juneau.annotation.URI @URI}
+ * </ul>
* <p>
- * For example, to define a URI context that causes relative URIs to be converted to root-relative form and
- * assumes relative URIs are relative to the servlet path:
- * <p class='bcode'>
- * <js>"{resolution:'ROOT_RELATIVE',relativity:'RESOURCE',contextRoot:'/myContext',servletPath:'/myServlet'}"</js>
- * </p>
+ * Possible values are:
+ * <ul>
+ * <li>{@link UriRelativity#RESOURCE}
+ * - Relative URIs should be considered relative to the servlet URI.
+ * <li>{@link UriRelativity#PATH_INFO}
+ * - Relative URIs should be considered relative to the request URI.
+ * </ul>
*/
- public static final String SERIALIZER_uriContext = "Serializer.uriContext";
+ public static final String SERIALIZER_uriRelativity = "Serializer.uriRelativity";
/**
* <b>Configuration property:</b> Sort arrays and collections alphabetically.
@@ -368,8 +350,9 @@ public class SerializerContext extends BeanContext {
sortMaps,
abridged;
final char quoteChar;
- final String relativeUriBase, absolutePathUriBase;
final UriContext uriContext;
+ final UriResolution uriResolution;
+ final UriRelativity uriRelativity;
/**
* Constructor.
@@ -392,27 +375,9 @@ public class SerializerContext extends BeanContext {
sortMaps = ps.getProperty(SERIALIZER_sortMaps, boolean.class, false);
abridged = ps.getProperty(SERIALIZER_abridged, boolean.class, false);
quoteChar = ps.getProperty(SERIALIZER_quoteChar, String.class, "\"").charAt(0);
- relativeUriBase = resolveRelativeUriBase(ps.getProperty(SERIALIZER_relativeUriBase, String.class, ""));
- absolutePathUriBase = resolveAbsolutePathUriBase(ps.getProperty(SERIALIZER_absolutePathUriBase, String.class, ""));
uriContext = ps.getProperty(SERIALIZER_uriContext, UriContext.class, UriContext.DEFAULT);
- }
-
- private static String resolveRelativeUriBase(String s) {
- if (StringUtils.isEmpty(s))
- return null;
- if (s.equals("/"))
- return s;
- else if (StringUtils.endsWith(s, '/'))
- s = s.substring(0, s.length()-1);
- return s;
- }
-
- private static String resolveAbsolutePathUriBase(String s) {
- if (StringUtils.isEmpty(s))
- return null;
- if (StringUtils.endsWith(s, '/'))
- s = s.substring(0, s.length()-1);
- return s;
+ uriResolution = ps.getProperty(SERIALIZER_uriResolution, UriResolution.class, UriResolution.ROOT_RELATIVE);
+ uriRelativity = ps.getProperty(SERIALIZER_uriRelativity, UriRelativity.class, UriRelativity.RESOURCE);
}
@Override /* Context */
@@ -433,9 +398,9 @@ public class SerializerContext extends BeanContext {
.append("sortMaps", sortMaps)
.append("parserKnowsRootTypes", abridged)
.append("quoteChar", quoteChar)
- .append("relativeUriBase", relativeUriBase)
- .append("absolutePathUriBase", absolutePathUriBase)
.append("uriContext", uriContext)
+ .append("uriResolution", uriResolution)
+ .append("uriRelativity", uriRelativity)
);
}
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c4952d2c/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerGroupBuilder.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerGroupBuilder.java b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerGroupBuilder.java
index f97ed7a..bbe7577 100644
--- a/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerGroupBuilder.java
+++ b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerGroupBuilder.java
@@ -347,25 +347,36 @@ public class SerializerGroupBuilder {
}
/**
- * Sets the {@link SerializerContext#SERIALIZER_relativeUriBase} property on all serializers in this group.
+ * Sets the {@link SerializerContext#SERIALIZER_uriContext} property on all serializers in this group.
*
* @param value The new value for this property.
* @return This object (for method chaining).
- * @see SerializerContext#SERIALIZER_relativeUriBase
+ * @see SerializerContext#SERIALIZER_uriContext
*/
- public SerializerGroupBuilder relativeUriBase(String value) {
- return property(SERIALIZER_relativeUriBase, value);
+ public SerializerGroupBuilder uriContext(UriContext value) {
+ return property(SERIALIZER_uriContext, value);
}
/**
- * Sets the {@link SerializerContext#SERIALIZER_absolutePathUriBase} property on all serializers in this group.
+ * Sets the {@link SerializerContext#SERIALIZER_uriResolution} property on all serializers in this group.
*
* @param value The new value for this property.
* @return This object (for method chaining).
- * @see SerializerContext#SERIALIZER_absolutePathUriBase
+ * @see SerializerContext#SERIALIZER_uriResolution
*/
- public SerializerGroupBuilder absolutePathUriBase(String value) {
- return property(SERIALIZER_absolutePathUriBase, value);
+ public SerializerGroupBuilder uriResolution(UriResolution value) {
+ return property(SERIALIZER_uriResolution, value);
+ }
+
+ /**
+ * Sets the {@link SerializerContext#SERIALIZER_uriRelativity} property on all serializers in this group.
+ *
+ * @param value The new value for this property.
+ * @return This object (for method chaining).
+ * @see SerializerContext#SERIALIZER_uriRelativity
+ */
+ public SerializerGroupBuilder uriRelativity(UriRelativity value) {
+ return property(SERIALIZER_uriRelativity, value);
}
/**
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c4952d2c/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerSession.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerSession.java b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerSession.java
index d89203b..9a96a30 100644
--- a/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerSession.java
+++ b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerSession.java
@@ -53,8 +53,7 @@ public class SerializerSession extends BeanSession {
sortMaps,
abridged;
private final char quoteChar;
- private final String relativeUriBase, absolutePathUriBase;
- private final UriContext uriContext;
+ private final UriResolver uriResolver;
/** The current indentation depth into the model. */
public int indent;
@@ -102,7 +101,8 @@ public class SerializerSession extends BeanSession {
super(ctx, op, locale, timeZone, mediaType);
this.javaMethod = javaMethod;
this.output = output;
- this.uriContext = (uriContext != null ? uriContext : ctx.uriContext);
+ UriResolution uriResolution;
+ UriRelativity uriRelativity;
if (op == null || op.isEmpty()) {
maxDepth = ctx.maxDepth;
initialDepth = ctx.initialDepth;
@@ -115,11 +115,11 @@ public class SerializerSession extends BeanSession {
trimEmptyMaps = ctx.trimEmptyMaps;
trimStrings = ctx.trimStrings;
quoteChar = ctx.quoteChar;
- relativeUriBase = ctx.relativeUriBase;
- absolutePathUriBase = ctx.absolutePathUriBase;
sortCollections = ctx.sortCollections;
sortMaps = ctx.sortMaps;
abridged = ctx.abridged;
+ uriResolution = ctx.uriResolution;
+ uriRelativity = ctx.uriRelativity;
} else {
maxDepth = op.getInt(SERIALIZER_maxDepth, ctx.maxDepth);
initialDepth = op.getInt(SERIALIZER_initialDepth, ctx.initialDepth);
@@ -132,13 +132,15 @@ public class SerializerSession extends BeanSession {
trimEmptyMaps = op.getBoolean(SERIALIZER_trimEmptyMaps, ctx.trimEmptyMaps);
trimStrings = op.getBoolean(SERIALIZER_trimStrings, ctx.trimStrings);
quoteChar = op.getString(SERIALIZER_quoteChar, ""+ctx.quoteChar).charAt(0);
- relativeUriBase = op.getString(SERIALIZER_relativeUriBase, ctx.relativeUriBase);
- absolutePathUriBase = op.getString(SERIALIZER_absolutePathUriBase, ctx.absolutePathUriBase);
sortCollections = op.getBoolean(SERIALIZER_sortCollections, ctx.sortMaps);
sortMaps = op.getBoolean(SERIALIZER_sortMaps, ctx.sortMaps);
abridged = op.getBoolean(SERIALIZER_abridged, ctx.abridged);
+ uriResolution = op.get(UriResolution.class, SERIALIZER_uriResolution, UriResolution.ROOT_RELATIVE);
+ uriRelativity = op.get(UriRelativity.class, SERIALIZER_uriRelativity, UriRelativity.RESOURCE);
}
+ uriResolver = new UriResolver(uriResolution, uriRelativity, uriContext == null ? ctx.uriContext : uriContext);
+
this.indent = initialDepth;
if (detectRecursions || isDebug()) {
set = new IdentityHashMap<Object,Object>();
@@ -244,12 +246,12 @@ public class SerializerSession extends BeanSession {
}
/**
- * Returns the URI context passed in to this constructor.
+ * Returns the URI resolver.
*
- * @return The URI context passed in to this constructor.
+ * @return The URI resolver.
*/
- public final UriContext getUriContext() {
- return uriContext;
+ public final UriResolver getUriResolver() {
+ return uriResolver;
}
/**
@@ -370,24 +372,6 @@ public class SerializerSession extends BeanSession {
}
/**
- * Returns the {@link SerializerContext#SERIALIZER_relativeUriBase} setting value for this session.
- *
- * @return The {@link SerializerContext#SERIALIZER_relativeUriBase} setting value for this session.
- */
- public final String getRelativeUriBase() {
- return relativeUriBase;
- }
-
- /**
- * Returns the {@link SerializerContext#SERIALIZER_absolutePathUriBase} setting value for this session.
- *
- * @return The {@link SerializerContext#SERIALIZER_absolutePathUriBase} setting value for this session.
- */
- public final String getAbsolutePathUriBase() {
- return absolutePathUriBase;
- }
-
- /**
* Push the specified object onto the stack.
*
* @param attrName The attribute name.
@@ -578,30 +562,67 @@ public class SerializerSession extends BeanSession {
}
/**
- * Converts a String to an absolute URI based on the {@link SerializerContext#SERIALIZER_absolutePathUriBase} and
- * {@link SerializerContext#SERIALIZER_relativeUriBase} settings on this context.
+ * Converts a String to an absolute URI based on the {@link UriContext} on this session.
*
* @param uri The input URI.
+ * Can be any of the following:
+ * <ul>
+ * <li>{@link java.net.URI}
+ * <li>{@link java.net.URL}
+ * <li>{@link CharSequence}
+ * </ul>
+ * URI can be any of the following forms:
+ * <ul>
+ * <li><js>"foo://foo"</js> - Absolute URI.
+ * <li><js>"/foo"</js> - Root-relative URI.
+ * <li><js>"/"</js> - Root URI.
+ * <li><js>"context:/foo"</js> - Context-root-relative URI.
+ * <li><js>"context:/"</js> - Context-root URI.
+ * <li><js>"servlet:/foo"</js> - Servlet-path-relative URI.
+ * <li><js>"servlet:/"</js> - Servlet-path URI.
+ * <li><js>"request:/foo"</js> - Request-path-relative URI.
+ * <li><js>"request:/"</js> - Request-path URI.
+ * <li><js>"foo"</js> - Path-info-relative URI.
+ * <li><js>""</js> - Path-info URI.
+ * </ul>
* @return The resolved URI.
*/
- public String resolveUri(String uri) {
- if (uri.indexOf("://") != -1 || (absolutePathUriBase == null && relativeUriBase == null))
- return uri;
- StringBuilder sb = getStringBuilder();
- if (StringUtils.startsWith(uri, '/')) {
- if (absolutePathUriBase != null)
- sb.append(absolutePathUriBase);
- } else {
- if (relativeUriBase != null) {
- sb.append(relativeUriBase);
- if (! uri.equals("/"))
- sb.append("/");
- }
- }
- sb.append(uri);
- String s = sb.toString();
- returnStringBuilder(sb);
- return s;
+ public String resolveUri(Object uri) {
+ return uriResolver.resolve(uri);
+ }
+
+ /**
+ * Opposite of {@link #resolveUri(Object)}.
+ * <p>
+ * Converts the URI to a value relative to the specified <code>relativeTo</code> parameter.
+ * <p>
+ * Both parameters can be any of the following:
+ * <ul>
+ * <li>{@link java.net.URI}
+ * <li>{@link java.net.URL}
+ * <li>{@link CharSequence}
+ * </ul>
+ * Both URIs can be any of the following forms:
+ * <ul>
+ * <li><js>"foo://foo"</js> - Absolute URI.
+ * <li><js>"/foo"</js> - Root-relative URI.
+ * <li><js>"/"</js> - Root URI.
+ * <li><js>"context:/foo"</js> - Context-root-relative URI.
+ * <li><js>"context:/"</js> - Context-root URI.
+ * <li><js>"servlet:/foo"</js> - Servlet-path-relative URI.
+ * <li><js>"servlet:/"</js> - Servlet-path URI.
+ * <li><js>"request:/foo"</js> - Request-path-relative URI.
+ * <li><js>"request:/"</js> - Request-path URI.
+ * <li><js>"foo"</js> - Path-info-relative URI.
+ * <li><js>""</js> - Path-info URI.
+ * </ul>
+ *
+ * @param relativeTo The URI to relativize against.
+ * @param uri The URI to relativize.
+ * @return The relativized URI.
+ */
+ public String relativizeUri(Object relativeTo, Object uri) {
+ return uriResolver.relativize(relativeTo, uri);
}
/**
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c4952d2c/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerWriter.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerWriter.java b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerWriter.java
index 413aa63..3250d1a 100644
--- a/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerWriter.java
+++ b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerWriter.java
@@ -16,7 +16,6 @@ import java.io.*;
import java.net.*;
import org.apache.juneau.*;
-import org.apache.juneau.internal.*;
/**
* Simple wrapper around a standard {@link Writer} with additional methods.
@@ -43,14 +42,8 @@ public class SerializerWriter extends Writer {
/** The quote character being used by this writer. */
protected final char quoteChar;
- /** The base (e.g. <js>https://localhost:9443/contextPath"</js>) for relative URIs (e.g. <js>"my/path"</js>). */
- protected final String relativeUriBase;
-
- /** The base (e.g. <js>https://localhost:9443"</js>) for relative URIs with absolute paths (e.g. <js>"/contextPath/my/path"</js>). */
- protected final String absolutePathUriBase;
-
- /** The URI context of the request. (i.e. the REST request URL broken down into authority/context/servlet/pathInfo parts. */
- protected final UriContext uriContext;
+ /** The URI resolver of the request. */
+ protected final UriResolver uriResolver;
/**
* @param out The writer being wrapped.
@@ -58,19 +51,14 @@ public class SerializerWriter extends Writer {
* {@link #s()} will write a space character.
* @param trimStrings If <jk>true</jk>, strings should be trimmed before they're serialized.
* @param quoteChar The character to write when {@link #q()} is called.
- * @param relativeUriBase The base (e.g. <js>https://localhost:9443/contextPath"</js>) for relative URIs (e.g. <js>"my/path"</js>).
- * @param absolutePathUriBase The base (e.g. <js>https://localhost:9443"</js>) for relative URIs with absolute paths (e.g. <js>"/contextPath/my/path"</js>).
- * @param uriContext The URI context.
- * Identifies the current request URI used for resolution of URIs to absolute or root-relative form.
+ * @param uriResolver The URI resolver for resolving URIs to absolute or root-relative form.
*/
- public SerializerWriter(Writer out, boolean useWhitespace, boolean trimStrings, char quoteChar, String relativeUriBase, String absolutePathUriBase, UriContext uriContext) {
+ public SerializerWriter(Writer out, boolean useWhitespace, boolean trimStrings, char quoteChar, UriResolver uriResolver) {
this.out = out;
this.useWhitespace = useWhitespace;
this.trimStrings = trimStrings;
this.quoteChar = quoteChar;
- this.relativeUriBase = relativeUriBase;
- this.absolutePathUriBase = absolutePathUriBase;
- this.uriContext = uriContext != null ? uriContext : new UriContext();
+ this.uriResolver = uriResolver;
}
/**
@@ -160,32 +148,17 @@ public class SerializerWriter extends Writer {
* Object is converted to a <code>String</code> using <code>toString()</code>, so this will work on {@link URL} or {@link URI} objects,
* or any other type that returns a URI via it's <code>toString()</code> method.
* <p>
- * If the URI is relative (i.e. without a schema and not prepended with <js>'/'</js>) the URI
- * will be prepended with {@link #absolutePathUriBase} and {@link #relativeUriBase}.
- * <p>
- * If the URI is context-absolute (i.e. without a schema, but prepended with <js>'/'</js>)
- * the URI will be prepended with {@link #absolutePathUriBase}.
- *
+ * The URI is resolved based on the {@link SerializerContext#SERIALIZER_uriRelativity} and
+ * {@link SerializerContext#SERIALIZER_uriResolution} settings and the {@link UriContext} that's part of the
+ * session.
+ *
* @param uri The URI to serialize.
* @return This object (for method chaining).
* @throws IOException If a problem occurred trying to write to the writer.
*/
public SerializerWriter appendUri(Object uri) throws IOException {
- String s = uri.toString();
- if (s.indexOf("://") == -1) {
- if (StringUtils.startsWith(s, '/')) {
- if (absolutePathUriBase != null)
- append(absolutePathUriBase);
- } else {
- if (relativeUriBase != null) {
- append(relativeUriBase);
- if (! relativeUriBase.equals("/"))
- append("/");
-
- }
- }
- }
- return append(s);
+ uriResolver.append(this, uri);
+ return this;
}
/**
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c4952d2c/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializerBuilder.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializerBuilder.java b/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializerBuilder.java
index 7436ddc..015e662 100644
--- a/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializerBuilder.java
+++ b/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializerBuilder.java
@@ -188,14 +188,20 @@ public class SoapXmlSerializerBuilder extends XmlSerializerBuilder {
}
@Override /* SerializerBuilder */
- public SoapXmlSerializerBuilder relativeUriBase(String value) {
- super.relativeUriBase(value);
+ public SoapXmlSerializerBuilder uriContext(UriContext value) {
+ super.uriContext(value);
return this;
}
@Override /* SerializerBuilder */
- public SoapXmlSerializerBuilder absolutePathUriBase(String value) {
- super.absolutePathUriBase(value);
+ public SoapXmlSerializerBuilder uriResolution(UriResolution value) {
+ super.uriResolution(value);
+ return this;
+ }
+
+ @Override /* SerializerBuilder */
+ public SoapXmlSerializerBuilder uriRelativity(UriRelativity value) {
+ super.uriRelativity(value);
return this;
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c4952d2c/juneau-core/src/main/java/org/apache/juneau/uon/UonSerializerBuilder.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/uon/UonSerializerBuilder.java b/juneau-core/src/main/java/org/apache/juneau/uon/UonSerializerBuilder.java
index 2106637..3d0ee3d 100644
--- a/juneau-core/src/main/java/org/apache/juneau/uon/UonSerializerBuilder.java
+++ b/juneau-core/src/main/java/org/apache/juneau/uon/UonSerializerBuilder.java
@@ -169,14 +169,20 @@ public class UonSerializerBuilder extends SerializerBuilder {
}
@Override /* SerializerBuilder */
- public UonSerializerBuilder relativeUriBase(String value) {
- super.relativeUriBase(value);
+ public UonSerializerBuilder uriContext(UriContext value) {
+ super.uriContext(value);
return this;
}
@Override /* SerializerBuilder */
- public UonSerializerBuilder absolutePathUriBase(String value) {
- super.absolutePathUriBase(value);
+ public UonSerializerBuilder uriResolution(UriResolution value) {
+ super.uriResolution(value);
+ return this;
+ }
+
+ @Override /* SerializerBuilder */
+ public UonSerializerBuilder uriRelativity(UriRelativity value) {
+ super.uriRelativity(value);
return this;
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c4952d2c/juneau-core/src/main/java/org/apache/juneau/uon/UonSerializerSession.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/uon/UonSerializerSession.java b/juneau-core/src/main/java/org/apache/juneau/uon/UonSerializerSession.java
index 0f9b5a1..e9f9659 100644
--- a/juneau-core/src/main/java/org/apache/juneau/uon/UonSerializerSession.java
+++ b/juneau-core/src/main/java/org/apache/juneau/uon/UonSerializerSession.java
@@ -87,6 +87,6 @@ public class UonSerializerSession extends SerializerSession {
Object output = getOutput();
if (output instanceof UonWriter)
return (UonWriter)output;
- return new UonWriter(this, super.getWriter(), isUseWhitespace(), isEncodeChars(), isTrimStrings(), getRelativeUriBase(), getAbsolutePathUriBase(), getUriContext());
+ return new UonWriter(this, super.getWriter(), isUseWhitespace(), isEncodeChars(), isTrimStrings(), getUriResolver());
}
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c4952d2c/juneau-core/src/main/java/org/apache/juneau/uon/UonWriter.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/uon/UonWriter.java b/juneau-core/src/main/java/org/apache/juneau/uon/UonWriter.java
index 85f9cd0..2844e62 100644
--- a/juneau-core/src/main/java/org/apache/juneau/uon/UonWriter.java
+++ b/juneau-core/src/main/java/org/apache/juneau/uon/UonWriter.java
@@ -53,13 +53,10 @@ public final class UonWriter extends SerializerWriter {
* @param useWhitespace If <jk>true</jk>, tabs will be used in output.
* @param encodeChars If <jk>true</jk>, special characters should be encoded.
* @param trimStrings If <jk>true</jk>, strings should be trimmed before they're serialized.
- * @param relativeUriBase The base (e.g. <js>https://localhost:9443/contextPath"</js>) for relative URIs (e.g. <js>"my/path"</js>).
- * @param absolutePathUriBase The base (e.g. <js>https://localhost:9443"</js>) for relative URIs with absolute paths (e.g. <js>"/contextPath/my/path"</js>).
- * @param uriContext The URI context.
- * Identifies the current request URI used for resolution of URIs to absolute or root-relative form.
+ * @param uriResolver The URI resolver for resolving URIs to absolute or root-relative form.
*/
- protected UonWriter(UonSerializerSession session, Writer out, boolean useWhitespace, boolean encodeChars, boolean trimStrings, String relativeUriBase, String absolutePathUriBase, UriContext uriContext) {
- super(out, useWhitespace, trimStrings, '\'', relativeUriBase, absolutePathUriBase, uriContext);
+ protected UonWriter(UonSerializerSession session, Writer out, boolean useWhitespace, boolean encodeChars, boolean trimStrings, UriResolver uriResolver) {
+ super(out, useWhitespace, trimStrings, '\'', uriResolver);
this.session = session;
this.encodeChars = encodeChars;
}
@@ -167,20 +164,7 @@ public final class UonWriter extends SerializerWriter {
*/
@Override
public SerializerWriter appendUri(Object uri) throws IOException {
- String s = uri.toString();
- if (s.indexOf("://") == -1) {
- if (StringUtils.startsWith(s, '/')) {
- if (absolutePathUriBase != null)
- append(absolutePathUriBase);
- } else {
- if (relativeUriBase != null) {
- append(relativeUriBase);
- if (! relativeUriBase.equals("/"))
- append("/");
- }
- }
- }
- return appendObject(s, false, false);
+ return appendObject(uriResolver.resolve(uri), false, false);
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c4952d2c/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializerBuilder.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializerBuilder.java b/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializerBuilder.java
index e795a48..7c79631 100644
--- a/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializerBuilder.java
+++ b/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializerBuilder.java
@@ -231,14 +231,20 @@ public class UrlEncodingSerializerBuilder extends UonSerializerBuilder {
}
@Override /* SerializerBuilder */
- public UrlEncodingSerializerBuilder relativeUriBase(String value) {
- super.relativeUriBase(value);
+ public UrlEncodingSerializerBuilder uriContext(UriContext value) {
+ super.uriContext(value);
return this;
}
@Override /* SerializerBuilder */
- public UrlEncodingSerializerBuilder absolutePathUriBase(String value) {
- super.absolutePathUriBase(value);
+ public UrlEncodingSerializerBuilder uriResolution(UriResolution value) {
+ super.uriResolution(value);
+ return this;
+ }
+
+ @Override /* SerializerBuilder */
+ public UrlEncodingSerializerBuilder uriRelativity(UriRelativity value) {
+ super.uriRelativity(value);
return this;
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c4952d2c/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaSerializer.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaSerializer.java b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaSerializer.java
index 0e42c27..9a499d2 100644
--- a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaSerializer.java
+++ b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaSerializer.java
@@ -266,7 +266,7 @@ public class XmlSchemaSerializer extends XmlSerializer {
this.defaultNs = defaultNs;
this.targetNs = targetNs;
this.session = session;
- w = new XmlWriter(sw, session.isUseWhitespace(), session.isTrimStrings(), session.getQuoteChar(), null, null, null, true, null);
+ w = new XmlWriter(sw, session.isUseWhitespace(), session.isTrimStrings(), session.getQuoteChar(), null, true, null);
int i = session.getIndent();
w.oTag(i, "schema");
w.attr("xmlns", xs.getUri());
@@ -418,7 +418,7 @@ public class XmlSchemaSerializer extends XmlSerializer {
Namespace cNs = first(xmlMeta.getNamespace(), ct2.getExtendedMeta(XmlClassMeta.class).getNamespace(), cm.getExtendedMeta(XmlClassMeta.class).getNamespace(), defaultNs);
if (xmlMeta.getNamespace() == null) {
w.oTag(i+2, "element")
- .attr("name", XmlUtils.encodeElementName(childName), true)
+ .attr("name", XmlUtils.encodeElementName(childName), false)
.attr("type", getXmlType(cNs, ct2))
.attr("minOccurs", 0);
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c4952d2c/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaSerializerBuilder.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaSerializerBuilder.java b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaSerializerBuilder.java
index 4af4739..9e24414 100644
--- a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaSerializerBuilder.java
+++ b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaSerializerBuilder.java
@@ -168,14 +168,20 @@ public class XmlSchemaSerializerBuilder extends XmlSerializerBuilder {
}
@Override /* SerializerBuilder */
- public XmlSchemaSerializerBuilder relativeUriBase(String value) {
- super.relativeUriBase(value);
+ public XmlSchemaSerializerBuilder uriContext(UriContext value) {
+ super.uriContext(value);
return this;
}
@Override /* SerializerBuilder */
- public XmlSchemaSerializerBuilder absolutePathUriBase(String value) {
- super.absolutePathUriBase(value);
+ public XmlSchemaSerializerBuilder uriResolution(UriResolution value) {
+ super.uriResolution(value);
+ return this;
+ }
+
+ @Override /* SerializerBuilder */
+ public XmlSchemaSerializerBuilder uriRelativity(UriRelativity value) {
+ super.uriRelativity(value);
return this;
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c4952d2c/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializer.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializer.java b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializer.java
index cd46134..af5ce51 100644
--- a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializer.java
+++ b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializer.java
@@ -520,7 +520,7 @@ public class XmlSerializer extends WriterSerializer {
// Render the tag contents.
if (o != null) {
if (sType.isUri() || (pMeta != null && pMeta.isUri())) {
- out.appendUri(o);
+ out.textUri(o);
} else if (sType.isCharSequence() || sType.isChar()) {
if (format == XMLTEXT)
out.append(o);
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c4952d2c/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializerBuilder.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializerBuilder.java b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializerBuilder.java
index dfdef5a..2e812b9 100644
--- a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializerBuilder.java
+++ b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializerBuilder.java
@@ -315,14 +315,20 @@ public class XmlSerializerBuilder extends SerializerBuilder {
}
@Override /* SerializerBuilder */
- public XmlSerializerBuilder relativeUriBase(String value) {
- super.relativeUriBase(value);
+ public XmlSerializerBuilder uriContext(UriContext value) {
+ super.uriContext(value);
return this;
}
@Override /* SerializerBuilder */
- public XmlSerializerBuilder absolutePathUriBase(String value) {
- super.absolutePathUriBase(value);
+ public XmlSerializerBuilder uriResolution(UriResolution value) {
+ super.uriResolution(value);
+ return this;
+ }
+
+ @Override /* SerializerBuilder */
+ public XmlSerializerBuilder uriRelativity(UriRelativity value) {
+ super.uriRelativity(value);
return this;
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c4952d2c/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java
index ef08f48..bd99974 100644
--- a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java
+++ b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java
@@ -200,6 +200,6 @@ public class XmlSerializerSession extends SerializerSession {
Object output = getOutput();
if (output instanceof XmlWriter)
return (XmlWriter)output;
- return new XmlWriter(super.getWriter(), isUseWhitespace(), isTrimStrings(), getQuoteChar(), getRelativeUriBase(), getAbsolutePathUriBase(), getUriContext(), isEnableNamespaces(), getDefaultNamespace());
+ return new XmlWriter(super.getWriter(), isUseWhitespace(), isTrimStrings(), getQuoteChar(), getUriResolver(), isEnableNamespaces(), getDefaultNamespace());
}
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c4952d2c/juneau-core/src/main/java/org/apache/juneau/xml/XmlUtils.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/XmlUtils.java b/juneau-core/src/main/java/org/apache/juneau/xml/XmlUtils.java
index 0567d73..483e06d 100644
--- a/juneau-core/src/main/java/org/apache/juneau/xml/XmlUtils.java
+++ b/juneau-core/src/main/java/org/apache/juneau/xml/XmlUtils.java
@@ -13,6 +13,7 @@
package org.apache.juneau.xml;
import java.io.*;
+import java.net.*;
import java.util.*;
import javax.xml.stream.*;
@@ -27,16 +28,110 @@ import org.apache.juneau.xml.annotation.*;
public final class XmlUtils {
//--------------------------------------------------------------------------------
- // Encode URI part
+ // XML element names
//--------------------------------------------------------------------------------
/**
- * Encodes invalid XML text characters to <code>_x####_</code> sequences.
+ * Encodes any invalid XML element name characters to <code>_x####_</code> sequences.
+ *
+ * @param w The writer to send the output to.
+ * @param o The object being encoded.
+ * @return The same writer passed in.
+ * @throws IOException Throw by the writer.
+ */
+ public static final Writer encodeElementName(Writer w, Object o) throws IOException {
+
+ if (o == null)
+ return w.append("_x0000_");
+
+ String s = o.toString();
+
+ if (needsElementNameEncoding(s))
+ return encodeElementNameInner(w, s);
+
+ w.append(s);
+ return w;
+ }
+
+ /**
+ * Encodes any invalid XML element name characters to <code>_x####_</code> sequences.
+ *
+ * @param o The object being encoded.
+ * @return The encoded element name string.
+ */
+ public static final String encodeElementName(Object o) {
+ if (o == null)
+ return "_x0000_";
+
+ String s = o.toString();
+ if (s.isEmpty())
+ return "_xE000_";
+ try {
+ if (needsElementNameEncoding(s))
+ return encodeElementNameInner(new StringBuilderWriter(s.length() * 2), s).toString();
+ } catch (IOException e) {
+ throw new RuntimeException(e); // Never happens
+ }
+
+ return s;
+ }
+
+ private static final Writer encodeElementNameInner(Writer w, String s) throws IOException {
+ for (int i = 0; i < s.length(); i++) {
+ char c = s.charAt(i);
+ if ((c >= 'A' && c <= 'Z')
+ || (c == '_' && ! isEscapeSequence(s,i))
+ || (c >= 'a' && c <= 'z')
+ || (i != 0 && (
+ c == '-'
+ || c == '.'
+ || (c >= '0' && c <= '9')
+ || c == '\u00b7'
+ || (c >= '\u0300' && c <= '\u036f')
+ || (c >= '\u203f' && c <= '\u2040')
+ ))
+ || (c >= '\u00c0' && c <= '\u00d6')
+ || (c >= '\u00d8' && c <= '\u00f6')
+ || (c >= '\u00f8' && c <= '\u02ff')
+ || (c >= '\u0370' && c <= '\u037d')
+ || (c >= '\u037f' && c <= '\u1fff')
+ || (c >= '\u200c' && c <= '\u200d')
+ || (c >= '\u2070' && c <= '\u218f')
+ || (c >= '\u2c00' && c <= '\u2fef')
+ || (c >= '\u3001' && c <= '\ud7ff')
+ || (c >= '\uf900' && c <= '\ufdcf')
+ || (c >= '\ufdf0' && c <= '\ufffd')) {
+ w.append(c);
+ } else {
+ appendPaddedHexChar(w, c);
+ }
+ }
+ return w;
+ }
+
+ private static final boolean needsElementNameEncoding(String s) {
+ // Note that this doesn't need to be perfect, just fast.
+ for (int i = 0; i < s.length(); i++) {
+ char c = s.charAt(i);
+ if (! (c >= '0' && c <= '9' || c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z'))
+ return true;
+ if (i == 0 && (c >= '0' && c <= '9'))
+ return true;
+ }
+ return false;
+ }
+
+ //--------------------------------------------------------------------------------
+ // XML element text
+ //--------------------------------------------------------------------------------
+
+ /**
+ * Escapes invalid XML text characters to <code>_x####_</code> sequences.
*
* @param o The object being encoded.
* @return The encoded string.
*/
- public static final String encodeInvalidCharsForText(Object o) {
+ public static final String escapeText(Object o) {
if (o == null)
return "_x0000_";
@@ -66,10 +161,12 @@ public final class XmlUtils {
}
/**
+ * Encodes the specified element text and sends the results to the specified writer.
+ * <p>
* Encodes any invalid XML text characters to <code>_x####_</code> sequences and sends the response
* to the specified writer.
- * Encodes <js>'&'</js>, <js>'<'</js>, and <js>'>'</js> as XML entities.<br>
- * Encodes invalid XML text characters to <code>_x####_</code> sequences.
+ * <br>Encodes <js>'&'</js>, <js>'<'</js>, and <js>'>'</js> as XML entities.<br>
+ * <br>Encodes invalid XML text characters to <code>_x####_</code> sequences.
*
* @param w The writer to send the output to.
* @param o The object being encoded.
@@ -97,18 +194,12 @@ public final class XmlUtils {
char c = s.charAt(i);
if ((i == 0 || i == len-1) && Character.isWhitespace(c) && ! preserveWhitespace)
appendPaddedHexChar(w, c);
- else if (c == '&')
- w.append("&");
- else if (c == '<')
- w.append("<");
- else if (c == '>')
- w.append(">");
+ else if (REPLACE_TEXT.contains(c))
+ w.append(REPLACE_TEXT.get(c));
else if (c == '_' && isEscapeSequence(s,i))
appendPaddedHexChar(w, c);
else if (isValidXmlCharacter(c))
w.append(c);
- else if (c == 0x09 || c == 0x0A || c == 0x0D)
- w.append("�").append(Integer.toHexString(c)).append(";");
else
appendPaddedHexChar(w, c);
}
@@ -119,7 +210,6 @@ public final class XmlUtils {
return w;
}
-
private static final boolean needsTextEncoding(String s) {
// See if we need to convert the string.
// Conversion is somewhat expensive, so make sure we need to do so before hand.
@@ -128,116 +218,115 @@ public final class XmlUtils {
char c = s.charAt(i);
if ((i == 0 || i == len-1) && Character.isWhitespace(c))
return true;
- if (c == '&' || c == '<' || c == '>' || c == '\n' || ! isValidXmlCharacter(c) || (c == '_' && isEscapeSequence(s,i)))
+ if (REPLACE_TEXT.contains(c) || ! isValidXmlCharacter(c) || (c == '_' && isEscapeSequence(s,i)))
return true;
}
return false;
}
+ private static AsciiMap REPLACE_TEXT = new AsciiMap()
+ .append('&', "&")
+ .append('<', "<")
+ .append('>', ">")
+ .append((char)0x09, "	")
+ .append((char)0x0A, "
")
+ .append((char)0x0D, "
");
+
//--------------------------------------------------------------------------------
- // Decode XML text
+ // XML attribute names
//--------------------------------------------------------------------------------
/**
- * Translates any _x####_ sequences (introduced by the various encode methods) back into their original characters.
+ * Serializes and encodes the specified object as valid XML attribute name.
*
- * @param s The string being decoded.
- * @param sb The string builder to use as a scratch pad.
- * @return The decoded string.
+ * @param w The writer to send the output to.
+ * @param o The object being serialized.
+ * @return This object (for method chaining).
+ * @throws IOException If a problem occurred.
*/
- public static final String decode(String s, StringBuilder sb) {
- if (s == null) return null;
- if (s.length() == 0)
- return s;
- if (s.indexOf('_') == -1)
- return s;
-
- if (sb == null)
- sb = new StringBuilder(s.length());
- for (int i = 0; i < s.length(); i++) {
- char c = s.charAt(i);
- if (c == '_' && isEscapeSequence(s,i)) {
+ public static final Writer encodeAttrName(Writer w, Object o) throws IOException {
- int x = Integer.parseInt(s.substring(i+2, i+6), 16);
+ if (o == null)
+ return w.append("_x0000_");
- // If we find _x0000_, then that means a null.
- // If we find _xE000_, then that means an empty string.
- if (x == 0)
- return null;
- else if (x != 0xE000)
- sb.append((char)x);
+ String s = o.toString();
- i+=6;
- } else {
- sb.append(c);
+ if (needsAttrNameEncoding(s)) {
+ for (int i = 0; i < s.length(); i++) {
+ char c = s.charAt(i);
+ if (i == 0) {
+ if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == ':')
+ w.append(c);
+ else if (c == '_' && ! isEscapeSequence(s,i))
+ w.append(c);
+ else
+ appendPaddedHexChar(w, c);
+ } else {
+ if ((c >= '0' && c <= '9' || c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == ':'))
+ w.append(c);
+ else if (c == '_' && ! isEscapeSequence(s,i))
+ w.append(c);
+ else
+ appendPaddedHexChar(w, c);
+ }
}
+ } else {
+ w.append(s);
}
- return sb.toString();
- }
-
- /**
- * Given a list of Strings and other Objects, combines Strings that are next to each other in the list.
- *
- * @param l The list of text nodes to collapse.
- * @return The same list.
- */
- public static LinkedList<Object> collapseTextNodes(LinkedList<Object> l) {
+ return w;
+ }
- String prev = null;
- for (ListIterator<Object> i = l.listIterator(); i.hasNext();) {
- Object o = i.next();
- if (o instanceof String) {
- if (prev == null)
- prev = o.toString();
- else {
- prev += o;
- i.remove();
- i.previous();
- i.remove();
- i.add(prev);
- }
- } else {
- prev = null;
- }
+ private static final boolean needsAttrNameEncoding(String s) {
+ // Note that this doesn't need to be perfect, just fast.
+ for (int i = 0; i < s.length(); i++) {
+ char c = s.charAt(i);
+ if (! (c >= '0' && c <= '9' || c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z'))
+ return true;
+ if (i == 0 && ! (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z'))
+ return true;
}
- return l;
+ return false;
}
-
//--------------------------------------------------------------------------------
- // Encode XML attributes
+ // XML attribute values
//--------------------------------------------------------------------------------
/**
- * Serializes and encodes the specified object as valid XML attribute name.
+ * Encodes the specified attribute value and sends the results to the specified writer.
+ * <p>
+ * Encodes any invalid XML text characters to <code>_x####_</code> sequences and sends the response
+ * to the specified writer.
+ * <br>Encodes <js>'&'</js>, <js>'<'</js>, <js>'>'</js>, <js>'"'</js>, and <js>'\''</js> as XML entities.<br>
+ * <br?Encodes invalid XML text characters to <code>_x####_</code> sequences.
*
* @param w The writer to send the output to.
- * @param o The object being serialized.
- * @return This object (for method chaining).
- * @throws IOException If a problem occurred.
+ * @param o The object being encoded.
+ * @param trim Trim the text before serializing it.
+ * If <jk>true</jk>, leading and trailing whitespace characters will be encoded.
+ * @return The same writer passed in.
+ * @throws IOException Thrown from the writer.
*/
- public static final Writer encodeAttr(Writer w, Object o) throws IOException {
-
+ public static final Writer encodeAttrValue(Writer w, Object o, boolean trim) throws IOException {
if (o == null)
return w.append("_x0000_");
String s = o.toString();
+ if (s.isEmpty())
+ return w;
+ if (trim)
+ s = s.trim();
- if (needsAttributeEncoding(s)) {
- for (int i = 0; i < s.length(); i++) {
+ if (needsAttrValueEncoding(s)) {
+ final int len = s.length();
+ for (int i = 0; i < len; i++) {
char c = s.charAt(i);
- if (c == '&')
- w.append("&");
- else if (c == '<')
- w.append("<");
- else if (c == '>')
- w.append(">");
- else if (c == '\'')
- w.append("'");
- else if (c == '"')
- w.append(""");
+ if ((i == 0 || i == len-1) && Character.isWhitespace(c))
+ appendPaddedHexChar(w, c);
+ else if (REPLACE_ATTR_VAL.contains(c))
+ w.append(REPLACE_ATTR_VAL.get(c));
else if (c == '_' && isEscapeSequence(s,i))
appendPaddedHexChar(w, c);
else if (isValidXmlCharacter(c))
@@ -252,114 +341,101 @@ public final class XmlUtils {
return w;
}
-
- private static boolean needsAttributeEncoding(String s) {
+ private static final boolean needsAttrValueEncoding(String s) {
// See if we need to convert the string.
// Conversion is somewhat expensive, so make sure we need to do so before hand.
- for (int i = 0; i < s.length(); i++) {
+ final int len = s.length();
+ for (int i = 0; i < len; i++) {
char c = s.charAt(i);
- if (c == '&' || c == '<' || c == '>' || c == '\n' || c == '\'' || c == '"' || ! isValidXmlCharacter(c))
+ if ((i == 0 || i == len-1) && Character.isWhitespace(c))
+ return true;
+ if (REPLACE_ATTR_VAL.contains(c) || ! isValidXmlCharacter(c) || (c == '_' && isEscapeSequence(s,i)))
return true;
}
return false;
}
+ private static AsciiMap REPLACE_ATTR_VAL = new AsciiMap()
+ .append('&', "&")
+ .append('<', "<")
+ .append('>', ">")
+ .append('"', """)
+ .append('\'', "'")
+ .append((char)0x09, "	")
+ .append((char)0x0A, "
")
+ .append((char)0x0D, "
");
+
//--------------------------------------------------------------------------------
- // Encode XML element names
+ // Decode XML text
//--------------------------------------------------------------------------------
/**
- * Encodes any invalid XML element name characters to <code>_x####_</code> sequences.
+ * Translates any _x####_ sequences (introduced by the various encode methods) back into their original characters.
*
- * @param w The writer to send the output to.
- * @param o The object being encoded.
- * @return The same writer passed in.
- * @throws IOException Throw by the writer.
+ * @param s The string being decoded.
+ * @param sb The string builder to use as a scratch pad.
+ * @return The decoded string.
*/
- public static final Writer encodeElementName(Writer w, Object o) throws IOException {
+ public static final String decode(String s, StringBuilder sb) {
+ if (s == null) return null;
+ if (s.length() == 0)
+ return s;
+ if (s.indexOf('_') == -1)
+ return s;
- if (o == null)
- return w.append("_x0000_");
+ if (sb == null)
+ sb = new StringBuilder(s.length());
+ for (int i = 0; i < s.length(); i++) {
+ char c = s.charAt(i);
+ if (c == '_' && isEscapeSequence(s,i)) {
- String s = o.toString();
+ int x = Integer.parseInt(s.substring(i+2, i+6), 16);
- if (needsElementNameEncoding(s))
- return encodeElementNameInner(w, s);
+ // If we find _x0000_, then that means a null.
+ // If we find _xE000_, then that means an empty string.
+ if (x == 0)
+ return null;
+ else if (x != 0xE000)
+ sb.append((char)x);
- w.append(s);
- return w;
+ i+=6;
+ } else {
+ sb.append(c);
+ }
+ }
+ return sb.toString();
}
+
/**
- * Encodes any invalid XML element name characters to <code>_x####_</code> sequences.
+ * Given a list of Strings and other Objects, combines Strings that are next to each other in the list.
*
- * @param o The object being encoded.
- * @return The encoded element name string.
+ * @param l The list of text nodes to collapse.
+ * @return The same list.
*/
- public static final String encodeElementName(Object o) {
- if (o == null)
- return "_x0000_";
-
- String s = o.toString();
- if (s.isEmpty())
- return "_xE000_";
- try {
- if (needsElementNameEncoding(s))
- return encodeElementNameInner(new StringBuilderWriter(s.length() * 2), s).toString();
- } catch (IOException e) {
- throw new RuntimeException(e); // Never happens
- }
-
- return s;
- }
+ public static LinkedList<Object> collapseTextNodes(LinkedList<Object> l) {
- private static final Writer encodeElementNameInner(Writer w, String s) throws IOException {
- for (int i = 0; i < s.length(); i++) {
- char c = s.charAt(i);
- if ((c >= 'A' && c <= 'Z')
- || (c == '_' && ! isEscapeSequence(s,i))
- || (c >= 'a' && c <= 'z')
- || (i != 0 && (
- c == '-'
- || c == '.'
- || (c >= '0' && c <= '9')
- || c == '\u00b7'
- || (c >= '\u0300' && c <= '\u036f')
- || (c >= '\u203f' && c <= '\u2040')
- ))
- || (c >= '\u00c0' && c <= '\u00d6')
- || (c >= '\u00d8' && c <= '\u00f6')
- || (c >= '\u00f8' && c <= '\u02ff')
- || (c >= '\u0370' && c <= '\u037d')
- || (c >= '\u037f' && c <= '\u1fff')
- || (c >= '\u200c' && c <= '\u200d')
- || (c >= '\u2070' && c <= '\u218f')
- || (c >= '\u2c00' && c <= '\u2fef')
- || (c >= '\u3001' && c <= '\ud7ff')
- || (c >= '\uf900' && c <= '\ufdcf')
- || (c >= '\ufdf0' && c <= '\ufffd')) {
- w.append(c);
- } else {
- appendPaddedHexChar(w, c);
+ String prev = null;
+ for (ListIterator<Object> i = l.listIterator(); i.hasNext();) {
+ Object o = i.next();
+ if (o instanceof String) {
+ if (prev == null)
+ prev = o.toString();
+ else {
+ prev += o;
+ i.remove();
+ i.previous();
+ i.remove();
+ i.add(prev);
+ }
+ } else {
+ prev = null;
}
}
- return w;
- }
-
- private static final boolean needsElementNameEncoding(String s) {
- // Note that this doesn't need to be perfect, just fast.
- for (int i = 0; i < s.length(); i++) {
- char c = s.charAt(i);
- if (! (c >= '0' && c <= '9' || c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z'))
- return true;
- if (i == 0 && (c >= '0' && c <= '9'))
- return true;
- }
- return false;
+ return l;
}
-
//--------------------------------------------------------------------------------
// Other methods
//--------------------------------------------------------------------------------
@@ -501,4 +577,32 @@ public final class XmlUtils {
return "ENTITY_DECLARATION";
return "UNKNOWN";
}
+
+ /**
+ * Shortcut for calling <code>URLEncoder.<jsm>encode</jsm>(o.toString(), <js>"UTF-8"</js>)</code>.
+ *
+ * @param o The object to encode.
+ * @return The URL encoded string, or <jk>null</jk> if the object was null.
+ */
+ public static String urlEncode(Object o) {
+ try {
+ if (o != null)
+ return URLEncoder.encode(o.toString(), "UTF-8");
+ } catch (UnsupportedEncodingException e) {}
+ return null;
+ }
+
+ /**
+ * Shortcut for calling <code>URLEncoder.<jsm>decode</jsm>(o.toString(), <js>"UTF-8"</js>)</code>.
+ *
+ * @param s The string to decode.
+ * @return The decoded string, or <jk>null</jk> if the string was null.
+ */
+ public static String urlDecode(String s) {
+ try {
+ if (s != null)
+ return URLDecoder.decode(s, "UTF-8");
+ } catch (UnsupportedEncodingException e) {}
+ return null;
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c4952d2c/juneau-core/src/main/java/org/apache/juneau/xml/XmlWriter.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/XmlWriter.java b/juneau-core/src/main/java/org/apache/juneau/xml/XmlWriter.java
index 6b8caeb..d1ed78e 100644
--- a/juneau-core/src/main/java/org/apache/juneau/xml/XmlWriter.java
+++ b/juneau-core/src/main/java/org/apache/juneau/xml/XmlWriter.java
@@ -38,15 +38,12 @@ public class XmlWriter extends SerializerWriter {
* @param useWhitespace If <jk>true</jk> XML elements will be indented.
* @param trimStrings If <jk>true</jk>, strings should be trimmed before they're serialized.
* @param quoteChar The quote character to use for attributes. Should be <js>'\''</js> or <js>'"'</js>.
- * @param relativeUriBase The base (e.g. <js>https://localhost:9443/contextPath"</js>) for relative URIs (e.g. <js>"my/path"</js>).
- * @param absolutePathUriBase The base (e.g. <js>https://localhost:9443"</js>) for relative URIs with absolute paths (e.g. <js>"/contextPath/my/path"</js>).
- * @param uriContext The URI context.
- * Identifies the current request URI used for resolution of URIs to absolute or root-relative form.
+ * @param uriResolver The URI resolver for resolving URIs to absolute or root-relative form.
* @param enableNs Flag to indicate if XML namespaces are enabled.
* @param defaultNamespace The default namespace if XML namespaces are enabled.
*/
- public XmlWriter(Writer out, boolean useWhitespace, boolean trimStrings, char quoteChar, String relativeUriBase, String absolutePathUriBase, UriContext uriContext, boolean enableNs, Namespace defaultNamespace) {
- super(out, useWhitespace, trimStrings, quoteChar, relativeUriBase, absolutePathUriBase, uriContext);
+ public XmlWriter(Writer out, boolean useWhitespace, boolean trimStrings, char quoteChar, UriResolver uriResolver, boolean enableNs, Namespace defaultNamespace) {
+ super(out, useWhitespace, trimStrings, quoteChar, uriResolver);
this.enableNs = enableNs;
this.defaultNsPrefix = defaultNamespace == null ? null : defaultNamespace.name;
}
@@ -404,17 +401,12 @@ public class XmlWriter extends SerializerWriter {
* @param ns The namespace. Can be <jk>null</jk>.
* @param name The attribute name.
* @param value The attribute value.
- * @param needsEncoding If <jk>true</jk>, attribute name will be encoded.
+ * @param valNeedsEncoding If <jk>true</jk>, attribute name will be encoded.
* @return This object (for method chaining).
* @throws IOException If a problem occurred.
*/
- public XmlWriter attr(String ns, String name, Object value, boolean needsEncoding) throws IOException {
- oAttr(ns, name).q();
- if (needsEncoding)
- encodeAttr(value);
- else
- append(value);
- return q();
+ public XmlWriter attr(String ns, String name, Object value, boolean valNeedsEncoding) throws IOException {
+ return oAttr(ns, name).q().attrValue(value, valNeedsEncoding).q();
}
/**
@@ -422,12 +414,12 @@ public class XmlWriter extends SerializerWriter {
*
* @param name The attribute name.
* @param value The attribute value.
- * @param needsEncoding If <jk>true</jk>, attribute name will be encoded.
+ * @param valNeedsEncoding If <jk>true</jk>, attribute name will be encoded.
* @return This object (for method chaining).
* @throws IOException If a problem occurred.
*/
- public XmlWriter attr(String name, Object value, boolean needsEncoding) throws IOException {
- return attr(null, name, value, needsEncoding);
+ public XmlWriter attr(String name, Object value, boolean valNeedsEncoding) throws IOException {
+ return attr(null, name, value, valNeedsEncoding);
}
/**
@@ -440,11 +432,11 @@ public class XmlWriter extends SerializerWriter {
* @throws IOException If a problem occurred.
*/
public XmlWriter attr(String ns, String name, Object value) throws IOException {
- return oAttr(ns, name).q().append(value).q();
+ return oAttr(ns, name).q().attrValue(value, false).q();
}
/**
- * Same as {@link #attr(String, Object, boolean)}, except pass in a {@link Namespace} object for the namespace.
+ * Same as {@link #attr(String, String, Object)}, except pass in a {@link Namespace} object for the namespace.
*
* @param ns The namespace. Can be <jk>null</jk>.
* @param name The attribute name.
@@ -453,7 +445,7 @@ public class XmlWriter extends SerializerWriter {
* @throws IOException If a problem occurred.
*/
public XmlWriter attr(Namespace ns, String name, Object value) throws IOException {
- return oAttr(ns == null ? null : ns.name, name).q().append(value).q();
+ return oAttr(ns == null ? null : ns.name, name).q().attrValue(value, false).q();
}
/**
@@ -507,8 +499,7 @@ public class XmlWriter extends SerializerWriter {
* @throws IOException If a problem occurred.
*/
public XmlWriter attrUri(Namespace ns, String name, Object value) throws IOException {
- oAttr(ns, name).q().appendUri(value).q();
- return this;
+ return attr(ns, name, uriResolver.resolve(value));
}
/**
@@ -521,8 +512,7 @@ public class XmlWriter extends SerializerWriter {
* @throws IOException If a problem occurred.
*/
public XmlWriter attrUri(String ns, String name, Object value) throws IOException {
- oAttr(ns, name).q().appendUri(value).q();
- return this;
+ return attr(ns, name, uriResolver.resolve(value), true);
}
/**
@@ -551,14 +541,22 @@ public class XmlWriter extends SerializerWriter {
}
/**
- * Serializes and encodes the specified object as valid XML attribute name.
+ * Same as {@link #text(Object)} but treats the value as a URL to resolved then serialized.
*
* @param o The object being serialized.
* @return This object (for method chaining).
- * @throws IOException If a problem occurred.
+ * @throws IOException
*/
- public XmlWriter encodeAttr(Object o) throws IOException {
- XmlUtils.encodeAttr(out, o);
+ public XmlWriter textUri(Object o) throws IOException {
+ text(uriResolver.resolve(o), false);
+ return this;
+ }
+
+ private XmlWriter attrValue(Object o, boolean needsEncoding) throws IOException {
+ if (needsEncoding)
+ XmlUtils.encodeAttrValue(out, o, this.trimStrings);
+ else
+ append(o.toString());
return this;
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c4952d2c/juneau-core/src/main/javadoc/overview.html
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/javadoc/overview.html b/juneau-core/src/main/javadoc/overview.html
index fe24402..64714b6 100644
--- a/juneau-core/src/main/javadoc/overview.html
+++ b/juneau-core/src/main/javadoc/overview.html
@@ -2187,14 +2187,30 @@
<p class='bcode'>
<ja>@RestResource</ja>(
path=<js>"/systemProperties"</js>,
+
+ <jc>// Title and description that show up on HTML rendition page.
+ // Also used in Swagger doc.</jc>
title=<js>"System properties resource"</js>,
description=<js>"REST interface for performing CRUD operations on system properties."</js>,
- pageLinks=<js>"{up:'$R{requestParentURI}',options:'?method=OPTIONS'}"</js>,
+
+ <jc>// Links on the HTML rendition page.
+ // "request:/..." URIs are relative to the request URI.
+ // "servlet:/..." URIs are relative to the servlet URI.</jc>
+ pageLinks=<js>"{up:'request:/..',options:'servlet:/?method=OPTIONS'}"</js>,
+
+ <jc>// Properties that get applied to all serializers and parsers.</jc>
properties={
+ <jc>// Use single quotes.</jc>
<ja>@Property</ja>(name=<jsf>SERIALIZER_quoteChar</jsf>, value=<js>"'"</js>)
},
+
+ <jc>// Our stylesheet for the HTML rendition.</jc>
stylesheet=<js>"styles/devops.css"</js>,
+
+ <jc>// Support GZIP encoding on Accept-Encoding header.</jc>
encoders=GzipEncoder.<jk>class</jk>,
+
+ <jc>// Swagger info.</jc>
contact=<js>"{name:'John Smith',email:'john@smith.com'}"</js>,
license=<js>"{name:'Apache 2.0',url:'http://www.apache.org/licenses/LICENSE-2.0.html'}"</js>,
version=<js>"2.0"</js>,
@@ -3076,7 +3092,7 @@
<ja>@RestResource</ja>(
messages=<js>"nls/HelloWorldResource"</js>,
path=<js>"/helloWorld"</js>,
- pageLinks=<js>"{up:'$R{requestParentURI}',options:'?method=OPTIONS'}"</js>
+ pageLinks=<js>"{up:'request:/..',options:'servlet:/?method=OPTIONS'}"</js>
)
<jk>public class</jk> HelloWorldResource <jk>extends</jk> Resource {
<jk>private static final long</jk> <jsf>serialVersionUID</jsf> = 1L;
@@ -3151,7 +3167,7 @@
<ja>@RestResource</ja>(
path=<js>"/methodExample"</js>,
messages=<js>"nls/MethodExampleResource"</js>,
- pageLinks=<js>"{up:'$R{requestParentURI}',options:'?method=OPTIONS'}"</js>
+ pageLinks=<js>"{up:'request:/..',options:'servlet:/?method=OPTIONS'}"</js>
)
<jk>public class</jk> MethodExampleResource <jk>extends</jk> Resource {
<jk>private static final long</jk> <jsf>serialVersionUID</jsf> = 1L;
@@ -3620,7 +3636,7 @@
<ja>@RestResource</ja>(
path=<js>"/echo"</js>,
messages=<js>"nls/RequestEchoResource"</js>,
- pageLinks=<js>"{up:'$R{requestParentURI}',options:'?method=OPTIONS'}"</js>,
+ pageLinks=<js>"{up:'request:/..',options:'servlet:/?method=OPTIONS'}"</js>,
properties={
<ja>@Property</ja>(name=<jsf>SERIALIZER_maxDepth</jsf>, value=<js>"10"</js>),
<ja>@Property</ja>(name=<jsf>SERIALIZER_detectRecursions</jsf>, value=<js>"true"</js>)
@@ -4075,18 +4091,42 @@
<ja>@RestResource</ja>(
path=<js>"/addressBook"</js>,
messages=<js>"nls/AddressBookResource"</js>,
- pageLinks=<js>"{up:'$R{requestParentURI}',options:'?method=OPTIONS'}"</js>,
- properties={
- <ja>@Property</ja>(name=<jsf>REST_allowMethodParam</jsf>, value=<js>"*"</js>),
- <ja>@Property</ja>(name=<jsf>HTML_uriAnchorText</jsf>, value=<jsf>TO_STRING</jsf>),
- <ja>@Property</ja>(name=<jsf>SERIALIZER_quoteChar</jsf>, value=<js>"'"</js>),
- <ja>@Property</ja>(name=<jsf>RDF_rdfxml_tab</jsf>, value=<js>"5"</js>),
- <ja>@Property</ja>(name=<jsf>RDF_addRootProperty</jsf>, value=<js>"true"</js>),
- <jc>// Resolve all relative URIs so that they're relative to this servlet!</jc>
- <ja>@Property</ja>(name=<jsf>SERIALIZER_relativeUriBase</jsf>, value=<js>"$R{servletURI}"</js>),
- },
+
+ <jc>// Links on the HTML rendition page.
+ // "request:/..." URIs are relative to the request URI.
+ // "servlet:/..." URIs are relative to the servlet URI.
+ // "$C{...}" variables are pulled from the config file.</jc>
+ pageLinks=<js>"{up:'request:/..', options:'servlet:/?method=OPTIONS', source:'$C{Source/gitHub}/org/apache/juneau/examples/rest/addressbook/AddressBookResource.java'}"</js>,
+
+ <jc>// Properties that get applied to all serializers and parsers.</jc>
+ properties={
+
+ <jc>// Allow INIT as a method parameter.</jc>
+ <ja>@Property</ja>(name=<jsf>REST_allowMethodParam</jsf>, value=<js>"*"</js>),
+
+ <jc>// Use single quotes.</jc>
+ <ja>@Property</ja>(name=<jsf>SERIALIZER_quoteChar</jsf>, value=<js>"'"</js>),
+
+ <jc>// Make RDF/XML readable.</jc>
+ <ja>@Property</ja>(name=<jsf>RDF_rdfxml_tab</jsf>, value=<js>"5"</js>),
+
+ <jc>// Make RDF parsable by adding a root node.</jc>
+ <ja>@Property</ja>(name=<jsf>RDF_addRootProperty</jsf>, value=<js>"true"</js>),
+
+ <jc>// Make URIs absolute so that we can easily reference them on the client side.</jc>
+ <ja>@Property</ja>(name=<jsf>SERIALIZER_uriResolution</jsf>, value=<js>"ABSOLUTE"</js>)
+
+ <jc>// Make the anchor text on URLs be just the path relative to the servlet.</jc>
+ <ja>@Property</ja>(name=<jsf>HTML_uriAnchorText</jsf>, value=<js>"SERVLET_RELATIVE"</js>)
+ },
+
+ <jc>// Our stylesheet for the HTML rendition.</jc>
stylesheet=<js>"styles/devops.css"</js>,
+
+ <jc>// Support GZIP encoding on Accept-Encoding header.</jc>
encoders=GzipEncoder.<jk>class</jk>,
+
+ <jc>// Swagger info.</jc>
contact=<js>"{name:'John Smith',email:'john@smith.com'}"</js>,
license=<js>"{name:'Apache 2.0',url:'http://www.apache.org/licenses/LICENSE-2.0.html'}"</js>,
version=<js>"2.0"</js>,
@@ -4105,7 +4145,7 @@
<jk>try</jk> {
<jc>// Create the address book</jc>
- <jf>addressBook</jf> = <jk>new</jk> AddressBook(java.net.URI.create(<js>""</js>));
+ <jf>addressBook</jf> = <jk>new</jk> AddressBook(java.net.URI.create(<js>"servlet:/"</js>));
<jc>// Add some people to our address book by default</jc>
<jf>addressBook</jf>.createPerson(
@@ -4724,7 +4764,7 @@
messages=<js>"nls/SampleRemoteableServlet"</js>,
title=<js>"Remoteable Service Proxy API"</js>,
description=<js>"Sample class showing how to use remoteable proxies. The list below are exposed services that can be retrieved using RestClient.getProxyInterface(Class)."</js>,
- pageLinks=<js>"{up:'$R{requestParentURI}',options:'?method=OPTIONS'}"</js>,
+ pageLinks=<js>"{up:'request:/..',options:'servlet:/?method=OPTIONS'}"</js>,
properties={
<jc>// Allow us to use method=POST from a browser.</jc>
<ja>@Property</ja>(name=<jsf>REST_allowMethodParam</jsf>, value=<js>"*"</js>)
@@ -4827,7 +4867,7 @@
<ja>@RestResource</ja>(
path=<js>"/tempDir"</js>,
messages=<js>"nls/TempDirResource"</js>,
- pageLinks=<js>"{up:'$R{requestParentURI}',options:'?method=OPTIONS',upload:'upload'}"</js>,
+ pageLinks=<js>"{up:'request:/..', options:'servlet:/?method=OPTIONS', upload:'servlet:/upload'}"</js>,
properties={
<ja>@Property</ja>(name=<js>"DirectoryResource.rootDir"</js>, value=<js>"$S{java.io.tmpdir}"</js>),
<ja>@Property</ja>(name=<js>"DirectoryResource.allowViews"</js>, value=<js>"true"</js>),
@@ -4945,7 +4985,7 @@
<ja>@RestResource</ja>(
path=<js>"/atom"</js>,
messages=<js>"nls/AtomFeedResource"</js>,
- pageLinks=<js>"{up:'$R{requestParentURI}',options:'?method=OPTIONS'}"</js>,
+ pageLinks=<js>"{up:'request:/..',options:'servlet:/?method=OPTIONS'}"</js>,
properties={
<ja>@Property</ja>(name=<jsf>SERIALIZER_quoteChar</jsf>, value=<js>"'"</js>),
<ja>@Property</ja>(name=<jsf>RDF_rdfxml_tab</jsf>, value=<js>"5"</js>),
@@ -5053,7 +5093,7 @@
<ja>@RestResource</ja>(
path=<js>"/docker"</js>,
title=<js>"Sample Docker resource"</js>,
- pageLinks=<js>"{up:'$R{requestParentURI}',options:'?method=OPTIONS'}"</js>
+ pageLinks=<js>"{up:'request:/..',options:'servlet:/?method=OPTIONS'}"</js>
)
<jk>public class</jk> DockerRegistryResource <jk>extends</jk> Resource {
<jk>private static final long</jk> <jsf>serialVersionUID</jsf> = 1L;
@@ -5134,7 +5174,7 @@
messages=<js>"nls/TumblrParserResource"</js>,
title=<js>"Tumblr parser service"</js>,
description=<js>"Specify a URL to a Tumblr blog and parse the results."</js>,
- pageLinks=<js>"{up:'$R{requestParentURI}',options:'?method=OPTIONS'}"</js>
+ pageLinks=<js>"{up:'request:/..',options:'servlet:/?method=OPTIONS'}"</js>
)
<jk>public class</jk> TumblrParserResource <jk>extends</jk> Resource {
<jk>private static final long</jk> <jsf>serialVersionUID</jsf> = 1L;
@@ -5355,7 +5395,7 @@
path=<js>"/jsonSchema"</js>,
messages=<js>"nls/JsonSchemaResource"</js>,
title=<js>"Sample JSON-Schema document"</js>,
- pageLinks=<js>"{up:'$R{requestParentURI}',options:'?method=OPTIONS'}"</js>
+ pageLinks=<js>"{up:'request:/..',options:'servlet:/?method=OPTIONS'}"</js>
)
<jk>public class</jk> JsonSchemaResource <jk>extends</jk> ResourceJena {
<jk>private static final long</jk> <jsf>serialVersionUID</jsf> = 1L;
@@ -5436,7 +5476,7 @@
messages=<js>"nls/SqlQueryResource"</js>,
title=<js>"SQL query service"</js>,
description=<js>"Executes queries against the local derby '$C{SqlQueryResource/connectionUrl}' database"</js>,
- pageLinks=<js>"{up:'$R{requestParentURI}',options:'?method=OPTIONS'}"</js>
+ pageLinks=<js>"{up:'request:/..',options:'servlet:/?method=OPTIONS'}"</js>
)
<jk>public class</jk> SqlQueryResource <jk>extends</jk> Resource {
<jk>private static final long</jk> <jsf>serialVersionUID</jsf> = 1L;
@@ -5623,7 +5663,7 @@
path=<js>"/config"</js>,
title=<js>"Configuration"</js>,
description=<js>"Contents of configuration file."</js>,
- pageLinks=<js>"{up:'$R{requestParentURI}',options:'?method=OPTIONS',edit:'edit'}"</js>
+ pageLinks=<js>"{up:'request:/..', options:'servlet:/?method=OPTIONS', edit:'servlet:/edit'}"</js>
)
<jk>public class</jk> ConfigResource <jk>extends</jk> Resource {
<jk>private static final long</jk> <jsf>serialVersionUID</jsf> = 1L;
@@ -6150,6 +6190,39 @@
<li>{@link org.apache.juneau.remoteable.Header#serializer}
<li>{@link org.apache.juneau.remoteable.HeaderIfNE#serializer}
</ul>
+ <li>Across-the-board improvements to the URI-resolution support (i.e. how URIs get serialized).
+ <ul>
+ <li>New support for resolving URIs with the following newly-recognized protocols:
+ <ul>
+ <li><js>"context:/..."</js> - Relative to context-root of the application.
+ <li><js>"servlet:/..."</js> - Relative to the servlet URI.
+ <li><js>"request:/..."</js> - Relative to the request URI.
+ </ul>
+ For example, currently we define HTML page links using variables and servlet-relative URIs...
+ <p class='bcode'>
+ pageLinks=<js>"{up:'$R{requestParentURI}', options:'?method=OPTIONS', upload:'upload'}"</js>
+ </p>
+ With these new protocols, we can define them like so:
+ <p class='bcode'>
+ pageLinks=<js>"{top:'context:/', up:'request:/..' ,options:'servlet:/?method=OPTIONS', upload:'servlet:/upload'}"</js>
+ </p>
+ The old method of using variables and servlet-relative URIs will still be supported, but using
+ these new protocols should (hopefully) be easier to understand.
+ <br>
+ These protocols work on all serialized URL and URI objects, as well as classes and properties
+ annotated with {@link org.apache.juneau.annotation.URI @URI}.
+ <li>New classes:
+ <ul>
+ <li>{@link org.apache.juneau.UriContext}
+ <li>{@link org.apache.juneau.UriRelativity}
+ <li>{@link org.apache.juneau.UriResolution}
+ <li>{@link org.apache.juneau.UriResolver}
+ </ul>
+ <li>New configuration properties:
+ <li>{@link org.apache.juneau.serializer.SerializerContext#SERIALIZER_uriContext}
+ <li>{@link org.apache.juneau.serializer.SerializerContext#SERIALIZER_uriRelativity}
+ <li>{@link org.apache.juneau.serializer.SerializerContext#SERIALIZER_uriResolution}
+ </ul>
</ul>
<h6 class='topic'>org.apache.juneau.rest</h6>
@@ -7839,8 +7912,8 @@
<ul class='spaced-list'>
<li>New properties in {@link org.apache.juneau.serializer.SerializerContext}:
<ol>
- <li>{@link org.apache.juneau.serializer.SerializerContext#SERIALIZER_relativeUriBase}
- <li>{@link org.apache.juneau.serializer.SerializerContext#SERIALIZER_absolutePathUriBase}
+ <li><code><del>SerializerContext.SERIALIZER_relativeUriBase</del></code>
+ <li><code><del>SerializerContext.SERIALIZER_absolutePathUriBase</del></code>
</ol>
These replace the <code>SERIALIZER_uriAuthority</code> and <code>SERIALIZER_uriContext</code> properties.
</li>
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c4952d2c/juneau-examples-rest/src/main/java/org/apache/juneau/examples/addressbook/Person.java
----------------------------------------------------------------------
diff --git a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/addressbook/Person.java b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/addressbook/Person.java
index 1d8c73c..e95806a 100755
--- a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/addressbook/Person.java
+++ b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/addressbook/Person.java
@@ -32,7 +32,7 @@ public class Person {
// Bean properties
@Rdf(beanUri=true) public URI uri;
- public URI addressBookUri;
+ private URI addressBookUri;
public int id;
public String name;
@BeanProperty(swap=CalendarSwap.DateMedium.class) public Calendar birthDate;
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c4952d2c/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/AtomFeedResource.java
----------------------------------------------------------------------
diff --git a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/AtomFeedResource.java b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/AtomFeedResource.java
index ff453ae..49fc847 100644
--- a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/AtomFeedResource.java
+++ b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/AtomFeedResource.java
@@ -31,7 +31,7 @@ import org.apache.juneau.rest.annotation.*;
path="/atom",
title="Sample ATOM feed resource",
description="Sample resource that shows how to render ATOM feeds",
- pageLinks="{up:'$R{requestParentURI}',options:'?method=OPTIONS',source:'$C{Source/gitHub}/org/apache/juneau/examples/rest/AtomFeedResource.java'}",
+ pageLinks="{up:'request:/..',options:'servlet:/?method=OPTIONS',source:'$C{Source/gitHub}/org/apache/juneau/examples/rest/AtomFeedResource.java'}",
properties={
@Property(name=SERIALIZER_quoteChar, value="'"),
@Property(name=RDF_rdfxml_tab, value="5"),
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c4952d2c/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DirectoryResource.java
----------------------------------------------------------------------
diff --git a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DirectoryResource.java b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DirectoryResource.java
index 9c3919f..753fcf7 100644
--- a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DirectoryResource.java
+++ b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DirectoryResource.java
@@ -35,7 +35,7 @@ import org.apache.juneau.utils.*;
*/
@RestResource(
messages="nls/DirectoryResource",
- pageLinks="{up:'$R{requestParentURI}',options:'?method=OPTIONS',source:'$C{Source/gitHub}/org/apache/juneau/examples/rest/DirectoryResource.java'}",
+ pageLinks="{up:'request:/..',options:'servlet:/?method=OPTIONS',source:'$C{Source/gitHub}/org/apache/juneau/examples/rest/DirectoryResource.java'}",
properties={
@Property(name=HTML_uriAnchorText, value=PROPERTY_NAME),
@Property(name=REST_allowMethodParam, value="*"),
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c4952d2c/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DockerRegistryResource.java
----------------------------------------------------------------------
diff --git a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DockerRegistryResource.java b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DockerRegistryResource.java
index 61192f0..1b021f0 100644
--- a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DockerRegistryResource.java
+++ b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DockerRegistryResource.java
@@ -27,7 +27,7 @@ import org.apache.juneau.rest.labels.*;
@RestResource(
path="/docker",
title="Sample Docker resource",
- pageLinks="{up:'$R{requestParentURI}',options:'?method=OPTIONS',source:'$C{Source/gitHub}/org/apache/juneau/examples/rest/DockerRegistryResource.java'}"
+ pageLinks="{up:'request:/..',options:'servlet:/?method=OPTIONS',source:'$C{Source/gitHub}/org/apache/juneau/examples/rest/DockerRegistryResource.java'}"
)
public class DockerRegistryResource extends Resource {
private static final long serialVersionUID = 1L;