Subversion Repositories bacoAlunos

Rev

Blame | Last modification | View Log | RSS feed

package com.owlike.genson.ext.jaxb;

import static com.owlike.genson.reflect.TypeUtil.*;

import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Map;

import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.Duration;
import javax.xml.datatype.XMLGregorianCalendar;

import com.owlike.genson.*;
import com.owlike.genson.annotation.HandleClassMetadata;
import com.owlike.genson.annotation.HandleBeanView;
import com.owlike.genson.convert.ChainedFactory;
import com.owlike.genson.convert.ContextualFactory;
import com.owlike.genson.convert.DefaultConverters.WrappedRootValueConverter;
import com.owlike.genson.convert.DefaultConverters.DateConverter;
import com.owlike.genson.ext.GensonBundle;
import com.owlike.genson.reflect.*;
import com.owlike.genson.stream.ObjectReader;
import com.owlike.genson.stream.ObjectWriter;

/**
 * Provides support for some JAXB annotations and data types.
 *
 * @author eugen
 */

public class JAXBBundle extends GensonBundle {
  private final 1.5.0/docs/api/javax/xml/datatype/DatatypeFactory.html">DatatypeFactory dateFactory;
  private boolean wrapRootValues = false;

  public JAXBBundle() {
    try {
      dateFactory = 1.5.0/docs/api/javax/xml/datatype/DatatypeFactory.html">DatatypeFactory.newInstance();
    } catch (1.5.0/docs/api/javax/xml/datatype/DatatypeConfigurationException.html">DatatypeConfigurationException dce) {
      throw new 1.5.0/docs/api/java/lang/IllegalStateException.html">IllegalStateException("Could not obtain an instance of DatatypeFactory.", dce);
    }
  }

  /**
   * When enabled allows to use @XmlRootElement annotation on root objects to wrap them inside a object under some key.
   * The key by default will be the class name with the first letter to lower case.
   */

  public JAXBBundle wrapRootValues(boolean enable) {
    wrapRootValues = enable;
    return this;
  }

  @1.5.0/docs/api/java/lang/Override.html">Override
  public void configure(GensonBuilder builder) {
    // forcing here the order of GensonAnnotationsResolver and AnnotationPropertyNameResolver allows
    // us to give them preference over Jaxb annotations. We can not assume it true for any bundle,
    // as in some cases a bundle might want to have preference over all std Genson components
    builder.withConverters(new XMLGregorianCalendarConverter(), new DurationConveter())
      .with(new BeanMutatorAccessorResolver.GensonAnnotationsResolver(), new JaxbAnnotationsResolver())
      .with(new PropertyNameResolver.AnnotationPropertyNameResolver(), new JaxbNameResolver())
      .withConverterFactory(new EnumConverterFactory())
      .withBeanPropertyFactory(new JaxbBeanPropertyFactory())
      .withContextualFactory(new XmlTypeAdapterFactory());

    if (wrapRootValues)
      builder.withConverterFactory(new ChainedFactory() {
        @1.5.0/docs/api/java/lang/Override.html">Override
        protected Converter<?> create(1.5.0/docs/api/java/lang/reflect/Type.html">Type type, Genson genson, Converter<?> nextConverter) {
          Class<?> clazz = TypeUtil.getRawClass(type);
          XmlRootElement ann = clazz.getAnnotation(XmlRootElement.class);

          if (ann != null) {
            1.5.0/docs/api/java/lang/String.html">String name = "##default".equals(ann.name()) ? firstCharToLower(clazz.getSimpleName()) : ann.name();
            return new WrappedRootValueConverter<Object>(name, name, (Converter<Object>) nextConverter);
          }
          return null;
        }
      });
  }

  private 1.5.0/docs/api/java/lang/String.html">String firstCharToLower(1.5.0/docs/api/java/lang/String.html">String str) {
    return 1.5.0/docs/api/java/lang/Character.html">Character.toLowerCase(str.charAt(0)) + (str.length() > 0 ? str.substring(1) : "");
  }

  private class DurationConveter implements Converter<Duration> {
    @1.5.0/docs/api/java/lang/Override.html">Override
    public void serialize(1.5.0/docs/api/javax/xml/datatype/Duration.html">Duration object, ObjectWriter writer, 5+0%2Fdocs%2Fapi+Context">Context ctx) {
      writer.writeValue(object.toString());
    }

    @1.5.0/docs/api/java/lang/Override.html">Override
    public 1.5.0/docs/api/javax/xml/datatype/Duration.html">Duration deserialize(ObjectReader reader, 5+0%2Fdocs%2Fapi+Context">Context ctx) {
      return dateFactory.newDuration(reader.valueAsString());
    }
  }

  @HandleClassMetadata
  @HandleBeanView
  private class XMLGregorianCalendarConverter implements Converter<XMLGregorianCalendar> {
    private final DateConverter converter = new DateConverter();
    private final 1.5.0/docs/api/java/text/SimpleDateFormat.html">SimpleDateFormat dateFormat = new 1.5.0/docs/api/java/text/SimpleDateFormat.html">SimpleDateFormat("yyyy-MM-DD'T'hh:mm:ssZ");

    @1.5.0/docs/api/java/lang/Override.html">Override
    public void serialize(1.5.0/docs/api/javax/xml/datatype/XMLGregorianCalendar.html">XMLGregorianCalendar object, ObjectWriter writer, 5+0%2Fdocs%2Fapi+Context">Context ctx) {
      converter.serialize(object.toGregorianCalendar().getTime(), writer, ctx);
    }

    @1.5.0/docs/api/java/lang/Override.html">Override
    public synchronized 1.5.0/docs/api/javax/xml/datatype/XMLGregorianCalendar.html">XMLGregorianCalendar deserialize(ObjectReader reader, 5+0%2Fdocs%2Fapi+Context">Context ctx) {
      1.5.0/docs/api/java/util/GregorianCalendar.html">GregorianCalendar cal = new 1.5.0/docs/api/java/util/GregorianCalendar.html">GregorianCalendar();
      try {
        cal.setTime(dateFormat.parse(reader.valueAsString()));
      } catch (1.5.0/docs/api/java/text/ParseException.html">ParseException e) {
        throw new JsonBindingException("Could not parse date "
          + reader.valueAsString(), e);
      }

      return dateFactory.newXMLGregorianCalendar(cal);
    }

  }

  private class XmlTypeAdapterFactory implements ContextualFactory<Object> {
    @1.5.0/docs/api/java/lang/Override.html">Override
    public Converter<Object> create(BeanProperty property, Genson genson) {
      XmlJavaTypeAdapter ann = property.getAnnotation(XmlJavaTypeAdapter.class);
      Converter<Object> converter = null;

      if (ann != null) {
        Class<? extends XmlAdapter> adapterClass = ann.value();
        1.5.0/docs/api/java/lang/reflect/Type.html">Type adapterExpandedType = expandType(
          lookupGenericType(XmlAdapter.class, adapterClass), adapterClass);
        1.5.0/docs/api/java/lang/reflect/Type.html">Type adaptedType = typeOf(0, adapterExpandedType);
        1.5.0/docs/api/java/lang/reflect/Type.html">Type originalType = typeOf(1, adapterExpandedType);
        1.5.0/docs/api/java/lang/reflect/Type.html">Type propertyType = property.getType();

        if (getRawClass(propertyType).isPrimitive())
          propertyType = wrap(getRawClass(propertyType));

        XmlElement el = property.getAnnotation(XmlElement.class);  
        1.5.0/docs/api/java/lang/reflect/Type.html">Type xmlElementType = el != null && el.type() != XmlElement.DEFAULT.class ? el.type() : null;

        // checking type consistency
        if (xmlElementType != null) {
          if (!match(adaptedType, xmlElementType, false))
            throw new 1.5.0/docs/api/java/lang/ClassCastException.html">ClassCastException("The BoundType of XMLAdapter " + adapterClass
              + " is not assignable from property " + property.getName()
              + " declared in " + property.getDeclaringClass());
           
        } else if (!match(propertyType, originalType, false))
          throw new 1.5.0/docs/api/java/lang/ClassCastException.html">ClassCastException("The BoundType of XMLAdapter " + adapterClass
            + " is not assignable from property " + property.getName()
            + " declared in " + property.getDeclaringClass());

        try {
          XmlAdapter adapter = adapterClass.newInstance();
          // we also need to find a converter for the adapted type
          Converter<Object> adaptedTypeConverter = genson.provideConverter(
            xmlElementType != null ? xmlElementType : adaptedType);
          converter = new AdaptedConverter(adapter, adaptedTypeConverter);
        } catch (1.5.0/docs/api/java/lang/InstantiationException.html">InstantiationException e) {
          throw new JsonBindingException(
            "Could not instantiate XmlAdapter of type " + adapterClass, e);
        } catch (1.5.0/docs/api/java/lang/IllegalAccessException.html">IllegalAccessException e) {
          throw new JsonBindingException(
            "Could not instantiate XmlAdapter of type " + adapterClass, e);
        }
      }
      return converter;
    }

    private class AdaptedConverter implements Converter<Object> {
      private final XmlAdapter<Object, Object> adapter;
      private final Converter<Object> converter;

      public AdaptedConverter(XmlAdapter<Object, Object> adapter, Converter<Object> converter) {
        super();
        this.adapter = adapter;
        this.converter = converter;
      }

      @1.5.0/docs/api/java/lang/Override.html">Override
      public 5+0%2Fdocs%2Fapi+Object">Object deserialize(ObjectReader reader, 5+0%2Fdocs%2Fapi+Context">Context ctx) throws 1.5.0/docs/api/java/lang/Exception.html">Exception {
        5+0%2Fdocs%2Fapi+Object">Object value = converter.deserialize(reader, ctx);
        try {
          return adapter.unmarshal(value);
        } catch (1.5.0/docs/api/java/lang/Exception.html">Exception e) {
          throw new JsonBindingException("Could not unmarshal object using adapter "
            + adapter.getClass());
        }
      }

      @1.5.0/docs/api/java/lang/Override.html">Override
      public void serialize(5+0%2Fdocs%2Fapi+Object">Object object, ObjectWriter writer, 5+0%2Fdocs%2Fapi+Context">Context ctx) throws 1.5.0/docs/api/java/lang/Exception.html">Exception {
        5+0%2Fdocs%2Fapi+Object">Object adaptedValue = null;
        try {
          adaptedValue = adapter.marshal(object);
        } catch (1.5.0/docs/api/java/lang/Exception.html">Exception e) {
          throw new JsonBindingException("Could not marshal object using adapter "
            + adapter.getClass());
        }
        converter.serialize(adaptedValue, writer, ctx);
      }
    }
  }

  private class EnumConverterFactory implements Factory<Converter<Enum<?>>> {

    @1.5.0/docs/api/java/lang/Override.html">Override
    public Converter<Enum<?>> create(1.5.0/docs/api/java/lang/reflect/Type.html">Type type, Genson genson) {
      Class<?> rawClass = getRawClass(type);
      if (rawClass.isEnum() || 1.5.0/docs/api/java/lang/Enum.html">Enum.class.isAssignableFrom(rawClass)) {
        @1.5.0/docs/api/java/lang/SuppressWarnings.html">SuppressWarnings({"unchecked"})
        Class<? extends Enum<?>> enumClass = (Class<? extends Enum<?>>) rawClass;

        try {
          Map<String, Enum<?>> valueToEnum = new HashMap<String, Enum<?>>();
          Map<Enum<?>, String> enumToValue = new HashMap<Enum<?>, String>();
          for (Enum<?> enumConstant : enumClass.getEnumConstants()) {
            XmlEnumValue ann = rawClass.getField(enumConstant.name()).getAnnotation(
              XmlEnumValue.class);

            if (ann != null) {
              valueToEnum.put(ann.value(), enumConstant);
              enumToValue.put(enumConstant, ann.value());
            } else {
              valueToEnum.put(enumConstant.name(), enumConstant);
              enumToValue.put(enumConstant, enumConstant.name());
            }
          }

          return new EnumConverter(valueToEnum, enumToValue);
        } catch (1.5.0/docs/api/java/lang/SecurityException.html">SecurityException e) {
          throw new JsonBindingException("Unable to introspect enum "
            + enumClass, e);
        } catch (1.5.0/docs/api/java/lang/NoSuchFieldException.html">NoSuchFieldException e) {
        }
      }

      // otherwise let genson standard converter handle the conversion
      return null;
    }

    @HandleClassMetadata
    @HandleBeanView
    private class EnumConverter implements Converter<Enum<?>> {
      private final Map<String, Enum<?>> valueToEnum;
      private final Map<Enum<?>, String> enumToValue;

      public EnumConverter(Map<String, Enum<?>> valueToEnum, Map<Enum<?>, String> enumToValue) {
        super();
        this.valueToEnum = valueToEnum;
        this.enumToValue = enumToValue;
      }

      @1.5.0/docs/api/java/lang/Override.html">Override
      public void serialize(Enum<?> object, ObjectWriter writer, 5+0%2Fdocs%2Fapi+Context">Context ctx) {
        writer.writeUnsafeValue(enumToValue.get(object));
      }

      @1.5.0/docs/api/java/lang/Override.html">Override
      public Enum<?> deserialize(ObjectReader reader, 5+0%2Fdocs%2Fapi+Context">Context ctx) {
        return valueToEnum.get(reader.valueAsString());
      }
    }
  }

  private class JaxbBeanPropertyFactory implements BeanPropertyFactory {

    @1.5.0/docs/api/java/lang/Override.html">Override
    public PropertyAccessor createAccessor(1.5.0/docs/api/java/lang/String.html">String name, 1.5.0/docs/api/java/lang/reflect/Field.html">Field field, 1.5.0/docs/api/java/lang/reflect/Type.html">Type ofType, Genson genson) {
      1.5.0/docs/api/java/lang/reflect/Type.html">Type newType = getType(field, field.getGenericType(), ofType);
      if (newType != null) {
        return new PropertyAccessor.FieldAccessor(name, field, newType, getRawClass(ofType));
      }

      return null;
    }

    @1.5.0/docs/api/java/lang/Override.html">Override
    public PropertyAccessor createAccessor(1.5.0/docs/api/java/lang/String.html">String name, 1.5.0/docs/api/java/lang/reflect/Method.html">Method method, 1.5.0/docs/api/java/lang/reflect/Type.html">Type ofType,
                                           Genson genson) {
      1.5.0/docs/api/java/lang/reflect/Type.html">Type newType = getType(method, method.getReturnType(), ofType);
      if (newType != null) {
        return new PropertyAccessor.MethodAccessor(name, method, newType,
          getRawClass(ofType));
      }
      return null;
    }

    @1.5.0/docs/api/java/lang/Override.html">Override
    public PropertyMutator createMutator(1.5.0/docs/api/java/lang/String.html">String name, 1.5.0/docs/api/java/lang/reflect/Field.html">Field field, 1.5.0/docs/api/java/lang/reflect/Type.html">Type ofType, Genson genson) {
      1.5.0/docs/api/java/lang/reflect/Type.html">Type newType = getType(field, field.getGenericType(), ofType);
      if (newType != null) {
        return new PropertyMutator.FieldMutator(name, field, newType, getRawClass(ofType));
      }

      return null;
    }

    @1.5.0/docs/api/java/lang/Override.html">Override
    public PropertyMutator createMutator(1.5.0/docs/api/java/lang/String.html">String name, 1.5.0/docs/api/java/lang/reflect/Method.html">Method method, 1.5.0/docs/api/java/lang/reflect/Type.html">Type ofType, Genson genson) {
      if (method.getParameterTypes().length == 1) {
        1.5.0/docs/api/java/lang/reflect/Type.html">Type newType = getType(method, method.getReturnType(), ofType);
        if (newType != null) {
          return new PropertyMutator.MethodMutator(name, method, newType,
            getRawClass(ofType));
        }
      }
      return null;
    }

    @1.5.0/docs/api/java/lang/Override.html">Override
    public BeanCreator createCreator(1.5.0/docs/api/java/lang/reflect/Type.html">Type ofType, Constructor<?> ctr, 1.5.0/docs/api/java/lang/String.html">String[] resolvedNames,
                                     Genson genson) {
      return null;
    }

    @1.5.0/docs/api/java/lang/Override.html">Override
    public BeanCreator createCreator(1.5.0/docs/api/java/lang/reflect/Type.html">Type ofType, 1.5.0/docs/api/java/lang/reflect/Method.html">Method method, 1.5.0/docs/api/java/lang/String.html">String[] resolvedNames,
                                     Genson genson) {
      return null;
    }

    private 1.5.0/docs/api/java/lang/reflect/Type.html">Type getType(1.5.0/docs/api/java/lang/reflect/AccessibleObject.html">AccessibleObject object, 1.5.0/docs/api/java/lang/reflect/Type.html">Type objectType, 1.5.0/docs/api/java/lang/reflect/Type.html">Type contextType) {
      XmlElement el = object.getAnnotation(XmlElement.class);
      if (el != null && el.type() != XmlElement.DEFAULT.class) {
        if (!TypeUtil.getRawClass(objectType).isAssignableFrom(el.type())) {
          XmlJavaTypeAdapter ad = object.getAnnotation(XmlJavaTypeAdapter.class);
          if (ad == null)
            throw new 1.5.0/docs/api/java/lang/ClassCastException.html">ClassCastException("Inavlid XmlElement annotation, " + objectType
              + " is not assignable from " + el.type());
        }
        return el.type();
      } else
        return null;
    }
  }

  private class JaxbNameResolver implements PropertyNameResolver {
    private final static 1.5.0/docs/api/java/lang/String.html">String DEFAULT_NAME = "##default";

    @1.5.0/docs/api/java/lang/Override.html">Override
    public 1.5.0/docs/api/java/lang/String.html">String resolve(int parameterIdx, Constructor<?> fromConstructor) {
      return null;
    }

    @1.5.0/docs/api/java/lang/Override.html">Override
    public 1.5.0/docs/api/java/lang/String.html">String resolve(int parameterIdx, 1.5.0/docs/api/java/lang/reflect/Method.html">Method fromMethod) {
      return null;
    }

    @1.5.0/docs/api/java/lang/Override.html">Override
    public 1.5.0/docs/api/java/lang/String.html">String resolve(1.5.0/docs/api/java/lang/reflect/Field.html">Field fromField) {
      return extractName(fromField);
    }

    @1.5.0/docs/api/java/lang/Override.html">Override
    public 1.5.0/docs/api/java/lang/String.html">String resolve(1.5.0/docs/api/java/lang/reflect/Method.html">Method fromMethod) {
      return extractName(fromMethod);
    }

    private 1.5.0/docs/api/java/lang/String.html">String extractName(1.5.0/docs/api/java/lang/reflect/AccessibleObject.html">AccessibleObject object) {
      1.5.0/docs/api/java/lang/String.html">String name = null;
      XmlAttribute attr = object.getAnnotation(XmlAttribute.class);
      if (attr != null)
        name = attr.name();
      else {
        XmlElement el = object.getAnnotation(XmlElement.class);
        if (el != null) name = el.name();
      }
      return DEFAULT_NAME.equals(name) ? null : name;
    }
  }

  private class JaxbAnnotationsResolver extends BeanMutatorAccessorResolver.PropertyBaseResolver {
    @1.5.0/docs/api/java/lang/Override.html">Override
    public Trilean isAccessor(1.5.0/docs/api/java/lang/reflect/Field.html">Field field, Class<?> fromClass) {
      if (ignore(field, field.getType(), fromClass)) return Trilean.FALSE;
      if (include(field, field.getType(), fromClass)) return Trilean.TRUE;
      return shouldResolveField(field, fromClass);
    }

    @1.5.0/docs/api/java/lang/Override.html">Override
    public Trilean isMutator(1.5.0/docs/api/java/lang/reflect/Field.html">Field field, Class<?> fromClass) {
      if (ignore(field, field.getType(), fromClass)) return Trilean.FALSE;
      if (include(field, field.getType(), fromClass)) return Trilean.TRUE;
      return shouldResolveField(field, fromClass);
    }

    @1.5.0/docs/api/java/lang/Override.html">Override
    public Trilean isAccessor(1.5.0/docs/api/java/lang/reflect/Method.html">Method method, Class<?> fromClass) {
      if (ignore(method, method.getReturnType(), fromClass)) return Trilean.FALSE;

      1.5.0/docs/api/java/lang/String.html">String name = null;
      if (method.getName().startsWith("get") && method.getName().length() > 3)
        name = method.getName().substring(3);
      else if (method.getName().startsWith("is") && method.getName().length() > 2
        && method.getReturnType() == boolean.class
        || method.getReturnType() == 1.5.0/docs/api/java/lang/Boolean.html">Boolean.class)
        name = method.getName().substring(2);

      if (name != null) {
        if (include(method, method.getReturnType(), fromClass)) return Trilean.TRUE;
        if (find(XmlTransient.class, fromClass, "set" + name, method.getReturnType()) != null)
          return Trilean.FALSE;


        return shouldResolveMethod(method, fromClass);
      }

      return Trilean.FALSE;
    }

    @1.5.0/docs/api/java/lang/Override.html">Override
    public Trilean isMutator(1.5.0/docs/api/java/lang/reflect/Method.html">Method method, Class<?> fromClass) {
      Class<?> paramClass = method.getParameterTypes().length == 1 ? method
        .getParameterTypes()[0] : 5+0%2Fdocs%2Fapi+Object">Object.class;
      if (ignore(method, paramClass, fromClass)) return Trilean.FALSE;

      if (method.getName().startsWith("set") && method.getName().length() > 3) {
        if (include(method, method.getReturnType(), fromClass)) return Trilean.TRUE;

        1.5.0/docs/api/java/lang/String.html">String name = method.getName().substring(3);

        // Exclude it if there is a corresponding accessor annotated with XmlTransient
        if (find(XmlTransient.class, fromClass, "get" + name) != null) return Trilean.FALSE;
        if (paramClass.equals(boolean.class) || paramClass.equals(1.5.0/docs/api/java/lang/Boolean.html">Boolean.class)) {
          if (find(XmlTransient.class, fromClass, "is" + name) != null)
            return Trilean.FALSE;
        }


        return shouldResolveMethod(method, fromClass);
      }

      return Trilean.FALSE;
    }

    private Trilean shouldResolveField(1.5.0/docs/api/java/lang/reflect/Field.html">Field field, Class<?> fromClass) {
      XmlAccessorType ann = find(XmlAccessorType.class, field, fromClass);

      if (isDefaultVisibilityMember(field, ann) || isValidPublicMember(field, ann) || isValidFieldMember(field, ann)) {
        return Trilean.TRUE;
      } else {
        return Trilean.FALSE;
      }
    }

    private Trilean shouldResolveMethod(1.5.0/docs/api/java/lang/reflect/Method.html">Method m, Class<?> fromClass) {
      XmlAccessorType ann = find(XmlAccessorType.class, m, fromClass);

      if (isDefaultVisibilityMember(m, ann) || isValidPropertyMember(m, ann) || isValidPublicMember(m, ann)) {
        return Trilean.TRUE;
      } else {
        return Trilean.FALSE;
      }
    }

    private boolean isDefaultVisibilityMember(1.5.0/docs/api/java/lang/reflect/Member.html">Member m, XmlAccessorType xmlAccessTypeAnn) {
      return xmlAccessTypeAnn == null && VisibilityFilter.PACKAGE_PUBLIC.isVisible(m);
    }

    private boolean isValidFieldMember(1.5.0/docs/api/java/lang/reflect/Member.html">Member m, XmlAccessorType ann) {
      return ann != null && ann.value() == XmlAccessType.FIELD && VisibilityFilter.PRIVATE.isVisible(m);
    }

    private boolean isValidPublicMember(1.5.0/docs/api/java/lang/reflect/Member.html">Member m, XmlAccessorType ann) {
      return ann != null && ann.value() == XmlAccessType.1.5.0/docs/api/org/omg/CORBA/PUBLIC_MEMBER.html">PUBLIC_MEMBER && VisibilityFilter.PACKAGE_PUBLIC.isVisible(m);
    }

    private boolean isValidPropertyMember(1.5.0/docs/api/java/lang/reflect/Member.html">Member m, XmlAccessorType ann) {
      return ann != null && ann.value() == XmlAccessType.PROPERTY && VisibilityFilter.PACKAGE_PUBLIC.isVisible(m);
    }

    private boolean ignore(1.5.0/docs/api/java/lang/reflect/AccessibleObject.html">AccessibleObject property, Class<?> ofType, Class<?> fromClass) {
      XmlTransient xmlTransientAnn = find(XmlTransient.class, property, ofType);
      if (xmlTransientAnn != null) return true;

      return false;
    }

    private boolean include(1.5.0/docs/api/java/lang/reflect/AccessibleObject.html">AccessibleObject property, Class<?> ofType, Class<?> fromClass) {
      if (find(XmlAttribute.class, property, ofType) != null
        || find(XmlElement.class, property, ofType) != null) return true;

      return false;
    }
  }

  private <A extends Annotation> A find(Class<A> annotation, 1.5.0/docs/api/java/lang/reflect/AccessibleObject.html">AccessibleObject onObject,
                                        Class<?> onClass) {
    A ann = onObject.getAnnotation(annotation);
    if (ann != null) return ann;
    return find(annotation, onClass);
  }

  private <A extends Annotation> A find(Class<A> annotation, Class<?> onClass) {
    A ann = onClass.getAnnotation(annotation);
    if (ann == null && onClass.getPackage() != null)
      ann = onClass.getPackage().getAnnotation(annotation);
    return ann;
  }

  private <A extends Annotation> A find(Class<A> annotation, Class<?> inClass, 1.5.0/docs/api/java/lang/String.html">String methodName,
                                        Class<?>... parameterTypes) {
    A ann = null;
    for (Class<?> clazz = inClass; clazz != null; clazz = clazz.getSuperclass()) {
      try {
        for (1.5.0/docs/api/java/lang/reflect/Method.html">Method m : clazz.getDeclaredMethods())
          if (m.getName().equals(methodName)
            && 1.5.0/docs/api/java/util/Arrays.html">Arrays.equals(m.getParameterTypes(), parameterTypes))
            if (m.isAnnotationPresent(annotation))
              return m.getAnnotation(annotation);
            else
              break;

      } catch (1.5.0/docs/api/java/lang/SecurityException.html">SecurityException e) {
        throw new 1.5.0/docs/api/java/lang/RuntimeException.html">RuntimeException(e);
      }
    }
    return ann;
  }
}