001    /*
002     * Copyright 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.tools.tasks;
020    
021    import java.io.File;
022    import java.io.OutputStream;
023    import java.io.FileOutputStream;
024    import java.io.IOException;
025    import java.net.URI;
026    import java.util.ArrayList;
027    import java.util.List;
028    
029    import net.dpml.lang.Classpath;
030    import net.dpml.lang.Category;
031    import net.dpml.lang.Info;
032    import net.dpml.lang.Part;
033    import net.dpml.lang.PartDecoder;
034    
035    import net.dpml.library.Type;
036    import net.dpml.library.Resource;
037    
038    import net.dpml.transit.Artifact;
039    
040    import net.dpml.tools.Context;
041    import net.dpml.tools.BuildError;
042    
043    import net.dpml.util.DefaultLogger;
044    
045    import org.apache.tools.ant.Project;
046    import org.apache.tools.ant.BuildException;
047    
048    import org.w3c.dom.Element;
049    
050    /**
051     * Creation of an part definition in XML.
052     *
053     * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
054     * @version 1.1.3
055     */
056    public class PartTask extends GenericTask
057    {    
058       /**
059        * Constant artifact type for a plugin.
060        */
061        public static final String TYPE = "part";
062    
063       /**
064        * Property key used to identify the plugin title.
065        */
066        public static final String PLUGIN_TITLE_KEY = "project.title";
067        
068       /**
069        * Property key used to identify the plugin description.
070        */
071        public static final String PLUGIN_DESCRIPTION_KEY = "project.description";
072        
073       /**
074        * Property key used to identify a custom plugin handler classname.
075        */
076        public static final String PLUGIN_HANDLER_KEY = "project.plugin.handler";
077        
078        private boolean m_test = false;
079        
080        private File m_output;
081        
082       /**
083        * Set the test build policy.  The default is to include 
084        * the project artifact in the classpath of a created part, however - in a 
085        * test scenario we don't want to do this.  Setting test to true will result
086        * in the association of a local file uri to the project resource.
087        *
088        * @param test true if this is a local test part
089        */
090        public void setTest( boolean test )
091        {
092            m_test = test;
093        }
094    
095       /**
096        * Override the default output destination.
097        *
098        * @param file the overriding destination
099        */
100        public void setDest( File file )
101        {
102            m_output = file;
103        }
104    
105       /**
106        * Task execution.
107        */
108        public void execute()
109        {
110            Resource resource = getResource();
111            Part part = build( resource );
112            writePart( part );
113        }
114        
115       /**
116        * Externalize the part as a deliverable.
117        * @param part the part to be externalized as XML
118        */
119        public void writePart( Part part )
120        {
121            File file = getOutputFile();
122            if( file.exists() )
123            {
124                try
125                {
126                    Part existing = Part.load( file.toURI(), false );
127                    if( part.equals( existing ) )
128                    {
129                        return;
130                    }
131                }
132                catch( Throwable e )
133                {
134                    final String error = 
135                      "Part comparisom failure.";
136                    throw new BuildError( error, e );
137                }
138            }
139            
140            log( "Building part: " + file );
141            try
142            {
143                file.createNewFile();
144                final OutputStream output = new FileOutputStream( file );
145                try
146                {
147                    part.encode( output );
148                    checksum( file );
149                    asc( file );
150                }
151                finally
152                {
153                    try
154                    {
155                        output.close();
156                    }
157                    catch( IOException e )
158                    {
159                        e.printStackTrace();
160                    }
161                }
162            }
163            catch( Exception e )
164            {
165                final String error = 
166                  "Part externalization error.";
167                throw new BuildError( error, e );
168            }
169        }
170        
171        private File getOutputFile()
172        {
173            if( null != m_output )
174            {
175                return m_output;
176            }
177            else
178            {
179                Context context = getContext();
180                final String path = context.getLayoutFilename( TYPE );
181                final File deliverables = context.getTargetDeliverablesDirectory();
182                final File parts = new File( deliverables, TYPE + "s" );
183                final File file = new File( parts, path );
184                parts.mkdirs();
185                return file;
186            }
187        }
188        
189       /**
190        * Build the part definition.
191        * @param resource the resource
192        * @return the part
193        */
194        protected Part build( Resource resource )
195        {
196            try
197            {
198                Info info = getInfo( resource );
199                Classpath classpath = getClasspath( resource );
200                Type type = resource.getType( TYPE );
201                Element element = type.getElement();
202                PartDecoder decoder = PartDecoder.getInstance();
203                DefaultLogger logger = new DefaultLogger( getTaskName() );
204                return decoder.build( logger, info, classpath, element, resource );
205            }
206            catch( Throwable e )
207            {
208                final String error = 
209                  "Internal error while attempting to build an external part definition."
210                  + "\nResource: " + resource;
211                throw new BuildError( error, e, getLocation() );
212            }
213        }
214        
215       /**
216        * Construct the info object based on properties declared by the 
217        * supplied resource.
218        * @param resource the resource
219        * @return the info descriptor
220        */
221        protected Info getInfo( Resource resource )
222        {
223            Artifact artifact = resource.getArtifact( TYPE );
224            URI uri = artifact.toURI();
225            String title = resource.getInfo().getTitle();
226            String description = resource.getInfo().getDescription();
227            return new Info( uri, title, description );
228        }
229    
230        private String getTitle( Resource resource )
231        {
232            return resource.getProperty( PLUGIN_TITLE_KEY );
233        }
234    
235        private String getDescription( Resource resource )
236        {
237            return resource.getProperty( PLUGIN_DESCRIPTION_KEY );
238        }
239        
240       /**
241        * Construct the classpath for the supplied resource.
242        * @param resource the resource
243        * @return the classpath 
244        * @exception IOException is an IO error occurs
245        */
246        protected Classpath getClasspath( Resource resource ) throws IOException
247        {
248            URI[] sysUris = getURIs( resource, Category.SYSTEM );
249            URI[] publicUris = getURIs( resource, Category.PUBLIC );
250            URI[] protectedUris = getURIs( resource, Category.PROTECTED );
251            URI[] privateUris = getURIs( resource, Category.PRIVATE, true );
252            return new Classpath( sysUris, publicUris, protectedUris, privateUris );
253        }
254    
255        private URI[] getURIs( Resource resource, Category category ) throws IOException
256        {
257            return getURIs( resource, category, false );
258        }
259        
260        private URI[] getURIs( Resource resource, Category category, boolean self ) throws IOException
261        {
262            Resource[] resources = resource.getClasspathProviders( category );
263            ArrayList list = new ArrayList();
264            for( int i=0; i<resources.length; i++ )
265            {
266                Resource r = resources[i];
267                addURI( list, r );
268            }
269            if( self )
270            {
271                addURI( list, resource );
272            }
273            URI[] uris = (URI[]) list.toArray( new URI[0] );
274            return uris;
275        }
276        
277        private void addURI( List list, Resource resource )  throws IOException
278        {
279            if( resource.isa( "jar" ) )
280            {
281                try
282                {
283                    URI uri = toURI( resource );
284                    list.add( uri );
285                }
286                catch( Exception e )
287                {
288                    final String error = 
289                      "Unexpected error while attempting to resolve resource.";
290                    IOException ioe = new IOException( error );
291                    ioe.initCause( e );
292                    throw ioe;
293                }
294            }
295        }
296        
297        private URI toURI( Resource resource ) throws Exception
298        {
299            if( m_test && resource.equals( getResource() ) )
300            {
301                File local = getContext().getTargetDeliverable( "jar" );
302                return local.toURI();
303            }
304            else
305            {
306                return resource.getArtifact( "jar" ).toURI();
307            }
308        }
309        
310       /**
311        * Get the project definition.
312        * @param project the project
313        * @return the build context
314        */
315        protected Context getContext( Project project )
316        {
317            Context context = (Context) project.getReference( "project.context" );
318            context.init();
319            return context;
320        }
321    }