/*! ****************************************************************************** * * Pentaho Data Integration * * Copyright (C) 2002-2013 by Pentaho : http://www.pentaho.com * ******************************************************************************* * * Licensed 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.pentaho.di.trans.steps.webservices.wsdl; import javax.xml.namespace.QName; import org.w3c.dom.Element; /** * WSDL operation parameter abstraction. */ public final class WsdlOpParameter extends WsdlOpReturnType implements java.io.Serializable { private static final long serialVersionUID = 1L; public static class ParameterMode { private String mode; public static final ParameterMode IN = new ParameterMode( "IN" ); public static final ParameterMode OUT = new ParameterMode( "OUT" ); public static final ParameterMode INOUT = new ParameterMode( "INOUT" ); public static final ParameterMode UNDEFINED = new ParameterMode( "UNDEFINED" ); private ParameterMode( String mode ) { this.mode = mode; } public String toString() { return mode; } } private QName _name; private ParameterMode _mode; private boolean _isHeader; private boolean _elementFormQualified; /** * Create operation parameters whose types do not need to be unwrapped. Typically used for RPC style parameters but * also may be used for DOCUMENT style when parameter style is BARE. * * @param name * QName of the parameter. * @param xmlType * XML type of the parameter. * @param schemaTypeElement * The type element from the schema for this parameter, will be null if xmlType is a built-in schema type. * @param wsdlTypes * Wsdl types abstraction. */ WsdlOpParameter( String name, QName xmlType, Element schemaTypeElement, WsdlTypes wsdlTypes ) { setName( name, wsdlTypes ); _xmlType = xmlType; _itemXmlType = getArrayItemType( schemaTypeElement, wsdlTypes ); _isArray = _itemXmlType != null; _isHeader = false; _mode = ParameterMode.UNDEFINED; } /** * Create a new WsdlOpParameter for a simple schema type. For pararmeters of simple type, the name of the parameter * corresponds to the name of the message part which defines it. * * @param name * Name of the attribute, if a namespace ref is included it will be resolved. * @param e * The schema element which defines the XML type of this attribute. * @param wsdlTypes * Wsdl types abstraction. */ WsdlOpParameter( String name, Element e, WsdlTypes wsdlTypes ) { this( e, wsdlTypes ); setName( name, wsdlTypes ); } /** * Create a new WsdlOpParameter for a complex type. * * @param e * The schema element which defines the XML type of this attribute. * @param wsdlTypes * Wsdl types abstraction. */ WsdlOpParameter( Element e, WsdlTypes wsdlTypes ) { _mode = ParameterMode.UNDEFINED; _isArray = isArray( e ); _isHeader = false; if ( e.hasAttribute( WsdlUtils.NAME_ATTR ) && e.hasAttribute( WsdlUtils.ELEMENT_TYPE_ATTR ) ) { setName( e.getAttribute( WsdlUtils.NAME_ATTR ), wsdlTypes ); _xmlType = wsdlTypes.getTypeQName( e.getAttribute( WsdlUtils.ELEMENT_TYPE_ATTR ) ); } else if ( e.hasAttribute( WsdlUtils.ELEMENT_REF_ATTR ) ) { _xmlType = wsdlTypes.getTypeQName( e.getAttribute( WsdlUtils.ELEMENT_REF_ATTR ) ); _name = new QName( "", _xmlType.getLocalPart() ); } else if ( e.hasAttribute( WsdlUtils.NAME_ATTR ) ) { setName( e.getAttribute( WsdlUtils.NAME_ATTR ), wsdlTypes ); _xmlType = getElementType( e, wsdlTypes ); } else { throw new RuntimeException( "invalid element: " + e.getNodeName() ); } // check to see if the xml type of this element is an array type Element t = wsdlTypes.findNamedType( _xmlType ); if ( t != null && WsdlUtils.COMPLEX_TYPE_NAME.equals( t.getLocalName() ) ) { _itemXmlType = getArrayItemType( t, wsdlTypes ); _isArray = _itemXmlType != null; if ( _itemXmlType != null ) { _itemComplexType = wsdlTypes.getNamedComplexTypes().getComplexType( _itemXmlType.getLocalPart() ); } } } /** * Get the name of this parameter. * * @return QName. */ public QName getName() { return _name; } /** * Get the mode of this parameter. * * @return ParameterMode */ public ParameterMode getMode() { return _mode; } /** * Is this paramter's name element form qualified? * * @return True if element form qualified. */ public boolean isNameElementFormQualified() { return _elementFormQualified; } /** * Is this parameter a SOAP header parameter? * * @return true if it is. */ public boolean isHeader() { return _isHeader; } /** * Mark this parameter as a SOAP header parameter. */ protected void setHeader() { _isHeader = true; } /** * Set the mode of this parameter (IN/OUT/INOUT). * * @param mode * the mode to set. */ protected void setMode( ParameterMode mode ) { _mode = mode; } /** * Set the name of this parameter. * * @param name * parameter name. * @param wsdlTypes * Wsdl types abstraction. */ protected void setName( String name, WsdlTypes wsdlTypes ) { _name = wsdlTypes.getTypeQName( name ); _elementFormQualified = wsdlTypes.isElementFormQualified( _name.getNamespaceURI() ); } /** * Does this element represent an array type? * * @param e * Element to check. * @return true if this element represents an array type. */ private boolean isArray( Element e ) { if ( e.hasAttribute( WsdlUtils.MAXOCCURS_ATTR ) && !"1".equals( e.getAttribute( WsdlUtils.MAXOCCURS_ATTR ) ) ) { return true; } if ( e.hasAttribute( WsdlUtils.MINOCCURS_ATTR ) ) { String minOccurs = e.getAttribute( WsdlUtils.MINOCCURS_ATTR ); try { int i = Integer.parseInt( minOccurs ); if ( i > 1 ) { return true; } } catch ( NumberFormatException nfe ) { // don't fail - just means minOccurs isn't set } } return false; } /** * Get the xml type of an element. * * @param element * Element to determine the xml type of. * @param wsdlTypes * Wsdl types abstraction. * @return QName of the element's xml type, null if type cannot be determined. */ private QName getElementType( Element element, WsdlTypes wsdlTypes ) { /* * Get the type of an element when the element is in the form of: <element name="foo"> <complexType> <sequence> * <element name="bar" ref="s:schema"/> </sequence> </complexType> </element> * * or * * <element name="foo"> <complexType> <sequence> <any/> </sequence> </complexType> </element> * * This code is extremely brittle, this is a construct used by dot net when DataTypes are employed. The code will * need to be enhanced if other samples of this construct arise. */ Element child; if ( ( child = DomUtils.getChildElementByName( element, WsdlUtils.COMPLEX_TYPE_NAME ) ) != null ) { if ( ( child = DomUtils.getChildElementByName( child, WsdlUtils.SEQUENCE_TAG_NAME ) ) != null ) { Element childElement = DomUtils.getChildElementByName( child, WsdlUtils.ELEMENT_NAME ); if ( childElement != null ) { if ( child.hasAttribute( WsdlUtils.ELEMENT_REF_ATTR ) ) { return wsdlTypes.getTypeQName( child.getAttribute( WsdlUtils.ELEMENT_REF_ATTR ) ); } else if ( child.hasAttribute( WsdlUtils.ELEMENT_TYPE_ATTR ) ) { return wsdlTypes.getTypeQName( child.getAttribute( WsdlUtils.ELEMENT_TYPE_ATTR ) ); } } else if ( ( childElement = DomUtils.getChildElementByName( child, WsdlUtils.ANY_TAG_NAME ) ) != null ) { return new QName( childElement.getNamespaceURI(), childElement.getLocalName() ); } } } else { // no children / no type map to 'any' return new QName( "http://www.w3.org/2001/XMLSchema", "any" ); } return new QName( "http://www.w3.org/2001/XMLSchema", "String" ); } /** * This method differs from the isArray(e) method in that it is checking a schema type to see if it is an array type. * In order to be an array type it must be a complex type which includes a sequence of a single element which has it's * minoccurs and/or maxoccures attributes set to values which denote an array. * * @param type * Either a complexType or a simpleType node from the schema. * @return The QName of the array's item type, null if the type is not an array, */ private QName getArrayItemType( Element type, WsdlTypes wsdlTypes ) { if ( type == null || "simpleElement".equals( type.getLocalName() ) ) { return null; } Element sequence = DomUtils.getChildElementByName( type, "sequence" ); if ( sequence != null ) { return getArrayItemTypeFromSequence( sequence, wsdlTypes ); } Element complexContent = DomUtils.getChildElementByName( type, "complexContent" ); if ( complexContent != null ) { return getArrayItemTypeFromComplexContent( complexContent, wsdlTypes ); } return null; } /** * Get an array items xml type from a sequence element. * * @param sequenceElement * Sequence element. * @param wsdlTypes * Wsdl types abstraction. * @return QName QName of the array item xml type. */ private QName getArrayItemTypeFromSequence( Element sequenceElement, WsdlTypes wsdlTypes ) { Element element = DomUtils.getChildElementByName( sequenceElement, "element" ); if ( element == null ) { return null; } if ( !isArray( element ) ) { return null; } return wsdlTypes.getTypeQName( element.getAttribute( "type" ) ); } /** * Get an array items xml type from a complexContent element. * * @param ccElement * Complex content element. * @param wsdlTypes * Wsdl types abstraction. * @return QName QName of the array item xml type. */ private QName getArrayItemTypeFromComplexContent( Element ccElement, WsdlTypes wsdlTypes ) { Element restriction = DomUtils.getChildElementByName( ccElement, "restriction" ); if ( restriction == null ) { return null; } String base = restriction.getAttribute( "base" ); if ( !"soapenc:Array".equals( base ) ) { return null; } Element attribute = DomUtils.getChildElementByName( restriction, "attribute" ); if ( attribute == null ) { return null; } String arrayType = attribute.getAttribute( "wsdl:arrayType" ); if ( arrayType == null ) { return null; } return wsdlTypes.getTypeQName( arrayType ); } /** * Override the equals method. * * @param o * Object to compare to. * @return true if equal */ public boolean equals( Object o ) { if ( o instanceof WsdlOpParameter ) { return _name.equals( ( (WsdlOpParameter) o ).getName() ); } return false; } }