001    /*
002     * Copyright 2004-2005 Stephen 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.tools.tasks;
020    
021    import java.io.File;
022    import java.util.ArrayList;
023    import java.util.List;
024    
025    import net.dpml.library.info.Scope;
026    import net.dpml.library.Resource;
027    
028    import org.apache.tools.ant.BuildException;
029    import org.apache.tools.ant.Project;
030    import org.apache.tools.ant.taskdefs.Javadoc;
031    import org.apache.tools.ant.taskdefs.Javadoc.AccessType;
032    import org.apache.tools.ant.types.DirSet;
033    import org.apache.tools.ant.types.Path;
034    
035    /**
036     * Build the javadoc for a project.
037     *
038     * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
039     * @version 1.1.3
040     */
041    public class JavadocTask extends ResourceTask
042    {
043        //-----------------------------------------------------------------------
044        // static
045        //-----------------------------------------------------------------------
046    
047        private static final String JAVADOC_TASK_NAME = "javadoc";
048        
049       /**
050        * Property key for declaration of an overriding access level.
051        */
052        public static final String JAVADOC_ACCESS_KEY = "project.javadoc.access";
053    
054       /**
055        * Property key for declaration of the linksource option.
056        */
057        public static final String JAVADOC_LINK_SOURCE_KEY = "project.javadoc.linksource";
058    
059        //-----------------------------------------------------------------------
060        // state
061        //-----------------------------------------------------------------------
062    
063        private String m_title;
064        private List m_links = new ArrayList();
065        private List m_groups = new ArrayList();
066        private File m_overview;
067        private AccessType m_access;
068        private File m_dest;
069    
070        //-----------------------------------------------------------------------
071        // JavadocTask
072        //-----------------------------------------------------------------------
073    
074       /**
075        * Task initialization.
076        */
077        public void init()
078        {
079            if( !isInitialized() )
080            {
081                super.init();
082                getContext().getPath( Scope.RUNTIME );
083            }
084        }
085    
086       /**
087        * Set the javadoc title.
088        * @param title the title
089        */
090        public void setTitle( final String title )
091        {
092            m_title = title;
093        }
094    
095       /**
096        * Set the javadoc overview file.
097        * @param overview the overview file
098        */
099        public void setOverview( final File overview )
100        {
101            m_overview = overview;
102        }
103    
104       /**
105        * Override the default destination.
106        * @param dir the destination directory
107        */
108        public void setDest( final File dir )
109        {
110            m_dest = dir;
111        }
112    
113       /**
114        * Create and add a new link to the javadoc run.
115        * @return the new link
116        */
117        public Link createLink()
118        {
119            final Link link = new Link();
120            m_links.add( link );
121            return link;
122        }
123    
124       /**
125        * Create and add a new group to the javadoc defintion.
126        * @return the new group
127        */
128        public Group createGroup()
129        {
130            final Group group = new Group();
131            m_groups.add( group );
132            return group;
133        }
134        
135       /**
136        * Set the documentation access level.
137        * @param access the access level
138        */
139        public void setAccess( AccessType access )
140        {
141            m_access = access;
142        }
143        
144        //-----------------------------------------------------------------------
145        // Task
146        //-----------------------------------------------------------------------
147    
148       /**
149        * Task execution.
150        * @exception BuildException if a build error occurs
151        */
152        public void execute() throws BuildException
153        {
154            final Resource resource = getResource();
155    
156            log( "Generating javadoc for project: " + resource, Project.MSG_VERBOSE );
157    
158            File api = getDestination();
159            mkDir( api );
160    
161            //
162            // construct the classpath by getting all of the classpaths for
163            // all of the projects within the module
164            //
165    
166            Resource[] providers = resource.getClasspathProviders( Scope.RUNTIME );
167            final Path classpath = getContext().createPath( providers, true, true );
168            process( resource, classpath, api );
169        }
170        
171        private File getDestination()
172        {
173            if( null == m_dest )
174            {
175                return getContext().getTargetReportsJavadocDirectory();
176            }
177            else
178            {
179                return m_dest;
180            }
181        }
182    
183        //-----------------------------------------------------------------------
184        // internal
185        //-----------------------------------------------------------------------
186    
187        private Group[] getGroups()
188        {
189            return (Group[]) m_groups.toArray( new Group[0] );
190        }
191    
192        private void process(
193           final Resource resource, final Path classpath, final File root )
194        {
195            log( "Preparing javadoc for output to: " + root );
196    
197            final Javadoc javadoc = (Javadoc) getProject().createTask( JAVADOC_TASK_NAME );
198            javadoc.setTaskName( getTaskName() );
199            javadoc.init();
200            javadoc.setDestdir( root );
201            javadoc.setUse( true );
202            javadoc.createClasspath().add( classpath );
203            
204            final Path source = javadoc.createSourcepath();
205            addSourcePath( resource, javadoc, source );
206            
207            if( null == m_access )
208            {
209                String access = getContext().getProperty( JAVADOC_ACCESS_KEY, "protected" );
210                AccessType type = new AccessType();
211                type.setValue( access );
212                javadoc.setAccess( type );
213            }
214            else
215            {
216                javadoc.setAccess( m_access );
217            }
218            
219            if( "true".equals( getContext().getProperty( JAVADOC_LINK_SOURCE_KEY, "false" ) ) )
220            {
221                javadoc.setLinksource( true );
222            }
223            
224            aggregateLinks( javadoc, resource );
225            Link[] links = (Link[]) m_links.toArray( new Link[0] );
226            for( int i=0; i<links.length; i++ )
227            {
228                Link link = links[i];
229                Javadoc.LinkArgument arg = javadoc.createLink();
230                String href = link.getHref();
231                log( "Adding link: " + href );
232                arg.setHref( href );
233            }
234            
235            if( null == m_title )
236            {
237                final String title = resource.getName() + " API";
238                javadoc.setDoctitle( title );
239            }
240            else
241            {
242                javadoc.setDoctitle( m_title );
243            }
244    
245            if( null != m_overview )
246            {
247                javadoc.setOverview( m_overview );
248            }
249    
250            Group[] groups = getGroups();
251            for( int i=0; i < groups.length; i++ )
252            {
253                Group group = groups[i];
254                Javadoc.GroupArgument jgroup = javadoc.createGroup();
255                jgroup.setTitle( group.getTitle() );
256                Group.Package[] packages = group.getPackages();
257                for( int j=0; j < packages.length; j++ )
258                {
259                    Javadoc.PackageName name = new Javadoc.PackageName();
260                    name.setName( packages[j].getName() );
261                    jgroup.addPackage( name );
262                }
263            }
264    
265            log( "Generating: " + resource.getName() + " API" );
266            javadoc.execute();
267        }
268        
269        private void aggregateLinks( Javadoc task, Resource resource )
270        {
271            String path = resource.getResourcePath();
272            ArrayList list = new ArrayList();
273            Resource[] providers = resource.getAggregatedProviders( Scope.RUNTIME, true, false );
274            for( int i=0; i<providers.length; i++ )
275            {
276                Resource provider = providers[i];
277                if( !provider.getResourcePath().startsWith( path ) )
278                {
279                    String source = provider.getProperty( "project.api.root" );
280                    if( null != source )
281                    {
282                        log( "Adding provider link: " + source + " from " + provider );
283                        Javadoc.LinkArgument arg = task.createLink();
284                        arg.setHref( source );
285                    }
286                }
287            }
288        }
289    
290        private void addSourcePath( Resource resource, Javadoc javadoc, Path source )
291        {
292            //
293            // check for a classic src directory
294            //
295    
296            if( null != resource.getBaseDir() )
297            {
298                File src = new File( resource.getBaseDir(), "target/build/main" );
299                if( src.exists() )
300                {
301                    log( "Adding src path: " + src );
302                    source.createPathElement().setLocation( src );
303                    final DirSet packages = new DirSet();
304                    packages.setDir( src );
305                    packages.setIncludes( "**/*" );
306                    javadoc.addPackageset( packages );
307                }
308            }
309            
310            String path = resource.getResourcePath();
311            Resource[] providers = resource.getAggregatedProviders( Scope.RUNTIME, true, false );
312            for( int i=0; i<providers.length; i++ )
313            {
314                Resource provider = providers[i];
315                if( provider.getResourcePath().startsWith( path ) )
316                {
317                    String flag = provider.getProperty( "project.javadoc.exclude", "false" );
318                    if( "false".equals( flag ) )
319                    {
320                        File basedir = provider.getBaseDir();
321                        if( null != basedir )
322                        {
323                            File src = new File( basedir, "target/build/main" );
324                            if( src.exists() )
325                            {
326                                log( "Adding src path: " + src );
327                                source.createPathElement().setLocation( src );
328                                final DirSet packages = new DirSet();
329                                packages.setDir( src );
330                                packages.setIncludes( "**/**" );
331                                javadoc.addPackageset( packages );
332                            }
333                        }
334                    }
335                }
336            }
337        }
338    
339        //-----------------------------------------------------------------------
340        // static classes
341        //-----------------------------------------------------------------------
342    
343       /**
344        * Delcaration of a link.
345        */
346        public class Link
347        {
348            private String m_href;
349    
350           /**
351            * Creation of a new link instance.
352            */
353            public Link()
354            {
355                m_href = null;
356            }
357    
358           /**
359            * Set the href attrbute for the api packagelist base.
360            * @param href the href to the packacke list base 
361            */
362            public void setHref( final String href )
363            {
364                m_href = href;
365            }
366    
367           /**
368            * Get the href attribute.
369            * @return the href attriute value
370            */
371            public String getHref()
372            {
373                if( null != m_href )
374                {
375                    return m_href;
376                }
377                else
378                {
379                    final String error =
380                          "Link element must contain an 'href' attribute.";
381                    throw new BuildException( error, getLocation() );
382                }
383            }
384        }
385    
386       /**
387        * Defintion of a package group.
388        */
389        public static class Group
390        {
391            private String m_title;
392            private ArrayList m_packages = new ArrayList();
393    
394           /**
395            * Set the package group title.
396            * @param title the package group title
397            */
398            public void setTitle( String title )
399            {
400                m_title = title;
401            }
402    
403           /**
404            * Return the package group title.
405            * @return the title
406            */
407            public String getTitle()
408            {
409                if( null == m_title )
410                {
411                    final String error =
412                      "Required 'title' attribute on group element undefined.";
413                    throw new BuildException( error );
414                }
415                else
416                {
417                    return m_title;
418                }
419            }
420    
421           /**
422            * Return the packages within the group.
423            * @return the array of packages
424            */
425            public Group.Package[] getPackages()
426            {
427                return (Group.Package[]) m_packages.toArray( new Group.Package[0] );
428            }
429    
430           /**
431            * Create and add a new package.
432            * @return the new package
433            */
434            public Group.Package createPackage()
435            {
436                final Package pkg = new Package();
437                m_packages.add( pkg );
438                return pkg;
439            }
440    
441           /**
442            * Defintionof a package.
443            */
444            public static class Package
445            {
446                private String m_name;
447    
448               /**
449                * Set the package name.
450                * @param name the package name
451                */
452                public void setName( String name )
453                {
454                    m_name = name;
455                }
456    
457               /**
458                * Return the package name.
459                * @return the name 
460                */
461                public String getName()
462                {
463                    if( null == m_name )
464                    {
465                        final String error =
466                          "Required 'name' attribute on package element undefined.";
467                        throw new BuildException( error );
468                    }
469                    else
470                    {
471                        return m_name;
472                    }
473                }
474            }
475        }
476    }