/* * Copyright (c) 2010-2016 Eike Stepper (Berlin, Germany) 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: * Eike Stepper - initial API and implementation */ package org.eclipse.emf.cdo.spi.common.revision; import org.eclipse.emf.cdo.common.branch.CDOBranch; import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; import org.eclipse.emf.cdo.common.branch.CDOBranchVersion; import org.eclipse.emf.cdo.common.id.CDOID; import org.eclipse.emf.cdo.common.protocol.CDODataInput; import org.eclipse.emf.cdo.common.protocol.CDODataOutput; import org.eclipse.emf.cdo.common.revision.CDORevision; import org.eclipse.net4j.util.CheckUtil; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EClassifier; import java.io.IOException; import java.text.MessageFormat; import java.util.List; /** * If the meaning of this type isn't clear, there really should be more of a description here... * * @author Eike Stepper * @since 3.0 */ public abstract class RevisionInfo { private static final int NO_RESULT = 0; private static final int POINTER_RESULT = 1; private static final int DETACHED_RESULT = 2; private static final int NORMAL_RESULT = 3; private CDOID id; private CDOBranchPoint requestedBranchPoint; private InternalCDORevision result; private SyntheticCDORevision synthetic; protected RevisionInfo(CDOID id, CDOBranchPoint requestedBranchPoint) { CheckUtil.checkArg(requestedBranchPoint, "requestedBranchPoint"); this.id = id; this.requestedBranchPoint = requestedBranchPoint; } protected RevisionInfo(CDODataInput in, CDOBranchPoint requestedBranchPoint) throws IOException { CheckUtil.checkArg(requestedBranchPoint, "requestedBranchPoint"); id = in.readCDOID(); this.requestedBranchPoint = requestedBranchPoint; } public abstract Type getType(); public final CDOID getID() { return id; } public final CDOBranchPoint getRequestedBranchPoint() { return requestedBranchPoint; } public InternalCDORevision getResult() { return result; } public void setResult(InternalCDORevision result) { this.result = result; } public SyntheticCDORevision getSynthetic() { return synthetic; } public void setSynthetic(SyntheticCDORevision synthetic) { this.synthetic = synthetic; } public abstract boolean isLoadNeeded(); public void write(CDODataOutput out) throws IOException { out.writeByte(getType().ordinal()); out.writeCDOID(getID()); } @Override public String toString() { return MessageFormat.format("RevisionInfo.{0}[{1}, {2}]", getClass().getSimpleName(), id, requestedBranchPoint); //$NON-NLS-1$ } public static RevisionInfo read(CDODataInput in, CDOBranchPoint requestedBranchPoint) throws IOException { byte ordinal = in.readByte(); Type type = Type.values()[ordinal]; switch (type) { case AVAILABLE_NORMAL: return new Available.Normal(in, requestedBranchPoint); case AVAILABLE_POINTER: return new Available.Pointer(in, requestedBranchPoint); case AVAILABLE_DETACHED: return new Available.Detached(in, requestedBranchPoint); case MISSING: return new Missing(in, requestedBranchPoint); default: throw new IOException("Invalid revision info type: " + type); } } public void execute(InternalCDORevisionManager revisionManager, int referenceChunk) { SyntheticCDORevision[] synthetics = new SyntheticCDORevision[1]; result = revisionManager.getRevision(getID(), requestedBranchPoint, referenceChunk, CDORevision.DEPTH_NONE, true, synthetics); synthetic = synthetics[0]; } /** * @deprecated Not called anymore by the framework */ @Deprecated public void writeResult(CDODataOutput out, int referenceChunk) throws IOException { writeResult(out, referenceChunk, null); } /** * @since 4.1 */ public void writeResult(CDODataOutput out, int referenceChunk, CDOBranchPoint securityContext) throws IOException { writeRevision(out, referenceChunk, securityContext); writeResult(out, synthetic, referenceChunk, securityContext); } public void readResult(CDODataInput in) throws IOException { readRevision(in); synthetic = (SyntheticCDORevision)readResult(in, getID(), requestedBranchPoint.getBranch(), result); } public void processResult(InternalCDORevisionManager revisionManager, List<CDORevision> results, SyntheticCDORevision[] synthetics, int i) { if (result instanceof DetachedCDORevision) { results.add(null); } else { results.add(result); } if (result != null) { revisionManager.addRevision(result); } if (synthetic != null) { revisionManager.addRevision(synthetic); if (synthetic instanceof PointerCDORevision) { PointerCDORevision pointer = (PointerCDORevision)synthetic; CDOBranchVersion target = pointer.getTarget(); if (target != result && target instanceof InternalCDORevision) { revisionManager.addRevision((CDORevision)target); } } if (synthetics != null) { synthetics[i] = synthetic; } } } /** * @deprecated Not called anymore by the framework */ @Deprecated protected void writeRevision(CDODataOutput out, int referenceChunk) throws IOException { writeRevision(out, referenceChunk, null); } /** * @since 4.1 */ protected void writeRevision(CDODataOutput out, int referenceChunk, CDOBranchPoint securityContext) throws IOException { out.writeCDORevision(result, referenceChunk, securityContext); } protected void readRevision(CDODataInput in) throws IOException { result = (InternalCDORevision)in.readCDORevision(); } /** * @since 4.0 * @deprecated Call {@link #writeResult(CDODataOutput, InternalCDORevision, int, CDOBranchPoint)} */ @Deprecated public static void writeResult(CDODataOutput out, InternalCDORevision revision, int referenceChunk) throws IOException { writeResult(out, revision, referenceChunk, null); } /** * @since 4.1 */ public static void writeResult(CDODataOutput out, InternalCDORevision revision, int referenceChunk, CDOBranchPoint securityContext) throws IOException { if (revision == null) { out.writeByte(NO_RESULT); } else if (revision instanceof PointerCDORevision) { PointerCDORevision pointer = (PointerCDORevision)revision; out.writeByte(POINTER_RESULT); out.writeCDOClassifierRef(pointer.getEClass()); out.writeLong(pointer.getRevised()); CDOBranchVersion target = pointer.getTarget(); if (target instanceof InternalCDORevision) { writeResult(out, (InternalCDORevision)target, referenceChunk, securityContext); } else { out.writeByte(NO_RESULT); } } else if (revision instanceof DetachedCDORevision) { DetachedCDORevision detached = (DetachedCDORevision)revision; out.writeByte(DETACHED_RESULT); out.writeCDOClassifierRef(detached.getEClass()); out.writeLong(detached.getTimeStamp()); out.writeLong(detached.getRevised()); out.writeInt(detached.getVersion()); } else { out.writeByte(NORMAL_RESULT); out.writeCDORevision(revision, referenceChunk, securityContext); } } private static InternalCDORevision readResult(CDODataInput in, CDOID id, CDOBranch branch, InternalCDORevision result) throws IOException { byte type = in.readByte(); switch (type) { case NO_RESULT: return null; case POINTER_RESULT: { EClassifier classifier = in.readCDOClassifierRefAndResolve(); long revised = in.readLong(); InternalCDORevision target = readResult(in, id, branch); // If target is null and we are in a Available RevisionInfo it means that we can use // availableBranchVersion/result as target if (target == null && result != null) { target = result; } return new PointerCDORevision((EClass)classifier, id, branch, revised, target); } case DETACHED_RESULT: { EClassifier classifier = in.readCDOClassifierRefAndResolve(); long timeStamp = in.readLong(); long revised = in.readLong(); int version = in.readInt(); return new DetachedCDORevision((EClass)classifier, id, branch, version, timeStamp, revised); } case NORMAL_RESULT: return (InternalCDORevision)in.readCDORevision(); default: throw new IllegalStateException("Invalid synthetic type: " + type); } } /** * @since 4.0 */ public static InternalCDORevision readResult(CDODataInput in, CDOID id, CDOBranch branch) throws IOException { return readResult(in, id, branch, null); } /** * @deprecated Not called anymore by the framework */ @Deprecated protected void doWriteResult(CDODataOutput out, InternalCDORevision revision, int referenceChunk) throws IOException { throw new UnsupportedOperationException(); } /** * @deprecated Not called anymore by the framework */ @Deprecated protected InternalCDORevision doReadResult(CDODataInput in) throws IOException { throw new UnsupportedOperationException(); } /** * If the meaning of this type isn't clear, there really should be more of a description here... * * @author Eike Stepper * @since 3.0 */ public static enum Type { AVAILABLE_NORMAL, AVAILABLE_POINTER, AVAILABLE_DETACHED, MISSING } /** * If the meaning of this type isn't clear, there really should be more of a description here... * * @author Eike Stepper * @since 3.0 */ public static abstract class Available extends RevisionInfo { private CDOBranchVersion availableBranchVersion; protected Available(CDOID id, CDOBranchPoint requestedBranchPoint, CDOBranchVersion availableBranchVersion) { super(id, requestedBranchPoint); this.availableBranchVersion = availableBranchVersion; } protected Available(CDODataInput in, CDOBranchPoint requestedBranchPoint) throws IOException { super(in, requestedBranchPoint); availableBranchVersion = in.readCDOBranchVersion(); } public CDOBranchVersion getAvailableBranchVersion() { return availableBranchVersion; } public boolean isDirect() { return availableBranchVersion.getBranch() == getRequestedBranchPoint().getBranch(); } @Override public boolean isLoadNeeded() { return !isDirect(); } @Override public void write(CDODataOutput out) throws IOException { super.write(out); out.writeCDOBranchVersion(availableBranchVersion); } @Override protected void writeRevision(CDODataOutput out, int referenceChunk, CDOBranchPoint securityContext) throws IOException { InternalCDORevision result = getResult(); if (result != null && result.getBranch() == availableBranchVersion.getBranch()) { // Use available out.writeBoolean(true); } else { out.writeBoolean(false); super.writeRevision(out, referenceChunk, securityContext); } } @Override protected void readRevision(CDODataInput in) throws IOException { boolean useAvailable = in.readBoolean(); if (useAvailable) { setResult((InternalCDORevision)availableBranchVersion); } else { super.readRevision(in); } } /** * If the meaning of this type isn't clear, there really should be more of a description here... * * @author Eike Stepper * @since 3.0 */ public static class Normal extends Available { public Normal(CDOID id, CDOBranchPoint requestedBranchPoint, CDOBranchVersion availableBranchVersion) { super(id, requestedBranchPoint, availableBranchVersion); } private Normal(CDODataInput in, CDOBranchPoint requestedBranchPoint) throws IOException { super(in, requestedBranchPoint); } @Override public Type getType() { return Type.AVAILABLE_NORMAL; } @Override public InternalCDORevision getResult() { if (isDirect()) { CDOBranchVersion branchVersion = getAvailableBranchVersion(); if (branchVersion instanceof InternalCDORevision) { return (InternalCDORevision)branchVersion; } } return super.getResult(); } @Override public void processResult(InternalCDORevisionManager revisionManager, List<CDORevision> results, SyntheticCDORevision[] synthetics, int i) { if (!isLoadNeeded()) { setResult((InternalCDORevision)getAvailableBranchVersion()); } super.processResult(revisionManager, results, synthetics, i); } } /** * If the meaning of this type isn't clear, there really should be more of a description here... * * @author Eike Stepper * @since 3.0 */ public static class Pointer extends Available { private CDOBranchVersion targetBranchVersion; private boolean hasTarget; public Pointer(CDOID id, CDOBranchPoint requestedBranchPoint, CDOBranchVersion availableBranchVersion, CDOBranchVersion targetBranchVersion) { super(id, requestedBranchPoint, availableBranchVersion); this.targetBranchVersion = targetBranchVersion; hasTarget = targetBranchVersion instanceof InternalCDORevision; } private Pointer(CDODataInput in, CDOBranchPoint requestedBranchPoint) throws IOException { super(in, requestedBranchPoint); if (in.readBoolean()) { targetBranchVersion = in.readCDOBranchVersion(); hasTarget = in.readBoolean(); } } public CDOBranchVersion getTargetBranchVersion() { return targetBranchVersion; } @Override public Type getType() { return Type.AVAILABLE_POINTER; } public boolean hasTarget() { return hasTarget; } @Override public boolean isLoadNeeded() { if (getRequestedBranchPoint().getBranch().isMainBranch()) { return false; } return !isDirect() || !hasTarget(); } @Override public void write(CDODataOutput out) throws IOException { super.write(out); if (targetBranchVersion != null) { out.writeBoolean(true); out.writeCDOBranchVersion(targetBranchVersion); out.writeBoolean(hasTarget); } else { out.writeBoolean(false); } } @Override public void processResult(InternalCDORevisionManager revisionManager, List<CDORevision> results, SyntheticCDORevision[] synthetics, int i) { if (!isLoadNeeded()) { CDOBranchVersion target = getTargetBranchVersion(); if (target instanceof InternalCDORevision) { setResult((InternalCDORevision)target); } setSynthetic((PointerCDORevision)getAvailableBranchVersion()); } super.processResult(revisionManager, results, synthetics, i); } } /** * If the meaning of this type isn't clear, there really should be more of a description here... * * @author Eike Stepper * @since 3.0 */ public static class Detached extends Available { public Detached(CDOID id, CDOBranchPoint requestedBranchPoint, CDOBranchVersion availableBranchVersion) { super(id, requestedBranchPoint, availableBranchVersion); } private Detached(CDODataInput in, CDOBranchPoint requestedBranchPoint) throws IOException { super(in, requestedBranchPoint); } @Override public Type getType() { return Type.AVAILABLE_DETACHED; } @Override public void processResult(InternalCDORevisionManager revisionManager, List<CDORevision> results, SyntheticCDORevision[] synthetics, int i) { if (!isLoadNeeded()) { setSynthetic((DetachedCDORevision)getAvailableBranchVersion()); } super.processResult(revisionManager, results, synthetics, i); } } } /** * If the meaning of this type isn't clear, there really should be more of a description here... * * @author Eike Stepper * @since 3.0 */ public static class Missing extends RevisionInfo { public Missing(CDOID id, CDOBranchPoint requestedBranchPoint) { super(id, requestedBranchPoint); } private Missing(CDODataInput in, CDOBranchPoint requestedBranchPoint) throws IOException { super(in, requestedBranchPoint); } @Override public Type getType() { return Type.MISSING; } @Override public boolean isLoadNeeded() { return true; } } }