/* * Copyright (c) 2007, Dennis M. Sosnoski All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the * following conditions are met: * * Redistributions of source code must retain the above copyright notice, this list of conditions and the following * disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of * JiBX nor the names of its contributors may be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.jibx.ws.wsdl; import java.util.ArrayList; import java.util.List; import java.util.Set; import org.jibx.binding.generator.CustomBase; import org.jibx.binding.generator.SharedNestingBase; import org.jibx.binding.model.DocumentFormatter; import org.jibx.binding.model.IClass; import org.jibx.binding.model.IClassItem; import org.jibx.binding.model.IClassLocator; import org.jibx.binding.util.StringArray; import org.jibx.custom.CustomUtils; import org.jibx.runtime.IUnmarshallingContext; import org.jibx.runtime.JiBXException; import org.jibx.runtime.impl.UnmarshallingContext; /** * Operation customization information. This supports direct operation customizations (such as the corresponding request * and/or response element name) and also acts as a container for parameter and/or return customizations. * * @author Dennis M. Sosnoski */ public class OperationCustom extends NestingBase { /** Enumeration of allowed attribute names */ public static final StringArray s_allowedAttributes = new StringArray(new String[] { "service-base", "set-actions", "use-nillable", "wrapped" }, SharedNestingBase.s_allowedAttributes); // values specific to class level private String m_methodName; private String m_operationName; private String m_requestMessageName; private String m_requestWrapperName; private String m_responseMessageName; private String m_responseWrapperName; private String m_soapAction; private List m_documentation; private String[] m_requireds; private String[] m_optionals; // list of contained parameter customizations private final ArrayList m_parameters; // contained result customization (null if none) private ValueCustom m_return; // list of contained throws customizations private final ArrayList m_throws; /** * Constructor. * * @param parent * @param name method name */ OperationCustom(NestingBase parent, String name) { super(parent); m_methodName = name; m_parameters = new ArrayList(); m_throws = new ArrayList(); } /** * Get the namespace for WSDL definitions of this service. * * @return WSDL namespace */ public String getWsdlNamespace() { return ((NestingBase)getParent()).getWsdlNamespace(); } /** * Get method name. * * @return name */ public String getMethodName() { return m_methodName; } /** * Get the operation name. * * @return operation name */ public String getOperationName() { return m_operationName; } /** * Get request message name. * * @return name */ public String getRequestMessageName() { return m_requestMessageName; } /** * Get request wrapper element name. * * @return name */ public String getRequestWrapperName() { return m_requestWrapperName; } /** * Get response message name. * * @return name */ public String getResponseMessageName() { return m_responseMessageName; } /** * Get response wrapper name. * * @return name */ public String getResponseWrapperName() { return m_responseWrapperName; } /** * Get return value. * * @return return */ public ValueCustom getReturn() { return m_return; } /** * Get SOAPAction. * * @return soapAction */ public String getSoapAction() { return m_soapAction; } /** * Get operation documentation. * * @return list of documentation nodes (null if none) */ public List getDocumentation() { return m_documentation; } /** * Get list of children. * * @return list */ public ArrayList getParameters() { return m_parameters; } /** * Get list of throws customizations. * * @return list */ public ArrayList getThrows() { return m_throws; } /** * Add child. * * @param child */ protected void addChild(CustomBase child) { if (child.getParent() == this) { m_parameters.add(child); } else { throw new IllegalStateException("Internal error: child not linked"); } } /** * Unmarshalling factory. This gets the containing element and the name so that the standard constructor can be * used. * * @param ictx * @return created instance * @throws JiBXException */ private static OperationCustom factory(IUnmarshallingContext ictx) throws JiBXException { return new OperationCustom((NestingBase)getContainingObject(ictx), ((UnmarshallingContext)ictx).attributeText( null, "method-name")); } /** * Check if type is a collection type (specifically collection, not array). * * @param type * @return item type, null if not a collection type */ private boolean isCollection(String type, IClassLocator icl) { IClass info = icl.getClassInfo(type); return info.isImplements("Ljava/util/Collection;"); } /** * Parse parameter type. * * @param parse * @return parameter type */ private String parameterType(SignatureParser parse) { String itype = null; while (parse.next() != SignatureParser.TYPE_PARAMETERS_END_EVENT) { if (itype == null && parse.getEvent() == SignatureParser.TYPE_EVENT) { itype = parse.getType(); } } return itype; } /** * Build value representation. The value may be either a simple value or a collection value. * * @param name * @param itype item type (null if not a collection) * @return value */ private ValueCustom buildValue(String name, String itype) { if (itype == null) { ValueCustom parm = new ValueCustom(this, name); return parm; } else { CollectionValueCustom parm = new CollectionValueCustom(this, name); return parm; } } /** * Check if a particular value is required or optional. * * @param name * @param reqset * @param optset * @return TRUE if required, FALSE if optional, null if unknown */ private static Boolean checkRequired(String name, Set reqset, Set optset) { if (reqset != null && reqset.contains(name)) { return Boolean.TRUE; } else if (optset != null && optset.contains(name)) { return Boolean.FALSE; } else { return null; } } /** * Apply customizations to method to fill out parameter and return information. * * @param method * @param icl * @param fmt */ public void apply(IClassItem method, IClassLocator icl, DocumentFormatter fmt) { // fill in missing details if (m_operationName == null) { String name = convertName(m_methodName, CAMEL_CASE_NAMES); m_operationName = registerName(name, this); } else if (!m_operationName.equals(registerName(m_operationName, this))) { throw new IllegalStateException("Operation name conflict for '" + m_operationName + '\''); } if (m_requestMessageName == null) { m_requestMessageName = m_operationName + "Message"; } if (m_requestWrapperName == null) { m_requestWrapperName = m_operationName; } if (m_responseMessageName == null) { m_responseMessageName = m_operationName + "ResponseMessage"; } if (m_responseWrapperName == null) { m_responseWrapperName = m_operationName + "Response"; } if (m_soapAction == null && isSoapAction()) { m_soapAction = "urn:" + m_operationName; } if (m_documentation == null) { m_documentation = fmt.docToNodes(method.getJavaDoc()); } // find parameter types, and item types for collections int count = method.getArgumentCount(); String[] ptypes = new String[count]; String[] pitypes = new String[count]; String rtype = null; String ritype = null; String sig = method.getGenericsSignature(); if (sig == null) { // no signature, just use basic type information for (int i = 0; i < count; i++) { String type = method.getArgumentType(i); ptypes[i] = type; if (isCollection(type, icl)) { pitypes[i] = "java.lang.Object"; } } rtype = method.getTypeName(); if (isCollection(rtype, icl)) { ritype = "java.lang.Object"; } } else { // parse the signature to check collection item types SignatureParser parse = new SignatureParser(sig); int index = 0; boolean inparms = false; while (parse.next() != SignatureParser.END_EVENT) { switch (parse.getEvent()) { case SignatureParser.METHOD_PARAMETERS_START_EVENT: inparms = true; index = 0; break; case SignatureParser.METHOD_PARAMETERS_END_EVENT: inparms = false; break; case SignatureParser.TYPE_EVENT: String type = parse.getType(); String itype = null; if (parse.isParameterized()) { String ptype = parameterType(parse); IClass info = icl.getClassInfo(type); if (info.isImplements("Ljava/util/Collection;")) { itype = ptype; } } if (inparms) { ptypes[index] = type; pitypes[index++] = itype; } else { rtype = type; ritype = itype; } break; } } } // fill in the parameters and return customizations Set reqset = CustomUtils.noCaseNameSet(m_requireds); Set optset = CustomUtils.noCaseNameSet(m_optionals); if (m_parameters.size() == 0) { for (int i = 0; i < count; i++) { String name = method.getParameterName(i); if (name == null) { name = "arg" + (i + 1); } m_parameters.add(buildValue(name, pitypes[i])); } } else { if (m_parameters.size() != count) { // TODO: merge generated with supplied parameter information? throw new IllegalStateException("Wrong parameter count"); } } for (int i = 0; i < count; i++) { ValueCustom param = (ValueCustom)m_parameters.get(i); String name = param.getValueName(); param.setElementName(convertName(name, CAMEL_CASE_NAMES)); String ptype = ptypes[i]; String pitype = pitypes[i]; Boolean req = checkRequired(name, reqset, optset); List docs = fmt.docToNodes(method.getParameterJavaDoc(i)); if (param instanceof CollectionValueCustom) { ((CollectionValueCustom)param).complete(ptype, pitype, docs, req); } else { param.complete(ptype, docs, req); } } Boolean req = checkRequired("return", reqset, optset); String text = method.getReturnJavaDoc(); boolean isname = false; if (text != null && Character.isJavaIdentifierStart(text.charAt(0))) { isname = true; for (int i = 1; i < text.length(); i++) { if (!Character.isJavaIdentifierPart(text.charAt(i))) { isname = false; break; } } } String name = "return"; if (isname) { name = text; text = null; } List docs = fmt.docToNodes(text); if (m_return == null) { m_return = buildValue(name, ritype); } if (m_return instanceof CollectionValueCustom) { ((CollectionValueCustom)m_return).complete(rtype, ritype, docs, req); } else { m_return.complete(rtype, docs, req); } // add throws information count = method.getExceptions().length; if (m_throws.size() == 0) { for (int i = 0; i < count; i++) { name = method.getExceptions()[i]; ThrowsCustom thrw = new ThrowsCustom(this, name); thrw.complete(fmt.docToNodes(method.getExceptionJavaDoc(i))); m_throws.add(thrw); } } } }