package org.codehaus.jackson.map.ser; import java.lang.reflect.Field; import java.lang.reflect.Method; import org.codehaus.jackson.map.AnnotationIntrospector; import org.codehaus.jackson.map.JsonSerializer; import org.codehaus.jackson.map.SerializationConfig; import org.codehaus.jackson.map.TypeSerializer; import org.codehaus.jackson.map.annotate.JsonSerialize; import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion; import org.codehaus.jackson.map.introspect.Annotated; import org.codehaus.jackson.map.introspect.AnnotatedField; import org.codehaus.jackson.map.introspect.AnnotatedMember; import org.codehaus.jackson.map.introspect.AnnotatedMethod; import org.codehaus.jackson.map.introspect.BasicBeanDescription; import org.codehaus.jackson.map.type.TypeFactory; import org.codehaus.jackson.type.JavaType; /** * Helper class for {@link BeanSerializerFactory} that is used to * construct {@link BeanPropertyWriter} instances. Can be sub-classed * to change behavior. */ public class PropertyBuilder { final SerializationConfig _config; final BasicBeanDescription _beanDesc; final JsonSerialize.Inclusion _outputProps; final AnnotationIntrospector _annotationIntrospector; /** * If a property has serialization inclusion value of * {@link Inclusion#ALWAYS}, we need to know the default * value of the bean, to know if property value equals default * one. */ protected Object _defaultBean; public PropertyBuilder(SerializationConfig config, BasicBeanDescription beanDesc) { _config = config; _beanDesc = beanDesc; _outputProps = beanDesc.findSerializationInclusion(config.getSerializationInclusion()); _annotationIntrospector = _config.getAnnotationIntrospector(); } /* /************************************************ /* Public API /************************************************ */ /** * @param contentTypeSer Optional explicit type information serializer * to use for contained values (only used for properties that are * of container type) */ protected BeanPropertyWriter buildProperty(String name, JsonSerializer<Object> ser, TypeSerializer typeSer, TypeSerializer contentTypeSer, AnnotatedMember am, boolean defaultUseStaticTyping) { Field f; Method m; if (am instanceof AnnotatedField) { m = null; f = ((AnnotatedField) am).getAnnotated(); } else { m = ((AnnotatedMethod) am).getAnnotated(); f = null; } // do we have annotation that forces type to use (to declared type or its super type)? JavaType serializationType = findSerializationType(am, defaultUseStaticTyping); // Container types can have separate type serializers for content (value / element) type if (contentTypeSer != null) { /* 04-Feb-2010, tatu: Let's force static typing for collection, if there is * type information for contents. Should work well (for JAXB case); can be * revisited if this causes problems. */ if (serializationType == null) { serializationType = TypeFactory.type(am.getGenericType()); } JavaType ct = serializationType.getContentType(); ct.setTypeHandler(contentTypeSer); } Object suppValue = null; boolean suppressNulls = false; JsonSerialize.Inclusion methodProps = _annotationIntrospector.findSerializationInclusion(am, _outputProps); if (methodProps != null) { switch (methodProps) { case NON_DEFAULT: suppValue = getDefaultValue(name, m, f); if (suppValue == null) { suppressNulls = true; } break; case NON_NULL: suppressNulls = true; break; } } return new BeanPropertyWriter(name, ser, typeSer, serializationType, m, f, suppressNulls, suppValue); } /* ////////////////////////////////////////////////// // Helper methods, generic ////////////////////////////////////////////////// */ /** * Method that will try to determine statically defined type of property * being serialized, based on annotations (for overrides), and alternatively * declared type (if static typing for serialization is enabled). * If neither can be used (no annotations, dynamic typing), returns null. */ protected JavaType findSerializationType(Annotated a, boolean useStaticTyping) { // [JACKSON-120]: Check to see if serialization type is fixed Class<?> serializationType = _annotationIntrospector.findSerializationType(a); if (serializationType != null) { // Must be a super type... Class<?> raw = a.getRawType(); if (!serializationType.isAssignableFrom(raw)) { throw new IllegalArgumentException("Illegal concrete-type annotation for method '"+a.getName()+"': class "+serializationType.getName()+" not a super-type of (declared) class "+raw.getName()); } return TypeFactory.type(serializationType); } /* [JACKSON-114]: if using static typing, declared type is known * to be the type... */ JsonSerialize.Typing typing = _annotationIntrospector.findSerializationTyping(a); if (typing != null) { useStaticTyping = (typing == JsonSerialize.Typing.STATIC); } if (useStaticTyping) { return TypeFactory.type(a.getGenericType()); } return null; } protected Object getDefaultBean() { if (_defaultBean == null) { /* If we can fix access rights, we should; otherwise non-public * classes or default constructor will prevent instantiation */ _defaultBean = _beanDesc.instantiateBean(_config.isEnabled(SerializationConfig.Feature.CAN_OVERRIDE_ACCESS_MODIFIERS)); if (_defaultBean == null) { Class<?> cls = _beanDesc.getClassInfo().getAnnotated(); throw new IllegalArgumentException("Class "+cls.getName()+" has no default constructor; can not instantiate default bean value to support 'properties=JsonSerialize.Inclusion.NON_DEFAULT' annotation"); } } return _defaultBean; } protected Object getDefaultValue(String name, Method m, Field f) { Object defaultBean = getDefaultBean(); try { if (m != null) { return m.invoke(defaultBean); } return f.get(defaultBean); } catch (Exception e) { return _throwWrapped(e, name, defaultBean); } } protected Object _throwWrapped(Exception e, String propName, Object defaultBean) { Throwable t = e; while (t.getCause() != null) { t = t.getCause(); } if (t instanceof Error) throw (Error) t; if (t instanceof RuntimeException) throw (RuntimeException) t; throw new IllegalArgumentException("Failed to get property '"+propName+"' of default "+defaultBean.getClass().getName()+" instance"); } }