Passed
Push — master ( 30bcbc...eba4fe )
by Siad
06:53
created

MoveTask   A

Complexity

Total Complexity 40

Size/Duplication

Total Lines 200
Duplicated Lines 0 %

Test Coverage

Coverage 66%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 103
dl 0
loc 200
ccs 66
cts 100
cp 0.66
rs 9.2
c 1
b 0
f 0
wmc 40

4 Methods

Rating   Name   Duplication   Size   Complexity  
A deleteDir() 0 21 5
A okToDelete() 0 20 5
B validateAttributes() 0 22 9
D doWork() 0 101 21

How to fix   Complexity   

Complex Class

Complex classes like MoveTask often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use MoveTask, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the LGPL. For more information please see
17
 * <http://phing.info>.
18
 */
19
20
/**
21
 * Moves a file or directory to a new file or directory.
22
 *
23
 * By default, the destination file is overwritten if it
24
 * already exists.  When overwrite is turned off, then files
25
 * are only moved if the source file is newer than the
26
 * destination file, or when the destination file does not
27
 * exist.
28
 *
29
 * Source files and directories are only deleted when the file or
30
 * directory has been copied to the destination successfully.
31
 *
32
 * @package phing.tasks.system
33
 */
34
class MoveTask extends CopyTask
35
{
36
    /**
37
     * Validates attributes coming in from XML
38
     *
39
     * @return void
40
     *
41
     * @throws BuildException
42
     */
43 5
    protected function validateAttributes()
44
    {
45 5
        if ($this->file !== null && $this->file->isDirectory()) {
46 1
            if (($this->destFile !== null && $this->destDir !== null)
47 1
                || ($this->destFile === null && $this->destDir === null)
48
            ) {
49
                throw new BuildException("One and only one of tofile and todir must be set.");
50
            }
51
52 1
            if ($this->destFile === null) {
53
                $this->destFile = new PhingFile($this->destDir, $this->file->getName());
54
            }
55
56 1
            if ($this->destDir === null) {
57 1
                $this->destDir = $this->destFile->getParentFile();
58
            }
59
60 1
            $this->completeDirMap[$this->file->getAbsolutePath()] = $this->destFile->getAbsolutePath();
61
62 1
            $this->file = null;
63
        } else {
64 4
            parent::validateAttributes();
65
        }
66 5
    }
67
68 5
    protected function doWork()
69
    {
70 5
        if (count($this->completeDirMap) > 0) {
71 1
            foreach ($this->completeDirMap as $from => $to) {
72 1
                $f = new PhingFile($from);
73 1
                $d = new PhingFile($to);
74
75
                try { // try to rename
76 1
                    $this->log("Attempting to rename $from to $to", $this->verbosity);
77 1
                    if (!empty($this->filterChains)) {
78
                        $this->fileUtils->copyFile(
79
                            $f,
80
                            $d,
81
                            $this->getProject(),
82
                            $this->overwrite,
83
                            $this->preserveLMT,
84
                            $this->filterChains,
85
                            $this->mode
86
                        );
87
                        $f->delete(true);
88
                    } else {
89 1
                        $this->fileUtils->renameFile($f, $d, $this->overwrite);
90
                    }
91
                } catch (IOException $ioe) {
92
                    $this->logError("Failed to rename $from to $to: " . $ioe->getMessage());
93
                }
94
            }
95
        }
96
97 5
        $copyMapSize = count($this->fileCopyMap);
98 5
        if ($copyMapSize > 0) {
99
            // files to move
100 3
            $this->log("Moving $copyMapSize files to " . $this->destDir->getAbsolutePath());
101
102 3
            foreach ($this->fileCopyMap as $from => $to) {
103 3
                if ($from == $to) {
104
                    $this->log("Skipping self-move of $from", $this->verbosity);
105
                    continue;
106
                }
107
108 3
                $f = new PhingFile($from);
109 3
                $d = new PhingFile($to);
110
111
                try { // try to move
112 3
                    $this->log("Moving $from to $to", $this->verbosity);
113
114 3
                    $this->fileUtils->copyFile(
115 3
                        $f,
116 3
                        $d,
117 3
                        $this->getProject(),
118 3
                        $this->overwrite,
119 3
                        $this->preserveLMT,
120 3
                        $this->filterChains,
121 3
                        $this->mode
122
                    );
123
124 3
                    $f->delete();
125
                } catch (IOException $ioe) {
126
                    $this->logError("Failed to move $from to $to: " . $ioe->getMessage(), $this->getLocation());
127
                }
128
            } // foreach fileCopyMap
129
        } // if copyMapSize
130
131
        // handle empty dirs if appropriate
132 5
        if ($this->includeEmpty) {
133 5
            $count = 0;
134 5
            foreach ($this->dirCopyMap as $srcDir => $destDir) {
135
                $d = new PhingFile((string) $destDir);
136
                if (!$d->exists()) {
137
                    if (!$d->mkdirs()) {
138
                        $this->logError("Unable to create directory " . $d->getAbsolutePath());
139
                    } else {
140
                        $count++;
141
                    }
142
                }
143
            }
144 5
            if ($count > 0) {
145
                $this->log(
146
                    "moved $count empty director" . ($count == 1 ? "y" : "ies") . " to " . $this->destDir->getAbsolutePath(
147
                    )
148
                );
149
            }
150
        }
151
152 5
        if (count($this->filesets) > 0) {
153
            // process filesets
154 1
            foreach ($this->filesets as $fs) {
155 1
                $dir = $fs->getDir($this->project);
156 1
                if ($this->okToDelete($dir)) {
157 1
                    $this->deleteDir($dir);
158
                }
159
            }
160
        }
161
162 5
        $dirsets = $this->getDirSets();
163 5
        if (count($dirsets) > 0) {
164
            // process dirsets
165
            foreach ($dirsets as $ds) {
166
                $dir = $ds->getDir($this->project);
167
                if ($this->okToDelete($dir)) {
168
                    $this->deleteDir($dir);
169
                }
170
            }
171
        }
172 5
    }
173
174
    /**
175
     * Its only ok to delete a dir tree if there are no files in it.
176
     *
177
     * @param $d
178
     *
179
     * @throws IOException
180
     *
181
     * @return bool
182
     */
183 1
    private function okToDelete(PhingFile $d)
184
    {
185 1
        $list = $d->listDir();
186 1
        if ($list === null) {
187
            return false; // maybe io error?
188
        }
189
190 1
        foreach ($list as $s) {
191 1
            $f = new PhingFile($d, $s);
192 1
            if ($f->isDirectory()) {
193 1
                if (!$this->okToDelete($f)) {
194 1
                    return false;
195
                }
196
            } else {
197
                // found a file
198
                return false;
199
            }
200
        }
201
202 1
        return true;
203
    }
204
205
    /**
206
     * Go and delete the directory tree.
207
     *
208
     * @param $d
209
     *
210
     * @throws BuildException
211
     * @throws IOException
212
     */
213 1
    private function deleteDir(PhingFile $d)
214
    {
215 1
        $list = $d->listDir();
216 1
        if ($list === null) {
217
            return; // on an io error list() can return null
218
        }
219
220 1
        foreach ($list as $fname) {
221 1
            $f = new PhingFile($d, $fname);
222 1
            if ($f->isDirectory()) {
223 1
                $this->deleteDir($f);
224
            } else {
225
                throw new BuildException("UNEXPECTED ERROR - The file " . $f->getAbsolutePath() . " should not exist!");
226
            }
227
        }
228
229 1
        $this->log("Deleting directory " . $d->getPath(), $this->verbosity);
230
        try {
231 1
            $d->delete();
232
        } catch (Exception $e) {
233
            $this->logError("Unable to delete directory " . $d->__toString() . ": " . $e->getMessage());
234
        }
235 1
    }
236
}
237