Package org.apache.tuscany.tools.java2wsdl.generate

Source Code of org.apache.tuscany.tools.java2wsdl.generate.SchemaBuilder

/*
* 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.tools.java2wsdl.generate;

import java.io.StringReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import javax.xml.namespace.QName;

import org.apache.axis2.description.java2wsdl.Java2WSDLUtils;
import org.apache.ws.commons.schema.XmlSchema;
import org.apache.ws.commons.schema.XmlSchemaCollection;
import org.apache.ws.commons.schema.XmlSchemaComplexType;
import org.apache.ws.commons.schema.XmlSchemaElement;
import org.apache.ws.commons.schema.XmlSchemaForm;
import org.apache.ws.commons.schema.XmlSchemaGroupBase;
import org.apache.ws.commons.schema.XmlSchemaImport;
import org.apache.ws.commons.schema.XmlSchemaInclude;
import org.apache.ws.commons.schema.XmlSchemaSequence;
import org.apache.ws.commons.schema.XmlSchemaType;
import org.apache.ws.commons.schema.utils.NamespaceMap;
import org.codehaus.jam.JClass;
import org.codehaus.jam.JProperty;

import commonj.sdo.DataObject;
import commonj.sdo.Type;
import commonj.sdo.helper.HelperContext;
import commonj.sdo.helper.XSDHelper;

public class SchemaBuilder implements TuscanyJava2WSDLConstants {
    public static final String NAME_SPACE_PREFIX = "stn_";

    private static int prefixCount = 1;

    public static final String MIXED = "mixed";

    public static final String GROUP = "group";

    protected String attrFormDefault = null;

    protected String elementFormDefault = null;

    protected XmlSchemaCollection xmlSchemaCollection = new XmlSchemaCollection();

    private Hashtable schemaMap = new Hashtable();

    protected Hashtable targetNamespacePrefixMap = new Hashtable();

    protected TuscanyTypeTable typeTable = new TuscanyTypeTable();

    protected Map schemaLocationMap = null;

    private ClassLoader classLoader = null;

    private ArrayList factoryClassNames = null;

    private HelperContext helperContext = null;
   
    private XSDHelper xsdHelper = null;

    private Set<String> registeredSDOFactories = new HashSet<String>();
   
    boolean alreadyPrintedDefaultSDOFactoryFound = false;
   
    boolean alreadyPrintedDefaultSDOFactoryNotFound = false;

    protected SchemaBuilder(XmlSchemaCollection schemaCollection,
                            Hashtable schemaMap,
                            Hashtable nsPrefixMap,
                            TuscanyTypeTable typeTable,
                            String attrFormDef,
                            String eleFormDef,
                            Map schemaLocMap,
                            ClassLoader classLoader,
                            ArrayList factoryClassNames) {
        this.schemaMap = schemaMap;
        this.xmlSchemaCollection = schemaCollection;
        this.targetNamespacePrefixMap = nsPrefixMap;
        this.typeTable = typeTable;
        this.schemaLocationMap = schemaLocMap;
        this.classLoader = classLoader;
        this.attrFormDefault = attrFormDef;
        this.elementFormDefault = eleFormDef;
        this.factoryClassNames = factoryClassNames;
 
        // Register the types in the generated SDO factories
        this.helperContext = org.apache.tuscany.sdo.api.SDOUtil.createHelperContext();
        this.xsdHelper = helperContext.getXSDHelper();

        Class factoryClass = null;
        for (Object factoryClassName : this.factoryClassNames) {
            try {
                factoryClass = Class.forName((String)factoryClassName, true, classLoader);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
                System.out.println("");
                System.out.println("Generated SDO Factory class with name: " + factoryClassName + " could not be loaded.  Exiting.");
                throw new IllegalArgumentException(e);
            }
            registerSDOFactory(factoryClass);
        }
   }

    private boolean isSDO(JClass javaType) throws Exception {

        Class sdoClass = Class.forName(javaType.getQualifiedName(), true, classLoader);

        return DataObject.class.isAssignableFrom(sdoClass);
    }

    private void buildComplexTypeContents_JavaType(JClass javaType,
                                                   XmlSchemaComplexType complexType,
                                                   XmlSchema xmlSchema) throws Exception {
        JProperty[] properties = javaType.getDeclaredProperties();

        for (int i = 0; i < properties.length; i++) {
            JProperty property = properties[i];
            String propertyName = property.getType().getQualifiedName();
            boolean isArryType = property.getType().isArrayType();
            if (isArryType) {
                propertyName = property.getType().getArrayComponentType().getQualifiedName();
            }

            if (typeTable.isSimpleType(propertyName)) {
                XmlSchemaElement elt1 = new XmlSchemaElement();
                elt1.setName(getCorrectName(property.getSimpleName()));
                elt1.setSchemaTypeName(typeTable.getSimpleSchemaTypeName(propertyName));
                ((XmlSchemaGroupBase) complexType.getParticle()).getItems().add(elt1);
                if (isArryType) {
                    elt1.setMaxOccurs(Long.MAX_VALUE);
                    elt1.setMinOccurs(0);
                }
            } else {
                QName schemaTypeName = null;
                if (isArryType) {
                    schemaTypeName = generateSchema(property.getType().getArrayComponentType());
                } else {
                    schemaTypeName = generateSchema(property.getType());
                }

                XmlSchemaElement elt1 = new XmlSchemaElement();
                elt1.setName(getCorrectName(property.getSimpleName()));
                elt1.setSchemaTypeName(schemaTypeName);
                ((XmlSchemaGroupBase) complexType.getParticle()).getItems().add(elt1);

                if (isArryType) {
                    elt1.setMaxOccurs(Long.MAX_VALUE);
                    elt1.setMinOccurs(0);
                }

                addImports(xmlSchema,
                           schemaTypeName);
            }
        }
    }

    protected QName buildSchema_JavaType(JClass javaType) throws Exception {
        QName schemaTypeName = typeTable.getComplexSchemaTypeName(javaType, this.classLoader);
        if (schemaTypeName == null) {
            String simpleName = javaType.getSimpleName();

            String packageName = javaType.getContainingPackage().getQualifiedName();

            String targetNameSpace =
              Java2WSDLUtils.schemaNamespaceFromClassName(javaType.getQualifiedName(), this.classLoader)
                                                   .toString();

            XmlSchema xmlSchema = getXmlSchema(targetNameSpace);
            String targetNamespacePrefix = (String) targetNamespacePrefixMap.get(targetNameSpace);

            schemaTypeName = new QName(targetNameSpace, simpleName, targetNamespacePrefix);
            XmlSchemaComplexType complexType = new XmlSchemaComplexType(xmlSchema);
            complexType.setName(simpleName);

            XmlSchemaSequence sequence = new XmlSchemaSequence();
            complexType.setParticle(sequence);

            createGlobalElement(xmlSchema,
                                complexType,
                                schemaTypeName);
            xmlSchema.getItems().add(complexType);
            xmlSchema.getSchemaTypes().add(schemaTypeName,
                                           complexType);

            // adding this type to the table
            // typeTable.addComplexScheam(name, complexType.getQName());
            typeTable.addComplexSchemaType(targetNameSpace,
                                           simpleName,
                                           schemaTypeName);
            buildComplexTypeContents_JavaType(javaType,
                                              complexType,
                                              xmlSchema);
        }
        return schemaTypeName;
    }

    protected QName buildSchema_SDO(Type dataType) // throws Exception
    {
        QName schemaTypeName = typeTable.getComplexSchemaTypeName(dataType.getURI(),
                                                                  dataType.getName());

        if (schemaTypeName == null) {
            // We can't load the XSDs into an XSDHelper and match them against the static SDOs; they will
            // never match.  Instead let's take an all-or-nothing approach and say, if we've got this NS
            // in our map then we assume we have this Type as well in the corresponding XSD file. 
            //
            boolean inXSDForm = schemaLocationMap.get(dataType.getURI()) != null;

            if (inXSDForm) {
                // if schemalocations for xsd has been specified, include them

                // External XSDs will be handled in processing the schema TNS of the wrapper elements.
                // This is partly because SDO codegen needs some modification in this area
                // So we won't bother including the external XSDs here at all.
                //
                //  includeExtXSD(dataType);
            } else {
                List<Type> typeList = new Vector<Type>();
                typeList.add(dataType);

                // the xsdhelper returns a string that contains the schemas for this type
                String schemaDefns = xsdHelper.generate(typeList, schemaLocationMap);

                // extract the schema elements and store them in the schema map
                extractSchemas(schemaDefns);
            }
            // since the XSDHelper will not return the type name, create it and store it in typetable
            schemaTypeName = new QName(dataType.getURI(), dataType.getName(), generatePrefix());
            typeTable.addComplexSchemaType(dataType.getURI(),
                                           dataType.getName(),
                                           schemaTypeName);

        }
        return schemaTypeName;
    }

    /**
     * Identify the java type (pojo versus sdo) and build the schema accordingly
     *
     * @param javaType reference to the classs
     * @return
     * @throws Exception
     */
    public QName generateSchema(JClass javaType) throws Exception {
        if (isSDO(javaType)) {
            Type dataType = createDataObject(javaType).getType();
            return buildSchema_SDO(dataType);
        } else {
            return buildSchema_JavaType(javaType);
        }
    }

    private XmlSchema getXmlSchema(String targetNamespace) {
        XmlSchema xmlSchema;

        if ((xmlSchema = (XmlSchema) schemaMap.get(targetNamespace)) == null) {
            String targetNamespacePrefix = generatePrefix();

            xmlSchema = new XmlSchema(targetNamespace, xmlSchemaCollection);
            xmlSchema.setAttributeFormDefault(getAttrFormDefaultSetting());
            xmlSchema.setElementFormDefault(getElementFormDefaultSetting());

            targetNamespacePrefixMap.put(targetNamespace, targetNamespacePrefix);
            schemaMap.put(targetNamespace, xmlSchema);

            NamespaceMap prefixmap = new NamespaceMap();
            prefixmap.put(TuscanyTypeTable.XS_URI_PREFIX, TuscanyTypeTable.XML_SCHEMA_URI);
            prefixmap.put(targetNamespacePrefix, targetNamespace);
            xmlSchema.setNamespaceContext(prefixmap);
        }
        return xmlSchema;
    }

    /**
     * JAM convert first name of an attribute into UpperCase as an example if there is a instance variable called foo in a bean , then Jam give that
     * as Foo so this method is to correct that error
     *
     * @param wrongName
     * @return the right name, using english as the locale for case conversion
     */
    public static String getCorrectName(String wrongName) {
        if (wrongName.length() > 1) {
            return wrongName.substring(0, 1).toLowerCase(Locale.ENGLISH) + wrongName.substring(1, wrongName.length());
        } else {
            return wrongName.substring(0, 1).toLowerCase(Locale.ENGLISH);
        }
    }

    private String addImports(XmlSchema xmlSchema, QName schemaTypeName) {
        String prefix = null;
        String[] prefixes = xmlSchema.getNamespaceContext().getDeclaredPrefixes();
        for (int count = 0; count < prefixes.length; ++count) {
            if (schemaTypeName.getNamespaceURI().
                    equals(xmlSchema.getNamespaceContext().getNamespaceURI(prefixes[count])) ) {
                return prefixes[count];
            }
        }

        XmlSchemaImport importElement = new XmlSchemaImport();
        importElement.setNamespace(schemaTypeName.getNamespaceURI());
        xmlSchema.getItems().add(importElement);
        prefix = generatePrefix();
        //it is safe to cast like this since it was this class that instantiated the
        //NamespaceContext and assigned it to an instance of a NamespaceMap (see method getXmlSchema)
        ((NamespaceMap)xmlSchema.getNamespaceContext()).put(prefix,
                                                schemaTypeName.getNamespaceURI());

        return prefix;
    }

    private String formGlobalElementName(String typeName) {
        String firstChar = typeName.substring(0, 1);
        return typeName.replaceFirst(firstChar, firstChar.toLowerCase());
    }

    private void createGlobalElement(XmlSchema xmlSchema, XmlSchemaComplexType complexType, QName elementName) {
        XmlSchemaElement globalElement = new XmlSchemaElement();
        globalElement.setSchemaTypeName(complexType.getQName());
        globalElement.setName(formGlobalElementName(complexType.getName()));
        globalElement.setQName(elementName);

        xmlSchema.getItems().add(globalElement);
        xmlSchema.getElements().add(elementName, globalElement);
    }

    private DataObject createDataObject(JClass sdoClass) throws Exception {
        Class sdoType = Class.forName(sdoClass.getQualifiedName(), true, classLoader);

        //register the factory
        detectAndRegisterFactory(sdoType);
       
        //create data object
        Constructor constructor = sdoType.getDeclaredConstructor(new Class[0]);
        constructor.setAccessible(true);
        Object instance = constructor.newInstance(new Object[0]);
        return (DataObject) instance;
    }

    private String generatePrefix() {
        return NAME_SPACE_PREFIX + prefixCount++;
    }

    private void includeExtXSD(Type dataType) {
        // now we know there is a type for which the xsd must come from outside
        // create a schema for the namespace of this type and add an include in it for
        // the xsd that is defined externally
        XmlSchema xmlSchema = getXmlSchema(dataType.getURI());

        // ideally there could be more than one external schema defintions for a namespace
        // and hence schemalocations will be a list of locations
        // List schemaLocations = (List)schemaLocationMap.get(dataType.getURI());

        // since as per the specs the input to XSDHelper is a map of <String, String> allowing
        // only one schemalocation for a namespace. So for now this single location will be
        // picked up and put into a list
        List schemaLocations = new Vector();

        if (schemaLocationMap.get(dataType.getURI()) != null) {
            schemaLocations.add(schemaLocationMap.get(dataType.getURI()));
        }

        if (schemaLocations.size() <= 0) {
            schemaLocations.add(DEFAULT_SCHEMA_LOCATION);
        }

        Iterator includesIterator = xmlSchema.getIncludes().getIterator();
        Iterator schemaLocIterator = schemaLocations.iterator();
        String aSchemaLocation = null;
        boolean includeExists = false;
        // include all external schema locations
        while (schemaLocIterator.hasNext()) {
            aSchemaLocation = (String) schemaLocIterator.next();
            while (includesIterator.hasNext()) {
                if (!includeExists
                        && aSchemaLocation.equals(((XmlSchemaInclude) includesIterator.next()).getSchemaLocation())) {
                    includeExists = true;
                }
            }

            if (!includeExists) {
                XmlSchemaInclude includeElement = new XmlSchemaInclude();
                includeElement.setSchemaLocation(aSchemaLocation);
                xmlSchema.getIncludes().add(includeElement);
                xmlSchema.getItems().add(includeElement);
            }
        }

    }

    private void extractSchemas(String schemaDefns) {
        // load each schema element and add it to the schema map

        String token = getToken(schemaDefns);
        int curIndex = schemaDefns.indexOf(token);
        int nextIndex = schemaDefns.indexOf(token,
                                            curIndex + token.length());

        while (curIndex != -1) {
            StringReader sr = null;
            if (nextIndex != -1)
                sr = new StringReader(schemaDefns.substring(curIndex,
                                                            nextIndex));
            else
                sr = new StringReader(schemaDefns.substring(curIndex));

            XmlSchemaCollection collection = new XmlSchemaCollection();
            XmlSchema aSchema = collection.read(sr,
                                                null);
            addSchemaToMap(aSchema);

            curIndex = nextIndex;
            nextIndex = schemaDefns.indexOf(token,
                                            curIndex + token.length());
        }
    }

    private void addSchemaToMap(XmlSchema extractedSchema) {
        // check if a Schema object already exists in schema map for targetNamespace of this schema element
        // if it does then copy the contents of this schema element to the existing one, ensuring that
        // duplicate elements are not created. i.e. before adding some child element like 'include' or 'import'
        // check if it already exists, if it does don't add this
        XmlSchema existingSchema = (XmlSchema) schemaMap.get(extractedSchema.getTargetNamespace());

        if (existingSchema == null) {
            extractedSchema.setAttributeFormDefault(getAttrFormDefaultSetting());
            extractedSchema.setElementFormDefault(getElementFormDefaultSetting());
            schemaMap.put(extractedSchema.getTargetNamespace(), extractedSchema);

        } else {
            copySchemaItems(existingSchema,
                            extractedSchema);
        }
    }

    private void copySchemaItems(XmlSchema existingSchema, XmlSchema aSchema) {
        // items to copy are imports, includes, elements, types ...
        // each item is checked if it is a duplicate entry and copied only if it isn't
        Iterator itemsIterator = aSchema.getItems().getIterator();
        Object schemaObject = null;
        XmlSchemaElement schemaElement = null;
        XmlSchemaType schemaType = null;
        XmlSchemaInclude schemaInclude = null;
        QName qName = null;
        List existingIncludes = getExistingIncludes(existingSchema);

        while (itemsIterator.hasNext()) {
            schemaObject = itemsIterator.next();
            if (schemaObject instanceof XmlSchemaElement) {
                schemaElement = (XmlSchemaElement) schemaObject;
                qName = schemaElement.getQName();
                // if the element does not exist in the existing schema
                if (existingSchema.getElementByName(qName) == null) {
                    // add it to the existing schema
                    existingSchema.getElements().add(qName, schemaElement);
                    existingSchema.getItems().add(schemaElement);
                }
            } else if (schemaObject instanceof XmlSchemaType) {
                schemaType = (XmlSchemaType) itemsIterator.next();
                qName = schemaType.getQName();
                // if the element does not exist in the existing schema
                if (existingSchema.getElementByName(qName) == null) {
                    // add it to the existing schema
                    existingSchema.getSchemaTypes().add(qName, schemaType);
                    existingSchema.getItems().add(schemaType);
                    // add imports
                    addImports(existingSchema, qName);
                }
            } else if (schemaObject instanceof XmlSchemaInclude) {
                schemaInclude = (XmlSchemaInclude) itemsIterator.next();
                if (!existingIncludes.contains(schemaInclude.getSchemaLocation())) {
                    existingSchema.getIncludes().add(schemaInclude);
                    existingSchema.getItems().add(schemaInclude);
                }
            }
        }
    }

    private List getExistingIncludes(XmlSchema xmlSchema) {
        List includeSchemaLocations = new Vector();
        Iterator iterator = xmlSchema.getIncludes().getIterator();

        while (iterator.hasNext()) {
            includeSchemaLocations.add(((XmlSchemaInclude) iterator.next()).getSchemaLocation());
        }
        return includeSchemaLocations;
    }

    private XmlSchemaForm getAttrFormDefaultSetting() {
        if (FORM_DEFAULT_UNQUALIFIED.equals(getAttrFormDefault())) {
            return new XmlSchemaForm(XmlSchemaForm.UNQUALIFIED);
        } else {
            return new XmlSchemaForm(XmlSchemaForm.QUALIFIED);
        }
    }

    private XmlSchemaForm getElementFormDefaultSetting() {
        if (FORM_DEFAULT_UNQUALIFIED.equals(getElementFormDefault())) {
            return new XmlSchemaForm(XmlSchemaForm.UNQUALIFIED);
        } else {
            return new XmlSchemaForm(XmlSchemaForm.QUALIFIED);
        }
    }

    private String getToken(String s) {
        // get the schema element name eg. <xs:schema or <xsd:schema. We only know that 'schema' will be used
        // but not sure what suffix is used. Hence this method to get the actual element name used
        int i = s.indexOf(SCHEMA_ELEMENT_NAME);
        int j = s.substring(0,
                            i).lastIndexOf("<");
        return s.substring(j,
                           i + SCHEMA_ELEMENT_NAME.length());
    }

    public String getAttrFormDefault() {
        return attrFormDefault;
    }

    public void setAttrFormDefault(String attrFormDefault) {
        this.attrFormDefault = attrFormDefault;
    }

    public String getElementFormDefault() {
        return elementFormDefault;
    }

    public void setElementFormDefault(String elementFormDefault) {
        this.elementFormDefault = elementFormDefault;
    }


    /**
     * Recognize the pattern of generated SDO type names vs. SDO factory names.
     * E.g. SDO class:   test.sca.w2j.gen.Company will be associated with
     *      SDO factory: test.sca.w2j.gen.GenFactory
     */
    private void detectAndRegisterFactory(Class sdoClass) {
        String pkgName = sdoClass.getPackage().getName();

        // Find last segment, e.g. from 'test.sca.w2j.gen' produce 'gen'.
        int lastDot = pkgName.lastIndexOf('.');
        String lastSegment = pkgName.substring(lastDot+1);

        String rest = lastSegment.substring(1);
        String firstChar = lastSegment.substring(0,1).toUpperCase();

        String factoryBaseName = pkgName + "." + firstChar + rest + "Factory";

        Class factoryClass = null;
        try {
            factoryClass = Class.forName(factoryBaseName, true, classLoader);
            if (!alreadyPrintedDefaultSDOFactoryFound) {
                System.out.println("Found default generated SDO Factory with name: " + factoryBaseName + "; Registering.");
                alreadyPrintedDefaultSDOFactoryFound = true;
            }
            registerSDOFactory(factoryClass);
        } catch (ClassNotFoundException e) {
            if (!alreadyPrintedDefaultSDOFactoryNotFound) {
                System.out.println("Did not find default generated SDO Factory with name: " + factoryBaseName + "; Continue." );
                alreadyPrintedDefaultSDOFactoryNotFound = true;
            }
        }
    }

    private void registerSDOFactory(Class factoryClass) {
        String factoryClassName = factoryClass.getName();
        if (!registeredSDOFactories.contains(factoryClassName)) {
            try {               
                Field field = factoryClass.getField("INSTANCE");
                Object factoryImpl = field.get(null);
                Method method = factoryImpl.getClass().getMethod("register", new Class[] {HelperContext.class});
                method.invoke(factoryImpl, new Object[] {this.helperContext});
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println("");
                System.out.println("Fatal error registering factoryClassName = " + factoryClassName);
                throw new IllegalArgumentException(e);
            }
            registeredSDOFactories.add(factoryClassName);
        }
    }
}
TOP

Related Classes of org.apache.tuscany.tools.java2wsdl.generate.SchemaBuilder

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.