/*! ******************************************************************************
*
* 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 java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import javax.wsdl.Binding;
import javax.wsdl.Operation;
import javax.wsdl.Part;
import org.pentaho.di.core.exception.KettleStepException;
import org.w3c.dom.Element;
/**
* WsdlOpParameterList represents the list of parameters for an operation.
*/
public final class WsdlOpParameterList extends ArrayList<WsdlOpParameter> {
private static final long serialVersionUID = 1L;
private final Operation _operation;
private final WsdlTypes _wsdlTypes;
private final HashSet<String> _headerNames;
private WsdlOperation.SOAPParameterStyle _parameterStyle;
private WsdlOpParameter _returnParam;
private boolean _outOnly = false;
/**
* Constructor.
*
* @param op
* Operation this arg list is for.
* @param binding
* Binding for the operation.
* @param wsdlTypes
* Wsdl types.
*/
protected WsdlOpParameterList( Operation op, Binding binding, WsdlTypes wsdlTypes ) {
_wsdlTypes = wsdlTypes;
_returnParam = null;
_operation = op;
_parameterStyle = WsdlOperation.SOAPParameterStyle.BARE;
_headerNames = WsdlUtils.getSOAPHeaders( binding, op.getName() );
}
/**
* Was there a 'return type' parameter in this list? If so return its XML type.
*
* @return QName of the XML type, null if not present.
*/
protected WsdlOpReturnType getReturnType() {
return _returnParam;
}
/**
* Get the style (WRAPPED or BARE) of the parameters in this list.
*
* @return WsdlOperation.SOAPParamaterStyle enumeration value.
*/
protected WsdlOperation.SOAPParameterStyle getParameterStyle() {
return _parameterStyle;
}
/**
* @return the operation for this parameter list
*/
public Operation getOperation() {
return _operation;
}
/**
* Add a parameter to this list.
*
* @param p
* Message part defining the parameter.
* @param requestPart
* tue if this parameter is part of an reqest message.
* @return true if this collection was modified as a result of this call.
*/
protected boolean add( Part p, boolean requestPart ) throws KettleStepException {
List<WsdlOpParameter> params = getParameter( p, requestPart );
for ( WsdlOpParameter op : params ) {
if ( _headerNames.contains( op.getName().getLocalPart() ) ) {
op.setHeader();
}
if ( requestPart ) {
// just set mode and add
op.setMode( op.getMode() ); // TODO: WTF??
add( op );
} else {
addOutputParameter( op );
}
}
return true;
}
/**
* Generate a WsdlOpParameter from the message part.
*
* @param part
* A list of message part.
* @param requesPart
* true if part from request message.
*/
private List<WsdlOpParameter> getParameter( Part part, boolean requesPart ) throws KettleStepException {
List<WsdlOpParameter> params = new ArrayList<WsdlOpParameter>();
if ( part.getElementName() != null ) {
if ( WsdlUtils.isWrappedParameterStyle( _operation.getName(), !requesPart, part.getName() ) ) {
_parameterStyle = WsdlOperation.SOAPParameterStyle.WRAPPED;
}
params.addAll( resolvePartElement( part ) );
} else {
params.add( new WsdlOpParameter( part.getName(), part.getTypeName(), _wsdlTypes.findNamedType( part
.getTypeName() ), _wsdlTypes ) );
}
return params;
}
/**
* Add an response param to the parameter list. Some rules for determining if the request param is the return value
* for the operation:
* <p/>
* <ol>
* <li>If the operation has 'parameterOrder' set:</li>
* <ol>
* <li>If the response parameter is not in the operation's parameterOrder attribute, then it represents the return
* value of the call. If there is no such part, then the method does not return a value.</li>
* b) If the response parameter is found in the parameterOrder list, add it as an OUT mode parameter.</li>
* </ol>
* <li>If the operation does not have 'parameterOrder' set:</li>
* <ol>
* <li>If there is a single part in the output message that is not also in the input message it is mapped to the
* return type of the method.</li>
* <li>If there is more than one part in the output message that is not in the input message they are all mapped as
* out arguments and the return type of the method is void.</li>
* </ol>
* </ol>
*
* @param responseParam
* Parameter to process.
*/
@SuppressWarnings( "unchecked" )
private void addOutputParameter( WsdlOpParameter responseParam ) {
//
// is this in IN/OUT param ?
//
/*
* for (WsdlOpParameter param : this) { if (param.equals(responseParam)) {
* param.setMode(WsdlOpParameter.ParameterMode.INOUT); return; } }
*/
// If made we it to this far, we're talking about an out mode param
// responseParam.setMode(WsdlOpParameter.ParameterMode.OUT);
List<String> parameterOrder = _operation.getParameterOrdering();
if ( parameterOrder != null ) {
if ( !parameterOrder.contains( responseParam.getName().getLocalPart() ) ) {
// assert _returnParam == null : "Invalid state!!!";
_returnParam = responseParam;
} else {
add( responseParam );
}
} else {
if ( _returnParam == null && !_outOnly ) {
_returnParam = responseParam;
} else if ( _returnParam != null ) {
// move _returnParam into main arg list
add( _returnParam );
_returnParam = null;
_outOnly = true;
add( responseParam );
} else {
add( responseParam );
}
}
}
/**
* Resolve a Part's element attribute value to a concrete XML type.
*
* @param p
* A message part.
* @return A list of parameters resulting from the schema type -- typically the list will only contains a single
* parameter.
*/
private List<WsdlOpParameter> resolvePartElement( Part p ) throws KettleStepException {
List<WsdlOpParameter> resolvedParams = new ArrayList<WsdlOpParameter>();
Element schemaElement = _wsdlTypes.findNamedElement( p.getElementName() );
if ( schemaElement.hasAttribute( WsdlUtils.ELEMENT_TYPE_ATTR ) ) {
// this is a simple type
resolvedParams.add( new WsdlOpParameter( p.getName(), schemaElement, _wsdlTypes ) );
} else {
// this is a complex type
Element complex = DomUtils.getChildElementByName( schemaElement, WsdlUtils.COMPLEX_TYPE_NAME );
Element sequence = DomUtils.getChildElementByName( complex, WsdlUtils.SEQUENCE_TAG_NAME );
// may occasionally find a <complex/> tag map to empty but this may be a bug in WSM
//
if ( sequence == null ) {
return resolvedParams;
}
List<Element> seqElements = DomUtils.getChildElementsByName( sequence, WsdlUtils.ELEMENT_NAME );
for ( Element e : seqElements ) {
WsdlOpParameter op = new WsdlOpParameter( e, _wsdlTypes );
// special case for bare arrays, change the name of the param
// to the name of the complex type.
if ( op.isArray() && _parameterStyle == WsdlOperation.SOAPParameterStyle.BARE ) {
op.setName( schemaElement.getAttribute( WsdlUtils.NAME_ATTR ), _wsdlTypes );
}
resolvedParams.add( op );
}
}
return resolvedParams;
}
public HashSet<String> getHeaderNames() {
return _headerNames;
}
}