001    /*
002    /*
003     * Copyright 2005 Stephen J. McConnell
004     *
005     * Licensed  under the  Apache License,  Version 2.0  (the "License");
006     * you may not use  this file  except in  compliance with the License.
007     * You may obtain a copy of the License at
008     *
009     *   http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed  under the  License is distributed on an "AS IS" BASIS,
013     * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
014     * implied.
015     *
016     * See the License for the specific language governing permissions and
017     * limitations under the License.
018     */
019    
020    package net.dpml.library.info;
021    
022    import java.io.File;
023    import java.io.IOException;
024    import java.io.FileNotFoundException;
025    import java.net.URI;
026    import java.net.URISyntaxException;
027    import java.util.List;
028    import java.util.ArrayList;
029    import java.util.Properties;
030    
031    import net.dpml.library.Feature;
032    import net.dpml.library.TypeBuilder;
033    import net.dpml.library.info.ResourceDirective.Classifier;
034    
035    import net.dpml.lang.Category;
036    import net.dpml.lang.Part;
037    import net.dpml.lang.Version;
038    
039    import net.dpml.util.DecodingException;
040    import net.dpml.util.DOM3DocumentBuilder;
041    import net.dpml.util.ElementHelper;
042    
043    import org.w3c.dom.Element;
044    import org.w3c.dom.Document;
045    import org.w3c.dom.TypeInfo;
046    
047    /**
048     * Utility class used for construction of a module model from an XML source.
049     *
050     * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
051     * @version 1.2.0
052     */
053    public final class LibraryDecoder extends LibraryConstants
054    {
055        private static final DOM3DocumentBuilder DOCUMENT_BUILDER = new DOM3DocumentBuilder();
056        
057       /**
058        * Construct a library directive from XML source.
059        * @param source the XML source file
060        * @return the library directive
061        * @exception IOException if an IO exception occurs
062        */
063        public LibraryDirective build( File source ) throws IOException
064        {
065            if( null == source )
066            {
067                throw new NullPointerException( "source" );
068            }
069            if( !source.exists() )
070            {
071                throw new FileNotFoundException( source.toString() );
072            }
073            if( source.isDirectory() )
074            {
075                final String error = 
076                  "File ["
077                  + source 
078                  + "] references a directory.";
079                throw new IllegalArgumentException( error );
080            }
081            try
082            {
083                final Element root = getRootElement( source );
084                File base = source.getParentFile();
085                return buildLibraryDirective( base, root );
086            }
087            catch( Exception e )
088            {
089                final String error = 
090                  "Unexpected error while attempting to load library."
091                  + "File: '" + source + "'";
092                IOException ioe = new IOException( error );
093                ioe.initCause( e );
094                throw ioe;
095            }
096        }
097        
098       /**
099        * Construct a resource directive from source.
100        * @param uri the source uri
101        * @return the resource directive
102        * @exception IOException if an IO exception occurs
103        */
104        public ResourceDirective buildResource( URI uri ) throws IOException
105        {
106            try
107            {
108                final Document document = DOCUMENT_BUILDER.parse( uri );
109                Element root = document.getDocumentElement();
110                return buildResourceDirectiveFromElement( null, root, null );
111            }
112            catch( Exception e )
113            {
114                final String error = 
115                  "Unexpected error while attempting to load module."
116                  + "URI: '" + uri + "'";
117                IOException ioe = new IOException( error );
118                ioe.initCause( e );
119                throw ioe;
120            }
121        }
122        
123       /**
124        * Resolve the root DOM element of the supplied file.
125        * @param source the source XML file
126        * @return the root element
127        * @exception IOException if an io error occurs
128        */
129        private Element getRootElement( File source ) throws IOException
130        {
131            File file = source.getCanonicalFile();
132            final Document document = DOCUMENT_BUILDER.parse( file.toURI() );
133            return document.getDocumentElement();
134        }
135        
136       /**
137        * Build a library directive using an XML element.
138        * @param base the base directory
139        * @param element the module element
140        * @return the library directive
141        * @exception Exception if an exception occurs
142        */
143        private LibraryDirective buildLibraryDirective( File base, Element element ) throws Exception
144        {
145            final String elementName = element.getTagName();
146            if( !LIBRARY_ELEMENT_NAME.equals( elementName ) )
147            {
148                final String error =
149                  "Element is not a library.";
150                throw new IllegalArgumentException( error );
151            }
152            
153            // get type descriptors, modules and properties
154            
155            Properties properties = null;
156            ImportDirective[] imports = new ImportDirective[0];
157            List list = new ArrayList();
158            Element[] children = ElementHelper.getChildren( element );
159            for( int i=0; i<children.length; i++ )
160            {
161                Element child = children[i];
162                final String tag = child.getTagName();
163                if( PROPERTIES_ELEMENT_NAME.equals( tag ) )
164                {
165                    properties = buildProperties( child );
166                }
167                else if( IMPORTS_ELEMENT_NAME.equals( tag ) )
168                {
169                    imports = buildImportDirectives( child );
170                }
171                else
172                {
173                    ResourceDirective resource = buildResourceDirectiveFromElement( base, child, null );
174                    list.add( resource );
175                }
176            }
177            ResourceDirective[] resources = (ResourceDirective[]) list.toArray( new ResourceDirective[0] );
178            return new LibraryDirective( imports, resources, properties );
179        }
180        
181       /**
182        * Build a resource using an XML element.
183        * @param base the base directory
184        * @param element the module element
185        * @param offset the imported file directory offset
186        * @throws Exception if an error occurs
187        */
188        private ResourceDirective buildResourceDirectiveFromElement( 
189          File base, Element element, String offset ) throws Exception
190        {
191            final String elementName = element.getTagName();
192            final String path = ElementHelper.getAttribute( element, "file" );
193            if( null != path )
194            {
195                try
196                {
197                    File file = new File( base, path );
198                    File source = file.getCanonicalFile();
199                    if( !source.exists() )
200                    {
201                        final String error = 
202                          "Local file does not exist."
203                          + "\n  base: " + base
204                          + "\n  path: " + path
205                          + "\n  source: " + source;
206                        throw new DecodingException( element, error ); 
207                    }
208                    else if( source.isDirectory() )
209                    {
210                        final String error = 
211                          "Local file references a directory."
212                          + "\n  base: " + base
213                          + "\n  path: " + path
214                          + "\n  source: " + source;
215                        throw new DecodingException( element, error ); 
216                    }
217                    else
218                    {
219                        final File parent = source.getParentFile();
220                        final Element root = getRootElement( source );
221                        String basedir = getRelativePath( base, parent );
222                        if( null != offset )
223                        {
224                            basedir = offset + "/" + basedir;
225                        }
226                        return buildResourceDirectiveFromElement( base, root, basedir );
227                    }
228                }
229                catch( DecodingException e )
230                {
231                    throw e;
232                }
233                catch( Throwable e )
234                {
235                    final String error = 
236                      "Internal error while attempting to resolve a import directive.";
237                    throw new DecodingException( element, error, e );
238                }
239            }
240            else
241            {
242                return buildResourceDirective( base, element, offset );
243            }
244        }
245        
246        private ResourceDirective buildResourceDirective( File base, Element element )  throws Exception
247        {
248            return buildResourceDirective( base, element, null );
249        }
250        
251        private ResourceDirective buildResourceDirective( File base, Element element, String path ) throws Exception
252        {
253            Classifier classifier = null;
254            final String tag = element.getTagName();
255            if( RESOURCE_ELEMENT_NAME.equals( tag ) || PROJECT_ELEMENT_NAME.equals( tag ) 
256              || MODULE_ELEMENT_NAME.equals( tag ) )
257            {
258                final String name = ElementHelper.getAttribute( element, "name" );
259                final String version = ElementHelper.getAttribute( element, "version" );
260                String basedir = ElementHelper.getAttribute( element, "basedir" );
261                if( path != null )
262                {
263                    if( basedir == null )
264                    {
265                        basedir = path;
266                    }
267                    else
268                    {
269                        basedir = path + "/" + basedir;
270                    }
271                }
272                
273                if( PROJECT_ELEMENT_NAME.equals( tag ) )
274                {
275                    classifier = Classifier.LOCAL;
276                    if( null == basedir )
277                    {
278                        basedir = ".";
279                    }
280                }
281                else if( MODULE_ELEMENT_NAME.equals( tag ) )
282                {
283                    if( null != basedir )
284                    {
285                        classifier = Classifier.LOCAL;
286                    }
287                    else
288                    {
289                        classifier = Classifier.EXTERNAL;
290                    }
291                }
292                else
293                {
294                    classifier = Classifier.EXTERNAL;
295                }
296                
297                final InfoDirective info = 
298                  buildInfoDirective( 
299                    ElementHelper.getChild( element, "info" ) );
300                
301                final DataDirective[] data = 
302                  buildDataTypes( 
303                    ElementHelper.getChild( element, "types" ) );
304                
305                final DependencyDirective[] dependencies = 
306                  buildDependencyDirectives( 
307                    ElementHelper.getChild( element, "dependencies" ) );
308                    
309                final FilterDirective[] filters = 
310                  buildFilterDirectives( 
311                    ElementHelper.getChild( element, "filters" ) );
312                
313                final Properties properties = 
314                  buildProperties( 
315                    ElementHelper.getChild( element, "properties" ) );
316                
317                if( MODULE_ELEMENT_NAME.equals( tag ) )
318                {
319                    File anchor = getAnchorDirectory( base, basedir );
320                    ArrayList list = new ArrayList();
321                    Element[] children = ElementHelper.getChildren( element );
322                    for( int i=0; i<children.length; i++ )
323                    {
324                        Element child = children[i];
325                        final String t = child.getTagName();
326                        
327                        if( RESOURCE_ELEMENT_NAME.equals( t ) 
328                          || PROJECT_ELEMENT_NAME.equals( t ) 
329                          || MODULE_ELEMENT_NAME.equals( t ) )
330                        {
331                            ResourceDirective directive = 
332                              buildResourceDirectiveFromElement( anchor, child, null );
333                            list.add( directive );
334                        }
335                    }
336                    
337                    ResourceDirective[] resources = 
338                      (ResourceDirective[]) list.toArray( new ResourceDirective[0] );
339                    return ModuleDirective.createModuleDirective( 
340                      name, version, classifier, basedir, info, data, dependencies, 
341                      properties, filters, resources );
342                }
343                else
344                {
345                    return ResourceDirective.createResourceDirective( 
346                      name, version, classifier, basedir, info, data, dependencies, 
347                      properties, filters );
348                }
349            }
350            else
351            {
352                final String error = 
353                  "Invalid element name [" 
354                  + tag
355                  + "].";
356                throw new DecodingException( element, error );
357            }
358        }
359        
360        private File getAnchorDirectory( File base, String path ) throws IOException
361        {
362            if( null == base )
363            {
364                return null;
365            }
366            if( !base.exists() )
367            {
368                final String error = 
369                  "Base directory [" + base + "] does not exist.";
370                throw new IllegalArgumentException( error );
371            }
372            if( null == path )
373            {
374                return base;
375            }
376            else
377            {
378                return new File( base, path ).getCanonicalFile();
379            }
380        }
381        
382        private String getRelativePath( File base, File dir ) throws IOException
383        {
384            String baseSpec = base.getCanonicalPath();
385            String dirSpec = dir.getCanonicalPath();
386            if( dirSpec.equals( baseSpec ) )
387            {
388                return ".";
389            }
390            if( dirSpec.startsWith( baseSpec ) )
391            {
392                return dirSpec.substring( baseSpec.length() + 1 );
393            }
394            else
395            {
396                final String error =
397                 "Supplied dir [" + dirSpec + "] is not with base [" + baseSpec + "].";
398                throw new IllegalArgumentException( error );
399            }
400        }
401        
402       /**
403        * Build an array of include directives contained within the supplied enclosing element.
404        * @param element the enclosing element
405        * @return the array of includes
406        */
407        private ImportDirective[] buildImportDirectives( Element element ) throws DecodingException
408        {
409            Element[] children = ElementHelper.getChildren( element );
410            ImportDirective[] includes = new ImportDirective[ children.length ];
411            for( int i=0; i<children.length; i++ )
412            {
413                Element child = children[i];
414                includes[i] = buildImportDirective( child );
415            }
416            return includes;
417        }
418        
419        private ImportDirective buildImportDirective( Element element ) throws DecodingException
420        {
421            final String tag = element.getTagName();
422            final Properties properties = buildProperties( element );
423            if( IMPORT_ELEMENT_NAME.equals( tag ) )
424            {
425                try
426                {
427                    if( element.hasAttribute( "file" ) )
428                    {
429                        final String value = ElementHelper.getAttribute( element, "file", null );
430                        return new ImportDirective( ImportDirective.FILE, value, properties );
431                    }
432                    else if( element.hasAttribute( "uri" ) )
433                    {
434                        final String value = ElementHelper.getAttribute( element, "uri", null );
435                        return new ImportDirective( ImportDirective.URI, value, properties );
436                    }
437                    else
438                    {
439                        final String error = 
440                          "Import element does not declare a 'file' or 'uri' attribute.\n"
441                          + element.toString();
442                        throw new IllegalArgumentException( error );
443                    }
444                }
445                catch( Throwable e )
446                {
447                    final String error = 
448                      "Internal error while attempting to resolve an import directive.";
449                    throw new DecodingException( element, error, e );
450                }
451            }
452            else
453            {
454                final String error = 
455                  "Invalid include element name [" 
456                  + tag 
457                  + "].";
458                throw new IllegalArgumentException( error );
459            }
460        }
461    
462        private DependencyDirective[] buildDependencyDirectives( Element element ) throws DecodingException
463        {
464            if( null == element )
465            {
466                return new DependencyDirective[0];
467            }
468            else
469            {
470                final String tag = element.getTagName();
471                if( DEPENDENCIES_ELEMENT_NAME.equals( tag ) )
472                {
473                    Element[] children = ElementHelper.getChildren( element );
474                    DependencyDirective[] dependencies = new DependencyDirective[ children.length ];
475                    for( int i=0; i<children.length; i++ )
476                    {
477                        Element child = children[i];
478                        dependencies[i] = buildDependencyDirective( child );
479                    }
480                    return dependencies;
481                }
482                else
483                {
484                    final String error = 
485                      "Invalid dependency element name [" 
486                      + tag
487                      + "].";
488                    throw new IllegalArgumentException( error );
489                }
490            }
491        }
492        
493        private DependencyDirective buildDependencyDirective( Element element ) throws DecodingException
494        {
495            String name = element.getTagName();
496            Scope scope = Scope.parse( name );
497            if( Scope.BUILD.equals( scope ) )
498            {
499                IncludeDirective[] includes = buildIncludeDirectives( element, false );
500                return new DependencyDirective( Scope.BUILD, includes );
501            }
502            else if( Scope.RUNTIME.equals( scope ) )
503            {
504                IncludeDirective[] includes = buildIncludeDirectives( element, true );
505                return new DependencyDirective( Scope.RUNTIME, includes );
506            }
507            else
508            {
509                IncludeDirective[] includes = buildIncludeDirectives( element, false );
510                return new DependencyDirective( Scope.TEST, includes );
511            }
512        }
513        
514       /**
515        * Build an array of include directives contained within the supplied enclosing element.
516        * @param element the enclosing element
517        * @param flag if category processing is required
518        * @return the array of includes
519        */
520        private IncludeDirective[] buildIncludeDirectives( Element element, boolean flag ) throws DecodingException
521        {
522            Element[] children = ElementHelper.getChildren( element );
523            IncludeDirective[] includes = new IncludeDirective[ children.length ];
524            for( int i=0; i<children.length; i++ )
525            {
526                Element child = children[i];
527                includes[i] = buildIncludeDirective( child, flag );
528            }
529            return includes;
530        }
531        
532        private IncludeDirective buildIncludeDirective( Element element, boolean flag ) throws DecodingException
533        {
534            final String tag = element.getTagName();
535            final Properties properties = buildProperties( element );
536            if( INCLUDE_ELEMENT_NAME.equals( tag ) )
537            {
538                Category category = buildCategory( element, flag );
539                if( element.hasAttribute( "key" ) )
540                {
541                    final String value = ElementHelper.getAttribute( element, "key", null );
542                    return new IncludeDirective( IncludeDirective.KEY, category, value, properties );
543                }
544                else if( element.hasAttribute( "ref" ) )
545                {
546                    final String value = ElementHelper.getAttribute( element, "ref", null );
547                    return new IncludeDirective( IncludeDirective.REF, category, value, properties );
548                }
549                else if( element.hasAttribute( "uri" ) )
550                {
551                    final String value = ElementHelper.getAttribute( element, "uri", null );
552                    return new IncludeDirective( IncludeDirective.URI, category, value, properties );
553                }
554                else
555                {
556                    final String error = 
557                      "Include directive does not declare a 'uri', 'key' or 'ref' attribute.\n"
558                      + element.toString();
559                    throw new IllegalArgumentException( error );
560                }
561            }
562            else
563            {
564                final String error = 
565                  "Invalid include element name [" 
566                  + tag 
567                  + "].";
568                throw new DecodingException( element, error );
569            }
570        }
571        
572        private Category buildCategory( Element element, boolean flag )
573        {
574            if( !flag ) 
575            {
576                return Category.UNDEFINED;
577            }
578            else
579            {
580                final String value = ElementHelper.getAttribute( element, "tag", "private" );
581                return Category.parse( value );
582            }
583        }
584        
585        private InfoDirective buildInfoDirective( Element element )
586        {
587            if( null == element )
588            {
589                return null;
590            }
591            else
592            {
593                String title = ElementHelper.getAttribute( element, "title" );
594                Element child = ElementHelper.getChild( element, "description" );
595                if( null == child )
596                {
597                    return new InfoDirective( title, null );
598                }
599                else
600                {
601                    String value = ElementHelper.getValue( child );
602                    String description = trim( value );
603                    return new InfoDirective( title, description );
604                }
605            }
606        }
607        
608        private String trim( String value )
609        {
610            if( null == value )
611            {
612                return null;
613            }
614            String trimmed = value.trim();
615            if( trimmed.startsWith( "\n" ) )
616            {
617                return trim( trimmed.substring( 1 ) );
618            }
619            else if( trimmed.endsWith( "\n" ) )
620            {
621                return trim( trimmed.substring( 0, trimmed.length() - 1 ) );
622            }
623            else
624            {
625                return trimmed;
626            }
627        }
628        
629        private FilterDirective[] buildFilterDirectives( Element element ) throws Exception
630        {
631            if( null == element )
632            {
633                return new FilterDirective[0];
634            }
635            else
636            {
637                Element[] children = ElementHelper.getChildren( element );
638                FilterDirective[] filters = new FilterDirective[ children.length ];
639                for( int i=0; i<children.length; i++ )
640                {
641                    Element child = children[i];
642                    String token = ElementHelper.getAttribute( child, "token" );
643                    if( "filter".equals( child.getTagName() ) )
644                    {
645                        String value = ElementHelper.getAttribute( child, "value" );
646                        filters[i] = new SimpleFilterDirective( token, value );
647                    }
648                    else if( "feature".equals( child.getTagName() ) )
649                    {
650                        String id = ElementHelper.getAttribute( child, "id" );
651                        Feature feature = Feature.parse( id );
652                        String ref = ElementHelper.getAttribute( child, "ref" );
653                        String type = ElementHelper.getAttribute( child, "type" );
654                        boolean alias = ElementHelper.getBooleanAttribute( child, "alias" );
655                        filters[i] = new FeatureFilterDirective( token, ref, feature, type, alias );
656                    }
657                    else
658                    {
659                        final String error = 
660                          "Element name not recognized [" 
661                          + child.getTagName()
662                          + "] (expecting 'filter').";
663                        throw new DecodingException( element, error );
664                    }
665                }
666                return filters;
667            }
668        }
669        
670        private DataDirective[] buildDataTypes( Element element ) throws Exception
671        {
672            if( null == element )
673            {
674                return new DataDirective[0];
675            }
676            Element[] children = ElementHelper.getChildren( element );
677            DataDirective[] data = new DataDirective[ children.length ];
678            for( int i=0; i<children.length; i++ )
679            {
680                Element child = children[i];
681                data[i] = buildDataType( child );
682            }
683            return data;
684        }
685    
686        private DataDirective buildDataType( Element element ) throws Exception
687        {
688            TypeInfo info = element.getSchemaTypeInfo();
689            String namespace = info.getTypeNamespace();
690            String typeName = info.getTypeName();
691            
692            if( null == namespace )
693            {
694                throw new NullPointerException( "namespace" );
695            }
696            
697            if( MODULE_XSD_URI.equals( namespace ) )
698            {
699                // it's a generic type declaration
700                
701                final String id = getID( element );
702                final Properties properties = getProperties( element );
703                final Version version = getVersion( element );
704                return new TypeDirective( id, version, properties );
705            }
706            else if( info.isDerivedFrom( MODULE_XSD_URI, "AbstractType", TypeInfo.DERIVATION_EXTENSION ) )
707            {
708                if( 
709                  PART_XSD_URI.equals( namespace ) 
710                  || info.isDerivedFrom( PART_XSD_URI, "StrategyType", TypeInfo.DERIVATION_EXTENSION ) )
711                {
712                    final Version version = getVersion( element );
713                    return new TypeDirective( element, "part", version );
714                }
715                else
716                {
717                    // id attribute is required 
718                    
719                    final String id = getID( element );
720                    final Version version = getVersion( element );
721                    return new TypeDirective( element, id, version );
722                }
723            }
724            else
725            {
726                final String error = 
727                  "Unsupported element encountered during type directive production."
728                  + "\nNamespace: " 
729                  + namespace
730                  + "\nType: " 
731                  + info.getTypeName()
732                  + "\nName: " 
733                  + element.getTagName();
734                throw new DecodingException( element, error );
735            }
736        }
737        
738        private Version getVersion( Element element )
739        {
740            final String version = ElementHelper.getAttribute( element, "version" );
741            if( null != version )
742            {
743                return Version.parse( version );
744            }
745            else if( getAliasFlag( element ) )
746            {
747                return Version.NULL_VERSION;
748            }
749            else
750            {
751                return null;
752            }
753        }
754        
755        private Properties buildProperties( Element element )
756        {
757            if( null == element )
758            {
759                return null;
760            }
761            Properties properties = new Properties();
762            Element[] children = ElementHelper.getChildren( element );
763            for( int i=0; i<children.length; i++ )
764            {
765                Element child = children[i];
766                String tag = child.getTagName();
767                if( "property".equals( tag ) )
768                {
769                    String key = ElementHelper.getAttribute( child, "name", null );
770                    if( null == key )
771                    {
772                        final String error =
773                          "Property declaration does not contain a 'name' attribute.";
774                        throw new IllegalArgumentException( error );
775                    }
776                    else
777                    {
778                        String value = ElementHelper.getAttribute( child, "value", null );
779                        properties.setProperty( key, value );
780                    }
781                }
782            }
783            return properties;
784        }
785    
786        private URI getURIFromSpec( String spec ) 
787        {
788            if( null == spec )
789            {
790                return null;
791            }
792            else
793            {
794                try
795                {
796                    return new URI( spec );
797                }
798                catch( URISyntaxException e )
799                {
800                    final String error = 
801                      "Type descriptor uri ["
802                      + spec 
803                      + "] could not be converted to a URI value.";
804                    throw new IllegalArgumentException( error );
805                }
806            }
807        }
808    
809        private TypeBuilder loadTypeBuilder( URI uri ) throws Exception
810        {
811            ClassLoader classloader = TypeBuilder.class.getClassLoader();
812            Object[] args = new Object[0];
813            Part part = Part.load( uri );
814            Object handler = part.instantiate( args );
815            if( handler instanceof TypeBuilder )
816            {
817                return (TypeBuilder) handler;
818            }
819            else
820            {
821                final String error = 
822                  "Plugin ["
823                  + uri
824                  + "] does not implement the "
825                  + TypeBuilder.class.getName()
826                  + " interface.";
827                throw new IllegalArgumentException( error );
828            }
829        }
830        
831       /**
832        * Return the id attribute of the supplied element.
833        * @param element the DOM element
834        * @return the id value
835        * @exception DecodingException if an error occurs during element evaluation
836        */
837        protected String getID( Element element ) throws DecodingException
838        {
839            final String id = ElementHelper.getAttribute( element, "id" );
840            if( null == id )
841            {
842                final String error = 
843                  "Missing type 'id'.";
844                throw new DecodingException( element, error );
845            }
846            else
847            {
848                return id;
849            }
850        }
851    
852       /**
853        * Return the alias attribute of the supplied element.
854        * @param element the DOM element
855        * @return the alias production flag value
856        */
857        protected boolean getAliasFlag( Element element )
858        {
859            return ElementHelper.getBooleanAttribute( element, "alias", false );
860        }
861        
862       /**
863        * Return properties declared within the supplied element as immediate
864        * child <property> elements.
865        * @param element the enclosing DOM element
866        * @return the resolved properties instance
867        */
868        protected Properties getProperties( Element element )
869        {
870            Properties properties = new Properties();
871            Element[] children = ElementHelper.getChildren( element );
872            for( int i=0; i<children.length; i++ )
873            {
874                Element child = children[i];
875                String tag = child.getTagName();
876                if( "property".equals( tag ) )
877                {
878                    String key = ElementHelper.getAttribute( child, "name", null );
879                    if( null == key )
880                    {
881                        final String error =
882                          "Property declaration does not contain a 'name' attribute.";
883                        throw new IllegalArgumentException( error );
884                    }
885                    else
886                    {
887                        String value = ElementHelper.getAttribute( child, "value", null );
888                        properties.setProperty( key, value );
889                    }
890                }
891            }
892            return properties;
893        }
894    }