View Javadoc
1   /*
2    * Copyright (C) 2012 Alberto Irurueta Carro (alberto@irurueta.com)
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *         http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package com.irurueta.geometry.io;
17  
18  import java.io.File;
19  import java.io.IOException;
20  import java.io.RandomAccessFile;
21  import java.nio.channels.FileChannel;
22  
23  /**
24   * This class provides methods to access file data at random positions.
25   */
26  @SuppressWarnings("DuplicatedCode")
27  public class FileReaderAndWriter extends AbstractFileReaderAndWriter {
28  
29      /**
30       * Underlying input file.
31       */
32      private final RandomAccessFile randomAccessFile;
33  
34      /**
35       * Constructor.
36       *
37       * @param f    file to read from or write to.
38       * @param mode file opening mode (read only or read write).
39       * @throws IOException if an I/O error occurs.
40       */
41      public FileReaderAndWriter(final File f, final FileChannel.MapMode mode) throws IOException {
42          if (mode == FileChannel.MapMode.READ_ONLY) {
43              this.randomAccessFile = new RandomAccessFile(f, "r");
44          } else {
45              this.randomAccessFile = new RandomAccessFile(f, "rw");
46          }
47      }
48  
49      /**
50       * Reads one byte at current file position and advances one position.
51       *
52       * @return Next byte of data or -1 if end of file is reached.
53       * @throws IOException if an I/O error occurs. Not thrown if end-of-file has
54       *                     been reached.
55       */
56      @Override
57      public int read() throws IOException {
58          return randomAccessFile.read();
59      }
60  
61      /**
62       * Reads up to b.length bytes of data from this file into an array of bytes.
63       * This method blocks until at least one byte of input is available.
64       *
65       * @param b The buffer into which the data is read.
66       * @return The total number of bytes read into the buffer, or -1 if there is
67       * no more data because the end of the file has been reached.
68       * @throws IOException If the first byte cannot be read for any reason other
69       *                     than end of file, or if the file has been closed, or if some other I/O
70       *                     error occurs.
71       */
72      @Override
73      public int read(final byte[] b) throws IOException {
74          return randomAccessFile.read(b);
75      }
76  
77      /**
78       * Reads up to len bytes of data from this file into an array of bytes. This
79       * method blocks until at least one byte of input is available.
80       * This method behaves in exactly the same way as the
81       * InputStream.read(byte[], int, int) method of InputStream.
82       *
83       * @param b   the buffer into which the data is read.
84       * @param off the start offset in array b at which the data is written.
85       * @param len the maximum number of bytes read.
86       * @return the total number of bytes read into the buffer, or -1 if there is
87       * no more data because the end of the file has been reached.
88       * @throws IOException If the first byte cannot be read for any reason other
89       *                     than end of file, or if the random access file has been closed, or if
90       *                     some other I/O error occurs.
91       */
92      @Override
93      public int read(final byte[] b, final int off, final int len) throws IOException {
94          return randomAccessFile.read(b, off, len);
95      }
96  
97      /**
98       * Reads b.length bytes from this file into the byte array, starting at the
99       * current file pointer. This method reads repeatedly from the file until
100      * the requested number of bytes are read. This method blocks until the
101      * requested number of bytes are read, the end of the stream is detected, or
102      * an exception is thrown.
103      *
104      * @param b the buffer into which the data is read.
105      * @throws IOException if this file reaches the end before reading all the
106      *                     bytes.
107      */
108     public void readFully(final byte[] b) throws IOException {
109         randomAccessFile.readFully(b);
110     }
111 
112     /**
113      * Reads exactly len bytes from this file into the byte array, starting at
114      * the current file pointer. This method reads repeatedly from the file
115      * until the requested number of bytes are read. This method blocks until
116      * the requested number of bytes are read, the end of the stream is
117      * detected, or an exception is thrown.
118      *
119      * @param b   the buffer into which the data is read.
120      * @param off the start offset of the data.
121      * @param len the number of byte to read.
122      * @throws IOException if this file reaches the end before reading all the
123      *                     bytes or if an I/O error occurs.
124      */
125     public void readFully(final byte[] b, final int off, final int len) throws IOException {
126         randomAccessFile.readFully(b, off, len);
127     }
128 
129     /**
130      * Attempts to skip over n byte of input discarding the skipped bytes.
131      * <p>
132      * This method may skip over some number of bytes, possibly zero. This may
133      * result from any of a number of conditions; reaching end of file before n
134      * bytes have been skipped is only one possibility. The actual number of
135      * bytes skipped is returned. If n is negative, no bytes are skipped.
136      *
137      * @param n the number of bytes to be skipped.
138      * @return the actual number of bytes skipped.
139      * @throws IOException if an I/O error occurs.
140      */
141     @Override
142     public long skip(final long n) throws IOException {
143         return randomAccessFile.skipBytes((int) n);
144     }
145 
146     /**
147      * Writes the specified byte to this file. The write starts at the current
148      * file pointer.
149      *
150      * @param b the byte to be written.
151      * @throws IOException if an I/O error occurs.
152      */
153     @Override
154     public void write(final int b) throws IOException {
155         randomAccessFile.write(b);
156     }
157 
158     /**
159      * Writes b.length bytes from the specified byte array to this file,
160      * starting at the current file pointer.
161      *
162      * @param b the data.
163      * @throws IOException if an I/O error occurs.
164      */
165     @Override
166     public void write(final byte[] b) throws IOException {
167         randomAccessFile.write(b);
168     }
169 
170     /**
171      * Writes len bytes from the specified byte array starting at offset off to
172      * this file.
173      *
174      * @param b   the data.
175      * @param off the start offset in the data.
176      * @param len the number of bytes to write.
177      * @throws IOException if an I/O error occurs.
178      */
179     @Override
180     public void write(final byte[] b, final int off, final int len) throws IOException {
181         randomAccessFile.write(b, off, len);
182     }
183 
184     /**
185      * Returns the current offset in this file.
186      *
187      * @return the offset from the beginning of the file, in bytes, at which the
188      * next read or write occurs.
189      * @throws IOException in an I/O error occurs.
190      */
191     @Override
192     public long getPosition() throws IOException {
193         return randomAccessFile.getFilePointer();
194     }
195 
196     /**
197      * Determines whether end of file has been reached (next read() will return
198      * -1). or not.
199      *
200      * @return True if end of file has been reached, false otherwise.
201      * @throws IOException if an I/O error occurs.
202      */
203     @Override
204     public boolean isEndOfStream() throws IOException {
205         return getPosition() >= randomAccessFile.length();
206     }
207 
208     /**
209      * Sets the file-pointer offset, measured from the beginning of this file,
210      * at which the next read or write occurs. The offset may be set beyond the
211      * end of the file. Setting the offset beyond the end of the file does not
212      * change the file length.
213      * The file length will change only by writing after the offset has been set
214      * beyond the end of the file.
215      *
216      * @param pos the offset position, measured in bytes from the beginning of
217      *            the file, at which to set the file pointer.
218      * @throws IOException if pos is less than 0 or if an I/O error occurs.
219      */
220     @Override
221     public void seek(final long pos) throws IOException {
222         randomAccessFile.seek(pos);
223     }
224 
225     /**
226      * Closes this file stream and releases any system resources associated with
227      * the stream. A closed file cannot perform input or output operations and
228      * cannot be reopened.
229      * If this file has an associated channel then the channel is closed as
230      * well.
231      *
232      * @throws IOException if an I/O error occurs.
233      */
234     @Override
235     public void close() throws IOException {
236         randomAccessFile.close();
237     }
238 
239     /**
240      * Reads a boolean from this file. This method reads a single byte from the
241      * file, starting at the current file pointer. A value of 0 represents
242      * false. Any other value represents true. This method blocks until the byte
243      * is read, the end of the stream is detected, or an exception is thrown.
244      *
245      * @return the boolean value read.
246      * @throws IOException if an I/O error occurs.
247      */
248     @Override
249     public boolean readBoolean() throws IOException {
250         return randomAccessFile.readBoolean();
251     }
252 
253     /**
254      * Reads a signed eight-bit value from this file. This method reads a byte
255      * from the file, starting from the current file pointer. If the byte read
256      * is b, where 0 &lt;= b &lt;= 255, then the result is: (byte)(b)
257      * This method blocks until the byte is read, the end of the stream is
258      * detected, or an exception is thrown.
259      *
260      * @return the next byte of this file is a signed eight-bit byte.
261      * @throws IOException if an I/O error occurs.
262      */
263     @Override
264     public byte readByte() throws IOException {
265         return randomAccessFile.readByte();
266     }
267 
268     /**
269      * Reads an unsigned eight-bit number from this file. This method reads a
270      * byte from this file, starting at the current file pointer, and returns
271      * that byte.
272      * This method blocks until the byte is read, the end of the stream is
273      * detected, or an exception is thrown.
274      *
275      * @return the next byte of this file, interpreted as an unsigned eight-bit
276      * number.
277      * @throws IOException if an I/O error occurs.
278      */
279     @Override
280     public short readUnsignedByte() throws IOException {
281         return (short) randomAccessFile.readUnsignedByte();
282     }
283 
284     /**
285      * Reads a signed 16-bit number from this file. The method reads two byte
286      * from this file, starting at the current file pointer. If the two bytes
287      * read, in order, are b1 and b2, where each of the two values is between 0
288      * and 255, inclusive, then the result is equal to: (short)(b1 &lt;&lt; 8 |
289      * b2).
290      * This method blocks until the two bytes are read, the end of the stream is
291      * detected, or an exception is thrown.
292      *
293      * @return the next two bytes of this file, interpreted as a signed 16-bit
294      * number.
295      * @throws IOException if an I/O error occurs.
296      */
297     @Override
298     public short readShort() throws IOException {
299         return randomAccessFile.readShort();
300     }
301 
302     /**
303      * Reads a signed 16-bit number from this file assuming that file is encoded
304      * using provided endian type. If endian type is big endian type, then
305      * natural binary order is preserved, otherwise byte order is reversed.
306      * This method blocks until the two bytes of the 16-bit number are read, the
307      * end of the stream is detected, or an exception is thrown.
308      *
309      * @param endianType Endian type. Big endian preserves natural binary order,
310      *                   little endian reverses byte order.
311      * @return the next two bytes of this file, interpreted as a signed 16-bit
312      * number encoded in provided endian type.
313      * @throws IOException if an I/O error occurs.
314      */
315     @Override
316     public short readShort(final EndianType endianType) throws IOException {
317         final var value = randomAccessFile.readShort();
318         return Util.fromEndianType(endianType, value);
319     }
320 
321     /**
322      * Reads an unsigned 16-bit number from this file. This method reads two
323      * bytes from this file, starting at the current file pointer. If the bytes
324      * read, in order, are b1 and b2, where 0 &lt;= b1, b2 &lt;= 255, then the
325      * result is equal to: (b1 &lt;&lt; 8) | b2.
326      * This method blocks until the two bytes are read, the end of the stream is
327      * detected, or an exception is thrown.
328      *
329      * @return the next two bytes of this file, interpreted as an unsigned
330      * 16-bit integer.
331      * @throws IOException if an I/O error occurs.
332      */
333     @Override
334     public int readUnsignedShort() throws IOException {
335         return randomAccessFile.readUnsignedShort();
336     }
337 
338     /**
339      * Reads an unsigned 16-bit number from this file. This method reads two
340      * bytes from this file, starting at the current file pointer and using
341      * provided endian type. If endian type is big endian, then natural binary
342      * order is preserved, otherwise byte order is reversed.
343      * This method blocks until the two bytes are read, the end of the stream is
344      * detected, or an exception is thrown.
345      *
346      * @param endianType Endian type. Big endian preserves natural binary order,
347      *                   little endian reverses byte order.
348      * @return the next two bytes of this file, interpreted as an unsigned
349      * 16-bit integer.
350      * @throws IOException if an I/O error occurs.
351      */
352     @Override
353     public int readUnsignedShort(final EndianType endianType) throws IOException {
354         final var streamValue = randomAccessFile.readShort();
355         final var value = Util.fromEndianType(endianType, streamValue);
356 
357         // convert value to byte array
358         final var firstShortByte = 0xff & (value >> 8);
359         final var secondShortByte = 0xff & ((value << 8) >> 8);
360 
361         // return it as integer
362         return 0xffff & ((firstShortByte << 8) | secondShortByte);
363     }
364 
365     /**
366      * Reads a signed 32-bit integer from this file. This method reads 4 bytes
367      * from the file, starting at the current file pointer. If the bytes read,
368      * in order, are b1, b2, b3, and b4, where 0 &lt;= b1, b3, b4 &lt;= 255, then
369      * the result is equal to: (b1 &lt;&lt; 24) | (b2 &lt;&lt; 16) + (b3 &lt;&lt; 8) +
370      * b4.
371      * This method blocks until the four bytes are read, the end of the stream
372      * is detected, or an exception is thrown.
373      *
374      * @return the next four bytes of this file, interpreted as an int.
375      * @throws IOException if an I/O error occurs.
376      */
377     @Override
378     public int readInt() throws IOException {
379         return randomAccessFile.readInt();
380     }
381 
382     /**
383      * Reads a signed 32-bit integer from this file. This method reads 4 bytes
384      * from the file, starting at the current file pointer and using provided
385      * endian type. If endian type is big endian, then natural binary order is
386      * preserved, otherwise byte order is reversed.
387      * This method blocks until the four bytes are read, the end of the stream
388      * is detected, or an exception is thrown.
389      *
390      * @param endianType Endian type. Big endian preserves natural binary order,
391      *                   little endian reverses byte order.
392      * @return the next four bytes of this file, interpreted as an int.
393      * @throws IOException if an I/O error occurs.
394      */
395     @Override
396     public int readInt(final EndianType endianType) throws IOException {
397         final var value = randomAccessFile.readInt();
398         return Util.fromEndianType(endianType, value);
399     }
400 
401     /**
402      * Reads an unsigned 32-bit integer from this file. This method reads 4
403      * bytes from the file, starting at the current file pointer. If the bytes
404      * read, in order, are b1, b2, b3, and b4, where 0 &lt;= b1, b3, b4 &lt;= 255,
405      * then the result is equal to: (b1 &lt;&lt; 24) | (b2 &lt;&lt; 16) +
406      * (b3 &lt;&lt; 8) + b4
407      * This method blocks until the four bytes are read, the end of the stream
408      * is detected, or an exception is thrown.
409      *
410      * @return the next four bytes of this file, interpreted as a long.
411      * @throws IOException if an I/O error occurs.
412      */
413     @Override
414     public long readUnsignedInt() throws IOException {
415         final var value = randomAccessFile.readInt();
416 
417         // convert value to byte array
418         final var firstIntByte = 0xff & (value >> 24);
419         final var secondIntByte = 0xff & ((value << 8) >> 24);
420         final var thirdIntByte = 0xff & ((value << 16) >> 24);
421         final var fourthIntByte = 0xff & ((value << 24) >> 24);
422 
423         // return it as integer
424         return ((long) firstIntByte << 24) | (secondIntByte << 16) | (thirdIntByte << 8) | fourthIntByte;
425     }
426 
427     /**
428      * Reads an unsigned 32-bit integer from this file. This method reads 4
429      * bytes from the file, starting at the current file pointer and using
430      * provided endian type. If endian type is big endian, then natural binary
431      * order is preserved, otherwise byte order is reversed.
432      * This method blocks until the four bytes are read, the end of the stream
433      * is detected, or an exception is thrown.
434      *
435      * @param endianType Endian type. Big endian preserves natural binary order,
436      *                   little endian reverses byte order.
437      * @return the next four bytes of this file, interpreted as an int.
438      * @throws IOException if an I/O error occurs.
439      */
440     @Override
441     public long readUnsignedInt(final EndianType endianType) throws IOException {
442         final var streamValue = randomAccessFile.readInt();
443         final var value = Util.fromEndianType(endianType, streamValue);
444 
445         // convert value to byte array
446         final var firstIntByte = 0xff & (value >> 24);
447         final var secondIntByte = 0xff & ((value << 8) >> 24);
448         final var thirdIntByte = 0xff & ((value << 16) >> 24);
449         final var fourthIntByte = 0xff & ((value << 24) >> 24);
450 
451         // return it as integer
452         return ((long) firstIntByte << 24) | (secondIntByte << 16) | (thirdIntByte << 8) | fourthIntByte;
453     }
454 
455     /**
456      * Reads a signed 64-bit integer from this file. This method reads eight
457      * bytes from the file, starting at the current file pointer. If the bytes
458      * read, in order, are b1, b2, b3, b4, b5, b6, b7, and b8, where:
459      * 0 &lt;= b1, b2, b3, b4. b5. b6. b7. b8 &lt;= 255,
460      * then the result is equal to:
461      * ((long)b1 &lt;&lt; 56) + ((long)b2 &lt;&lt; 48)
462      * + ((long)b3 &lt;&lt; 40) + ((long)b4 &lt;&lt; 32)
463      * + ((long)b5 &lt;&lt; 24) + ((long)b6 &lt;&lt; 16)
464      * + ((long)b7 &lt;&lt; 8) + b8
465      * This method blocks until the eight bytes are read, the end of the stream
466      * is detected, or an exception is thrown.
467      *
468      * @return the next eight bytes of this file, interpreted as a long.
469      * @throws IOException if an I/O error occurs.
470      */
471     @Override
472     public long readLong() throws IOException {
473         return randomAccessFile.readLong();
474     }
475 
476     /**
477      * Reads a signed 64-bit integer from this file. This method reads eight
478      * bytes from the file, starting at the current file pointer and using
479      * provided endian type. If endian type is big endian, then natural binary
480      * order is preserved, otherwise byte order is reversed.
481      * This method blocks until the eight bytes are read, the end of the stream
482      * is detected, or an exception is thrown.
483      *
484      * @param endianType Endian type. Big endian preserves natural binary order,
485      *                   little endian reverses byte order.
486      * @return the next eight bytes of this file, interpreted as a long
487      * @throws IOException if an I/O error occurs.
488      */
489     @Override
490     public long readLong(final EndianType endianType) throws IOException {
491         final var value = randomAccessFile.readLong();
492         return Util.fromEndianType(endianType, value);
493     }
494 
495     /**
496      * Reads a float from this file. This method reads an int value, starting at
497      * the current file pointer, as if by the readInt method and then converts
498      * that in to a float using the intBitsToFloat method in class Float.
499      * This method blocks until the four bytes are read, the end of the stream
500      * is detected, or an exception is thrown.
501      *
502      * @return the next four bytes of this file, interpreted as a float.
503      * @throws IOException if an I/O error occurs.
504      */
505     @Override
506     public float readFloat() throws IOException {
507         return randomAccessFile.readFloat();
508     }
509 
510     /**
511      * Reads a float from this file. This method reads four bytes using
512      * provided endian type. If endian type is big endian, then natural binary
513      * order is preserved, otherwise byte order is reversed.
514      * This method blocks until the four bytes are read, the end of the stream
515      * is detected, or an exception is thrown.
516      *
517      * @param endianType Endian type. Big endian preserves natural binary order,
518      *                   little endian reverses byte order.
519      * @return the next four bytes of this file, interpreted as a float.
520      * @throws IOException if an I/O error occurs.
521      */
522     @Override
523     public float readFloat(final EndianType endianType) throws IOException {
524         final var value = randomAccessFile.readFloat();
525         return Util.fromEndianType(endianType, value);
526     }
527 
528     /**
529      * Reads a double from this file. This method reads a long value, starting
530      * at the current file pointer, as if by the readLong method and then
531      * converts that long to a double using the longBitsToDouble method in class
532      * Double.
533      * This method blocks until the eight bytes are read, the end of the stream
534      * is detected, or an exception is thrown.
535      *
536      * @return the next eight bytes of this file, interpreted as a double.
537      * @throws IOException if an I/O error occurs.
538      */
539     @Override
540     public double readDouble() throws IOException {
541         return randomAccessFile.readDouble();
542     }
543 
544     /**
545      * Reads a double from this file. This method reads eight bytes using
546      * provided endian type. If endian type is big endian, then natural binary
547      * order is preserved, otherwise byte order is reversed.
548      * This method blocks until the eight bytes are read, the end of the stream
549      * is detected, or an exception is thrown.
550      *
551      * @param endianType Endian type. Big endian preserves natural binary order,
552      *                   little endian reverses byte order.
553      * @return the next eight bytes of this file, interpreted as a double.
554      * @throws IOException if an I/O error occurs.
555      */
556     @Override
557     public double readDouble(final EndianType endianType) throws IOException {
558         final var value = randomAccessFile.readDouble();
559         return Util.fromEndianType(endianType, value);
560     }
561 
562     /**
563      * Reads the next line of text from this file. This method successively
564      * reads bytes from the file, starting at the current file pointer, until it
565      * reaches a line terminator of the end of the file. Each byte is converted
566      * into a character by taking the byte's value for the lower eight bits of
567      * the character and setting the high eight bits of the character to zero.
568      * This method does not, therefore, support the full Unicode character set.
569      * A line of text is terminated by a carriage-return character ('\r'), a
570      * newline character('\n'), a carriage-return character immediately followed
571      * by a newline character, or the end of the file. Line-terminating
572      * characters are discarded and are not included as part of the string
573      * returned.
574      * This method blocks until a newline character is read, a carriage return
575      * and the byte following it are read (to see if it is a newline), the end
576      * of the file is reached, or an exception is thrown.
577      *
578      * @return the next line of text from this file, or null if end of file is
579      * encountered before even one byte is read.
580      * @throws IOException if an I/O error occurs.
581      */
582     @Override
583     public String readLine() throws IOException {
584         return randomAccessFile.readLine();
585     }
586 
587     /**
588      * Reads in a string from this file. The string has been encoded using a
589      * modified UTF-8 format.
590      * The first two bytes are read, starting from the current file pointer, as
591      * if by readUnsignedShort. This value gives the number of following bytes
592      * that are in the encoded string, not the length of the resulting string.
593      * The following bytes are then interpreted as bytes encoding characters in
594      * the modified UTF-8 format and are converted into characters.
595      * This method blocks until all the bytes are read, the end of the stream is
596      * detected, or an exception is thrown.
597      *
598      * @return a Unicode string.
599      * @throws IOException if an I/O error occurs.
600      */
601     public String readUTF() throws IOException {
602         return randomAccessFile.readUTF();
603     }
604 
605     /**
606      * Sequentially reads characters starting at current file position until one
607      * of the characters in provided pattern is found.
608      * All characters read so far will be returned without including any of the
609      * pattern characters.
610      *
611      * @param pattern Stop characters to stop reading when they are found.
612      * @return String read so far until any of the pattern characters was found
613      * or an empty string if the first character is contained in provided
614      * pattern.
615      * @throws IOException              if an I/O error occurs.
616      * @throws IllegalArgumentException if no pattern characters are provided.
617      */
618     @Override
619     public String readUntilAnyOfTheseCharactersIsFound(final String pattern) throws IOException {
620         if (pattern.isEmpty()) {
621             throw new IllegalArgumentException();
622         }
623 
624         final var buffer = new StringBuilder();
625         final var charBuffer = new byte[1];
626         String character;
627         while (randomAccessFile.read(charBuffer) == 1) {
628             character = new String(charBuffer);
629             if (pattern.contains(character)) {
630                 // character found
631                 break;
632             }
633             // add character to output buffer
634             buffer.append(character);
635         }
636 
637         return buffer.toString();
638     }
639 
640     /**
641      * Writes a boolean to the file as a one-byte value. The value true is
642      * written out as the value (byte)1; the value false is written out as the
643      * value (byte)0. The write starts at the current position of the file
644      * pointer.
645      *
646      * @param v a boolean value to be written.
647      * @throws IOException if an I/O error occurs.
648      */
649     @Override
650     public void writeBoolean(final boolean v) throws IOException {
651         randomAccessFile.writeBoolean(v);
652     }
653 
654     /**
655      * Writes a byte to the file as a one-byte value. The write starts at the
656      * current position of the file pointer.
657      *
658      * @param v a byte value to be written.
659      * @throws IOException if an I/O error occurs.
660      */
661     @Override
662     public void writeByte(final byte v) throws IOException {
663         randomAccessFile.writeByte(v);
664     }
665 
666     /**
667      * Writes provided value in the range 0-255 as an unsigned byte. The write
668      * starts at the current position of the file pointer.
669      *
670      * @param v a value to be written as an unsigned byte.
671      * @throws IOException if an I/O error occurs.
672      */
673     @Override
674     public void writeUnsignedByte(final short v) throws IOException {
675         final var data = (byte) (0xff & v);
676         randomAccessFile.writeByte(data);
677     }
678 
679     /**
680      * Writes a short to the file as two bytes, high byte first. The write
681      * starts at the current position of the file pointer.
682      *
683      * @param v a short to be written.
684      * @throws IOException if an I/O error occurs.
685      */
686     @Override
687     public void writeShort(final short v) throws IOException {
688         randomAccessFile.writeShort(v);
689     }
690 
691     /**
692      * Writes a short to the file as two bytes using provided endian type.
693      * If endian type is big endian, then natural byte order is preserved (and
694      * high byte is written first), if little endian order is chosen, then byte
695      * order is reversed.
696      *
697      * @param v          a short to be written.
698      * @param endianType endian type. If it is big endian, natural byte order is
699      *                   preserved, otherwise byte order is reversed.
700      * @throws IOException if an I/O error occurs.
701      */
702     @Override
703     public void writeShort(final short v, final EndianType endianType) throws IOException {
704         final var value = Util.toEndianType(endianType, v);
705         randomAccessFile.writeShort(value);
706     }
707 
708     /**
709      * Writes an unsigned short to the file as two bytes, high byte first.
710      * Provided integer value is converted to an unsigned short by taking into
711      * account only the two lower bytes. The write starts at the current
712      * position of the file pointer.
713      *
714      * @param v an unsigned short to be written (int is converted to unsigned
715      *          short).
716      * @throws IOException if an I/O error occurs.
717      */
718     @Override
719     public void writeUnsignedShort(final int v) throws IOException {
720         final var firstShortByte = 0xff & (v >> 8);
721         final var secondShortByte = 0xff & ((v << 8) >> 8);
722 
723         final var value = (short) ((firstShortByte << 8) | secondShortByte);
724 
725         randomAccessFile.writeShort(value);
726     }
727 
728     /**
729      * Writes an unsigned short to the file as two bytes, using provided endian
730      * type.
731      * Provided integer value is converted to an unsigned short by taking into
732      * account only the two lower bytes.
733      * If endian type is big endian, then natural byte order is preserved (and
734      * high byte is written first), if little endian order is chosen, then byte
735      * order is reversed.
736      * The write starts at the current position of the file pointer.
737      *
738      * @param v          an unsigned short to be written (int is converted to unsigned
739      *                   short).
740      * @param endianType endian type. If it is big endian, natural byte order is
741      *                   preserved, otherwise byte order is reversed.
742      * @throws IOException if an I/O error occurs.
743      */
744     @Override
745     public void writeUnsignedShort(final int v, final EndianType endianType) throws IOException {
746         final var firstShortByte = 0xff & (v >> 8);
747         final var secondShortByte = 0xff & ((v << 8) >> 8);
748 
749         final var machineValue = (short) ((firstShortByte << 8) | secondShortByte);
750         final var value = Util.toEndianType(endianType, machineValue);
751 
752         randomAccessFile.writeShort(value);
753     }
754 
755     /**
756      * Writes an int to the file as four bytes, high byte first. The write
757      * starts at the current position of the file pointer.
758      *
759      * @param v an int to be written.
760      * @throws IOException if an I/O error occurs.
761      */
762     @Override
763     public void writeInt(final int v) throws IOException {
764         randomAccessFile.writeInt(v);
765     }
766 
767     /**
768      * Writes an int to the file as four bytes, using provided endian type.
769      * If endian type is big endian, then natural byte order is preserved (and
770      * high byte is written first), if little endian order is chosen, then byte
771      * order is reversed.
772      * The write starts at the current position of the file pointer.
773      *
774      * @param v          an int to be written.
775      * @param endianType endian type. If it is big endian, natural byte order is
776      *                   preserved, otherwise byte order is reversed.
777      * @throws IOException if an I/O error occurs.
778      */
779     @Override
780     public void writeInt(final int v, final EndianType endianType) throws IOException {
781         final var value = Util.toEndianType(endianType, v);
782         randomAccessFile.writeInt(value);
783     }
784 
785     /**
786      * Writes an unsigned int to the file as four bytes, high byte first.
787      * Provided integer value is converted to an unsigned int by taking into
788      * account only the four lower bytes. The write starts at the current
789      * position of the file pointer.
790      *
791      * @param v an unsigned int to be written (long is converted to unsigned
792      *          int).
793      * @throws IOException if an I/O error occurs.
794      */
795     @Override
796     public void writeUnsignedInt(final long v) throws IOException {
797         final var firstIntByte = (int) (0xff & (v >> 24));
798         final var secondIntByte = (int) (0xff & ((v << 8) >> 24));
799         final var thirdIntByte = (int) (0xff & ((v << 16) >> 24));
800         final var fourthIntByte = (int) (0xff & ((v << 24) >> 24));
801 
802         final var value = (firstIntByte << 24) | (secondIntByte << 16) | (thirdIntByte << 8) | fourthIntByte;
803 
804         randomAccessFile.writeInt(value);
805     }
806 
807     /**
808      * Writes an unsigned int to the file as four bytes, using provided endian
809      * type.
810      * Provided integer value is converted to an unsigned int by taking into
811      * account only the four lower bytes.
812      * If endian type is big endian, then natural byte order is preserved (and
813      * high byte is written first), if little endian order is chosen, then byte
814      * order is reversed.
815      * The write starts at the current position of the file pointer.
816      *
817      * @param v          an unsigned int to be written (long is converted to unsigned
818      *                   short).
819      * @param endianType endian type. If it is big endian, natural byte order is
820      *                   preserved, otherwise byte order is reversed.
821      * @throws IOException if an I/O error occurs.
822      */
823     @Override
824     public void writeUnsignedInt(final long v, final EndianType endianType) throws IOException {
825         final var firstIntByte = (int) (0xff & (v >> 24));
826         final var secondIntByte = (int) (0xff & ((v << 8) >> 24));
827         final var thirdIntByte = (int) (0xff & ((v << 16) >> 24));
828         final var fourthIntByte = (int) (0xff & ((v << 24) >> 24));
829 
830         final var machineValue = (firstIntByte << 24) | (secondIntByte << 16) | (thirdIntByte << 8) | fourthIntByte;
831         final var value = Util.toEndianType(endianType, machineValue);
832 
833         randomAccessFile.writeInt(value);
834     }
835 
836     /**
837      * Writes a long to the file as eight bytes, high byte first. The write
838      * starts at the current position of the file pointer.
839      *
840      * @param v a long to be written.
841      * @throws IOException if an I/O error occurs.
842      */
843     @Override
844     public void writeLong(final long v) throws IOException {
845         randomAccessFile.writeLong(v);
846     }
847 
848     /**
849      * Writes a long to the file as eight bytes, using provided endian type.
850      * If endian type is big endian, then natural byte order is preserved (and
851      * high byte is written first), if little endian order is chosen, then byte
852      * order is reversed.
853      * The write starts at the current position of the file pointer.
854      *
855      * @param v          a long to be written.
856      * @param endianType endian type. If it is big endian, natural byte order is
857      *                   preserved, otherwise byte order is reversed.
858      * @throws IOException if an I/O error occurs.
859      */
860     @Override
861     public void writeLong(final long v, final EndianType endianType) throws IOException {
862         final var value = Util.toEndianType(endianType, v);
863         randomAccessFile.writeLong(value);
864     }
865 
866     /**
867      * Converts the float argument to an int using the floatToIntBits method in
868      * class Float, and then write that int value to the file as a four-byte
869      * quantity, high byte first. The write starts at the current position of
870      * the file pointer.
871      *
872      * @param v a float value to be written.
873      * @throws IOException if an I/O error occurs.
874      */
875     @Override
876     public void writeFloat(final float v) throws IOException {
877         randomAccessFile.writeFloat(v);
878     }
879 
880     /**
881      * Converts the float argument to an int using the floatToIntBits method in
882      * class Float, and then write that int value to the file as a four-byte
883      * quantity, using provided endian type.
884      * If endian type is big endian, then natural byte order is preserved (and
885      * high byte is written first), if little endian order is chosen, then byte
886      * order is reversed.
887      * The write starts at the current position of the file pointer.
888      *
889      * @param v          a float value to be written.
890      * @param endianType endian type. If it is big endian, natural byte order is
891      *                   preserved, otherwise byte order is reversed.
892      * @throws IOException if an I/O error occurs.
893      */
894     @Override
895     public void writeFloat(final float v, final EndianType endianType) throws IOException {
896         final var value = Util.toEndianType(endianType, v);
897         randomAccessFile.writeFloat(value);
898     }
899 
900     /**
901      * Converts the double argument to a long using the doubleToLongBits method
902      * in class Double, and then writes that long value to the file as an eight
903      * byte quantity, high byte first. The write starts at the current position
904      * of the file pointer.
905      *
906      * @param v a double value to be written.
907      * @throws IOException if an I/O error occurs.
908      */
909     @Override
910     public void writeDouble(final double v) throws IOException {
911         randomAccessFile.writeDouble(v);
912     }
913 
914     /**
915      * Converts the double argument to a long using the doubleToLongBits method
916      * in class Double, and then writes that long value to the file as an eight
917      * byte quantity, using provided endian type.
918      * If endian type is big endian, then natural byte order is preserved (and
919      * high byte is written first), if little endian order is chosen, then byte
920      * order is reversed.
921      * The write starts at the current position of the file pointer.
922      *
923      * @param v          a double value to be written.
924      * @param endianType endian type. If it is big endian, natural byte order is
925      *                   preserved, otherwise byte order is reversed.
926      * @throws IOException if an I/O error occurs.
927      */
928     @Override
929     public void writeDouble(final double v, final EndianType endianType) throws IOException {
930         final var value = Util.toEndianType(endianType, v);
931         randomAccessFile.writeDouble(value);
932     }
933 
934     /**
935      * Writes the string to the file as a sequence of bytes. Each character in
936      * the string is written out, in sequence, by discarding its high eight
937      * bits. The write starts at the current position of the file pointer.
938      *
939      * @param s a string of bytes to be written.
940      * @throws IOException if an I/O error occurs.
941      */
942     @Override
943     public void writeASCII(final String s) throws IOException {
944         randomAccessFile.writeBytes(s);
945     }
946 
947     /**
948      * Writes a string to the file using modified UTF-8 encoding in a machine-
949      * independent manner.
950      * First, two bytes are written to the file, starting at the current file
951      * pointer, as if by the writeShort method giving the number of bytes to
952      * follow. This value is the number of bytes actually written out, not the
953      * length of the string.
954      * Following the length, each character of the string is output, in
955      * sequence, using the modified UTF-8 encoding for each character.
956      *
957      * @param str a string to be written.
958      * @throws IOException if an I/O error occurs.
959      */
960     public void writeUTF(final String str) throws IOException {
961         randomAccessFile.writeUTF(str);
962     }
963 }