View Javadoc

1   /**
2    *    Copyright 2011 meltmedia
3    *
4    *    Licensed under the Apache License, Version 2.0 (the "License");
5    *    you may not use this file except in compliance with the License.
6    *    You may obtain a copy of the License at
7    *
8    *        http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *    Unless required by applicable law or agreed to in writing, software
11   *    distributed under the License is distributed on an "AS IS" BASIS,
12   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *    See the License for the specific language governing permissions and
14   *    limitations under the License.
15   */
16  package org.xchain.framework.sax;
17  
18  import org.xml.sax.helpers.DefaultHandler;
19  import org.xml.sax.Attributes;
20  import org.xml.sax.helpers.AttributesImpl;
21  import org.xml.sax.ext.LexicalHandler;
22  import java.util.ArrayList;
23  import java.util.HashMap;
24  import java.util.HashSet;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Set;
28  
29  /**
30   * The SaxEventRecorder is a DefaultHandler implementation that will simply record all incoming SAX events.  The events are stored as SaxEvent objects.
31   * The EventList is in the same order as the original SAX events.  By default no SAX events are tracked.  Types of event tracking can be enabled with {@link #setTrackDocumentEvents(boolean)},
32   * {@link #setTrackElementEvents(boolean)}, {@link #setTrackCharactersEvents(boolean)}, and {@link #setTrackPrefixMappingEvents(boolean)}.
33   *
34   * @author Christian Trimble
35   * @author Devon Tackett
36   */
37  public class SaxEventRecorder
38    extends DefaultHandler
39    implements LexicalHandler
40  {
41    /** The list of recorded SAX events. */
42    protected List<SaxEvent> eventList = new ArrayList<SaxEvent>();
43    /** Whether document events should be tracked. */
44    protected boolean trackDocumentEvents = false;
45    /** Whether element events should be tracked. */
46    protected boolean trackElementEvents = false;
47    /** Whether character events should be tracked. */
48    protected boolean trackCharactersEvents = false;
49    /** Whether prefix mapping events should be tracked. */
50    protected boolean trackPrefixMappingEvents = false;
51    /** Whether comment events should be tracked. */
52    protected boolean trackCommentEvents = false;
53  
54    /**
55     * @return The recorded list of SAX events.
56     */
57    public List<SaxEvent> getEventList() { return eventList; }
58    
59    /** 
60     * Set whether document SAX events should be tracked.
61     */
62    public void setTrackDocumentEvents( boolean trackDocumentEvents ) { this.trackDocumentEvents = trackDocumentEvents; }
63    
64    /**
65     * Set whether element SAX events should be tracked.
66     */
67    public void setTrackElementEvents( boolean trackElementEvents ) { this.trackElementEvents = trackElementEvents; }
68    
69    /**
70     * Set whether character SAX events should be tracked.
71     */
72    public void setTrackCharactersEvents( boolean trackCharactersEvents ) { this.trackCharactersEvents = trackCharactersEvents; }
73    
74    /**
75     * Set whether prefix mapping events should be tracked.
76     */
77    public void setTrackPrefixMappingEvents( boolean trackPrefixMappingEvents ) { this.trackPrefixMappingEvents = trackPrefixMappingEvents; }
78  
79    public void setTrackCommentEvents( boolean trackCommentEvents ) { this.trackCommentEvents = trackCommentEvents; }
80  
81    public void startDocument()
82    {
83      if( trackDocumentEvents ) {
84        SaxEvent saxEvent = new SaxEvent();
85        saxEvent.setType(EventType.START_DOCUMENT);
86        eventList.add(saxEvent);
87      }
88    }
89  
90    public void endDocument()
91    {
92      if( trackDocumentEvents ) {
93        SaxEvent saxEvent = new SaxEvent();
94        saxEvent.setType(EventType.END_DOCUMENT);
95        eventList.add(saxEvent);
96      }
97    }
98  
99    public void startElement( String uri, String localName, String qName, Attributes attributes )
100   {
101     if( trackElementEvents ) {
102       SaxEvent saxEvent = new SaxEvent();
103       saxEvent.setType(EventType.START_ELEMENT);
104       saxEvent.setUri(uri);
105       saxEvent.setLocalName(localName);
106       saxEvent.setQName(qName);
107       saxEvent.setAttributes(new AttributesImpl(attributes));
108       eventList.add(saxEvent);
109     }
110   }
111 
112   public void endElement( String uri, String localName, String qName )
113   {
114     if( trackElementEvents ) {
115       SaxEvent saxEvent = new SaxEvent();
116       saxEvent.setType(EventType.END_ELEMENT);
117       saxEvent.setUri(uri);
118       saxEvent.setLocalName(localName);
119       eventList.add(saxEvent);
120     }
121   }
122 
123   public void characters( char[] characters, int start, int length )
124   {
125     if( trackCharactersEvents ) {
126       // if the previous event was characters, then append.
127       if( !eventList.isEmpty() && eventList.get(eventList.size()-1).getType() == EventType.CHARACTERS ) {
128         SaxEvent saxEvent = eventList.get(eventList.size()-1);
129         saxEvent.setText(new StringBuilder().append(saxEvent.getText()).append( characters, start, length ).toString());
130       }
131       else {
132         SaxEvent saxEvent = new SaxEvent();
133         saxEvent.setType(EventType.CHARACTERS);
134         saxEvent.setText(new StringBuilder().append( characters, start, length ).toString());
135         eventList.add(saxEvent);
136       }
137     }
138   }
139 
140   public void startPrefixMapping( String prefix, String uri )
141   {
142     if( trackPrefixMappingEvents ) {
143       if( !eventList.isEmpty() && eventList.get(eventList.size()-1).getType() == EventType.START_PREFIX_MAPPING ) {
144         eventList.get(eventList.size()-1).getPrefixMapping().put(prefix, uri);
145       }
146       else {
147         SaxEvent saxEvent = new SaxEvent();
148         saxEvent.setType(EventType.START_PREFIX_MAPPING);
149         HashMap<String, String> prefixMapping = new HashMap<String, String>();
150         prefixMapping.put(prefix, uri);
151         saxEvent.setPrefixMapping(prefixMapping);
152         eventList.add(saxEvent);
153       }
154     }
155   }
156 
157   public void endPrefixMapping( String prefix )
158   {
159     if( trackPrefixMappingEvents ) {
160       if( !eventList.isEmpty() && eventList.get(eventList.size()-1).getType() == EventType.END_PREFIX_MAPPING ) {
161         eventList.get(eventList.size()-1).getPrefixSet().add(prefix);
162       }
163       else {
164         SaxEvent saxEvent = new SaxEvent();
165         saxEvent.setType(EventType.END_PREFIX_MAPPING);
166         Set<String> prefixSet = new HashSet<String>();
167         prefixSet.add(prefix);
168         saxEvent.setPrefixSet(prefixSet);
169         eventList.add(saxEvent);
170       }
171     }
172   }
173 
174   public void comment( char[] comment, int start, int length )
175   {
176     if( trackCommentEvents ) {
177       SaxEvent saxEvent = new SaxEvent();
178       saxEvent.setType(EventType.COMMENT);
179       saxEvent.setText(new StringBuilder().append( comment, start, length ).toString());
180       eventList.add(saxEvent);
181     }
182   }
183   public void startDTD( String name, String publicId, String systemId ) { }
184   public void endDTD() { }
185   public void startCDATA() { }
186   public void endCDATA() { }
187   public void startEntity( String name ) { } 
188   public void endEntity( String name ) { }
189 
190   public String toString()
191   {
192     StringBuilder sb = new StringBuilder();
193     for( SaxEvent event : eventList ) {
194       sb.append(event.toString()).append("\n");
195     }
196     return sb.toString();
197   }
198 
199   public static enum EventType
200   {
201     START_DOCUMENT,
202     END_DOCUMENT,
203     START_PREFIX_MAPPING,
204     END_PREFIX_MAPPING,
205     START_ELEMENT,
206     END_ELEMENT,
207     CHARACTERS,
208     COMMENT
209   }
210 
211   /**
212    * This class represents a recorded SAX event.
213    */
214   public static class SaxEvent
215   {
216     private EventType type;
217     private String text;
218     private String uri;
219     private String localName;
220     private String qName;
221     private Attributes attributes;
222     private Map<String, String> prefixMapping;
223     private Set<String> prefixSet;
224 
225     public EventType getType() { return this.type; }
226     public void setType( EventType type ) { this.type = type; }
227     public String getText() { return this.text; }
228     public void setText( String text ) { this.text = text; }
229     public String getUri() { return uri; }
230     public void setUri( String uri ) { this.uri = uri; }
231     public String getLocalName() { return localName; }
232     public void setLocalName( String localName ) { this.localName = localName; }
233     public String getQName() { return qName; }
234     public void setQName( String qName ) { this.qName = qName; }
235     public Map<String, String> getPrefixMapping() { return this.prefixMapping; }
236     public void setPrefixMapping( Map<String, String> prefixMapping ) { this.prefixMapping = prefixMapping; }
237     public Set<String> getPrefixSet() { return this.prefixSet; }
238     public void setPrefixSet( Set<String> prefixSet ) { this.prefixSet = prefixSet; }
239     public Attributes getAttributes() { return this.attributes; }
240     public void setAttributes( Attributes attributes ) { this.attributes = attributes; }
241     public String toString() {
242       StringBuilder sb = new StringBuilder();
243       sb.append("  ").append(type).append("\n");
244       if( uri != null ) {
245         sb.append("  ").append("uri:").append(uri).append("\n");
246       }
247       if( localName != null ) {
248         sb.append("  ").append("localName:").append(localName).append("\n");
249       }
250       if( prefixMapping != null ) {
251         for( Map.Entry<String, String> entry : prefixMapping.entrySet() ) {
252           sb.append("  ").append("prefix:").append(entry.getKey()).append(" namespace:").append(entry.getValue()).append("\n");
253         }
254       }
255       return sb.toString();
256     }
257   }
258 
259 }