Bug 369103 - Enable Edapt Migrator

* Created AbstractOperationEmitter
* Created SerializedOperationEmitter for being able to iterate over a
FileBasedChangePackage without having to deserialize the operations

Change-Id: I9cd6068e949ac0ee68a7f1b5ac14553285e9c47f
Signed-off-by: Johannes Faltermeier <jfaltermeier@eclipsesource.com>
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 a943276..fe8f274 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
@@ -102,9 +102,11 @@
 	private static final String TEMP_FILE_PREFIX = "temp-"; //$NON-NLS-1$
 
 	/**
+	 * Suffix for temporary operation files.
+	 * 
 	 * @generated NOT
 	 */
-	private static final String TEMP_SUFFIX = ".temp"; //$NON-NLS-1$
+	public static final String TEMP_SUFFIX = ".temp"; //$NON-NLS-1$
 
 	// FIXME we also have a constant for this on the client side
 	private static final String OPERATION_FILE_SUFFIX = ".eoc"; //$NON-NLS-1$
diff --git a/bundles/org.eclipse.emf.emfstore.server.model/src/org/eclipse/emf/emfstore/internal/server/model/versioning/impl/persistent/AbstractOperationEmitter.java b/bundles/org.eclipse.emf.emfstore.server.model/src/org/eclipse/emf/emfstore/internal/server/model/versioning/impl/persistent/AbstractOperationEmitter.java
new file mode 100644
index 0000000..0e07075
--- /dev/null
+++ b/bundles/org.eclipse.emf.emfstore.server.model/src/org/eclipse/emf/emfstore/internal/server/model/versioning/impl/persistent/AbstractOperationEmitter.java
@@ -0,0 +1,275 @@
+/*******************************************************************************
+ * Copyright (c) 2011-2016 EclipseSource Muenchen GmbH and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Edgar Mueller - initial API and implementation
+ * Johannes Faltermeier - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.emf.emfstore.internal.server.model.versioning.impl.persistent;
+
+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.InputStream;
+import java.io.InputStreamReader;
+import java.io.PipedOutputStream;
+import java.io.RandomAccessFile;
+import java.nio.channels.Channels;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.io.input.ReversedLinesFileReader;
+import org.eclipse.emf.emfstore.internal.common.model.util.ModelUtil;
+
+/**
+ * Abstract super class for implementing types which emit operations when given an {@link ReadLineCapable} type.
+ *
+ */
+public abstract class AbstractOperationEmitter implements Closeable {
+
+	private final Direction direction;
+
+	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 AbstractOperationEmitter(Direction direction, File file) {
+		this.direction = direction;
+		operationsFile = file;
+		determineOperationOffsets();
+		currentOpIndex = direction == Direction.Forward ? 0 : backwardsOffsets.size() - 1;
+		initReader();
+	}
+
+	/**
+	 * @return the direction the {@link Direction}
+	 */
+	protected final Direction getDirection() {
+		return direction;
+	}
+
+	private void determineOperationOffsets() {
+		try {
+			final RandomAccessFile randomAccessFile = new RandomAccessFile(operationsFile, "r"); //$NON-NLS-1$
+			final InputStream inputStream = Channels.newInputStream(randomAccessFile.getChannel());
+			final InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
+			final BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
+
+			long filePointer = 0;
+			try {
+				String line;
+				while ((line = bufferedReader.readLine()) != null) {
+					filePointer += line.getBytes().length;
+					final long filePointerAfterReadline = randomAccessFile.getFilePointer();
+					randomAccessFile.seek(filePointer);
+					int byteAfterLine = randomAccessFile.read();
+					/*
+					 * Line is terminated by either:
+					 * \r\n
+					 * \r
+					 * \n
+					 */
+					if (byteAfterLine == '\r') {
+						filePointer += 1;
+						byteAfterLine = randomAccessFile.read();
+					}
+					if (byteAfterLine == '\n') {
+						filePointer += 1;
+					}
+					randomAccessFile.seek(filePointerAfterReadline);
+
+					if (line.contains(XmlTags.CHANGE_PACKAGE_START)) {
+						startOffset = filePointer;
+					} else if (line.contains(XmlTags.OPERATIONS_START_TAG)) {
+						forwardOffsets.add(filePointer);
+					} else if (line.contains(XmlTags.OPERATIONS_END_TAG)) {
+						backwardsOffsets.add(filePointer);
+					}
+				}
+			} finally {
+				bufferedReader.close();
+				randomAccessFile.close();
+			}
+		} catch (final IOException ex) {
+			ModelUtil.logException(ex);
+		}
+	}
+
+	private void initReader() {
+		try {
+			if (getDirection() == 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);
+		}
+	}
+
+	/**
+	 * Returns the current offset.
+	 *
+	 * @return the current offset
+	 */
+	public long getOffset() {
+		if (currentOpIndex < 0) {
+			return startOffset;
+		}
+		return backwardsOffsets.get(currentOpIndex);
+	}
+
+	/**
+	 * Since an XML Resource needs exactly one root object, we have to write a dummy object to the stream.
+	 *
+	 * @param pos the {@link PipedOutputStream}
+	 * @throws IOException in case there is a problem during write
+	 */
+	private static void writeDummyResourceToStream(PipedOutputStream pos) throws IOException {
+		pos.write(XmlTags.XML_RESOURCE_WITH_EOBJECT.getBytes());
+	}
+
+	/**
+	 * Reads the file in forward direction and writes read lines to the given stream.
+	 *
+	 * @param pos the output stream
+	 */
+	protected final void readForward(PipedOutputStream pos) {
+		try {
+			boolean operationsFound = false;
+			boolean withinOperationsElement = false;
+			final boolean isForwardDir = getDirection() == 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) {
+					operationsFound = true;
+					pos.write(line.getBytes());
+				}
+				line = reader.readLine();
+			}
+			if (line != null) {
+				withinOperationsElement = false;
+			}
+			if (!operationsFound) {
+				writeDummyResourceToStream(pos);
+			}
+		} 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 operationsFound = false;
+			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) {
+					operationsFound = true;
+					pos.write(line.getBytes());
+				}
+				line = reader.readLine();
+			}
+
+			if (!operationsFound) {
+				writeDummyResourceToStream(pos);
+			}
+		} catch (final IOException ex) {
+			ModelUtil.logException(ex);
+		} finally {
+			try {
+				pos.close();
+			} catch (final IOException ex) {
+				ModelUtil.logException(ex);
+			}
+		}
+	}
+
+	/**
+	 * Reads the file in backward direction and writes read lines to the given stream.
+	 *
+	 * @param pos the output strea,
+	 */
+	protected final void readBackward(PipedOutputStream pos) {
+
+		if (currentOpIndex < 0) {
+			try {
+				writeDummyResourceToStream(pos);
+				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);
+			}
+		}
+	}
+
+	private String getClosingTag(boolean isForward) {
+		return isForward ? XmlTags.OPERATIONS_END_TAG : XmlTags.OPERATIONS_START_TAG;
+	}
+
+	private String getOpeningTag(boolean isForward) {
+		return isForward ? XmlTags.OPERATIONS_START_TAG : XmlTags.OPERATIONS_END_TAG;
+	}
+
+	/**
+	 * Closes the emitter.
+	 */
+	public void close() {
+		try {
+			reader.close();
+		} catch (final IOException ex) {
+			ModelUtil.logException(ex);
+		}
+	}
+
+}
\ 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/OperationEmitter.java b/bundles/org.eclipse.emf.emfstore.server.model/src/org/eclipse/emf/emfstore/internal/server/model/versioning/impl/persistent/OperationEmitter.java
index 36136bd..5acdf25 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,22 +11,11 @@
  ******************************************************************************/
 package org.eclipse.emf.emfstore.internal.server.model.versioning.impl.persistent;
 
-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.InputStream;
-import java.io.InputStreamReader;
 import java.io.PipedInputStream;
 import java.io.PipedOutputStream;
-import java.io.RandomAccessFile;
-import java.nio.channels.Channels;
-import java.util.ArrayList;
-import java.util.List;
 
-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;
@@ -48,17 +37,7 @@
  * @author emueller
  *
  */
-public class OperationEmitter implements Closeable {
-
-	private final Direction direction;
-
-	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;
+public class OperationEmitter extends AbstractOperationEmitter {
 
 	/**
 	 * Constructor.
@@ -69,186 +48,7 @@
 	 *            the operation file
 	 */
 	public OperationEmitter(Direction direction, File file) {
-		this.direction = direction;
-		operationsFile = file;
-		determineOperationOffsets();
-		currentOpIndex = direction == Direction.Forward ? 0 : backwardsOffsets.size() - 1;
-		initReader();
-	}
-
-	private void determineOperationOffsets() {
-		try {
-			final RandomAccessFile randomAccessFile = new RandomAccessFile(operationsFile, "r"); //$NON-NLS-1$
-			final InputStream inputStream = Channels.newInputStream(randomAccessFile.getChannel());
-			final InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
-			final BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
-
-			long filePointer = 0;
-			try {
-				String line;
-				while ((line = bufferedReader.readLine()) != null) {
-					filePointer += line.getBytes().length;
-					final long filePointerAfterReadline = randomAccessFile.getFilePointer();
-					randomAccessFile.seek(filePointer);
-					int byteAfterLine = randomAccessFile.read();
-					/*
-					 * Line is terminated by either:
-					 * \r\n
-					 * \r
-					 * \n
-					 */
-					if (byteAfterLine == '\r') {
-						filePointer += 1;
-						byteAfterLine = randomAccessFile.read();
-					}
-					if (byteAfterLine == '\n') {
-						filePointer += 1;
-					}
-					randomAccessFile.seek(filePointerAfterReadline);
-
-					if (line.contains(XmlTags.CHANGE_PACKAGE_START)) {
-						startOffset = filePointer;
-					} else if (line.contains(XmlTags.OPERATIONS_START_TAG)) {
-						forwardOffsets.add(filePointer);
-					} else if (line.contains(XmlTags.OPERATIONS_END_TAG)) {
-						backwardsOffsets.add(filePointer);
-					}
-				}
-			} finally {
-				bufferedReader.close();
-				randomAccessFile.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);
-		}
-	}
-
-	/**
-	 * Returns the current offset.
-	 *
-	 * @return the current offset
-	 */
-	public long getOffset() {
-		if (currentOpIndex < 0) {
-			return startOffset;
-		}
-		return backwardsOffsets.get(currentOpIndex);
-	}
-
-	/**
-	 * Since an XML Resource needs exactly one root object, we have to write a dummy object to the stream.
-	 *
-	 * @param pos the {@link PipedOutputStream}
-	 * @throws IOException in case there is a problem during write
-	 */
-	private static void writeDummyResourceToStream(PipedOutputStream pos) throws IOException {
-		pos.write(XmlTags.XML_RESOURCE_WITH_EOBJECT.getBytes());
-	}
-
-	private void readForward(PipedOutputStream pos) {
-		try {
-			boolean operationsFound = false;
-			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) {
-					operationsFound = true;
-					pos.write(line.getBytes());
-				}
-				line = reader.readLine();
-			}
-			if (line != null) {
-				withinOperationsElement = false;
-			}
-			if (!operationsFound) {
-				writeDummyResourceToStream(pos);
-			}
-		} 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 operationsFound = false;
-			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) {
-					operationsFound = true;
-					pos.write(line.getBytes());
-				}
-				line = reader.readLine();
-			}
-
-			if (!operationsFound) {
-				writeDummyResourceToStream(pos);
-			}
-		} 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 {
-				writeDummyResourceToStream(pos);
-				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);
-			}
-		}
+		super(direction, file);
 	}
 
 	/**
@@ -265,7 +65,7 @@
 
 		new Thread(new Runnable() {
 			public void run() {
-				if (direction == Direction.Forward) {
+				if (getDirection() == Direction.Forward) {
 					readForward(pos);
 				} else {
 					readBackward(pos);
@@ -284,15 +84,6 @@
 		} finally {
 			pis.close();
 		}
-
-	}
-
-	private String getClosingTag(boolean isForward) {
-		return isForward ? XmlTags.OPERATIONS_END_TAG : XmlTags.OPERATIONS_START_TAG;
-	}
-
-	private String getOpeningTag(boolean isForward) {
-		return isForward ? XmlTags.OPERATIONS_START_TAG : XmlTags.OPERATIONS_END_TAG;
 	}
 
 	private EObject deserialize(final PipedInputStream pis) throws IOException {
@@ -303,15 +94,4 @@
 		xmlLoadImpl.load((XMLResource) resource, pis, ModelUtil.getResourceLoadOptions());
 		return resource.getContents().get(0);
 	}
-
-	/**
-	 * 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/SerializedOperationEmitter.java b/bundles/org.eclipse.emf.emfstore.server.model/src/org/eclipse/emf/emfstore/internal/server/model/versioning/impl/persistent/SerializedOperationEmitter.java
new file mode 100644
index 0000000..77486ba
--- /dev/null
+++ b/bundles/org.eclipse.emf.emfstore.server.model/src/org/eclipse/emf/emfstore/internal/server/model/versioning/impl/persistent/SerializedOperationEmitter.java
@@ -0,0 +1,86 @@
+/*******************************************************************************
+ * Copyright (c) 2011-2016 EclipseSource Muenchen GmbH and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Johannes Faltermeier - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.emf.emfstore.internal.server.model.versioning.impl.persistent;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.util.Scanner;
+
+import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.AbstractOperation;
+
+import com.google.common.base.Optional;
+
+/**
+ * Type for emitting {@link AbstractOperation}s in their serialized form when given a {@link ReadLineCapable} type.
+ *
+ * @author Johannes Faltermeier
+ *
+ */
+public class SerializedOperationEmitter extends AbstractOperationEmitter {
+
+	/**
+	 * Constructor.
+	 *
+	 * @param direction
+	 *            the {@link Direction} that is used for reading
+	 * @param file
+	 *            the operation file
+	 */
+	public SerializedOperationEmitter(Direction direction, File file) {
+		super(direction, file);
+	}
+
+	/**
+	 * Tries to parse an operation in the reading directions and emits it,
+	 * if parsing has been successful. The operation is returned in its parsed string representation.
+	 *
+	 * @return the successfully parsed operation as a string representation
+	 * @throws IOException
+	 *             in case reading from the {@link ReadLineCapable} fails
+	 */
+	public Optional<String> tryEmit() throws IOException {
+		final PipedOutputStream pos = new PipedOutputStream();
+		final PipedInputStream pis = new PipedInputStream(pos);
+
+		new Thread(new Runnable() {
+			public void run() {
+				if (getDirection() == Direction.Forward) {
+					readForward(pos);
+				} else {
+					readBackward(pos);
+				}
+			}
+		}).start();
+
+		try {
+			final String streamToString = convertStreamToString(pis);
+			if (XmlTags.XML_RESOURCE_WITH_EOBJECT.equals(streamToString)) {
+				return Optional.absent();
+			}
+			return Optional.of(streamToString);
+		} finally {
+			pis.close();
+		}
+	}
+
+	private static String convertStreamToString(InputStream inputStream) {
+		final Scanner scanner = new Scanner(inputStream);
+		scanner.useDelimiter("\\A"); //$NON-NLS-1$
+		final String result = scanner.hasNext() ? scanner.next() : ""; //$NON-NLS-1$
+		scanner.close();
+		return result;
+	}
+
+}