http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/0ce0e663/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParser.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParser.java b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParser.java index 68c9341..a22242b 100644 --- a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParser.java +++ b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParser.java @@ -56,7 +56,7 @@ public final class MsgPackParser extends InputStreamParser { BeanContext bc = session.getBeanContext(); if (nt == null) nt = (ClassMeta<T>)object(); - PojoTransform<T,Object> transform = (PojoTransform<T,Object>)nt.getPojoTransform(); + PojoSwap<T,Object> transform = (PojoSwap<T,Object>)nt.getPojoSwap(); ClassMeta<?> ft = nt.getTransformedClassMeta(); session.setCurrentClass(ft); @@ -182,7 +182,7 @@ public final class MsgPackParser extends InputStreamParser { } if (transform != null && o != null) - o = transform.normalize(o, nt); + o = transform.unswap(o, nt); if (outer != null) setParent(nt, o, outer);
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/0ce0e663/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializer.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializer.java b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializer.java index cd734b8..8795869 100644 --- a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializer.java +++ b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializer.java @@ -78,9 +78,9 @@ public class MsgPackSerializer extends OutputStreamSerializer { addClassAttr = (session.isAddClassAttrs() && ! eType.equals(aType)); // Transform if necessary - PojoTransform transform = aType.getPojoTransform(); // The transform + PojoSwap transform = aType.getPojoSwap(); // The transform if (transform != null) { - o = transform.transform(o); + o = transform.swap(o); // If the transform's getTransformedClass() method returns Object, we need to figure out // the actual type now. http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/0ce0e663/juneau-core/src/main/java/org/apache/juneau/package.html ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/package.html b/juneau-core/src/main/java/org/apache/juneau/package.html index 8b3d585..ca30bd5 100644 --- a/juneau-core/src/main/java/org/apache/juneau/package.html +++ b/juneau-core/src/main/java/org/apache/juneau/package.html @@ -157,10 +157,10 @@ BeanContext beanContext = BeanContext.<jsf>DEFAULT</jsf>; <jc>// Create a context from scratch with your own settings.</jc> - beanContext = <jk>new</jk> BeanContext().addTransforms(DateTransform.ISO8601DT.<jk>class</jk>); + beanContext = <jk>new</jk> BeanContext().addTransforms(DateSwap.ISO8601DT.<jk>class</jk>); <jc>// Clone and modify an existing context.</jc> - beanContext = BeanContext.<jsf>DEFAULT</jsf>.clone().addTransforms(DateTransform.ISO8601DT.<jk>class</jk>); + beanContext = BeanContext.<jsf>DEFAULT</jsf>.clone().addTransforms(DateSwap.ISO8601DT.<jk>class</jk>); </p> <p> The {@link org.apache.juneau.BeanContext} class is a highly-customizable class. @@ -204,7 +204,7 @@ Using the <ja>@Bean</ja> and <ja>@BeanProperty</ja> annotations, it's also possible to include non-standard properties (for example, getters or setters with non-standard names), or override the names of properties (for example, {@code "Name"} or {@code "fullName"} instead of {@code "name"}). </p> <p> - It should be noted that the {@link org.apache.juneau.transform.BeanTransform} class can also be used to exclude properties from beans. + It should be noted that the {@link org.apache.juneau.transform.BeanFilter} class can also be used to exclude properties from beans. However, only the annotations can be used to include non-standard properties or override property names. </p> <p> http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/0ce0e663/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 1a1bd79..9a858b1 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 @@ -94,10 +94,10 @@ import org.apache.juneau.utils.*; * </tr> * </table> * <p> - * In addition, any class types with {@link PojoTransform PojoTransforms} associated with them on the registered + * In addition, any class types with {@link PojoSwap PojoSwaps} associated with them on the registered * {@link #getBeanContext() beanContext} can also be passed in. * <p> - * For example, if the {@link CalendarTransform} transform is used to generalize {@code Calendar} objects to {@code String} objects. When registered + * For example, if the {@link CalendarSwap} transform is used to generalize {@code Calendar} objects to {@code String} objects. When registered * with this parser, you can construct {@code Calendar} objects from {@code Strings} using the following syntax... * <p class='bcode'> * Calendar c = parser.parse(<js>"'Sun Mar 03 04:05:06 EST 2001'"</js>, GregorianCalendar.<jk>class</jk>); @@ -579,7 +579,7 @@ public abstract class Parser extends CoreApi { if (type == null) type = (ClassMeta<T>)object(); - PojoTransform transform = type.getPojoTransform(); + PojoSwap transform = type.getPojoSwap(); ClassMeta<?> gType = type.getTransformedClassMeta(); Object o = s; @@ -600,7 +600,7 @@ public abstract class Parser extends CoreApi { } if (transform != null) - o = transform.normalize(o, type); + o = transform.unswap(o, type); return (T)o; } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/0ce0e663/juneau-core/src/main/java/org/apache/juneau/parser/ParserGroup.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/parser/ParserGroup.java b/juneau-core/src/main/java/org/apache/juneau/parser/ParserGroup.java index 0e810b8..4b86852 100644 --- a/juneau-core/src/main/java/org/apache/juneau/parser/ParserGroup.java +++ b/juneau-core/src/main/java/org/apache/juneau/parser/ParserGroup.java @@ -56,7 +56,7 @@ import org.apache.juneau.*; * * <jc>// Change settings on parsers simultaneously</jc> * g.setProperty(BeanContext.<jsf>BEAN_beansRequireSerializable</jsf>, <jk>true</jk>) - * .addTransforms(CalendarTransform.ISO8601DT.<jk>class</jk>) + * .addTransforms(CalendarSwap.ISO8601DT.<jk>class</jk>) * .lock(); * * <jc>// Find the appropriate parser by Content-Type</jc> @@ -254,7 +254,7 @@ public final class ParserGroup extends Lockable { /** * Shortcut for calling {@link Parser#addTransforms(Class[])} on all parsers in this group. * - * @param classes The classes to add bean transforms for to the underlying bean context of all parsers in this group. + * @param classes The classes to add bean filters for to the underlying bean context of all parsers in this group. * @throws LockedException If {@link #lock()} was called on this object. * @return This object (for method chaining). */ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/0ce0e663/juneau-core/src/main/java/org/apache/juneau/plaintext/PlainTextParser.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/plaintext/PlainTextParser.java b/juneau-core/src/main/java/org/apache/juneau/plaintext/PlainTextParser.java index 36ab2b8..e3aeda1 100644 --- a/juneau-core/src/main/java/org/apache/juneau/plaintext/PlainTextParser.java +++ b/juneau-core/src/main/java/org/apache/juneau/plaintext/PlainTextParser.java @@ -34,7 +34,7 @@ import org.apache.juneau.transform.*; * Essentially just converts plain text to POJOs via static <code>fromString()</code> or <code>valueOf()</code>, or * through constructors that take a single string argument. * <p> - * Also parses objects using a transform if the object class has an {@link PojoTransform PojoTransform<?,String>} transform defined on it. + * Also parses objects using a transform if the object class has an {@link PojoSwap PojoSwap<?,String>} transform defined on it. * * * <h6 class='topic'>Configurable properties</h6> http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/0ce0e663/juneau-core/src/main/java/org/apache/juneau/plaintext/PlainTextSerializer.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/plaintext/PlainTextSerializer.java b/juneau-core/src/main/java/org/apache/juneau/plaintext/PlainTextSerializer.java index fd916f3..6be54ea 100644 --- a/juneau-core/src/main/java/org/apache/juneau/plaintext/PlainTextSerializer.java +++ b/juneau-core/src/main/java/org/apache/juneau/plaintext/PlainTextSerializer.java @@ -32,7 +32,7 @@ import org.apache.juneau.transform.*; * <p> * Essentially converts POJOs to plain text using the <code>toString()</code> method. * <p> - * Also serializes objects using a transform if the object class has an {@link PojoTransform PojoTransform<?,String>} transform defined on it. + * Also serializes objects using a transform if the object class has an {@link PojoSwap PojoSwap<?,String>} transform defined on it. * * * <h6 class='topic'>Configurable properties</h6> http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/0ce0e663/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerGroup.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerGroup.java b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerGroup.java index 926075e..ef35199 100644 --- a/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerGroup.java +++ b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerGroup.java @@ -56,7 +56,7 @@ import org.apache.juneau.*; * * <jc>// Change settings for all serializers in the group and lock it.</jc> * g.setProperty(SerializerContext.<jsf>SERIALIZER_useIndentation</jsf>, <jk>true</jk>) - * .addTransforms(CalendarTransform.ISO8601DT.<jk>class</jk>) + * .addTransforms(CalendarSwap.ISO8601DT.<jk>class</jk>) * .lock(); * * <jc>// Find the appropriate serializer by Accept type</jc> @@ -277,7 +277,7 @@ public final class SerializerGroup extends Lockable { /** * Shortcut for calling {@link Serializer#addTransforms(Class[])} on all serializers in this group. * - * @param classes The classes to add bean transforms for to the underlying bean context of all serializers in this group. + * @param classes The classes to add bean filters for to the underlying bean context of all serializers in this group. * @throws LockedException If {@link #lock()} was called on this object. * @return This object (for method chaining). */ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/0ce0e663/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerSession.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerSession.java b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerSession.java index 0749066..f7d070a 100644 --- a/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerSession.java +++ b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerSession.java @@ -537,10 +537,10 @@ public class SerializerSession extends Session { public final Object generalize(Object o, ClassMeta<?> type) throws SerializeException { if (o == null) return null; - PojoTransform f = (type == null || type.isObject() ? getBeanContext().getClassMeta(o.getClass()).getPojoTransform() : type.getPojoTransform()); + PojoSwap f = (type == null || type.isObject() ? getBeanContext().getClassMeta(o.getClass()).getPojoSwap() : type.getPojoSwap()); if (f == null) return o; - return f.transform(o); + return f.swap(o); } /** http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/0ce0e663/juneau-core/src/main/java/org/apache/juneau/transform/AnnotationBeanFilter.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/transform/AnnotationBeanFilter.java b/juneau-core/src/main/java/org/apache/juneau/transform/AnnotationBeanFilter.java new file mode 100644 index 0000000..bf502b7 --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/transform/AnnotationBeanFilter.java @@ -0,0 +1,70 @@ +/*************************************************************************************************************************** + * 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.transform; + +import java.util.*; + +import org.apache.juneau.annotation.*; + +/** + * Bean filter constructed from a {@link Bean @Bean} annotation found on a class. + * <p> + * <b>*** Internal class - Not intended for external use ***</b> + * + * @author James Bognar ([email protected]) + * @param <T> The class type that this transform applies to. + */ +public final class AnnotationBeanFilter<T> extends BeanFilter<T> { + + /** + * Constructor. + * + * @param annotatedClass The class found to have a {@link Bean @Bean} annotation. + * @param annotations The {@link Bean @Bean} annotations found on the class and all parent classes in child-to-parent order. + */ + public AnnotationBeanFilter(Class<T> annotatedClass, List<Bean> annotations) { + super(annotatedClass); + + ListIterator<Bean> li = annotations.listIterator(annotations.size()); + while (li.hasPrevious()) { + Bean b = li.previous(); + + if (b.properties().length > 0 && getProperties() == null) + setProperties(b.properties()); + + if (b.sort()) + setSortProperties(true); + + if (b.excludeProperties().length > 0) + setExcludeProperties(b.excludeProperties()); + + setPropertyNamer(b.propertyNamer()); + + if (b.interfaceClass() != Object.class) + setInterfaceClass(b.interfaceClass()); + + if (b.stopClass() != Object.class) + setStopClass(b.stopClass()); + + if (! b.subTypeProperty().isEmpty()) { + setSubTypeProperty(b.subTypeProperty()); + + LinkedHashMap<Class<?>,String> subTypes = new LinkedHashMap<Class<?>,String>(); + for (BeanSubType bst : b.subTypes()) + subTypes.put(bst.type(), bst.id()); + + setSubTypes(subTypes); + } + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/0ce0e663/juneau-core/src/main/java/org/apache/juneau/transform/AnnotationBeanTransform.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/transform/AnnotationBeanTransform.java b/juneau-core/src/main/java/org/apache/juneau/transform/AnnotationBeanTransform.java deleted file mode 100644 index a6bc86f..0000000 --- a/juneau-core/src/main/java/org/apache/juneau/transform/AnnotationBeanTransform.java +++ /dev/null @@ -1,70 +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.transform; - -import java.util.*; - -import org.apache.juneau.annotation.*; - -/** - * Bean transform constructed from a {@link Bean @Bean} annotation found on a class. - * <p> - * <b>*** Internal class - Not intended for external use ***</b> - * - * @author James Bognar ([email protected]) - * @param <T> The class type that this transform applies to. - */ -public final class AnnotationBeanTransform<T> extends BeanTransform<T> { - - /** - * Constructor. - * - * @param annotatedClass The class found to have a {@link Bean @Bean} annotation. - * @param annotations The {@link Bean @Bean} annotations found on the class and all parent classes in child-to-parent order. - */ - public AnnotationBeanTransform(Class<T> annotatedClass, List<Bean> annotations) { - super(annotatedClass); - - ListIterator<Bean> li = annotations.listIterator(annotations.size()); - while (li.hasPrevious()) { - Bean b = li.previous(); - - if (b.properties().length > 0 && getProperties() == null) - setProperties(b.properties()); - - if (b.sort()) - setSortProperties(true); - - if (b.excludeProperties().length > 0) - setExcludeProperties(b.excludeProperties()); - - setPropertyNamer(b.propertyNamer()); - - if (b.interfaceClass() != Object.class) - setInterfaceClass(b.interfaceClass()); - - if (b.stopClass() != Object.class) - setStopClass(b.stopClass()); - - if (! b.subTypeProperty().isEmpty()) { - setSubTypeProperty(b.subTypeProperty()); - - LinkedHashMap<Class<?>,String> subTypes = new LinkedHashMap<Class<?>,String>(); - for (BeanSubType bst : b.subTypes()) - subTypes.put(bst.type(), bst.id()); - - setSubTypes(subTypes); - } - } - } -} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/0ce0e663/juneau-core/src/main/java/org/apache/juneau/transform/BeanFilter.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/transform/BeanFilter.java b/juneau-core/src/main/java/org/apache/juneau/transform/BeanFilter.java new file mode 100644 index 0000000..1538d0a --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/transform/BeanFilter.java @@ -0,0 +1,526 @@ +/*************************************************************************************************************************** + * 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.transform; + +import java.beans.*; +import java.lang.reflect.*; +import java.util.*; + +import org.apache.juneau.*; +import org.apache.juneau.annotation.*; +import org.apache.juneau.internal.*; + +/** + * Parent class for all bean filters. + * <p> + * Bean filters are used to control aspects of how beans are handled during serialization and parsing. + * <p> + * This class can be considered a programmatic equivalent to using the {@link Bean @Bean} annotation on bean classes. + * Thus, it can be used to perform the same function as the <code>@Bean</code> annotation when you don't have + * the ability to annotate those classes (e.g. you don't have access to the source code). + * <p> + * Note that value returned by the {@link Transform#forClass()} method is automatically determined through reflection + * when the no-arg constructor is used. + * + * <p> + * When defining bean filters, you can either call the setters in the contructor, or override getters. + * + * <h6 class='topic'>Example</h6> + * <p class='bcode'> + * <jc>// Create our serializer with a filter.</jc> + * WriterSerializer s = <jk>new</jk> JsonSerializer().addTransforms(AddressFilter.<jk>class</jk>); + * + * Address a = <jk>new</jk> Address(); + * String json = s.serialize(a); <jc>// Serializes only street, city, state.</jc> + * + * <jc>// Filter class defined via setters</jc> + * <jk>public class</jk> AddressFilter <jk>extends</jk> BeanFilter<Address> { + * <jk>public</jk> AddressFilter() { + * setProperties(<js>"street"</js>,<js>"city"</js>,<js>"state"</js>); + * } + * } + * + * <jc>// Filter class defined by overriding getters</jc> + * <jk>public class</jk> AddressFilter <jk>extends</jk> BeanFilter<Address> { + * <jk>public</jk> String[] getProperties() { + * <jk>return new</jk> String[]{<js>"street"</js>,<js>"city"</js>,<js>"state"</js>}; + * } + * } + * </p> + * <p> + * The examples in this class use the setters approach. + * + * <h6 class='topic'>Additional information</h6> + * See {@link org.apache.juneau.transform} for more information. + * + * + * @author James Bognar ([email protected]) + * @param <T> The class type that this filter applies to. + */ +public abstract class BeanFilter<T> extends Transform { + + private String[] properties, excludeProperties; + private LinkedHashMap<Class<?>, String> subTypes; + private String subTypeAttr; + private Class<? extends PropertyNamer> propertyNamer; + private Class<?> interfaceClass, stopClass; + private boolean sortProperties; + + /** + * Constructor that determines the for-class value using reflection. + */ + @SuppressWarnings("unchecked") + public BeanFilter() { + super(); + this.type = TransformType.BEAN; + + Class<?> c = this.getClass().getSuperclass(); + Type t = this.getClass().getGenericSuperclass(); + while (c != BeanFilter.class) { + t = c.getGenericSuperclass(); + c = c.getSuperclass(); + } + + // Attempt to determine the T and G classes using reflection. + if (t instanceof ParameterizedType) { + ParameterizedType pt = (ParameterizedType)t; + Type[] pta = pt.getActualTypeArguments(); + if (pta.length > 0) { + Type nType = pta[0]; + if (nType instanceof Class) + this.forClass = (Class<T>)nType; + + else + throw new RuntimeException("Unsupported parameter type: " + nType); + } + } + } + + /** + * Constructor that specifies the for-class explicitly. + * <p> + * This constructor only needs to be called when the class type cannot be inferred through reflection. + * + * <dl> + * <dt>Example:</dt> + * <dd> + * <p class='bcode'> + * <jk>public class</jk> SomeArbitraryFilter <jk>extends</jk> BeanFilter<?> { + * <jk>public</jk> SomeArbitraryFiter(Class<?> forClass) { + * <jk>super</jk>(forClass); + * ... + * } + * } + * </p> + * </dd> + * </dl> + * + * @param forClass The class that this bean filter applies to. + */ + public BeanFilter(Class<T> forClass) { + super(forClass); + this.type = TransformType.BEAN; + } + + /** + * Returns the set and order of names of properties associated with a bean class. + * + * @see #setProperties(String...) + * @return The name of the properties associated with a bean class, or <jk>null</jk> if all bean properties should be used. + */ + public String[] getProperties() { + return properties; + } + + /** + * Specifies the set and order of names of properties associated with a bean class. + * <p> + * The order specified is the same order that the entries will be returned by the {@link BeanMap#entrySet()} and related methods. + * <p> + * This method is an alternative to using the {@link Bean#properties()} annotation on a class. + * + * <dl> + * <dt>Example:</dt> + * <dd> + * <p class='bcode'> + * <jc>// Create our serializer with a filter.</jc> + * WriterSerializer s = <jk>new</jk> JsonSerializer().addTransforms(AddressFilter.<jk>class</jk>); + * + * Address a = <jk>new</jk> Address(); + * String json = s.serialize(a); <jc>// Serializes only street, city, state.</jc> + * + * <jc>// Transform class</jc> + * <jk>public class</jk> AddressFilter <jk>extends</jk> BeanFilter<Address> { + * <jk>public</jk> AddressFilter() { + * setProperties(<js>"street"</js>,<js>"city"</js>,<js>"state"</js>); + * } + * } + * </p> + * </dd> + * </dl> + * + * @param properties The name of the properties associated with a bean class. + * @return This object (for method chaining). + */ + public BeanFilter<T> setProperties(String...properties) { + this.properties = properties; + return this; + } + + /** + * Same as {@link #setProperties(String[])} but pass in a comma-delimited list of values. + * + * @param properties A comma-delimited list of properties. + * @return This object (for method chaining). + */ + public BeanFilter<T> setProperties(String properties) { + return setProperties(StringUtils.split(properties, ',')); + } + + /** + * Returns <jk>true</jk> if the properties defined on this bean class should be ordered alphabetically. + * <p> + * This method is only used when the {@link #getProperties()} method returns <jk>null</jk>. + * Otherwise, the ordering of the properties in the returned value is used. + * + * @see #setSortProperties(boolean) + * @return <jk>true</jk> if bean properties should be sorted. + */ + public boolean isSortProperties() { + return sortProperties; + } + + /** + * Specifies whether the properties on this bean should be ordered alphabetically. + * <p> + * This method is ignored if the {@link #getProperties()} method does not return <jk>null</jk>. + * <p> + * This method is an alternative to using the {@link Bean#sort()} annotation on a class. + * + * @param sortProperties The new value for the sort properties property. + * @return This object (for method chaining). + */ + public BeanFilter<T> setSortProperties(boolean sortProperties) { + this.sortProperties = sortProperties; + return this; + } + + /** + * Returns the list of properties to ignore on a bean. + * + * @see #setExcludeProperties(String...) + * @return The name of the properties to ignore on a bean, or <jk>null</jk> to not ignore any properties. + */ + public String[] getExcludeProperties() { + return excludeProperties; + } + + /** + * Specifies a list of properties to ignore on a bean. + * <p> + * This method is an alternative to using the {@link Bean#excludeProperties()} annotation on a class. + * + * <dl> + * <dt>Example:</dt> + * <dd> + * <p class='bcode'> + * <jc>// Create our serializer with a filter.</jc> + * WriterSerializer s = <jk>new</jk> JsonSerializer().addTransforms(NoCityOrStateFilter.<jk>class</jk>); + * + * Address a = <jk>new</jk> Address(); + * String json = s.serialize(a); <jc>// Excludes city and state.</jc> + * + * <jc>// Transform class</jc> + * <jk>public class</jk> NoCityOrStateFilter <jk>extends</jk> BeanFilter<Address> { + * <jk>public</jk> AddressFilter() { + * setExcludeProperties(<js>"city"</js>,<js>"state"</js>); + * } + * } + * </p> + * </dd> + * </dl> + * + * @param excludeProperties The name of the properties to ignore on a bean class. + * @return This object (for method chaining). + */ + public BeanFilter<T> setExcludeProperties(String...excludeProperties) { + this.excludeProperties = excludeProperties; + return this; + } + + /** + * Same as {@link #setExcludeProperties(String[])} but pass in a comma-delimited list of values. + * + * @param excludeProperties A comma-delimited list of properties to eclipse. + * @return This object (for method chaining). + */ + public BeanFilter<T> setExcludeProperties(String excludeProperties) { + return setExcludeProperties(StringUtils.split(excludeProperties, ',')); + } + + /** + * Returns the {@link PropertyNamer} associated with the bean to tailor the names of bean properties. + * + * @see #setPropertyNamer(Class) + * @return The property namer class, or <jk>null</jk> if no property namer is associated with this bean property. + */ + public Class<? extends PropertyNamer> getPropertyNamer() { + return propertyNamer; + } + + /** + * Associates a {@link PropertyNamer} with this bean to tailor the names of the bean properties. + * <p> + * Property namers are used to transform bean property names from standard form to some other form. + * For example, the {@link PropertyNamerDashedLC} will convert property names to dashed-lowercase, and + * these will be used as attribute names in JSON, and element names in XML. + * <p> + * This method is an alternative to using the {@link Bean#propertyNamer()} annotation on a class. + * + * @param propertyNamer The property namer class. + * @return This object (for method chaining). + */ + public BeanFilter<T> setPropertyNamer(Class<? extends PropertyNamer> propertyNamer) { + this.propertyNamer = propertyNamer; + return this; + } + + /** + * Returns the name of the sub type property associated with the bean class. + * + * @see #setSubTypeProperty(String) + * @return The sub type property name, or <jk>null</jk> if bean has no subtypes defined. + */ + public String getSubTypeProperty() { + return subTypeAttr; + } + + /** + * Defines a virtual property on a superclass that identifies bean subtype classes. + * <p> + * In the following example, the abstract class has two subclasses that are differentiated + * by a property called <code>subType</code> + * + * <p class='bcode'> + * <jc>// Abstract superclass</jc> + * <jk>public abstract class</jk> A { + * <jk>public</jk> String <jf>f0</jf> = <js>"f0"</js>; + * } + * + * <jc>// Subclass 1</jc> + * <jk>public class</jk> A1 <jk>extends</jk> A { + * <jk>public</jk> String <jf>f1</jf>; + * } + * + * <jc>// Subclass 2</jc> + * <jk>public class</jk> A2 <jk>extends</jk> A { + * <jk>public</jk> String <jf>f2</jf>; + * } + * + * <jc>// Transform for defining subtypes</jc> + * <jk>public class</jk> ATransform <jk>extends</jk> BeanFilter<A> { + * <jk>public</jk> ATransform() { + * setSubTypeProperty(<js>"subType"</js>); + * addSubType(Al.<jk>class</jk>, <js>"A1"</js>); + * addSubType(A2.<jk>class</jk>, <js>"A2"</js>); + * } + * } + * </p> + * <p> + * The following shows what happens when serializing a subclassed object to JSON: + * <p class='bcode'> + * JsonSerializer s = <jk>new</jk> JsonSerializer().addTransforms(ATransform.<jk>class</jk>); + * A1 a1 = <jk>new</jk> A1(); + * a1.<jf>f1</jf> = <js>"f1"</js>; + * String r = s.serialize(a1); + * <jsm>assertEquals</jsm>(<js>"{subType:'A1',f1:'f1',f0:'f0'}"</js>, r); + * </p> + * <p> + * The following shows what happens when parsing back into the original object. + * <p class='bcode'> + * JsonParser p = <jk>new</jk> JsonParser().addTransforms(ATransform.<jk>class</jk>); + * A a = p.parse(r, A.<jk>class</jk>); + * <jsm>assertTrue</jsm>(a <jk>instanceof</jk> A1); + * </p> + * <p> + * This method is an alternative to using the {@link Bean#subTypeProperty()} annotation on a class. + * + * @param subTypeAttr The name of the attribute representing the subtype. + * @return This object (for method chaining). + */ + public BeanFilter<T> setSubTypeProperty(String subTypeAttr) { + this.subTypeAttr = subTypeAttr; + return this; + } + + /** + * Returns the subtypes associated with the bean class. + * + * @see #setSubTypeProperty(String) + * @return The set of sub types associated with this bean class, or <jk>null</jk> if bean has no subtypes defined. + */ + public LinkedHashMap<Class<?>, String> getSubTypes() { + return subTypes; + } + + /** + * Specifies the set of subclasses of this bean class in addition to a string identifier for that subclass. + * + * @see #setSubTypeProperty(String) + * @param subTypes the map of subtype classes to subtype identifier strings. + * @return This object (for method chaining). + */ + public BeanFilter<T> setSubTypes(LinkedHashMap<Class<?>, String> subTypes) { + this.subTypes = subTypes; + return this; + } + + /** + * Convenience method for adding a single subtype in leu of using {@link #setSubTypes(LinkedHashMap)} in one call. + * + * @see #setSubTypeProperty(String) + * @param c The subtype class. + * @param id The subtype identifier string for the specified subtype class. + * @return This object (for method chaining). + */ + public BeanFilter<T> addSubType(Class<?> c, String id) { + if (subTypes == null) + subTypes = new LinkedHashMap<Class<?>, String>(); + subTypes.put(c, id); + return this; + } + + /** + * Returns the interface class associated with this class. + * + * @see #setInterfaceClass(Class) + * @return The interface class associated with this class, or <jk>null</jk> if no interface class is associated. + */ + public Class<?> getInterfaceClass() { + return interfaceClass; + } + + /** + * Identifies a class to be used as the interface class for this and all subclasses. + * <p> + * Functionally equivalent to using the {@link Bean#interfaceClass()} annotation. + * <p> + * When specified, only the list of properties defined on the interface class will be used during serialization. + * Additional properties on subclasses will be ignored. + * <p class='bcode'> + * <jc>// Parent class</jc> + * <jk>public abstract class</jk> A { + * <jk>public</jk> String <jf>f0</jf> = <js>"f0"</js>; + * } + * + * <jc>// Sub class</jc> + * <jk>public class</jk> A1 <jk>extends</jk> A { + * <jk>public</jk> String <jf>f1</jf> = <js>"f1"</js>; + * } + * + * <jc>// Transform class</jc> + * <jk>public class</jk> ATransform <jk>extends</jk> BeanFilter<A> { + * <jk>public</jk> ATransform() { + * setInterfaceClass(A.<jk>class</jk>); + * } + * } + * + * JsonSerializer s = new JsonSerializer().addTransforms(ATransform.<jk>class</jk>); + * A1 a1 = <jk>new</jk> A1(); + * String r = s.serialize(a1); + * <jsm>assertEquals</jsm>(<js>"{f0:'f0'}"</js>, r); <jc>// Note f1 is not serialized</jc> + * </p> + * <p> + * Note that this filter can be used on the parent class so that it filters to all child classes, + * or can be set individually on the child classes. + * <p> + * This method is an alternative to using the {@link Bean#interfaceClass()}} annotation. + * + * @param interfaceClass The interface class. + * @return This object (for method chaining). + */ + public BeanFilter<T> setInterfaceClass(Class<?> interfaceClass) { + this.interfaceClass = interfaceClass; + return this; + } + + /** + * Returns the stop class associated with this class. + * + * @see #setStopClass(Class) + * @return The stop class associated with this class, or <jk>null</jk> if no stop class is associated. + */ + public Class<?> getStopClass() { + return stopClass; + } + + /** + * Identifies a stop class for this class and all subclasses. + * <p> + * Functionally equivalent to using the {@link Bean#stopClass()} annotation. + * <p> + * Identical in purpose to the stop class specified by {@link Introspector#getBeanInfo(Class, Class)}. + * Any properties in the stop class or in its baseclasses will be ignored during analysis. + * <p> + * For example, in the following class hierarchy, instances of <code>C3</code> will include property <code>p3</code>, but + * not <code>p1</code> or <code>p2</code>. + * <p class='bcode'> + * <jk>public class</jk> C1 { + * <jk>public int</jk> getP1(); + * } + * + * <jk>public class</jk> C2 <jk>extends</jk> C1 { + * <jk>public int</jk> getP2(); + * } + * + * <ja>@Bean</ja>(stopClass=C2.<jk>class</jk>) + * <jk>public class</jk> C3 <jk>extends</jk> C2 { + * <jk>public int</jk> getP3(); + * } + * </p> + * + * @param stopClass The stop class. + * @return This object (for method chaining). + */ + public BeanFilter<T> setStopClass(Class<?> stopClass) { + this.stopClass = stopClass; + return this; + } + + /** + * Subclasses can override this property to convert property values to some other + * object just before serialization. + * + * @param bean The bean from which the property was read. + * @param name The property name. + * @param value The value just extracted from calling the bean getter. + * @return The value to serialize. Default is just to return the existing value. + */ + public Object readProperty(Object bean, String name, Object value) { + return value; + } + + /** + * Subclasses can override this property to convert property values to some other + * object just before calling the bean setter. + * + * @param bean The bean from which the property was read. + * @param name The property name. + * @param value The value just parsed. + * @return <jk>true</jk> if we set the property, <jk>false</jk> if we should allow the + * framework to call the setter. + */ + public boolean writeProperty(Object bean, String name, Object value) { + return false; + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/0ce0e663/juneau-core/src/main/java/org/apache/juneau/transform/BeanTransform.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/transform/BeanTransform.java b/juneau-core/src/main/java/org/apache/juneau/transform/BeanTransform.java deleted file mode 100644 index f0b9f3c..0000000 --- a/juneau-core/src/main/java/org/apache/juneau/transform/BeanTransform.java +++ /dev/null @@ -1,526 +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.transform; - -import java.beans.*; -import java.lang.reflect.*; -import java.util.*; - -import org.apache.juneau.*; -import org.apache.juneau.annotation.*; -import org.apache.juneau.internal.*; - -/** - * Parent class for all bean transforms. - * <p> - * Bean transforms are used to control aspects of how beans are handled during serialization and parsing. - * <p> - * This class can be considered a programmatic equivalent to using the {@link Bean @Bean} annotation on bean classes. - * Thus, it can be used to perform the same function as the <code>@Bean</code> annotation when you don't have - * the ability to annotate those classes (e.g. you don't have access to the source code). - * <p> - * Note that value returned by the {@link Transform#forClass()} method is automatically determined through reflection - * when the no-arg constructor is used. - * - * <p> - * When defining bean transforms, you can either call the setters in the contructor, or override getters. - * - * <h6 class='topic'>Example</h6> - * <p class='bcode'> - * <jc>// Create our serializer with a transform.</jc> - * WriterSerializer s = <jk>new</jk> JsonSerializer().addTransforms(AddressTransform.<jk>class</jk>); - * - * Address a = <jk>new</jk> Address(); - * String json = s.serialize(a); <jc>// Serializes only street, city, state.</jc> - * - * <jc>// Transform class defined via setters</jc> - * <jk>public class</jk> AddressTransform <jk>extends</jk> BeanTransform<Address> { - * <jk>public</jk> AddressTransform() { - * setProperties(<js>"street"</js>,<js>"city"</js>,<js>"state"</js>); - * } - * } - * - * <jc>// Transform class defined by overriding getters</jc> - * <jk>public class</jk> AddressTransform <jk>extends</jk> BeanTransform<Address> { - * <jk>public</jk> String[] getProperties() { - * <jk>return new</jk> String[]{<js>"street"</js>,<js>"city"</js>,<js>"state"</js>}; - * } - * } - * </p> - * <p> - * The examples in this class use the setters approach. - * - * <h6 class='topic'>Additional information</h6> - * See {@link org.apache.juneau.transform} for more information. - * - * - * @author James Bognar ([email protected]) - * @param <T> The class type that this transform applies to. - */ -public abstract class BeanTransform<T> extends Transform { - - private String[] properties, excludeProperties; - private LinkedHashMap<Class<?>, String> subTypes; - private String subTypeAttr; - private Class<? extends PropertyNamer> propertyNamer; - private Class<?> interfaceClass, stopClass; - private boolean sortProperties; - - /** - * Constructor that determines the for-class value using reflection. - */ - @SuppressWarnings("unchecked") - public BeanTransform() { - super(); - this.type = TransformType.BEAN; - - Class<?> c = this.getClass().getSuperclass(); - Type t = this.getClass().getGenericSuperclass(); - while (c != BeanTransform.class) { - t = c.getGenericSuperclass(); - c = c.getSuperclass(); - } - - // Attempt to determine the T and G classes using reflection. - if (t instanceof ParameterizedType) { - ParameterizedType pt = (ParameterizedType)t; - Type[] pta = pt.getActualTypeArguments(); - if (pta.length > 0) { - Type nType = pta[0]; - if (nType instanceof Class) - this.forClass = (Class<T>)nType; - - else - throw new RuntimeException("Unsupported parameter type: " + nType); - } - } - } - - /** - * Constructor that specifies the for-class explicitly. - * <p> - * This constructor only needs to be called when the class type cannot be inferred through reflection. - * - * <dl> - * <dt>Example:</dt> - * <dd> - * <p class='bcode'> - * <jk>public class</jk> SomeArbitraryTransform <jk>extends</jk> BeanTransform<?> { - * <jk>public</jk> SomeArbitraryTransform(Class<?> forClass) { - * <jk>super</jk>(forClass); - * ... - * } - * } - * </p> - * </dd> - * </dl> - * - * @param forClass The class that this bean transform applies to. - */ - public BeanTransform(Class<T> forClass) { - super(forClass); - this.type = TransformType.BEAN; - } - - /** - * Returns the set and order of names of properties associated with a bean class. - * - * @see #setProperties(String...) - * @return The name of the properties associated with a bean class, or <jk>null</jk> if all bean properties should be used. - */ - public String[] getProperties() { - return properties; - } - - /** - * Specifies the set and order of names of properties associated with a bean class. - * <p> - * The order specified is the same order that the entries will be returned by the {@link BeanMap#entrySet()} and related methods. - * <p> - * This method is an alternative to using the {@link Bean#properties()} annotation on a class. - * - * <dl> - * <dt>Example:</dt> - * <dd> - * <p class='bcode'> - * <jc>// Create our serializer with a transform.</jc> - * WriterSerializer s = <jk>new</jk> JsonSerializer().addTransforms(AddressTransform.<jk>class</jk>); - * - * Address a = <jk>new</jk> Address(); - * String json = s.serialize(a); <jc>// Serializes only street, city, state.</jc> - * - * <jc>// Transform class</jc> - * <jk>public class</jk> AddressTransform <jk>extends</jk> BeanTransform<Address> { - * <jk>public</jk> AddressTransform() { - * setProperties(<js>"street"</js>,<js>"city"</js>,<js>"state"</js>); - * } - * } - * </p> - * </dd> - * </dl> - * - * @param properties The name of the properties associated with a bean class. - * @return This object (for method chaining). - */ - public BeanTransform<T> setProperties(String...properties) { - this.properties = properties; - return this; - } - - /** - * Same as {@link #setProperties(String[])} but pass in a comma-delimited list of values. - * - * @param properties A comma-delimited list of properties. - * @return This object (for method chaining). - */ - public BeanTransform<T> setProperties(String properties) { - return setProperties(StringUtils.split(properties, ',')); - } - - /** - * Returns <jk>true</jk> if the properties defined on this bean class should be ordered alphabetically. - * <p> - * This method is only used when the {@link #getProperties()} method returns <jk>null</jk>. - * Otherwise, the ordering of the properties in the returned value is used. - * - * @see #setSortProperties(boolean) - * @return <jk>true</jk> if bean properties should be sorted. - */ - public boolean isSortProperties() { - return sortProperties; - } - - /** - * Specifies whether the properties on this bean should be ordered alphabetically. - * <p> - * This method is ignored if the {@link #getProperties()} method does not return <jk>null</jk>. - * <p> - * This method is an alternative to using the {@link Bean#sort()} annotation on a class. - * - * @param sortProperties The new value for the sort properties property. - * @return This object (for method chaining). - */ - public BeanTransform<T> setSortProperties(boolean sortProperties) { - this.sortProperties = sortProperties; - return this; - } - - /** - * Returns the list of properties to ignore on a bean. - * - * @see #setExcludeProperties(String...) - * @return The name of the properties to ignore on a bean, or <jk>null</jk> to not ignore any properties. - */ - public String[] getExcludeProperties() { - return excludeProperties; - } - - /** - * Specifies a list of properties to ignore on a bean. - * <p> - * This method is an alternative to using the {@link Bean#excludeProperties()} annotation on a class. - * - * <dl> - * <dt>Example:</dt> - * <dd> - * <p class='bcode'> - * <jc>// Create our serializer with a transform.</jc> - * WriterSerializer s = <jk>new</jk> JsonSerializer().addTransforms(NoCityOrStateTransform.<jk>class</jk>); - * - * Address a = <jk>new</jk> Address(); - * String json = s.serialize(a); <jc>// Excludes city and state.</jc> - * - * <jc>// Transform class</jc> - * <jk>public class</jk> NoCityOrStateTransform <jk>extends</jk> BeanTransform<Address> { - * <jk>public</jk> AddressTransform() { - * setExcludeProperties(<js>"city"</js>,<js>"state"</js>); - * } - * } - * </p> - * </dd> - * </dl> - * - * @param excludeProperties The name of the properties to ignore on a bean class. - * @return This object (for method chaining). - */ - public BeanTransform<T> setExcludeProperties(String...excludeProperties) { - this.excludeProperties = excludeProperties; - return this; - } - - /** - * Same as {@link #setExcludeProperties(String[])} but pass in a comma-delimited list of values. - * - * @param excludeProperties A comma-delimited list of properties to eclipse. - * @return This object (for method chaining). - */ - public BeanTransform<T> setExcludeProperties(String excludeProperties) { - return setExcludeProperties(StringUtils.split(excludeProperties, ',')); - } - - /** - * Returns the {@link PropertyNamer} associated with the bean to tailor the names of bean properties. - * - * @see #setPropertyNamer(Class) - * @return The property namer class, or <jk>null</jk> if no property namer is associated with this bean property. - */ - public Class<? extends PropertyNamer> getPropertyNamer() { - return propertyNamer; - } - - /** - * Associates a {@link PropertyNamer} with this bean to tailor the names of the bean properties. - * <p> - * Property namers are used to transform bean property names from standard form to some other form. - * For example, the {@link PropertyNamerDashedLC} will convert property names to dashed-lowercase, and - * these will be used as attribute names in JSON, and element names in XML. - * <p> - * This method is an alternative to using the {@link Bean#propertyNamer()} annotation on a class. - * - * @param propertyNamer The property namer class. - * @return This object (for method chaining). - */ - public BeanTransform<T> setPropertyNamer(Class<? extends PropertyNamer> propertyNamer) { - this.propertyNamer = propertyNamer; - return this; - } - - /** - * Returns the name of the sub type property associated with the bean class. - * - * @see #setSubTypeProperty(String) - * @return The sub type property name, or <jk>null</jk> if bean has no subtypes defined. - */ - public String getSubTypeProperty() { - return subTypeAttr; - } - - /** - * Defines a virtual property on a superclass that identifies bean subtype classes. - * <p> - * In the following example, the abstract class has two subclasses that are differentiated - * by a property called <code>subType</code> - * - * <p class='bcode'> - * <jc>// Abstract superclass</jc> - * <jk>public abstract class</jk> A { - * <jk>public</jk> String <jf>f0</jf> = <js>"f0"</js>; - * } - * - * <jc>// Subclass 1</jc> - * <jk>public class</jk> A1 <jk>extends</jk> A { - * <jk>public</jk> String <jf>f1</jf>; - * } - * - * <jc>// Subclass 2</jc> - * <jk>public class</jk> A2 <jk>extends</jk> A { - * <jk>public</jk> String <jf>f2</jf>; - * } - * - * <jc>// Transform for defining subtypes</jc> - * <jk>public class</jk> ATransform <jk>extends</jk> BeanTransform<A> { - * <jk>public</jk> ATransform() { - * setSubTypeProperty(<js>"subType"</js>); - * addSubType(Al.<jk>class</jk>, <js>"A1"</js>); - * addSubType(A2.<jk>class</jk>, <js>"A2"</js>); - * } - * } - * </p> - * <p> - * The following shows what happens when serializing a subclassed object to JSON: - * <p class='bcode'> - * JsonSerializer s = <jk>new</jk> JsonSerializer().addTransforms(ATransform.<jk>class</jk>); - * A1 a1 = <jk>new</jk> A1(); - * a1.<jf>f1</jf> = <js>"f1"</js>; - * String r = s.serialize(a1); - * <jsm>assertEquals</jsm>(<js>"{subType:'A1',f1:'f1',f0:'f0'}"</js>, r); - * </p> - * <p> - * The following shows what happens when parsing back into the original object. - * <p class='bcode'> - * JsonParser p = <jk>new</jk> JsonParser().addTransforms(ATransform.<jk>class</jk>); - * A a = p.parse(r, A.<jk>class</jk>); - * <jsm>assertTrue</jsm>(a <jk>instanceof</jk> A1); - * </p> - * <p> - * This method is an alternative to using the {@link Bean#subTypeProperty()} annotation on a class. - * - * @param subTypeAttr The name of the attribute representing the subtype. - * @return This object (for method chaining). - */ - public BeanTransform<T> setSubTypeProperty(String subTypeAttr) { - this.subTypeAttr = subTypeAttr; - return this; - } - - /** - * Returns the subtypes associated with the bean class. - * - * @see #setSubTypeProperty(String) - * @return The set of sub types associated with this bean class, or <jk>null</jk> if bean has no subtypes defined. - */ - public LinkedHashMap<Class<?>, String> getSubTypes() { - return subTypes; - } - - /** - * Specifies the set of subclasses of this bean class in addition to a string identifier for that subclass. - * - * @see #setSubTypeProperty(String) - * @param subTypes the map of subtype classes to subtype identifier strings. - * @return This object (for method chaining). - */ - public BeanTransform<T> setSubTypes(LinkedHashMap<Class<?>, String> subTypes) { - this.subTypes = subTypes; - return this; - } - - /** - * Convenience method for adding a single subtype in leu of using {@link #setSubTypes(LinkedHashMap)} in one call. - * - * @see #setSubTypeProperty(String) - * @param c The subtype class. - * @param id The subtype identifier string for the specified subtype class. - * @return This object (for method chaining). - */ - public BeanTransform<T> addSubType(Class<?> c, String id) { - if (subTypes == null) - subTypes = new LinkedHashMap<Class<?>, String>(); - subTypes.put(c, id); - return this; - } - - /** - * Returns the interface class associated with this class. - * - * @see #setInterfaceClass(Class) - * @return The interface class associated with this class, or <jk>null</jk> if no interface class is associated. - */ - public Class<?> getInterfaceClass() { - return interfaceClass; - } - - /** - * Identifies a class to be used as the interface class for this and all subclasses. - * <p> - * Functionally equivalent to using the {@link Bean#interfaceClass()} annotation. - * <p> - * When specified, only the list of properties defined on the interface class will be used during serialization. - * Additional properties on subclasses will be ignored. - * <p class='bcode'> - * <jc>// Parent class</jc> - * <jk>public abstract class</jk> A { - * <jk>public</jk> String <jf>f0</jf> = <js>"f0"</js>; - * } - * - * <jc>// Sub class</jc> - * <jk>public class</jk> A1 <jk>extends</jk> A { - * <jk>public</jk> String <jf>f1</jf> = <js>"f1"</js>; - * } - * - * <jc>// Transform class</jc> - * <jk>public class</jk> ATransform <jk>extends</jk> BeanTransform<A> { - * <jk>public</jk> ATransform() { - * setInterfaceClass(A.<jk>class</jk>); - * } - * } - * - * JsonSerializer s = new JsonSerializer().addTransforms(ATransform.<jk>class</jk>); - * A1 a1 = <jk>new</jk> A1(); - * String r = s.serialize(a1); - * <jsm>assertEquals</jsm>(<js>"{f0:'f0'}"</js>, r); <jc>// Note f1 is not serialized</jc> - * </p> - * <p> - * Note that this transform can be used on the parent class so that it transforms to all child classes, - * or can be set individually on the child classes. - * <p> - * This method is an alternative to using the {@link Bean#interfaceClass()}} annotation. - * - * @param interfaceClass The interface class. - * @return This object (for method chaining). - */ - public BeanTransform<T> setInterfaceClass(Class<?> interfaceClass) { - this.interfaceClass = interfaceClass; - return this; - } - - /** - * Returns the stop class associated with this class. - * - * @see #setStopClass(Class) - * @return The stop class associated with this class, or <jk>null</jk> if no stop class is associated. - */ - public Class<?> getStopClass() { - return stopClass; - } - - /** - * Identifies a stop class for this class and all subclasses. - * <p> - * Functionally equivalent to using the {@link Bean#stopClass()} annotation. - * <p> - * Identical in purpose to the stop class specified by {@link Introspector#getBeanInfo(Class, Class)}. - * Any properties in the stop class or in its baseclasses will be ignored during analysis. - * <p> - * For example, in the following class hierarchy, instances of <code>C3</code> will include property <code>p3</code>, but - * not <code>p1</code> or <code>p2</code>. - * <p class='bcode'> - * <jk>public class</jk> C1 { - * <jk>public int</jk> getP1(); - * } - * - * <jk>public class</jk> C2 <jk>extends</jk> C1 { - * <jk>public int</jk> getP2(); - * } - * - * <ja>@Bean</ja>(stopClass=C2.<jk>class</jk>) - * <jk>public class</jk> C3 <jk>extends</jk> C2 { - * <jk>public int</jk> getP3(); - * } - * </p> - * - * @param stopClass The stop class. - * @return This object (for method chaining). - */ - public BeanTransform<T> setStopClass(Class<?> stopClass) { - this.stopClass = stopClass; - return this; - } - - /** - * Subclasses can override this property to convert property values to some other - * object just before serialization. - * - * @param bean The bean from which the property was read. - * @param name The property name. - * @param value The value just extracted from calling the bean getter. - * @return The value to serialize. Default is just to return the existing value. - */ - public Object readProperty(Object bean, String name, Object value) { - return value; - } - - /** - * Subclasses can override this property to convert property values to some other - * object just before calling the bean setter. - * - * @param bean The bean from which the property was read. - * @param name The property name. - * @param value The value just parsed. - * @return <jk>true</jk> if we set the property, <jk>false</jk> if we should allow the - * framework to call the setter. - */ - public boolean writeProperty(Object bean, String name, Object value) { - return false; - } -} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/0ce0e663/juneau-core/src/main/java/org/apache/juneau/transform/InterfaceBeanFilter.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/transform/InterfaceBeanFilter.java b/juneau-core/src/main/java/org/apache/juneau/transform/InterfaceBeanFilter.java new file mode 100644 index 0000000..2e9f95c --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/transform/InterfaceBeanFilter.java @@ -0,0 +1,39 @@ +/*************************************************************************************************************************** + * 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.transform; + +import org.apache.juneau.*; + + +/** + * Simple bean filter that simply identifies a class to be used as an interface + * class for all child classes. + * <p> + * These objects are created when you pass in non-<code>Transform</code> classes to {@link ContextFactory#addToProperty(String,Object)}, + * and are equivalent to adding a <code><ja>@Bean</ja>(interfaceClass=Foo.<jk>class</jk>)</code> annotation on the <code>Foo</code> class. + * + * @author James Bognar ([email protected]) + * @param <T> The class type that this transform applies to. + */ +public class InterfaceBeanFilter<T> extends BeanFilter<T> { + + /** + * Constructor. + * + * @param interfaceClass The class to use as an interface on all child classes. + */ + public InterfaceBeanFilter(Class<T> interfaceClass) { + super(interfaceClass); + setInterfaceClass(interfaceClass); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/0ce0e663/juneau-core/src/main/java/org/apache/juneau/transform/InterfaceBeanTransform.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/transform/InterfaceBeanTransform.java b/juneau-core/src/main/java/org/apache/juneau/transform/InterfaceBeanTransform.java deleted file mode 100644 index 5f281f3..0000000 --- a/juneau-core/src/main/java/org/apache/juneau/transform/InterfaceBeanTransform.java +++ /dev/null @@ -1,39 +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.transform; - -import org.apache.juneau.*; - - -/** - * Simple bean transform that simply identifies a class to be used as an interface - * class for all child classes. - * <p> - * These objects are created when you pass in non-<code>Transform</code> classes to {@link ContextFactory#addToProperty(String,Object)}, - * and are equivalent to adding a <code><ja>@Bean</ja>(interfaceClass=Foo.<jk>class</jk>)</code> annotation on the <code>Foo</code> class. - * - * @author James Bognar ([email protected]) - * @param <T> The class type that this transform applies to. - */ -public class InterfaceBeanTransform<T> extends BeanTransform<T> { - - /** - * Constructor. - * - * @param interfaceClass The class to use as an interface on all child classes. - */ - public InterfaceBeanTransform(Class<T> interfaceClass) { - super(interfaceClass); - setInterfaceClass(interfaceClass); - } -} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/0ce0e663/juneau-core/src/main/java/org/apache/juneau/transform/PojoSwap.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/transform/PojoSwap.java b/juneau-core/src/main/java/org/apache/juneau/transform/PojoSwap.java new file mode 100644 index 0000000..3bde495 --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/transform/PojoSwap.java @@ -0,0 +1,265 @@ +/*************************************************************************************************************************** + * 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.transform; + +import java.lang.reflect.*; +import java.util.*; + +import org.apache.juneau.*; +import org.apache.juneau.internal.*; +import org.apache.juneau.parser.*; +import org.apache.juneau.serializer.*; + +/** + * Used to convert non-serializable objects to a serializable form. + * + * + * <h6 class='topic'>Description</h6> + * <p> + * <code>PojoSwaps</code> are used to extend the functionality of the serializers and parsers to be able to handle POJOs + * that aren't automatically handled by the serializers or parsers. For example, JSON does not have a standard + * representation for rendering dates. By defining a special {@code Date} swap and associating it with a serializer and + * parser, you can convert a {@code Date} object to a {@code String} during serialization, and convert that {@code String} object back into + * a {@code Date} object during parsing. + * <p> + * Swaps MUST declare a public no-arg constructor so that the bean context can instantiate them. + * <p> + * <code>PojoSwaps</code> are associated with instances of {@link BeanContext BeanContexts} by passing the swap class to + * the {@link CoreApi#addTransforms(Class...)} method.<br> + * When associated with a bean context, fields of the specified type will automatically be converted when the + * {@link BeanMap#get(Object)} or {@link BeanMap#put(String, Object)} methods are called.<br> + * <p> + * <code>PojoSwaps</code> have two parameters: + * <ol> + * <li>{@code <T>} - The normal representation of an object. + * <li>{@code <S>} - The swapped representation of an object. + * </ol> + * <br> + * {@link Serializer Serializers} use swaps to convert objects of type T into objects of type S, and on calls to {@link BeanMap#get(Object)}.<br> + * {@link Parser Parsers} use swaps to convert objects of type S into objects of type T, and on calls to {@link BeanMap#put(String,Object)}. + * + * + * <h6 class='topic'>Swap Class Type {@code <S>}</h6> + * <p> + * The swapped object representation of an object must be an object type that the serializers can + * natively convert to JSON (or language-specific equivalent). The list of valid transformed types are as follows... + * <ul class='spaced-list'> + * <li>{@link String} + * <li>{@link Number} + * <li>{@link Boolean} + * <li>{@link Collection} containing anything on this list. + * <li>{@link Map} containing anything on this list. + * <li>A java bean with properties of anything on this list. + * <li>An array of anything on this list. + * </ul> + * + * + * <h6 class='topic'>Normal Class Type {@code <T>}</h6> + * <p> + * The normal object representation of an object.<br> + * + * + * <h6 class='topic'>One-way vs. Two-way Serialization</h6> + * <p> + * Note that while there is a unified interface for handling swaps during both serialization and parsing, + * in many cases only one of the {@link #swap(Object)} or {@link #unswap(Object, ClassMeta)} methods will be defined + * because the swap is one-way. For example, a swap may be defined to convert an {@code Iterator} to a {@code ObjectList}, but + * it's not possible to unswap an {@code Iterator}. In that case, the {@code generalize(Object}} method would + * be implemented, but the {@code narrow(ObjectMap)} object would not, and the swap would be associated on + * the serializer, but not the parser. Also, you may choose to serialize objects like {@code Dates} to readable {@code Strings}, + * in which case it's not possible to reparse it back into a {@code Date}, since there is no way for the {@code Parser} to + * know it's a {@code Date} from just the JSON or XML text. + * + * + * <h6 class='topic'>Additional information</h6> + * See {@link org.apache.juneau.transform} for more information. + * + * + * @author James Bognar ([email protected]) + * @param <T> The normal form of the class. + * @param <S> The swapped form of the class. + */ +public abstract class PojoSwap<T,S> extends Transform { + + /** Represents no transform. */ + public static class NULL extends PojoSwap<Object,Object> {} + + Class<T> normalClass; + Class<S> transformedClass; + ClassMeta<S> transformedClassMeta; + + /** + * Constructor. + */ + @SuppressWarnings("unchecked") + protected PojoSwap() { + super(); + + Class<?> c = this.getClass().getSuperclass(); + Type t = this.getClass().getGenericSuperclass(); + while (c != PojoSwap.class) { + t = c.getGenericSuperclass(); + c = c.getSuperclass(); + } + + // Attempt to determine the T and G classes using reflection. + if (t instanceof ParameterizedType) { + ParameterizedType pt = (ParameterizedType)t; + Type[] pta = pt.getActualTypeArguments(); + if (pta.length == 2) { + Type nType = pta[0]; + if (nType instanceof Class) { + this.normalClass = (Class<T>)nType; + + // <byte[],x> ends up containing a GenericArrayType, so it has to + // be handled as a special case. + } else if (nType instanceof GenericArrayType) { + Class<?> cmpntType = (Class<?>)((GenericArrayType)nType).getGenericComponentType(); + this.normalClass = (Class<T>)Array.newInstance(cmpntType, 0).getClass(); + + // <Class<?>,x> ends up containing a ParameterizedType, so just use the raw type. + } else if (nType instanceof ParameterizedType) { + this.normalClass = (Class<T>)((ParameterizedType)nType).getRawType(); + + } else + throw new RuntimeException("Unsupported parameter type: " + nType); + if (pta[1] instanceof Class) + this.transformedClass = (Class<S>)pta[1]; + else if (pta[1] instanceof ParameterizedType) + this.transformedClass = (Class<S>)((ParameterizedType)pta[1]).getRawType(); + else + throw new RuntimeException("Unexpected transformed class type: " + pta[1].getClass().getName()); + } + } + } + + /** + * Constructor for when the normal and transformed classes are already known. + * + * @param normalClass The normal class (cannot be serialized). + * @param transformedClass The transformed class (serializable). + */ + protected PojoSwap(Class<T> normalClass, Class<S> transformedClass) { + this.normalClass = normalClass; + this.transformedClass = transformedClass; + } + + /** + * If this transform is to be used to serialize non-serializable POJOs, it must implement this method. + * <p> + * The object must be converted into one of the following serializable types: + * <ul class='spaced-list'> + * <li>{@link String} + * <li>{@link Number} + * <li>{@link Boolean} + * <li>{@link Collection} containing anything on this list. + * <li>{@link Map} containing anything on this list. + * <li>A java bean with properties of anything on this list. + * <li>An array of anything on this list. + * </ul> + * + * @param o The object to be transformed. + * @return The transformed object. + * @throws SerializeException If a problem occurred trying to convert the output. + */ + public S swap(T o) throws SerializeException { + throw new SerializeException("Generalize method not implemented on transform ''{0}''", this.getClass().getName()); + } + + /** + * If this transform is to be used to reconstitute POJOs that aren't true Java beans, it must implement this method. + * + * @param f The transformed object. + * @param hint If possible, the parser will try to tell you the object type being created. For example, + * on a serialized date, this may tell you that the object being created must be of type {@code GregorianCalendar}.<br> + * This may be <jk>null</jk> if the parser cannot make this determination. + * @return The narrowed object. + * @throws ParseException If this method is not implemented. + */ + public T unswap(S f, ClassMeta<?> hint) throws ParseException { + throw new ParseException("Narrow method not implemented on transform ''{0}''", this.getClass().getName()); + } + + /** + * Returns the T class, the normalized form of the class. + * + * @return The normal form of this class. + */ + public Class<T> getNormalClass() { + return normalClass; + } + + /** + * Returns the G class, the generialized form of the class. + * <p> + * Subclasses must override this method if the generialized class is {@code Object}, + * meaning it can produce multiple generialized forms. + * + * @return The transformed form of this class. + */ + public Class<S> getTransformedClass() { + return transformedClass; + } + + /** + * Returns the {@link ClassMeta} of the transformed class type. + * This value is cached for quick lookup. + * + * @return The {@link ClassMeta} of the transformed class type. + */ + public ClassMeta<S> getTransformedClassMeta() { + if (transformedClassMeta == null) + transformedClassMeta = beanContext.getClassMeta(transformedClass); + return transformedClassMeta; + } + + /** + * Checks if the specified object is an instance of the normal class defined on this transform. + * + * @param o The object to check. + * @return <jk>true</jk> if the specified object is a subclass of the normal class defined on this transform. + * <jk>null</jk> always return <jk>false</jk>. + */ + public boolean isNormalObject(Object o) { + if (o == null) + return false; + return ClassUtils.isParentClass(normalClass, o.getClass()); + } + + /** + * Checks if the specified object is an instance of the transformed class defined on this transform. + * + * @param o The object to check. + * @return <jk>true</jk> if the specified object is a subclass of the transformed class defined on this transform. + * <jk>null</jk> always return <jk>false</jk>. + */ + public boolean isTransformedObject(Object o) { + if (o == null) + return false; + return ClassUtils.isParentClass(transformedClass, o.getClass()); + } + + //-------------------------------------------------------------------------------- + // Overridden methods + //-------------------------------------------------------------------------------- + + @Override /* Transform */ + public Class<?> forClass() { + return normalClass; + } + + @Override /* Object */ + public String toString() { + return getClass().getSimpleName() + '<' + getNormalClass().getSimpleName() + "," + getTransformedClass().getSimpleName() + '>'; + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/0ce0e663/juneau-core/src/main/java/org/apache/juneau/transform/PojoTransform.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/transform/PojoTransform.java b/juneau-core/src/main/java/org/apache/juneau/transform/PojoTransform.java deleted file mode 100644 index 4d41b3f..0000000 --- a/juneau-core/src/main/java/org/apache/juneau/transform/PojoTransform.java +++ /dev/null @@ -1,265 +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.transform; - -import java.lang.reflect.*; -import java.util.*; - -import org.apache.juneau.*; -import org.apache.juneau.internal.*; -import org.apache.juneau.parser.*; -import org.apache.juneau.serializer.*; - -/** - * Used to convert non-serializable objects to a serializable form. - * - * - * <h6 class='topic'>Description</h6> - * <p> - * <code>PojoTransforms</code> are used to extend the functionality of the serializers and parsers to be able to handle POJOs - * that aren't automatically handled by the serializers or parsers. For example, JSON does not have a standard - * representation for rendering dates. By defining a special {@code Date} transform and associating it with a serializer and - * parser, you can convert a {@code Date} object to a {@code String} during serialization, and convert that {@code String} object back into - * a {@code Date} object during parsing. - * <p> - * Object transforms MUST declare a public no-arg constructor so that the bean context can instantiate them. - * <p> - * <code>PojoTransforms</code> are associated with instances of {@link BeanContext BeanContexts} by passing the transform class to - * the {@link CoreApi#addTransforms(Class...)} method.<br> - * When associated with a bean context, fields of the specified type will automatically be converted when the - * {@link BeanMap#get(Object)} or {@link BeanMap#put(String, Object)} methods are called.<br> - * <p> - * <code>PojoTransforms</code> have two parameters: - * <ol> - * <li>{@code <F>} - The transformed representation of an object. - * <li>{@code <T>} - The normal representation of an object. - * </ol> - * <br> - * {@link Serializer Serializers} use object transforms to convert objects of type T into objects of type F, and on calls to {@link BeanMap#get(Object)}.<br> - * {@link Parser Parsers} use object transforms to convert objects of type F into objects of type T, and on calls to {@link BeanMap#put(String,Object)}. - * - * - * <h6 class='topic'>Transformed Class Type {@code <T>}</h6> - * <p> - * The transformed object representation of an object must be an object type that the serializers can - * natively convert to JSON (or language-specific equivalent). The list of valid transformed types are as follows... - * <ul class='spaced-list'> - * <li>{@link String} - * <li>{@link Number} - * <li>{@link Boolean} - * <li>{@link Collection} containing anything on this list. - * <li>{@link Map} containing anything on this list. - * <li>A java bean with properties of anything on this list. - * <li>An array of anything on this list. - * </ul> - * - * - * <h6 class='topic'>Normal Class Type {@code <N>}</h6> - * <p> - * The normal object representation of an object.<br> - * - * - * <h6 class='topic'>One-way vs. Two-way Serialization</h6> - * <p> - * Note that while there is a unified interface for handling transforming during both serialization and parsing, - * in many cases only one of the {@link #transform(Object)} or {@link #normalize(Object, ClassMeta)} methods will be defined - * because the transform is one-way. For example, a transform may be defined to convert an {@code Iterator} to a {@code ObjectList}, but - * it's not possible to untransform an {@code Iterator}. In that case, the {@code generalize(Object}} method would - * be implemented, but the {@code narrow(ObjectMap)} object would not, and the transform would be associated on - * the serializer, but not the parser. Also, you may choose to serialize objects like {@code Dates} to readable {@code Strings}, - * in which case it's not possible to reparse it back into a {@code Date}, since there is no way for the {@code Parser} to - * know it's a {@code Date} from just the JSON or XML text. - * - * - * <h6 class='topic'>Additional information</h6> - * See {@link org.apache.juneau.transform} for more information. - * - * - * @author James Bognar ([email protected]) - * @param <N> The normal form of the class. - * @param <T> The transformed form of the class. - */ -public abstract class PojoTransform<N,T> extends Transform { - - /** Represents no transform. */ - public static class NULL extends PojoTransform<Object,Object> {} - - Class<N> normalClass; - Class<T> transformedClass; - ClassMeta<T> transformedClassMeta; - - /** - * Constructor. - */ - @SuppressWarnings("unchecked") - protected PojoTransform() { - super(); - - Class<?> c = this.getClass().getSuperclass(); - Type t = this.getClass().getGenericSuperclass(); - while (c != PojoTransform.class) { - t = c.getGenericSuperclass(); - c = c.getSuperclass(); - } - - // Attempt to determine the T and G classes using reflection. - if (t instanceof ParameterizedType) { - ParameterizedType pt = (ParameterizedType)t; - Type[] pta = pt.getActualTypeArguments(); - if (pta.length == 2) { - Type nType = pta[0]; - if (nType instanceof Class) { - this.normalClass = (Class<N>)nType; - - // <byte[],x> ends up containing a GenericArrayType, so it has to - // be handled as a special case. - } else if (nType instanceof GenericArrayType) { - Class<?> cmpntType = (Class<?>)((GenericArrayType)nType).getGenericComponentType(); - this.normalClass = (Class<N>)Array.newInstance(cmpntType, 0).getClass(); - - // <Class<?>,x> ends up containing a ParameterizedType, so just use the raw type. - } else if (nType instanceof ParameterizedType) { - this.normalClass = (Class<N>)((ParameterizedType)nType).getRawType(); - - } else - throw new RuntimeException("Unsupported parameter type: " + nType); - if (pta[1] instanceof Class) - this.transformedClass = (Class<T>)pta[1]; - else if (pta[1] instanceof ParameterizedType) - this.transformedClass = (Class<T>)((ParameterizedType)pta[1]).getRawType(); - else - throw new RuntimeException("Unexpected transformed class type: " + pta[1].getClass().getName()); - } - } - } - - /** - * Constructor for when the normal and transformed classes are already known. - * - * @param normalClass The normal class (cannot be serialized). - * @param transformedClass The transformed class (serializable). - */ - protected PojoTransform(Class<N> normalClass, Class<T> transformedClass) { - this.normalClass = normalClass; - this.transformedClass = transformedClass; - } - - /** - * If this transform is to be used to serialize non-serializable POJOs, it must implement this method. - * <p> - * The object must be converted into one of the following serializable types: - * <ul class='spaced-list'> - * <li>{@link String} - * <li>{@link Number} - * <li>{@link Boolean} - * <li>{@link Collection} containing anything on this list. - * <li>{@link Map} containing anything on this list. - * <li>A java bean with properties of anything on this list. - * <li>An array of anything on this list. - * </ul> - * - * @param o The object to be transformed. - * @return The transformed object. - * @throws SerializeException If a problem occurred trying to convert the output. - */ - public T transform(N o) throws SerializeException { - throw new SerializeException("Generalize method not implemented on transform ''{0}''", this.getClass().getName()); - } - - /** - * If this transform is to be used to reconstitute POJOs that aren't true Java beans, it must implement this method. - * - * @param f The transformed object. - * @param hint If possible, the parser will try to tell you the object type being created. For example, - * on a serialized date, this may tell you that the object being created must be of type {@code GregorianCalendar}.<br> - * This may be <jk>null</jk> if the parser cannot make this determination. - * @return The narrowed object. - * @throws ParseException If this method is not implemented. - */ - public N normalize(T f, ClassMeta<?> hint) throws ParseException { - throw new ParseException("Narrow method not implemented on transform ''{0}''", this.getClass().getName()); - } - - /** - * Returns the T class, the normalized form of the class. - * - * @return The normal form of this class. - */ - public Class<N> getNormalClass() { - return normalClass; - } - - /** - * Returns the G class, the generialized form of the class. - * <p> - * Subclasses must override this method if the generialized class is {@code Object}, - * meaning it can produce multiple generialized forms. - * - * @return The transformed form of this class. - */ - public Class<T> getTransformedClass() { - return transformedClass; - } - - /** - * Returns the {@link ClassMeta} of the transformed class type. - * This value is cached for quick lookup. - * - * @return The {@link ClassMeta} of the transformed class type. - */ - public ClassMeta<T> getTransformedClassMeta() { - if (transformedClassMeta == null) - transformedClassMeta = beanContext.getClassMeta(transformedClass); - return transformedClassMeta; - } - - /** - * Checks if the specified object is an instance of the normal class defined on this transform. - * - * @param o The object to check. - * @return <jk>true</jk> if the specified object is a subclass of the normal class defined on this transform. - * <jk>null</jk> always return <jk>false</jk>. - */ - public boolean isNormalObject(Object o) { - if (o == null) - return false; - return ClassUtils.isParentClass(normalClass, o.getClass()); - } - - /** - * Checks if the specified object is an instance of the transformed class defined on this transform. - * - * @param o The object to check. - * @return <jk>true</jk> if the specified object is a subclass of the transformed class defined on this transform. - * <jk>null</jk> always return <jk>false</jk>. - */ - public boolean isTransformedObject(Object o) { - if (o == null) - return false; - return ClassUtils.isParentClass(transformedClass, o.getClass()); - } - - //-------------------------------------------------------------------------------- - // Overridden methods - //-------------------------------------------------------------------------------- - - @Override /* Transform */ - public Class<?> forClass() { - return normalClass; - } - - @Override /* Object */ - public String toString() { - return getClass().getSimpleName() + '<' + getNormalClass().getSimpleName() + "," + getTransformedClass().getSimpleName() + '>'; - } -}
