blob: 50fdcfbe7ac161ae6ffdaee8d6d36244f1b25700 [file] [log] [blame]
/*
* Copyright (c) 2008-2015 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.common.revision;
import org.eclipse.emf.cdo.common.branch.CDOBranch;
import org.eclipse.emf.cdo.common.branch.CDOBranchManager;
import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
import org.eclipse.emf.cdo.common.branch.CDOBranchVersion;
import org.eclipse.emf.cdo.common.commit.CDOChangeSet;
import org.eclipse.emf.cdo.common.commit.CDOChangeSetData;
import org.eclipse.emf.cdo.common.id.CDOID;
import org.eclipse.emf.cdo.common.id.CDOIDUtil;
import org.eclipse.emf.cdo.common.id.CDOWithID;
import org.eclipse.emf.cdo.common.model.CDOClassInfo;
import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta;
import org.eclipse.emf.cdo.common.util.CDOCommonUtil;
import org.eclipse.emf.cdo.internal.common.commit.CDOChangeSetDataImpl;
import org.eclipse.emf.cdo.internal.common.commit.CDOChangeSetImpl;
import org.eclipse.emf.cdo.internal.common.messages.Messages;
import org.eclipse.emf.cdo.internal.common.revision.CDOFeatureMapEntryImpl;
import org.eclipse.emf.cdo.internal.common.revision.CDORevisableImpl;
import org.eclipse.emf.cdo.internal.common.revision.CDORevisionCacheAuditing;
import org.eclipse.emf.cdo.internal.common.revision.CDORevisionCacheBranching;
import org.eclipse.emf.cdo.internal.common.revision.CDORevisionCacheNonAuditing;
import org.eclipse.emf.cdo.internal.common.revision.CDORevisionImpl;
import org.eclipse.emf.cdo.internal.common.revision.CDORevisionKeyImpl;
import org.eclipse.emf.cdo.internal.common.revision.CDORevisionManagerImpl;
import org.eclipse.emf.cdo.internal.common.revision.delta.CDORevisionDeltaImpl;
import org.eclipse.emf.cdo.spi.common.revision.CDOFeatureMapEntry;
import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager;
import org.eclipse.emf.cdo.spi.common.revision.ManagedRevisionProvider;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.util.FeatureMap;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
/**
* Various static helper methods for dealing with {@link CDORevision revisions}.
*
* @author Eike Stepper
* @apiviz.exclude
*/
public final class CDORevisionUtil
{
public static final Object UNINITIALIZED = new Uninitialized();
private static EAttribute resourceNodeNameAttribute;
private CDORevisionUtil()
{
}
/**
* Creates and returns a new memory sensitive revision cache.
*
* @since 4.0
*/
public static CDORevisionCache createRevisionCache(boolean supportingAudits, boolean supportingBranches)
{
if (supportingBranches)
{
return new CDORevisionCacheBranching();
}
if (supportingAudits)
{
return new CDORevisionCacheAuditing();
}
return new CDORevisionCacheNonAuditing();
}
/**
* @since 4.0
*/
public static CDORevisionManager createRevisionManager()
{
return new CDORevisionManagerImpl();
}
/**
* @since 4.0
*/
public static CDORevisionManager createRevisionManager(CDORevisionCache cache)
{
InternalCDORevisionManager revisionManager = (InternalCDORevisionManager)createRevisionManager();
revisionManager.setCache(cache);
return revisionManager;
}
/**
* @since 4.0
*/
public static CDORevisable copyRevisable(CDORevisable source)
{
return new CDORevisableImpl(source);
}
/**
* @since 4.0
*/
public static CDORevisable createRevisable(CDOBranch branch, int version, long timeStamp, long revised)
{
return new CDORevisableImpl(branch, version, timeStamp, revised);
}
/**
* @since 4.0
*/
public static CDORevisionKey copyRevisionKey(CDORevisionKey source)
{
return new CDORevisionKeyImpl(source.getID(), source.getBranch(), source.getVersion());
}
/**
* @since 3.0
*/
public static CDORevisionKey createRevisionKey(CDOID id, CDOBranch branch, int version)
{
return new CDORevisionKeyImpl(id, branch, version);
}
/**
* @since 4.0
*/
public static String formatRevisionKey(CDORevisionKey key)
{
StringBuilder builder = new StringBuilder();
CDOIDUtil.write(builder, key.getID());
builder.append(":");
builder.append(key.getBranch().getID());
builder.append(":");
builder.append(key.getVersion());
return builder.toString();
}
/**
* @since 4.0
*/
public static CDORevisionKey parseRevisionKey(String source, CDOBranchManager branchManager)
{
StringTokenizer tokenizer = new StringTokenizer(source, ":");
if (!tokenizer.hasMoreTokens())
{
throw new IllegalArgumentException("No ID segment");
}
String idSegment = tokenizer.nextToken();
CDOID id = CDOIDUtil.read(idSegment);
if (!tokenizer.hasMoreTokens())
{
throw new IllegalArgumentException("No branch segment");
}
String branchSegment = tokenizer.nextToken();
CDOBranch branch = branchManager.getBranch(Integer.parseInt(branchSegment));
if (!tokenizer.hasMoreTokens())
{
throw new IllegalArgumentException("No version segment");
}
String versionSegment = tokenizer.nextToken();
int version = Integer.parseInt(versionSegment);
return new CDORevisionKeyImpl(id, branch, version);
}
/**
* @since 2.0
*/
public static FeatureMap.Entry createFeatureMapEntry(EStructuralFeature feature, Object value)
{
return new CDOFeatureMapEntryImpl(feature, value);
}
/**
* @since 3.0
*/
public static CDOFeatureMapEntry createCDOFeatureMapEntry()
{
return new CDOFeatureMapEntryImpl();
}
/**
* @since 4.0
*/
public static CDORevisionDelta createDelta(CDORevision revision)
{
return new CDORevisionDeltaImpl(revision);
}
/**
* @since 4.0
*/
public static CDOChangeSetData createChangeSetData(Set<CDOID> ids, final CDOBranchPoint startPoint,
final CDOBranchPoint endPoint, final CDORevisionManager revisionManager)
{
CDORevisionProvider startProvider = new ManagedRevisionProvider(revisionManager, startPoint);
CDORevisionProvider endProvider = new ManagedRevisionProvider(revisionManager, endPoint);
return createChangeSetData(ids, startProvider, endProvider);
}
/**
* @since 4.0
*/
public static CDOChangeSetData createChangeSetData(Set<CDOID> ids, CDORevisionProvider startProvider,
CDORevisionProvider endProvider)
{
return createChangeSetData(ids, startProvider, endProvider, false);
}
/**
* @since 4.1
*/
public static CDOChangeSetData createChangeSetData(Set<CDOID> ids, CDORevisionProvider startProvider,
CDORevisionProvider endProvider, boolean useStartVersions)
{
List<CDOIDAndVersion> newObjects = new ArrayList<CDOIDAndVersion>();
List<CDORevisionKey> changedObjects = new ArrayList<CDORevisionKey>();
List<CDOIDAndVersion> detachedObjects = new ArrayList<CDOIDAndVersion>();
for (CDOID id : ids)
{
CDORevision startRevision = startProvider.getRevision(id);
CDORevision endRevision = endProvider.getRevision(id);
if (startRevision == null && endRevision != null)
{
if (useStartVersions)
{
((InternalCDORevision)endRevision).setVersion(0);
}
newObjects.add(endRevision);
}
else if (startRevision != null && endRevision == null)
{
detachedObjects.add(CDOIDUtil.createIDAndVersion(id, CDOBranchVersion.UNSPECIFIED_VERSION));
}
else if (startRevision != null && endRevision != null)
{
if (!startRevision.equals(endRevision))
{
if (useStartVersions)
{
((InternalCDORevision)endRevision).setVersion(startRevision.getVersion());
}
CDORevisionDelta delta = endRevision.compare(startRevision);
if (!delta.isEmpty())
{
changedObjects.add(delta);
}
}
}
}
return createChangeSetData(newObjects, changedObjects, detachedObjects);
}
/**
* @since 4.0
*/
public static CDOChangeSetData createChangeSetData(List<CDOIDAndVersion> newObjects,
List<CDORevisionKey> changedObjects, List<CDOIDAndVersion> detachedObjects)
{
return new CDOChangeSetDataImpl(newObjects, changedObjects, detachedObjects);
}
/**
* @since 4.0
*/
public static CDOChangeSet createChangeSet(CDOBranchPoint startPoint, CDOBranchPoint endPoint, CDOChangeSetData data)
{
return new CDOChangeSetImpl(startPoint, endPoint, data);
}
/**
* @since 3.0
*/
public static Object remapID(Object value, Map<CDOID, CDOID> idMappings, boolean allowUnmappedTempIDs)
{
return CDORevisionImpl.remapID(value, idMappings, allowUnmappedTempIDs);
}
/**
* @since 4.3
*/
public static boolean isContained(CDOID child, CDOID container, CDORevisionProvider provider)
{
InternalCDORevision revision = (InternalCDORevision)provider.getRevision(child);
return isContained(revision, container, provider);
}
/**
* @since 4.3
*/
public static boolean isContained(InternalCDORevision child, CDOID container, CDORevisionProvider provider)
{
if (child.getID() == container)
{
return true;
}
InternalCDORevision parent = getParentRevision(child, provider);
if (parent != null)
{
return isContained(parent, container, provider);
}
return false;
}
private static InternalCDORevision getParentRevision(InternalCDORevision revision, CDORevisionProvider provider)
{
CDOID parentID;
Object containerID = revision.getContainerID();
if (containerID instanceof CDOWithID)
{
parentID = ((CDOWithID)containerID).cdoID();
}
else
{
parentID = (CDOID)containerID;
}
if (CDOIDUtil.isNull(parentID))
{
parentID = revision.getResourceID();
if (CDOIDUtil.isNull(parentID))
{
return null;
}
else if (parentID == revision.getID())
{
// This must be the root resource!
return null;
}
}
return (InternalCDORevision)provider.getRevision(parentID);
}
/**
* @since 4.4
*/
public static List<CDORevision> getChildRevisions(CDOID container, CDORevisionProvider provider)
{
InternalCDORevision revision = (InternalCDORevision)provider.getRevision(container);
return getChildRevisions(revision, provider);
}
/**
* @since 4.4
*/
public static List<CDORevision> getChildRevisions(CDORevision container, CDORevisionProvider provider)
{
List<CDORevision> children = new ArrayList<CDORevision>();
InternalCDORevision revisionData = (InternalCDORevision)container;
CDOClassInfo classInfo = revisionData.getClassInfo();
for (EStructuralFeature feature : classInfo.getAllPersistentContainments())
{
if (feature instanceof EReference)
{
if (feature.isMany())
{
CDOList list = revisionData.getList(feature);
if (list != null)
{
for (Object value : list)
{
addChildRevision(value, provider, children);
}
}
}
else
{
Object value = revisionData.getValue(feature);
addChildRevision(value, provider, children);
}
}
}
return children;
}
private static void addChildRevision(Object value, CDORevisionProvider provider, List<CDORevision> children)
{
if (value instanceof CDOID)
{
CDOID id = (CDOID)value;
CDORevision child = provider.getRevision(id);
if (child != null)
{
children.add(child);
}
}
}
/**
* @since 4.3
*/
public static String getResourceNodePath(CDOID id, CDORevisionProvider provider)
{
CDORevision revision = provider.getRevision(id);
return getResourceNodePath(revision, provider);
}
/**
* @since 4.0
*/
public static String getResourceNodePath(CDORevision revision, CDORevisionProvider provider)
{
StringBuilder builder = new StringBuilder();
getResourceNodePath((InternalCDORevision)revision, provider, builder);
return builder.toString();
}
private static void getResourceNodePath(InternalCDORevision revision, CDORevisionProvider provider,
StringBuilder result)
{
InternalCDORevision container = null;
if (!revision.isResourceNode())
{
container = getResourceRevision(revision, provider);
}
else
{
container = getParentRevision(revision, provider);
}
if (container != null)
{
getResourceNodePath(container, provider, result);
}
EAttribute attribute = getResourceNodeNameAttribute(revision);
if (attribute != null)
{
int length = result.length();
if (length == 0 || result.charAt(length - 1) != '/')
{
result.append("/");
}
String name = (String)revision.get(attribute, 0);
if (name != null) // Exclude root resource
{
result.append(name);
}
}
}
private static InternalCDORevision getResourceRevision(InternalCDORevision revision, CDORevisionProvider provider)
{
CDOID resourceID = revision.getResourceID();
if (revision.isResourceNode())
{
return null;
}
if (CDOIDUtil.isNull(resourceID))
{
CDOID parentID = null;
Object containerID = revision.getContainerID();
if (containerID instanceof CDOWithID)
{
parentID = ((CDOWithID)containerID).cdoID();
}
else
{
parentID = (CDOID)containerID;
}
if (!CDOIDUtil.isNull(parentID))
{
InternalCDORevision parentRevision = (InternalCDORevision)provider.getRevision(parentID);
return getResourceRevision(parentRevision, provider);
}
}
return (InternalCDORevision)provider.getRevision(resourceID);
}
private static EAttribute getResourceNodeNameAttribute(CDORevision revision)
{
if (revision.isResourceNode())
{
if (CDORevisionUtil.resourceNodeNameAttribute == null)
{
CDORevisionUtil.resourceNodeNameAttribute = (EAttribute)revision.getEClass().getEStructuralFeature("name");
}
return CDORevisionUtil.resourceNodeNameAttribute;
}
return null;
}
/**
* @since 3.0
*/
public static String dumpAllRevisions(Map<CDOBranch, List<CDORevision>> map)
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream out = new PrintStream(baos);
dumpAllRevisions(map, out);
return baos.toString();
}
/**
* @since 3.0
*/
public static void dumpAllRevisions(Map<CDOBranch, List<CDORevision>> map, PrintStream out)
{
new AllRevisionsDumper.Stream.Plain(map, out).dump();
}
/**
* Dumps {@link CDORevision revisions}, sorted and grouped by {@link CDOBranch branch}, to various output formats and
* targets. Concrete output formats and targets are implemented by subclasses.
*
* @since 4.0
* @apiviz.exclude
*/
public static abstract class AllRevisionsDumper
{
private Map<CDOBranch, List<CDORevision>> map;
public AllRevisionsDumper(Map<CDOBranch, List<CDORevision>> map)
{
this.map = map;
}
public Map<CDOBranch, List<CDORevision>> getMap()
{
return map;
}
public void dump()
{
ArrayList<CDOBranch> branches = new ArrayList<CDOBranch>(map.keySet());
Collections.sort(branches);
dumpStart(branches);
for (CDOBranch branch : branches)
{
dumpBranch(branch);
List<CDORevision> revisions = map.get(branch);
Collections.sort(revisions, new CDORevisionComparator());
for (CDORevision revision : revisions)
{
dumpRevision(revision);
}
}
dumpEnd(branches);
}
protected void dumpStart(List<CDOBranch> branches)
{
}
protected void dumpEnd(List<CDOBranch> branches)
{
}
protected abstract void dumpBranch(CDOBranch branch);
protected abstract void dumpRevision(CDORevision revision);
/**
* A {@link AllRevisionsDumper revision dumper} that directs all output to a stream. The concrete output format is
* implemented by subclasses.
*
* @author Eike Stepper
* @apiviz.exclude
*/
public static abstract class Stream extends AllRevisionsDumper
{
private PrintStream out;
public Stream(Map<CDOBranch, List<CDORevision>> map, PrintStream out)
{
super(map);
this.out = out;
}
public PrintStream out()
{
return out;
}
/**
* A {@link Stream revision dumper} that directs all output as plain text to a stream.
*
* @author Eike Stepper
* @apiviz.exclude
*/
public static class Plain extends Stream
{
public static final int pad = 48;
public Plain(Map<CDOBranch, List<CDORevision>> map, PrintStream out)
{
super(map, out);
}
@Override
protected void dumpEnd(List<CDOBranch> branches)
{
out().println();
}
@Override
protected void dumpBranch(CDOBranch branch)
{
out()
.println(padTimeRange(branch.getName() + "[" + branch.getID() + "]", pad, branch.getBase().getTimeStamp(), //$NON-NLS-1$ //$NON-NLS-2$
CDORevision.UNSPECIFIED_DATE));
}
@Override
protected void dumpRevision(CDORevision revision)
{
out().println(padTimeRange(" " + revision, pad, revision.getTimeStamp(), revision.getRevised())); //$NON-NLS-1$
}
private static String padTimeRange(String s, int pos, long t1, long t2)
{
StringBuffer buffer = new StringBuffer(s);
while (buffer.length() < pos)
{
buffer.append(' ');
}
buffer.append(CDOCommonUtil.formatTimeStamp(t1));
buffer.append("/");
buffer.append(CDOCommonUtil.formatTimeStamp(t2));
return buffer.toString();
}
}
/**
* A {@link Stream revision dumper} that directs all output as HTML text to a stream.
*
* @author Eike Stepper
* @apiviz.exclude
*/
public static class Html extends Stream
{
public Html(Map<CDOBranch, List<CDORevision>> map, PrintStream out)
{
super(map, out);
}
@Override
protected void dumpStart(List<CDOBranch> branches)
{
out().println("<table border=\"0\">");
}
@Override
protected void dumpEnd(List<CDOBranch> branches)
{
out().println("</table>");
}
@Override
protected void dumpBranch(CDOBranch branch)
{
PrintStream out = out();
if (!branch.isMainBranch())
{
out.println("<tr><td>&nbsp;</td><td>&nbsp;</td></tr>");
}
out.println("<tr>");
out.println("<td>");
out.println("<h4>" + branch.getName() + "[" + branch.getID() + "]</h4>");
out.println("</td>");
out.println("<td>");
out.println("<h4>" + CDOCommonUtil.formatTimeStamp(branch.getBase().getTimeStamp()) + " / "
+ CDOCommonUtil.formatTimeStamp(CDORevision.UNSPECIFIED_DATE) + "</h4>");
out.println("</td>");
out.println("</tr>");
}
@Override
protected void dumpRevision(CDORevision revision)
{
PrintStream out = out();
out.println("<tr>");
out.println("<td>&nbsp;&nbsp;&nbsp;&nbsp;");
dumpRevision(revision, out);
out.println("&nbsp;&nbsp;&nbsp;&nbsp;</td>");
out.println("<td>");
out.println(CDOCommonUtil.formatTimeStamp(revision.getTimeStamp()) + " / "
+ CDOCommonUtil.formatTimeStamp(revision.getRevised()));
out.println("</td>");
out.println("</tr>");
}
protected void dumpRevision(CDORevision revision, PrintStream out)
{
out.println(revision);
}
}
}
}
/**
* Compares {@link CDORevisionKey revision keys} by {@link CDORevision#getID() ID} and
* {@link CDORevision#getVersion() version}.
*
* @author Eike Stepper
* @since 4.0
* @apiviz.exclude
*/
public static class CDORevisionComparator implements Comparator<CDORevisionKey>
{
public CDORevisionComparator()
{
}
public int compare(CDORevisionKey rev1, CDORevisionKey rev2)
{
int result = rev1.getID().compareTo(rev2.getID());
if (result == 0)
{
int version1 = rev1.getVersion();
int version2 = rev2.getVersion();
result = version1 < version2 ? -1 : version1 == version2 ? 0 : 1;
}
return result;
}
}
/**
* @author Eike Stepper
*/
private static final class Uninitialized
{
public Uninitialized()
{
}
@Override
public String toString()
{
return Messages.getString("CDORevisionUtil.0"); //$NON-NLS-1$
}
}
}