001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019package lucee.runtime.net.rpc.client; 020 021import java.util.ArrayList; 022import java.util.HashMap; 023import java.util.Iterator; 024import java.util.List; 025import java.util.Map; 026import java.util.Vector; 027 028import javax.xml.namespace.QName; 029 030import lucee.commons.lang.Pair; 031import lucee.commons.lang.StringUtil; 032import lucee.runtime.exp.ApplicationException; 033import lucee.runtime.text.xml.ArrayNodeList; 034import lucee.runtime.text.xml.XMLUtil; 035 036import org.apache.axis.Constants; 037import org.apache.axis.wsdl.symbolTable.BaseType; 038import org.apache.axis.wsdl.symbolTable.DefinedType; 039import org.apache.axis.wsdl.symbolTable.ElementDecl; 040import org.apache.axis.wsdl.symbolTable.SymbolTable; 041import org.apache.axis.wsdl.symbolTable.Type; 042import org.w3c.dom.Attr; 043import org.w3c.dom.Element; 044import org.w3c.dom.NamedNodeMap; 045import org.w3c.dom.Node; 046 047public class SOAPUtil { 048 049 private static QName[] SOAP=new QName[]{ 050 Constants.SOAP_ARRAY 051 ,Constants.SOAP_ARRAY12 052 ,Constants.SOAP_ARRAY_ATTRS11 053 ,Constants.SOAP_ARRAY_ATTRS12 054 ,Constants.SOAP_BASE64 055 ,Constants.SOAP_BASE64BINARY 056 ,Constants.SOAP_BOOLEAN 057 ,Constants.SOAP_BYTE 058 ,Constants.SOAP_COMMON_ATTRS11 059 ,Constants.SOAP_COMMON_ATTRS12 060 ,Constants.SOAP_DECIMAL 061 ,Constants.SOAP_DOCUMENT 062 ,Constants.SOAP_DOUBLE 063 ,Constants.SOAP_ELEMENT 064 ,Constants.SOAP_FLOAT 065 ,Constants.SOAP_INT 066 ,Constants.SOAP_INTEGER 067 ,Constants.SOAP_LONG 068 ,Constants.SOAP_MAP 069 ,Constants.SOAP_SHORT 070 ,Constants.SOAP_STRING 071 ,Constants.SOAP_VECTOR 072 }; 073 074 private static QName[] XSD=new QName[]{ 075 Constants.XSD_ANY 076 ,Constants.XSD_ANYSIMPLETYPE 077 ,Constants.XSD_ANYTYPE 078 ,Constants.XSD_ANYURI 079 ,Constants.XSD_BASE64 080 ,Constants.XSD_BOOLEAN 081 ,Constants.XSD_BYTE 082 ,Constants.XSD_DATE 083 ,Constants.XSD_DATETIME 084 ,Constants.XSD_DAY 085 ,Constants.XSD_DECIMAL 086 ,Constants.XSD_DOUBLE 087 ,Constants.XSD_DURATION 088 ,Constants.XSD_ENTITIES 089 ,Constants.XSD_ENTITY 090 ,Constants.XSD_FLOAT 091 ,Constants.XSD_HEXBIN 092 ,Constants.XSD_ID 093 ,Constants.XSD_IDREF 094 ,Constants.XSD_IDREFS 095 ,Constants.XSD_INT 096 ,Constants.XSD_INTEGER 097 ,Constants.XSD_LANGUAGE 098 ,Constants.XSD_LONG 099 ,Constants.XSD_MONTH 100 ,Constants.XSD_MONTHDAY 101 ,Constants.XSD_NAME 102 ,Constants.XSD_NCNAME 103 ,Constants.XSD_NEGATIVEINTEGER 104 ,Constants.XSD_NMTOKEN 105 ,Constants.XSD_NMTOKENS 106 ,Constants.XSD_NONNEGATIVEINTEGER 107 ,Constants.XSD_NONPOSITIVEINTEGER 108 ,Constants.XSD_NORMALIZEDSTRING 109 ,Constants.XSD_NOTATION 110 ,Constants.XSD_POSITIVEINTEGER 111 ,Constants.XSD_QNAME 112 ,Constants.XSD_SCHEMA 113 ,Constants.XSD_SHORT 114 ,Constants.XSD_STRING 115 ,Constants.XSD_TIME 116 ,Constants.XSD_TIMEINSTANT1999 117 ,Constants.XSD_TIMEINSTANT2000 118 ,Constants.XSD_TOKEN 119 ,Constants.XSD_UNSIGNEDBYTE 120 ,Constants.XSD_UNSIGNEDINT 121 ,Constants.XSD_UNSIGNEDLONG 122 ,Constants.XSD_UNSIGNEDSHORT 123 ,Constants.XSD_YEAR 124 ,Constants.XSD_YEARMONTH 125 }; 126 127 128 public static Vector getTypes(Element body, SymbolTable st ) throws ApplicationException { 129 130 131 // get the data 132 List<TempType> hrefs=new ArrayList<SOAPUtil.TempType>(); 133 Map<String,TempType> ids=new HashMap<String,SOAPUtil.TempType>(); 134 ArrayList<TempType> res = new ArrayList<SOAPUtil.TempType>(); 135 toTempTypes(XMLUtil.getChildNodes(body, Node.ELEMENT_NODE).iterator(),res,hrefs,ids,res); 136 137 // replace href with real data 138 Iterator<TempType> it = hrefs.iterator(); 139 TempType href,id; 140 while(it.hasNext()){ 141 href = it.next(); 142 id=ids.get(href.href); 143 if(StringUtil.isEmpty(id)) throw new ApplicationException("cannot handle href "+href.href); 144 145 href.href=null; 146 href.id=id.id; 147 href.prefix=id.prefix; 148 href.namespace=id.namespace; 149 href.type=id.type; 150 href.children=id.children; 151 id.replicated=true; 152 } 153 154 155 // removes replicated types in root 156 it = res.iterator(); 157 TempType t; 158 while(it.hasNext()){ 159 t=it.next(); 160 if(t.replicated)it.remove(); 161 } 162 163 // now convert to types 164 return toTypes(res,false); 165 } 166 167 private static Vector toTypes(List<TempType> res,boolean contained) { 168 Iterator<TempType> it = res.iterator(); 169 Vector types=new Vector(); 170 Type t; 171 TempType tt; 172 Object o; 173 while(it.hasNext()){ 174 tt = it.next(); 175 o=t=toType(tt); 176 if(contained)o=new ElementDecl(t,new QName(tt.name)); 177 types.add(o); 178 } 179 return types; 180 } 181 182 private static Type toType(TempType tt) { 183 Type t=toBaseType(tt.prefix, tt.type); 184 if(t==null) t=toDefinedType(tt); 185 186 187 return t; 188 } 189 190 private static DefinedType toDefinedType(TempType tt) { 191 if(tt.isArray) { 192 tt.isArray=false; 193 DefinedType ref = toDefinedType(tt); 194 String type=ref.getQName().getLocalPart(); 195 if(type.startsWith("ArrayOf")) type="ArrayOf"+type; 196 else type="ArrayOf:"+type; 197 198 QName qn = StringUtil.isEmpty(tt.namespace)?new QName(type):new QName(tt.namespace,type); 199 DefinedType dt=new DefinedType(qn, tt.parent); 200 dt.setRefType(ref); 201 } 202 203 QName qn = StringUtil.isEmpty(tt.namespace)?new QName(tt.type):new QName(tt.namespace,tt.type); 204 DefinedType dt=new DefinedType(qn, tt.parent); 205 //DefinedType dt=new DefinedType(new QName("http://rpc.xml.coldfusion",tt.type), tt.parent); 206 dt.setBaseType(false); 207 // children 208 if(tt.children!=null && tt.children.size()>0) { 209 dt.setContainedElements(toTypes(tt.children,true)); 210 } 211 212 return dt; 213 } 214 215 private static void toTempTypes(Iterator<? extends Node> it,List<TempType> children,List<TempType> hrefs,Map<String,TempType> ids,List<TempType> root) { 216 Element e; 217 TempType t; 218 while(it.hasNext()){ 219 e=(Element)it.next(); 220 if(StringUtil.isEmpty(e.getAttribute("xsi:type")) && StringUtil.isEmpty(e.getAttribute("soapenc:type")) && StringUtil.isEmpty(e.getAttribute("href"))) continue; 221 t=toTempType(e,hrefs,ids,root); 222 children.add(t); 223 } 224 } 225 226 private static TempType toTempType(Element e,List<TempType> hrefs,Map<String,TempType> ids,List<TempType> root) { 227 String name=e.getLocalName(); 228 Pair<String, String> arrayType=null; 229 // type and namespace 230 int index; 231 Pair<String, String> type = parseType(e.getAttribute("xsi:type")); 232 233 // optional values 234 String id=e.getAttribute("id"); 235 String href=e.getAttribute("href"); 236 String array=e.getAttribute("soapenc:arrayType"); 237 238 // is Array 239 if(!StringUtil.isEmpty(type.getValue()) && !StringUtil.isEmpty(array)) { 240 arrayType=type; 241 array=array.trim(); 242 array=array.substring(0,array.indexOf('[')); 243 type = parseType(array); 244 } 245 246 // namespace 247 String namespace=e.getAttribute("xmlns:"+type.getName()); 248 if(StringUtil.isEmpty(namespace)) { 249 NamedNodeMap attrs = e.getAttributes(); 250 int len = attrs.getLength(); 251 Attr attr; 252 for(int i=0;i<len;i++) { 253 attr=(Attr)attrs.item(i); 254 if(attr.getName().startsWith("xmlns:")) 255 namespace=attr.getValue(); 256 } 257 } 258 259 260 TempType t = new TempType(namespace,name,type.getName(),type.getValue(),id,href,arrayType!=null,e.getParentNode()); 261 ArrayNodeList children = XMLUtil.getChildNodes(e, Node.ELEMENT_NODE); 262 if(children!=null && children.size()>0) { 263 List<TempType> _children = new ArrayList<SOAPUtil.TempType>(); 264 // if no array type, the children are members 265 if(arrayType==null){ 266 toTempTypes(children.iterator(),_children,hrefs,ids,root); 267 t.setChildren(_children); 268 269 } 270 // no members just values in the array 271 else { 272 toTempTypes(children.iterator(),_children,hrefs,ids,root); 273 274 // make sure we have every type only once 275 Map<String, TempType> tmp=new HashMap<String, SOAPUtil.TempType>(); 276 Iterator<TempType> it = _children.iterator(); 277 TempType tt; 278 while(it.hasNext()){ 279 tt=it.next(); 280 tmp.put(tt.prefix+":"+tt.type, tt); 281 } 282 283 it=tmp.values().iterator(); 284 while(it.hasNext()){ 285 tt=it.next(); 286 root.add(tt); 287 } 288 } 289 } 290 291 if(!StringUtil.isEmpty(href)) hrefs.add(t); 292 if(!StringUtil.isEmpty(id)) ids.put(id,t); 293 294 return t; 295 296 } 297 298 private static Pair<String,String> parseType(String strType) { 299 int index; 300 String ns=null; 301 if(!StringUtil.isEmpty(strType) && (index=strType.indexOf(':'))!=-1) { 302 ns=strType.substring(0,index); 303 strType=strType.substring(index+1); 304 } 305 return new Pair<String, String>(ns, strType); 306 } 307 308 private static BaseType toBaseType(String ns, String type) { 309 if(StringUtil.isEmpty(ns) || StringUtil.isEmpty(type)) return null; 310 311 if("xsd".equalsIgnoreCase(ns)) { 312 for(int i=0;i<XSD.length;i++){ 313 if(XSD[i].getLocalPart().equalsIgnoreCase(type.trim())) 314 return new BaseType(XSD[i]); 315 } 316 } 317 if("soap".equalsIgnoreCase(ns) || "soapenc".equalsIgnoreCase(ns)) { 318 for(int i=0;i<SOAP.length;i++){ 319 if(SOAP[i].getLocalPart().equalsIgnoreCase(type.trim())) 320 return new BaseType(SOAP[i]); 321 } 322 } 323 return null; 324 } 325 326 327 328 public static class TempType { 329 330 public boolean replicated; 331 private String type; 332 private String id; 333 private String prefix; 334 private String namespace; 335 private String name; 336 private String href; 337 private List<TempType> children; 338 private boolean isArray; 339 private Node parent; 340 341 public TempType(String namespace, String name, String prefix, String type,String id,String href, boolean isArray, Node parent) { 342 if(!StringUtil.isEmpty(href)) { 343 href=href.trim(); 344 if(href.startsWith("#"))href=href.substring(1); 345 } 346 347 this.namespace=!StringUtil.isEmpty(namespace)?namespace.trim():null; 348 this.id=!StringUtil.isEmpty(id)?id.trim():null; 349 this.type=!StringUtil.isEmpty(type)?type.trim():null; 350 this.prefix=!StringUtil.isEmpty(prefix)?prefix.trim():null; 351 this.href=href; 352 this.name=!StringUtil.isEmpty(name)?name.trim():null; 353 this.isArray=isArray; 354 this.parent=parent; 355 } 356 357 public void setChildren(List<TempType> children) { 358 this.children=children; 359 } 360 public List<TempType> getChildren() { 361 return children; 362 } 363 364 @Override 365 public String toString() { 366 StringBuilder sb=new StringBuilder(); 367 if(!StringUtil.isEmpty(name))sb.append("name:").append(name); 368 if(!StringUtil.isEmpty(id))sb.append(";id:").append(id); 369 if(!StringUtil.isEmpty(prefix))sb.append(";prefix:").append(prefix); 370 if(!StringUtil.isEmpty(type))sb.append(";type:").append(type); 371 if(!StringUtil.isEmpty(href))sb.append(";href:").append(href); 372 sb.append(";array?:").append(isArray); 373 374 if(children!=null && children.size()>0) { 375 sb.append(";children:{\n"); 376 Iterator<TempType> it = children.iterator(); 377 while(it.hasNext()){ 378 sb.append(' ').append(StringUtil.replace(it.next().toString(),"\n","\t\n",false)).append(",\n"); 379 } 380 381 sb.append("}"); 382 } 383 384 385 386 return sb.toString(); 387 } 388 } 389 390}