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 &lt;contribution&gt; and
061     * &lt;invoke-factory&gt; ... 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 &lt;module&gt; 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 &lt;schema&;gt; within a &lt;extension-point&gt;, and for
092         * &lt;parameters-schema&gt; within a &lt;service&gt;.
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 &lt;invoke-factory&gt; and &lt;interceptor&gt; 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 &lt;conversion&gt; element (an alternative to using &lt;rules&gt;. Finds
108         * &lt;map&gt; 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 &lt;custom&gt; 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    }