Passed
Branch master (d51fdb)
by Joao
05:45 queued 02:33
created

AnyDataset::fixUTF8()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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