303371c6791e25579d226e7a0f32693f8f577a1f
[core.git] / inc / classes / main / file_directories / class_BaseFile.php
1 <?php
2 /**
3  * A general file class
4  *
5  * @author              Roland Haeder <webmaster@ship-simu.org>
6  * @version             0.0.0
7  * @copyright   Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2012 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 BaseFile extends BaseFrameworkSystem {
25         /**
26          * The current file we are working in
27          */
28         private $fileName = '';
29
30         /**
31          * Back-buffer
32          */
33         private $backBuffer = '';
34
35         /**
36          * Currently loaded block (will be returned by current())
37          */
38         private $currentBlock = '';
39
40         /**
41          * Protected constructor
42          *
43          * @param       $className      Name of the class
44 y        * @return      void
45          */
46         protected function __construct ($className) {
47                 // Call parent constructor
48                 parent::__construct($className);
49
50                 // Init counters and gaps array
51                 $this->initCountersGapsArray();
52         }
53
54         /**
55          * Destructor for cleaning purposes, etc
56          *
57          * @return      void
58          */
59         public final function __destruct() {
60                 // Try to close a file
61                 $this->closeFile();
62
63                 // Call the parent destructor
64                 parent::__destruct();
65         }
66
67         /**
68          * Getter for the file pointer
69          *
70          * @return      $filePointer    The file pointer which shall be a valid file resource
71          * @throws      UnsupportedOperationException   If this method is called
72          */
73         public final function getPointer () {
74                 throw new UnsupportedOperationException(array($this, __FUNCTION__), self::EXCEPTION_UNSPPORTED_OPERATION);
75         }
76
77         /**
78          * Setter for file name
79          *
80          * @param       $fileName       The new file name
81          * @return      void
82          */
83         protected final function setFileName ($fileName) {
84                 $fileName = (string) $fileName;
85                 $this->fileName = $fileName;
86         }
87
88         /**
89          * Getter for file name
90          *
91          * @return      $fileName       The current file name
92          */
93         public final function getFileName () {
94                 return $this->fileName;
95         }
96
97         /**
98          * Initializes the back-buffer by setting it to an empty string.
99          *
100          * @return      void
101          */
102         private function initBackBuffer () {
103                 // Simply call the setter
104                 $this->setBackBuffer('');
105         }
106
107         /**
108          * Setter for backBuffer field
109          *
110          * @param       $backBuffer             Characters to "store" in back-buffer
111          * @return      void
112          */
113         private function setBackBuffer ($backBuffer) {
114                 // Cast to string (so no arrays or objects)
115                 $backBuffer = (string) $backBuffer;
116
117                 // ... and set it
118                 $this->backBuffer = $backBuffer;
119         }
120
121         /**
122          * Getter for backBuffer field
123          *
124          * @return      $backBuffer             Characters "stored" in back-buffer
125          */
126         private function getBackBuffer () {
127                 return $this->backBuffer;
128         }
129
130         /**
131          * Setter for currentBlock field
132          *
133          * @param       $currentBlock           Characters to set a currently loaded block
134          * @return      void
135          */
136         private function setCurrentBlock ($currentBlock) {
137                 // Cast to string (so no arrays or objects)
138                 $currentBlock = (string) $currentBlock;
139
140                 // ... and set it
141                 $this->currentBlock = $currentBlock;
142         }
143
144         /**
145          * Gets currently read data
146          *
147          * @return      $current        Currently read data
148          */
149         public function getCurrentBlock () {
150                 // Return it
151                 return $this->currentBlock;
152         }
153
154         /**
155          * Initializes this file class
156          *
157          * @param       $fileName       Name of this abstract file
158          * @return      void
159          */
160         protected function initFile ($fileName) {
161                 // Get a file i/o pointer instance
162                 $pointerInstance = ObjectFactory::createObjectByConfiguredName('file_raw_input_output_class', array($fileName));
163
164                 // ... and set it here
165                 $this->setPointerInstance($pointerInstance);
166         }
167
168         /**
169          * Close a file source and set it's instance to null and the file name
170          * to empty
171          *
172          * @return      void
173          * @todo        ~10% done?
174          */
175         public function closeFile () {
176                 $this->partialStub('Unfinished method.');
177
178                 // Remove file name
179                 $this->setFileName('');
180         }
181
182         /**
183          * Determines seek position
184          *
185          * @return      $seekPosition   Current seek position
186          */
187         public function determineSeekPosition () {
188                 // Call pointer instance
189                 return $this->getPointerInstance()->determineSeekPosition();
190         }
191
192         /**
193          * Seek to given offset (default) or other possibilities as fseek() gives.
194          *
195          * @param       $offset         Offset to seek to (or used as "base" for other seeks)
196          * @param       $whence         Added to offset (default: only use offset to seek to)
197          * @return      $status         Status of file seek: 0 = success, -1 = failed
198          */
199         public function seek ($offset, $whence = SEEK_SET) {
200                 // Call pointer instance
201                 return $this->getPointerInstance()->seek($offset, $whence);
202         }
203
204         /**
205          * Size of this file
206          *
207          * @return      $size   Size (in bytes) of file
208          * @todo        Handle seekStatus
209          */
210         public function size () {
211                 // Call pointer instance
212                 return $this->getPointerInstance()->size();
213         }
214
215         /**
216          * Read data a file pointer
217          *
218          * @return      mixed   The result of fread()
219          * @throws      NullPointerException    If the file pointer instance
220          *                                                                      is not set by setPointer()
221          * @throws      InvalidResourceException        If there is being set
222          */
223         public function readFromFile () {
224                 // Call pointer instance
225                 return $this->getPointerInstance()->readFromFile();
226         }
227
228         /**
229          * Reads given amount of bytes from file.
230          *
231          * @param       $bytes  Amount of bytes to read
232          * @return      $data   Data read from file
233          */
234         public function read ($bytes) {
235                 // Call pointer instance
236                 return $this->getPointerInstance()->read($bytes);
237         }
238
239         /**
240          * Write data to a file pointer
241          *
242          * @param       $dataStream             The data stream we shall write to the file
243          * @return      mixed                   Number of writes bytes or FALSE on error
244          * @throws      NullPointerException    If the file pointer instance
245          *                                                                      is not set by setPointer()
246          * @throws      InvalidResourceException        If there is being set
247          *                                                                                      an invalid file resource
248          */
249         public function writeToFile ($dataStream) {
250                 // Call pointer instance
251                 return $this->getPointerInstance()->writeToFile($dataStream);
252         }
253
254         /**
255          * Rewinds to the beginning of the file
256          *
257          * @return      $status         Status of this operation
258          */
259         public function rewind () {
260                 // Call pointer instance
261                 return $this->getPointerInstance()->rewind();
262         }
263
264         /**
265          * Determines whether the EOF has been reached
266          *
267          * @return      $isEndOfFileReached             Whether the EOF has been reached
268          */
269         public final function isEndOfFileReached () {
270                 // Call pointer instance
271                 return $this->getPointerInstance()->isEndOfFileReached();
272         }
273
274         /**
275          * Analyzes entries in index file. This will count all found (and valid)
276          * entries, mark invalid as damaged and count gaps ("fragmentation"). If
277          * only gaps are found, the file is considered as "virgin" (no entries).
278          *
279          * @return      void
280          */
281         public function analyzeFile () {
282                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] CALLED!', __METHOD__, __LINE__));
283
284                 // Make sure the file is initialized
285                 assert($this->isFileInitialized());
286
287                 // Init counters and gaps array
288                 $this->initCountersGapsArray();
289
290                 // Output message (as this may take some time)
291                 self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] Analyzing file structure ... (this may take some time)', __METHOD__, __LINE__));
292
293                 // First rewind to the begining
294                 $this->rewind();
295
296                 // Then try to load all entries
297                 while ($this->valid()) {
298                         // Go to next entry
299                         $this->next();
300
301                         // Get current entry
302                         $current = $this->getCurrentBlock();
303
304                         // Simply output it
305                         self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] current=%s', __METHOD__, __LINE__, print_r($current, TRUE)));
306                 } // END - while
307
308                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] EXIT!', __METHOD__, __LINE__));
309         }
310
311         /**
312          * Advances to next "block" of bytes
313          *
314          * @return      void
315          * @todo        This method will load large but empty files in a whole
316          */
317         public function next () {
318                 // Is there nothing to read?
319                 if (!$this->valid()) {
320                         // Nothing to read
321                         return;
322                 } // END - if
323
324                 // Make sure the block instance is set
325                 assert($this->getBlockInstance() instanceof CalculatableBlock);
326
327                 // First calculate minimum block length
328                 $length = $this->getBlockInstance()->caluclateMinimumBlockLength();
329
330                 // Short be more than zero!
331                 assert($length > 0);
332
333                 // Wait until a entry/block separator has been found
334                 $data = $this->getBackBuffer();
335                 while ((!$this->isEndOfFileReached()) && (!$this->getBlockInstance()->isBlockSeparatorFound($data))) {
336                         // Then read the block
337                         $data .= $this->read($length);
338                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('data()=' . strlen($data));
339                 } // END - if
340
341                 // EOF reached?
342                 if ($this->isEndOfFileReached()) {
343                         // Set whole data as current block
344                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('Calling setCurrentBlock(' . strlen($data) . ') ...');
345                         $this->setCurrentBlock($data);
346
347                         // Then abort here silently
348                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('EOF reached.');
349                         return;
350                 } // END - if
351
352                 /*
353                  * Init back-buffer which is the data that has been found beyond the
354                  * separator.
355                  */
356                 $this->initBackBuffer();
357
358                 // Separate data
359                 $dataArray = explode(self::getBlockSeparator(), $data);
360
361                 // This array must contain two elements
362                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('dataArray=' . print_r($dataArray, TRUE));
363                 assert(count($dataArray) == 2);
364
365                 // Left part is the actual block, right one the back-buffer data
366                 $this->setCurrentBlock($dataArray[0]);
367                 $this->setBackBuffer($dataArray[1]);
368         }
369
370         /**
371          * Checks wether the current entry is valid (not at the end of the file).
372          * This method will return TRUE if an emptied (nulled) entry has been found.
373          *
374          * @return      $isValid        Whether the next entry is valid
375          */
376         public function valid () {
377                 // Make sure the block instance is set
378                 assert($this->getBlockInstance() instanceof CalculatableBlock);
379
380                 // First calculate minimum block length
381                 $length = $this->getBlockInstance()->caluclateMinimumBlockLength();
382
383                 // Short be more than zero!
384                 assert($length > 0);
385
386                 // Get current seek position
387                 $seekPosition = $this->key();
388
389                 // Then try to read it
390                 $data = $this->read($length);
391
392                 // If some bytes could be read, all is fine
393                 $isValid = ((is_string($data)) && (strlen($data) > 0));
394
395                 // Get header size
396                 $headerSize = $this->getBlockInstance()->getHeaderSize();
397
398                 // Is the seek position at or beyond the header?
399                 if ($seekPosition >= $headerSize) {
400                         // Seek back to old position
401                         $this->seek($seekPosition);
402                 } else {
403                         // Seek directly behind the header
404                         $this->seek($headerSize);
405                 }
406
407                 // Return result
408                 return $isValid;
409         }
410
411         /**
412          * Gets current seek position ("key").
413          *
414          * @return      $key    Current key in iteration
415          */
416         public function key () {
417                 // Call pointer instance
418                 return $this->getPointerInstance()->determineSeekPosition();
419         }
420 }
421
422 // [EOF]
423 ?>