blob: d8fa2509153593aedda1045f8f17f7ceaa3e8d20 [file] [log] [blame]
/*
* 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.XmlSchemaObjectCollection;
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 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().getCount() != 0) {
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();
XmlSchemaObjectCollection items = sequence.getItems();
boolean first = true;
for (Iterator iter = items.getIterator(); iter.hasNext();) {
// check that child item obeys the unwrapping rules
XmlSchemaParticle item = (XmlSchemaParticle)iter.next();
if (!(item instanceof XmlSchemaElement)) {
throw new RuntimeException("Cannot unwrap element " +
qname + ": only element items allowed in sequence");
}
XmlSchemaElement element = (XmlSchemaElement)item;
QName refname = element.getRefName();
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;
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();
}
}
}