70bd7006f5c1c345e273f59790c256470af3a0f8
[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                 // Default is not initialized
159                 $isInitialized = FALSE;
160
161                 // Get it from iterator which holds the pointer instance. If FALSE is returned
162                 $fileSize = $this->getIteratorInstance()->size();
163
164                 /*
165                  * The returned file size should not be FALSE or NULL as this means
166                  * that the pointer class does not work correctly.
167                  */
168                 assert(is_int($fileSize));
169
170                 // Is more than 0 returned?
171                 if ($fileSize > 0) {
172                         // So is the header written?
173                         $isInitialized = $this->getIteratorInstance()->isHeaderInitialized();
174                 } // END - if
175
176                 // Return result
177                 return $isInitialized;
178         }
179
180         /**
181          * Creates the file-stack's header
182          *
183          * @return      void
184          */
185         private function createFileHeader () {
186                 // The file's header should not be initialized here
187                 assert(!$this->isFileHeaderInitialized());
188
189                 // Flush file header
190                 $this->flushFileHeader();
191         }
192
193         /**
194          * Flushes the file header
195          *
196          * @return      void
197          */
198         private function flushFileHeader () {
199                 // Put all informations together
200                 $header = sprintf('%s%s%s%s%s',
201                         // Magic
202                         self::STACK_MAGIC,
203
204                         // Separator magic<->count
205                         chr(self::SEPARATOR_MAGIC_COUNT),
206
207                         // Total entries (will be zero) and pad it to 20 chars
208                         str_pad($this->dec2hex($this->getCounter()), self::COUNT_LENGTH, '0', STR_PAD_LEFT),
209
210                         // Position (will be zero)
211                         str_pad($this->dec2hex(0, 2), self::COUNT_POSITION, '0', STR_PAD_LEFT),
212
213                         // Separator position<->entries
214                         chr(self::SEPARATOR_SEEK_POS_ENTRIES)
215                 );
216
217                 // Write it to disk (header is always at seek position 0)
218                 $this->getIteratorInstance()->writeAtPosition(0, $header);
219
220                 // Update seek position
221                 $this->updateSeekPosition();
222         }
223
224         /**
225          * Initializes this file-based stack.
226          *
227          * @param       $fileName       File name of this stack
228          * @return      void
229          */
230         protected function initFileStack ($fileName) {
231                 // Get a file i/o pointer instance
232                 $pointerInstance = ObjectFactory::createObjectByConfiguredName('file_raw_input_output_class', array($fileName));
233
234                 // Get iterator instance
235                 $iteratorInstance = ObjectFactory::createObjectByConfiguredName('file_io_iterator_class', array($pointerInstance));
236
237                 // Is the instance implementing the right interface?
238                 assert($iteratorInstance instanceof SeekableWritableFileIterator);
239
240                 // Set iterator here
241                 $this->setIteratorInstance($iteratorInstance);
242
243                 // Is the file's header initialized?
244                 if ($this->isFileHeaderInitialized()) {
245                         // Then load it
246                         $this->loadFileHeader();
247                 } else {
248                         // No, then create it (which may pre-allocate the stack)
249                         $this->createFileHeader();
250
251                         // And pre-allocate a bit
252                         $this->preAllocateFile();
253                 }
254         }
255
256         /**
257          * Initializes given stacker
258          *
259          * @param       $stackerName    Name of the stack
260          * @param       $forceReInit    Force re-initialization
261          * @return      void
262          * @throws      AlreadyInitializedStackerException      If the stack is already initialized
263          */
264         public function initStack ($stackerName, $forceReInit = FALSE) {
265                 // Is the stack already initialized?
266                 if (($forceReInit === FALSE) && ($this->isStackInitialized($stackerName))) {
267                         // Then throw the exception
268                         throw new AlreadyInitializedStackerException(array($this, $stackerName, $forceReInit), self::EXCEPTION_STACKER_ALREADY_INITIALIZED);
269                 } // END - if
270
271                 // Initialize the given stack
272                 $this->partialStub('stackerName=' . $stackerName . ',forceReInit=' . intval($forceReInit));
273         }
274
275         /**
276          * Checks whether the given stack is initialized (set in array $stackers)
277          *
278          * @param       $stackerName    Name of the stack
279          * @return      $isInitialized  Whether the stack is initialized
280          */
281         public function isStackInitialized ($stackerName) {
282                 // Is is there?
283                 $this->partialStub('stackerName=' . $stackerName);
284                 $isInitialized = TRUE;
285
286                 // Return result
287                 return $isInitialized;
288         }
289
290         /**
291          * Getter for size of given stack (array count)
292          *
293          * @param       $stackerName    Name of the stack
294          * @return      $count                  Size of stack (array count)
295          * @throws      NoStackerException      If given stack is missing
296          */
297         public function getStackCount ($stackerName) {
298                 // Is the stack not yet initialized?
299                 if (!$this->isStackInitialized($stackerName)) {
300                         // Throw an exception
301                         throw new NoStackerException(array($this, $stackerName), self::EXCEPTION_NO_STACKER_FOUND);
302                 } // END - if
303
304                 // Now, count the array of entries
305                 $this->partialStub('stackerName=' . $stackerName);
306                 $count = 0;
307
308                 // Return result
309                 return $count;
310         }
311
312         /**
313          * Adds a value to given stack
314          *
315          * @param       $stackerName    Name of the stack
316          * @param       $value                  Value to add to this stacker
317          * @return      void
318          * @throws      FullStackerException    Thrown if the stack is full
319          */
320         protected function addValue ($stackerName, $value) {
321                 // Is the stack not yet initialized or full?
322                 if (!$this->isStackInitialized($stackerName)) {
323                         // Then do it here
324                         $this->initStack($stackerName);
325                 } elseif ($this->isStackFull($stackerName)) {
326                         // Stacker is full
327                         throw new FullStackerException(array($this, $stackerName, $value), self::EXCEPTION_STACKER_IS_FULL);
328                 }
329
330                 // Now add the value to the stack
331                 $this->partialStub('stackerName=' . $stackerName . ',value[]=' . gettype($value));
332         }
333
334         /**
335          * Get last value from named stacker
336          *
337          * @param       $stackerName    Name of the stack
338          * @return      $value                  Value of last added value
339          * @throws      NoStackerException      If the named stacker was not found
340          * @throws      EmptyStackerException   If the named stacker is empty
341          */
342         protected function getLastValue ($stackerName) {
343                 // Is the stack not yet initialized or full?
344                 if (!$this->isStackInitialized($stackerName)) {
345                         // Throw an exception
346                         throw new NoStackerException(array($this, $stackerName), self::EXCEPTION_NO_STACKER_FOUND);
347                 } elseif ($this->isStackEmpty($stackerName)) {
348                         // Throw an exception
349                         throw new EmptyStackerException(array($this, $stackerName), self::EXCEPTION_STACKER_IS_EMPTY);
350                 }
351
352                 // Now get the last value
353                 $this->partialStub('stackerName=' . $stackerName);
354                 $value = NULL;
355
356                 // Return it
357                 return $value;
358         }
359
360         /**
361          * Get first value from named stacker
362          *
363          * @param       $stackerName    Name of the stack
364          * @return      $value                  Value of last added value
365          * @throws      NoStackerException      If the named stacker was not found
366          * @throws      EmptyStackerException   If the named stacker is empty
367          */
368         protected function getFirstValue ($stackerName) {
369                 // Is the stack not yet initialized or full?
370                 if (!$this->isStackInitialized($stackerName)) {
371                         // Throw an exception
372                         throw new NoStackerException(array($this, $stackerName), self::EXCEPTION_NO_STACKER_FOUND);
373                 } elseif ($this->isStackEmpty($stackerName)) {
374                         // Throw an exception
375                         throw new EmptyStackerException(array($this, $stackerName), self::EXCEPTION_STACKER_IS_EMPTY);
376                 }
377
378                 // Now get the first value
379                 $this->partialStub('stackerName=' . $stackerName);
380                 $value = NULL;
381
382                 // Return it
383                 return $value;
384         }
385
386         /**
387          * "Pops" last entry from stack
388          *
389          * @param       $stackerName    Name of the stack
390          * @return      $value                  Value "poped" from array
391          * @throws      NoStackerException      If the named stacker was not found
392          * @throws      EmptyStackerException   If the named stacker is empty
393          */
394         protected function popLast ($stackerName) {
395                 // Is the stack not yet initialized or full?
396                 if (!$this->isStackInitialized($stackerName)) {
397                         // Throw an exception
398                         throw new NoStackerException(array($this, $stackerName), self::EXCEPTION_NO_STACKER_FOUND);
399                 } elseif ($this->isStackEmpty($stackerName)) {
400                         // Throw an exception
401                         throw new EmptyStackerException(array($this, $stackerName), self::EXCEPTION_STACKER_IS_EMPTY);
402                 }
403
404                 // Now, remove the last entry, we don't care about the return value here, see elseif() block above
405                 $this->partialStub('stackerName=' . $stackerName);
406                 return NULL;
407         }
408
409         /**
410          * "Pops" first entry from stack
411          *
412          * @param       $stackerName    Name of the stack
413          * @return      $value                  Value "shifted" from array
414          * @throws      NoStackerException      If the named stacker was not found
415          * @throws      EmptyStackerException   If the named stacker is empty
416          */
417         protected function popFirst ($stackerName) {
418                 // Is the stack not yet initialized or full?
419                 if (!$this->isStackInitialized($stackerName)) {
420                         // Throw an exception
421                         throw new NoStackerException(array($this, $stackerName), self::EXCEPTION_NO_STACKER_FOUND);
422                 } elseif ($this->isStackEmpty($stackerName)) {
423                         // Throw an exception
424                         throw new EmptyStackerException(array($this, $stackerName), self::EXCEPTION_STACKER_IS_EMPTY);
425                 }
426
427                 // Now, remove the last entry, we don't care about the return value here, see elseif() block above
428                 $this->partialStub('stackerName=' . $stackerName);
429                 return NULL;
430         }
431 }
432
433 // [EOF]
434 ?>