Package com.envoisolutions.sxc.jaxb

Source Code of com.envoisolutions.sxc.jaxb.ReaderIntrospector

package com.envoisolutions.sxc.jaxb;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

import javax.xml.bind.JAXBElement;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.Duration;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.namespace.QName;

import com.envoisolutions.sxc.builder.BuildException;
import com.envoisolutions.sxc.builder.Builder;
import com.envoisolutions.sxc.builder.CodeBody;
import com.envoisolutions.sxc.builder.ElementParserBuilder;
import com.envoisolutions.sxc.builder.ParserBuilder;
import com.envoisolutions.sxc.util.Base64;
import com.sun.codemodel.JBlock;
import com.sun.codemodel.JClass;
import com.sun.codemodel.JCodeModel;
import com.sun.codemodel.JDefinedClass;
import com.sun.codemodel.JExpr;
import com.sun.codemodel.JExpression;
import com.sun.codemodel.JFieldVar;
import com.sun.codemodel.JMethod;
import com.sun.codemodel.JMod;
import com.sun.codemodel.JTryBlock;
import com.sun.codemodel.JType;
import com.sun.codemodel.JVar;
import com.sun.xml.bind.v2.model.core.Adapter;
import com.sun.xml.bind.v2.model.runtime.RuntimeAttributePropertyInfo;
import com.sun.xml.bind.v2.model.runtime.RuntimeClassInfo;
import com.sun.xml.bind.v2.model.runtime.RuntimeElement;
import com.sun.xml.bind.v2.model.runtime.RuntimeElementInfo;
import com.sun.xml.bind.v2.model.runtime.RuntimeElementPropertyInfo;
import com.sun.xml.bind.v2.model.runtime.RuntimeEnumLeafInfo;
import com.sun.xml.bind.v2.model.runtime.RuntimeNonElement;
import com.sun.xml.bind.v2.model.runtime.RuntimePropertyInfo;
import com.sun.xml.bind.v2.model.runtime.RuntimeReferencePropertyInfo;
import com.sun.xml.bind.v2.model.runtime.RuntimeTypeInfoSet;
import com.sun.xml.bind.v2.model.runtime.RuntimeTypeRef;
import com.sun.xml.bind.v2.model.runtime.RuntimeValuePropertyInfo;

public class ReaderIntrospector {
    private static final Logger logger = Logger.getLogger(ReaderIntrospector.class.getName());
 
    private ElementParserBuilder rootReader;
    private JType type_dtFactory;
    private Set<Class<?>> addedXsiTypes = new HashSet<Class<?>>();
    private Builder builder;
    private JFieldVar dtf;
    private JCodeModel model;
    private Map<BuilderKey, ElementParserBuilder> type2Parser
        = new HashMap<BuilderKey, ElementParserBuilder>();
    private Map<RuntimeElementInfo, ElementParserBuilder> ref2Parser
        = new HashMap<RuntimeElementInfo, ElementParserBuilder>();
    private RuntimeTypeInfoSet set;
    private Map<Class, JVar> adapters = new HashMap<Class, JVar>();
    private int adapterCount = 0;
   
    public ReaderIntrospector(Builder builder2, RuntimeTypeInfoSet set) {
        this.builder = builder2;
        rootReader = this.builder.getParserBuilder();
        this.model = rootReader.getCodeModel();
        this.set = set;
       
        type_dtFactory = model._ref(DatatypeFactory.class);
       
        JDefinedClass readerCls = rootReader.getReaderClass();
        dtf = readerCls.field(JMod.STATIC, type_dtFactory, "dtFactory");
        JMethod constructor = builder.getParserConstructor();
        JTryBlock tb = constructor.body()._try();
        JBlock body = tb.body();
        body.assign(dtf, JExpr.direct(DatatypeFactory.class.getName()+".newInstance()"));
        tb._catch((JClass) rootReader.getCodeModel()._ref(DatatypeConfigurationException.class));
       
        Map<QName, ? extends RuntimeElementInfo> elementMappings = set.getElementMappings(null);
        for (Map.Entry<QName, ? extends RuntimeElementInfo> e : elementMappings.entrySet()) {
            QName q = e.getKey();
            RuntimeElementInfo rei = e.getValue();
           
            if (!ref2Parser.containsKey(rei)) {
                createRefParser(rootReader, q, rei);
            }
        }       
       
        Map<Class, ? extends RuntimeClassInfo> beans = set.beans();
        for (Map.Entry<Class, ? extends RuntimeClassInfo> e : beans.entrySet()) {
            Class cls = e.getKey();
            if (!Modifier.isAbstract(cls.getModifiers())) {
                add(rootReader, cls, e.getValue());
            }
        }
       
        for (Map.Entry<Class, ? extends RuntimeEnumLeafInfo> e : set.enums().entrySet()) {
            Class cls = e.getKey();
            RuntimeEnumLeafInfo info = e.getValue();
           
            ElementParserBuilder enumBuilder;
            if (info.isElement()) {
                enumBuilder = rootReader.expectElement(info.getElementName());
            } else {
                enumBuilder = rootReader.expectXsiType(info.getTypeName());
            }
            JExpression toSet = handleElement(enumBuilder, info, null, cls, true);
           
            CodeBody cb = enumBuilder.getBody();
           
            JType type = model._ref(cls);
            JType jaxbElementType = model._ref(JAXBElement.class);
            JType qnameType = model._ref(QName.class);
            JVar qname = cb.decl(qnameType, "_xname", JExpr.direct("reader").invoke("getName"));
           
            enumBuilder.getBody()._return(jaxbElementType,
                                          JExpr._new(jaxbElementType)
                                          .arg(qname).arg(JExpr.dotclass((JClass) type))
                                          .arg(toSet));
        }
    }


    private void createRefParser(ElementParserBuilder builder, QName q, RuntimeElementInfo rei) {
        ElementParserBuilder elBuilder = builder.expectElement(q);
        ref2Parser.put(rei, elBuilder);

        Class<?> c = (Class<?>) rei.getContentType().getType();
        JExpression expression = handleElement(elBuilder, rei.getContentType(), null, c, !c.isPrimitive());
       
        CodeBody cb = elBuilder.getBody();
       
        JType type = model._ref(c);
        JType jaxbElementType = model._ref(JAXBElement.class);
        JType qnameType = model._ref(QName.class);
        JVar qname = cb.decl(qnameType, "_xname", JExpr.direct("reader").invoke("getName"));
       
        elBuilder.getBody()._return(jaxbElementType,
                                    JExpr._new(jaxbElementType)
                                    .arg(qname).arg(JExpr.dotclass((JClass) type))
                                    .arg(expression));
    }


    private void add(ElementParserBuilder b, Class cls, RuntimeClassInfo info) {
        if (!info.isElement()) {
            if (b == rootReader &&
                info.getTypeName() != null) { // this is an anonymous schema type
                addComplexType(b, cls, info);
            }
            return;
        }
       
        ElementParserBuilder classBuilder = b.expectElement(info.getElementName());
        add(classBuilder, info.getElementName(), cls, null, info, false);
    }
   
    private void addComplexType(ElementParserBuilder b, Class<?> cls, RuntimeClassInfo info) {
        if (addedXsiTypes.contains(cls)) return;
       
        ElementParserBuilder builder = b.expectXsiType(info.getTypeName());
       
        add(builder, info.getTypeName(), cls, null, info, true);
    }

    private ElementParserBuilder add(ElementParserBuilder classBuilder,
                                     QName name,
                                     Class cls,
                                     Class parentCls,
                                     RuntimeClassInfo info,
                                     boolean wrap) {
        JCodeModel model = classBuilder.getCodeModel();
        CodeBody body = classBuilder.getBody();
        JType type = model._ref(cls);
       
        JBlock xsiNilReturnBlock = body.getBlock()._if(JExpr.direct("reader").invoke("isXsiNil"))._then();

        // Method factoryMethod = info.getFactoryMethod();
        JVar var = body.decl(type, info.getClazz().getSimpleName(), JExpr._new(type));
        if (wrap) {
            JType jaxbElementType = model._ref(JAXBElement.class);
            JType qnameType = model._ref(QName.class);
            JVar qname = body.getBlock().decl(qnameType, "_xname", JExpr.direct("reader").invoke("getName"));
            body._return(jaxbElementType,
                         JExpr._new(jaxbElementType)
                                .arg(qname).arg(JExpr.dotclass((JClass) type)).arg(var));
            xsiNilReturnBlock._return(JExpr._null());
        } else if (parentCls == null) {
            body._return(var);
            xsiNilReturnBlock._return(JExpr._null());
        } else {
            xsiNilReturnBlock._return();
        }
       
       
        if (info.isOrdered()) {
            // classBuilder.beginSequence();
        }
       
        handleProperties(info, cls, classBuilder, var);
       
        if (info.isOrdered()) {
            // classBuilder.endSequence();
        }
       
        return classBuilder;
    }

    private void handleProperties(RuntimeClassInfo info,
                                  Class beanClass,
                                  ElementParserBuilder classBuilder,
                                  JVar beanVar) {
        for (RuntimePropertyInfo prop : info.getProperties()) {
            if (prop instanceof RuntimeElementPropertyInfo) {
                // Handle an reference to a <complexType>
                RuntimeElementPropertyInfo propEl = (RuntimeElementPropertyInfo) prop;

                // handle the possible choices for this property.
                for (RuntimeTypeRef typeRef : propEl.getTypes()) {
                    handleTypeRef(classBuilder, propEl, typeRef, beanClass, beanVar);
                }
            } else if (prop instanceof RuntimeAttributePropertyInfo) {
                // Handle an attribute
                RuntimeAttributePropertyInfo propAt = (RuntimeAttributePropertyInfo) prop;
                RuntimeNonElement target = propAt.getTarget();

                ParserBuilder attBuilder = classBuilder.expectAttribute(propAt.getXmlName());
                JVar propVar = attBuilder.passParentVariable(beanVar);
               
                if (propAt.isCollection()) {
                  logger.info("Reader: attribute lists are not supported yet!");
                } else {
                    handlePropertyAttribute(attBuilder, propVar, beanClass, propAt, target, set);
                }
            } else if (prop instanceof RuntimeReferencePropertyInfo) {
               
                // Handle a reference to an <element> or choice of elements
                RuntimeReferencePropertyInfo propRef = (RuntimeReferencePropertyInfo) prop;
                for (RuntimeElement re : propRef.getElements()) {
                    RuntimeElementInfo rei = (RuntimeElementInfo) re;
                  
                    ElementParserBuilder elBuilder = classBuilder.expectElement(rei.getElementName());
                    JVar beanVar2 = elBuilder.passParentVariable(beanVar);
                   
                    ElementParserBuilder objBuilder = ref2Parser.get(rei);
                    if (objBuilder == null) {
                        createRefParser(rootReader, rei.getElementName(), rei);
                        objBuilder = ref2Parser.get(rei);
                    }
                   
                    JVar retVar = elBuilder.call(model._ref(rei.getType()), "_ret", objBuilder);
                    elBuilder.getBody()._return(retVar);
                    doSet(elBuilder.getBody().getBlock(), beanVar2, beanClass, propRef, retVar);

                }
            } else if (prop instanceof RuntimeValuePropertyInfo) {
              logger.info("Reader: Attributes on simple types are not supported yet!");
                RuntimeValuePropertyInfo propv = (RuntimeValuePropertyInfo) prop;
               
                ElementParserBuilder builder2 = (ElementParserBuilder) classBuilder.newState();
                JVar var = builder2.passParentVariable(beanVar);
               
                handlePropertyElement(builder2, var, beanClass, propv, propv.getTarget().getTypeName(),
                                      true, propv.getTarget());
            } else {
              logger.info("(JAXB Reader) Cannot yet map property " + prop.getName()
                                   + " with type " + prop.getRawType()
                                   + " on " + beanClass
                                   + " for " + prop.getClass().getName());
            }
        }
       
        if (info.getBaseClass() != null) {
            handleProperties(info.getBaseClass(), beanClass, classBuilder, beanVar);
        }
     }

   
    private void handleTypeRef(ElementParserBuilder classBuilder,
                               RuntimeElementPropertyInfo propEl,
                               RuntimeTypeRef typeRef,
                               Class beanClass,
                               JVar beanVar) {
        QName name = typeRef.getTagName();
    
        // Lets check to see if we alredy we have a parser to handle this scenario
        BuilderKey key = new BuilderKey();
        key.parentClass = beanClass;
        key.type = name;
       
        if (key.type != null && type2Parser.containsKey(key)) {
            ElementParserBuilder child = type2Parser.get(key);
            classBuilder.expectElement(name, child, beanVar);
            return;
        } else {
            // No previous parser available, lets build one.
            ElementParserBuilder propBuilder = classBuilder.expectElement(name);
            JVar propVar = propBuilder.passParentVariable(beanVar);
           
            Class<?> propCls = (Class<?>) typeRef.getTarget().getType();
           
            type2Parser.put(key, propBuilder);
           
            // Check to see if the property class is abstract
            handlePropertyElement(propBuilder, propVar, beanClass,
                                  propEl,
                                  typeRef.getTagName(),
                                  typeRef.isNillable(),
                                  typeRef.getTarget());
           
            // Handle all the possible child types
            Map<Class, ? extends RuntimeClassInfo> beans = set.beans();
            for (Map.Entry<Class, ? extends RuntimeClassInfo> e : beans.entrySet()) {
                Class c = e.getKey();
                RuntimeClassInfo clsInfo = e.getValue();
               
                if (propCls.isAssignableFrom(c)
                    && propCls != c
                    && !Modifier.isAbstract(c.getModifiers())) {
                    ElementParserBuilder xsiBuilder = propBuilder.expectXsiType(clsInfo.getTypeName());
                    JVar xsiVar = xsiBuilder.passParentVariable(propVar);
                    handlePropertyElement(xsiBuilder, xsiVar, beanClass,
                                          propEl,
                                          typeRef.getTagName(),
                                          typeRef.isNillable(),
                                          clsInfo);
                }
            }
        }
    }

    private void handlePropertyElement(ElementParserBuilder builder,
                                       JVar bean,
                                       Class beanClass,
                                       RuntimePropertyInfo propEl,
                                       QName name,
                                       boolean nillable,
                                       RuntimeNonElement target) {
//        if (propEl.isRequired()) {
//            builder.setRequired(true);
//        }
       
        Adapter<Type,Class> adapter = propEl.getAdapter();
        Class c = (Class) target.getType();

        if (target.isSimpleType()) {
            Type type = propEl.getRawType();
            if (type instanceof Class) {
                c = (Class) type;
            }
           
            // type to create for this property
            JBlock block = builder.getBody().getBlock();
            JExpression toSet = handleElement(builder, target, adapter, c, nillable);

            if (toSet != null)
                doSet(block, bean, beanClass, propEl, toSet);
        } else if (target instanceof RuntimeClassInfo) {
            RuntimeClassInfo rci = (RuntimeClassInfo) target;
           
            BuilderKey key = new BuilderKey();
            key.parentClass = beanClass;
            key.type = name;
           
            ElementParserBuilder propBuilder = add(builder, name, c, beanClass, rci, false);
            if (propBuilder != null) {
                JExpression exp = JExpr.direct(c.getSimpleName());
                doSet(propBuilder.getBody().getBlock(), bean, beanClass, propEl, exp);
            }
        } else {
          logger.info("(JAXB Reader) Cannot map type " + c + " yet on " + beanClass);
        }
    }
   
    private JExpression handleElement(ElementParserBuilder builder,
                                      RuntimeNonElement target,
                                      Adapter<Type, Class> adapter,
                                      Class c,
                                      boolean nillable) {
        JExpression toSet;
        if (adapter != null) {
             JVar adapterVar = getAdapter(adapter);
             toSet = adapterVar.invoke("unmarshal").arg(builder.getXSR().invoke("getElementText"));
        } else if ((c.equals(String.class)
            || c.equals(Integer.class)
            || c.equals(Double.class)
            || c.equals(Float.class)
            || c.equals(Short.class)
            || c.equals(Long.class)
            || c.equals(Boolean.class)
            || c.equals(Byte.class)
            || c.isPrimitive())
            && c != byte.class) {
            toSet = builder.as(c, nillable);
        } else if (c.equals(XMLGregorianCalendar.class)) {
            toSet = handleXMLGregorianCalendar(builder, builder.as(String.class, nillable));
        } else if (c.equals(Duration.class)) {
            toSet = handleDuration(builder, builder.as(String.class, nillable));
        } else if (c.isEnum()) {
            try {
                Method method = c.getMethod("value", new Class[0]);
               
                Class<?> readAs = method.getReturnType();
                JExpression val = handleElement(builder, target, adapter, readAs, nillable);
               
                toSet = handleEnum(builder, val, c);
            } catch (Exception e) {
                throw new BuildException(e);
            }
        } else if (c.equals(byte[].class)) {
            JClass buType = (JClass) builder.getCodeModel()._ref(BinaryUtils.class);
           
            toSet = buType.staticInvoke("decodeAsBytes").arg(builder.getXSR());
        } else if (c.equals(Byte.class) || c.equals(byte.class)) {
            toSet = JExpr.cast(model.BYTE, builder.getXSR().invoke("getElementAsInt"));
        } else if (c.equals(BigDecimal.class)) {
            JType bdType = builder.getCodeModel()._ref(BigDecimal.class);
            JVar var = builder.as(String.class, nillable);
            toSet = JExpr._new(bdType).arg(var);
        } else if (c.equals(BigInteger.class)) {
            JType bdType = builder.getCodeModel()._ref(BigInteger.class);
            JVar var = builder.as(String.class, nillable);
            toSet = JExpr._new(bdType).arg(var);
        } else if (c.equals(QName.class)) {
            toSet = builder.getXSR().invoke("getElementAsQName");
        } else if (target instanceof RuntimeClassInfo) {
            RuntimeClassInfo rci = (RuntimeClassInfo) target;
            JType type = builder.getCodeModel()._ref(c);
           
            JVar var = builder.getBody().decl(type, type.name(), JExpr._new(type));
           
            handleProperties(rci, c, builder, var);
           
            return var;
        } else {
          logger.info("(JAXB Reader) Can not map simple type yet: " + c);
            return JExpr._null();
        }
        return toSet;
    }

    private JVar getAdapter(Adapter<Type, Class> adapter) {
        Class c = adapter.adapterType;
        JVar var = adapters.get(c);
        if (var == null) {
            JType jt = model._ref(c);
            var = rootReader.getReaderClass().field(JMod.STATIC, jt, "adapter" + adapterCount++,
                                              JExpr._new(jt));
            adapters.put(c, var);
        }
        return var;
    }


    private void handlePropertyAttribute(ParserBuilder builder,
                                         JVar bean,
                                         Class beanClass,
                                         RuntimeAttributePropertyInfo propEl,
                                         RuntimeNonElement target,
                                         RuntimeTypeInfoSet set) {
        if (propEl.isRequired()) {
            builder.setRequired(true);
        }

        Class<?> c = (Class<?>)propEl.getRawType();
       
        if (target.isSimpleType()) {
            // type to create for this property
            JBlock block = builder.getBody().getBlock();
            JExpression toSet = handleAttribute(builder, target, propEl.getAdapter(), c);

            if (toSet != null)
                doSet(block, bean, beanClass, propEl, toSet);
        } else {
            System.err.println("Unknown type " + c + " - " + target.getClass());
        }

    }
   
    private JExpression handleAttribute(ParserBuilder builder,
                                        RuntimeNonElement target,
                                        Adapter<Type, Class> adapter,
                                        Class c) {
        JExpression toSet;
        if (adapter != null) {
            JVar adapterVar = getAdapter(adapter);
            toSet = adapterVar.invoke("unmarshal").arg(JExpr.direct("_attValue"));
       } else if ((c.equals(String.class)
            || c.equals(Integer.class)
            || c.equals(Double.class)
            || c.equals(Float.class)
            || c.equals(Short.class)
            || c.equals(Long.class)
            || c.equals(Boolean.class)
            || c.equals(Byte.class)
            || c.isPrimitive())
            && c != byte.class) {
            toSet = builder.as(c);
        } else if (c.equals(XMLGregorianCalendar.class)) {
            toSet = handleXMLGregorianCalendar(builder, builder.as(String.class));
        } else if (c.equals(Duration.class)) {
            toSet = handleDuration(builder, builder.as(String.class));
        } else if (c.isEnum()) {
            toSet = handleEnum(builder, builder.as(String.class), c);
        } else if (c.equals(byte[].class)) {
            JClass b64Type = (JClass) builder.getCodeModel()._ref(Base64.class);
           
            toSet = b64Type.staticInvoke("decode").arg(builder.as(String.class));
        } else if (c.equals(BigDecimal.class)) {
            JType bdType = builder.getCodeModel()._ref(BigDecimal.class);
            JVar var = builder.as(String.class);
            toSet = JExpr._new(bdType).arg(var);
        } else if (c.equals(BigInteger.class)) {
            JType bdType = builder.getCodeModel()._ref(BigInteger.class);
            JVar var = builder.as(String.class);
            toSet = JExpr._new(bdType).arg(var);
        } else if (c.equals(QName.class)) {
            JVar var = builder.as(String.class);
            toSet = builder.getXSR().invoke("getAsQName").arg(var);
        } else if (target instanceof RuntimeClassInfo) {
            RuntimeClassInfo rci = (RuntimeClassInfo) target;
            rci.getProperties();
            System.err.println("Could not map attribute " + c);
            return JExpr._null();
        } else {
           
            System.err.println("Could not map attribute " + c);
            return JExpr._null();
        }
        return toSet;
    }
    private void doSet(JBlock block,
                       JVar bean,
                       Class beanClass,
                       RuntimePropertyInfo propEl,
                       JExpression var) {
        if (propEl.isCollection()) {
            String getter = JaxbUtil.getGetter(beanClass, propEl.getName(), propEl.getRawType());
            block.add(bean.invoke(getter).invoke("add").arg(var));
        } else {
            String setter = JaxbUtil.getSetter(beanClass, propEl.getName());
            block.add(bean.invoke(setter).arg(var));
        }
    }

    private JExpression handleXMLGregorianCalendar(ParserBuilder builder, JVar value) {
        return dtf.invoke("newXMLGregorianCalendar").arg(value);
    }
   
    private JExpression handleDuration(ParserBuilder builder, JVar value) {
        return dtf.invoke("newDuration").arg(value);
    }
   
    private JExpression handleEnum(ParserBuilder builder, JExpression value, Class c) {
        JBlock body =  builder.getBody().getBlock();
        JClass jc = (JClass) builder.getCodeModel()._ref(c);
       
        return body.staticInvoke(jc, "fromValue").arg(value);
    }

    public Builder getBuilder() {
        return builder;
    }
}
TOP

Related Classes of com.envoisolutions.sxc.jaxb.ReaderIntrospector

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.