View Javadoc

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      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  	public XMLRPCDecoderHelper()
58      {
59          valueStack = new Stack();
60          parameters = new LinkedList();
61          cdataBuffer = new StringBuffer();
62          reset();
63      } 
64      
65      public void reset()
66      {
67          valueStack.clear();
68          parameters.clear();
69          cdataBuffer.setLength( 0 );
70          methodName = null;
71          faultCode = 0;
72          faultString = null;
73      }
74      
75      public boolean isRequest()
76      {
77          return ( methodName != null );   
78      }
79      
80      public boolean isResponse()
81      {
82          return ( methodName == null );   
83      }
84      
85      public boolean isFault()
86      {
87          // faults are responses
88          return ( isResponse() ) && ( faultString != null );   
89      }
90      
91      public int getFaultCode() 
92      {
93          return faultCode;   
94      }
95      
96      public String getFaultString()
97      {
98          return faultString;   
99      }
100     
101     public String getMethodName()
102     {
103         return methodName;   
104     }
105     
106     public Object[] getParameters()
107     {
108         return parameters.toArray();   
109     }
110     
111     public void endDocument() throws SAXException {
112 		// TODO Auto-generated method stub
113 		super.endDocument();
114 	}
115 
116 	public void startDocument() throws SAXException {
117 		// TODO Auto-generated method stub
118 		super.startDocument();
119 		reset();
120 	}
121 	
122     public Object getResult() 
123     {
124         if ( valueStack.empty() ) return null;
125         Object result = valueStack.peek();
126         if ( result == nilMarker ) result = null;
127         return result;
128     }
129 
130     
131 	public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
132 		super.startElement(uri, localName, qName, attributes);
133         if ( XMLRPCEncoder.VALUE.equals( localName ) )
134         {
135             ValueMarker marker = new ValueMarker(); 
136             String classname = attributes.getValue( uri, XMLRPCEncoder.CLASS );
137             if ( classname != null )
138             {
139                 try
140                 {
141                     Class c = Class.forName( classname );
142                     if ( c != null )
143                     {
144                         marker.setMarkerClass( c );
145                     }
146                 }
147                 catch ( Exception exc )
148                 {
149                     System.out.println( "XMLRPCDecoderHelper.startElement: " +
150                         "Can't find class: " + classname );
151                 }
152             }
153             valueStack.push( marker );
154         }
155 	}
156 	
157     public void characters(char[] ch, int start, int length) throws SAXException {
158 		super.characters(ch, start, length);
159 		char[] someChars = new char[((start+length)<=ch.length) ? length : ch.length-start];
160 		for (int i = 0; i < someChars.length; i++ ) {
161 			someChars[i] = ch[start+i];
162 		}
163 		cdataBuffer.append(someChars);
164 	}
165 
166 	public void endElement(String uri, String localName, String qName) throws SAXException {
167 		super.endElement(uri, localName, qName);
168 
169         // if any cdata is buffered or if string value
170         if ( ( XMLRPCEncoder.STRING.equals( localName ) ) 
171         ||   ( cdataBuffer.length() > 0 ) )
172         {
173             // push value on the stack
174             valueStack.push( cdataBuffer.toString() );
175             cdataBuffer.setLength( 0 );
176         }
177 
178         if ( XMLRPCEncoder.VALUE.equals( localName ) )  
179         {	
180             Object value = valueStack.pop();
181             try
182             {
183 //                ValueMarker marker = (ValueMarker) valueStack.pop();
184                 ValueMarker marker = null;
185                 Object markerValue = valueStack.pop();
186                 if ( markerValue instanceof ValueMarker )
187                 {
188                     marker = (ValueMarker) markerValue;
189                 }
190                 else
191                 {
192                     throw new WotonomyException( "Expected value marker, found" 
193                         + markerValue.getClass() + " : " + markerValue );
194                 }
195                 
196 //System.out.println( "getMarkerClass: " + marker.getMarkerClass() + " : " + value );                            
197 //System.out.println( valueStack );
198                 if ( marker.getMarkerClass() != null )
199                 {
200                     // apply introspection
201                     if ( value instanceof Map )
202                     {
203                         Map map = (Map)value;
204                         Map.Entry entry;
205                         value = marker.getMarkerClass().newInstance();
206                         Iterator it = map.entrySet().iterator();
207                         Object entryValue;
208                         while( it.hasNext() )
209                         {
210                             entry = (Map.Entry) it.next();
211                             entryValue = entry.getValue();
212                             if ( entryValue == nilMarker ) entryValue = null;
213                             Introspector.set( 
214                                 value, entry.getKey().toString(), entryValue );
215                         }
216                     }
217                     if ( ! ( value.getClass().equals( marker.getMarkerClass() ) ) )
218                     {
219                         Object converted = 
220                             ValueConverter.convertObjectToClass( 
221                                 value, marker.getMarkerClass() );
222                         if ( converted != null )
223                         {
224                             value = converted;
225                         }
226                     }
227                 }
228             }
229             catch ( Exception exc )
230             {
231                 // fall back on unconverted value
232             }
233             
234             valueStack.push( value );
235 // System.out.println( "convertedValue: " + value + "("+ value.getClass() +")" );            
236         }
237         else
238         if ( XMLRPCEncoder.MEMBER.equals( localName ) )  // Map.Entry
239         {
240             // leave key and value to be handled by struct
241         }
242         else
243         if ( XMLRPCEncoder.STRUCT.equals( localName ) ) // write Entries to map or object
244         {
245             // write values to array (reverse the order)  
246             Object value;      
247             Map map = new HashMap();
248             while ( ( ! valueStack.empty() ) 
249             && ( ! ( valueStack.peek() instanceof ValueMarker ) ) )
250             {
251                 value = valueStack.pop();
252                 map.put( valueStack.pop(), value );
253             }
254             // push the list on the stack
255             valueStack.push( map );
256         }
257         else
258         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             LinkedList list = new LinkedList();
264             while ( ( ! valueStack.empty() ) 
265             && ( ! ( valueStack.peek() instanceof ValueMarker ) ) )
266             {
267                 value = valueStack.pop();
268                 if ( value == nilMarker ) value = null;                
269                 list.addFirst( value );
270             }
271             // push the list on the stack
272             valueStack.push( list );
273         }
274         else
275         if ( XMLRPCEncoder.INT.equals( localName ) )  
276         {
277             Object value = valueStack.pop();
278             try
279             {
280                 valueStack.push( 
281                     new Integer( value.toString() ) );
282             }
283             catch ( NumberFormatException exc )
284             {
285                 throw new WotonomyException( 
286                     "Invalid double format: " + value.toString() );
287             }
288         }
289         else
290         if ( XMLRPCEncoder.I4.equals( localName ) )  
291         {
292             Object value = valueStack.pop();
293             try
294             {
295                 valueStack.push( 
296                     new Integer( value.toString() ) );
297             }
298             catch ( NumberFormatException exc )
299             {
300                 throw new WotonomyException( 
301                     "Invalid double format: " + value.toString() );
302             }
303         }
304         else
305         if ( XMLRPCEncoder.NIL.equals( localName ) )  
306         {
307             valueStack.push( nilMarker );
308         }
309         else
310         if ( XMLRPCEncoder.DOUBLE.equals( localName ) )  
311         {
312             Object value = valueStack.pop();
313             try
314             {
315                 valueStack.push( 
316                     new Double( value.toString() ) );
317             }
318             catch ( NumberFormatException exc )
319             {
320                 throw new WotonomyException( 
321                     "Invalid double format: " + value.toString() );
322             }
323         }
324         else
325         if ( XMLRPCEncoder.DATE.equals( localName ) )  
326         {
327             Object value = valueStack.pop();
328             try
329             {
330                 valueStack.push( 
331                     XMLRPCEncoder.DATEFORMAT8601.parseObject( 
332                         value.toString() ) );
333             }
334             catch ( Exception exc )
335             {
336                 throw new WotonomyException( 
337                     "Invalid date format: " + value );
338             }
339         }
340         else
341         if ( XMLRPCEncoder.BOOLEAN.equals( localName ) )  
342         {
343             Object value = valueStack.pop();
344             if ( XMLRPCEncoder.TRUE.equals( value ) )
345             {
346                 valueStack.push( Boolean.TRUE );
347             }
348             else
349             if ( XMLRPCEncoder.FALSE.equals( value ) )
350             {
351                 valueStack.push( Boolean.FALSE );
352             }
353             else
354             {
355                 throw new WotonomyException( 
356                     "Invalid boolean format: " + value );
357             }
358         }
359         else
360         if ( XMLRPCEncoder.BASE64.equals( localName ) )  
361         {
362             throw new WotonomyException( "Not implemented yet." );
363         }
364         else
365         if ( XMLRPCEncoder.FAULT.equals( localName ) )  
366         {
367             Map faultMap = (Map) valueStack.pop();
368             try
369             {
370                 faultCode = ((Integer)
371                     faultMap.get( XMLRPCEncoder.FAULTCODE )).intValue();
372                 faultString = (String) faultMap.get( XMLRPCEncoder.FAULTSTRING );
373             }
374             catch ( Exception exc )
375             {
376                 throw new WotonomyException(
377                     "Invalid fault format: " + faultMap );
378             }
379         }
380         else
381         if ( XMLRPCEncoder.METHODNAME.equals( localName ) )  
382         {
383             methodName = (String) valueStack.pop();
384         }
385         else
386         if ( XMLRPCEncoder.PARAM.equals( localName ) )  
387         {
388             //NOTE: this leaves the parameter on the stack
389             parameters.add( getResult() );
390         }
391     }
392     
393 
394 
395 	public void endPrefixMapping(String prefix) throws SAXException {
396 		// TODO Auto-generated method stub
397 		super.endPrefixMapping(prefix);
398 	}
399 
400 	public void error(SAXParseException e) throws SAXException {
401 		// TODO Auto-generated method stub
402 		super.error(e);
403 	}
404 
405 	public void fatalError(SAXParseException e) throws SAXException {
406 		// TODO Auto-generated method stub
407 		super.fatalError(e);
408 	}
409 
410 	public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
411 		// TODO Auto-generated method stub
412 		super.ignorableWhitespace(ch, start, length);
413 	}
414 
415 	public void notationDecl(String name, String publicId, String systemId) throws SAXException {
416 		// TODO Auto-generated method stub
417 		super.notationDecl(name, publicId, systemId);
418 	}
419 
420 	public void processingInstruction(String target, String data) throws SAXException {
421 		// TODO Auto-generated method stub
422 		super.processingInstruction(target, data);
423 	}
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 			return super.resolveEntity(publicId, systemId);		
430 		} catch (IOException e) {
431 			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 		super.setDocumentLocator(locator);
438 	}
439 
440 	public void skippedEntity(String name) throws SAXException {
441 		// TODO Auto-generated method stub
442 		super.skippedEntity(name);
443 	}
444 
445 
446 
447 	public void startPrefixMapping(String prefix, String uri) throws SAXException {
448 		// TODO Auto-generated method stub
449 		super.startPrefixMapping(prefix, uri);
450 	}
451 
452 	public void unparsedEntityDecl(String name, String publicId, String systemId, String notationName) throws SAXException {
453 		// TODO Auto-generated method stub
454 		super.unparsedEntityDecl(name, publicId, systemId, notationName);
455 	}
456 
457 	public void warning(SAXParseException e) throws SAXException {
458 		// TODO Auto-generated method stub
459 		super.warning(e);
460 	}
461 
462     
463     // marker class
464     
465     private class ValueMarker 
466     {
467         private Class theClass;
468     
469         public ValueMarker()
470         {
471             theClass = null;
472         }
473     
474         public void setMarkerClass( Class aClass )
475         {
476             theClass = aClass;
477         }
478         
479         public Class getMarkerClass()
480         {
481             return theClass;
482         }
483 
484         public String toString()
485         {
486             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