001: //The contents of this file are subject to the Mozilla Public License Version 1.1
002: //(the "License"); you may not use this file except in compliance with the
003: //License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
004: //
005: //Software distributed under the License is distributed on an "AS IS" basis,
006: //WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
007: //for the specific language governing rights and
008: //limitations under the License.
009: //
010: //The Original Code is "The Columba Project"
011: //
012: //The Initial Developers of the Original Code are Frederik Dietz and Timo Stich.
013: //Portions created by Frederik Dietz and Timo Stich are Copyright (C) 2003.
014: //
015: //All Rights Reserved.
016: package org.columba.core.xml;
017:
018: /////////////////////////////////////////////////////////////////////////////
019: // IMPORT STATEMENTS //
020: /////////////////////////////////////////////////////////////////////////////
021: import java.util.Enumeration;
022: import java.util.Hashtable;
023: import java.util.Iterator;
024: import java.util.List;
025: import java.util.Observable;
026: import java.util.Vector;
027:
028: /////////////////////////////////////////////////////////////////////////////
029: // CODE //
030: /////////////////////////////////////////////////////////////////////////////
031:
032: /**
033: * The XmlElement is a generic containment class for elements within an XML
034: * file.
035: * <p>
036: *
037: * It extends Observable which should be used for gui elements which are
038: * interested in configuration changes.
039: * <p>
040: *
041: * Show interested in:
042: *
043: * <pre>
044: * xmlElement.addObserver(yourObserver);
045: * </pre>
046: *
047: * <p>
048: * When making bigger changes on XmlElement and probably its subnodes and/or a
049: * greater number of attributes at once, you should just change XmlElement
050: * directly and manually notify the Observers by calling:
051: * <p>
052: *
053: * <pre>
054: * xmlElement.setChanged();
055: * xmlElement.notifyObservers();
056: * </pre>
057: *
058: * <p>
059: * There a good introduction for the Observable/Observer pattern in
060: * Model/View/Controller based applications at www.javaworld.com: -
061: * http://www.javaworld.com/javaworld/jw-10-1996/jw-10-howto.html
062: *
063: * @see org.columba.mail.gui.config.general.MailOptionsDialog#initObservables()
064: * @see org.columba.mail.gui.message.viewer.MarkAsReadTimer
065: *
066: * @author Tony Parent, fdietz
067: */
068: public class XmlElement extends Observable implements Cloneable {
069:
070: private static final java.util.logging.Logger LOG = java.util.logging.Logger
071: .getLogger("org.columba.core.xml"); //$NON-NLS-1$
072:
073: String name;
074:
075: String data;
076:
077: Hashtable attributes;
078:
079: List subElements;
080:
081: XmlElement parent;
082:
083: /**
084: * **FIXME** This function needs documentation
085: *
086: * Constructor
087: *
088: */
089: public XmlElement() {
090: subElements = new Vector();
091: this .attributes = new Hashtable(10);
092: }
093:
094: /**
095: * **FIXME** This function needs documentation
096: *
097: * Constructor
098: *
099: * @param String
100: * Name
101: *
102: */
103: public XmlElement(String name) {
104: this .name = name;
105: this .attributes = new Hashtable(10);
106: subElements = new Vector();
107: data = "";
108: }
109:
110: /**
111: * **FIXME** This function needs documentation
112: *
113: * Constructor
114: *
115: * @param String
116: * Name
117: * @param Hashtable
118: * Attributes
119: *
120: */
121: public XmlElement(String name, Hashtable attributes) {
122: this .name = name;
123: this .attributes = attributes;
124: subElements = new Vector();
125: }
126:
127: /**
128: * **FIXME** This function needs documentation
129: *
130: * Constructor
131: *
132: * @param Name
133: * String
134: * @param Data
135: * String
136: *
137: */
138: public XmlElement(String name, String data) {
139: this .name = name;
140: this .data = data;
141: subElements = new Vector();
142: this .attributes = new Hashtable(10);
143: }
144:
145: /**
146: * Add attribute to this xml element.
147: *
148: * @param name
149: * name of key
150: * @param value
151: * new attribute value
152: * @return old attribute value
153: *
154: */
155: public Object addAttribute(String name, String value) {
156: if ((value != null) && (name != null)) {
157: Object returnValue = (attributes.put(name, value));
158:
159: return returnValue;
160: }
161:
162: return null;
163: }
164:
165: /**
166: * **FIXME** This function needs documentation
167: *
168: * @return String
169: * @param String
170: * Name
171: *
172: */
173: public String getAttribute(String name) {
174: return ((String) attributes.get(name));
175: }
176:
177: public String getAttribute(String name, String defaultValue) {
178: if (getAttribute(name) == null) {
179: addAttribute(name, defaultValue);
180: }
181:
182: return getAttribute(name);
183: }
184:
185: /**
186: * **FIXME** This function needs documentation
187: *
188: * @return String
189: * @param String
190: * Name
191: *
192: */
193: public Hashtable getAttributes() {
194: return attributes;
195: }
196:
197: /**
198: * **FIXME** This function needs documentation
199: *
200: *
201: * @param Attrs
202: * Hashtable to use as the attributes
203: *
204: */
205: public void setAttributes(Hashtable attrs) {
206: attributes = attrs;
207: }
208:
209: /**
210: * **FIXME** This function needs documentation
211: *
212: * @return Enumeration
213: *
214: */
215: public Enumeration getAttributeNames() {
216: return (attributes.keys());
217: }
218:
219: /**
220: * **FIXME** This function needs documentation
221: *
222: * @return boolean
223: * @param XmlElement
224: * E
225: *
226: */
227: public boolean addElement(XmlElement e) {
228: e.setParent(this );
229:
230: return (subElements.add(e));
231: }
232:
233: public XmlElement removeElement(XmlElement e) {
234: XmlElement child = null;
235:
236: for (int i = 0; i < subElements.size(); i++) {
237: child = (XmlElement) subElements.get(i);
238:
239: // FIXME -- This will most likely not work.
240: // You want the element removed if the contents are the same
241: // Not just if the element reference is the same.
242: if (child == e) {
243: subElements.remove(i);
244: }
245: }
246:
247: return (child);
248: }
249:
250: public XmlElement removeElement(int index) {
251: return (XmlElement) subElements.remove(index);
252: }
253:
254: public void removeAllElements() {
255: subElements.clear();
256: }
257:
258: /**
259: * convienience method for the TreeView
260: *
261: * this method is modeled after the DefaultMutableTreeNode-class
262: *
263: * DefaultMutableTreeNode wraps XmlElement for this purpose
264: *
265: */
266: public void removeFromParent() {
267: if (parent == null) {
268: return;
269: }
270:
271: parent.removeElement(this );
272: parent = null;
273: }
274:
275: public void append(XmlElement e) {
276: e.removeFromParent();
277:
278: addElement(e);
279: }
280:
281: /**
282: *
283: * convienience method for the TreeView
284: *
285: * @param e
286: * @param index
287: */
288: public void insertElement(XmlElement e, int index) {
289: e.removeFromParent();
290:
291: subElements.add(index, e);
292: e.setParent(this );
293: }
294:
295: /**
296: * **FIXME** This function needs documentation
297: *
298: * @return Vector
299: *
300: */
301: public List getElements() {
302: return subElements;
303: }
304:
305: public int count() {
306: return subElements.size();
307: }
308:
309: /**
310: * **FIXME** This function needs documentation
311: *
312: * @return XmlElement
313: * @param String
314: * Path
315: *
316: */
317: public XmlElement getElement(String path) {
318: int i = path.indexOf('/');
319: String topName;
320: String subName;
321:
322: if (i == 0) {
323: path = path.substring(1);
324: i = path.indexOf('/');
325: }
326:
327: if (i > 0) {
328: topName = path.substring(0, i);
329: subName = path.substring(i + 1);
330: } else {
331: topName = path;
332: subName = null;
333: }
334:
335: int j;
336:
337: for (j = 0; j < subElements.size(); j++) {
338: if (((XmlElement) subElements.get(j)).getName().equals(
339: topName)) {
340: if (subName != null) {
341: return (((XmlElement) subElements.get(j))
342: .getElement(subName));
343: } else {
344: return ((XmlElement) subElements.get(j));
345: }
346: }
347: }
348:
349: return null;
350: }
351:
352: public XmlElement getElement(int index) {
353: return (XmlElement) subElements.get(index);
354: }
355:
356: /**
357: * Adds a sub element to this one
358: *
359: * @return XmlElement
360: * @param Name
361: * The subpath of the sub element to add
362: *
363: */
364: public XmlElement addSubElement(String path) {
365: XmlElement parent = this ;
366: XmlElement child;
367: String name;
368:
369: while (path.indexOf('/') != -1) {
370: name = path.substring(0, path.indexOf('/'));
371: path = path.substring(path.indexOf('/') + 1);
372:
373: // if path startsWith "/" -> skip
374: if (name.length() == 0)
375: continue;
376:
377: if (parent.getElement(name) != null) {
378: parent = parent.getElement(name);
379: } else {
380: child = new XmlElement(name);
381:
382: parent.addElement(child);
383: parent = child;
384: }
385:
386: }
387:
388: child = new XmlElement(path);
389: parent.addElement(child);
390:
391: return child;
392: }
393:
394: /**
395: * Adds a sub element to this one
396: *
397: * @return XmlElement
398: * @param element
399: * The XmlElement to add
400: *
401: */
402: public XmlElement addSubElement(XmlElement e) {
403: e.setParent(this );
404: subElements.add(e);
405:
406: return e;
407: }
408:
409: /**
410: * Adds a sub element to this one
411: *
412: * @return XmlElement
413: * @param Name
414: * The name of the sub element to add
415: * @param Data
416: * String Data for this element
417: */
418: public XmlElement addSubElement(String name, String data) {
419: XmlElement e = new XmlElement(name);
420: e.setData(data);
421: e.setParent(this );
422: subElements.add(e);
423:
424: return e;
425: }
426:
427: /**
428: * Sets the parent element
429: *
430: * @param Parent
431: * The XmlElement that contains this one
432: *
433: */
434: public void setParent(XmlElement parent) {
435: this .parent = parent;
436: }
437:
438: /**
439: * Gives the XmlElement containing the current element
440: *
441: * @return XmlElement
442: *
443: */
444: public XmlElement getParent() {
445: return parent;
446: }
447:
448: /**
449: * Sets the data for this element
450: *
451: * @param D
452: * The String representation of the data
453: *
454: */
455: public void setData(String d) {
456: data = d;
457: }
458:
459: /**
460: * Returns the data associated with the current Xml element
461: *
462: * @return String
463: *
464: */
465: public String getData() {
466: return data;
467: }
468:
469: /**
470: * Returns the name of the current Xml element
471: *
472: * @return String
473: *
474: */
475: public String getName() {
476: return name;
477: }
478:
479: /**
480: * **FIXME** This function needs documentation
481: *
482: * @param out
483: * OutputStream to print the data to
484: *
485: */
486:
487: /*
488: * public void write(OutputStream out) throws IOException { PrintWriter PW =
489: * new PrintWriter(out); PW.println(" <?xml version=\"1.0\"
490: * encoding=\"UTF-8\"?>"); if (SubElements.size() > 0) { for (int i = 0; i <
491: * SubElements.size(); i++) { ((XmlElement)
492: * SubElements.get(i))._writeSubNode(PW, 4); } } PW.flush(); }
493: */
494:
495: /**
496: * Prints sub nodes to the given data stream
497: *
498: * @param out
499: * PrintWriter to use for printing
500: * @param indent
501: * Number of spaces to indent things
502: *
503: */
504:
505: /*
506: * private void _writeSubNode(PrintWriter out, int indent) throws
507: * IOException { _writeSpace(out, indent); out.print(" <" + Name); //if (
508: * Attributes.size()>1) out.print(" ");
509: *
510: * for (Enumeration e = Attributes.keys(); e.hasMoreElements();) { String K =
511: * (String) e.nextElement(); out.print(K + "=\"" + Attributes.get(K) + "\"
512: * b");
513: * } out.print(">");
514: *
515: * if (Data != null && !Data.equals("")) { if (Data.length() > 20) {
516: * out.println(""); _writeSpace(out, indent + 2); } out.print(Data); } if
517: * (SubElements.size() > 0) { out.println(""); for (int i = 0; i <
518: * SubElements.size(); i++) { ((XmlElement)
519: * SubElements.get(i))._writeSubNode( out, indent + 4); } _writeSpace(out,
520: * indent); } out.println(" </" + Name + ">");
521: * }
522: */
523:
524: /**
525: * Prints out a given number of spaces
526: *
527: * @param out
528: * PrintWriter to use for printing
529: * @param numSpaces
530: * Number of spaces to print
531: *
532: */
533:
534: /*
535: * private void _writeSpace(PrintWriter out, int numSpaces) throws
536: * IOException {
537: *
538: * for (int i = 0; i < numSpaces; i++) out.print(" "); }
539: *
540: * public static void printNode(XmlElement Node, String indent) { String
541: * Data = Node.getData(); if (Data == null || Data.equals("")) {
542: * System.out.println(indent + Node.getName()); } else {
543: * System.out.println(indent + Node.getName() + " = '" + Data + "'"); }
544: * Vector Subs = Node.getElements(); int i, j; for (i = 0; i < Subs.size();
545: * i++) { printNode((XmlElement) Subs.get(i), indent + " "); } }
546: */
547: public static void printNode(XmlElement node, String indent) {
548: String data = node.getData();
549:
550: if ((data == null) || data.equals("")) {
551: LOG.info(indent + node.getName());
552: } else {
553: LOG.info(indent + node.getName() + " = '" + data + "'"); //$NON-NLS-1$
554: }
555:
556: // print attributes
557: for (Enumeration enumeration = node.getAttributes().keys(); enumeration
558: .hasMoreElements();) {
559: String key = (String) enumeration.nextElement();
560: String value = node.getAttribute(key);
561: LOG.info(indent + key + ":" + value); //$NON-NLS-1$
562: }
563:
564: List subs = node.getElements();
565:
566: for (Iterator it = subs.iterator(); it.hasNext();) {
567: printNode((XmlElement) it.next(), indent + " ");
568:
569: // for (i = 0; i < subs.size(); i++) {
570: // printNode((XmlElement) subs.get(i), indent + " ");
571: }
572: }
573:
574: /** {@inheritDoc} */
575: public Object clone() {
576: try {
577: XmlElement clone = (XmlElement) super .clone(); // creates a shallow
578: // copy of this
579: // object
580:
581: if (attributes != null) {
582: clone
583: .setAttributes((Hashtable) getAttributes()
584: .clone());
585: }
586:
587: if (subElements != null) {
588: clone.subElements = new Vector();
589:
590: List childs = getElements();
591: XmlElement child;
592:
593: for (Iterator it = childs.iterator(); it.hasNext();) {
594: child = (XmlElement) it.next();
595:
596: // for( int i=0; i<childs.size(); i++ ) {
597: // child = (XmlElement) childs.get(i);
598: clone.addSubElement((XmlElement) child.clone());
599: }
600: }
601:
602: return clone;
603: } catch (CloneNotSupportedException cnse) {
604: throw new InternalError("Could not clone XmlElement: "
605: + cnse);
606: }
607: }
608:
609: /**
610: * Sets the name.
611: *
612: * @param name
613: * The name to set
614: */
615: public void setName(String name) {
616: this .name = name;
617: }
618:
619: /**
620: * Notify all Observers.
621: *
622: * @see java.util.Observable#notifyObservers()
623: */
624: public void notifyObservers() {
625: setChanged();
626: super .notifyObservers();
627: }
628:
629: /**
630: * Returns true if the specified objects are equal. They are equal if they
631: * are both null OR if the <code>equals()</code> method return true. (
632: * <code>obj1.equals(obj2)</code>).
633: *
634: * @param obj1
635: * first object to compare with.
636: * @param obj2
637: * second object to compare with.
638: * @return true if they represent the same object; false if one of them is
639: * null or the <code>equals()</code> method returns false.
640: */
641: private boolean equals(Object obj1, Object obj2) {
642: boolean equal = false;
643:
644: if ((obj1 == null) && (obj2 == null)) {
645: equal = true;
646: } else if ((obj1 != null) && (obj2 != null)) {
647: equal = obj1.equals(obj2);
648: }
649:
650: return equal;
651: }
652:
653: /** {@inheritDoc} */
654: public boolean equals(Object obj) {
655: boolean equal = false;
656:
657: if ((obj != null) && (obj instanceof XmlElement)) {
658: XmlElement other = (XmlElement) obj;
659:
660: if (equals(attributes, other.attributes)
661: && equals(data, other.data)
662: && equals(name, other.name)
663: && equals(subElements, other.subElements)) {
664: equal = true;
665: }
666: }
667:
668: return equal;
669: }
670:
671: /** {@inheritDoc} */
672: public int hashCode() {
673: //Hashcode value should be buffered.
674: int hashCode = 23;
675:
676: if (attributes != null) {
677: hashCode += (attributes.hashCode() * 13);
678: }
679:
680: if (data != null) {
681: hashCode += (data.hashCode() * 17);
682: }
683:
684: if (name != null) {
685: hashCode += (name.hashCode() * 29);
686: }
687:
688: if (subElements != null) {
689: hashCode += (subElements.hashCode() * 57);
690: }
691:
692: return hashCode;
693: }
694:
695: /**
696: * @see java.lang.Object#toString()
697: */
698: public String toString() {
699: return getName();
700: }
701: }
702:
703: // END public class XmlElement
|