Package org.apache.tuscany.sca.binding.corba.provider.types

Source Code of org.apache.tuscany.sca.binding.corba.provider.types.TypeTreeCreator

/*
* 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.tuscany.sca.binding.corba.provider.types;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.tuscany.sca.binding.corba.provider.exceptions.RequestConfigurationException;
import org.apache.tuscany.sca.binding.corba.provider.meta.CorbaArray;
import org.apache.tuscany.sca.binding.corba.provider.meta.CorbaUnionElement;
import org.apache.tuscany.sca.binding.corba.provider.meta.CorbaUnionElementType;

/**
* @version $Rev: 697647 $ $Date: 2008-09-22 01:52:08 +0100 (Mon, 22 Sep 2008) $
*          Creator of types tree.
*/
public class TypeTreeCreator {

    /**
     * Helps to determine if type is a primitive.
     */
    private static List<Class<?>> primitives = new ArrayList<Class<?>>();

    static {
        primitives.add(boolean.class);
        primitives.add(byte.class);
        primitives.add(short.class);
        primitives.add(int.class);
        primitives.add(long.class);
        primitives.add(double.class);
        primitives.add(float.class);
        primitives.add(char.class);
        primitives.add(String.class);
        primitives.add(Boolean.class);
        primitives.add(Byte.class);
        primitives.add(Short.class);
        primitives.add(Integer.class);
        primitives.add(Long.class);
        primitives.add(Double.class);
        primitives.add(Float.class);
        primitives.add(Character.class);
    }

    /**
     * Creates class for given string argument.
     *
     * @param name name of type
     * @return type
     */
    private static Class<?> createClassFromString(String name) {
        Class<?> result = null;
        try {
            if (name.length() == 1) {
                // primitives
                switch (name.charAt(0)) {
                    case 'Z':
                        result = boolean.class;
                        break;
                    case 'C':
                        result = char.class;
                        break;
                    case 'B':
                        result = byte.class;
                        break;
                    case 'S':
                        result = short.class;
                        break;
                    case 'I':
                        result = int.class;
                        break;
                    case 'J':
                        result = long.class;
                        break;
                    case 'F':
                        result = float.class;
                        break;
                    case 'D':
                        result = double.class;
                        break;
                }
            } else {
                // class
                name = name.substring(1, name.length() - 1);
                result = Class.forName(name);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * Reduces dimension of array.
     *
     * @param forClass array class
     * @return reduced array
     */
    private static Class<?> reduceArrayDimension(Class<?> forClass) {
        String name = forClass.getName();
        try {
            String reduced = name.substring(1, name.length());
            if (reduced.startsWith("[")) {
                // reduced class is still an array
                return Class.forName(reduced);
            } else {
                // reduced class may be primitive or class
                return createClassFromString(reduced);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Return given array without first element
     *
     * @param array
     * @return
     */
    private static int[] removeFirstElement(int[] array) {
        int[] result = new int[array.length - 1];
        System.arraycopy(array, 1, result, 0, result.length);
        return result;
    }

    /**
     * Converts objects annotations to structure which will be used by this
     * class
     *
     * @param notes
     * @return
     */
    private static AnnotationAttributes createAnnotationAttributes(Annotation[] notes) {
        AnnotationAttributes attrs = new AnnotationAttributes();
        for (int i = 0; notes != null && i < notes.length; i++) {
            if (notes[i].annotationType().equals(CorbaArray.class)) {
                attrs.setCorbaArray(true);
                attrs.setCorbaArrayLength(((CorbaArray)notes[i]).value());
            }
        }
        return attrs;
    }

    /**
     * Creates tree for given type.
     *
     * @param forClass
     * @return type tree
     */
    public static TypeTree createTypeTree(Class<?> forClass, Annotation[] notes) throws RequestConfigurationException {
        TypeTree tree = new TypeTree();
        TypeTreeNode rootNode = null;
        AnnotationAttributes attrs = createAnnotationAttributes(notes);
        rootNode = inspectClassHierarchy(forClass, attrs, tree);
        tree.setRootNode(rootNode);
        return tree;

    }

    /**
     * Recurrent method which builds type tree.
     *
     * @param forClass
     * @param tree
     * @return
     */
    private static TypeTreeNode inspectClassHierarchy(Class<?> forClass, AnnotationAttributes attributes, TypeTree tree)
        throws RequestConfigurationException {

        TypeTreeNode node = null;
        node = createTypeNode(forClass, attributes);

        NodeType nodeType = node.getNodeType();
        TypeTreeNode[] children = null;

        if (nodeType.equals(NodeType.primitive)) {
            // stop condition for recurrent method
        } else if (nodeType.equals(NodeType.array)) {
            Class<?> reduced = reduceArrayDimension(node.getJavaClass());
            children = new TypeTreeNode[1];
            int[] newLengths = removeFirstElement(attributes.getCorbaArrayLength());
            attributes.setCorbaArrayLength(newLengths);
            children[0] = inspectClassHierarchy(reduced, attributes, tree);
        } else if (nodeType.equals(NodeType.sequence)) {
            // reducing sequence dimension
            Class<?> reduced = reduceArrayDimension(node.getJavaClass());
            children = new TypeTreeNode[1];
            children[0] = inspectClassHierarchy(reduced, attributes, tree);
            // System.arraycopy(src, srcPos, dest, destPos, length)
        } else if (nodeType.equals(NodeType.struct) || nodeType.equals(NodeType.exception)) {
            // inspect types for every structure member
            Field[] fields = node.getJavaClass().getFields();
            children = new TypeTreeNode[fields.length];
            for (int i = 0; i < fields.length; i++) {
                Class<?> field = fields[i].getType();
                AnnotationAttributes fAttrs = createAnnotationAttributes(fields[i].getAnnotations());
                TypeTreeNode child = inspectClassHierarchy(field, fAttrs, tree);
                child.setName(fields[i].getName());
                children[i] = child;
            }
        } else if (nodeType.equals(NodeType.idl_enum)) {

        } else if (nodeType.equals(NodeType.union)) {
            // inspect types for every structure member
            Field[] fields = node.getJavaClass().getDeclaredFields();
            children = new TypeTreeNode[fields.length];
            for (int i = 0; i < fields.length; i++) {
                Class<?> field = fields[i].getType();
                AnnotationAttributes fAttrs = createAnnotationAttributes(fields[i].getAnnotations());
                TypeTreeNode child = inspectClassHierarchy(field, fAttrs, tree);
                child.setName(fields[i].getName());
                children[i] = child;
            }
        } else if (nodeType.equals(NodeType.reference)) {
            // TODO: CORBA references
        }

        node.setChildren(children);
        return node;
    }

    /**
     * Creating and configuring TypeTreeNode for given class.
     *
     * @param forClass class
     * @return node
     * @throws RequestConfigurationException
     */
    private static TypeTreeNode createTypeNode(Class<?> forClass, AnnotationAttributes attributes)
        throws RequestConfigurationException {
        TypeTreeNode node = new TypeTreeNode();
        if (forClass.isArray() && !attributes.isCorbaArray()) {
            node.setNodeType(NodeType.sequence);
            node.setJavaClass(forClass);
        } else if (forClass.isArray() && attributes.isCorbaArray()) {
            node.setNodeType(NodeType.array);
            node.setJavaClass(forClass);
            try {
                // set the actual array size for further use by ArrayTypeHelper
                node.setAttributes(attributes.getCorbaArrayLength()[0]);
            } catch (ArrayIndexOutOfBoundsException e) {
                RequestConfigurationException exc =
                    new RequestConfigurationException("Annotated array size doesn't match declared arrays size");
                throw exc;
            }
        } else if (primitives.contains(forClass)) {
            node.setNodeType(NodeType.primitive);
            node.setJavaClass(forClass);
            node.setChildren(null);
        } else if (forClass.isInterface()) {
            node.setNodeType(NodeType.reference);
            node.setJavaClass(forClass);
            node.setChildren(null);
        } else if (isStructType(forClass)) {
            node.setNodeType(NodeType.struct);
            node.setJavaClass(forClass);
        } else if (isEnumType(forClass)) {
            node.setNodeType(NodeType.idl_enum);
            node.setJavaClass(forClass);
        } else if (isUserException(forClass)) {
            node.setNodeType(NodeType.exception);
            node.setJavaClass(forClass);
        } else if (isUnionType(forClass)) {
            node.setNodeType(NodeType.union);
            node.setJavaClass(forClass);
            node.setAttributes(getUnionAttributes(forClass));
        } else {
            RequestConfigurationException e =
                new RequestConfigurationException("User defined type which cannot be handled: " + forClass
                    .getCanonicalName());
            throw e;
        }
        return node;
    }

    /**
     * Tells whether given class is structure
     *
     * @param forClass
     * @return
     */
    private static boolean isStructType(Class<?> forClass) {
        int classMods = forClass.getModifiers();
        if (!Modifier.isFinal(classMods)) {
            return false;
        }
        boolean areCtorsValid = false;
        Class<?>[] fieldsTypes = null;
        Constructor<?>[] ctors = forClass.getConstructors();
        /*
         * Do we have 2 ctors and one of them is null ctor?
         */
        if (ctors.length != 2) {
            return false;
        }
        for (int i = 0; i < ctors.length; i++) {
            Class<?>[] params = ctors[i].getParameterTypes();
            if (params.length == 0) {
                areCtorsValid = true;
            } else {
                fieldsTypes = params;
            }
        }
        if (!areCtorsValid) {
            return false;
        }
        /*
         * Are constructor args declared as class fields?
         */
        Field[] fields = forClass.getFields();
        Set<Class<?>> fieldsSet = new HashSet<Class<?>>(Arrays.asList(fieldsTypes));
        for (int i = 0; i < fields.length && !fieldsSet.isEmpty(); i++) {
            int mods = fields[i].getModifiers();
            if (Modifier.isPublic(mods) && !Modifier.isStatic(mods) && !Modifier.isFinal(mods)) {
                fieldsSet.remove(fields[i].getType());
            }

        }
        return fieldsSet.isEmpty();
    }

    /**
     * Tells whether given class is enum
     *
     * @param forClass
     * @return
     */
    private static boolean isEnumType(Class<?> forClass) {
        boolean isValueMethod = false;
        boolean isFromIntMethod = false;
        /*
         * enum type should have value and from_int methods
         */
        try {
            Method valueMet = forClass.getMethod("value", new Class[] {});
            int modValueMet = valueMet.getModifiers();
            if (valueMet.getReturnType().equals(int.class) && Modifier.isPublic(modValueMet)) {
                isValueMethod = true;
            }
            Method fromIntMet = forClass.getMethod("from_int", new Class[] {int.class});
            int modFromIntMet = fromIntMet.getModifiers();
            if ((fromIntMet.getReturnType().equals(forClass) && Modifier.isPublic(modFromIntMet) && Modifier
                .isStatic(modFromIntMet))) {
                isFromIntMethod = true;
            }
        } catch (NoSuchMethodException e) {
        }
        if (!isFromIntMethod || !isValueMethod) {
            return false;
        }
        /*
         * enum type should also contain minimum one pair of fields: EnumType
         * field and int _field
         */
        int enumCount = 0;
        Field[] fields = forClass.getFields();
        for (int i = 0; i < fields.length; i++) {
            if (fields[i].getType().equals(forClass)) {
                int modifiers = fields[i].getModifiers();
                if (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers) && Modifier.isFinal(modifiers)) {
                    try {
                        Field field = forClass.getField("_" + fields[i].getName());
                        if (field.getType().equals(int.class)) {
                            enumCount++;
                        }
                    } catch (NoSuchFieldException e) {
                    }

                }
            }
        }
        return enumCount > 0;
    }

    /**
     * Tells whether given class is CORBA user exception
     *
     * @param forClass
     * @return
     */
    private static boolean isUserException(Class<?> forClass) {
        do {
            if (forClass.equals(Exception.class)) {
                return true;
            } else {
                forClass = forClass.getSuperclass();
            }
        } while (forClass != null && !forClass.equals(Object.class));
        return false;
    }

    /**
     * Tells whether given class is CORBA union. This method validates usage of
     * unions annotations.
     *
     * @param forClass
     * @return
     * @throws RequestConfigurationException
     */
    private static boolean isUnionType(Class<?> forClass) throws RequestConfigurationException {
        int classMods = forClass.getModifiers();
        if (!Modifier.isFinal(classMods)) {
            return false;
        }
        boolean atLeastOneOption = false;
        boolean discriminatorPresent = false;
        for (int i = 0; i < forClass.getDeclaredFields().length; i++) {
            CorbaUnionElement note = forClass.getDeclaredFields()[i].getAnnotation(CorbaUnionElement.class);
            if (note != null) {
                int fieldMod = forClass.getDeclaredFields()[i].getModifiers();
                if (Modifier.isPrivate(fieldMod) && !Modifier.isFinal(fieldMod) && !Modifier.isStatic(fieldMod)) {
                    if (note.type().equals(CorbaUnionElementType.discriminator)) {
                        if (discriminatorPresent) {
                            throw new RequestConfigurationException(
                                                                    "More than one discriminators declared on: " + forClass);
                        }
                        discriminatorPresent = true;
                    } else {
                        atLeastOneOption = true;
                    }
                } else {
                    throw new RequestConfigurationException(
                                                            "Annotated union field should be private, not final and no static on class: " + forClass);
                }
            }
        }
        if (atLeastOneOption && !discriminatorPresent) {
            throw new RequestConfigurationException("No discriminator annotation found on: " + forClass);
        } else if (!atLeastOneOption && discriminatorPresent) {
            throw new RequestConfigurationException("No union option found on: " + forClass);
        } else if (discriminatorPresent && atLeastOneOption) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Gets union attributes - discriminator field name, option fields etc. This
     * method relies that previously scanned class is valid (method isUnionType)
     *
     * @param forClass
     * @return
     * @throws RequestConfigurationException
     */
    private static UnionAttributes getUnionAttributes(Class<?> forClass) throws RequestConfigurationException {
        UnionAttributes attributes = new UnionAttributes();
        for (int i = 0; i < forClass.getDeclaredFields().length; i++) {
            CorbaUnionElement note = forClass.getDeclaredFields()[i].getAnnotation(CorbaUnionElement.class);
            if (note != null) {
                if (note.type().equals(CorbaUnionElementType.discriminator)) {
                    attributes.setDiscriminatorName(forClass.getDeclaredFields()[i].getName());
                } else if (note.type().equals(CorbaUnionElementType.defaultOption)) {
                    attributes.setDefaultOptionName(forClass.getDeclaredFields()[i].getName());
                } else if (note.type().equals(CorbaUnionElementType.option)) {
                    if (attributes.getOptionsMapping().containsKey(note.optionNumber())) {
                        throw new RequestConfigurationException("In " + forClass
                            + ": field \""
                            + forClass.getDeclaredFields()[i].getName()
                            + "\" uses already used option id: "
                            + note.optionNumber());
                    } else {
                        attributes.getOptionsMapping().put(note.optionNumber(),
                                                           forClass.getDeclaredFields()[i].getName());
                    }
                }
            }
        }
        return attributes;
    }
}
TOP

Related Classes of org.apache.tuscany.sca.binding.corba.provider.types.TypeTreeCreator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.