1 /* 2 * Copyright (c) 2003,2004, Stefan Haustein, Oberhausen, Rhld., Germany 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 5 * associated documentation files (the "Software"), to deal in the Software without restriction, 6 * including 7 * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell 9 * copies of the Software, and to permit persons to whom the Software is furnished to do so, 10 * subject to the 11 * following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in all copies or 14 * substantial 15 * portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 18 * BUT NOT 19 * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 * NONINFRINGEMENT. IN NO 21 * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 * LIABILITY, WHETHER 23 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 * SOFTWARE OR THE 25 * USE OR OTHER DEALINGS IN THE SOFTWARE. 26 */ 27 28 package org.ksoap2.serialization; 29 30 import org.ksoap2.SoapEnvelope; 31 import org.ksoap2.SoapFault; 32 import org.ksoap2.SoapFault12; 33 import org.xmlpull.v1.XmlPullParser; 34 import org.xmlpull.v1.XmlPullParserException; 35 import org.xmlpull.v1.XmlSerializer; 36 37 import java.io.IOException; 38 import java.util.ArrayList; 39 import java.util.Hashtable; 40 import java.util.Vector; 41 42 /** 43 * @author Stefan Haustein 44 * 45 * This class extends the SoapEnvelope with Soap Serialization functionality. 46 */ 47 public class SoapSerializationEnvelope extends SoapEnvelope { 48 protected static final int QNAME_TYPE = 1; 49 protected static final int QNAME_NAMESPACE = 0; 50 protected static final int QNAME_MARSHAL = 3; 51 protected static final String NULL_LABEL = "null"; 52 protected static final String NIL_LABEL = "nil"; 53 static final Marshal DEFAULT_MARSHAL = new DM(); 54 private static final String ANY_TYPE_LABEL = "anyType"; 55 private static final String ARRAY_MAPPING_NAME = "Array"; 56 private static final String HREF_LABEL = "href"; 57 private static final String ID_LABEL = "id"; 58 private static final String ROOT_LABEL = "root"; 59 private static final String TYPE_LABEL = "type"; 60 private static final String ITEM_LABEL = "item"; 61 private static final String ARRAY_TYPE_LABEL = "arrayType"; 62 public Hashtable properties = new Hashtable(); 63 /** 64 * Set this variable to true if you don't want that type definitions for complex types/objects 65 * are automatically generated (with type "anyType") in the XML-Request, if you don't call the 66 * Method addMapping. This is needed by some Servers which have problems with these 67 * type-definitions. 68 */ 69 public boolean implicitTypes; 70 /** 71 * If set to true then all properties with null value will be skipped from the soap message. 72 * If false then null properties will be sent as <element nil="true" /> 73 */ 74 public boolean skipNullProperties; 75 /** 76 * Set this variable to true for compatibility with what seems to be the default encoding for 77 * .Net-Services. This feature is an extremely ugly hack. A much better option is to change the 78 * configuration of the .Net-Server to standard Soap Serialization! 79 */ 80 81 public boolean dotNet; 82 83 /** 84 * Set this variable to true if you prefer to silently skip unknown properties. 85 * {@link RuntimeException} will be thrown otherwise. 86 */ 87 public boolean avoidExceptionForUnknownProperty; 88 89 /** 90 * Map from XML qualified names to Java classes 91 */ 92 93 protected Hashtable qNameToClass = new Hashtable(); 94 95 /** 96 * Map from Java class names to XML name and namespace pairs 97 */ 98 99 protected Hashtable classToQName = new Hashtable(); 100 101 /** 102 * Set to true to add and ID and ROOT label to the envelope. Change to false for 103 * compatibility with WSDL. 104 */ 105 protected boolean addAdornments = true; 106 Hashtable idMap = new Hashtable(); 107 Vector multiRef; // = new Vector(); 108 109 public SoapSerializationEnvelope(int version) { 110 super(version); 111 addMapping(enc, ARRAY_MAPPING_NAME, PropertyInfo.VECTOR_CLASS); 112 DEFAULT_MARSHAL.register(this); 113 } 114 115 /** 116 * @return the addAdornments 117 */ 118 public boolean isAddAdornments() { 119 return addAdornments; 120 } 121 122 /** 123 * @param addAdornments the addAdornments to set 124 */ 125 public void setAddAdornments(boolean addAdornments) { 126 this.addAdornments = addAdornments; 127 } 128 129 /** 130 * Set the bodyOut to be empty so that no un-needed xml is create. The null value for bodyOut 131 * will 132 * cause #writeBody to skip writing anything redundant. 133 * 134 * @see "http://code.google.com/p/ksoap2-android/issues/detail?id=77" 135 */ 136 public void setBodyOutEmpty(boolean emptyBody) { 137 if (emptyBody) { 138 bodyOut = null; 139 } 140 } 141 142 public void parseBody(XmlPullParser parser) throws IOException, XmlPullParserException { 143 bodyIn = null; 144 parser.nextTag(); 145 if (parser.getEventType() == XmlPullParser.START_TAG && parser.getNamespace().equals(env) 146 && parser.getName().equals("Fault")) { 147 SoapFault fault; 148 if (this.version < SoapEnvelope.VER12) { 149 fault = new SoapFault(this.version); 150 } else { 151 fault = new SoapFault12(this.version); 152 } 153 fault.parse(parser); 154 bodyIn = fault; 155 } else { 156 while (parser.getEventType() == XmlPullParser.START_TAG) { 157 String rootAttr = parser.getAttributeValue(enc, ROOT_LABEL); 158 159 Object o = read(parser, null, -1, parser.getNamespace(), parser.getName(), 160 PropertyInfo.OBJECT_TYPE); 161 if ("1".equals(rootAttr) || bodyIn == null) { 162 bodyIn = o; 163 } 164 parser.nextTag(); 165 } 166 } 167 } 168 169 /** 170 * Read a SoapObject. This extracts any attributes and then reads the object as a 171 * KvmSerializable. 172 */ 173 protected void readSerializable(XmlPullParser parser, SoapObject obj) throws IOException, 174 XmlPullParserException { 175 for (int counter = 0; counter < parser.getAttributeCount(); counter++) { 176 String attributeName = parser.getAttributeName(counter); 177 String value = parser.getAttributeValue(counter); 178 ((SoapObject) obj).addAttribute(attributeName, value); 179 } 180 readSerializable(parser, (KvmSerializable) obj); 181 } 182 183 /** 184 * Read a KvmSerializable. 185 */ 186 protected void readSerializable(XmlPullParser parser, KvmSerializable obj) throws IOException, 187 XmlPullParserException { 188 int tag = 0; 189 try { 190 tag = parser.nextTag(); 191 } catch (XmlPullParserException e) { 192 if (obj instanceof HasInnerText) { 193 ((HasInnerText) obj).setInnerText( 194 (parser.getText() != null) ? parser.getText() : ""); 195 } 196 tag = parser.nextTag(); 197 } 198 while (tag != XmlPullParser.END_TAG) { 199 String name = parser.getName(); 200 if (!implicitTypes || !(obj instanceof SoapObject)) { 201 PropertyInfo info = new PropertyInfo(); 202 int propertyCount = obj.getPropertyCount(); 203 boolean propertyFound = false; 204 205 for (int i = 0; i < propertyCount && !propertyFound; i++) { 206 info.clear(); 207 obj.getPropertyInfo(i, properties, info); 208 209 if ((name.equals(info.name) && info.namespace == null) || 210 (name.equals(info.name) && parser.getNamespace().equals( 211 info.namespace))) { 212 propertyFound = true; 213 obj.setProperty(i, read(parser, obj, i, null, null, info)); 214 } 215 } 216 217 if (!propertyFound) { 218 if (avoidExceptionForUnknownProperty) { 219 // Dummy loop to read until corresponding END tag 220 while (parser.next() != XmlPullParser.END_TAG || !name.equals( 221 parser.getName())) { 222 } 223 ; 224 } else { 225 throw new RuntimeException("Unknown Property: " + name); 226 } 227 } else { 228 if (obj instanceof HasAttributes) { 229 HasAttributes soapObject = (HasAttributes) obj; 230 int cnt = parser.getAttributeCount(); 231 for (int counter = 0; counter < cnt; counter++) { 232 AttributeInfo attributeInfo = new AttributeInfo(); 233 attributeInfo.setName(parser.getAttributeName(counter)); 234 attributeInfo.setValue(parser.getAttributeValue(counter)); 235 attributeInfo.setNamespace(parser.getAttributeNamespace(counter)); 236 attributeInfo.setType(parser.getAttributeType(counter)); 237 soapObject.setAttribute(attributeInfo); 238 239 } 240 } 241 } 242 } else { 243 // I can only make this work for SoapObjects - hence the check above 244 // I don't understand namespaces well enough to know whether it is correct in the 245 // next line... 246 ((SoapObject) obj).addProperty(parser.getName(), 247 read(parser, obj, obj.getPropertyCount(), 248 ((SoapObject) obj).getNamespace(), name, PropertyInfo.OBJECT_TYPE)); 249 } 250 try { 251 tag = parser.nextTag(); 252 } catch (XmlPullParserException e) { 253 if (obj instanceof HasInnerText) { 254 ((HasInnerText) obj).setInnerText( 255 (parser.getText() != null) ? parser.getText() : ""); 256 } 257 tag = parser.nextTag(); 258 } 259 260 } 261 parser.require(XmlPullParser.END_TAG, null, null); 262 } 263 264 /** 265 * If the type of the object cannot be determined, and thus no Marshal class can handle the 266 * object, this 267 * method is called. It will build either a SoapPrimitive or a SoapObject 268 * 269 * @return unknownObject wrapped as a SoapPrimitive or SoapObject 270 */ 271 272 protected Object readUnknown(XmlPullParser parser, String typeNamespace, String typeName) 273 throws IOException, XmlPullParserException { 274 String name = parser.getName(); 275 String namespace = parser.getNamespace(); 276 277 // cache the attribute info list from the current element before we move on 278 Vector attributeInfoVector = new Vector(); 279 for (int attributeCount = 0; attributeCount < parser.getAttributeCount(); 280 attributeCount++) { 281 AttributeInfo attributeInfo = new AttributeInfo(); 282 attributeInfo.setName(parser.getAttributeName(attributeCount)); 283 attributeInfo.setValue(parser.getAttributeValue(attributeCount)); 284 attributeInfo.setNamespace(parser.getAttributeNamespace(attributeCount)); 285 attributeInfo.setType(parser.getAttributeType(attributeCount)); 286 attributeInfoVector.addElement(attributeInfo); 287 } 288 289 parser.next(); // move to text, inner start tag or end tag 290 Object result = null; 291 String text = null; 292 if (parser.getEventType() == XmlPullParser.TEXT) { 293 text = parser.getText(); 294 SoapPrimitive sp = new SoapPrimitive(typeNamespace, typeName, text); 295 result = sp; 296 // apply all the cached attribute info list before we add the property and descend 297 // further for parsing 298 for (int i = 0; i < attributeInfoVector.size(); i++) { 299 sp.addAttribute((AttributeInfo) attributeInfoVector.elementAt(i)); 300 } 301 parser.next(); 302 } else if (parser.getEventType() == XmlPullParser.END_TAG) { 303 SoapObject so = new SoapObject(typeNamespace, typeName); 304 // apply all the cached attribute info list before we add the property and descend 305 // further for parsing 306 for (int i = 0; i < attributeInfoVector.size(); i++) { 307 so.addAttribute((AttributeInfo) attributeInfoVector.elementAt(i)); 308 } 309 result = so; 310 } 311 312 if (parser.getEventType() == XmlPullParser.START_TAG) { 313 if (text != null && text.trim().length() != 0) { 314 throw new RuntimeException("Malformed input: Mixed content"); 315 } 316 SoapObject so = new SoapObject(typeNamespace, typeName); 317 // apply all the cached attribute info list before we add the property and descend 318 // further for parsing 319 for (int i = 0; i < attributeInfoVector.size(); i++) { 320 so.addAttribute((AttributeInfo) attributeInfoVector.elementAt(i)); 321 } 322 323 while (parser.getEventType() != XmlPullParser.END_TAG) { 324 so.addProperty(parser.getNamespace(), parser.getName(), 325 read(parser, so, so.getPropertyCount(), 326 null, null, PropertyInfo.OBJECT_TYPE)); 327 parser.nextTag(); 328 } 329 result = so; 330 } 331 parser.require(XmlPullParser.END_TAG, namespace, name); 332 return result; 333 } 334 335 private int getIndex(String value, int start, int dflt) { 336 if (value == null) { 337 return dflt; 338 } 339 try { 340 return value.length() - start < 3 ? dflt : Integer.parseInt(value.substring(start + 1, 341 value.length() - 1)); 342 } catch (Exception ex) { 343 return dflt; 344 } 345 } 346 347 protected void readVector(XmlPullParser parser, Vector v, PropertyInfo elementType) 348 throws IOException, 349 XmlPullParserException { 350 String namespace = null; 351 String name = null; 352 int size = v.size(); 353 boolean dynamic = true; 354 String type = parser.getAttributeValue(enc, ARRAY_TYPE_LABEL); 355 if (type != null) { 356 int cut0 = type.indexOf(':'); 357 int cut1 = type.indexOf("[", cut0); 358 name = type.substring(cut0 + 1, cut1); 359 String prefix = cut0 == -1 ? "" : type.substring(0, cut0); 360 namespace = parser.getNamespace(prefix); 361 size = getIndex(type, cut1, -1); 362 if (size != -1) { 363 v.setSize(size); 364 dynamic = false; 365 } 366 } 367 if (elementType == null) { 368 elementType = PropertyInfo.OBJECT_TYPE; 369 } 370 parser.nextTag(); 371 int position = getIndex(parser.getAttributeValue(enc, "offset"), 0, 0); 372 while (parser.getEventType() != XmlPullParser.END_TAG) { 373 // handle position 374 position = getIndex(parser.getAttributeValue(enc, "position"), 0, position); 375 if (dynamic && position >= size) { 376 size = position + 1; 377 v.setSize(size); 378 } 379 // implicit handling of position exceeding specified size 380 v.setElementAt(read(parser, v, position, namespace, name, elementType), position); 381 position++; 382 parser.nextTag(); 383 } 384 parser.require(XmlPullParser.END_TAG, null, null); 385 } 386 387 /** 388 * This method returns id from the href attribute value. 389 * By default we assume that href value looks like this: #id so we basically have to remove 390 * the first character. 391 * But in theory there could be a different value format, like cid:value, etc... 392 */ 393 protected String getIdFromHref(String hrefValue) { 394 return hrefValue.substring(1); 395 } 396 397 /** 398 * Builds an object from the XML stream. This method is public for usage in conjuction with 399 * Marshal 400 * subclasses. Precondition: On the start tag of the object or property, so href can be read. 401 */ 402 403 public Object read(XmlPullParser parser, Object owner, int index, String namespace, String name, 404 PropertyInfo expected) 405 throws IOException, XmlPullParserException { 406 String elementName = parser.getName(); 407 String href = parser.getAttributeValue(null, HREF_LABEL); 408 Object obj; 409 if (href != null) { 410 if (owner == null) { 411 throw new RuntimeException("href at root level?!?"); 412 } 413 href = getIdFromHref(href); 414 obj = idMap.get(href); 415 if (obj == null || obj instanceof FwdRef) { 416 FwdRef f = new FwdRef(); 417 f.next = (FwdRef) obj; 418 f.obj = owner; 419 f.index = index; 420 idMap.put(href, f); 421 obj = null; 422 } 423 parser.nextTag(); // start tag 424 parser.require(XmlPullParser.END_TAG, null, elementName); 425 } else { 426 String nullAttr = parser.getAttributeValue(xsi, NIL_LABEL); 427 String id = parser.getAttributeValue(null, ID_LABEL); 428 if (nullAttr == null) { 429 nullAttr = parser.getAttributeValue(xsi, NULL_LABEL); 430 } 431 if (nullAttr != null && SoapEnvelope.stringToBoolean(nullAttr)) { 432 obj = null; 433 parser.nextTag(); 434 parser.require(XmlPullParser.END_TAG, null, elementName); 435 } else { 436 String type = parser.getAttributeValue(xsi, TYPE_LABEL); 437 if (type != null) { 438 int cut = type.indexOf(':'); 439 name = type.substring(cut + 1); 440 String prefix = cut == -1 ? "" : type.substring(0, cut); 441 namespace = parser.getNamespace(prefix); 442 } else if (name == null && namespace == null) { 443 if (parser.getAttributeValue(enc, ARRAY_TYPE_LABEL) != null) { 444 namespace = enc; 445 name = ARRAY_MAPPING_NAME; 446 } else { 447 Object[] names = getInfo(expected.type, null); 448 namespace = (String) names[0]; 449 name = (String) names[1]; 450 } 451 } 452 // be sure to set this flag if we don't know the types. 453 if (type == null) { 454 implicitTypes = true; 455 } 456 obj = readInstance(parser, namespace, name, expected); 457 if (obj == null) { 458 obj = readUnknown(parser, namespace, name); 459 } 460 } 461 // finally, care about the id.... 462 if (id != null) { 463 resolveReference(id, obj); 464 465 } 466 } 467 468 parser.require(XmlPullParser.END_TAG, null, elementName); 469 return obj; 470 } 471 472 protected void resolveReference(String id, Object obj) { 473 Object hlp = idMap.get(id); 474 if (hlp instanceof FwdRef) { 475 FwdRef f = (FwdRef) hlp; 476 do { 477 if (f.obj instanceof KvmSerializable) { 478 ((KvmSerializable) f.obj).setProperty(f.index, obj); 479 } else { 480 ((Vector) f.obj).setElementAt(obj, f.index); 481 } 482 f = f.next; 483 } 484 while (f != null); 485 } else if (hlp != null) { 486 throw new RuntimeException("double ID"); 487 } 488 idMap.put(id, obj); 489 } 490 491 /** 492 * Returns a new object read from the given parser. If no mapping is found, null is returned. 493 * This method 494 * is used by the SoapParser in order to convert the XML code to Java objects. 495 */ 496 public Object readInstance(XmlPullParser parser, String namespace, String name, 497 PropertyInfo expected) 498 throws IOException, XmlPullParserException { 499 Object obj = qNameToClass.get(new SoapPrimitive(namespace, name, null)); 500 if (obj == null) { 501 return null; 502 } 503 if (obj instanceof Marshal) { 504 return ((Marshal) obj).readInstance(parser, namespace, name, expected); 505 } else if (obj instanceof SoapObject) { 506 obj = ((SoapObject) obj).newInstance(); 507 } else if (obj == SoapObject.class) { 508 obj = new SoapObject(namespace, name); 509 } else { 510 try { 511 obj = ((Class) obj).newInstance(); 512 } catch (Exception e) { 513 throw new RuntimeException(e.toString()); 514 } 515 } 516 if (obj instanceof HasAttributes) { 517 HasAttributes soapObject = (HasAttributes) obj; 518 int cnt = parser.getAttributeCount(); 519 for (int counter = 0; counter < cnt; counter++) { 520 521 AttributeInfo attributeInfo = new AttributeInfo(); 522 attributeInfo.setName(parser.getAttributeName(counter)); 523 attributeInfo.setValue(parser.getAttributeValue(counter)); 524 attributeInfo.setNamespace(parser.getAttributeNamespace(counter)); 525 attributeInfo.setType(parser.getAttributeType(counter)); 526 527 soapObject.setAttribute(attributeInfo); 528 529 } 530 } 531 532 // ok, obj is now the instance, fill it.... 533 if (obj instanceof SoapObject) { 534 readSerializable(parser, (SoapObject) obj); 535 } else if (obj instanceof KvmSerializable) { 536 537 if (obj instanceof HasInnerText) { 538 ((HasInnerText) obj).setInnerText( 539 (parser.getText() != null) ? parser.getText() : ""); 540 } 541 readSerializable(parser, (KvmSerializable) obj); 542 } else if (obj instanceof Vector) { 543 readVector(parser, (Vector) obj, expected.elementType); 544 } else { 545 throw new RuntimeException("no deserializer for " + obj.getClass()); 546 } 547 return obj; 548 } 549 550 /** 551 * Returns a string array containing the namespace, name, id and Marshal object for the given 552 * java object. 553 * This method is used by the SoapWriter in order to map Java objects to the corresponding 554 * SOAP section 555 * five XML code. 556 */ 557 public Object[] getInfo(Object type, Object instance) { 558 if (type == null) { 559 if (instance instanceof SoapObject || instance instanceof SoapPrimitive) { 560 type = instance; 561 } else { 562 type = instance.getClass(); 563 } 564 } 565 if (type instanceof SoapObject) { 566 SoapObject so = (SoapObject) type; 567 return new Object[]{so.getNamespace(), so.getName(), null, null}; 568 } 569 if (type instanceof SoapPrimitive) { 570 SoapPrimitive sp = (SoapPrimitive) type; 571 return new Object[]{sp.getNamespace(), sp.getName(), null, DEFAULT_MARSHAL}; 572 } 573 if ((type instanceof Class) && type != PropertyInfo.OBJECT_CLASS) { 574 Object[] tmp = (Object[]) classToQName.get(((Class) type).getName()); 575 if (tmp != null) { 576 return tmp; 577 } 578 } 579 return new Object[]{xsd, ANY_TYPE_LABEL, null, null}; 580 } 581 582 /** 583 * Defines a direct mapping from a namespace and name to a java class (and vice versa), using 584 * the given 585 * marshal mechanism 586 */ 587 public void addMapping(String namespace, String name, Class clazz, Marshal marshal) { 588 qNameToClass 589 .put(new SoapPrimitive(namespace, name, null), 590 marshal == null ? (Object) clazz : marshal); 591 classToQName.put(clazz.getName(), new Object[]{namespace, name, null, marshal}); 592 } 593 594 /** 595 * Defines a direct mapping from a namespace and name to a java class (and vice versa) 596 */ 597 public void addMapping(String namespace, String name, Class clazz) { 598 addMapping(namespace, name, clazz, null); 599 } 600 601 /** 602 * Adds a SoapObject to the class map. During parsing, objects of the given type 603 * (namespace/name) will be 604 * mapped to corresponding copies of the given SoapObject, maintaining the structure of the 605 * template. 606 */ 607 public void addTemplate(SoapObject so) { 608 qNameToClass.put(new SoapPrimitive(so.namespace, so.name, null), so); 609 } 610 611 /** 612 * Response from the soap call. Pulls the object from the wrapper object and returns it. 613 * 614 * @return response from the soap call. 615 * @since 2.0.3 616 */ 617 public Object getResponse() throws SoapFault { 618 if (bodyIn == null) { 619 return null; 620 } 621 if (bodyIn instanceof SoapFault) { 622 throw (SoapFault) bodyIn; 623 } 624 KvmSerializable ks = (KvmSerializable) bodyIn; 625 626 if (ks.getPropertyCount() == 0) { 627 return null; 628 } else if (ks.getPropertyCount() == 1) { 629 return ks.getProperty(0); 630 } else { 631 Vector ret = new Vector(); 632 for (int i = 0; i < ks.getPropertyCount(); i++) { 633 ret.add(ks.getProperty(i)); 634 } 635 return ret; 636 } 637 } 638 639 /** 640 * Serializes the request object to the given XmlSerliazer object 641 * 642 * @param writer XmlSerializer object to write the body into. 643 */ 644 public void writeBody(XmlSerializer writer) throws IOException { 645 // allow an empty body without any tags in it 646 // see http://code.google.com/p/ksoap2-android/issues/detail?id=77 647 if (bodyOut != null) { 648 multiRef = new Vector(); 649 multiRef.addElement(bodyOut); 650 Object[] qName = getInfo(null, bodyOut); 651 652 writer.startTag((dotNet) ? "" : (String) qName[QNAME_NAMESPACE], 653 (String) qName[QNAME_TYPE]); 654 655 if (dotNet) { 656 writer.attribute(null, "xmlns", (String) qName[QNAME_NAMESPACE]); 657 } 658 659 if (addAdornments) { 660 writer.attribute(null, ID_LABEL, qName[2] == null ? ("o" + 0) : (String) qName[2]); 661 writer.attribute(enc, ROOT_LABEL, "1"); 662 } 663 writeElement(writer, bodyOut, null, qName[QNAME_MARSHAL]); 664 writer.endTag((dotNet) ? "" : (String) qName[QNAME_NAMESPACE], 665 (String) qName[QNAME_TYPE]); 666 } 667 } 668 669 private void writeAttributes(XmlSerializer writer, HasAttributes obj) throws IOException { 670 HasAttributes soapObject = (HasAttributes) obj; 671 int cnt = soapObject.getAttributeCount(); 672 for (int counter = 0; counter < cnt; counter++) { 673 AttributeInfo attributeInfo = new AttributeInfo(); 674 soapObject.getAttributeInfo(counter, attributeInfo); 675 soapObject.getAttribute(counter, attributeInfo); 676 if (attributeInfo.getValue() != null) { 677 writer.attribute(attributeInfo.getNamespace(), attributeInfo.getName(), 678 attributeInfo.getValue().toString()); 679 } 680 } 681 } 682 683 public void writeArrayListBodyWithAttributes(XmlSerializer writer, KvmSerializable obj) 684 throws IOException { 685 if (obj instanceof HasAttributes) { 686 writeAttributes(writer, (HasAttributes) obj); 687 } 688 writeArrayListBody(writer, (ArrayList) obj); 689 } 690 691 public void writeObjectBodyWithAttributes(XmlSerializer writer, KvmSerializable obj) 692 throws IOException { 693 if (obj instanceof HasAttributes) { 694 writeAttributes(writer, (HasAttributes) obj); 695 } 696 writeObjectBody(writer, obj); 697 } 698 699 /** 700 * Writes the body of an KvmSerializable object. This method is public for access from 701 * Marshal subclasses. 702 */ 703 public void writeObjectBody(XmlSerializer writer, KvmSerializable obj) throws IOException { 704 int cnt = obj.getPropertyCount(); 705 PropertyInfo propertyInfo = new PropertyInfo(); 706 String namespace; 707 String name; 708 String type; 709 for (int i = 0; i < cnt; i++) { 710 // get the property 711 Object prop = obj.getProperty(i); 712 // and importantly also get the property info which holds the name potentially! 713 obj.getPropertyInfo(i, properties, propertyInfo); 714 715 if (!(prop instanceof SoapObject)) { 716 // prop is a PropertyInfo 717 if ((propertyInfo.flags & PropertyInfo.TRANSIENT) == 0) { 718 Object objValue = obj.getProperty(i); 719 if ((prop != null || !skipNullProperties) && (objValue 720 != SoapPrimitive.NullSkip)) { 721 writer.startTag(propertyInfo.namespace, propertyInfo.name); 722 writeProperty(writer, objValue, propertyInfo); 723 writer.endTag(propertyInfo.namespace, propertyInfo.name); 724 } 725 } 726 } else { 727 // prop is a SoapObject 728 SoapObject nestedSoap = (SoapObject) prop; 729 // lets get the info from the soap object itself 730 Object[] qName = getInfo(null, nestedSoap); 731 namespace = (String) qName[QNAME_NAMESPACE]; 732 type = (String) qName[QNAME_TYPE]; 733 734 // prefer the name from the property info 735 if (propertyInfo.name != null && propertyInfo.name.length() > 0) { 736 name = propertyInfo.name; 737 } else { 738 name = (String) qName[QNAME_TYPE]; 739 } 740 741 // prefer the namespace from the property info 742 if (propertyInfo.namespace != null && propertyInfo.namespace.length() > 0) { 743 namespace = propertyInfo.namespace; 744 } else { 745 namespace = (String) qName[QNAME_NAMESPACE]; 746 } 747 748 writer.startTag(namespace, name); 749 if (!implicitTypes) { 750 String prefix = writer.getPrefix(namespace, true); 751 writer.attribute(xsi, TYPE_LABEL, prefix + ":" + type); 752 } 753 writeObjectBodyWithAttributes(writer, nestedSoap); 754 writer.endTag(namespace, name); 755 } 756 } 757 writeInnerText(writer, obj); 758 759 } 760 761 private void writeInnerText(XmlSerializer writer, KvmSerializable obj) throws IOException { 762 if (obj instanceof HasInnerText) { 763 764 Object value = ((HasInnerText) obj).getInnerText(); 765 if (value != null) { 766 if (value instanceof ValueWriter) { 767 ((ValueWriter) value).write(writer); 768 } else { 769 writer.cdsect(value.toString()); 770 } 771 772 } 773 } 774 } 775 776 protected void writeProperty(XmlSerializer writer, Object obj, PropertyInfo type) 777 throws IOException { 778 if (obj == null || obj == SoapPrimitive.NullNilElement) { 779 writer.attribute(xsi, version >= VER12 ? NIL_LABEL : NULL_LABEL, "true"); 780 return; 781 } 782 Object[] qName = getInfo(null, obj); 783 if (type.multiRef || qName[2] != null) { 784 int i = multiRef.indexOf(obj); 785 if (i == -1) { 786 i = multiRef.size(); 787 multiRef.addElement(obj); 788 } 789 writer.attribute(null, HREF_LABEL, qName[2] == null ? ("#o" + i) : "#" + qName[2]); 790 } else { 791 if (!implicitTypes || obj.getClass() != type.type) { 792 String prefix = writer.getPrefix((String) qName[QNAME_NAMESPACE], true); 793 writer.attribute(xsi, TYPE_LABEL, prefix + ":" + qName[QNAME_TYPE]); 794 } 795 writeElement(writer, obj, type, qName[QNAME_MARSHAL]); 796 } 797 } 798 799 protected void writeElement(XmlSerializer writer, Object element, PropertyInfo type, 800 Object marshal) 801 throws IOException { 802 if (marshal != null) { 803 ((Marshal) marshal).writeInstance(writer, element); 804 } else if (element instanceof KvmSerializable || element == SoapPrimitive.NullNilElement 805 || element == SoapPrimitive.NullSkip) { 806 if (element instanceof ArrayList) { 807 writeArrayListBodyWithAttributes(writer, (KvmSerializable) element); 808 } else { 809 writeObjectBodyWithAttributes(writer, (KvmSerializable) element); 810 } 811 } else if (element instanceof HasAttributes) { 812 writeAttributes(writer, (HasAttributes) element); 813 } else if (element instanceof Vector) { 814 writeVectorBody(writer, (Vector) element, type.elementType); 815 } else { 816 throw new RuntimeException("Cannot serialize: " + element); 817 } 818 } 819 820 protected void writeArrayListBody(XmlSerializer writer, ArrayList list) 821 throws IOException { 822 KvmSerializable obj = (KvmSerializable) list; 823 int cnt = list.size(); 824 PropertyInfo propertyInfo = new PropertyInfo(); 825 String namespace; 826 String name; 827 String type; 828 for (int i = 0; i < cnt; i++) { 829 // get the property 830 Object prop = obj.getProperty(i); 831 // and importantly also get the property info which holds the name potentially! 832 obj.getPropertyInfo(i, properties, propertyInfo); 833 834 if (!(prop instanceof SoapObject)) { 835 // prop is a PropertyInfo 836 if ((propertyInfo.flags & PropertyInfo.TRANSIENT) == 0) { 837 Object objValue = obj.getProperty(i); 838 if ((prop != null || !skipNullProperties) && (objValue 839 != SoapPrimitive.NullSkip)) { 840 writer.startTag(propertyInfo.namespace, propertyInfo.name); 841 writeProperty(writer, objValue, propertyInfo); 842 writer.endTag(propertyInfo.namespace, propertyInfo.name); 843 } 844 } 845 } else { 846 847 // prop is a SoapObject 848 SoapObject nestedSoap = (SoapObject) prop; 849 // lets get the info from the soap object itself 850 Object[] qName = getInfo(null, nestedSoap); 851 namespace = (String) qName[QNAME_NAMESPACE]; 852 type = (String) qName[QNAME_TYPE]; 853 854 // prefer the name from the property info 855 if (propertyInfo.name != null && propertyInfo.name.length() > 0) { 856 name = propertyInfo.name; 857 } else { 858 name = (String) qName[QNAME_TYPE]; 859 } 860 861 // prefer the namespace from the property info 862 if (propertyInfo.namespace != null && propertyInfo.namespace.length() > 0) { 863 namespace = propertyInfo.namespace; 864 } else { 865 namespace = (String) qName[QNAME_NAMESPACE]; 866 } 867 868 writer.startTag(namespace, name); 869 if (!implicitTypes) { 870 String prefix = writer.getPrefix(namespace, true); 871 writer.attribute(xsi, TYPE_LABEL, prefix + ":" + type); 872 } 873 writeObjectBodyWithAttributes(writer, nestedSoap); 874 writer.endTag(namespace, name); 875 } 876 } 877 writeInnerText(writer, obj); 878 } 879 880 protected void writeVectorBody(XmlSerializer writer, Vector vector, PropertyInfo elementType) 881 throws IOException { 882 String itemsTagName = ITEM_LABEL; 883 String itemsNamespace = null; 884 885 if (elementType == null) { 886 elementType = PropertyInfo.OBJECT_TYPE; 887 } else if (elementType instanceof PropertyInfo) { 888 if (elementType.name != null) { 889 itemsTagName = elementType.name; 890 itemsNamespace = elementType.namespace; 891 } 892 } 893 894 int cnt = vector.size(); 895 Object[] arrType = getInfo(elementType.type, null); 896 897 // This removes the arrayType attribute from the xml for arrays(required for most .Net 898 // services to work) 899 if (!implicitTypes) { 900 writer.attribute(enc, ARRAY_TYPE_LABEL, 901 writer.getPrefix((String) arrType[0], false) + ":" 902 + arrType[1] + "[" + cnt + "]"); 903 } else { 904 // Get the namespace from mappings if available when arrayType is removed for .Net 905 if (itemsNamespace == null) { 906 itemsNamespace = (String) arrType[0]; 907 } 908 } 909 910 boolean skipped = false; 911 for (int i = 0; i < cnt; i++) { 912 if (vector.elementAt(i) == null) { 913 skipped = true; 914 } else { 915 writer.startTag(itemsNamespace, itemsTagName); 916 if (skipped) { 917 writer.attribute(enc, "position", "[" + i + "]"); 918 skipped = false; 919 } 920 writeProperty(writer, vector.elementAt(i), elementType); 921 writer.endTag(itemsNamespace, itemsTagName); 922 } 923 } 924 } 925 } 926