Package org.apache.axis2.schema

Source Code of org.apache.axis2.schema.SchemaCompiler

package org.apache.axis2.schema;

import org.apache.axis2.om.OMElement;
import org.apache.axis2.schema.i18n.SchemaCompilerMessages;
import org.apache.axis2.schema.util.SchemaPropertyLoader;
import org.apache.axis2.schema.writer.BeanWriter;
import org.apache.ws.commons.schema.XmlSchema;
import org.apache.ws.commons.schema.XmlSchemaAll;
import org.apache.ws.commons.schema.XmlSchemaAny;
import org.apache.ws.commons.schema.XmlSchemaAnyAttribute;
import org.apache.ws.commons.schema.XmlSchemaAttribute;
import org.apache.ws.commons.schema.XmlSchemaChoice;
import org.apache.ws.commons.schema.XmlSchemaComplexContent;
import org.apache.ws.commons.schema.XmlSchemaComplexContentExtension;
import org.apache.ws.commons.schema.XmlSchemaComplexContentRestriction;
import org.apache.ws.commons.schema.XmlSchemaComplexType;
import org.apache.ws.commons.schema.XmlSchemaContent;
import org.apache.ws.commons.schema.XmlSchemaElement;
import org.apache.ws.commons.schema.XmlSchemaImport;
import org.apache.ws.commons.schema.XmlSchemaObject;
import org.apache.ws.commons.schema.XmlSchemaObjectCollection;
import org.apache.ws.commons.schema.XmlSchemaObjectTable;
import org.apache.ws.commons.schema.XmlSchemaParticle;
import org.apache.ws.commons.schema.XmlSchemaSequence;
import org.apache.ws.commons.schema.XmlSchemaSimpleType;
import org.apache.ws.commons.schema.XmlSchemaSimpleTypeContent;
import org.apache.ws.commons.schema.XmlSchemaSimpleTypeRestriction;
import org.apache.ws.commons.schema.XmlSchemaType;

import javax.xml.namespace.QName;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
/*
* Copyright 2004,2005 The Apache Software Foundation.
*
* 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.
*/

/**
* Schema compiler for ADB. Based on WS-Commons schema object model.
*/
public class SchemaCompiler {


    private CompilerOptions options;
    private HashMap processedTypemap;

    //The processedElementMap and the processedElementList have a subtle difference
    //The writing to the processedElementList happens when an outer element is processed.
    private HashMap processedElementMap;
    private HashMap processedAnonymousComplexTypesMap;
    private HashMap simpleTypesMap;
    private HashMap changedTypeMap;
    private ArrayList processedElementList;


    private BeanWriter writer = null;

    private Map baseSchemaTypeMap = null;

    private static final String ANY_ELEMENT_FIELD_NAME = "extraElements";
    private static final String EXTRA_ATTRIBUTE_FIELD_NAME = "extraAttributes";


    /**
     * @return the processes element map
     * includes the Qname of the element as the key and a
     * String representing the fully qualified class name
     */
    public HashMap getProcessedElementMap() {
        return processedElementMap;
    }

    /**
     * @return a map of Qname vs models. A model can be anything,
     * ranging from a DOM document to a stream. This is taken from the
     * writer and the schema compiler has no control over it
     */
    public Map getProcessedModelMap() {
        return writer.getModelMap();
    }

    /**
     * Constructor - Accepts a options bean
     *
     * @param options
     */
    public SchemaCompiler(CompilerOptions options) throws SchemaCompilationException {

        if (options == null) {
            //create an empty options object
            this.options = new CompilerOptions();
        } else {
            this.options = options;
        }

        this.processedTypemap = new HashMap();
        this.processedElementMap = new HashMap();
        this.simpleTypesMap = new HashMap();
        this.processedElementList = new ArrayList();
        this.processedAnonymousComplexTypesMap = new HashMap();
        this.changedTypeMap = new HashMap();

        //load the writer a nd initiliaze the base type
        this.writer = SchemaPropertyLoader.getBeanWriterInstance();
        this.writer.init(this.options);

        //load the base types
        baseSchemaTypeMap = SchemaPropertyLoader.getTypeMapperInstance().getTypeMap();


    }

    /**
     * Compile a list of schemas
     * This actually calls the compile (XmlSchema s) method repeatedly
     *
     * @param schemalist
     * @throws SchemaCompilationException
     * @see #compile(org.apache.ws.commons.schema.XmlSchema)
     */
    public void compile(List schemalist) throws SchemaCompilationException {
        XmlSchema schema;
        try {
            for (int i = 0; i < schemalist.size(); i++) {
                schema = (XmlSchema) schemalist.get(i);
                compile(schema);
            }
        } catch (SchemaCompilationException e) {
            throw e;
        } catch (Exception e) {
            throw new SchemaCompilationException(e);
        }
    }

    /**
     * Compile (rather codegen) a single schema element
     * @param schema
     * @throws SchemaCompilationException
     */
    public void compile(XmlSchema schema) throws SchemaCompilationException {

        //First look for the schemas that are imported and process them
        //Note that these are processed recursively!

        XmlSchemaObjectCollection includes = schema.getIncludes();
        if (includes != null) {
            Iterator tempIterator = includes.getIterator();
            while (tempIterator.hasNext()) {
                Object o = tempIterator.next();
                if (o instanceof XmlSchemaImport) {
                    XmlSchema schema1 = ((XmlSchemaImport) o).getSchema();
                    if (schema1 != null) compile(schema1);
                }
            }
        }
        //select all the elements. We generate the code for types
        //only if the elements refer them!!!
        XmlSchemaObjectTable elements = schema.getElements();
        Iterator xmlSchemaElement1Iterator = elements.getValues();
        while (xmlSchemaElement1Iterator.hasNext()) {
            //this is the set of outer elements so we need to generate classes
            //The outermost elements do not contain occurence counts (!) so we do not need
            //to check for arraytypes
            processElement((XmlSchemaElement) xmlSchemaElement1Iterator.next(), true);
        }


        Iterator xmlSchemaElement2Iterator = elements.getValues();

        // re-iterate through the elements and write them one by one
        // if the mode is unwrap this process will not really write the
        // classes but will accumilate the models for a final single shot
        // write
        while (xmlSchemaElement2Iterator.hasNext()) {
            //this is the set of outer elements so we need to generate classes
            writeElement((XmlSchemaElement) xmlSchemaElement2Iterator.next());
        }

        if (options.isWrapClasses()) {
            writer.writeBatch();
        }
    }

    /**
     * @return the property map of the schemacompiler.
     * In this case it would be the property map loaded from
     * the configuration file
     */
    public Properties getCompilerProperties(){
        return SchemaPropertyLoader.getPropertyMap() ;
    }


    /**
     * Writes the element
     *
     * @param xsElt
     * @throws SchemaCompilationException
     */
    private void writeElement(XmlSchemaElement xsElt) throws SchemaCompilationException {

        if (this.processedElementMap.containsKey(xsElt.getQName())) {
            return;
        }

        XmlSchemaType schemaType = xsElt.getSchemaType();

        if (schemaType != null) {
            BeanWriterMetaInfoHolder metainf = new BeanWriterMetaInfoHolder();
            if (schemaType.getName() != null) {
                //this is a named type
                QName qName = schemaType.getQName();
                //find the class name
                String className = findClassName(qName, isArray(xsElt));
                //this means the schema type actually returns a different QName
                if (changedTypeMap.containsKey(qName)) {
                    metainf.registerMapping(xsElt.getQName(),
                            (QName) changedTypeMap.get(qName),
                            className);
                } else {
                    metainf.registerMapping(xsElt.getQName(),
                            qName,
                            className);
                }


            } else {
                //we are going to special case the anonymous complex type. Our algorithm for dealing
                //with it is to generate a single object that has the complex content inside. Really the
                //intent of the user when he declares the complexType anonymously is to use it privately
                //First copy the schema types content into the metainf holder
                metainf = (BeanWriterMetaInfoHolder) this.processedAnonymousComplexTypesMap.get(xsElt);
                metainf.setAnonymous(true);
            }


            String writtenClassName = writer.write(xsElt, processedTypemap, metainf);
            processedElementMap.put(xsElt.getQName(), writtenClassName);
        }
    }


    /**
     * Process the element
     *
     * @param xsElt
     * @param isOuter - We need to know this since the treatment of outer elements is different that
     *                inner elements
     * @throws SchemaCompilationException
     */
    private void processElement(XmlSchemaElement xsElt, boolean isOuter) throws SchemaCompilationException {
        processElement(xsElt, isOuter, false);
    }

    /**
     * Process and Element
     *
     * @param xsElt
     * @param isOuter-We   need to know this since the treatment of outer elements is different that
     *                     inner elements
     * @param isArray-flag saying whether the elements represents an array
     * @throws SchemaCompilationException
     */
    private void processElement(XmlSchemaElement xsElt, boolean isOuter, boolean isArray) throws SchemaCompilationException {
        //The processing element logic seems to be quite simple. Look at the relevant schema type
        //for each and every element and process that accordingly.
        //this means that any unused type definitions would not be generated!
        if (isOuter && processedElementList.contains(xsElt.getQName())) {
            return;
        }

        XmlSchemaType schemaType = xsElt.getSchemaType();
        if (schemaType != null) {
            processSchema(xsElt, schemaType);
            //at this time it is not wise to directly write the class for the element
            //so we push the complete element to an arraylist and let the process
            //pass through. We'll be iterating through the elements writing them
            //later
        }

        //There can be instances where the SchemaType is null but the schemaTypeName is not
        //this specifically happens with xsd:anyType.
        if (!isOuter) {
            String className = findClassName(xsElt.getSchemaTypeName(), isArray(xsElt));
            this.processedElementMap.put(xsElt.getQName(), className);
        }
        this.processedElementList.add(xsElt.getQName());

    }

    /**
     * Finds a class name from the given Qname
     *
     * @param schemaType
     * @return
     */
    private String findClassName(QName qName, boolean isArray) {

        //find the class name
        String className;
        if (processedTypemap.containsKey(qName)) {
            className = (String) processedTypemap.get(qName);
        } else if (simpleTypesMap.containsKey(qName)) {
            className = (String) simpleTypesMap.get(qName);
        } else if (baseSchemaTypeMap.containsKey(qName)) {
            className = (String) baseSchemaTypeMap.get(qName);
        } else {
            // We seem to have failed in finding a class name for the
            //contained schema type. We better set the default then
            //however it's better if the default can be set through the
            //property file
            className = OMElement.class.getName();
        }
        if (isArray) {
            //append the square braces that say this is an array
            //hope this works for all cases!!!!!!!
            //todo this however is a thing that needs to be
            //todo fixed to get complete language support
            className = className + "[]";
        }
        return className;
    }

    /**
     * Process a schema element
     *
     * @param schemaType
     * @throws SchemaCompilationException
     */
    private void processSchema(XmlSchemaElement xsElt, XmlSchemaType schemaType) throws SchemaCompilationException {
        if (schemaType instanceof XmlSchemaComplexType) {
            //write classes for complex types
            XmlSchemaComplexType complexType = (XmlSchemaComplexType) schemaType;
            if (complexType.getName() != null) {
                processNamedComplexSchemaType(complexType);
            } else {
                processAnonymousComplexSchemaType(xsElt, complexType);
            }
        } else if (schemaType instanceof XmlSchemaSimpleType) {
            //process simple type
            processSimpleSchemaType(xsElt, (XmlSchemaSimpleType) schemaType);
        }
    }


    /**
     * @param complexType
     * @throws SchemaCompilationException
     */
    private void processAnonymousComplexSchemaType(XmlSchemaElement elt, XmlSchemaComplexType complexType) throws SchemaCompilationException {
        XmlSchemaParticle particle = complexType.getParticle();
        BeanWriterMetaInfoHolder metaInfHolder = new BeanWriterMetaInfoHolder();
        if (particle != null) {
            //Process the particle
            processParticle(particle, metaInfHolder);
        }

        //process attributes - first look for the explicit attributes
        XmlSchemaObjectCollection attribs = complexType.getAttributes();
        Iterator attribIterator = attribs.getIterator();
        while (attribIterator.hasNext()) {
            Object o = attribIterator.next();
            if (o instanceof XmlSchemaAttribute) {
                processAttribute((XmlSchemaAttribute) o, metaInfHolder);

            }
        }

        //process any attribute
        //somehow the xml schema parser does not seem to pickup the any attribute!!
        XmlSchemaAnyAttribute anyAtt = complexType.getAnyAttribute();
        if (anyAtt != null) {
            processAnyAttribute(metaInfHolder);
        }

        //since this is a special case (an unnamed complex type) we'll put the already processed
        //metainf holder in a special map to be used later
        this.processedAnonymousComplexTypesMap.put(elt, metaInfHolder);
    }

    /**
     * handle the complex types which are named
     *
     * @param complexType
     */
    private void processNamedComplexSchemaType(XmlSchemaComplexType complexType) throws SchemaCompilationException {

        if (processedTypemap.containsKey(complexType.getQName())
                || baseSchemaTypeMap.containsKey(complexType.getQName())) {
            return;
        }

        XmlSchemaParticle particle = complexType.getParticle();
        BeanWriterMetaInfoHolder metaInfHolder = new BeanWriterMetaInfoHolder();
        if (particle != null) {
            //Process the particle
            processParticle(particle, metaInfHolder);
        }

        //process attributes - first look for the explicit attributes
        XmlSchemaObjectCollection attribs = complexType.getAttributes();
        Iterator attribIterator = attribs.getIterator();
        while (attribIterator.hasNext()) {
            Object o = attribIterator.next();
            if (o instanceof XmlSchemaAttribute) {
                processAttribute((XmlSchemaAttribute) o, metaInfHolder);

            }
        }

        //process any attribute
        //somehow the xml schema parser does not seem to pickup the any attribute!!
        XmlSchemaAnyAttribute anyAtt = complexType.getAnyAttribute();
        if (anyAtt != null) {
            processAnyAttribute(metaInfHolder);
        }


        if (complexType.getContentModel()!=null){
            //for the time being we cannot deal with these content. so throw an exception
            throw new RuntimeException(SchemaCompilerMessages.getMessage("schema.unsupportedcontenterror"));
        }

        // Process the other types - Say the complex content, extensions and so on

        //write the class. This type mapping would have been populated right now
        //Note - We always write classes for complex types
        String fullyQualifiedClassName = writer.write(complexType, processedTypemap, metaInfHolder);
        //populate the type mapping with the elements
        processedTypemap.put(complexType.getQName(), fullyQualifiedClassName);


    }

    /**
     * Handle any attribute
     *
     * @param metainf
     */
    private void processAnyAttribute(BeanWriterMetaInfoHolder metainf) {
        //The best thing we can do here is to add a set of OMAttributes
        metainf.registerMapping(new QName(EXTRA_ATTRIBUTE_FIELD_NAME),
                null,
                OMElement[].class.getName(),
                SchemaConstants.ANY_ATTRIBUTE_TYPE);

    }

    /**
     * Process the attribute
     *
     * @param att
     * @param metainf
     */
    public void processAttribute(XmlSchemaAttribute att, BeanWriterMetaInfoHolder metainf) {
        //for now we assume (!!!) that attributes refer to standard types only
        QName schemaTypeName = att.getSchemaTypeName();
        if (baseSchemaTypeMap.containsKey(schemaTypeName)) {
            metainf.registerMapping(att.getQName(),
                    schemaTypeName,
                    baseSchemaTypeMap.get(schemaTypeName).toString(), SchemaConstants.ATTRIBUTE_TYPE);
        } else {
            //this attribute refers to a custom type, probably one of the extended simple types.
            //handle it here
        }
    }

    /**
     * Process a particle- A particle may be a sequence,all or a choice
     *
     * @param particle
     * @param metainfHolder
     * @throws SchemaCompilationException
     */
    private void processParticle(XmlSchemaParticle particle, //particle being processed
                                 BeanWriterMetaInfoHolder metainfHolder // metainf holder
    ) throws SchemaCompilationException {
        if (particle instanceof XmlSchemaSequence) {
            XmlSchemaObjectCollection items = ((XmlSchemaSequence) particle).getItems();
            process(items, metainfHolder, true);
        } else if (particle instanceof XmlSchemaAll) {
            XmlSchemaObjectCollection items = ((XmlSchemaAll) particle).getItems();
            process(items, metainfHolder, false);
        } else if (particle instanceof XmlSchemaChoice) {
            //put the code here to handle the choice!
        }
    }

    /**
     * @param items
     * @param metainfHolder
     * @param order
     * @throws SchemaCompilationException
     */
    private void process(XmlSchemaObjectCollection items,
                         BeanWriterMetaInfoHolder metainfHolder,
                         boolean order) throws SchemaCompilationException {
        int count = items.getCount();
        Map processedElements = new HashMap();
        Map elementOrderMap = new HashMap();

        for (int i = 0; i < count; i++) {
            XmlSchemaObject item = items.getItem(i);

            if (item instanceof XmlSchemaElement) {
                //recursively process the element
                XmlSchemaElement xsElt = (XmlSchemaElement) item;

                boolean isArray = isArray(xsElt);
                processElement(xsElt, false, isArray); //we know for sure this is not an outer type
                processedElements.put(xsElt, (isArray) ? Boolean.TRUE : Boolean.FALSE);
                if (order) {
                    //we need to keep the order of the elements. So push the elements to another
                    //hashmap with the order number
                    elementOrderMap.put(xsElt, new Integer(i));
                }
            } else if (item instanceof XmlSchemaComplexContent) {
                // process the extension
                XmlSchemaContent content = ((XmlSchemaComplexContent) item).getContent();
                if (content instanceof XmlSchemaComplexContentExtension) {
                    // handle the complex extension
                } else if (content instanceof XmlSchemaComplexContentRestriction) {
                    //handle complex restriction
                }

                //handle xsd:any ! We place an OMElement in the generated class
            } else if (item instanceof XmlSchemaAny) {
                processAny((XmlSchemaAny) item, metainfHolder);
            }


        }

        // loop through the processed items and add them to the matainf object
        Iterator processedElementsIterator = processedElements.keySet().iterator();
        while (processedElementsIterator.hasNext()) {
            XmlSchemaElement elt = (XmlSchemaElement) processedElementsIterator.next();
            QName qName = elt.getQName();
            String clazzName = (String) processedElementMap.get(qName);
            metainfHolder.registerMapping(qName,
                    elt.getSchemaTypeName()
                    , clazzName,
                    ((Boolean) processedElements.get(elt)).booleanValue() ?
                            SchemaConstants.ANY_ARRAY_TYPE :
                            SchemaConstants.ELEMENT_TYPE);

            //register the occurence counts
            metainfHolder.addMaxOccurs(qName, elt.getMaxOccurs());
            metainfHolder.addMinOccurs(qName, elt.getMinOccurs());
            //we need the order to be preserved. So record the order also
            if (order) {
                //record the order in the metainf holder
                Integer integer = (Integer) elementOrderMap.get(elt);
                metainfHolder.registerQNameIndex(qName,
                        integer.intValue());
            }

        }

        //set the ordered flag in the metainf holder
        metainfHolder.setOrdered(order);
    }

    /**
     * process the 'any'
     *
     * @param any
     * @param metainf
     */
    private void processAny(XmlSchemaAny any, BeanWriterMetaInfoHolder metainf) {
        //handle the minoccurs/maxoccurs here.
        //However since the any element does not have a name
        //we need to put a name here
        metainf.registerMapping(new QName(ANY_ELEMENT_FIELD_NAME),
                null,
                OMElement.class.getName(),
                SchemaConstants.ANY_TYPE);

    }

    /**
     * Handle the simple content
     *
     * @param simpleType
     */
    private void processSimpleSchemaType(XmlSchemaElement xsElt, XmlSchemaSimpleType simpleType) {
        // handle the restriction
        XmlSchemaSimpleTypeContent content = simpleType.getContent();
        if (content != null) {
            if (content instanceof XmlSchemaSimpleTypeRestriction) {
                XmlSchemaSimpleTypeRestriction restriction = (XmlSchemaSimpleTypeRestriction) content;
                QName baseTypeName = restriction.getBaseTypeName();
                //check whether the base type is one of the base schema types
                if (baseSchemaTypeMap.containsKey(baseTypeName)) {
                    //this is a basic xsd datatype. Populate the map and populate
                    //the mappings map
                    String className = (String) baseSchemaTypeMap.get(baseTypeName);
                    this.simpleTypesMap.put(simpleType.getQName(), className);
                    //set the old schema type QName and the new schema type QName
                    this.changedTypeMap.put(simpleType.getQName(), baseTypeName);
                } else {
                    //recurse
                    if (restriction.getBaseType() != null) {
                        processSimpleSchemaType(xsElt, restriction.getBaseType());
                    }
                    //processSimpleSchemaType(xsElt, new XmlSchemaSimpleType());
                }
            }
            //We still don't handle UNIONS of  simple types
        }


    }


    /**
     * Find whether a given particle is an array. The logic for deciding
     * whether a given particle is an array is depending on their minOccurs
     * and maxOccurs counts. If Maxoccurs is greater than one (1) then the
     * content is an array.
     * Also no higher level element will have the maxOccurs greater than one
     *
     * @param particle
     * @return
     * @throws SchemaCompilationException
     */
    private boolean isArray(XmlSchemaParticle particle) throws SchemaCompilationException {
        long minOccurs = particle.getMinOccurs();
        long maxOccurs = particle.getMaxOccurs();

        if (maxOccurs < minOccurs) {
            throw new SchemaCompilationException();
        } else {
            return (maxOccurs > 1);
        }

    }



}
TOP

Related Classes of org.apache.axis2.schema.SchemaCompiler

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.