/* * 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.jibx; import org.apache.axis2.AxisFault; import org.apache.axis2.description.AxisMessage; import org.apache.axis2.description.AxisOperation; import org.apache.axis2.description.Parameter; import org.apache.axis2.wsdl.SOAPHeaderMessage; import org.apache.axis2.wsdl.WSDLConstants; import org.apache.axis2.wsdl.WSDLUtil; import org.apache.axis2.wsdl.codegen.CodeGenConfiguration; import org.apache.axis2.wsdl.codegen.extension.JiBXExtension; import org.apache.axis2.wsdl.databinding.JavaTypeMapper; import org.apache.axis2.wsdl.util.Constants; import org.apache.axis2.wsdl.util.MessagePartInformationHolder; import org.apache.ws.commons.schema.XmlSchemaComplexType; import org.apache.ws.commons.schema.XmlSchemaElement; 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.XmlSchemaSimpleTypeContent; import org.apache.ws.commons.schema.XmlSchemaSimpleTypeRestriction; import org.apache.ws.commons.schema.XmlSchemaType; import org.jibx.binding.model.BindingElement; import org.jibx.binding.model.ElementBase; import org.jibx.binding.model.FormatElement; import org.jibx.binding.model.IncludeElement; import org.jibx.binding.model.MappingElementBase; import org.jibx.binding.model.ModelVisitor; import org.jibx.binding.model.NamespaceElement; import org.jibx.binding.model.ValidationContext; import org.jibx.binding.model.ValidationProblem; import org.jibx.runtime.JiBXException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import javax.xml.namespace.QName; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.net.MalformedURLException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** * Framework-linked code used by JiBX data binding support. This is accessed via reflection from the * JiBX code generation extension when JiBX data binding is selected. JiBX uses a different approach * to unwrapping method parameters from that implemented by ADB, and since the ADB technique is * assumed by all code generation templates this has to create the same data structures. These data * structures are undocumented, and can only be determined by going through the {@link * org.apache.axis2.wsdl.codegen.extension.SchemaUnwrapperExtension} and {@link * org.apache.axis2.wsdl.codegen.emitter.AxisServiceBasedMultiLanguageEmitter} code. */ public class CodeGenerationUtility { private static final String SCHEMA_NAMESPACE = "http://www.w3.org/2001/XMLSchema"; private final CodeGenConfiguration codeGenConfig; private static HashSet s_primitiveSet = new HashSet(); static { s_primitiveSet.add("boolean"); s_primitiveSet.add("byte"); s_primitiveSet.add("char"); s_primitiveSet.add("double"); s_primitiveSet.add("float"); s_primitiveSet.add("int"); s_primitiveSet.add("long"); s_primitiveSet.add("short"); s_primitiveSet.add("void"); } private static HashMap s_wrapperMap = new HashMap(); static { s_wrapperMap.put("boolean", "Boolean"); s_wrapperMap.put("byte", "Byte"); s_wrapperMap.put("char", "Character"); s_wrapperMap.put("double", "Double"); s_wrapperMap.put("float", "Float"); s_wrapperMap.put("int", "Integer"); s_wrapperMap.put("long", "Long"); s_wrapperMap.put("short", "Short"); } /** Reserved words for Java (keywords and literals). */ private static final HashSet s_reservedWords = new HashSet(); static { // keywords s_reservedWords.add("abstract"); s_reservedWords.add("assert"); s_reservedWords.add("boolean"); s_reservedWords.add("break"); s_reservedWords.add("byte"); s_reservedWords.add("case"); s_reservedWords.add("catch"); s_reservedWords.add("char"); s_reservedWords.add("class"); s_reservedWords.add("const"); s_reservedWords.add("continue"); s_reservedWords.add("default"); s_reservedWords.add("do"); s_reservedWords.add("double"); s_reservedWords.add("else"); s_reservedWords.add("enum"); s_reservedWords.add("extends"); s_reservedWords.add("final"); s_reservedWords.add("finally"); s_reservedWords.add("float"); s_reservedWords.add("for"); s_reservedWords.add("goto"); s_reservedWords.add("if"); s_reservedWords.add("implements"); s_reservedWords.add("import"); s_reservedWords.add("instanceof"); s_reservedWords.add("int"); s_reservedWords.add("interface"); s_reservedWords.add("long"); s_reservedWords.add("native"); s_reservedWords.add("new"); s_reservedWords.add("package"); s_reservedWords.add("private"); s_reservedWords.add("protected"); s_reservedWords.add("public"); s_reservedWords.add("return"); s_reservedWords.add("short"); s_reservedWords.add("static"); s_reservedWords.add("strictfp"); s_reservedWords.add("super"); s_reservedWords.add("switch"); s_reservedWords.add("synchronized"); s_reservedWords.add("this"); s_reservedWords.add("throw"); s_reservedWords.add("throws"); s_reservedWords.add("transient"); s_reservedWords.add("try"); s_reservedWords.add("void"); s_reservedWords.add("volatile"); s_reservedWords.add("while"); // literals s_reservedWords.add("true"); s_reservedWords.add("false"); s_reservedWords.add("null"); // variable names used by template unwrapped code generation // TODO change templates to use $xxx names, block generation from WSDL s_reservedWords.add("true"); s_reservedWords.add("uctx"); s_reservedWords.add("child"); s_reservedWords.add("wrapper"); } /** * Constructor. * * @param config code generation configuration */ public CodeGenerationUtility(CodeGenConfiguration config) { codeGenConfig = config; } /** * Configure the code generation based on the supplied parameters and WSDL. This first gets type * mappings from binding definition, then goes through the operations checking the input and * output messages. If unwrapping is disabled the message element must be handled directly by a * mapping. If unwrapping is enabled, this checks that the message element is of the proper form * (a sequence of other elements, which all have maxOccurs="1"). It then generates an unwrapping * description and adds it to the code generation configuration, where it'll be picked up and * included in the XML passed to code generation. This also constructs a type mapping, since * that's required by the base Axis2 code generation. In the case of unwrapped elements, the * type mapping includes a synthesized qname for each unwrapped parameter, and the detailed * information is set on the message information. Sound confusing? Welcome to Axis2 code * generation. * * @param path binding path (<code>null</code> if none) */ public void engage(String path) { // make sure the binding definition file is present, if passed File file = null; if (path != null) { file = new File(path); if (!file.exists()) { throw new RuntimeException("jibx binding definition file " + path + " not found"); } } try { // set flag for unwrapping boolean unwrap = !codeGenConfig.isParametersWrapped(); // initialize the binding information BindingElement binding = null; if (file == null) { // unwrapped can be used without a binding, but wrapped requires one if (!unwrap) { throw new RuntimeException( "JiBX wrapped support requires a binding definition to be provided using the -E" + JiBXExtension.BINDING_PATH_OPTION + " {file-path} parameter"); } } else { // Read the JiBX binding definition into memory. The binding definition // is only prevalidated so as not to require the user to have all // the referenced classes in the classpath, though this does make for // added work in finding the namespaces. ValidationContext vctx = BindingElement.newValidationContext(); binding = BindingElement.readBinding(new FileInputStream(file), path, vctx); binding.setBaseUrl(file.toURL()); vctx.setBindingRoot(binding); IncludePrevalidationVisitor ipv = new IncludePrevalidationVisitor(vctx); vctx.tourTree(binding, ipv); if (vctx.getErrorCount() != 0 || vctx.getFatalCount() != 0) { ArrayList probs = vctx.getProblems(); System.err.println("Errors in generated binding:"); for (int j = 0; j < probs.size(); j++) { ValidationProblem prob = (ValidationProblem)probs.get(j); System.err.print(prob.getSeverity() >= ValidationProblem.ERROR_LEVEL ? "Error: " : "Warning: "); System.err.println(prob.getDescription()); } throw new RuntimeException("invalid jibx binding definition file " + path); } } // create table with all built-in format definitions Map simpleTypeMap = new HashMap(); buildFormat("byte", "byte", "org.jibx.runtime.Utility.serializeByte", "org.jibx.runtime.Utility.parseByte", "0", simpleTypeMap); buildFormat("unsignedShort", "char", "org.jibx.runtime.Utility.serializeChar", "org.jibx.runtime.Utility.parseChar", "0", simpleTypeMap); buildFormat("double", "double", "org.jibx.runtime.Utility.serializeDouble", "org.jibx.runtime.Utility.parseDouble", "0.0", simpleTypeMap); buildFormat("float", "float", "org.jibx.runtime.Utility.serializeFloat", "org.jibx.runtime.Utility.parseFloat", "0.0", simpleTypeMap); buildFormat("int", "int", "org.jibx.runtime.Utility.serializeInt", "org.jibx.runtime.Utility.parseInt", "0", simpleTypeMap); buildFormat("long", "long", "org.jibx.runtime.Utility.serializeLong", "org.jibx.runtime.Utility.parseLong", "0", simpleTypeMap); buildFormat("short", "short", "org.jibx.runtime.Utility.serializeShort", "org.jibx.runtime.Utility.parseShort", "0", simpleTypeMap); buildFormat("boolean", "boolean", "org.jibx.runtime.Utility.serializeBoolean", "org.jibx.runtime.Utility.parseBoolean", "false", simpleTypeMap); buildFormat("dateTime", "java.util.Date", "org.jibx.runtime.Utility.serializeDateTime", "org.jibx.runtime.Utility.deserializeDateTime", null, simpleTypeMap); buildFormat("date", "java.sql.Date", "org.jibx.runtime.Utility.serializeSqlDate", "org.jibx.runtime.Utility.deserializeSqlDate", null, simpleTypeMap); buildFormat("time", "java.sql.Time", "org.jibx.runtime.Utility.serializeSqlTime", "org.jibx.runtime.Utility.deserializeSqlTime", null, simpleTypeMap); buildFormat("base64Binary", "byte[]", "org.jibx.runtime.Utility.serializeBase64", "org.jibx.runtime.Utility.deserializeBase64", null, simpleTypeMap); buildFormat("string", "java.lang.String", null, null, null, simpleTypeMap); // collect all the top-level mapping and format definitions Map elementMap = new HashMap(); Map complexTypeMap = new HashMap(); Map bindingMap = new HashMap(); if (binding != null) { collectTopLevelComponents(binding, "", elementMap, complexTypeMap, simpleTypeMap, bindingMap); } // make sure classes will be generated for abstract mappings if (unwrap && complexTypeMap.size() > 0 && (binding == null || !binding.isForceClasses())) { throw new RuntimeException( "unwrapped binding must use force-classes='true' option in " + path); } // force off inappropriate option (set by error in options handling) codeGenConfig.setPackClasses(false); // configure handling for all operations of service codeGenConfig.setTypeMapper(new NamedParameterTypeMapper()); Iterator operations = codeGenConfig.getAxisService().getOperations(); Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); int opindex = 0; Map typeMappedClassMap = new HashMap(); String mappedclass = null; Set objins = new HashSet(); Set objouts = new HashSet(); Set objfaults = new HashSet(); Map nsMap = new HashMap(); ArrayList wrappers = new ArrayList(); while (operations.hasNext()) { // get the basic operation information AxisOperation op = (AxisOperation)operations.next(); String mep = op.getMessageExchangePattern(); AxisMessage inmsg = null; AxisMessage outmsg = null; if (WSDLUtil.isInputPresentForMEP(mep)) { inmsg = op.getMessage(WSDLConstants.MESSAGE_LABEL_IN_VALUE); if (inmsg == null) { throw new RuntimeException( "Expected input message not found for operation " + op.getName()); } ArrayList headers = inmsg.getSoapHeaders(); for (int i = 0; i < headers.size(); i++) { SOAPHeaderMessage header = (SOAPHeaderMessage)headers.get(i); String cname = mapMessage(header, elementMap); objins.add(cname); if (mappedclass == null && isLookupClass(cname)) { mappedclass = cname; } } } if (WSDLUtil.isOutputPresentForMEP(mep)) { outmsg = op.getMessage(WSDLConstants.MESSAGE_LABEL_OUT_VALUE); if (outmsg == null) { throw new RuntimeException( "Expected output message not found for operation " + op.getName()); } ArrayList headers = outmsg.getSoapHeaders(); for (int i = 0; i < headers.size(); i++) { SOAPHeaderMessage header = (SOAPHeaderMessage)headers.get(i); String cname = mapMessage(header, elementMap); objouts.add(cname); if (mappedclass == null && isLookupClass(cname)) { mappedclass = cname; } } } if (unwrap) { // use unwrapping for both input and output String receivername = "jibxReceiver" + opindex++; Element dbmethod = doc.createElement("dbmethod"); dbmethod.setAttribute("receiver-name", receivername); dbmethod.setAttribute("method-name", op.getName().getLocalPart()); Set nameset = new HashSet(s_reservedWords); if (inmsg != null) { Element wrapper = unwrapMessage(inmsg, false, simpleTypeMap, elementMap, complexTypeMap, typeMappedClassMap, bindingMap, nameset, nsMap, doc); dbmethod.appendChild(wrapper); wrappers.add(wrapper); } if (outmsg != null) { Element wrapper = unwrapMessage(outmsg, true, simpleTypeMap, elementMap, complexTypeMap, typeMappedClassMap, bindingMap, nameset, nsMap, doc); dbmethod.appendChild(wrapper); wrappers.add(wrapper); } // save unwrapping information for use in code generation op.addParameter( new Parameter(Constants.DATABINDING_GENERATED_RECEIVER, receivername)); op.addParameter(new Parameter(Constants.DATABINDING_GENERATED_IMPLEMENTATION, Boolean.TRUE)); op.addParameter( new Parameter(Constants.DATABINDING_OPERATION_DETAILS, dbmethod)); } else { // concrete mappings, just save the mapped class name(s) if (inmsg != null) { String cname = mapMessage(inmsg, elementMap); objins.add(cname); if (mappedclass == null && isLookupClass(cname)) { mappedclass = cname; } } if (outmsg != null) { String cname = mapMessage(outmsg, elementMap); objouts.add(cname); if (mappedclass == null && isLookupClass(cname)) { mappedclass = cname; } } } // always handle faults as wrapped for (Iterator iter = op.getFaultMessages().iterator(); iter.hasNext();) { String cname = mapMessage((AxisMessage)iter.next(), elementMap); objfaults.add(cname); if (mappedclass == null && isLookupClass(cname)) { mappedclass = cname; } } } // check for default namespace usage within bindings or wrappers // (meaning we can't declare a conflicting default namespace) Collection prefixes = nsMap.values(); boolean dfltns = prefixes.contains(""); boolean wrapdflt = false; if (!dfltns) { for (int i = 0; i < wrappers.size(); i++) { Element wrapper = (Element)wrappers.get(i); if ("true".equals(wrapper.getAttribute("uses-default"))) { wrapdflt = true; break; } } } // find a prefix that we can use where needed for extra namespace String xtrapref = ""; if (dfltns || wrapdflt) { xtrapref = "_"; int index = 0; while (prefixes.contains(xtrapref)) { xtrapref = "_" + index++; } } // for each wrapper (input and output), determine what additional // namespaces need to be declared, what prefix is to be used for // the wrapper element, and what prefix to be used for each child // element for (int i = 0; i < wrappers.size(); i++) { Element wrapper = (Element)wrappers.get(i); boolean addns = false; String ns = wrapper.getAttribute("ns"); String prefix = ""; if ("true".equals(wrapper.getAttribute("need-namespaces"))) { // check extra definition needed for wrapper namespace if (!"".equals(ns)) { if (dfltns || wrapdflt) { // need a namespace, can't be default, get or set it prefix = (String)nsMap.get(ns); if (prefix == null) { prefix = xtrapref; addns = true; } } else { // just make the wrapper namespace the default prefix = ""; addns = true; } } wrapper.setAttribute("prefix", prefix); // set prefixes for child elements of wrapper Node node = wrapper.getFirstChild(); while (node != null) { if (node.getNodeType() == Node.ELEMENT_NODE) { Element element = (Element)node; String lname = element.getNodeName(); if ("parameter-element".equals(lname) || "return-element".equals(lname)) { String childns = element.getAttribute("ns"); if ("".equals(childns)) { element.setAttribute("prefix", ""); } else if (ns.equals(childns)) { element.setAttribute("prefix", prefix); } else { String childprefix = (String)nsMap.get(childns); if (childprefix == null) { throw new RuntimeException("Unable to set namespace " + childns + " for child element"); } element.setAttribute("prefix", childprefix); } } } node = node.getNextSibling(); } } else { // check extra definition needed for wrapper namespace if (!"".equals(ns)) { // just make the wrapper namespace the default prefix = ""; addns = true; } wrapper.setAttribute("prefix", prefix); // set prefixes for child elements of wrapper Node node = wrapper.getFirstChild(); while (node != null) { if (node.getNodeType() == Node.ELEMENT_NODE) { Element element = (Element)node; String lname = element.getNodeName(); if ("parameter-element".equals(lname) || "return-element".equals(lname)) { String childns = element.getAttribute("ns"); if ("".equals(childns)) { element.setAttribute("prefix", ""); } else if (ns.equals(childns)) { element.setAttribute("prefix", prefix); } else { throw new RuntimeException("Unable to set namespace " + childns + " for child element"); } } } node = node.getNextSibling(); } } if (addns) { Element addedns = doc.createElement("extra-namespace"); addedns.setAttribute("ns", ns); addedns.setAttribute("prefix", prefix); wrapper.appendChild(addedns); } } // add type usage information for binding initialization List details = new ArrayList(); Element bindinit = doc.createElement("initialize-binding"); if (!typeMappedClassMap.isEmpty()) { for (Iterator iter = typeMappedClassMap.keySet().iterator(); iter.hasNext();) { QName tname = (QName)iter.next(); String clsindex = ((Integer)typeMappedClassMap.get(tname)).toString(); Element detail = doc.createElement("abstract-type"); detail.setAttribute("ns", tname.getNamespaceURI()); detail.setAttribute("name", tname.getLocalPart()); detail.setAttribute("type-index", clsindex); bindinit.appendChild(detail); if (mappedclass == null) { MappingElementBase mapping = (MappingElementBase)complexTypeMap.get(tname); mappedclass = mapping.getClassName(); } } } // set binding lookup parameters if (binding != null && binding.getName() != null && binding.getTargetPackage() != null) { bindinit.setAttribute("binding-name", binding.getName()); bindinit.setAttribute("binding-package", binding.getTargetPackage()); } else { if (mappedclass == null) { mappedclass = ""; } bindinit.setAttribute("bound-class", mappedclass); } // include binding namespaces in initialization data for (Iterator iter = nsMap.keySet().iterator(); iter.hasNext();) { String ns = (String)iter.next(); String prefix = (String)nsMap.get(ns); Element detail = doc.createElement("binding-namespace"); detail.setAttribute("ns", ns); detail.setAttribute("prefix", prefix); bindinit.appendChild(detail); } details.add(bindinit); // add details for all objects used as inputs/outputs/faults for (Iterator iter = objins.iterator(); iter.hasNext();) { String classname = (String)iter.next(); Element detail = doc.createElement("object-input"); detail.setAttribute("type", classname); details.add(detail); } for (Iterator iter = objouts.iterator(); iter.hasNext();) { String classname = (String)iter.next(); Element detail = doc.createElement("object-output"); detail.setAttribute("type", classname); details.add(detail); } for (Iterator iter = objfaults.iterator(); iter.hasNext();) { String classname = (String)iter.next(); Element detail = doc.createElement("object-fault"); detail.setAttribute("type", classname); details.add(detail); } codeGenConfig.getAxisService() .addParameter(new Parameter(Constants.DATABINDING_SERVICE_DETAILS, details)); } catch (FileNotFoundException e) { throw new RuntimeException(e); } catch (JiBXException e) { throw new RuntimeException(e); } catch (ParserConfigurationException e) { throw new RuntimeException(e); } catch (AxisFault e) { throw new RuntimeException(e); } catch (MalformedURLException e) { throw new RuntimeException(e); } } /** * Check if a class is potentially usable for looking up binding information. * * @param type fully-qualified type name * @return <code>true</code> if potentially usable, <code>false</code> if not */ private static boolean isLookupClass(String type) { return !type.startsWith("java.") && !type.startsWith("javax.") && !type.startsWith("org.w3c."); } /** * Add format definition for type with built-in JiBX handling to map. * * @param stype schema type name * @param jtype java type name * @param sname serializer method name * @param dname deserializer method name * @param dflt default value * @param map schema type qname to format definition map */ private static void buildFormat(String stype, String jtype, String sname, String dname, String dflt, Map map) { FormatElement format = new FormatElement(); format.setTypeName(jtype); format.setSerializerName(sname); format.setDeserializerName(dname); format.setDefaultText(dflt); map.put(new QName(SCHEMA_NAMESPACE, stype), format); } /** * Handles unwrapping a message. This generates and returns the detailed description of how the * message is to be unwrapped. It also creates the data structures expected by the code * generation in order to be somewhat compatible with ADB unwrapping. * * @param msg message to be unwrapped * @param isout output message flag (wrapper inherits inner type, for XSLTs) * @param simpleTypeMap binding formats * @param elementMap map from element names to concrete mapping components of binding * @param complexTypeMap binding mappings * @param typeMappedClassMap map from type qname to index * @param bindingMap map from mapping components to containing binding definition * @param nameset parameter variable names used in method * @param nsmap mapr from URI to prefix for namespaces to be defined on wrapper * element * @param doc document used for DOM components * @return detailed description element for code generation */ private Element unwrapMessage(AxisMessage msg, boolean isout, Map simpleTypeMap, Map elementMap, Map complexTypeMap, Map typeMappedClassMap, Map bindingMap, Set nameset, Map nsmap, Document doc) { // find the schema definition for this message element QName qname = msg.getElementQName(); if (qname == null) { throw new RuntimeException("No element reference in message " + msg.getName()); } XmlSchemaElement wrapdef = codeGenConfig.getAxisService().getSchemaElement(qname); if (wrapdef == null) { throw new RuntimeException("Cannot unwrap - no definition found for element " + qname); } XmlSchemaType type = wrapdef.getSchemaType(); // create document to hold data binding details for element Element wrapdetail = doc.createElement(isout ? "out-wrapper" : "in-wrapper"); wrapdetail.setAttribute("ns", qname.getNamespaceURI()); wrapdetail.setAttribute("name", qname.getLocalPart()); // dig down to the complexType particle List partNameList = new ArrayList(); String wrappertype = ""; boolean nons = qname.getNamespaceURI().length() == 0; boolean dfltns = false; boolean complex = false; if (type instanceof XmlSchemaComplexType) { XmlSchemaComplexType ctype = (XmlSchemaComplexType)type; if (!ctype.getAttributes().isEmpty()) { throw new RuntimeException("Cannot unwrap element " + qname + ": attributes not allowed on type to be unwrapped"); } XmlSchemaParticle particle = ctype.getParticle(); if (particle != null) { // if there's a particle present, it must be a sequence if (!(particle instanceof XmlSchemaSequence)) { throw new RuntimeException("Cannot unwrap element " + qname + ": type to be unwrapped must be a sequence"); } if (particle.getMinOccurs() != 1 || particle.getMaxOccurs() != 1) { throw new RuntimeException("Cannot unwrap element " + qname + ": contained sequence must have minOccurs='1' and maxOccurs='1'"); } XmlSchemaSequence sequence = (XmlSchemaSequence)particle; // add child param element matching each child of wrapper element QName opName = msg.getAxisOperation().getName(); boolean first = true; for (XmlSchemaSequenceMember member : sequence.getItems()) { // check that child item obeys the unwrapping rules XmlSchemaParticle item = (XmlSchemaParticle)member; if (!(item instanceof XmlSchemaElement)) { throw new RuntimeException("Cannot unwrap element " + qname + ": only element items allowed in sequence"); } XmlSchemaElement element = (XmlSchemaElement)item; QName refname = element.getRef().getTargetQName(); QName typename = element.getSchemaTypeName(); if (refname == null && typename == null) { throw new RuntimeException("Cannot unwrap element " + qname + ": all elements in contained sequence must be element references or reference a named type"); } if (first) { first = false; } else if (isout) { throw new RuntimeException("Cannot unwrap element " + qname + ": only one child element allowed in sequence for wrapped output"); } // add element to output with details of this element handling Element param = doc.createElement(isout ? "return-element" : "parameter-element"); QName itemname = (refname == null) ? element.getQName() : refname; nons = nons || itemname.getNamespaceURI().length() == 0; param.setAttribute("ns", itemname.getNamespaceURI()); param.setAttribute("name", itemname.getLocalPart()); String javaname = toJavaName(itemname.getLocalPart(), nameset); param.setAttribute("java-name", javaname); param.setAttribute("nillable", Boolean.toString(element.isNillable())); boolean optional = element.getMinOccurs() == 0; param.setAttribute("optional", Boolean.toString(optional)); boolean isarray = element.getMaxOccurs() > 1; param.setAttribute("array", Boolean.toString(isarray)); String javatype; String createtype = null; if (element.getSchemaType() instanceof XmlSchemaSimpleType) { // simple type translates to format element in binding FormatElement format = (FormatElement)simpleTypeMap.get(typename); if (format == null) { // check for restriction with simple base, and treat as base if so XmlSchemaSimpleType stype = (XmlSchemaSimpleType)element.getSchemaType(); XmlSchemaSimpleTypeContent content = stype.getContent(); if (content instanceof XmlSchemaSimpleTypeRestriction) { QName tname = ((XmlSchemaSimpleTypeRestriction)content).getBaseTypeName(); if (SCHEMA_NAMESPACE.equals(tname.getNamespaceURI())) { format = (FormatElement)simpleTypeMap.get(tname); if (format != null) { typename = tname; } } } } if (format == null) { throw new RuntimeException("Cannot unwrap element " + qname + ": no format definition found for type " + typename + " (used by element " + itemname + ')'); } javatype = format.getTypeName(); param.setAttribute("form", "simple"); param.setAttribute("serializer", format.getSerializerName()); param.setAttribute("deserializer", format.getDeserializerName()); // convert primitive types to wrapper types for nillable if ((optional || element.isNillable()) && s_wrapperMap.containsKey(javatype)) { param.setAttribute("wrapped-primitive", "true"); param.setAttribute("value-method", javatype + "Value"); javatype = (String)s_wrapperMap.get(javatype); } else { param.setAttribute("wrapped-primitive", "false"); String dflt = element.getDefaultValue(); if (dflt == null) { dflt = format.getDefaultText(); if (javatype.equals("float")) { dflt = dflt + 'F'; } } if (dflt != null) { param.setAttribute("default", dflt); } } } else { // conversion must be defined by mapping MappingElementBase mapping; if (refname == null) { // complex type reference translates to abstract mapping in binding mapping = (MappingElementBase)complexTypeMap.get(typename); if (mapping == null) { throw new RuntimeException("Cannot unwrap element " + qname + ": no abstract mapping definition found for type " + typename + " (used by element " + itemname + ')'); } Integer tindex = (Integer)typeMappedClassMap.get(typename); if (tindex == null) { tindex = new Integer(typeMappedClassMap.size()); typeMappedClassMap.put(typename, tindex); } param.setAttribute("type-index", tindex.toString()); } else { // element reference translates to concrete mapping mapping = (MappingElementBase)elementMap.get(refname); if (mapping == null) { throw new RuntimeException("Cannot unwrap element " + qname + ": no concrete mapping definition found for element " + refname + " (used by element " + itemname + ')'); } param.setAttribute("type-index", ""); } // configure based on the mapping information param.setAttribute("form", "complex"); complex = true; javatype = mapping.getClassName(); createtype = mapping.getCreateType(); if (createtype == null && mapping.isAbstract() && mapping.getExtensionTypes().isEmpty()) { // abstract mapping with no extensions requires instance // this assumes the mapped type can be created, but no easy way to check createtype = javatype; } // merge contained namespace definitions into set for operation Iterator citer = mapping.topChildIterator(); while (citer.hasNext()) { ElementBase child = (ElementBase)citer.next(); if (child.type() == ElementBase.NAMESPACE_ELEMENT) { dfltns = mapNamespace((NamespaceElement)child, dfltns, nsmap); } else { break; } } // also merge namespace definitions from binding BindingElement binding = (BindingElement)bindingMap.get(mapping); citer = binding.topChildIterator(); while (citer.hasNext()) { ElementBase child = (ElementBase)citer.next(); if (child.type() == ElementBase.NAMESPACE_ELEMENT) { dfltns = mapNamespace((NamespaceElement)child, dfltns, nsmap); } else if (child.type() != ElementBase.INCLUDE_ELEMENT) { break; } } } param.setAttribute("java-type", javatype); if (createtype != null) { param.setAttribute("create-type", createtype); } boolean isobj = !s_primitiveSet.contains(javatype); String fulltype = javatype; if (isarray) { fulltype += "[]"; isobj = false; } param.setAttribute("object", Boolean.toString(isobj)); if (isout) { wrappertype = fulltype; } else { wrappertype = "java.lang.Object"; } wrapdetail.appendChild(param); // this magic code comes from org.apache.axis2.wsdl.codegen.extension.SchemaUnwrapperExtension // it's used here to fit into the ADB-based code generation model QName partqname = WSDLUtil.getPartQName(opName.getLocalPart(), WSDLConstants.INPUT_PART_QNAME_SUFFIX, javaname); partNameList.add(partqname); // add type mapping so we look like ADB codeGenConfig.getTypeMapper().addTypeMappingName(partqname, fulltype); } // check namespace prefix usage if (nons && dfltns) { throw new RuntimeException("Cannot unwrap element " + qname + ": no-namespace element(s) conflict with default namespace use in binding"); } wrapdetail.setAttribute("uses-default", Boolean.toString(nons)); // set flag for namespace declarations needed on wrapper wrapdetail.setAttribute("need-namespaces", Boolean.toString(complex)); } } else if (type != null) { throw new RuntimeException("Cannot unwrap element " + qname + ": not a complexType definition"); } if (wrapdetail.getFirstChild() == null) { wrapdetail.setAttribute("empty", "true"); wrapdetail.setAttribute("need-namespaces", "false"); wrappertype = ""; } else { wrapdetail.setAttribute("empty", "false"); } // this magic code comes from org.apache.axis2.wsdl.codegen.extension.SchemaUnwrapperExtension // it's used here to fit into the ADB-based code generation model MessagePartInformationHolder infoHolder = new MessagePartInformationHolder(); infoHolder.setOperationName(msg.getAxisOperation().getName()); infoHolder.setPartsList(partNameList); try { msg.addParameter(new Parameter(Constants.UNWRAPPED_DETAILS, infoHolder)); } catch (AxisFault e) { throw new RuntimeException(e); } // set indication for unwrapped message try { msg.addParameter(new Parameter(Constants.UNWRAPPED_KEY, Boolean.TRUE)); } catch (AxisFault e) { throw new RuntimeException(e); } // add fake mapping for wrapper name (necessary for current XSLTs) codeGenConfig.getTypeMapper().addTypeMappingName(qname, wrappertype); // return the unwrapping details return wrapdetail; } /** * Add mapping from namespace URI to prefix. In the case where multiple prefixes are used with a * single URI, this will preserve the last non-empty prefix for that URI. * * @param ns namespace definition * @param dfltns flag for default namespace used in binding * @param nsmap map from namespace URIs to prefixes * @return flag for default namespace used in binding */ private boolean mapNamespace(NamespaceElement ns, boolean dfltns, Map nsmap) { String prefix = ns.getPrefix(); if (prefix == null) { prefix = ""; } String prior = (String)nsmap.get(ns.getUri()); if (prior != null) { if (prefix.length() == 0) { return dfltns; } else if (prior.length() == 0) { dfltns = false; } } nsmap.put(ns.getUri(), prefix); return dfltns || prefix.length() == 0; } private static String toJavaName(String name, Set nameset) { StringBuffer buff = new StringBuffer(name.length()); for (int i = 0; i < name.length(); i++) { char chr = name.charAt(i); if ((i == 0 && Character.isJavaIdentifierStart(chr)) || (i > 0 && Character.isJavaIdentifierPart(chr))) { buff.append(chr); } else if (chr == ':' || chr == '.') { buff.append('$'); } else { buff.append('_'); } } int count = 0; String jname = buff.toString(); while (!nameset.add(jname)) { jname = buff.toString() + count++; } return jname; } private String mapMessage(AxisMessage msg, Map complexTypeMap) { QName qname = msg.getElementQName(); if (qname == null) { throw new RuntimeException("No element reference in message " + msg.getName()); } return mapQName(qname, complexTypeMap); } private String mapMessage(SOAPHeaderMessage msg, Map complexTypeMap) { QName qname = msg.getElement(); if (qname == null) { throw new RuntimeException("No element reference in header"); } return mapQName(qname, complexTypeMap); } private String mapQName(QName qname, Map complexTypeMap) throws RuntimeException { Object obj = complexTypeMap.get(qname); if (obj == null) { throw new RuntimeException("No mapping defined for element " + qname); } MappingElementBase mapping = (MappingElementBase)obj; String cname = mapping.getClassName(); codeGenConfig.getTypeMapper().addTypeMappingName(qname, cname); return cname; } /** * Collect mapping from qnames to classes for top level mappings in JiBX binding. * * @param binding * @param dns default namespace to be used unless overridden (empty string if none) * @param elementMap map from element names to concrete mapping components of binding * @param complexTypeMap map from type names to abstract mapping components of binding * @param simpleTypeMap map from type names to format definition components of binding * @param bindingMap map from mapping components to containing binding definition */ private static void collectTopLevelComponents(BindingElement binding, String dns, Map elementMap, Map complexTypeMap, Map simpleTypeMap, Map bindingMap) { // check default namespace set at top level of binding String defaultns = findDefaultNS(binding.topChildIterator(), dns); // add all top level mapping and format definitions to maps for (Iterator iter = binding.topChildIterator(); iter.hasNext();) { ElementBase child = (ElementBase)iter.next(); if (child.type() == ElementBase.INCLUDE_ELEMENT) { // recurse to process included binding definitions IncludeElement include = (IncludeElement)child; if(include.getBinding() != null) { collectTopLevelComponents(include.getBinding(), defaultns, elementMap, complexTypeMap, simpleTypeMap, bindingMap); } } else if (child.type() == ElementBase.FORMAT_ELEMENT) { // register named formats as simple type conversions FormatElement format = (FormatElement)child; registerElement(format.getQName(), format, simpleTypeMap); bindingMap.put(format, binding); } else if (child.type() == ElementBase.MAPPING_ELEMENT) { // record only abstract mappings with type names, and mappings with names MappingElementBase mapping = (MappingElementBase)child; bindingMap.put(mapping, binding); if (mapping.isAbstract() && mapping.getTypeQName() != null) { // register named abstract mappings as complex type conversions registerElement(mapping.getTypeQName(), mapping, complexTypeMap); } else if (mapping.getName() != null) { // register concrete mappings as element conversions String uri = mapping.getUri(); if (uri == null) { uri = findDefaultNS(mapping.topChildIterator(), defaultns); } elementMap.put(new QName(uri, mapping.getName()), mapping); } } } } /** * Register binding element by qualified name. This converts the qualified name format used by * the JiBX binding model to that used by Axis2. * * @param qname qualified name in JiBX format (<code>null</code> if none) * @param element corresponding element of binding definition * @param map qualified name to element map */ private static void registerElement(org.jibx.runtime.QName qname, ElementBase element, Map map) { if (qname != null) { map.put(new QName(qname.getUri(), qname.getName()), element); } } /** * Find the default namespace within a list of JiBX binding model elements possibly including * namespace definitions. Once a non-namespace definition element is seen in the list, this just * returns (since the namespace definitions always come first in JiBX's binding format). * * @param iter iterator for elements in list * @param dns default namespace if not overridden * @return default namespace */ private static String findDefaultNS(Iterator iter, String dns) { while (iter.hasNext()) { ElementBase child = (ElementBase)iter.next(); if (child.type() == ElementBase.NAMESPACE_ELEMENT) { NamespaceElement namespace = (NamespaceElement)child; String defaultName = namespace.getDefaultName(); if ("elements".equals(defaultName) || "all".equals(defaultName)) { return namespace.getUri(); } } else { break; } } return dns; } /** * Inner class for handling prevalidation of include elements only. Unlike the normal JiBX * binding definition prevalidation step, this visitor ignores everything except include * elements. */ private class IncludePrevalidationVisitor extends ModelVisitor { private final ValidationContext m_context; private IncludePrevalidationVisitor(ValidationContext vctx) { m_context = vctx; } /* (non-Javadoc) * @see org.jibx.binding.model.ModelVisitor#visit(org.jibx.binding.model.ElementBase) */ public boolean visit(IncludeElement node) { try { // force creation of defintions context for containing binding m_context.getFormatDefinitions(); node.prevalidate(m_context); } catch (Throwable t) { m_context.addFatal("Error during validation: " + t.getMessage()); t.printStackTrace(); return false; } return true; } } private static class NamedParameterTypeMapper extends JavaTypeMapper { /** * Return the real parameter name, not a dummy. * * @param qname * @return local part of name */ public String getParameterName(QName qname) { return qname.getLocalPart(); } } }