Much easier (but not reliable) test.
[core.git] / inc / classes / main / stacker / file / class_BaseFileStack.php
1 <?php
2 /**
3  * A general file-based stack class
4  *
5  * @author              Roland Haeder <webmaster@ship-simu.org>
6  * @version             0.0.0
7  * @copyright   Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2013 Core Developer Team
8  * @license             GNU GPL 3.0 or any newer version
9  * @link                http://www.ship-simu.org
10  *
11  * This program is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23  */
24 class BaseFileStack extends BaseStacker {
25         /**
26          * Magic for this stack
27          */
28         const STACK_MAGIC = 'STACKv0.1';
29
30         /**
31          * Separator magic->count
32          */
33         const SEPARATOR_MAGIC_COUNT = 0x00;
34
35         /**
36          * Separator position->entries
37          */
38         const SEPARATOR_SEEK_POS_ENTRIES = 0xff;
39
40         /**
41          * Separator hash->name
42          */
43         const SEPARATOR_HASH_NAME = 0x05;
44
45         /**
46          * Length of count
47          */
48         const COUNT_LENGTH = 20;
49
50         /**
51          * Length of position
52          */
53         const COUNT_POSITION = 20;
54
55         /**
56          * Counter for total entries
57          */
58         private $totalEntries = 0;
59
60         /**
61          * Current seek position
62          */
63         private $seekPosition = 0;
64
65         /**
66          * Protected constructor
67          *
68          * @param       $className      Name of the class
69          * @return      void
70          */
71         protected function __construct ($className) {
72                 // Call parent constructor
73                 parent::__construct($className);
74         }
75
76         /**
77          * Getter for total entries
78          *
79          * @return      $totalEntries   Total entries in this stack
80          */
81         private function getCounter () {
82                 // Get it
83                 return $this->totalEntries;
84         }
85
86         /**
87          * Increment counter
88          *
89          * @return      void
90          */
91         private function incrementCounter () {
92                 // Get it
93                 $this->totalEntries++;
94         }
95
96         /**
97          * Getter for seek position
98          *
99          * @return      $seekPosition   Current seek position (stored here in object)
100          */
101         private function getSeekPosition () {
102                 // Get it
103                 return $this->seekPosition;
104         }
105
106         /**
107          * Setter for seek position
108          *
109          * @param       $seekPosition   Current seek position (stored here in object)
110          * @return      void
111          */
112         private function setSeekPosition ($seekPosition) {
113                 // And set it
114                 $this->seekPosition = $seekPosition;
115         }
116
117         /**
118          * Updates seekPosition attribute from file to avoid to much access on file.
119          *
120          * @return      void
121          */
122         private function updateSeekPosition () {
123                 // Get key (= seek position)
124                 $seekPosition = $this->getIteratorInstance()->key();
125
126                 // And set it here
127                 $this->setSeekPosition($seekPosition);
128         }
129
130         /**
131          * Checks whether the file header is initialized
132          *
133          * @return      $isInitialized  Whether the file header is initialized
134          */
135         private function isFileHeaderInitialized () {
136                 // Default is not initialized
137                 $isInitialized = FALSE;
138
139                 // Is the file initialized?
140                 if ($this->isFileInitialized()) {
141                         // Some bytes has been written, so rewind to start of it.
142                         $this->getIteratorInstance()->rewind();
143
144                         // Read file header
145                         $this->readFileHeader();
146                 } // END - if
147
148                 // Return result
149                 return $isInitialized;
150         }
151
152         /**
153          * Checks whether the file-based stack has been initialized
154          *
155          * @return      $isInitialized          Whether the file's size is zero
156          */
157         private function isFileInitialized () {
158                 // Get it from iterator which holds the pointer instance. If FALSE is returned
159                 $fileSize = $this->getIteratorInstance()->size();
160
161                 /*
162                  * The returned file size should not be FALSE or NULL as this means
163                  * that the pointer class does not work correctly.
164                  */
165                 assert(is_int($fileSize));
166
167                 // Is more than 0 returned?
168                 $isInitialized = ($fileSize > 0);
169
170                 // Return result
171                 return $isInitialized;
172         }
173
174         /**
175          * Creates the file-stack's header
176          *
177          * @return      void
178          */
179         private function createFileHeader () {
180                 // The file's header should not be initialized here
181                 assert(!$this->isFileHeaderInitialized());
182
183                 // Flush file header
184                 $this->flushFileHeader();
185         }
186
187         /**
188          * Flushes the file header
189          *
190          * @return      void
191          */
192         private function flushFileHeader () {
193                 // Put all informations together
194                 $header = sprintf('%s%s%s%s%s',
195                         // Magic
196                         self::STACK_MAGIC,
197
198                         // Separator magic<->count
199                         chr(self::SEPARATOR_MAGIC_COUNT),
200
201                         // Total entries (will be zero) and pad it to 20 chars
202                         str_pad($this->dec2hex($this->getCounter()), self::COUNT_LENGTH, '0', STR_PAD_LEFT),
203
204                         // Position (will be zero)
205                         str_pad($this->dec2hex(0, 2), self::COUNT_POSITION, '0', STR_PAD_LEFT),
206
207                         // Separator position<->entries
208                         chr(self::SEPARATOR_SEEK_POS_ENTRIES)
209                 );
210
211                 // Write it to disk (header is always at seek position 0)
212                 $this->getIteratorInstance()->writeAtPosition(0, $header);
213
214                 // Update seek position
215                 $this->updateSeekPosition();
216         }
217
218         /**
219          * Initializes this file-based stack.
220          *
221          * @param       $fileName       File name of this stack
222          * @return      void
223          */
224         protected function initFileStack ($fileName) {
225                 // Get a file i/o pointer instance
226                 $pointerInstance = ObjectFactory::createObjectByConfiguredName('file_raw_input_output_class', array($fileName));
227
228                 // Get iterator instance
229                 $iteratorInstance = ObjectFactory::createObjectByConfiguredName('file_io_iterator_class', array($pointerInstance));
230
231                 // Is the instance implementing the right interface?
232                 assert($iteratorInstance instanceof SeekableWritableFileIterator);
233
234                 // Set iterator here
235                 $this->setIteratorInstance($iteratorInstance);
236
237                 // Is the file's header initialized?
238                 if ($this->isFileHeaderInitialized()) {
239                         // Then load it
240                         $this->loadFileHeader();
241                 } else {
242                         // No, then create it (which may pre-allocate the stack)
243                         $this->createFileHeader();
244
245                         // And pre-allocate a bit
246                         $this->preAllocateFile();
247                 }
248         }
249
250         /**
251          * Initializes given stacker
252          *
253          * @param       $stackerName    Name of the stack
254          * @param       $forceReInit    Force re-initialization
255          * @return      void
256          * @throws      AlreadyInitializedStackerException      If the stack is already initialized
257          */
258         public function initStack ($stackerName, $forceReInit = FALSE) {
259                 // Is the stack already initialized?
260                 if (($forceReInit === FALSE) && ($this->isStackInitialized($stackerName))) {
261                         // Then throw the exception
262                         throw new AlreadyInitializedStackerException(array($this, $stackerName, $forceReInit), self::EXCEPTION_STACKER_ALREADY_INITIALIZED);
263                 } // END - if
264
265                 // Initialize the given stack
266                 $this->partialStub('stackerName=' . $stackerName . ',forceReInit=' . intval($forceReInit));
267         }
268
269         /**
270          * Checks whether the given stack is initialized (set in array $stackers)
271          *
272          * @param       $stackerName    Name of the stack
273          * @return      $isInitialized  Whether the stack is initialized
274          */
275         public function isStackInitialized ($stackerName) {
276                 // Is is there?
277                 $this->partialStub('stackerName=' . $stackerName);
278                 $isInitialized = TRUE;
279
280                 // Return result
281                 return $isInitialized;
282         }
283
284         /**
285          * Getter for size of given stack (array count)
286          *
287          * @param       $stackerName    Name of the stack
288          * @return      $count                  Size of stack (array count)
289          * @throws      NoStackerException      If given stack is missing
290          */
291         public function getStackCount ($stackerName) {
292                 // Is the stack not yet initialized?
293                 if (!$this->isStackInitialized($stackerName)) {
294                         // Throw an exception
295                         throw new NoStackerException(array($this, $stackerName), self::EXCEPTION_NO_STACKER_FOUND);
296                 } // END - if
297
298                 // Now, count the array of entries
299                 $this->partialStub('stackerName=' . $stackerName);
300                 $count = 0;
301
302                 // Return result
303                 return $count;
304         }
305
306         /**
307          * Adds a value to given stack
308          *
309          * @param       $stackerName    Name of the stack
310          * @param       $value                  Value to add to this stacker
311          * @return      void
312          * @throws      FullStackerException    Thrown if the stack is full
313          */
314         protected function addValue ($stackerName, $value) {
315                 // Is the stack not yet initialized or full?
316                 if (!$this->isStackInitialized($stackerName)) {
317                         // Then do it here
318                         $this->initStack($stackerName);
319                 } elseif ($this->isStackFull($stackerName)) {
320                         // Stacker is full
321                         throw new FullStackerException(array($this, $stackerName, $value), self::EXCEPTION_STACKER_IS_FULL);
322                 }
323
324                 // Now add the value to the stack
325                 $this->partialStub('stackerName=' . $stackerName . ',value[]=' . gettype($value));
326         }
327
328         /**
329          * Get last value from named stacker
330          *
331          * @param       $stackerName    Name of the stack
332          * @return      $value                  Value of last added value
333          * @throws      NoStackerException      If the named stacker was not found
334          * @throws      EmptyStackerException   If the named stacker is empty
335          */
336         protected function getLastValue ($stackerName) {
337                 // Is the stack not yet initialized or full?
338                 if (!$this->isStackInitialized($stackerName)) {
339                         // Throw an exception
340                         throw new NoStackerException(array($this, $stackerName), self::EXCEPTION_NO_STACKER_FOUND);
341                 } elseif ($this->isStackEmpty($stackerName)) {
342                         // Throw an exception
343                         throw new EmptyStackerException(array($this, $stackerName), self::EXCEPTION_STACKER_IS_EMPTY);
344                 }
345
346                 // Now get the last value
347                 $this->partialStub('stackerName=' . $stackerName);
348                 $value = NULL;
349
350                 // Return it
351                 return $value;
352         }
353
354         /**
355          * Get first value from named stacker
356          *
357          * @param       $stackerName    Name of the stack
358          * @return      $value                  Value of last added value
359          * @throws      NoStackerException      If the named stacker was not found
360          * @throws      EmptyStackerException   If the named stacker is empty
361          */
362         protected function getFirstValue ($stackerName) {
363                 // Is the stack not yet initialized or full?
364                 if (!$this->isStackInitialized($stackerName)) {
365                         // Throw an exception
366                         throw new NoStackerException(array($this, $stackerName), self::EXCEPTION_NO_STACKER_FOUND);
367                 } elseif ($this->isStackEmpty($stackerName)) {
368                         // Throw an exception
369                         throw new EmptyStackerException(array($this, $stackerName), self::EXCEPTION_STACKER_IS_EMPTY);
370                 }
371
372                 // Now get the first value
373                 $this->partialStub('stackerName=' . $stackerName);
374                 $value = NULL;
375
376                 // Return it
377                 return $value;
378         }
379
380         /**
381          * "Pops" last entry from stack
382          *
383          * @param       $stackerName    Name of the stack
384          * @return      $value                  Value "poped" from array
385          * @throws      NoStackerException      If the named stacker was not found
386          * @throws      EmptyStackerException   If the named stacker is empty
387          */
388         protected function popLast ($stackerName) {
389                 // Is the stack not yet initialized or full?
390                 if (!$this->isStackInitialized($stackerName)) {
391                         // Throw an exception
392                         throw new NoStackerException(array($this, $stackerName), self::EXCEPTION_NO_STACKER_FOUND);
393                 } elseif ($this->isStackEmpty($stackerName)) {
394                         // Throw an exception
395                         throw new EmptyStackerException(array($this, $stackerName), self::EXCEPTION_STACKER_IS_EMPTY);
396                 }
397
398                 // Now, remove the last entry, we don't care about the return value here, see elseif() block above
399                 $this->partialStub('stackerName=' . $stackerName);
400                 return NULL;
401         }
402
403         /**
404          * "Pops" first entry from stack
405          *
406          * @param       $stackerName    Name of the stack
407          * @return      $value                  Value "shifted" from array
408          * @throws      NoStackerException      If the named stacker was not found
409          * @throws      EmptyStackerException   If the named stacker is empty
410          */
411         protected function popFirst ($stackerName) {
412                 // Is the stack not yet initialized or full?
413                 if (!$this->isStackInitialized($stackerName)) {
414                         // Throw an exception
415                         throw new NoStackerException(array($this, $stackerName), self::EXCEPTION_NO_STACKER_FOUND);
416                 } elseif ($this->isStackEmpty($stackerName)) {
417                         // Throw an exception
418                         throw new EmptyStackerException(array($this, $stackerName), self::EXCEPTION_STACKER_IS_EMPTY);
419                 }
420
421                 // Now, remove the last entry, we don't care about the return value here, see elseif() block above
422                 $this->partialStub('stackerName=' . $stackerName);
423                 return NULL;
424         }
425 }
426
427 // [EOF]
428 ?>