Package com.envoisolutions.sxc.builder.impl

Source Code of com.envoisolutions.sxc.builder.impl.ElementParserBuilderImpl$ElementCall

package com.envoisolutions.sxc.builder.impl;

import com.envoisolutions.sxc.Reader;
import com.envoisolutions.sxc.Context;
import com.envoisolutions.sxc.builder.BuildException;
import com.envoisolutions.sxc.builder.CodeBody;
import com.envoisolutions.sxc.builder.ElementParserBuilder;
import com.envoisolutions.sxc.builder.ParserBuilder;
import com.envoisolutions.sxc.util.XoXMLStreamReader;
import com.sun.codemodel.JBlock;
import com.sun.codemodel.JClassAlreadyExistsException;
import com.sun.codemodel.JConditional;
import com.sun.codemodel.JExpr;
import com.sun.codemodel.JExpression;
import com.sun.codemodel.JForLoop;
import com.sun.codemodel.JInvocation;
import com.sun.codemodel.JMethod;
import com.sun.codemodel.JMod;
import com.sun.codemodel.JType;
import com.sun.codemodel.JVar;
import com.sun.codemodel.JClass;

import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ElementParserBuilderImpl extends AbstractParserBuilder implements ElementParserBuilder {

    Map<QName, ElementParserBuilderImpl> elements = new HashMap<QName, ElementParserBuilderImpl>();
    Map<QName, ElementCall> elementCalls = new HashMap<QName, ElementCall>();
    Map<QName, AttributeParserBuilderImpl> attributes = new HashMap<QName, AttributeParserBuilderImpl>();
    Map<QName, ElementParserBuilderImpl> xsiTypes = new HashMap<QName, ElementParserBuilderImpl>();
    List<ElementParserBuilderImpl> states = new ArrayList<ElementParserBuilderImpl>();
    int varCount = 0;
   
    ElementParserBuilderImpl anyElement;
    boolean root;
    int depth = 1;
    QName name;
    private boolean valueType;
    private boolean checkXsiTypes = true;
    private JInvocation methodInvocation;
    JMethod constructor;

    /**
     * Base class to extend from. Only used when root.
     */
    JClass baseClass;

    public ElementParserBuilderImpl(BuildContext buildContext, String className) throws BuildException {
        this.buildContext = buildContext;
        model = buildContext.getCodeModel();
        try {
            readerClass = model._class(className);
            baseClass = model.ref(Reader.class);
        } catch (JClassAlreadyExistsException e) {
            throw new BuildException(e);
        }

        constructor = readerClass.constructor(JMod.PUBLIC);
        constructor.body().invoke("super").arg(constructor.param(Context.class,"context"));

        method = readerClass.method(JMod.PUBLIC | JMod.FINAL, Object.class, "read");
       
        addBasicArgs(method);
       
        root = true;
    }

    public ElementParserBuilderImpl(ElementParserBuilderImpl parent, boolean increaseDepth, QName name) {
        this.model = parent.getCodeModel();
        this.buildContext = parent.getBuildContext();
        this.readerClass = parent.getReaderClass();
        // this.variables.addAll(parent.getVariables());
        this.name = name;

        method = buildContext.getNextReadMethod(readerClass);
        addBasicArgs(method);

        if (increaseDepth) {
            depth = parent.depth + 1;
        } else {
            depth = parent.depth;
        }
    }
   
    public ElementParserBuilderImpl(ElementParserBuilderImpl parent, QName name) {
        this(parent, true, name);
    }

    public ParserBuilder expectAttribute(QName qname) {
        if (qname == null) {
            throw new NullPointerException("Attribute name cannot be null!");
        }
       
        AttributeParserBuilderImpl b = attributes.get(qname);
        if (b == null) {
            b = new AttributeParserBuilderImpl(this);
            attributes.put(qname, b);
        }
        return b;
    }

    public ElementParserBuilder expectElement(QName qname) {
        if (qname == null) {
            throw new NullPointerException("Element name cannot be null!");
        }
       
        ElementParserBuilderImpl b = elements.get(qname);
        if (b == null) {
            ElementCall call = elementCalls.get(qname);
            if (call != null) {
                b = call.getElement();
            } else {
                b = new ElementParserBuilderImpl(this, qname);
                elements.put(qname, b);
            }
        }
        return b;
    }

    public void expectElement(QName name, ElementParserBuilder elementBuilder, JExpression... vars) {
        if (name == null) {
            throw new NullPointerException("Element name cannot be null!");
        }
        if (elementBuilder == null) {
            throw new NullPointerException("ElementParserBuilder cannot be null!");
        }
        elements.remove(name);
        elementCalls.put(name, new ElementCall((ElementParserBuilderImpl) elementBuilder, vars));
    }

    public ElementParserBuilder expectXsiType(QName qname) {
        if (qname == null) {
            throw new NullPointerException("Element name cannot be null!");
        }
       
        ElementParserBuilderImpl b = getXsiTypes().get(qname);
        if (b == null) {
            b = new ElementParserBuilderImpl(this, qname);
            b.checkXsiTypes  = false;
            getXsiTypes().put(qname, b);
        }
       
        return b;
    }
   
    public ElementParserBuilder expectAnyElement() {
        if (anyElement == null) {
            anyElement = new ElementParserBuilderImpl(this, null);
        }
        return anyElement;
    }

    public ElementParserBuilder expectGlobalElement(QName qname) {
        if (qname == null) {
            throw new NullPointerException("Element name cannot be null!");
        }
       
        ElementParserBuilderImpl b = buildContext.getGlobalElements().get(qname);
        if (b == null) {
            b = new ElementParserBuilderImpl(this, qname);
            buildContext.getGlobalElements().put(qname, b);
        }
        return b;
    }
   
   
    public JVar as(Class<?> cls) {
        return as(cls, false);
    }
   
    public JVar as(Class<?> cls, boolean nillable) {
        this.valueType = true;
       
        if (cls.equals(String.class)) {
            return createVar("getElementAsString", String.class, nillable);
        } else if (cls.equals(int.class) || cls.equals(Integer.class)) {
            return createVar("getElementAsInt", Integer.class, nillable);
        } else if (cls.equals(double.class) || cls.equals(Double.class)) {
            return createVar("getElementAsDouble", Double.class, nillable);
        } else if (cls.equals(float.class) || cls.equals(Float.class)) {
            return createVar("getElementAsFloat", Float.class, nillable);
        } else if (cls.equals(long.class) || cls.equals(Long.class)) {
            return createVar("getElementAsLong", Long.class, nillable);
        } else if (cls.equals(short.class) || cls.equals(Short.class)) {
            return createVar("getElementAsShort", Short.class, nillable);
        } else if (cls.equals(boolean.class) || cls.equals(Boolean.class)) {
            return createVar("getElementAsBoolean", Boolean.class, nillable);
        else if (cls.equals(byte.class) || cls.equals(Byte.class)) {
            return createVar("getElementAsByte", Byte.class, nillable);
        }
        throw new UnsupportedOperationException("Invalid type " + cls);
    }

    private JVar createVar(String value, Class<?> cls, boolean nillable) {
        JVar var;
        if (nillable) {
            var = method.body().decl(model._ref(cls), "value" + varCount++, JExpr._null());
            JConditional cond = method.body()._if(xsrVar.invoke("isXsiNil").not());
           
            JInvocation invocation = xsrVar.invoke(value);
            cond._then().assign(var, invocation);
        } else {
            var = method.body().decl(model._ref(cls), "value" + varCount++, xsrVar.invoke(value));
        }
       
        return var;
    }

    public JVar asString() {
        return as(String.class, false);
    }
   
    public ParserBuilder newState() {
        return newState(codeBlock);
    }

    public ParserBuilder newState(JBlock block) {
        ElementParserBuilderImpl b = new ElementParserBuilderImpl(this, false, name);
        states.add(b);
       
        JMethod nextMethod = b.getMethod();
       
        JInvocation invocation = JExpr.invoke(nextMethod).arg(xsrVar).arg(rtContextVar);
        b.methodInvocation = invocation;
       
        block.add(invocation);
       
        return b;
    }

    @Override
    public JVar passParentVariable(JVar parentVar) {
        if (methodInvocation != null) {
            methodInvocation.arg(parentVar);
        }
       
        return super.passParentVariable(parentVar);
    }

    public JVar call(JType type, String varName, ElementParserBuilder builder) {
        JBlock block = codeBlock;
       
        ElementParserBuilderImpl b = (ElementParserBuilderImpl) builder;
        JMethod nextMethod = b.getMethod();
       
        JInvocation invocation = JExpr.invoke(nextMethod).arg(xsrVar).arg(rtContextVar);
        for (JVar v : b.variables) {
            invocation.arg(v);
        }
       
        return block.decl(type, varName, invocation);
    }

    public CodeBody getBody() {
        return new CodeBodyImpl(this);
    }
   
    protected void write() {
        if (written) return;
       
        written = true;
       
        JBlock b = method.body();
       
        if (!valueType &&
            (elements.size() > 0
            || elementCalls.size() > 0
            || buildContext.getGlobalElements().size() > 0
            || getXsiTypes().size() > 0
            || attributes.size() > 0
            || anyElement != null)){
            writeMainLoop();
        } else {
            b.add(codeBlock);
        }
       
        for (ElementParserBuilderImpl e : states) {
            e.write();
        }
       
        // Add return statement to the end of the block
        if (returnType != null) {
            if(root)
                throw new IllegalStateException("root builder is not allowed to have the return type");
            setReturnType(returnType);
            codeBlock._return(_return);
        }
       
        if (root) {
            b._return(JExpr._null());
        }

        if (root) {
            writeReadAsType();
            readerClass._extends(baseClass);
        }
    }
   
    protected void writeReadAsType() {
        JMethod m = getReaderClass().method(JMod.PUBLIC, Object.class, "read");
        m.param(XoXMLStreamReader.class, "reader");
        m.param(buildContext.getStringToObjectMap(), "properties");
        JVar typeVar = m.param(QName.class, "type");
        m._throws(XMLStreamException.class);
       
        JBlock block = m.body();
        writeXsiChecks(block, typeVar);
       
        block._return(JExpr._null());
    }
   
    public Map<QName, ElementParserBuilderImpl> getXsiTypes() {
        return xsiTypes;
    }

    /**
     * Write out a loop which will read in a sequence of elements.
     *
     */
    private void writeMainLoop() {
        JBlock b = method.body();
       
        // Add XSI checks
        if (!valueType && depth > 1) {
            writeXsiChecks(b);
        }
       
        // Add the user constructed codeblock and continue from there
        b.add(codeBlock);
        b = codeBlock;
       
        if (attributes.size() > 0) {
            writeAttributeReader(b);
        }
       
        JVar depthVar = b.decl(model._ref(int.class), "depth", xsrVar.invoke("getDepth"));
        JVar targetDepthVar;
        JVar event;
        if (depth == 1) {
            targetDepthVar = b.decl(model._ref(int.class), "targetDepth", JExpr.lit(depth));
            event = b.decl(model._ref(int.class), "event", xsrVar.invoke("getEventType"));
        } else {
            targetDepthVar = b.decl(model._ref(int.class), "targetDepth", depthVar.plus(JExpr.lit(1)));
            event = b.decl(model._ref(int.class), "event", xsrVar.invoke("nextTagIgnoreAll"));
        }
       
       
       
        b.assign(depthVar, xsrVar.invoke("getDepth"));
       
//        JClass sysType = (JClass) model._ref(System.class);
//        if (depth != 1)
//            b.add(sysType.staticRef("out").invoke("println").arg(JExpr.lit("TD ").plus(targetDepthVar)
//                     .plus(JExpr.lit(" Depth: ")).plus(depthVar)
//                     .plus(JExpr.lit(" Name: " + name).plus(JExpr.lit(" Current: "))
//                           .plus(xsrVar.invoke("getName")))));

        JBlock loop = b._while(depthVar.gte(targetDepthVar.minus(JExpr.lit(1)))).body();
       
        b = loop._if(event.eq(JExpr.lit(XMLStreamConstants.START_ELEMENT)))._then();

       
        JConditional ifDepth = b._if(depthVar.eq(targetDepthVar));
       
        writeElementReader(elements, ifDepth._then(), false);
       
        if (allowUnknown) {
            writeElementReader(buildContext.getGlobalElements(), ifDepth._else(), true);
        }
       
        JConditional ifHasNext = loop._if(xsrVar.invoke("hasNext"));
        ifHasNext._then().assign(event, xsrVar.invoke("next"));
        ifHasNext._then().assign(depthVar, xsrVar.invoke("getDepth"));
        ifHasNext._else()._break();
    }

    /**
     * Write out a loop which will read in a sequence of elements.
     *
     */
    private void writeAttributeReader(JBlock b) {
        JForLoop loop = b._for();
        JVar var = loop.init(model._ref(int.class), "i", JExpr.lit(0));
        loop.test(var.lt(xsrVar.invoke("getAttributeCount")));
        loop.update(var.assignPlus(JExpr.lit(1)));
       
        b = loop.body();
        JVar attName = b.decl(model._ref(String.class), "attName", xsrVar.invoke("getAttributeLocalName").arg(var));
        JVar attNs = b.decl(model._ref(String.class), "attNs", xsrVar.invoke("getAttributeNamespace").arg(var));
        JVar attValue = b.decl(model._ref(String.class), "attValue", xsrVar.invoke("getAttributeValue").arg(var));
       
        JConditional cond = null;
       
        for (Map.Entry<QName, AttributeParserBuilderImpl> e : attributes.entrySet()) {
            QName name = e.getKey();
            AttributeParserBuilderImpl builder = e.getValue();
           
            JExpression localInv = attName.eq(JExpr.lit(name.getLocalPart()));
            String ns = name.getNamespaceURI();
            JExpression nsInv = JExpr.lit(name.getNamespaceURI()).eq(attNs);
           
            if (ns.equals("")) {
                nsInv = nsInv.cor(attNs.eq(JExpr._null()));
            }
           
            JExpression qnameCompare = localInv.cand(nsInv);

            if (cond == null) {
                cond = b._if(qnameCompare);
            } else {
                cond = cond._else()._if(qnameCompare);
            }
           
            JMethod nextMethod = builder.getMethod();
           
            JInvocation invocation = JExpr.invoke(nextMethod).arg(xsrVar).arg(rtContextVar);
            for (JVar v : builder.variables) {
                invocation.arg(v);
            }
           
            nextMethod.param(model._ref(String.class), "_attValue");
            invocation.arg(attValue);
           
            nextMethod.body().add(builder.codeBlock);
           
            if (root && builder.returnType != null) {
                cond._then()._return(invocation);
            } else {
                cond._then().add(invocation);
            }
           
            // TODO: throw exception if unknown elements are encountered and allowUnknown == false
            builder.write();
        }
    }

    private void writeXsiChecks(JBlock b) {
        if(getXsiTypes().isEmpty())
            return; // no @xsi:type to look for.

        JVar xsiType = b.decl(model._ref(QName.class), "xsiType", xsrVar.invoke("getXsiType"));
        JConditional cond = b._if(xsiType.ne(JExpr._null()));
       
        writeXsiChecks(cond._then(), xsiType);
    }

    private void writeXsiChecks(JBlock b, JVar xsiType) {
        JConditional xsiCond = null;
       
        for (Map.Entry<QName, ElementParserBuilderImpl> e : getXsiTypes().entrySet()) {
            QName name = e.getKey();
           
            ElementParserBuilderImpl builder = e.getValue();
           
            JExpression localInv = xsiType.invoke("getLocalPart").eq(JExpr.lit(name.getLocalPart()));
            String ns = name.getNamespaceURI();
            JExpression nsInv = JExpr.lit(name.getNamespaceURI()).eq(xsiType.invoke("getNamespaceURI"));
            if (ns.equals("")) {
                nsInv = nsInv.cor(xsiType.invoke("getNamespaceURI").eq(JExpr._null()));
            }
           
            JExpression qnameCompare = localInv.cand(nsInv);

            if (xsiCond == null) {
                xsiCond = b._if(qnameCompare);
            } else {
                xsiCond = xsiCond._else()._if(qnameCompare);
            }
           
            JBlock block = xsiCond._then();
           
            writeElementReader(builder, block, false);
           
            if (builder.returnType == null) {
                block._return();
            }
        }
    }

   
    private void writeElementReader(Map<QName, ElementParserBuilderImpl> els, JBlock b, boolean global) {
        JConditional cond = null;
       
        if (depth == 1 && !global && checkXsiTypes) {
            writeXsiChecks(b);
        }
       
        for (Map.Entry<QName, ElementParserBuilderImpl> e : els.entrySet()) {
            QName name = e.getKey();
            ElementParserBuilderImpl builder = e.getValue();
           
            JExpression localInv = xsrVar.invoke("getLocalName").eq(JExpr.lit(name.getLocalPart()));
            String ns = name.getNamespaceURI();
            JExpression nsInv = JExpr.lit(name.getNamespaceURI()).eq(xsrVar.invoke("getNamespaceURI"));
            if (ns.equals("")) {
                nsInv = nsInv.cor(xsrVar.invoke("getNamespaceURI").eq(JExpr._null()));
            }
           
            JExpression qnameCompare = localInv.cand(nsInv);

            if (cond == null) {
                cond = b._if(qnameCompare);
            } else {
                cond = cond._else()._if(qnameCompare);
            }
           
            JBlock block = cond._then();
           
            writeElementReader(builder, block, global);
        }
       
        for (Map.Entry<QName, ElementCall> e : elementCalls.entrySet()) {
            QName name = e.getKey();
            ElementCall call = e.getValue();
            ElementParserBuilderImpl builder = call.getElement();
           
            JExpression localInv = xsrVar.invoke("getLocalName").eq(JExpr.lit(name.getLocalPart()));
            String ns = name.getNamespaceURI();
            JExpression nsInv = JExpr.lit(name.getNamespaceURI()).eq(xsrVar.invoke("getNamespaceURI"));
            if (ns.equals("")) {
                nsInv = nsInv.cor(xsrVar.invoke("getNamespaceURI").eq(JExpr._null()));
            }
           
            JExpression qnameCompare = localInv.cand(nsInv);

            if (cond == null) {
                cond = b._if(qnameCompare);
            } else {
                cond = cond._else()._if(qnameCompare);
            }
           
            JBlock block = cond._then();
           
            JMethod nextMethod = builder.getMethod();
            JInvocation invocation = JExpr.invoke(nextMethod).arg(xsrVar).arg(rtContextVar);
            for (JExpression var : call.getVars()) {
                invocation.arg(var);
            }
            block.add(invocation);
            if (builder != this)
                builder.write();
        }
       
        if (anyElement != null) {
            JBlock anyBlock = b;
            if (cond != null) {
                anyBlock = cond._else().block();
            }
           
            writeElementReader(anyElement, anyBlock, false);
        }
    }

    private void writeElementReader(ElementParserBuilderImpl builder,
                                    JBlock block,
                                    boolean global) {
        JMethod nextMethod = builder.getMethod();
       
        JInvocation invocation = JExpr.invoke(nextMethod).arg(xsrVar).arg(rtContextVar);

        if (!global) {
            for (JVar v : builder.variables) {
                invocation.arg(v);
            }
        }
       
        if (root && builder.returnType != null) {
            block._return(invocation);
        } else {
            block.add(invocation);
        }
       
        // TODO: throw exception if unknown elements are encountered and allowUnknown == false
        if (builder != this)
            builder.write();
    }

    private void setReturnType(JType type) {
        try {
            Field field = JMethod.class.getDeclaredField("type");
            boolean accessibility = field.isAccessible();
            field.setAccessible(true);
            field.set(method, type);
            field.setAccessible(accessibility);
        } catch (Exception e) {
            throw new BuildException(e);
        }
    }

    public QName getName() {
        return name;
    }

    public boolean isRequired() {
        return required;
    }

    public void setRequired(boolean required) {
        this.required = required;
    }   
   
    static class ElementCall {
        private JExpression[] vars;
        private ElementParserBuilderImpl element;
       
        public ElementCall(ElementParserBuilderImpl element, JExpression[] vars) {
            super();
            this.vars = vars;
            this.element = element;
        }
        public ElementParserBuilderImpl getElement() {
            return element;
        }
        public JExpression[] getVars() {
            return vars;
        }
       
    }
}
TOP

Related Classes of com.envoisolutions.sxc.builder.impl.ElementParserBuilderImpl$ElementCall

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.