Bug 485361 - OperationEmitter should stream read operations
Change-Id: Id35d5774e1d4b74c5353f8d95de319cf73c0fafe
diff --git a/bundles/org.eclipse.emf.emfstore.server.model/src/org/eclipse/emf/emfstore/internal/server/model/versioning/impl/FileBasedChangePackageImpl.java b/bundles/org.eclipse.emf.emfstore.server.model/src/org/eclipse/emf/emfstore/internal/server/model/versioning/impl/FileBasedChangePackageImpl.java
index 013fca3..05fdee3 100644
--- a/bundles/org.eclipse.emf.emfstore.server.model/src/org/eclipse/emf/emfstore/internal/server/model/versioning/impl/FileBasedChangePackageImpl.java
+++ b/bundles/org.eclipse.emf.emfstore.server.model/src/org/eclipse/emf/emfstore/internal/server/model/versioning/impl/FileBasedChangePackageImpl.java
@@ -55,7 +55,6 @@
import org.eclipse.emf.emfstore.internal.server.model.versioning.impl.persistent.Direction;
import org.eclipse.emf.emfstore.internal.server.model.versioning.impl.persistent.FileBasedOperationIterable;
import org.eclipse.emf.emfstore.internal.server.model.versioning.impl.persistent.OperationEmitter;
-import org.eclipse.emf.emfstore.internal.server.model.versioning.impl.persistent.ReadLineCapable;
import org.eclipse.emf.emfstore.internal.server.model.versioning.impl.persistent.XmlTags;
import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.AbstractOperation;
import org.eclipse.emf.emfstore.server.ESCloseableIterable;
@@ -63,6 +62,7 @@
import com.google.common.base.Optional;
import com.google.common.collect.Iterables;
+import com.google.common.io.Closeables;
/**
* <!-- begin-user-doc -->
@@ -93,7 +93,7 @@
/**
* @generated NOT
*/
- private static final String EMPTY_CHANGE_PACKAGE = XmlTags.XML_HEADER + XmlTags.CHANGE_PACKAGE_START
+ private static final String EMPTY_CHANGE_PACKAGE = XmlTags.XML_HEADER + XmlTags.CHANGE_PACKAGE_START_WITH_NEWLINE
+ XmlTags.CHANGE_PACKAGE_END;
/**
@@ -687,16 +687,15 @@
*/
public List<AbstractOperation> removeAtEnd(int n) {
final List<AbstractOperation> ops = new ArrayList<AbstractOperation>();
- final OperationEmitter operationEmitter = new OperationEmitter(Direction.Backward);
- Optional<ReversedLinesFileReader> maybeReversedReader = Optional.absent();
- int counter = n;
+ final Optional<ReversedLinesFileReader> maybeReversedReader = Optional.absent();
+ RandomAccessFile raf = null;
+ OperationEmitter operationEmitter = null;
try {
- final ReversedLinesFileReader reversedReader = new ReversedLinesFileReader(new File(getTempFilePath()));
- maybeReversedReader = Optional.of(reversedReader);
+ operationEmitter = new OperationEmitter(Direction.Backward, new File(getTempFilePath()));
+ int counter = n;
AbstractOperation operation;
- final ReadLineCapable reader = ReadLineCapable.INSTANCE.create(reversedReader);
- final Optional<AbstractOperation> maybeOperation = operationEmitter.tryEmit(reader);
+ final Optional<AbstractOperation> maybeOperation = operationEmitter.tryEmit();
int removedOps = 0;
int removedLeafOps = 0;
@@ -710,16 +709,12 @@
updateCaches(-removedOps, -removedLeafOps);
- // FIXME: reuse ReadLineCapable?
- final long offset = operationEmitter.getOffset();
-
- final RandomAccessFile raf = new RandomAccessFile(getTempFilePath(), "rw"); //$NON-NLS-1$
- final long skip = raf.length() + 1 - offset;
+ raf = new RandomAccessFile(getTempFilePath(), "rw"); //$NON-NLS-1$
+ final long skip = operationEmitter.getOffset();
raf.seek(skip);
final byte[] bytes = asBytes(XmlTags.NEWLINE + XmlTags.CHANGE_PACKAGE_END);
raf.write(bytes);
- raf.setLength(skip + bytes.length);
- raf.close();
+ raf.setLength(skip + bytes.length);
return ops;
@@ -727,12 +722,14 @@
// ESException not available
throw new IllegalStateException(ex);
} finally {
- if (maybeReversedReader.isPresent()) {
- try {
- maybeReversedReader.get().close();
- } catch (final IOException ex) {
- ModelUtil.logException(ex);
+ try {
+ Closeables.close(raf, true);
+ Closeables.close(operationEmitter, true);
+ if (maybeReversedReader.isPresent()) {
+ Closeables.close(maybeReversedReader.get(), true);
}
+ } catch (final IOException ex) {
+ ModelUtil.logException(ex);
}
}
}
diff --git a/bundles/org.eclipse.emf.emfstore.server.model/src/org/eclipse/emf/emfstore/internal/server/model/versioning/impl/persistent/OperationEmitter.java b/bundles/org.eclipse.emf.emfstore.server.model/src/org/eclipse/emf/emfstore/internal/server/model/versioning/impl/persistent/OperationEmitter.java
index f056d89..99bbc7f 100644
--- a/bundles/org.eclipse.emf.emfstore.server.model/src/org/eclipse/emf/emfstore/internal/server/model/versioning/impl/persistent/OperationEmitter.java
+++ b/bundles/org.eclipse.emf.emfstore.server.model/src/org/eclipse/emf/emfstore/internal/server/model/versioning/impl/persistent/OperationEmitter.java
@@ -11,23 +11,33 @@
******************************************************************************/
package org.eclipse.emf.emfstore.internal.server.model.versioning.impl.persistent;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
+import java.io.BufferedReader;
+import java.io.Closeable;
+import java.io.DataInput;
+import java.io.File;
+import java.io.FileReader;
import java.io.IOException;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.io.RandomAccessFile;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
-import org.apache.commons.lang.StringUtils;
+import org.apache.commons.io.input.ReversedLinesFileReader;
import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
+import org.eclipse.emf.ecore.xmi.XMLResource;
+import org.eclipse.emf.ecore.xmi.impl.XMLHelperImpl;
+import org.eclipse.emf.ecore.xmi.impl.XMLLoadImpl;
+import org.eclipse.emf.ecore.xmi.impl.XMLResourceImpl;
import org.eclipse.emf.emfstore.internal.common.model.util.ModelUtil;
import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.AbstractOperation;
-import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.CreateDeleteOperation;
import com.google.common.base.Optional;
+import com.google.common.collect.Maps;
/**
* Type for emitting {@link AbstractOperation}s when given an {@link ReadLineCapable} type.
@@ -35,23 +45,67 @@
* @author emueller
*
*/
-public class OperationEmitter {
+public class OperationEmitter implements Closeable {
- private static final long NEWLINE_LENGTH = System.getProperty("line.separator").getBytes().length; //$NON-NLS-1$
-
- private boolean withinOperationsElement;
private final Direction direction;
- private long offset;
+
+ private final File operationsFile;
+
+ private ReadLineCapable reader;
+ private final List<Long> forwardOffsets = new ArrayList<Long>();
+ private final List<Long> backwardsOffsets = new ArrayList<Long>();
+ private int currentOpIndex;
+ private long startOffset;
/**
* Constructor.
*
* @param direction
* the {@link Direction} that is used for reading
+ * @param file
+ * the operation file
*/
- public OperationEmitter(Direction direction) {
+ public OperationEmitter(Direction direction, File file) {
this.direction = direction;
- offset = 0;
+ operationsFile = file;
+ determineOperationOffsets();
+ currentOpIndex = direction == Direction.Forward ? 0 : backwardsOffsets.size() - 1;
+ initReader();
+ }
+
+ private void determineOperationOffsets() {
+ try {
+ // TODO: are there any alternative implementations (e.g. Apache)?
+ final OptimizedRandomAccessFile raf = new OptimizedRandomAccessFile(operationsFile, "r"); //$NON-NLS-1$
+ try {
+ String line;
+ while ((line = raf.readLine()) != null) {
+ if (line.contains(XmlTags.CHANGE_PACKAGE_START)) {
+ startOffset = raf.getFilePointer();
+ } else if (line.contains(XmlTags.OPERATIONS_START_TAG)) {
+ forwardOffsets.add(raf.getFilePointer());
+ } else if (line.contains(XmlTags.OPERATIONS_END_TAG)) {
+ backwardsOffsets.add(raf.getFilePointer());
+ }
+ }
+ } finally {
+ raf.close();
+ }
+ } catch (final IOException ex) {
+ ModelUtil.logException(ex);
+ }
+ }
+
+ private void initReader() {
+ try {
+ if (direction == Direction.Forward) {
+ reader = ReadLineCapable.INSTANCE.create(new BufferedReader(new FileReader(operationsFile)));
+ } else {
+ reader = ReadLineCapable.INSTANCE.create(new ReversedLinesFileReader(operationsFile));
+ }
+ } catch (final IOException ex) {
+ ModelUtil.logException(ex);
+ }
}
/**
@@ -60,45 +114,125 @@
* @return the current offset
*/
public long getOffset() {
- return offset;
+ if (currentOpIndex < 0) {
+ return startOffset;
+ }
+ return backwardsOffsets.get(currentOpIndex);
+ }
+
+ private void readForward(PipedOutputStream pos) {
+ try {
+ boolean withinOperationsElement = false;
+ final boolean isForwardDir = direction == Direction.Forward;
+ final String closingTag = getClosingTag(isForwardDir);
+ String line = reader.readLine();
+ while (line != null && !line.contains(closingTag)) {
+ if (line.contains(getOpeningTag(isForwardDir))) {
+ withinOperationsElement = true;
+ } else if (withinOperationsElement) {
+ pos.write(line.getBytes());
+ }
+ line = reader.readLine();
+ }
+ if (line != null) {
+ withinOperationsElement = false;
+ }
+ } catch (final IOException ex) {
+ ModelUtil.logException(ex);
+ } finally {
+ try {
+ pos.close();
+ } catch (final IOException ex) {
+ ModelUtil.logException(ex);
+ }
+ }
+ }
+
+ private void readForward(DataInput reader, PipedOutputStream pos) {
+ try {
+ boolean withinOperationsElement = true;
+ final String closingTag = getClosingTag(true);
+ String line = reader.readLine();
+ while (line != null && !line.contains(closingTag)) {
+ if (line.contains(getOpeningTag(true))) {
+ withinOperationsElement = true;
+ } else if (withinOperationsElement && line.length() > 0) {
+ pos.write(line.getBytes());
+ }
+ line = reader.readLine();
+ }
+ } catch (final IOException ex) {
+ ModelUtil.logException(ex);
+ } finally {
+ try {
+ pos.close();
+ } catch (final IOException ex) {
+ ModelUtil.logException(ex);
+ }
+ }
+ }
+
+ private void readBackward(PipedOutputStream pos) {
+
+ if (currentOpIndex < 0) {
+ try {
+ pos.close();
+ } catch (final IOException ex) {
+ ModelUtil.logException(ex);
+ }
+ return;
+ }
+
+ final long offset = forwardOffsets.get(currentOpIndex);
+ currentOpIndex -= 1;
+
+ RandomAccessFile raf = null;
+ try {
+ raf = new RandomAccessFile(operationsFile, "r"); //$NON-NLS-1$
+ raf.skipBytes((int) offset);
+ readForward(raf, pos);
+ } catch (final IOException ex) {
+ ModelUtil.logException(ex);
+ } finally {
+ try {
+ raf.close();
+ } catch (final IOException ex) {
+ ModelUtil.logException(ex);
+ }
+ }
}
/**
- * Given a reader, tries to parse an operation and emit it is,
+ * Tries to parse an operation in the reading directions and emits it,
* if parsing has been successful.
*
- * @param reader
- * the reader that is used to de-serialize operations
* @return the successfully parsed operation
* @throws IOException
* in case reading from the {@link ReadLineCapable} fails
*/
- public Optional<AbstractOperation> tryEmit(ReadLineCapable reader) throws IOException {
- final List<String> readLines = new ArrayList<String>();
- withinOperationsElement = false;
- String line;
- final boolean isForwardDir = direction == Direction.Forward;
- while ((line = reader.readLine()) != null && !line.contains(getClosingTag(isForwardDir))) {
- if (line.contains(getOpeningTag(isForwardDir))) {
- withinOperationsElement = true;
- } else if (withinOperationsElement) {
- readLines.add(line);
+ public Optional<AbstractOperation> tryEmit() throws IOException {
+ final PipedOutputStream pos = new PipedOutputStream();
+ final PipedInputStream pis = new PipedInputStream(pos);
+
+ new Thread(new Runnable() {
+ public void run() {
+ if (direction == Direction.Forward) {
+ readForward(pos);
+ } else {
+ readBackward(pos);
+ }
}
- offset += line.getBytes().length;
- offset += NEWLINE_LENGTH;
- }
- if (line != null) {
- withinOperationsElement = false;
- offset += line.getBytes().length;
- }
- if (!withinOperationsElement && !readLines.isEmpty()) {
- if (direction == Direction.Backward) {
- Collections.reverse(readLines);
- }
- return Optional.of(deserialize(StringUtils.join(readLines, StringUtils.EMPTY)));
+ }).start();
+
+ try {
+ return Optional.of(deserialize(pis));
+ } catch (final IOException e) {
+ // e.printStackTrace();
+ return Optional.absent();
+ } finally {
+ pis.close();
}
- return Optional.absent();
}
private String getClosingTag(boolean isForward) {
@@ -109,18 +243,25 @@
return isForward ? XmlTags.OPERATIONS_START_TAG : XmlTags.OPERATIONS_END_TAG;
}
- private AbstractOperation deserialize(final String string) throws IOException {
+ private AbstractOperation deserialize(final PipedInputStream pis) throws IOException {
final ResourceSet resourceSet = new ResourceSetImpl();
final Resource resource = resourceSet.createResource(URI.createURI("virtualResource.xmi")); //$NON-NLS-1$
- final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
- outputStream.write(string.getBytes());
- final ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
- resource.load(inputStream, ModelUtil.getResourceLoadOptions());
+ ((XMLResourceImpl) resource).setIntrinsicIDToEObjectMap(Maps.<String, EObject> newLinkedHashMap());
+ final XMLLoadImpl xmlLoadImpl = new XMLLoadImpl(new XMLHelperImpl());
+ xmlLoadImpl.load((XMLResource) resource, pis, ModelUtil.getResourceLoadOptions());
final AbstractOperation operation = (AbstractOperation) resource.getContents().get(0);
- if (operation instanceof CreateDeleteOperation) {
- ((CreateDeleteOperation) operation).getSubOperations();
- }
+ // ((XMLResourceImpl) resource).getIntrinsicIDToEObjectMap().clear();
return operation;
-
}
-}
\ No newline at end of file
+
+ /**
+ * Closes the emitter.
+ */
+ public void close() {
+ try {
+ reader.close();
+ } catch (final IOException ex) {
+ ModelUtil.logException(ex);
+ }
+ }
+}
diff --git a/bundles/org.eclipse.emf.emfstore.server.model/src/org/eclipse/emf/emfstore/internal/server/model/versioning/impl/persistent/OperationIterator.java b/bundles/org.eclipse.emf.emfstore.server.model/src/org/eclipse/emf/emfstore/internal/server/model/versioning/impl/persistent/OperationIterator.java
index 79597dd..f3c3448 100644
--- a/bundles/org.eclipse.emf.emfstore.server.model/src/org/eclipse/emf/emfstore/internal/server/model/versioning/impl/persistent/OperationIterator.java
+++ b/bundles/org.eclipse.emf.emfstore.server.model/src/org/eclipse/emf/emfstore/internal/server/model/versioning/impl/persistent/OperationIterator.java
@@ -11,13 +11,10 @@
******************************************************************************/
package org.eclipse.emf.emfstore.internal.server.model.versioning.impl.persistent;
-import java.io.BufferedReader;
import java.io.File;
-import java.io.FileReader;
import java.io.IOException;
import java.util.Iterator;
-import org.apache.commons.io.input.ReversedLinesFileReader;
import org.apache.commons.lang.NotImplementedException;
import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.AbstractOperation;
@@ -34,7 +31,7 @@
private Optional<AbstractOperation> operation;
private OperationEmitter operationEmitter;
- private ReadLineCapable reader;
+ // private ReadLineCapable reader;
private boolean isInitialized;
private final String operationsFilePath;
private final Direction direction;
@@ -54,17 +51,7 @@
}
private void init() {
- operationEmitter = new OperationEmitter(direction);
- try {
- if (direction == Direction.Forward) {
- reader = ReadLineCapable.INSTANCE
- .create(new BufferedReader(new FileReader(new File(operationsFilePath))));
- } else {
- reader = ReadLineCapable.INSTANCE.create(new ReversedLinesFileReader(new File(operationsFilePath)));
- }
- } catch (final IOException ex1) {
- ex1.printStackTrace();
- }
+ operationEmitter = new OperationEmitter(direction, new File(operationsFilePath));
isInitialized = true;
}
@@ -79,13 +66,14 @@
init();
}
try {
- operation = operationEmitter.tryEmit(reader);
+ operation = operationEmitter.tryEmit();
final boolean hasNext = operation.isPresent();
if (!hasNext) {
close();
}
return hasNext;
} catch (final IOException ex) {
+ // TODO
// replace operations file
ex.printStackTrace();
}
@@ -120,13 +108,6 @@
* Closes the underlying operations file.
*/
public void close() {
- try {
- if (reader != null) {
- reader.close();
- }
- } catch (final IOException ex) {
- // TODO
- ex.printStackTrace();
- }
+ operationEmitter.close();
}
}
diff --git a/bundles/org.eclipse.emf.emfstore.server.model/src/org/eclipse/emf/emfstore/internal/server/model/versioning/impl/persistent/OptimizedRandomAccessFile.java b/bundles/org.eclipse.emf.emfstore.server.model/src/org/eclipse/emf/emfstore/internal/server/model/versioning/impl/persistent/OptimizedRandomAccessFile.java
new file mode 100644
index 0000000..3ab37e6
--- /dev/null
+++ b/bundles/org.eclipse.emf.emfstore.server.model/src/org/eclipse/emf/emfstore/internal/server/model/versioning/impl/persistent/OptimizedRandomAccessFile.java
@@ -0,0 +1,1088 @@
+/*
+ * The MIT License
+ * Copyright 2013 Joos Kiener <Joos.Kiener@gmail.com>.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package org.eclipse.emf.emfstore.internal.server.model.versioning.impl.persistent;
+
+import java.io.EOFException;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.io.UTFDataFormatException;
+
+/**
+ * <p>
+ * Wrapper of {@link java.io.RandomAccessFile
+ * <code>RandomAccessFile</code>} that has an readLine method performing similar
+ * to {@link java.io.BufferedReader#readLine()} while keeping the random access
+ * functionality.
+ * </p>
+ *
+ * <p>
+ * {@link java.io.RandomAccessFile#readLine()} is very slow as it reads a
+ * file byte by byte. This here will perform 2 orders of magnitude faster.
+ * For thread-safety all read and write methods are synchronized.
+ * </p>
+ * </p>
+ * If the underlying {@link java.nio.channels.FileChannel
+ * <code>FileChannel</code>} is manipulated, the behavior is unpredictable.
+ * Therefore this class does not expose the <code>getFileChannel()</code> method
+ * of <code>RandomAccessFile</code>.
+ * </p>
+ *
+ * @author Joos Kiener <Joos.Kiener@gmail.com>
+ */
+public class OptimizedRandomAccessFile {
+
+ private static final int BUFFER_SIZE = 8192;
+ private static int defaultExpectedLineLength = 80;
+ private final RandomAccessFile raf;
+ private Long actualFilePointer;
+ private final char[] charBuffer;
+ private int nChars, nextChar;
+ private final int bufferSize;
+ private long lastOffset;
+ private boolean skipLF;
+
+ /**
+ * see {@link RandomAccessFile#RandomAccessFile(String,String)}
+ *
+ * @param name path to the text file
+ * @param mode r, rw, rws, rwd
+ * @throws FileNotFoundException
+ */
+ public OptimizedRandomAccessFile(String name, String mode) throws FileNotFoundException {
+ this(name != null ? new File(name) : null, mode);
+ }
+
+ /**
+ *
+ * see {@link RandomAccessFile#RandomAccessFile(File,String)}
+ *
+ * @param file
+ * @param mode
+ * @throws FileNotFoundException
+ */
+ public OptimizedRandomAccessFile(File file, String mode) throws FileNotFoundException {
+ raf = new RandomAccessFile(file, mode);
+ actualFilePointer = null;
+ bufferSize = BUFFER_SIZE;
+ charBuffer = new char[bufferSize];
+ }
+
+ /**
+ * <p>
+ * Returns the opaque file descriptor object associated with this
+ * stream.
+ * </p>
+ *
+ * @return the file descriptor object associated with this stream.
+ * @exception IOException if an I/O error occurs.
+ * @see java.io.FileDescriptor
+ */
+ public final FileDescriptor getFD() throws IOException {
+ return raf.getFD();
+ }
+
+ /**
+ * Reads a byte of data from this file. The byte is returned as an integer
+ * in the range 0 to 255 (
+ * <code>0x00-0x0ff</code>). This method behaves similar to the
+ * {@link java.io.BufferedReader#read()} method of
+ * <code>BufferedReader</code>.
+ *
+ * @return the next byte of data, or <code>-1</code> if the end of the file
+ * has been reached.
+ * @exception IOException if an I/O error occurs. Not thrown if end-of-file
+ * has been reached.
+ */
+ public synchronized int read() throws IOException {
+ // resetPosition();
+ for (;;) {
+ if (nextChar >= nChars) {
+ fill();
+ if (nextChar >= nChars) {
+ return -1;
+ }
+ }
+ if (skipLF) {
+ skipLF = false;
+ if (charBuffer[nextChar] == '\n') {
+ nextChar++;
+ continue;
+ }
+ }
+ final int result = charBuffer[nextChar++];
+ actualFilePointer++;
+ return result;
+ }
+ }
+
+ /**
+ * Reads characters into a portion of an array, reading from the underlying
+ * stream if necessary.
+ */
+ private int read1(byte[] cbuf, int off, int len) throws IOException {
+ if (nextChar >= nChars) {
+ /*
+ * If the requested length is at least as large as the buffer and
+ * if line feeds are not being skipped, do not bother to copy the
+ * characters into the local buffer. In this way buffered streams
+ * will cascade harmlessly.
+ */
+ if (len >= charBuffer.length && !skipLF) {
+ actualFilePointer = null;
+ return raf.read(cbuf, off, len);
+ }
+ fill();
+ }
+ if (nextChar >= nChars) {
+ return -1;
+ }
+ if (skipLF) {
+ skipLF = false;
+ if (charBuffer[nextChar] == '\n') {
+ nextChar++;
+ if (nextChar >= nChars) {
+ fill();
+ }
+ if (nextChar >= nChars) {
+ return -1;
+ }
+ }
+ }
+ final int n = Math.min(len, nChars - nextChar);
+ for (int i = 0; i < n; i++) {
+ cbuf[off + i] = (byte) charBuffer[nextChar + i];
+ }
+ // System.arraycopy(charBuffer, nextChar, cbuf, off, n);
+ nextChar += n;
+ actualFilePointer += n;
+ return n;
+ }
+
+ /**
+ * Reads up to
+ * <code>len</code> bytes of data from this file into an array of bytes.
+ * This method behaves similar to the
+ * {@link java.io.BufferedReader#read(byte[],int,int)} method of
+ * <code>BufferedReader</code>.
+ *
+ * @param b the buffer into which the data is read.
+ * @param off the start offset in array <code>b</code> at which the data is
+ * written.
+ * @param len the maximum number of bytes read.
+ * @return the total number of bytes read into the buffer, or
+ * <code>-1</code> if there is no more data because the end of the file has
+ * been reached.
+ * @exception IOException If the first byte cannot be read for any reason
+ * other than end of file, or if the random access file has been closed, or
+ * if some other I/O error occurs.
+ * @exception NullPointerException If <code>b</code> is <code>null</code>.
+ * @exception IndexOutOfBoundsException If <code>off</code> is negative,
+ * <code>len</code> is negative, or <code>len</code> is greater than
+ * <code>b.length - off</code>
+ */
+ public synchronized int read(byte b[], int off, int len) throws IOException {
+ // resetPosition();
+ if (off < 0 || off > b.length || len < 0 || off + len > b.length || off + len < 0) {
+ throw new IndexOutOfBoundsException();
+ } else if (len == 0) {
+ return 0;
+ }
+
+ int n = read1(b, off, len);
+ if (n <= 0) {
+ return n;
+ }
+ while (n < len) {
+ final int n1 = read1(b, off + n, len - n);
+ if (n1 <= 0) {
+ break;
+ }
+ n += n1;
+ }
+ return n;
+ // return raf.read(b, off, len);
+ }
+
+ /**
+ * Reads up to
+ * <code>b.length</code> bytes of data from this file into an array of
+ * bytes. This method behaves similar to the
+ * {@link java.io.BufferedReader#read(byte[])} method of
+ * <code>BufferedReader</code>.
+ *
+ * @param b the buffer into which the data is read.
+ * @return the total number of bytes read into the buffer, or
+ * <code>-1</code> if there is no more data because the end of this file has
+ * been reached.
+ * @exception IOException If the first byte cannot be read for any reason
+ * other than end of file, or if the random access file has been closed, or
+ * if some other I/O error occurs.
+ * @exception NullPointerException If <code>b</code> is <code>null</code>.
+ */
+ public synchronized int read(byte b[]) throws IOException {
+ return read(b, 0, b.length);
+ }
+
+ /**
+ * Reads
+ * <code>b.length</code> bytes from this file into the byte array, starting
+ * at the current file pointer. This method reads repeatedly from the file
+ * until the requested number of bytes are read. This method blocks until
+ * the requested number of bytes are read, the end of the stream is
+ * detected, or an exception is thrown.
+ *
+ * @param b the buffer into which the data is read.
+ * @exception EOFException if this file reaches the end before reading all
+ * the bytes.
+ * @exception IOException if an I/O error occurs.
+ */
+ public synchronized final void readFully(byte b[]) throws IOException {
+ resetPosition();
+ raf.readFully(b);
+ }
+
+ /**
+ * Reads exactly
+ * <code>len</code> bytes from this file into the byte array, starting at
+ * the current file pointer. This method reads repeatedly from the file
+ * until the requested number of bytes are read. This method blocks until
+ * the requested number of bytes are read, the end of the stream is
+ * detected, or an exception is thrown.
+ *
+ * @param b the buffer into which the data is read.
+ * @param off the start offset of the data.
+ * @param len the number of bytes to read.
+ * @exception EOFException if this file reaches the end before reading all
+ * the bytes.
+ * @exception IOException if an I/O error occurs.
+ */
+ public synchronized final void readFully(byte b[], int off, int len) throws IOException {
+ resetPosition();
+ raf.readFully(b, off, len);
+ }
+
+ /**
+ * Attempts to skip over
+ * <code>n</code> bytes of input discarding the skipped bytes.
+ * <p>
+ *
+ * This method may skip over some smaller number of bytes, possibly zero.
+ * This may result from any of a number of conditions; reaching end of file
+ * before
+ * <code>n</code> bytes have been skipped is only one possibility. This
+ * method never throws an
+ * <code>EOFException</code>. The actual number of bytes skipped is
+ * returned. If
+ * <code>n</code> is negative, no bytes are skipped.
+ *
+ * @param n the number of bytes to be skipped.
+ * @return the actual number of bytes skipped.
+ * @exception IOException if an I/O error occurs.
+ */
+ public synchronized int skipBytes(int n) throws IOException {
+
+ if (n < 0L) {
+ throw new IllegalArgumentException("skip value is negative");
+ }
+
+ int r = n;
+ while (r > 0) {
+ if (nextChar >= nChars) {
+ fill();
+ }
+ if (nextChar >= nChars) /* EOF */ {
+ break;
+ }
+ if (skipLF) {
+ skipLF = false;
+ if (charBuffer[nextChar] == '\n') {
+ nextChar++;
+ }
+ }
+ final long d = nChars - nextChar;
+ if (r <= d) {
+ nextChar += r;
+ r = 0;
+ break;
+ } else {
+ r -= d;
+ nextChar = nChars;
+ }
+ }
+ final int skipped = n - r;
+ actualFilePointer += skipped;
+ return skipped;
+
+ // resetPosition();
+ // return raf.skipBytes(n);
+ }
+
+ // 'Write' primitives
+ /**
+ * Writes the specified byte to this file. The write starts at the current
+ * file pointer.
+ *
+ * @param b the <code>byte</code> to be written.
+ * @exception IOException if an I/O error occurs.
+ */
+ public synchronized void write(int b) throws IOException {
+ resetPosition();
+ raf.write(b);
+ }
+
+ /**
+ * Writes
+ * <code>b.length</code> bytes from the specified byte array to this file,
+ * starting at the current file pointer.
+ *
+ * @param b the data.
+ * @exception IOException if an I/O error occurs.
+ */
+ public synchronized void write(byte b[]) throws IOException {
+ resetPosition();
+ raf.write(b, 0, b.length);
+ }
+
+ /**
+ * Writes
+ * <code>len</code> bytes from the specified byte array starting at offset
+ * <code>off</code> to this file.
+ *
+ * @param b the data.
+ * @param off the start offset in the data.
+ * @param len the number of bytes to write.
+ * @exception IOException if an I/O error occurs.
+ */
+ public synchronized void write(byte b[], int off, int len) throws IOException {
+ resetPosition();
+ raf.write(b, off, len);
+ }
+
+ // 'Random access' stuff
+ /**
+ * Returns the current offset in this file.
+ *
+ * @return the offset from the beginning of the file, in bytes, at which the
+ * next read or write occurs.
+ * @exception IOException if an I/O error occurs.
+ */
+ public synchronized long getFilePointer() throws IOException {
+ if (actualFilePointer == null) {
+ return raf.getFilePointer();
+ } else {
+ return actualFilePointer;
+ }
+ }
+
+ /**
+ * Sets the file-pointer offset, measured from the beginning of this file,
+ * at which the next read or write occurs. The offset may be set beyond the
+ * end of the file. Setting the offset beyond the end of the file does not
+ * change the file length. The file length will change only by writing after
+ * the offset has been set beyond the end of the file.
+ *
+ * @param pos the offset position, measured in bytes from the beginning of
+ * the file, at which to set the file pointer.
+ * @exception IOException if <code>pos</code> is less than <code>0</code> or
+ * if an I/O error occurs.
+ */
+ public synchronized void seek(long pos) throws IOException {
+ actualFilePointer = null;
+ resetPosition();
+ raf.seek(pos);
+ }
+
+ /**
+ * Returns the length of this file.
+ *
+ * @return the length of this file, measured in bytes.
+ * @exception IOException if an I/O error occurs.
+ */
+ public synchronized long length() throws IOException {
+ return raf.length();
+ }
+
+ /**
+ * Sets the length of this file.
+ *
+ * <p>
+ * If the present length of the file as returned by the
+ * <code>length</code> method is greater than the
+ * <code>newLength</code> argument then the file will be truncated. In this
+ * case, if the file offset as returned by the
+ * <code>getFilePointer</code> method is greater than
+ * <code>newLength</code> then after this method returns the offset will be
+ * equal to
+ * <code>newLength</code>.
+ *
+ * <p>
+ * If the present length of the file as returned by the
+ * <code>length</code> method is smaller than the
+ * <code>newLength</code> argument then the file will be extended. In this
+ * case, the contents of the extended portion of the file are not defined.
+ *
+ * @param newLength The desired length of the file
+ * @exception IOException If an I/O error occurs
+ * @since 1.2
+ */
+ public synchronized void setLength(long newLength) throws IOException {
+ if (newLength < raf.length()) {
+ resetPosition();
+ }
+ raf.setLength(newLength);
+ }
+
+ /**
+ * Closes this random access file stream and releases any system resources
+ * associated with the stream. A closed random access file cannot perform
+ * input or output operations and cannot be reopened.
+ *
+ * <p>
+ * If this file has an associated channel then the channel is closed as
+ * well.
+ *
+ * @exception IOException if an I/O error occurs.
+ *
+ * @revised 1.4
+ * @spec JSR-51
+ */
+ public void close() throws IOException {
+ raf.close();
+ }
+
+ //
+ // Some "reading/writing Java data types" methods stolen from
+ // DataInputStream and DataOutputStream.
+ //
+ /**
+ * Reads a
+ * <code>boolean</code> from this file. This method reads a single byte from
+ * the file, starting at the current file pointer. A value of
+ * <code>0</code> represents
+ * <code>false</code>. Any other value represents
+ * <code>true</code>. This method blocks until the byte is read, the end of
+ * the stream is detected, or an exception is thrown.
+ *
+ * @return the <code>boolean</code> value read.
+ * @exception EOFException if this file has reached the end.
+ * @exception IOException if an I/O error occurs.
+ */
+ public synchronized final boolean readBoolean() throws IOException {
+ resetPosition();
+ return raf.readBoolean();
+ }
+
+ /**
+ * Reads a signed eight-bit value from this file. This method reads a byte
+ * from the file, starting from the current file pointer. If the byte read
+ * is
+ * <code>b</code>, where
+ * <code>0 <= b <= 255</code>, then the result is:
+ * null null null null null null null null null null null null null null <blockquote>
+ *
+ * <pre>
+ * (byte) (b)
+ * </pre>
+ *
+ * </blockquote>
+ * <p>
+ * This method blocks until the byte is read, the
+ * end of the stream is detected, or an exception is thrown.
+ *
+ * @return the next byte of this file as a signed eight-bit
+ * <code>byte</code>.
+ * @exception EOFException if this file has reached the end.
+ * @exception IOException if an I/O error occurs.
+ */
+ public synchronized final byte readByte() throws IOException {
+ resetPosition();
+ return raf.readByte();
+ }
+
+ /**
+ * Reads an unsigned eight-bit number from this file. This method reads a
+ * byte from this file, starting at the current file pointer, and returns
+ * that byte.
+ * <p>
+ * This method blocks until the byte is read, the end of the
+ * stream is detected, or an exception is thrown.
+ *
+ * @return the next byte of this file, interpreted as an unsigned eight-bit
+ * number.
+ * @exception EOFException if this file has reached the end.
+ * @exception IOException if an I/O error occurs.
+ */
+ public synchronized final int readUnsignedByte() throws IOException {
+ resetPosition();
+ return raf.readUnsignedByte();
+ }
+
+ /**
+ * Reads a signed 16-bit number from this file. The method reads two bytes
+ * from this file, starting at the current file pointer. If the two bytes
+ * read, in order, are
+ * <code>b1</code> and
+ * <code>b2</code>, where each of the two values is between
+ * <code>0</code> and
+ * <code>255</code>, inclusive, then the result is equal to: null null null
+ * null null null null null null null null null null null <blockquote>
+ *
+ * <pre>
+ * (short) ((b1 << 8) | b2)
+ * </pre>
+ *
+ * </blockquote>
+ * <p>
+ * This method blocks until the two bytes are read,
+ * the end of the stream is detected, or an exception is thrown.
+ *
+ * @return the next two bytes of this file, interpreted as a signed 16-bit
+ * number.
+ * @exception EOFException if this file reaches the end before reading two
+ * bytes.
+ * @exception IOException if an I/O error occurs.
+ */
+ public synchronized final short readShort() throws IOException {
+ resetPosition();
+ return raf.readShort();
+ }
+
+ /**
+ * Reads an unsigned 16-bit number from this file. This method reads two
+ * bytes from the file, starting at the current file pointer. If the bytes
+ * read, in order, are
+ * <code>b1</code> and
+ * <code>b2</code>, where
+ * <code>0 <= b1, b2 <= 255</code>, then the
+ * result is equal to: null null null null null null null null null null
+ * null null null null <blockquote>
+ *
+ * <pre>
+ * (b1 << 8) | b2
+ * </pre>
+ *
+ * </blockquote>
+ * <p>
+ * This method blocks until the two bytes are read,
+ * the end of the stream is detected, or an exception is thrown.
+ *
+ * @return the next two bytes of this file, interpreted as an unsigned
+ * 16-bit integer.
+ * @exception EOFException if this file reaches the end before reading two
+ * bytes.
+ * @exception IOException if an I/O error occurs.
+ */
+ public synchronized final int readUnsignedShort() throws IOException {
+ resetPosition();
+ return raf.readUnsignedShort();
+ }
+
+ /**
+ * Reads a character from this file. This method reads two bytes from the
+ * file, starting at the current file pointer. If the bytes read, in order,
+ * are
+ * <code>b1</code> and
+ * <code>b2</code>, where
+ * <code>0 <= b1, b2 <= 255</code>, then the
+ * result is equal to: null null null null null null null null null null
+ * null null null null <blockquote>
+ *
+ * <pre>
+ * (char) ((b1 << 8) | b2)
+ * </pre>
+ *
+ * </blockquote>
+ * <p>
+ * This method blocks until the two bytes are read,
+ * the end of the stream is detected, or an exception is thrown.
+ *
+ * @return the next two bytes of this file, interpreted as a
+ * <code>char</code>.
+ * @exception EOFException if this file reaches the end before reading two
+ * bytes.
+ * @exception IOException if an I/O error occurs.
+ */
+ public synchronized final char readChar() throws IOException {
+ resetPosition();
+ return raf.readChar();
+ }
+
+ /**
+ * Reads a signed 32-bit integer from this file. This method reads 4 bytes
+ * from the file, starting at the current file pointer. If the bytes read,
+ * in order, are
+ * <code>b1</code>,
+ * <code>b2</code>,
+ * <code>b3</code>, and
+ * <code>b4</code>, where
+ * <code>0 <= b1, b2, b3, b4 <= 255</code>, then
+ * the result is equal to: null null null null null null null null null null
+ * null null null null <blockquote>
+ *
+ * <pre>
+ * (b1 << 24) | (b2 << 16) + (b3 << 8) + b4
+ * </pre>
+ *
+ * </blockquote>
+ * <p>
+ * This method blocks until the four bytes are read,
+ * the end of the stream is detected, or an exception is thrown.
+ *
+ * @return the next four bytes of this file, interpreted as an
+ * <code>int</code>.
+ * @exception EOFException if this file reaches the end before reading four
+ * bytes.
+ * @exception IOException if an I/O error occurs.
+ */
+ public synchronized final int readInt() throws IOException {
+ resetPosition();
+ return raf.readInt();
+ }
+
+ /**
+ * Reads a signed 64-bit integer from this file. This method reads eight
+ * bytes from the file, starting at the current file pointer. If the bytes
+ * read, in order, are
+ * <code>b1</code>,
+ * <code>b2</code>,
+ * <code>b3</code>,
+ * <code>b4</code>,
+ * <code>b5</code>,
+ * <code>b6</code>,
+ * <code>b7</code>, and
+ * <code>b8,</code> where: null null null null null null null null null null
+ * null null null null <blockquote>
+ *
+ * <pre>
+ * 0 <= b1, b2, b3, b4, b5, b6, b7, b8 <=255,
+ * </pre>
+ *
+ * </blockquote>
+ * <p>
+ * then the result is equal to: null null null null
+ * null null null null null null null null null null
+ * <p>
+ * <blockquote>
+ *
+ * <pre>
+ * ((long) b1 << 56) + ((long) b2 << 48) + ((long) b3 << 40) + ((long) b4 << 32) + ((long) b5 << 24)
+ * + ((long) b6 << 16) + ((long) b7 << 8) + b8
+ * </pre>
+ *
+ * </blockquote>
+ * <p>
+ * This method blocks until the eight bytes are
+ * read, the end of the stream is detected, or an exception is thrown.
+ *
+ * @return the next eight bytes of this file, interpreted as a
+ * <code>long</code>.
+ * @exception EOFException if this file reaches the end before reading eight
+ * bytes.
+ * @exception IOException if an I/O error occurs.
+ */
+ public synchronized final long readLong() throws IOException {
+ resetPosition();
+ return raf.readLong();
+ }
+
+ /**
+ * Reads a
+ * <code>float</code> from this file. This method reads an
+ * <code>int</code> value, starting at the current file pointer, as if by
+ * the
+ * <code>readInt</code> method and then converts that
+ * <code>int</code> to a
+ * <code>float</code> using the
+ * <code>intBitsToFloat</code> method in class
+ * <code>Float</code>.
+ * <p>
+ * This method blocks until the four bytes are read,
+ * the end of the stream is detected, or an exception is thrown.
+ *
+ * @return the next four bytes of this file, interpreted as a
+ * <code>float</code>.
+ * @exception EOFException if this file reaches the end before reading four
+ * bytes.
+ * @exception IOException if an I/O error occurs.
+ * @see java.io.RandomAccessFile#readInt()
+ * @see java.lang.Float#intBitsToFloat(int)
+ */
+ public synchronized final float readFloat() throws IOException {
+ resetPosition();
+ return raf.readFloat();
+ }
+
+ /**
+ * Reads a
+ * <code>double</code> from this file. This method reads a
+ * <code>long</code> value, starting at the current file pointer, as if by
+ * the
+ * <code>readLong</code> method and then converts that
+ * <code>long</code> to a
+ * <code>double</code> using the
+ * <code>longBitsToDouble</code> method in class
+ * <code>Double</code>.
+ * <p>
+ * This method blocks until the eight bytes are
+ * read, the end of the stream is detected, or an exception is thrown.
+ *
+ * @return the next eight bytes of this file, interpreted as a
+ * <code>double</code>.
+ * @exception EOFException if this file reaches the end before reading eight
+ * bytes.
+ * @exception IOException if an I/O error occurs.
+ * @see java.io.RandomAccessFile#readLong()
+ * @see java.lang.Double#longBitsToDouble(long)
+ */
+ public synchronized final double readDouble() throws IOException {
+ resetPosition();
+ return raf.readDouble();
+ }
+
+ /**
+ * <p>
+ * Read the file line by line omitting the line separator.
+ * </p>
+ * <p>
+ * see
+ * {@link java.io.RandomAccessFile#readLine() readLine()} and see
+ * {@link java.io.BufferedReader#readLine(boolean) readLine(boolean ignoreLF)}.
+ * <p>
+ *
+ * <p>
+ * Subsequent calls of this method are buffered. If certain other
+ * methods that are affected by the current position of the reader in the
+ * file is called after this method, the position is set to the start of the
+ * next line and the buffer is invalidated.
+ * </p>
+ *
+ * <p>
+ * This method is copied from
+ * {@link java.io.BufferedReader BufferedReader} with minor changes like
+ * tracking position (offset) were next line starts.
+ * </p>
+ *
+ * @return the next line of text from this file, or null if end of file is
+ * encountered before even one byte is read.
+ * @exception IOException if an I/O error occurs.
+ */
+ public synchronized final String readLine(boolean ignoreLF) throws IOException {
+
+ StringBuilder s = null;
+ int startChar;
+ int separatorIndex = 0;
+
+ boolean omitLF = ignoreLF || skipLF;
+
+ bufferLoop: for (;;) {
+
+ if (nextChar >= nChars) {
+ fill();
+ }
+ if (nextChar >= nChars) { /* EOF */
+ if (s != null && s.length() > 0) {
+ // EOF -> hence no need to adjust position in file
+ // changed by fill()
+ return s.toString();
+ } else {
+ return null;
+ }
+ }
+ boolean eol = false;
+ char c = 0;
+ int i;
+
+ /* Skip a leftover '\n', if necessary */
+ if (omitLF && charBuffer[nextChar] == '\n') {
+ nextChar++;
+ }
+ skipLF = false;
+ omitLF = false;
+
+ charLoop: for (i = nextChar; i < nChars; i++) {
+ c = charBuffer[i];
+ if (c == '\n' || c == '\r') {
+ eol = true;
+ break charLoop;
+ }
+ }
+
+ startChar = nextChar;
+ nextChar = i;
+
+ if (eol) {
+ String str;
+ if (s == null) {
+ str = new String(charBuffer, startChar, i - startChar);
+ } else {
+ s.append(charBuffer, startChar, i - startChar);
+ str = s.toString();
+ }
+ nextChar++;
+ if (c == '\r') {
+ skipLF = true;
+ if (nextChar >= nChars) {
+ fill();
+ }
+ if (charBuffer[nextChar] == '\n') {
+ separatorIndex = 1;
+ }
+ }
+ actualFilePointer = lastOffset + nextChar + separatorIndex;
+ return str;
+ }
+
+ if (s == null) {
+ s = new StringBuilder(defaultExpectedLineLength);
+ }
+ s.append(charBuffer, startChar, i - startChar);
+ }
+ }
+
+ /**
+ * see {@link #readLine(boolean) readLine(boolean ignoreLF)}
+ *
+ * @return
+ * @throws IOException
+ */
+ public synchronized String readLine() throws IOException {
+ return readLine(false);
+ }
+
+ private void fill() throws IOException {
+
+ lastOffset = raf.getFilePointer();
+ actualFilePointer = lastOffset;
+ final byte[] buffer = new byte[bufferSize];
+ final int n = raf.read(buffer);
+ if (n > 0) {
+ nChars = n;
+ nextChar = 0;
+ }
+ for (int i = 0; i < buffer.length; i++) {
+ charBuffer[i] = (char) buffer[i];
+ }
+ }
+
+ /**
+ * Reads in a string from this file. The string has been encoded using a <a
+ * href="DataInput.html#modified-utf-8">modified UTF-8</a> format.
+ * <p>
+ * The
+ * first two bytes are read, starting from the current file pointer, as if
+ * by
+ * <code>readUnsignedShort</code>. This value gives the number of following
+ * bytes that are in the encoded string, not the length of the resulting
+ * string. The following bytes are then interpreted as bytes encoding
+ * characters in the modified UTF-8 format and are converted into
+ * characters.
+ * <p>
+ * This method blocks until all the bytes are read, the end
+ * of the stream is detected, or an exception is thrown.
+ *
+ * @return a Unicode string.
+ * @exception EOFException if this file reaches the end before reading all
+ * the bytes.
+ * @exception IOException if an I/O error occurs.
+ * @exception UTFDataFormatException if the bytes do not represent valid
+ * modified UTF-8 encoding of a Unicode string.
+ * @see java.io.RandomAccessFile#readUnsignedShort()
+ */
+ public synchronized final String readUTF() throws IOException {
+ resetPosition();
+ return raf.readUTF();
+ }
+
+ /**
+ * Writes a
+ * <code>boolean</code> to the file as a one-byte value. The value
+ * <code>true</code> is written out as the value
+ * <code>(byte)1</code>; the value
+ * <code>false</code> is written out as the value
+ * <code>(byte)0</code>. The write starts at the current position of the
+ * file pointer.
+ *
+ * @param v a <code>boolean</code> value to be written.
+ * @exception IOException if an I/O error occurs.
+ */
+ public synchronized final void writeBoolean(boolean v) throws IOException {
+ resetPosition();
+ raf.writeBoolean(v);
+ }
+
+ /**
+ * Writes a
+ * <code>byte</code> to the file as a one-byte value. The write starts at
+ * the current position of the file pointer.
+ *
+ * @param v a <code>byte</code> value to be written.
+ * @exception IOException if an I/O error occurs.
+ */
+ public synchronized final void writeByte(int v) throws IOException {
+ resetPosition();
+ raf.writeByte(v);
+ }
+
+ /**
+ * Writes a
+ * <code>short</code> to the file as two bytes, high byte first. The write
+ * starts at the current position of the file pointer.
+ *
+ * @param v a <code>short</code> to be written.
+ * @exception IOException if an I/O error occurs.
+ */
+ public synchronized final void writeShort(int v) throws IOException {
+ resetPosition();
+ raf.writeShort(v);
+ }
+
+ /**
+ * Writes a
+ * <code>char</code> to the file as a two-byte value, high byte first. The
+ * write starts at the current position of the file pointer.
+ *
+ * @param v a <code>char</code> value to be written.
+ * @exception IOException if an I/O error occurs.
+ */
+ public synchronized final void writeChar(int v) throws IOException {
+ resetPosition();
+ raf.writeChar(v);
+ }
+
+ /**
+ * Writes an
+ * <code>int</code> to the file as four bytes, high byte first. The write
+ * starts at the current position of the file pointer.
+ *
+ * @param v an <code>int</code> to be written.
+ * @exception IOException if an I/O error occurs.
+ */
+ public synchronized final void writeInt(int v) throws IOException {
+ resetPosition();
+ raf.writeInt(v);
+ }
+
+ /**
+ * Writes a
+ * <code>long</code> to the file as eight bytes, high byte first. The write
+ * starts at the current position of the file pointer.
+ *
+ * @param v a <code>long</code> to be written.
+ * @exception IOException if an I/O error occurs.
+ */
+ public synchronized final void writeLong(long v) throws IOException {
+ resetPosition();
+ raf.writeLong(v);
+ }
+
+ /**
+ * Converts the float argument to an
+ * <code>int</code> using the
+ * <code>floatToIntBits</code> method in class
+ * <code>Float</code>, and then writes that
+ * <code>int</code> value to the file as a four-byte quantity, high byte
+ * first. The write starts at the current position of the file pointer.
+ *
+ * @param v a <code>float</code> value to be written.
+ * @exception IOException if an I/O error occurs.
+ * @see java.lang.Float#floatToIntBits(float)
+ */
+ public synchronized final void writeFloat(float v) throws IOException {
+ resetPosition();
+ raf.writeFloat(v);
+ }
+
+ /**
+ * Converts the double argument to a
+ * <code>long</code> using the
+ * <code>doubleToLongBits</code> method in class
+ * <code>Double</code>, and then writes that
+ * <code>long</code> value to the file as an eight-byte quantity, high byte
+ * first. The write starts at the current position of the file pointer.
+ *
+ * @param v a <code>double</code> value to be written.
+ * @exception IOException if an I/O error occurs.
+ * @see java.lang.Double#doubleToLongBits(double)
+ */
+ public synchronized final void writeDouble(double v) throws IOException {
+ resetPosition();
+ raf.writeDouble(v);
+ }
+
+ /**
+ * Writes the string to the file as a sequence of bytes. Each character in
+ * the string is written out, in sequence, by discarding its high eight
+ * bits. The write starts at the current position of the file pointer.
+ *
+ * @param s a string of bytes to be written.
+ * @exception IOException if an I/O error occurs.
+ */
+ public synchronized final void writeBytes(String s) throws IOException {
+ resetPosition();
+ raf.writeBytes(s);
+ }
+
+ /**
+ * Writes a string to the file as a sequence of characters. Each character
+ * is written to the data output stream as if by the
+ * <code>writeChar</code> method. The write starts at the current position
+ * of the file pointer.
+ *
+ * @param s a <code>String</code> value to be written.
+ * @exception IOException if an I/O error occurs.
+ * @see java.io.RandomAccessFile#writeChar(int)
+ */
+ public synchronized final void writeChars(String s) throws IOException {
+ resetPosition();
+ raf.writeChars(s);
+ }
+
+ /**
+ * Writes a string to the file using <a
+ * href="DataInput.html#modified-utf-8">modified UTF-8</a> encoding in a
+ * machine-independent manner.
+ * <p>
+ * First, two bytes are written to the file,
+ * starting at the current file pointer, as if by the
+ * <code>writeShort</code> method giving the number of bytes to follow. This
+ * value is the number of bytes actually written out, not the length of the
+ * string. Following the length, each character of the string is output, in
+ * sequence, using the modified UTF-8 encoding for each character.
+ *
+ * @param str a string to be written.
+ * @exception IOException if an I/O error occurs.
+ */
+ public synchronized final void writeUTF(String str) throws IOException {
+ resetPosition();
+ raf.writeUTF(str);
+ }
+
+ private void resetPosition() throws IOException {
+ if (actualFilePointer != null) {
+ raf.seek(actualFilePointer);
+ actualFilePointer = null;
+ }
+ nChars = 0;
+ nextChar = 0;
+ }
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.emf.emfstore.server.model/src/org/eclipse/emf/emfstore/internal/server/model/versioning/impl/persistent/XmlTags.java b/bundles/org.eclipse.emf.emfstore.server.model/src/org/eclipse/emf/emfstore/internal/server/model/versioning/impl/persistent/XmlTags.java
index ee74dfd..f46efbc 100644
--- a/bundles/org.eclipse.emf.emfstore.server.model/src/org/eclipse/emf/emfstore/internal/server/model/versioning/impl/persistent/XmlTags.java
+++ b/bundles/org.eclipse.emf.emfstore.server.model/src/org/eclipse/emf/emfstore/internal/server/model/versioning/impl/persistent/XmlTags.java
@@ -36,8 +36,12 @@
* Change package opening tag.
*/
public static final String CHANGE_PACKAGE_START = "<org.eclipse.emf.emfstore.internal.server.model.versioning:ChangePackage " //$NON-NLS-1$
- + "xmi:version=\"2.0\" xmlns:xmi=\"http://www.omg.org/XMI\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:org.eclipse.emf.emfstore.internal.server.model.versioning=\"http://eclipse.org/emf/emfstore/server/model/versioning\" xmlns:org.eclipse.emf.emfstore.internal.server.model.versioning.operations=\"http://eclipse.org/emf/emfstore/server/model/versioning/operations\">" //$NON-NLS-1$
- + NEWLINE;
+ + "xmi:version=\"2.0\" xmlns:xmi=\"http://www.omg.org/XMI\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:org.eclipse.emf.emfstore.internal.server.model.versioning=\"http://eclipse.org/emf/emfstore/server/model/versioning\" xmlns:org.eclipse.emf.emfstore.internal.server.model.versioning.operations=\"http://eclipse.org/emf/emfstore/server/model/versioning/operations\">"; //$NON-NLS-1$
+
+ /**
+ * Change package opening tag with newline.
+ */
+ public static final String CHANGE_PACKAGE_START_WITH_NEWLINE = CHANGE_PACKAGE_START + NEWLINE;
/**
* Change package closing tag.