org.carrot2.util.attribute.BindableDescriptorBuilder.java Source code

Java tutorial

Introduction

Here is the source code for org.carrot2.util.attribute.BindableDescriptorBuilder.java

Source

/*
 * Carrot2 project.
 *
 * Copyright (C) 2002-2010, Dawid Weiss, Stanisaw Osiski.
 * All rights reserved.
 *
 * Refer to the full license file "carrot2.LICENSE"
 * in the root folder of the repository checkout or at:
 * http://www.carrot2.org/carrot2.LICENSE
 */

package org.carrot2.util.attribute;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.carrot2.util.attribute.constraint.IsConstraint;
import org.carrot2.util.attribute.metadata.AttributeMetadata;
import org.carrot2.util.attribute.metadata.BindableMetadata;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

/**
 * Builds {@link BindableDescriptor}s based on the provided bindable type instances.
 */
public class BindableDescriptorBuilder {
    /**
     * Builds a {@link BindableDescriptor} for an initialized instance of a
     * {@link Bindable} type. Notice that the set of {@link AttributeDescriptor} found in
     * the returned {@link BindableDescriptor} may vary depending on how the provided
     * instance is initialized.
     * 
     * @param initializedInstance initialized instance of a {@link Bindable} type for
     *            which to build the descriptor
     * @return the descriptor built
     */
    public static BindableDescriptor buildDescriptor(Object initializedInstance) {
        return buildDescriptor(initializedInstance, new HashSet<Object>());
    }

    /**
     * Internal implementation of descriptor building.
     */
    private static BindableDescriptor buildDescriptor(Object initializedInstance, Set<Object> processedInstances) {
        final Class<?> clazz = initializedInstance.getClass();
        if (clazz.getAnnotation(Bindable.class) == null) {
            throw new IllegalArgumentException("Provided instance must be @Bindable");
        }

        if (!processedInstances.add(initializedInstance)) {
            throw new UnsupportedOperationException("Circular references are not supported");
        }

        // Load metadata
        final BindableMetadata bindableMetadata = BindableMetadata.forClassWithParents(clazz);

        // Build descriptors for direct attributes
        final Map<String, AttributeDescriptor> attributeDescriptors = buildAttributeDescriptors(initializedInstance,
                bindableMetadata);

        // Build descriptors for nested bindables
        final Map<Field, BindableDescriptor> bindableDescriptors = Maps.newLinkedHashMap();

        final Collection<Field> fieldsFromBindableHierarchy = BindableUtils.getFieldsFromBindableHierarchy(clazz);
        for (final Field field : fieldsFromBindableHierarchy) {
            // Get class of runtime value
            Object fieldValue = null;
            try {
                field.setAccessible(true);
                fieldValue = field.get(initializedInstance);
            } catch (final Exception e) {
                throw new RuntimeException("Could not retrieve default value of field: "
                        + field.getClass().getName() + "#" + field.getName());
            }

            // Descend only for non-null values
            if (fieldValue != null && fieldValue.getClass().getAnnotation(Bindable.class) != null) {
                bindableDescriptors.put(field, buildDescriptor(fieldValue, processedInstances));
            }
        }

        return new BindableDescriptor(clazz, bindableMetadata, bindableDescriptors, attributeDescriptors);
    }

    /**
     * Builds descriptors for direct attributes.
     */
    private static Map<String, AttributeDescriptor> buildAttributeDescriptors(Object initializedInstance,
            BindableMetadata bindableMetadata) {
        final Class<?> clazz = initializedInstance.getClass();

        final Map<String, AttributeDescriptor> result = Maps.newLinkedHashMap();
        final Collection<Field> fieldsFromBindableHierarchy = BindableUtils.getFieldsFromBindableHierarchy(clazz);

        for (final Field field : fieldsFromBindableHierarchy) {
            if (field.getAnnotation(Attribute.class) != null) {
                result.put(BindableUtils.getKey(field),
                        buildAttributeDescriptor(initializedInstance, field, bindableMetadata));
            }
        }

        return result;
    }

    /**
     * Builds {@link AttributeDescriptor} for a field from a {@link Bindable} type.
     */
    private static AttributeDescriptor buildAttributeDescriptor(Object initializedInstance, Field field,
            BindableMetadata bindableMetadata) {
        Object defaultValue = null;

        try {
            field.setAccessible(true);
            defaultValue = field.get(initializedInstance);
        } catch (final Exception e) {
            throw new RuntimeException(
                    "Could not retrieve default value of attribute: " + BindableUtils.getKey(field));
        }

        AttributeMetadata attributeMetadata = null;
        if (bindableMetadata != null) {
            attributeMetadata = bindableMetadata.getAttributeMetadata().get(field.getName());
        }
        return new AttributeDescriptor(field, defaultValue, getConstraintAnnotations(field), attributeMetadata);
    }

    /**
     * Gets constraint annotations for a field, ignoring any other annotations.
     */
    private static List<Annotation> getConstraintAnnotations(Field field) {
        final Annotation[] annotations = field.getAnnotations();
        final ArrayList<Annotation> constraintAnnotations = Lists.newArrayList();
        for (Annotation annotation : annotations) {
            if (annotation.annotationType().isAnnotationPresent(IsConstraint.class)) {
                constraintAnnotations.add(annotation);
            }
        }

        return constraintAnnotations;
    }
}