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