| /* |
| * Copyright (c) 2010-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 |
| * Simon McDuff - bug 201266 |
| * Simon McDuff - bug 212958 |
| * Simon McDuff - bug 213402 |
| */ |
| package org.eclipse.emf.cdo.spi.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.id.CDOID; |
| import org.eclipse.emf.cdo.common.id.CDOIDProvider; |
| import org.eclipse.emf.cdo.common.id.CDOIDTemp; |
| import org.eclipse.emf.cdo.common.id.CDOIDUtil; |
| import org.eclipse.emf.cdo.common.model.CDOClassifierRef; |
| import org.eclipse.emf.cdo.common.model.CDOModelUtil; |
| import org.eclipse.emf.cdo.common.model.CDOType; |
| import org.eclipse.emf.cdo.common.protocol.CDODataInput; |
| import org.eclipse.emf.cdo.common.protocol.CDODataOutput; |
| import org.eclipse.emf.cdo.common.revision.CDOElementProxy; |
| import org.eclipse.emf.cdo.common.revision.CDOList; |
| import org.eclipse.emf.cdo.common.revision.CDOListFactory; |
| import org.eclipse.emf.cdo.common.revision.CDORevision; |
| import org.eclipse.emf.cdo.common.revision.CDORevisionData; |
| import org.eclipse.emf.cdo.common.revision.delta.CDOContainerFeatureDelta; |
| import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta; |
| import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta; |
| import org.eclipse.emf.cdo.common.security.CDOPermission; |
| import org.eclipse.emf.cdo.common.security.CDOPermissionProvider; |
| import org.eclipse.emf.cdo.common.security.NoPermissionException; |
| import org.eclipse.emf.cdo.common.util.CDOCommonUtil; |
| import org.eclipse.emf.cdo.internal.common.bundle.OM; |
| import org.eclipse.emf.cdo.internal.common.messages.Messages; |
| import org.eclipse.emf.cdo.internal.common.revision.delta.CDORevisionDeltaImpl; |
| import org.eclipse.emf.cdo.spi.common.branch.CDOBranchUtil; |
| import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranch; |
| import org.eclipse.emf.cdo.spi.common.revision.InternalCDOList.ConfigurableEquality; |
| |
| import org.eclipse.net4j.util.om.trace.ContextTracer; |
| import org.eclipse.net4j.util.om.trace.PerfTracer; |
| |
| import org.eclipse.emf.common.util.EList; |
| import org.eclipse.emf.ecore.EClass; |
| import org.eclipse.emf.ecore.EClassifier; |
| import org.eclipse.emf.ecore.EReference; |
| import org.eclipse.emf.ecore.EStructuralFeature; |
| import org.eclipse.emf.ecore.util.FeatureMap; |
| import org.eclipse.emf.ecore.util.FeatureMap.Entry; |
| import org.eclipse.emf.ecore.util.FeatureMapUtil; |
| |
| import java.io.IOException; |
| import java.text.MessageFormat; |
| import java.util.Map; |
| |
| /** |
| * If the meaning of this type isn't clear, there really should be more of a description here... |
| * |
| * @author Eike Stepper |
| * @since 3.0 |
| * @noextend This class is not intended to be subclassed by clients. |
| */ |
| public abstract class BaseCDORevision extends AbstractCDORevision |
| { |
| private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_REVISION, BaseCDORevision.class); |
| |
| private static final PerfTracer READING = new PerfTracer(OM.PERF_REVISION_READING, BaseCDORevision.class); |
| |
| private static final PerfTracer WRITING = new PerfTracer(OM.PERF_REVISION_WRITING, BaseCDORevision.class); |
| |
| private static final int RESOURCE_NODE_NAME_INDEX = 1; |
| |
| private static final int RESOURCE_FOLDER_NODES_INDEX = 2; |
| |
| private static final byte UNSET_OPCODE = 0; |
| |
| private static final byte SET_NULL_OPCODE = 1; |
| |
| private static final byte SET_NOT_NULL_OPCODE = 2; |
| |
| private static final byte READ_PERMISSION_FLAG = 1 << 0; // 1 |
| |
| private static final byte WRITE_PERMISSION_FLAG = 1 << 1; // 2 |
| |
| private static final byte FROZEN_FLAG = 1 << 2; // 4 |
| |
| private static final byte UNCHUNKED_FLAG = 1 << 3; // 8 |
| |
| private static final byte BYPASS_PERMISSION_CHECKS_FLAG = 1 << 4; // 16 |
| |
| private static final byte LIST_PRESERVING_FLAG = 1 << 5; // 32; |
| |
| private static final byte PERMISSION_MASK = READ_PERMISSION_FLAG | WRITE_PERMISSION_FLAG; // 3 |
| |
| private CDOID id; |
| |
| private CDOBranchPoint branchPoint; |
| |
| private int version; |
| |
| private long revised; |
| |
| private CDOID resourceID; |
| |
| /** |
| * On a client, between a local modification and the commit the value of this <i>ID</i> can be an EObject. |
| */ |
| private Object containerID; |
| |
| private int containingFeatureID; |
| |
| private transient byte flags; |
| |
| /** |
| * @since 3.0 |
| */ |
| public BaseCDORevision(EClass eClass) |
| { |
| super(eClass); |
| if (eClass != null) |
| { |
| version = UNSPECIFIED_VERSION; |
| revised = UNSPECIFIED_DATE; |
| resourceID = CDOID.NULL; |
| containerID = CDOID.NULL; |
| containingFeatureID = 0; |
| initValues(getAllPersistentFeatures()); |
| } |
| |
| flags = CDOPermission.WRITE.getBits(); |
| } |
| |
| protected BaseCDORevision(BaseCDORevision source) |
| { |
| super(source.getEClass()); |
| id = source.id; |
| branchPoint = source.branchPoint; |
| version = source.version; |
| revised = source.revised; |
| resourceID = source.resourceID; |
| containerID = source.containerID; |
| containingFeatureID = source.containingFeatureID; |
| flags = (byte)(source.flags & ~FROZEN_FLAG); |
| } |
| |
| /** |
| * @since 3.0 |
| */ |
| public void read(CDODataInput in) throws IOException |
| { |
| if (READING.isEnabled()) |
| { |
| READING.start(this); |
| } |
| |
| readSystemValues(in); |
| |
| flags = in.readByte(); // Don't set permissions into this.falgs before readValues() |
| flags |= UNCHUNKED_FLAG; // First assume all lists are unchunked; may be revised below |
| flags |= BYPASS_PERMISSION_CHECKS_FLAG; // Temporarily disable permission checking to be able to set the read values |
| |
| if ((flags & PERMISSION_MASK) == CDOPermission.NONE.ordinal()) |
| { |
| if (getClassInfo().isResourceNode()) |
| { |
| clearValues(); |
| |
| EClass eClass = getEClass(); |
| EStructuralFeature[] features = getAllPersistentFeatures(); |
| readValue(in, eClass, features, RESOURCE_NODE_NAME_INDEX, true); |
| |
| if (getClassInfo().isResourceFolder()) |
| { |
| if (!readValue(in, eClass, features, RESOURCE_FOLDER_NODES_INDEX, true)) |
| { |
| flags &= ~UNCHUNKED_FLAG; |
| } |
| } |
| } |
| } |
| else |
| { |
| if (!readValues(in)) |
| { |
| flags &= ~UNCHUNKED_FLAG; |
| } |
| } |
| |
| // Enable permission checking |
| flags &= ~BYPASS_PERMISSION_CHECKS_FLAG; |
| |
| if (READING.isEnabled()) |
| { |
| READING.stop(this); |
| } |
| } |
| |
| /** |
| * @since 4.0 |
| */ |
| protected void readSystemValues(CDODataInput in) throws IOException |
| { |
| EClassifier classifier = in.readCDOClassifierRefAndResolve(); |
| initClassInfo((EClass)classifier); |
| |
| id = in.readCDOID(); |
| branchPoint = in.readCDOBranchPoint(); |
| version = in.readInt(); |
| if (!id.isTemporary()) |
| { |
| revised = in.readLong(); |
| } |
| |
| resourceID = in.readCDOID(); |
| containerID = in.readCDOID(); |
| containingFeatureID = in.readInt(); |
| |
| if (TRACER.isEnabled()) |
| { |
| TRACER.format( |
| "Reading revision: ID={0}, className={1}, version={2}, branchPoint={3}, revised={4}, resource={5}, container={6}, featureID={7}", //$NON-NLS-1$ |
| id, getEClass().getName(), version, branchPoint, revised, resourceID, containerID, containingFeatureID); |
| } |
| } |
| |
| /** |
| * @since 4.3 |
| */ |
| public boolean readValues(CDODataInput in) throws IOException |
| { |
| EClass owner = getEClass(); |
| EStructuralFeature[] features = getAllPersistentFeatures(); |
| initValues(features); |
| |
| boolean unchunked = true; |
| for (int i = 0; i < features.length; i++) |
| { |
| unchunked = readValue(in, owner, features, i, unchunked); |
| } |
| |
| return unchunked; |
| } |
| |
| private boolean readValue(CDODataInput in, EClass owner, EStructuralFeature[] features, int i, boolean unchunked) |
| throws IOException |
| { |
| Object value; |
| byte unsetState = in.readByte(); |
| switch (unsetState) |
| { |
| case UNSET_OPCODE: |
| return unchunked; |
| |
| case SET_NULL_OPCODE: |
| setValue(i, CDORevisionData.NIL); |
| return unchunked; |
| } |
| |
| EStructuralFeature feature = features[i]; |
| if (feature.isMany()) |
| { |
| CDOList list = in.readCDOList(owner, feature); |
| if (unchunked) |
| { |
| int size = list.size(); |
| if (size != 0) |
| { |
| Object lastElement = list.get(size - 1); |
| if (lastElement == InternalCDOList.UNINITIALIZED || lastElement instanceof CDOElementProxy) |
| { |
| unchunked = false; |
| } |
| } |
| } |
| |
| value = list; |
| } |
| else |
| { |
| value = in.readCDOFeatureValue(feature); |
| if (TRACER.isEnabled()) |
| { |
| TRACER.format("Read feature {0}: {1}", feature.getName(), value); |
| } |
| } |
| |
| setValue(i, value); |
| return unchunked; |
| } |
| |
| /** |
| * @since 4.0 |
| */ |
| public void write(CDODataOutput out, int referenceChunk) throws IOException |
| { |
| write(out, referenceChunk, null); |
| } |
| |
| /** |
| * @since 4.1 |
| */ |
| public void write(CDODataOutput out, int referenceChunk, CDOBranchPoint securityContext) throws IOException |
| { |
| if (WRITING.isEnabled()) |
| { |
| WRITING.start(this); |
| } |
| |
| writeSystemValues(out); |
| |
| CDOPermissionProvider permissionProvider = out.getPermissionProvider(); |
| CDOPermission permission = permissionProvider.getPermission(this, securityContext); |
| out.writeByte(permission.getBits()); |
| |
| if (permission == CDOPermission.NONE) |
| { |
| if (getClassInfo().isResourceNode()) |
| { |
| EClass eClass = getEClass(); |
| EStructuralFeature[] features = getAllPersistentFeatures(); |
| writeValue(out, eClass, features, RESOURCE_NODE_NAME_INDEX, referenceChunk); |
| |
| if (getClassInfo().isResourceFolder()) |
| { |
| writeValue(out, eClass, features, RESOURCE_FOLDER_NODES_INDEX, referenceChunk); |
| } |
| } |
| } |
| else |
| { |
| if (!isUnchunked() && referenceChunk != 0) |
| { |
| CDORevisionUnchunker unchunker = out.getRevisionUnchunker(); |
| if (unchunker != null) |
| { |
| unchunker.ensureChunks(this, referenceChunk); |
| } |
| } |
| |
| writeValues(out, referenceChunk); |
| } |
| |
| if (WRITING.isEnabled()) |
| { |
| WRITING.stop(this); |
| } |
| } |
| |
| /** |
| * @since 4.0 |
| */ |
| protected void writeSystemValues(CDODataOutput out) throws IOException |
| { |
| EClass eClass = getEClass(); |
| CDOClassifierRef classRef = new CDOClassifierRef(eClass); |
| |
| if (TRACER.isEnabled()) |
| { |
| TRACER.format( |
| "Writing revision: ID={0}, className={1}, version={2}, branchPoint={3}, revised={4}, resource={5}, container={6}, featureID={7}", //$NON-NLS-1$ |
| id, eClass.getName(), getVersion(), branchPoint, revised, resourceID, containerID, containingFeatureID); |
| } |
| |
| out.writeCDOClassifierRef(classRef); |
| out.writeCDOID(id); |
| out.writeCDOBranchPoint(branchPoint); |
| out.writeInt(getVersion()); |
| if (!id.isTemporary()) |
| { |
| out.writeLong(revised); |
| } |
| |
| out.writeCDOID(resourceID); |
| out.writeCDOID(out.getIDProvider().provideCDOID(containerID)); |
| out.writeInt(containingFeatureID); |
| } |
| |
| /** |
| * @since 4.3 |
| */ |
| public void writeValues(CDODataOutput out, int referenceChunk) throws IOException |
| { |
| EClass owner = getEClass(); |
| EStructuralFeature[] features = getAllPersistentFeatures(); |
| for (int i = 0; i < features.length; i++) |
| { |
| writeValue(out, owner, features, i, referenceChunk); |
| } |
| } |
| |
| private void writeValue(CDODataOutput out, EClass owner, EStructuralFeature[] features, int i, int referenceChunk) |
| throws IOException |
| { |
| EStructuralFeature feature = features[i]; |
| Object value = getValue(i); |
| if (value == null) |
| { |
| // Feature is NOT set |
| out.writeByte(UNSET_OPCODE); |
| return; |
| } |
| |
| // Feature IS set |
| if (value == CDORevisionData.NIL) |
| { |
| // Feature IS null |
| out.writeByte(SET_NULL_OPCODE); |
| return; |
| } |
| |
| // Feature is NOT null |
| out.writeByte(SET_NOT_NULL_OPCODE); |
| if (feature.isMany()) |
| { |
| CDOList list = (CDOList)value; |
| out.writeCDOList(owner, feature, list, referenceChunk); |
| } |
| else |
| { |
| checkNoFeatureMap(feature); |
| if (feature instanceof EReference) |
| { |
| value = out.getIDProvider().provideCDOID(value); |
| } |
| |
| if (TRACER.isEnabled()) |
| { |
| TRACER.format("Writing feature {0}: {1}", feature.getName(), value); |
| } |
| |
| out.writeCDOFeatureValue(feature, value); |
| } |
| } |
| |
| /** |
| * @see #write(CDODataOutput, int) |
| * @since 3.0 |
| */ |
| public void convertEObjects(CDOIDProvider idProvider) |
| { |
| if (!(containerID instanceof CDOID)) |
| { |
| containerID = idProvider.provideCDOID(containerID); |
| } |
| |
| EStructuralFeature[] features = getAllPersistentFeatures(); |
| for (int i = 0; i < features.length; i++) |
| { |
| EStructuralFeature feature = features[i]; |
| if (feature.isMany()) |
| { |
| CDOList list = getValueAsList(i); |
| if (list != null) |
| { |
| boolean isFeatureMap = FeatureMapUtil.isFeatureMap(feature); |
| for (int j = 0; j < list.size(); j++) |
| { |
| Object value = list.get(j, false); |
| EStructuralFeature innerFeature = feature; // Prepare for possible feature map |
| if (isFeatureMap) |
| { |
| Entry entry = (FeatureMap.Entry)value; |
| innerFeature = entry.getEStructuralFeature(); |
| value = entry.getValue(); |
| } |
| |
| if (value != null && innerFeature instanceof EReference) |
| { |
| CDOID newValue = idProvider.provideCDOID(value); |
| if (newValue != value) |
| { |
| list.set(j, newValue); |
| } |
| } |
| } |
| } |
| } |
| else |
| { |
| checkNoFeatureMap(feature); |
| Object value = getValue(i); |
| if (value != null && feature instanceof EReference) |
| { |
| CDOID newValue = idProvider.provideCDOID(value); |
| if (newValue != value) |
| { |
| setValue(i, newValue); |
| } |
| } |
| } |
| } |
| } |
| |
| public CDOID getID() |
| { |
| return id; |
| } |
| |
| public void setID(CDOID id) |
| { |
| if (CDOIDUtil.isNull(id)) |
| { |
| throw new IllegalArgumentException(Messages.getString("AbstractCDORevision.1")); //$NON-NLS-1$ |
| } |
| |
| if (TRACER.isEnabled()) |
| { |
| TRACER.format("Setting ID: {0}", id); |
| } |
| |
| this.id = id; |
| } |
| |
| /** |
| * @since 4.2 |
| */ |
| public InternalCDOBranch getBranch() |
| { |
| if (branchPoint == null) |
| { |
| return null; |
| } |
| |
| return (InternalCDOBranch)branchPoint.getBranch(); |
| } |
| |
| /** |
| * @since 3.0 |
| */ |
| public long getTimeStamp() |
| { |
| if (branchPoint == null) |
| { |
| return UNSPECIFIED_DATE; |
| } |
| |
| return branchPoint.getTimeStamp(); |
| } |
| |
| /** |
| * @since 3.0 |
| */ |
| public void setBranchPoint(CDOBranchPoint branchPoint) |
| { |
| branchPoint = CDOBranchUtil.copyBranchPoint(branchPoint); |
| if (TRACER.isEnabled()) |
| { |
| TRACER.format("Setting branchPoint {0}: {1}", this, branchPoint); |
| } |
| |
| this.branchPoint = branchPoint; |
| } |
| |
| public int getVersion() |
| { |
| return version; |
| } |
| |
| public void setVersion(int version) |
| { |
| if (TRACER.isEnabled()) |
| { |
| TRACER.format("Setting version for {0}: v{1}", this, version); |
| } |
| |
| this.version = version; |
| } |
| |
| public long getRevised() |
| { |
| return revised; |
| } |
| |
| public void setRevised(long revised) |
| { |
| long created = branchPoint.getTimeStamp(); |
| if (revised != UNSPECIFIED_DATE && revised < Math.max(0, created)) |
| { |
| throw new IllegalArgumentException("revision=" + this + ", created=" + CDOCommonUtil.formatTimeStamp(created) |
| + ", revised=" + CDOCommonUtil.formatTimeStamp(revised)); |
| } |
| |
| if (TRACER.isEnabled()) |
| { |
| TRACER.format("Setting revised {0}: {1}", this, CDOCommonUtil.formatTimeStamp(revised)); |
| } |
| |
| this.revised = revised; |
| } |
| |
| public InternalCDORevisionDelta compare(CDORevision origin) |
| { |
| return new CDORevisionDeltaImpl(origin, this); |
| } |
| |
| public void merge(CDORevisionDelta delta) |
| { |
| CDORevisionMerger applier = new CDORevisionMerger(); |
| applier.merge(this, delta); |
| } |
| |
| public CDOID getResourceID() |
| { |
| return resourceID; |
| } |
| |
| public void setResourceID(CDOID resourceID) |
| { |
| if (TRACER.isEnabled()) |
| { |
| TRACER.format("Setting resourceID {0}: {1}", this, resourceID); |
| } |
| |
| this.resourceID = resourceID; |
| } |
| |
| public Object getContainerID() |
| { |
| return containerID; |
| } |
| |
| public void setContainerID(Object containerID) |
| { |
| if (TRACER.isEnabled()) |
| { |
| TRACER.format("Setting containerID {0}: {1}", this, containerID); |
| } |
| |
| this.containerID = containerID; |
| } |
| |
| public int getContainingFeatureID() |
| { |
| return containingFeatureID; |
| } |
| |
| public void setContainingFeatureID(int containingFeatureID) |
| { |
| if (TRACER.isEnabled()) |
| { |
| TRACER.format("Setting containingFeatureID {0}: {1}", this, containingFeatureID); |
| } |
| |
| this.containingFeatureID = containingFeatureID; |
| } |
| |
| public int hashCode(EStructuralFeature feature) |
| { |
| Object value = getValue(feature); |
| if (value == null) |
| { |
| // See how AbstractEList.hashCode() returns 1 for an empty list. |
| return 1; |
| } |
| |
| return value.hashCode(); |
| } |
| |
| public Object get(EStructuralFeature feature, int index) |
| { |
| if (feature.isMany()) |
| { |
| CDOList list = getList(feature); |
| return list.get(index); |
| } |
| |
| return getValue(feature); |
| } |
| |
| public boolean contains(EStructuralFeature feature, Object value) |
| { |
| CDOList list = getList(feature); |
| return list.contains(value); |
| } |
| |
| public int indexOf(EStructuralFeature feature, Object value) |
| { |
| CDOList list = getList(feature); |
| return list.indexOf(value); |
| } |
| |
| public int lastIndexOf(EStructuralFeature feature, Object value) |
| { |
| CDOList list = getList(feature); |
| return list.lastIndexOf(value); |
| } |
| |
| public boolean isEmpty(EStructuralFeature feature) |
| { |
| CDOList list = getList(feature); |
| return list.isEmpty(); |
| } |
| |
| public int size(EStructuralFeature feature) |
| { |
| CDOList list = getList(feature); |
| return list.size(); |
| } |
| |
| public Object[] toArray(EStructuralFeature feature) |
| { |
| if (!feature.isMany()) |
| { |
| throw new IllegalStateException("!feature.isMany()"); |
| } |
| |
| CDOList list = getList(feature); |
| return list.toArray(); |
| } |
| |
| public <T> T[] toArray(EStructuralFeature feature, T[] array) |
| { |
| if (!feature.isMany()) |
| { |
| throw new IllegalStateException("!feature.isMany()"); |
| } |
| |
| CDOList list = getList(feature); |
| return list.toArray(array); |
| } |
| |
| public void add(EStructuralFeature feature, int index, Object value) |
| { |
| CDOList list = getList(feature); |
| list.add(index, value); |
| } |
| |
| public void clear(EStructuralFeature feature) |
| { |
| if (feature.isMany() && isListPreserving()) |
| { |
| getList(feature).clear(); |
| } |
| else |
| { |
| setValue(feature, null); |
| } |
| } |
| |
| public Object move(EStructuralFeature feature, int targetIndex, int sourceIndex) |
| { |
| CDOList list = getList(feature); |
| return list.move(targetIndex, sourceIndex); |
| } |
| |
| public Object remove(EStructuralFeature feature, int index) |
| { |
| CDOList list = getList(feature); |
| return list.remove(index); |
| } |
| |
| public Object set(EStructuralFeature feature, int index, Object value) |
| { |
| if (feature.isMany()) |
| { |
| CDOList list = getList(feature); |
| return list.set(index, value); |
| } |
| |
| return setValue(feature, value); |
| } |
| |
| public void unset(EStructuralFeature feature) |
| { |
| if (feature.isMany() && isListPreserving()) |
| { |
| getList(feature).clear(); |
| } |
| else |
| { |
| setValue(feature, null); |
| } |
| } |
| |
| /** |
| * @since 4.0 |
| */ |
| public boolean adjustReferences(CDOReferenceAdjuster referenceAdjuster) |
| { |
| if (TRACER.isEnabled()) |
| { |
| TRACER.format("Adjusting references for revision {0}", this); |
| } |
| |
| boolean changed = false; |
| |
| CDOID id1 = (CDOID)referenceAdjuster.adjustReference(resourceID, CDOContainerFeatureDelta.CONTAINER_FEATURE, |
| CDOFeatureDelta.NO_INDEX); |
| if (id1 != resourceID) |
| { |
| resourceID = id1; |
| changed = true; |
| } |
| |
| Object id2 = referenceAdjuster.adjustReference(containerID, CDOContainerFeatureDelta.CONTAINER_FEATURE, |
| CDOFeatureDelta.NO_INDEX); |
| if (id2 != containerID) |
| { |
| containerID = id2; |
| changed = true; |
| } |
| |
| EStructuralFeature[] features = getAllPersistentFeatures(); |
| for (int i = 0; i < features.length; i++) |
| { |
| EStructuralFeature feature = features[i]; |
| if (feature instanceof EReference || FeatureMapUtil.isFeatureMap(feature)) |
| { |
| if (feature.isMany()) |
| { |
| InternalCDOList list = (InternalCDOList)getValueAsList(i); |
| if (list != null) |
| { |
| changed |= list.adjustReferences(referenceAdjuster, feature); |
| } |
| } |
| else |
| { |
| CDOType type = CDOModelUtil.getType(feature); |
| Object oldValue = getValue(i); |
| Object newValue = type.adjustReferences(referenceAdjuster, oldValue, feature, CDOFeatureDelta.NO_INDEX); |
| if (oldValue != newValue) // Just an optimization for NOOP adjusters |
| { |
| setValue(i, newValue); |
| changed = true; |
| } |
| } |
| } |
| } |
| |
| return changed; |
| } |
| |
| /** |
| * @since 4.3 |
| */ |
| public void adjustBranches(CDOBranchManager newBranchManager) |
| { |
| if (branchPoint != null) |
| { |
| CDOBranch branch = branchPoint.getBranch(); |
| if (branch != null) |
| { |
| branch = newBranchManager.getBranch(branch.getID()); |
| branchPoint = branch.getPoint(branchPoint.getTimeStamp()); |
| } |
| } |
| } |
| |
| public Object getValue(EStructuralFeature feature) |
| { |
| checkReadable(feature); |
| |
| int featureIndex = getFeatureIndex(feature); |
| return getValue(featureIndex); |
| } |
| |
| public Object setValue(EStructuralFeature feature, Object value) |
| { |
| int featureIndex = getFeatureIndex(feature); |
| |
| try |
| { |
| Object old = getValue(featureIndex); |
| setValue(featureIndex, value); |
| return old; |
| } |
| catch (ArrayIndexOutOfBoundsException ex) |
| { |
| throw new IllegalArgumentException( |
| MessageFormat.format(Messages.getString("AbstractCDORevision.20"), feature, getClassInfo()), ex); |
| } |
| } |
| |
| public CDOList getList(EStructuralFeature feature) |
| { |
| return getList(feature, 0); |
| } |
| |
| public CDOList getList(EStructuralFeature feature, int size) |
| { |
| checkReadable(feature); |
| |
| int featureIndex = getFeatureIndex(feature); |
| InternalCDOList list = (InternalCDOList)getValue(featureIndex); |
| if (list == null && size != -1) |
| { |
| list = (InternalCDOList)CDOListFactory.DEFAULT.createList(size, 0, 0); |
| if (feature instanceof EReference && list instanceof ConfigurableEquality) |
| { |
| ((ConfigurableEquality)list).setUseEquals(false); |
| } |
| |
| synchronized (this) |
| { |
| boolean bypassPermissionChecks = bypassPermissionChecks(true); |
| |
| try |
| { |
| setValue(featureIndex, list); |
| } |
| finally |
| { |
| bypassPermissionChecks(bypassPermissionChecks); |
| } |
| } |
| } |
| |
| return list; |
| } |
| |
| public void setList(EStructuralFeature feature, InternalCDOList list) |
| { |
| int featureIndex = getFeatureIndex(feature); |
| setValue(featureIndex, list); |
| } |
| |
| /** |
| * @since 4.2 |
| */ |
| public EStructuralFeature[] clearValues() |
| { |
| EStructuralFeature[] features = getClassInfo().getAllPersistentFeatures(); |
| initValues(features); |
| return features; |
| } |
| |
| /** |
| * @since 4.3 |
| */ |
| public String getResourceNodeName() |
| { |
| return (String)doGetValue(RESOURCE_NODE_NAME_INDEX); |
| } |
| |
| /** |
| * @since 4.1 |
| */ |
| public CDOPermission getPermission() |
| { |
| return CDOPermission.get(flags & PERMISSION_MASK); |
| } |
| |
| /** |
| * @since 4.1 |
| */ |
| public void setPermission(CDOPermission permission) |
| { |
| flags = (byte)(flags & ~PERMISSION_MASK | permission.getBits() & PERMISSION_MASK); |
| } |
| |
| /** |
| * @since 4.3 |
| */ |
| public boolean bypassPermissionChecks(boolean on) |
| { |
| boolean old = (flags & BYPASS_PERMISSION_CHECKS_FLAG) != 0; |
| |
| if (on) |
| { |
| flags |= BYPASS_PERMISSION_CHECKS_FLAG; |
| } |
| else |
| { |
| flags &= ~BYPASS_PERMISSION_CHECKS_FLAG; |
| } |
| |
| return old; |
| } |
| |
| /** |
| * @since 4.3 |
| */ |
| public boolean isListPreserving() |
| { |
| return (flags & LIST_PRESERVING_FLAG) != 0; |
| } |
| |
| /** |
| * @since 4.3 |
| */ |
| public void setListPreserving() |
| { |
| flags |= LIST_PRESERVING_FLAG; |
| } |
| |
| /** |
| * @since 4.1 |
| */ |
| public void freeze() |
| { |
| flags |= FROZEN_FLAG; |
| |
| if (isReadable()) |
| { |
| EStructuralFeature[] features = getAllPersistentFeatures(); |
| for (int i = 0; i < features.length; i++) |
| { |
| EStructuralFeature feature = features[i]; |
| if (feature.isMany()) |
| { |
| InternalCDOList list = (InternalCDOList)doGetValue(i); |
| if (list != null) |
| { |
| list.freeze(); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * @since 4.2 |
| */ |
| public boolean isFrozen() |
| { |
| return (flags & FROZEN_FLAG) != 0; |
| } |
| |
| /** |
| * @since 4.1 |
| */ |
| public boolean isUnchunked() |
| { |
| return (flags & UNCHUNKED_FLAG) != 0; |
| } |
| |
| /** |
| * @since 4.1 |
| */ |
| public void setUnchunked() |
| { |
| flags |= UNCHUNKED_FLAG; |
| } |
| |
| protected Object getValue(int featureIndex) |
| { |
| return doGetValue(featureIndex); |
| } |
| |
| protected void setValue(int featureIndex, Object value) |
| { |
| checkUnfrozen(featureIndex, value); |
| checkWritable(); |
| doSetValue(featureIndex, value); |
| } |
| |
| protected abstract void initValues(EStructuralFeature[] allPersistentFeatures); |
| |
| /** |
| * @since 4.1 |
| */ |
| protected abstract Object doGetValue(int featureIndex); |
| |
| /** |
| * @since 4.1 |
| */ |
| protected abstract void doSetValue(int featureIndex, Object value); |
| |
| private CDOList getValueAsList(int i) |
| { |
| return (CDOList)getValue(i); |
| } |
| |
| private void checkUnfrozen(int featureIndex, Object value) |
| { |
| if ((flags & FROZEN_FLAG) != 0) |
| { |
| // Exception 1: LoadPermissionsRequest needs to "reload" revision values in case the original permission was NONE. |
| // In this case BYPASS_PERMISSION_CHECKS_FLAG is set. |
| if ((flags & BYPASS_PERMISSION_CHECKS_FLAG) != 0) |
| { |
| return; |
| } |
| |
| Object oldValue = getValue(featureIndex); |
| |
| // Exception 2: Setting an empty list as the value for an isMany feature, is allowed if the old value is null. |
| // This is a case of lazy initialization. |
| boolean newIsEmptyList = value instanceof EList<?> && ((EList<?>)value).size() == 0; |
| if (newIsEmptyList && oldValue == null) |
| { |
| return; |
| } |
| |
| // Exception 3a: Replacing a temp ID with a regular ID is allowed (happens during postCommit of new objects) |
| // Exception 3b: Replacing a temp ID with another temp ID is also allowed (happens when changes are imported in a |
| // PushTx). |
| if (oldValue instanceof CDOIDTemp && value instanceof CDOID) |
| { |
| return; |
| } |
| |
| throw new IllegalStateException("Cannot modify a frozen revision"); |
| } |
| } |
| |
| private void checkReadable(EStructuralFeature feature) |
| { |
| if ((flags & BYPASS_PERMISSION_CHECKS_FLAG) != 0) |
| { |
| return; |
| } |
| |
| if (CDOModelUtil.isResourcePathFeature(feature)) |
| { |
| return; |
| } |
| |
| if ((flags & READ_PERMISSION_FLAG) == 0) |
| { |
| throw new NoPermissionException(this); |
| } |
| } |
| |
| private void checkWritable() |
| { |
| if ((flags & BYPASS_PERMISSION_CHECKS_FLAG) != 0) |
| { |
| return; |
| } |
| |
| if ((flags & WRITE_PERMISSION_FLAG) == 0) |
| { |
| throw new NoPermissionException(this); |
| } |
| } |
| |
| public static void checkNoFeatureMap(EStructuralFeature feature) |
| { |
| if (FeatureMapUtil.isFeatureMap(feature)) |
| { |
| throw new UnsupportedOperationException("Single-valued feature maps not yet handled"); |
| } |
| } |
| |
| public static Object remapID(Object value, Map<CDOID, CDOID> idMappings, boolean allowUnmappedTempIDs) |
| { |
| if (value instanceof CDOID) |
| { |
| CDOID oldID = (CDOID)value; |
| if (!oldID.isNull()) |
| { |
| CDOID newID = idMappings.get(oldID); |
| if (newID != null) |
| { |
| if (TRACER.isEnabled()) |
| { |
| TRACER.format("Adjusting ID: {0} --> {1}", oldID, newID); |
| } |
| |
| return newID; |
| } |
| |
| if (oldID instanceof CDOIDTemp) |
| { |
| throw new IllegalStateException(MessageFormat.format(Messages.getString("AbstractCDORevision.2"), oldID)); |
| } |
| } |
| } |
| |
| return value; |
| } |
| |
| /** |
| * @since 4.3 |
| */ |
| public static String formatFlags(BaseCDORevision revision) |
| { |
| int flags = revision.flags; |
| |
| StringBuilder builder = new StringBuilder(); |
| if ((flags & UNCHUNKED_FLAG) != 0) |
| { |
| builder.append("UNCHUNKED"); |
| } |
| |
| if ((flags & FROZEN_FLAG) != 0) |
| { |
| if (builder.length() != 0) |
| { |
| builder.append("|"); |
| } |
| |
| builder.append("FROZEN"); |
| } |
| |
| if ((flags & READ_PERMISSION_FLAG) != 0) |
| { |
| if (builder.length() != 0) |
| { |
| builder.append("|"); |
| } |
| |
| builder.append("READ"); |
| } |
| |
| if ((flags & WRITE_PERMISSION_FLAG) != 0) |
| { |
| if (builder.length() != 0) |
| { |
| builder.append("|"); |
| } |
| |
| builder.append("WRITE"); |
| } |
| |
| if ((flags & BYPASS_PERMISSION_CHECKS_FLAG) != 0) |
| { |
| if (builder.length() != 0) |
| { |
| builder.append("|"); |
| } |
| |
| builder.append("BYPASS_PERMISSION_CHECKS"); |
| } |
| |
| return builder.toString(); |
| } |
| } |