/Users/lyon/j4p/src/classUtils/pack/util/xml/configuration/DispatcherHandler.java
|
1 package classUtils.pack.util.xml.configuration;
2
3 import org.xml.sax.*;
4 import org.xml.sax.helpers.DefaultHandler;
5
6 import java.io.IOException;
7 import java.util.*;
8
9 /**
10 * <a name="desc">
11 * A SAX handler which dispatches events to subclasses implementing the
12 * {@link Configurator Configurator} interface.
13 * <p>
14 * Dispatching can be set by tree depth level (using {@link #setDispatchLevel(int)
15 * setDispatchLevel()}) or by namespaceURI/tag combination (using
16 * {@link #setToDispatch(java.lang.String, java.lang.String)}.
17 * <p>
18 * {@link Configurator Configurator} classes are located automatically in a package
19 * whose name is set at {@link #DispatcherHandler(java.lang.String) construction}
20 * and their name must match a transformation the element tag obtained by the
21 * following rules:
22 * <p>
23 * <ul>
24 * <li> The tag name is altered so that
25 * <p>
26 * <ul>
27 * <li>the first letter is capitalized;
28 * <li>dashes are eliminated and the following letter
29 * capitalized.
30 * </ul>
31 * <p>
32 * For example <tt><b><my-tag></b></tt> becomes <b>MyTag</b>.
33 * <p>
34 * <li> If the so transformed tag ends with <b>Configuration</b>, this part is stripped.
35 * <p>
36 * For example, <tt><b>MyTagConfiguration</b></tt> becomes also <b>MyTag</b>
37 * <p>
38 * <li> If the {@link #DispatcherHandler(java.lang.String, java.lang.String) two-parameters
39 * constructor} is used to create the DispatcherHandler, the given postfix
40 * is concatenated to the name.
41 * <p>
42 * For example, if the postfix is <b>Configurator</b>, the final result for
43 * a tag <tt><b><my-tag></b></tt> will be the class name <b>MyTagConfigurator</b>.
44 * </ul>
45 * <p>
46 * The class works via a <i>current configurator</i> (initially null).
47 * <p>
48 * Whenever a dispatching occurs, the class found in the package name given at constructor is instantiated and
49 * control is sent to that instance, which becomes the current configurator.
50 * <p>
51 * When the level/element for which the dispatch was enabled is terminated, the method {@link
52 * ) configuratorReady()}
53 * (which must be implemented by the class extending this one) is invoked, and the previous configurator
54 * is reinstated as "current".
55 * <p>
56 * Otherwise, the current configurator (if any) keeps receiving the SAX events.
57 *
58 * @author Cristiano Sadun
59 *
60 */
61 public abstract class DispatcherHandler extends DefaultHandler {
62
63 private String packageName;
64 private String postfix;
65 private Stack elementsStack = new Stack();
66 private Configurator currentConfigurator = null;
67
68 private Map dispatcherMap = new HashMap();
69 private Set dispatchLevels = new HashSet();
70
71 private Stack configuratorsStack = new Stack();
72
73 /**
74 * Constructor for DispatcherHandler. Indicates that the {@link Configurator Configurator}
75 * classes will be searched for in the given package (see <a href="#desc">class description</a>).
76 * @param packageName the name of the package where to attempt to locate the
77 * necessary {@link Configurator Configurator} classes.
78 * @param postfix a fixed postfix which will be added to the {@link Configurator Configurator}
79 * class name (see <a href="#desc">class description</a>).
80 */
81 protected DispatcherHandler(String packageName, String postfix) {
82 this.packageName = packageName;
83 this.postfix = postfix;
84 }
85
86 /**
87 * Constructor for DispatcherHandler. Indicates that the {@link Configurator Configurator}
88 * classes will be searched for in the given package (see <a href="#desc">class description</a>).
89 * @param packageName the name of the package where to attempt to locate the
90 * necessary {@link Configurator Configurator} classes.
91 */
92 protected DispatcherHandler(String packageName) {
93 this(packageName, "");
94 }
95
96 /**
97 * Indicate that the given namespace URI/local name combination
98 * is associated to a Configurator.
99 */
100 protected void setToDispatch(String namespaceURI, String localName) {
101 dispatcherMap.put(
102 namespaceURI + ":" + localName,
103 makeConfiguratorClassNameFromQName(localName));
104 }
105
106 /**
107 * Sets wether or not every element at a given depth level in the XML
108 * tree is associated to a Configurator.
109 * @param level the depth level in the XML tree
110
111 */
112 protected void setDispatchLevel(int level, boolean toDispatch) {
113 if (toDispatch)
114 dispatchLevels.add(new Integer(level));
115 else
116 dispatchLevels.remove(new Integer(level));
117 }
118
119 /**
120 * Indicates that every element at a given depth level in the XML
121 * tree is associated to a Configurator.
122 * @param level the depth level in the XML tree
123 */
124 protected void setDispatchLevel(int level) {
125 setDispatchLevel(level, true);
126 }
127
128 private String makeConfiguratorClassNameFromQName(String qName) {
129 String tag = getTagFromQName(qName);
130 StringBuffer sb = new StringBuffer();
131 boolean doChange = true;
132 for (int i = 0; i < qName.length(); i++) {
133 char c = tag.charAt(i);
134 if (doChange) {
135 sb.append(Character.toUpperCase(c));
136 doChange = false;
137 } else if (c == '-')
138 doChange = true;
139 else
140 sb.append(c);
141 }
142 tag = sb.toString();
143 if (tag.endsWith("Configuration"))
144 tag = tag.substring(0, tag.length() - 13);
145 return packageName + "." + tag + postfix;
146 }
147
148 private String getTagFromQName(String qName) {
149 int i = qName.indexOf(":");
150 String tag;
151 if (i == -1)
152 tag = qName;
153 else
154 tag = qName.substring(i + 1);
155 return tag;
156 }
157
158 /**
159 * Check whether the namespace/tag are associated to a configurator
160 * or we're at a dispatch level.
161 * <p>
162 * If yes, instantiate a new associated {@link Configurator Configurator}.
163 * and invoke its {@link Configurator#startElement(java.lang.String,
164 * java.lang.String, java.lang.String, org.xml.sax.Attributes)
165 * startElement()} method.
166 * <p>
167 * If no, invoke the
168 * {@link Configurator#startElement(java.lang.String, java.lang.String,
169 * java.lang.String, org.xml.sax.Attributes) startElement()} method
170 * of the current {@link Configurator Configurator} (if any).
171 */
172 public final void startElement(
173 String namespaceURI,
174 String localName,
175 String qName,
176 Attributes atts)
177 throws SAXException {
178
179 elementsStack.push(qName);
180
181 if (dispatchLevels.contains(new Integer(elementsStack.size()))
182 || hasConfigurator(namespaceURI, localName)) {
183 String clsName = makeConfiguratorClassNameFromQName(qName);
184
185 try {
186
187 Class configuratorCls = Class.forName(clsName);
188 try {
189 currentConfigurator =
190 (Configurator) configuratorCls.newInstance();
191 configuratorsStack.push(currentConfigurator);
192 } catch (InstantiationException e) {
193 throw new SAXException(
194 "Could not instantiate XSpace configurator object",
195 e);
196 } catch (IllegalAccessException e) {
197 throw new SAXException(
198 "Could not access XSpace configurator object",
199 e);
200 }
201 } catch (ClassNotFoundException e) {
202 throw new SAXException(
203 qName
204 + " configuration element not handled (class "
205 + clsName
206 + " not found");
207 }
208
209 }
210
211 if (currentConfigurator != null)
212 currentConfigurator.startElement(
213 namespaceURI,
214 localName,
215 qName,
216 atts);
217
218 }
219
220 public final void characters(char[] ch, int start, int length)
221 throws SAXException {
222 if (currentConfigurator != null)
223 currentConfigurator.characters(ch, start, length);
224
225 }
226
227 public final void endElement(
228 String namespaceURI,
229 String localName,
230 String qName)
231 throws SAXException {
232 elementsStack.pop();
233 if (currentConfigurator == null)
234 return;
235 currentConfigurator.endElement(
236 namespaceURI,
237 localName,
238 qName);
239
240
241
242 if (dispatchLevels.contains(new Integer(elementsStack.size()+1))
243 || hasConfigurator(namespaceURI, localName)) {
244 configuratorReady(
245 namespaceURI,
246 localName,
247 qName,
248 currentConfigurator);
249 currentConfigurator = (Configurator)configuratorsStack.pop();
250 }
251 }
252
253 /**
254 * Method hasConfigurator.
255 * @param namespaceURI
256 * @param localName
257 * @return boolean
258 */
259 private boolean hasConfigurator(String namespaceURI, String localName) {
260 return dispatcherMap.containsKey(namespaceURI + ":" + localName);
261 }
262
263 protected abstract void configuratorReady(
264 String namespaceURI,
265 String localName,
266 String qName,
267 Configurator configurator)
268 throws SAXException;
269 /**
270 * @see org.xml.sax.ContentHandler#endDocument()
271 */
272 public void endDocument() throws SAXException {
273 if (currentConfigurator!=null) currentConfigurator.endDocument();
274 }
275
276 /**
277 * @see org.xml.sax.ContentHandler#endPrefixMapping(String)
278 */
279 public void endPrefixMapping(String prefix) throws SAXException {
280 if (currentConfigurator!=null) currentConfigurator.endPrefixMapping(prefix);
281 }
282
283 /**
284 * @see org.xml.sax.ErrorHandler#error(SAXParseException)
285 */
286 public void error(SAXParseException exception) throws SAXException {
287 if (currentConfigurator!=null && currentConfigurator instanceof ErrorHandler) ((ErrorHandler)currentConfigurator).error(exception);
288 }
289
290 /**
291 * @see org.xml.sax.ErrorHandler#fatalError(SAXParseException)
292 */
293 public void fatalError(SAXParseException exception) throws SAXException {
294 if (currentConfigurator!=null && currentConfigurator instanceof ErrorHandler) ((ErrorHandler)currentConfigurator).fatalError(exception);
295 }
296
297 /**
298 * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int)
299 */
300 public void ignorableWhitespace(char[] ch, int start, int length)
301 throws SAXException {
302 if (currentConfigurator!=null) currentConfigurator.ignorableWhitespace(ch, start, length);
303 }
304
305 /**
306 * @see org.xml.sax.DTDHandler#notationDecl(String, String, String)
307 */
308 public void notationDecl(String name, String publicId, String systemId)
309 throws SAXException {
310 if (currentConfigurator!=null && currentConfigurator instanceof DTDHandler) ((DTDHandler)currentConfigurator).notationDecl(name, publicId, systemId);
311 }
312
313 /**
314 * @see org.xml.sax.ContentHandler#processingInstruction(String, String)
315 */
316 public void processingInstruction(String target, String data)
317 throws SAXException {
318 if (currentConfigurator!=null) currentConfigurator.processingInstruction(target, data);
319 }
320
321 /**
322 * @see org.xml.sax.EntityResolver#resolveEntity(String, String)
323 */
324 public InputSource resolveEntity(String publicId, String systemId)
325 throws SAXException {
326 if (currentConfigurator!=null && currentConfigurator instanceof EntityResolver) try {
327 return ((EntityResolver)currentConfigurator).resolveEntity(publicId, systemId);
328 } catch (IOException e) {
329 e.printStackTrace();
330 return super.resolveEntity(publicId, systemId);
331 }
332 else return super.resolveEntity(publicId, systemId);
333 }
334
335 /**
336 * @see org.xml.sax.ContentHandler#setDocumentLocator(Locator)
337 */
338 public void setDocumentLocator(Locator locator) {
339 if (currentConfigurator!=null) currentConfigurator.setDocumentLocator(locator);
340 }
341
342 /**
343 * @see org.xml.sax.ContentHandler#skippedEntity(String)
344 */
345 public void skippedEntity(String name) throws SAXException {
346 if (currentConfigurator!=null) currentConfigurator.skippedEntity(name);
347 }
348
349 /**
350 * @see org.xml.sax.ContentHandler#startDocument()
351 */
352 public void startDocument() throws SAXException {
353 if (currentConfigurator!=null) currentConfigurator.startDocument();
354 }
355
356 /**
357 * @see org.xml.sax.ContentHandler#startPrefixMapping(String, String)
358 */
359 public void startPrefixMapping(String prefix, String uri)
360 throws SAXException {
361 if (currentConfigurator!=null) currentConfigurator.startPrefixMapping(prefix, uri);
362 }
363
364 /**
365 * @see org.xml.sax.DTDHandler#unparsedEntityDecl(String, String, String, String)
366 */
367 public void unparsedEntityDecl(
368 String name,
369 String publicId,
370 String systemId,
371 String notationName)
372 throws SAXException {
373 if (currentConfigurator!=null && currentConfigurator instanceof DTDHandler)
374 ((DTDHandler)currentConfigurator).unparsedEntityDecl(name, publicId, systemId, notationName);
375 }
376
377 /**
378 * @see org.xml.sax.ErrorHandler#warning(SAXParseException)
379 */
380 public void warning(SAXParseException exception) throws SAXException {
381 if (currentConfigurator!=null&& currentConfigurator instanceof ErrorHandler)
382 ((ErrorHandler)currentConfigurator).warning(exception);
383 }
384
385 }
386