1 16 17 18 package org.apache.xmlrpc; 19 20 import java.io.IOException ; 21 import java.io.OutputStream ; 22 import java.io.OutputStreamWriter ; 23 import java.io.UnsupportedEncodingException ; 24 import java.util.Date ; 25 import java.util.Enumeration ; 26 import java.util.Hashtable ; 27 import java.util.Properties ; 28 import java.util.Vector ; 29 30 import org.apache.xmlrpc.util.DateTool; 31 import org.apache.commons.codec.binary.Base64; 32 import org.apache.commons.codec.EncoderException; 33 34 46 class XmlWriter extends OutputStreamWriter 47 { 48 protected static final String PROLOG_START = "<?xml version=\"1.0"; 50 protected static final String PROLOG_END = "\"?>"; 51 protected static final String CLOSING_TAG_START = "</"; 52 protected static final String SINGLE_TAG_END = "/>"; 53 protected static final String LESS_THAN_ENTITY = "<"; 54 protected static final String GREATER_THAN_ENTITY = ">"; 55 protected static final String AMPERSAND_ENTITY = "&"; 56 57 private static final char[] PROLOG = 58 new char[PROLOG_START.length() + PROLOG_END.length()]; 59 static 60 { 61 int len = PROLOG_START.length(); 62 PROLOG_START.getChars(0, len, PROLOG, 0); 63 PROLOG_END.getChars(0, PROLOG_END.length(), PROLOG, len); 64 } 65 66 69 static final String ISO8859_1 = "ISO8859_1"; 70 71 74 static final String UTF8 = "UTF8"; 75 76 79 static final String UTF16 = "UTF-16"; 80 81 protected static final Base64 base64Codec = new Base64(); 82 83 86 protected static TypeDecoder typeDecoder; 87 88 94 private static Properties encodings = new Properties (); 95 96 static 97 { 98 encodings.put(UTF8, "UTF-8"); 99 encodings.put(ISO8859_1, "ISO-8859-1"); 100 typeDecoder = new DefaultTypeDecoder(); 101 } 102 103 107 private static DateTool dateTool = new DateTool(); 108 109 112 boolean hasWrittenProlog = false; 113 114 126 public XmlWriter(OutputStream out, String enc) 127 throws UnsupportedEncodingException 128 { 129 super(out, forceUnicode(enc)); 131 } 132 133 137 private static String forceUnicode(String encoding) 138 { 139 if (encoding == null || !encoding.toUpperCase().startsWith("UTF")) 140 { 141 encoding = UTF8; 142 } 143 return encoding; 144 } 145 146 156 protected static String canonicalizeEncoding(String javaEncoding) 157 { 158 return encodings.getProperty(javaEncoding, javaEncoding); 159 } 160 161 168 public void write(char[] cbuf, int off, int len) 169 throws IOException 170 { 171 if (!hasWrittenProlog) 172 { 173 super.write(PROLOG, 0, PROLOG.length); 174 hasWrittenProlog = true; 175 } 176 super.write(cbuf, off, len); 177 } 178 179 186 public void write(char c) 187 throws IOException 188 { 189 if (!hasWrittenProlog) 190 { 191 super.write(PROLOG, 0, PROLOG.length); 192 hasWrittenProlog = true; 193 } 194 super.write(c); 195 } 196 197 204 public void write(String str, int off, int len) 205 throws IOException 206 { 207 if (!hasWrittenProlog) 208 { 209 super.write(PROLOG, 0, PROLOG.length); 210 hasWrittenProlog = true; 211 } 212 super.write(str, off, len); 213 } 214 215 225 public void writeObject(Object obj) 226 throws XmlRpcException, IOException 227 { 228 startElement("value"); 229 if (obj == null) 230 { 231 throw new XmlRpcException 232 (0, "null values not supported by XML-RPC"); 233 } 234 else if (obj instanceof String ) 235 { 236 chardata(obj.toString()); 237 } 238 else if (typeDecoder.isXmlRpcI4(obj)) 239 { 240 startElement("int"); 241 write(obj.toString()); 242 endElement("int"); 243 } 244 else if (obj instanceof Boolean ) 245 { 246 startElement("boolean"); 247 write(((Boolean ) obj).booleanValue() ? "1" : "0"); 248 endElement("boolean"); 249 } 250 else if (typeDecoder.isXmlRpcDouble(obj)) 251 { 252 startElement("double"); 253 write(obj.toString()); 254 endElement("double"); 255 } 256 else if (obj instanceof Date ) 257 { 258 startElement("dateTime.iso8601"); 259 Date d = (Date ) obj; 260 write(dateTool.format(d)); 261 endElement("dateTime.iso8601"); 262 } 263 else if (obj instanceof byte[]) 264 { 265 startElement("base64"); 266 try 267 { 268 this.write((byte[]) base64Codec.encode(obj)); 269 } 270 catch (EncoderException e) 271 { 272 throw new XmlRpcException 273 (0, "Unable to Base 64 encode byte array", e); 274 } 275 endElement("base64"); 276 } 277 else if (obj instanceof Object []) 278 { 279 startElement("array"); 280 startElement("data"); 281 Object [] array = (Object []) obj; 282 for (int i = 0; i < array.length; i++) 283 { 284 writeObject(array[i]); 285 } 286 endElement("data"); 287 endElement("array"); 288 } 289 else if (obj instanceof Vector ) 290 { 291 startElement("array"); 292 startElement("data"); 293 Vector array = (Vector ) obj; 294 int size = array.size(); 295 for (int i = 0; i < size; i++) 296 { 297 writeObject(array.elementAt(i)); 298 } 299 endElement("data"); 300 endElement("array"); 301 } 302 else if (obj instanceof Hashtable ) 303 { 304 startElement("struct"); 305 Hashtable struct = (Hashtable ) obj; 306 for (Enumeration e = struct.keys(); e.hasMoreElements(); ) 307 { 308 String key = (String ) e.nextElement(); 309 Object value = struct.get(key); 310 startElement("member"); 311 startElement("name"); 312 chardata(key); 313 endElement("name"); 314 writeObject(value); 315 endElement("member"); 316 } 317 endElement("struct"); 318 } 319 else 320 { 321 throw new XmlRpcException(0, "Unsupported Java type: " 322 + obj.getClass(), null); 323 } 324 endElement("value"); 325 } 326 327 330 protected void write(byte[] byteData) throws IOException 331 { 332 for (int i = 0; i < byteData.length; i++) 333 { 334 write(byteData[i]); 335 } 336 } 337 338 341 private void writeCharacterReference(char c) 342 throws IOException 343 { 344 write("&#"); 345 write(String.valueOf((int) c)); 346 write(';'); 347 } 348 349 354 protected void startElement(String elem) throws IOException 355 { 356 write('<'); 357 write(elem); 358 write('>'); 359 } 360 361 366 protected void endElement(String elem) throws IOException 367 { 368 write(CLOSING_TAG_START); 369 write(elem); 370 write('>'); 371 } 372 373 378 protected void emptyElement(String elem) throws IOException 379 { 380 write('<'); 381 write(elem); 382 write(SINGLE_TAG_END); 383 } 384 385 392 protected void chardata(String text) 393 throws XmlRpcException, IOException 394 { 395 int l = text.length (); 396 for (int i = 0; i < l; i++) 400 { 401 char c = text.charAt (i); 402 switch (c) 403 { 404 case '\t': 405 case '\n': 406 write(c); 407 break; 408 case '\r': 409 writeCharacterReference(c); 411 break; 412 case '<': 413 write(LESS_THAN_ENTITY); 414 break; 415 case '>': 416 write(GREATER_THAN_ENTITY); 417 break; 418 case '&': 419 write(AMPERSAND_ENTITY); 420 break; 421 default: 422 if (c > 0x7f || !isValidXMLChar(c)) 431 { 432 writeCharacterReference(c); 434 } 435 else 436 { 437 write(c); 438 } 439 } 440 } 441 } 442 443 456 private static final boolean isValidXMLChar(char c) 457 { 458 switch (c) 459 { 460 case 0x9: 461 case 0xa: case 0xd: return true; 464 465 default: 466 return ( (0x20 < c && c <= 0xd7ff) || 467 (0xe000 < c && c <= 0xfffd) || 468 (0x10000 < c && c <= 0x10ffff) ); 469 } 470 } 471 472 protected static void setTypeDecoder(TypeDecoder newTypeDecoder) 473 { 474 typeDecoder = newTypeDecoder; 475 } 476 } 477 | Popular Tags |