package org.jnect.emfstore; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.Calendar; import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.util.EContentAdapter; import org.jnect.bodymodel.Body; import org.jnect.bodymodel.PositionedElement; public class BodyBuffer { public enum Coordinate { X, Y, Z } Body body; final int NEEDED_CHANGES; List<float[]> buffer = Collections.synchronizedList(new LinkedList<float[]>()); public BodyBuffer() { this.body = EMFStorage.createAndFillBody(); body.eAdapters().add(new CommitBodyChangesAdapter()); // 3 changes (x, y, z) in every body element NEEDED_CHANGES = body.eContents().size() * 3; } public Body getBufferBody() { return body; } private class CommitBodyChangesAdapter extends EContentAdapter { private int currChanges = 0; @Override public void notifyChanged(Notification notification) { if (++currChanges % NEEDED_CHANGES == 0) { nextBody(); } } } void nextBody() { float[] state = new float[NEEDED_CHANGES]; assert NEEDED_CHANGES / 3 == body.eContents().size(); for (int i = 0; i < NEEDED_CHANGES / 3; i++) { EObject elem = body.eContents().get(i); if (!(elem instanceof PositionedElement)) continue; PositionedElement pos = (PositionedElement) elem; state[i * 3] = pos.getX(); state[i * 3 + 1] = pos.getY(); state[i * 3 + 2] = pos.getZ(); } buffer.add(state); } public void flushToBody(Body flushBody, ICommitter committer, int commitResolution, IProgressMonitor monitor) { final int BODY_PART_COUNT = NEEDED_CHANGES / 3; assert flushBody.eContents().size() == BODY_PART_COUNT; int commitCount = buffer.size() / commitResolution; int roundUp = buffer.size() % commitResolution == 0 ? 0 : 1; commitCount += roundUp; monitor.beginTask("Saving to EMFStore Server", buffer.size() + commitCount); EList<EObject> bodyContents = flushBody.eContents(); long timeBefore = Calendar.getInstance().getTimeInMillis(); synchronized (buffer) { org.eclipse.emf.emfstore.client.model.Configuration.setAutoSave(false); Iterator<float[]> bufferIt = buffer.iterator(); int collectedBodyChanges = 0; while (bufferIt.hasNext() && !monitor.isCanceled()) { monitor.subTask("Writing to EMFStore..."); float[] values = bufferIt.next(); for (int i = 0; i < BODY_PART_COUNT/* - 1 */; i++) { EObject elem = bodyContents.get(i); if (!(elem instanceof PositionedElement)) continue; PositionedElement pos = (PositionedElement) elem; setAndForceModification(pos, Coordinate.X, values[i * 3]); setAndForceModification(pos, Coordinate.Y, values[i * 3 + 1]); setAndForceModification(pos, Coordinate.Z, values[i * 3 + 2]); } collectedBodyChanges++; if (collectedBodyChanges == commitResolution) { monitor.subTask("Committing..."); committer.commit(); collectedBodyChanges = 0; monitor.worked(1); } monitor.worked(1); } if (collectedBodyChanges != 0) { assert roundUp == 1 : "Only when the number of changes is not dividable by the commit resolution there should be changes left..."; committer.commit(); monitor.worked(1); } buffer.clear(); org.eclipse.emf.emfstore.client.model.Configuration.setAutoSave(true); } long timeAfter = Calendar.getInstance().getTimeInMillis(); System.out.println("Saving took: " + (timeAfter - timeBefore) / 1000 + " seconds."); } /** * Sets the positioned element to a new value, and adds a dummy change epsilon if the value did not change, so that * all change packages are fully filled. * * @param pos * @param coord * @param newValue */ private void setAndForceModification(PositionedElement pos, Coordinate coord, float newValue) { float oldVal; // change the smallest amount possible // Note: Math.nextAfter() is Java 1.6 // Use Float.floatToIntBits(arg0), increment it by one and reassign it if java 1.5 should be a requirement in // the future switch (coord) { case X: oldVal = pos.getX(); if (oldVal == newValue) newValue = Math.nextAfter(newValue, newValue + 1); pos.setX(newValue); break; case Y: oldVal = pos.getY(); if (oldVal == newValue) newValue = Math.nextAfter(newValue, newValue + 1); pos.setY(newValue); break; case Z: oldVal = pos.getZ(); if (oldVal == newValue) newValue = Math.nextAfter(newValue, newValue + 1); pos.setZ(newValue); break; default: assert false; oldVal = Float.NaN; // disable compiler not-init warning below } assert oldVal != newValue; } public void storeToFile(String string) { BufferedWriter writer = null; try { writer = new BufferedWriter(new FileWriter(new File(string))); StringBuilder currLine = new StringBuilder(); for (float[] vals : buffer) { for (float value : vals) { currLine.append(value + " "); } writer.write(currLine.substring(0, currLine.length() - 1) + "\n"); currLine.setLength(0); } writer.close(); } catch (IOException e) { e.printStackTrace(); } finally { try { if (writer != null) writer.close(); } catch (IOException e) { e.printStackTrace(); } } } }