blob: 5dbde8c870bd7d9acded82ec8a095a79fe24658f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015 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
******************************************************************************/
package org.eclipse.emf.emfstore.internal.server.model.versioning.operations.util;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.LineIterator;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.emfstore.internal.common.model.util.FileUtil;
import org.eclipse.emf.emfstore.internal.common.model.util.ModelUtil;
import org.eclipse.emf.emfstore.internal.server.model.versioning.AbstractChangePackage;
import org.eclipse.emf.emfstore.internal.server.model.versioning.ChangePackage;
import org.eclipse.emf.emfstore.internal.server.model.versioning.ChangePackageEnvelope;
import org.eclipse.emf.emfstore.internal.server.model.versioning.FileBasedChangePackage;
import org.eclipse.emf.emfstore.internal.server.model.versioning.VersioningFactory;
import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.AbstractOperation;
import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.CompositeOperation;
import org.eclipse.emf.emfstore.server.ESCloseableIterable;
/**
* Change package helper class.
*
* @author emueller
*
*/
public final class ChangePackageUtil {
private ChangePackageUtil() {
}
/**
* Creates a new {@link AbstractChangePackage} depending on the client configuration behavior whether
* to create in-memory of file-based change packages.
*
* @param useInMemoryChangePackage
* whether an in-memory change package should be created
*
* @return the created change package
*/
public static AbstractChangePackage createChangePackage(boolean useInMemoryChangePackage) {
if (useInMemoryChangePackage) {
return VersioningFactory.eINSTANCE.createChangePackage();
}
final FileBasedChangePackage fileBasedChangePackage = VersioningFactory.eINSTANCE
.createFileBasedChangePackage();
fileBasedChangePackage.initialize(FileUtil.createLocationForTemporaryChangePackage());
return fileBasedChangePackage;
}
/**
* Given a single change package, splits it into multiple fragments.
*
* @param changePackage
* the change package to be splitted
* @param changePackageFragmentSize
* the max number of operations a single fragment may consists of
* @return an iterator for the created fragments
*/
public static Iterator<ChangePackageEnvelope> splitChangePackage(final FileBasedChangePackage changePackage,
final int changePackageFragmentSize) {
return new ChangePackageSplittingIterator(changePackageFragmentSize, changePackage);
}
/**
* Count all leaf operations of a collection of {@link AbstractOperation}s.
*
* @param operations
* a collection of operations
* @return the leaf operation count of all involved operations
*/
public static int countLeafOperations(Collection<AbstractOperation> operations) {
int ret = 0;
for (final AbstractOperation operation : operations) {
if (operation instanceof CompositeOperation) {
ret = ret + getSize((CompositeOperation) operation);
} else {
ret++;
}
}
return ret;
}
/**
* Count all leaf operations of a single {@link AbstractOperation}s.
*
* @param operation
* a single operation
* @return the leaf operation count of the given operation
*/
public static int countLeafOperations(AbstractOperation operation) {
return countLeafOperations(Collections.singleton(operation));
}
/**
* Count all leaf operations of all operations contained in the given list of {@link ChangePackage}s.
*
* @param changePackages
* list of change packages
* @return the leaf operation count of all operations contained in the list of change packages
*/
public static int countLeafOperations(List<AbstractChangePackage> changePackages) {
int count = 0;
for (final AbstractChangePackage changePackage : changePackages) {
final ESCloseableIterable<AbstractOperation> operations = changePackage.operations();
try {
for (final AbstractOperation operation : operations.iterable()) {
count += countLeafOperations(operation);
}
} finally {
operations.close();
}
}
return count;
}
/**
* Count all root operations within the given list of {@link ChangePackage}s.
*
* @param changePackages
* list of change packages
* @return the root operation count of all change packages
*/
public static int countOperations(List<AbstractChangePackage> changePackages) {
int count = 0;
for (final AbstractChangePackage changePackage : changePackages) {
count += changePackage.size();
}
return count;
}
private static int getSize(CompositeOperation compositeOperation) {
int ret = 0;
final EList<AbstractOperation> subOperations = compositeOperation.getSubOperations();
for (final AbstractOperation abstractOperation : subOperations) {
if (abstractOperation instanceof CompositeOperation) {
ret = ret + getSize((CompositeOperation) abstractOperation);
} else {
ret++;
}
}
return ret;
}
/**
* Iterator for splitting change packages.
*
* @author emueller
*
*/
private static final class ChangePackageSplittingIterator implements Iterator<ChangePackageEnvelope> {
private final int changePackageFragmentSize;
private final FileBasedChangePackage changePackage;
private int fragmentIndex;
private int count;
private ChangePackageEnvelope envelope;
private boolean isInitialized;
ChangePackageSplittingIterator(int changePackageFragmentSize, FileBasedChangePackage changePackage) {
this.changePackageFragmentSize = changePackageFragmentSize;
this.changePackage = changePackage;
ModelUtil.logProjectDetails(
MessageFormat.format("Splitting change package {0}", changePackage.getFilePath()), null, null, null, //$NON-NLS-1$
null, -1);
}
public boolean hasNext() {
if (!isInitialized) {
init();
}
if (envelope == null) {
envelope = VersioningFactory.eINSTANCE.createChangePackageEnvelope();
envelope.setFragmentCount(count);
}
final List<String> readLines = readLines(fragmentIndex * changePackageFragmentSize, changePackage,
changePackageFragmentSize);
envelope.getFragment().addAll(readLines);
envelope.setFragmentIndex(fragmentIndex);
if (!envelope.getFragment().isEmpty() || fragmentIndex == 0) {
ModelUtil.logProjectDetails(MessageFormat.format("Fragment {1} for Change package {0} prepared", //$NON-NLS-1$
changePackage.getFilePath(), fragmentIndex), null, null, null, null, -1);
return true;
}
ModelUtil.logProjectDetails(
MessageFormat.format("No more change package fragments for {0}", changePackage.getFilePath()), null, //$NON-NLS-1$
null, null, null, -1);
return false;
}
private void init() {
LineNumberReader lineNumberReader = null;
try {
lineNumberReader = new LineNumberReader(new FileReader(new File(changePackage.getTempFilePath())));
lineNumberReader.skip(Long.MAX_VALUE);
final int lines = lineNumberReader.getLineNumber() + 1;
count = lines / changePackageFragmentSize;
if (lines % changePackageFragmentSize != 0) {
count += 1;
}
} catch (final FileNotFoundException ex) {
throw new IllegalStateException(ex);
} catch (final IOException ex) {
throw new IllegalStateException(ex);
} finally {
IOUtils.closeQuietly(lineNumberReader);
}
isInitialized = true;
}
private List<String> readLines(int from, final FileBasedChangePackage changePackage,
final int changePackageFragmentSize) {
int readLines = 0;
FileReader reader;
final List<String> lines = new ArrayList<String>();
try {
reader = new FileReader(new File(changePackage.getTempFilePath()));
final LineIterator lineIterator = new LineIterator(reader);
int read = 0;
while (read < from) {
if (!lineIterator.hasNext()) {
return lines;
}
lineIterator.next();
read += 1;
}
while (readLines < changePackageFragmentSize && lineIterator.hasNext()) {
final String nextLine = lineIterator.next();
readLines += 1;
lines.add(nextLine);
}
} catch (final FileNotFoundException ex) {
throw new IllegalStateException(ex);
}
return lines;
}
public ChangePackageEnvelope next() {
if (envelope == null) {
final boolean hasNext = hasNext();
if (!hasNext) {
throw new NoSuchElementException();
}
}
final ChangePackageEnvelope ret = envelope;
envelope = null;
fragmentIndex += 1;
return ret;
}
public void remove() {
throw new UnsupportedOperationException();
}
}
}