001    /*
002     * Copyright 2005-2006 Stephen J. McConnell
003     *
004     * Licensed  under the  Apache License,  Version 2.0  (the "License");
005     * you may not use  this file  except in  compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *   http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed  under the  License is distributed on an "AS IS" BASIS,
012     * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
013     * implied.
014     *
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    
019    package net.dpml.library.impl;
020    
021    import java.io.File;
022    import java.util.Arrays;
023    import java.util.ArrayList;
024    import java.util.List;
025    import java.util.regex.Pattern;
026    import java.util.regex.Matcher;
027    import java.util.Properties;
028    import java.util.Hashtable;
029    import java.util.Map;
030    
031    import net.dpml.library.info.Scope;
032    import net.dpml.library.info.AbstractDirective;
033    import net.dpml.library.info.ModuleDirective;
034    import net.dpml.library.info.ResourceDirective;
035    import net.dpml.library.info.DependencyDirective;
036    import net.dpml.library.info.ResourceDirective.Classifier;
037    import net.dpml.library.info.TypeDirective;
038    import net.dpml.library.info.InfoDirective;
039    import net.dpml.library.Module;
040    import net.dpml.library.Resource;
041    import net.dpml.library.ResourceNotFoundException;
042    import net.dpml.library.ModuleNotFoundException;
043    
044    import net.dpml.lang.Category;
045    import net.dpml.lang.DuplicateKeyException;
046    
047    /**
048     * A Module is a collection of resources.  It serves to establish a 
049     * namespace and a framework for sharing properties and characteristics 
050     * defined within within the module with resources contained within the
051     * module.
052     *
053     * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
054     * @version 1.2.0
055     */
056    public final class DefaultModule extends DefaultResource implements Module
057    {
058        private final boolean m_root;
059        private final ModuleDirective m_directive;
060        private final Map m_map = new Hashtable();
061        
062       /**
063        * Constructor used by the library to create a virtual root module os shared 
064        * resource references.
065        * @param logger the assigned logging channel
066        * @param library the library
067        * @param directive the directive from which common properties are established
068        */
069        DefaultModule( final DefaultLibrary library, final AbstractDirective directive ) 
070        {
071            super( library, directive );
072            
073            m_root = true;
074            m_directive = null;
075        }
076        
077       /**
078        * Creation of a new nested module.
079        * @param logger the assigned logging channel
080        * @param library the library
081        * @param module the parent module
082        * @param directive the directive from which common properties are established
083        */
084        DefaultModule( 
085          final DefaultLibrary library, final DefaultModule module, final ModuleDirective directive ) 
086          throws DuplicateKeyException
087        {
088            super( library, module, directive );
089            
090            m_root = false;
091            m_directive = directive;
092            ResourceDirective[] directives = directive.getResourceDirectives();
093            for( int i=0; i<directives.length; i++ )
094            {
095                ResourceDirective res = directives[i];
096                addResource( res ); 
097            }
098        }
099        
100       /**
101        * Add a resource to the module.
102        * @param directive the resource directive to add to the module
103        * @throws DuplicateKeyException if a resource name is already bound
104        */
105        DefaultResource addResource( final ResourceDirective directive ) throws DuplicateKeyException
106        {
107            if( null == directive )
108            {
109                throw new NullPointerException( "directive" );
110            }
111            
112            synchronized( m_map )
113            {
114                String key = directive.getName();
115                if( m_map.containsKey( key ) )
116                {
117                    if( directive instanceof ModuleDirective )
118                    {
119                        DefaultModule module = (DefaultModule) m_map.get( key );
120                        
121                        // check basedir values
122                        
123                        if( null != directive.getBasedir() )
124                        {
125                            if( null != module.getBaseDir() )
126                            {
127                                File base = new File( getBaseDir(), directive.getBasedir() );
128                                if( !module.getBaseDir().equals( base ) )
129                                {
130                                    final String error = 
131                                      "Cannot merge modules with different base directories."
132                                      + "\nModule: " + module
133                                      + "\nPrimary base: " + module.getBaseDir()
134                                      + "\nSecondary base: " + base;
135                                    throw new IllegalStateException( error );
136                                }
137                            }
138                        }
139                        
140                        // check versions
141                        
142                        if( null != directive.getVersion() )
143                        {
144                            if( module.getVersion().equals( directive.getVersion() ) )
145                            {
146                                final String error = 
147                                  "Cannot merge modules with different versions."
148                                  + "\nModule: " + module
149                                  + "\nPrimary version: " + module.getVersion()
150                                  + "\nSecondary version: " + directive.getVersion();
151                                throw new IllegalStateException( error );
152                            }
153                        }
154                        
155                        // check types
156                        
157                        if( directive.getTypeDirectives().length > 0 ) 
158                        {
159                            final String error = 
160                              "Cannot merge a module with type production directives."
161                              + "\nModule: " + module;
162                            throw new IllegalStateException( error );
163                        }
164                        
165                        // check dependencies
166                        
167                        if( directive.getDependencyDirectives().length > 0 ) 
168                        {
169                            final String error = 
170                              "Cannot merge a module with dependency directives."
171                              + "\nModule: " + module;
172                            throw new IllegalStateException( error );
173                        }
174                        
175                        // add additional resources
176                        
177                        ModuleDirective d = (ModuleDirective) directive;
178                        ResourceDirective[] resources = d.getResourceDirectives();
179                        for( int i=0; i<resources.length; i++ )
180                        {
181                            ResourceDirective r = resources[i];
182                            module.addResource( r );
183                        }
184                        return module;
185                    }
186                    else
187                    {
188                        throw new DuplicateKeyException( key );
189                    }
190                }
191                else
192                {
193                    DefaultLibrary library = getDefaultLibrary();
194                    if( directive instanceof ModuleDirective )
195                    {
196                        ModuleDirective d = (ModuleDirective) directive;
197                        DefaultModule module = new DefaultModule( library, this, d );
198                        m_map.put( key, module );
199                        return module;
200                    }
201                    else
202                    {
203                        DefaultResource resource = new DefaultResource( library, this, directive );
204                        m_map.put( key, resource );
205                        return resource;
206                    }
207                }
208            }
209        }
210        
211        //----------------------------------------------------------------------------
212        // Module
213        //----------------------------------------------------------------------------
214        
215       /**
216        * Return an array of immediate resources contained within the
217        * module.
218        * @return the resource array
219        */
220        public Resource[] getResources()
221        {
222            return getDefaultResources();
223        }
224        
225       /**
226        * Return a resource using a supplied name.
227        * @param ref a path relative to the module
228        * @return the resource array
229        * @exception ResourceNotFoundException if the resource does not exist
230        */
231        public Resource getResource( final String ref ) throws ResourceNotFoundException
232        {
233            try
234            {
235                return getDefaultResource( ref );
236            }
237            catch( InvalidNameException e )
238            {
239                final String error = 
240                  "Resource reference ["
241                  + ref
242                  + "] is undefined.";
243                throw new ResourceNotFoundException( error, e );
244            }
245        }
246        
247       /**
248        * Return the array of modules that are direct children of this module.
249        * @return the child modules
250        */
251        public Module[] getModules()
252        {
253            return getDefaultModules();
254        }
255        
256       /**
257        * Return a module using a supplied reference.
258        * @param ref a path relative to the module
259        * @return the module array
260        * @exception ModuleNotFoundException if the module does not exist
261        */
262        public Module getModule( final String ref ) throws ModuleNotFoundException
263        {
264            try
265            {
266                return getDefaultModule( ref );
267            }
268            catch( InvalidNameException e )
269            {
270                final String error = 
271                  "Cannot locate module [" 
272                  + ref
273                  + "] within module ["
274                  + getResourcePath()
275                  + "]";
276                throw new ModuleNotFoundException( error, e );
277            }
278        }
279        
280       /**
281        * Return the array of modules that are descendants of this module.
282        * @return the descendants module array
283        */
284        public Module[] getAllModules()
285        {
286            return getAllDefaultModules();
287        }
288        
289       /**
290        * <p>Select a set of resource matching a supplied a resource selection 
291        * constraint.  The constraint may contain the wildcards '**' and '*'.
292        * @param criteria the selection criteria
293        * @param local if true limit the selection to local projects
294        * @param sort if true the returned array will be sorted relative to dependencies
295        *   otherwise the array will be sorted alphanumerically
296        * @return an array of resources matching the selection criteria
297        */
298        public Resource[] select( final String criteria, final boolean local, final boolean sort )
299        {
300            DefaultResource[] resources = selectDefaultResources( local, criteria );
301            if( sort )
302            {
303                return sortDefaultResources( resources, Scope.TEST );
304            }
305            else
306            {
307                Arrays.sort( resources );
308                return resources;
309            }
310            
311        }
312        
313       /**
314        * Locate a resource relative to a base directory.
315        * @param base the base directory
316        * @return a resource with a matching basedir
317        * @exception ResourceNotFoundException if resource match  relative to the supplied base
318        */
319        public Resource locate( final File base ) throws ResourceNotFoundException
320        {
321            DefaultResource[] resources = selectDefaultResources( true, "**/*" );
322            for( int i=0; i<resources.length; i++ )
323            {
324                DefaultResource resource = resources[i];
325                File basedir = resource.getBaseDir();
326                if( ( null != basedir ) && base.equals( basedir ) )
327                {
328                    return resource;
329                }
330            }
331            throw new ResourceNotFoundException( base.toString() );
332        }
333        
334       /**
335        * Return a directive suitable for publication as an external description.
336        * @return the resource directive
337        */
338        public ModuleDirective export()
339        {
340            if( null == m_directive )
341            {
342                final String error = 
343                  "Cannot export from the virtual root.";
344                throw new UnsupportedOperationException( error );
345            }
346            
347            DefaultModule parent = getDefaultParent();
348            if( null == parent )
349            {
350                //
351                // exporting a top-level module
352                //
353                
354                return (ModuleDirective) exportResource( this );
355            }
356            else
357            {
358                //
359                // exporting the nested module
360                //
361                
362                String path = getResourcePath();
363                return exportModule( this, path );
364            }
365        }
366        
367       /**
368        * Return a directive suitable for publication as an external description.
369        * @param module the enclosing module
370        * @return the resource directive
371        * @exception IllegalArgumentException if the module is not a top-level module
372        */
373        ResourceDirective exportResource( final DefaultModule module ) throws IllegalArgumentException
374        {
375            String name = getName();
376            return exportModule( module, name );
377        }
378        
379       /**
380        * Return a directive suitable for publication as an external description.
381        * @param module the enclosing module
382        * @return the resource directive
383        * @exception IllegalArgumentException if the module is not a top-level module
384        */
385        ModuleDirective exportModule( final DefaultModule module, final String name ) throws IllegalArgumentException
386        {
387            DefaultResource[] resources = getDefaultResources();
388            ResourceDirective[] directives = new ResourceDirective[ resources.length ];
389            for( int i=0; i<directives.length; i++ )
390            {
391                DefaultResource resource = resources[i];
392                directives[i] = resource.exportResource( module );
393            }
394            String version = getVersion();
395            String basedir = null;
396            InfoDirective info = m_directive.getInfoDirective();
397            TypeDirective[] types = m_directive.getTypeDirectives();
398            TypeDirective[] exportedTypes = createExportedTypes( types );
399            Properties properties = getExportProperties();
400            return new ModuleDirective( 
401              name, version, Classifier.EXTERNAL, basedir,
402              info, exportedTypes, new DependencyDirective[0], 
403              directives, properties, null );
404        }
405        
406        //----------------------------------------------------------------------------
407        // Object
408        //----------------------------------------------------------------------------
409        
410       /**
411       * Return the string representation of the module.
412       * @return the string value
413       */
414        public String toString()
415        {
416            return toString( "module" );
417        }
418        
419        //----------------------------------------------------------------------------
420        // DefaultResource (overriding)
421        //----------------------------------------------------------------------------
422    
423        DefaultResource[] getLocalDefaultProviders( final Scope scope, final Category category )
424        {
425            DefaultResource[] local = super.getLocalDefaultProviders( scope, category );
426            if( Scope.BUILD.equals( scope ) )
427            {
428                ArrayList stack = new ArrayList();
429                for( int i=0; i<local.length; i++ )
430                {
431                    DefaultResource resource = local[i];
432                    stack.add( resource );
433                }
434                return expandLocalDefaultProviders( stack );
435            }
436            else if( Scope.RUNTIME.equals( scope ) )
437            {
438                ArrayList stack = new ArrayList();
439                for( int i=0; i<local.length; i++ )
440                {
441                    DefaultResource resource = local[i];
442                    stack.add( resource );
443                }
444                
445                DefaultResource[] resources = getDefaultResources();
446                for( int i=0; i<resources.length; i++ )
447                {
448                    DefaultResource resource = resources[i];
449                    if( !stack.contains( resource ) )
450                    {
451                        stack.add( resource );
452                    }
453                }
454                return (DefaultResource[]) stack.toArray( new DefaultResource[0] );
455            }
456            else
457            {
458                return local;
459            }
460        }
461        
462        DefaultResource[] expandLocalDefaultProviders( final List stack )
463        {
464            DefaultResource[] resources = getDefaultResources();
465            for( int i=0; i<resources.length; i++ )
466            {
467                DefaultResource resource = resources[i];
468                if( resource instanceof DefaultModule )
469                {
470                    DefaultModule module = (DefaultModule) resource;
471                    String path = module.getResourcePath();
472                    if( !path.startsWith( getResourcePath() ) )
473                    //if( !getResourcePath().startsWith( path ) )
474                    {
475                        DefaultResource[] providers = module.expandLocalDefaultProviders( stack );
476                        for( int j=0; j<providers.length; j++ )
477                        {
478                            DefaultResource r = providers[j];
479                            if( !stack.contains( r ) )
480                            {
481                                stack.add( r );
482                            }
483                        }
484                    }
485                }
486                else
487                {
488                    DefaultResource[] providers = 
489                      resource.getAggregatedDefaultProviders( Scope.TEST, true, false );
490                    getParentModules( stack, providers );
491                }
492            }
493            return (DefaultResource[]) stack.toArray( new DefaultResource[0] );
494        }
495        
496        private void getParentModules( final List stack, final DefaultResource[] resources )
497        {
498            for( int i=0; i<resources.length; i++ )
499            {
500                DefaultResource resource = resources[i];
501                if( !resource.isAnonymous() )
502                {
503                    DefaultModule parent = resource.getDefaultParent();
504                    if( null != parent )
505                    {
506                        String path = parent.getResourcePath();
507                        if( !path.startsWith( getResourcePath() ) && !stack.contains( parent ) )
508                        {
509                            stack.add( parent );
510                        }
511                    }
512                }
513            }
514        }
515    
516        void sortDefaultResource( 
517          final List visited, final List stack, final Scope scope, final DefaultResource[] resources )
518        {
519            if( visited.contains( this ) )
520            {
521                return;
522            }
523            
524            visited.add( this );
525            DefaultResource[] providers = 
526              getAggregatedDefaultProviders( scope, false, false );
527            for( int i=0; i<providers.length; i++ )
528            {
529                DefaultResource provider = providers[i];
530                if( isaMember( resources, provider ) )
531                {
532                    provider.sortDefaultResource( visited, stack, scope, resources );
533                }
534            }
535    
536            DefaultModule[] modules = getProviderModules( scope );
537            for( int i=0; i<modules.length; i++ )
538            {
539                DefaultResource module = modules[i];
540                if( isaMember( resources, module ) )
541                {
542                    module.sortDefaultResource( visited, stack, scope, resources );
543                }
544            }
545            
546            if( !stack.contains( this ) )
547            {
548                stack.add( this );
549            }
550        }
551        
552        //----------------------------------------------------------------------------
553        // root semantics
554        //----------------------------------------------------------------------------
555        
556        boolean isRoot()
557        {
558            return m_root;
559        }
560        
561        //----------------------------------------------------------------------------
562        // resource and module lookup
563        //----------------------------------------------------------------------------
564        
565        DefaultResource[] getDefaultResources()
566        {
567            return (DefaultResource[]) m_map.values().toArray( new DefaultResource[0] );
568        }
569        
570        DefaultResource getDefaultResource( final String ref )
571        {
572            if( null == ref )
573            {
574                throw new NullPointerException( "ref" );
575            }
576            int n = ref.indexOf( "/" );
577            if( n > 0 )
578            {
579                String pre = ref.substring( 0, n );
580                String post = ref.substring( n+1 );
581                DefaultModule module = getDefaultModule( pre );
582                return module.getDefaultResource( post );
583            }
584            else
585            {
586                DefaultResource[] resources = getDefaultResources();
587                for( int i=0; i<resources.length; i++ )
588                {
589                    DefaultResource resource = resources[i];
590                    if( ref.equals( resource.getName() ) )
591                    {
592                        return resource;
593                    }
594                }
595                final String error = 
596                  "The reference to ["
597                    + ref
598                    + "] within the module ["
599                    + getResourcePath()
600                    + "] could not be resolved.";
601                throw new InvalidNameException( error );
602            }
603        }
604        
605        DefaultModule getDefaultModule( final String ref )
606        {
607            if( null == ref )
608            {
609                throw new NullPointerException( "ref" );
610            }
611            int n = ref.indexOf( "/" );
612            if( n == 0 )
613            {
614                String newRef = ref.substring( 1 );
615                return getDefaultModule( newRef );
616            }
617            else if( n > 0 )
618            {
619                String pre = ref.substring( 0, n );
620                String post = ref.substring( n+1 );
621                DefaultModule module = getDefaultModule( pre );
622                return module.getDefaultModule( post );
623            }
624            else
625            {
626                DefaultResource resource = null;
627                try
628                {
629                    resource = getDefaultResource( ref );
630                }
631                catch( InvalidNameException e )
632                {
633                    final String error = 
634                      "The reference to module ["
635                        + ref
636                        + "] within the module ["
637                        + getResourcePath()
638                        + "] does not exist.";
639                    throw new InvalidNameException( error, e );
640                }
641                if( resource instanceof DefaultModule )
642                {
643                    return (DefaultModule) resource;
644                }
645                else
646                {
647                    final String error = 
648                      "A reference to module ["
649                      + ref
650                      + "] within the module ["
651                      + getResourcePath()
652                      + "] returned a reference to a non-module resource.";
653                    throw new InvalidNameException( error );
654                }
655            }
656        }
657        
658        DefaultModule[] getDefaultModules()
659        {
660            ArrayList stack = new ArrayList();
661            DefaultResource[] resources = getDefaultResources();
662            for( int i=0; i<resources.length; i++ )
663            {
664                DefaultResource resource = resources[i];
665                if( resource instanceof DefaultModule )
666                {
667                    stack.add( resource );
668                }
669            }
670            return (DefaultModule[]) stack.toArray( new DefaultModule[0] );
671        }
672        
673        DefaultModule[] getAllDefaultModules()
674        {
675            return getAllDefaultModules( true, false );
676        }
677        
678        DefaultModule[] getAllDefaultModules( final boolean sort, final boolean self )
679        {
680            if( sort )
681            {
682                DefaultModule[] modules = getAllDefaultModules( false, self );
683                return sortDefaultModules( modules );
684            }
685            else
686            {
687                DefaultModule[] modules = getDefaultModules();
688                ArrayList visited = new ArrayList();
689                ArrayList stack = new ArrayList();
690                for( int i=0; i<modules.length; i++ )
691                {
692                    DefaultModule module = modules[i];
693                    collectChildModules( visited, stack, module );
694                }
695                if( self )
696                {
697                    stack.add( this );
698                }
699                return (DefaultModule[]) stack.toArray( new DefaultModule[0] );
700            }
701        }
702        
703        private void collectChildModules( 
704          final List visited, final List stack, final DefaultModule module )
705        {
706            if( visited.contains( module ) )
707            {
708                return;
709            }
710            visited.add( module );
711            DefaultModule[] children = module.getDefaultModules();
712            for( int i=0; i<children.length; i++ )
713            {
714                collectChildModules( visited, stack, children[i] );
715            }
716            stack.add( module );
717        }
718        
719        //----------------------------------------------------------------------------
720        // Selection logic
721        //----------------------------------------------------------------------------
722            
723        DefaultResource[] selectDefaultResources( final String spec )
724        {
725            return selectDefaultResources( false, spec );
726        }
727        
728        DefaultResource[] selectDefaultResources( final boolean local, final String spec )
729        {
730            DefaultResource[] resources = doSelectDefaultResources( false, spec );
731            if( local )
732            {
733                return filterProjects( resources );
734            }
735            else
736            {
737                return resources;
738            }
739        }
740        
741        DefaultResource[] doSelectDefaultResources( final boolean wild, final String spec )
742        {
743            String[] tokens = spec.split( "/" );
744            if( tokens.length == 0 )
745            {
746                return new DefaultResource[0];
747            }
748            else if( tokens.length == 1 )
749            {
750                String token = tokens[0];
751                if( "**".equals( token ) )
752                {
753                    return getAllDefaultModules( true, !m_root );
754                }
755                else if( "*".equals( token ) )
756                {
757                    if( wild && !m_root )
758                    {
759                        DefaultResource[] resources = getDefaultResources();
760                        DefaultResource[] result = new DefaultResource[ resources.length + 1 ];
761                        System.arraycopy( resources, 0, result, 0, resources.length );
762                        result[ resources.length ] = this;
763                        return result;
764                    }
765                    else
766                    {
767                        return getDefaultResources();
768                    }
769                }
770                else
771                {
772                    Pattern pattern = createSelectionPattern( token );
773                    DefaultResource[] resources = getDefaultResources();
774                    DefaultResource[] selection = selectUsingPattern( resources, pattern );
775                    return selection;
776                }
777            }
778            else
779            {
780                String token = tokens[0];
781                boolean wildcard = ( token.indexOf( "**" ) > -1 );
782                String remainder = getRemainderOfSelection( spec, "/", token );
783                DefaultModule[] modules = selectDefaultModules( token );
784                ArrayList list = new ArrayList();
785                for( int i=0; i<modules.length; i++ )
786                {
787                    DefaultModule module = modules[i];
788                    DefaultResource[] selection = module.doSelectDefaultResources( wildcard, remainder );
789                    aggregate( list, selection );
790                }
791                if( wildcard )
792                {
793                    DefaultResource[] selection = doSelectDefaultResources( wildcard, remainder );
794                    aggregate( list, selection );
795                }
796                return (DefaultResource[]) list.toArray( new DefaultResource[0] );
797            }
798        }
799    
800        DefaultModule[] selectDefaultModules( final String token )
801        {
802            if( "**".equals( token ) )
803            {
804                return getAllDefaultModules( true, !m_root );
805            }
806            else if( "*".equals( token ) )
807            {
808                return getDefaultModules();
809            }
810            else
811            {
812                Pattern pattern = createSelectionPattern( token );
813                DefaultModule[] modules = getDefaultModules();
814                DefaultResource[] selection = selectUsingPattern( modules, pattern );
815                DefaultModule[] result = new DefaultModule[ selection.length ];
816                for( int i=0; i<selection.length; i++ )
817                {
818                    result[i] = (DefaultModule) selection[i];
819                }
820                return result;
821            }
822        }
823        
824        private DefaultResource[] selectUsingPattern( final DefaultResource[] resources, final Pattern pattern )
825        {
826            ArrayList list = new ArrayList();
827            for( int i=0; i<resources.length; i++ )
828            {
829                Resource resource = resources[i];
830                String name = resource.getName();
831                Matcher matcher = pattern.matcher( name );
832                boolean matches = matcher.matches();
833                if( matches )
834                {
835                    list.add( resource );
836                }
837            }
838            return (DefaultResource[]) list.toArray( new DefaultResource[0] );
839        }
840        
841        private String getRemainderOfSelection( final String spec, final String delimiter, final String token )
842        {
843            int n = token.length() + delimiter.length();
844            return spec.substring( n );
845        }
846        
847        private void aggregate( final List list, final DefaultResource[] resources )
848        {
849            for( int i=0; i<resources.length; i++ )
850            {
851                DefaultResource resource = resources[i];
852                if( !list.contains( resource ) )
853                {
854                    list.add( resources[i] );
855                }
856            }
857        }
858        
859        private Pattern createSelectionPattern( final String token )
860        {
861            StringBuffer buffer = new StringBuffer();
862            boolean wildcard = ( token.indexOf( "*" ) > -1 );
863            if( wildcard )
864            {
865                String[] blocks = token.split( "\\*", -1 );
866                buffer.append( "(" );
867                for( int j=0; j<blocks.length; j++ )
868                {
869                    buffer.append( "\\Q" );
870                    buffer.append( blocks[j] );
871                    buffer.append( "\\E" );
872                    if( j < ( blocks.length-1 ) )
873                    {
874                        buffer.append( ".*" );
875                    }
876                }
877                buffer.append( ")" );
878            }
879            else
880            {
881                buffer.append( "(\\Q" );
882                buffer.append( token );
883                buffer.append( "\\E)" );
884            }
885            String expression = buffer.toString();
886            return Pattern.compile( expression );
887        }
888        
889        //----------------------------------------------------------------------------
890        // Module sorting
891        //----------------------------------------------------------------------------
892    
893        private DefaultModule[] sortDefaultModules( final DefaultModule[] modules )
894        {
895            Scope scope = Scope.TEST;
896            ArrayList visited = new ArrayList();
897            ArrayList stack = new ArrayList();
898            for( int i=0; i<modules.length; i++ )
899            {
900                DefaultModule module = modules[i];
901                module.sortDefaultResource( visited, stack, scope, modules );
902            }
903            return (DefaultModule[]) stack.toArray( new DefaultModule[0] );
904        }
905        
906       /**
907        * Returns a sorted array of the provider modules this this module depends on.  
908        * The notion of dependency is ressolved via (a) direct depedencies declared
909        * by the module, (b) subsidiary module which are considered as sippliers to 
910        * this module, and (c) module referenced by any resources contained within 
911        * this or any subsidiary module.
912        */
913        private DefaultModule[] getProviderModules( final Scope scope )
914        {
915            ArrayList visited = new ArrayList();
916            ArrayList stack = new ArrayList();
917            processModuleDependencies( visited, stack, scope, this );
918            return (DefaultModule[]) stack.toArray( new DefaultModule[0] );
919        }
920        
921        private void processModuleDependencies( 
922          final List visited, final List stack, final Scope scope, final DefaultResource resource ) 
923        {
924            if( visited.contains( resource ) )
925            {
926                return;
927            }
928            
929            visited.add( resource );
930            final boolean expansion = false;
931            final boolean filtering = false;
932            
933            Classifier classifier = resource.getClassifier();
934            if( classifier.equals( Classifier.ANONYMOUS ) )
935            {
936                return;
937            }
938            
939            if( resource instanceof DefaultModule )
940            {
941                DefaultModule module = (DefaultModule) resource;
942                DefaultResource[] providers = module.getAggregatedDefaultProviders( scope, expansion, filtering );
943                for( int i=0; i<providers.length; i++ )
944                {
945                    processModuleDependencies( visited, stack, scope, providers[i] );
946                }
947                DefaultResource[] children = module.getDefaultModules();
948                for( int i=0; i<children.length; i++ )
949                {
950                    processModuleDependencies( visited, stack, scope, children[i] );
951                }
952                if( !resource.equals( this ) )
953                {
954                    stack.add( module );
955                }
956            }
957            else
958            {
959                DefaultResource[] resources = 
960                  resource.getAggregatedDefaultProviders( scope, expansion, filtering );
961                for( int i=0; i<resources.length; i++ )
962                {
963                    DefaultModule m = resources[i].getDefaultParent();
964                    processModuleDependencies( visited, stack, scope, m );
965                }
966            }
967        }
968    
969        private DefaultResource[] filterProjects( final DefaultResource[] resources )
970        {
971            ArrayList list = new ArrayList();
972            for( int i=0; i<resources.length; i++ )
973            {
974                DefaultResource resource = resources[i];
975                if( resource.isLocal() )
976                {
977                    list.add( resource );
978                }
979            }
980            return (DefaultResource[]) list.toArray( new DefaultResource[0] );
981        }
982    }