Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/aendvari/cerberus/component/assembly/ComponentAssembler.java


1   /*
2    * ComponentAssembler.java
3    *
4    * Copyright (c) 2001, 2002 Aendvari, Ltd. All Rights Reserved.
5    *
6    * See the file LICENSE for terms of use.
7    *
8    */
9   
10  package com.aendvari.cerberus.component.assembly;
11  
12  import java.util.ArrayList;
13  import java.util.Collection;
14  import java.util.HashSet;
15  import java.util.Iterator;
16  
17  import com.aendvari.cerberus.component.descriptor.*;
18  import com.aendvari.cerberus.component.directory.*;
19  
20  /**
21   * <p>Builds the components defined in a {@link AssemblyDescriptor}.</p>
22   *
23   * <p>The {@link AssemblyContext} may be used to provide context specific
24   * information during the build process.</p>
25   *
26   * @author  Trevor Milne
27   *
28   */
29  
30  public class ComponentAssembler
31  {
32    /** The {@link AssemblyContext} supplying context specific information. */
33    private AssemblyContext context;
34  
35    /** The {@link AssemblyDescriptor} to build the components from. */
36    private AssemblyDescriptor assembly;
37  
38    /** The {@link ComponentDirectory} to register components into. */
39    private ComponentDirectory directory;
40  
41  
42    /* Constructors. */
43  
44  
45    /**
46     * Constructs a <code>ComponentAssembler</code> instance.
47     *
48     * The components defined in the supplied {@link AssemblyDescriptor} are assembled
49     * in context of the supplied {@link AssemblyContext}.
50     *
51     * @param    setAssemblyContext      The {@link AssemblyContext} to build with.
52     * @param    setAssembly          The {@link AssemblyDescriptor} to build from.
53     * @param    setDirectory        The {@link ComponentDirectory} to register components.
54     *
55     */
56  
57    public ComponentAssembler(
58      AssemblyContext setAssemblyContext, AssemblyDescriptor setAssembly, ComponentDirectory setDirectory)
59    {
60      context = setAssemblyContext;
61      assembly = setAssembly;
62      directory = setDirectory;
63    }
64  
65  
66    /* Namespaces. */
67  
68  
69    /**
70     * Adds the supplied namespace to a property name if one does not exist.
71     *
72     * @param    namespace          The namespace that uniquely identifies the property.
73     * @param    name            The name of the property.
74     *
75     * @return                  A uniquely identifiable property name.
76     *
77     */
78  
79    private String addNamespace(String namespace, String name)
80    {
81      // check for namespace
82      if (name.indexOf('.') < 0)
83      {
84        // relative attribute name, include namespace
85        name = namespace + "." + name;
86      }
87  
88      return name;
89    }
90  
91    /**
92     * Allows various resolution methods to act on both attributes and messages.
93     *
94     */
95  
96    private interface PropertyResolver
97    {
98      /**
99       * Returns whether the specified property is available within the component definition.
100      *
101      * @param    definition          The {@link ComponentDefinition} to search.
102      * @param    name            The name of the property.
103      *
104      * @return                  True if the definition contains the named property.
105      *
106      */
107 
108     public boolean hasProperty(ComponentDefinition definition, String name);
109 
110     /**
111      * Creates an error message describing an undefined property.
112      *
113      * @param    property          The name of the property.
114      * @param    reference          The context in which the property was referenced.
115      *
116      * @return                  A string describing the error.
117      *
118      */
119 
120     public String getUndefinedPropertyMessage(String property, String reference);
121 
122     /**
123      * Creates an error message describing an ambiguously reference property.
124      *
125      * @param    property          The name of the property.
126      * @param    namespaces          The potential namespaces that could be chosen.
127      * @param    reference          The context inwhich the property was referenced.
128      *
129      * @return                  A string describing the error.
130      *
131      */
132 
133     public String getAmbiguousPropertyMessage(String property, Collection namespaces, String reference);
134   }
135 
136   /**
137    * An implementation of the {@link PropertyResolver} for component attributes.
138    *
139    */
140 
141   private class AttributeResolver implements PropertyResolver
142   {
143     public boolean hasProperty(ComponentDefinition definition, String name)
144     {
145       return (definition.getAttribute(name) != null);
146     }
147 
148     public String getUndefinedPropertyMessage(String attribute, String reference)
149     {
150       // call outer class method
151       return createUndefinedPropertyMessage("attribute", attribute, reference);
152     }
153 
154     public String getAmbiguousPropertyMessage(String attribute, Collection namespaces, String reference)
155     {
156       // call outer class method
157       return createAmbiguousPropertyMessage("attribute", attribute, namespaces, reference);
158     }
159   }
160 
161   /**
162    * An implementation of the {@link PropertyResolver} for component messages.
163    *
164    */
165 
166   private class MessageResolver implements PropertyResolver
167   {
168     public boolean hasProperty(ComponentDefinition definition, String name)
169     {
170       return (definition.getMessage(name) != null);
171     }
172 
173     public String getUndefinedPropertyMessage(String message, String reference)
174     {
175       // call outer class method
176       return createUndefinedPropertyMessage("message", message, reference);
177     }
178 
179     public String getAmbiguousPropertyMessage(String message, Collection namespaces, String reference)
180     {
181       // call outer class method
182       return createAmbiguousPropertyMessage("message", message, namespaces, reference);
183     }
184   }
185 
186   /**
187    * Resolves the namespace for the specified property. The definition hierarchy
188    * is traversed to find the namespace for the property.
189    *
190    * @param    definition          The {@link ComponentDefinition} containing the property.
191    * @param    property          The name of the property to resolve.
192    * @param    resolver          The {@link PropertyResolver} to access property data.
193    * @param    context            The current operating context for error reporting.
194    *
195    * @return                  The resolved property name.
196    *
197    * @exception  AssemblyException      Thrown when a problem occurs during assembly.
198    *
199    */
200 
201   private String resolveProperty(
202     ComponentDefinition definition, String property, PropertyResolver resolver, String context)
203       throws AssemblyException
204   {
205     // check for namespace
206     if (property.indexOf('.') >= 0)
207     {
208       // check that reference is valid
209       validateProperty(definition, property, resolver, context);
210 
211       // if namespace available, then no resolution is needed
212       return property;
213     }
214 
215     // travel up hierarchy and search for match
216     ArrayList searchDefinitions = new ArrayList();
217     HashSet searchResults = new HashSet();
218 
219     // start at local namespace
220     searchDefinitions.add(definition);
221 
222     // continue until all parents have been visited
223     while (searchDefinitions.size() > 0)
224     {
225       // create copy of search list to allow safe iteration
226       ArrayList iterationCollection = new ArrayList(searchDefinitions);
227       Iterator searchIterator = iterationCollection.iterator();
228 
229       while (searchIterator.hasNext())
230       {
231         ComponentDefinition searchDefinition = (ComponentDefinition)searchIterator.next();
232 
233         // does property exist in this namespace
234 
235         // make namespace same as current definition
236         String potential = addNamespace(searchDefinition.getName(), property);
237 
238         // search definition
239         if (resolver.hasProperty(searchDefinition, potential))
240         {
241           // property exists, add namespace to result (should be only one)
242           searchResults.add(searchDefinition);
243         }
244         else
245         {
246           // property not found continue up tree
247           Iterator parentIterator = searchDefinition.getParents().iterator();
248 
249           while (parentIterator.hasNext())
250           {
251             String parentName = (String)parentIterator.next();
252             ComponentDefinition parentDefinition = assembly.getDefinition(parentName);
253 
254             // add parent to search list
255             searchDefinitions.add(parentDefinition);
256           }
257         }
258 
259         // remove definition from search list
260         searchDefinitions.remove(searchDefinition);
261       }
262     }
263 
264     // error if no match was found
265     if (searchResults.size() == 0)
266     {
267       // construct exception message
268       String message = resolver.getUndefinedPropertyMessage(property, context);
269 
270       // throw exception
271       throw new AssemblyException(message.toString());
272     }
273     else
274     // error if more than one match was found
275     if (searchResults.size() > 1)
276     {
277       // construct exception message
278       String message = resolver.getAmbiguousPropertyMessage(property, searchResults, context);
279 
280       // throw exception
281       throw new AssemblyException(message.toString());
282     }
283 
284     // retrieve property namespace
285     ComponentDefinition namespaceDefinition = (ComponentDefinition)searchResults.iterator().next();
286 
287     String resolved = addNamespace(namespaceDefinition.getName(), property);
288 
289     return resolved;
290   }
291 
292   /**
293    * Determines whether the supplied property name is valid.
294    *
295    * @param    definition          The {@link ComponentDefinition} containing the property.
296    * @param    property          The name of the property to resolve.
297    * @param    resolver          The {@link PropertyResolver} to access property data.
298    * @param    context            The current operating context for error reporting.
299    *
300    * @return                  True if the property is valid.
301    *
302    * @exception  AssemblyException      Thrown when a problem occurs during assembly.
303    *
304    */
305 
306   private void validateProperty(
307     ComponentDefinition definition, String property, PropertyResolver resolver, String context)
308       throws AssemblyException
309   {
310     // extract namespace
311     String namespace = property.substring(0, property.indexOf('.'));
312 
313     // check for valid namespace (definition)
314     ComponentDefinition propertyDefinition = assembly.getDefinition(namespace);
315 
316     if (propertyDefinition == null)
317     {
318       // construct exception message
319       String message = createUndefinedComponentDefinitionMessage(
320         namespace, context + "(" + property + ")");
321 
322       // throw exception
323       throw new AssemblyException(message.toString());
324     }
325 
326     // check for valid property within namespace
327     if (!resolver.hasProperty(propertyDefinition, property))
328     {
329       // construct exception message
330       String message = resolver.getUndefinedPropertyMessage(property, context);
331 
332       // throw exception
333       throw new AssemblyException(message.toString());
334     }
335 
336     // does namespace exist in active definition
337 
338     // travel up hierarchy and search for definition
339     ArrayList searchDefinitions = new ArrayList();
340     HashSet searchResults = new HashSet();
341 
342     // start at local namespace
343     searchDefinitions.add(definition);
344 
345     // continue until all parents have been visited
346     while (searchDefinitions.size() > 0)
347     {
348       // create copy of search list to allow safe iteration
349       ArrayList iterationCollection = new ArrayList(searchDefinitions);
350       Iterator searchIterator = iterationCollection.iterator();
351 
352       while (searchIterator.hasNext())
353       {
354         ComponentDefinition searchDefinition = (ComponentDefinition)searchIterator.next();
355 
356         // does the definition exist in the hierarchy
357         if (searchDefinition.getName().equals(propertyDefinition.getName()))
358         {
359           // the property name is valid
360           return;
361         }
362 
363         // definition not found continue up tree
364         Iterator parentIterator = searchDefinition.getParents().iterator();
365 
366         while (parentIterator.hasNext())
367         {
368           String parentName = (String)parentIterator.next();
369           ComponentDefinition parentDefinition = assembly.getDefinition(parentName);
370 
371           // add parent to search list
372           searchDefinitions.add(parentDefinition);
373         }
374 
375         // remove definition from search list
376         searchDefinitions.remove(searchDefinition);
377       }
378     }
379 
380     // the definition could not be found in the heritage
381 
382     // construct exception message
383     String message = createNonParentDefinitionMessage(
384       definition.getName(), namespace, context + "(" + property + ")");
385 
386     // throw exception
387     throw new AssemblyException(message.toString());
388   }
389 
390   /**
391    * Resolves the namespaces for the attributes referenced within the supplied value.
392    *
393    * @param    definition          The {@link ComponentDefinition} containing the property.
394    * @param    value            The {@link MultiPartValue} to modify.
395    * @param    context            The current operating context for error reporting.
396    *
397    * @exception  AssemblyException      Thrown when a problem occurs during assembly.
398    *
399    */
400 
401   private void resolveReferencedAttributes(ComponentDefinition definition, MultiPartValue value, String context)
402     throws AssemblyException
403   {
404     // resolve attributes
405     PropertyResolver resolver = new AttributeResolver();
406 
407     // create copy of original value
408     MultiPartValue originalValue = new MultiPartValue(value);
409 
410     // erase value for replacement
411     value.clear();
412 
413     Iterator valueParts = originalValue.getParts().iterator();
414 
415     while (valueParts.hasNext())
416     {
417       MultiPartValue.Part part = (MultiPartValue.Part)valueParts.next();
418 
419       // literal parts stay the same
420       if (part.getType() == MultiPartValue.Part.Type.Literal)
421       {
422         value.addLiteral(part.getValue());
423       }
424       // attribute references need to be resolved
425       else
426       {
427         String resolved = resolveProperty(definition, part.getValue(), resolver, context);
428         value.addAttribute(resolved);
429       }
430     }
431   }
432 
433   /**
434    * Resolves the namespaces for the message referenced within the supplied topic.
435    *
436    * @param    definition          The {@link ComponentDefinition} containing the message.
437    * @param    topic            The {@link MultiPartValue} topic to modify.
438    * @param    context            The current operating context for error reporting.
439    *
440    * @exception  AssemblyException      Thrown when a problem occurs during assembly.
441    *
442    */
443 
444   private void resolveReferencedMessage(ComponentDefinition definition, MultiPartValue topic, String context)
445     throws AssemblyException
446   {
447     // resolve messages
448     PropertyResolver resolver = new MessageResolver();
449 
450     // get the message name
451     String mappedName = ((MultiPartValue.Part)topic.getParts().iterator().next()).getValue();
452 
453     // erase value for replacement
454     topic.clear();
455 
456     String resolved = resolveProperty(definition, mappedName, resolver, context);
457     topic.addAttribute(resolved);
458   }
459 
460   /**
461    * Resolves the namespaces and references within the supplied component definition.
462    *
463    * @param    definition          The {@link ComponentDefinition} to resolve.
464    *
465    * @exception  AssemblyException      Thrown when a problem occurs during assembly.
466    *
467    */
468 
469   private void resolveDefinition(ComponentDefinition definition)
470     throws AssemblyException
471   {
472     String context = "the definition " + definition.getName();
473 
474     // resolve property names
475     // if no namespace is specified for property, assume local (definition) namespace
476 
477     // resolve attributes
478     Iterator attributeIterator = new ArrayList(definition.getAttributes()).iterator();
479 
480     while (attributeIterator.hasNext())
481     {
482       ComponentAttribute attribute = (ComponentAttribute)attributeIterator.next();
483 
484       // specify namespace of attribute
485       String oldName = attribute.getName();
486       String newName = addNamespace(definition.getName(), attribute.getName());
487 
488       attribute.setName(newName);
489 
490       definition.updateAttribute(oldName, attribute);
491     }
492 
493     // resolve messages
494     Iterator messageIterator = new ArrayList(definition.getMessages()).iterator();
495 
496     while (messageIterator.hasNext())
497     {
498       ComponentMessage message = (ComponentMessage)messageIterator.next();
499 
500       // specify namespace of message
501       String oldName = message.getName();
502       String newName = addNamespace(definition.getName(), message.getName());
503 
504       message.setName(newName);
505 
506       definition.updateMessage(oldName, message);
507     }
508 
509     // resolve property values
510     // if no namespace is specified for a referenced property, then search definition
511 
512     // resolve attributes
513     attributeIterator = definition.getAttributes().iterator();
514 
515     while (attributeIterator.hasNext())
516     {
517       ComponentAttribute attribute = (ComponentAttribute)attributeIterator.next();
518 
519       // manipulate attribute values
520       resolveReferencedAttributes(definition, attribute.getValue(), context);
521     }
522 
523     // resolve messages
524     messageIterator = definition.getMessages().iterator();
525 
526     while (messageIterator.hasNext())
527     {
528       ComponentMessage message = (ComponentMessage)messageIterator.next();
529 
530       // check for mapped message
531       if (message.isMapped())
532       {
533         // manipulate mapped message
534         resolveReferencedMessage(definition, message.getTopic(), context);
535       }
536       else
537       {
538         // manipulate attribute values
539         resolveReferencedAttributes(definition, message.getTopic(), context);
540       }
541     }
542   }
543 
544   /**
545    * Resolves the namespaces and references within the supplied component definitions.
546    *
547    * @param    definitions          The {@link ComponentDefinition ComponentDefinitions} to resolve.
548    *
549    * @exception  AssemblyException      Thrown when a problem occurs during assembly.
550    *
551    */
552 
553   private void resolveDefinitions(Collection definitions)
554     throws AssemblyException
555   {
556     // resolve definition namespaces, visit in order of hierarchy
557 
558     ArrayList resolvedParents = new ArrayList();
559     ArrayList unresolved = new ArrayList(definitions);
560 
561     // continue until all definitions have been resolved
562     while (unresolved.size() > 0)
563     {
564       // create copy of unresolved list to allow safe iteration
565       ArrayList iterationCollection = new ArrayList(unresolved);
566       int unresolvedSize = unresolved.size();
567 
568       // resolve any definitions that have their parents resolved
569       Iterator definitionIterator = iterationCollection.iterator();
570 
571       while (definitionIterator.hasNext())
572       {
573         ComponentDefinition definition = (ComponentDefinition)definitionIterator.next();
574 
575         // can be resolved, if all parents have been resolved
576         if (resolvedParents.containsAll(definition.getParents()))
577         {
578           resolveDefinition(definition);
579 
580           // remove from unresolved list
581           unresolved.remove(definition);
582 
583           // add as potential parent
584           resolvedParents.add(definition.getName());
585         }
586       }
587 
588       // if no changes were made, then a cycle or undefined parent has been detected
589       if (unresolved.size() == unresolvedSize)
590       {
591         // construct exception message
592         String message = createUndefinedParentsMessage(unresolved);
593 
594         // throw exception
595         throw new AssemblyException(message.toString());
596       }
597     }
598   }
599 
600   /**
601    * Resolves the namespaces and references within the supplied component instance.
602    *
603    * @param    instance          The {@link ComponentInstance} to resolve.
604    * @param    definition          The {@link ComponentDefinition} of the instance.
605    *
606    * @exception  AssemblyException      Thrown when a problem occurs during assembly.
607    *
608    */
609 
610   private void resolveInstance(ComponentInstance instance, ComponentDefinition definition)
611     throws AssemblyException
612   {
613     PropertyResolver attributeResolver = new AttributeResolver();
614     PropertyResolver messageResolver = new MessageResolver();
615 
616     String context = "the instance " + instance.getName() + "(" + definition.getName() + ")";
617 
618     // resolve property names
619 
620     // resolve attributes
621     Iterator attributeIterator = new ArrayList(instance.getAttributes()).iterator();
622 
623     while (attributeIterator.hasNext())
624     {
625       ComponentAttribute attribute = (ComponentAttribute)attributeIterator.next();
626 
627       // determine namespace of attribute
628       String oldName = attribute.getName();
629       String newName = resolveProperty(definition, oldName, attributeResolver, context);
630 
631       attribute.setName(newName);
632 
633       instance.updateAttribute(oldName, attribute);
634     }
635 
636     // resolve messages
637     Iterator messageIterator = new ArrayList(instance.getMessages()).iterator();
638 
639     while (messageIterator.hasNext())
640     {
641       ComponentMessage message = (ComponentMessage)messageIterator.next();
642 
643       // determine namespace of message
644       String oldName = message.getName();
645       String newName = resolveProperty(definition, oldName, messageResolver, context);
646 
647       message.setName(newName);
648 
649       instance.updateMessage(oldName, message);
650     }
651 
652     // resolve property values
653 
654     // resolve attributes
655     attributeIterator = instance.getAttributes().iterator();
656 
657     while (attributeIterator.hasNext())
658     {
659       ComponentAttribute attribute = (ComponentAttribute)attributeIterator.next();
660 
661       // manipulate attribute values
662       resolveReferencedAttributes(definition, attribute.getValue(), context);
663     }
664 
665     // resolve messages
666     messageIterator = instance.getMessages().iterator();
667 
668     while (messageIterator.hasNext())
669     {
670       ComponentMessage message = (ComponentMessage)messageIterator.next();
671 
672       // check for mapped message
673       if (message.isMapped())
674       {
675         // manipulate mapped message
676         resolveReferencedMessage(definition, message.getTopic(), context);
677       }
678       else
679       {
680         // manipulate attribute values
681         resolveReferencedAttributes(definition, message.getTopic(), context);
682       }
683     }
684   }
685 
686 
687   /* Assembly. */
688 
689 
690   /**
691    * Creates a property value based on the {@link MultiPartValue} and the
692    * attributes defined in the {@link ComponentDescriptor}.
693    *
694    * @param    descriptor          The {@link ComponentDescriptor} for the component.
695    * @param    name            The name of the property.
696    * @param    value            A {@link MultiPartValue} describing the value.
697    *
698    * @exception  AssemblyException      Thrown when a problem occurs during assembly.
699    *
700    */
701 
702   private String assemblePropertyValue(ComponentDescriptor descriptor, String name, MultiPartValue value)
703     throws AssemblyException
704   {
705     String context = "the attribute " + name + " of the component " + descriptor.getName();
706     StringBuffer complete = new StringBuffer();
707 
708     Iterator partIterator = value.getParts().iterator();
709 
710     while (partIterator.hasNext())
711     {
712       // get value part
713       MultiPartValue.Part part = (MultiPartValue.Part)partIterator.next();
714 
715       // translate into string value
716       if (part.getType() == MultiPartValue.Part.Type.Literal)
717       {
718         // append literal value
719         complete.append(part.getValue());
720       }
721       else
722       {
723         // get attribute
724         ComponentDescriptor.Attribute attributeObject = descriptor.getAttribute(part.getValue());
725 
726         if (attributeObject == null)
727         {
728           // construct exception message
729           String message = createUndefinedPropertyMessage("attribute", part.getValue(), context);
730 
731           // throw exception
732           throw new AssemblyException(message.toString());
733         }
734 
735         // get attribute value
736         String attribute = attributeObject.getValue();
737 
738         // check for null value
739         if (attribute == null)
740         {
741           // construct exception message
742           String message = createUndefinedPropertyValueMessage("attribute", part.getValue(), context);
743 
744           // throw exception
745           throw new AssemblyException(message.toString());
746         }
747 
748         // append to value
749         complete.append(attribute);
750       }
751     }
752 
753     return complete.toString();
754   }
755 
756   /**
757    * Checks that the attributes used in the supplied {@link MultiPartValue}
758    * have been defined in the {@link ComponentDescriptor}.
759    *
760    * @param    descriptor          The {@link ComponentDescriptor} for the component.
761    * @param    value            A {@link MultiPartValue} describing the value.
762    *
763    */
764 
765   private boolean dependenciesAvailable(ComponentDescriptor descriptor, MultiPartValue value)
766   {
767     Iterator partIterator = value.getParts().iterator();
768 
769     while (partIterator.hasNext())
770     {
771       // get value part
772       MultiPartValue.Part part = (MultiPartValue.Part)partIterator.next();
773 
774       // check if values for internal attributes have been provided
775       if (part.getType() == MultiPartValue.Part.Type.Attribute)
776       {
777         // get attribute value
778         ComponentDescriptor.Attribute attribute = descriptor.getAttribute(part.getValue());
779 
780         // check for null value
781         if (attribute == null)
782         {
783           return false;
784         }
785       }
786     }
787 
788     return true;
789   }
790 
791   /**
792    * Creates and stores attribute values in the {@link ComponentDescriptor}.
793    * Any attributes referenced by the {@link MultiPartValue} are taken from the other
794    * attributes in the {@link ComponentDescriptor}. Those attributes must have their
795    * values specified before being used by another attribute.
796    *
797    * @param    definition          The {@link ComponentDefinition} for the component.
798    * @param    instance          The {@link ComponentInstance} for the component.
799    * @param    descriptor          The {@link ComponentDescriptor} for the component.
800    *
801    * @exception  AssemblyException      Thrown when a problem occurs during assembly.
802    *
803    */
804 
805   private void assembleComponentAttributes(
806     ComponentDefinition definition, ComponentInstance instance, ComponentDescriptor descriptor)
807       throws AssemblyException
808   {
809     Collection unresolved = new ArrayList(definition.getAttributes());
810 
811     // continue looping through attributes until all have been resolved or cycle has been detected
812     while (unresolved.size() > 0)
813     {
814       // make a pass through the attributes and set any that can be resolved
815       Collection iterationCollection = new ArrayList(unresolved);
816       Iterator attributeIterator = iterationCollection.iterator();
817 
818       int unresolvedSize = unresolved.size();
819 
820       while (attributeIterator.hasNext())
821       {
822         // get attribute definitions
823         ComponentAttribute definitionAttribute = (ComponentAttribute)attributeIterator.next();
824         ComponentAttribute instanceAttribute = instance.getAttribute(definitionAttribute.getName());
825 
826         // check if instance provides value
827         if (instanceAttribute != null)
828         {
829           // use value specified by instance
830 
831           // check if dependent attributes have been resolved
832           boolean valid = dependenciesAvailable(descriptor, instanceAttribute.getValue());
833 
834           if (valid)
835           {
836             // the value has been specified
837 
838             // remove attribute from undefined list
839             unresolved.remove(definitionAttribute);
840 
841             // set value in descriptor
842             String value = assemblePropertyValue(
843               descriptor, instanceAttribute.getName(), instanceAttribute.getValue());
844 
845             descriptor.addAttribute(definitionAttribute.getName(), definitionAttribute.getAccess(), value);
846           }
847         }
848         else
849         {
850           // check if attribute already defined (by parent or composite)
851           if (descriptor.getAttribute(definitionAttribute.getName()) != null)
852           {
853             // the value has been specified (typically by composite)
854 
855             // remove attribute from undefined list
856             unresolved.remove(definitionAttribute);
857           }
858           else
859           {
860             // check if default value is available in definition
861             MultiPartValue defaultValue = definitionAttribute.getValue();
862 
863             if (defaultValue.getParts().size() > 0)
864             {
865               // check if dependent attributes have been resolved
866               boolean valid = dependenciesAvailable(descriptor, defaultValue);
867 
868               if (valid)
869               {
870                 // the value has been specified
871 
872                 // remove attribute from undefined list
873                 unresolved.remove(definitionAttribute);
874 
875                 // set value in descriptor
876                 String value = assemblePropertyValue(
877                   descriptor, definitionAttribute.getName(), definitionAttribute.getValue());
878 
879                 descriptor.addAttribute(definitionAttribute.getName(), definitionAttribute.getAccess(), value);
880               }
881             }
882           }
883         }
884       }
885 
886       // check for attribute cycle
887       if (unresolved.size() == unresolvedSize)
888       {
889         // remove any non-required attributes
890         iterationCollection = new ArrayList(unresolved);
891         attributeIterator = iterationCollection.iterator();
892 
893         while (attributeIterator.hasNext())
894         {
895           ComponentAttribute definitionAttribute = (ComponentAttribute)attributeIterator.next();
896 
897           // non-required attributes do not cause cycle
898           if (!definitionAttribute.getRequired())
899           {
900             unresolved.remove(definitionAttribute);
901           }
902         }
903 
904         // check if still unresolved attributes
905         if (unresolved.size() == unresolvedSize)
906         {
907           // construct exception message
908           String message = createUnresolvedAttributesMessage(descriptor, unresolved);
909 
910           // throw exception
911           throw new AssemblyException(message.toString());
912         }
913       }
914     }
915   }
916 
917   /**
918    * Determines the message topic referenced by the supplied message.
919    *
920    * @param    descriptor          The {@link ComponentDescriptor} for the component.
921    * @param    definitionMessage      The {@link ComponentMessage} of the message definition.
922    * @param    resolveMessage        The {@link ComponentMessage} containing the message to resolve.
923    * @param    unresolved          The list of currently unresolved messages.
924    *
925    */
926 
927   private void mapMessage(
928     ComponentDescriptor descriptor,
929     ComponentMessage definitionMessage, ComponentMessage resolveMessage,
930     Collection unresolved)
931   {
932     // get name of message, 'topic' contains literal part of mapped message
933     String messageName =
934       ((MultiPartValue.Part)resolveMessage.getTopic().getParts().iterator().next()).getValue();
935 
936     // get predefined message from descriptor
937     ComponentDescriptor.Message mappedMessage = descriptor.getMessage(messageName);
938 
939     // check if message has topic
940     if (mappedMessage != null)
941     {
942       // copy message topic
943       descriptor.addMessage(
944         definitionMessage.getName(), definitionMessage.getType(), definitionMessage.getAccess(),
945         mappedMessage.getTopic(), definitionMessage.getSignature());
946 
947       // remove message from undefined list
948       unresolved.remove(definitionMessage);
949     }
950   }
951 
952   /**
953    * Creates and stores message topic values in the {@link ComponentDescriptor}.
954    * Any attributes referenced by the {@link MultiPartValue} are taken from the
955    * attributes in the {@link ComponentDescriptor}. Any messages referenced are
956    * taken from those in the {@link ComponentDescriptor}.
957    *
958    * @param    definition          The {@link ComponentDefinition} for the component.
959    * @param    instance          The {@link ComponentInstance} for the component.
960    * @param    descriptor          The {@link ComponentDescriptor} for the component.
961    *
962    * @exception  AssemblyException      Thrown when a problem occurs during assembly.
963    *
964    */
965 
966   private void assembleComponentMessages(
967     ComponentDefinition definition, ComponentInstance instance, ComponentDescriptor descriptor)
968       throws AssemblyException
969   {
970     Collection unresolved = new ArrayList(definition.getMessages());
971 
972     // continue looping through messages until all have been resolved or cycle has been detected
973     while (unresolved.size() > 0)
974     {
975       // pass through the message and set any that can be resolved
976       Collection iterationCollection = new ArrayList(unresolved);
977       Iterator messageIterator = definition.getMessages().iterator();
978 
979       int unresolvedSize = unresolved.size();
980 
981       while (messageIterator.hasNext())
982       {
983         // get message definitions
984         ComponentMessage definitionMessage = (ComponentMessage)messageIterator.next();
985         ComponentMessage instanceMessage = instance.getMessage(definitionMessage.getName());
986 
987         // check if instance provides value
988         if (instanceMessage != null)
989         {
990           // use topic specified by instance
991 
992           // check if mapped message
993           if (instanceMessage.isMapped())
994           {
995             // map to the message specified in the instance message
996             mapMessage(descriptor, definitionMessage, instanceMessage, unresolved);
997           }
998           else
999           // use value of instance
1000          {
1001            // set value in descriptor
1002            String value = assemblePropertyValue(
1003              descriptor, instanceMessage.getName(), instanceMessage.getTopic());
1004
1005            descriptor.addMessage(
1006              definitionMessage.getName(), definitionMessage.getType(), definitionMessage.getAccess(),
1007              value, definitionMessage.getSignature());
1008
1009            // remove message from undefined list
1010            unresolved.remove(definitionMessage);
1011          }
1012        }
1013        else
1014        {
1015          // check if topic already defined (by parent or composite)
1016          if (descriptor.getMessage(definitionMessage.getName()) != null)
1017          {
1018            // the value has been specified (typically by composite)
1019
1020            // remove message from undefined list
1021            unresolved.remove(definitionMessage);
1022          }
1023          // use topic specified by definition, if any
1024          else
1025          {
1026            // check if mapped message
1027            if (definitionMessage.isMapped())
1028            {
1029              // map to the message specified in the definition message
1030              mapMessage(descriptor, definitionMessage, definitionMessage, unresolved);
1031            }
1032            else
1033            {
1034              // check if default topic is available in definition
1035              MultiPartValue defaultTopic = definitionMessage.getTopic();
1036
1037              // if available, override any previously defined topic (ie. composite)
1038              if (defaultTopic.getParts().size() > 0)
1039              {
1040                // set value in descriptor
1041                String value = assemblePropertyValue(
1042                  descriptor, definitionMessage.getName(), definitionMessage.getTopic());
1043
1044                descriptor.addMessage(
1045                  definitionMessage.getName(), definitionMessage.getType(), definitionMessage.getAccess(),
1046                  value, definitionMessage.getSignature());
1047
1048                // remove message from undefined list
1049                unresolved.remove(definitionMessage);
1050              }
1051            }
1052          }
1053        }
1054      }
1055
1056      // check for message mapping cycle
1057      if (unresolved.size() == unresolvedSize)
1058      {
1059        // remove any non-required messages
1060        iterationCollection = new ArrayList(unresolved);
1061        messageIterator = iterationCollection.iterator();
1062
1063        while (messageIterator.hasNext())
1064        {
1065          ComponentMessage definitionMessage = (ComponentMessage)messageIterator.next();
1066
1067          // non-required messages do not cause cycle
1068          if (!definitionMessage.getRequired())
1069          {
1070            unresolved.remove(definitionMessage);
1071          }
1072        }
1073
1074        // check if still unresolved messages
1075        if (unresolved.size() == unresolvedSize)
1076        {
1077          // construct exception message
1078          String message = createUnresolvedMessageTopicsMessage(descriptor, unresolved);
1079
1080          // throw exception
1081          throw new AssemblyException(message.toString());
1082        }
1083      }
1084    }
1085  }
1086
1087  /**
1088   * Creates the specified component from the {@link ComponentInstance} and {@link ComponentDescriptor}.
1089   *
1090   * @param    instance          The {@link ComponentInstance} to assemble.
1091   * @param    definition          The {@link ComponentDefinition} of the component.
1092   * @param    descriptor          The {@link ComponentDescriptor} to store information.
1093   *
1094   * @exception  AssemblyException      Thrown when a problem occurs during assembly.
1095   *
1096   */
1097
1098  private void createComponent(ComponentInstance instance, ComponentDefinition definition, ComponentDescriptor descriptor)
1099    throws AssemblyException
1100  {
1101    try
1102    {
1103      // get component class
1104      Class componentClass = Class.forName(definition.getBackingClass());
1105
1106      // create instance
1107      AssembledComponent componentInstance = (AssembledComponent)componentClass.newInstance();
1108
1109      // provide descriptor to component
1110      componentInstance.createComponent(context, descriptor);
1111    }
1112    catch (ClassNotFoundException exception)
1113    {
1114      throw new AssemblyException(exception);
1115    }
1116    catch (InstantiationException exception)
1117    {
1118      throw new AssemblyException(exception);
1119    }
1120    catch (IllegalAccessException exception)
1121    {
1122      throw new AssemblyException(exception);
1123    }
1124  }
1125
1126  /**
1127   * Stores the information of the base definition into the full definition.
1128   *
1129   * @param    baseDefinition        The {@link ComponentDefinition} to retrieve data from.
1130   * @param    fullDefinition        The {@link ComponentDefinition} to store data into.
1131   *
1132   */
1133
1134  private void assembleDefinition(ComponentDefinition baseDefinition, ComponentDefinition fullDefinition)
1135  {
1136    // parents are copied into full definition first, children override parent
1137
1138    // copy parent values
1139    Iterator parents = baseDefinition.getParents().iterator();
1140
1141    while (parents.hasNext())
1142    {
1143      // get name of definition
1144      String name = (String)parents.next();
1145
1146      // add as parent
1147      fullDefinition.addParent(name);
1148
1149      // get component definition from assembly descriptor
1150      ComponentDefinition definition = assembly.getDefinition(name);
1151
1152      // add parent's information to full definition
1153      assembleDefinition(definition, fullDefinition);
1154    }
1155
1156    // copy attributes
1157    Iterator attributeIterator = baseDefinition.getAttributes().iterator();
1158
1159    while (attributeIterator.hasNext())
1160    {
1161      ComponentAttribute attribute = (ComponentAttribute)attributeIterator.next();
1162
1163      fullDefinition.addAttribute(attribute);
1164    }
1165
1166    // copy messages
1167    Iterator messageIterator = baseDefinition.getMessages().iterator();
1168
1169    while (messageIterator.hasNext())
1170    {
1171      ComponentMessage message = (ComponentMessage)messageIterator.next();
1172
1173      fullDefinition.addMessage(message);
1174    }
1175
1176    // copy components
1177    Iterator componentIterator = baseDefinition.getComponents().iterator();
1178
1179    while (componentIterator.hasNext())
1180    {
1181      ComponentInstance instance = (ComponentInstance)componentIterator.next();
1182
1183      fullDefinition.addComponent(instance);
1184    }
1185  }
1186
1187  /**
1188   * Copies the attributes and messages of the source definition into the destination definition.
1189   * The source definition becomes an implicit parent of the destination definition.
1190   *
1191   * @param    source            The source {@link ComponentDefinition}.
1192   * @param    destination          The destination {@link ComponentDefinition}.
1193   *
1194   * @exception  AssemblyException      Thrown when a problem occurs during assembly.
1195   *
1196   */
1197
1198  private void includeDefinition(ComponentDefinition source, ComponentDefinition destination)
1199    throws AssemblyException
1200  {
1201    // source definition as parent of definition
1202    if (source.getName() != null)
1203    {
1204      destination.addParent(source.getName());
1205    }
1206
1207    // add parents
1208    Iterator parentIterator = source.getParents().iterator();
1209
1210    while (parentIterator.hasNext())
1211    {
1212      String parent = (String)parentIterator.next();
1213      destination.addParent(parent);
1214    }
1215
1216    // add attributes
1217    Iterator attributeIterator = source.getAttributes().iterator();
1218
1219    while (attributeIterator.hasNext())
1220    {
1221      ComponentAttribute attribute = (ComponentAttribute)attributeIterator.next();
1222      destination.addAttribute(attribute);
1223    }
1224
1225    // add messages
1226    Iterator messageIterator = source.getMessages().iterator();
1227
1228    while (messageIterator.hasNext())
1229    {
1230      ComponentMessage message = (ComponentMessage)messageIterator.next();
1231      destination.addMessage(message);
1232    }
1233  }
1234
1235  /**
1236   * Creates a new descriptor containing only attributes and messages of the
1237   * specified definition. Elliminates temporary properties built during assembly.
1238   * Whenever possible, simple references (no namespace) to attributes and messages
1239   * are created.
1240   *
1241   * @param    definition          The instance {@link ComponentDefinition}.
1242   * @param    descriptor          The {@link ComponentDescriptor} containing final values.
1243   *
1244   * @return                  A descriptor containing only those properties in the definition.
1245   *
1246   * @exception  AssemblyException      Thrown when a problem occurs during assembly.
1247   *
1248   */
1249
1250  private ComponentDescriptor createDefinitionDescriptor(ComponentDefinition specification, ComponentDescriptor descriptor)
1251    throws AssemblyException
1252  {
1253    // create basic definition (do not include owning composite)
1254    ComponentDefinition definition = new ComponentDefinition();
1255
1256    definition.setName(specification.getName());
1257    assembleDefinition(specification, definition);
1258
1259    // create empty descriptor
1260    ComponentDescriptor definitionDescriptor = new ComponentDescriptor();
1261
1262    // add attributes
1263    PropertyResolver attributeResolver = new AttributeResolver();
1264    Iterator attributeIterator = definition.getAttributes().iterator();
1265
1266    while (attributeIterator.hasNext())
1267    {
1268      ComponentAttribute definitionAttribute = (ComponentAttribute)attributeIterator.next();
1269
1270      ComponentDescriptor.Attribute descriptorAttribute =
1271        (ComponentDescriptor.Attribute)descriptor.getAttribute(definitionAttribute.getName());
1272
1273      // if no value provided, then skip attribute
1274      if (descriptorAttribute == null)
1275      {
1276        continue;
1277      }
1278
1279      // add attribute
1280      definitionDescriptor.addAttribute(
1281        descriptorAttribute.getName(), descriptorAttribute.getAccess(), descriptorAttribute.getValue());
1282
1283      // consider simple name
1284      String simple = descriptorAttribute.getName().substring(descriptorAttribute.getName().indexOf('.') + 1);
1285
1286      try
1287      {
1288        // check if simple is immediately available
1289        String resolved = resolveProperty(definition, simple, attributeResolver, "");
1290
1291        // if resolved is same, then a simple reference is valid
1292        if (descriptorAttribute.getName().equals(resolved))
1293        {
1294          // add attribute as simple name
1295          definitionDescriptor.addAttribute(
1296            simple, descriptorAttribute.getAccess(), descriptorAttribute.getValue());
1297        }
1298      }
1299      catch (AssemblyException exception)
1300      {
1301        // several attributes having simple name were found, can not add as simple
1302      }
1303    }
1304
1305    // add messages
1306    PropertyResolver messageResolver = new MessageResolver();
1307    Iterator messageIterator = definition.getMessages().iterator();
1308
1309    while (messageIterator.hasNext())
1310    {
1311      ComponentMessage definitionMessage = (ComponentMessage)messageIterator.next();
1312
1313      ComponentDescriptor.Message descriptorMessage =
1314        (ComponentDescriptor.Message)descriptor.getMessage(definitionMessage.getName());
1315
1316      // if no value provided, then skip message
1317      if (descriptorMessage == null)
1318      {
1319        continue;
1320      }
1321
1322      // add message
1323      definitionDescriptor.addMessage(
1324        descriptorMessage.getName(), descriptorMessage.getType(), descriptorMessage.getAccess(),
1325        descriptorMessage.getTopic(), descriptorMessage.getSignature());
1326
1327      // consider simple name
1328      String simple = descriptorMessage.getName().substring(descriptorMessage.getName().indexOf('.') + 1);
1329
1330      try
1331      {
1332        // check if simple is immediately available
1333        String resolved = resolveProperty(definition, simple, messageResolver, "");
1334
1335        // if resolved is same, then a simple reference is valid
1336        if (descriptorMessage.getName().equals(resolved))
1337        {
1338          // add attribute as simple name
1339          definitionDescriptor.addMessage(
1340            simple, descriptorMessage.getType(), descriptorMessage.getAccess(),
1341            descriptorMessage.getTopic(), descriptorMessage.getSignature());
1342        }
1343      }
1344      catch (AssemblyException exception)
1345      {
1346        // several messages having simple name were found, can not add as simple
1347      }
1348    }
1349
1350    return definitionDescriptor;
1351  }
1352
1353  /**
1354   * Assembles the specified component from the {@link ComponentInstance}.
1355   *
1356   * @param    instanceNamespace      The prefix to uniquely identify this component instance.
1357   * @param    instance          The {@link ComponentInstance} to assemble.
1358   * @param    ownerDefinition        The {@link ComponentDefinition} of the owning composite.
1359   * @param    ownerDescriptor        The {@link ComponentDescriptor} of the owning composite.
1360   *
1361   * @exception  AssemblyException      Thrown when a problem occurs during assembly.
1362   *
1363   */
1364
1365  private void assembleComponent(
1366    String instanceNamespace, ComponentInstance instance,
1367    ComponentDefinition ownerDefinition, ComponentDescriptor ownerDescriptor)
1368      throws AssemblyException
1369  {
1370    // check for instance name
1371    if (instance.getName() == null)
1372    {
1373      // construct exception message
1374      String message = createUndefinedInstanceName(instance.getDefinition());
1375
1376      // throw exception
1377      throw new AssemblyException(message.toString());
1378    }
1379
1380    // get component definition
1381    ComponentDefinition specification = assembly.getDefinition(instance.getDefinition());
1382
1383    if (specification == null)
1384    {
1385      // construct exception message
1386      String message = createUndefinedComponentDefinitionMessage(instance.getName(), instance.getDefinition());
1387
1388      // throw exception
1389      throw new AssemblyException(message.toString());
1390    }
1391
1392    // create full definition
1393    ComponentDefinition definition = new ComponentDefinition();
1394
1395    // can't use copy constructor since sub-components would be included
1396    // and assembleDefinition() would include them a second time
1397
1398    // copy basic information
1399    definition.setName(specification.getName());
1400    definition.setBackingClass(specification.getBackingClass());
1401
1402    // copy specification parents
1403    Iterator parentIterator = specification.getParents().iterator();
1404
1405    while (parentIterator.hasNext())
1406    {
1407      String parent = (String)parentIterator.next();
1408      definition.addParent(parent);
1409    }
1410
1411    // include owning composite, heritage of owner has already been built
1412    // only copy parents, attributes, and messages, ignore components
1413    includeDefinition(ownerDefinition, definition);
1414
1415    // include heritage
1416    assembleDefinition(specification, definition);
1417