/** * * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see <http://www.gnu.org/licenses/>. * **/ package lucee.runtime.net.rpc.client; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Vector; import javax.xml.namespace.QName; import lucee.commons.lang.Pair; import lucee.commons.lang.StringUtil; import lucee.runtime.exp.ApplicationException; import lucee.runtime.text.xml.ArrayNodeList; import lucee.runtime.text.xml.XMLUtil; import org.apache.axis.Constants; import org.apache.axis.wsdl.symbolTable.BaseType; import org.apache.axis.wsdl.symbolTable.DefinedType; import org.apache.axis.wsdl.symbolTable.ElementDecl; import org.apache.axis.wsdl.symbolTable.SymbolTable; import org.apache.axis.wsdl.symbolTable.Type; import org.w3c.dom.Attr; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; public class SOAPUtil { private static QName[] SOAP=new QName[]{ Constants.SOAP_ARRAY ,Constants.SOAP_ARRAY12 ,Constants.SOAP_ARRAY_ATTRS11 ,Constants.SOAP_ARRAY_ATTRS12 ,Constants.SOAP_BASE64 ,Constants.SOAP_BASE64BINARY ,Constants.SOAP_BOOLEAN ,Constants.SOAP_BYTE ,Constants.SOAP_COMMON_ATTRS11 ,Constants.SOAP_COMMON_ATTRS12 ,Constants.SOAP_DECIMAL ,Constants.SOAP_DOCUMENT ,Constants.SOAP_DOUBLE ,Constants.SOAP_ELEMENT ,Constants.SOAP_FLOAT ,Constants.SOAP_INT ,Constants.SOAP_INTEGER ,Constants.SOAP_LONG ,Constants.SOAP_MAP ,Constants.SOAP_SHORT ,Constants.SOAP_STRING ,Constants.SOAP_VECTOR }; private static QName[] XSD=new QName[]{ Constants.XSD_ANY ,Constants.XSD_ANYSIMPLETYPE ,Constants.XSD_ANYTYPE ,Constants.XSD_ANYURI ,Constants.XSD_BASE64 ,Constants.XSD_BOOLEAN ,Constants.XSD_BYTE ,Constants.XSD_DATE ,Constants.XSD_DATETIME ,Constants.XSD_DAY ,Constants.XSD_DECIMAL ,Constants.XSD_DOUBLE ,Constants.XSD_DURATION ,Constants.XSD_ENTITIES ,Constants.XSD_ENTITY ,Constants.XSD_FLOAT ,Constants.XSD_HEXBIN ,Constants.XSD_ID ,Constants.XSD_IDREF ,Constants.XSD_IDREFS ,Constants.XSD_INT ,Constants.XSD_INTEGER ,Constants.XSD_LANGUAGE ,Constants.XSD_LONG ,Constants.XSD_MONTH ,Constants.XSD_MONTHDAY ,Constants.XSD_NAME ,Constants.XSD_NCNAME ,Constants.XSD_NEGATIVEINTEGER ,Constants.XSD_NMTOKEN ,Constants.XSD_NMTOKENS ,Constants.XSD_NONNEGATIVEINTEGER ,Constants.XSD_NONPOSITIVEINTEGER ,Constants.XSD_NORMALIZEDSTRING ,Constants.XSD_NOTATION ,Constants.XSD_POSITIVEINTEGER ,Constants.XSD_QNAME ,Constants.XSD_SCHEMA ,Constants.XSD_SHORT ,Constants.XSD_STRING ,Constants.XSD_TIME ,Constants.XSD_TIMEINSTANT1999 ,Constants.XSD_TIMEINSTANT2000 ,Constants.XSD_TOKEN ,Constants.XSD_UNSIGNEDBYTE ,Constants.XSD_UNSIGNEDINT ,Constants.XSD_UNSIGNEDLONG ,Constants.XSD_UNSIGNEDSHORT ,Constants.XSD_YEAR ,Constants.XSD_YEARMONTH }; public static Vector getTypes(Element body, SymbolTable st ) throws ApplicationException { // get the data List<TempType> hrefs=new ArrayList<SOAPUtil.TempType>(); Map<String,TempType> ids=new HashMap<String,SOAPUtil.TempType>(); ArrayList<TempType> res = new ArrayList<SOAPUtil.TempType>(); toTempTypes(XMLUtil.getChildNodes(body, Node.ELEMENT_NODE).iterator(),res,hrefs,ids,res); // replace href with real data Iterator<TempType> it = hrefs.iterator(); TempType href,id; while(it.hasNext()){ href = it.next(); id=ids.get(href.href); if(StringUtil.isEmpty(id)) throw new ApplicationException("cannot handle href "+href.href); href.href=null; href.id=id.id; href.prefix=id.prefix; href.namespace=id.namespace; href.type=id.type; href.children=id.children; id.replicated=true; } // removes replicated types in root it = res.iterator(); TempType t; while(it.hasNext()){ t=it.next(); if(t.replicated)it.remove(); } // now convert to types return toTypes(res,false); } private static Vector toTypes(List<TempType> res,boolean contained) { Iterator<TempType> it = res.iterator(); Vector types=new Vector(); Type t; TempType tt; Object o; while(it.hasNext()){ tt = it.next(); o=t=toType(tt); if(contained)o=new ElementDecl(t,new QName(tt.name)); types.add(o); } return types; } private static Type toType(TempType tt) { Type t=toBaseType(tt.prefix, tt.type); if(t==null) t=toDefinedType(tt); return t; } private static DefinedType toDefinedType(TempType tt) { if(tt.isArray) { tt.isArray=false; DefinedType ref = toDefinedType(tt); String type=ref.getQName().getLocalPart(); if(type.startsWith("ArrayOf")) type="ArrayOf"+type; else type="ArrayOf:"+type; QName qn = StringUtil.isEmpty(tt.namespace)?new QName(type):new QName(tt.namespace,type); DefinedType dt=new DefinedType(qn, tt.parent); dt.setRefType(ref); } QName qn = StringUtil.isEmpty(tt.namespace)?new QName(tt.type):new QName(tt.namespace,tt.type); DefinedType dt=new DefinedType(qn, tt.parent); dt.setBaseType(false); // children if(tt.children!=null && tt.children.size()>0) { dt.setContainedElements(toTypes(tt.children,true)); } return dt; } private static void toTempTypes(Iterator<? extends Node> it,List<TempType> children,List<TempType> hrefs,Map<String,TempType> ids,List<TempType> root) { Element e; TempType t; while(it.hasNext()){ e=(Element)it.next(); if(StringUtil.isEmpty(e.getAttribute("xsi:type")) && StringUtil.isEmpty(e.getAttribute("soapenc:type")) && StringUtil.isEmpty(e.getAttribute("href"))) continue; t=toTempType(e,hrefs,ids,root); children.add(t); } } private static TempType toTempType(Element e,List<TempType> hrefs,Map<String,TempType> ids,List<TempType> root) { String name=e.getLocalName(); Pair<String, String> arrayType=null; // type and namespace int index; Pair<String, String> type = parseType(e.getAttribute("xsi:type")); // optional values String id=e.getAttribute("id"); String href=e.getAttribute("href"); String array=e.getAttribute("soapenc:arrayType"); // is Array if(!StringUtil.isEmpty(type.getValue()) && !StringUtil.isEmpty(array)) { arrayType=type; array=array.trim(); array=array.substring(0,array.indexOf('[')); type = parseType(array); } // namespace String namespace=e.getAttribute("xmlns:"+type.getName()); if(StringUtil.isEmpty(namespace)) { NamedNodeMap attrs = e.getAttributes(); int len = attrs.getLength(); Attr attr; for(int i=0;i<len;i++) { attr=(Attr)attrs.item(i); if(attr.getName().startsWith("xmlns:")) namespace=attr.getValue(); } } TempType t = new TempType(namespace,name,type.getName(),type.getValue(),id,href,arrayType!=null,e.getParentNode()); ArrayNodeList children = XMLUtil.getChildNodes(e, Node.ELEMENT_NODE); if(children!=null && children.size()>0) { List<TempType> _children = new ArrayList<SOAPUtil.TempType>(); // if no array type, the children are members if(arrayType==null){ toTempTypes(children.iterator(),_children,hrefs,ids,root); t.setChildren(_children); } // no members just values in the array else { toTempTypes(children.iterator(),_children,hrefs,ids,root); // make sure we have every type only once Map<String, TempType> tmp=new HashMap<String, SOAPUtil.TempType>(); Iterator<TempType> it = _children.iterator(); TempType tt; while(it.hasNext()){ tt=it.next(); tmp.put(tt.prefix+":"+tt.type, tt); } it=tmp.values().iterator(); while(it.hasNext()){ tt=it.next(); root.add(tt); } } } if(!StringUtil.isEmpty(href)) hrefs.add(t); if(!StringUtil.isEmpty(id)) ids.put(id,t); return t; } private static Pair<String,String> parseType(String strType) { int index; String ns=null; if(!StringUtil.isEmpty(strType) && (index=strType.indexOf(':'))!=-1) { ns=strType.substring(0,index); strType=strType.substring(index+1); } return new Pair<String, String>(ns, strType); } private static BaseType toBaseType(String ns, String type) { if(StringUtil.isEmpty(ns) || StringUtil.isEmpty(type)) return null; if("xsd".equalsIgnoreCase(ns)) { for(int i=0;i<XSD.length;i++){ if(XSD[i].getLocalPart().equalsIgnoreCase(type.trim())) return new BaseType(XSD[i]); } } if("soap".equalsIgnoreCase(ns) || "soapenc".equalsIgnoreCase(ns)) { for(int i=0;i<SOAP.length;i++){ if(SOAP[i].getLocalPart().equalsIgnoreCase(type.trim())) return new BaseType(SOAP[i]); } } return null; } public static class TempType { public boolean replicated; private String type; private String id; private String prefix; private String namespace; private String name; private String href; private List<TempType> children; private boolean isArray; private Node parent; public TempType(String namespace, String name, String prefix, String type,String id,String href, boolean isArray, Node parent) { if(!StringUtil.isEmpty(href)) { href=href.trim(); if(href.startsWith("#"))href=href.substring(1); } this.namespace=!StringUtil.isEmpty(namespace)?namespace.trim():null; this.id=!StringUtil.isEmpty(id)?id.trim():null; this.type=!StringUtil.isEmpty(type)?type.trim():null; this.prefix=!StringUtil.isEmpty(prefix)?prefix.trim():null; this.href=href; this.name=!StringUtil.isEmpty(name)?name.trim():null; this.isArray=isArray; this.parent=parent; } public void setChildren(List<TempType> children) { this.children=children; } public List<TempType> getChildren() { return children; } @Override public String toString() { StringBuilder sb=new StringBuilder(); if(!StringUtil.isEmpty(name))sb.append("name:").append(name); if(!StringUtil.isEmpty(id))sb.append(";id:").append(id); if(!StringUtil.isEmpty(prefix))sb.append(";prefix:").append(prefix); if(!StringUtil.isEmpty(type))sb.append(";type:").append(type); if(!StringUtil.isEmpty(href))sb.append(";href:").append(href); sb.append(";array?:").append(isArray); if(children!=null && children.size()>0) { sb.append(";children:{\n"); Iterator<TempType> it = children.iterator(); while(it.hasNext()){ sb.append(' ').append(StringUtil.replace(it.next().toString(),"\n","\t\n",false)).append(",\n"); } sb.append("}"); } return sb.toString(); } } }