KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > cyberneko > html > filters > NamespaceBinder


1 /*
2  * (C) Copyright 2004-2005, Andy Clark. All rights reserved.
3  *
4  * This file is distributed under an Apache style license. Please
5  * refer to the LICENSE file for specific details.
6  */

7
8 package org.cyberneko.html.filters;
9
10 import org.cyberneko.html.HTMLElements;
11
12 import java.lang.reflect.InvocationTargetException JavaDoc;
13 import java.lang.reflect.Method JavaDoc;
14 import java.util.Enumeration JavaDoc;
15 import java.util.Vector JavaDoc;
16
17 import org.apache.xerces.xni.Augmentations;
18 import org.apache.xerces.xni.NamespaceContext;
19 import org.apache.xerces.xni.QName;
20 import org.apache.xerces.xni.XMLAttributes;
21 import org.apache.xerces.xni.XMLLocator;
22 import org.apache.xerces.xni.XNIException;
23 import org.apache.xerces.xni.parser.XMLComponentManager;
24 import org.apache.xerces.xni.parser.XMLConfigurationException;
25
26 /**
27  * This filter binds namespaces if namespace processing is turned on
28  * by setting the feature "http://xml.org/sax/features/namespaces" is
29  * set to <code>true</code>.
30  * <p>
31  * This configuration recognizes the following features:
32  * <ul>
33  * <li>http://xml.org/sax/features/namespaces
34  * </ul>
35  *
36  * @author Andy Clark
37  *
38  * @version $Id: NamespaceBinder.java,v 1.8 2005/05/30 00:19:28 andyc Exp $
39  */

40 public class NamespaceBinder
41     extends DefaultFilter {
42
43     //
44
// Constants
45
//
46

47     // namespace uris
48

49     /** XHTML 1.0 namespace URI (http://www.w3.org/1999/xhtml). */
50     public static final String JavaDoc XHTML_1_0_URI = "http://www.w3.org/1999/xhtml";
51
52     /** XML namespace URI (http://www.w3.org/XML/1998/namespace). */
53     public static final String JavaDoc XML_URI = "http://www.w3.org/XML/1998/namespace";
54
55     /** XMLNS namespace URI (http://www.w3.org/2000/xmlns/). */
56     public static final String JavaDoc XMLNS_URI = "http://www.w3.org/2000/xmlns/";
57
58     // features
59

60     /** Namespaces. */
61     protected static final String JavaDoc NAMESPACES = "http://xml.org/sax/features/namespaces";
62
63     /** Override namespace binding URI. */
64     protected static final String JavaDoc OVERRIDE_NAMESPACES = "http://cyberneko.org/html/features/override-namespaces";
65
66     /** Insert namespace binding URIs. */
67     protected static final String JavaDoc INSERT_NAMESPACES = "http://cyberneko.org/html/features/insert-namespaces";
68
69     /** Recognized features. */
70     private static final String JavaDoc[] RECOGNIZED_FEATURES = {
71         NAMESPACES,
72         OVERRIDE_NAMESPACES,
73         INSERT_NAMESPACES,
74     };
75
76     /** Feature defaults. */
77     private static final Boolean JavaDoc[] FEATURE_DEFAULTS = {
78         null,
79         Boolean.FALSE,
80         Boolean.FALSE,
81     };
82
83     // properties
84

85     /** Modify HTML element names: { "upper", "lower", "default" }. */
86     protected static final String JavaDoc NAMES_ELEMS = "http://cyberneko.org/html/properties/names/elems";
87
88     /** Modify HTML attribute names: { "upper", "lower", "default" }. */
89     protected static final String JavaDoc NAMES_ATTRS = "http://cyberneko.org/html/properties/names/attrs";
90
91     /** Namespaces URI. */
92     protected static final String JavaDoc NAMESPACES_URI = "http://cyberneko.org/html/properties/namespaces-uri";
93
94     /** Recognized properties. */
95     private static final String JavaDoc[] RECOGNIZED_PROPERTIES = new String JavaDoc[] {
96         NAMES_ELEMS,
97         NAMES_ATTRS,
98         NAMESPACES_URI,
99     };
100
101     /** Property defaults. */
102     private static final Object JavaDoc[] PROPERTY_DEFAULTS = {
103         null,
104         null,
105         XHTML_1_0_URI,
106     };
107
108     // modify HTML names
109

110     /** Don't modify HTML names. */
111     protected static final short NAMES_NO_CHANGE = 0;
112
113     /** Uppercase HTML names. */
114     protected static final short NAMES_UPPERCASE = 1;
115
116     /** Lowercase HTML names. */
117     protected static final short NAMES_LOWERCASE = 2;
118
119     //
120
// Data
121
//
122

123     // features
124

125     /** Namespaces. */
126     protected boolean fNamespaces;
127
128     /** Namespace prefixes. */
129     protected boolean fNamespacePrefixes;
130
131     /** Override namespaces. */
132     protected boolean fOverrideNamespaces;
133
134     /** Insert namespaces. */
135     protected boolean fInsertNamespaces;
136
137     // properties
138

139     /** Modify HTML element names. */
140     protected short fNamesElems;
141
142     /** Modify HTML attribute names. */
143     protected short fNamesAttrs;
144
145     /** Namespaces URI. */
146     protected String JavaDoc fNamespacesURI;
147
148     // state
149

150     /** Namespace context. */
151     protected final NamespaceSupport fNamespaceContext = new NamespaceSupport();
152
153     // temp vars
154

155     /** QName. */
156     private final QName fQName = new QName();
157
158     //
159
// HTMLComponent methods
160
//
161

162     /**
163      * Returns a list of feature identifiers that are recognized by
164      * this component. This method may return null if no features
165      * are recognized by this component.
166      */

167     public String JavaDoc[] getRecognizedFeatures() {
168         return merge(super.getRecognizedFeatures(), RECOGNIZED_FEATURES);
169     } // getRecognizedFeatures():String[]
170

171     /**
172      * Returns the default state for a feature, or null if this
173      * component does not want to report a default value for this
174      * feature.
175      */

176     public Boolean JavaDoc getFeatureDefault(String JavaDoc featureId) {
177         for (int i = 0; i < RECOGNIZED_FEATURES.length; i++) {
178             if (RECOGNIZED_FEATURES[i].equals(featureId)) {
179                 return FEATURE_DEFAULTS[i];
180             }
181         }
182         return super.getFeatureDefault(featureId);
183     } // getFeatureDefault(String):Boolean
184

185     /**
186      * Returns a list of property identifiers that are recognized by
187      * this component. This method may return null if no properties
188      * are recognized by this component.
189      */

190     public String JavaDoc[] getRecognizedProperties() {
191         return merge(super.getRecognizedProperties(), RECOGNIZED_PROPERTIES);
192     } // getRecognizedProperties():String[]
193

194     /**
195      * Returns the default value for a property, or null if this
196      * component does not want to report a default value for this
197      * property.
198      */

199     public Object JavaDoc getPropertyDefault(String JavaDoc propertyId) {
200         for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) {
201             if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) {
202                 return PROPERTY_DEFAULTS[i];
203             }
204         }
205         return super.getPropertyDefault(propertyId);
206     } // getPropertyDefault(String):Object
207

208     /**
209      * Resets the component. The component can query the component manager
210      * about any features and properties that affect the operation of the
211      * component.
212      *
213      * @param manager The component manager.
214      *
215      * @throws XNIException Thrown by component on initialization error.
216      */

217     public void reset(XMLComponentManager manager)
218         throws XMLConfigurationException {
219         super.reset(manager);
220
221         // features
222
fNamespaces = manager.getFeature(NAMESPACES);
223         fOverrideNamespaces = manager.getFeature(OVERRIDE_NAMESPACES);
224         fInsertNamespaces = manager.getFeature(INSERT_NAMESPACES);
225
226         // get properties
227
fNamesElems = getNamesValue(String.valueOf(manager.getProperty(NAMES_ELEMS)));
228         fNamesAttrs = getNamesValue(String.valueOf(manager.getProperty(NAMES_ATTRS)));
229         fNamespacesURI = String.valueOf(manager.getProperty(NAMESPACES_URI));
230     
231         // initialize state
232
fNamespaceContext.reset();
233
234     } // reset(XMLComponentManager)
235

236     //
237
// XMLDocumentHandler methods
238
//
239

240     /** Start document. */
241     public void startDocument(XMLLocator locator, String JavaDoc encoding,
242                               NamespaceContext nscontext, Augmentations augs)
243         throws XNIException {
244         
245         // perform default handling
246
// NOTE: using own namespace context
247
super.startDocument(locator,encoding,fNamespaceContext,augs);
248
249     } // startDocument(XMLLocator,String,NamespaceContext,Augmentations)
250

251     /** Start element. */
252     public void startElement(QName element, XMLAttributes attrs,
253                              Augmentations augs) throws XNIException {
254         
255         // bind namespaces, if needed
256
if (fNamespaces) {
257             fNamespaceContext.pushContext();
258             bindNamespaces(element, attrs);
259
260             int dcount = fNamespaceContext.getDeclaredPrefixCount();
261             if (fDocumentHandler != null && dcount > 0) {
262                 try {
263                     Class JavaDoc cls = fDocumentHandler.getClass();
264                     Class JavaDoc[] types = { String JavaDoc.class, String JavaDoc.class };
265                     Method JavaDoc method = cls.getMethod("startPrefixMapping", types);
266                     for (int i = 0; i < dcount; i++) {
267                         String JavaDoc prefix = fNamespaceContext.getDeclaredPrefixAt(i);
268                         String JavaDoc uri = fNamespaceContext.getURI(prefix);
269                         Object JavaDoc[] args = { prefix, uri };
270                         method.invoke(fDocumentHandler, args);
271                     }
272                 }
273                 catch (NoSuchMethodException JavaDoc e) {
274                     // ignore
275
}
276                 catch (InvocationTargetException JavaDoc e) {
277                     // ignore
278
}
279                 catch (IllegalAccessException JavaDoc e) {
280                     // ignore
281
}
282             }
283         }
284
285         // perform default handling
286
super.startElement(element, attrs, augs);
287
288     } // startElement(QName,XMLAttributes,Augmentations)
289

290     /** Empty element. */
291     public void emptyElement(QName element, XMLAttributes attrs,
292                              Augmentations augs) throws XNIException {
293         
294         // bind namespaces, if needed
295
if (fNamespaces) {
296             fNamespaceContext.pushContext();
297             bindNamespaces(element, attrs);
298
299             int dcount = fNamespaceContext.getDeclaredPrefixCount();
300             if (fDocumentHandler != null && dcount > 0) {
301                 try {
302                     Class JavaDoc cls = fDocumentHandler.getClass();
303                     Class JavaDoc[] types = { String JavaDoc.class, String JavaDoc.class };
304                     Method JavaDoc method = cls.getMethod("startPrefixMapping", types);
305                     for (int i = 0; i < dcount; i++) {
306                         String JavaDoc prefix = fNamespaceContext.getDeclaredPrefixAt(i);
307                         String JavaDoc uri = fNamespaceContext.getURI(prefix);
308                         Object JavaDoc[] args = { prefix, uri };
309                         method.invoke(fDocumentHandler, args);
310                     }
311                 }
312                 catch (NoSuchMethodException JavaDoc e) {
313                     // ignore
314
}
315                 catch (InvocationTargetException JavaDoc e) {
316                     // ignore
317
}
318                 catch (IllegalAccessException JavaDoc e) {
319                     // ignore
320
}
321             }
322         }
323
324         // perform default handling
325
super.emptyElement(element, attrs, augs);
326
327         // pop context
328
if (fNamespaces) {
329             int dcount = fNamespaceContext.getDeclaredPrefixCount();
330             if (fDocumentHandler != null && dcount > 0) {
331                 try {
332                     Class JavaDoc cls = fDocumentHandler.getClass();
333                     Class JavaDoc[] types = { String JavaDoc.class };
334                     Method JavaDoc method = cls.getMethod("endPrefixMapping", types);
335                     for (int i = dcount-1; i >= 0; i--) {
336                         String JavaDoc prefix = fNamespaceContext.getDeclaredPrefixAt(i);
337                         Object JavaDoc[] args = { prefix };
338                         method.invoke(fDocumentHandler, args);
339                     }
340                 }
341                 catch (NoSuchMethodException JavaDoc e) {
342                     // ignore
343
}
344                 catch (InvocationTargetException JavaDoc e) {
345                     // ignore
346
}
347                 catch (IllegalAccessException JavaDoc e) {
348                     // ignore
349
}
350             }
351             
352             fNamespaceContext.popContext();
353         }
354
355     } // startElement(QName,XMLAttributes,Augmentations)
356

357     /** End element. */
358     public void endElement(QName element, Augmentations augs)
359         throws XNIException {
360         
361         // bind namespaces, if needed
362
if (fNamespaces) {
363             bindNamespaces(element, null);
364         }
365
366         // perform default handling
367
super.endElement(element, augs);
368
369         // pop context
370
if (fNamespaces) {
371             int dcount = fNamespaceContext.getDeclaredPrefixCount();
372             if (fDocumentHandler != null && dcount > 0) {
373                 try {
374                     Class JavaDoc cls = fDocumentHandler.getClass();
375                     Class JavaDoc[] types = { String JavaDoc.class };
376                     Method JavaDoc method = cls.getMethod("endPrefixMapping", types);
377                     for (int i = dcount-1; i >= 0; i--) {
378                         String JavaDoc prefix = fNamespaceContext.getDeclaredPrefixAt(i);
379                         Object JavaDoc[] args = { prefix };
380                         method.invoke(fDocumentHandler, args);
381                     }
382                 }
383                 catch (NoSuchMethodException JavaDoc e) {
384                     // ignore
385
}
386                 catch (InvocationTargetException JavaDoc e) {
387                     // ignore
388
}
389                 catch (IllegalAccessException JavaDoc e) {
390                     // ignore
391
}
392             }
393             
394             fNamespaceContext.popContext();
395         }
396
397     } // endElement(QName,Augmentations)
398

399     //
400
// Protected static methods
401
//
402

403     /** Splits a qualified name. */
404     protected static void splitQName(QName qname) {
405         int index = qname.rawname.indexOf(':');
406         if (index != -1) {
407             qname.prefix = qname.rawname.substring(0,index);
408             qname.localpart = qname.rawname.substring(index+1);
409         }
410     } // splitQName(QName)
411

412     /**
413      * Converts HTML names string value to constant value.
414      *
415      * @see #NAMES_NO_CHANGE
416      * @see #NAMES_LOWERCASE
417      * @see #NAMES_UPPERCASE
418      */

419     protected static final short getNamesValue(String JavaDoc value) {
420         if (value.equals("lower")) { return NAMES_LOWERCASE; }
421         if (value.equals("upper")) { return NAMES_UPPERCASE; }
422         return NAMES_NO_CHANGE;
423     } // getNamesValue(String):short
424

425     /** Modifies the given name based on the specified mode. */
426     protected static final String JavaDoc modifyName(String JavaDoc name, short mode) {
427         switch (mode) {
428             case NAMES_UPPERCASE: return name.toUpperCase();
429             case NAMES_LOWERCASE: return name.toLowerCase();
430         }
431         return name;
432     } // modifyName(String,short):String
433

434     //
435
// Protected methods
436
//
437

438     /** Binds namespaces. */
439     protected void bindNamespaces(QName element, XMLAttributes attrs) {
440
441         // split element qname
442
splitQName(element);
443
444         // declare namespace prefixes
445
int attrCount = attrs != null ? attrs.getLength() : 0;
446         for (int i = attrCount - 1; i >= 0; i--) {
447             attrs.getName(i, fQName);
448             String JavaDoc aname = fQName.rawname;
449             String JavaDoc ANAME = aname.toUpperCase();
450             if (ANAME.startsWith("XMLNS:") || ANAME.equals("XMLNS")) {
451                 int anamelen = aname.length();
452
453                 // get parts
454
String JavaDoc aprefix = anamelen > 5 ? aname.substring(0,5) : null;
455                 String JavaDoc alocal = anamelen > 5 ? aname.substring(6) : aname;
456                 String JavaDoc avalue = attrs.getValue(i);
457                 
458                 // re-case parts and set them back into attributes
459
if (anamelen > 5) {
460                     aprefix = modifyName(aprefix, NAMES_LOWERCASE);
461                     alocal = modifyName(alocal, fNamesElems);
462                     aname = aprefix + ':' + alocal;
463                 }
464                 else {
465                     alocal = modifyName(alocal, NAMES_LOWERCASE);
466                     aname = alocal;
467                 }
468                 fQName.setValues(aprefix, alocal, aname, null);
469                 attrs.setName(i, fQName);
470
471                 // declare prefix
472
String JavaDoc prefix = alocal != aname ? alocal : "";
473                 String JavaDoc uri = avalue.length() > 0 ? avalue : null;
474                 if (fOverrideNamespaces &&
475                     prefix.equals(element.prefix) &&
476                     HTMLElements.getElement(element.localpart, null) != null) {
477                     uri = fNamespacesURI;
478                 }
479                 fNamespaceContext.declarePrefix(prefix, uri);
480             }
481         }
482
483         // bind element
484
String JavaDoc prefix = element.prefix != null ? element.prefix : "";
485         element.uri = fNamespaceContext.getURI(prefix);
486         // REVISIT: The prefix of a qualified element name that is
487
// bound to a namespace is passed (as recent as
488
// Xerces 2.4.0) as "" for start elements and null
489
// for end elements. Why? One of them is a bug,
490
// clearly. -Ac
491
if (element.uri != null && element.prefix == null) {
492             element.prefix = "";
493         }
494
495         // do we need to insert namespace bindings?
496
if (fInsertNamespaces &&
497             HTMLElements.getElement(element.localpart,null) != null) {
498             if (element.prefix == null ||
499                 fNamespaceContext.getURI(element.prefix) == null) {
500                 String JavaDoc xmlns = "xmlns" + ((element.prefix != null)
501                              ? ":"+element.prefix : "");
502                 fQName.setValues(null, xmlns, xmlns, null);
503                 attrs.addAttribute(fQName, "CDATA", fNamespacesURI);
504                 bindNamespaces(element, attrs);
505                 return;
506             }
507         }
508
509         // bind attributes
510
attrCount = attrs != null ? attrs.getLength() : 0;
511         for (int i = 0; i < attrCount; i++) {
512             attrs.getName(i, fQName);
513             splitQName(fQName);
514             prefix = !fQName.rawname.equals("xmlns")
515                    ? (fQName.prefix != null ? fQName.prefix : "") : "xmlns";
516             // PATCH: Joseph Walton
517
if (!prefix.equals("")) {
518                 fQName.uri = prefix.equals("xml") ? XML_URI : fNamespaceContext.getURI(prefix);
519             }
520             // NOTE: You would think the xmlns namespace would be handled
521
// by NamespaceSupport but it's not. -Ac
522
if (prefix.equals("xmlns") && fQName.uri == null) {
523                 fQName.uri = XMLNS_URI;
524             }
525             attrs.setName(i, fQName);
526         }
527
528     } // bindNamespaces(QName,XMLAttributes)
529

530     //
531
// Classes
532
//
533

534     /**
535      * This namespace context object implements the old and new XNI
536      * <code>NamespaceContext</code> interface methods so that it can
537      * be used across all versions of Xerces2.
538      */

539     public static class NamespaceSupport
540         implements NamespaceContext {
541
542         //
543
// Data
544
//
545

546         /** Top of the levels list. */
547         protected int fTop = 0;
548
549         /** The levels of the entries. */
550         protected int[] fLevels = new int[10];
551
552         /** The entries. */
553         protected Entry[] fEntries = new Entry[10];
554
555         //
556
// Constructors
557
//
558

559         /** Default constructor. */
560         public NamespaceSupport() {
561             pushContext();
562             declarePrefix("xml", NamespaceContext.XML_URI);
563             declarePrefix("xmlns", NamespaceContext.XMLNS_URI);
564         } // <init>()
565

566         //
567
// NamespaceContext methods
568
//
569

570         // since Xerces 2.0.0-beta2 (old XNI namespaces)
571

572         /** Get URI. */
573         public String JavaDoc getURI(String JavaDoc prefix) {
574             for (int i = fLevels[fTop]-1; i >= 0; i--) {
575                 Entry entry = (Entry)fEntries[i];
576                 if (entry.prefix.equals(prefix)) {
577                     return entry.uri;
578                 }
579             }
580             return null;
581         } // getURI(String):String
582

583         /** Get declared prefix count. */
584         public int getDeclaredPrefixCount() {
585             return fLevels[fTop] - fLevels[fTop-1];
586         } // getDeclaredPrefixCount():int
587

588         /** Get declared prefix at. */
589         public String JavaDoc getDeclaredPrefixAt(int index) {
590             return fEntries[fLevels[fTop-1] + index].prefix;
591         } // getDeclaredPrefixAt(int):String
592

593         /** Get parent context. */
594         public NamespaceContext getParentContext() {
595             return this;
596         } // getParentContext():NamespaceContext
597

598         // since Xerces #.#.# (new XNI namespaces)
599

600         /** Reset. */
601         public void reset() {
602             fLevels[fTop = 1] = fLevels[fTop-1];
603         } // reset()
604

605         /** Push context. */
606         public void pushContext() {
607             if (++fTop == fLevels.length) {
608                 int[] iarray = new int[fLevels.length + 10];
609                 System.arraycopy(fLevels, 0, iarray, 0, fLevels.length);
610                 fLevels = iarray;
611             }
612             fLevels[fTop] = fLevels[fTop-1];
613         } // pushContext()
614

615         /** Pop context. */
616         public void popContext() {
617             fTop--;
618         } // popContext()
619

620         /** Declare prefix. */
621         public boolean declarePrefix(String JavaDoc prefix, String JavaDoc uri) {
622             int count = getDeclaredPrefixCount();
623             for (int i = 0; i < count; i++) {
624                 String JavaDoc dprefix = getDeclaredPrefixAt(i);
625                 if (dprefix.equals(prefix)) {
626                     return false;
627                 }
628             }
629             Entry entry = new Entry(prefix, uri);
630             if (fLevels[fTop] == fEntries.length) {
631                 Entry[] earray = new Entry[fEntries.length + 10];
632                 System.arraycopy(fEntries, 0, earray, 0, fEntries.length);
633                 fEntries = earray;
634             }
635             fEntries[fLevels[fTop]++] = entry;
636             return true;
637         } // declarePrefix(String,String):boolean
638

639         /** Get prefix. */
640         public String JavaDoc getPrefix(String JavaDoc uri) {
641             for (int i = fLevels[fTop]-1; i >= 0; i--) {
642                 Entry entry = (Entry)fEntries[i];
643                 if (entry.uri.equals(uri)) {
644                     return entry.prefix;
645                 }
646             }
647             return null;
648         } // getPrefix(String):String
649

650         /** Get all prefixes. */
651         public Enumeration JavaDoc getAllPrefixes() {
652             Vector JavaDoc prefixes = new Vector JavaDoc();
653             for (int i = fLevels[1]; i < fLevels[fTop]; i++) {
654                 String JavaDoc prefix = fEntries[i].prefix;
655                 if (!prefixes.contains(prefix)) {
656                     prefixes.addElement(prefix);
657                 }
658             }
659             return prefixes.elements();
660         } // getAllPrefixes():Enumeration
661

662         //
663
// Classes
664
//
665

666         /** A namespace binding entry. */
667         static class Entry {
668
669             //
670
// Data
671
//
672

673             /** Prefix. */
674             public String JavaDoc prefix;
675
676             /** URI. */
677             public String JavaDoc uri;
678
679             //
680
// Constructors
681
//
682

683             /** Constructs an entry. */
684             public Entry(String JavaDoc prefix, String JavaDoc uri) {
685                 this.prefix = prefix;
686                 this.uri = uri;
687             } // <init>(String,String)
688

689         } // class Entry
690

691     } // class NamespaceSupport
692

693 } // class NamespaceBinder
694
Popular Tags