3d1711e72fecef8057a2eabbb06dbf17ab50b106
[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          * Initializes this file class
146          *
147          * @param       $fileName       Name of this abstract file
148          * @return      void
149          */
150         protected function initFile ($fileName) {
151                 // Get a file i/o pointer instance
152                 $pointerInstance = ObjectFactory::createObjectByConfiguredName('file_raw_input_output_class', array($fileName));
153
154                 // ... and set it here
155                 $this->setPointerInstance($pointerInstance);
156         }
157
158         /**
159          * Close a file source and set it's instance to null and the file name
160          * to empty
161          *
162          * @return      void
163          * @todo        ~10% done?
164          */
165         public function closeFile () {
166                 $this->partialStub('Unfinished method.');
167
168                 // Remove file name
169                 $this->setFileName('');
170         }
171
172         /**
173          * Determines seek position
174          *
175          * @return      $seekPosition   Current seek position
176          */
177         public function determineSeekPosition () {
178                 // Call pointer instance
179                 return $this->getPointerInstance()->determineSeekPosition();
180         }
181
182         /**
183          * Seek to given offset (default) or other possibilities as fseek() gives.
184          *
185          * @param       $offset         Offset to seek to (or used as "base" for other seeks)
186          * @param       $whence         Added to offset (default: only use offset to seek to)
187          * @return      $status         Status of file seek: 0 = success, -1 = failed
188          */
189         public function seek ($offset, $whence = SEEK_SET) {
190                 // Call pointer instance
191                 return $this->getPointerInstance()->seek($offset, $whence);
192         }
193
194         /**
195          * Size of this file
196          *
197          * @return      $size   Size (in bytes) of file
198          * @todo        Handle seekStatus
199          */
200         public function size () {
201                 // Call pointer instance
202                 return $this->getPointerInstance()->size();
203         }
204
205         /**
206          * Read data a file pointer
207          *
208          * @return      mixed   The result of fread()
209          * @throws      NullPointerException    If the file pointer instance
210          *                                                                      is not set by setPointer()
211          * @throws      InvalidResourceException        If there is being set
212          */
213         public function readFromFile () {
214                 // Call pointer instance
215                 return $this->getPointerInstance()->readFromFile();
216         }
217
218         /**
219          * Reads given amount of bytes from file.
220          *
221          * @param       $bytes  Amount of bytes to read
222          * @return      $data   Data read from file
223          */
224         public function read ($bytes) {
225                 // Call pointer instance
226                 return $this->getPointerInstance()->read($bytes);
227         }
228
229         /**
230          * Write data to a file pointer
231          *
232          * @param       $dataStream             The data stream we shall write to the file
233          * @return      mixed                   Number of writes bytes or FALSE on error
234          * @throws      NullPointerException    If the file pointer instance
235          *                                                                      is not set by setPointer()
236          * @throws      InvalidResourceException        If there is being set
237          *                                                                                      an invalid file resource
238          */
239         public function writeToFile ($dataStream) {
240                 // Call pointer instance
241                 return $this->getPointerInstance()->writeToFile($dataStream);
242         }
243
244         /**
245          * Rewinds to the beginning of the file
246          *
247          * @return      $status         Status of this operation
248          */
249         public function rewind () {
250                 // Call pointer instance
251                 return $this->getPointerInstance()->rewind();
252         }
253
254         /**
255          * Determines whether the EOF has been reached
256          *
257          * @return      $isEndOfFileReached             Whether the EOF has been reached
258          */
259         public final function isEndOfFileReached () {
260                 // Call pointer instance
261                 return $this->getPointerInstance()->isEndOfFileReached();
262         }
263
264         /**
265          * Analyzes entries in index file. This will count all found (and valid)
266          * entries, mark invalid as damaged and count gaps ("fragmentation"). If
267          * only gaps are found, the file is considered as "virgin" (no entries).
268          *
269          * @return      void
270          */
271         public function analyzeFile () {
272                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] CALLED!', __METHOD__, __LINE__));
273
274                 // Make sure the file is initialized
275                 assert($this->isFileInitialized());
276
277                 // Init counters and gaps array
278                 $this->initCountersGapsArray();
279
280                 // Output message (as this may take some time)
281                 self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] Analyzing file structure ... (this may take some time)', __METHOD__, __LINE__));
282
283                 // First rewind to the begining
284                 $this->rewind();
285
286                 // Then try to load all entries
287                 while ($this->valid()) {
288                         // Go to next entry
289                         $this->next();
290
291                         // Get current entry
292                         $current = $this->current();
293
294                         // Simply output it
295                         self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] current=%s', __METHOD__, __LINE__, print_r($current, TRUE)));
296                 } // END - while
297
298                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] EXIT!', __METHOD__, __LINE__));
299         }
300
301         /**
302          * Advances to next "block" of bytes
303          *
304          * @return      void
305          * @todo        This method will load large but empty files in a whole
306          */
307         public function next () {
308                 // Is there nothing to read?
309                 if (!$this->valid()) {
310                         // Nothing to read
311                         return;
312                 } // END - if
313
314                 // Make sure the block instance is set
315                 assert($this->getBlockInstance() instanceof CalculatableBlock);
316
317                 // First calculate minimum block length
318                 $length = $this->getBlockInstance()->caluclateMinimumBlockLength();
319
320                 // Short be more than zero!
321                 assert($length > 0);
322
323                 // Wait until a entry/block separator has been found
324                 $data = $this->getBackBuffer();
325                 while ((!$this->isEndOfFileReached()) && (!$this->getBlockInstance()->isBlockSeparatorFound($data))) {
326                         // Then read the block
327                         $data .= $this->read($length);
328                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('data()=' . strlen($data));
329                 } // END - if
330
331                 // EOF reached?
332                 if ($this->isEndOfFileReached()) {
333                         // Set whole data as current block
334                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('Calling setCurrentBlock(' . strlen($data) . ') ...');
335                         $this->setCurrentBlock($data);
336
337                         // Then abort here silently
338                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('EOF reached.');
339                         return;
340                 } // END - if
341
342                 /*
343                  * Init back-buffer which is the data that has been found beyond the
344                  * separator.
345                  */
346                 $this->initBackBuffer();
347
348                 // Separate data
349                 $dataArray = explode(self::getBlockSeparator(), $data);
350
351                 // This array must contain two elements
352                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('dataArray=' . print_r($dataArray, TRUE));
353                 assert(count($dataArray) == 2);
354
355                 // Left part is the actual block, right one the back-buffer data
356                 $this->setCurrentBlock($dataArray[0]);
357                 $this->setBackBuffer($dataArray[1]);
358         }
359
360         /**
361          * Checks wether the current entry is valid (not at the end of the file).
362          * This method will return TRUE if an emptied (nulled) entry has been found.
363          *
364          * @return      $isValid        Whether the next entry is valid
365          */
366         public function valid () {
367                 // Make sure the block instance is set
368                 assert($this->getBlockInstance() instanceof CalculatableBlock);
369
370                 // First calculate minimum block length
371                 $length = $this->getBlockInstance()->caluclateMinimumBlockLength();
372
373                 // Short be more than zero!
374                 assert($length > 0);
375
376                 // Get current seek position
377                 $seekPosition = $this->key();
378
379                 // Then try to read it
380                 $data = $this->read($length);
381
382                 // If some bytes could be read, all is fine
383                 $isValid = ((is_string($data)) && (strlen($data) > 0));
384
385                 // Get header size
386                 $headerSize = $this->getBlockInstance()->getHeaderSize();
387
388                 // Is the seek position at or beyond the header?
389                 if ($seekPosition >= $headerSize) {
390                         // Seek back to old position
391                         $this->seek($seekPosition);
392                 } else {
393                         // Seek directly behind the header
394                         $this->seek($headerSize);
395                 }
396
397                 // Return result
398                 return $isValid;
399         }
400 }
401
402 // [EOF]
403 ?>