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 }