001: /* DefinitionLoaders.java
002:
003: {{IS_NOTE
004: Purpose:
005:
006: Description:
007:
008: History:
009: Mon Aug 29 21:57:08 2005, Created by tomyeh
010: }}IS_NOTE
011:
012: Copyright (C) 2005 Potix Corporation. All Rights Reserved.
013:
014: {{IS_RIGHT
015: This program is distributed under GPL Version 2.0 in the hope that
016: it will be useful, but WITHOUT ANY WARRANTY.
017: }}IS_RIGHT
018: */
019: package org.zkoss.zk.ui.metainfo;
020:
021: import java.lang.reflect.Field;
022: import java.util.Iterator;
023: import java.util.ListIterator;
024: import java.util.List;
025: import java.util.LinkedList;
026: import java.util.Set;
027: import java.util.HashSet;
028: import java.util.Map;
029: import java.util.HashMap;
030: import java.util.Enumeration;
031: import java.net.URL;
032: import java.io.IOException;
033:
034: import org.zkoss.lang.D;
035: import org.zkoss.lang.Classes;
036: import org.zkoss.lang.Strings;
037: import org.zkoss.util.Utils;
038: import org.zkoss.util.logging.Log;
039: import org.zkoss.util.resource.Locator;
040: import org.zkoss.util.resource.ClassLocator;
041: import org.zkoss.idom.Document;
042: import org.zkoss.idom.Element;
043: import org.zkoss.idom.Attribute;
044: import org.zkoss.idom.ProcessingInstruction;
045: import org.zkoss.idom.input.SAXBuilder;
046: import org.zkoss.idom.util.IDOMs;
047: import org.zkoss.xel.taglib.Taglib;
048: import org.zkoss.web.servlet.JavaScript;
049: import org.zkoss.web.servlet.StyleSheet;
050:
051: import org.zkoss.zk.Version;
052: import org.zkoss.zk.ui.UiException;
053: import org.zkoss.zk.ui.metainfo.impl.*;
054: import org.zkoss.zk.ui.impl.Attributes;
055: import org.zkoss.zk.scripting.Interpreters;
056: import org.zkoss.zk.device.Devices;
057: import org.zkoss.zk.au.AuWriters;
058:
059: /**
060: * Utilities to load language definitions.
061: *
062: * @author tomyeh
063: */
064: public class DefinitionLoaders {
065: private static final Log log = Log.lookup(DefinitionLoaders.class);
066:
067: private static final int MAX_VERSION_SEGMENT = 4;
068: private static List _addons;
069: /** A map of (String ext, String lang). */
070: private static Map _exts;
071: private static boolean _loaded, _loading;
072:
073: //CONSIDER:
074: //Sotre language definitions per WebApp, since diff app may add its
075: //own definitions thru WEB-INF's lang-addon, or a JAR in WEB-INF/lib.
076: //
077: //CONSEQUENCE:
078: //Other Web app is affected by another in the current implementation
079: //
080: //WORKAROUND:
081: //Copy ZK libraries into WEB-INF/lib
082: //
083: //DIFFICULTY TO SUPPORT
084: //To support it we have to pass WebApp around. It is a challenge for
085: //1) a working thread (other than servlet/event thread);
086: //2) deserialize LanguageDefinition (and maybe ComponentDefinition)
087:
088: /** Adds a language addon.
089: */
090: public static void addAddon(Locator locator, URL url) {
091: if (locator == null || url == null)
092: throw new IllegalArgumentException("null");
093:
094: if (_loaded) {
095: loadAddon(locator, url);
096: } else {
097: if (_addons == null)
098: _addons = new LinkedList();
099: _addons.add(new Object[] { locator, url });
100: }
101: }
102:
103: /** Associates an extension to a language.
104: *
105: * @param lang the language name. It cannot be null.
106: * @param ext the extension, e.g., "svg". It cannot be null.
107: * @since 3.0.0
108: */
109: public static final void addExtension(String ext, String lang) {
110: if (_loaded) {
111: LanguageDefinition.addExtension(ext, lang);
112: } else {
113: if (lang == null || ext == null)
114: throw new IllegalArgumentException("null");
115: if (_exts == null)
116: _exts = new HashMap();
117: _exts.put(ext, lang);
118: }
119: }
120:
121: /** Loads all config.xml, lang.xml and lang-addon.xml found in
122: * the /metainfo/zk path.
123: *
124: * <p>Remember to call {@link #addAddon}, if necessary, before
125: * calling this method.
126: */
127: /*package*/static void load() {
128: if (!_loaded) {
129: synchronized (DefinitionLoaders.class) {
130: if (!_loaded && !_loading) {
131: try {
132: _loading = true; //prevent re-entry for the same thread
133: load0();
134: } finally {
135: _loaded = true; //only once
136: _loading = false;
137: }
138: }
139: }
140: }
141: }
142:
143: private static void load0() {
144: final ClassLocator locator = new ClassLocator();
145:
146: final int[] zkver = new int[MAX_VERSION_SEGMENT];
147: for (int j = 0; j < MAX_VERSION_SEGMENT; ++j)
148: zkver[j] = Utils.getSubversion(Version.UID, j);
149:
150: //1. process config.xml (no particular dependency)
151: try {
152: final List xmls = locator.getDependentXMLResources(
153: "metainfo/zk/config.xml", "config-name", "depends");
154: for (Iterator it = xmls.iterator(); it.hasNext();) {
155: final ClassLocator.Resource res = (ClassLocator.Resource) it
156: .next();
157: if (log.debugable())
158: log.debug("Loading " + res.url);
159: try {
160: if (checkVersion(zkver, res.url, res.document))
161: parseConfig(res.document.getRootElement());
162: } catch (Exception ex) {
163: throw UiException.Aide.wrap(ex, "Failed to load "
164: + res.url);
165: //abort since it is hardly to work then
166: }
167: }
168: } catch (Exception ex) {
169: throw UiException.Aide.wrap(ex); //abort
170: }
171:
172: //2. process lang.xml (no particular dependency)
173: try {
174: for (Enumeration en = locator
175: .getResources("metainfo/zk/lang.xml"); en
176: .hasMoreElements();) {
177: final URL url = (URL) en.nextElement();
178: if (log.debugable())
179: log.debug("Loading " + url);
180: try {
181: final Document doc = new SAXBuilder(false, false,
182: true).build(url);
183: if (checkVersion(zkver, url, doc))
184: parseLang(doc, locator, url, false);
185: } catch (Exception ex) {
186: throw UiException.Aide.wrap(ex, "Failed to load "
187: + url);
188: //abort since it is hardly to work then
189: }
190: }
191: } catch (Exception ex) {
192: throw UiException.Aide.wrap(ex); //abort
193: }
194:
195: //3. process lang-addon.xml (with dependency)
196: try {
197: final List xmls = locator.getDependentXMLResources(
198: "metainfo/zk/lang-addon.xml", "addon-name",
199: "depends");
200: for (Iterator it = xmls.iterator(); it.hasNext();) {
201: final ClassLocator.Resource res = (ClassLocator.Resource) it
202: .next();
203: try {
204: if (checkVersion(zkver, res.url, res.document))
205: parseLang(res.document, locator, res.url, true);
206: } catch (Exception ex) {
207: log.error("Failed to load addon", ex);
208: //keep running
209: }
210: }
211: } catch (Exception ex) {
212: log.error("Failed to load addon", ex);
213: //keep running
214: }
215:
216: //4. process other addon (from addAddon)
217: if (_addons != null) {
218: for (Iterator it = _addons.iterator(); it.hasNext();) {
219: final Object[] p = (Object[]) it.next();
220: loadAddon((Locator) p[0], (URL) p[1]);
221: }
222: _addons = null; //free memory
223: }
224:
225: //5. process the extension
226: if (_exts != null) {
227: for (Iterator it = _exts.entrySet().iterator(); it
228: .hasNext();) {
229: final Map.Entry me = (Map.Entry) it.next();
230: LanguageDefinition.addExtension((String) me.getKey(),
231: (String) me.getValue());
232: }
233: _exts = null;
234: }
235: }
236:
237: /** Loads a language addon.
238: */
239: private static void loadAddon(Locator locator, URL url) {
240: try {
241: parseLang(new SAXBuilder(false, false, true).build(url),
242: locator, url, true);
243: } catch (Exception ex) {
244: log.error("Failed to load addon: " + url, ex);
245: //keep running
246: }
247: }
248:
249: /** Checks and returns whether the loaded document's version is correct.
250: */
251: private static boolean checkVersion(int[] zkver, URL url,
252: Document doc) throws Exception {
253: final Element el = doc.getRootElement().getElement("version");
254: if (el != null) {
255: final String reqzkver = el.getElementValue("zk-version",
256: true);
257: if (reqzkver != null) {
258: for (int j = 0; j < MAX_VERSION_SEGMENT; ++j) {
259: int v = Utils.getSubversion(reqzkver, j);
260: if (v < zkver[j])
261: break; //ok
262: if (v > zkver[j]) {//failed
263: log.info("Ignore " + url
264: + "\nCause: ZK version must be "
265: + zkver + " or later, not "
266: + Version.UID);
267: return false;
268: }
269: }
270: }
271:
272: final String clsnm = IDOMs.getRequiredElementValue(el,
273: "version-class");
274: final String uid = IDOMs.getRequiredElementValue(el,
275: "version-uid");
276: final Class cls = Classes.forNameByThread(clsnm);
277: final Field fld = cls.getField("UID");
278: final String uidInClass = (String) fld.get(null);
279: if (uid.equals(uidInClass)) {
280: return true;
281: } else {
282: log.info("Ignore " + url
283: + "\nCause: version not matched; expected="
284: + uidInClass + ", xml=" + uid);
285: return false;
286: }
287: } else {
288: log
289: .info("Ignore " + url
290: + "\nCause: version not specified");
291: return false; //backward compatible
292: }
293: }
294:
295: private static void parseConfig(Element el) throws Exception {
296: parseZScriptConfig(el);
297: parseDeviceConfig(el);
298: parseSystemConfig(el);
299: parseClientConfig(el);
300: }
301:
302: private static void parseZScriptConfig(Element root) {
303: for (Iterator it = root.getElements("zscript-config")
304: .iterator(); it.hasNext();) {
305: final Element el = (Element) it.next();
306: Interpreters.add(el);
307: //Note: zscript-config is applied to the whole system, not just langdef
308: }
309: }
310:
311: private static void parseDeviceConfig(Element root) {
312: for (Iterator it = root.getElements("device-config").iterator(); it
313: .hasNext();) {
314: final Element el = (Element) it.next();
315: Devices.add(el);
316: }
317: }
318:
319: private static void parseSystemConfig(Element root)
320: throws Exception {
321: final Element el = root.getElement("system-config");
322: if (el != null) {
323: String s = el.getElementValue("au-writer-class", true);
324: if (s != null)
325: AuWriters.setImplementationClass(s.length() == 0 ? null
326: : Classes.forNameByThread(s));
327: }
328: }
329:
330: private static void parseClientConfig(Element root)
331: throws Exception {
332: final Element el = root.getElement("client-config");
333: if (el != null) {
334: Integer v = parseInteger(el, "resend-delay", false);
335: if (v != null)
336: System.setProperty(Attributes.RESEND_DELAY, v
337: .toString());
338: }
339: }
340:
341: private static void parseLang(Document doc, Locator locator,
342: URL url, boolean addon) throws Exception {
343: final Element root = doc.getRootElement();
344: final String lang = IDOMs.getRequiredElementValue(root,
345: "language-name");
346: final LanguageDefinition langdef;
347: if (addon) {
348: if (log.debugable())
349: log.debug("Addon language to " + lang + " from "
350: + root.getElementValue("addon-name", true));
351: langdef = LanguageDefinition.lookup(lang);
352:
353: if (root.getElement("case-insensitive") != null)
354: throw new UiException(
355: "case-insensitive not allowed in addon");
356: } else {
357: final String ns = (String) IDOMs.getRequiredElementValue(
358: root, "namespace");
359: final String deviceType = (String) IDOMs
360: .getRequiredElementValue(root, "device-type");
361:
362: //if (log.debugable()) log.debug("Load language: "+lang+", "+ns);
363:
364: final Map pagemolds = parseMolds(root);
365: final String desktopURI = (String) pagemolds.get("desktop");
366: final String pageURI = (String) pagemolds.get("page");
367: if (desktopURI == null || pageURI == null)
368: throw new UiException(
369: "Both desktop and page molds must be specified, "
370: + root.getLocator());
371: if (desktopURI.startsWith("class:")
372: || pageURI.startsWith("class:"))
373: throw new UiException(
374: "Both desktop and page molds don't support 'class:', "
375: + root.getLocator());
376:
377: final List exts = parseExtensions(root);
378: if (exts.isEmpty())
379: throw new UiException(
380: "The extension must be specified for " + lang);
381:
382: String ignoreCase = root.getElementValue(
383: "case-insensitive", true);
384: String bNative = root.getElementValue("native-namespace",
385: true);
386:
387: langdef = new LanguageDefinition(deviceType, lang, ns,
388: exts, desktopURI, pageURI, "true"
389: .equals(ignoreCase),
390: "true".equals(bNative), locator);
391: }
392:
393: parsePI(langdef, doc);
394: parseLabelTemplate(langdef, root);
395: parseDynamicTag(langdef, root);
396: parseMacroTemplate(langdef, root);
397: parseNativeTemplate(langdef, root);
398:
399: for (Iterator it = root.getElements("javascript").iterator(); it
400: .hasNext();) {
401: final Element el = (Element) it.next();
402: final String src = el.getAttributeValue("src");
403: final String ctn = el.getText(true);
404: final JavaScript js;
405: if (src != null && src.length() > 0) {
406: if (ctn != null && ctn.length() > 0)
407: throw new UiException(
408: "You cannot specify the content if the src attribute is specified, "
409: + el.getLocator());
410: final String charset = el.getAttributeValue("charset");
411: js = new JavaScript(src, charset);
412: } else if (ctn != null && ctn.length() > 0) {
413: js = new JavaScript(ctn);
414: } else {
415: throw new UiException(
416: "You must specify either the src attribute or the content, "
417: + el.getLocator());
418: }
419: langdef.addJavaScript(js);
420: }
421: for (Iterator it = root.getElements("javascript-module")
422: .iterator(); it.hasNext();) {
423: final Element el = (Element) it.next();
424: langdef.addJavaScriptModule(IDOMs
425: .getRequiredAttributeValue(el, "name"), IDOMs
426: .getRequiredAttributeValue(el, "version"));
427: }
428:
429: for (Iterator it = root.getElements("stylesheet").iterator(); it
430: .hasNext();) {
431: final Element el = (Element) it.next();
432: final String href = el.getAttributeValue("href");
433: final String ctn = el.getText(true);
434: final StyleSheet ss;
435: if (href != null && href.length() > 0) {
436: if (ctn != null && ctn.length() > 0)
437: throw new UiException(
438: "You cannot specify the content if the href attribute is specified, "
439: + el.getLocator());
440: ss = new StyleSheet(href, el.getAttributeValue("type"));
441: } else if (ctn != null && ctn.length() > 0) {
442: ss = new StyleSheet(ctn, el.getAttributeValue("type"),
443: true);
444: } else {
445: throw new UiException(
446: "You must specify either the href attribute or the content, "
447: + el.getLocator());
448: }
449: langdef.addStyleSheet(ss);
450: }
451:
452: for (Iterator it = root.getElements("zscript").iterator(); it
453: .hasNext();) {
454: final Element el = (Element) it.next();
455: final String zslang;
456: final Attribute attr = el.getAttributeItem("language");
457: if (attr == null) {
458: zslang = "Java";
459: } else {
460: zslang = attr.getValue();
461: if (zslang == null || zslang.length() == 0)
462: throw new UiException(
463: "The language attribute cannot be empty, "
464: + attr.getLocator());
465: }
466: final String s = el.getText(true);
467: final String eachTime = el.getAttributeValue("each-time");
468: if ("true".equals(eachTime))
469: langdef.addEachTimeScript(zslang, s);
470: else
471: langdef.addInitScript(zslang, s);
472: }
473:
474: for (Iterator it = root.getElements("component").iterator(); it
475: .hasNext();) {
476: final Element el = (Element) it.next();
477: final String name = IDOMs.getRequiredElementValue(el,
478: "component-name");
479:
480: Class cls = null;
481: {
482: String clsnm = el.getElementValue("component-class",
483: true);
484: if (clsnm != null && clsnm.length() > 0) {
485: noEL("component-class", clsnm, el);
486: try {
487: cls = locateClass(clsnm);
488: } catch (Throwable ex) { //Feature 1873426
489: log.warningBriefly("Component " + name
490: + " ignored. Reason: unable to load "
491: + clsnm + ".\n" + el.getLocator(), ex);
492: continue;
493: }
494: }
495: }
496:
497: final String macroURI = el.getElementValue("macro-uri",
498: true);
499: final ComponentDefinitionImpl compdef;
500: if (macroURI != null && macroURI.length() != 0) {
501: if (log.finerable())
502: log.finer("macro component definition: " + name);
503:
504: final String inline = el
505: .getElementValue("inline", true);
506: compdef = (ComponentDefinitionImpl) langdef
507: .getMacroDefinition(name, macroURI, "true"
508: .equals(inline), null);
509:
510: if (cls != null)
511: compdef.setImplementationClass(cls);
512: //resolve it now because it is part of lang-addon
513:
514: compdef.setDeclarationURL(url);
515: langdef.addComponentDefinition(compdef);
516: } else if (el.getElement("extends") != null) { //override
517: if (log.finerable())
518: log.finer("Override component definition: " + name);
519:
520: final String extnm = el
521: .getElementValue("extends", true);
522: final ComponentDefinition ref = langdef
523: .getComponentDefinitionIfAny(extnm);
524: if (ref == null) {
525: log
526: .warning("Component "
527: + name
528: + " ignored. Reason: override a non-existent component "
529: + extnm + ".\n" + el.getLocator());
530: //not throw exception since the derived component might be
531: //ignored due to class-not-found
532: continue;
533: }
534:
535: if (ref.isMacro())
536: throw new UiException(
537: "Unable to extend from a macro component, "
538: + el.getLocator());
539:
540: if (extnm.equals(name)) {
541: compdef = (ComponentDefinitionImpl) ref;
542: } else {
543: compdef = (ComponentDefinitionImpl) ref.clone(ref
544: .getLanguageDefinition(), name);
545: compdef.setDeclarationURL(url);
546: langdef.addComponentDefinition(compdef);
547: }
548:
549: if (cls != null)
550: compdef.setImplementationClass(cls);
551: } else {
552: if (log.finerable())
553: log.finer("Add component definition: name=" + name);
554:
555: if (cls == null)
556: throw new UiException(
557: "component-class is required, "
558: + el.getLocator());
559: compdef = new ComponentDefinitionImpl(langdef, null,
560: name, cls);
561: compdef.setDeclarationURL(url);
562: langdef.addComponentDefinition(compdef);
563: }
564:
565: final String textAs = el.getElementValue("text-as", true);
566: if (textAs != null) { //empty means cleanup (for overriding)
567: noEL("text-as", textAs, el);
568: compdef.setTextAs(textAs);
569: }
570:
571: for (Iterator e = parseMolds(el).entrySet().iterator(); e
572: .hasNext();) {
573: final Map.Entry me = (Map.Entry) e.next();
574: compdef.addMold((String) me.getKey(), (String) me
575: .getValue());
576: }
577:
578: for (Iterator e = parseCustAttrs(el).entrySet().iterator(); e
579: .hasNext();) {
580: final Map.Entry me = (Map.Entry) e.next();
581: compdef.addCustomAttribute((String) me.getKey(),
582: (String) me.getValue());
583: }
584:
585: for (Iterator e = parseProps(el).entrySet().iterator(); e
586: .hasNext();) {
587: final Map.Entry me = (Map.Entry) e.next();
588: compdef.addProperty((String) me.getKey(), (String) me
589: .getValue());
590: }
591:
592: parseAnnots(compdef, el);
593: }
594: }
595:
596: private static Class locateClass(String clsnm) throws Exception {
597: try {
598: return Classes.forNameByThread(clsnm);
599: } catch (ClassNotFoundException ex) {
600: throw new ClassNotFoundException("Not found: " + clsnm, ex);
601: }
602: }
603:
604: private static void noEL(String nm, String val, Element el)
605: throws UiException {
606: if (val != null && val.indexOf("${") >= 0)
607: throw new UiException(nm
608: + " does not support EL expressions, "
609: + el.getLocator());
610: }
611:
612: /** Parse the processing instructions. */
613: private static void parsePI(LanguageDefinition langdef, Document doc)
614: throws Exception {
615: for (Iterator it = doc.getChildren().iterator(); it.hasNext();) {
616: final Object o = it.next();
617: if (!(o instanceof ProcessingInstruction))
618: continue;
619:
620: final ProcessingInstruction pi = (ProcessingInstruction) o;
621: final String target = pi.getTarget();
622: final Map params = pi.parseData();
623: if ("taglib".equals(target)) {
624: final String uri = (String) params.remove("uri");
625: final String prefix = (String) params.remove("prefix");
626: if (!params.isEmpty())
627: log.warning("Ignored unknown attribute: " + params
628: + ", " + pi.getLocator());
629: if (uri == null || prefix == null)
630: throw new UiException(
631: "Both uri and prefix attribute are required, "
632: + pi.getLocator());
633: if (log.debugable())
634: log.debug("taglib: prefix=" + prefix + " uri="
635: + uri);
636: langdef.addTaglib(new Taglib(prefix, uri));
637: } else {
638: log
639: .warning("Unknown processing instruction: "
640: + target);
641: }
642: }
643: }
644:
645: /** Parse the component used to represent a label.
646: */
647: private static void parseLabelTemplate(LanguageDefinition langdef,
648: Element el) {
649: el = el.getElement("label-template");
650: if (el != null) {
651: final Element raw = el.getElement("raw");
652: langdef.setLabelTemplate(IDOMs.getRequiredElementValue(el,
653: "component-name"), IDOMs.getRequiredElementValue(
654: el, "component-attribute"), raw != null
655: && !"false".equals(raw.getText(true)));
656: }
657: }
658:
659: private static void parseMacroTemplate(LanguageDefinition langdef,
660: Element el) throws Exception {
661: el = el.getElement("macro-template");
662: if (el != null) {
663: langdef.setMacroTemplate(locateClass(IDOMs
664: .getRequiredElementValue(el, "macro-class")), IDOMs
665: .getRequiredElementValue(el, "macro-uri"));
666: }
667: }
668:
669: private static void parseNativeTemplate(LanguageDefinition langdef,
670: Element el) throws Exception {
671: el = el.getElement("native-template");
672: if (el != null) {
673: langdef.setNativeTemplate(locateClass(IDOMs
674: .getRequiredElementValue(el, "native-class")));
675: }
676: }
677:
678: private static void parseDynamicTag(LanguageDefinition langdef,
679: Element el) throws ClassNotFoundException {
680: el = el.getElement("dynamic-tag");
681: if (el != null) {
682: final String compnm = IDOMs.getRequiredElementValue(el,
683: "component-name");
684: final Set reservedAttrs = new HashSet(5);
685: for (Iterator it = el.getElements("reserved-attribute")
686: .iterator(); it.hasNext();)
687: reservedAttrs.add(((Element) it.next()).getText(true));
688: langdef.setDynamicTagInfo(compnm, reservedAttrs);
689: }
690: //if (log.finerable()) log.finer(el);
691: }
692:
693: private static List parseExtensions(Element elm) {
694: final List exts = new LinkedList();
695: for (Iterator it = elm.getElements("extension").iterator(); it
696: .hasNext();) {
697: final Element el = (Element) it.next();
698: final String ext = el.getText(true);
699: if (ext.length() != 0) {
700: for (int j = 0, len = ext.length(); j < len; ++j) {
701: final char cc = ext.charAt(j);
702: if ((cc < 'a' || cc > 'z')
703: && (cc < 'A' || cc > 'Z')
704: && (cc < '0' || cc > '9'))
705: throw new UiException(
706: "Invalid extension; only letters and numbers are allowed: "
707: + ext);
708: }
709: exts.add(ext);
710: }
711: }
712: ///if (log.finerable()) log.finer(exts);
713: return exts;
714: }
715:
716: private static Map parseProps(Element elm) {
717: return IDOMs.parseParams(elm, "property", "property-name",
718: "property-value");
719: }
720:
721: private static Map parseMolds(Element elm) {
722: return IDOMs.parseParams(elm, "mold", "mold-name", "mold-uri");
723: }
724:
725: private static Map parseCustAttrs(Element elm) {
726: return IDOMs.parseParams(elm, "custom-attribute",
727: "attribute-name", "attribute-value");
728: }
729:
730: private static Map parseAttrs(Element elm) {
731: return IDOMs.parseParams(elm, "attribute", "attribute-name",
732: "attribute-value");
733: }
734:
735: private static void parseAnnots(ComponentDefinitionImpl compdef,
736: Element top) {
737: for (Iterator it = top.getElements("annotation").iterator(); it
738: .hasNext();) {
739: final Element el = (Element) it.next();
740: final String annotName = IDOMs.getRequiredElementValue(el,
741: "annotation-name");
742: final Map annotAttrs = parseAttrs(el);
743: final String prop = el.getElementValue("property-name",
744: true);
745: if (prop == null || prop.length() == 0)
746: compdef.addAnnotation(annotName, annotAttrs);
747: else
748: compdef.addAnnotation(prop, annotName, annotAttrs);
749: }
750: }
751:
752: /** Configures an integer. */
753: private static Integer parseInteger(Element el, String subnm,
754: boolean positiveOnly) throws UiException {
755: //Warning instead of exception since config.xml is embedded in jar, so
756: //better not to stop the process
757: String val = el.getElementValue(subnm, true);
758: if (val != null && val.length() > 0) {
759: try {
760: final int v = Integer.parseInt(val);
761: if (!positiveOnly || v > 0)
762: return new Integer(v);
763: log.warning("Ignored: the " + subnm
764: + " element must be a positive number, not "
765: + val + ", at " + el.getLocator());
766: } catch (NumberFormatException ex) { //eat
767: log.warning("Ignored: the " + subnm
768: + " element must be a number, not " + val
769: + ", at " + el.getLocator());
770: }
771: }
772: return null;
773: }
774:
775: private static class Addon {
776: private final Document document;
777: private final int priority;
778:
779: private Addon(Document document) {
780: this .document = document;
781:
782: final String p = document.getRootElement().getElementValue(
783: "priority", true);
784: this .priority = p != null && p.length() > 0 ? Integer
785: .parseInt(p) : 0;
786: }
787:
788: private static void add(List addons, Document document) {
789: final Addon addon = new Addon(document);
790: for (ListIterator it = addons.listIterator(); it.hasNext();) {
791: final Addon a = (Addon) it.next();
792: if (a.priority < addon.priority) {
793: it.previous();
794: it.add(addon);
795: return; //done
796: }
797: }
798: addons.add(addon);
799: }
800: }
801: }
|