Clean up how HtmlDoc properties are defined. Project: http://git-wip-us.apache.org/repos/asf/incubator-juneau/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-juneau/commit/9c746e64 Tree: http://git-wip-us.apache.org/repos/asf/incubator-juneau/tree/9c746e64 Diff: http://git-wip-us.apache.org/repos/asf/incubator-juneau/diff/9c746e64
Branch: refs/heads/master Commit: 9c746e647280eeb49cd78b5956eb5c1250cf71f3 Parents: d767cb6 Author: JamesBognar <[email protected]> Authored: Mon Oct 9 18:57:45 2017 -0400 Committer: JamesBognar <[email protected]> Committed: Mon Oct 9 18:57:45 2017 -0400 ---------------------------------------------------------------------- .../juneau/svl/ResolvingObjectMapTest.java | 121 +++++ .../main/java/org/apache/juneau/ObjectMap.java | 19 +- .../juneau/html/HtmlDocSerializerContext.java | 61 +-- .../juneau/html/HtmlDocSerializerSession.java | 20 +- .../juneau/html/HtmlDocTemplateBasic.java | 61 ++- .../juneau/serializer/SerializerWriter.java | 17 + .../apache/juneau/svl/ResolvingObjectMap.java | 119 +++++ .../apache/juneau/rest/test/HtmlDocTest.java | 20 +- .../apache/juneau/rest/test/PropertiesTest.java | 10 - .../java/org/apache/juneau/rest/CallMethod.java | 63 +-- .../org/apache/juneau/rest/HtmlDocBuilder.java | 151 +++++- .../org/apache/juneau/rest/HtmlDocConfig.java | 456 ------------------- .../org/apache/juneau/rest/HtmlDocContext.java | 171 ------- .../java/org/apache/juneau/rest/RestConfig.java | 34 +- .../org/apache/juneau/rest/RestContext.java | 29 +- .../org/apache/juneau/rest/RestRequest.java | 123 +---- .../apache/juneau/rest/annotation/HtmlDoc.java | 32 +- .../org/apache/juneau/rest/vars/UrlVar.java | 1 - .../org/apache/juneau/rest/vars/WidgetVar.java | 18 +- .../org/apache/juneau/rest/widget/Widget.java | 2 +- 20 files changed, 573 insertions(+), 955 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/9c746e64/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/svl/ResolvingObjectMapTest.java ---------------------------------------------------------------------- diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/svl/ResolvingObjectMapTest.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/svl/ResolvingObjectMapTest.java new file mode 100644 index 0000000..17a1dee --- /dev/null +++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/svl/ResolvingObjectMapTest.java @@ -0,0 +1,121 @@ +// *************************************************************************************************************************** +// * 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.svl; + +import static org.junit.Assert.*; +import static org.apache.juneau.TestUtils.*; + +import org.apache.juneau.*; +import org.apache.juneau.utils.*; +import org.junit.*; + +public class ResolvingObjectMapTest { + + //==================================================================================================== + // test - Basic tests + //==================================================================================================== + @Test + public void testBasic() throws Exception { + VarResolver vr = new VarResolverBuilder().defaultVars().vars(XVar.class).build(); + ObjectMap m = new ResolvingObjectMap(vr.createSession()); + + m.put("foo", "$X{a}"); + assertEquals(m.get("foo"), "1"); + + m.put("foo", new String[]{"$X{a}"}); + assertObjectEquals("['1']", m.get("foo")); + + m.put("foo", new AList<String>().append("$X{a}")); + assertObjectEquals("['1']", m.get("foo")); + + m.put("foo", new AMap<String,String>().append("k1","$X{a}")); + assertObjectEquals("{k1:'1'}", m.get("foo")); + } + + public static class XVar extends MapVar { + public XVar() { + super("X", new ObjectMap().append("a", 1).append("b", 2).append("c", 3)); + } + } + + //==================================================================================================== + // testNulls + //==================================================================================================== + @Test + public void testNulls() throws Exception { + VarResolver vr = new VarResolverBuilder().defaultVars().vars(XVar.class).build(); + ObjectMap m = new ResolvingObjectMap(vr.createSession()); + + m.put("foo", null); + assertNull(m.get("foo")); + + m.put("foo", new String[]{null}); + assertObjectEquals("[null]", m.get("foo")); + + m.put("foo", new AList<String>().append(null)); + assertObjectEquals("[null]", m.get("foo")); + + m.put("foo", new AMap<String,String>().append("k1",null)); + assertObjectEquals("{k1:null}", m.get("foo")); + } + + //==================================================================================================== + // testNonStrings + //==================================================================================================== + @Test + public void testNonStrings() throws Exception { + VarResolver vr = new VarResolverBuilder().defaultVars().vars(XVar.class).build(); + ObjectMap m = new ResolvingObjectMap(vr.createSession()); + + m.put("foo", FooEnum.ONE); + assertObjectEquals("'ONE'", m.get("foo")); + + m.put("foo", new Object[]{FooEnum.ONE}); + assertObjectEquals("['ONE']", m.get("foo")); + + m.put("foo", new AList<FooEnum>().append(FooEnum.ONE)); + assertObjectEquals("['ONE']", m.get("foo")); + + m.put("foo", new AMap<FooEnum,FooEnum>().append(FooEnum.ONE,FooEnum.ONE)); + assertObjectEquals("{ONE:'ONE'}", m.get("foo")); + } + + public static enum FooEnum { + ONE + } + + //==================================================================================================== + // testInner - Test inner maps + //==================================================================================================== + @Test + public void testInner() throws Exception { + VarResolver vr = new VarResolverBuilder().defaultVars().vars(XVar.class).build(); + ObjectMap m = new ResolvingObjectMap(vr.createSession()); + ObjectMap m2 = new ObjectMap(); + ObjectMap m3 = new ObjectMap(); + m.setInner(m2); + m2.setInner(m3); + + m3.put("foo", "$X{a}"); + assertEquals(m.get("foo"), "1"); + + m3.put("foo", new String[]{"$X{a}"}); + assertObjectEquals("['1']", m.get("foo")); + + m3.put("foo", new AList<String>().append("$X{a}")); + assertObjectEquals("['1']", m.get("foo")); + + m3.put("foo", new AMap<String,String>().append("k1","$X{a}")); + assertObjectEquals("{k1:'1'}", m.get("foo")); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/9c746e64/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ObjectMap.java ---------------------------------------------------------------------- diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ObjectMap.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ObjectMap.java index e3ab9bc..7eef2dd 100644 --- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ObjectMap.java +++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ObjectMap.java @@ -692,25 +692,16 @@ public class ObjectMap extends LinkedHashMap<String,Object> { } /** - * Specialized method that calls {@link #getString(String)} and splits the results as a simple comma-delimited list. + * Returns the specified entry value converted to a {@link String}. * * <p> - * If the value is already a collection, the individual entries are converted to strings using {@link #toString()}. + * Shortcut for <code>get(key, String[].<jk>class</jk>)</code>. * - * @param key the key. - * @return - * A list of tokens, trimmed of whitespace. - * An empty list if entry not found. - * Never <jk>null</jk>. + * @param key The key. + * @return The converted value, or <jk>null</jk> if the map contains no mapping for this key. */ public String[] getStringArray(String key) { - Object s = get(key, Object.class); - if (s == null) - return new String[0]; - if (s instanceof Collection) - return ArrayUtils.toStringArray((Collection<?>)s); - String[] r = split(StringUtils.toString(s)); - return r; + return getStringArray(key, null); } /** http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/9c746e64/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlDocSerializerContext.java ---------------------------------------------------------------------- diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlDocSerializerContext.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlDocSerializerContext.java index 6296ff3..ef2b964 100644 --- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlDocSerializerContext.java +++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlDocSerializerContext.java @@ -85,7 +85,7 @@ public final class HtmlDocSerializerContext extends HtmlSerializerContext { * * <ul> * <li><b>Name:</b> <js>"HtmlDocSerializer.header"</js> - * <li><b>Data type:</b> <code>String</code> + * <li><b>Data type:</b> <code>String[]</code> * <li><b>Default:</b> <jk>null</jk> * <li><b>Session-overridable:</b> <jk>true</jk> * </ul> @@ -104,11 +104,8 @@ public final class HtmlDocSerializerContext extends HtmlSerializerContext { * ) * ) * </p> - * - * <p> - * A value of <js>"NONE"</js> can be used to represent no value to differentiate it from an empty string. */ - public static final String HTMLDOC_header = PREFIX + "header"; + public static final String HTMLDOC_header = PREFIX + "header.list"; /** * <b>Configuration property:</b> Page navigation links. @@ -185,7 +182,7 @@ public final class HtmlDocSerializerContext extends HtmlSerializerContext { * * <ul> * <li><b>Name:</b> <js>"HtmlDocSerializer.nav"</js> - * <li><b>Data type:</b> <code>String</code> + * <li><b>Data type:</b> <code>String[]</code> * <li><b>Default:</b> <jk>null</jk> * <li><b>Session-overridable:</b> <jk>true</jk> * </ul> @@ -207,18 +204,15 @@ public final class HtmlDocSerializerContext extends HtmlSerializerContext { * * <p> * When this property is specified, the {@link #HTMLDOC_navlinks} property is ignored. - * - * <p> - * A value of <js>"NONE"</js> can be used to represent no value to differentiate it from an empty string. */ - public static final String HTMLDOC_nav = PREFIX + "nav"; + public static final String HTMLDOC_nav = PREFIX + "nav.list"; /** * <b>Configuration property:</b> Aside section contents. * * <ul> * <li><b>Name:</b> <js>"HtmlDocSerializer.aside"</js> - * <li><b>Data type:</b> <code>String</code> + * <li><b>Data type:</b> <code>String[]</code> * <li><b>Default:</b> <jk>null</jk> * <li><b>Session-overridable:</b> <jk>true</jk> * </ul> @@ -245,18 +239,15 @@ public final class HtmlDocSerializerContext extends HtmlSerializerContext { * ) * ) * </p> - * - * <p> - * A value of <js>"NONE"</js> can be used to represent no value to differentiate it from an empty string. */ - public static final String HTMLDOC_aside = PREFIX + "aside"; + public static final String HTMLDOC_aside = PREFIX + "aside.list"; /** * <b>Configuration property:</b> Footer section contents. * * <ul> * <li><b>Name:</b> <js>"HtmlDocSerializer.footer"</js> - * <li><b>Data type:</b> <code>String</code> + * <li><b>Data type:</b> <code>String[]</code> * <li><b>Default:</b> <jk>null</jk> * <li><b>Session-overridable:</b> <jk>true</jk> * </ul> @@ -277,11 +268,8 @@ public final class HtmlDocSerializerContext extends HtmlSerializerContext { * ) * ) * </p> - * - * <p> - * A value of <js>"NONE"</js> can be used to represent no value to differentiate it from an empty string. */ - public static final String HTMLDOC_footer = PREFIX + "footer"; + public static final String HTMLDOC_footer = PREFIX + "footer.list"; /** * <b>Configuration property:</b> No-results message. @@ -330,8 +318,8 @@ public final class HtmlDocSerializerContext extends HtmlSerializerContext { * * <ul> * <li><b>Name:</b> <js>"HtmlDocSerializer.stylesheet"</js> - * <li><b>Data type:</b> <code>List<String></code> - * <li><b>Default:</b> empty list + * <li><b>Data type:</b> <code>String[]</code> + * <li><b>Default:</b> empty array * <li><b>Session-overridable:</b> <jk>true</jk> * </ul> * @@ -340,11 +328,8 @@ public final class HtmlDocSerializerContext extends HtmlSerializerContext { * * <p> * Note that this stylesheet is controlled by the <code><ja>@RestResource</ja>.stylesheet()</code> annotation. - * - * <p> - * A value of <js>"NONE"</js> can be used to represent no value to differentiate it from an empty string. */ - public static final String HTMLDOC_stylesheet = PREFIX + "stylesheet"; + public static final String HTMLDOC_stylesheet = PREFIX + "stylesheet.list"; /** * <b>Configuration property:</b> Add to the {@link #HTMLDOC_stylesheet} property. @@ -356,8 +341,8 @@ public final class HtmlDocSerializerContext extends HtmlSerializerContext { * * <ul> * <li><b>Name:</b> <js>"HtmlDocSerializer.style.list"</js> - * <li><b>Data type:</b> <code>List<String></code> - * <li><b>Default:</b> empty list + * <li><b>Data type:</b> <code>String[]</code> + * <li><b>Default:</b> empty array * <li><b>Session-overridable:</b> <jk>true</jk> * </ul> * @@ -399,8 +384,8 @@ public final class HtmlDocSerializerContext extends HtmlSerializerContext { * * <ul> * <li><b>Name:</b> <js>"HtmlDocSerializer.script.list"</js> - * <li><b>Data type:</b> <code>List<String></code> - * <li><b>Default:</b> empty list + * <li><b>Data type:</b> <code>String[]</code> + * <li><b>Default:</b> empty array * <li><b>Session-overridable:</b> <jk>true</jk> * </ul> * @@ -441,8 +426,8 @@ public final class HtmlDocSerializerContext extends HtmlSerializerContext { * * <ul> * <li><b>Name:</b> <js>"HtmlDocSerializer.head.list"</js> - * <li><b>Data type:</b> <code>List<String></code> - * <li><b>Default:</b> empty list + * <li><b>Data type:</b> <code>String[]</code> + * <li><b>Default:</b> empty array * <li><b>Session-overridable:</b> <jk>true</jk> * </ul> * @@ -507,8 +492,8 @@ public final class HtmlDocSerializerContext extends HtmlSerializerContext { public static final String HTMLDOC_template = PREFIX + "template"; - final String[] style, stylesheet, script, navlinks, head; - final String header, nav, aside, footer, noResultsMessage; + final String[] style, stylesheet, script, navlinks, head, header, nav, aside, footer; + final String noResultsMessage; final boolean nowrap; final HtmlDocTemplate template; @@ -526,10 +511,10 @@ public final class HtmlDocSerializerContext extends HtmlSerializerContext { stylesheet = ps.getProperty(HTMLDOC_stylesheet, String[].class, new String[0]); script = ps.getProperty(HTMLDOC_script, String[].class, new String[0]); head = ps.getProperty(HTMLDOC_head, String[].class, new String[0]); - header = ps.getProperty(HTMLDOC_header, String.class, null); - nav = ps.getProperty(HTMLDOC_nav, String.class, null); - aside = ps.getProperty(HTMLDOC_aside, String.class, null); - footer = ps.getProperty(HTMLDOC_footer, String.class, null); + header = ps.getProperty(HTMLDOC_header, String[].class, new String[0]); + nav = ps.getProperty(HTMLDOC_nav, String[].class, new String[0]); + aside = ps.getProperty(HTMLDOC_aside, String[].class, new String[0]); + footer = ps.getProperty(HTMLDOC_footer, String[].class, new String[0]); nowrap = ps.getProperty(HTMLDOC_nowrap, boolean.class, false); navlinks = ps.getProperty(HTMLDOC_navlinks, String[].class, new String[0]); noResultsMessage = ps.getProperty(HTMLDOC_noResultsMessage, String.class, "<p>no results</p>"); http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/9c746e64/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlDocSerializerSession.java ---------------------------------------------------------------------- diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlDocSerializerSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlDocSerializerSession.java index bd59d78..007c260 100644 --- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlDocSerializerSession.java +++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlDocSerializerSession.java @@ -29,8 +29,8 @@ import org.apache.juneau.serializer.*; */ public class HtmlDocSerializerSession extends HtmlStrippedDocSerializerSession { - private final String header, nav, aside, footer, noResultsMessage; - private final String[] style, stylesheet, script, navlinks, head; + private final String noResultsMessage; + private final String[] style, stylesheet, script, navlinks, head, header, nav, aside, footer; private final boolean nowrap; private final HtmlDocTemplate template; @@ -47,10 +47,10 @@ public class HtmlDocSerializerSession extends HtmlStrippedDocSerializerSession { super(ctx, args); ObjectMap p = getProperties(); - header = p.getString(HTMLDOC_header, ctx.nav); - nav = p.getString(HTMLDOC_nav, ctx.nav); - aside = p.getString(HTMLDOC_aside, ctx.aside); - footer = p.getString(HTMLDOC_footer, ctx.footer); + header = p.getStringArray(HTMLDOC_header, ctx.nav); + nav = p.getStringArray(HTMLDOC_nav, ctx.nav); + aside = p.getStringArray(HTMLDOC_aside, ctx.aside); + footer = p.getStringArray(HTMLDOC_footer, ctx.footer); navlinks = p.getStringArray(HTMLDOC_navlinks, ctx.navlinks); style = p.getStringArray(HTMLDOC_style, ctx.style); stylesheet = p.getStringArray(HTMLDOC_stylesheet, ctx.stylesheet); @@ -145,7 +145,7 @@ public class HtmlDocSerializerSession extends HtmlStrippedDocSerializerSession { * <jk>null</jk> if not specified. * Never an empty string. */ - public final String getHeader() { + public final String[] getHeader() { return header; } @@ -180,7 +180,7 @@ public class HtmlDocSerializerSession extends HtmlStrippedDocSerializerSession { * <jk>null</jk> if not specified. * Never an empty string. */ - public final String getNav() { + public final String[] getNav() { return nav; } @@ -192,7 +192,7 @@ public class HtmlDocSerializerSession extends HtmlStrippedDocSerializerSession { * <jk>null</jk> if not specified. * Never an empty string. */ - public final String getAside() { + public final String[] getAside() { return aside; } @@ -204,7 +204,7 @@ public class HtmlDocSerializerSession extends HtmlStrippedDocSerializerSession { * <jk>null</jk> if not specified. * Never an empty string. */ - public final String getFooter() { + public final String[] getFooter() { return footer; } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/9c746e64/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlDocTemplateBasic.java ---------------------------------------------------------------------- diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlDocTemplateBasic.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlDocTemplateBasic.java index ffaabb5..86044b2 100644 --- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlDocTemplateBasic.java +++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlDocTemplateBasic.java @@ -24,10 +24,11 @@ public class HtmlDocTemplateBasic implements HtmlDocTemplate { @Override /* HtmlDocTemplate */ public void head(HtmlDocSerializerSession session, HtmlWriter w, Object o) throws Exception { - - for (String h : session.getHead()) - w.appendln(2, h); - + + String[] head = session.getHead(); + for (int i = 0; i < head.length; i++) + w.sIf(i > 0).appendln(2, head[i]); + if (hasStyle(session)) { w.sTag(2, "style").nl(2); style(session, w, o); @@ -44,24 +45,22 @@ public class HtmlDocTemplateBasic implements HtmlDocTemplate { public void style(HtmlDocSerializerSession session, HtmlWriter w, Object o) throws Exception { String[] stylesheet = session.getStylesheet(); - if (! ArrayUtils.contains("NONE", stylesheet)) - for (String ss : stylesheet) - w.append(3, "@import ").q().append(session.resolveUri(ss)).q().appendln("; "); + for (int i = 0; i < stylesheet.length; i++) + w.sIf(i > 0).append(3, "@import ").q().append(session.resolveUri(stylesheet[i])).q().appendln(";"); if (session.isNoWrap()) w.appendln(3, "div.data * {white-space:nowrap;} "); - if (session.getStyle() != null) - for (String style : session.getStyle()) - w.append(3, style).appendln(" "); + String[] style = session.getStyle(); + for (int i = 0; i < style.length; i++) + w.sIf(i > 0 || stylesheet.length > 0).appendln(3, style[i]); } @Override /* HtmlDocTemplate */ public void script(HtmlDocSerializerSession session, HtmlWriter w, Object o) throws Exception { - - if (session.getScript() != null) - for (String script : session.getScript()) - w.append(3, script); + String[] script = session.getScript(); + for (int i = 0; i < script.length; i++) + w.sIf(i > 0).appendln(3, script[i]); } @Override /* HtmlDocTemplate */ @@ -103,18 +102,18 @@ public class HtmlDocTemplateBasic implements HtmlDocTemplate { @Override /* HtmlDocTemplate */ public void header(HtmlDocSerializerSession session, HtmlWriter w, Object o) throws Exception { // Write the title of the page. - String header = session.getHeader(); - if (exists(header)) - w.append(3, header).nl(3); + String[] header = session.getHeader(); + for (int i = 0; i < header.length; i++) + w.sIf(i > 0).appendln(3, header[i]); } @Override /* HtmlDocTemplate */ public void nav(HtmlDocSerializerSession session, HtmlWriter w, Object o) throws Exception { - String nav = session.getNav(); - if (nav != null) { - if (exists(nav)) - w.append(3, nav).nl(3); + String[] nav = session.getNav(); + if (nav.length > 0) { + for (int i = 0; i < nav.length; i++) + w.sIf(i > 0).appendln(3, nav[i]); } else { String[] links = session.getNavLinks(); if (links.length > 0) { @@ -142,9 +141,9 @@ public class HtmlDocTemplateBasic implements HtmlDocTemplate { @Override /* HtmlDocTemplate */ public void aside(HtmlDocSerializerSession session, HtmlWriter w, Object o) throws Exception { - String aside = session.getAside(); - if (exists(aside)) - w.append(4, aside); + String[] aside = session.getAside(); + for (int i = 0; i < aside.length; i++) + w.sIf(i > 0).appendln(4, aside[i]); } @Override /* HtmlDocTemplate */ @@ -172,9 +171,9 @@ public class HtmlDocTemplateBasic implements HtmlDocTemplate { @Override /* HtmlDocTemplate */ public void footer(HtmlDocSerializerSession session, HtmlWriter w, Object o) throws Exception { - String footer = session.getFooter(); - if (exists(footer)) - w.append(3, footer).nl(3); + String[] footer = session.getFooter(); + for (int i = 0; i < footer.length; i++) + w.sIf(i > 0).appendln(3, footer[i]); } @Override /* HtmlDocTemplate */ @@ -189,22 +188,22 @@ public class HtmlDocTemplateBasic implements HtmlDocTemplate { @Override /* HtmlDocTemplate */ public boolean hasHeader(HtmlDocSerializerSession session) { - return exists(session.getHeader()); + return session.getHeader().length > 0; } @Override /* HtmlDocTemplate */ public boolean hasNav(HtmlDocSerializerSession session) { - return exists(session.getNav()) || session.getNavLinks().length > 0; + return session.getNav().length > 0 || session.getNavLinks().length > 0; } @Override /* HtmlDocTemplate */ public boolean hasAside(HtmlDocSerializerSession session) { - return exists(session.getAside()); + return session.getAside().length > 0; } @Override /* HtmlDocTemplate */ public boolean hasFooter(HtmlDocSerializerSession session) { - return exists(session.getFooter()); + return session.getFooter().length > 0; } private static boolean exists(String s) { http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/9c746e64/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerWriter.java ---------------------------------------------------------------------- diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerWriter.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerWriter.java index 41e76d7..8595fe8 100644 --- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerWriter.java +++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerWriter.java @@ -277,6 +277,23 @@ public class SerializerWriter extends Writer { } /** + * Writes a space if the boolean expression is <jk>true</jk> and {@code useWhitespace} is false. + * + * <p> + * Intended for cases in XML where text should be separated by either a space or newline. + * This ensures the text is separated by a space if whitespace is disabled. + * + * @param b The boolean flag. + * @return This object (for method chaining). + * @throws IOException If a problem occurred trying to write to the writer. + */ + public SerializerWriter sIf(boolean b) throws IOException { + if (b && ! useWhitespace) + out.write(' '); + return this; + } + + /** * Writes a newline to the writer if the {@code useWhitespace} setting is enabled and the boolean flag is true. * * @param b The boolean flag. http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/9c746e64/juneau-core/juneau-svl/src/main/java/org/apache/juneau/svl/ResolvingObjectMap.java ---------------------------------------------------------------------- diff --git a/juneau-core/juneau-svl/src/main/java/org/apache/juneau/svl/ResolvingObjectMap.java b/juneau-core/juneau-svl/src/main/java/org/apache/juneau/svl/ResolvingObjectMap.java new file mode 100644 index 0000000..0890e52 --- /dev/null +++ b/juneau-core/juneau-svl/src/main/java/org/apache/juneau/svl/ResolvingObjectMap.java @@ -0,0 +1,119 @@ +// *************************************************************************************************************************** +// * 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.svl; + +import java.lang.reflect.*; +import java.util.*; + +import org.apache.juneau.*; + +/** + * Subclass of an {@link ObjectMap} that automatically resolves any SVL variables in values. + * + * <p> + * Resolves variables in the following values: + * <ul> + * <li>Values of type {@link CharSequence}. + * <li>Arrays containing values of type {@link CharSequence}. + * <li>Collections containing values of type {@link CharSequence}. + * <li>Maps containing values of type {@link CharSequence}. + * </ul> + * + * <p> + * All other data types are left as-is. + */ +@SuppressWarnings({"serial","unchecked","rawtypes"}) +public class ResolvingObjectMap extends ObjectMap { + + private final VarResolverSession varResolver; + + /** + * Constructor. + * + * @param varResolver The var resolver session to use for resolving SVL variables. + */ + public ResolvingObjectMap(VarResolverSession varResolver) { + super(); + this.varResolver = varResolver; + } + + @Override /* Map */ + public Object get(Object key) { + return resolve(super.get(key)); + } + + private Object resolve(Object o) { + if (o == null) + return null; + if (o instanceof CharSequence) + return varResolver.resolve(o.toString()); + if (o.getClass().isArray()) { + if (! containsVars(o)) + return o; + Object o2 = Array.newInstance(o.getClass().getComponentType(), Array.getLength(o)); + for (int i = 0; i < Array.getLength(o); i++) + Array.set(o2, i, resolve(Array.get(o, i))); + return o2; + } + if (o instanceof Collection) { + try { + Collection c = (Collection)o; + if (! containsVars(c)) + return o; + Collection c2 = c.getClass().newInstance(); + for (Object o2 : c) + c2.add(resolve(o2)); + return c2; + } catch (Exception e) { + return o; + } + } + if (o instanceof Map) { + try { + Map m = (Map)o; + if (! containsVars(m)) + return o; + Map m2 = m.getClass().newInstance(); + for (Map.Entry e : (Set<Map.Entry>)m.entrySet()) + m2.put(e.getKey(), resolve(e.getValue())); + return m2; + } catch (Exception e) { + return o; + } + } + return o; + } + + private static boolean containsVars(Object array) { + for (int i = 0; i < Array.getLength(array); i++) { + Object o = Array.get(array, i); + if (o instanceof CharSequence && o.toString().contains("$")) + return true; + } + return false; + } + + private static boolean containsVars(Collection c) { + for (Object o : c) + if (o instanceof CharSequence && o.toString().contains("$")) + return true; + return false; + } + + private static boolean containsVars(Map m) { + for (Object o : m.values()) + if (o instanceof CharSequence && o.toString().contains("$")) + return true; + return false; + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/9c746e64/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/HtmlDocTest.java ---------------------------------------------------------------------- diff --git a/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/HtmlDocTest.java b/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/HtmlDocTest.java index d3c337b..976b83b 100644 --- a/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/HtmlDocTest.java +++ b/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/HtmlDocTest.java @@ -64,7 +64,7 @@ public class HtmlDocTest extends RestTestcase { String r = get("/testHtmlDoc/test1"); assertEquals("header1a header1b", header(r)); assertEquals("script1a script1b", script(r)); - assertEquals("@import '/testHtmlDoc/stylesheet1'; style1a style1b ", style(r)); + assertEquals("@import '/testHtmlDoc/stylesheet1'; style1a style1b", style(r)); assertEquals("nav1a nav1b", nav(r)); assertEquals("aside1a aside1b", aside(r)); assertEquals("footer1a footer1b", footer(r)); @@ -89,7 +89,7 @@ public class HtmlDocTest extends RestTestcase { String r = get("/testHtmlDoc/test2"); assertEquals("header2a header2b", header(r)); assertEquals("script2a script2b", script(r)); - assertEquals("@import '/testHtmlDoc/stylesheet2'; style2a style2b ", style(r)); + assertEquals("@import '/testHtmlDoc/stylesheet2'; style2a style2b", style(r)); assertEquals("nav2a nav2b", nav(r)); assertEquals("aside2a aside2b", aside(r)); assertEquals("footer2a footer2b", footer(r)); @@ -113,7 +113,7 @@ public class HtmlDocTest extends RestTestcase { String r = get("/testHtmlDoc/test3"); assertEquals("header1a header1b header3a header3b", header(r)); assertEquals("script1a script1b script3a script3b", script(r)); - assertEquals("@import '/testHtmlDoc/stylesheet1'; style1a style1b style3a style3b ", style(r)); + assertEquals("@import '/testHtmlDoc/stylesheet1'; style1a style1b style3a style3b", style(r)); assertEquals("nav1a nav1b nav3a nav3b", nav(r)); assertEquals("aside1a aside1b aside3a aside3b", aside(r)); assertEquals("footer1a footer1b footer3a footer3b", footer(r)); @@ -137,7 +137,7 @@ public class HtmlDocTest extends RestTestcase { String r = get("/testHtmlDoc/test4"); assertEquals("header4a header1a header1b header4b", header(r)); assertEquals("script4a script1a script1b script4b", script(r)); - assertEquals("@import '/testHtmlDoc/stylesheet1'; style4a style1a style1b style4b ", style(r)); + assertEquals("@import '/testHtmlDoc/stylesheet1'; style4a style1a style1b style4b", style(r)); assertEquals("nav4a nav1a nav1b nav4b", nav(r)); assertEquals("aside4a aside1a aside1b aside4b", aside(r)); assertEquals("footer4a footer1a footer1b footer4b", footer(r)); @@ -161,7 +161,7 @@ public class HtmlDocTest extends RestTestcase { String r = get("/testHtmlDoc/test5"); assertEquals("header5a header5b header1a header1b", header(r)); assertEquals("script5a script5b script1a script1b", script(r)); - assertEquals("@import '/testHtmlDoc/stylesheet1'; style5a style5b style1a style1b ", style(r)); + assertEquals("@import '/testHtmlDoc/stylesheet1'; style5a style5b style1a style1b", style(r)); assertEquals("nav5a nav5b nav1a nav1b", nav(r)); assertEquals("aside5a aside5b aside1a aside1b", aside(r)); assertEquals("footer5a footer5b footer1a footer1b", footer(r)); @@ -186,7 +186,7 @@ public class HtmlDocTest extends RestTestcase { String r = get("/testHtmlDoc/testHtmlDoc2/test11"); assertEquals("header11a header11b header1a header1b", header(r)); assertEquals("script11a script11b", script(r)); - assertEquals("@import '/testHtmlDoc/testHtmlDoc2/stylesheet11'; style11a style11b ", style(r)); + assertEquals("@import '/testHtmlDoc/testHtmlDoc2/stylesheet11'; style11a style11b", style(r)); assertEquals("nav1a nav1b nav11a nav11b", nav(r)); assertEquals("aside1a aside1b aside11a aside11b", aside(r)); assertEquals("footer11a footer1a footer1b footer11b", footer(r)); @@ -211,7 +211,7 @@ public class HtmlDocTest extends RestTestcase { String r = get("/testHtmlDoc/testHtmlDoc2/test12"); assertEquals("header12a header12b", header(r)); assertEquals("script12a script12b", script(r)); - assertEquals("@import '/testHtmlDoc/testHtmlDoc2/stylesheet12'; style12a style12b ", style(r)); + assertEquals("@import '/testHtmlDoc/testHtmlDoc2/stylesheet12'; style12a style12b", style(r)); assertEquals("nav12a nav12b", nav(r)); assertEquals("aside12a aside12b", aside(r)); assertEquals("footer12a footer12b", footer(r)); @@ -235,7 +235,7 @@ public class HtmlDocTest extends RestTestcase { String r = get("/testHtmlDoc/testHtmlDoc2/test13"); assertEquals("header11a header11b header1a header1b header13a header13b", header(r)); assertEquals("script11a script11b script13a script13b", script(r)); - assertEquals("@import '/testHtmlDoc/testHtmlDoc2/stylesheet11'; style11a style11b style13a style13b ", style(r)); + assertEquals("@import '/testHtmlDoc/testHtmlDoc2/stylesheet11'; style11a style11b style13a style13b", style(r)); assertEquals("nav1a nav1b nav11a nav11b nav13a nav13b", nav(r)); assertEquals("aside1a aside1b aside11a aside11b aside13a aside13b", aside(r)); assertEquals("footer11a footer1a footer1b footer11b footer13a footer13b", footer(r)); @@ -259,7 +259,7 @@ public class HtmlDocTest extends RestTestcase { String r = get("/testHtmlDoc/testHtmlDoc2/test14"); assertEquals("header14a header11a header11b header1a header1b header14b", header(r)); assertEquals("script14a script11a script11b script14b", script(r)); - assertEquals("@import '/testHtmlDoc/testHtmlDoc2/stylesheet11'; style14a style11a style11b style14b ", style(r)); + assertEquals("@import '/testHtmlDoc/testHtmlDoc2/stylesheet11'; style14a style11a style11b style14b", style(r)); assertEquals("nav14a nav1a nav1b nav11a nav11b nav14b", nav(r)); assertEquals("aside14a aside1a aside1b aside11a aside11b aside14b", aside(r)); assertEquals("footer14a footer11a footer1a footer1b footer11b footer14b", footer(r)); @@ -283,7 +283,7 @@ public class HtmlDocTest extends RestTestcase { String r = get("/testHtmlDoc/testHtmlDoc2/test15"); assertEquals("header15a header15b header11a header11b header1a header1b", header(r)); assertEquals("script15a script15b script11a script11b", script(r)); - assertEquals("@import '/testHtmlDoc/testHtmlDoc2/stylesheet11'; style15a style15b style11a style11b ", style(r)); + assertEquals("@import '/testHtmlDoc/testHtmlDoc2/stylesheet11'; style15a style15b style11a style11b", style(r)); assertEquals("nav15a nav15b nav1a nav1b nav11a nav11b", nav(r)); assertEquals("aside15a aside15b aside1a aside1b aside11a aside11b", aside(r)); assertEquals("footer15a footer15b footer11a footer1a footer1b footer11b", footer(r)); http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/9c746e64/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/PropertiesTest.java ---------------------------------------------------------------------- diff --git a/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/PropertiesTest.java b/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/PropertiesTest.java index f52a564..96cbfb4 100644 --- a/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/PropertiesTest.java +++ b/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/PropertiesTest.java @@ -30,14 +30,4 @@ public class PropertiesTest extends RestTestcase { String r = client.doGet(URL + "/testPropertiesDefinedOnMethod").getResponseAsString(); assertTrue(r.matches("A1=a1,A2=c,B1=b1,B2=c,C=c,R1a=.*/testProperties/testPropertiesDefinedOnMethod,R1b=.*/testProperties,R2=bar,R3=baz,R4=a1,R5=c,R6=c")); } - - //==================================================================================================== - // Make sure attributes/parameters/headers are available through ctx.getProperties(). - //==================================================================================================== - @Test - public void testProperties() throws Exception { - RestClient client = TestMicroservice.DEFAULT_CLIENT; - String r = client.doGet(URL + "/testProperties/a1?P=p1").header("H", "h1").getResponseAsString(); - assertEquals("A=a1,P=p1,H=h1", r); - } } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/9c746e64/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/CallMethod.java ---------------------------------------------------------------------- diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/CallMethod.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/CallMethod.java index 55ec42c..7880746 100644 --- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/CallMethod.java +++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/CallMethod.java @@ -28,9 +28,11 @@ import javax.servlet.http.*; import org.apache.juneau.*; import org.apache.juneau.dto.swagger.*; import org.apache.juneau.encoders.*; +import org.apache.juneau.internal.*; import org.apache.juneau.json.*; import org.apache.juneau.parser.*; import org.apache.juneau.rest.annotation.*; +import org.apache.juneau.rest.widget.*; import org.apache.juneau.serializer.*; import org.apache.juneau.svl.*; import org.apache.juneau.urlencoding.*; @@ -63,7 +65,7 @@ class CallMethod implements Comparable<CallMethod> { private final Response[] responses; private final RestContext context; private final BeanContext beanContext; - private final HtmlDocContext htmlDocContext; + private final Map<String,Widget> widgets; CallMethod(Object servlet, java.lang.reflect.Method method, RestContext context) throws RestServletException { Builder b = new Builder(servlet, method, context); @@ -95,7 +97,7 @@ class CallMethod implements Comparable<CallMethod> { this.priority = b.priority; this.parameters = b.parameters; this.responses = b.responses; - this.htmlDocContext = new HtmlDocContext(method, context.getHtmlDocContext()); + this.widgets = Collections.unmodifiableMap(b.widgets); } private static class Builder { @@ -117,6 +119,7 @@ class CallMethod implements Comparable<CallMethod> { private Integer priority; private org.apache.juneau.rest.annotation.Parameter[] parameters; private Response[] responses; + private Map<String,Widget> widgets; private Builder(Object servlet, java.lang.reflect.Method method, RestContext context) throws RestServletException { String sig = method.getDeclaringClass().getName() + '.' + method.getName(); @@ -145,7 +148,7 @@ class CallMethod implements Comparable<CallMethod> { urlEncodingParser = context.getUrlEncodingParser(); beanContext = context.getBeanContext(); encoders = context.getEncoders(); - properties = context.getProperties(); + properties = new ObjectMap().setInner(context.getProperties()); defaultCharset = context.getDefaultCharset(); String paramFormat = context.getParamFormat(); @@ -154,6 +157,19 @@ class CallMethod implements Comparable<CallMethod> { if (! m.paramFormat().isEmpty()) paramFormat = context.getVarResolver().resolve(m.paramFormat()); + HtmlDocBuilder hdb = new HtmlDocBuilder(properties); + + HtmlDoc hd = m.htmldoc(); + hdb.process(hd); + + widgets = new HashMap<String,Widget>(context.getWidgets()); + for (Class<? extends Widget> wc : hd.widgets()) { + Widget w = ClassUtils.newInstance(Widget.class, wc); + widgets.put(w.getName(), w); + hdb.script("INHERIT", "$W{"+w.getName()+".script}"); + hdb.style("INHERIT", "$W{"+w.getName()+".style}"); + } + List<Inherit> si = Arrays.asList(m.serializersInherit()); List<Inherit> pi = Arrays.asList(m.parsersInherit()); @@ -461,10 +477,6 @@ class CallMethod implements Comparable<CallMethod> { return null; } - HtmlDocContext getHtmlDocContext() { - return htmlDocContext; - } - /** * Returns the localized Swagger tags for this Java method. */ @@ -749,9 +761,10 @@ class CallMethod implements Comparable<CallMethod> { req.getPathMatch().put(pathPattern.getVars()[i], patternVals[i]); req.getPathMatch().setRemainder(remainder); - ObjectMap requestProperties = createRequestProperties(properties, req); + ObjectMap requestProperties = new ResolvingObjectMap(req.getVarResolverSession()).setInner(properties); + req.init(method, requestProperties, defaultRequestHeaders, defaultQuery, defaultFormData, defaultCharset, - serializers, parsers, urlEncodingParser, beanContext, encoders, htmlDocContext.widgets, htmlDocContext); + serializers, parsers, urlEncodingParser, beanContext, encoders, widgets); res.init(requestProperties, defaultCharset, serializers, urlEncodingSerializer, encoders); // Class-level guards @@ -828,38 +841,6 @@ class CallMethod implements Comparable<CallMethod> { return SC_OK; } - /** - * This method creates all the request-time properties. - */ - ObjectMap createRequestProperties(final ObjectMap methodProperties, final RestRequest req) { - @SuppressWarnings("serial") - ObjectMap m = new ObjectMap() { - @Override /* Map */ - public Object get(Object key) { - Object o = super.get(key); - if (o == null) { - String k = key.toString(); - int i = k.indexOf('.'); - if (i != -1) { - String prefix = k.substring(0, i); - String remainder = k.substring(i+1); - Object v = req.resolveProperty(CallMethod.this, prefix, remainder); - if (v != null) - return v; - } - o = req.getPathMatch().get(k); - if (o == null) - o = req.getHeader(k); - } - if (o instanceof String) - o = req.getVarResolverSession().resolve(o.toString()); - return o; - } - }; - m.setInner(methodProperties); - return m; - } - @Override /* Object */ public String toString() { return "SimpleMethod: name=" + httpMethod + ", path=" + pathPattern.getPatternString(); http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/9c746e64/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/HtmlDocBuilder.java ---------------------------------------------------------------------- diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/HtmlDocBuilder.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/HtmlDocBuilder.java index 99c6a3b..2eab1ec 100644 --- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/HtmlDocBuilder.java +++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/HtmlDocBuilder.java @@ -14,8 +14,12 @@ package org.apache.juneau.rest; import static org.apache.juneau.html.HtmlDocSerializerContext.*; +import java.util.*; +import java.util.regex.*; + import org.apache.juneau.*; import org.apache.juneau.html.*; +import org.apache.juneau.internal.*; import org.apache.juneau.rest.annotation.*; import org.apache.juneau.utils.*; @@ -23,7 +27,15 @@ import org.apache.juneau.utils.*; * Programmatic interface for setting properties used by the HtmlDoc serializer. * * <p> - * This class is instantiated by calling the {@link RestResponse#getHtmlDocBuilder()} method. + * Basically just a convenience wrapper around the servlet or method level properties for setting properties defined + * by the {@link HtmlDocSerializerContext} class. + * + * <p> + * This class is instantiated through the following methods. + * <ul> + * <li>{@link RestConfig#getHtmlDocBuilder()} - Set values programmatically during servlet initialization. + * <li>{@link RestResponse#getHtmlDocBuilder()} - Set values programmatically during a REST request. + * </ul> */ public class HtmlDocBuilder { @@ -33,6 +45,33 @@ public class HtmlDocBuilder { this.properties = properties; } + void process(HtmlDoc hd) { + if (hd.header().length > 0) + header((Object[])hd.header()); + if (hd.nav().length > 0) + nav((Object[])hd.nav()); + if (hd.aside().length > 0) + aside((Object[])hd.aside()); + if (hd.footer().length > 0) + footer((Object[])hd.footer()); + if (hd.style().length > 0) + style((Object[])hd.style()); + if (hd.script().length > 0) + script((Object[])hd.script()); + if (hd.navlinks().length > 0) + navlinks((Object[])hd.navlinks()); + if (hd.head().length > 0) + head((Object[])hd.head()); + if (hd.stylesheet().length > 0) + stylesheet((Object[])hd.stylesheet()); + if (! hd.noResultsMessage().isEmpty()) + noResultsMessage(hd.noResultsMessage()); + if (hd.nowrap()) + nowrap(true); + if (hd.template() != HtmlDocTemplate.class) + template(hd.template()); + } + /** * Sets the HTML header section contents. * @@ -44,7 +83,8 @@ public class HtmlDocBuilder { * to be whatever you want. * * <p> - * A value of <js>"NONE"</js> can be used to force no header. + * A value of <js>"INHERIT"</js> means copy the values from the parent. + * <br>A value of <js>"NONE"</js> can be used to force no value. * * <p> * This field can contain variables (e.g. <js>"$L{my.localized.variable}"</js>). @@ -64,8 +104,8 @@ public class HtmlDocBuilder { * </ul> * @return This object (for method chaining). */ - public HtmlDocBuilder header(Object value) { - return set(HTMLDOC_header, value); + public HtmlDocBuilder header(Object...value) { + return set(HTMLDOC_header, resolveList(value, properties.getStringArray(HTMLDOC_header))); } /** @@ -83,7 +123,8 @@ public class HtmlDocBuilder { * <br>See {@link RestContext#getVarResolver()} for the list of supported variables. * * <p> - * A value of <js>"NONE"</js> can be used to force no value. + * A value of <js>"INHERIT"</js> means copy the values from the parent. + * <br>A value of <js>"NONE"</js> can be used to force no value. * * <p> * This field can also use URIs of any support type in {@link UriResolver}. @@ -102,7 +143,7 @@ public class HtmlDocBuilder { * @return This object (for method chaining). */ public HtmlDocBuilder navlinks(Object...value) { - return set(HTMLDOC_navlinks, value); + return set(HTMLDOC_navlinks, resolveLinks(value, properties.getStringArray(HTMLDOC_navlinks))); } /** @@ -125,7 +166,8 @@ public class HtmlDocBuilder { * <br>See {@link RestContext#getVarResolver()} for the list of supported variables. * * <p> - * A value of <js>"NONE"</js> can be used to force no value. + * A value of <js>"INHERIT"</js> means copy the values from the parent. + * <br>A value of <js>"NONE"</js> can be used to force no value. * * <p> * This is the programmatic equivalent to the {@link HtmlDoc#nav() @HtmlDoc.nav()} annotation. @@ -141,8 +183,8 @@ public class HtmlDocBuilder { * </ul> * @return This object (for method chaining). */ - public HtmlDocBuilder nav(Object value) { - return set(HTMLDOC_nav, value); + public HtmlDocBuilder nav(Object...value) { + return set(HTMLDOC_nav, resolveList(value, properties.getStringArray(HTMLDOC_nav))); } /** @@ -159,7 +201,8 @@ public class HtmlDocBuilder { * <br>See {@link RestContext#getVarResolver()} for the list of supported variables. * * <p> - * A value of <js>"NONE"</js> can be used to force no value. + * A value of <js>"INHERIT"</js> means copy the values from the parent. + * <br>A value of <js>"NONE"</js> can be used to force no value. * * <p> * This is the programmatic equivalent to the {@link HtmlDoc#aside() @HtmlDoc.aside()} annotation. @@ -175,8 +218,8 @@ public class HtmlDocBuilder { * </ul> * @return This object (for method chaining). */ - public HtmlDocBuilder aside(Object value) { - return set(HTMLDOC_aside, value); + public HtmlDocBuilder aside(Object...value) { + return set(HTMLDOC_aside, resolveList(value, properties.getStringArray(HTMLDOC_aside))); } /** @@ -193,7 +236,8 @@ public class HtmlDocBuilder { * <br>See {@link RestContext#getVarResolver()} for the list of supported variables. * * <p> - * A value of <js>"NONE"</js> can be used to force no value. + * A value of <js>"INHERIT"</js> means copy the values from the parent. + * <br>A value of <js>"NONE"</js> can be used to force no value. * * <p> * This is the programmatic equivalent to the {@link HtmlDoc#footer() @HtmlDoc.footer()} annotation. @@ -209,8 +253,8 @@ public class HtmlDocBuilder { * </ul> * @return This object (for method chaining). */ - public HtmlDocBuilder footer(Object value) { - return set(HTMLDOC_footer, value); + public HtmlDocBuilder footer(Object...value) { + return set(HTMLDOC_footer, resolveList(value, properties.getStringArray(HTMLDOC_footer))); } /** @@ -224,7 +268,8 @@ public class HtmlDocBuilder { * <br>See {@link RestContext#getVarResolver()} for the list of supported variables. * * <p> - * A value of <js>"NONE"</js> can be used to force no value. + * A value of <js>"INHERIT"</js> means copy the values from the parent. + * <br>A value of <js>"NONE"</js> can be used to force no value. * * <p> * This is the programmatic equivalent to the {@link HtmlDoc#style() @HtmlDoc.style()} annotation. @@ -240,8 +285,8 @@ public class HtmlDocBuilder { * </ul> * @return This object (for method chaining). */ - public HtmlDocBuilder style(Object value) { - return set(HTMLDOC_style, value); + public HtmlDocBuilder style(Object...value) { + return set(HTMLDOC_style, resolveList(value, properties.getStringArray(HTMLDOC_style))); } /** @@ -275,8 +320,8 @@ public class HtmlDocBuilder { * </ul> * @return This object (for method chaining). */ - public HtmlDocBuilder stylesheet(Object value) { - return set(HTMLDOC_stylesheet, value); + public HtmlDocBuilder stylesheet(Object...value) { + return set(HTMLDOC_stylesheet, resolveSet(value, properties.getStringArray(HTMLDOC_nav))); } /** @@ -290,7 +335,8 @@ public class HtmlDocBuilder { * <br>See {@link RestContext#getVarResolver()} for the list of supported variables. * * <p> - * A value of <js>"NONE"</js> can be used to force no value. + * A value of <js>"INHERIT"</js> means copy the values from the parent. + * <br>A value of <js>"NONE"</js> can be used to force no value. * * <p> * This is the programmatic equivalent to the {@link HtmlDoc#script() @HtmlDoc.script()} annotation. @@ -306,8 +352,8 @@ public class HtmlDocBuilder { * </ul> * @return This object (for method chaining). */ - public HtmlDocBuilder script(Object value) { - return set(HTMLDOC_script, value); + public HtmlDocBuilder script(Object...value) { + return set(HTMLDOC_script, resolveList(value, properties.getStringArray(HTMLDOC_script))); } /** @@ -321,7 +367,8 @@ public class HtmlDocBuilder { * <br>See {@link RestContext#getVarResolver()} for the list of supported variables. * * <p> - * A value of <js>"NONE"</js> can be used to force no value. + * A value of <js>"INHERIT"</js> means copy the values from the parent. + * <br>A value of <js>"NONE"</js> can be used to force no value. * * <p> * This is the programmatic equivalent to the {@link HtmlDoc#head() @HtmlDoc.head()} annotation. @@ -337,7 +384,7 @@ public class HtmlDocBuilder { * @return This object (for method chaining). */ public HtmlDocBuilder head(Object...value) { - return set(HTMLDOC_head, value); + return set(HTMLDOC_head, resolveList(value, properties.getStringArray(HTMLDOC_head))); } /** @@ -401,6 +448,60 @@ public class HtmlDocBuilder { return set(HTMLDOC_template, value); } + private static final Pattern INDEXED_LINK_PATTERN = Pattern.compile("(?s)(\\S*)\\[(\\d+)\\]\\:(.*)"); + + private static String[] resolveLinks(Object[] value, String[] prev) { + List<String> list = new ArrayList<String>(); + for (Object v : value) { + String s = StringUtils.toString(v); + if ("INHERIT".equals(s)) { + list.addAll(Arrays.asList(prev)); + } else if (s.indexOf('[') != -1 && INDEXED_LINK_PATTERN.matcher(s).matches()) { + Matcher lm = INDEXED_LINK_PATTERN.matcher(s); + lm.matches(); + String key = lm.group(1); + int index = Math.min(list.size(), Integer.parseInt(lm.group(2))); + String remainder = lm.group(3); + list.add(index, key.isEmpty() ? remainder : key + ":" + remainder); + } else { + list.add(s); + } + } + return list.toArray(new String[list.size()]); + } + + private static String[] resolveSet(Object[] value, String[] prev) { + Set<String> set = new HashSet<>(); + for (Object v : value) { + String s = StringUtils.toString(v); + if ("INHERIT".equals(s)) { + if (prev != null) + set.addAll(Arrays.asList(prev)); + } else if ("NONE".equals(s)) { + return new String[0]; + } else { + set.add(s); + } + } + return set.toArray(new String[set.size()]); + } + + private static String[] resolveList(Object[] value, String[] prev) { + List<String> set = new LinkedList<>(); + for (Object v : value) { + String s = StringUtils.toString(v); + if ("INHERIT".equals(s)) { + if (prev != null) + set.addAll(Arrays.asList(prev)); + } else if ("NONE".equals(s)) { + return new String[0]; + } else { + set.add(s); + } + } + return set.toArray(new String[set.size()]); + } + private HtmlDocBuilder set(String key, Object value) { properties.put(key, value); return this; http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/9c746e64/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/HtmlDocConfig.java ---------------------------------------------------------------------- diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/HtmlDocConfig.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/HtmlDocConfig.java deleted file mode 100644 index 0397198..0000000 --- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/HtmlDocConfig.java +++ /dev/null @@ -1,456 +0,0 @@ -// *************************************************************************************************************************** -// * 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.rest; - -import static org.apache.juneau.rest.RestUtils.*; - -import java.util.*; - -import org.apache.juneau.*; -import org.apache.juneau.html.*; -import org.apache.juneau.internal.*; -import org.apache.juneau.rest.annotation.*; -import org.apache.juneau.rest.widget.*; -import org.apache.juneau.utils.*; - -/** - * Programmatic interface for setting properties used by the HtmlDoc serializer. - */ -public class HtmlDocConfig { - - String header, nav, aside, footer, style, stylesheet, script, noResultsMessage; - String[] navlinks, head; - boolean nowrap; - Object template = HtmlDocTemplateBasic.class; - List<Class<? extends Widget>> widgets = new ArrayList<Class<? extends Widget>>(); - - HtmlDocConfig process(HtmlDoc hd) { - for (Class<? extends Widget> cw : hd.widgets()) - widget(cw); - header(resolveNewlineSeparatedAnnotation(hd.header(), header)); - nav(resolveNewlineSeparatedAnnotation(hd.nav(), nav)); - aside(resolveNewlineSeparatedAnnotation(hd.aside(), aside)); - footer(resolveNewlineSeparatedAnnotation(hd.footer(), footer)); - style(resolveNewlineSeparatedAnnotation(hd.style(), style)); - script(resolveNewlineSeparatedAnnotation(hd.script(), script)); - navlinks((Object[])resolveLinks(hd.navlinks(), navlinks)); - head((Object[])resolveContent(hd.head(), head)); - - if (! hd.stylesheet().isEmpty()) - stylesheet(hd.stylesheet()); - if (! hd.noResultsMessage().isEmpty()) - noResultsMessage(hd.noResultsMessage()); - if (hd.nowrap()) - nowrap(true); - if (hd.template() != HtmlDocTemplate.class) - template(hd.template()); - - return this; - } - - /** - * Sets the HTML header section contents. - * - * <p> - * The format of this value is HTML. - * - * <p> - * The page header normally contains the title and description, but this value can be used to override the contents - * to be whatever you want. - * - * <p> - * A value of <js>"NONE"</js> can be used to force no header. - * - * <p> - * This field can contain variables (e.g. <js>"$L{my.localized.variable}"</js>). - * <br>See {@link RestContext#getVarResolver()} for the list of supported variables. - * - * <p> - * This is the programmatic equivalent to the {@link HtmlDoc#header() @HtmlDoc.header()} annotation. - * - * @param value - * The HTML header section contents. - * Object will be converted to a string using {@link Object#toString()}. - * <p> - * <ul class='doctree'> - * <li class='info'> - * <b>Tip:</b> Use {@link StringMessage} to generate value with delayed serialization so as not to - * waste string concatenation cycles on non-HTML views. - * </ul> - * @return This object (for method chaining). - */ - public HtmlDocConfig header(Object value) { - header = StringUtils.toString(value); - return this; - } - - /** - * Sets the links in the HTML nav section. - * - * <p> - * The format of this value is a lax-JSON map of key/value pairs where the keys are the link text and the values are - * relative (to the servlet) or absolute URLs. - * - * <p> - * The page links are positioned immediately under the title and text. - * - * <p> - * This field can contain variables (e.g. <js>"$L{my.localized.variable}"</js>). - * <br>See {@link RestContext#getVarResolver()} for the list of supported variables. - * - * <p> - * A value of <js>"NONE"</js> can be used to force no value. - * - * <p> - * This field can also use URIs of any support type in {@link UriResolver}. - * - * <p> - * This is the programmatic equivalent to the {@link HtmlDoc#navlinks() @HtmlDoc.navlinks()} annotation. - * - * @param value - * The HTML nav section links links. - * <p> - * <ul class='doctree'> - * <li class='info'> - * <b>Tip:</b> Use {@link StringMessage} to generate value with delayed serialization so as not to - * waste string concatenation cycles on non-HTML views. - * </ul> - * @return This object (for method chaining). - */ - public HtmlDocConfig navlinks(Object...value) { - navlinks = StringUtils.toStrings(value); - return this; - } - - /** - * Sets the HTML nav section contents. - * - * <p> - * The format of this value is HTML. - * - * <p> - * The nav section of the page contains the links. - * - * <p> - * The format of this value is HTML. - * - * <p> - * When a value is specified, the {@link #navlinks(Object[])} value will be ignored. - * - * <p> - * This field can contain variables (e.g. <js>"$L{my.localized.variable}"</js>). - * <br>See {@link RestContext#getVarResolver()} for the list of supported variables. - * - * <p> - * A value of <js>"NONE"</js> can be used to force no value. - * - * <p> - * This is the programmatic equivalent to the {@link HtmlDoc#nav() @HtmlDoc.nav()} annotation. - * - * @param value - * The HTML nav section contents. - * Object will be converted to a string using {@link Object#toString()}. - * <p> - * <ul class='doctree'> - * <li class='info'> - * <b>Tip:</b> Use {@link StringMessage} to generate value with delayed serialization so as not to - * waste string concatenation cycles on non-HTML views. - * </ul> - * @return This object (for method chaining). - */ - public HtmlDocConfig nav(Object value) { - this.nav = StringUtils.toString(value); - return this; - } - - /** - * Sets the HTML aside section contents. - * - * <p> - * The format of this value is HTML. - * - * <p> - * The aside section typically floats on the right side of the page. - * - * <p> - * This field can contain variables (e.g. <js>"$L{my.localized.variable}"</js>). - * <br>See {@link RestContext#getVarResolver()} for the list of supported variables. - * - * <p> - * A value of <js>"NONE"</js> can be used to force no value. - * - * <p> - * This is the programmatic equivalent to the {@link HtmlDoc#aside() @HtmlDoc.aside()} annotation. - * - * @param value - * The HTML aside section contents. - * Object will be converted to a string using {@link Object#toString()}. - * <p> - * <ul class='doctree'> - * <li class='info'> - * <b>Tip:</b> Use {@link StringMessage} to generate value with delayed serialization so as not to waste - * string concatenation cycles on non-HTML views. - * </ul> - * @return This object (for method chaining). - */ - public HtmlDocConfig aside(Object value) { - this.aside = StringUtils.toString(value); - return this; - } - - /** - * Sets the HTML footer section contents. - * - * <p> - * The format of this value is HTML. - * - * <p> - * The footer section typically floats on the bottom of the page. - * - * <p> - * This field can contain variables (e.g. <js>"$L{my.localized.variable}"</js>). - * <br>See {@link RestContext#getVarResolver()} for the list of supported variables. - * - * <p> - * A value of <js>"NONE"</js> can be used to force no value. - * - * <p> - * This is the programmatic equivalent to the {@link HtmlDoc#footer() @HtmlDoc.footer()} annotation. - * - * @param value - * The HTML footer section contents. - * Object will be converted to a string using {@link Object#toString()}. - * <p> - * <ul class='doctree'> - * <li class='info'> - * <b>Tip:</b> Use {@link StringMessage} to generate value with delayed serialization so as not to - * waste string concatenation cycles on non-HTML views. - * </ul> - * @return This object (for method chaining). - */ - public HtmlDocConfig footer(Object value) { - this.footer = StringUtils.toString(value); - return this; - } - - /** - * Sets the HTML CSS style section contents. - * - * <p> - * The format of this value is CSS. - * - * <p> - * This field can contain variables (e.g. <js>"$L{my.localized.variable}"</js>). - * <br>See {@link RestContext#getVarResolver()} for the list of supported variables. - * - * <p> - * A value of <js>"NONE"</js> can be used to force no value. - * - * <p> - * This is the programmatic equivalent to the {@link HtmlDoc#style() @HtmlDoc.style()} annotation. - * - * @param value - * The HTML CSS style section contents. - * Object will be converted to a string using {@link Object#toString()}. - * <p> - * <ul class='doctree'> - * <li class='info'> - * <b>Tip:</b> Use {@link StringMessage} to generate value with delayed serialization so as not to - * waste string concatenation cycles on non-HTML views. - * </ul> - * @return This object (for method chaining). - */ - public HtmlDocConfig style(Object value) { - this.style = StringUtils.toString(value); - return this; - } - - /** - * Sets the CSS URL in the HTML CSS style section. - * - * <p> - * The format of this value is a comma-delimited list of URLs. - * - * <p> - * Specifies the URL to the stylesheet to add as a link in the style tag in the header. - * - * <p> - * The format of this value is CSS. - * - * <p> - * This field can contain variables (e.g. <js>"$L{my.localized.variable}"</js>) and can use URL protocols defined - * by {@link UriResolver}. - * <br>See {@link RestContext#getVarResolver()} for the list of supported variables. - * - * <p> - * This is the programmatic equivalent to the {@link HtmlDoc#stylesheet() @HtmlDoc.stylesheet()} annotation. - * - * @param value - * The CSS URL in the HTML CSS style section. - * Object will be converted to a string using {@link Object#toString()}. - * <p> - * <ul class='doctree'> - * <li class='info'> - * <b>Tip:</b> Use {@link StringMessage} to generate value with delayed serialization so as not to - * waste string concatenation cycles on non-HTML views. - * </ul> - * @return This object (for method chaining). - */ - public HtmlDocConfig stylesheet(Object value) { - this.stylesheet = StringUtils.toString(value); - return this; - } - - /** - * Sets the HTML script section contents. - * - * <p> - * The format of this value is Javascript. - * - * <p> - * This field can contain variables (e.g. <js>"$L{my.localized.variable}"</js>). - * <br>See {@link RestContext#getVarResolver()} for the list of supported variables. - * - * <p> - * A value of <js>"NONE"</js> can be used to force no value. - * - * <p> - * This is the programmatic equivalent to the {@link HtmlDoc#script() @HtmlDoc.script()} annotation. - * - * @param value - * The HTML script section contents. - * Object will be converted to a string using {@link Object#toString()}. - * <p> - * <ul class='doctree'> - * <li class='info'> - * <b>Tip:</b> Use {@link StringMessage} to generate value with delayed serialization so as not to - * waste string concatenation cycles on non-HTML views. - * </ul> - * @return This object (for method chaining). - */ - public HtmlDocConfig script(Object value) { - this.script = StringUtils.toString(value); - return this; - } - - /** - * Sets the HTML head section contents. - * - * <p> - * The format of this value is HTML. - * - * <p> - * This field can contain variables (e.g. <js>"$L{my.localized.variable}"</js>). - * <br>See {@link RestContext#getVarResolver()} for the list of supported variables. - * - * <p> - * A value of <js>"NONE"</js> can be used to force no value. - * - * <p> - * This is the programmatic equivalent to the {@link HtmlDoc#head() @HtmlDoc.head()} annotation. - * - * @param value - * The HTML head section contents. - * <p> - * <ul class='doctree'> - * <li class='info'> - * <b>Tip:</b> Use {@link StringMessage} to generate value with delayed serialization so as not to - * waste string concatenation cycles on non-HTML views. - * </ul> - * @return This object (for method chaining). - */ - public HtmlDocConfig head(Object...value) { - this.head = StringUtils.toStrings(value); - return this; - } - - /** - * Shorthand method for forcing the rendered HTML content to be no-wrap. - * - * <p> - * This is the programmatic equivalent to the {@link HtmlDoc#nowrap() @HtmlDoc.nowrap()} annotation. - * - * @param value The new nowrap setting. - * @return This object (for method chaining). - */ - public HtmlDocConfig nowrap(boolean value) { - this.nowrap = value; - return this; - } - - /** - * Specifies the text to display when serializing an empty array or collection. - * - * <p> - * This is the programmatic equivalent to the {@link HtmlDoc#noResultsMessage() @HtmlDoc.noResultsMessage()} - * annotation. - * - * @param value The text to display when serializing an empty array or collection. - * @return This object (for method chaining). - */ - public HtmlDocConfig noResultsMessage(Object value) { - this.noResultsMessage = StringUtils.toString(value); - return this; - } - - /** - * Specifies the template class to use for rendering the HTML page. - * - * <p> - * By default, uses {@link HtmlDocTemplateBasic} to render the contents, although you can provide your own custom - * renderer or subclasses from the basic class to have full control over how the page is rendered. - * - * <p> - * This is the programmatic equivalent to the {@link HtmlDoc#template() @HtmlDoc.template()} annotation. - * - * @param value The HTML page template to use to render the HTML page. - * @return This object (for method chaining). - */ - public HtmlDocConfig template(Class<? extends HtmlDocTemplate> value) { - this.template = value; - return this; - } - - /** - * Specifies the template class to use for rendering the HTML page. - * - * <p> - * By default, uses {@link HtmlDocTemplateBasic} to render the contents, although you can provide your own custom - * renderer or subclasses from the basic class to have full control over how the page is rendered. - * - * <p> - * This is the programmatic equivalent to the {@link HtmlDoc#template() @HtmlDoc.template()} annotation. - * - * @param value The HTML page template to use to render the HTML page. - * @return This object (for method chaining). - */ - public HtmlDocConfig template(HtmlDocTemplate value) { - this.template = value; - return this; - } - - /** - * Defines widgets that can be used in conjunction with string variables of the form <js>"$W{name}"</js>to quickly - * generate arbitrary replacement text. - * - * <p> - * Widgets are inherited from parent to child, but can be overridden by reusing the widget name. - * - * @param value The widget class to add. - * @return This object (for method chaining). - */ - public HtmlDocConfig widget(Class<? extends Widget> value) { - this.widgets.add(value); - return this; - } -} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/9c746e64/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/HtmlDocContext.java ---------------------------------------------------------------------- diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/HtmlDocContext.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/HtmlDocContext.java deleted file mode 100644 index 9f3809e..0000000 --- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/HtmlDocContext.java +++ /dev/null @@ -1,171 +0,0 @@ -// *************************************************************************************************************************** -// * 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.rest; - -import static org.apache.juneau.rest.RestUtils.*; - -import java.util.*; - -import org.apache.juneau.html.*; -import org.apache.juneau.internal.*; -import org.apache.juneau.rest.annotation.*; -import org.apache.juneau.rest.widget.*; - -/** - * Programmatic interface for set properties used by the HtmlDoc serializer. - */ -public final class HtmlDocContext { - - final String header, nav, aside, style, stylesheet, script, footer, noResultsMessage; - final String[] navlinks, head; - final boolean nowrap; - final HtmlDocTemplate template; - final Map<String,Widget> widgets; - - HtmlDocContext(Object resource, RestConfig config) throws RestServletException { - try { - Builder b = new Builder(resource, config); - - this.header = b.header; - this.nav = b.nav; - this.aside = b.aside; - this.style = b.style; - this.stylesheet = b.stylesheet; - this.script = b.script; - this.footer = b.footer; - this.noResultsMessage = b.noResultsMessage; - this.navlinks = b.navlinks; - this.head = b.head; - this.nowrap = b.nowrap; - this.widgets = Collections.unmodifiableMap(b.widgets); - this.template = b.template; - } catch (RestServletException e) { - throw e; - } catch (Exception e) { - throw new RestServletException("Exception occurred while initializing resource ''{0}''", resource.getClass().getSimpleName()).initCause(e); - } - } - - HtmlDocContext(java.lang.reflect.Method method, HtmlDocContext pc) throws RestServletException { - try { - Builder b = new Builder(method, pc); - this.header = b.header; - this.nav = b.nav; - this.aside = b.aside; - this.style = b.style; - this.stylesheet = b.stylesheet; - this.script = b.script; - this.footer = b.footer; - this.noResultsMessage = b.noResultsMessage; - this.navlinks = b.navlinks; - this.head = b.head; - this.nowrap = b.nowrap; - this.widgets = Collections.unmodifiableMap(b.widgets); - this.template = b.template; - } catch (RestServletException e) { - throw e; - } catch (Exception e) { - String sig = method.getDeclaringClass().getName() + '.' + method.getName(); - throw new RestServletException("Exception occurred while initializing method ''{0}''", sig).initCause(e); - } - } - - - static class Builder { - - String header, nav, aside, style, stylesheet, script, footer, noResultsMessage; - String[] navlinks, head; - boolean nowrap; - HtmlDocTemplate template; - Map<String,Widget> widgets; - - - Builder(java.lang.reflect.Method method, HtmlDocContext pc) throws Exception { - String sig = method.getDeclaringClass().getName() + '.' + method.getName(); - - try { - RestMethod m = method.getAnnotation(RestMethod.class); - if (m == null) - throw new RestServletException("@RestMethod annotation not found on method ''{0}''", sig); - - HtmlDoc hd = m.htmldoc(); - - widgets = new HashMap<String,Widget>(pc.widgets); - for (Class<? extends Widget> wc : hd.widgets()) { - Widget w = ClassUtils.newInstance(Widget.class, wc); - widgets.put(w.getName(), w); - } - - header = resolveNewlineSeparatedAnnotation(hd.header(), pc.header); - nav = resolveNewlineSeparatedAnnotation(hd.nav(), pc.nav); - aside = resolveNewlineSeparatedAnnotation(hd.aside(), pc.aside); - footer = resolveNewlineSeparatedAnnotation(hd.footer(), pc.footer); - style = resolveNewlineSeparatedAnnotation(hd.style(), pc.style); - script = resolveNewlineSeparatedAnnotation(hd.script(), pc.script); - head = resolveContent(hd.head(), pc.head); - navlinks = resolveLinks(hd.navlinks(), pc.navlinks); - stylesheet = hd.stylesheet().isEmpty() ? pc.stylesheet : hd.stylesheet(); - nowrap = hd.nowrap() ? hd.nowrap() : pc.nowrap; - noResultsMessage = hd.noResultsMessage().isEmpty() ? pc.noResultsMessage : hd.noResultsMessage(); - template = - hd.template() == HtmlDocTemplate.class - ? pc.template - : ClassUtils.newInstance(HtmlDocTemplate.class, hd.template()); - } catch (RestServletException e) { - throw e; - } catch (Exception e) { - throw new RestServletException("Exception occurred while initializing method ''{0}''", sig).initCause(e); - } - } - - - Builder(Object resource, RestConfig sc) throws Exception { - - HtmlDocConfig hdc = sc.htmlDocConfig; - - this.widgets = new LinkedHashMap<String,Widget>(); - for (Class<? extends Widget> wc : hdc.widgets) { - Widget w = resolve(resource, Widget.class, wc); - this.widgets.put(w.getName(), w); - } - - header = hdc.header; - navlinks = hdc.navlinks; - nav = hdc.nav; - aside = hdc.aside; - style = hdc.style; - stylesheet = hdc.stylesheet; - script = hdc.script; - head = hdc.head; - footer = hdc.footer; - nowrap = hdc.nowrap; - noResultsMessage = hdc.noResultsMessage; - template = resolve(resource, HtmlDocTemplate.class, hdc.template); - } - } - - //---------------------------------------------------------------------------------------------------- - // Utility methods - //---------------------------------------------------------------------------------------------------- - - /** - * Takes in an object of type T or a Class<T> and either casts or constructs a T. - */ - private static <T> T resolve(Object outer, Class<T> c, Object o, Object...cArgs) throws RestServletException { - try { - return ClassUtils.newInstanceFromOuter(outer, c, o, cArgs); - } catch (Exception e) { - throw new RestServletException("Exception occurred while constructing class ''{0}''", c).initCause(e); - } - } -} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/9c746e64/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestConfig.java ---------------------------------------------------------------------- diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestConfig.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestConfig.java index ca8e558..3255041 100644 --- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestConfig.java +++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestConfig.java @@ -33,6 +33,7 @@ import org.apache.juneau.parser.*; import org.apache.juneau.rest.annotation.*; import org.apache.juneau.rest.response.*; import org.apache.juneau.rest.vars.*; +import org.apache.juneau.rest.widget.*; import org.apache.juneau.serializer.*; import org.apache.juneau.svl.*; import org.apache.juneau.svl.vars.*; @@ -113,7 +114,8 @@ public class RestConfig implements ServletConfig { String path; String clientVersionHeader = "X-Client-Version"; String contextPath; - HtmlDocConfig htmlDocConfig = new HtmlDocConfig(); + HtmlDocBuilder htmlDocBuilder; + List<Class<? extends Widget>> widgets = new ArrayList<Class<? extends Widget>>(); Object resourceResolver = RestResourceResolverSimple.class; Object logger = RestLogger.Normal.class; @@ -156,6 +158,7 @@ public class RestConfig implements ServletConfig { ConfigFileBuilder cfb = new ConfigFileBuilder(); properties = new ObjectMap(); + htmlDocBuilder = new HtmlDocBuilder(properties); configFile = cfb.build(); varResolverBuilder = new VarResolverBuilder() .vars( @@ -245,7 +248,11 @@ public class RestConfig implements ServletConfig { if (! r.paramFormat().isEmpty()) setParamFormat(vr.resolve(r.paramFormat())); - htmlDocConfig.process(r.htmldoc()); + HtmlDoc hd = r.htmldoc(); + for (Class<? extends Widget> cw : hd.widgets()) + this.widgets.add(cw); + + htmlDocBuilder.process(hd); } addResponseHandlers( @@ -1176,12 +1183,27 @@ public class RestConfig implements ServletConfig { } /** - * Returns the configuration settings specific to the HTML doc view. + * Defines widgets that can be used in conjunction with string variables of the form <js>"$W{name}"</js>to quickly + * generate arbitrary replacement text. + * + * <p> + * Widgets are inherited from parent to child, but can be overridden by reusing the widget name. * - * @return The configuration settings specific to the HTML doc view. + * @param value The widget class to add. + * @return This object (for method chaining). + */ + public RestConfig addWidget(Class<? extends Widget> value) { + this.widgets.add(value); + return this; + } + + /** + * Returns an instance of an HTMLDOC builder for setting HTMLDOC-related properties. + * + * @return An instance of an HTMLDOC builder for setting HTMLDOC-related properties. */ - public HtmlDocConfig getHtmlDocConfig() { - return htmlDocConfig; + public HtmlDocBuilder getHtmlDocBuilder() { + return htmlDocBuilder; } /** http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/9c746e64/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java ---------------------------------------------------------------------- diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java index ef9200b..5515e48 100644 --- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java +++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java @@ -41,6 +41,7 @@ import org.apache.juneau.parser.*; import org.apache.juneau.rest.annotation.*; import org.apache.juneau.rest.annotation.Properties; import org.apache.juneau.rest.vars.*; +import org.apache.juneau.rest.widget.*; import org.apache.juneau.serializer.*; import org.apache.juneau.svl.*; import org.apache.juneau.svl.vars.*; @@ -69,7 +70,7 @@ public final class RestContext extends Context { fullPath, contextPath; - private final HtmlDocContext htmlDocContext; + private final Map<String,Widget> widgets; private final Set<String> allowMethodParams; @@ -190,7 +191,7 @@ public final class RestContext extends Context { this.logger = b.logger; this.fullPath = b.fullPath; this.contextPath = nullIfEmpty(b.contextPath); - this.htmlDocContext = new HtmlDocContext(resource, config); + this.widgets = Collections.unmodifiableMap(b.widgets); //---------------------------------------------------------------------------------------------------- // Initialize the child resources. @@ -471,6 +472,7 @@ public final class RestContext extends Context { Set<String> allowMethodParams = new LinkedHashSet<String>(); RestLogger logger; String fullPath; + Map<String,Widget> widgets; Object resourceResolver; String contextPath; @@ -573,6 +575,18 @@ public final class RestContext extends Context { logger = sc.logger == null ? new RestLogger.NoOp() : resolve(resource, RestLogger.class, sc.logger); fullPath = (sc.parentContext == null ? "" : (sc.parentContext.fullPath + '/')) + sc.path; + + HtmlDocBuilder hdb = new HtmlDocBuilder(sc.properties); + + this.widgets = new LinkedHashMap<String,Widget>(); + + for (Class<? extends Widget> wc : sc.widgets) { + Widget w = resolve(resource, Widget.class, wc); + String n = w.getName(); + this.widgets.put(n, w); + hdb.script("INHERIT", "$W{"+n+".script}"); + hdb.style("INHERIT", "$W{"+n+".style}"); + } } } @@ -838,12 +852,15 @@ public final class RestContext extends Context { } /** - * Returns the context values for the HTML doc view. + * The widgets used for resolving <js>"$W{...}"<js> variables. + * + * <p> + * Defined by the {@link HtmlDoc#widgets()} annotation or {@link RestConfig#addWidget(Class)} method. * - * @return The context values for the HTML doc view. + * @return The var resolver widgets as a map with keys being the name returned by {@link Widget#getName()}. */ - public HtmlDocContext getHtmlDocContext() { - return htmlDocContext; + public Map<String,Widget> getWidgets() { + return widgets; } /**
