org.commonjava.rwx.binding.internal.xbr.XBRBindingContext.java Source code

Java tutorial

Introduction

Here is the source code for org.commonjava.rwx.binding.internal.xbr.XBRBindingContext.java

Source

/**
 * Copyright (C) 2010 Red Hat, Inc. (jdcasey@commonjava.org)
 *
 * Licensed 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.commonjava.rwx.binding.internal.xbr;

import static org.apache.commons.lang.StringUtils.join;
import static org.commonjava.rwx.binding.anno.AnnotationUtils.hasAnnotation;
import static org.commonjava.rwx.binding.anno.AnnotationUtils.isMessage;

import org.apache.xbean.recipe.ArrayRecipe;
import org.apache.xbean.recipe.CollectionRecipe;
import org.apache.xbean.recipe.DefaultRepository;
import org.apache.xbean.recipe.MapRecipe;
import org.apache.xbean.recipe.ObjectRecipe;
import org.apache.xbean.recipe.Recipe;
import org.apache.xbean.recipe.Repository;
import org.commonjava.rwx.binding.anno.ArrayPart;
import org.commonjava.rwx.binding.anno.Contains;
import org.commonjava.rwx.binding.anno.Converter;
import org.commonjava.rwx.binding.anno.StructPart;
import org.commonjava.rwx.binding.error.BindException;
import org.commonjava.rwx.binding.internal.xbr.helper.ArrayBinder;
import org.commonjava.rwx.binding.internal.xbr.helper.ArrayMappingBinder;
import org.commonjava.rwx.binding.internal.xbr.helper.CollectionBinder;
import org.commonjava.rwx.binding.internal.xbr.helper.MapBinder;
import org.commonjava.rwx.binding.internal.xbr.helper.MessageBinder;
import org.commonjava.rwx.binding.internal.xbr.helper.StructMappingBinder;
import org.commonjava.rwx.binding.mapping.ArrayMapping;
import org.commonjava.rwx.binding.mapping.FieldBinding;
import org.commonjava.rwx.binding.mapping.Mapping;
import org.commonjava.rwx.binding.mapping.StructMapping;
import org.commonjava.rwx.binding.spi.Binder;
import org.commonjava.rwx.binding.spi.BindingContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Map;

public class XBRBindingContext implements BindingContext {

    private final Repository repository = new DefaultRepository();

    private final Map<Class<?>, Mapping<?>> mappings;

    public XBRBindingContext(final Map<Class<?>, Mapping<?>> mappings) throws BindException {
        this.mappings = mappings;

        for (final Mapping<?> recipe : mappings.values()) {
            final Recipe builder = new ObjectRecipe(recipe.getObjectType());

            repository.add(recipe.getObjectType().getName(), builder);
        }

        // TODO: setup MapRecipe / CollectionRecipe / ArrayRecipe to deal with fields appropriately!
        for (final Mapping<?> recipe : mappings.values()) {
            final ObjectRecipe r = (ObjectRecipe) repository.get(recipe.getObjectType().getName());

            final Map<? extends Object, FieldBinding> bindings = recipe.getFieldBindings();

            Logger logger = LoggerFactory.getLogger(getClass());
            logger.debug("Processing recipe: {}", recipe.getObjectType());

            final Object[] ctorKeys = recipe.getConstructorKeys();
            final String[] ctorArgs = new String[ctorKeys.length];
            for (int i = 0; i < ctorKeys.length; i++) {
                logger.debug("Getting field binding: {}", ctorKeys[i]);
                final FieldBinding binding = bindings.get(ctorKeys[i]);
                ctorArgs[i] = binding.getFieldName();
            }

            r.setConstructorArgNames(ctorArgs);

            for (final Map.Entry<? extends Object, FieldBinding> entry : bindings.entrySet()) {
                final FieldBinding binding = entry.getValue();
                if (mappings.containsKey(binding.getFieldType())) {
                    final Recipe ref = (Recipe) repository.get(binding.getFieldType().getName());
                    if (ref == null) {
                        throw new BindException("Cannot find XBR recipe: " + binding.getFieldType() + " for field: "
                                + entry.getKey() + " in: " + recipe.getObjectType());
                    }

                    r.setProperty(binding.getFieldName(), ref);
                } else if (Map.class.isAssignableFrom(binding.getFieldType())) {
                    final MapRecipe fr = new MapRecipe(binding.getFieldType());
                    r.setProperty(binding.getFieldName(), fr);
                } else if (Collection.class.isAssignableFrom(binding.getFieldType())) {
                    final CollectionRecipe cr = new CollectionRecipe(binding.getFieldType());
                    r.setProperty(binding.getFieldName(), cr);
                } else if (binding.getFieldType().isArray()) {
                    final ArrayRecipe ar = new ArrayRecipe(binding.getFieldType().getComponentType());
                    r.setProperty(binding.getFieldName(), ar);
                }
            }
        }
    }

    public static ObjectRecipe setupObjectRecipe(final Mapping<?> mapping) {
        final ObjectRecipe recipe = new ObjectRecipe(mapping.getObjectType());

        final Map<? extends Object, FieldBinding> bindings = mapping.getFieldBindings();

        final Object[] ctorKeys = mapping.getConstructorKeys();
        final String[] ctorArgs = new String[ctorKeys.length];
        final Class<?>[] ctorArgTypes = new Class<?>[ctorKeys.length];
        for (int i = 0; i < ctorKeys.length; i++) {
            final FieldBinding binding = bindings.get(ctorKeys[i]);
            ctorArgs[i] = binding.getFieldName();
            ctorArgTypes[i] = binding.getFieldType();
        }

        Logger logger = LoggerFactory.getLogger(XBRBindingContext.class);
        logger.debug("CTOR {} with names: ({}) and types: ({})", mapping.getObjectType().getName(),
                join(ctorArgs, ", "), join(ctorArgTypes, ", "));
        recipe.setConstructorArgNames(ctorArgs);
        recipe.setConstructorArgTypes(ctorArgTypes);

        return recipe;
    }

    public Field findField(final FieldBinding binding, final Class<?> parentCls) throws BindException {
        Field field = null;
        Class<?> fieldOwner = parentCls;
        while (field == null && fieldOwner != null) {
            try {
                field = fieldOwner.getDeclaredField(binding.getFieldName());
            } catch (final NoSuchFieldException e) {
                // TODO: log this to debug log-level.
                field = null;
                fieldOwner = fieldOwner.getSuperclass();
            }
        }

        if (field == null) {
            throw new BindException("Cannot find field: " + binding.getFieldName()
                    + " in class (or parent classes of): " + parentCls.getName());
        }

        return field;
    }

    public MessageBinder newMessageBinder(final Class<?> messageType) throws BindException {
        if (!isMessage(messageType)) {
            throw new BindException("Invalid entry-point class. " + messageType
                    + " must be annotated with either @Request or @Response!");
        }

        final ArrayMapping mapping = (ArrayMapping) mappings.get(messageType);
        if (mapping == null) {
            throw new BindException("Cannot find mapping for entry-point class: " + messageType);
        }

        return new MessageBinder(messageType, mapping, this);
    }

    public Binder newBinder(final Binder parent, final Class<?> type) throws BindException {
        return newBinder(parent, type, null);
    }

    public Binder newBinder(final Binder parent, final Field field) throws BindException {
        return newBinder(parent, field.getType(), field);
    }

    @SuppressWarnings("unchecked")
    protected Binder newBinder(final Binder parent, final Class<?> type, final Field field) throws BindException {
        Converter bindVia = null;
        if (field != null) {
            bindVia = field.getAnnotation(Converter.class);
            if (bindVia == null) {
                bindVia = field.getType().getAnnotation(Converter.class);
            }
        }

        Logger logger = LoggerFactory.getLogger(getClass());
        logger.trace("Using ValueBinder: {} for field: {}", bindVia, field);

        Binder binder = null;
        if (bindVia != null && field.getAnnotation(Contains.class) == null) {
            return XBRBinderInstantiator.newValueBinder(bindVia, parent, type, this);
        } else if (Map.class.isAssignableFrom(type)) {
            binder = new MapBinder(parent, type, field, this);
        } else if (Collection.class.isAssignableFrom(type)) {
            binder = new CollectionBinder(parent, type, field, this);
        } else if (type.isArray() && !type.getComponentType().isPrimitive()) {
            binder = new ArrayBinder(parent, type.getComponentType(), field, this);
        } else if (bindVia != null) {
            return XBRBinderInstantiator.newValueBinder(bindVia, parent, type, this);
        } else if (mappings.containsKey(type)) {
            final Mapping<?> mapping = mappings.get(type);

            if (isMessage(type)) {
                return new MessageBinder(type, (ArrayMapping) mapping, this);
            } else if (hasAnnotation(type, ArrayPart.class)) {
                binder = new ArrayMappingBinder(parent, type, (ArrayMapping) mapping, this);
            } else if (hasAnnotation(type, StructPart.class)) {
                binder = new StructMappingBinder(parent, type, (StructMapping) mapping, this);
            } else {
                throw new BindException("Unknown Mapping: " + mapping);
            }

        }

        return binder;
    }
}