001    /*
002     * Copyright (c) 2005 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.metro.tools;
020    
021    import java.io.File;
022    import java.io.FileFilter;
023    import java.io.FileWriter;
024    import java.io.IOException;
025    import java.net.URI;
026    import java.util.List;
027    import java.util.ArrayList;
028    import java.util.LinkedList;
029    import java.util.Hashtable;
030    import java.util.Arrays;
031    import java.util.Map;
032    
033    import net.dpml.metro.info.EntryDescriptor;
034    import net.dpml.metro.info.Type;
035    import net.dpml.metro.info.ContextDescriptor;
036    import net.dpml.metro.info.ServiceDescriptor;
037    
038    import net.dpml.metro.builder.ComponentTypeDecoder;
039    
040    import net.dpml.transit.Transit;
041    
042    import net.dpml.util.Resolver;
043    import net.dpml.util.SimpleResolver;
044    
045    import org.apache.tools.ant.Task;
046    import org.apache.tools.ant.BuildException;
047    import org.apache.tools.ant.Project;
048    import org.apache.tools.ant.DirectoryScanner;
049    import org.apache.tools.ant.taskdefs.Copy;
050    import org.apache.tools.ant.types.FileSet;
051    
052    /**
053     * Create a set of html reports about component types.
054     *
055     * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
056     * @version 1.2.0
057     */
058    public class CatalogTask extends Task
059    {
060        private static final ComponentTypeDecoder COMPONENT_TYPE_DECODER = 
061          new ComponentTypeDecoder();
062    
063        private File m_work;
064        private File m_destination;
065        private String m_style;
066        private String m_title;
067        private List m_filesets = new LinkedList();
068        private Map m_packages = new Hashtable();
069    
070       /**
071        * Set the final destination directory for generated HTML content.
072        * @param destination the destination directory
073        */
074        public void setDest( File destination )
075        {
076            m_destination = destination;
077        }
078    
079       /**
080        * Optional override of the XSL stylesheet used to transform type descriptors.
081        * @param style the XSL transformation to use
082        */
083        public void setStyle( String style )
084        {
085            m_style = style;
086        }
087    
088       /**
089        * Optional override of the index page title.
090        * @param title the index page title
091        */
092        public void setTitle( String title )
093        {
094            m_title = title;
095        }
096    
097       /**
098        * Add a fileset to the set of filesets definted within the catalog generation. If no
099        * filesets are added the default behaviour will be resolve against component types 
100        * declared under the current projects ${basedir}/target/classes directory.
101        *
102        * @param set the fileset to add
103        */
104        public void addFileset( FileSet set )
105        {
106            m_filesets.add( set );
107        }
108    
109       /**
110        * Task initialization.
111        */
112        public void init()
113        {
114            String targetDirPath = getProject().getProperty( "project.target.dir" );
115            File target = new File( targetDirPath );
116            File reports = new File( target, "reports" );
117            File types = new File( reports, "catalog" );
118            m_work = types;
119        }
120    
121       /**
122        * Task execution during which all component types declared in the current project or referenced
123        * by nested filesets will be used as the source defintions fro the generation of a Type catalog.
124        */
125        public void execute()
126        {
127            log( "building catalog index" );
128            Project proj = getProject();
129            File[] types = getTypes();
130            log( "Catalog type count: " + types.length );
131            if( types.length == 0 )
132            {
133                return;
134            }
135            else
136            {
137                File reports = getWorkingDirectory();
138                //File html = new File( reports, "html" );
139                File html = reports;
140                html.mkdirs();
141    
142                File css = new File( Transit.DPML_PREFS, "dpml/metro/csss/type.css" );
143                File stylesheet = new File( html, "stylesheet.css" );
144                final Copy copy = (Copy) getProject().createTask( "copy" );
145                copy.setFile( css );
146                copy.setTofile( stylesheet );
147                copy.execute();
148    
149                for( int i=0; i < types.length; i++ )
150                {
151                    File type = types[i];
152                    processType( reports, type );
153                }
154    
155                createCatalog( html );
156            }
157        }
158    
159       /**
160        * A side effect of type processing is the population of the m_packages map.  The 
161        * map keys are package names and the map values are list of types within the 
162        * respective package.  The keys constitute the package list and for each package 
163        * we construct an index of types within that package.
164        *
165        * @param root the root html directory
166        */
167        private void createCatalog( File root )
168        {
169             createIndex( root );
170             createPackagesIndex( root );
171             createTypesIndex( root );
172             createOverviewPage( root );
173        }
174    
175       /**
176        * Create the list of all types.
177        * @param root the root html directory
178        */
179        private void createTypesIndex( File root )
180        {
181            File index = new File( root, "types.html" );
182            try
183            {
184                FileWriter writer = new FileWriter( index );
185                writer.write( "<html>" );
186                writer.write( "\n  <head>" );
187                writer.write( "\n    <META http-equiv=\"Content-Type\" content=\"text/html; charset=US-ASCII\">" );
188                writer.write( "\n    <title>Package Index</title>" );
189                writer.write( "\n    <link href=\"stylesheet.css\" type=\"text/css\" rel=\"stylesheet\"/>" );
190                writer.write( "\n  </head>" );
191                writer.write( "\n  <body>" );
192                writer.write( "\n    <p><b>Overview</b></p>" );
193                writer.write( "\n    <p><a href=\"packages-overview.html\" target=\"typeFrame\">All Packages</a></p>" );
194                writer.write( "\n    <p><b>All Types</b></p>" );
195                writer.write( "\n    <table>" );
196    
197                List[] lists = (List[]) m_packages.values().toArray( new List[0] );
198                for( int i=0; i < lists.length; i++ )
199                {
200                    List list = lists[i];
201                    Type[] types = (Type[]) list.toArray( new Type[0] );
202                    for( int j=0; j < types.length; j++ )
203                    {
204                        Type type = types[j];
205                        String path = type.getInfo().getClassname().replace( '.', '/' ); 
206                        String classname = getClassName( type );
207                        String filename = path + ".html"; 
208                        writer.write( "\n      <tr><td><a href=\"" );
209                        writer.write( filename );
210                        writer.write( "\" target=\"typeFrame\">" );
211                        writer.write( classname );
212                        writer.write( "</a></td></tr>" );
213                    }
214                }
215                writer.write( "\n    </table>" );
216                writer.write( "\n  </body>" );
217                writer.write( "\n</html>" );
218                writer.close();
219            }
220            catch( Exception e )
221            {
222                final String error = 
223                  "Internal error while attempting to create packages list.";
224                throw new BuildException( error, e, getLocation() );
225            }
226        }
227    
228        private void createOverviewPage( File root )
229        {
230            File overview = new File( root, "packages-overview.html" );
231            try
232            {
233                FileWriter writer = new FileWriter( overview );
234                writer.write( "<html>" );
235                writer.write( "\n  <head>" );
236                writer.write( "\n    <META http-equiv=\"Content-Type\" content=\"text/html; charset=US-ASCII\">" );
237                writer.write( "\n    <title>Type Packages List</title>" );
238                writer.write( "\n    <link href=\"stylesheet.css\" type=\"text/css\" rel=\"stylesheet\"/>" );
239                writer.write( "\n  </head>" );
240                writer.write( "\n  <body>" );
241    
242                //
243                // add menu containing packages overview links
244                //
245    
246                writer.write( "\n    <table class=\"menubar\">" );
247                writer.write( "\n      <tr>" );
248                writer.write( "<td class=\"package\"><a class=\"package\" href=\"packages-overview.html\">" );
249                writer.write( "overview" );
250                writer.write( "</a></td>" );
251                writer.write( "\n      </tr>" );
252                writer.write( "\n    </table>" );
253    
254                writer.write( "\n  <p class=\"title\">Overview</p>" );
255    
256                writer.write( "\n    <table>" );
257                writer.write( "\n    <tr><th>Package</th><th>Components</th></tr>" );
258    
259                boolean flag = true;
260                Object[] keys = m_packages.keySet().toArray();
261                for( int i=0; i < keys.length; i++ )
262                {
263                    String name = (String) keys[i];
264                    createPackageOverview( root, name );
265                    List list = getPackageList( name );
266                    if( flag )
267                    {
268                        writer.write( "\n      <tr class=\"p-even\">" );
269                    }
270                    else
271                    {
272                        writer.write( "\n      <tr class=\"p-odd\">" );
273                    }
274                    flag = !flag;
275    
276                    writer.write( "<td><a href=\"" );
277                    String path = name.replace( '.', '/' );
278                    writer.write( path + "/overview.html\" target=\"typeFrame\">" );
279                    writer.write( name );
280                    writer.write( "</a></td>"  );
281                    writer.write( "<td>" );
282                    Type[] types = (Type[]) list.toArray( new Type[0] );
283                    for( int j=0; j < types.length; j++ )
284                    {
285                        Type type = types[j];
286                        String classname = getClassName( type );
287                        String filename = type.getInfo().getClassname().replace( '.', '/' ) + ".html";
288                        if( j > 0 )
289                        {
290                            writer.write( ", " );
291                        }
292                        writer.write( "<a href=\"" );
293                        writer.write( filename );
294                        writer.write( "\" target=\"typeFrame\">" );
295                        writer.write( classname );
296                        writer.write( "</a>" );
297                    }
298                    writer.write( "</td>" );
299                    writer.write( "</tr>" );
300                }
301    
302                writer.write( "\n    </table>" );
303                writer.write( "\n  </body>" );
304                writer.write( "\n</html>" );
305                writer.close();
306            }
307            catch( Exception e )
308            {
309                final String error = 
310                  "Internal error while attempting to create packages overview.";
311                throw new BuildException( error, e, getLocation() );
312            }
313        }
314    
315        private void createPackagesIndex( File root )
316        {
317            File packages = new File( root, "packages.html" );
318            try
319            {
320                FileWriter writer = new FileWriter( packages );
321                writer.write( "<html>" );
322                writer.write( "\n  <head>" );
323                writer.write( "\n    <META http-equiv=\"Content-Type\" content=\"text/html; charset=US-ASCII\">" );
324                writer.write( "\n    <title>Type Packages List</title>" );
325                writer.write( "\n    <link href=\"stylesheet.css\" type=\"text/css\" rel=\"stylesheet\">" );
326                writer.write( "\n  </head>" );
327                writer.write( "\n  <body>" );
328    
329                String title = getTitle();
330    
331                writer.write( "\n    <table>" );
332                writer.write( "\n      <tr><td class=\"title\">" + title + "</td></tr>" );
333                writer.write( "\n    </table>" );
334    
335                writer.write( "\n  <p><a href=\"types.html\" target=\"typeListFrame\">All Types</a></p>" );
336                writer.write( "\n  <p><b>Packages</b></p>" );
337                writer.write( "\n    <table>" );
338    
339                Object[] keys = m_packages.keySet().toArray();
340                for( int i=0; i < keys.length; i++ )
341                {
342                    String name = (String) keys[i];
343                    writer.write( "\n      <tr><td><a href=\"" );
344                    String path = name.replace( '.', '/' );
345                    writer.write( path + "/package-index.html\" target=\"typeListFrame\">" );
346                    writer.write( name  );
347                    writer.write( "</a></td></tr>"  );
348                    createPackageIndex( root, name );
349                }
350      
351                writer.write( "\n    </table>" );
352                writer.write( "\n  </body>" );
353                writer.write( "\n</html>" );
354                writer.close();
355            }
356            catch( Exception e )
357            {
358                final String error = 
359                  "Internal error while attempting to create packages list.";
360                throw new BuildException( error, e, getLocation() );
361            }
362        }
363    
364        private void createPackageIndex( File root, String name )
365        {
366            String path = name.replace( '.', '/' );
367            File dir = new File( root, path );
368            dir.mkdirs();
369            File index = new File( dir, "package-index.html" );
370            String offset = getOffset( name );
371            try
372            {
373                FileWriter writer = new FileWriter( index );
374                writer.write( "<html>" );
375                writer.write( "\n  <head>" );
376                writer.write( "\n    <META http-equiv=\"Content-Type\" content=\"text/html; charset=US-ASCII\">" );
377                writer.write( "\n    <title>Package Index</title>" );
378                writer.write( "\n    <link href=\"" + offset + "/stylesheet.css\" type=\"text/css\" rel=\"stylesheet\"/>" );
379                writer.write( "\n  </head>" );
380                writer.write( "\n  <body>" );
381                writer.write( "\n    <p><b>Overview</b></p>" );
382                writer.write( "\n    <p><a href=\"overview.html\" target=\"typeFrame\">" + name + "</a></p>" );
383                writer.write( "\n    <p><b>Types</b></p>" );
384                writer.write( "\n    <table>" );
385    
386                List list = getPackageList( name );
387                Type[] types = (Type[]) list.toArray( new Type[0] );
388                for( int i=0; i < types.length; i++ )
389                {
390                    Type type = types[i];
391                    String classname = getClassName( type );
392                    String filename = classname + ".html"; 
393                    writer.write( "\n      <tr><td><a href=\"" );
394                    writer.write( filename );
395                    writer.write( "\" target=\"typeFrame\">" );
396                    writer.write( classname );
397                    writer.write( "</a></td></tr>" );
398                }
399                writer.write( "\n    </table>" );
400                writer.write( "\n  </body>" );
401                writer.write( "\n</html>" );
402                writer.close();
403            }
404            catch( Exception e )
405            {
406                final String error = 
407                  "Internal error while attempting to create packages list.";
408                throw new BuildException( error, e, getLocation() );
409            }
410        }
411    
412        private void createPackageOverview( File root, String name )
413        {
414            String path = name.replace( '.', '/' );
415            File dir = new File( root, path );
416            dir.mkdirs();
417            File index = new File( dir, "overview.html" );
418            String offset = getOffset( name );
419            try
420            {
421                FileWriter writer = new FileWriter( index );
422                writer.write( "<html>" );
423                writer.write( "\n  <head>" );
424                writer.write( "\n    <META http-equiv=\"Content-Type\" content=\"text/html; charset=US-ASCII\">" );
425                writer.write( "\n    <title>Package Overview: " + name + "</title>" );
426                writer.write( "\n    <link href=\"" + offset + "/stylesheet.css\" type=\"text/css\" rel=\"stylesheet\"/>" );
427                writer.write( "\n  </head>" );
428                writer.write( "\n  <body>" );
429    
430                //
431                // add the menubar containing the link to the all packages overview
432                //
433    
434                writer.write( "\n    <table class=\"menubar\">" );
435                writer.write( "\n      <tr>" );
436                writer.write( "<td class=\"package\"><a class=\"package\" href=\"" + offset + "/packages-overview.html\">" );
437                writer.write( "overview" );
438                writer.write( "</a></td>" );
439                writer.write( "<td class=\"package-selected\"><a class=\"package-selected\" href=\"overview.html\">" );
440                writer.write( name );
441                writer.write( "</a></td>" );
442                writer.write( "\n      </tr>" );
443                writer.write( "\n    </table>" );
444    
445                //
446                // add the package name
447                //
448    
449                writer.write( "\n    <table>" );
450                writer.write( "\n      <tr><td class=\"title\">" + name + "</td></tr>" );
451                writer.write( "\n    </table>" );
452    
453                writer.write( "\n    <table width=\"100%\">" );
454                writer.write( "\n      <tr><th>Type</th><th>Name</th><th>Lifestyle</th></tr>" );
455    
456                //
457                // list all component types in the package
458                //
459    
460                boolean flag = true;
461                List list = getPackageList( name );
462                Type[] types = (Type[]) list.toArray( new Type[0] );
463                for( int i=0; i < types.length; i++ )
464                {
465                    Type type = types[i];
466                    if( flag )
467                    {
468                        writer.write( "\n      <tr class=\"p-even\">" );
469                    }
470                    else
471                    {
472                        writer.write( "\n      <tr class=\"p-odd\">" );
473                    }
474                    flag = !flag;
475                    
476                    String classname = getClassName( type );
477                    String filename = classname + ".html"; 
478                    writer.write( "\n        <td><a href=\"" );
479                    writer.write( filename );
480                    writer.write( "\" target=\"typeFrame\">" );
481                    writer.write( classname );
482                    writer.write( "</a>" );
483                    writer.write( "\n        </td>" );
484                    
485                    writer.write( "\n        <td>" );
486                    writer.write( type.getInfo().getName() );
487                    writer.write( "\n        </td>" );
488                    
489                    writer.write( "\n        <td>" );
490                    writer.write( type.getInfo().getLifestylePolicy().getName() );
491                    writer.write( "\n        </td>" );
492                    
493                    writer.write( "\n      </tr>" );
494                }
495                writer.write( "\n    </table>" );
496                writer.write( "\n  </body>" );
497                writer.write( "\n</html>" );
498                writer.close();
499            }
500            catch( Exception e )
501            {
502                final String error = 
503                  "Internal error while attempting to create packages list.";
504                throw new BuildException( error, e, getLocation() );
505            }
506        }
507    
508        private void createTypePage( File root, Type type )
509        {
510            File html = getReportDestination( root, type, "html" );
511            html.getParentFile().mkdirs();
512            String classname = type.getInfo().getClassname();
513            String cname = getClassName( type );
514            String packagename = getPackageName( type );
515            String offset = getOffset( packagename );
516            List packageList = getPackageList( packagename );
517            packageList.add( type );
518            try
519            {
520                FileWriter writer = new FileWriter( html );
521                writer.write( "<html>" );
522                writer.write( "\n  <head>" );
523                writer.write( "\n    <META http-equiv=\"Content-Type\" content=\"text/html; charset=US-ASCII\">" );
524                writer.write( "\n    <title>Component Type: " + classname + "</title>" );
525                writer.write( "\n    <link href=\"" + offset + "/stylesheet.css\" type=\"text/css\" rel=\"stylesheet\"/>" );
526                writer.write( "\n  </head>" );
527                writer.write( "\n  <body>" );
528    
529                //
530                // add menu containing packages and package overview links
531                //
532    
533                writer.write( "\n    <table class=\"menubar\">" );
534                writer.write( "\n      <tr>" );
535                writer.write( "<td class=\"package\"><a class=\"package\" href=\"" 
536                  + offset + "/packages-overview.html\">" );
537                writer.write( "overview" );
538                writer.write( "</a></td>" );
539                writer.write( "<td class=\"package\"><a class=\"package\" href=\"overview.html\">" );
540                writer.write( packagename );
541                writer.write( "</a></td>" );
542                writer.write( "<td class=\"package-selected\">" + cname + "</td>" );
543                writer.write( "\n      </tr>" );
544                writer.write( "\n    </table>" );
545    
546                //
547                // add component type classname as title
548                //
549    
550                writer.write( "\n    <table>" );
551                writer.write( "\n      <tr><td class=\"type\">" + cname + "</td></tr>" );
552                writer.write( "\n    </table>" );
553    
554                //
555                // write out the InfoDescriptor features
556                //
557    
558                writer.write( "\n    <table>" );
559                writer.write( "\n      <tr><td class=\"feature\">Version:</td><td>" 
560                  + type.getInfo().getVersion() + "</td></tr>" );
561                writer.write( "\n      <tr><td class=\"feature\">Name:</td><td>" 
562                  + type.getInfo().getName() + "</td></tr>" );
563                writer.write( "\n      <tr><td class=\"feature\">Lifestyle:</td><td>" 
564                  + type.getInfo().getLifestylePolicy().getName() + "</td></tr>" );
565                writer.write( "\n      <tr><td class=\"feature\">Thread-Safe:</td><td>" 
566                  + type.getInfo().getThreadSafePolicy() + "</td></tr>" );
567                writer.write( "\n      <tr><td class=\"feature\">Collection:</td><td>" 
568                  + type.getInfo().getCollectionPolicy().getName() + "</td></tr>" );
569                writer.write( "\n    </table>" );
570    
571                //
572                // write out the exported services that the component provides
573                //
574    
575                ServiceDescriptor[] services = type.getServiceDescriptors();
576                writeServiceDescriptors( writer, services );
577    
578                //
579                // write out the context model
580                //
581    
582                boolean flag = true;
583                ContextDescriptor context = type.getContextDescriptor();
584                EntryDescriptor[] entries = context.getEntryDescriptors();
585                if( entries.length > 0  )
586                {
587                    Arrays.sort( entries );
588                    writer.write( "\n    <p class=\"category\">Context</p>" );
589                    writer.write( "\n    <table width=\"100%\">" );
590                    for( int j=0; j<entries.length; j++ )
591                    {
592                        EntryDescriptor entry = entries[j];
593                        String key = entry.getKey();
594                        String entryClassname = entry.getClassname();
595                        String entryDisplayClassname = getDisplayClassname( entryClassname );
596                        boolean optional = entry.isOptional();
597                        
598                        if( flag )
599                        {
600                            writer.write( "<tr class=\"c-even\">" );
601                        }
602                        else
603                        {
604                            writer.write( "<tr class=\"c-odd\">" );
605                        }
606                        writer.write( "<td>" + key + "</td>" );
607                        if( optional )
608                        {
609                            writer.write( "<td>optional</td>" );
610                        }
611                        else
612                        {
613                            writer.write( "<td>required</td>" );
614                        }
615                        writer.write( "<td>" + entryDisplayClassname + "</td>" );
616                        writer.write( "</tr>" );
617                        flag = !flag;
618                    }
619                    writer.write( "\n    </table>" );
620                }
621                
622                writer.write( "\n  </body>" );
623                writer.write( "\n</html>" );
624                writer.close();
625            }
626            catch( Exception e )
627            {
628                final String error = 
629                  "Internal error while attempting to create type page.";
630                throw new BuildException( error, e, getLocation() );
631            }
632        }
633        
634        private String getDisplayClassname( String classname )
635        {
636            if( classname.startsWith( "[L" ) )
637            {
638                if( classname.endsWith( ";" ) )
639                {
640                    int n = classname.length();
641                    return classname.substring( 2, n-1 ) + "[]";
642                }
643                else
644                {
645                    return classname.substring( 2 ) + "[]";
646                }
647            }
648            else
649            {
650                return classname;
651            }
652        }
653        
654        private void writeServiceDescriptors( FileWriter writer, ServiceDescriptor[] services ) throws IOException
655        {
656            boolean flag = true;
657            if( services.length > 0 )
658            {
659                writer.write( "\n    <p class=\"category\">Services</p>" );
660                writer.write( "\n    <table width=\"100%\">" );
661                for( int j=0; j < services.length; j++ )
662                {
663                    String service = services[j].getClassname();
664                    if( flag )
665                    {
666                        writer.write( "<tr class=\"even\"><td>" + service + "</td></tr>" );
667                    }
668                    else
669                    {
670                        writer.write( "<tr class=\"odd\"><td>" + service + "</td></tr>" );
671                    }
672                    flag = !flag;
673                }
674                writer.write( "\n    </table>" );
675            }
676        }
677        
678        private List getPackageList( String name )
679        {
680            List list = (List) m_packages.get( name );
681            if( null == list )
682            {
683                list = new ArrayList();
684                m_packages.put( name, list );
685            }
686            return list;
687        }
688    
689        private void processType( File reports, File source )
690        {
691            log( source.toString() );
692            File htmls = reports;
693            try
694            {
695                URI uri = source.toURI();
696                Resolver resolver = new SimpleResolver();
697                Type type = COMPONENT_TYPE_DECODER.loadType( uri, resolver );
698                createTypePage( htmls, type );
699            }
700            catch( Exception e )
701            {
702                throw new BuildException( e.getMessage(), e, getLocation() );
703            }
704        }
705    
706        private File getReportDestination( File dir, Type type, String suffix )
707        {
708            final String classname = type.getInfo().getClassname();
709            String path = classname.replace( '.', '/' );
710            String filename = path + "." + suffix;
711            return new File( dir, filename );
712        }
713    
714        private String getClassName( Type type )
715        {
716            String path = type.getInfo().getClassname();
717            int n = path.lastIndexOf( "." );
718            if( n > -1 )
719            {
720                return path.substring( n + 1 );
721            }
722            else
723            {
724                return path;
725            }
726        }
727    
728        private String getPackageName( Type type )
729        {
730            String path = type.getInfo().getClassname();
731            int n = path.lastIndexOf( "." );
732            if( n > -1 )
733            {
734                return path.substring( 0, n );
735            }
736            else
737            {
738                return "";
739            }
740        }
741    
742        private String getBasePath( Type type )
743        {
744            String base = "";
745            String path = type.getInfo().getClassname();
746            while( path.indexOf( "." ) > 0 )
747            {
748                path = path.substring( path.indexOf( "." ) + 1 );
749                if( base.equals( "" ) )
750                {
751                    base = "..";
752                }
753                else
754                {
755                    base = base + "/..";
756                }
757            }
758            return base;
759        }
760    
761        private String getOffset( String packagename )
762        {
763            String base = "..";
764            String path = packagename;
765            while( path.indexOf( "." ) > 0 )
766            {
767                path = path.substring( path.indexOf( "." ) + 1 );
768                base = base + "/..";
769            }
770            return base;
771        }
772    
773        private String getStyle()
774        {
775            if( null == m_style )
776            {
777                return new File( Transit.DPML_PREFS, "metro/style/type-formatter.xsl" ).toString();
778            }
779            else
780            {
781                return m_style;
782            }
783        }
784    
785        private String getTitle()
786        {
787            if( null == m_title )
788            {
789                return "DPML Metro Catalog";
790            }
791            else
792            {
793                return m_title;
794            }
795        }
796    
797        private File getWorkingDirectory()
798        {
799            if( null == m_destination )
800            {
801                return m_work;
802            }
803            else
804            {
805                return m_destination;
806            }
807        }
808    
809        private File[] getTypes()
810        {
811            if( m_filesets.size() == 0 )
812            {         
813                String classesDirPath = getProject().getProperty( "project.target.classes.main.dir" );
814                File classes = new File( classesDirPath );
815                return getTypes( classes );
816            }
817            else
818            {
819                ArrayList list = new ArrayList();
820                Project project = getProject();
821                FileSet[] filesets = (FileSet[]) m_filesets.toArray( new FileSet[0] );
822                for( int i=0; i < filesets.length; i++ )
823                {
824                    FileSet fileset = filesets[i];
825                    File basedir = fileset.getDir( project );
826                    DirectoryScanner ds = fileset.getDirectoryScanner( project );
827                    String[] files = ds.getIncludedFiles();
828                    for( int j=0; j < files.length; j++ )
829                    {
830                        String path = files[j];
831                        File file = new File( basedir, path );
832                        list.add( file );
833                    }
834                }
835                return (File[]) list.toArray( new File[0] );
836            }
837        }
838    
839        private File[] getTypes( File dir )
840        {
841            ArrayList list = new ArrayList();
842            getTypes( list, dir );
843            return (File[]) list.toArray( new File[0] );
844        }
845    
846        private void getTypes( List list, File dir )
847        {
848            File[] types = dir.listFiles( new TypeFileFilter() );
849            for( int i=0; i<types.length; i++ )
850            {
851                list.add( types[i] );
852            }
853            File[] dirs = dir.listFiles( new DirectoryFilter() );
854            for( int i=0; i<dirs.length; i++ )
855            {
856                getTypes( list, dirs[i] );
857            }
858        }
859    
860       /**
861        * Type file filter impl.
862        */
863        private static class TypeFileFilter implements FileFilter
864        {
865           /**
866            * Return true if the file is a type.
867            * @param file the file to test
868            * @return true if the file is a type
869            */
870            public boolean accept( File file )
871            {
872                return file.getName().endsWith( ".type" );
873            }
874        }
875    
876       /**
877        * Directory filer impl.
878        */
879        private static class DirectoryFilter implements FileFilter
880        {
881           /**
882            * Return true if the file is a directory.
883            * @param file the file to test
884            * @return true if the file is a directory
885            */
886            public boolean accept( File file )
887            {
888                return file.isDirectory();
889            }
890        }
891    
892        private void createIndex( File root )
893        {
894            String title = getTitle();
895            File index = new File( root, "index.html" );
896            try
897            {
898                FileWriter writer = new FileWriter( index );
899                writer.write( "<html>" );
900                writer.write( "\n  <head>" );
901                writer.write( "\n    <META http-equiv=\"Content-Type\" content=\"text/html; charset=US-ASCII\">" );
902                writer.write( "\n    <title>" + title + "</title>" );
903                writer.write( "\n    <link href=\"stylesheet.css\" type=\"text/css\" rel=\"stylesheet\"/>" );
904                writer.write( "\n  </head>" );
905                writer.write( "\n  <frameset cols=\"20%,80%\">" );
906                writer.write( "\n    <frameset rows=\"30%,70%\">" );
907                writer.write( "\n      <frame name=\"packageListFrame\" src=\"packages.html\">" );
908                writer.write( "\n      <frame name=\"typeListFrame\" src=\"types.html\">" );
909                writer.write( "\n    </frameset>" );
910                writer.write( "\n    <frame name=\"typeFrame\" src=\"packages-overview.html\">" );
911                writer.write( "\n  </frameset>" );
912                writer.write( "\n</html>" );
913                writer.close();
914            }
915            catch( Exception e )
916            {
917                final String error = 
918                  "Internal error while attempting to create frameset.";
919                throw new BuildException( error, e, getLocation() );
920            }
921        }
922    }