Ticket #23: ConcatTask.php

File ConcatTask.php, 10.9 kB (added by anonymous, 3 years ago)
Line 
1 <?php
2 /*
3  *  $Id: CopyTask.php,v 1.16 2005/10/05 20:23:22 hlellelid Exp $
4  *
5  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
6  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
7  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
8  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
9  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
10  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
11  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
12  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
13  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
14  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
15  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16  *
17  * This software consists of voluntary contributions made by many individuals
18  * and is licensed under the LGPL. For more information please see
19  * <http://phing.info>.
20  */
21  
22 require_once 'phing/Task.php';
23 include_once 'phing/system/io/PhingFile.php';
24 include_once 'phing/util/FileUtils.php';
25 include_once 'phing/util/SourceFileScanner.php';
26 include_once 'phing/mappers/IdentityMapper.php';
27 include_once 'phing/mappers/FlattenMapper.php';
28
29 /**
30  * A phing copy task.  Copies a file or directory to a new file
31  * or directory.  Files are only copied if the source file is newer
32  * than the destination file, or when the destination file does not
33  * exist. It is possible to explictly overwrite existing files.
34  *
35  * @author   Andreas Aderhold, andi@binarycloud.com
36  * @version  $Revision: 1.16 $ $Date: 2005/10/05 20:23:22 $
37  * @package  phing.tasks.system
38  */
39 class ConcatTask extends Task {
40     
41     protected $destfile      = null;   // the destiantion file (from xml attribute)
42     protected $force         = true;   // destiantion file should be written even if it is newser than all source files.
43     protected $append         = false// append to destfile   
44     protected $mapperElement = null;
45
46     protected $fileCopyMap   = array(); // asoc array containing mapped file names
47     protected $dirCopyMap    = array(); // asoc array containing mapped file names
48     protected $fileUtils     = null;    // a instance of fileutils
49     protected $filesets      = array(); // all fileset objects assigned to this task
50     protected $filterChains  = array(); // all filterchains objects assigned to this task
51
52     protected $verbosity     = PROJECT_MSG_VERBOSE;
53
54     /**
55      * Sets up this object internal stuff. i.e. the Fileutils instance
56      *
57      * @return object   The CopyTask instnace
58      * @access public
59      */
60     function __construct() {
61         $this->fileUtils = new FileUtils();
62     }
63
64     /**
65      * Set the force flag. IntrospectionHelper takes care of
66      * booleans in set* methods so we can assume that the right
67      * value (boolean primitive) is coming in here.
68      *
69      * @param  boolean  Overwrite the destination file(s) if it/they already exist
70      * @return void
71      * @access public
72      */
73     function setForce($bool) {
74         $this->force = (boolean) $bool;
75     }
76
77     /**
78      * Set the append flag. IntrospectionHelper takes care of
79      * booleans in set* methods so we can assume that the right
80      * value (boolean primitive) is coming in here.
81      *
82      * @param  boolean  Overwrite the destination file(s) if it/they already exist
83      * @return void
84      * @access public
85      */
86     function setAppend($bool) {
87         $this->append = (boolean) $bool;
88     }
89
90
91     /**
92      * Used to force listing of all names of copied files.
93      * @param boolean $verbosity
94      */
95     function setVerbose($verbosity) {
96         if ($verbosity) {
97             $this->verbosity = PROJECT_MSG_INFO;
98         } else {
99             $this->verbosity = PROJECT_MSG_VERBOSE;
100         }
101     }
102     
103     /**
104      * Set the destfile. We have to manually take care of the
105      * type that is coming due to limited type support in php
106      * in and convert it manually if neccessary.
107      *
108      * @param  string/object  The source file. Either a string or an PhingFile object
109      * @return void
110      * @access public
111      */
112     function setDestfile(PhingFile $file) {       
113         $this->destfile = $file;
114     }
115
116     /**
117      * Nested creator, creates a FileSet for this task
118      *
119      * @access  public
120      * @return  object  The created fileset object
121      */
122     function createFileSet() {
123         $num = array_push($this->filesets, new FileSet());
124         return $this->filesets[$num-1];
125     }
126
127     /**
128      * Creates a filterchain
129      *
130      * @access public
131      * @return  object  The created filterchain object
132      */
133     function createFilterChain() {
134         $num = array_push($this->filterChains, new FilterChain($this->project));
135         return $this->filterChains[$num-1];
136     }
137
138     /**
139      * Nested creator, creates one Mapper for this task
140      *
141      * @access  public
142      * @return  object  The created Mapper type object
143      * @throws  BuildException
144      */
145     function createMapper() {
146         if ($this->mapperElement !== null) {
147             throw new BuildException("Cannot define more than one mapper", $this->location);
148         }
149         $this->mapperElement = new Mapper($this->project);
150         return $this->mapperElement;
151     }
152
153     /**
154      * The main entry point where everything gets in motion.
155      *
156      * @access  public
157      * @return  true on success
158      * @throws  BuildException
159      */
160     function main() {
161     
162         $this->validateAttributes();
163
164         if ($this->destfile === null) {
165             $this->destfile = new PhingFile($this->destDir, (string) $this->file->getName());
166         }
167         if ($this->force === true || ($this->file->lastModified() > $this->destFile->lastModified())) {
168             $this->fileCopyMap[$this->destfile->getAbsolutePath()] = $this->destfile->getAbsolutePath();
169         } else {
170             $this->log($this->destfile->getName()." omitted, is up to date");
171         }
172
173         $project = $this->getProject();
174
175         // process filesets
176         foreach($this->filesets as $fs) {
177             $ds = $fs->getDirectoryScanner($project);
178             $fromDir  = $fs->getDir($project);
179             $srcFiles = $ds->getIncludedFiles();
180             $srcDirs  = $ds->getIncludedDirectories();
181             $this->_scan($fromDir, $this->destDir, $srcFiles, $srcDirs);
182         }
183
184         // go and concat the stuff
185         $this->doWork();
186
187         if ($this->destfile !== null) {
188             $this->destDir = null;
189         }
190     }
191
192     /**
193      * Validates attributes coming in from XML
194      *
195      * @access  private
196      * @return  void
197      * @throws  BuildException
198      */
199     private function validateAttributes() {
200     
201         if (count($this->filesets) === 0) {
202             throw new BuildException("CopyTask. Specify at least one source - a file or a fileset.");
203         }
204
205         if ($this->destfile === null) {
206             throw new BuildException("destfile must be set.");
207         }
208
209         if ($this->destfile !== null) {
210             $this->destDir = new PhingFile($this->destfile->getParent());
211         }
212     }
213
214     /**
215      * Compares source files to destination files to see if they
216      * should be copied.
217      *
218      * @access  private
219      * @return  void
220      */
221     private function _scan(&$fromDir, &$toDir, &$files, &$dirs) {
222         /* mappers should be generic, so we get the mappers here and
223         pass them on to builMap. This method is not redundan like it seems */
224         $mapper = null;
225         if ($this->mapperElement !== null) {
226             $mapper = $this->mapperElement->getImplementation();
227         } else if ($this->flatten) {
228             $mapper = new FlattenMapper();
229         } else {
230             $mapper = new IdentityMapper();
231         }
232         $this->buildMap($fromDir, $toDir, $files, $mapper, $this->fileCopyMap);
233     }
234
235     /**
236      * Builds a map of filenames (from->to) that should be copied
237      *
238      * @access  private
239      * @return  void
240      */
241     private function buildMap(&$fromDir, &$toDir, &$names, &$mapper, &$map) {
242         $toCopy = null;
243         if ($this->overwrite) {
244             $v = array();
245             foreach($names as $name) {
246                 $result = $mapper->main($name);
247                 if ($result !== null) {
248                     $v[] = $name;
249                 }
250             }
251             $toCopy = $v;
252         } else {
253             $ds = new SourceFileScanner($this);
254             $toCopy = $ds->restrict($names, $fromDir, $toDir, $mapper);
255         }
256
257         for ($i=0,$_i=count($toCopy); $i < $_i; $i++) {
258             $src  = new PhingFile($fromDir, $toCopy[$i]);
259             $mapped = $mapper->main($toCopy[$i]);
260             $dest = new PhingFile($toDir, $mapped[0]);
261             $map[$src->getAbsolutePath()] = $dest->getAbsolutePath();
262         }
263     }
264
265
266     /**
267      * Actually copies the files
268      *
269      * @access  private
270      * @return  void
271      * @throws  BuildException
272      */
273     private function doWork() {
274         
275         // These "slots" allow filters to retrieve information about the currently-being-process files       
276         $fromSlot = $this->getRegisterSlot("currentFromFile");
277         $fromBasenameSlot = $this->getRegisterSlot("currentFromFile.basename");   
278
279         $toSlot = $this->getRegisterSlot("currentToFile");
280         $toBasenameSlot = $this->getRegisterSlot("currentToFile.basename");   
281         
282         $mapSize = count($this->fileCopyMap);
283         $total = $mapSize;
284         if ($mapSize > 0) {
285             $this->log("Concatinating ".$mapSize." file".(($mapSize) === 1 ? '' : 's')." to ". $this->destfile->getAbsolutePath());
286             // walks the map and actually copies the files
287             $count=0;
288             $_toFile = $this->destfile->getAbsolutePath();
289             try {
290                 $toFile = new PhingFile($_toFile);
291                 
292               $toSlot->setValue($toFile->getPath());
293               $toBasenameSlot->setValue($toFile->getName());
294             
295               if ($toFile->exists() && $toFile->isFile()) {
296                  $toFile->delete();
297               }
298             
299             } catch (IOExeption $ioe) {
300                     $this->log("Failed to append to " . $to . ": " . $ioe->getMessage(), PROJECT_MSG_ERR);             
301             }
302             foreach($this->fileCopyMap as $from => $to) {
303                 if ($from === $_toFile) {
304                     $this->log("Skipping self-copy of " . $from, $this->verbosity);
305                     $total--;
306                     continue;
307                 }
308                 $this->log("From ".$from." to ".$_toFile, $this->verbosity);
309                 try { // try to append file
310                 
311                     $fromFile = new PhingFile($from);
312                     
313                     $fromSlot->setValue($fromFile->getPath());
314                     $fromBasenameSlot->setValue($fromFile->getName());
315                     
316                     $this->fileUtils->appendFile($fromFile, $toFile, $this->force, $this->filterChains, $this->getProject());
317             
318                     $count++;
319                 } catch (IOException $ioe) {
320                     $this->log("Failed to append " . $from . " to " . $to . ": " . $ioe->getMessage(), PROJECT_MSG_ERR);
321                 }
322             }
323         }
324
325     }
326
327 }
328