1 4 5 9 10 package org.openlaszlo.compiler; 11 12 import java.io.*; 13 import java.text.ChoiceFormat ; 14 import java.util.*; 15 16 import org.openlaszlo.compiler.ViewSchema.ColorFormatException; 17 import org.openlaszlo.css.CSSParser; 18 import org.openlaszlo.sc.Function; 19 import org.openlaszlo.sc.ScriptCompiler; 20 import org.openlaszlo.server.*; 21 import org.openlaszlo.utils.ChainedException; 22 import org.openlaszlo.utils.ListFormat; 23 import org.openlaszlo.utils.ComparisonMap; 24 import org.openlaszlo.xml.internal.MissingAttributeException; 25 import org.openlaszlo.xml.internal.Schema; 26 import org.openlaszlo.xml.internal.XMLUtils; 27 import org.apache.oro.text.regex.*; 28 import org.apache.commons.collections.CollectionUtils; 29 import org.jdom.Attribute; 30 import org.jdom.Element; 31 import org.jdom.Namespace; 32 33 34 public class NodeModel implements Cloneable { 35 public static final String FONTSTYLE_ATTRIBUTE = "fontstyle"; 36 public static final String WHEN_IMMEDIATELY = "immediately"; 37 public static final String WHEN_ONCE = "once"; 38 public static final String WHEN_ALWAYS = "always"; 39 public static final String WHEN_PATH = "path"; 40 private static final String SOURCE_LOCATION_ATTRIBUTE_NAME = "__LZsourceLocation"; 41 42 protected final ViewSchema schema; 43 protected final Element element; 44 protected String className; 45 protected String id = null; 46 protected ComparisonMap attrs = new ComparisonMap(); 47 protected List children = new Vector(); 48 50 protected ComparisonMap delegates = new ComparisonMap(); 51 protected ComparisonMap events = new ComparisonMap(); 52 protected ComparisonMap references = new ComparisonMap(); 53 protected ComparisonMap paths = new ComparisonMap(); 54 protected ComparisonMap setters = new ComparisonMap(); 55 56 protected List delegateList = new Vector(); 57 protected ClassModel parentClassModel; 58 protected String initstage = null; 59 protected int totalSubnodes = 1; 60 protected final CompilationEnvironment env; 61 62 public Object clone() { 63 NodeModel copy; 64 try { 65 copy = (NodeModel) super.clone(); 66 } catch (CloneNotSupportedException e) { 67 throw new RuntimeException (e); 68 } 69 copy.attrs = new ComparisonMap(copy.attrs); 70 copy.delegates = new ComparisonMap(copy.delegates); 71 copy.events = new ComparisonMap(copy.events); 72 copy.references = new ComparisonMap(copy.references); 73 copy.paths = new ComparisonMap(copy.paths); 74 copy.setters = new ComparisonMap(copy.setters); 75 copy.delegateList = new Vector(copy.delegateList); 76 copy.children = new Vector(); 77 for (Iterator iter = children.iterator(); iter.hasNext(); ) { 78 copy.children.add(((NodeModel) iter.next()).clone()); 79 } 80 return copy; 81 } 82 83 NodeModel(Element element, ViewSchema schema, CompilationEnvironment env) { 84 this.element = element; 85 this.schema = schema; 86 this.env = env; 87 88 this.className = element.getName(); 89 this.parentClassModel = this.getParentClassModel(); 91 this.initstage = 92 this.element.getAttributeValue("initstage"); 93 if (this.initstage != null) { 94 this.initstage = this.initstage.intern(); 95 } 96 97 if (this.parentClassModel != null) { 103 ElementWithLocationInfo parentElement = 104 (ElementWithLocationInfo) this.parentClassModel.definition; 105 if (parentElement != null) { 108 if (parentElement.model != null) { 112 this.totalSubnodes = 113 parentElement.model.classSubnodes(); 114 if (this.initstage == null) { 115 this.initstage = 116 parentElement.model.initstage; 117 } 118 } 119 } 120 } 121 } 122 123 private static final String DEPRECATED_METHODS_PROPERTY_FILE = ( 124 LPS.getMiscDirectory() + File.separator + "lzx-deprecated-methods.properties" 125 ); 126 private static final Properties sDeprecatedMethods = new Properties(); 127 128 static { 129 try { 130 InputStream is = new FileInputStream(DEPRECATED_METHODS_PROPERTY_FILE); 131 try { 132 sDeprecatedMethods.load(is); 133 } finally { 134 is.close(); 135 } 136 } catch (java.io.IOException e) { 137 throw new ChainedException(e); 138 } 139 } 140 141 142 143 private static final String FLASH6_BUILTINS_PROPERTY_FILE = ( 144 LPS.getMiscDirectory() + File.separator + "flash6-builtins.properties" 145 ); 146 147 148 private static final String FLASH7_BUILTINS_PROPERTY_FILE = ( 149 LPS.getMiscDirectory() + File.separator + "flash7-builtins.properties" 150 ); 151 152 public static final Properties sFlash6Builtins = new Properties(); 153 public static final Properties sFlash7Builtins = new Properties(); 154 155 static { 156 try { 157 InputStream is6 = new FileInputStream(FLASH6_BUILTINS_PROPERTY_FILE); 158 try { 159 sFlash6Builtins.load(is6); 160 } finally { 161 is6.close(); 162 } 163 164 InputStream is7 = new FileInputStream(FLASH7_BUILTINS_PROPERTY_FILE); 165 try { 166 sFlash7Builtins.load(is7); 167 } finally { 168 is7.close(); 169 } 170 } catch (java.io.IOException e) { 171 throw new ChainedException(e); 172 } 173 } 174 175 static class CompiledAttribute { 176 static final int ATTRIBUTE = 0; 177 static final int EVENT = 1; 178 static final int REFERENCE = 2; 179 static final int PATH = 3; 180 181 final int type; 182 final Object value; 183 184 CompiledAttribute(int type, Object value) { 185 this.type = type; 186 this.value = value; 187 } 188 CompiledAttribute(Object value) { 189 this(ATTRIBUTE, value); 190 } 191 } 192 193 public String toString() { 194 StringBuffer buffer = new StringBuffer (); 195 buffer.append("{NodeModel class=" + className); 196 if (!attrs.isEmpty()) 197 buffer.append(" attrs=" + attrs.keySet()); 198 if (!delegates.isEmpty()) 199 buffer.append(" delegates=" + delegates.keySet()); 200 if (!events.isEmpty()) 201 buffer.append(" events=" + events.keySet()); 202 if (!references.isEmpty()) 203 buffer.append(" references=" + references.keySet()); 204 if (!paths.isEmpty()) 205 buffer.append(" paths=" + paths.keySet()); 206 if (!setters.isEmpty()) 207 buffer.append(" setters=" + setters.keySet()); 208 if (!delegateList.isEmpty()) 209 buffer.append(" delegateList=" + delegateList); 210 if (!children.isEmpty()) 211 buffer.append(" children=" + children); 212 buffer.append("}"); 213 return buffer.toString(); 214 } 215 216 List getChildren() { 217 return children; 218 } 219 220 public static boolean isPropertyElement(Element elt) { 221 String name = elt.getName(); 222 return name.equals("attribute") || name.equals("method"); 223 } 224 225 227 String getMessageName() { 228 return "element " + element.getName(); 229 } 230 231 241 public String asJavascript() { 242 try { 243 java.io.Writer writer = new java.io.StringWriter (); 244 ScriptCompiler.writeObject(this.asMap(), writer); 245 return writer.toString(); 246 } catch (java.io.IOException e) { 247 throw new ChainedException(e); 248 } 249 } 250 251 252 private static boolean computeDefaultClickable(ViewSchema schema, 253 Map attrs, 254 Map events, 255 Map delegates) { 256 if ("true".equals(attrs.get("cursor"))) { 257 return true; 258 } 259 for (Iterator iter = events.keySet().iterator(); iter.hasNext();) { 260 String eventName = (String ) iter.next(); 261 if (schema.isMouseEventAttribute(eventName)) { 262 return true; 263 } 264 } 265 for (Iterator iter = delegates.keySet().iterator(); iter.hasNext();) { 266 String eventName = (String ) iter.next(); 267 if (schema.isMouseEventAttribute(eventName)) { 268 return true; 269 } 270 } 271 return false; 272 } 273 274 282 public static NodeModel elementAsModel(Element elt, ViewSchema schema, 283 CompilationEnvironment env) { 284 return elementAsModelInternal(elt, schema, true, env); 285 } 286 287 295 public static NodeModel elementOnlyAsModel(Element elt, ViewSchema schema, 296 CompilationEnvironment env) { 297 return elementAsModelInternal(elt, schema, false, env); 298 } 299 300 307 private static NodeModel elementAsModelInternal( 308 Element elt, ViewSchema schema, 309 boolean includeChildren, CompilationEnvironment env) 310 { 311 NodeModel model = new NodeModel(elt, schema, env); 312 ComparisonMap attrs = model.attrs; 313 Map events = model.events; 314 Map delegates = model.delegates; 315 model.addAttributes(env); 316 if (includeChildren) { 317 model.addChildren(env); 318 model.addText(); 319 if (!attrs.containsKey("clickable") 320 && computeDefaultClickable(schema, attrs, events, delegates)) { 321 attrs.put("clickable", "true"); 322 } 323 } 324 ((ElementWithLocationInfo) elt).model = model; 326 return model; 327 } 328 329 int totalSubnodes() { 332 if (ClassCompiler.isElement(element) || 337 className.equals("state")) { 338 return 1; 339 } 340 if (this.initstage != null && 342 (this.initstage == "late" || 343 this.initstage == "defer")) { 344 return 0; 345 } 346 return this.totalSubnodes; 347 } 348 349 int classSubnodes() { 351 if (ClassCompiler.isElement(element)) { 352 return this.totalSubnodes; 353 } 354 return 0; 355 } 356 357 ClassModel getClassModel() { 358 return schema.getClassModel(this.className); 359 } 360 361 364 ClassModel getParentClassModel() { 365 String parentName = this.className; 366 return 367 parentName.equals("class")? 368 schema.getClassModel(element.getAttributeValue("extends")): 369 schema.getClassModel(parentName); 370 } 371 372 void setClassName(String name) { 373 this.className = name; 374 this.parentClassModel = getParentClassModel(); 375 } 376 377 ViewSchema.Type getAttributeTypeInfoFromSuperclass( 379 Element classDefElement, String attrname) 380 throws UnknownAttributeException 381 { 382 String superclassname = classDefElement.getAttributeValue("extends", ClassCompiler.DEFAULT_SUPERCLASS_NAME); 383 ClassModel superclassModel = schema.getClassModel(superclassname); 384 385 if (superclassModel == null) { 386 throw new CompilationError("Could not find superclass info for class " + superclassname, classDefElement); 387 } 388 389 AttributeSpec attr = superclassModel.getAttribute(attrname); 392 if (attr != null) { 393 return attr.type; 394 } 395 superclassModel = schema.getClassModel("class"); 398 return superclassModel.getAttributeTypeOrException(attrname); 399 } 400 401 String getAttributeValueDefault(String attribute, 404 String name, 405 String defaultValue) { 406 if (this.parentClassModel != null) { 408 AttributeSpec attrSpec = 409 this.parentClassModel.getAttribute(attribute); 410 if (attrSpec != null) { 411 Element source = attrSpec.source; 412 if (source != null) { 413 return XMLUtils.getAttributeValue(source, name, defaultValue); 414 } 415 } 416 } 417 418 return defaultValue; 419 } 420 421 422 boolean topLevelDeclaration() { 427 Element parent = element.getParent(); 428 if (parent == null) { 429 return false; 430 } 431 return ("canvas".equals(parent.getName())); 432 } 433 434 void addAttributes(CompilationEnvironment env) { 435 boolean swf7 = env.getSWFVersion().equals("swf7"); 436 if (env.getBooleanProperty(env.SOURCELOCATOR_PROPERTY)) { 439 String location = "document(" + 440 ScriptCompiler.quote(Parser.getSourceMessagePathname(element)) + 441 ")" + 442 XMLUtils.getXPathTo(element); 443 CompiledAttribute cattr = compileAttribute( 444 element, SOURCE_LOCATION_ATTRIBUTE_NAME, 445 location, ViewSchema.STRING_TYPE, 446 WHEN_IMMEDIATELY); 447 addAttribute(cattr, SOURCE_LOCATION_ATTRIBUTE_NAME, 448 attrs, events, references, paths); 449 } 450 451 for (Iterator iter = element.getAttributes().iterator(); iter.hasNext(); ) { 453 Attribute attr = (Attribute) iter.next(); 454 Namespace ns = attr.getNamespace(); 455 String name = attr.getName(); 456 String value = element.getAttributeValue(name, ns); 457 458 if (name.equals(FONTSTYLE_ATTRIBUTE)) { 459 value = FontInfo.normalizeStyleString(value, false); 461 } 462 463 if (name.toLowerCase().equals("datacontrolsvisibility")) { 464 env.warn("The attribute \"datacontrolsvisibility\" is deprecated. "+ 465 "Use visible=\"null\" instead. "+ 466 "For future compatibility you should make this change to your source code.", 467 element); 468 } 469 470 if (name.toLowerCase().equals("defaultplacement")) { 471 if (value != null && value.matches("\\s*['\"]\\S*['\"]\\s*")) { 472 String oldValue = value; 473 value = value.trim(); 475 value = value.substring(1, value.length()-1); 476 env.warn( 477 "Replacing defaultPlacement=\"" + oldValue + 478 "\" by \"" + value + "\". For future compatability" + 479 ", you should make this change to your source code.", 480 element); 481 } 482 } 483 484 485 486 if ((name.equals("id") || name.equals("name")) && 488 (value != null && 489 (swf7 ? 490 sFlash7Builtins.containsKey(value) : 491 sFlash6Builtins.containsKey(value.toLowerCase())))) { 492 env.warn( 493 "You have given the "+getMessageName()+ 494 " an attribute "+name+"=\""+value+"\", "+ 495 "which may overwrite the Flash builtin class named \""+value+"\".", 496 element); 497 498 } 499 500 if ((name.equals("id")) || 511 (name.equals("name") && 512 topLevelDeclaration() && !className.equals("class"))) { 513 514 ClassModel superclassModel = schema.getClassModel(value); 515 if (superclassModel != null && !superclassModel.isBuiltin()) { 516 env.warn( 517 "You have given the "+getMessageName()+ 518 " an attribute "+name+"=\""+value+"\", "+ 519 "which may overwrite the class \""+value+"\".", 520 element); 521 } else { 522 ElementWithLocationInfo dup = 523 (ElementWithLocationInfo) env.getId(value); 524 if (dup != null && dup != element) { 528 String locstring = 529 CompilerUtils.sourceLocationPrettyString(dup); 530 env.warn( 531 "Duplicate id attribute \""+value+"\" at "+locstring, 532 element); 533 } else { 534 env.addId(value, element); 539 } 540 } 541 } 542 543 Schema.Type type; 547 try { 548 if (className.equals("class")) { 549 type = getAttributeTypeInfoFromSuperclass(element, name); 550 } else { 551 type = schema.getAttributeType(element, name); 552 } 553 554 } catch (UnknownAttributeException e) { 555 String solution; 556 AttributeSpec alt = schema.findSimilarAttribute(className, name); 557 if (alt != null) { 558 String classmessage = ""; 559 if (alt.source != null) { 560 classmessage = " on class "+alt.source.getName()+"\""; 561 } else { 562 classmessage = " on class "+getMessageName(); 563 } 564 solution = "found an unknown attribute named \""+name 565 +"\" on "+getMessageName()+", however there is an attribute named \"" 566 +alt.name+"\""+ classmessage+ ", did you mean to use that?"; 567 } else { 568 solution = "found an unknown attribute named \""+name+"\" on "+getMessageName()+ 569 ", check the spelling of this attribute name"; 570 } 571 env.warn(solution, element); 572 type = ViewSchema.EXPRESSION_TYPE; 573 } 574 575 if (type == schema.ID_TYPE) { 576 this.id = value; 577 } else { 578 String when = this.getAttributeValueDefault( 579 name, "when", WHEN_IMMEDIATELY); 580 try { 581 CompiledAttribute cattr = compileAttribute( 582 element, name, value, type, when); 583 addAttribute(cattr, name, attrs, events, 584 references, paths); 585 if (name.equals("name")) { 588 Element parent = element.getParent(); 589 if (parent != null) { 590 for (Iterator iter2 = parent.getChildren().iterator(); iter2.hasNext(); 591 ) { 592 Element e = (Element) iter2.next(); 593 if (!e.getName().equals("resource") && !e.getName().equals("font") 594 && e != element && value.equals(e.getAttributeValue("name"))) { 595 String dup_location = 596 CompilerUtils.sourceLocationPrettyString(e); 597 env.warn( 598 getMessageName() + " has the same name=\""+ 599 value+"\" attribute as a sibling element at "+dup_location, 600 element); 601 } 602 } 603 } 604 } 605 } catch (CompilationError e) { 606 env.warn(e); 607 } 608 } 609 } 610 } 611 612 void addAttribute(CompiledAttribute cattr, String name, 613 ComparisonMap attrs, ComparisonMap events, 614 ComparisonMap references, ComparisonMap paths) { 615 if (cattr.type == cattr.ATTRIBUTE) { 616 if (attrs.containsKey(name)) { 617 env.warn( 618 "an attribute or method named '"+name+ 619 "' already is defined on "+getMessageName(), 620 element); 621 } 622 attrs.put(name, cattr.value); 623 } else if (cattr.type == cattr.EVENT) { 624 if (events.containsKey(name)) { 625 env.warn( 626 "redefining event '"+name+ 627 "' which has already been defined on "+getMessageName(), 628 element); 629 } 630 events.put(name, cattr.value); 631 } else if (cattr.type == cattr.REFERENCE) { 632 if (references.containsKey(name)) { 633 env.warn( 634 "redefining reference '"+name+ 635 "' which has already been defined on "+getMessageName(), 636 element); 637 } 638 references.put(name, cattr.value); 639 } else if (cattr.type == cattr.PATH) { 640 references.put(name, cattr.value); 641 } 642 } 643 644 void addChildren(CompilationEnvironment env) { 645 for (Iterator iter = element.getChildren().iterator(); iter.hasNext(); ) { 647 ElementWithLocationInfo child = (ElementWithLocationInfo) iter.next(); 648 try { 649 if (isPropertyElement(child)) { 650 addPropertyElement(child); 651 } else if (schema.isHTMLElement(child)) { 652 ; } else { 654 NodeModel childModel = elementAsModel(child, schema, env); 655 children.add(childModel); 656 totalSubnodes += childModel.totalSubnodes(); 657 } 658 } catch (CompilationError e) { 659 env.warn(e); 660 } 661 } 662 } 663 664 void addPropertyElement(Element element) { 665 String tagName = element.getName(); 666 if (tagName.equals("method")) { 667 addMethodElement(element); 668 } else if (tagName.equals("attribute")) { 669 addAttributeElement(element); 670 } 671 } 672 673 void addMethodElement(Element element) { 674 String srcloc = 675 CompilerUtils.sourceLocationDirective(element, true); 676 String name = element.getAttributeValue("name"); 677 String event = element.getAttributeValue("event"); 678 String args = CompilerUtils.attributeLocationDirective(element, "args") + 679 XMLUtils.getAttributeValue(element, "args", ""); 680 if ((name == null || !ScriptCompiler.isIdentifier(name)) && 681 (event == null || !ScriptCompiler.isIdentifier(event))) { 682 env.warn("method needs a non-null name or event attribute"); 683 return; 684 } 685 if (name != null && sDeprecatedMethods.containsKey(name)) { 686 String oldName = name; 687 String newName = (String ) sDeprecatedMethods.get(name); 688 name = newName; 689 env.warn( 690 oldName + " is deprecated. " + 691 "This method will be compiled as <method name='" + newName + "' instead. " + 692 "Please update your sources.", 693 element); 694 } 695 696 String parent_name = 697 element.getParent().getAttributeValue("id"); 698 String name_loc = 699 (name == null ? 700 CompilerUtils.attributeLocationDirective(element, "event") : 701 CompilerUtils.attributeLocationDirective(element, "name")); 702 if (parent_name == null) { 703 parent_name = 704 (name == null ? 705 CompilerUtils.attributeUniqueName(element, "event") : 706 CompilerUtils.attributeUniqueName(element, "name")); 707 } 708 if (event != null) { 709 if (name == null) { 710 name = env.methodNameGenerator.next(); 720 } 721 String reference = element.getAttributeValue("reference"); 722 Object referencefn = "null"; 723 if (reference != null) { 724 String ref_loc = 725 CompilerUtils.attributeLocationDirective(element, "reference"); 726 referencefn = new Function( 727 ref_loc + 728 parent_name + "_" + name + "_reference", 729 args, 730 "\n#pragma 'withThis'\n" + 731 "return (" + 732 "#beginAttribute\n" + ref_loc + 733 reference + "\n#endAttribute\n)"); 734 } 735 if (reference == null) 740 delegates.put(event, Boolean.TRUE); 741 delegateList.add(ScriptCompiler.quote(event)); 742 delegateList.add(ScriptCompiler.quote(name)); 743 delegateList.add(referencefn); 744 } 745 String body = element.getText(); 746 747 String childcontentloc = 748 CompilerUtils.sourceLocationDirective(element, true); 749 Function fndef = new 750 Function(name_loc + 752 parent_name + "_" + name, 753 CompilerUtils.attributeLocationDirective(element, "args") + args, 755 "\n#beginContent\n" + 756 "\n#pragma 'methodName=" + name + "'\n" + 757 "\n#pragma 'withThis'\n" + 758 childcontentloc + 759 body + "\n#endContent"); 760 761 if (attrs.containsKey(name)) { 762 env.warn( 763 "an attribute or method named '"+name+ 764 "' already is defined on "+getMessageName(), 765 element); 766 } 767 768 attrs.put(name, fndef); 769 } 770 771 CompiledAttribute compileAttribute( 772 Element source, String name, 773 String value, Schema.Type type, 774 String when) 775 { 776 String srcloc = CompilerUtils.sourceLocationDirective(source, true); 777 String parent_name = source.getAttributeValue("id"); 778 if (parent_name == null) { 779 parent_name = CompilerUtils.attributeUniqueName(source, name); 780 } 781 Object canonicalValue = null; 783 boolean warnOnDeprecatedConstraints = true; 784 785 if (value == null) { 786 throw new RuntimeException ("value is null in " + source); 787 } 788 789 if (value.startsWith("$(")) { 790 env.warn( 791 "The syntax '$(...)' is not valid, " 792 + "you probably meant to use curly-braces instead '${...}'", 793 source); 794 } else if (value.startsWith("$") && value.endsWith("}")) { 795 int brace = value.indexOf('{'); 797 if (brace >= 1) { 800 when = value.substring(1, brace); 801 value = value.substring(brace + 1, value.length() - 1); 802 if (when.equals("")) 803 when = WHEN_ALWAYS; 804 } 805 } else if (type == ViewSchema.COLOR_TYPE) { 806 if (when.equals(WHEN_IMMEDIATELY)) { 807 try { 808 value = "0x" + 809 Integer.toHexString(ViewSchema.parseColor(value)); 810 } catch (ColorFormatException e) { 811 throw new CompilationError(source, name, e); 814 } 815 } 816 } else if (type == ViewSchema.CSS_TYPE) { 819 if (when.equals(WHEN_IMMEDIATELY)) { 820 try { 821 Map cssProperties = new CSSParser 822 (new AttributeStream(source, name, value)).Parse(); 823 for (Iterator i2 = cssProperties.entrySet().iterator(); i2.hasNext(); ) { 824 Map.Entry entry = (Map.Entry) i2.next(); 825 Object mv = entry.getValue(); 826 if (mv instanceof String ) { 827 entry.setValue(ScriptCompiler.quote((String ) mv)); 828 } 829 } 830 canonicalValue = cssProperties; 831 } catch (org.openlaszlo.css.ParseException e) { 832 throw new CompilationError(e); 835 } catch (org.openlaszlo.css.TokenMgrError e) { 836 throw new CompilationError(e); 839 } 840 } 841 } else if (type == ViewSchema.STRING_TYPE 844 || type == ViewSchema.TOKEN_TYPE 845 ) { 846 if (when.equals(WHEN_IMMEDIATELY)) { 848 value = ScriptCompiler.quote(value); 849 } 850 } else if (type == ViewSchema.EXPRESSION_TYPE) { 851 } else if (type == ViewSchema.INHERITABLE_BOOLEAN_TYPE) { 855 if ("inherit".equals(value)) { 857 value = "null"; 858 } 859 } else if (type == ViewSchema.NUMBER_TYPE) { 860 } else if (type == ViewSchema.NUMBER_EXPRESSION_TYPE || 864 type == ViewSchema.SIZE_EXPRESSION_TYPE) { 865 if (value.trim().endsWith("%")) { 867 String numstr = value.trim(); 868 numstr = numstr.substring(0, numstr.length() - 1); 869 try { 870 double scale = new Float (numstr).floatValue() / 100.0; 871 warnOnDeprecatedConstraints = false; 872 String referenceAttribute = name; 873 if (name.equals("x")) { 874 referenceAttribute = "width"; 875 } else if (name.equals("y")) { 876 referenceAttribute = "height"; 877 } 878 value = "immediateparent." + referenceAttribute; 879 if (scale != 1.0) { 880 value += "\n * " + scale; 885 } 886 } catch (NumberFormatException e) { 888 } 890 } 891 try { 893 new Float (value); when = WHEN_IMMEDIATELY; 895 } catch (NumberFormatException e) { 896 if (when.equals(WHEN_IMMEDIATELY)) { 899 if (warnOnDeprecatedConstraints) { 900 env.warn( 901 "The value of the '" + name + "' attribute uses a deprecated syntax. " + 902 "Use " + name + "=\"${" + value + "}\" instead.", 903 element); 904 } 905 when = WHEN_ALWAYS; 906 } 907 } 908 } else if (type == ViewSchema.EVENT_TYPE) { 909 return new CompiledAttribute( 911 CompiledAttribute.EVENT, 912 "function " + 913 parent_name + "_" + name + "_event" + 914 " () {" + 915 "\n#pragma 'withThis'\n" + 916 "{\n#beginAttributeStatements\n" + 917 srcloc + value + "\n#endAttributeStatements\n}}"); 918 } else if (type == ViewSchema.REFERENCE_TYPE) { 919 if (when.equals(WHEN_IMMEDIATELY)) { 923 when = WHEN_ONCE; 924 } 925 } else { 926 throw new RuntimeException ("unknown schema datatype " + type); 927 } 928 929 if (canonicalValue == null) 930 canonicalValue = value; 931 932 if (when.equals(WHEN_PATH)) { 934 return new CompiledAttribute( 935 CompiledAttribute.PATH, 936 srcloc + ScriptCompiler.quote(value) + "\n"); 937 } else if (when.equals(WHEN_ONCE)) { 938 return new CompiledAttribute( 939 CompiledAttribute.REFERENCE, 940 "function " + 941 parent_name + "_" + name + "_once" + 942 " () {" + 943 "\n#pragma 'withThis'\n" + 944 "this.setAttribute(" + 947 ScriptCompiler.quote(name) + " , " + 948 "\n#beginAttribute\n" + srcloc + canonicalValue + "\n#endAttribute\n)}"); 949 } else if (when.equals(WHEN_ALWAYS)) { 950 return new CompiledAttribute( 951 CompiledAttribute.REFERENCE, 952 "function " + 953 parent_name + "_" + name + "_always" + 954 " () {" + 955 "\n#pragma 'constraintFunction'\n" + 956 "\n#pragma 'withThis'\n" + 957 "this.setAttribute(" + 960 ScriptCompiler.quote(name) + ", " + 961 "\n#beginAttribute\n" + srcloc + canonicalValue + 962 "\n#endAttribute\n)}"); 963 } else if (when.equals(WHEN_IMMEDIATELY)) { 964 if (type == ViewSchema.EXPRESSION_TYPE) { 965 return new CompiledAttribute("\n#beginAttribute\n" + srcloc + canonicalValue + "\n#endAttribute"); 966 } else { 967 return new CompiledAttribute(canonicalValue); 969 } 970 } else { 971 throw new CompilationError("invalid when value '" + 972 when + "'", source); 973 } 974 } 975 976 void addAttributeElement(Element element) { 977 String name; 978 try { 979 name = ElementCompiler.requireIdentifierAttributeValue(element, "name"); 980 } catch (MissingAttributeException e) { 981 throw new CompilationError("'name' is a required attribute of <" + 982 element.getName() + "> and must be a valid identifier", element); 983 } 984 985 String value = element.getAttributeValue("value"); 986 String when = element.getAttributeValue("when"); 987 String typestr = element.getAttributeValue("type"); 988 Element parent = element.getParent(); 989 String parent_name = parent.getAttributeValue("id"); 990 991 if (parent_name == null) { 992 parent_name = CompilerUtils.attributeUniqueName(element, name); 993 } 994 995 if (when == null) { 997 when = this.getAttributeValueDefault( 998 name, "when", WHEN_IMMEDIATELY); 999 } 1000 1001 Schema.Type type = null; 1002 Schema.Type parenttype = null; 1003 1004 try { 1005 if (parent.getName().equals("class")) { 1006 parenttype = getAttributeTypeInfoFromSuperclass(parent, name); 1007 } else { 1008 parenttype = schema.getAttributeType(parent, name); 1009 } 1010 } catch (UnknownAttributeException e) { 1011 } 1015 1016 if (typestr == null) { 1017 if (parenttype == null) { 1021 type = ViewSchema.EXPRESSION_TYPE; 1022 } else { 1023 type = parenttype; 1024 } 1025 } else { 1026 type = schema.getTypeForName(typestr); 1028 if (type == null) { 1029 throw new CompilationError("unknown attribute type: " + typestr, element); 1030 } 1031 if (parenttype != null && type != parenttype) { 1034 env.warn( 1035 new CompilationError( 1036 element, 1037 name, 1038 new Throwable ( 1039 "In element '" + parent.getName() + 1040 "' attribute '"+ name + "' with type '"+type.toString() + 1041 "' is overriding parent class attribute with same name but different type: "+ 1042 parenttype.toString()) 1043 ) 1044 ); 1045 } 1046 } 1047 1048 if (value != null) { 1050 CompiledAttribute cattr = compileAttribute(element, name, 1051 value, type, 1052 when); 1053 addAttribute(cattr, name, attrs, events, references, paths); 1054 } 1055 1056 String setter = element.getAttributeValue("setter"); 1058 if (setter == null) { 1060 setter = element.getAttributeValue("onset"); 1061 } 1062 if (setter != null) { 1063 String srcloc = 1064 CompilerUtils.sourceLocationDirective(element, true); 1065 String setterfn = 1067 srcloc + "function " + 1068 parent_name + "_" + name + "_onset" + 1069 " (" + name + ") {" + 1070 "\n#pragma 'withThis'\n" + 1071 srcloc + setter + "\n}"; 1072 1073 if (setters.get(name) != null) { 1074 env.warn( 1075 "a setter for attribute named '"+name+ 1076 "' is already defined on "+getMessageName(), 1077 element); 1078 } 1079 1080 setters.put(name, setterfn); 1081 } 1082 } 1083 1084 boolean hasAttribute(String name) { 1085 return attrs.containsKey(name); 1086 } 1087 1088 void removeAttribute(String name) { 1089 attrs.remove(name); 1090 } 1091 1092 void setAttribute(String name, Object value) { 1093 attrs.put(name, value); 1094 } 1095 1096 void addText() { 1097 if (schema.hasHTMLContent(element)) { 1098 String text = TextCompiler.getHTMLContent(element); 1099 if (text.length() != 0) { 1100 if (!attrs.containsKey("text")) { 1101 attrs.put("text", ScriptCompiler.quote(text)); 1102 } 1103 } 1104 } else if (schema.hasTextContent(element)) { 1105 String text; 1106 text = TextCompiler.getInputText(element); 1110 if (text.length() != 0) { 1111 if (!attrs.containsKey("text")) { 1112 attrs.put("text", ScriptCompiler.quote(text)); 1113 } 1114 } 1115 } 1116 } 1117 1118 void updateAttrs() { 1119 if (!setters.isEmpty()) { 1120 attrs.put("$setters", setters); 1121 } 1122 if (!delegateList.isEmpty()) { 1123 attrs.put("$delegates", delegateList); 1124 } 1125 if (!events.isEmpty()) { 1126 attrs.put("$events", events); 1127 } 1128 if (!references.isEmpty()) { 1129 attrs.put("$refs", references); 1130 } 1131 if (!paths.isEmpty()) { 1132 attrs.put("$paths", paths); 1133 } 1134 } 1135 1136 Map asMap() { 1137 Map map = new HashMap(); 1138 updateAttrs(); 1139 map.put("name", ScriptCompiler.quote(className)); 1140 map.put("attrs", attrs); 1141 if (id != null) { 1142 map.put("id", ScriptCompiler.quote(id)); 1143 } 1144 if (!children.isEmpty()) { 1145 List childMaps = new Vector(children.size()); 1146 for (Iterator iter = children.iterator(); iter.hasNext(); ) 1147 childMaps.add(((NodeModel) iter.next()).asMap()); 1148 map.put("children", childMaps); 1149 } 1150 return map; 1151 } 1152 1153 1162 NodeModel expandClassDefinitions() { 1163 NodeModel model = this; 1164 while (true) { 1165 ClassModel classModel = schema.getClassModel(model.className); 1166 if (classModel == null) 1167 break; 1168 if (classModel.getSuperclassName() == null) 1169 break; 1170 if (!classModel.getInline()) 1171 break; 1172 model = classModel.applyClass(model); 1173 } 1176 model = (NodeModel) model.clone(); 1180 for (ListIterator iter = model.children.listIterator(); 1181 iter.hasNext(); ) { 1182 NodeModel child = (NodeModel) iter.next(); 1183 iter.set(child.expandClassDefinitions()); 1184 } 1185 return model; 1186 } 1187 1188 1189 void updateMembers(NodeModel source) { 1190 final String OPTIONS_ATTR_NAME = "options"; 1191 1192 if (CollectionUtils.containsAny( 1194 events.normalizedKeySet(), source.events.normalizedKeySet())) { 1195 Collection sharedEvents = CollectionUtils.intersection( 1196 events.normalizedKeySet(), source.events.normalizedKeySet()); 1197 throw new CompilationError( 1198 "Both the class and the instance or subclass define the " + 1199 new ChoiceFormat ("1#event |1<events ").format(sharedEvents.size()) + 1200 new ListFormat("and").format(sharedEvents)); 1201 } 1202 1203 List sharedMethods = new Vector(); 1206 for (Iterator iter = attrs.normalizedKeySet().iterator(); 1207 iter.hasNext(); ) { 1208 String key = (String ) iter.next(); 1209 if (attrs.get(key) instanceof Function && 1210 source.attrs.get(key) instanceof Function) 1211 sharedMethods.add(key); 1212 } 1213 if (!sharedMethods.isEmpty()) 1214 throw new CompilationError( 1215 "Both the class and the instance or subclass define the method" + 1216 new ChoiceFormat ("1# |1<s ").format(sharedMethods.size()) + 1217 new ListFormat("and").format(sharedMethods)); 1218 1219 Collection overriddenAttributes = CollectionUtils.intersection( 1222 attrs.normalizedKeySet(), source.setters.normalizedKeySet()); 1223 if (!overriddenAttributes.isEmpty()) 1224 throw new CompilationError( 1225 "A class that defines a value can't be inlined against a " + 1226 "subclass or instance that defines a setter. The following " + 1227 new ChoiceFormat ("1#attribute violates|1<attributes violate") 1228 .format(overriddenAttributes.size()) + 1229 " this condition: " + 1230 new ListFormat("and").format(overriddenAttributes)); 1231 1232 id = source.id; 1234 if (source.initstage != null) 1235 initstage = source.initstage; 1236 Object options = attrs.get(OPTIONS_ATTR_NAME); 1237 Object sourceOptions = source.attrs.get(OPTIONS_ATTR_NAME); 1238 attrs.putAll(source.attrs); 1239 if (options instanceof Map && sourceOptions instanceof Map) { 1240 System.err.println(options); 1241 System.err.println(sourceOptions); 1242 Map newOptions = new HashMap((Map) options); 1243 newOptions.putAll((Map) sourceOptions); 1244 attrs.put(OPTIONS_ATTR_NAME, newOptions); 1245 } 1246 delegates.putAll(source.delegates); 1247 events.putAll(source.events); 1248 references.putAll(source.references); 1249 paths.putAll(source.paths); 1250 setters.putAll(source.setters); 1251 delegateList.addAll(source.delegateList); 1252 children.addAll(source.children); 1255 } 1256} 1257 | Popular Tags |