Repository: incubator-juneau Updated Branches: refs/heads/master f4812b7ce -> e6998a7ea
Bug fixes. Project: http://git-wip-us.apache.org/repos/asf/incubator-juneau/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-juneau/commit/e6998a7e Tree: http://git-wip-us.apache.org/repos/asf/incubator-juneau/tree/e6998a7e Diff: http://git-wip-us.apache.org/repos/asf/incubator-juneau/diff/e6998a7e Branch: refs/heads/master Commit: e6998a7ea07622ed641a77c6bfb168e0844193bc Parents: f4812b7 Author: JamesBognar <[email protected]> Authored: Thu Jun 8 12:50:39 2017 -0400 Committer: JamesBognar <[email protected]> Committed: Thu Jun 8 12:50:39 2017 -0400 ---------------------------------------------------------------------- .../org/apache/juneau/utils/ClassUtilsTest.java | 113 +++++++++++++++ .../main/java/org/apache/juneau/ClassMeta.java | 35 +++-- .../src/main/java/org/apache/juneau/Setter.java | 64 +++++++++ .../apache/juneau/annotation/NameProperty.java | 23 ++- .../juneau/annotation/ParentProperty.java | 20 ++- .../juneau/html/HtmlDocSerializerContext.java | 8 +- .../org/apache/juneau/html/HtmlDocTemplate.java | 22 +-- .../org/apache/juneau/html/HtmlSerializer.java | 2 +- .../org/apache/juneau/internal/ArrayUtils.java | 15 +- .../org/apache/juneau/internal/ClassUtils.java | 142 +++++++++++++++++++ .../java/org/apache/juneau/parser/Parser.java | 8 +- .../transform/InterfaceBeanFilterBuilder.java | 40 ++++++ juneau-core/src/main/javadoc/overview.html | 9 +- .../org/apache/juneau/rest/RestContext.java | 2 + .../org/apache/juneau/rest/RestResponse.java | 4 +- .../org/apache/juneau/rest/RestServlet.java | 4 +- .../juneau/rest/annotation/MethodSwagger.java | 56 +++++--- .../juneau/rest/annotation/Parameter.java | 2 +- .../juneau/rest/annotation/ResourceSwagger.java | 24 +++- 19 files changed, 527 insertions(+), 66 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6998a7e/juneau-core-test/src/test/java/org/apache/juneau/utils/ClassUtilsTest.java ---------------------------------------------------------------------- diff --git a/juneau-core-test/src/test/java/org/apache/juneau/utils/ClassUtilsTest.java b/juneau-core-test/src/test/java/org/apache/juneau/utils/ClassUtilsTest.java index c333fa7..9c003d9 100755 --- a/juneau-core-test/src/test/java/org/apache/juneau/utils/ClassUtilsTest.java +++ b/juneau-core-test/src/test/java/org/apache/juneau/utils/ClassUtilsTest.java @@ -16,9 +16,13 @@ import static java.lang.annotation.ElementType.*; import static java.lang.annotation.RetentionPolicy.*; import static org.apache.juneau.internal.ClassUtils.*; import static org.junit.Assert.*; +import static org.apache.juneau.TestUtils.*; import java.lang.annotation.*; +import java.lang.reflect.*; +import java.util.*; +import org.apache.juneau.internal.*; import org.junit.*; @SuppressWarnings("javadoc") @@ -197,4 +201,113 @@ public class ClassUtilsTest { public @interface TestAnnotation { String value() default ""; } + + //==================================================================================================== + // getParentClassesParentFirst() + //==================================================================================================== + @Test + public void getParentClassesParentFirst() throws Exception { + Set<String> s = new TreeSet<String>(); + for (Iterator<Class<?>> i = ClassUtils.getParentClasses(CD.class, true, true); i.hasNext();) { + Class<?> c = i.next(); + s.add(c.getSimpleName()); + } + assertObjectEquals("['CA1','CA2','CA3','CB','CC','CD']", s); + + s = new TreeSet<String>(); + for (Iterator<Class<?>> i = ClassUtils.getParentClasses(CD.class, true, false); i.hasNext();) { + Class<?> c = i.next(); + s.add(c.getSimpleName()); + } + assertObjectEquals("['CB','CC','CD']", s); + + s = new TreeSet<String>(); + for (Iterator<Class<?>> i = ClassUtils.getParentClasses(CD.class, false, true); i.hasNext();) { + Class<?> c = i.next(); + s.add(c.getSimpleName()); + } + assertObjectEquals("['CA1','CA2','CA3','CB','CC','CD']", s); + + s = new TreeSet<String>(); + for (Iterator<Class<?>> i = ClassUtils.getParentClasses(CD.class, false, false); i.hasNext();) { + Class<?> c = i.next(); + s.add(c.getSimpleName()); + } + assertObjectEquals("['CB','CC','CD']", s); + } + + static interface CA1 {} + static interface CA2 extends CA1 {} + static interface CA3 {} + static interface CA4 {} + static class CB implements CA1, CA2 {} + static class CC extends CB implements CA3 {} + static class CD extends CC {} + + //==================================================================================================== + // getAllMethodsParentFirst() + //==================================================================================================== + @Test + public void getParentMethodsParentFirst() throws Exception { + Set<String> s = new TreeSet<String>(); + for (Method m : ClassUtils.getAllMethods(DD.class, true)) + if (! m.getName().startsWith("$")) + s.add(m.getDeclaringClass().getSimpleName() + '.' + m.getName()); + assertObjectEquals("['DA1.da1','DA2.da2','DB.da1','DB.db','DC.da2','DC.dc','DD.da2','DD.dd']", s); + + s = new TreeSet<String>(); + for (Method m : ClassUtils.getAllMethods(DD.class, false)) + if (! m.getName().startsWith("$")) + s.add(m.getDeclaringClass().getSimpleName() + '.' + m.getName()); + assertObjectEquals("['DA1.da1','DA2.da2','DB.da1','DB.db','DC.da2','DC.dc','DD.da2','DD.dd']", s); + } + + static interface DA1 { + void da1(); + } + static interface DA2 extends DA1 { + void da2(); + } + static interface DA3 {} + static interface DA4 {} + static abstract class DB implements DA1, DA2 { + public void da1() {} + public void db() {} + } + static class DC extends DB implements DA3 { + public void da2() {} + public void dc() {} + } + static class DD extends DC { + public void da2() {} + public void dd() {} + } + + //==================================================================================================== + // getAllFieldsParentFirst() + //==================================================================================================== + @Test + public void getParentFieldsParentFirst() throws Exception { + Set<String> s = new TreeSet<String>(); + for (Field f : ClassUtils.getAllFields(EB.class, true)) { + if (! f.getName().startsWith("$")) + s.add(f.getDeclaringClass().getSimpleName() + '.' + f.getName()); + } + assertObjectEquals("['EA.a1','EB.a1','EB.b1']", s); + + s = new TreeSet<String>(); + for (Field f : ClassUtils.getAllFields(EB.class, false)) { + if (! f.getName().startsWith("$")) + s.add(f.getDeclaringClass().getSimpleName() + '.' + f.getName()); + } + assertObjectEquals("['EA.a1','EB.a1','EB.b1']", s); + } + + static class EA { + int a1; + } + static class EB extends EA { + int a1; + int b1; + } } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6998a7e/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java b/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java index 8313709..0dcb8cc 100644 --- a/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java +++ b/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java @@ -76,7 +76,8 @@ public final class ClassMeta<T> implements Type { numberConstructorType; private final Method swapMethod, // The swap() method (if it has one). - unswapMethod, // The unswap() method (if it has one). + unswapMethod; // The unswap() method (if it has one). + private final Setter namePropertyMethod, // The method to set the name on an object (if it has one). parentPropertyMethod; // The method to set the parent on an object (if it has one). private final boolean @@ -307,7 +308,8 @@ public final class ClassMeta<T> implements Type { Method fromStringMethod = null, swapMethod = null, - unswapMethod = null, + unswapMethod = null; + Setter parentPropertyMethod = null, namePropertyMethod = null; Constructor<T> @@ -473,15 +475,26 @@ public final class ClassMeta<T> implements Type { } } + for (Field f : getAllFields(c, true)) { + if (f.isAnnotationPresent(ParentProperty.class)) { + f.setAccessible(true); + parentPropertyMethod = new Setter.FieldSetter(f); + } + if (f.isAnnotationPresent(NameProperty.class)) { + f.setAccessible(true); + namePropertyMethod = new Setter.FieldSetter(f); + } + } + // Find @NameProperty and @ParentProperty methods if present. - for (Method m : c.getDeclaredMethods()) { + for (Method m : getAllMethods(c, true)) { if (m.isAnnotationPresent(ParentProperty.class) && m.getParameterTypes().length == 1) { m.setAccessible(true); - parentPropertyMethod = m; + parentPropertyMethod = new Setter.MethodSetter(m); } if (m.isAnnotationPresent(NameProperty.class) && m.getParameterTypes().length == 1) { m.setAccessible(true); - namePropertyMethod = m; + namePropertyMethod = new Setter.MethodSetter(m); } } @@ -1357,20 +1370,20 @@ public final class ClassMeta<T> implements Type { } /** - * Returns the method annotated with {@link NameProperty @NameProperty}. + * Returns the method or field annotated with {@link NameProperty @NameProperty}. * - * @return The method annotated with {@link NameProperty @NameProperty} or <jk>null</jk> if method does not exist. + * @return The method or field annotated with {@link NameProperty @NameProperty} or <jk>null</jk> if method does not exist. */ - public Method getNameProperty() { + public Setter getNameProperty() { return namePropertyMethod; } /** - * Returns the method annotated with {@link ParentProperty @ParentProperty}. + * Returns the method or field annotated with {@link ParentProperty @ParentProperty}. * - * @return The method annotated with {@link ParentProperty @ParentProperty} or <jk>null</jk> if method does not exist. + * @return The method or field annotated with {@link ParentProperty @ParentProperty} or <jk>null</jk> if method does not exist. */ - public Method getParentProperty() { + public Setter getParentProperty() { return parentPropertyMethod; } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6998a7e/juneau-core/src/main/java/org/apache/juneau/Setter.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/Setter.java b/juneau-core/src/main/java/org/apache/juneau/Setter.java new file mode 100644 index 0000000..d81da79 --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/Setter.java @@ -0,0 +1,64 @@ +// *************************************************************************************************************************** +// * 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 java.lang.reflect.*; + +/** + * Encapsulate a bean setter method that may be a method or field. + */ +public interface Setter { + + /** + * Call the setter on the specified object. + * + * @param object The object to call the setter on + * @param value The value to set. + * @throws Exception + */ + void set(Object object, Object value) throws Exception; + + /** + * Field setter + */ + static class FieldSetter implements Setter { + + private final Field f; + + public FieldSetter(Field f) { + this.f = f; + } + + @Override /* Setter */ + public void set(Object object, Object value) throws Exception { + f.set(object, value); + } + } + + /** + * Method setter + */ + static class MethodSetter implements Setter { + + private final Method m; + + public MethodSetter(Method m) { + this.m = m; + } + + @Override /* Setter */ + public void set(Object object, Object value) throws Exception { + m.invoke(object, value); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6998a7e/juneau-core/src/main/java/org/apache/juneau/annotation/NameProperty.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/annotation/NameProperty.java b/juneau-core/src/main/java/org/apache/juneau/annotation/NameProperty.java index 492b0c6..0ad6c17 100644 --- a/juneau-core/src/main/java/org/apache/juneau/annotation/NameProperty.java +++ b/juneau-core/src/main/java/org/apache/juneau/annotation/NameProperty.java @@ -26,8 +26,29 @@ import org.apache.juneau.ini.*; * For example, the {@link Section} class must know the name it's known by it's parent * {@link ConfigFileImpl} class, so parsers will call this method with the section name * using the {@link Section#setName(String)} method. + * <p> + * A commonly-used case is when you're parsing a JSON map containing beans where one of the bean properties + * is the key used in the map. For example: + * <p class='bcode'> + * { + * id1: {name: <js>'John Smith'</js>, sex:<js>'M'</js>}, + * id2: {name: <js>'Jane Doe'</js>, sex:<js>'F'</js>} + * } + * </p> + * <p class='bcode'> + * <jk>public class</jk> Person { + * <ja>@NameProperty</ja> <jk>public</jk> String <jf>id</jf>; + * <jk>public</jk> String <jf>name</jf>; + * <jk>public char</jk> <jf>sex</jf>; + * } + * </p> + * <p> + * <h5 class='section'>Notes:</h5> + * <ul> + * <li>The annotated field or method does not need to be public. + * </ul> */ -@Target({METHOD}) +@Target({METHOD,FIELD}) @Retention(RUNTIME) @Inherited public @interface NameProperty {} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6998a7e/juneau-core/src/main/java/org/apache/juneau/annotation/ParentProperty.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/annotation/ParentProperty.java b/juneau-core/src/main/java/org/apache/juneau/annotation/ParentProperty.java index 282d02c..c26de34 100644 --- a/juneau-core/src/main/java/org/apache/juneau/annotation/ParentProperty.java +++ b/juneau-core/src/main/java/org/apache/juneau/annotation/ParentProperty.java @@ -26,8 +26,26 @@ import org.apache.juneau.ini.*; * For example, the {@link Section} class cannot exist outside the scope of a parent * {@link ConfigFileImpl} class, so parsers will add a reference to the config file * using the {@link Section#setParent(ConfigFileImpl)} method. + * <p> + * A commonly-used case is when you're parsing beans, and a child bean has a reference to a parent bean. + * <p class='bcode'> + * <jk>public class</jk> AddressBook { + * <jk>public</jk> List<Person> <jf>people</jf>; + * } + * + * <jk>public class</jk> Person { + * <ja>@ParentProperty</ja> <jk>public</jk> AddressBook <jf>addressBook</jf>; + * <jk>public</jk> String <jf>name</jf>; + * <jk>public char</jk> <jf>sex</jf>; + * } + * </p> + * <p> + * <h5 class='section'>Notes:</h5> + * <ul> + * <li>The annotated field or method does not need to be public. + * </ul> */ -@Target({METHOD}) +@Target({METHOD,FIELD}) @Retention(RUNTIME) @Inherited public @interface ParentProperty {} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6998a7e/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocSerializerContext.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocSerializerContext.java b/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocSerializerContext.java index 3d7616a..b8f5ddf 100644 --- a/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocSerializerContext.java +++ b/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocSerializerContext.java @@ -197,7 +197,7 @@ public final class HtmlDocSerializerContext extends HtmlSerializerContext { * <p class='bcode'> * <ja>@RestResource</ja>( * htmldoc=<ja>@HtmlDoc</ja>( - * nav=<js>"<:p class='special-navigation'>This is my special navigation content<:/p>"</js> + * nav=<js>"<p class='special-navigation'>This is my special navigation content</p>"</js> * ) * ) * </p> @@ -228,7 +228,7 @@ public final class HtmlDocSerializerContext extends HtmlSerializerContext { * For example, if the servlet path is <js>"http://localhost/myContext/myServlet"</js>, and the * URL is <js>"foo"</js>, the link becomes <js>"http://localhost/myContext/myServlet/foo"</js>. * Absolute (<js>"/myOtherContext/foo"</js>) and fully-qualified (<js>"http://localhost2/foo"</js>) URLs - * can also be used. + * can also be used in addition to various other protocols specified by {@link UriResolver} such as <js>"servlet:/..."</js>. * * <h5 class='section'>Example:</h5> * <p> @@ -282,7 +282,7 @@ public final class HtmlDocSerializerContext extends HtmlSerializerContext { * <p class='bcode'> * <ja>@RestResource</ja>( * htmldoc=<ja>@HtmlDoc</ja>( - * nav=<js>"<:p class='special-navigation'>This is my special navigation content<:/p>"</js> + * nav=<js>"<p class='special-navigation'>This is my special navigation content</p>"</js> * ) * ) * </p> @@ -314,7 +314,7 @@ public final class HtmlDocSerializerContext extends HtmlSerializerContext { * <p class='bcode'> * <ja>@RestResource</ja>( * htmldoc=<ja>@HtmlDoc</ja>( - * aside=<js>"<:ul><:li>Item 1<:li>Item 2<:li>Item 3<:/ul>"</js> + * aside=<js>"<ul><li>Item 1<li>Item 2<li>Item 3</ul>"</js> * ) * ) * </p> http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6998a7e/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocTemplate.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocTemplate.java b/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocTemplate.java index f850b6b..6748540 100644 --- a/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocTemplate.java +++ b/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocTemplate.java @@ -58,7 +58,7 @@ public interface HtmlDocTemplate { public void head(HtmlDocSerializerSession session, HtmlWriter w, HtmlDocSerializer s, Object o) throws Exception; /** - * Renders the contents of the <code><xt><head></xt> / <xt><style</xt> <xa>type</xa>=<xs>"text/css"</xs><xt>></xt></code> element. + * Renders the contents of the <code><xt><head></xt>/<xt><style</xt> <xa>type</xa>=<xs>"text/css"</xs><xt>></xt></code> element. * @param session The current serializer session. * @param w The writer being written to. * @param s The serializer calling this method. @@ -78,7 +78,7 @@ public interface HtmlDocTemplate { public void body(HtmlDocSerializerSession session, HtmlWriter w, HtmlDocSerializer s, Object o) throws Exception; /** - * Renders the contents of the <code><xt><body></xt> / <xt><header></xt></code> element. + * Renders the contents of the <code><xt><body></xt>/<xt><header></xt></code> element. * @param session The current serializer session. * @param w The writer being written to. * @param s The serializer calling this method. @@ -88,7 +88,7 @@ public interface HtmlDocTemplate { public void header(HtmlDocSerializerSession session, HtmlWriter w, HtmlDocSerializer s, Object o) throws Exception; /** - * Renders the contents of the <code><xt><body></xt> / <xt><nav></xt></code> element. + * Renders the contents of the <code><xt><body></xt>/<xt><nav></xt></code> element. * @param session The current serializer session. * @param w The writer being written to. * @param s The serializer calling this method. @@ -98,7 +98,7 @@ public interface HtmlDocTemplate { public void nav(HtmlDocSerializerSession session, HtmlWriter w, HtmlDocSerializer s, Object o) throws Exception; /** - * Renders the contents of the <code><xt><body></xt> / <xt><article></xt></code> element. + * Renders the contents of the <code><xt><body></xt>/<xt><article></xt></code> element. * @param session The current serializer session. * @param w The writer being written to. * @param s The serializer calling this method. @@ -108,7 +108,7 @@ public interface HtmlDocTemplate { public void article(HtmlDocSerializerSession session, HtmlWriter w, HtmlDocSerializer s, Object o) throws Exception; /** - * Renders the contents of the <code><xt><body></xt> / <xt><aside></xt></code> element. + * Renders the contents of the <code><xt><body></xt>/<xt><aside></xt></code> element. * @param session The current serializer session. * @param w The writer being written to. * @param s The serializer calling this method. @@ -118,7 +118,7 @@ public interface HtmlDocTemplate { public void aside(HtmlDocSerializerSession session, HtmlWriter w, HtmlDocSerializer s, Object o) throws Exception; /** - * Renders the contents of the <code><xt><body></xt> / <xt><footer></xt></code> element. + * Renders the contents of the <code><xt><body></xt>/<xt><footer></xt></code> element. * @param session The current serializer session. * @param w The writer being written to. * @param s The serializer calling this method. @@ -128,35 +128,35 @@ public interface HtmlDocTemplate { public void footer(HtmlDocSerializerSession session, HtmlWriter w, HtmlDocSerializer s, Object o) throws Exception; /** - * Returns <jk>true</jk> if this page should render a <code><xt><head></xt> / <xt><style</xt> <xa>type</xa>=<xs>"text/css"</xs><xt>></xt></code> element. + * Returns <jk>true</jk> if this page should render a <code><xt><head></xt>/<xt><style</xt> <xa>type</xa>=<xs>"text/css"</xs><xt>></xt></code> element. * @param session The current serializer session. * @return A boolean flag. */ public boolean hasCss(HtmlDocSerializerSession session); /** - * Returns <jk>true</jk> if this page should render a <code><xt><body></xt> / <xt>header</xt></code> element. + * Returns <jk>true</jk> if this page should render a <code><xt><body></xt>/<xt><header></xt></code> element. * @param session The current serializer session. * @return A boolean flag. */ public boolean hasHeader(HtmlDocSerializerSession session); /** - * Returns <jk>true</jk> if this page should render a <code><xt><body></xt> / <xt>nav</xt></code> element. + * Returns <jk>true</jk> if this page should render a <code><xt><body></xt>/<xt><nav></xt></code> element. * @param session The current serializer session. * @return A boolean flag. */ public boolean hasNav(HtmlDocSerializerSession session); /** - * Returns <jk>true</jk> if this page should render a <code><xt><body></xt> / <xt>aside</xt></code> element. + * Returns <jk>true</jk> if this page should render a <code><xt><body></xt>/<xt><aside></xt></code> element. * @param session The current serializer session. * @return A boolean flag. */ public boolean hasAside(HtmlDocSerializerSession session); /** - * Returns <jk>true</jk> if this page should render a <code><xt><body></xt> / <xt>footer</xt></code> element. + * Returns <jk>true</jk> if this page should render a <code><xt><body></xt>/<xt><footer></xt></code> element. * @param session The current serializer session. * @return A boolean flag. */ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6998a7e/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 f17f7d7..934bd77 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 @@ -555,7 +555,7 @@ public class HtmlSerializer extends XmlSerializer { ClassMeta<?> cMeta = session.getClassMetaForObject(value); HtmlRender render = hpMeta.getRender(); - if (render == null) + if (render == null && cMeta != null) render = cMeta.getExtendedMeta(HtmlClassMeta.class).getRender(); out.oTag(i+2, "td"); http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6998a7e/juneau-core/src/main/java/org/apache/juneau/internal/ArrayUtils.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/internal/ArrayUtils.java b/juneau-core/src/main/java/org/apache/juneau/internal/ArrayUtils.java index 176dbd5..3fbaeaa 100644 --- a/juneau-core/src/main/java/org/apache/juneau/internal/ArrayUtils.java +++ b/juneau-core/src/main/java/org/apache/juneau/internal/ArrayUtils.java @@ -215,7 +215,7 @@ public final class ArrayUtils { /** * Converts the specified array to an <code>ArrayList</code> - * + * * @param array The array to convert. * @param componentType The type of objects in the array. * It must match the actual component type in the array. @@ -350,4 +350,17 @@ public final class ArrayUtils { Array.set(a, i, Array.get(o, i)); return a; } + + /** + * Converts an Iterable to a list. + * @param i The iterable to convert. + * @return A new list of objects copied from the iterable. + */ + public static List<?> toList(Iterable<?> i) { + List<Object> l = new ArrayList<Object>(); + Iterator<?> i2 = i.iterator(); + while (i2.hasNext()) + l.add(i2.next()); + return l; + } } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6998a7e/juneau-core/src/main/java/org/apache/juneau/internal/ClassUtils.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/internal/ClassUtils.java b/juneau-core/src/main/java/org/apache/juneau/internal/ClassUtils.java index f4ebf10..0f52ed5 100644 --- a/juneau-core/src/main/java/org/apache/juneau/internal/ClassUtils.java +++ b/juneau-core/src/main/java/org/apache/juneau/internal/ClassUtils.java @@ -781,4 +781,146 @@ public final class ClassUtils { throw new FormattedRuntimeException("Object of type {0} found but was expecting {1}.", c2.getClass(), c.getClass()); } } + + /** + * Returns all the fields in the specified class and all parent classes. + * + * @param c The class to get all fields on. + * @param parentFirst Order them in parent-class-to-child-class order, otherwise child-class-to-parent-class order. + * @return An iterable of all fields in the specified class. + */ + @SuppressWarnings("rawtypes") + public static Iterable<Field> getAllFields(final Class c, final boolean parentFirst) { + return new Iterable<Field>() { + @Override + public Iterator<Field> iterator() { + return new Iterator<Field>(){ + final Iterator<Class<?>> classIterator = getParentClasses(c, parentFirst, false); + Field[] fields = classIterator.hasNext() ? classIterator.next().getDeclaredFields() : new Field[0]; + int fIndex = 0; + Field next; + + @Override + public boolean hasNext() { + prime(); + return next != null; + } + + private void prime() { + if (next == null) { + while (fIndex >= fields.length) { + if (classIterator.hasNext()) { + fields = classIterator.next().getDeclaredFields(); + fIndex = 0; + } else { + fIndex = -1; + } + } + if (fIndex != -1) + next = fields[fIndex++]; + } + } + + @Override + public Field next() { + prime(); + Field f = next; + next = null; + return f; + } + + @Override + public void remove() { + } + }; + } + }; + } + + /** + * Returns all the methods in the specified class and all parent classes. + * + * @param c The class to get all methods on. + * @param parentFirst Order them in parent-class-to-child-class order, otherwise child-class-to-parent-class order. + * @return An iterable of all methods in the specified class. + */ + @SuppressWarnings("rawtypes") + public static Iterable<Method> getAllMethods(final Class c, final boolean parentFirst) { + return new Iterable<Method>() { + @Override + public Iterator<Method> iterator() { + return new Iterator<Method>(){ + final Iterator<Class<?>> classIterator = getParentClasses(c, parentFirst, true); + Method[] methods = classIterator.hasNext() ? classIterator.next().getDeclaredMethods() : new Method[0]; + int mIndex = 0; + Method next; + + @Override + public boolean hasNext() { + prime(); + return next != null; + } + + private void prime() { + if (next == null) { + while (mIndex >= methods.length) { + if (classIterator.hasNext()) { + methods = classIterator.next().getDeclaredMethods(); + mIndex = 0; + } else { + mIndex = -1; + } + } + if (mIndex != -1) + next = methods[mIndex++]; + } + } + + @Override + public Method next() { + prime(); + Method m = next; + next = null; + return m; + } + + @Override + public void remove() { + } + }; + } + }; + } + + /** + * Returns a list of all the parent classes of the specified class including the class itself. + * + * @param c The class to retrieve the parent classes. + * @param parentFirst In parent-to-child order, otherwise child-to-parent. + * @param includeInterfaces Include interfaces. + * @return An iterator of parent classes in the class hierarchy. + */ + public static Iterator<Class<?>> getParentClasses(final Class<?> c, boolean parentFirst, boolean includeInterfaces) { + List<Class<?>> l = getParentClasses(new ArrayList<Class<?>>(), c, parentFirst, includeInterfaces); + return l.iterator(); + } + + private static List<Class<?>> getParentClasses(List<Class<?>> l, Class<?> c, boolean parentFirst, boolean includeInterfaces) { + if (parentFirst) { + if (includeInterfaces) + for (Class<?> i : c.getInterfaces()) + l.add(i); + if (c.getSuperclass() != Object.class && c.getSuperclass() != null) + getParentClasses(l, c.getSuperclass(), parentFirst, includeInterfaces); + l.add(c); + } else { + l.add(c); + if (c.getSuperclass() != Object.class && c.getSuperclass() != null) + getParentClasses(l, c.getSuperclass(), parentFirst, includeInterfaces); + if (includeInterfaces) + for (Class<?> i : c.getInterfaces()) + l.add(i); + } + return l; + } } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6998a7e/juneau-core/src/main/java/org/apache/juneau/parser/Parser.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/parser/Parser.java b/juneau-core/src/main/java/org/apache/juneau/parser/Parser.java index 231665a..6c94fc3 100644 --- a/juneau-core/src/main/java/org/apache/juneau/parser/Parser.java +++ b/juneau-core/src/main/java/org/apache/juneau/parser/Parser.java @@ -558,9 +558,9 @@ public abstract class Parser extends CoreObject { * @throws Exception */ protected void setParent(ClassMeta<?> cm, Object o, Object parent) throws Exception { - Method m = cm.getParentProperty(); + Setter m = cm.getParentProperty(); if (m != null) - m.invoke(o, parent); + m.set(o, parent); } /** @@ -574,9 +574,9 @@ public abstract class Parser extends CoreObject { */ protected void setName(ClassMeta<?> cm, Object o, Object name) throws Exception { if (cm != null) { - Method m = cm.getNameProperty(); + Setter m = cm.getNameProperty(); if (m != null) - m.invoke(o, name); + m.set(o, name); } } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6998a7e/juneau-core/src/main/java/org/apache/juneau/transform/InterfaceBeanFilterBuilder.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/transform/InterfaceBeanFilterBuilder.java b/juneau-core/src/main/java/org/apache/juneau/transform/InterfaceBeanFilterBuilder.java index 70300c0..80f49cf 100644 --- a/juneau-core/src/main/java/org/apache/juneau/transform/InterfaceBeanFilterBuilder.java +++ b/juneau-core/src/main/java/org/apache/juneau/transform/InterfaceBeanFilterBuilder.java @@ -12,7 +12,13 @@ // *************************************************************************************************************************** package org.apache.juneau.transform; +import static org.apache.juneau.internal.StringUtils.*; + +import java.util.*; + import org.apache.juneau.*; +import org.apache.juneau.annotation.*; +import org.apache.juneau.internal.*; /** * Simple bean filter that simply identifies a class to be used as an interface @@ -31,5 +37,39 @@ public class InterfaceBeanFilterBuilder extends BeanFilterBuilder { public InterfaceBeanFilterBuilder(Class<?> interfaceClass) { super(interfaceClass); interfaceClass(interfaceClass); + Map<Class<?>,Bean> annotations = ReflectionUtils.findAnnotationsMap(Bean.class, interfaceClass); + + ListIterator<Bean> li = new ArrayList<Bean>(annotations.values()).listIterator(annotations.size()); + while (li.hasPrevious()) { + Bean b = li.previous(); + + if (! b.properties().isEmpty()) + properties(split(b.properties(), ',')); + + if (! b.typeName().isEmpty()) + typeName(b.typeName()); + + if (b.sort()) + sortProperties(true); + + if (! b.excludeProperties().isEmpty()) + excludeProperties(split(b.excludeProperties(), ',')); + + try { + if (b.propertyNamer() != PropertyNamerDefault.class) + propertyNamer(b.propertyNamer()); + } catch (Exception e) { + throw new RuntimeException(e); + } + + if (b.interfaceClass() != Object.class) + interfaceClass(b.interfaceClass()); + + if (b.stopClass() != Object.class) + stopClass(b.stopClass()); + + if (b.beanDictionary().length > 0) + beanDictionary(b.beanDictionary()); + } } } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6998a7e/juneau-core/src/main/javadoc/overview.html ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/javadoc/overview.html b/juneau-core/src/main/javadoc/overview.html index bc56b1e..9f7d54f 100644 --- a/juneau-core/src/main/javadoc/overview.html +++ b/juneau-core/src/main/javadoc/overview.html @@ -3670,7 +3670,7 @@ <jd>/** GET request handler */</jd> <ja>@RestMethod</ja>(name=<js>"GET"</js>, path=<js>"/*"</js>, converters={Queryable.<jk>class</jk>,Traversable.<jk>class</jk>}) <jk>public</jk> HttpServletRequest doGet(RestRequest req, RestResponse res, <ja>@Properties</ja> ObjectMap properties) { - <jc>// Set the HtmlDocSerializer title programmatically. + <jc>// Set the HtmlDocSerializer title programmatically.</jc> res.setPageTitle(req.getPathInfo()); <jc>// Just echo the request back as the response.</jc> @@ -6307,6 +6307,8 @@ of HTML produced by {@link org.apache.juneau.html.HtmlDocSerializer}. </ul> + <li>{@link org.apache.juneau.annotation.NameProperty @NameProperty} and {@link org.apache.juneau.annotation.ParentProperty @ParentProperty} + can now be applied to fields. </ul> <h6 class='topic'>org.apache.juneau.rest</h6> @@ -6512,8 +6514,9 @@ <h6 class='topic'>org.apache.juneau.examples.rest</h6> <ul class='spaced-list'> <li>Many code enhancements make to examples to reflect new functionality. - <li>All pages now render aside comments to help explain what feature they're trying to explain. - <br><img class='bordered' src='doc-files/NewExamplesPage.png'> + <li>All pages now render aside comments to help explain what feature they're trying to explain using the + new features that allow you to customize various elements of the page. + <br><img class='bordered' width="50%" src='doc-files/NewExamplesPage.png'> </ul> </div> http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6998a7e/juneau-rest/src/main/java/org/apache/juneau/rest/RestContext.java ---------------------------------------------------------------------- diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestContext.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestContext.java index cec08c0..7fc0a94 100644 --- a/juneau-rest/src/main/java/org/apache/juneau/rest/RestContext.java +++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestContext.java @@ -543,6 +543,8 @@ public final class RestContext extends Context { if (r instanceof RestServlet) { RestServlet rs = (RestServlet)r; rs.init(childConfig); + if (rs.getContext() == null) + throw new RestException(SC_INTERNAL_SERVER_ERROR, "Servlet {0} not initialized. init(RestConfig) was not called. This can occur if you've overridden this method but didn't call super.init(RestConfig).", rs.getClass().getName()); path = childConfig.path; childResources.put(path, rs.getContext()); } else { http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6998a7e/juneau-rest/src/main/java/org/apache/juneau/rest/RestResponse.java ---------------------------------------------------------------------- diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestResponse.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestResponse.java index 725387a..e14771a 100644 --- a/juneau-rest/src/main/java/org/apache/juneau/rest/RestResponse.java +++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestResponse.java @@ -438,7 +438,7 @@ public final class RestResponse extends HttpServletResponseWrapper { * <li><code><ja>@RestResource</ja>(title)</code> annotation. * <li><code>{servletClass}.title</code> resource bundle value. * <li><code>info/title</code> entry in swagger file. - * <ol> + * </ol> * <p> * This field can contain variables (e.g. <js>"$L{my.localized.variable}"</js>). * <p> @@ -485,7 +485,7 @@ public final class RestResponse extends HttpServletResponseWrapper { * <li><code>summary</code> entry in swagger file for method. * <li><code>{servletClass}.description</code> resource bundle value. * <li><code>info/description</code> entry in swagger file. - * <ol> + * </ol> * <p> * This field can contain variables (e.g. <js>"$L{my.localized.variable}"</js>). * <p> http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6998a7e/juneau-rest/src/main/java/org/apache/juneau/rest/RestServlet.java ---------------------------------------------------------------------- diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestServlet.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestServlet.java index d91a7f0..7e6d98b 100644 --- a/juneau-rest/src/main/java/org/apache/juneau/rest/RestServlet.java +++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestServlet.java @@ -140,9 +140,9 @@ public abstract class RestServlet extends HttpServlet { throw new RestException(SC_INTERNAL_SERVER_ERROR, initException); } if (context == null) - throw new RestException(SC_INTERNAL_SERVER_ERROR, "Servlet not initialized. init(RestServletConfig) was not called."); + throw new RestException(SC_INTERNAL_SERVER_ERROR, "Servlet {0} not initialized. init(RestConfig) was not called. This can occur if you've overridden this method but didn't call super.init(RestConfig).", getClass().getName()); if (! isInitialized) - throw new RestException(SC_INTERNAL_SERVER_ERROR, "Servlet has not been initialized"); + throw new RestException(SC_INTERNAL_SERVER_ERROR, "Servlet {0} has not been initialized", getClass().getName()); context.getCallHandler().service(r1, r2); http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6998a7e/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/MethodSwagger.java ---------------------------------------------------------------------- diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/MethodSwagger.java b/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/MethodSwagger.java index b43affe..c7cb743 100644 --- a/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/MethodSwagger.java +++ b/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/MethodSwagger.java @@ -16,7 +16,7 @@ package org.apache.juneau.rest.annotation; * Extended annotation for {@link RestMethod#swagger() RestMethod.swagger()}. */ public @interface MethodSwagger { - + /** * Optional external documentation information for the exposed API. * <p> @@ -35,7 +35,11 @@ public @interface MethodSwagger { * * <h5 class='section'>Example:</h5> * <p class='bcode'> - * <ja>@RestMethod</ja>(externalDocs=<js>"{url:'http://juneau.apache.org'}"</js>) + * <ja>@RestMethod</ja>( + * swagger=<ja>@MethodSwagger</ja>( + * <js>"{url:'http://juneau.apache.org'}"</js> + * ) + * ) * </p> * <p> * This field can contain variables (e.g. "$L{my.localized.variable}"). @@ -57,7 +61,11 @@ public @interface MethodSwagger { * * <h5 class='section'>Example:</h5> * <p class='bcode'> - * <ja>@RestMethod</ja>(tags=<js>"foo,bar"</js>) + * <ja>@RestMethod</ja>( + * swagger=<ja>@MethodSwagger</ja>( + * tags=<js>"foo,bar"</js> + * ) + * ) * </p> * <p> * This field can contain variables (e.g. "$L{my.localized.variable}"). @@ -76,7 +84,11 @@ public @interface MethodSwagger { * * <h5 class='section'>Example:</h5> * <p class='bcode'> - * <ja>@RestMethod</ja>(deprecated=<jk>true</jk>) + * <ja>@RestMethod</ja>( + * swagger=<ja>@MethodSwagger</ja>( + * deprecated=<jk>true</jk> + * ) + * ) * </p> * <p> * This field can contain variables (e.g. "$L{my.localized.variable}"). @@ -96,12 +108,14 @@ public @interface MethodSwagger { * <ja>@RestMethod</ja>( * name=<js>"POST"</js>, path=<js>"/{a}"</js>, * description=<js>"This is my method."</js>, - * parameters={ - * <ja>@Parameter</ja>(in=<js>"path"</js>, name=<js>"a"</js>, description=<js>"The 'a' attribute"</js>), - * <ja>@Parameter</ja>(in=<js>"query"</js>, name=<js>"b"</js>, description=<js>"The 'b' parameter"</js>, required=<jk>true</jk>), - * <ja>@Parameter</ja>(in=<js>"body"</js>, description=<js>"The HTTP content"</js>), - * <ja>@Parameter</ja>(in=<js>"header"</js>, name=<js>"D"</js>, description=<js>"The 'D' header"</js>), - * } + * swagger=<ja>@MethodSwagger</ja>( + * parameters={ + * <ja>@Parameter</ja>(in=<js>"path"</js>, name=<js>"a"</js>, description=<js>"The 'a' attribute"</js>), + * <ja>@Parameter</ja>(in=<js>"query"</js>, name=<js>"b"</js>, description=<js>"The 'b' parameter"</js>, required=<jk>true</jk>), + * <ja>@Parameter</ja>(in=<js>"body"</js>, description=<js>"The HTTP content"</js>), + * <ja>@Parameter</ja>(in=<js>"header"</js>, name=<js>"D"</js>, description=<js>"The 'D' header"</js>), + * } + * ) * ) * </p> * This is functionally equivalent to specifying the following keys in the resource bundle for the class, except in this case @@ -132,16 +146,18 @@ public @interface MethodSwagger { * <p class='bcode'> * <ja>@RestMethod</ja>( * name=<js>"GET"</js>, path=<js>"/"</js>, - * responses={ - * <ja>@Response</ja>(200), - * <ja>@Response</ja>( - * value=302, - * description=<js>"Thing wasn't found here"</js>, - * headers={ - * <ja>@Parameter</ja>(name=<js>"Location"</js>, description=<js>"The place to find the thing"</js>) - * } - * ) - * } + * swagger=<ja>@MethodSwagger</ja>( + * responses={ + * <ja>@Response</ja>(200), + * <ja>@Response</ja>( + * value=302, + * description=<js>"Thing wasn't found here"</js>, + * headers={ + * <ja>@Parameter</ja>(name=<js>"Location"</js>, description=<js>"The place to find the thing"</js>) + * } + * ) + * } + * ) * ) * </p> * This is functionally equivalent to specifying the following keys in the resource bundle for the class, except in this case http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6998a7e/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/Parameter.java ---------------------------------------------------------------------- diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/Parameter.java b/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/Parameter.java index 26c0971..a5d10c8 100644 --- a/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/Parameter.java +++ b/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/Parameter.java @@ -29,7 +29,7 @@ import java.lang.annotation.*; * parameters={ * <ja>@Parameter</ja>(in=<js>"header"</js>, name=<js>"Range"</js>, description=<js>"$L{ContentRange.description}"</js>) * } - * ) + * ) * ) * <jk>public void</jk> doAnything(RestRequest req, RestResponse res, <ja>@Method</ja> String method) { * ... http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6998a7e/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/ResourceSwagger.java ---------------------------------------------------------------------- diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/ResourceSwagger.java b/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/ResourceSwagger.java index d925bbc..0ce1eca 100644 --- a/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/ResourceSwagger.java +++ b/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/ResourceSwagger.java @@ -53,7 +53,11 @@ public @interface ResourceSwagger { * * <h5 class='section'>Example:</h5> * <p class='bcode'> - * <ja>@RestResource</ja>(contact=<js>"{name:'John Smith',email:'[email protected]'}"</js>) + * <ja>@RestResource</ja>( + * swagger=<ja>@MethodSwagger</ja>( + * contact=<js>"{name:'John Smith',email:'[email protected]'}"</js> + * ) + * ) * </p> * <p> * This field can contain variables (e.g. "$L{my.localized.variable}"). @@ -82,7 +86,11 @@ public @interface ResourceSwagger { * * <h5 class='section'>Example:</h5> * <p class='bcode'> - * <ja>@RestResource</ja>(license=<js>"{name:'Apache 2.0',url:'http://www.apache.org/licenses/LICENSE-2.0.html'}"</js>) + * <ja>@RestResource</ja>( + * swagger=<ja>@MethodSwagger</ja>( + * license=<js>"{name:'Apache 2.0',url:'http://www.apache.org/licenses/LICENSE-2.0.html'}"</js> + * ) + * ) * </p> * <p> * This field can contain variables (e.g. "$L{my.localized.variable}"). @@ -133,7 +141,11 @@ public @interface ResourceSwagger { * * <h5 class='section'>Example:</h5> * <p class='bcode'> - * <ja>@RestResource</ja>(tags=<js>"[{name:'Foo',description:'Foobar'}]"</js>) + * <ja>@RestResource</ja>( + * swagger=<ja>@MethodSwagger</ja>( + * tags=<js>"[{name:'Foo',description:'Foobar'}]"</js> + * ) + * ) * </p> * <p> * This field can contain variables (e.g. "$L{my.localized.variable}"). @@ -162,7 +174,11 @@ public @interface ResourceSwagger { * * <h5 class='section'>Example:</h5> * <p class='bcode'> - * <ja>@RestResource</ja>(externalDocs=<js>"{url:'http://juneau.apache.org'}"</js>) + * <ja>@RestResource</ja>( + * swagger=<ja>@MethodSwagger</ja>( + * externalDocs=<js>"{url:'http://juneau.apache.org'}"</js> + * ) + * ) * </p> * <p> * This field can contain variables (e.g. "$L{my.localized.variable}").
