Passed
Push — master ( 6801ab...73be85 )
by Joao
01:01 queued 11s
created

AnyDataset::getFilename()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 2
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 2
1
<?php
2
3
namespace ByJG\AnyDataset\Core;
4
5
use ByJG\AnyDataset\Core\Exception\DatabaseException;
6
use ByJG\Util\XmlUtil;
7
use InvalidArgumentException;
8
9
/**
10
 * AnyDataset is a simple way to store data using only XML file.
11
 * Your structure is hierarquical and each "row" contains "fields" but these structure can vary for each row.
12
 * Anydataset files have extension ".anydata.xml" and have many classes to put and get data into anydataset xml file.
13
 * Anydataset class just read and write files. To search elements you need use AnyIterator
14
 * and IteratorFilter. Each row have a class Row.
15
16
 * XML Structure
17
 * <code>
18
 * <anydataset>
19
 *    <row>
20
 *        <field name="fieldname1">value of fieldname 1</field>
21
 *        <field name="fieldname2">value of fieldname 2</field>
22
 *        <field name="fieldname3">value of fieldname 3</field>
23
 *    </row>
24
 *    <row>
25
 *        <field name="fieldname1">value of fieldname 1</field>
26
 *        <field name="fieldname4">value of fieldname 4</field>
27
 *    </row>
28
 * </anydataset>
29
 * </code>
30
31
 * How to use:
32
 * <code>
33
 * $any = new AnyDataset();
34
 * </code>
35
36
 *
37
*@see Row
38
 * @see AnyIterator
39
 * @see IteratorFilter
40
41
 */
42
class AnyDataset
43
{
44
45
    /**
46
     * Internal structure represent the current Row
47
     *
48
     * @var Row[]
49
     */
50
    private $collection;
51
52
    /**
53
     * Current node anydataset works
54
     * @var int
55
     */
56
    private $currentRow;
57
58
    /**
59
     * Path to anydataset file
60
     * @var string
61
     */
62
    private $filename;
63
64
    /**
65
     * @param string $filename
66
     * @throws \ByJG\Serializer\Exception\InvalidArgumentException
67
     * @throws \ByJG\Util\Exception\XmlUtilException
68
     */
69 14
    public function __construct($filename = null)
70
    {
71 14
        $this->collection = array();
72 14
        $this->currentRow = -1;
73
74 14
        $this->filename = null;
75
        $this->defineSavePath($filename, function () {
76 14
            if (!is_null($this->filename)) {
77 7
                $this->createFrom($this->filename);
78
            }
79 14
        });
80 14
    }
81
82
    public function getFilename()
83
    {
84
        return $this->filename;
85
    }
86
87 14
    private function defineSavePath($file, $closure)
88
    {
89 14
        if (!is_null($file)) {
90 7
            if (!is_string($file)) {
91
                throw new \InvalidArgumentException('I expected a string as a file name');
92
            }
93
94 7
            $ext = pathinfo($file, PATHINFO_EXTENSION);
95 7
            if (empty($ext) && substr($file, 0, 6) !== "php://") {
96 7
                $file .= '.anydata.xml';
97
            }
98 7
            $this->filename = $file;
99
        }
100
101 14
        $closure();
102 14
    }
103
104
    /**
105
     * Private method used to read and populate anydataset class from specified file
106
     *
107
     * @param string $filepath Path and Filename to be read
108
     * @throws \ByJG\Serializer\Exception\InvalidArgumentException
109
     * @throws \ByJG\Util\Exception\XmlUtilException
110
     */
111 7
    private function createFrom($filepath)
112
    {
113 7
        if (file_exists($filepath)) {
114 7
            $anyDataSet = XmlUtil::createXmlDocumentFromFile($filepath);
115 7
            $this->collection = array();
116
117 7
            $rows = $anyDataSet->getElementsByTagName("row");
118 7
            foreach ($rows as $row) {
119 7
                $sr = new Row();
120 7
                $fields = $row->getElementsByTagName("field");
121 7
                foreach ($fields as $field) {
122 7
                    $attr = $field->attributes->getNamedItem("name");
123 7
                    if (is_null($attr)) {
124
                        throw new \InvalidArgumentException('Malformed anydataset file ' . basename($filepath));
125
                    }
126
127 7
                    $sr->addField($attr->nodeValue, $field->nodeValue);
128
                }
129 7
                $sr->acceptChanges();
130 7
                $this->collection[] = $sr;
131
            }
132 7
            $this->currentRow = count($this->collection) - 1;
133
        }
134 7
    }
135
136
    /**
137
     * Returns the AnyDataset XML representative structure.
138
     *
139
     * @return string XML String
140
     * @throws \ByJG\Util\Exception\XmlUtilException
141
     */
142 1
    public function xml()
143
    {
144 1
        return $this->getAsDom()->saveXML();
145
    }
146
147
    /**
148
     * Returns the AnyDataset XmlDocument representive object
149
     *
150
     * @return \DOMDocument XmlDocument object
151
     * @throws \ByJG\Util\Exception\XmlUtilException
152
     */
153 3
    public function getAsDom()
154
    {
155 3
        $anyDataSet = XmlUtil::createXmlDocumentFromStr("<anydataset></anydataset>");
156 3
        $nodeRoot = $anyDataSet->getElementsByTagName("anydataset")->item(0);
157 3
        foreach ($this->collection as $sr) {
158 3
            $row = $sr->getAsDom();
159 3
            $nodeRow = $row->getElementsByTagName("row")->item(0);
160 3
            $newRow = XmlUtil::createChild($nodeRoot, "row");
161 3
            XmlUtil::addNodeFromNode($newRow, $nodeRow);
162
        }
163
164 3
        return $anyDataSet;
165
    }
166
167
    /**
168
     * @param string $filename
169
     * @throws DatabaseException
170
     * @throws \ByJG\Util\Exception\XmlUtilException
171
     */
172 2
    public function save($filename = null)
173
    {
174
        $this->defineSavePath($filename, function () {
175 2
            if (is_null($this->filename)) {
176
                throw new DatabaseException("No such file path to save anydataset");
177
            }
178
179 2
            XmlUtil::saveXmlDocument($this->getAsDom(), $this->filename);
180 2
        });
181 2
    }
182
183
    /**
184
     * Append one row to AnyDataset.
185
     *
186
     * @param Row $singleRow
187
     * @return void
188
     * @throws \ByJG\Serializer\Exception\InvalidArgumentException
189
     */
190 10
    public function appendRow($singleRow = null)
191
    {
192 10
        if (!is_null($singleRow)) {
193 4
            if ($singleRow instanceof Row) {
194 3
                $this->collection[] = $singleRow;
195 3
                $singleRow->acceptChanges();
196 2
            } elseif (is_array($singleRow)) {
197 2
                $this->collection[] = new Row($singleRow);
198
            } else {
199 4
                throw new InvalidArgumentException("You must pass an array or a Row object");
200
            }
201
        } else {
202 6
            $singleRow = new Row();
203 6
            $this->collection[] = $singleRow;
204 6
            $singleRow->acceptChanges();
205
        }
206 10
        $this->currentRow = count($this->collection) - 1;
207 10
    }
208
209
    /**
210
     * Enter description here...
211
     *
212
     * @param GenericIterator $iterator
213
     * @throws \ByJG\Serializer\Exception\InvalidArgumentException
214
     */
215 2
    public function import($iterator)
216
    {
217 2
        foreach ($iterator as $singleRow) {
218 2
            $this->appendRow($singleRow);
219
        }
220 2
    }
221
222
    /**
223
     * Insert one row before specified position.
224
     *
225
     * @param int $rowNumber
226
     * @param mixed $row
227
     * @throws \ByJG\Serializer\Exception\InvalidArgumentException
228
     */
229 1
    public function insertRowBefore($rowNumber, $row = null)
230
    {
231 1
        if ($rowNumber > count($this->collection)) {
232
            $this->appendRow($row);
233
        } else {
234 1
            $singleRow = $row;
235 1
            if (!($row instanceof Row)) {
236 1
                $singleRow = new Row($row);
237
            }
238 1
            array_splice($this->collection, $rowNumber, 0, '');
239 1
            $this->collection[$rowNumber] = $singleRow;
240
        }
241 1
    }
242
243
    /**
244
     *
245
     * @param mixed $row
246
     * @throws \ByJG\Serializer\Exception\InvalidArgumentException
247
     */
248 2
    public function removeRow($row = null)
249
    {
250 2
        if (is_null($row)) {
251
            $row = $this->currentRow;
252
        }
253 2
        if ($row instanceof Row) {
254
            $iPos = 0;
255
            foreach ($this->collection as $sr) {
256
                if ($sr->toArray() == $row->toArray()) {
257
                    $this->removeRow($iPos);
258
                    break;
259
                }
260
                $iPos++;
261
            }
262
            return;
263
        }
264
265 2
        if ($row == 0) {
266 1
            $this->collection = array_slice($this->collection, 1);
267
        } else {
268 1
            $this->collection = array_slice($this->collection, 0, $row) + array_slice($this->collection, $row);
269
        }
270 2
    }
271
272
    /**
273
     * Add a single string field to an existing row
274
     *
275
     * @param string $name - Field name
276
     * @param string $value - Field value
277
     * @return void
278
     * @throws \ByJG\Serializer\Exception\InvalidArgumentException
279
     */
280 5
    public function addField($name, $value)
281
    {
282 5
        if ($this->currentRow < 0) {
283 2
            $this->appendRow();
284
        }
285 5
        $this->collection[$this->currentRow]->addField($name, $value);
286 5
    }
287
288
    /**
289
     * Get an Iterator filtered by an IteratorFilter
290
     * @param IteratorFilter $itf
291
     * @return GenericIterator
292
     */
293 13
    public function getIterator(IteratorFilter $itf = null)
294
    {
295 13
        if (is_null($itf)) {
296 13
            return new AnyIterator($this->collection);
297
        }
298
299
        return new AnyIterator($itf->match($this->collection));
300
    }
301
302
    /**
303
     * @desc
304
     * @param IteratorFilter $itf
305
     * @param string $fieldName
306
     * @return array
307
     */
308 1
    public function getArray($itf, $fieldName)
309
    {
310 1
        $iterator = $this->getIterator($itf);
311 1
        $result = array();
312 1
        while ($iterator->hasNext()) {
313 1
            $singleRow = $iterator->moveNext();
314 1
            $result [] = $singleRow->get($fieldName);
315
        }
316 1
        return $result;
317
    }
318
319
    /**
320
     *
321
     * @param string $field
322
     * @return void
323
     */
324 1
    public function sort($field)
325
    {
326 1
        if (count($this->collection) == 0) {
327
            return;
328
        }
329
330 1
        $this->collection = $this->quickSortExec($this->collection, $field);
331
332 1
        return;
333
    }
334
335
    /**
336
     * @param Row[] $seq
337
     * @param $field
338
     * @return array
339
     */
340 1
    protected function quickSortExec($seq, $field)
341
    {
342 1
        if (!count($seq)) {
343 1
            return $seq;
344
        }
345
346 1
        $key = $seq[0];
347 1
        $left = $right = array();
348
349 1
        $cntSeq = count($seq);
350 1
        for ($i = 1; $i < $cntSeq; $i ++) {
351 1
            if ($seq[$i]->get($field) <= $key->get($field)) {
352 1
                $left[] = $seq[$i];
353
            } else {
354 1
                $right[] = $seq[$i];
355
            }
356
        }
357
358 1
        return array_merge(
359 1
            $this->quickSortExec($left, $field),
360 1
            [ $key ],
361 1
            $this->quickSortExec($right, $field)
362
        );
363
    }
364
}
365