001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.mq.xml;
023:
024: import java.util.Enumeration;
025: import java.util.Hashtable;
026: import java.util.Vector;
027:
028: import org.xml.sax.Attributes;
029:
030: /**
031: * XElement provides an interface to an XML element. An XElement represents an
032: * XML element which contains: <br>
033: *
034: * <ul>
035: * <li> Name (required)
036: * <li> Attributes (optional)
037: * <li> character data (optional)
038: * <li> other elements (optional)
039: * </ul>
040: * <p>
041: *
042: * It is important to understand the diffrence between an "field" XElement and
043: * a non "field" XElement. If an XElement does not contain any sub elements, it
044: * is considered a "field" XElement. The <code>getField(String)</code> and
045: * <code>getValue()</code> functions will throw an XElementException if they
046: * are used on non "attribute" objects. This give you a little bit type
047: * checking (You'll get an exception if you try to access the character data of
048: * an element that has sub elements). <p>
049: *
050: * If XElement is not an field, then it contains other XElements and optionaly
051: * some text. The text data can be accessed with the <code>getText()</code>
052: * method and the sub elements with the <code>iterator()</code> or with <code>
053: * getElementXXX()</code> fuctions. Since XML and thus XElements provide a tree
054: * type data structure, traversing the tree to access leaf data can be
055: * cumbersom if you have a 'deep' tree. For example, you may have to do: <code>
056: * element.getElement("tree").getElement("branch").getElement("leaf")</code>
057: * access a XElement 3 levels deep in the tree. To access deep elements easier,
058: * XElements lets you use 'reletive' names to access deep elements. Using
059: * reletive names, you could access the same element in previous example doing:
060: * <code>element.getElement("tree/branch/leaf")</code> When using relative
061: * names, keep in mind that "." will get the current XElement, and ".." will
062: * get parent XElement. Very similar to how URLs work.
063: *
064: * @author Hiram Chirino (Cojonudo14@hotmail.com)
065: * @created August 16, 2001
066: * @version $Revision: 57198 $
067: */
068: public class XElement {
069:
070: private XElement parent = null;
071: private String name = null;
072: private Hashtable metadata = new Hashtable();
073: private Vector contents = new Vector();
074: private final static String nl = System
075: .getProperty("line.separator");
076:
077: /**
078: * Constructs an empty object.
079: *
080: * @param objectName the tag or element name that this object represents.
081: */
082: public XElement(String objectName) {
083: if (objectName == null) {
084: throw new NullPointerException();
085: }
086: name = objectName;
087: contents.addElement(new StringBuffer());
088: }
089:
090: /**
091: * Constructs an XElement with it's parent and metatags set.
092: *
093: * @param objectName the tag or element name that this object represents.
094: * @param atts Description of Parameter
095: */
096: public XElement(String objectName, Attributes atts) {
097: if (objectName == null) {
098: throw new NullPointerException();
099: }
100: if (atts == null) {
101: throw new NullPointerException();
102: }
103: name = objectName;
104: contents.addElement(new StringBuffer());
105: for (int i = 0; i < atts.getLength(); i++) {
106: metadata.put(atts.getQName(i), atts.getValue(i));
107: //metadata.put( atts.getLocalName( i ), atts.getValue( i ) );
108: }
109: }
110:
111: /**
112: * Sets/Adds a metatag value Only metatags whose value is not empty will
113: * display when the <code>toString()</code> methods is called.
114: *
115: * @param key the name of the metatag
116: * @param value the value to set the metatag to.
117: */
118: public void setAttribute(String key, String value) {
119: metadata.put(key, value);
120: }
121:
122: /**
123: * Sets the object name
124: *
125: * @param newName
126: */
127: public void setName(String newName) {
128: name = newName;
129: }
130:
131: /**
132: * Gets the character data that was within this object. This fuction can
133: * only be used on objects that are attributes.
134: *
135: * @param value The new Value value
136: * @returns the character data contained within this
137: * object.
138: * @throws XElementException if the object was not an attribute object
139: */
140: public void setValue(String value) throws XElementException {
141: if (!isField()) {
142: throw new XElementException("" + getName()
143: + " is not an attribute object");
144: }
145: contents.setElementAt(new StringBuffer(value), 0);
146: }
147:
148: /**
149: * Sets/Adds a attribute
150: *
151: * @param key the name of the attribute element
152: * @param value the value to set the attribute to.
153: * @exception XElementException Description of Exception
154: */
155: public void setField(String key, String value)
156: throws XElementException {
157: getElement(key).setValue(value);
158: }
159:
160: /**
161: * Returns the value of a meta data value.
162: *
163: * @param key Description of Parameter
164: * @return The Attribute value
165: * @returns the value of the metadata item, or "" if the item has not
166: * been set.
167: */
168: public String getAttribute(String key) {
169: String t = (String) metadata.get(key);
170: if (t == null) {
171: return "";
172: }
173: return t;
174: }
175:
176: /**
177: * Returns the element name (tag name) of this XElement
178: *
179: * @return The Name value
180: * @returns
181: */
182: public java.lang.String getName() {
183: return name;
184: }
185:
186: /**
187: * Get the parent of this object, or the object the contains this one.
188: *
189: * @return The Parent value
190: * @returns null if this object is not contained by any other XElement.
191: */
192: public XElement getParent() {
193: return parent;
194: }
195:
196: /**
197: * Gets the TRIMMED character data that was within this object. This differs
198: * from getValue() in that:
199: * <UL>
200: * <LI> this fuction will work on attribute and non attribute XElements
201: *
202: * <LI> it will trim both ends of the character data before returning it.
203: *
204: * </UL>
205: *
206: *
207: * @return The Text value
208: * @returns the character data contained within this object.
209: */
210: public String getText() {
211: return contents.elementAt(0).toString().trim();
212: }
213:
214: /**
215: * Gets the character data that was within this object. This fuction can
216: * only be used on objects that are attributes.
217: *
218: * @return The Value value
219: * @returns the character data contained within this
220: * object.
221: * @throws XElementException if the object was not an attribute object
222: */
223: public String getValue() throws XElementException {
224: if (!isField()) {
225: throw new XElementException("" + getName()
226: + " is not an attribute object");
227: }
228: return contents.elementAt(0).toString();
229: }
230:
231: /**
232: * Returns the first object contained in this object named relativeName.
233: *
234: * @param relativeName The name of the object to find
235: * @return The Element value
236: * @returns the XElement named relativeName
237: * @throws XElementException if the object could not be found.
238: */
239: public XElement getElement(String relativeName)
240: throws XElementException {
241: if (relativeName == null) {
242: throw new NullPointerException();
243: }
244:
245: String names[] = { null, relativeName };
246:
247: // Does the name have a "/" in it?
248: String split[] = splitFront(relativeName, "/");
249: if (split != null) {
250:
251: // was it an absolute name? (started with a '/')
252: if (split[0].length() == 0) {
253: // we are the parent
254: if (parent == null) {
255: split[0] = null;
256: }
257: // Let my parent handle the request.
258: else {
259: return parent.getElement(relativeName);
260: }
261: }
262:
263: // did we have a trailing / in the name?
264: if (split[1].length() == 0) {
265: // For the case of "/",
266: if (split[0].equals(null)) {
267: return this ;
268: }
269:
270: //otherwise it is an error
271: // to leave a trailing /, for example tree/leaf/
272: throw new XElementException(
273: "Invalid name (trailing '/') : " + relativeName);
274: }
275:
276: names = split;
277: }
278:
279: if (names[0] == null) {
280: for (int i = 1; i < contents.size(); i++) {
281: XElement o = (XElement) contents.elementAt(i);
282: if (names[1].equals(o.getName())) {
283: return o;
284: }
285: }
286: } else {
287: if (names[0].equals(".")) {
288: return getElement(names[1]);
289: } else if (names[0].equals("..")) {
290: if (parent != null) {
291: return parent.getElement(names[1]);
292: } else {
293: throw new XElementException("Invalid name ("
294: + getName() + " has no parent) : "
295: + relativeName);
296: }
297: } else {
298: for (int i = 1; i < contents.size(); i++) {
299: XElement o = (XElement) contents.elementAt(i);
300: if (names[0].equals(o.getName())) {
301: return o.getElement(names[1]);
302: }
303: }
304: }
305: }
306:
307: throw new XElementException("Invalid name (" + getName()
308: + " does not contain the name) : " + relativeName);
309: }
310:
311: /**
312: * Gets the value of a contained attribute object.
313: *
314: * @param objectName The name of the attribute object.
315: * @return The Field value
316: * @returns the value of the attribute object.
317: * @throws XElementException if the object does not exist or if its not an
318: * attribute object.
319: */
320: public String getField(String objectName) throws XElementException {
321: return getElement(objectName).getValue();
322: }
323:
324: /**
325: * Returns true if the object is an attribute object. An object is an
326: * attribute object if it does not contain any other objects.
327: *
328: * @return The Field value
329: * @returns true if the object is an attribute object.
330: */
331: public boolean isField() {
332: return contents.size() == 1;
333: }
334:
335: /**
336: * Returns all the contained objects with the specified name.
337: *
338: * @param relativeName The name of the objects
339: * @return The ElementsNamed value
340: * @returns whose name is relativeName;
341: */
342: public java.util.Enumeration getElementsNamed(String relativeName) {
343:
344: Vector t = new Vector();
345: addElementsToVector(t, relativeName);
346: return t.elements();
347: }
348:
349: /**
350: * Adds and appends string data to the objects text.
351: *
352: * @param data Description of Parameter
353: */
354: public void add(String data) {
355: ((StringBuffer) contents.elementAt(0)).append(data);
356: }
357:
358: /**
359: * Serializes this object into a string.
360: *
361: * @return Description of the Returned Value
362: */
363: public String toString() {
364: return toString(0, true);
365: }
366:
367: /**
368: * Adds an XElement to the set of XElements that are contained by this
369: * object.
370: *
371: * @param subObject
372: */
373: public void addElement(XElement subObject) {
374: contents.addElement(subObject);
375: subObject.parent = this ;
376: }
377:
378: /**
379: * Adds an XElement to the set of XElements that are contained by this
380: * object.
381: *
382: * @param key The feature to be added to the Field attribute
383: * @param value The feature to be added to the Field attribute
384: */
385: public void addField(String key, String value) {
386: XElement subObject = new XElement(key);
387: subObject.add(value);
388: addElement(subObject);
389: }
390:
391: /**
392: * Tests to see if this object contains the specified object.
393: *
394: * @param objectName The name of the object.
395: * @return Description of the Returned Value
396: * @returns true if the object exits.
397: */
398: public boolean containsElement(String objectName) {
399: try {
400: getElement(objectName);
401: return true;
402: } catch (XElementException e) {
403: return false;
404: }
405: }
406:
407: /**
408: * Tests to see if this object contains the specified attribute object.
409: *
410: * @param objectName The name of the attribute object.
411: * @return Description of the Returned Value
412: * @returns true if the attribute exits.
413: */
414: public boolean containsField(String objectName) {
415: try {
416: XElement obj = getElement(objectName);
417: return obj.isField();
418: } catch (XElementException e) {
419: return false;
420: }
421: }
422:
423: /**
424: * Serializes this object into a string.
425: *
426: * @param nestingLevel how many tabs to prepend to output
427: * @param indent Description of Parameter
428: * @return Description of the Returned Value
429: */
430: public String toString(int nestingLevel, boolean indent) {
431: try {
432: StringBuffer indentation = new StringBuffer();
433: StringBuffer rc = new StringBuffer();
434: if (indent) {
435: for (int i = 0; i < nestingLevel; i++) {
436: indentation.append('\t');
437: }
438: }
439: rc.append(indentation.toString());
440: rc.append("<");
441: rc.append(getName());
442: Enumeration enumeration = metadata.keys();
443: while (enumeration.hasMoreElements()) {
444: String key = (String) enumeration.nextElement();
445: String value = (String) metadata.get(key);
446: rc.append(' ');
447: rc.append(key);
448: rc.append("=\"");
449: rc.append(metaValueEncode(value));
450: rc.append('"');
451: }
452: if (isField()) {
453: if (getValue().length() == 0) {
454: rc.append("/>");
455: rc.append(nl);
456: } else {
457: rc.append('>');
458: rc.append(valueEncode(getValue()));
459: rc.append("</");
460: rc.append(getName());
461: rc.append('>');
462: rc.append(nl);
463: }
464: } else {
465: rc.append('>');
466: rc.append(nl);
467: String text = getText();
468: if (text.length() > 0) {
469: rc.append(indentation.toString() + "\t");
470: rc.append(getText());
471: rc.append(nl);
472: }
473: for (int i = 1; i < contents.size(); i++) {
474: Object o = contents.elementAt(i);
475: rc.append(((XElement) o).toString(nestingLevel + 1,
476: indent));
477: }
478: rc.append(indentation.toString());
479: rc.append("</");
480: rc.append(getName());
481: rc.append('>');
482: rc.append(nl);
483: }
484: return rc.toString();
485: } catch (XElementException e) {
486: // This should not occur!
487: e.printStackTrace();
488: System.exit(1);
489: return "";
490: }
491: }
492:
493: /**
494: * Serializes this object into a XML document String.
495: *
496: * @param indent Description of Parameter
497: * @return Description of the Returned Value
498: */
499: public String toXML(boolean indent) {
500: return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + nl
501: + toString(0, indent);
502: }
503:
504: /**
505: * Removes this XElement from the parent.
506: *
507: * @throws XElementException if the object did not have a parent
508: */
509: public void removeFromParent() throws XElementException {
510: if (parent == null) {
511: throw new XElementException("" + getName()
512: + " does not have a parent");
513: }
514:
515: parent.contents.remove(this );
516: parent = null;
517: }
518:
519: /**
520: * @return Description of the Returned Value
521: * @returns an Enumeration of all the XElement conatained within this
522: * object.
523: */
524: public Enumeration elements() {
525: return getElementsNamed("*");
526: }
527:
528: /**
529: * adds all the contains elements to the vector that match the relative
530: * name.
531: *
532: * @param t The feature to be added to the ElementsToVector
533: * attribute
534: * @param relativeName The feature to be added to the ElementsToVector
535: * attribute
536: */
537: private void addElementsToVector(Vector t, String relativeName) {
538:
539: String names[] = { null, relativeName };
540:
541: // Does the name have a "/" in it?
542: String split[] = splitFront(relativeName, "/");
543: if (split != null) {
544:
545: // was it an absolute name? (started with a '/')
546: if (split[0].length() == 0) {
547: // we are the parent
548: if (parent == null) {
549: split[0] = null;
550: } else {
551: // Let my parent handle the request.
552: parent.addElementsToVector(t, relativeName);
553: return;
554: }
555: }
556:
557: // did we have a trailing / in the name?
558: if (split[1].length() == 0) {
559: return;
560: }
561: names = split;
562: }
563:
564: if (names[0] == null) {
565: if (names[1].equals("*")) {
566: for (int i = 1; i < contents.size(); i++) {
567: t.addElement(contents.elementAt(i));
568: }
569: } else {
570: for (int i = 1; i < contents.size(); i++) {
571: XElement o = (XElement) contents.elementAt(i);
572: if (names[1].equals(o.getName())) {
573: t.addElement(o);
574: }
575: }
576: }
577: } else {
578: if (names[0].equals(".")) {
579: addElementsToVector(t, names[1]);
580: return;
581: } else if (names[0].equals("..")) {
582: if (parent != null) {
583: parent.addElementsToVector(t, names[1]);
584: }
585: return;
586: } else {
587: for (int i = 1; i < contents.size(); i++) {
588: XElement o = (XElement) contents.elementAt(i);
589: if (names[0].equals(o.getName())) {
590: o.addElementsToVector(t, names[1]);
591: }
592: }
593: }
594: }
595: }
596:
597: /**
598: * Constructs an empty object.
599: *
600: * @param is Description of Parameter
601: * @return Description of the Returned Value
602: * @exception XElementException Description of Exception
603: * @exception java.io.IOException Description of Exception
604: */
605: public static XElement createFrom(java.io.InputStream is)
606: throws XElementException, java.io.IOException {
607: class MyRecordConsumer implements XElementConsumer {
608:
609: XElement root = null;
610:
611: public void documentEndEvent() {
612: }
613:
614: public void documentStartEvent() {
615: }
616:
617: public void recordReadEvent(XElement o) {
618: root = o;
619: }
620: }
621:
622: MyRecordConsumer consumer = new MyRecordConsumer();
623: XElementProducer producer = new XElementProducer(consumer);
624:
625: try {
626: producer.parse(is);
627: if (consumer.root == null) {
628: throw new XElementException("No root element");
629: }
630: return consumer.root;
631: } catch (java.io.IOException e) {
632: throw e;
633: } catch (Exception e) {
634: throw new XElementException("Parse Error: " + e);
635: }
636: }
637:
638: /**
639: * Constructs an empty object.
640: *
641: * @param url Description of Parameter
642: * @return Description of the Returned Value
643: * @exception XElementException Description of Exception
644: * @exception java.io.IOException Description of Exception
645: */
646: public static XElement createFrom(java.net.URL url)
647: throws XElementException, java.io.IOException {
648: class MyRecordConsumer implements XElementConsumer {
649:
650: XElement root = null;
651:
652: public void documentEndEvent() {
653: }
654:
655: public void documentStartEvent() {
656: }
657:
658: public void recordReadEvent(XElement o) {
659: root = o;
660: }
661: }
662:
663: MyRecordConsumer consumer = new MyRecordConsumer();
664: XElementProducer producer = new XElementProducer(consumer);
665:
666: try {
667: producer.parse(url);
668: if (consumer.root == null) {
669: throw new XElementException("No root element");
670: }
671: return consumer.root;
672: } catch (java.io.IOException e) {
673: throw e;
674: } catch (Exception e) {
675: throw new XElementException("Parse Error: " + e);
676: }
677: }
678:
679: private static String findAndReplace(String value,
680: String searchStr, String replaceStr) {
681: StringBuffer buffer = new StringBuffer(value.length());
682: while (value.length() > 0) {
683: int pos = value.indexOf(searchStr);
684: if (pos != -1) {
685: buffer.append(value.substring(0, pos));
686: buffer.append(replaceStr);
687: if (pos + searchStr.length() < value.length()) {
688: value = value.substring(pos + searchStr.length());
689: } else {
690: value = "";
691: }
692: } else {
693: buffer.append(value);
694: value = "";
695: }
696: }
697: return buffer.toString();
698: }
699:
700: private static String metaValueEncode(String value) {
701: value = findAndReplace(value, "&", "&");
702: value = findAndReplace(value, "\"", """);
703: value = findAndReplace(value, "'", "'");
704: return utf8Encode(value);
705: }
706:
707: private static String utf8Encode(String value) {
708: try {
709: //char buff[] = new char[value.length()];
710: //value.getChars( 0, buff.length, buff, 0 );
711: //sun.io.CharToByteUTF8 conv = new sun.io.CharToByteUTF8();
712: //byte b[] = conv.convertAll( buff );
713: return new String(value.getBytes("UTF-8"));
714: } catch (Exception e) {
715: return null;
716: }
717: }
718:
719: private static String valueEncode(String value) {
720: value = findAndReplace(value, "&", "&");
721: value = findAndReplace(value, "<", "<");
722: value = findAndReplace(value, ">", ">");
723: return utf8Encode(value);
724: }
725:
726: private static String[] splitFront(String string, String splitMarker) {
727:
728: if (string == null || splitMarker == null) {
729: throw new NullPointerException();
730: }
731:
732: String front;
733: String back;
734:
735: int pos = string.indexOf(splitMarker);
736: if (pos == -1) {
737: return null;
738: }
739:
740: int l = splitMarker.length();
741: front = string.substring(0, pos);
742: if (pos + l >= string.length()) {
743: back = "";
744: } else {
745: back = string.substring(pos + l);
746: }
747:
748: String rc[] = { front, back };
749: return rc;
750: }
751:
752: public String getOptionalField(String field)
753: throws XElementException {
754: if (!containsField(field))
755: return null;
756: return getField(field);
757: }
758:
759: public void setOptionalField(String field, String value)
760: throws XElementException {
761: if (value == null) {
762: if (containsField(field))
763: getElement(field).removeFromParent();
764: return;
765: }
766: if (containsField(field))
767: setField(field, value);
768: else
769: addField(field, value);
770: }
771:
772: }
|