/* * 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.axis2.schema; import org.apache.axis2.AxisFault; import org.apache.axis2.description.AxisMessage; import org.apache.axis2.description.AxisOperation; import org.apache.axis2.description.AxisService; import org.apache.axis2.description.Parameter; import org.apache.axis2.schema.typemap.JavaTypeMap; import org.apache.axis2.schema.typemap.TypeMap; import org.apache.axis2.wsdl.WSDLConstants; import org.apache.axis2.wsdl.WSDLUtil; import org.apache.axis2.wsdl.codegen.CodeGenConfiguration; import org.apache.axis2.wsdl.databinding.CTypeMapper; import org.apache.axis2.wsdl.databinding.DefaultTypeMapper; import org.apache.axis2.wsdl.databinding.JavaTypeMapper; import org.apache.axis2.wsdl.databinding.TypeMapper; import org.apache.axis2.wsdl.util.Constants; import org.apache.ws.commons.schema.XmlSchema; import org.apache.ws.commons.schema.XmlSchemaAny; import org.apache.ws.commons.schema.XmlSchemaAttribute; import org.apache.ws.commons.schema.XmlSchemaComplexContent; import org.apache.ws.commons.schema.XmlSchemaComplexContentExtension; import org.apache.ws.commons.schema.XmlSchemaComplexType; import org.apache.ws.commons.schema.XmlSchemaContent; import org.apache.ws.commons.schema.XmlSchemaContentModel; import org.apache.ws.commons.schema.XmlSchemaElement; import org.apache.ws.commons.schema.XmlSchemaImport; import org.apache.ws.commons.schema.XmlSchemaInclude; import org.apache.ws.commons.schema.XmlSchemaObject; import org.apache.ws.commons.schema.XmlSchemaParticle; import org.apache.ws.commons.schema.XmlSchemaSequence; import org.apache.ws.commons.schema.XmlSchemaSequenceMember; import org.apache.ws.commons.schema.XmlSchemaSimpleType; import org.apache.ws.commons.schema.XmlSchemaType; import javax.xml.namespace.QName; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; /** * This is the utility for the extension to call by reflection. */ public class ExtensionUtility { public static void invoke(CodeGenConfiguration configuration) throws Exception { List<XmlSchema> schemaList = new ArrayList<XmlSchema>(); // add all the schemas to the list for (AxisService service : configuration.getAxisServices()) { schemaList.addAll(service.getSchema()); } //hashmap that keeps the targetnamespace and the xmlSchema object //this is a convenience to locate the relevant schema quickly //by looking at the target namespace Map<String,XmlSchema> schemaMap = new HashMap<String,XmlSchema>(); populateSchemaMap(schemaMap, schemaList); if (schemaList == null || schemaList.isEmpty()) { //there are no types to be code generated //However if the type mapper is left empty it will be a problem for the other //processes. Hence the default type mapper is set to the configuration configuration.setTypeMapper(new DefaultTypeMapper()); return; } //call the schema compiler CompilerOptions options = new CompilerOptions(); //set the default options populateDefaultOptions(options, configuration); //set the user parameters. the user parameters get the preference over //the default ones. But the user better know what he's doing if he //used module specific parameters populateUserparameters(options, configuration); SchemaCompiler schemaCompiler = new SchemaCompiler(options); // run the schema compiler schemaCompiler.compile(schemaList); //create the type mapper //First try to take the one that is already there TypeMapper mapper = configuration.getTypeMapper(); if (mapper == null) { if (configuration.getOutputLanguage() != null && !configuration.getOutputLanguage().trim().equals("") && configuration.getOutputLanguage().toLowerCase().equals("c")) { mapper = new CTypeMapper(); } else { mapper = new JavaTypeMapper(); } } if (options.isWriteOutput()) { //get the processed element map and transfer it to the type mapper for (Map.Entry<QName,String> entry : schemaCompiler.getProcessedElementMap().entrySet()) { mapper.addTypeMappingName(entry.getKey(), entry.getValue()); } } else { //get the processed model map and transfer it to the type mapper //since the options mentiond that its not writable, it should have //populated the model map Map processedModelMap = schemaCompiler.getProcessedModelMap(); Iterator processedkeys = processedModelMap.keySet().iterator(); QName qNameKey; while (processedkeys.hasNext()) { qNameKey = (QName) processedkeys.next(); mapper.addTypeMappingObject(qNameKey, processedModelMap.get(qNameKey)); } for (Map.Entry<QName,String> entry : schemaCompiler.getProcessedElementMap().entrySet()) { mapper.addTypeMappingName(entry.getKey(), entry.getValue()); } //get the ADB template from the schema compilers property bag and set the //template configuration.putProperty(Constants.EXTERNAL_TEMPLATE_PROPERTY_KEY, schemaCompiler.getCompilerProperties().getProperty( SchemaConstants.SchemaPropertyNames.BEAN_WRITER_TEMPLATE_KEY)); } //replace the Axis2 schemas with the processed ones. //otherwise it gives some problems if we try to code generate with multiple //services with the -uw option. //inorder to work for -uw option there must be some metadata with the schema list Map<String, XmlSchema> loadedSchemaMap = schemaCompiler.getLoadedSchemaMap(); for (AxisService service : configuration.getAxisServices()) { List<XmlSchema> serviceSchemaList = service.getSchema(); List<XmlSchema> schemaListToAdd = new ArrayList<XmlSchema>(); for (XmlSchema xmlSchema : serviceSchemaList){ if (loadedSchemaMap.containsKey(xmlSchema.getTargetNamespace())){ schemaListToAdd.add(loadedSchemaMap.get(xmlSchema.getTargetNamespace())); } else { schemaListToAdd.add(xmlSchema); } } service.releaseSchemaList(); service.addSchema(schemaListToAdd); } //process the unwrapped parameters if (!configuration.isParametersWrapped()) { //figure out the unwrapped operations for (AxisService axisService : configuration.getAxisServices()) { for (Iterator<AxisOperation> operations = axisService.getOperations(); operations.hasNext();) { AxisOperation op = operations.next(); if (WSDLUtil.isInputPresentForMEP(op.getMessageExchangePattern())) { walkSchema(op.getMessage( WSDLConstants.MESSAGE_LABEL_IN_VALUE), mapper, schemaMap, op.getName().getLocalPart(), WSDLConstants.INPUT_PART_QNAME_SUFFIX); } // TODO: support for xml beans if (configuration.getDatabindingType().equals("adb")) { if (WSDLUtil.isOutputPresentForMEP(op.getMessageExchangePattern())) { walkSchema(op.getMessage( WSDLConstants.MESSAGE_LABEL_OUT_VALUE), mapper, schemaMap, op.getName().getLocalPart(), WSDLConstants.OUTPUT_PART_QNAME_SUFFIX); } } } } } //put the complext types for the top level elements having them // this is needed in unwrapping and to provide backwordCompatibility if (!configuration.isParametersWrapped() || configuration.isBackwordCompatibilityMode()) { for (AxisService axisService : configuration.getAxisServices()) { AxisOperation axisOperation; AxisMessage axisMessage; for (Iterator<AxisOperation> operators = axisService.getOperations(); operators.hasNext();) { axisOperation = operators.next(); if (WSDLUtil.isInputPresentForMEP(axisOperation.getMessageExchangePattern())) { axisMessage = axisOperation.getMessage(WSDLConstants.MESSAGE_LABEL_IN_VALUE); setComplexTypeName(axisMessage); } if (WSDLUtil.isOutputPresentForMEP(axisOperation.getMessageExchangePattern())) { axisMessage = axisOperation.getMessage(WSDLConstants.MESSAGE_LABEL_OUT_VALUE); setComplexTypeName(axisMessage); } } } } //set the type mapper to the config configuration.setTypeMapper(mapper); } /** * set the complext type class name as an message parameter if it exits * @param axisMessage */ private static void setComplexTypeName(AxisMessage axisMessage) throws AxisFault { if (axisMessage.getSchemaElement() != null){ XmlSchemaElement schemaElement = axisMessage.getSchemaElement(); XmlSchemaType schemaType = schemaElement.getSchemaType(); QName schemaTypeQname = schemaElement.getSchemaTypeName(); if (schemaType == null) { if (schemaTypeQname != null) { // find the schema type from all the schemas // now we need to get the schema of the extension type from the parent schema. For that let's first retrieve // the parent schema AxisService axisService = axisMessage.getAxisOperation().getAxisService(); for (XmlSchema schema : axisService.getSchema()) { schemaType = getSchemaType(schema, schemaTypeQname); if (schemaType != null) { break; } } } } if (schemaType instanceof XmlSchemaComplexType){ XmlSchemaComplexType complexType = (XmlSchemaComplexType) schemaType; if ((complexType.getName() != null) && (complexType.getQName() != null)) { Map metaInfo = complexType.getMetaInfoMap(); String complexTypeName = (String) metaInfo.get(SchemaConstants.SchemaCompilerInfoHolder.CLASSNAME_KEY); if (complexTypeName.endsWith("[]")){ complexTypeName = complexTypeName.substring(0,complexTypeName.length() -2); } // store the complext type name to process later axisMessage.addParameter(new Parameter(Constants.COMPLEX_TYPE, complexTypeName)); } } } } /** * Populate the schema objects into the * * @param schemaMap * @param schemaList */ private static void populateSchemaMap(Map<String,XmlSchema> schemaMap, List<XmlSchema> schemaList) { for (XmlSchema xmlSchema : schemaList) { schemaMap.put(xmlSchema.getTargetNamespace(), xmlSchema); } } /** * @param message * @param mapper */ private static void walkSchema(AxisMessage message, TypeMapper mapper, Map<String,XmlSchema> schemaMap, String opName, String qnameSuffix) { if (message.getParameter(Constants.UNWRAPPED_KEY) != null) { XmlSchemaElement schemaElement = message.getSchemaElement(); XmlSchemaType schemaType = schemaElement.getSchemaType(); QName schemaTypeQname = schemaElement.getSchemaTypeName(); if (schemaType == null) { if (schemaTypeQname != null) { // find the schema type from all the schemas // now we need to get the schema of the extension type from the parent schema. For that let's first retrieve // the parent schema AxisService axisService = message.getAxisOperation().getAxisService(); for (XmlSchema schema : axisService.getSchema()) { schemaType = getSchemaType(schema, schemaTypeQname); if (schemaType != null) { break; } } } } //create a type mapper TypeMap basicTypeMap = new JavaTypeMap(); if (schemaType instanceof XmlSchemaComplexType) { processXMLSchemaComplexType(schemaType, mapper, opName, schemaMap, qnameSuffix); } else if ((schemaTypeQname != null) && basicTypeMap.getTypeMap().containsKey(schemaTypeQname)){ QName partQName = WSDLUtil.getPartQName(opName, qnameSuffix, message.getElementQName().getLocalPart()); mapper.addTypeMappingName(partQName, (String)basicTypeMap.getTypeMap().get(schemaTypeQname)); } else if (schemaType instanceof XmlSchemaSimpleType) { XmlSchemaSimpleType xmlSchemaSimpleType = (XmlSchemaSimpleType) schemaType; populateClassName(xmlSchemaSimpleType.getMetaInfoMap(), mapper, opName, false, message.getElementQName().getLocalPart(), qnameSuffix); // handle xsd:anyType } } } private static void processXMLSchemaComplexType(XmlSchemaType schemaType, TypeMapper mapper, String opName, Map schemaMap, String qnameSuffix) { if (schemaType instanceof XmlSchemaComplexType) { XmlSchemaComplexType cmplxType = (XmlSchemaComplexType) schemaType; if (cmplxType.getContentModel() == null) { processSchemaSequence(cmplxType.getParticle(), mapper, opName, schemaMap, qnameSuffix); } else { processComplexContentModel(cmplxType, mapper, opName, schemaMap, qnameSuffix); } processAttributes(cmplxType, opName, qnameSuffix, mapper); } } private static void processAttributes(XmlSchemaComplexType complexType, String opName, String qnameSuffix, TypeMapper typeMap) { for (XmlSchemaObject item : complexType.getAttributes()) { XmlSchemaAttribute xmlSchemaAttribute; if (item instanceof XmlSchemaAttribute) { xmlSchemaAttribute = (XmlSchemaAttribute) item; populateClassName(xmlSchemaAttribute.getMetaInfoMap(), typeMap, opName, false, xmlSchemaAttribute.getName(), qnameSuffix); } } } private static XmlSchemaType getSchemaType(XmlSchema schema, QName typeName) { XmlSchemaType xmlSchemaType = null; if (schema != null) { xmlSchemaType = schema.getTypeByName(typeName); if (xmlSchemaType == null) { // try to find in an import or an include) { for (XmlSchemaObject object : schema.getExternals()) { if (object instanceof XmlSchemaImport) { XmlSchema schema1 = ((XmlSchemaImport) object).getSchema(); xmlSchemaType = getSchemaType(schema1, typeName); } if (object instanceof XmlSchemaInclude) { XmlSchema schema1 = ((XmlSchemaInclude) object).getSchema(); xmlSchemaType = getSchemaType(schema1, typeName); } if (xmlSchemaType != null) { break; } } } } return xmlSchemaType; } private static void processComplexContentModel(XmlSchemaComplexType cmplxType, TypeMapper mapper, String opName, Map<String,XmlSchema> schemaMap, String qnameSuffix) { XmlSchemaContentModel contentModel = cmplxType.getContentModel(); if (contentModel instanceof XmlSchemaComplexContent) { XmlSchemaComplexContent xmlSchemaComplexContent = (XmlSchemaComplexContent) contentModel; XmlSchemaContent content = xmlSchemaComplexContent.getContent(); if (content instanceof XmlSchemaComplexContentExtension) { XmlSchemaComplexContentExtension schemaExtension = (XmlSchemaComplexContentExtension) content; // process particles inside this extension, if any processSchemaSequence(schemaExtension.getParticle(), mapper, opName, schemaMap, qnameSuffix); XmlSchemaType extensionSchemaType = null; for (XmlSchema xmlSchema : schemaMap.values()) { extensionSchemaType = getSchemaType(xmlSchema,schemaExtension.getBaseTypeName()); if (extensionSchemaType != null){ break; } } processXMLSchemaComplexType(extensionSchemaType, mapper, opName, schemaMap, qnameSuffix); } } } private static void processSchemaSequence(XmlSchemaParticle particle, TypeMapper mapper, String opName, Map<String,XmlSchema> schemaMap, String qnameSuffix) { if (particle instanceof XmlSchemaSequence) { List<XmlSchemaSequenceMember> items = ((XmlSchemaSequence) particle).getItems(); for (XmlSchemaSequenceMember item : items) { // get each and every element in the sequence and // traverse through them if (item instanceof XmlSchemaElement) { //populate the map with the partname - class name //attached to the schema element XmlSchemaElement xmlSchemaElement = (XmlSchemaElement) item; boolean isArray = xmlSchemaElement.getMaxOccurs() > 1; XmlSchemaType schemaType = xmlSchemaElement.getSchemaType(); String partName = null; if (xmlSchemaElement.getRef().getTargetQName() != null) { partName = xmlSchemaElement.getRef().getTargetQName().getLocalPart(); } else { partName = xmlSchemaElement.getName(); } // get the element class name from the element and populate the mapper populateClassName(xmlSchemaElement.getMetaInfoMap(), mapper, opName, isArray, partName, qnameSuffix); } else if (item instanceof XmlSchemaAny) { // if this is an instance of xs:any, then there is no part name for it. Using ANY_ELEMENT_FIELD_NAME // for it for now XmlSchemaAny xmlSchemaAny = (XmlSchemaAny) item; boolean isArray = xmlSchemaAny.getMaxOccurs() > 1; QName partQName = WSDLUtil.getPartQName(opName, qnameSuffix, Constants.ANY_ELEMENT_FIELD_NAME); if (isArray) { mapper.addTypeMappingName(partQName, "org.apache.axiom.om.OMElement[]"); } else { mapper.addTypeMappingName(partQName, "org.apache.axiom.om.OMElement"); } } } } } // private static void /** * Util method to populate the class name into the typeMap * */ private static void populateClassName(Map metaInfoMap, TypeMapper typeMap, String opName, boolean isArray, String partName, String qnameSuffix) { if (metaInfoMap != null) { String className = (String) metaInfoMap. get(SchemaConstants.SchemaCompilerInfoHolder.CLASSNAME_KEY); // this is a temporary patch // the acual problem is keeping the class name details on the schemaType in // XmlSchema compiler. // we have to store them in XmlElement if (isArray && !className.endsWith("[]")) { className += "[]"; } else if (!isArray && className.endsWith("[]")) { className = className.substring(0, className.length() - 2); } QName partQName = WSDLUtil.getPartQName(opName, qnameSuffix, partName); typeMap.addTypeMappingName(partQName, className); if (Boolean.TRUE.equals( metaInfoMap.get(SchemaConstants. SchemaCompilerInfoHolder.CLASSNAME_PRIMITVE_KEY))) { //this type is primitive - add that to the type mapper status //for now lets add a boolean typeMap.addTypeMappingStatus(partQName, Boolean.TRUE); } } } /** * Look for a given schema type given the schema type Qname * * @param schemaMap * @return null if the schema is not found */ private static XmlSchemaType findSchemaType(Map<String,XmlSchema> schemaMap, QName schemaTypeName) { //find the schema XmlSchema schema = schemaMap.get(schemaTypeName.getNamespaceURI()); if (schema != null) { return schema.getTypeByName(schemaTypeName); } return null; } /** * populate parameters from the user * * @param options */ private static void populateUserparameters(CompilerOptions options, CodeGenConfiguration configuration) { Map<Object,Object> propertyMap = configuration.getProperties(); if (propertyMap.containsKey(SchemaConstants.SchemaCompilerArguments.WRAP_SCHEMA_CLASSES)) { if (Boolean.valueOf( propertyMap.get(SchemaConstants.SchemaCompilerArguments.WRAP_SCHEMA_CLASSES).toString()). booleanValue()) { options.setWrapClasses(true); } else { options.setWrapClasses(false); } } if (propertyMap.containsKey(SchemaConstants.SchemaCompilerArguments.WRITE_SCHEMA_CLASSES)) { if (Boolean.valueOf( propertyMap.get(SchemaConstants.SchemaCompilerArguments.WRITE_SCHEMA_CLASSES).toString()). booleanValue()) { options.setWriteOutput(true); } else { options.setWriteOutput(false); } } // add the custom package name if (propertyMap.containsKey(SchemaConstants.SchemaCompilerArguments.PACKAGE)) { String packageName = (String) propertyMap.get(SchemaConstants.SchemaCompilerArguments.PACKAGE); if (packageName != null || !"".equals(packageName)) { options.setPackageName(packageName); } } // set the package namespace to uri details options.setNs2PackageMap(configuration.getUri2PackageNameMap()); //add custom mapper package name if (propertyMap.containsKey(SchemaConstants.SchemaCompilerArguments.MAPPER_PACKAGE)) { String packageName = (String) propertyMap.get(SchemaConstants.SchemaCompilerArguments.MAPPER_PACKAGE); if (packageName != null || !"".equals(packageName)) { options.setMapperClassPackage(packageName); } } if (propertyMap.containsKey(SchemaConstants.SchemaCompilerArguments.OFF_STRICT_VALIDATION)){ options.setOffStrictValidation(true); } if (propertyMap.containsKey(SchemaConstants.SchemaCompilerArguments.USE_WRAPPER_CLASSES)){ options.setUseWrapperClasses(true); } if (propertyMap.containsKey(SchemaConstants.SchemaCompilerArguments.IGNORE_UNEXPECTED)){ options.setIgnoreUnexpected(true); } //set helper mode //this becomes effective only if the classes are unpacked if (!options.isWrapClasses()) { if (propertyMap.containsKey(SchemaConstants.SchemaCompilerArguments.HELPER_MODE)) { options.setHelperMode(true); } } } /** * populate the default options - called before the applying of user parameters * * @param options */ private static void populateDefaultOptions(CompilerOptions options, CodeGenConfiguration configuration) { //create the output directory File outputDir = configuration.isFlattenFiles() ? configuration.getOutputLocation() : new File(configuration.getOutputLocation(), configuration.getSourceLocation()); if (!outputDir.exists()) { outputDir.mkdirs(); } /// these options need to be taken from the command line options.setOutputLocation(outputDir); options.setNs2PackageMap(configuration.getUri2PackageNameMap() == null ? new HashMap<String,String>() : configuration.getUri2PackageNameMap()); //default setting is to set the wrap status depending on whether it's //the server side or the client side if (configuration.isServerSide()) { //for the serverside we generate unwrapped by default options.setWrapClasses(false); //for the serverside we write the output by default options.setWriteOutput(true); } else { // for the client let the users preference be the word here options.setWrapClasses(configuration.isPackClasses()); //for the client side the default setting is not to write the //output options.setWriteOutput(!configuration.isPackClasses()); } if (configuration.isGenerateAll()) { options.setGenerateAll(true); } if (configuration.isBackwordCompatibilityMode()) { options.setBackwordCompatibilityMode(true); } if (configuration.isSuppressPrefixesMode()) { options.setSuppressPrefixesMode(true); } } }