001: /* -*- mode: Java; c-basic-offset: 2; -*- */
002:
003: /**
004: * LZX View Compiler
005: */package org.openlaszlo.compiler;
006:
007: import java.io.*;
008: import java.text.DecimalFormat;
009: import java.text.FieldPosition;
010: import java.util.*;
011:
012: import org.openlaszlo.css.CSSParser;
013: import org.openlaszlo.sc.ScriptCompiler;
014: import org.openlaszlo.server.LPS;
015: import org.openlaszlo.xml.internal.Schema;
016: import org.openlaszlo.xml.internal.MissingAttributeException;
017: import org.openlaszlo.utils.ChainedException;
018: import org.openlaszlo.xml.internal.XMLUtils;
019: import org.apache.log4j.*;
020: import java.util.regex.*;
021: import org.openlaszlo.compiler.ViewSchema.ColorFormatException;
022: import org.openlaszlo.utils.FileUtils;
023:
024: import java.io.*;
025: import java.util.*;
026: import org.jdom.Attribute;
027: import org.jdom.Element;
028:
029: /** Responsible for compiling elements that compile into instances of
030: * LzNode. This is called ViewCompiler for historical reasons; it
031: * would more appropriately be called NodeCompiler.
032: *
033: * Node compilation consists of these steps:<ul>
034: * <li> compute text metrics
035: * <li> call the XML compiler to generate bytecodes that recreate the
036: * view template subtree and pass it to a runtime view instantiation
037: * library
038: * </ul>
039: */
040: public class ViewCompiler extends ElementCompiler {
041: // +=2 so you can see cursor at end of line
042: private static final int INPUT_TEXTWIDTH_FUDGE_FACTOR = 2;
043:
044: private static final String FONTSTYLE_ATTRIBUTE = "fontstyle";
045: private static final String WHEN_IMMEDIATELY = "immediately";
046: private static final String WHEN_ONCE = "once";
047: private static final String WHEN_ALWAYS = "always";
048: private static final String WHEN_PATH = "path";
049:
050: private ViewSchema mSchema;
051:
052: private static Logger mLogger = Logger
053: .getLogger(ViewCompiler.class);
054: private static Logger mTraceLogger = Logger.getLogger("trace.xml");
055:
056: public ViewCompiler(CompilationEnvironment env) {
057: super (env);
058: mSchema = env.getSchema();
059: mTraceLogger.setLevel(Level.INFO);
060: }
061:
062: private static final String SERVERLESS_WARNINGS_PROPERTY_FILE = (LPS
063: .getMiscDirectory()
064: + File.separator + "lzx-server-only-apis.properties");
065: private static final Properties sServerOnlyTags = new Properties();
066:
067: static {
068: try {
069: InputStream is = new FileInputStream(
070: SERVERLESS_WARNINGS_PROPERTY_FILE);
071: try {
072: sServerOnlyTags.load(is);
073: } finally {
074: is.close();
075: }
076: } catch (java.io.IOException e) {
077: mLogger.warn("Can't find server-only APIs config file "
078: + SERVERLESS_WARNINGS_PROPERTY_FILE);
079: }
080: }
081:
082: public void compile(Element element) throws CompilationError {
083: preprocess(element, mEnv);
084: FontInfo fontInfo = null;
085:
086: String name = element.getName();
087: if (mEnv.isCanvas()) {
088: if (name != null && sServerOnlyTags.containsKey(name)
089: && !mEnv.getCanvas().isProxied()) {
090: mEnv.warn(
091: /* (non-Javadoc)
092: * @i18n.test
093: * @org-mes="The tag '" + p[0] + "' will not work as expected when used in a serverless application."
094: */
095: org.openlaszlo.i18n.LaszloMessages.getMessage(
096: ViewCompiler.class.getName(), "051018-99",
097: new Object[] { name }));
098: }
099:
100: fontInfo = new FontInfo(mEnv.getCanvas().getFontInfo());
101: try {
102: fontInfo = new FontInfo(mEnv.getCanvas().getFontInfo());
103: mapTextMetricsCompilation(element, mEnv, fontInfo,
104: new HashSet());
105: } catch (NumberFormatException e) {
106: throw new CompilationError(e.getMessage());
107: }
108: }
109: compileXML(element, fontInfo);
110: }
111:
112: /** Returns true if this node applies to the element. Anything
113: * that the interface compiler doesn't recognize is considered a
114: * view node, so this always returns true.
115: * @param element an element
116: * @return see doc
117: */
118: static boolean isElement(Element element) {
119: return true;
120: }
121:
122: /** Collect the names of classes that are referenced. */
123: static void collectElementNames(Element element, Set names) {
124: names.add(element.getName());
125: collectLayoutElement(element, names);
126: for (Iterator iter = element.getChildren().iterator(); iter
127: .hasNext();) {
128: collectElementNames((Element) iter.next(), names);
129: }
130: }
131:
132: static void collectLayoutElement(Element element, Set names) {
133: if (element.getAttributeValue("layout") != null) {
134: try {
135: Map properties = new CSSParser(new AttributeStream(
136: element, "layout")).Parse();
137: String layoutClass = (String) properties.get("class");
138: if (layoutClass == null)
139: layoutClass = "simplelayout";
140: names.add(layoutClass);
141: } catch (org.openlaszlo.css.ParseException e) {
142: } catch (org.openlaszlo.css.TokenMgrError e) {
143: // The compilation phase will report the error.
144: }
145: }
146: }
147:
148: /** Compile a XML element and generate code that binds it to a
149: * runtime data structure named _lzViewTemplate.
150: *
151: * @param element an element
152: * @param fontInfo font info inherited from canvas
153: */
154: void compileXML(Element element, FontInfo fontInfo) {
155: // TODO: [2007-09-03 ptw] (LPP-4634) You should not be able to
156: // get here. This appears to happen when a binary file has
157: // already included a sub-library and that same sub-library is
158: // included by a source file. Since the include is already
159: // loaded, it is not expanded in expandIncludes, but because
160: // ViewCompiler.isElement returns true for everything, we end up
161: // here. For now, just don't do anything.
162: if ("include".equals(element.getName()))
163: return;
164:
165: // TODO: [12-27-2002 ows] use the log4j API instead of this property
166: boolean tracexml = mEnv.getBooleanProperty("trace.xml");
167: if (tracexml) {
168: mTraceLogger.info("compiling XML:");
169: org.jdom.output.XMLOutputter outputter = new org.jdom.output.XMLOutputter();
170: mTraceLogger.info(outputter.outputString(element));
171: }
172:
173: NodeModel model = NodeModel.elementAsModel(element, mSchema,
174: mEnv);
175: model = model.expandClassDefinitions();
176: String script = VIEW_INSTANTIATION_FNAME + "("
177: + model.asJavascript() + ", " + model.totalSubnodes()
178: + ");";
179:
180: // Don't keep non-class models around
181: if (!element.getName().equals("class")) {
182: ((ElementWithLocationInfo) element).model = null;
183: }
184:
185: if (tracexml) {
186: mLogger.debug(
187: /* (non-Javadoc)
188: * @i18n.test
189: * @org-mes="compiled to:\n" + p[0] + "\n"
190: */
191: org.openlaszlo.i18n.LaszloMessages.getMessage(
192: ViewCompiler.class.getName(), "051018-186",
193: new Object[] { script }));
194: }
195: try {
196: mEnv.compileScript(script, element);
197: } catch (CompilationError e) {
198: String solution = SolutionMessages.findSolution(e
199: .getMessage());
200: e.setSolution(solution);
201: throw e;
202: }
203: }
204:
205: /**
206: * Modify the DOM in place, to what the runtime expects. This
207: * function encapsulates the behavior that is common to root
208: * views, and class definitions.
209: *
210: * Preprocessing consists of compiling resources, and turning
211: * view-specific source-format attributes into runtime format
212: *
213: * @param elt an <code>Element</code> value
214: * @param env a <code>CompilationEnvironment</code> value
215: */
216: static void preprocess(Element elt, CompilationEnvironment env) {
217: compileResources(elt, env);
218: compileClickResources(elt, env);
219: compileAttributes(elt, env);
220: }
221:
222: /**
223: * Modify elt and its children to replace source attribute values
224: * by runtime values.
225: */
226: static void compileAttributes(Element elt,
227: CompilationEnvironment env) {
228: if (elt.getName().equals("dataset")) {
229: String src = elt.getAttributeValue("src");
230: if (src == null) {
231: // This is a local dataset. DataCompiler has already
232: // processed it. TBD: move this check to isElement,
233: // and make it an assert since DataCompiler should
234: // have already processed it.
235: return;
236: }
237: src = env.adjustRelativeURL(src, elt);
238: elt.setAttribute("src", src);
239: }
240: Iterator iter;
241: for (iter = elt.getChildren().iterator(); iter.hasNext();) {
242: compileAttributes((Element) iter.next(), env);
243: }
244: }
245:
246: static HashMap sUnsupportedServerlessFiletypes = new HashMap();
247: static HashMap sUnsupportedServerlessFiletypesSWF7 = new HashMap();
248: {
249: sUnsupportedServerlessFiletypes.put("bmp", "true");
250: sUnsupportedServerlessFiletypes.put("tiff", "true");
251: sUnsupportedServerlessFiletypes.put("tif", "true");
252: sUnsupportedServerlessFiletypes.put("wmf", "true");
253: sUnsupportedServerlessFiletypes.put("wmv", "true");
254:
255: sUnsupportedServerlessFiletypesSWF7.put("png", "true");
256: sUnsupportedServerlessFiletypesSWF7.put("gif", "true");
257:
258: }
259:
260: static void checkUnsupportedMediaTypes(CompilationEnvironment env,
261: Element elt, String url) {
262: String suffix = FileUtils.getExtension(url);
263: if (env.isSWF()) {
264: if ((sUnsupportedServerlessFiletypes.containsKey(suffix
265: .toLowerCase()))
266: || (env.getSWFVersionInt() < 8 && sUnsupportedServerlessFiletypesSWF7
267: .containsKey(suffix.toLowerCase()))) {
268: env.warn(
269: /* (non-Javadoc)
270: * @i18n.test
271: * @org-mes="The runtime loadable resource type '" + p[0] + " is not supported by the Flash runtime. Supported resource types are JPEG (non-interlaced), SWF, and MP3"
272: */
273: org.openlaszlo.i18n.LaszloMessages.getMessage(
274: ViewCompiler.class.getName(), "051018-258",
275: new Object[] { url }), elt);
276: }
277: } else {
278: // TODO: [2006-06-19 ptw] Handle media types for DHTML, etc.
279: }
280: }
281:
282: /**
283: * Compiles all resources under the current element
284: *
285: * @param env
286: * @param elt
287: */
288: static void compileResources(Element elt, CompilationEnvironment env) {
289: final String RESOURCE_ATTR_NAME = "resource";
290:
291: // check for immediate <attribute name="resource" .../> children
292: for (Iterator iter = elt.getChildren().iterator(); iter
293: .hasNext();) {
294: Element child = (Element) iter.next();
295: if (child.getName().equals("attribute")
296: && RESOURCE_ATTR_NAME.equals(child
297: .getAttributeValue("name"))) {
298: String val = child.getAttributeValue("value");
299: // You are not allowed declare a resource attribute value twice
300: if (val == null) {
301: continue;
302: } else {
303: String val2 = elt
304: .getAttributeValue(RESOURCE_ATTR_NAME);
305: if (val2 != null) {
306: env.warn(
307: /* (non-Javadoc)
308: * @i18n.test
309: * @org-mes="The resource attribute on this view was declared more than once, as '" + p[0] + "', and as '" + p[1] + "'"
310: */
311: org.openlaszlo.i18n.LaszloMessages.getMessage(
312: ViewCompiler.class.getName(),
313: "051018-292",
314: new Object[] { val2, val }), elt);
315: }
316:
317: // This is needed for backward compatibility with
318: // the deprecated "when='...'" syntax
319: String when = child.getAttributeValue("when");
320: if (when != null) {
321: val = "$" + when + "{" + val + "}";
322: }
323:
324: elt.setAttribute(RESOURCE_ATTR_NAME, val);
325: // remove this <attribute name="resource" .../>
326: // child because we just copied the value to the
327: // parent elt.
328: iter.remove();
329: }
330: }
331: }
332:
333: String value = elt.getAttributeValue(RESOURCE_ATTR_NAME);
334:
335: if (value != null) {
336: if (value.matches("\\s*\\$\\s*\\(")) {
337: env
338: .warn(
339: "The syntax '$(...)' is not valid, "
340: + "you probably meant to use curly-braces instead '${...}'",
341: elt);
342: //} else if (value.startsWith("$") && value.endsWith("}")) {
343: } else if (value.matches(sConstraintPatStr)) {
344: // It's a $xxx{...} attribute initializer, let's not
345: // do anything at all, and let the viewsystem takes
346: // care of finding the resource by id.
347: } else if (ScriptCompiler.isIdentifier(value)) {
348: // id: leave intact: nothing to do
349: Set resourceNames = env.getResourceNames();
350: if (!resourceNames.contains(value)) {
351: // Add this reference to be checked again after
352: // we've fully parsed the whole app.
353: env.addResourceReference(value, elt);
354: }
355: } else if (XMLUtils.isURL(value)) {
356: if (env.isCanvas() && !env.getCanvas().isProxied()) {
357: checkUnsupportedMediaTypes(env, elt, value);
358: }
359: // URL: relativize, and rename to "source" for runtime
360: value = env.adjustRelativeURL(value, elt);
361: // If it's a relative pathname with no hostname
362: // (e.g. "http:resource"), the runtime expects a
363: // bare name (e.g. "resource")
364: try {
365: java.net.URL url = new java.net.URL(value);
366: if (url.getHost().equals("")
367: && !url.getPath().startsWith("/")) {
368: value = url.getPath();
369: if (url.getQuery() != null
370: && url.getQuery().length() > 0) {
371: value += "?" + url.getQuery();
372: }
373: }
374: } catch (java.net.MalformedURLException e) {
375: throw new ChainedException(e);
376: }
377: elt.removeAttribute(RESOURCE_ATTR_NAME);
378: elt.setAttribute("source", ScriptCompiler.quote(value));
379:
380: } else {
381: // pathname: turn into an id
382: File file = env.resolveReference(elt,
383: RESOURCE_ATTR_NAME);
384: // N.B.: Resources are always imported into the main
385: // program for the Flash target, hence the use of
386: // getResourceGenerator below
387: try {
388: value = env.getResourceGenerator().importResource(
389: file);
390: } catch (ObjectWriter.ImportResourceError e) {
391: env.warn(e, elt);
392: }
393: elt.setAttribute(RESOURCE_ATTR_NAME, value);
394:
395: if (env.isCanvas()) {
396: Element info = new Element("resolve");
397: info.setAttribute("src", elt
398: .getAttributeValue(RESOURCE_ATTR_NAME));
399: try {
400: info.setAttribute("pathname", file
401: .getCanonicalPath());
402: } catch (java.io.IOException ioe) {
403: mLogger.warn(
404: /* (non-Javadoc)
405: * @i18n.test
406: * @org-mes="Can't canonicalize " + p[0]
407: */
408: org.openlaszlo.i18n.LaszloMessages.getMessage(
409: ViewCompiler.class.getName(),
410: "051018-384", new Object[] { file
411: .toString() }));
412: }
413: env.getCanvas().addInfo(info);
414: }
415: }
416: }
417:
418: // Recurse
419: Iterator iter;
420: for (iter = elt.getChildren().iterator(); iter.hasNext();) {
421: compileResources((Element) iter.next(), env);
422: }
423: }
424:
425: static void compileClickResources(Element elt,
426: CompilationEnvironment env) {
427: final String ATTR_NAME = "clickregion";
428: String value = elt.getAttributeValue(ATTR_NAME);
429:
430: if (value != null) {
431: if (value.matches(sConstraintPatStr)
432: || ScriptCompiler.isIdentifier(value)
433: || XMLUtils.isURL(value)) {
434: env.warn(
435: /* (non-Javadoc)
436: * @i18n.test
437: * @org-mes="The value of the " + p[0] + "attribute" + "must be a file name."
438: */
439: org.openlaszlo.i18n.LaszloMessages.getMessage(
440: ViewCompiler.class.getName(), "051018-414",
441: new Object[] { ATTR_NAME }), elt);
442: } else {
443: // pathname: turn into an id
444: File file = env.resolveReference(elt, ATTR_NAME);
445: try {
446: value = env.getResourceGenerator()
447: .importClickResource(file);
448: } catch (ObjectWriter.ImportResourceError e) {
449: env.warn(e, elt);
450: }
451: elt.setAttribute(ATTR_NAME, value);
452: }
453: }
454:
455: // Recurse
456: Iterator iter;
457: for (iter = elt.getChildren().iterator(); iter.hasNext();) {
458: compileClickResources((Element) iter.next(), env);
459: }
460: }
461:
462: static void checkUnresolvedResourceReferences(
463: CompilationEnvironment env) {
464: Map refs = env.resourceReferences();
465: Set resourceNames = env.getResourceNames();
466: for (Iterator iter = refs.keySet().iterator(); iter.hasNext();) {
467: String resourceId = (String) iter.next();
468: Element elt = (Element) refs.get(resourceId);
469: if (!resourceNames.contains(resourceId)) {
470: env.warn(
471: /* (non-Javadoc)
472: * @i18n.test
473: * @org-mes="The resource named '" + p[0] + "' has not been declared"
474: */
475: org.openlaszlo.i18n.LaszloMessages.getMessage(
476: ViewCompiler.class.getName(), "051018-450",
477: new Object[] { resourceId }), elt);
478: }
479: }
480: }
481:
482: /**
483: * Walk the whole superclass chain, starting at the root, merging fontInfo.
484: */
485: protected static void mergeClassFontInfo(Element elt,
486: FontInfo fontInfo, CompilationEnvironment env) {
487: String classname = elt.getName();
488: // check for a cached fontInfo on the class
489: FontInfo cachedInfo = env.getClassFontInfo(classname);
490: if (cachedInfo != null) {
491: fontInfo.mergeFontInfoFrom(cachedInfo);
492: return;
493: }
494:
495: ViewSchema schema = env.getSchema();
496: ClassModel classinfo = schema.getClassModel(classname);
497: if (classinfo == null || classinfo.definition == null) {
498: return;
499: }
500:
501: // Build a list of superclasses
502: Vector parents = new Vector();
503: ClassModel lzxclass = classinfo;
504: // walk
505: while (lzxclass != null) {
506: parents.insertElementAt(lzxclass, 0);
507: lzxclass = lzxclass.super class;
508: }
509:
510: // A blank FontInfo with all empty slots
511: FontInfo cinfo = FontInfo.blankFontInfo();
512:
513: // Pop off elements starting at base class
514: while (parents.size() > 0) {
515: lzxclass = (ClassModel) parents.firstElement();
516: parents.removeElementAt(0); // pop
517: mergeClassFontInfo(lzxclass, cinfo);
518: }
519:
520: env.addClassFontInfo(classname, cinfo);
521: // apply the class' style changes, if any, to our fontInfo arg
522: fontInfo.mergeFontInfoFrom(cinfo);
523: }
524:
525: /**
526: * Merge FontInfo from a class definition.
527: */
528: protected static void mergeClassFontInfo(ClassModel classinfo,
529: FontInfo fontInfo) {
530: if (classinfo != null && classinfo.definition != null) {
531: Element celt = classinfo.definition;
532: mergeFontInfo(celt, fontInfo);
533: }
534: }
535:
536: /**
537: * Adds in text widths for all text views below this element that
538: * need them. This walks down into class definitions, merging
539: * font info as it goes. We don't need to walk into class defs
540: * for measuring text, since we have no way to pass those text
541: * widths to the runtime, but we do need this to check if we need
542: * to import the default bold or italic fonts.
543: *
544: *
545: *
546: * @param env
547: * @param elt
548: * @param fontInfo the current font name/style/size
549: */
550: protected void mapTextMetricsCompilation(Element elt,
551: CompilationEnvironment env, FontInfo fontInfo, Set classList) {
552:
553: classList = new HashSet(classList);
554:
555: // Clone a copy of the font info
556: fontInfo = new FontInfo(fontInfo);
557:
558: // Check class defaults for font info
559: mergeClassFontInfo(elt, fontInfo, env);
560: // Now override with any directly declared attributes
561: mergeFontInfo(elt, fontInfo);
562:
563: String fontName = fontInfo.getName();
564:
565: // If it inherits from text or inputttext, annotate it with font info
566: if ("text".equals(elt.getName())
567: || "text".equals(mSchema
568: .getBaseClassname(elt.getName()))
569: || "inputtext".equals(elt.getName())
570: || "inputtext".equals(mSchema.getBaseClassname(elt
571: .getName()))) {
572: compileTextMetrics(elt, env, fontInfo);
573: }
574: ClassModel classinfo = env.getSchema().getClassModel(
575: elt.getName());
576:
577: // If this invokes a 'user-defined' class, let's walk that
578: // class's source tree now
579: if (classinfo != null && classinfo.definition != null) {
580: // check if we are in an instance of a class that we are
581: // already descended into (loop detection)
582: if (classList.contains(elt.getName().intern())) {
583: return;
584: }
585: for (Iterator iter = classinfo.definition.getChildren()
586: .iterator(); iter.hasNext();) {
587: Element e = (Element) iter.next();
588: String ename = e.getName();
589: if (!(ename.equals("method") || ename
590: .equals("attribute"))) {
591: // Avoid recursively traversing class definitions.
592: // Mark this class as having been traversed, to
593: // avoid loops.
594: classList.add(classinfo.className.intern());
595: mapTextMetricsCompilation(e, env, fontInfo,
596: classList);
597: }
598: }
599: }
600:
601: // Now do immediate children
602: for (Iterator iter = elt.getChildren().iterator(); iter
603: .hasNext();) {
604: Element e = (Element) iter.next();
605: mapTextMetricsCompilation(e, env, fontInfo, classList);
606: }
607: }
608:
609: /** Merges font name/size/style from an element's direct
610: * attributes into a FontInfo */
611: protected static void mergeFontAttributes(Element elt,
612: FontInfo fontInfo) {
613: String myfont = getAttributeValue(elt, "font");
614: if (myfont != null) {
615: if (myfont.matches("\\s*[^${}]*\\s*")) {
616: fontInfo.setName(myfont);
617: } else {
618: // we don't know what font value is, so set back to the 'unknown' value
619: fontInfo.setName(FontInfo.NULL_FONT);
620: }
621: }
622:
623: String mysize = getAttributeValue(elt, "fontsize");
624: if (mysize != null) {
625: if (mysize.matches(sFontSizePatStr)) {
626: fontInfo.setSize(mysize);
627: } else {
628: // we don't know what font size is, so set back to the
629: // 'unknown' value
630: fontInfo.setSize(FontInfo.NULL_SIZE);
631: }
632: }
633:
634: String mystyle = getAttributeValue(elt,
635: NodeModel.FONTSTYLE_ATTRIBUTE);
636: if (mystyle != null) {
637: if (mystyle.matches(sFontstylePatStr)) {
638: fontInfo.setStyle(mystyle);
639: } else {
640: // we don't know what font size is, so set back to the 'unknown' value
641: fontInfo.setStyleBits(FontInfo.NULL_STYLE);
642: }
643: }
644: }
645:
646: /** Merge in font attribute info from an element into a FontInfo.
647: *
648: * @param elt the element to look for font attributes on
649: * @param fontInfo merge font attribute info into this struct
650: */
651: private static void mergeFontInfo(Element elt, FontInfo fontInfo) {
652: mergeFontAttributes(elt, fontInfo);
653:
654: // Static sized textfield optimization; need to cascade resizable
655: String resizable = getAttributeValue(elt, "resizable");
656: if ("true".equals(resizable)) {
657: fontInfo.resizable = FontInfo.FONTINFO_TRUE;
658: } else if ("false".equals(resizable)) {
659: fontInfo.resizable = FontInfo.FONTINFO_FALSE;
660: }
661:
662: // Static sized textfield optimization; need to cascade multiline
663: String multiline = getAttributeValue(elt, "multiline");
664: if ("true".equals(multiline)) {
665: fontInfo.multiline = FontInfo.FONTINFO_TRUE;
666: } else if ("false".equals(multiline)) {
667: fontInfo.multiline = FontInfo.FONTINFO_FALSE;
668: }
669: }
670:
671: /** Pattern matcher for compile-time optimizations */
672: static String sConstPatStr = "\\s*(\\d*)\\s*";
673: static String sFontstylePatStr = "\\s*(bold italic|bold-italic|bold|plain|italic)\\s*";
674: static String sFontSizePatStr = "\\s*\\d*\\s*";
675: static String sFontNamePatStr = "\\s*[^${}]*\\s*";
676: static String sConstraintPatStr = "^\\s*\\$(\\w*)\\{(.*)}\\s*";
677: static final Pattern sConstraintPat;
678:
679: static {
680: // $once{parent +|- DDDDD}
681: sConstraintPat = Pattern.compile(sConstraintPatStr);
682: }
683:
684: /** return true if element has an attribute named ATTRIBUTE in
685: * it's attribute list, or has a child lzx element
686: * <attribute name="ATTRIBUTE"/>
687: */
688: protected static boolean hasAttribute(Element elt, String attrName) {
689: if (elt.getAttributeValue(attrName) != null) {
690: return true;
691: }
692:
693: Iterator iter;
694: for (iter = elt.getChildren().iterator(); iter.hasNext();) {
695: Element child = (Element) iter.next();
696: if ((child.getName().equals("attribute"))
697: && (child.getAttribute(attrName) != null)) {
698: return true;
699: }
700: }
701: return false;
702: }
703:
704: /** return value if element has an attribute named ATTRNAME in
705: * it's attribute list, or has a child lzx element
706: * <attribute name="ATTRNAME" value="VAL"/>
707: */
708: protected static String getAttributeValue(Element elt,
709: String attrName) {
710: String attrval = elt.getAttributeValue(attrName);
711: if (attrval != null) {
712: return attrval;
713: }
714:
715: Iterator iter;
716: for (iter = elt.getChildren().iterator(); iter.hasNext();) {
717: Element child = (Element) iter.next();
718: if ((child.getName().equals("attribute"))
719: && attrName.equals(child.getAttributeValue("name"))) {
720: return child.getAttributeValue("value");
721: }
722: }
723: return null;
724: }
725:
726: /**
727: * Adds in text metrics for this element.
728: *
729: * @param env
730: * @param elt
731: * @param fontInfo font information for this element
732: */
733: private void compileTextMetrics(Element elt,
734: CompilationEnvironment env, FontInfo fontInfo) {
735:
736: if (fontInfo.getName() != null) {
737: elt.setAttribute("font", fontInfo.getName());
738: }
739:
740: if (fontInfo.getSize() != -1) {
741: elt.setAttribute("fontsize", "" + fontInfo.getSize());
742: }
743:
744: if (fontInfo.getStyle() != null) {
745: elt.setAttribute("fontstyle", fontInfo.getStyle());
746: }
747:
748: }
749:
750: static void setFontInfo(FontInfo info, Element elt) {
751: String face = elt.getAttributeValue("face");
752: String size = elt.getAttributeValue("size");
753:
754: if (face != null) {
755: info.setName(face);
756: }
757: if (size != null) {
758: info.setSize(size);
759: }
760: }
761: }
762:
763: /**
764: * @copyright Copyright 2001-2007 Laszlo Systems, Inc. All Rights
765: * Reserved. Use is subject to license terms.
766: */
|