Coverage Report - net.wotonomy.web.xml.XMLRPCDecoderHelper
 
Classes in this File Line Coverage Branch Coverage Complexity
XMLRPCDecoderHelper
0% 
0% 
2.742
 
 1  
 /*
 2  
 Wotonomy: OpenStep design patterns for pure Java applications.
 3  
 Copyright (C) 2000 Michael Powers
 4  
 
 5  
 This library is free software; you can redistribute it and/or
 6  
 modify it under the terms of the GNU Lesser General Public
 7  
 License as published by the Free Software Foundation; either
 8  
 version 2.1 of the License, or (at your option) any later version.
 9  
 
 10  
 This library is distributed in the hope that it will be useful,
 11  
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 13  
 Lesser General Public License for more details.
 14  
 
 15  
 You should have received a copy of the GNU Lesser General Public
 16  
 License along with this library; if not, see http://www.gnu.org
 17  
 */
 18  
 
 19  
 package net.wotonomy.web.xml;
 20  
 
 21  
 import java.io.IOException;
 22  
 import java.util.HashMap;
 23  
 import java.util.Iterator;
 24  
 import java.util.LinkedList;
 25  
 import java.util.List;
 26  
 import java.util.Map;
 27  
 import java.util.Stack;
 28  
 
 29  
 import net.wotonomy.foundation.internal.Introspector;
 30  
 import net.wotonomy.foundation.internal.ValueConverter;
 31  
 import net.wotonomy.foundation.internal.WotonomyException;
 32  
 
 33  
 import org.xml.sax.Attributes;
 34  
 import org.xml.sax.InputSource;
 35  
 import org.xml.sax.Locator;
 36  
 import org.xml.sax.SAXException;
 37  
 import org.xml.sax.SAXParseException;
 38  
 import org.xml.sax.helpers.DefaultHandler;
 39  
 
 40  
 /**
 41  
 * Used by XMLDecoder to implement the necessary interfaces
 42  
 * required by the jclark xp parser. 
 43  
 * This class is not thread safe.
 44  
 */
 45  
 class XMLRPCDecoderHelper extends DefaultHandler
 46  
 {
 47  0
     protected final Object nilMarker = new Object();
 48  
     protected Stack valueStack;
 49  
     protected String methodName;
 50  
     protected int faultCode;
 51  
     protected String faultString;
 52  
     protected List parameters;
 53  
     protected StringBuffer cdataBuffer;
 54  
 
 55  
 
 56  
 
 57  0
         public XMLRPCDecoderHelper()
 58  0
     {
 59  0
         valueStack = new Stack();
 60  0
         parameters = new LinkedList();
 61  0
         cdataBuffer = new StringBuffer();
 62  0
         reset();
 63  0
     } 
 64  
     
 65  
     public void reset()
 66  
     {
 67  0
         valueStack.clear();
 68  0
         parameters.clear();
 69  0
         cdataBuffer.setLength( 0 );
 70  0
         methodName = null;
 71  0
         faultCode = 0;
 72  0
         faultString = null;
 73  0
     }
 74  
     
 75  
     public boolean isRequest()
 76  
     {
 77  0
         return ( methodName != null );   
 78  
     }
 79  
     
 80  
     public boolean isResponse()
 81  
     {
 82  0
         return ( methodName == null );   
 83  
     }
 84  
     
 85  
     public boolean isFault()
 86  
     {
 87  
         // faults are responses
 88  0
         return ( isResponse() ) && ( faultString != null );   
 89  
     }
 90  
     
 91  
     public int getFaultCode() 
 92  
     {
 93  0
         return faultCode;   
 94  
     }
 95  
     
 96  
     public String getFaultString()
 97  
     {
 98  0
         return faultString;   
 99  
     }
 100  
     
 101  
     public String getMethodName()
 102  
     {
 103  0
         return methodName;   
 104  
     }
 105  
     
 106  
     public Object[] getParameters()
 107  
     {
 108  0
         return parameters.toArray();   
 109  
     }
 110  
     
 111  
     public void endDocument() throws SAXException {
 112  
                 // TODO Auto-generated method stub
 113  0
                 super.endDocument();
 114  0
         }
 115  
 
 116  
         public void startDocument() throws SAXException {
 117  
                 // TODO Auto-generated method stub
 118  0
                 super.startDocument();
 119  0
                 reset();
 120  0
         }
 121  
         
 122  
     public Object getResult() 
 123  
     {
 124  0
         if ( valueStack.empty() ) return null;
 125  0
         Object result = valueStack.peek();
 126  0
         if ( result == nilMarker ) result = null;
 127  0
         return result;
 128  
     }
 129  
 
 130  
     
 131  
         public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
 132  0
                 super.startElement(uri, localName, qName, attributes);
 133  0
         if ( XMLRPCEncoder.VALUE.equals( localName ) )
 134  
         {
 135  0
             ValueMarker marker = new ValueMarker(); 
 136  0
             String classname = attributes.getValue( uri, XMLRPCEncoder.CLASS );
 137  0
             if ( classname != null )
 138  
             {
 139  
                 try
 140  
                 {
 141  0
                     Class c = Class.forName( classname );
 142  0
                     if ( c != null )
 143  
                     {
 144  0
                         marker.setMarkerClass( c );
 145  
                     }
 146  
                 }
 147  0
                 catch ( Exception exc )
 148  
                 {
 149  0
                     System.out.println( "XMLRPCDecoderHelper.startElement: " +
 150  0
                         "Can't find class: " + classname );
 151  0
                 }
 152  
             }
 153  0
             valueStack.push( marker );
 154  
         }
 155  0
         }
 156  
         
 157  
     public void characters(char[] ch, int start, int length) throws SAXException {
 158  0
                 super.characters(ch, start, length);
 159  0
                 char[] someChars = new char[((start+length)<=ch.length) ? length : ch.length-start];
 160  0
                 for (int i = 0; i < someChars.length; i++ ) {
 161  0
                         someChars[i] = ch[start+i];
 162  
                 }
 163  0
                 cdataBuffer.append(someChars);
 164  0
         }
 165  
 
 166  
         public void endElement(String uri, String localName, String qName) throws SAXException {
 167  0
                 super.endElement(uri, localName, qName);
 168  
 
 169  
         // if any cdata is buffered or if string value
 170  0
         if ( ( XMLRPCEncoder.STRING.equals( localName ) ) 
 171  0
         ||   ( cdataBuffer.length() > 0 ) )
 172  
         {
 173  
             // push value on the stack
 174  0
             valueStack.push( cdataBuffer.toString() );
 175  0
             cdataBuffer.setLength( 0 );
 176  
         }
 177  
 
 178  0
         if ( XMLRPCEncoder.VALUE.equals( localName ) )  
 179  
         {        
 180  0
             Object value = valueStack.pop();
 181  
             try
 182  
             {
 183  
 //                ValueMarker marker = (ValueMarker) valueStack.pop();
 184  0
                 ValueMarker marker = null;
 185  0
                 Object markerValue = valueStack.pop();
 186  0
                 if ( markerValue instanceof ValueMarker )
 187  
                 {
 188  0
                     marker = (ValueMarker) markerValue;
 189  0
                 }
 190  
                 else
 191  
                 {
 192  0
                     throw new WotonomyException( "Expected value marker, found" 
 193  0
                         + markerValue.getClass() + " : " + markerValue );
 194  
                 }
 195  
                 
 196  
 //System.out.println( "getMarkerClass: " + marker.getMarkerClass() + " : " + value );                            
 197  
 //System.out.println( valueStack );
 198  0
                 if ( marker.getMarkerClass() != null )
 199  
                 {
 200  
                     // apply introspection
 201  0
                     if ( value instanceof Map )
 202  
                     {
 203  0
                         Map map = (Map)value;
 204  
                         Map.Entry entry;
 205  0
                         value = marker.getMarkerClass().newInstance();
 206  0
                         Iterator it = map.entrySet().iterator();
 207  
                         Object entryValue;
 208  0
                         while( it.hasNext() )
 209  
                         {
 210  0
                             entry = (Map.Entry) it.next();
 211  0
                             entryValue = entry.getValue();
 212  0
                             if ( entryValue == nilMarker ) entryValue = null;
 213  0
                             Introspector.set( 
 214  0
                                 value, entry.getKey().toString(), entryValue );
 215  0
                         }
 216  
                     }
 217  0
                     if ( ! ( value.getClass().equals( marker.getMarkerClass() ) ) )
 218  
                     {
 219  0
                         Object converted = 
 220  0
                             ValueConverter.convertObjectToClass( 
 221  0
                                 value, marker.getMarkerClass() );
 222  0
                         if ( converted != null )
 223  
                         {
 224  0
                             value = converted;
 225  
                         }
 226  
                     }
 227  
                 }
 228  
             }
 229  0
             catch ( Exception exc )
 230  
             {
 231  
                 // fall back on unconverted value
 232  0
             }
 233  
             
 234  0
             valueStack.push( value );
 235  
 // System.out.println( "convertedValue: " + value + "("+ value.getClass() +")" );            
 236  0
         }
 237  
         else
 238  0
         if ( XMLRPCEncoder.MEMBER.equals( localName ) )  // Map.Entry
 239  
         {
 240  
             // leave key and value to be handled by struct
 241  0
         }
 242  
         else
 243  0
         if ( XMLRPCEncoder.STRUCT.equals( localName ) ) // write Entries to map or object
 244  
         {
 245  
             // write values to array (reverse the order)  
 246  
             Object value;      
 247  0
             Map map = new HashMap();
 248  0
             while ( ( ! valueStack.empty() ) 
 249  0
             && ( ! ( valueStack.peek() instanceof ValueMarker ) ) )
 250  
             {
 251  0
                 value = valueStack.pop();
 252  0
                 map.put( valueStack.pop(), value );
 253  0
             }
 254  
             // push the list on the stack
 255  0
             valueStack.push( map );
 256  0
         }
 257  
         else
 258  0
         if ( XMLRPCEncoder.ARRAY.equals( localName ) ) 
 259  
         {
 260  
 //System.out.println( "ended ARRAY: " + valueStack.size() );
 261  
             // write values to array (reverse the order)        
 262  
             Object value;
 263  0
             LinkedList list = new LinkedList();
 264  0
             while ( ( ! valueStack.empty() ) 
 265  0
             && ( ! ( valueStack.peek() instanceof ValueMarker ) ) )
 266  
             {
 267  0
                 value = valueStack.pop();
 268  0
                 if ( value == nilMarker ) value = null;                
 269  0
                 list.addFirst( value );
 270  0
             }
 271  
             // push the list on the stack
 272  0
             valueStack.push( list );
 273  0
         }
 274  
         else
 275  0
         if ( XMLRPCEncoder.INT.equals( localName ) )  
 276  
         {
 277  0
             Object value = valueStack.pop();
 278  
             try
 279  
             {
 280  0
                 valueStack.push( 
 281  0
                     new Integer( value.toString() ) );
 282  
             }
 283  0
             catch ( NumberFormatException exc )
 284  
             {
 285  0
                 throw new WotonomyException( 
 286  0
                     "Invalid double format: " + value.toString() );
 287  0
             }
 288  0
         }
 289  
         else
 290  0
         if ( XMLRPCEncoder.I4.equals( localName ) )  
 291  
         {
 292  0
             Object value = valueStack.pop();
 293  
             try
 294  
             {
 295  0
                 valueStack.push( 
 296  0
                     new Integer( value.toString() ) );
 297  
             }
 298  0
             catch ( NumberFormatException exc )
 299  
             {
 300  0
                 throw new WotonomyException( 
 301  0
                     "Invalid double format: " + value.toString() );
 302  0
             }
 303  0
         }
 304  
         else
 305  0
         if ( XMLRPCEncoder.NIL.equals( localName ) )  
 306  
         {
 307  0
             valueStack.push( nilMarker );
 308  0
         }
 309  
         else
 310  0
         if ( XMLRPCEncoder.DOUBLE.equals( localName ) )  
 311  
         {
 312  0
             Object value = valueStack.pop();
 313  
             try
 314  
             {
 315  0
                 valueStack.push( 
 316  0
                     new Double( value.toString() ) );
 317  
             }
 318  0
             catch ( NumberFormatException exc )
 319  
             {
 320  0
                 throw new WotonomyException( 
 321  0
                     "Invalid double format: " + value.toString() );
 322  0
             }
 323  0
         }
 324  
         else
 325  0
         if ( XMLRPCEncoder.DATE.equals( localName ) )  
 326  
         {
 327  0
             Object value = valueStack.pop();
 328  
             try
 329  
             {
 330  0
                 valueStack.push( 
 331  0
                     XMLRPCEncoder.DATEFORMAT8601.parseObject( 
 332  0
                         value.toString() ) );
 333  
             }
 334  0
             catch ( Exception exc )
 335  
             {
 336  0
                 throw new WotonomyException( 
 337  0
                     "Invalid date format: " + value );
 338  0
             }
 339  0
         }
 340  
         else
 341  0
         if ( XMLRPCEncoder.BOOLEAN.equals( localName ) )  
 342  
         {
 343  0
             Object value = valueStack.pop();
 344  0
             if ( XMLRPCEncoder.TRUE.equals( value ) )
 345  
             {
 346  0
                 valueStack.push( Boolean.TRUE );
 347  0
             }
 348  
             else
 349  0
             if ( XMLRPCEncoder.FALSE.equals( value ) )
 350  
             {
 351  0
                 valueStack.push( Boolean.FALSE );
 352  0
             }
 353  
             else
 354  
             {
 355  0
                 throw new WotonomyException( 
 356  0
                     "Invalid boolean format: " + value );
 357  
             }
 358  0
         }
 359  
         else
 360  0
         if ( XMLRPCEncoder.BASE64.equals( localName ) )  
 361  
         {
 362  0
             throw new WotonomyException( "Not implemented yet." );
 363  
         }
 364  
         else
 365  0
         if ( XMLRPCEncoder.FAULT.equals( localName ) )  
 366  
         {
 367  0
             Map faultMap = (Map) valueStack.pop();
 368  
             try
 369  
             {
 370  0
                 faultCode = ((Integer)
 371  0
                     faultMap.get( XMLRPCEncoder.FAULTCODE )).intValue();
 372  0
                 faultString = (String) faultMap.get( XMLRPCEncoder.FAULTSTRING );
 373  
             }
 374  0
             catch ( Exception exc )
 375  
             {
 376  0
                 throw new WotonomyException(
 377  0
                     "Invalid fault format: " + faultMap );
 378  0
             }
 379  0
         }
 380  
         else
 381  0
         if ( XMLRPCEncoder.METHODNAME.equals( localName ) )  
 382  
         {
 383  0
             methodName = (String) valueStack.pop();
 384  0
         }
 385  
         else
 386  0
         if ( XMLRPCEncoder.PARAM.equals( localName ) )  
 387  
         {
 388  
             //NOTE: this leaves the parameter on the stack
 389  0
             parameters.add( getResult() );
 390  
         }
 391  0
     }
 392  
     
 393  
 
 394  
 
 395  
         public void endPrefixMapping(String prefix) throws SAXException {
 396  
                 // TODO Auto-generated method stub
 397  0
                 super.endPrefixMapping(prefix);
 398  0
         }
 399  
 
 400  
         public void error(SAXParseException e) throws SAXException {
 401  
                 // TODO Auto-generated method stub
 402  0
                 super.error(e);
 403  0
         }
 404  
 
 405  
         public void fatalError(SAXParseException e) throws SAXException {
 406  
                 // TODO Auto-generated method stub
 407  0
                 super.fatalError(e);
 408  0
         }
 409  
 
 410  
         public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
 411  
                 // TODO Auto-generated method stub
 412  0
                 super.ignorableWhitespace(ch, start, length);
 413  0
         }
 414  
 
 415  
         public void notationDecl(String name, String publicId, String systemId) throws SAXException {
 416  
                 // TODO Auto-generated method stub
 417  0
                 super.notationDecl(name, publicId, systemId);
 418  0
         }
 419  
 
 420  
         public void processingInstruction(String target, String data) throws SAXException {
 421  
                 // TODO Auto-generated method stub
 422  0
                 super.processingInstruction(target, data);
 423  0
         }
 424  
 
 425  
         public InputSource resolveEntity(String publicId, String systemId) throws SAXException {
 426  
                 // NOTE: Sun accepted an incompatible api difference.  The (false) should be tossed by hotspot
 427  
                 try {
 428  
                         if (false) throw new IOException("Fake exception to make it compile in both 1.4 and 1.5");
 429  0
                         return super.resolveEntity(publicId, systemId);                
 430  0
                 } catch (IOException e) {
 431  0
                         throw new SAXException(e.getClass().getName() + " thrown while resolving entity.",e);
 432  
                 }
 433  
         }
 434  
 
 435  
         public void setDocumentLocator(Locator locator) {
 436  
                 // TODO Auto-generated method stub
 437  0
                 super.setDocumentLocator(locator);
 438  0
         }
 439  
 
 440  
         public void skippedEntity(String name) throws SAXException {
 441  
                 // TODO Auto-generated method stub
 442  0
                 super.skippedEntity(name);
 443  0
         }
 444  
 
 445  
 
 446  
 
 447  
         public void startPrefixMapping(String prefix, String uri) throws SAXException {
 448  
                 // TODO Auto-generated method stub
 449  0
                 super.startPrefixMapping(prefix, uri);
 450  0
         }
 451  
 
 452  
         public void unparsedEntityDecl(String name, String publicId, String systemId, String notationName) throws SAXException {
 453  
                 // TODO Auto-generated method stub
 454  0
                 super.unparsedEntityDecl(name, publicId, systemId, notationName);
 455  0
         }
 456  
 
 457  
         public void warning(SAXParseException e) throws SAXException {
 458  
                 // TODO Auto-generated method stub
 459  0
                 super.warning(e);
 460  0
         }
 461  
 
 462  
     
 463  
     // marker class
 464  
     
 465  
     private class ValueMarker 
 466  
     {
 467  
         private Class theClass;
 468  
     
 469  0
         public ValueMarker()
 470  0
         {
 471  0
             theClass = null;
 472  0
         }
 473  
     
 474  
         public void setMarkerClass( Class aClass )
 475  
         {
 476  0
             theClass = aClass;
 477  0
         }
 478  
         
 479  
         public Class getMarkerClass()
 480  
         {
 481  0
             return theClass;
 482  
         }
 483  
 
 484  
         public String toString()
 485  
         {
 486  0
             return "[ValueMarker: " + theClass + "]";
 487  
         }
 488  
     }
 489  
     
 490  
 }
 491  
 
 492  
 /*
 493  
  * $Log$
 494  
  * Revision 1.1  2006/02/19 01:44:03  cgruber
 495  
  * Add xmlrpc files
 496  
  * Remove jclark and replace with dom4j and javax.xml.sax stuff
 497  
  * Re-work dependencies and imports so it all compiles.
 498  
  *
 499  
  * Revision 1.1  2006/02/16 13:22:22  cgruber
 500  
  * Check in all sources in eclipse-friendly maven-enabled packages.
 501  
  *
 502  
  * Revision 1.7  2003/08/06 23:07:53  chochos
 503  
  * general code cleanup (mostly, removing unused imports)
 504  
  *
 505  
  * Revision 1.6  2001/03/03 15:16:35  mpowers
 506  
  * Fixed bug in decoding empty strings: string handler did nothing and
 507  
  * empty cdatas were ignored, so no value was placed on the stack.
 508  
  *
 509  
  * Revision 1.5  2001/02/17 16:52:06  mpowers
 510  
  * Changes in imports to support building with jdk1.1 collections.
 511  
  *
 512  
  * Revision 1.4  2001/02/09 15:51:39  mpowers
 513  
  * Fixed a pernicious bug: I was using the character event entirely
 514  
  * incorrectly, but the problem only exhibited itself with large data
 515  
  * and even then only randomly.  Now using a string buffer.
 516  
  *
 517  
  * Revision 1.3  2001/02/07 19:24:28  mpowers
 518  
  * Moved XML classes to separate package.
 519  
  *
 520  
  * Revision 1.2  2001/02/06 14:34:23  mpowers
 521  
  * Forgot to rename the package declarations.
 522  
  *
 523  
  * Revision 1.1  2001/02/06 14:31:19  mpowers
 524  
  * Moving XML utilities from util to xml package.
 525  
  *
 526  
  * Revision 1.1.1.1  2000/12/21 15:52:39  mpowers
 527  
  * Contributing wotonomy.
 528  
  *
 529  
  * Revision 1.2  2000/12/20 16:25:48  michael
 530  
  * Added log to all files.
 531  
  *
 532  
  *
 533  
  */
 534