1<?php
2/*
3 *  $Id: e581b40ff4e3eac5f62a32b48b4a22285cbc51c1 $
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
22require_once 'phing/Task.php';
23include_once 'phing/util/DirectoryScanner.php';
24include_once 'phing/types/FileSet.php';
25include_once 'phing/util/FileUtils.php';
26include_once 'phing/system/io/PhingFile.php';
27include_once 'phing/system/io/IOException.php';
28
29/**
30 * Touch a file and/or fileset(s); corresponds to the Unix touch command.
31 *
32 * If the file to touch doesn't exist, an empty one is created.
33 *
34 * @version $Id$
35 * @package phing.tasks.system
36 */
37class TouchTask extends Task {
38
39    private $file;
40    private $millis    = -1;
41    private $dateTime;
42    private $filesets = array();
43    private $fileUtils;
44
45    function __construct() {
46        $this->fileUtils = new FileUtils();
47    }
48
49    /**
50     * Sets a single source file to touch.  If the file does not exist
51     * an empty file will be created.
52     */
53    function setFile(PhingFile $file) {
54        $this->file = $file;
55    }
56
57    /**
58     * the new modification time of the file
59     * in milliseconds since midnight Jan 1 1970.
60     * Optional, default=now
61     */
62    function setMillis($millis) {
63        $this->millis = (int) $millis;
64    }
65
66    /**
67     * the new modification time of the file
68     * in the format MM/DD/YYYY HH:MM AM or PM;
69     * Optional, default=now
70     */
71    function setDatetime($dateTime) {
72        $this->dateTime = (string) $dateTime;
73    }
74
75    /**
76     * Nested creator, adds a set of files (nested fileset attribute).
77     * @return FileSet
78     */
79    function createFileSet() {
80        $num = array_push($this->filesets, new FileSet());
81        return $this->filesets[$num-1];
82    }
83
84    /**
85     * Execute the touch operation.
86     */
87    function main() {
88        $savedMillis = $this->millis;
89
90        if ($this->file === null && count($this->filesets) === 0) {
91            throw new BuildException("Specify at least one source - a file or a fileset.");
92        }
93
94        if ($this->file !== null && $this->file->exists() && $this->file->isDirectory()) {
95            throw new BuildException("Use a fileset to touch directories.");
96        }
97
98        try { // try to touch file
99            if ($this->dateTime !== null) {
100                $this->setMillis(strtotime($this->dateTime));
101                if ($this->millis < 0) {
102                    throw new BuildException("Date of {$this->dateTime} results in negative milliseconds value relative to epoch (January 1, 1970, 00:00:00 GMT).");
103                }
104            }
105            $this->_touch();
106        } catch (Exception $ex) {
107            throw new BuildException("Error touch()ing file", $ex, $this->location);
108        }
109
110        $this->millis = $savedMillis;
111
112    }
113
114    /**
115     * Does the actual work.
116     */
117    function _touch() {
118        if ($this->file !== null) {
119            if (!$this->file->exists()) {
120                $this->log("Creating " . $this->file->__toString(), Project::MSG_INFO);
121                try { // try to create file
122                    $this->file->createNewFile();
123                } catch(IOException  $ioe) {
124                    throw new BuildException("Error creating new file " . $this->file->__toString(), $ioe, $this->location);
125                }
126            }
127        }
128
129        $resetMillis = false;
130        if ($this->millis < 0) {
131            $resetMillis = true;
132            $this->millis = Phing::currentTimeMillis();
133        }
134
135        if ($this->file !== null) {
136            $this->touchFile($this->file);
137        }
138
139        // deal with the filesets
140        foreach($this->filesets as $fs) {
141
142            $ds = $fs->getDirectoryScanner($this->getProject());
143            $fromDir = $fs->getDir($this->getProject());
144
145            $srcFiles = $ds->getIncludedFiles();
146            $srcDirs = $ds->getIncludedDirectories();
147
148            for ($j=0,$_j=count($srcFiles); $j < $_j; $j++) {
149                $this->touchFile(new PhingFile($fromDir, (string) $srcFiles[$j]));
150            }
151
152            for ($j=0,$_j=count($srcDirs); $j < $_j ; $j++) {
153                $this->touchFile(new PhingFile($fromDir, (string) $srcDirs[$j]));
154            }
155        }
156
157        if ($resetMillis) {
158            $this->millis = -1;
159        }
160    }
161
162    private function touchFile($file) {
163        if ( !$file->canWrite() ) {
164            throw new BuildException("Can not change modification date of read-only file " . $file->__toString());
165        }
166        $file->setLastModified($this->millis);
167    }
168
169}
170
171