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:'[email protected]'}"</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:'[email protected]'}"</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;
