001: /* IDOMs.java
002:
003: {{IS_NOTE
004:
005: Purpose:
006: Description:
007: History:
008: C91/01/08 10:37:16, reate, Tom M. Yeh
009: }}IS_NOTE
010:
011: Copyright (C) 2001 Potix Corporation. All Rights Reserved.
012:
013: {{IS_RIGHT
014: This program is distributed under GPL Version 2.0 in the hope that
015: it will be useful, but WITHOUT ANY WARRANTY.
016: }}IS_RIGHT
017: */
018: package org.zkoss.idom.util;
019:
020: import java.lang.reflect.Field;
021: import java.io.PrintWriter;
022: import java.io.PrintStream;
023: import java.io.StringWriter;
024: import java.util.List;
025: import java.util.LinkedList;
026: import java.util.Map;
027: import java.util.LinkedHashMap;
028: import java.util.Collection;
029: import java.util.Iterator;
030: import java.util.ListIterator;
031: import java.net.URL;
032: import java.io.File;
033: import javax.xml.transform.stream.StreamResult;
034: import javax.xml.transform.TransformerConfigurationException;
035: import javax.xml.transform.TransformerException;
036:
037: import org.zkoss.mesg.MCommon;
038: import org.zkoss.mesg.Messages;
039: import org.zkoss.lang.Classes;
040: import org.zkoss.lang.Objects;
041: import org.zkoss.lang.SystemException;
042: import org.zkoss.util.Locales;
043: import org.zkoss.util.IllegalSyntaxException;
044: import org.zkoss.util.resource.Locator;
045: import org.zkoss.util.logging.Log;
046:
047: import org.zkoss.idom.*;
048: import org.zkoss.idom.input.SAXBuilder;
049: import org.zkoss.idom.transform.Transformer;
050:
051: /**
052: * The iDOM relevant utilities.
053: *
054: * @author tomyeh
055: * @see org.zkoss.idom.Item
056: * @see org.zkoss.idom.Group
057: */
058: public class IDOMs {
059: private static final Log log = Log.lookup(IDOMs.class);
060:
061: /** Returns the required element.
062: * @param elemnm the element name
063: */
064: public static final Element getRequiredElement(Element e,
065: String elemnm) throws IllegalSyntaxException {
066: final Element sub = e.getElement(elemnm);
067: if (sub == null)
068: throw new IllegalSyntaxException(
069: MCommon.XML_ELEMENT_REQUIRED, new Object[] {
070: elemnm, e.getLocator() });
071: return sub;
072: }
073:
074: /** Returns the required element value.
075: * <p>Note: the returned value may be an empty string (if the element
076: * contains no text at all).
077: * @exception IllegalSyntaxException if the element is not found
078: */
079: public static final String getRequiredElementValue(Element e,
080: String elemnm) throws IllegalSyntaxException {
081: final Element sub = e.getElement(elemnm);
082: if (sub == null)
083: throw new IllegalSyntaxException(
084: MCommon.XML_ELEMENT_REQUIRED, new Object[] {
085: elemnm, e.getLocator() });
086: return sub.getText(true);
087: }
088:
089: /** Returns the required attribute value.
090: * @exception IllegalSyntaxException if the element is not found
091: */
092: public static final String getRequiredAttributeValue(Element e,
093: String attrnm) throws IllegalSyntaxException {
094: final Attribute attr = e.getAttributeItem(attrnm);
095: if (attr == null)
096: throw new IllegalSyntaxException(
097: MCommon.XML_ATTRIBUTE_REQUIRED, new Object[] {
098: attrnm, e.getLocator() });
099: return attr.getValue();
100: }
101:
102: /** Returns the first child element, or null if no child element at all.
103: */
104: public static final Element getFirstElement(Group group) {
105: final Iterator it = group.getElements().iterator();
106: return it.hasNext() ? (Element) it.next() : null;
107: }
108:
109: /** Returns the first element whose sub-element called "name" has the
110: * same content as the name argument, or null if not found.
111: *
112: * @param elems a list of elements to look for the specified name
113: */
114: public static final Element findElement(List elems, String name) {
115: for (final Iterator it = elems.iterator(); it.hasNext();) {
116: final Element e = (Element) it.next();
117: if (Objects.equals(name, e.getElementValue("name", true)))
118: return e;
119: }
120: return null;
121: }
122:
123: /** Parses a tree of parameter elements into a map.
124: *
125: * <p>The tree of parameter elements is as follows.
126: * <pre><code>
127: * <type>
128: * <name>any</name>
129: * <vaue>any</vaue>
130: * </type>
131: *
132: * @return the map after parsed (never null). An empty map is returned
133: * if no parameter is defined. The map is in the same order of the element tree.
134: */
135: public static final Map parseParams(Element elm, String type,
136: String name, String value) {
137: final Map map = new LinkedHashMap();
138: for (Iterator it = elm.getElements(type).iterator(); it
139: .hasNext();) {
140: final Element el = (Element) it.next();
141: final String nm = getRequiredElementValue(el, name);
142: final String val = getRequiredElementValue(el, value);
143: map.put(nm, val);
144: }
145: return map;
146: }
147:
148: /** Formats the specified element for better readability by
149: * adding white spaces.
150: */
151: public static void format(Element e) {
152: //add proper spacing between consecutive elements
153: boolean elemFound = true;
154: for (final ListIterator it = e.getChildren().listIterator(); it
155: .hasNext();) {
156: final Object o = it.next();
157: if (o instanceof Element) {
158: if (elemFound) { //insert space
159: it.previous();
160: it.add(new Text("\n\t"));
161: it.next();
162: } else {
163: elemFound = true;
164: }
165:
166: format((Element) o); //recursive
167: } else {
168: elemFound = false;
169: }
170: }
171: }
172:
173: /** Converts elements to their contents if the giving object is
174: * an element or an array or a collection of elements.
175: * One item of an collection might be another collection or array.
176: */
177: public static final Object toContents(Object obj) {
178: if (obj instanceof Collection) {
179: Collection c = (Collection) obj;
180: boolean cvted = false;
181: Collection rets = new LinkedList();
182: for (Iterator it = c.iterator(); it.hasNext();) {
183: Object o = it.next();
184: Object o2 = toContents(o); //recursive
185: if (o != o2)
186: cvted = true;
187: rets.add(o2);
188: }
189:
190: if (cvted)
191: return rets;
192: } else if (obj instanceof Object[]) {
193: Object[] ary = (Object[]) obj;
194: boolean cvted = false;
195: Object[] rets = new Object[ary.length];
196: for (int j = 0; j < ary.length; ++j) {
197: Object o2 = toContents(ary[j]); //recursive
198: if (ary[j] != o2)
199: cvted = true;
200: rets[j] = o2;
201: }
202: if (cvted)
203: return rets;
204: } else if (obj instanceof Element) {
205: return ((Element) obj).getContent();
206: }
207: return obj;
208: }
209:
210: /** Set the contents of elements.
211: * The val argument could be an array and a collection, and each
212: * item will be assigned to each of the list one-by-one.
213: *
214: * <p>Unlike {@link #toContents}, it handles only a collection of elements
215: * -- all items must be elements.
216: *
217: * @param elems the collection of elements
218: * @param val the value which could be an object, an array or a collection
219: */
220: public static final void setContents(Collection elems, Object val) {
221: Object[] ary = null;
222: if (val instanceof Object[]) {
223: ary = (Object[]) val;
224: } else if (val instanceof Collection) {
225: ary = ((Collection) val).toArray();
226: } else {
227: ary = new Object[] { val };
228: }
229:
230: Iterator it = elems.iterator();
231: for (int j = 0; it.hasNext(); ++j) {
232: ((Element) it.next()).setContent(j < ary.length ? ary[j]
233: : null);
234: }
235: }
236:
237: /** Transforms a document to a string.
238: * The string is XML correct.
239: */
240: public final static String toString(Document doc)
241: throws TransformerConfigurationException,
242: TransformerException {
243: final StringWriter writer = new StringWriter();
244: new Transformer().transform(doc, new StreamResult(writer));
245: return writer.toString();
246: }
247:
248: /**
249: * Print a readable tree of the specified group to System.out.
250: * It is for debug purpose and the generated format is <i>not</i> XML.
251: * To generate XML, uses {@link Transformer} or {@link #toString}.
252: * @see #toString
253: */
254: public static final void dumpTree(Group group) {
255: dumpTree(System.out, group);
256: }
257:
258: /**
259: * Print a readable tree of the specified group to the specified stream.
260: * It is for debug purpose and the generated format is <i>not</i> XML.
261: * To generate XML, uses {@link Transformer} or {@link #toString}.
262: * @see #toString
263: */
264: public static final void dumpTree(PrintStream s, Group group) {
265: dumpTree(new PrintWriter(s, true), group);
266: }
267:
268: /**
269: * Print a readable tree of the specified group to the specified writer.
270: * It is for debug purpose and the generated format is <i>not</i> XML.
271: * To generate XML, uses {@link Transformer} or {@link #toString}.
272: * @see #toString
273: */
274: public static final void dumpTree(PrintWriter s, Group group) {
275: dumpTree(s, group, "");
276: }
277:
278: private static final void dumpTree(PrintWriter s, Item vtx,
279: String prefix) {
280: s.print(prefix);
281: s.print(vtx);
282: s.print(vtx.isReadonly() ? 'R' : ' ');
283: s.println(vtx.isModified() ? 'M' : ' ');
284: if (vtx instanceof Group) {
285: prefix = prefix + " ";
286: for (Iterator it = ((Group) vtx).getChildren().iterator(); it
287: .hasNext();)
288: dumpTree(s, (Item) it.next(), prefix);
289: }
290: }
291:
292: /** Returnss whether the loaded document's version is correct.
293: *
294: * <p>It assumes the version info is specified in the document in
295: * the following format:
296: *
297: * <pre></code>
298: <version>
299: <version-class>org.zkoss.zul.Version</version-class>
300: <version-uid>3.0.0</version-uid>
301: </version>
302: </code></pre>
303: *
304: * <p>Note: it returns true if the version info is not found.
305: *
306: * @param doc the document to check
307: * @param url the URL used to show the readable message if the
308: * version doesn't match
309: * @since 3.0.0
310: */
311: public static boolean checkVersion(Document doc, URL url)
312: throws Exception {
313: final Element el = doc.getRootElement().getElement("version");
314: if (el != null) {
315: final String clsnm = IDOMs.getRequiredElementValue(el,
316: "version-class");
317: final String uid = IDOMs.getRequiredElementValue(el,
318: "version-uid");
319: final Class cls = Classes.forNameByThread(clsnm);
320: final Field fld = cls.getField("UID");
321: final String uidInClass = (String) fld.get(null);
322: if (uid.equals(uidInClass)) {
323: return true;
324: } else {
325: log.info("Ignore " + url
326: + "\nCause: version not matched; expected="
327: + uidInClass + ", xml=" + uid);
328: return false;
329: }
330: } else {
331: return true;
332: }
333: }
334: }
|