HtmlRender support. Project: http://git-wip-us.apache.org/repos/asf/incubator-juneau/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-juneau/commit/e1a50566 Tree: http://git-wip-us.apache.org/repos/asf/incubator-juneau/tree/e1a50566 Diff: http://git-wip-us.apache.org/repos/asf/incubator-juneau/diff/e1a50566
Branch: refs/heads/master Commit: e1a505668d7aa4d2aa816f697a8203dfbc4bd8c8 Parents: 9b04bb9 Author: JamesBognar <[email protected]> Authored: Sun May 21 01:17:14 2017 -0400 Committer: JamesBognar <[email protected]> Committed: Sun May 21 01:17:14 2017 -0400 ---------------------------------------------------------------------- .../org/apache/juneau/jena/RdfSerializer.java | 4 +- .../juneau/jena/RdfSerializerSession.java | 14 +- .../apache/juneau/utils/StringUtilsTest.java | 28 + .../utils/UriContextResolutionComboTest.java | 630 +++++++++++++++++++ .../juneau/utils/UriContextUriComboTest.java | 234 +++++++ .../org/apache/juneau/xml/XmlContentTest.java | 8 +- .../main/java/org/apache/juneau/BeanMap.java | 11 + .../main/java/org/apache/juneau/UriContext.java | 421 +++++++++++++ .../java/org/apache/juneau/UriRelativity.java | 29 + .../java/org/apache/juneau/UriResolution.java | 34 + .../apache/juneau/csv/CsvSerializerSession.java | 14 +- .../juneau/html/HtmlBeanPropertyMeta.java | 78 ++- .../apache/juneau/html/HtmlDocSerializer.java | 4 +- .../juneau/html/HtmlDocSerializerSession.java | 16 +- .../java/org/apache/juneau/html/HtmlRender.java | 149 +++++ .../juneau/html/HtmlSchemaDocSerializer.java | 4 +- .../org/apache/juneau/html/HtmlSerializer.java | 38 +- .../juneau/html/HtmlSerializerSession.java | 16 +- .../java/org/apache/juneau/html/HtmlWriter.java | 7 +- .../apache/juneau/html/SimpleHtmlWriter.java | 2 +- .../org/apache/juneau/html/annotation/Html.java | 35 ++ .../juneau/html/doc-files/HtmlRender_1.png | Bin 0 -> 60161 bytes .../org/apache/juneau/internal/StringUtils.java | 102 +++ .../juneau/json/JsonSchemaSerializer.java | 4 +- .../org/apache/juneau/json/JsonSerializer.java | 4 +- .../juneau/json/JsonSerializerSession.java | 16 +- .../java/org/apache/juneau/json/JsonWriter.java | 7 +- .../juneau/msgpack/MsgPackSerializer.java | 4 +- .../msgpack/MsgPackSerializerSession.java | 14 +- .../apache/juneau/serializer/Serializer.java | 12 +- .../juneau/serializer/SerializerSession.java | 45 +- .../juneau/serializer/SerializerWriter.java | 12 +- .../juneau/serializer/WriterSerializer.java | 2 +- .../org/apache/juneau/uon/UonSerializer.java | 4 +- .../apache/juneau/uon/UonSerializerSession.java | 16 +- .../java/org/apache/juneau/uon/UonWriter.java | 7 +- .../urlencoding/UrlEncodingSerializer.java | 6 +- .../UrlEncodingSerializerSession.java | 14 +- .../apache/juneau/xml/XmlSchemaSerializer.java | 8 +- .../org/apache/juneau/xml/XmlSerializer.java | 4 +- .../apache/juneau/xml/XmlSerializerSession.java | 16 +- .../java/org/apache/juneau/xml/XmlWriter.java | 7 +- .../src/main/javadoc/doc-files/HtmlRender_1.png | Bin 0 -> 60161 bytes juneau-core/src/main/javadoc/overview.html | 3 + .../juneau/examples/rest/FileSpaceResource.java | 148 +++++ .../juneau/examples/rest/RootResources.java | 1 + .../apache/juneau/examples/rest/htdocs/ok.png | Bin 0 -> 455 bytes .../juneau/examples/rest/htdocs/severe.png | Bin 0 -> 335 bytes .../juneau/examples/rest/htdocs/warning.png | Bin 0 -> 444 bytes .../juneau/rest/client/RestClientBuilder.java | 2 +- .../apache/juneau/rest/jaxrs/BaseProvider.java | 4 +- .../juneau/rest/test/HtmlPropertiesTest.java | 1 - .../org/apache/juneau/rest/RestRequest.java | 17 + .../juneau/rest/response/DefaultHandler.java | 4 +- 54 files changed, 2112 insertions(+), 148 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e1a50566/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfSerializer.java ---------------------------------------------------------------------- diff --git a/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfSerializer.java b/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfSerializer.java index 37b7d2c..734f38a 100644 --- a/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfSerializer.java +++ b/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfSerializer.java @@ -450,7 +450,7 @@ public class RdfSerializer extends WriterSerializer { //-------------------------------------------------------------------------------- @Override /* Serializer */ - public RdfSerializerSession createSession(Object output, ObjectMap op, Method javaMethod, Locale locale, TimeZone timeZone, MediaType mediaType) { - return new RdfSerializerSession(ctx, op, output, javaMethod, locale, timeZone, mediaType); + public RdfSerializerSession createSession(Object output, ObjectMap op, Method javaMethod, Locale locale, TimeZone timeZone, MediaType mediaType, UriContext uriContext) { + return new RdfSerializerSession(ctx, op, output, javaMethod, locale, timeZone, mediaType, uriContext); } } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e1a50566/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfSerializerSession.java ---------------------------------------------------------------------- diff --git a/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfSerializerSession.java b/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfSerializerSession.java index 199f144..55060ef 100644 --- a/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfSerializerSession.java +++ b/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfSerializerSession.java @@ -54,19 +54,21 @@ public final class RdfSerializerSession extends SerializerSession { * Create a new session using properties specified in the context. * * @param ctx The context creating this session object. - * The context contains all the configuration settings for this object. + * The context contains all the configuration settings for this object. * @param output The output object. See {@link JsonSerializerSession#getWriter()} for valid class types. * @param op The override properties. - * These override any context properties defined in the context. + * These override any context properties defined in the context. * @param javaMethod The java method that called this serializer, usually the method in a REST servlet. * @param locale The session locale. - * If <jk>null</jk>, then the locale defined on the context is used. + * If <jk>null</jk>, then the locale defined on the context is used. * @param timeZone The session timezone. - * If <jk>null</jk>, then the timezone defined on the context is used. + * If <jk>null</jk>, then the timezone defined on the context is used. * @param mediaType The session media type (e.g. <js>"application/json"</js>). + * @param uriContext The URI context. + * Identifies the current request URI used for resolution of URIs to absolute or root-relative form. */ - protected RdfSerializerSession(RdfSerializerContext ctx, ObjectMap op, Object output, Method javaMethod, Locale locale, TimeZone timeZone, MediaType mediaType) { - super(ctx, op, output, javaMethod, locale, timeZone, mediaType); + protected RdfSerializerSession(RdfSerializerContext ctx, ObjectMap op, Object output, Method javaMethod, Locale locale, TimeZone timeZone, MediaType mediaType, UriContext uriContext) { + super(ctx, op, output, javaMethod, locale, timeZone, mediaType, uriContext); ObjectMap jenaSettings = new ObjectMap(); jenaSettings.put("rdfXml.tab", isUseWhitespace() ? 2 : 0); jenaSettings.put("rdfXml.attributeQuoteChar", Character.toString(getQuoteChar())); http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e1a50566/juneau-core-test/src/test/java/org/apache/juneau/utils/StringUtilsTest.java ---------------------------------------------------------------------- diff --git a/juneau-core-test/src/test/java/org/apache/juneau/utils/StringUtilsTest.java b/juneau-core-test/src/test/java/org/apache/juneau/utils/StringUtilsTest.java index f74d75e..c7c3052 100755 --- a/juneau-core-test/src/test/java/org/apache/juneau/utils/StringUtilsTest.java +++ b/juneau-core-test/src/test/java/org/apache/juneau/utils/StringUtilsTest.java @@ -717,4 +717,32 @@ public class StringUtilsTest { assertObjectEquals("{'a=':'1'}", splitMap("a\\==1", ',', '=', true)); assertObjectEquals("{'a\\\\':'1'}", splitMap("a\\\\=1", ',', '=', true)); } + + //==================================================================================================== + // isAbsoluteUri(String) + //==================================================================================================== + @Test + public void testIsAbsoluteUri() { + assertFalse(isAbsoluteUri(null)); + assertFalse(isAbsoluteUri("")); + assertTrue(isAbsoluteUri("http://foo")); + assertTrue(isAbsoluteUri("x://x")); + assertFalse(isAbsoluteUri("xX://x")); + assertFalse(isAbsoluteUri("x ://x")); + assertFalse(isAbsoluteUri("x: //x")); + assertFalse(isAbsoluteUri("x:/ /x")); + assertFalse(isAbsoluteUri("x:x//x")); + assertFalse(isAbsoluteUri("x:/x/x")); + } + + //==================================================================================================== + // getAuthorityUri(String) + //==================================================================================================== + @Test + public void testGetAuthorityUri() { + assertEquals("http://foo", getAuthorityUri("http://foo")); + assertEquals("http://foo:123", getAuthorityUri("http://foo:123")); + assertEquals("http://foo:123", getAuthorityUri("http://foo:123/")); + assertEquals("http://foo:123", getAuthorityUri("http://foo:123/bar")); + } } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e1a50566/juneau-core-test/src/test/java/org/apache/juneau/utils/UriContextResolutionComboTest.java ---------------------------------------------------------------------- diff --git a/juneau-core-test/src/test/java/org/apache/juneau/utils/UriContextResolutionComboTest.java b/juneau-core-test/src/test/java/org/apache/juneau/utils/UriContextResolutionComboTest.java new file mode 100644 index 0000000..e69a635 --- /dev/null +++ b/juneau-core-test/src/test/java/org/apache/juneau/utils/UriContextResolutionComboTest.java @@ -0,0 +1,630 @@ +// *************************************************************************************************************************** +// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * +// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * +// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * +// * with the License. You may obtain a copy of the License at * +// * * +// * http://www.apache.org/licenses/LICENSE-2.0 * +// * * +// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * +// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * +// * specific language governing permissions and limitations under the License. * +// *************************************************************************************************************************** +package org.apache.juneau.utils; + +import static org.apache.juneau.TestUtils.*; + +import java.util.*; + +import org.apache.juneau.*; +import org.junit.*; +import org.junit.runner.*; +import org.junit.runners.*; + +/** + * Verifies that the resolveUri() methods in UriContext work correctly. + */ +@RunWith(Parameterized.class) +public class UriContextResolutionComboTest { + + @Parameterized.Parameters + public static Collection<Object[]> getInput() { + return Arrays.asList(new Object[][] { + + // Happy cases - All URL parts known. + { + input( + "Happy-1", + "http://host:port","/context","/resource","/path", + "http://foo.com:123/foobar", + "http://foo.com:123/foobar", + "http://foo.com:123/foobar" + ) + }, + { + input( + "Happy-2", + "http://host:port","/context","/resource","/path", + "http://foo.com:123", + "http://foo.com:123", + "http://foo.com:123" + ) + }, + { + input( + "Happy-3", + "http://host:port","/context","/resource","/path", + "/foobar", + "http://host:port/foobar", + "/foobar" + ) + }, + { + input( + "Happy-4", + "http://host:port","/context","/resource","/path", + "/", + "http://host:port", + "/" + ) + }, + { + input( + "Happy-5", + "http://host:port","/context","/resource","/path", + "foobar", + "http://host:port/context/resource/foobar", + "/context/resource/foobar" + ) + }, + { + input( + "Happy-6", + "http://host:port","/context","/resource","/path", + "", + "http://host:port/context/resource/path", + "/context/resource/path" + ) + }, + { + input( + "Happy-7", + "http://host:port","/context","/resource","/path", + "context:/foo", + "http://host:port/context/foo", + "/context/foo" + ) + }, + { + input( + "Happy-8", + "http://host:port","/context","/resource","/path", + "context:/", + "http://host:port/context", + "/context" + ) + }, + { + input( + "Happy-9", + "http://host:port","/context","/resource","/path", + "servlet:/foo", + "http://host:port/context/resource/foo", + "/context/resource/foo" + ) + }, + { + input( + "Happy-10", + "http://host:port","/context","/resource","/path", + "servlet:/", + "http://host:port/context/resource", + "/context/resource" + ) + }, + + // Multiple context and resource parts + { + input( + "MultiContextResource-1", + "http://host:port","/c1/c2","/r1/r2","/p1/p2", + "http://foo.com:123/foobar", + "http://foo.com:123/foobar", + "http://foo.com:123/foobar" + ) + }, + { + input( + "MultiContextResource-2", + "http://host:port","/c1/c2","/r1/r2","/p1/p2", + "http://foo.com:123", + "http://foo.com:123", + "http://foo.com:123" + ) + }, + { + input( + "MultiContextResource-3", + "http://host:port","/c1/c2","/r1/r2","/p1/p2", + "/foobar", + "http://host:port/foobar", + "/foobar" + ) + }, + { + input( + "MultiContextResource-4", + "http://host:port","/c1/c2","/r1/r2","/p1/p2", + "/", + "http://host:port", + "/" + ) + }, + { + input( + "MultiContextResource-5", + "http://host:port","/c1/c2","/r1/r2","/p1/p2", + "foobar", + "http://host:port/c1/c2/r1/r2/p1/foobar", + "/c1/c2/r1/r2/p1/foobar" + ) + }, + { + input( + "MultiContextResource-6", + "http://host:port","/c1/c2","/r1/r2","/p1/p2", + "", + "http://host:port/c1/c2/r1/r2/p1/p2", + "/c1/c2/r1/r2/p1/p2" + ) + }, + { + input( + "MultiContextResource-7", + "http://host:port","/c1/c2","/r1/r2","/p1/p2", + "context:/foo", + "http://host:port/c1/c2/foo", + "/c1/c2/foo" + ) + }, + { + input( + "MultiContextResource-8", + "http://host:port","/c1/c2","/r1/r2","/p1/p2", + "context:/", + "http://host:port/c1/c2", + "/c1/c2" + ) + }, + { + input( + "MultiContextResource-9", + "http://host:port","/c1/c2","/r1/r2","/p1/p2", + "servlet:/foo", + "http://host:port/c1/c2/r1/r2/foo", + "/c1/c2/r1/r2/foo" + ) + }, + { + input( + "MultiContextResource-10", + "http://host:port","/c1/c2","/r1/r2","/p1/p2", + "servlet:/", + "http://host:port/c1/c2/r1/r2", + "/c1/c2/r1/r2" + ) + }, + + // No authority given + { + input( + "NoAuthority-1", + "","/context","/resource","/path", + "http://foo.com:123/foobar", + "http://foo.com:123/foobar", + "http://foo.com:123/foobar" + ) + }, + { + input( + "NoAuthority-2", + "","/context","/resource","/path", + "http://foo.com:123", + "http://foo.com:123", + "http://foo.com:123" + ) + }, + { + input( + "NoAuthority-3", + "","/context","/resource","/path", + "/foobar", + "/foobar", + "/foobar" + ) + }, + { + input( + "NoAuthority-4", + "","/context","/resource","/path", + "/", + "/", + "/" + ) + }, + { + input( + "NoAuthority-5", + "","/context","/resource","/path", + "foobar", + "/context/resource/foobar", + "/context/resource/foobar" + ) + }, + { + input( + "NoAuthority-6", + "","/context","/resource","/path", + "", + "/context/resource/path", + "/context/resource/path" + ) + }, + { + input( + "NoAuthority-7", + "","/context","/resource","/path", + "context:/foo", + "/context/foo", + "/context/foo" + ) + }, + { + input( + "NoAuthority-8", + "","/context","/resource","/path", + "context:/", + "/context", + "/context" + ) + }, + { + input( + "NoAuthority-9", + "","/context","/resource","/path", + "servlet:/foo", + "/context/resource/foo", + "/context/resource/foo" + ) + }, + { + input( + "NoAuthority-10", + "","/context","/resource","/path", + "servlet:/", + "/context/resource", + "/context/resource" + ) + }, + + // No authority or context given + { + input( + "NoAuthorityOrContext-1", + "","","/resource","/path", + "http://foo.com:123/foobar", + "http://foo.com:123/foobar", + "http://foo.com:123/foobar" + ) + }, + { + input( + "NoAuthorityOrContext-2", + "","","/resource","/path", + "http://foo.com:123", + "http://foo.com:123", + "http://foo.com:123" + ) + }, + { + input( + "NoAuthorityOrContext-3", + "","","/resource","/path", + "/foobar", + "/foobar", + "/foobar" + ) + }, + { + input( + "NoAuthorityOrContext-4", + "","","/resource","/path", + "/", + "/", + "/" + ) + }, + { + input( + "NoAuthorityOrContext-5", + "","","/resource","/path", + "foobar", + "/resource/foobar", + "/resource/foobar" + ) + }, + { + input( + "NoAuthorityOrContext-6", + "","","/resource","/path", + "", + "/resource/path", + "/resource/path" + ) + }, + { + input( + "NoAuthorityOrContext-7", + "","","/resource","/path", + "context:/foo", + "/foo", + "/foo" + ) + }, + { + input( + "NoAuthorityOrContext-8", + "","","/resource","/path", + "context:/", + "/", + "/" + ) + }, + { + input( + "NoAuthorityOrContext-9", + "","","/resource","/path", + "servlet:/foo", + "/resource/foo", + "/resource/foo" + ) + }, + { + input( + "NoAuthorityOrContext-10", + "","","/resource","/path", + "servlet:/", + "/resource", + "/resource" + ) + }, + + // No authority or context or resource given + { + input( + "NoAuthorityOrContextOrResource-1", + "","","","/path", + "http://foo.com:123/foobar", + "http://foo.com:123/foobar", + "http://foo.com:123/foobar" + ) + }, + { + input( + "NoAuthorityOrContextOrResource-2", + "","","","/path", + "http://foo.com:123", + "http://foo.com:123", + "http://foo.com:123" + ) + }, + { + input( + "NoAuthorityOrContextOrResource-3", + "","","","/path", + "/foobar", + "/foobar", + "/foobar" + ) + }, + { + input( + "NoAuthorityOrContextOrResource-4", + "","","","/path", + "/", + "/", + "/" + ) + }, + { + input( + "NoAuthorityOrContextOrResource-5", + "","","","/path", + "foobar", + "/foobar", + "/foobar" + ) + }, + { + input( + "NoAuthorityOrContextOrResource-6", + "","","","/path", + "", + "/path", + "/path" + ) + }, + { + input( + "NoAuthorityOrContextOrResource-7", + "","","","/path", + "context:/foo", + "/foo", + "/foo" + ) + }, + { + input( + "NoAuthorityOrContextOrResource-8", + "","","","/path", + "context:/", + "/", + "/" + ) + }, + { + input( + "NoAuthorityOrContextOrResource-9", + "","","","/path", + "servlet:/foo", + "/foo", + "/foo" + ) + }, + { + input( + "NoAuthorityOrContextOrResource-10", + "","","","/path", + "servlet:/", + "/", + "/" + ) + }, + + // No context or resource given. + { + input( + "NoContextOrResource-1", + "http://host:port","","","/path", + "http://foo.com:123/foobar", + "http://foo.com:123/foobar", + "http://foo.com:123/foobar" + ) + }, + { + input( + "NoContextOrResource-2", + "http://host:port","","","/path", + "http://foo.com:123", + "http://foo.com:123", + "http://foo.com:123" + ) + }, + { + input( + "NoContextOrResource-3", + "http://host:port","","","/path", + "/foobar", + "http://host:port/foobar", + "/foobar" + ) + }, + { + input( + "NoContextOrResource-4", + "http://host:port","","","/path", + "/", + "http://host:port", + "/" + ) + }, + { + input( + "NoContextOrResource-5", + "http://host:port","","","/path", + "foobar", + "http://host:port/foobar", + "/foobar" + ) + }, + { + input( + "NoContextOrResource-6", + "http://host:port","","","/path", + "", + "http://host:port/path", + "/path" + ) + }, + { + input( + "NoContextOrResource-7", + "http://host:port","","","/path", + "context:/foo", + "http://host:port/foo", + "/foo" + ) + }, + { + input( + "NoContextOrResource-8", + "http://host:port","","","/path", + "context:/", + "http://host:port", + "/" + ) + }, + { + input( + "NoContextOrResource-9", + "http://host:port","","","/path", + "servlet:/foo", + "http://host:port/foo", + "/foo" + ) + }, + { + input( + "NoContextOrResource-10", + "http://host:port","","","/path", + "servlet:/", + "http://host:port", + "/" + ) + }, + }); + } + + public static Input input(String label, String authority, String context, String resource, String path, String uri, String expectedAbsolute, String expectedRootRelative) { + return new Input(label, authority, context, resource, path, uri, expectedAbsolute, expectedRootRelative); + } + + public static class Input { + private final UriContext uriContext; + private final String label, uri, expectedAbsolute, expectedRootRelative; + + public Input(String label, String authority, String context, String resource, String path, String uri, String expectedAbsolute, String expectedRootRelative) { + this.label = label; + this.uriContext = new UriContext(authority, context, resource, path); + this.uri = uri; + this.expectedAbsolute = expectedAbsolute; + this.expectedRootRelative = expectedRootRelative; + } + } + + private Input in; + + public UriContextResolutionComboTest(Input in) throws Exception { + this.in = in; + } + + @Test + public void testAbsolute() { + assertEquals(in.expectedAbsolute, in.uriContext.resolveAbsolute(in.uri), "{0}: testAbsolute() failed", in.label); + } + + @Test + public void testRootRelative() { + assertEquals(in.expectedRootRelative, in.uriContext.resolveRootRelative(in.uri), "{0}: testRootRelative() failed", in.label); + } + + @Test + public void testAbsoluteAppend() { + assertEquals(in.expectedAbsolute, in.uriContext.appendAbsolute(new StringBuilder(), in.uri).toString(), "{0}: testAbsolute() failed", in.label); + } + + @Test + public void testRootRelativeAppend() { + assertEquals(in.expectedRootRelative, in.uriContext.appendRootRelative(new StringBuilder(), in.uri).toString(), "{0}: testRootRelative() failed", in.label); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e1a50566/juneau-core-test/src/test/java/org/apache/juneau/utils/UriContextUriComboTest.java ---------------------------------------------------------------------- diff --git a/juneau-core-test/src/test/java/org/apache/juneau/utils/UriContextUriComboTest.java b/juneau-core-test/src/test/java/org/apache/juneau/utils/UriContextUriComboTest.java new file mode 100644 index 0000000..b3a3b60 --- /dev/null +++ b/juneau-core-test/src/test/java/org/apache/juneau/utils/UriContextUriComboTest.java @@ -0,0 +1,234 @@ +// *************************************************************************************************************************** +// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * +// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * +// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * +// * with the License. You may obtain a copy of the License at * +// * * +// * http://www.apache.org/licenses/LICENSE-2.0 * +// * * +// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * +// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * +// * specific language governing permissions and limitations under the License. * +// *************************************************************************************************************************** +package org.apache.juneau.utils; + +import static org.apache.juneau.TestUtils.*; + +import java.util.*; + +import org.apache.juneau.*; +import org.junit.*; +import org.junit.runner.*; +import org.junit.runners.*; + +/** + * Verifies that the getUri() methods in UriContext work correctly. + */ +@RunWith(Parameterized.class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class UriContextUriComboTest { + + @Parameterized.Parameters + public static Collection<Object[]> getInput() { + return Arrays.asList(new Object[][] { + + // Happy cases - All URL parts known. + { + input( + "Happy-1", + "http://foo.com:123","/context","/resource","/path", + "http://foo.com:123", + "http://foo.com:123/context", + "http://foo.com:123/context/resource", + "http://foo.com:123/context/resource/path", + "/context", + "/context/resource", + "/context/resource/path" + ) + }, + { + input( + "Happy-2", + "http://foo.com:123","/c1/c2","/r1/r2","/p1/p2", + "http://foo.com:123", + "http://foo.com:123/c1/c2", + "http://foo.com:123/c1/c2/r1/r2", + "http://foo.com:123/c1/c2/r1/r2/p1/p2", + "/c1/c2", + "/c1/c2/r1/r2", + "/c1/c2/r1/r2/p1/p2" + ) + }, + { + input( + "NoAuthority-1", + "","/context","/resource","/path", + "/", + "/context", + "/context/resource", + "/context/resource/path", + "/context", + "/context/resource", + "/context/resource/path" + ) + }, + { + input( + "NoContext-1", + "http://foo.com:123","","/resource","/path", + "http://foo.com:123", + "http://foo.com:123", + "http://foo.com:123/resource", + "http://foo.com:123/resource/path", + "/", + "/resource", + "/resource/path" + ) + }, + { + input( + "NoResource-1", + "http://foo.com:123","/context","","/path", + "http://foo.com:123", + "http://foo.com:123/context", + "http://foo.com:123/context", + "http://foo.com:123/context/path", + "/context", + "/context", + "/context/path" + ) + }, + { + input( + "NoPath-1", + "http://foo.com:123","/context","/resource","", + "http://foo.com:123", + "http://foo.com:123/context", + "http://foo.com:123/context/resource", + "http://foo.com:123/context/resource", + "/context", + "/context/resource", + "/context/resource" + ) + }, + { + input( + "NoAuthorityNoContext-1", + "","","/resource","/path", + "/", + "/", + "/resource", + "/resource/path", + "/", + "/resource", + "/resource/path" + ) + }, + { + input( + "NoContextNoResource-1", + "http://foo.com:123","","","/path", + "http://foo.com:123", + "http://foo.com:123", + "http://foo.com:123", + "http://foo.com:123/path", + "/", + "/", + "/path" + ) + }, + { + input( + "NoAuthorityNoContextNoResource-1", + "","","","/path", + "/", + "/", + "/", + "/path", + "/", + "/", + "/path" + ) + }, + { + input( + "Nothing-1", + "","","","", + "/", + "/", + "/", + "/", + "/", + "/", + "/" + ) + }, + }); + } + + public static Input input(String label, String authority, String context, String resource, String path, + String eAbsoluteAuthority, String eAbsoluteContext, String eAbsoluteResource, String eAbsolutePath, + String eRootRelativeContext, String eRootRelativeResource, String eRootRelativePath) { + return new Input(label, authority, context, resource, path, eAbsoluteAuthority, eAbsoluteContext, eAbsoluteResource, eAbsolutePath, eRootRelativeContext, eRootRelativeResource, eRootRelativePath); + } + + public static class Input { + private final UriContext uriContext; + private final String label, eAbsoluteAuthority, eAbsoluteContext, eAbsoluteResource, eAbsolutePath, eRootRelativeContext, eRootRelativeResource, eRootRelativePath; + + public Input(String label, String authority, String context, String resource, String path, + String eAbsoluteAuthority, String eAbsoluteContext, String eAbsoluteResource, String eAbsolutePath, + String eRootRelativeContext, String eRootRelativeResource, String eRootRelativePath) { + this.label = label; + this.uriContext = new UriContext(authority, context, resource, path); + this.eAbsoluteAuthority = eAbsoluteAuthority; + this.eAbsoluteContext = eAbsoluteContext; + this.eAbsoluteResource = eAbsoluteResource; + this.eAbsolutePath = eAbsolutePath; + this.eRootRelativeContext = eRootRelativeContext; + this.eRootRelativeResource = eRootRelativeResource; + this.eRootRelativePath = eRootRelativePath; + } + } + + private Input in; + + public UriContextUriComboTest(Input in) throws Exception { + this.in = in; + } + + @Test + public void a1_testAbsoluteAuthority() { + assertEquals(in.eAbsoluteAuthority, in.uriContext.getAbsoluteAuthority(), "{0}: testAbsoluteAuthority() failed", in.label); + } + + @Test + public void a2_testAbsoluteContext() { + assertEquals(in.eAbsoluteContext, in.uriContext.getAbsoluteContextRoot(), "{0}: testAbsoluteContext() failed", in.label); + } + + @Test + public void a3_testAbsoluteResource() { + assertEquals(in.eAbsoluteResource, in.uriContext.getAbsoluteServletPath(), "{0}: testAbsoluteResource() failed", in.label); + } + + @Test + public void a4_testAbsolutePath() { + assertEquals(in.eAbsolutePath, in.uriContext.getAbsolutePathInfo(), "{0}: testAbsolutePath() failed", in.label); + } + + @Test + public void a5_testRootRelativeContext() { + assertEquals(in.eRootRelativeContext, in.uriContext.getRootRelativeContextRoot(), "{0}: testRootRelativeContext() failed", in.label); + } + + @Test + public void a6_testRootRelativeResource() { + assertEquals(in.eRootRelativeResource, in.uriContext.getRootRelativeServletPath(), "{0}: testRootRelativeResource() failed", in.label); + } + + @Test + public void a7_testRootRelativePath() { + assertEquals(in.eRootRelativePath, in.uriContext.getRootRelativePathInfo(), "{0}: testRootRelativePath() failed", in.label); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e1a50566/juneau-core-test/src/test/java/org/apache/juneau/xml/XmlContentTest.java ---------------------------------------------------------------------- diff --git a/juneau-core-test/src/test/java/org/apache/juneau/xml/XmlContentTest.java b/juneau-core-test/src/test/java/org/apache/juneau/xml/XmlContentTest.java index 6f0738e..65e23e7 100755 --- a/juneau-core-test/src/test/java/org/apache/juneau/xml/XmlContentTest.java +++ b/juneau-core-test/src/test/java/org/apache/juneau/xml/XmlContentTest.java @@ -46,7 +46,7 @@ public class XmlContentTest { t.f2 = null; sw = new StringWriter(); - session = s1.createSession(sw, new ObjectMap("{"+SERIALIZER_trimNullProperties+":false}"), null, null, null, null); + session = s1.createSession(sw, new ObjectMap("{"+SERIALIZER_trimNullProperties+":false}"), null, null, null, null, null); s1.serialize(session, t); r = sw.toString(); assertEquals("<A f1='f1'>_x0000_</A>", r); @@ -54,7 +54,7 @@ public class XmlContentTest { assertEqualObjects(t, t2); sw = new StringWriter(); - session = s2.createSession(sw, new ObjectMap("{"+SERIALIZER_trimNullProperties+":false}"), null, null, null, null); + session = s2.createSession(sw, new ObjectMap("{"+SERIALIZER_trimNullProperties+":false}"), null, null, null, null, null); s2.serialize(session, t); r = sw.toString(); assertEquals("<A f1='f1'>_x0000_</A>\n", r); @@ -154,7 +154,7 @@ public class XmlContentTest { t.f2 = null; sw = new StringWriter(); - session = s1.createSession(sw, new ObjectMap("{"+SERIALIZER_trimNullProperties+":false}"), null, null, null, null); + session = s1.createSession(sw, new ObjectMap("{"+SERIALIZER_trimNullProperties+":false}"), null, null, null, null, null); s1.serialize(session, t); r = sw.toString(); assertEquals("<A f1='f1'>_x0000_</A>", r); @@ -162,7 +162,7 @@ public class XmlContentTest { assertEqualObjects(t, t2); sw = new StringWriter(); - session = s2.createSession(sw, new ObjectMap("{"+SERIALIZER_trimNullProperties+":false}"), null, null, null, null); + session = s2.createSession(sw, new ObjectMap("{"+SERIALIZER_trimNullProperties+":false}"), null, null, null, null, null); s2.serialize(session, t); r = sw.toString(); assertEquals("<A f1='f1'>_x0000_</A>\n", r); http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e1a50566/juneau-core/src/main/java/org/apache/juneau/BeanMap.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/BeanMap.java b/juneau-core/src/main/java/org/apache/juneau/BeanMap.java index e6a1566..af5634e 100644 --- a/juneau-core/src/main/java/org/apache/juneau/BeanMap.java +++ b/juneau-core/src/main/java/org/apache/juneau/BeanMap.java @@ -440,6 +440,17 @@ public class BeanMap<T> extends AbstractMap<String,Object> implements Delegate<T } /** + * Given a string containing variables of the form <code>"{property}"</code>, replaces those variables with + * property values in this bean. + * + * @param s The string containing variables. + * @return A new string with variables replaced, or the same string if no variables were found. + */ + public String resolveVars(String s) { + return StringUtils.replaceVars(s, this); + } + + /** * Returns a simple collection of properties for this bean map. * @return A simple collection of properties for this bean map. */ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e1a50566/juneau-core/src/main/java/org/apache/juneau/UriContext.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/UriContext.java b/juneau-core/src/main/java/org/apache/juneau/UriContext.java new file mode 100644 index 0000000..99ddd7f --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/UriContext.java @@ -0,0 +1,421 @@ +// *************************************************************************************************************************** +// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * +// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * +// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * +// * with the License. You may obtain a copy of the License at * +// * * +// * http://www.apache.org/licenses/LICENSE-2.0 * +// * * +// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * +// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * +// * specific language governing permissions and limitations under the License. * +// *************************************************************************************************************************** +package org.apache.juneau; + +import static org.apache.juneau.internal.StringUtils.*; + +import java.io.*; + +/** + * Represents a URL broken into authority/context-root/servlet-path/path-info parts. + * <p> + * A typical request against a URL takes the following form: + * <p class='bcode'> + * http://host:port/context-root/servlet-path/path-info + * | authority | context | resource | path | + * +--------------------------------------------------+ + * </p> + * <p> + * This class allows you to convert URL strings to absolute (e.g. <js>"http://host:port/foo/bar"</js>) or root-relative + * (e.g. <js>"/foo/bar"</js>) URLs. + * <p> + * Two special protocols are used to represent context-root-relative and servlet-relative URIs: + * <js>"context:/"</js> and <js>"servlet:/"</js>. + * + * The following list shows the types of URLs that can be resolved with this class: + * <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>"foo"</js> - Path-info-relative URI. + * <li><js>""</js> - Path-info URI. + * </ul> + * + * The following class shows how + */ +public class UriContext { + + private final String authority, contextRoot, servletPath, pathInfo, parentPath; + + // Lazy-initialized fields. + private String aContextRoot, rContextRoot, aServletPath, rResource, aPathInfo, rPath; + + + /** + * Constructor. + * <p> + * Leading and trailing slashes are trimmed of all parameters. + * <p> + * Any parameter can be <jk>null</jk>. Blanks and nulls are equivalent. + * + * @param authority - The authority portion of URL (e.g. <js>"http://hostname:port"</js>) + * @param contextRoot - The context root of the application (e.g. <js>"/context-root"</js>, or <js>"context-root"</js>) + * @param servletPath - The servlet path (e.g. <js>"/servlet-path"</js>, or <js>"servlet-path"</js>) + * @param pathInfo - The path info (e.g. <js>"/path-info"</js>, or <js>"path-info"</js>) + */ + public UriContext(String authority, String contextRoot, String servletPath, String pathInfo) { + this.authority = nullIfEmpty(trimSlashes(authority)); + this.contextRoot = nullIfEmpty(trimSlashes(contextRoot)); + this.servletPath = nullIfEmpty(trimSlashes(servletPath)); + this.pathInfo = nullIfEmpty(trimSlashes(pathInfo)); + this.parentPath = this.pathInfo == null || this.pathInfo.indexOf('/') == -1 ? null : this.pathInfo.substring(0, this.pathInfo.lastIndexOf('/')); + } + + /** + * Returns the absolute URI of just the authority portion of this URI context. + * <p> + * Example: <js>"http://hostname:port"</js> + * <p> + * If the authority is null/empty, returns <js>"/"</js>. + * + * @return The absolute URI of just the authority portion of this URI context. + * Never <jk>null</jk>. + */ + public String getAbsoluteAuthority() { + return authority == null ? "/" : authority; + } + + /** + * Returns the absolute URI of the context-root portion of this URI context. + * <p> + * Example: <js>"http://hostname:port/context-root"</js> + * + * @return The absolute URI of the context-root portion of this URI context. + * Never <jk>null</jk>. + */ + public String getAbsoluteContextRoot() { + if (aContextRoot == null) { + if (authority == null) + aContextRoot = getRootRelativeContextRoot(); + else + aContextRoot = (contextRoot == null ? authority : (authority + '/' + contextRoot)); + } + return aContextRoot; + } + + /** + * Returns the root-relative URI of the context portion of this URI context. + * <p> + * Example: <js>"/context-root"</js> + * + * @return The root-relative URI of the context portion of this URI context. + * Never <jk>null</jk>. + */ + public String getRootRelativeContextRoot() { + if (rContextRoot == null) + rContextRoot = contextRoot == null ? "/" : ('/' + contextRoot); + return rContextRoot; + } + + /** + * Returns the absolute URI of the resource portion of this URI context. + * <p> + * Example: <js>"http://hostname:port/context-root/servlet-path"</js> + * + * @return The absolute URI of the resource portion of this URI context. + * Never <jk>null</jk>. + */ + public String getAbsoluteServletPath() { + if (aServletPath == null) { + if (authority == null) + aServletPath = getRootRelativeServletPath(); + else { + if (contextRoot == null) + aServletPath = (servletPath == null ? authority : authority + '/' + servletPath); + else + aServletPath = (servletPath == null ? (authority + '/' + contextRoot) : (authority + '/' + contextRoot + '/' + servletPath)); + } + } + return aServletPath; + } + + /** + * Returns the root-relative URI of the resource portion of this URI context. + * <p> + * Example: <js>"/context-root/servlet-path"</js> + * + * @return The root-relative URI of the resource portion of this URI context. + * Never <jk>null</jk>. + */ + public String getRootRelativeServletPath() { + if (rResource == null) { + if (contextRoot == null) + rResource = (servletPath == null ? "/" : ('/' + servletPath)); + else + rResource = (servletPath == null ? ('/' + contextRoot) : ('/' + contextRoot + '/' + servletPath)); + } + return rResource; + } + + /** + * Returns the parent of the URL returned by {@link #getAbsoluteServletPath()}. + * + * @return The parent of the URL returned by {@link #getAbsoluteServletPath()}. + */ + public String getAbsoluteServletPathParent() { + return getParent(getAbsoluteServletPath()); + } + + /** + * Returns the parent of the URL returned by {@link #getRootRelativeServletPath()}. + * + * @return The parent of the URL returned by {@link #getRootRelativeServletPath()}. + */ + public String getRootRelativeServletPathParent() { + return getParent(getRootRelativeServletPath()); + } + + /** + * Returns the absolute URI of the path portion of this URI context. + * <p> + * Example: <js>"http://hostname:port/context-root/servlet-path/path-info"</js> + * + * @return The absolute URI of the path portion of this URI context. + * Never <jk>null</jk>. + */ + public String getAbsolutePathInfo() { + if (aPathInfo == null) { + if (authority == null) + aPathInfo = getRootRelativePathInfo(); + else { + if (contextRoot == null) { + if (servletPath == null) + aPathInfo = (pathInfo == null ? authority : (authority + '/' + pathInfo)); + else + aPathInfo = (pathInfo == null ? (authority + '/' + servletPath) : (authority + '/' + servletPath + '/' + pathInfo)); + } else { + if (servletPath == null) + aPathInfo = (pathInfo == null ? authority + '/' + contextRoot : (authority + '/' + contextRoot + '/' + pathInfo)); + else + aPathInfo = (pathInfo == null ? (authority + '/' + contextRoot + '/' + servletPath) : (authority + '/' + contextRoot + '/' + servletPath + '/' + pathInfo)); + } + } + } + return aPathInfo; + } + + /** + * Returns the root-relative URI of the path portion of this URI context. + * <p> + * Example: <js>"/context-root/servlet-path/path-info"</js> + * + * @return The root-relative URI of the path portion of this URI context. + * Never <jk>null</jk>. + */ + public String getRootRelativePathInfo() { + if (rPath == null) { + if (contextRoot == null) { + if (servletPath == null) + rPath = (pathInfo == null ? "/" : ('/' + pathInfo)); + else + rPath = (pathInfo == null ? ('/' + servletPath) : ('/' + servletPath + '/' + pathInfo)); + } else { + if (servletPath == null) + rPath = (pathInfo == null ? ('/' + contextRoot) : ('/' + contextRoot + '/' + pathInfo)); + else + rPath = (pathInfo == null ? ('/' + contextRoot + '/' + servletPath) : ('/' + contextRoot + '/' + servletPath + '/' + pathInfo)); + } + } + return rPath; + } + + /** + * Returns the parent of the URL returned by {@link #getAbsolutePathInfo()}. + * + * @return The parent of the URL returned by {@link #getAbsolutePathInfo()}. + */ + public String getAbsolutePathInfoParent() { + return getParent(getAbsolutePathInfo()); + } + + /** + * Returns the parent of the URL returned by {@link #getRootRelativePathInfo()}. + * + * @return The parent of the URL returned by {@link #getRootRelativePathInfo()}. + */ + public String getRootRelativePathInfoParent() { + return getParent(getRootRelativePathInfo()); + } + + /** + * Converts the specified URI to absolute form based on values in this context. + * + * @param uri The URI to convert to absolute form. + * @return The converted URI. + */ + public String resolveAbsolute(String uri) { + if (isAbsoluteUri(uri)) + return uri; + return appendAbsolute(new StringBuilder(), uri).toString(); + } + + /** + * Converts the specified URI to root-relative form based on values in this context. + * + * @param uri The URI to convert to root-relative form. + * @return The converted URI. + */ + public String resolveRootRelative(String uri) { + if (isAbsoluteUri(uri)) + return uri; + if (startsWith(uri, '/')) + return uri; + return appendRootRelative(new StringBuilder(), uri).toString(); + } + + /** + * Same as {@link #resolveAbsolute(String)} except appends result to the specified appendable. + * + * @param a The appendable to append the URL to. + * @param uri The URI to convert to absolute form. + * @return The same appendable passed in. + */ + public Appendable appendAbsolute(Appendable a, String uri) { + + try { + uri = nullIfEmpty(uri); + + // Absolute paths are not changed. + if (isAbsoluteUri(uri)) + return a.append(uri); + + // Root-relative path + if (startsWith(uri, '/')) { + if (authority != null){ + a.append(authority); + if (uri.length() == 1) + return a; + } + return a.append(uri); + + // Context-relative path + } else if (uri != null && uri.startsWith("context:/")) { + if (authority != null) + a.append(authority); + if (contextRoot != null) + a.append('/').append(contextRoot); + if (uri.length() > 9) + a.append('/').append(uri.substring(9)); + else if (contextRoot == null && authority == null) + a.append('/'); + + // Resource-relative path + } else if (uri != null && uri.startsWith("servlet:/")) { + if (authority != null) + a.append(authority); + if (contextRoot != null) + a.append('/').append(contextRoot); + if (servletPath != null) + a.append('/').append(servletPath); + if (uri.length() > 9) + a.append('/').append(uri.substring(9)); + else if (servletPath == null && contextRoot == null && authority == null) + a.append('/'); + + // Relative path + } else { + if (authority != null) + a.append(authority); + if (contextRoot != null) + a.append('/').append(contextRoot); + if (servletPath != null) + a.append('/').append(servletPath); + if (uri == null) { + if (pathInfo != null) + a.append('/').append(pathInfo); + } else { + if (parentPath != null) + a.append('/').append(parentPath); + a.append('/').append(uri); + } + } + + return a; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Same as {@link #resolveRootRelative(String)} except appends result to the specified appendable. + * + * @param a The appendable to append the URL to. + * @param uri The URI to convert to root-relative form. + * @return The same appendable passed in. + */ + public Appendable appendRootRelative(Appendable a, String uri) { + + try { + uri = nullIfEmpty(uri); + + // Absolute paths are not changed. + if (isAbsoluteUri(uri)) + return a.append(uri); + + // Root-relative path + if (startsWith(uri, '/')) { + return a.append(uri); + + // Context-relative path + } else if (uri != null && uri.startsWith("context:/")) { + if (contextRoot != null) + a.append('/').append(contextRoot); + if (uri.length() > 9) + a.append('/').append(uri.substring(9)); + else if (contextRoot == null) + a.append('/'); + + // Resource-relative path + } else if (uri != null && uri.startsWith("servlet:/")) { + if (contextRoot != null) + a.append('/').append(contextRoot); + if (servletPath != null) + a.append('/').append(servletPath); + if (uri.length() > 9) + a.append('/').append(uri.substring(9)); + else if (servletPath == null && contextRoot == null) + a.append('/'); + + // Relative path + } else { + if (contextRoot != null) + a.append('/').append(contextRoot); + if (servletPath != null) + a.append('/').append(servletPath); + if (uri == null) { + if (pathInfo != null) + a.append('/').append(pathInfo); + } else { + if (parentPath != null) + a.append('/').append(parentPath); + a.append('/').append(uri); + } + } + + return a; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private static String getParent(String uri) { + int i = uri.lastIndexOf('/'); + if (i <= 1) + return "/"; + return uri.substring(0, i); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e1a50566/juneau-core/src/main/java/org/apache/juneau/UriRelativity.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/UriRelativity.java b/juneau-core/src/main/java/org/apache/juneau/UriRelativity.java new file mode 100644 index 0000000..95fc46b --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/UriRelativity.java @@ -0,0 +1,29 @@ +// *************************************************************************************************************************** +// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * +// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * +// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * +// * with the License. You may obtain a copy of the License at * +// * * +// * http://www.apache.org/licenses/LICENSE-2.0 * +// * * +// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * +// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * +// * specific language governing permissions and limitations under the License. * +// *************************************************************************************************************************** +package org.apache.juneau; + +/** + * Identifies how relative URIs should resolve against. + */ +public enum UriRelativity { + + /** + * Relative URIs should be considered relative to the servlet URI. + */ + RESOURCE, + + /** + * Relative URIs should be considered relative to the request URI. + */ + PATH_INFO; +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e1a50566/juneau-core/src/main/java/org/apache/juneau/UriResolution.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/UriResolution.java b/juneau-core/src/main/java/org/apache/juneau/UriResolution.java new file mode 100644 index 0000000..aacfe6d --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/UriResolution.java @@ -0,0 +1,34 @@ +// *************************************************************************************************************************** +// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * +// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * +// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * +// * with the License. You may obtain a copy of the License at * +// * * +// * http://www.apache.org/licenses/LICENSE-2.0 * +// * * +// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * +// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * +// * specific language governing permissions and limitations under the License. * +// *************************************************************************************************************************** +package org.apache.juneau; + +/** + * Identifies the possible types of URL resolution. + */ +public enum UriResolution { + + /** + * Resolve to an absolute URL (e.g. <js>"http://host:port/context-root/servlet-path/path-info"</js>). + */ + ABSOLUTE, + + /** + * Resolve to a root-relative URL (e.g. <js>"/context-root/servlet-path/path-info"</js>). + */ + ROOT_RELATIVE, + + /** + * Don't do any URL resolution. + */ + NONE; +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e1a50566/juneau-core/src/main/java/org/apache/juneau/csv/CsvSerializerSession.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/csv/CsvSerializerSession.java b/juneau-core/src/main/java/org/apache/juneau/csv/CsvSerializerSession.java index 163928b..16a6c14 100644 --- a/juneau-core/src/main/java/org/apache/juneau/csv/CsvSerializerSession.java +++ b/juneau-core/src/main/java/org/apache/juneau/csv/CsvSerializerSession.java @@ -30,18 +30,20 @@ public final class CsvSerializerSession extends SerializerSession { * Create a new session using properties specified in the context. * * @param ctx The context creating this session object. - * The context contains all the configuration settings for this object. + * The context contains all the configuration settings for this object. * @param output The output object. * @param op The override properties. - * These override any context properties defined in the context. + * These override any context properties defined in the context. * @param javaMethod The java method that called this serializer, usually the method in a REST servlet. * @param locale The session locale. - * If <jk>null</jk>, then the locale defined on the context is used. + * If <jk>null</jk>, then the locale defined on the context is used. * @param timeZone The session timezone. - * If <jk>null</jk>, then the timezone defined on the context is used. + * If <jk>null</jk>, then the timezone defined on the context is used. * @param mediaType The session media type (e.g. <js>"application/json"</js>). + * @param uriContext The URI context. + * Identifies the current request URI used for resolution of URIs to absolute or root-relative form. */ - protected CsvSerializerSession(CsvSerializerContext ctx, ObjectMap op, Object output, Method javaMethod, Locale locale, TimeZone timeZone, MediaType mediaType) { - super(ctx, op, output, javaMethod, locale, timeZone, mediaType); + protected CsvSerializerSession(CsvSerializerContext ctx, ObjectMap op, Object output, Method javaMethod, Locale locale, TimeZone timeZone, MediaType mediaType, UriContext uriContext) { + super(ctx, op, output, javaMethod, locale, timeZone, mediaType, uriContext); } } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e1a50566/juneau-core/src/main/java/org/apache/juneau/html/HtmlBeanPropertyMeta.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/html/HtmlBeanPropertyMeta.java b/juneau-core/src/main/java/org/apache/juneau/html/HtmlBeanPropertyMeta.java index a2c3a6c..63ecbb4 100644 --- a/juneau-core/src/main/java/org/apache/juneau/html/HtmlBeanPropertyMeta.java +++ b/juneau-core/src/main/java/org/apache/juneau/html/HtmlBeanPropertyMeta.java @@ -18,36 +18,58 @@ import org.apache.juneau.html.annotation.*; /** * Metadata on bean properties specific to the HTML serializers and parsers pulled from the {@link Html @Html} annotation on the bean property. */ -public class HtmlBeanPropertyMeta extends BeanPropertyMetaExtended { +@SuppressWarnings("rawtypes") +public final class HtmlBeanPropertyMeta extends BeanPropertyMetaExtended { - private boolean asXml, noTables, noTableHeaders, asPlainText; + private final boolean asXml, noTables, noTableHeaders, asPlainText; + private final HtmlRender render; + private final String link; /** * Constructor. * * @param bpm The metadata of the bean property of this additional metadata. + * @throws Exception If render class could not be instantiated. */ - public HtmlBeanPropertyMeta(BeanPropertyMeta bpm) { + public HtmlBeanPropertyMeta(BeanPropertyMeta bpm) throws Exception { super(bpm); + Builder b = new Builder(); if (bpm.getField() != null) - findHtmlInfo(bpm.getField().getAnnotation(Html.class)); + b.findHtmlInfo(bpm.getField().getAnnotation(Html.class)); if (bpm.getGetter() != null) - findHtmlInfo(bpm.getGetter().getAnnotation(Html.class)); + b.findHtmlInfo(bpm.getGetter().getAnnotation(Html.class)); if (bpm.getSetter() != null) - findHtmlInfo(bpm.getSetter().getAnnotation(Html.class)); + b.findHtmlInfo(bpm.getSetter().getAnnotation(Html.class)); + + this.asXml = b.asXml; + this.noTables = b.noTables; + this.noTableHeaders = b.noTableHeaders; + this.asPlainText = b.asPlainText; + this.render = b.render.newInstance(); + this.link = b.link; } - private void findHtmlInfo(Html html) { - if (html == null) - return; - if (html.asXml()) - asXml = html.asXml(); - if (html.noTables()) - noTables = html.noTables(); - if (html.noTableHeaders()) - noTableHeaders = html.noTableHeaders(); - if (html.asPlainText()) - asPlainText = html.asPlainText(); + private static class Builder { + boolean asXml, noTables, noTableHeaders, asPlainText; + Class<? extends HtmlRender> render = HtmlRender.class; + String link; + + void findHtmlInfo(Html html) { + if (html == null) + return; + if (html.asXml()) + asXml = html.asXml(); + if (html.noTables()) + noTables = html.noTables(); + if (html.noTableHeaders()) + noTableHeaders = html.noTableHeaders(); + if (html.asPlainText()) + asPlainText = html.asPlainText(); + if (html.render() != HtmlRender.class) + render = html.render(); + if (! html.link().isEmpty()) + link = html.link(); + } } /** @@ -85,4 +107,26 @@ public class HtmlBeanPropertyMeta extends BeanPropertyMetaExtended { public boolean isNoTableHeaders() { return noTableHeaders; } + + /** + * Returns the render class for rendering the style and contents of this property value in HTML. + * <p> + * This value is specified via the {@link Html#render()} annotation. + * + * @return The render class, never <jk>null</jk>. + */ + public HtmlRender getRender() { + return render; + } + + /** + * Adds a hyperlink to this value in HTML. + * <p> + * This value is specified via the {@link Html#link()} annotation. + * + * @return The link string, or <jk>null</jk> if not specified. + */ + public String getLink() { + return link; + } } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e1a50566/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocSerializer.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocSerializer.java b/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocSerializer.java index d97afe4..68e19e2 100644 --- a/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocSerializer.java +++ b/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocSerializer.java @@ -75,8 +75,8 @@ public class HtmlDocSerializer extends HtmlStrippedDocSerializer { //-------------------------------------------------------------------------------- @Override /* Serializer */ - public HtmlDocSerializerSession createSession(Object output, ObjectMap op, Method javaMethod, Locale locale, TimeZone timeZone, MediaType mediaType) { - return new HtmlDocSerializerSession(ctx, op, output, javaMethod, locale, timeZone, mediaType); + public HtmlDocSerializerSession createSession(Object output, ObjectMap op, Method javaMethod, Locale locale, TimeZone timeZone, MediaType mediaType, UriContext uriContext) { + return new HtmlDocSerializerSession(ctx, op, output, javaMethod, locale, timeZone, mediaType, uriContext); } @Override /* Serializer */ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e1a50566/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocSerializerSession.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocSerializerSession.java b/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocSerializerSession.java index 0e334d1..f04d2b7 100644 --- a/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocSerializerSession.java +++ b/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocSerializerSession.java @@ -41,20 +41,22 @@ public final class HtmlDocSerializerSession extends HtmlSerializerSession { * Create a new session using properties specified in the context. * * @param ctx The context creating this session object. - * The context contains all the configuration settings for this object. + * The context contains all the configuration settings for this object. * @param output The output object. See {@link JsonSerializerSession#getWriter()} for valid class types. * @param op The override properties. - * These override any context properties defined in the context. + * These override any context properties defined in the context. * @param javaMethod The java method that called this serializer, usually the method in a REST servlet. * @param locale The session locale. - * If <jk>null</jk>, then the locale defined on the context is used. + * If <jk>null</jk>, then the locale defined on the context is used. * @param timeZone The session timezone. - * If <jk>null</jk>, then the timezone defined on the context is used. + * If <jk>null</jk>, then the timezone defined on the context is used. * @param mediaType The session media type (e.g. <js>"application/json"</js>). + * @param uriContext The URI context. + * Identifies the current request URI used for resolution of URIs to absolute or root-relative form. */ @SuppressWarnings({ "unchecked", "rawtypes" }) - protected HtmlDocSerializerSession(HtmlDocSerializerContext ctx, ObjectMap op, Object output, Method javaMethod, Locale locale, TimeZone timeZone, MediaType mediaType) { - super(ctx, op, output, javaMethod, locale, timeZone, mediaType); + protected HtmlDocSerializerSession(HtmlDocSerializerContext ctx, ObjectMap op, Object output, Method javaMethod, Locale locale, TimeZone timeZone, MediaType mediaType, UriContext uriContext) { + super(ctx, op, output, javaMethod, locale, timeZone, mediaType, uriContext); if (op == null || op.isEmpty()) { title = ctx.title; text = ctx.text; @@ -131,6 +133,6 @@ public final class HtmlDocSerializerSession extends HtmlSerializerSession { Object output = getOutput(); if (output instanceof HtmlWriter) return (HtmlWriter)output; - return new HtmlWriter(super.getWriter(), isUseWhitespace(), isTrimStrings(), getQuoteChar(), getRelativeUriBase(), getAbsolutePathUriBase()); + return new HtmlWriter(super.getWriter(), isUseWhitespace(), isTrimStrings(), getQuoteChar(), getRelativeUriBase(), getAbsolutePathUriBase(), getUriContext()); } } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e1a50566/juneau-core/src/main/java/org/apache/juneau/html/HtmlRender.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/html/HtmlRender.java b/juneau-core/src/main/java/org/apache/juneau/html/HtmlRender.java new file mode 100644 index 0000000..b30c67e --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/html/HtmlRender.java @@ -0,0 +1,149 @@ +// *************************************************************************************************************************** +// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * +// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * +// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * +// * with the License. You may obtain a copy of the License at * +// * * +// * http://www.apache.org/licenses/LICENSE-2.0 * +// * * +// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * +// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * +// * specific language governing permissions and limitations under the License. * +// *************************************************************************************************************************** +package org.apache.juneau.html; + +import org.apache.juneau.html.annotation.*; +import org.apache.juneau.serializer.*; + +/** + * Allows custom rendering of bean property values when serialized as HTML. + * <p> + * Associated with bean properties using the {@link Html#render() @Html.render()} annotation. + * <p> + * Using this class, you can alter the CSS style and HTML content of the bean property. + * <p> + * The following example shows two render classes that customize the appearance of the <code>pctFull</code> and + * <code>status</code> columns shown below: + * <p> + * <img class='bordered' src='doc-files/HtmlRender_1.png'> + * + * <p class='bcode'> + * + * <jc>// Our bean class</jc> + * <jk>public class</jk> FileSpace { + * + * <jk>private final</jk> String <jf>drive</jf>; + * <jk>private final long</jk> <jf>total</jf>, <jf>available</jf>; + * + * <jk>public</jk> FileSpace(String drive, <jk>long</jk> total, <jk>long</jk> available) { + * <jk>this</jk>.<jf>drive</jf> = drive; + * <jk>this</jk>.<jf>total</jf> = total; + * <jk>this</jk>.<jf>available</jf> = available; + * } + * + * <ja>@Html</ja>(link=<js>"drive/{drive}"</js>) + * <jk>public</jk> String getDrive() { + * <jk>return</jk> <jf>drive</jf>; + * } + * + * <jk>public long</jk> getTotal() { + * <jk>return</jk> <jf>total</jf>; + * } + * + * <jk>public long</jk> getAvailable() { + * <jk>return</jk> <jf>available</jf>; + * } + * + * <ja>@Html</ja>(render=FileSpacePctRender.<jk>class</jk>) + * <jk>public float</jk> getPctFull() { + * <jk>return</jk> ((100 * <jf>available</jf>) / <jf>total</jf>); + * } + * + * <ja>@Html</ja>(render=FileSpaceStatusRender.<jk>class</jk>) + * <jk>public</jk> FileSpaceStatus getStatus() { + * <jk>float</jk> pf = getPctFull(); + * <jk>if</jk> (pf < 80) + * <jk>return</jk> FileSpaceStatus.<jsf>OK</jsf>; + * <jk>if</jk> (pf < 90) + * <jk>return</jk> FileSpaceStatus.<jsf>WARNING</jsf>; + * <jk>return</jk> FileSpaceStatus.<jsf>SEVERE</jsf>; + * } + * } + * + * <jc>// Possible values for the getStatus() method</jc> + * <jk>public static enum</jk> FileSpaceStatus { + * <jsf>OK</jsf>, <jsf>WARNING</jsf>, <jsf>SEVERE</jsf>; + * } + * + * <jc>// Custom render for getPctFull() method</jc> + * <jk>public static class</jk> FileSpacePctRender <jk>extends</jk> HtmlRender<Float> { + * + * <ja>@Override</ja> + * <jk>public</jk> String getStyle(SerializerSession session, Float value) { + * <jk>if</jk> (value < 80) + * <jk>return</jk> <js>"background-color:lightgreen;text-align:center"</js>; + * <jk>if</jk> (value < 90) + * <jk>return</jk> <js>"background-color:yellow;text-align:center"</js>; + * <jk>return</jk> <js>"background-color:red;text-align:center;border:;animation:color_change 0.5s infinite alternate"</js>; + * } + * + * <ja>@Override</ja> + * <jk>public</jk> Object getContent(SerializerSession session, Float value) { + * <jk>if</jk> (value >= 90) + * <jk>return</jk> <jsm>div</jsm>( + * String.<jsm>format</jsm>(<js>"%.0f%%"</js>, value), + * <jsm>style</jsm>(<js>"@keyframes color_change { from { background-color: red; } to { background-color: yellow; }"</js>) + * ); + * <jk>return</jk> String.<jsm>format</jsm>(<js>"%.0f%%"</js>, value); + * } + * } + * + * <jc>// Custom render for getStatus() method</jc> + * <jk>public static class</jk> FileSpaceStatusRender <jk>extends</jk> HtmlRender<FileSpaceStatus> { + * + * <ja>@Override</ja> + * <jk>public</jk> String getStyle(SerializerSession session, FileSpaceStatus value) { + * <jk>return</jk> <js>"text-align:center"</js>; + * } + * + * <ja>@Override</ja> + * <jk>public</jk> Object getContent(SerializerSession session, FileSpaceStatus value) { + * <jk>switch</jk> (value) { + * <jk>case</jk> <jsf>OK</jsf>: <jk>return</jk> <jsm>img</jsm>().src(URI.<jsm>create</jsm>(<js>"servlet:/htdocs/ok.png"</js>)); + * <jk>case</jk> <jsf>WARNING</jsf>: <jk>return</jk> <jsm>img</jsm>().src(URI.<jsm>create</jsm>(<js>"servlet:/htdocs/warning.png"</js>)); + * <jk>default</jk>: <jk>return</jk> <jsm>img</jsm>().src(URI.<jsm>create</jsm>(<js>"servlet:/htdocs/severe.png"</js>)); + * } + * } + * } + * </p> + * @param <T> The bean property type. + */ +public class HtmlRender<T> { + + /** + * Returns the CSS style of the element containing the bean property value. + * + * @param session The current serializer session. + * Can be used to retrieve properties and session-level information. + * @param value The bean property value. + * @return The CSS style string, or <jk>null</jk> if no style should be added. + */ + public String getStyle(SerializerSession session, T value) { + return null; + } + + /** + * Returns the delegate value for the specified bean property value. + * <p> + * The default implementation simply returns the same value. + * A typical use is to return an HTML element using one of the HTML5 DOM beans. + * + * @param session The current serializer session. + * Can be used to retrieve properties and session-level information. + * @param value The bean property value. + * @return The new bean property value. + */ + public Object getContent(SerializerSession session, T value) { + return value; + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e1a50566/juneau-core/src/main/java/org/apache/juneau/html/HtmlSchemaDocSerializer.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/html/HtmlSchemaDocSerializer.java b/juneau-core/src/main/java/org/apache/juneau/html/HtmlSchemaDocSerializer.java index bc17387..fabcdaa 100644 --- a/juneau-core/src/main/java/org/apache/juneau/html/HtmlSchemaDocSerializer.java +++ b/juneau-core/src/main/java/org/apache/juneau/html/HtmlSchemaDocSerializer.java @@ -74,8 +74,8 @@ public final class HtmlSchemaDocSerializer extends HtmlDocSerializer { } @Override /* Serializer */ - public HtmlDocSerializerSession createSession(Object output, ObjectMap op, Method javaMethod, Locale locale, TimeZone timeZone, MediaType mediaType) { - return new HtmlDocSerializerSession(ctx, op, output, javaMethod, locale, timeZone, mediaType); + public HtmlDocSerializerSession createSession(Object output, ObjectMap op, Method javaMethod, Locale locale, TimeZone timeZone, MediaType mediaType, UriContext uriContext) { + return new HtmlDocSerializerSession(ctx, op, output, javaMethod, locale, timeZone, mediaType, uriContext); } @Override /* ISchemaSerializer */ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e1a50566/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializer.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializer.java b/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializer.java index c3d1088..91b8f16 100644 --- a/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializer.java +++ b/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializer.java @@ -400,6 +400,7 @@ public class HtmlSerializer extends XmlSerializer { out.eTag(i, "table").nl(); } + @SuppressWarnings({ "rawtypes", "unchecked" }) private void serializeBeanMap(HtmlSerializerSession session, HtmlWriter out, BeanMap<?> m, ClassMeta<?> eType, BeanPropertyMeta ppMeta) throws Exception { int i = session.getIndent(); @@ -420,6 +421,9 @@ public class HtmlSerializer extends XmlSerializer { for (BeanPropertyValue p : m.getValues(session.isTrimNulls())) { BeanPropertyMeta pMeta = p.getMeta(); ClassMeta<?> cMeta = p.getClassMeta(); + HtmlBeanPropertyMeta hbpMeta = pMeta.getExtendedMeta(HtmlBeanPropertyMeta.class); + String link = hbpMeta.getLink(); + HtmlRender render = hbpMeta.getRender(); String key = p.getName(); Object value = p.getValue(); @@ -432,11 +436,20 @@ public class HtmlSerializer extends XmlSerializer { out.sTag(i+1, "tr").nl(); out.sTag(i+2, "td").text(key).eTag("td").nl(); - out.sTag(i+2, "td"); + out.oTag(i+2, "td"); + String style = render.getStyle(session, value); + if (style != null) + out.attr("style", style); + out.cTag(); + try { - ContentResult cr = serializeAnything(session, out, value, cMeta, key, 2, pMeta, false); + if (link != null) + out.oTag(i+3, "a").attrUri("href", m.resolveVars(link)).cTag(); + ContentResult cr = serializeAnything(session, out, render.getContent(session, value), cMeta, key, 2, pMeta, false); if (cr == CR_NORMAL) out.i(i+2); + if (link != null) + out.eTag("a"); } catch (SerializeException e) { throw e; } catch (Error e) { @@ -533,10 +546,23 @@ public class HtmlSerializer extends XmlSerializer { for (Object k : th) { BeanMapEntry p = m2.getProperty(session.toString(k)); BeanPropertyMeta pMeta = p.getMeta(); - out.sTag(i+2, "td"); - ContentResult cr = serializeAnything(session, out, p.getValue(), pMeta.getClassMeta(), p.getKey().toString(), 2, pMeta, false); + HtmlBeanPropertyMeta hpMeta = pMeta.getExtendedMeta(HtmlBeanPropertyMeta.class); + String link = hpMeta.getLink(); + HtmlRender render = hpMeta.getRender(); + + Object value = p.getValue(); + out.oTag(i+2, "td"); + String style = render.getStyle(session, value); + if (style != null) + out.attr("style", style); + out.cTag(); + if (link != null) + out.oTag(i+3, "a").attrUri("href", m2.resolveVars(link)).cTag(); + ContentResult cr = serializeAnything(session, out, render.getContent(session, value), pMeta.getClassMeta(), p.getKey().toString(), 2, pMeta, false); if (cr == CR_NORMAL) out.i(i+2); + if (link != null) + out.eTag("a"); out.eTag("td").nl(); } } @@ -689,8 +715,8 @@ public class HtmlSerializer extends XmlSerializer { //-------------------------------------------------------------------------------- @Override /* Serializer */ - public HtmlSerializerSession createSession(Object output, ObjectMap op, Method javaMethod, Locale locale, TimeZone timeZone, MediaType mediaType) { - return new HtmlSerializerSession(ctx, op, output, javaMethod, locale, timeZone, mediaType); + public HtmlSerializerSession createSession(Object output, ObjectMap op, Method javaMethod, Locale locale, TimeZone timeZone, MediaType mediaType, UriContext uriContext) { + return new HtmlSerializerSession(ctx, op, output, javaMethod, locale, timeZone, mediaType, uriContext); } @Override /* Serializer */ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e1a50566/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java b/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java index 3faf271..48b16dd 100644 --- a/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java +++ b/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java @@ -52,19 +52,21 @@ public class HtmlSerializerSession extends XmlSerializerSession { * Create a new session using properties specified in the context. * * @param ctx The context creating this session object. - * The context contains all the configuration settings for this object. + * The context contains all the configuration settings for this object. * @param output The output object. See {@link JsonSerializerSession#getWriter()} for valid class types. * @param op The override properties. - * These override any context properties defined in the context. + * These override any context properties defined in the context. * @param javaMethod The java method that called this serializer, usually the method in a REST servlet. * @param locale The session locale. - * If <jk>null</jk>, then the locale defined on the context is used. + * If <jk>null</jk>, then the locale defined on the context is used. * @param timeZone The session timezone. - * If <jk>null</jk>, then the timezone defined on the context is used. + * If <jk>null</jk>, then the timezone defined on the context is used. * @param mediaType The session media type (e.g. <js>"application/json"</js>). + * @param uriContext The URI context. + * Identifies the current request URI used for resolution of URIs to absolute or root-relative form. */ - protected HtmlSerializerSession(HtmlSerializerContext ctx, ObjectMap op, Object output, Method javaMethod, Locale locale, TimeZone timeZone, MediaType mediaType) { - super(ctx, op, output, javaMethod, locale, timeZone, mediaType); + protected HtmlSerializerSession(HtmlSerializerContext ctx, ObjectMap op, Object output, Method javaMethod, Locale locale, TimeZone timeZone, MediaType mediaType, UriContext uriContext) { + super(ctx, op, output, javaMethod, locale, timeZone, mediaType, uriContext); String labelParameter; if (op == null || op.isEmpty()) { anchorText = Enum.valueOf(AnchorText.class, ctx.uriAnchorText); @@ -91,7 +93,7 @@ public class HtmlSerializerSession extends XmlSerializerSession { Object output = getOutput(); if (output instanceof HtmlWriter) return (HtmlWriter)output; - return new HtmlWriter(super.getWriter(), isUseWhitespace(), isTrimStrings(), getQuoteChar(), getRelativeUriBase(), getAbsolutePathUriBase()); + return new HtmlWriter(super.getWriter(), isUseWhitespace(), isTrimStrings(), getQuoteChar(), getRelativeUriBase(), getAbsolutePathUriBase(), getUriContext()); } /** http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e1a50566/juneau-core/src/main/java/org/apache/juneau/html/HtmlWriter.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/html/HtmlWriter.java b/juneau-core/src/main/java/org/apache/juneau/html/HtmlWriter.java index 95b6092..30114c3 100644 --- a/juneau-core/src/main/java/org/apache/juneau/html/HtmlWriter.java +++ b/juneau-core/src/main/java/org/apache/juneau/html/HtmlWriter.java @@ -14,6 +14,7 @@ package org.apache.juneau.html; import java.io.*; +import org.apache.juneau.*; import org.apache.juneau.internal.*; import org.apache.juneau.xml.*; @@ -31,9 +32,11 @@ public class HtmlWriter extends XmlWriter { * @param quoteChar The quote character to use (i.e. <js>'\''</js> or <js>'"'</js>) * @param uriContext The web application context path (e.g. "/contextRoot"). * @param uriAuthority The web application URI authority (e.g. "http://hostname:9080") + * @param uriContext2 The URI context. + * Identifies the current request URI used for resolution of URIs to absolute or root-relative form. */ - public HtmlWriter(Writer out, boolean useWhitespace, boolean trimStrings, char quoteChar, String uriContext, String uriAuthority) { - super(out, useWhitespace, trimStrings, quoteChar, uriContext, uriAuthority, false, null); + public HtmlWriter(Writer out, boolean useWhitespace, boolean trimStrings, char quoteChar, String uriContext, String uriAuthority, UriContext uriContext2) { + super(out, useWhitespace, trimStrings, quoteChar, uriContext, uriAuthority, uriContext2, false, null); } /** http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e1a50566/juneau-core/src/main/java/org/apache/juneau/html/SimpleHtmlWriter.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/html/SimpleHtmlWriter.java b/juneau-core/src/main/java/org/apache/juneau/html/SimpleHtmlWriter.java index 2ae07f8..b0b6cc3 100644 --- a/juneau-core/src/main/java/org/apache/juneau/html/SimpleHtmlWriter.java +++ b/juneau-core/src/main/java/org/apache/juneau/html/SimpleHtmlWriter.java @@ -28,7 +28,7 @@ public class SimpleHtmlWriter extends HtmlWriter { * Constructor. */ public SimpleHtmlWriter() { - super(new StringWriter(), true, false, '\'', null, null); + super(new StringWriter(), true, false, '\'', null, null, null); } @Override /* Object */ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e1a50566/juneau-core/src/main/java/org/apache/juneau/html/annotation/Html.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/html/annotation/Html.java b/juneau-core/src/main/java/org/apache/juneau/html/annotation/Html.java index c63cd2e..dcfc53c 100644 --- a/juneau-core/src/main/java/org/apache/juneau/html/annotation/Html.java +++ b/juneau-core/src/main/java/org/apache/juneau/html/annotation/Html.java @@ -53,4 +53,39 @@ public @interface Html { * Default is <jk>false</jk>. */ boolean noTableHeaders() default false; + + /** + * Associates an {@link HtmlRender} with a bean property for custom HTML rendering of the property. + * <p> + * This annotation applies to bean properties only. + */ + @SuppressWarnings("rawtypes") + Class<? extends HtmlRender> render() default HtmlRender.class; + + /** + * Adds a hyperlink to a bean property when rendered as HTML. + * <p> + * The text can contain any bean property values resolved through variables of the form <js>"{property-name}"</js>. + * <p> + * The URLs can be any of the following forms: + * <ul> + * <li>Absolute - e.g. <js>"http://host:123/myContext/myServlet/myPath"</js> + * <li>Context-root-relative - e.g. <js>"/myContext/myServlet/myPath"</js> + * <li>Context-relative - e.g. <js>"context:/myServlet/myPath"</js> + * <li>Servlet-relative - e.g. <js>"servlet:/myPath"</js> + * <li>Path-info-relative - e.g. <js>"myPath"</js> + * </ul> + * + * <h6 class='figure'>Example:</h6> + * <p class='bcode'> + * <jk>public class</jk> FileSpace { + * + * <ja>@Html</ja>(link=<js>"servlet:/drive/{drive}"</js>) + * <jk>public</jk> String getDrive() { + * ...; + * } + * } + * </p> + */ + String link() default ""; } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e1a50566/juneau-core/src/main/java/org/apache/juneau/html/doc-files/HtmlRender_1.png ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/html/doc-files/HtmlRender_1.png b/juneau-core/src/main/java/org/apache/juneau/html/doc-files/HtmlRender_1.png new file mode 100644 index 0000000..f070aea Binary files /dev/null and b/juneau-core/src/main/java/org/apache/juneau/html/doc-files/HtmlRender_1.png differ
