View Javadoc

1   /*
2   
3       stax  Stack API for XML.
4       Copyright (c) 2001-2006 held jointly by the individual authors.
5   
6       This library is free software; you can redistribute it and/or modify it
7       under the terms of the GNU Lesser General Public License as published
8       by the Free Software Foundation; either version 2.1 of the License, or (at
9       your option) any later version.
10  
11      This library is distributed in the hope that it will be useful, but WITHOUT
12      ANY WARRANTY; with out even the implied warranty of MERCHANTABILITY or
13      FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14      License for more details.
15  
16      You should have received a copy of the GNU Lesser General Public License
17      along with this library;  if not, write to the Free Software Foundation,
18      Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA.
19  
20      > http://www.gnu.org/copyleft/lesser.html
21      > http://www.opensource.org/licenses/lgpl-license.php
22  
23  */
24  package net.sf.stax;
25  
26  import java.util.Map;
27  import java.util.Set;
28  import java.util.List;
29  import java.util.ArrayList;
30  import java.util.AbstractList;
31  
32  import org.xml.sax.Locator;
33  import org.xml.sax.Attributes;
34  import org.xml.sax.ContentHandler;
35  import org.xml.sax.SAXException;
36  
37  /***
38   * Lightweight adaptor which translates SAX content events into
39   * StAX form, and provides delegation and id mapping services.
40   *
41   * @author  Thomas Down
42   * @author  Michael Heuer
43   * @version $Revision: 1.4 $ $Date: 2006/01/02 20:37:34 $
44   */
45  public final class SAX2StAXAdaptor
46      implements ContentHandler
47  {
48      /*** Handler binding stack. */
49      private List handlerBindingStack;
50  
51      /*** Current handler binding. */
52      private HandlerBinding currentHandlerBinding;
53  
54      /*** Context binding stack. */
55      private List contextBindingStack;
56  
57      /*** Current context binding. */
58      private ContextBinding currentContextBinding;
59  
60      /*** View on the context binding stack. */
61      private List contextBindingStackView;
62  
63      /*** True if <code>endDocument</code> should throw an exception if there are unresolved ids. */
64      private boolean checkIdsResolveFlag;
65  
66      /*** Id mapper. */
67      private IdMapperImpl idMapper;
68  
69      /*** Result. */
70      private Object result;
71  
72      /*** Context. */
73      private StAXContext context;
74  
75      {
76          handlerBindingStack = new ArrayList();
77          contextBindingStack = new ArrayList();
78          idMapper = new IdMapperImpl();
79  
80          contextBindingStackView = new AbstractList()
81              {
82  
83                  /*** @see List */
84                  public int size()
85                  {
86                      return contextBindingStack.size() - 1;
87                  }
88  
89                  /*** @see List */
90                  public Object get(final int index)
91                  {
92                      return transduce(contextBindingStack.get(index + 1));
93                  }
94  
95                  /***
96                   * Return the context name of the specified item.
97                   *
98                   * @param item item
99                   */
100                 private Object transduce(final Object item)
101                 {
102                     return ((ContextBinding) item).contextName;
103                 }
104             };
105 
106         context = new StAXContext()
107             {
108                 /*** Document locator. */
109                 private Locator locator;
110 
111 
112                 /*** @see StAXContext */
113                 public IdMapper getIdMapper()
114                 {
115                     return idMapper;
116                 }
117 
118                 /*** @see StAXContext */
119                 public List getContexts()
120                 {
121                     return contextBindingStackView;
122                 }
123 
124                 /*** @see StAXContext */
125                 public Locator getLocator()
126                 {
127                     return locator;
128                 }
129 
130                 /*** @see StAXContext */
131                 public void setLocator(final Locator locator)
132                 {
133                     this.locator = locator;
134                 }
135             };
136     }
137 
138 
139     /***
140      * Construct a new SAX Content handler which wraps a StAX
141      * handler.
142      *
143      * @param rootHandler root handler
144      * @param checkIdsResolveFlag true if <code>endDocument</code> should throw an exception
145      *    if there are unresolved ids
146      * @param existingIds map of existing ids to items
147      */
148     public SAX2StAXAdaptor(final StAXContentHandler rootHandler,
149                            final boolean checkIdsResolveFlag,
150                            final Map existingIds)
151     {
152         this(rootHandler, checkIdsResolveFlag);
153         idMapper = new IdMapperImpl(existingIds); // ugly - idMapper already filled
154     }
155 
156     /***
157      * Construct a new SAX Content handler which wraps a StAX
158      * handler.
159      *
160      * @param rootHandler root handler
161      * @param checkIdsResolveFlag true if <code>endDocument</code> should throw an exception
162      *    if there are unresolved ids
163      */
164     public SAX2StAXAdaptor(final StAXContentHandler rootHandler,
165                            final boolean checkIdsResolveFlag)
166     {
167         currentHandlerBinding = new HandlerBinding(rootHandler);
168         handlerBindingStack.add(currentHandlerBinding);
169         currentContextBinding = new ContextBinding();
170         contextBindingStack.add(currentContextBinding);
171         this.checkIdsResolveFlag = checkIdsResolveFlag;
172     }
173 
174     /***
175      * Construct a new SAX Content handler which wraps a StAX
176      * handler.
177      *
178      * @param rootHandler root handler
179      */
180     public SAX2StAXAdaptor(final StAXContentHandler rootHandler)
181     {
182         this(rootHandler, true);
183     }
184 
185 
186     /*** @see ContentHandler */
187     public void startDocument()
188       throws SAXException
189     {
190         currentHandlerBinding.getHandler().startTree(context);
191     }
192 
193     /*** @see ContentHandler */
194     public void endDocument()
195         throws SAXException
196     {
197         result = currentHandlerBinding.getHandler().endTree(context);
198         if (checkIdsResolveFlag)
199         {
200             Set ids = idMapper.getUnresolvedIds();
201             if (ids.size() > 0)
202             {
203                 throw new SAXException("Unresolved Ids: " + ids);
204             }
205         }
206     }
207 
208     /*** @see ContentHandler */
209     public void characters(final char[] ch,
210                            final int start,
211                            final int end)
212         throws SAXException
213     {
214         currentHandlerBinding.getHandler().characters(ch, start, end, context);
215     }
216 
217     /*** @see ContentHandler */
218     public void ignorableWhitespace(final char[] ch,
219                                     final int start,
220                                     final int end)
221         throws SAXException
222     {
223         currentHandlerBinding.getHandler().ignorableWhitespace(ch, start, end, context);
224     }
225 
226     /*** @see ContentHandler */
227     public void startPrefixMapping(final String prefix, final String uri)
228         throws SAXException
229     {
230         currentHandlerBinding.getHandler().startPrefixMapping(prefix, uri, context);
231     }
232 
233     /*** @see ContentHandler */
234     public void endPrefixMapping(final String prefix)
235         throws SAXException
236     {
237         currentHandlerBinding.getHandler().endPrefixMapping(prefix, context);
238     }
239 
240     /*** @see ContentHandler */
241     public void processingInstruction(final String target, final String data)
242         throws SAXException
243     {
244         currentHandlerBinding.getHandler().processingInstruction(target, data, context);
245     }
246 
247     /*** @see ContentHandler */
248     public void setDocumentLocator(final Locator locator)
249     {
250         currentHandlerBinding.getHandler().setDocumentLocator(locator, context);
251     }
252 
253     /*** @see ContentHandler */
254     public void skippedEntity(final String name)
255         throws SAXException
256     {
257         currentHandlerBinding.getHandler().skippedEntity(name, context);
258     }
259 
260     /*** @see ContentHandler */
261     public void startElement(final String nsURI,
262                              final String localName,
263                              final String qName,
264                              final Attributes attrs)
265         throws SAXException
266     {
267         S2SStAXDelegationContext dctx = new S2SStAXDelegationContext(context);
268         currentHandlerBinding.getHandler().startElement(nsURI,
269                                                         localName,
270                                                         qName,
271                                                         attrs,
272                                                         dctx);
273 
274         // must push the context before delegating
275         if (dctx.getContext() != null)
276         {
277             currentContextBinding = new ContextBinding(dctx.getContext());
278             contextBindingStack.add(currentContextBinding);
279         }
280         else
281         {
282             currentContextBinding.setCount(currentContextBinding.getCount() + 1);
283         }
284 
285         // now delegate to handler
286         if (dctx.getDelegate() != null)
287         {
288             currentHandlerBinding = new HandlerBinding(dctx.getDelegate());
289             handlerBindingStack.add(currentHandlerBinding);
290             currentHandlerBinding.getHandler().startTree(context);
291             // Recurse until someone takes responsibility.
292             startElement(nsURI, localName, qName, attrs);
293         }
294         else
295         {
296             currentHandlerBinding.setCount(currentHandlerBinding.getCount() + 1);
297         }
298     }
299 
300     /*** @see ContentHandler */
301     public void endElement(final String nsURI,
302                            final String localName,
303                            final String qName)
304         throws SAXException
305     {
306         currentHandlerBinding.getHandler().endElement(nsURI, localName, qName, null, context);
307 
308         // remove context after calling delegate, but before calling delegee
309         currentContextBinding.setCount(currentContextBinding.getCount() - 1);
310         while (currentContextBinding.getCount() == 0 && contextBindingStack.size() > 1)
311         {
312             contextBindingStack.remove(contextBindingStack.size() - 1);
313             currentContextBinding = (ContextBinding) contextBindingStack.get(contextBindingStack.size() - 1);
314         }
315 
316         // now remove delegates
317         currentHandlerBinding.setCount(currentHandlerBinding.getCount() - 1);
318         while (currentHandlerBinding.getCount() == 0 && handlerBindingStack.size() > 1)
319         {
320             StAXContentHandler oldHandler = currentHandlerBinding.getHandler();
321             Object res = currentHandlerBinding.getHandler().endTree(context);
322             handlerBindingStack.remove(handlerBindingStack.size() - 1);
323             currentHandlerBinding = (HandlerBinding) handlerBindingStack.get(handlerBindingStack.size() - 1);
324             currentHandlerBinding.getHandler().endElement(nsURI, localName, qName, res, context);
325 
326             // now remove context(s) for this end-element - is this in the right
327             // place, or should it be after endTree?
328             currentContextBinding.setCount(currentContextBinding.getCount() - 1);
329             while (currentContextBinding.getCount() == 0 && contextBindingStack.size() > 1)
330             {
331                 contextBindingStack.remove(contextBindingStack.size() - 1);
332                 currentContextBinding = (ContextBinding) contextBindingStack.get(contextBindingStack.size() - 1);
333             }
334         }
335     }
336 
337     /***
338      * SAX2StAX implementation of <b>StAXDelegationContext</b>.
339      */
340     private class S2SStAXDelegationContext
341         implements StAXDelegationContext
342     {
343         /*** StAX context. */
344         private final StAXContext ctx;
345 
346         /*** Delegate. */
347         private StAXContentHandler delegate;
348 
349         /*** Context. */
350         private String context;
351 
352 
353         /***
354          * Create a new SAX2StAX implementation of StAXDelegationContext
355          * with the specified StAX context.
356          *
357          * @param ctx StAX context
358          */
359         public S2SStAXDelegationContext(final StAXContext ctx)
360         {
361             this.ctx = ctx;
362         }
363 
364 
365         /*** @see StAXDelegationContext */
366         public void delegate(final StAXContentHandler handler)
367             throws SAXException
368         {
369             if (handler == null)
370             {
371                 throw new SAXException("Tried to delegate to null handler");
372             }
373 
374             if (this.delegate != null)
375             {
376                 throw new SAXException("Tried to multiply delegate a single StAX element");
377             }
378             this.delegate = handler;
379         }
380 
381         /*** @see StAXDelegationContext */
382         public void pushContext(final String context)
383             throws SAXException
384         {
385             if (this.context != null)
386             {
387                 throw new SAXException("Tried to push multiple contexts for a single StAX element");
388             }
389             this.context = context;
390         }
391 
392         /***
393          * Return the delegate.
394          *
395          * @return the delegate
396          */
397         public StAXContentHandler getDelegate()
398         {
399             return delegate;
400         }
401 
402         /***
403          * Return the context.
404          *
405          * @return the context
406          */
407         public String getContext()
408         {
409             return context;
410         }
411 
412         /*** @see StAXDelegationContext */
413         public IdMapper getIdMapper()
414         {
415             return ctx.getIdMapper();
416         }
417 
418         /*** @see StAXDelegationContext */
419         public List getContexts()
420         {
421             return ctx.getContexts();
422         }
423 
424         /*** @see StAXDelegationContext */
425         public Locator getLocator()
426         {
427             return ctx.getLocator();
428         }
429 
430         /*** @see StAXDelegationContext */
431         public void setLocator(final Locator locator)
432         {
433             ctx.setLocator(locator);
434         }
435     }
436 
437     /***
438      * Memento class binding a handler.
439      */
440     private final class HandlerBinding
441     {
442         /*** Count. */
443         private int count;
444 
445         /*** Handler. */
446         private final StAXContentHandler handler;
447 
448 
449         /***
450          * Create a new binding for the specified handler.
451          *
452          * @param handler handler
453          */
454         private HandlerBinding(final StAXContentHandler handler)
455         {
456             this.handler = handler;
457             this.count = 0;
458         }
459 
460 
461         /***
462          * Return the count for this handler binding.
463          *
464          * @return the count for this handler binding
465          */
466         public int getCount()
467         {
468             return count;
469         }
470 
471         /***
472          * Set the count for this handler binding to <code>count</code>.
473          *
474          * @param count count
475          */
476         public void setCount(final int count)
477         {
478             this.count = count;
479         }
480 
481         /***
482          * Return the handler for this binding.
483          *
484          * @return the handler for this binding
485          */
486         public StAXContentHandler getHandler()
487         {
488             return handler;
489         }
490     }
491 
492     /***
493      * Memento class binding a context.
494      */
495     private final class ContextBinding
496     {
497         /*** Count. */
498         private int count;
499 
500         /*** Context name. */
501         private String contextName;
502 
503 
504         /***
505          * Create a new context binding.
506          */
507         private ContextBinding()
508         {
509             contextName = "";
510             count = 0;
511         }
512 
513         /***
514          * Create a new context binding with the specified context name.
515          *
516          * @param contextName context name
517          */
518         private ContextBinding(final String contextName)
519         {
520             this.contextName = contextName;
521             count = 0;
522         }
523 
524 
525         /***
526          * Return the count for this context binding.
527          *
528          * @return the count for this context binding
529          */
530         public int getCount()
531         {
532             return count;
533         }
534 
535         /***
536          * Set the count for this context binding to <code>count</code>.
537          *
538          * @param count count
539          */
540         public void setCount(final int count)
541         {
542             this.count = count;
543         }
544 
545         /***
546          * Return the context name for this binding.
547          *
548          * @return the context name for this binding
549          */
550         public String getContextName()
551         {
552             return contextName;
553         }
554 
555         /***
556          * Set the context name for this binding to <code>contextName</code>.
557          *
558          * @param contextName context name
559          */
560         public void setContextName(final String contextName)
561         {
562             this.contextName = contextName;
563         }
564     }
565 }
566