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.util.ArrayList;
023    import java.util.List;
024    
025    import net.dpml.lang.UnknownKeyException;
026    
027    import net.dpml.library.Resource;
028    import net.dpml.library.ResourceNotFoundException;
029    import net.dpml.library.Type;
030    import net.dpml.library.info.Scope;
031    
032    import net.dpml.transit.Artifact;
033    import net.dpml.transit.Transit;
034    import net.dpml.transit.Layout;
035    
036    import org.apache.tools.ant.BuildException;
037    import org.apache.tools.ant.taskdefs.Copy;
038    
039    /**
040     * Consolidates a set of resources based on project dependencies.
041     *
042     * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
043     * @version 1.1.3
044     */
045    public class ReplicateTask extends GenericTask
046    {
047        private String m_key;
048        private String m_ref;
049        private File m_todir;
050        private String m_layout;
051    
052        private boolean m_verbose = false;
053        private boolean m_self = false;
054        
055        private ArrayList m_includes = new ArrayList();
056    
057       /**
058        * Set the key of the target project or resource.
059        *
060        * @param key the resource key
061        */
062        public void setKey( final String key )
063        {
064            m_key = key;
065        }
066    
067       /**
068        * Set the ref of the target project or resource.
069        *
070        * @param ref the resource reference
071        */
072        public void setRef( final String ref )
073        {
074            m_ref = ref;
075        }
076    
077       /**
078        * Set the id of the target layout strategy.
079        *
080        * @param id the layout identifier
081        */
082        public void setLayout( final String id )
083        {
084            m_layout = id;
085        }
086    
087       /**
088        * Set the verbose policy.
089        * @param flag the verbose flag
090        */
091        public void setVerbose( boolean flag )
092        {
093            m_verbose = flag;
094        }
095    
096       /**
097        * Settting self to TRUE will result in expansion of the path to include
098        * the target resource.
099        * @param flag the self inclusion flag
100        */
101        public void setSelf( boolean flag )
102        {
103            m_self = flag;
104        }
105        
106       /**
107        * Create and add a new include.
108        * @return the include
109        */
110        public Include createInclude()
111        {
112            Include include = new Include();
113            m_includes.add( include );
114            return include;
115        }
116    
117       /**
118        * The target directory to copy cached based path elements to.
119        * @param todir the destination directory
120        */
121        public void setTodir( File todir )
122        {
123            m_todir = todir;
124        }
125    
126       /**
127        * Execute the task.
128        */
129        public void execute()
130        {
131            if( null == m_todir )
132            {
133                File target =  getContext().getTargetDirectory();
134                m_todir = new File( target, "replicate" );
135            }
136            
137            File destination = m_todir;
138            Layout layout = resolveLayout();
139            ArrayList list = new ArrayList();
140            String ref = getRef();
141            if( null != ref )
142            {
143                Resource resource = getResource( ref );
144                aggregate( list, resource, m_self );
145            }
146            
147            //
148            // add nested includes
149            //
150            
151            Include[] includes = (Include[])  m_includes.toArray( new Include[0] );
152            for( int i=0; i<includes.length; i++ )
153            {
154                Include include = includes[i];
155                String includeRef = include.getRef();
156                Resource resource = getResource( includeRef );
157                aggregate( list, resource, true );
158            }
159            
160            //
161            // get sorted list of resources
162            //
163            
164            Resource[] resources = (Resource[]) list.toArray( new Resource[0] );
165            Resource[] selection = getContext().getLibrary().sort( resources );
166            File cache = (File) getProject().getReference( "dpml.cache" );
167            for( int i=0; i<selection.length; i++ )
168            {
169                Resource resource = selection[i];
170                copy( cache, destination, resource, layout );
171            }
172        }
173        
174       /**
175        * Copy the artifacts produced by the Resource from the cache to 
176        * the destination using a suppplied target layout.
177        */
178        private void copy( File cache, File destination, Resource resource, Layout layout )
179        {
180            Type[] types = resource.getTypes();
181            for( int j=0; j<types.length; j++ )
182            {
183                Type type = types[j];
184                String id = type.getID();
185                
186                Artifact artifact = resource.getArtifact( id );
187                copyArtifact( artifact, cache, destination, layout );
188                if( null != type.getVersion() )
189                {
190                    Artifact link = resource.getLinkArtifact( id );
191                    copyArtifact( link, cache, destination, layout );
192                }
193            }
194        }
195        
196        private void copyArtifact( Artifact artifact, File cache, File destination, Layout layout )
197        {
198            String sourcePath = Transit.getInstance().getCacheLayout().resolvePath( artifact );
199            File source = new File( cache, sourcePath );
200            if( !source.exists() )
201            {
202                final String error = 
203                  "Cached resource [" 
204                  + source 
205                  + "] does not exist.";
206                log( error );
207            }
208            else
209            {
210                String destPath = layout.resolvePath( artifact );
211                File dest = new File( destination, destPath );
212                copyFile( source, dest );
213                File md5 = new File( cache, sourcePath + ".md5" );
214                if( md5.exists() )
215                {
216                    copyFile( md5, new File( destination, destPath + ".md5" ) );
217                }
218                File asc = new File( cache, sourcePath + ".asc" );
219                if( asc.exists() )
220                {
221                    copyFile( asc, new File( destination, destPath + ".asc" ) );
222                }
223            }
224        }
225        
226        private void copyFile( File source, File dest )
227        {
228            dest.getParentFile().mkdir();
229            final Copy copy = (Copy) getProject().createTask( "copy" );
230            copy.setFile( source );
231            copy.setTofile( dest );
232            copy.setPreserveLastModified( true );
233            copy.setVerbose( m_verbose );
234            copy.init();
235            copy.execute();
236        }
237        
238        private Layout resolveLayout() 
239        {
240            if( null == m_layout )
241            {
242                return Transit.getInstance().getCacheLayout();
243            }
244            else
245            {
246                try
247                {
248                    return Transit.getInstance().getLayout( m_layout );
249                }
250                catch( UnknownKeyException e )
251                {
252                    final String error = 
253                      "Target layout id [" 
254                      + m_layout
255                      + "] is unknown.";
256                    throw new BuildException( error, e, getLocation() );
257                }
258                catch( Exception e )
259                {
260                    final String error = 
261                      "Unexpected error while resolving layout: " + m_layout;
262                    throw new BuildException( error, e, getLocation() );
263                }
264            }
265        }
266        
267        private void aggregate( List list, Resource resource, boolean self )
268        {
269            Resource[] resources = resource.getAggregatedProviders( Scope.RUNTIME, true, false );
270            for( int i=0; i<resources.length; i++ )
271            {
272                Resource r = resources[i];
273                if( !list.contains( r ) )
274                {
275                    list.add( r );
276                }
277            }
278            if( self )
279            {
280                if( !list.contains( resource ) )
281                {
282                    list.add( resource );
283                }
284            }
285        }
286        
287        private Resource[] getPathResources( Resource resource )
288        {
289            Resource[] resources = resource.getAggregatedProviders( Scope.RUNTIME, true, false );
290            if( m_self )
291            {
292                Resource[] result = new Resource[ resources.length + 1 ];
293                System.arraycopy( resources, 0, result, 0, resources.length );
294                result[ resources.length ] = resource;
295                return result;
296            }
297            else
298            {
299                return resources;
300            }
301        }
302        
303        
304        private String getRef()
305        {
306            if( null != m_ref )
307            {
308                return m_ref;
309            }
310            else if( null != m_key )
311            {
312                return getResource().getParent().getResourcePath() + "/" + m_key;
313            }
314            else
315            {
316                return getResource().getResourcePath();
317            }
318        }
319        
320        private Resource getResource( String ref )
321        {
322            try
323            {
324                return getContext().getLibrary().getResource( ref );
325            }
326            catch( ResourceNotFoundException e )
327            {
328                final String error = 
329                  "Feature reference ["
330                  + ref
331                  + "] in the project [" 
332                  + getResource()
333                  + "] is unknown.";
334                throw new BuildException( error, e );
335            }
336        }
337        
338       /**
339        * Declaration of an include.
340        */
341        public class Include
342        {
343            private String m_includekey;
344            private String m_includeRef;
345            
346            /**
347            * Set the key of the target project or resource.
348            *
349            * @param key the resource key
350            */
351            public void setKey( final String key )
352            {
353                m_includekey = key;
354            }
355    
356            /**
357            * Set the ref of the target project or resource.
358            *
359            * @param ref the resource reference
360            */
361            public void setRef( final String ref )
362            {
363                m_includeRef = ref;
364            }
365            
366            private String getRef()
367            {
368                if( null != m_includeRef )
369                {
370                    return m_includeRef;
371                }
372                else if( null != m_includekey )
373                {
374                    return getResource().getParent().getResourcePath() + "/" + m_includekey;
375                }
376                else
377                {
378                    final String error = 
379                      "Missing 'ref' or 'key' attribute.";
380                    throw new BuildException( error, getLocation() );
381                }
382            }
383        }
384    }