001 // Copyright 2004, 2005 The Apache Software Foundation 002 // 003 // Licensed under the Apache License, Version 2.0 (the "License"); 004 // you may not use this file except in compliance with the License. 005 // You may obtain a copy of the License at 006 // 007 // http://www.apache.org/licenses/LICENSE-2.0 008 // 009 // Unless required by applicable law or agreed to in writing, software 010 // distributed under the License is distributed on an "AS IS" BASIS, 011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012 // See the License for the specific language governing permissions and 013 // limitations under the License. 014 015 package org.apache.hivemind.parse; 016 017 import java.io.BufferedInputStream; 018 import java.io.IOException; 019 import java.io.InputStream; 020 import java.util.Enumeration; 021 import java.util.HashMap; 022 import java.util.Iterator; 023 import java.util.Map; 024 import java.util.Properties; 025 026 import org.apache.commons.logging.Log; 027 import org.apache.commons.logging.LogFactory; 028 import org.apache.hivemind.ApplicationRuntimeException; 029 import org.apache.hivemind.Attribute; 030 import org.apache.hivemind.ClassResolver; 031 import org.apache.hivemind.ErrorHandler; 032 import org.apache.hivemind.Occurances; 033 import org.apache.hivemind.Resource; 034 import org.apache.hivemind.impl.AttributeImpl; 035 import org.apache.hivemind.impl.ElementImpl; 036 import org.apache.hivemind.internal.Visibility; 037 import org.apache.hivemind.schema.ElementModel; 038 import org.apache.hivemind.schema.Rule; 039 import org.apache.hivemind.schema.impl.AttributeModelImpl; 040 import org.apache.hivemind.schema.impl.ElementModelImpl; 041 import org.apache.hivemind.schema.impl.SchemaImpl; 042 import org.apache.hivemind.schema.rules.CreateObjectRule; 043 import org.apache.hivemind.schema.rules.InvokeParentRule; 044 import org.apache.hivemind.schema.rules.PushAttributeRule; 045 import org.apache.hivemind.schema.rules.PushContentRule; 046 import org.apache.hivemind.schema.rules.ReadAttributeRule; 047 import org.apache.hivemind.schema.rules.ReadContentRule; 048 import org.apache.hivemind.schema.rules.SetModuleRule; 049 import org.apache.hivemind.schema.rules.SetParentRule; 050 import org.apache.hivemind.schema.rules.SetPropertyRule; 051 import org.apache.hivemind.util.IdUtils; 052 import org.apache.oro.text.regex.MalformedPatternException; 053 import org.apache.oro.text.regex.Pattern; 054 import org.apache.oro.text.regex.Perl5Compiler; 055 import org.apache.oro.text.regex.Perl5Matcher; 056 057 /** 058 * Used to parse HiveMind module deployment descriptors. 059 * <p> 060 * TODO: The parser ignores element content except inside <contribution> and 061 * <invoke-factory> ... it probably should forbid non-whitespace content. 062 * 063 * @author Howard Lewis Ship 064 */ 065 public final class DescriptorParser extends AbstractParser 066 { 067 private static final String DEFAULT_SERVICE_MODEL = "singleton"; 068 069 private static final Log LOG = LogFactory.getLog(DescriptorParser.class); 070 071 /** 072 * States used while parsing the document. Most states correspond to a particular XML element in 073 * the document. STATE_START is the initial state, before the <module> element is reached. 074 */ 075 private static final int STATE_START = 0; 076 077 private static final int STATE_MODULE = 1; 078 079 // private static final int STATE_DESCRIPTION = 2; 080 private static final int STATE_CONFIGURATION_POINT = 3; 081 082 private static final int STATE_CONTRIBUTION = 4; 083 084 private static final int STATE_SERVICE_POINT = 5; 085 086 private static final int STATE_CREATE_INSTANCE = 6; 087 088 private static final int STATE_IMPLEMENTATION = 8; 089 090 /** 091 * Used for both <schema&;gt; within a <extension-point>, and for 092 * <parameters-schema> within a <service>. 093 */ 094 private static final int STATE_SCHEMA = 9; 095 096 private static final int STATE_ELEMENT = 10; 097 098 private static final int STATE_RULES = 11; 099 100 /** 101 * Used with <invoke-factory> and <interceptor> to collect parameters that will be 102 * passed to the implementation or interceptor factory service. 103 */ 104 private static final int STATE_COLLECT_SERVICE_PARAMETERS = 12; 105 106 /** 107 * Used with the <conversion> element (an alternative to using <rules>. Finds 108 * <map> elements. 109 */ 110 private static final int STATE_CONVERSION = 13; 111 112 /** 113 * Represents building Element hierarchy as a light-wieght DOM. 114 */ 115 116 private static final int STATE_LWDOM = 100; 117 118 /** 119 * Special state for elements that are not allowed to contain any other elements. 120 */ 121 122 private static final int STATE_NO_CONTENT = 300; 123 124 private static final String SIMPLE_ID = "[a-zA-Z0-9_]+"; 125 126 /** 127 * Format for configuration point ids, service point ids and schema ids. Consists of an optional 128 * leading underscore, followed by alphanumerics and underscores. Normal naming convention is to 129 * use a single CamelCase word, like a Java class name. 130 */ 131 public static final String ID_PATTERN = "^" + SIMPLE_ID + "$"; 132 133 /** 134 * Module ids are a sequence of simple ids seperated by periods. In practice, they look like 135 * Java package names. 136 */ 137 public static final String MODULE_ID_PATTERN = "^" + SIMPLE_ID + "(\\." + SIMPLE_ID + ")*$"; 138 139 public static final String VERSION_PATTERN = "[0-9]+(\\.[0-9]+){2}$"; 140 141 /** 142 * Temporary storage of the current {@link org.xml.sax.Attributes}. 143 */ 144 private Map _attributes = new HashMap(); 145 146 /** 147 * Built from DescriptorParser.properties. Key is element name, value is an instance of 148 * {@link ElementParseInfo}. 149 */ 150 151 private Map _elementParseInfo = new HashMap(); 152 153 private ModuleDescriptor _moduleDescriptor; 154 155 private ErrorHandler _errorHandler; 156 157 private ClassResolver _resolver; 158 159 private Perl5Compiler _compiler; 160 161 private Perl5Matcher _matcher; 162 163 private Map _compiledPatterns; 164 165 /** 166 * Map of Rule keyed on class name, used with <custom> rules. 167 */ 168 private final Map _ruleMap = new HashMap(); 169 170 private final Map OCCURS_MAP = new HashMap(); 171 172 { 173 OCCURS_MAP.put("0..1", Occurances.OPTIONAL); 174 OCCURS_MAP.put("1", Occurances.REQUIRED); 175 OCCURS_MAP.put("1..n", Occurances.ONE_PLUS); 176 OCCURS_MAP.put("0..n", Occurances.UNBOUNDED); 177 OCCURS_MAP.put("none", Occurances.NONE); 178 } 179 180 private final Map VISIBILITY_MAP = new HashMap(); 181 182 { 183 VISIBILITY_MAP.put("public", Visibility.PUBLIC); 184 VISIBILITY_MAP.put("private", Visibility.PRIVATE); 185 } 186 187 public DescriptorParser(ErrorHandler errorHandler) 188 { 189 _errorHandler = errorHandler; 190 191 initializeFromPropertiesFile(); 192 } 193 194 public void begin(String elementName, Map attributes) 195 { 196 _attributes = attributes; 197 198 switch (getState()) 199 { 200 case STATE_START: 201 202 beginStart(elementName); 203 break; 204 205 case STATE_MODULE: 206 207 beginModule(elementName); 208 break; 209 210 case STATE_CONFIGURATION_POINT: 211 212 beginConfigurationPoint(elementName); 213 break; 214 215 case STATE_CONTRIBUTION: 216 217 beginContribution(elementName); 218 break; 219 220 case STATE_LWDOM: 221 222 beginLWDom(elementName); 223 break; 224 225 case STATE_SERVICE_POINT: 226 227 beginServicePoint(elementName); 228 break; 229 230 case STATE_IMPLEMENTATION: 231 232 beginImplementation(elementName); 233 break; 234 235 case STATE_SCHEMA: 236 237 beginSchema(elementName); 238 break; 239 240 case STATE_ELEMENT: 241 242 beginElement(elementName); 243 break; 244 245 case STATE_RULES: 246 247 beginRules(elementName); 248 break; 249 250 case STATE_COLLECT_SERVICE_PARAMETERS: 251 252 beginCollectServiceParameters(elementName); 253 break; 254 255 case STATE_CONVERSION: 256 257 beginConversion(elementName); 258 break; 259 260 default: 261 262 unexpectedElement(elementName); 263 break; 264 } 265 } 266 267 /** 268 * Very similar to {@link #beginContribution(String)}, in that it creates an 269 * {@link ElementImpl}, adds it as a parameter to the 270 * {@link AbstractServiceInvocationDescriptor}, then enters STATE_LWDOM to fill in its 271 * attributes and content. 272 */ 273 274 private void beginCollectServiceParameters(String elementName) 275 { 276 ElementImpl element = buildLWDomElement(elementName); 277 278 AbstractServiceInvocationDescriptor sid = (AbstractServiceInvocationDescriptor) peekObject(); 279 280 sid.addParameter(element); 281 282 push(elementName, element, STATE_LWDOM, false); 283 } 284 285 /** 286 * Invoked when a new element starts within STATE_CONFIGURATION_POINT. 287 */ 288 private void beginConfigurationPoint(String elementName) 289 { 290 if (elementName.equals("schema")) 291 { 292 enterEmbeddedConfigurationPointSchema(elementName); 293 return; 294 } 295 296 unexpectedElement(elementName); 297 } 298 299 private void beginContribution(String elementName) 300 { 301 // This is where things get tricky, the point where we outgrew Jakarta Digester. 302 303 ElementImpl element = buildLWDomElement(elementName); 304 305 ContributionDescriptor ed = (ContributionDescriptor) peekObject(); 306 ed.addElement(element); 307 308 push(elementName, element, STATE_LWDOM, false); 309 } 310 311 private void beginConversion(String elementName) 312 { 313 if (elementName.equals("map")) 314 { 315 ConversionDescriptor cd = (ConversionDescriptor) peekObject(); 316 317 AttributeMappingDescriptor amd = new AttributeMappingDescriptor(); 318 319 push(elementName, amd, STATE_NO_CONTENT); 320 321 checkAttributes(); 322 323 amd.setAttributeName(getAttribute("attribute")); 324 amd.setPropertyName(getAttribute("property")); 325 326 cd.addAttributeMapping(amd); 327 328 return; 329 } 330 331 unexpectedElement(elementName); 332 } 333 334 private void beginElement(String elementName) 335 { 336 if (elementName.equals("attribute")) 337 { 338 enterAttribute(elementName); 339 return; 340 } 341 342 if (elementName.equals("conversion")) 343 { 344 enterConversion(elementName); 345 return; 346 } 347 348 if (elementName.equals("rules")) 349 { 350 enterRules(elementName); 351 return; 352 } 353 354 // <element> is recursive ... possible, but tricky, if using Digester. 355 356 if (elementName.equals("element")) 357 { 358 ElementModelImpl elementModel = (ElementModelImpl) peekObject(); 359 360 elementModel.addElementModel(enterElement(elementName)); 361 return; 362 } 363 364 unexpectedElement(elementName); 365 } 366 367 private void beginImplementation(String elementName) 368 { 369 370 if (elementName.equals("create-instance")) 371 { 372 enterCreateInstance(elementName); 373 return; 374 } 375 376 if (elementName.equals("invoke-factory")) 377 { 378 enterInvokeFactory(elementName); 379 return; 380 } 381 382 if (elementName.equals("interceptor")) 383 { 384 enterInterceptor(elementName); 385 return; 386 } 387 388 unexpectedElement(elementName); 389 } 390 391 private void beginLWDom(String elementName) 392 { 393 ElementImpl element = buildLWDomElement(elementName); 394 395 ElementImpl parent = (ElementImpl) peekObject(); 396 parent.addElement(element); 397 398 push(elementName, element, STATE_LWDOM, false); 399 } 400 401 /** 402 * Invoked when a new element occurs while in STATE_MODULE. 403 */ 404 private void beginModule(String elementName) 405 { 406 if (elementName.equals("configuration-point")) 407 { 408 enterConfigurationPoint(elementName); 409 410 return; 411 } 412 413 if (elementName.equals("contribution")) 414 { 415 enterContribution(elementName); 416 return; 417 } 418 419 if (elementName.equals("service-point")) 420 { 421 enterServicePoint(elementName); 422 423 return; 424 } 425 426 if (elementName.equals("implementation")) 427 { 428 enterImplementation(elementName); 429 430 return; 431 } 432 433 if (elementName.equals("schema")) 434 { 435 enterSchema(elementName); 436 return; 437 } 438 439 if (elementName.equals("sub-module")) 440 { 441 enterSubModule(elementName); 442 443 return; 444 } 445 446 if (elementName.equals("dependency")) 447 { 448 enterDependency(elementName); 449 450 return; 451 } 452 453 unexpectedElement(elementName); 454 } 455 456 private void beginRules(String elementName) 457 { 458 459 if (elementName.equals("create-object")) 460 { 461 enterCreateObject(elementName); 462 return; 463 } 464 465 if (elementName.equals("invoke-parent")) 466 { 467 enterInvokeParent(elementName); 468 return; 469 } 470 471 if (elementName.equals("read-attribute")) 472 { 473 enterReadAttribute(elementName); 474 return; 475 } 476 477 if (elementName.equals("read-content")) 478 { 479 enterReadContent(elementName); 480 return; 481 } 482 483 if (elementName.equals("set-module")) 484 { 485 enterSetModule(elementName); 486 return; 487 } 488 489 if (elementName.equals("set-property")) 490 { 491 enterSetProperty(elementName); 492 return; 493 } 494 495 if (elementName.equals("push-attribute")) 496 { 497 enterPushAttribute(elementName); 498 return; 499 } 500 501 if (elementName.equals("push-content")) 502 { 503 enterPushContent(elementName); 504 return; 505 } 506 507 if (elementName.equals("set-parent")) 508 { 509 enterSetParent(elementName); 510 return; 511 } 512 513 if (elementName.equals("custom")) 514 { 515 enterCustom(elementName); 516 517 return; 518 } 519 520 unexpectedElement(elementName); 521 } 522 523 private void beginSchema(String elementName) 524 { 525 if (elementName.equals("element")) 526 { 527 SchemaImpl schema = (SchemaImpl) peekObject(); 528 529 schema.addElementModel(enterElement(elementName)); 530 return; 531 } 532 533 unexpectedElement(elementName); 534 } 535 536 private void beginServicePoint(String elementName) 537 { 538 if (elementName.equals("parameters-schema")) 539 { 540 enterParametersSchema(elementName); 541 return; 542 } 543 544 // <service-point> allows an super-set of <implementation>. 545 546 beginImplementation(elementName); 547 } 548 549 /** 550 * begin outermost element, expect "module". 551 */ 552 private void beginStart(String elementName) 553 { 554 if (!elementName.equals("module")) 555 throw new ApplicationRuntimeException(ParseMessages.notModule( 556 elementName, 557 getLocation()), getLocation(), null); 558 559 ModuleDescriptor md = new ModuleDescriptor(_resolver, _errorHandler); 560 561 push(elementName, md, STATE_MODULE); 562 563 checkAttributes(); 564 565 md.setModuleId(getValidatedAttribute("id", MODULE_ID_PATTERN, "module-id-format")); 566 md.setVersion(getValidatedAttribute("version", VERSION_PATTERN, "version-format")); 567 568 String packageName = getAttribute("package"); 569 if (packageName == null) 570 packageName = md.getModuleId(); 571 572 md.setPackageName(packageName); 573 574 // And, this is what we ultimately return from the parse. 575 576 _moduleDescriptor = md; 577 } 578 579 protected void push(String elementName, Object object, int state) 580 { 581 if (object instanceof AnnotationHolder) 582 super.push(elementName, object, state, false); 583 else 584 super.push(elementName, object, state, true); 585 } 586 587 private ElementImpl buildLWDomElement(String elementName) 588 { 589 ElementImpl result = new ElementImpl(); 590 result.setElementName(elementName); 591 592 Iterator i = _attributes.entrySet().iterator(); 593 while (i.hasNext()) 594 { 595 Map.Entry entry = (Map.Entry) i.next(); 596 597 String name = (String) entry.getKey(); 598 String value = (String) entry.getValue(); 599 600 Attribute a = new AttributeImpl(name, value); 601 602 result.addAttribute(a); 603 } 604 605 return result; 606 } 607 608 private void checkAttributes() 609 { 610 checkAttributes(peekElementName()); 611 } 612 613 /** 614 * Checks that only known attributes are specified. Checks that all required attribute are 615 * specified. 616 */ 617 private void checkAttributes(String elementName) 618 { 619 Iterator i = _attributes.keySet().iterator(); 620 621 ElementParseInfo epi = (ElementParseInfo) _elementParseInfo.get(elementName); 622 623 // A few elements have no attributes at all. 624 625 if (epi == null) 626 { 627 epi = new ElementParseInfo(); 628 _elementParseInfo.put(elementName, epi); 629 } 630 631 // First, check that each attribute is in the set of expected attributes. 632 633 while (i.hasNext()) 634 { 635 String name = (String) i.next(); 636 637 if (!epi.isKnown(name)) 638 _errorHandler.error( 639 LOG, 640 ParseMessages.unknownAttribute(name, getElementPath()), 641 getLocation(), 642 null); 643 } 644 645 // Now check that all required attributes have been specified. 646 647 i = epi.getRequiredNames(); 648 while (i.hasNext()) 649 { 650 String name = (String) i.next(); 651 652 if (!_attributes.containsKey(name)) 653 throw new ApplicationRuntimeException(ParseMessages.requiredAttribute( 654 name, 655 getElementPath(), 656 getLocation())); 657 } 658 659 } 660 661 public void end(String elementName) 662 { 663 switch (getState()) 664 { 665 case STATE_LWDOM: 666 667 endLWDom(); 668 break; 669 670 case STATE_CONVERSION: 671 672 endConversion(); 673 break; 674 675 case STATE_SCHEMA: 676 677 endSchema(); 678 break; 679 680 default: 681 682 String content = peekContent(); 683 684 if (content != null && (peekObject() instanceof AnnotationHolder)) 685 ((AnnotationHolder) peekObject()).setAnnotation(content); 686 687 break; 688 } 689 690 // Pop the top item off the stack. 691 692 pop(); 693 } 694 695 private void endSchema() 696 { 697 SchemaImpl schema = (SchemaImpl) peekObject(); 698 699 schema.setAnnotation(peekContent()); 700 701 try 702 { 703 schema.validateKeyAttributes(); 704 } 705 catch (ApplicationRuntimeException e) 706 { 707 _errorHandler.error(LOG, ParseMessages.invalidElementKeyAttribute(schema.getId(), e), e 708 .getLocation(), e); 709 } 710 } 711 712 private void endConversion() 713 { 714 ConversionDescriptor cd = (ConversionDescriptor) peekObject(); 715 716 cd.addRulesForModel(); 717 } 718 719 private void endLWDom() 720 { 721 ElementImpl element = (ElementImpl) peekObject(); 722 element.setContent(peekContent()); 723 } 724 725 private void enterAttribute(String elementName) 726 { 727 ElementModelImpl elementModel = (ElementModelImpl) peekObject(); 728 729 AttributeModelImpl attributeModel = new AttributeModelImpl(); 730 731 push(elementName, attributeModel, STATE_NO_CONTENT); 732 733 checkAttributes(); 734 735 attributeModel.setName(getAttribute("name")); 736 attributeModel.setRequired(getBooleanAttribute("required", false)); 737 attributeModel.setUnique(getBooleanAttribute("unique", false)); 738 attributeModel.setTranslator(getAttribute("translator", "smart")); 739 740 elementModel.addAttributeModel(attributeModel); 741 } 742 743 private void enterConfigurationPoint(String elementName) 744 { 745 ModuleDescriptor md = (ModuleDescriptor) peekObject(); 746 747 ConfigurationPointDescriptor cpd = new ConfigurationPointDescriptor(); 748 749 push(elementName, cpd, STATE_CONFIGURATION_POINT); 750 751 checkAttributes(); 752 753 cpd.setId(getValidatedAttribute("id", ID_PATTERN, "id-format")); 754 755 Occurances count = (Occurances) getEnumAttribute("occurs", OCCURS_MAP); 756 757 if (count != null) 758 cpd.setCount(count); 759 760 Visibility visibility = (Visibility) getEnumAttribute("visibility", VISIBILITY_MAP); 761 762 if (visibility != null) 763 cpd.setVisibility(visibility); 764 765 cpd.setContributionsSchemaId(getAttribute("schema-id")); 766 767 md.addConfigurationPoint(cpd); 768 } 769 770 private void enterContribution(String elementName) 771 { 772 ModuleDescriptor md = (ModuleDescriptor) peekObject(); 773 774 ContributionDescriptor cd = new ContributionDescriptor(); 775 776 push(elementName, cd, STATE_CONTRIBUTION); 777 778 checkAttributes(); 779 780 cd.setConfigurationId(getAttribute("configuration-id")); 781 cd.setConditionalExpression(getAttribute("if")); 782 783 md.addContribution(cd); 784 } 785 786 private void enterConversion(String elementName) 787 { 788 ElementModelImpl elementModel = (ElementModelImpl) peekObject(); 789 790 ConversionDescriptor cd = new ConversionDescriptor(_errorHandler, elementModel); 791 792 push(elementName, cd, STATE_CONVERSION); 793 794 checkAttributes(); 795 796 cd.setClassName(getAttribute("class")); 797 798 String methodName = getAttribute("parent-method"); 799 800 if (methodName != null) 801 cd.setParentMethodName(methodName); 802 803 elementModel.addRule(cd); 804 } 805 806 private void enterCreateInstance(String elementName) 807 { 808 AbstractServiceDescriptor sd = (AbstractServiceDescriptor) peekObject(); 809 CreateInstanceDescriptor cid = new CreateInstanceDescriptor(); 810 811 push(elementName, cid, STATE_CREATE_INSTANCE); 812 813 checkAttributes(); 814 815 cid.setInstanceClassName(getAttribute("class")); 816 817 String model = getAttribute("model", DEFAULT_SERVICE_MODEL); 818 819 cid.setServiceModel(model); 820 821 sd.setInstanceBuilder(cid); 822 823 } 824 825 private void enterCreateObject(String elementName) 826 { 827 ElementModelImpl elementModel = (ElementModelImpl) peekObject(); 828 CreateObjectRule rule = new CreateObjectRule(); 829 push(elementName, rule, STATE_NO_CONTENT); 830 831 checkAttributes(); 832 833 rule.setClassName(getAttribute("class")); 834 835 elementModel.addRule(rule); 836 } 837 838 private void enterCustom(String elementName) 839 { 840 ElementModelImpl elementModel = (ElementModelImpl) peekObject(); 841 842 // Don't know what it is going to be, yet. 843 844 push(elementName, null, STATE_NO_CONTENT); 845 846 checkAttributes(); 847 848 String ruleClassName = getAttribute("class"); 849 850 Rule rule = getCustomRule(ruleClassName); 851 852 elementModel.addRule(rule); 853 } 854 855 /** 856 * Pushes STATE_ELEMENT onto the stack and creates and returns the {@link ElementModelImpl} it 857 * creates. 858 */ 859 private ElementModel enterElement(String elementName) 860 { 861 ElementModelImpl result = new ElementModelImpl(); 862 863 push(elementName, result, STATE_ELEMENT); 864 865 checkAttributes(); 866 867 result.setElementName(getAttribute("name")); 868 result.setKeyAttribute(getAttribute("key-attribute")); 869 result.setContentTranslator(getAttribute("content-translator")); 870 871 return result; 872 } 873 874 private void enterEmbeddedConfigurationPointSchema(String elementName) 875 { 876 ConfigurationPointDescriptor cpd = (ConfigurationPointDescriptor) peekObject(); 877 878 SchemaImpl schema = new SchemaImpl(); 879 880 push(elementName, schema, STATE_SCHEMA); 881 882 if (cpd.getContributionsSchemaId() != null) 883 { 884 cpd.setContributionsSchemaId(null); 885 cpd.setContributionsSchema(schema); 886 _errorHandler.error(LOG, ParseMessages.multipleContributionsSchemas(cpd.getId(), schema 887 .getLocation()), schema.getLocation(), null); 888 } 889 else 890 cpd.setContributionsSchema(schema); 891 892 checkAttributes("schema{embedded}"); 893 } 894 895 private void enterParametersSchema(String elementName) 896 { 897 ServicePointDescriptor spd = (ServicePointDescriptor) peekObject(); 898 SchemaImpl schema = new SchemaImpl(); 899 900 push(elementName, schema, STATE_SCHEMA); 901 902 checkAttributes(); 903 904 if (spd.getParametersSchemaId() != null) 905 { 906 spd.setParametersSchemaId(null); 907 spd.setParametersSchema(schema); 908 _errorHandler.error(LOG, ParseMessages.multipleParametersSchemas(spd.getId(), schema 909 .getLocation()), schema.getLocation(), null); 910 } 911 else 912 spd.setParametersSchema(schema); 913 } 914 915 private void enterImplementation(String elementName) 916 { 917 ModuleDescriptor md = (ModuleDescriptor) peekObject(); 918 919 ImplementationDescriptor id = new ImplementationDescriptor(); 920 921 push(elementName, id, STATE_IMPLEMENTATION); 922 923 checkAttributes(); 924 925 id.setServiceId(getAttribute("service-id")); 926 id.setConditionalExpression(getAttribute("if")); 927 928 md.addImplementation(id); 929 } 930 931 private void enterInterceptor(String elementName) 932 { 933 AbstractServiceDescriptor sd = (AbstractServiceDescriptor) peekObject(); 934 InterceptorDescriptor id = new InterceptorDescriptor(); 935 936 push(elementName, id, STATE_COLLECT_SERVICE_PARAMETERS); 937 938 checkAttributes(); 939 940 id.setFactoryServiceId(getAttribute("service-id")); 941 942 id.setBefore(getAttribute("before")); 943 id.setAfter(getAttribute("after")); 944 id.setName(getAttribute("name")); 945 sd.addInterceptor(id); 946 947 } 948 949 private void enterInvokeFactory(String elementName) 950 { 951 AbstractServiceDescriptor sd = (AbstractServiceDescriptor) peekObject(); 952 InvokeFactoryDescriptor ifd = new InvokeFactoryDescriptor(); 953 954 push(elementName, ifd, STATE_COLLECT_SERVICE_PARAMETERS); 955 956 checkAttributes(); 957 958 ifd.setFactoryServiceId(getAttribute("service-id", "hivemind.BuilderFactory")); 959 960 String model = getAttribute("model", DEFAULT_SERVICE_MODEL); 961 962 ifd.setServiceModel(model); 963 964 // TODO: Check if instanceBuilder already set 965 966 sd.setInstanceBuilder(ifd); 967 968 } 969 970 private void enterInvokeParent(String elementName) 971 { 972 ElementModelImpl elementModel = (ElementModelImpl) peekObject(); 973 InvokeParentRule rule = new InvokeParentRule(); 974 975 push(elementName, rule, STATE_NO_CONTENT); 976 977 checkAttributes(); 978 979 rule.setMethodName(getAttribute("method")); 980 981 if (_attributes.containsKey("depth")) 982 rule.setDepth(getIntAttribute("depth")); 983 984 elementModel.addRule(rule); 985 } 986 987 private void enterReadAttribute(String elementName) 988 { 989 ElementModelImpl elementModel = (ElementModelImpl) peekObject(); 990 ReadAttributeRule rule = new ReadAttributeRule(); 991 992 push(elementName, rule, STATE_NO_CONTENT); 993 994 checkAttributes(); 995 996 rule.setPropertyName(getAttribute("property")); 997 rule.setAttributeName(getAttribute("attribute")); 998 rule.setSkipIfNull(getBooleanAttribute("skip-if-null", true)); 999 rule.setTranslator(getAttribute("translator")); 1000 1001 elementModel.addRule(rule); 1002 } 1003 1004 private void enterReadContent(String elementName) 1005 { 1006 ElementModelImpl elementModel = (ElementModelImpl) peekObject(); 1007 ReadContentRule rule = new ReadContentRule(); 1008 1009 push(elementName, rule, STATE_NO_CONTENT); 1010 1011 checkAttributes(); 1012 1013 rule.setPropertyName(getAttribute("property")); 1014 1015 elementModel.addRule(rule); 1016 } 1017 1018 private void enterRules(String elementName) 1019 { 1020 ElementModelImpl elementModel = (ElementModelImpl) peekObject(); 1021 1022 push(elementName, elementModel, STATE_RULES); 1023 1024 } 1025 1026 private void enterSchema(String elementName) 1027 { 1028 SchemaImpl schema = new SchemaImpl(); 1029 1030 push(elementName, schema, STATE_SCHEMA); 1031 1032 checkAttributes(); 1033 1034 String id = getValidatedAttribute("id", ID_PATTERN, "id-format"); 1035 1036 schema.setId(id); 1037 1038 Visibility visibility = (Visibility) getEnumAttribute("visibility", VISIBILITY_MAP); 1039 1040 if (visibility != null) 1041 schema.setVisibility(visibility); 1042 1043 _moduleDescriptor.addSchema(schema); 1044 } 1045 1046 private void enterServicePoint(String elementName) 1047 { 1048 ModuleDescriptor md = (ModuleDescriptor) peekObject(); 1049 1050 ServicePointDescriptor spd = new ServicePointDescriptor(); 1051 1052 push(elementName, spd, STATE_SERVICE_POINT); 1053 1054 checkAttributes(); 1055 1056 String id = getValidatedAttribute("id", ID_PATTERN, "id-format"); 1057 1058 // Get the interface name, and default it to the service id if omitted. 1059 1060 String interfaceAttribute = getAttribute("interface", id); 1061 1062 // Qualify the interface name with the defined package name (which will 1063 // often implicitly or explicitly match the module id). 1064 1065 String interfaceName = IdUtils.qualify( 1066 _moduleDescriptor.getPackageName(), 1067 interfaceAttribute); 1068 1069 spd.setId(id); 1070 1071 spd.setInterfaceClassName(interfaceName); 1072 1073 spd.setParametersSchemaId(getAttribute("parameters-schema-id")); 1074 1075 Occurances count = (Occurances) getEnumAttribute("parameters-occurs", OCCURS_MAP); 1076 1077 if (count != null) 1078 spd.setParametersCount(count); 1079 1080 Visibility visibility = (Visibility) getEnumAttribute("visibility", VISIBILITY_MAP); 1081 1082 if (visibility != null) 1083 spd.setVisibility(visibility); 1084 1085 md.addServicePoint(spd); 1086 } 1087 1088 private void enterSetModule(String elementName) 1089 { 1090 ElementModelImpl elementModel = (ElementModelImpl) peekObject(); 1091 SetModuleRule rule = new SetModuleRule(); 1092 1093 push(elementName, rule, STATE_NO_CONTENT); 1094 1095 checkAttributes(); 1096 1097 rule.setPropertyName(getAttribute("property")); 1098 1099 elementModel.addRule(rule); 1100 } 1101 1102 private void enterSetParent(String elementName) 1103 { 1104 ElementModelImpl elementModel = (ElementModelImpl) peekObject(); 1105 SetParentRule rule = new SetParentRule(); 1106 1107 push(elementName, rule, STATE_NO_CONTENT); 1108 1109 checkAttributes(); 1110 1111 rule.setPropertyName(getAttribute("property")); 1112 1113 elementModel.addRule(rule); 1114 } 1115 1116 private void enterSetProperty(String elementName) 1117 { 1118 ElementModelImpl elementModel = (ElementModelImpl) peekObject(); 1119 1120 SetPropertyRule rule = new SetPropertyRule(); 1121 1122 push(elementName, rule, STATE_NO_CONTENT); 1123 1124 checkAttributes(); 1125 1126 rule.setPropertyName(getAttribute("property")); 1127 rule.setValue(getAttribute("value")); 1128 1129 elementModel.addRule(rule); 1130 } 1131 1132 private void enterPushAttribute(String elementName) 1133 { 1134 ElementModelImpl elementModel = (ElementModelImpl) peekObject(); 1135 1136 PushAttributeRule rule = new PushAttributeRule(); 1137 1138 push(elementName, rule, STATE_NO_CONTENT); 1139 1140 checkAttributes(); 1141 1142 rule.setAttributeName(getAttribute("attribute")); 1143 1144 elementModel.addRule(rule); 1145 } 1146 1147 private void enterPushContent(String elementName) 1148 { 1149 ElementModelImpl elementModel = (ElementModelImpl) peekObject(); 1150 1151 PushContentRule rule = new PushContentRule(); 1152 1153 push(elementName, rule, STATE_NO_CONTENT); 1154 1155 checkAttributes(); 1156 1157 elementModel.addRule(rule); 1158 } 1159 1160 private void enterSubModule(String elementName) 1161 { 1162 ModuleDescriptor md = (ModuleDescriptor) peekObject(); 1163 1164 SubModuleDescriptor smd = new SubModuleDescriptor(); 1165 1166 push(elementName, smd, STATE_NO_CONTENT); 1167 1168 checkAttributes(); 1169 1170 Resource descriptor = getResource().getRelativeResource(getAttribute("descriptor")); 1171 1172 smd.setDescriptor(descriptor); 1173 1174 md.addSubModule(smd); 1175 } 1176 1177 private void enterDependency(String elementName) 1178 { 1179 ModuleDescriptor md = (ModuleDescriptor) peekObject(); 1180 1181 DependencyDescriptor dd = new DependencyDescriptor(); 1182 1183 push(elementName, dd, STATE_NO_CONTENT); 1184 1185 checkAttributes(); 1186 1187 dd.setModuleId(getAttribute("module-id")); 1188 dd.setVersion(getAttribute("version")); 1189 1190 md.addDependency(dd); 1191 } 1192 1193 private String getAttribute(String name) 1194 { 1195 return (String) _attributes.get(name); 1196 } 1197 1198 private String getAttribute(String name, String defaultValue) 1199 { 1200 String result = (String) _attributes.get(name); 1201 1202 if (result == null) 1203 result = defaultValue; 1204 1205 return result; 1206 } 1207 1208 private String getValidatedAttribute(String name, String pattern, String formatKey) 1209 { 1210 String result = getAttribute(name); 1211 1212 if (!validateFormat(result, pattern)) 1213 _errorHandler.error(LOG, ParseMessages.invalidAttributeFormat( 1214 name, 1215 result, 1216 getElementPath(), 1217 formatKey), getLocation(), null); 1218 1219 return result; 1220 } 1221 1222 private boolean validateFormat(String input, String pattern) 1223 { 1224 if (_compiler == null) 1225 { 1226 _compiler = new Perl5Compiler(); 1227 _matcher = new Perl5Matcher(); 1228 _compiledPatterns = new HashMap(); 1229 } 1230 1231 Pattern compiled = (Pattern) _compiledPatterns.get(pattern); 1232 if (compiled == null) 1233 { 1234 1235 try 1236 { 1237 compiled = _compiler.compile(pattern); 1238 } 1239 catch (MalformedPatternException ex) 1240 { 1241 throw new ApplicationRuntimeException(ex); 1242 } 1243 1244 _compiledPatterns.put(pattern, compiled); 1245 } 1246 1247 return _matcher.matches(input, compiled); 1248 } 1249 1250 private boolean getBooleanAttribute(String name, boolean defaultValue) 1251 { 1252 String value = getAttribute(name); 1253 1254 if (value == null) 1255 return defaultValue; 1256 1257 if (value.equals("true")) 1258 return true; 1259 1260 if (value.equals("false")) 1261 return false; 1262 1263 _errorHandler.error( 1264 LOG, 1265 ParseMessages.booleanAttribute(value, name, getElementPath()), 1266 getLocation(), 1267 null); 1268 1269 return defaultValue; 1270 } 1271 1272 private Rule getCustomRule(String ruleClassName) 1273 { 1274 Rule result = (Rule) _ruleMap.get(ruleClassName); 1275 1276 if (result == null) 1277 { 1278 result = instantiateRule(ruleClassName); 1279 1280 _ruleMap.put(ruleClassName, result); 1281 } 1282 1283 return result; 1284 } 1285 1286 /** 1287 * Gets the value for the attribute and uses the Map to translate it to an object value. Returns 1288 * the object value if succesfully translated. Returns null if unsuccesful. If a value is 1289 * provided that isn't a key of the map, and error is logged and null is returned. 1290 */ 1291 private Object getEnumAttribute(String name, Map translations) 1292 { 1293 String value = getAttribute(name); 1294 1295 if (value == null) 1296 return null; 1297 1298 Object result = translations.get(value); 1299 1300 if (result == null) 1301 _errorHandler.error(LOG, ParseMessages.invalidAttributeValue( 1302 value, 1303 name, 1304 getElementPath()), getLocation(), null); 1305 1306 return result; 1307 } 1308 1309 private int getIntAttribute(String name) 1310 { 1311 String value = getAttribute(name); 1312 1313 try 1314 { 1315 return Integer.parseInt(value); 1316 } 1317 catch (NumberFormatException ex) 1318 { 1319 _errorHandler.error(LOG, ParseMessages.invalidNumericValue( 1320 value, 1321 name, 1322 getElementPath()), getLocation(), ex); 1323 1324 return 0; 1325 } 1326 } 1327 1328 private void initializeFromProperties(Properties p) 1329 { 1330 Enumeration e = p.propertyNames(); 1331 1332 while (e.hasMoreElements()) 1333 { 1334 String key = (String) e.nextElement(); 1335 String value = p.getProperty(key); 1336 1337 initializeFromProperty(key, value); 1338 } 1339 } 1340 1341 /** 1342 * Invoked from the constructor to read the properties file that defines certain aspects of the 1343 * operation of the parser. 1344 */ 1345 private void initializeFromPropertiesFile() 1346 { 1347 Properties p = new Properties(); 1348 1349 try 1350 { 1351 1352 InputStream propertiesIn = getClass() 1353 .getResourceAsStream("DescriptorParser.properties"); 1354 InputStream bufferedIn = new BufferedInputStream(propertiesIn); 1355 1356 p.load(bufferedIn); 1357 1358 bufferedIn.close(); 1359 } 1360 catch (IOException ex) 1361 { 1362 _errorHandler.error(LOG, ParseMessages.unableToInitialize(ex), null, ex); 1363 } 1364 1365 initializeFromProperties(p); 1366 } 1367 1368 private void initializeFromProperty(String key, String value) 1369 { 1370 if (key.startsWith("required.")) 1371 { 1372 initializeRequired(key, value); 1373 return; 1374 } 1375 1376 } 1377 1378 private void initializeRequired(String key, String value) 1379 { 1380 boolean required = value.equals("true"); 1381 1382 int lastdotx = key.lastIndexOf('.'); 1383 1384 String elementName = key.substring(9, lastdotx); 1385 String attributeName = key.substring(lastdotx + 1); 1386 1387 ElementParseInfo epi = (ElementParseInfo) _elementParseInfo.get(elementName); 1388 1389 if (epi == null) 1390 { 1391 epi = new ElementParseInfo(); 1392 _elementParseInfo.put(elementName, epi); 1393 } 1394 1395 epi.addAttribute(attributeName, required); 1396 } 1397 1398 private Rule instantiateRule(String ruleClassName) 1399 { 1400 try 1401 { 1402 Class ruleClass = _resolver.findClass(ruleClassName); 1403 1404 return (Rule) ruleClass.newInstance(); 1405 } 1406 catch (Exception ex) 1407 { 1408 throw new ApplicationRuntimeException(ParseMessages.badRuleClass( 1409 ruleClassName, 1410 getLocation(), 1411 ex), getLocation(), ex); 1412 } 1413 } 1414 1415 /** @since 1.1 */ 1416 public void initialize(Resource resource, ClassResolver resolver) 1417 { 1418 initializeParser(resource, STATE_START); 1419 1420 _resolver = resolver; 1421 } 1422 1423 /** @since 1.1 */ 1424 public ModuleDescriptor getModuleDescriptor() 1425 { 1426 return _moduleDescriptor; 1427 } 1428 1429 /** @since 1.1 */ 1430 public void reset() 1431 { 1432 super.resetParser(); 1433 1434 _moduleDescriptor = null; 1435 _attributes.clear(); 1436 _resolver = null; 1437 } 1438 }