Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.modeshape.jcr.cache.document.SessionNode Maven / Gradle / Ivy
package org.modeshape.jcr.cache.document;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import javax.jcr.RepositoryException;
import org.infinispan.util.concurrent.ConcurrentHashSet;
import org.modeshape.common.annotation.ThreadSafe;
import org.modeshape.common.text.Inflector;
import org.modeshape.common.util.StringUtil;
import org.modeshape.jcr.Connectors;
import org.modeshape.jcr.JcrI18n;
import org.modeshape.jcr.JcrLexicon;
import org.modeshape.jcr.JcrNtLexicon;
import org.modeshape.jcr.JcrSession;
import org.modeshape.jcr.ModeShapeLexicon;
import org.modeshape.jcr.cache.CachedNode;
import org.modeshape.jcr.cache.ChildReference;
import org.modeshape.jcr.cache.ChildReferences;
import org.modeshape.jcr.cache.ChildReferences.BasicContext;
import org.modeshape.jcr.cache.ChildReferences.ChildInsertions;
import org.modeshape.jcr.cache.MutableCachedNode;
import org.modeshape.jcr.cache.NodeCache;
import org.modeshape.jcr.cache.NodeKey;
import org.modeshape.jcr.cache.NodeNotFoundException;
import org.modeshape.jcr.cache.NodeNotFoundInParentException;
import org.modeshape.jcr.cache.PathCache;
import org.modeshape.jcr.cache.ReferrerCounts;
import org.modeshape.jcr.cache.ReferrerCounts.MutableReferrerCounts;
import org.modeshape.jcr.cache.SessionCache;
import org.modeshape.jcr.cache.WrappedException;
import org.modeshape.jcr.value.BinaryValue;
import org.modeshape.jcr.value.Name;
import org.modeshape.jcr.value.NameFactory;
import org.modeshape.jcr.value.NamespaceRegistry;
import org.modeshape.jcr.value.Path;
import org.modeshape.jcr.value.Path.Segment;
import org.modeshape.jcr.value.Property;
import org.modeshape.jcr.value.PropertyFactory;
import org.modeshape.jcr.value.Reference;
import org.modeshape.jcr.value.ValueFactories;
import org.modeshape.jcr.value.basic.NodeKeyReference;
import org.modeshape.jcr.value.basic.StringReference;
import org.modeshape.jcr.value.binary.ExternalBinaryValue;
@ThreadSafe
public class SessionNode implements MutableCachedNode {
public enum LockChange {
LOCK_FOR_SESSION,
LOCK_FOR_NON_SESSION,
UNLOCK;
}
private final NodeKey key;
private final ConcurrentMap changedProperties = new ConcurrentHashMap();
private final ConcurrentMap removedProperties = new ConcurrentHashMap();
private final AtomicReference federatedSegments = new AtomicReference();
private volatile NodeKey newParent;
private final AtomicReference additionalParents = new AtomicReference();
private final ChangedChildren changedChildren = new ChangedChildren();
private final AtomicReference appended = new AtomicReference();
private final AtomicReference mixinChanges = new AtomicReference();
private final AtomicReference referrerChanges = new AtomicReference();
private final AtomicReference isQueryable = new AtomicReference();
private final boolean isNew;
private volatile LockChange lockChange;
private final AtomicReference permissionChanges = new AtomicReference<>();
public SessionNode ( NodeKey key,
boolean isNew ) {
this .key = key;
this .isNew = isNew;
assert this .key != null ;
}
protected final ChangedChildren changedChildren () {
return changedChildren;
}
protected final Set removedProperties () {
return removedProperties.keySet();
}
protected final ConcurrentMap changedProperties () {
return changedProperties;
}
protected final NodeKey newParent () {
return newParent;
}
@Override
public final boolean isNew () {
return isNew;
}
@Override
public final boolean isPropertyNew ( SessionCache cache,
Name propertyName ) {
return isNew || (changedProperties.containsKey(propertyName) && !isPropertyInWorkspaceCache(cache, propertyName));
}
@Override
public final boolean isPropertyModified ( SessionCache cache,
Name propertyName ) {
return !isNew && changedProperties.containsKey(propertyName) && isPropertyInWorkspaceCache(cache, propertyName);
}
private boolean isPropertyInWorkspaceCache ( SessionCache cache,
Name propertyName ) {
AbstractSessionCache session = session(cache);
CachedNode raw = nodeInWorkspace(session);
if (raw == null ) {
return false ;
}
WorkspaceCache workspaceCache = workspace(cache);
return raw.hasProperty(propertyName, workspaceCache);
}
@Override
public boolean hasChanges () {
return hasPropertyChanges() || hasNonPropertyChanges();
}
@Override
public boolean hasNonPropertyChanges () {
if (isNew) return true ;
if (newParent != null ) return true ;
ChangedChildren changedChildren = changedChildren();
if (changedChildren != null && !changedChildren.isEmpty()) return true ;
MutableChildReferences childRefChanges = appended(false );
if (childRefChanges != null && !childRefChanges.isEmpty()) return true ;
ChangedAdditionalParents additionalParents = additionalParents();
if (additionalParents != null && !additionalParents.isEmpty()) return true ;
ReferrerChanges referrerChanges = referrerChanges(false );
if (referrerChanges != null && !referrerChanges.isEmpty()) return true ;
return false ;
}
@Override
public boolean hasPropertyChanges () {
if (isNew) return true ;
if (!changedProperties.isEmpty()) return true ;
if (!removedProperties.isEmpty()) return true ;
return false ;
}
@Override
public void lock ( boolean sessionScoped ) {
this .lockChange = sessionScoped ? LockChange.LOCK_FOR_SESSION : LockChange.LOCK_FOR_NON_SESSION;
}
@Override
public void unlock () {
this .lockChange = LockChange.UNLOCK;
}
public LockChange getLockChange () {
return lockChange;
}
private boolean addAdditionalParent ( NodeCache cache,
NodeKey newParent ) {
assert newParent != null ;
if (newParent.equals(this .newParent)) return false ;
NodeKey existingParentKey = getParentKey(cache);
if (newParent.equals(existingParentKey)) {
return false ;
}
ChangedAdditionalParents additionalParents = this .additionalParents.get();
if (additionalParents == null ) {
additionalParents = new ChangedAdditionalParents();
if (!this .additionalParents.compareAndSet(null , additionalParents)) {
additionalParents = this .additionalParents.get();
}
}
return additionalParents.add(newParent);
}
private boolean removeAdditionalParent ( NodeCache cache,
NodeKey oldParent ) {
if (getAdditionalParentKeys(cache).contains(oldParent)) {
ChangedAdditionalParents additionalParents = this .additionalParents.get();
if (additionalParents == null ) {
additionalParents = new ChangedAdditionalParents();
if (!this .additionalParents.compareAndSet(null , additionalParents)) {
additionalParents = this .additionalParents.get();
}
}
return additionalParents.remove(oldParent);
}
return false ;
}
protected void replaceParentWithAdditionalParent ( NodeCache cache,
NodeKey oldParent,
NodeKey existingAdditionalParent ) {
assert getAdditionalParentKeys (cache) .contains (existingAdditionalParent) ;
newParent = existingAdditionalParent;
ChangedAdditionalParents additionalParents = this .additionalParents.get();
if (additionalParents == null ) {
additionalParents = new ChangedAdditionalParents();
if (!this .additionalParents.compareAndSet(null , additionalParents)) {
additionalParents = this .additionalParents.get();
}
}
additionalParents.remove(existingAdditionalParent);
additionalParents.remove(oldParent);
}
protected MutableChildReferences appended ( boolean createIfMissing ) {
MutableChildReferences appended = this .appended.get();
if (appended == null && createIfMissing) {
appended = new MutableChildReferences();
if (!this .appended.compareAndSet(null , appended)) {
appended = this .appended.get();
}
}
return appended;
}
protected MixinChanges mixinChanges ( boolean createIfMissing ) {
MixinChanges changes = this .mixinChanges.get();
if (changes == null && createIfMissing) {
changes = new MixinChanges();
if (!this .mixinChanges.compareAndSet(null , changes)) {
changes = this .mixinChanges.get();
}
}
return changes;
}
protected ReferrerChanges referrerChanges ( boolean createIfMissing ) {
ReferrerChanges changes = this .referrerChanges.get();
if (changes == null && createIfMissing) {
changes = new ReferrerChanges();
if (!this .referrerChanges.compareAndSet(null , changes)) {
changes = this .referrerChanges.get();
}
}
return changes;
}
protected final WritableSessionCache writableSession ( NodeCache cache ) {
return (WritableSessionCache)cache.unwrap();
}
protected final AbstractSessionCache session ( NodeCache cache ) {
return (AbstractSessionCache)cache.unwrap();
}
protected final WorkspaceCache workspace ( NodeCache cache ) {
return ((DocumentCache)cache.unwrap()).workspaceCache();
}
protected CachedNode nodeInWorkspace ( AbstractSessionCache session ) {
return isNew() ? null : session.getWorkspace().getNode(key);
}
@Override
public NodeKey getParentKey ( NodeCache cache ) {
if (newParent != null ) {
return newParent;
}
CachedNode cachedNode = nodeInWorkspace(session(cache));
return cachedNode != null ? cachedNode.getParentKey(cache) : null ;
}
@Override
public NodeKey getParentKeyInAnyWorkspace ( NodeCache cache ) {
if (newParent != null ) {
return newParent;
}
CachedNode cachedNode = nodeInWorkspace(session(cache));
return cachedNode != null ? cachedNode.getParentKeyInAnyWorkspace(cache) : null ;
}
protected CachedNode parent ( AbstractSessionCache session ) {
NodeKey parentKey = getParentKey(session);
if (parentKey == null ) {
return null ;
}
return session.getNode(parentKey);
}
protected ChangedAdditionalParents additionalParents () {
return this .additionalParents.get();
}
@Override
public Set getAdditionalParentKeys ( NodeCache cache ) {
AbstractSessionCache session = session(cache);
CachedNode raw = nodeInWorkspace(session);
Set persisted = raw != null ? raw.getAdditionalParentKeys(cache) : null ;
ChangedAdditionalParents additionalParents = this .additionalParents.get();
if (additionalParents == null || additionalParents.isEmpty()) {
return persisted != null ? persisted : Collections.emptySet();
}
Set copy = persisted != null ? new LinkedHashSet(persisted) : new LinkedHashSet();
copy.addAll(additionalParents.getAdditions());
copy.removeAll(additionalParents.getRemovals());
return Collections.unmodifiableSet(copy);
}
@Override
public boolean hasOnlyChangesToAdditionalParents () {
if (isNew) return false ;
if (newParent != null ) return false ;
if (!changedProperties.isEmpty()) return false ;
if (!removedProperties.isEmpty()) return false ;
ChangedChildren changedChildren = changedChildren();
if (changedChildren != null && !changedChildren.isEmpty()) return false ;
MutableChildReferences childRefChanges = appended(false );
if (childRefChanges != null && !childRefChanges.isEmpty()) return false ;
ChangedAdditionalParents additionalParents = additionalParents();
if (additionalParents != null && !additionalParents.isEmpty()) return true ;
return false ;
}
@Override
public boolean isAtOrBelow ( NodeCache cache,
Path path ) {
Path aPath = getPath(cache);
if (path.isAtOrAbove(aPath)) return true ;
ChangedAdditionalParents additionalParents = additionalParents();
if (additionalParents != null && !additionalParents.isEmpty()) {
for (NodeKey parentKey : additionalParents.getAdditions()) {
CachedNode parent = cache.getNode(parentKey);
if (parent.getPath(cache).isAtOrBelow(path)) {
return true ;
}
}
for (NodeKey parentKey : additionalParents.getRemovals()) {
CachedNode parent = cache.getNode(parentKey);
if (parent != null && parent.getPath(cache).isAtOrBelow(path)) {
return true ;
}
}
}
return false ;
}
@Override
public NodeKey getKey () {
return key;
}
@Override
public Name getName ( NodeCache cache ) {
return getSegment(cache).getName();
}
@Override
public Segment getSegment ( NodeCache cache ) {
AbstractSessionCache session = session(cache);
CachedNode parent = parent(session);
return getSegment(cache, parent);
}
protected final Segment getSegment ( NodeCache cache,
CachedNode parent ) {
if (parent != null ) {
ChildReference ref = parent.getChildReferences(cache).getChild(key, new BasicContext());
if (ref == null ) {
throw new NodeNotFoundInParentException(key, parent.getKey());
}
return ref.getSegment();
}
return workspace(cache).childReferenceForRoot().getSegment();
}
@Override
public int getDepth ( NodeCache cache ) throws NodeNotFoundException {
AbstractSessionCache session = session(cache);
CachedNode parent = parent(session);
if (parent != null ) {
return parent.getDepth(cache) + 1 ;
}
CachedNode persistedNode = workspace(cache).getNode(key);
if (persistedNode == null ) {
throw new NodeNotFoundException(key);
}
return 0 ;
}
@Override
public Path getPath ( NodeCache cache ) {
AbstractSessionCache session = session(cache);
CachedNode parent = parent(session);
if (parent != null ) {
Path parentPath = parent.getPath(session);
return session.pathFactory().create(parentPath, getSegment(session, parent));
}
CachedNode persistedNode = workspace(cache).getNode(key);
if (persistedNode == null ) {
throw new NodeNotFoundException(key);
}
return session.rootPath();
}
@Override
public Path getPath ( PathCache pathCache ) throws NodeNotFoundException {
NodeCache cache = pathCache.getCache();
AbstractSessionCache session = session(cache);
CachedNode parent = parent(session);
if (parent != null ) {
Path parentPath = pathCache.getPath(parent);
return session.pathFactory().create(parentPath, getSegment(session, parent));
}
CachedNode persistedNode = workspace(cache).getNode(key);
if (persistedNode == null ) {
throw new NodeNotFoundException(key);
}
return session.rootPath();
}
@Override
public Name getPrimaryType ( NodeCache cache ) {
AbstractSessionCache session = session(cache);
Property prop = getProperty(JcrLexicon.PRIMARY_TYPE, session);
NameFactory nameFactory = session.nameFactory();
return prop != null ? nameFactory.create(prop.getFirstValue()) : nameFactory.create((Object)null );
}
@Override
public boolean hasChangedPrimaryType () {
return changedProperties.containsKey(JcrLexicon.PRIMARY_TYPE);
}
@Override
public Set getMixinTypes ( NodeCache cache ) {
AbstractSessionCache session = session(cache);
Property prop = getProperty(JcrLexicon.MIXIN_TYPES, session);
MixinChanges changes = mixinChanges(false );
if (prop == null || prop.size() == 0 ) {
return changes != null ? changes.getAdded() : Collections.emptySet();
}
final NameFactory nameFactory = session.nameFactory();
if (prop.size() == 1 ) {
Name name = nameFactory.create(prop.getFirstValue());
if (changes == null ) return Collections.singleton(name);
Set all = new HashSet(changes.getAdded());
all.add(name);
all.removeAll(changes.getRemoved());
return all;
}
Set names = new HashSet();
for (Object value : prop) {
Name name = nameFactory.create(value);
if (changes == null || !changes.getRemoved().contains(name)) names.add(name);
}
if (changes != null ) names.addAll(changes.getAdded());
return names;
}
@Override
public void addMixin ( SessionCache cache,
Name mixinName ) {
assert mixinName != null ;
if (isNew) {
Property mixinTypes = cache.getContext().getPropertyFactory().create(JcrLexicon.MIXIN_TYPES, new Name[] {mixinName});
Property existing = changedProperties().putIfAbsent(mixinTypes.getName(), mixinTypes);
while (existing != null ) {
List values = new ArrayList(existing.size());
for (Object value : existing) {
if (mixinName.equals(value)) {
return ;
}
values.add(value);
}
values.add(mixinName);
mixinTypes = cache.getContext().getPropertyFactory().create(JcrLexicon.MIXIN_TYPES, values);
Property existing2 = changedProperties().put(mixinTypes.getName(), mixinTypes);
if (existing2 == existing) {
existing = null ;
} else {
existing = existing2;
}
}
} else {
mixinChanges(true ).add(mixinName);
}
}
@Override
public void removeMixin ( SessionCache cache,
Name mixinName ) {
if (isNew) {
Property existing = changedProperties().get(JcrLexicon.MIXIN_TYPES);
while (existing != null ) {
List values = new ArrayList(existing.size());
boolean found = false ;
for (Object value : existing) {
if (mixinName.equals(value)) {
found = true ;
} else {
values.add(value);
}
}
if (!found) {
return ;
}
Property mixinTypes = cache.getContext().getPropertyFactory().create(JcrLexicon.MIXIN_TYPES, values);
Property existing2 = changedProperties().put(mixinTypes.getName(), mixinTypes);
if (existing2 == existing) {
existing = null ;
} else {
existing = existing2;
}
}
} else {
mixinChanges(true ).remove(mixinName);
}
}
public MixinChanges getMixinChanges () {
return mixinChanges(false );
}
@Override
public Set getAddedMixins ( SessionCache cache ) {
if (!isNew()) {
MixinChanges mixinChanges = mixinChanges(false );
if (mixinChanges != null ) return mixinChanges.getAdded();
return Collections.emptySet();
}
Property prop = changedProperties.get(JcrLexicon.MIXIN_TYPES);
if (prop == null || prop.size() == 0 ) {
return Collections.emptySet();
}
final NameFactory nameFactory = session(cache).nameFactory();
Set names = new HashSet();
for (Object value : prop) {
Name name = nameFactory.create(value);
names.add(name);
}
return names;
}
@Override
public Set getReferrers ( NodeCache cache,
ReferenceType type ) {
AbstractSessionCache session = session(cache);
ReferrerChanges changes = referrerChanges(false );
CachedNode persisted = nodeInWorkspace(session);
if (persisted == null ) {
if (changes == null ) return Collections.emptySet();
return new HashSet(changes.getAddedReferrers(type));
}
Set referrers = persisted.getReferrers(workspace(cache), type);
if (changes != null ) {
referrers.addAll(changes.getAddedReferrers(type));
referrers.removeAll(changes.getRemovedReferrers(type));
}
return referrers;
}
@Override
public ReferrerCounts getReferrerCounts ( NodeCache cache ) {
AbstractSessionCache session = session(cache);
ReferrerChanges changes = referrerChanges(false );
CachedNode persisted = nodeInWorkspace(session);
if (persisted == null ) {
if (changes == null ) return null ;
return changes.getReferrerCounts(null );
}
ReferrerCounts persistedCounts = persisted.getReferrerCounts(workspace(cache));
return changes == null ? persistedCounts : changes.getReferrerCounts(persistedCounts);
}
protected ReferrerChanges getReferrerChanges () {
return referrerChanges(false );
}
@Override
public void addReferrer ( SessionCache cache,
Property property,
NodeKey referrerKey,
ReferenceType type ) {
ReferrerChanges changes = referrerChanges(true );
switch (type) {
case WEAK:
changes.addWeakReferrer(property, referrerKey);
break ;
case STRONG:
changes.addStrongReferrer(property, referrerKey);
break ;
case BOTH:
throw new IllegalArgumentException("The type parameter may be WEAK or STRONG, but may not be BOTH" );
}
}
@Override
public void removeReferrer ( SessionCache cache,
Property property,
NodeKey referrerKey,
ReferenceType type ) {
ReferrerChanges changes = referrerChanges(true );
switch (type) {
case WEAK:
changes.removeWeakReferrer(property, referrerKey);
break ;
case STRONG:
changes.removeStrongReferrer(property, referrerKey);
break ;
case BOTH:
throw new IllegalArgumentException("The type parameter may be WEAK or STRONG, but may not be BOTH" );
}
}
@Override
public int getPropertyCount ( NodeCache cache ) {
int count = changedProperties.size() - removedProperties.size();
AbstractSessionCache session = session(cache);
CachedNode raw = nodeInWorkspace(session);
return raw != null ? count + raw.getPropertyCount(session) : count;
}
@Override
public boolean hasProperties ( NodeCache cache ) {
if (!changedProperties.isEmpty()) return true ;
AbstractSessionCache session = session(cache);
CachedNode raw = nodeInWorkspace(session);
return raw != null ? raw.hasProperties(session) : false ;
}
@Override
public boolean hasProperty ( Name name,
NodeCache cache ) {
if (changedProperties.containsKey(name)) return true ;
if (isPropertyRemoved(name)) return false ;
AbstractSessionCache session = session(cache);
CachedNode raw = nodeInWorkspace(session);
return raw != null ? raw.hasProperty(name, session) : false ;
}
@Override
public Property getProperty ( Name name,
NodeCache cache ) {
Property prop = null ;
if ((prop = changedProperties.get(name)) != null ) return prop;
if (isPropertyRemoved(name)) return null ;
AbstractSessionCache session = session(cache);
CachedNode raw = nodeInWorkspace(session);
return raw != null ? raw.getProperty(name, session) : null ;
}
protected final boolean isPropertyRemoved ( Name name ) {
return !isNew && removedProperties.containsKey(name);
}
@Override
public Properties getPropertiesByName ( final NodeCache cache ) {
final AbstractSessionCache session = session(cache);
final CachedNode raw = nodeInWorkspace(session);
final ConcurrentMap changedProperties = this .changedProperties;
final ConcurrentMap removedProperties = this .removedProperties;
return new Properties() {
@Override
public Property getProperty ( Name name ) {
if (removedProperties.containsKey(name)) return null ;
Property property = changedProperties.get(name);
if (property == null && raw != null ) {
property = raw.getProperty(name, cache);
}
return property;
}
@Override
public Iterator iterator () {
return getProperties(cache);
}
};
}
@Override
public Iterator getProperties ( final NodeCache cache ) {
final AbstractSessionCache session = session(cache);
final CachedNode raw = nodeInWorkspace(session);
final ConcurrentMap changedProperties = this .changedProperties;
Iterable rawProps = raw == null ? null : new Iterable() {
@Override
public Iterator iterator () {
List values = new LinkedList();
for (Iterator iter = raw.getProperties(workspace(cache)); iter.hasNext();) {
Property prop = iter.next();
if (isPropertyRemoved(prop.getName()) || changedProperties.containsKey(prop.getName())) {
continue ;
}
values.add(prop);
}
return values.iterator();
}
};
return new UnionIterator(changedProperties.values().iterator(), rawProps);
}
@Override
public Iterator getProperties ( Collection namePatterns,
NodeCache cache ) {
final AbstractSessionCache session = session(cache);
final NamespaceRegistry registry = session.context().getNamespaceRegistry();
return new PatternIterator(getProperties(session), namePatterns) {
@Override
protected String matchable ( Property value ) {
return value.getName().getString(registry);
}
};
}
@Override
public void setProperty ( SessionCache cache,
Property property ) {
writableSession(cache).assertInSession(this );
Name name = property.getName();
changedProperties.put(name, property);
if (!isNew) removedProperties.remove(name);
updateReferences(cache, name, null );
}
@Override
public void setReference ( SessionCache cache,
Property property,
SessionCache systemCache ) {
assert property.isEmpty() || property.isReference();
writableSession(cache).assertInSession(this );
Name name = property.getName();
changedProperties.put(name, property);
if (!isNew) removedProperties.remove(name);
updateReferences(cache, name, systemCache);
}
private void updateReferences ( SessionCache cache,
Name propertyName,
SessionCache systemCache ) {
Property propertyWhichWasRemoved = null ;
Property propertyWhichWasAdded = null ;
boolean oldPropertyWasReference = false ;
List referencesToRemove = new ArrayList<>();
if (isPropertyModified(cache, propertyName) || isPropertyRemoved(propertyName)) {
CachedNode persistedNode = nodeInWorkspace(session(cache));
Property oldProperty = persistedNode.getProperty(propertyName, cache);
if (oldProperty != null && oldProperty.isReference()) {
oldPropertyWasReference = true ;
propertyWhichWasRemoved = oldProperty;
for (Object referenceObject : oldProperty.getValuesAsArray()) {
assert referenceObject instanceof Reference;
referencesToRemove.add((Reference)referenceObject);
}
}
}
boolean updatedPropertyIsReference = false ;
List referencesToAdd = new ArrayList<>();
Property property = changedProperties.get(propertyName);
if (property != null && property.isReference()) {
updatedPropertyIsReference = true ;
propertyWhichWasAdded = property;
for (Object referenceObject : property.getValuesAsArray()) {
assert referenceObject instanceof Reference;
Reference updatedReference = (Reference)referenceObject;
if (referencesToRemove.contains(updatedReference)) {
if (referencesToRemove.remove(updatedReference)) {
NodeKey referredKey = nodeKeyFromReference(updatedReference);
CachedNode referredNode = cache.getNode(referredKey);
if (referredNode instanceof SessionNode) {
SessionNode changedReferrerNode = (SessionNode)referredNode;
ReferrerChanges changes = changedReferrerNode.getReferrerChanges();
if (changes != null ) {
ReferenceType type = updatedReference.isWeak() ? ReferenceType.WEAK : ReferenceType.STRONG;
if (changes.isRemovedReferrer(key, type)) {
if (updatedReference.isWeak()) changes.addWeakReferrer(propertyWhichWasRemoved, key);
else changes.addStrongReferrer(propertyWhichWasRemoved, key);
}
}
}
}
} else {
referencesToAdd.add(updatedReference);
}
}
}
if (referencesToRemove.isEmpty() && referencesToAdd.isEmpty() && oldPropertyWasReference && updatedPropertyIsReference) {
changedProperties.remove(propertyName);
return ;
}
if (!referencesToRemove.isEmpty()) {
addOrRemoveReferrers(cache, systemCache, propertyWhichWasRemoved, referencesToRemove.iterator(), false );
}
if (!referencesToAdd.isEmpty()) {
addOrRemoveReferrers(cache, systemCache, propertyWhichWasAdded, referencesToAdd.iterator(), true );
}
}
protected void removeAllReferences ( SessionCache cache ) {
for (Iterator it = this .getProperties(cache); it.hasNext();) {
Property property = it.next();
if (!property.isReference()) {
continue ;
}
this .addOrRemoveReferrers(cache, null , property, property.getValues(), false );
}
}
protected void addOrRemoveReferrers ( SessionCache cache,
SessionCache systemCache,
Property property,
Iterator referenceValuesIterator,
boolean add ) {
boolean isFrozenNode = JcrNtLexicon.FROZEN_NODE.equals(this .getPrimaryType(cache));
while (referenceValuesIterator.hasNext()) {
Object value = referenceValuesIterator.next();
assert value instanceof Reference;
Reference reference = (Reference)value;
NodeKey referredKey = nodeKeyFromReference(reference);
boolean isWeak = reference.isWeak();
if (isFrozenNode && !isWeak) {
return ;
}
SessionNode referredNode = null ;
WritableSessionCache writableSessionCache = null ;
if (cache.getNode(referredKey) != null ) {
writableSessionCache = writableSession(cache);
referredNode = writableSessionCache.mutable(referredKey);
} else if (systemCache != null && systemCache.getNode(referredKey) != null ) {
writableSessionCache = writableSession(systemCache);
referredNode = writableSessionCache.mutable(referredKey);
}
if (referredNode == null ) {
continue ;
}
assert writableSessionCache != null ;
ReferenceType referenceType = isWeak ? ReferenceType.WEAK : ReferenceType.STRONG;
if (add) {
referredNode.addReferrer(cache, property, key, referenceType);
} else {
referredNode.removeReferrer(cache, property, key, referenceType);
}
if (!referredNode.hasChanges()) {
writableSessionCache.clear(referredNode);
}
}
}
private NodeKey nodeKeyFromReference ( Reference reference ) {
if (reference instanceof NodeKeyReference) {
return ((NodeKeyReference)reference).getNodeKey();
} else if (reference instanceof StringReference) {
return new NodeKey(reference.getString());
}
throw new IllegalArgumentException("Unknown reference type: " + reference.getClass().getSimpleName());
}
@Override
public void setPropertyIfUnchanged ( SessionCache cache,
Property property ) {
Name propertyName = property.getName();
boolean isModified = changedProperties.containsKey(propertyName)
&& (isNew || isPropertyInWorkspaceCache(cache, propertyName));
if (!isModified) {
setProperty(cache, property);
}
}
@Override
public void setProperties ( SessionCache cache,
Iterable properties ) {
writableSession(cache).assertInSession(this );
for (Property property : properties) {
Name name = property.getName();
changedProperties.put(name, property);
if (!isNew) removedProperties.remove(name);
updateReferences(cache, name, null );
}
}
@Override
public void setProperties ( SessionCache cache,
Iterator properties ) {
writableSession(cache).assertInSession(this );
while (properties.hasNext()) {
Property property = properties.next();
Name name = property.getName();
changedProperties.put(name, property);
if (!isNew) removedProperties.remove(name);
updateReferences(cache, name, null );
}
}
@Override
public void removeProperty ( SessionCache cache,
Name name ) {
writableSession(cache).assertInSession(this );
changedProperties.remove(name);
if (!isNew) {
AbstractSessionCache session = session(cache);
CachedNode raw = nodeInWorkspace(session);
if (raw.hasProperty(name, cache)) {
removedProperties.put(name, name);
}
}
updateReferences(cache, name, null );
}
@Override
public void removeAllProperties ( SessionCache cache ) {
writableSession(cache).assertInSession(this );
CachedNode raw = null ;
for (Iterator propertyIterator = getProperties(cache); propertyIterator.hasNext();) {
Name name = propertyIterator.next().getName();
changedProperties.remove(name);
if (!isNew) {
if (raw == null ) {
AbstractSessionCache session = session(cache);
raw = nodeInWorkspace(session);
}
if (raw.hasProperty(name, cache)) {
removedProperties.put(name, name);
}
}
updateReferences(cache, name, null );
}
}
@Override
public ChildReferences getChildReferences ( NodeCache cache ) {
if (isNew) {
return new SessionChildReferences(null , appended.get(), changedChildren);
}
CachedNode persistedNode = nodeInWorkspace(session(cache));
ChildReferences persisted = persistedNode != null ? persistedNode.getChildReferences(cache) : null ;
return new SessionChildReferences(persisted, appended.get(), changedChildren);
}
@Override
public MutableCachedNode createChild ( SessionCache cache,
NodeKey key,
Name name,
Property firstProperty,
Property... additionalProperties ) {
WritableSessionCache session = writableSession(cache);
session.assertInSession(this );
if (key == null ) key = getKey().withRandomId();
SessionNode child = new SessionNode(key, true );
child = session.add(child);
if (firstProperty != null ) {
child.setProperty(cache, firstProperty);
}
if (additionalProperties != null ) {
for (Property property : additionalProperties) {
if (property != null ) child.setProperty(cache, property);
}
}
child.newParent = this .key;
appended(true ).append(key, name);
return child;
}
@Override
public MutableCachedNode createChild ( SessionCache cache,
NodeKey key,
Name name,
Iterable properties ) {
WritableSessionCache session = writableSession(cache);
session.assertInSession(this );
if (key == null ) key = getKey().withRandomId();
SessionNode child = new SessionNode(key, true );
child = session.add(child);
if (properties != null ) {
for (Property property : properties) {
if (property != null ) child.setProperty(cache, property);
}
}
child.newParent = this .key;
appended(true ).append(key, name);
return child;
}
@Override
public void removeChild ( SessionCache cache,
NodeKey key ) {
WritableSessionCache session = writableSession(cache);
session.assertInSession(this );
removeChildFromNode(session, key);
}
@Override
public void moveChild ( SessionCache cache,
NodeKey key,
MutableCachedNode newParent,
Name newName ) {
assert newParent != this ;
assert newParent != null ;
WritableSessionCache session = writableSession(cache);
session.assertInSession(this );
SessionNode node = removeChildFromNode(session, key);
if (newName == null ) newName = node.getName(session);
((SessionNode)newParent).appended(true ).append(key, newName);
node.newParent = newParent.getKey();
}
protected SessionNode removeChildFromNode ( AbstractSessionCache session,
NodeKey childKey ) {
SessionNode child = session.mutable(childKey);
if (child.getParentKey(session).equals(this .key)) {
Set additionalParentKeys = child.getAdditionalParentKeys(session);
if (additionalParentKeys.isEmpty()) {
child.newParent = null ;
} else {
NodeKey newParentKey = additionalParentKeys.iterator().next();
child.replaceParentWithAdditionalParent(session, this .key, newParentKey);
}
} else {
boolean removed = child.removeAdditionalParent(session, this .key);
if (!removed) {
if (!getChildReferences(session).hasChild(childKey)) {
throw new NodeNotFoundException(childKey);
}
}
}
MutableChildReferences appended = this .appended.get();
ChildReference removed = null ;
if (appended != null ) {
removed = appended.remove(childKey);
}
if (removed == null ) {
changedChildren.remove(childKey);
}
return child;
}
@Override
public void reorderChild ( SessionCache cache,
NodeKey key,
NodeKey nextNode ) {
WritableSessionCache session = writableSession(cache);
session.assertInSession(this );
ChildReferences references = getChildReferences(session);
ChildReference before = null ;
if (nextNode != null ) {
before = references.getChild(nextNode);
if (before == null ) throw new NodeNotFoundException(key);
}
MutableChildReferences appended = this .appended.get();
ChildReference toBeMoved = null ;
if (appended != null ) {
toBeMoved = appended.remove(key);
}
if (toBeMoved == null ) {
toBeMoved = references.getChild(key);
if (toBeMoved == null ) throw new NodeNotFoundException(key);
if (changedChildren.inserted(key) == null ) {
changedChildren.remove(key);
}
}
if (nextNode == null ) {
appended(true ).append(key, toBeMoved.getName());
} else {
changedChildren.insertBefore(before, toBeMoved);
}
}
@Override
public void renameChild ( SessionCache cache,
NodeKey key,
Name newName ) {
WritableSessionCache session = writableSession(cache);
session.assertInSession(this );
ChildReferences references = getChildReferences(session);
if (!references.hasChild(key)) throw new NodeNotFoundException(key);
cache.mutable(key);
MutableChildReferences appended = this .appended.get();
if (appended != null && appended.hasChild(key)) {
appended.remove(key);
appended.append(key, newName);
} else {
changedChildren.renameTo(key, newName);
}
}
@Override
public boolean linkChild ( SessionCache cache,
NodeKey childKey,
Name name ) {
WritableSessionCache session = writableSession(cache);
session.assertInSession(this );
SessionNode child = session.mutable(childKey);
if (!child.isNew() && this .key.equals(child.getParentKey(cache))) {
return false ;
}
if (child.addAdditionalParent(cache, this .key)) {
appended(true ).append(childKey, name);
}
return true ;
}
@Override
public String getEtag ( SessionCache cache ) {
StringBuilder sb = new StringBuilder();
Iterator iter = getProperties(cache);
while (iter.hasNext()) {
Property prop = iter.next();
if (prop.isEmpty()) continue ;
for (Object value : prop) {
if (value instanceof BinaryValue) {
BinaryValue binary = (BinaryValue)value;
sb.append(binary.getHexHash());
}
}
}
return sb.toString();
}
@Override
public Map deepCopy ( SessionCache cache,
CachedNode sourceNode,
SessionCache sourceCache,
String systemWorkspaceKey,
Connectors connectors ) {
final WritableSessionCache writableSessionCache = writableSession(cache);
writableSessionCache.assertInSession(this );
DeepCopy copier = new DeepCopy(this , writableSessionCache, sourceNode, sourceCache, systemWorkspaceKey, connectors);
copier.execute();
return copier.getSourceToTargetKeys();
}
@Override
public void deepClone ( SessionCache cache,
CachedNode sourceNode,
SessionCache sourceCache,
String systemWorkspaceKey,
Connectors connectors ) {
final WritableSessionCache writableSessionCache = writableSession(cache);
writableSessionCache.assertInSession(this );
DeepClone cloner = new DeepClone(this , writableSessionCache, sourceNode, sourceCache, systemWorkspaceKey, connectors);
cloner.execute();
}
@Override
public Set removedChildren () {
return changedChildren().getRemovals();
}
@Override
public Set getChangedReferrerNodes () {
Set result = new HashSet();
ReferrerChanges referrerChanges = getReferrerChanges();
if (referrerChanges == null ) return Collections.emptySet();
result.addAll(referrerChanges.getAddedReferrers(ReferenceType.BOTH));
result.addAll(referrerChanges.getRemovedReferrers(ReferenceType.BOTH));
return result;
}
@Override
public void addFederatedSegment ( String externalNodeKey,
String segmentName ) {
if (federatedSegments.get() == null ) {
federatedSegments.compareAndSet(null , new FederatedSegmentChanges());
}
this .federatedSegments.get().addSegment(externalNodeKey, segmentName);
}
protected Map getAddedFederatedSegments () {
return this .federatedSegments.get() != null ? this .federatedSegments.get().getAdditions() : Collections.emptyMap();
}
@Override
public void removeFederatedSegment ( String externalNodeKey ) {
if (federatedSegments.get() == null ) {
federatedSegments.compareAndSet(null , new FederatedSegmentChanges());
}
this .federatedSegments.get().removeSegment(externalNodeKey);
}
protected Set getRemovedFederatedSegments () {
return this .federatedSegments.get() != null ? this .federatedSegments.get().getRemovals() : Collections.emptySet();
}
@SuppressWarnings ( "synthetic-access" )
@Override
public NodeChanges getNodeChanges () {
return new NodeChanges();
}
@Override
public boolean isQueryable ( NodeCache cache ) {
Boolean isQueryable = this .isQueryable.get();
if (isQueryable != null ) {
return isQueryable;
}
CachedNode persistedNode = nodeInWorkspace(session(cache));
return persistedNode == null || persistedNode.isQueryable(cache);
}
@Override
public void setQueryable ( boolean queryable ) {
this .isQueryable.set(queryable);
}
@Override
public boolean hasACL ( NodeCache cache ) {
return getChildReferences(cache).getChild(ModeShapeLexicon.ACCESS_LIST_NODE_NAME) != null ;
}
@Override
public Map> getPermissions( NodeCache cache ) {
if (!hasACL(cache)) {
return null ;
}
ChildReference aclNodeReference = getChildReferences(cache).getChild(ModeShapeLexicon.ACCESS_LIST_NODE_NAME);
if (aclNodeReference == null ) {
return null ;
}
CachedNode aclNode = cache.getNode(aclNodeReference);
if (aclNode == null ) {
return null ;
}
Map> result = new HashMap<>();
ChildReferences permissionsReference = aclNode.getChildReferences(cache);
for (ChildReference permissionReference : permissionsReference) {
CachedNode permission = cache.getNode(permissionReference);
String name = permission.getProperty(ModeShapeLexicon.PERMISSION_PRINCIPAL_NAME, cache).getFirstValue().toString();
Property privileges = permission.getProperty(ModeShapeLexicon.PERMISSION_PRIVILEGES_NAME, cache);
Set privilegeNames = new HashSet<>();
for (Object privilege : privileges.getValuesAsArray()) {
privilegeNames.add(privilege.toString());
}
result.put(name, privilegeNames);
}
return result;
}
@Override
public PermissionChanges setPermissions (SessionCache cache, Map > privilegesByPrincipalName ) {
assert privilegesByPrincipalName != null ;
if (this .isExternal(cache)) {
throw new UnsupportedOperationException(JcrI18n.aclsOnExternalNodesNotAllowed.text());
}
if (!this .getMixinTypes(cache).contains(ModeShapeLexicon.ACCESS_CONTROLLABLE)) {
addMixin(cache, ModeShapeLexicon.ACCESS_CONTROLLABLE);
}
ChildReference aclNodeRef = getChildReferences(cache).getChild(ModeShapeLexicon.ACCESS_LIST_NODE_NAME);
MutableCachedNode aclNode = null ;
PropertyFactory propertyFactory = cache.getContext().getPropertyFactory();
ChildReferences permissionsReferences = null ;
if (aclNodeRef != null ) {
aclNode = cache.mutable(aclNodeRef.getKey());
permissionsReferences = aclNode.getChildReferences(cache);
for (ChildReference permissionRef : permissionsReferences) {
CachedNode permissionNode = cache.getNode(permissionRef);
String principalName = permissionNode.getProperty(ModeShapeLexicon.PERMISSION_PRINCIPAL_NAME, cache)
.getFirstValue().toString();
if (!privilegesByPrincipalName.containsKey(principalName)) {
permissionChanges().principalRemoved(principalName);
NodeKey permissionNodeKey = permissionNode.getKey();
aclNode.removeChild(cache, permissionNodeKey);
cache.destroy(permissionNodeKey);
}
}
} else {
org.modeshape.jcr.value.Property primaryType = propertyFactory.create(JcrLexicon.PRIMARY_TYPE,
ModeShapeLexicon.ACCESS_LIST_NODE_TYPE);
aclNode = this .createChild(cache, null , ModeShapeLexicon.ACCESS_LIST_NODE_NAME, primaryType);
permissionsReferences = ImmutableChildReferences.EmptyChildReferences.INSTANCE;
}
NameFactory nameFactory = cache.getContext().getValueFactories().getNameFactory();
for (String principal : privilegesByPrincipalName.keySet()) {
Name principalName = nameFactory.create(principal);
ChildReference permissionRef = permissionsReferences.getChild(principalName);
if (permissionRef == null ) {
permissionChanges().principalAdded(principal);
org.modeshape.jcr.value.Property primaryType = propertyFactory.create(
JcrLexicon.PRIMARY_TYPE, ModeShapeLexicon.PERMISSION);
Property principalProp = propertyFactory.create(ModeShapeLexicon.PERMISSION_PRINCIPAL_NAME,
principal);
Property privileges = propertyFactory.create(ModeShapeLexicon.PERMISSION_PRIVILEGES_NAME,
privilegesByPrincipalName.get(principal));
aclNode.createChild(cache, null , principalName, primaryType, principalProp, privileges);
} else {
MutableCachedNode permissionNode = cache.mutable(permissionRef.getKey());
Property privileges = propertyFactory.create(ModeShapeLexicon.PERMISSION_PRIVILEGES_NAME,
privilegesByPrincipalName.get(principal));
permissionNode.setProperty(cache, privileges);
}
}
return permissionChanges();
}
protected PermissionChanges permissionChanges () {
if (permissionChanges.get() == null ) {
permissionChanges.compareAndSet(null , new PermissionChanges());
}
return permissionChanges.get();
}
@Override
public MutableCachedNode.PermissionChanges removeACL ( SessionCache cache ) {
if (this .isExternal(cache)) {
throw new UnsupportedOperationException(JcrI18n.aclsOnExternalNodesNotAllowed.text());
}
if (hasACL(cache)) {
NodeKey aclNodeKey = getChildReferences(cache).getChild(ModeShapeLexicon.ACCESS_LIST_NODE_NAME).getKey();
MutableCachedNode mutableACLNode = cache.mutable(aclNodeKey);
for (ChildReference permissionRef : mutableACLNode.getChildReferences(cache)) {
permissionChanges().principalRemoved(permissionRef.getName().getString());
}
if (!cache.isDestroyed(aclNodeKey)) {
this .removeChild(cache, aclNodeKey);
cache.destroy(aclNodeKey);
}
removeMixin(cache, ModeShapeLexicon.ACCESS_CONTROLLABLE);
}
return permissionChanges();
}
@Override
public boolean isExternal ( NodeCache cache ) {
return !getKey().getSourceKey().equals(cache.getRootKey().getSourceKey());
}
@Override
public int hashCode () {
return key.hashCode();
}
@Override
public boolean equals ( Object obj ) {
if (obj == this ) return true ;
if (obj instanceof CachedNode) {
CachedNode that = (CachedNode)obj;
return this .getKey().equals(that.getKey());
}
return false ;
}
@Override
public String toString () {
return getString(null );
}
public String getString ( NamespaceRegistry registry ) {
StringBuilder sb = new StringBuilder();
sb.append("Node '" ).append(key).append("' ->" );
NodeKey newParent = this .newParent;
if (isNew) {
if (newParent != null ) {
sb.append(" created under '" ).append(newParent).append('\'' );
} else {
sb.append(" created; " );
}
} else {
if (newParent != null ) {
sb.append(" moved to '" ).append(newParent).append('\'' );
}
}
ChangedAdditionalParents additionalParents = this .additionalParents();
if (additionalParents != null ) {
sb.append(" parents: [" );
if (!additionalParents.getAdditions().isEmpty()) {
sb.append("+" ).append(additionalParents.getAdditions());
}
if (!additionalParents.getRemovals().isEmpty()) {
sb.append("-" ).append(additionalParents.getRemovals());
}
sb.append(']' );
}
boolean changedProps = !changedProperties.isEmpty();
boolean removedProps = !removedProperties.isEmpty();
if (changedProps || removedProps) {
sb.append(" props: {" );
if (changedProps) {
boolean first = true ;
for (Map.Entry entry : changedProperties.entrySet()) {
if (first) {
first = false ;
} else {
sb.append(',' );
}
Property property = entry.getValue();
sb.append(" +" ).append(property.getString(registry));
}
}
if (removedProps) {
boolean first = true ;
for (Name name : removedProperties.keySet()) {
if (first) {
first = false ;
} else {
sb.append(',' );
}
sb.append(" -" ).append(name.getString(registry));
}
}
sb.append('}' );
}
MutableChildReferences appended = appended(false );
if (!changedChildren.isEmpty() || (appended != null && !appended.isEmpty())) {
sb.append(" children: " );
if (!changedChildren.isEmpty()) {
changedChildren.getString(sb);
sb.append(' ' );
}
if (appended != null && !appended.isEmpty()) {
sb.append("appended [" );
Iterator iter = appended.iterator();
if (iter.hasNext()) {
sb.append(iter.next().toString(registry));
while (iter.hasNext()) {
sb.append(',' );
sb.append(iter.next().toString(registry));
}
}
sb.append(']' );
}
}
ReferrerChanges referrerChg = getReferrerChanges();
if (referrerChg != null && !referrerChg.isEmpty()) {
sb.append(' ' );
referrerChg.getString(sb);
}
return sb.toString();
}
private class NodeChanges implements MutableCachedNode .NodeChanges {
private NodeChanges () {
}
@Override
public Set changedPropertyNames () {
Set result = new HashSet();
result.addAll(changedProperties().keySet());
return result;
}
@Override
public Set removedPropertyNames () {
return new HashSet(removedProperties());
}
@Override
public Set addedMixins () {
Set result = new HashSet();
MixinChanges mixinChanges = mixinChanges(false );
if (mixinChanges != null ) {
result.addAll(mixinChanges.getAdded());
}
return result;
}
@Override
public Set removedMixins () {
Set result = new HashSet();
MixinChanges mixinChanges = mixinChanges(false );
if (mixinChanges != null ) {
result.addAll(mixinChanges.getRemoved());
}
return result;
}
@Override
public LinkedHashMap appendedChildren () {
LinkedHashMap result = new LinkedHashMap();
MutableChildReferences appendedChildReferences = appended(false );
if (appendedChildReferences != null ) {
for (ChildReference appendedChildReference : appendedChildReferences) {
result.put(appendedChildReference.getKey(), appendedChildReference.getName());
}
}
return result;
}
@Override
public Set removedChildren () {
Set result = new HashSet();
result.addAll(changedChildren().getRemovals());
return result;
}
@Override
public Map renamedChildren () {
Map result = new HashMap();
result.putAll(changedChildren().getNewNames());
return result;
}
@Override
public Map> childrenInsertedBefore() {
Map> result = new HashMap>();
Map insertionsByBeforeKey = changedChildren().getInsertionsByBeforeKey();
for (NodeKey beforeNodeKey : insertionsByBeforeKey.keySet()) {
Insertions insertionsBefore = insertionsByBeforeKey.get(beforeNodeKey);
if (insertionsBefore != null ) {
LinkedHashMap insertionsBeforeMap = new LinkedHashMap();
for (ChildReference childReference : insertionsBefore.inserted()) {
insertionsBeforeMap.put(childReference.getKey(), childReference.getName());
}
result.put(beforeNodeKey, insertionsBeforeMap);
}
}
return result;
}
@Override
public Set addedParents () {
Set result = new HashSet();
if (additionalParents() != null ) {
result.addAll(additionalParents().getAdditions());
}
return result;
}
@Override
public Set removedParents () {
Set result = new HashSet();
if (additionalParents() != null ) {
result.addAll(additionalParents().getRemovals());
}
return result;
}
@Override
public NodeKey newPrimaryParent () {
return newParent();
}
@Override
public Set addedWeakReferrers () {
Set result = new HashSet();
ReferrerChanges referrerChanges = referrerChanges(false );
if (referrerChanges != null ) {
result.addAll(referrerChanges.getAddedReferrers(ReferenceType.WEAK));
}
return result;
}
@Override
public Set removedWeakReferrers () {
Set result = new HashSet();
ReferrerChanges referrerChanges = referrerChanges(false );
if (referrerChanges != null ) {
result.addAll(referrerChanges.getRemovedReferrers(ReferenceType.WEAK));
}
return result;
}
@Override
public Set addedStrongReferrers () {
Set result = new HashSet();
ReferrerChanges referrerChanges = referrerChanges(false );
if (referrerChanges != null ) {
result.addAll(referrerChanges.getAddedReferrers(ReferenceType.STRONG));
}
return result;
}
@Override
public Set removedStrongReferrers () {
Set result = new HashSet();
ReferrerChanges referrerChanges = referrerChanges(false );
if (referrerChanges != null ) {
result.addAll(referrerChanges.getRemovedReferrers(ReferenceType.STRONG));
}
return result;
}
}
@ThreadSafe
protected static class ChangedAdditionalParents {
private final Set removals = new ConcurrentHashSet();
private final Set additions = new CopyOnWriteArraySet();
public boolean isEmpty () {
return additionCount() == 0 && removalCount() == 0 ;
}
public int additionCount () {
return additions.size();
}
public int removalCount () {
return removals.size();
}
public boolean remove ( NodeKey key ) {
return additions.remove(key) || removals.add(key);
}
public boolean add ( NodeKey key ) {
return removals.remove(key) || additions.add(key);
}
public Set getAdditions () {
return additions;
}
public Set getRemovals () {
return removals;
}
}
@ThreadSafe
protected static class ChangedChildren implements ChildReferences .Changes {
private final AtomicReference> removals = new AtomicReference>();
private final AtomicReference insertions = new AtomicReference();
private final AtomicReference> newNames = new AtomicReference>();
@Override
public boolean isEmpty () {
return insertionCount() == 0 && removalCount() == 0 && renameCount() == 0 ;
}
@Override
public int insertionCount () {
InsertedChildReferences insertions = this .insertions.get();
return insertions == null ? 0 : insertions.size();
}
@Override
public int insertionCount ( Name name ) {
InsertedChildReferences insertions = this .insertions.get();
return insertions == null ? 0 : insertions.size(name);
}
@Override
public int removalCount () {
Set removals = this .removals.get();
return removals == null ? 0 : removals.size();
}
@Override
public int renameCount () {
Map newNames = this .newNames.get();
return newNames == null ? 0 : newNames.size();
}
@Override
public ChildReference inserted ( NodeKey key ) {
InsertedChildReferences insertions = this .insertions.get();
return insertions == null ? null : insertions.inserted(key);
}
@Override
public Iterator insertions ( Name name ) {
InsertedChildReferences insertions = this .insertions.get();
return insertions == null ? null : insertions.insertions(name);
}
@Override
public ChildInsertions insertionsBefore ( ChildReference key ) {
InsertedChildReferences insertions = this .insertions.get();
return insertions == null ? null : insertions.insertionsBefore(key.getKey());
}
public void insertBefore ( ChildReference before,
ChildReference inserted ) {
if (before == inserted) {
return ;
}
InsertedChildReferences insertions = this .insertions.get();
if (insertions == null ) {
insertions = new InsertedChildReferences();
if (!this .insertions.compareAndSet(null , insertions)) {
insertions = this .insertions.get();
}
}
insertions.insertBefore(before, inserted);
}
@Override
public boolean isRemoved ( ChildReference ref ) {
Set removals = this .removals.get();
return removals != null && removals.contains(ref.getKey());
}
@Override
public boolean isRenamed ( ChildReference ref ) {
Map renames = this .newNames.get();
return renames != null && renames.containsKey(ref.getKey());
}
@Override
public boolean isRenamed ( Name newName ) {
Map renames = this .newNames.get();
return renames != null && renames.containsValue(newName);
}
public boolean remove ( NodeKey key ) {
InsertedChildReferences insertions = this .insertions.get();
if (insertions != null && insertions.remove(key)) {
Set removals = this .removals.get();
assert removals == null || !removals.contains(key);
return true ;
}
Set removals = this .removals.get();
if (removals == null ) {
removals = new ConcurrentHashSet();
if (!this .removals.compareAndSet(null , removals)) {
removals = this .removals.get();
}
}
removals.add(key);
return true ;
}
@Override
public Name renamed ( NodeKey key ) {
Map newNames = this .newNames.get();
return newNames == null ? null : newNames.get(key);
}
public void renameTo ( NodeKey key,
Name newName ) {
Map newNames = this .newNames.get();
if (newNames == null ) {
newNames = new ConcurrentHashMap();
if (!this .newNames.compareAndSet(null , newNames)) {
newNames = this .newNames.get();
}
}
newNames.put(key, newName);
}
@Override
public String toString () {
return getString(new StringBuilder()).toString();
}
public StringBuilder getString ( StringBuilder sb ) {
InsertedChildReferences insertions = this .insertions.get();
Set removals = this .removals.get();
Map renames = this .newNames.get();
if (insertions != null ) {
insertions.toString(sb);
if (removals != null && !removals.isEmpty()) sb.append("; " );
}
if (removals != null && !removals.isEmpty()) {
sb.append("removals: " ).append(removals);
if (renames != null && !renames.isEmpty()) {
sb.append("; " );
}
}
if (renames != null && !renames.isEmpty()) {
sb.append("renames: " ).append(renames);
}
return sb;
}
public Map getNewNames () {
Map newNames = this .newNames.get();
return newNames == null ? Collections.emptyMap() : newNames;
}
public Set getRemovals () {
Set removals = this .removals.get();
return removals == null ? Collections.emptySet() : new HashSet(removals);
}
public Map getInsertionsByBeforeKey () {
InsertedChildReferences insertedRefs = insertions.get();
if (insertedRefs == null || insertedRefs.size() == 0 ) return Collections.emptyMap();
Map result = new HashMap();
for (Insertions insertions : insertedRefs) {
result.put(insertions.insertedBefore().getKey(), insertions);
}
return result;
}
}
@ThreadSafe
protected static class FederatedSegmentChanges {
private final ConcurrentHashMap additions = new ConcurrentHashMap();
private final Set removals = Collections.synchronizedSet(new HashSet());
protected void addSegment ( String externalNodeKey,
String name ) {
additions.putIfAbsent(externalNodeKey, name);
}
protected void removeSegment ( String externalNodeKey ) {
removals.add(externalNodeKey);
}
protected Map getAdditions () {
return Collections.unmodifiableMap(additions);
}
protected Set getRemovals () {
return Collections.unmodifiableSet(removals);
}
}
@ThreadSafe
protected static class InsertedChildReferences implements Iterable {
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Set inserted = new HashSet();
private final Map insertedNames = new HashMap();
private final ConcurrentMap insertedBefore = new ConcurrentHashMap();
public int size () {
return inserted.size();
}
@Override
public Iterator iterator () {
return insertedBefore.values().iterator();
}
public ChildReference inserted ( NodeKey key ) {
Lock lock = this .lock.readLock();
try {
lock.lock();
if (!inserted.contains(key)) {
return null ;
}
for (Insertions insertions : insertedBefore.values()) {
for (ChildReference inserted : insertions.inserted()) {
if (inserted.getKey().equals(key)) return inserted;
}
}
return null ;
} finally {
lock.unlock();
}
}
public Iterator insertions ( Name name ) {
Lock lock = this .lock.readLock();
try {
lock.lock();
if (!insertedNames.containsKey(name)) {
return null ;
}
List namedInsertions = new LinkedList();
for (Insertions insertions : insertedBefore.values()) {
Insertions byName = null ;
for (ChildReference inserted : insertions.inserted()) {
if (inserted.getName().equals(name)) {
if (byName == null ) {
byName = new Insertions(insertions.insertedBefore(), inserted);
} else {
byName.add(inserted);
}
}
}
if (byName != null ) namedInsertions.add(byName);
}
return namedInsertions.iterator();
} finally {
lock.unlock();
}
}
public ChildInsertions insertionsBefore ( NodeKey key ) {
return insertedBefore.get(key);
}
public void insertBefore ( ChildReference before,
ChildReference inserted ) {
Lock lock = this .lock.writeLock();
try {
lock.lock();
inserted = inserted.with(1 );
Insertions insertions = insertedBefore.get(before.getKey());
if (insertions == null ) {
insertions = new Insertions(before, inserted);
insertedBefore.put(before.getKey(), insertions);
} else {
insertions.add(inserted);
}
AtomicInteger count = this .insertedNames.get(inserted.getName());
if (count == null ) {
this .insertedNames.put(inserted.getName(), new AtomicInteger(1 ));
} else {
count.incrementAndGet();
}
boolean added = this .inserted.add(inserted.getKey());
assert added;
} finally {
lock.unlock();
}
}
public boolean remove ( NodeKey key ) {
Lock lock = this .lock.writeLock();
try {
lock.lock();
ChildReference removed = null ;
if (this .inserted.remove(key)) {
for (Insertions insertions : insertedBefore.values()) {
removed = insertions.remove(key);
if (removed != null ) {
insertedBefore.remove(insertions.insertedBefore(), new Insertions(insertions.insertedBefore()));
break ;
}
}
if (removed != null ) {
Name name = removed.getName();
AtomicInteger count = this .insertedNames.get(name);
if (count != null ) {
if (count.decrementAndGet() == 0 ) {
this .insertedNames.remove(name);
}
}
}
return true ;
}
return false ;
} finally {
lock.unlock();
}
}
public boolean remove ( ChildReference inserted ) {
Lock lock = this .lock.writeLock();
try {
lock.lock();
if (this .inserted.remove(inserted.getKey())) {
AtomicInteger count = this .insertedNames.get(inserted.getName());
if (count != null ) {
if (count.decrementAndGet() == 0 ) {
this .insertedNames.remove(inserted.getName());
}
}
for (Insertions insertions : insertedBefore.values()) {
if (insertions.remove(inserted)) {
insertedBefore.remove(insertions.insertedBefore(), new Insertions(insertions.insertedBefore()));
}
}
return true ;
}
return false ;
} finally {
lock.unlock();
}
}
@Override
public String toString () {
return toString(new StringBuilder()).toString();
}
public StringBuilder toString ( StringBuilder sb ) {
int number = size();
sb.append(number).append(' ' ).append(Inflector.getInstance().pluralize("insertion" , number)).append(": " );
Iterator iter = iterator();
if (iter.hasNext()) {
sb.append(iter.next());
while (iter.hasNext()) {
sb.append(", " );
sb.append(iter.next());
}
}
return sb;
}
protected int size ( Name name ) {
lock.readLock().lock();
try {
AtomicInteger count = insertedNames.get(name);
return count != null ? count.get() : 0 ;
} finally {
lock.readLock().unlock();
}
}
}
protected static class Insertions implements ChildReferences .ChildInsertions {
private final List inserted = new CopyOnWriteArrayList();
private final ChildReference before;
protected Insertions ( ChildReference before ) {
this .before = before;
}
protected Insertions ( ChildReference before,
ChildReference inserted ) {
this .before = before;
this .inserted.add(inserted);
}
@Override
public Iterable inserted () {
return inserted;
}
@Override
public ChildReference insertedBefore () {
return before;
}
public void add ( ChildReference reference ) {
this .inserted.add(reference);
}
public boolean contains ( ChildReference reference ) {
return this .inserted.contains(reference);
}
public boolean remove ( ChildReference reference ) {
return this .inserted.remove(reference);
}
public ChildReference remove ( NodeKey key ) {
for (ChildReference ref : this .inserted) {
if (ref.getKey().equals(key) && remove(ref)) return ref;
}
return null ;
}
@Override
public String toString () {
return inserted + " before " + before;
}
@Override
public int hashCode () {
return this .before.hashCode();
}
@Override
public boolean equals ( Object obj ) {
if (obj == this ) return true ;
if (obj instanceof Insertions) {
Insertions that = (Insertions)obj;
return this .before.equals(that.before) && this .inserted.equals(that.inserted);
}
return false ;
}
}
protected static class MixinChanges {
private final Set added = new HashSet();
private final Set removed = new HashSet();
public void add ( Name mixin ) {
this .added.add(mixin);
this .removed.remove(mixin);
}
public void remove ( Name mixin ) {
this .added.remove(mixin);
this .removed.add(mixin);
}
public Set getAdded () {
return added;
}
public Set getRemoved () {
return removed;
}
public boolean isEmpty () {
return added.isEmpty() && removed.isEmpty();
}
@Override
public String toString () {
return "added: " + added + ", removed: " + removed;
}
}
protected static class ReferrerChanges {
private final Map> addedWeak = new HashMap<>();
private final Map> removedWeak = new HashMap<>();
private final Map> addedStrong = new HashMap<>();
private final Map> removedStrong = new HashMap<>();
public void addWeakReferrer ( Property referenceProperty,
NodeKey referrerKey ) {
processReferrerChange(referenceProperty, referrerKey, addedWeak, removedWeak);
}
public void removeWeakReferrer ( Property referenceProperty,
NodeKey referrerKey ) {
processReferrerChange(referenceProperty, referrerKey, removedWeak, addedWeak);
}
public void addStrongReferrer ( Property referenceProperty,
NodeKey referrerKey ) {
processReferrerChange(referenceProperty, referrerKey, addedStrong, removedStrong);
}
public void removeStrongReferrer ( Property referenceProperty,
NodeKey referrerKey ) {
processReferrerChange(referenceProperty, referrerKey, removedStrong, addedStrong);
}
private void processReferrerChange ( Property referenceProperty,
NodeKey referrerKey,
Map > addToMap,
Map > removeFromMap ) {
String propertyKey = keyFromProperty(referenceProperty);
boolean shouldAdd = true ;
Set toRemove = removeFromMap.get(propertyKey);
if (toRemove != null ) {
shouldAdd = !toRemove.remove(referrerKey);
if (toRemove.isEmpty()) {
removeFromMap.remove(propertyKey);
}
}
if (!shouldAdd) {
return ;
}
Set toAdd = addToMap.get(propertyKey);
if (toAdd == null ) {
toAdd = new HashSet<>();
addToMap.put(propertyKey, toAdd);
}
toAdd.add(referrerKey);
}
private String keyFromProperty ( Property property ) {
StringBuilder key = new StringBuilder(property.getName().getString()).append("_" );
if (property.isSingle()) {
key.append("_sv" );
} else {
key.append("_mv" );
}
return key.toString();
}
public List getAddedReferrers ( ReferenceType type ) {
switch (type) {
case STRONG:
return collectKeys(addedStrong);
case WEAK:
return collectKeys(addedWeak);
case BOTH:
return collectKeys(addedWeak, addedStrong);
}
assert false : "Should never get here" ;
return null ;
}
public boolean isRemovedReferrer ( NodeKey key,
ReferenceType type ) {
switch (type) {
case STRONG:
return containsKey(key, removedStrong);
case WEAK:
return containsKey(key, removedWeak);
case BOTH:
return containsKey(key, removedStrong) || containsKey(key, removedWeak);
}
return false ;
}
public List getRemovedReferrers ( ReferenceType type ) {
switch (type) {
case STRONG:
return collectKeys(removedStrong);
case WEAK:
return collectKeys(removedWeak);
case BOTH:
return collectKeys(removedStrong, removedWeak);
}
assert false : "Should never get here" ;
return null ;
}
private final boolean containsKey ( NodeKey key,
Map > source ) {
for (Set sourceKeys : source.values()) {
if (sourceKeys.contains(key)) return true ;
}
return false ;
}
@SafeVarargs
private final List collectKeys ( Map >... sources ) {
List keys = new ArrayList<>();
for (Map> source : sources) {
for (Set sourceKeys : source.values()) {
keys.addAll(sourceKeys);
}
}
return keys;
}
public ReferrerCounts getReferrerCounts ( ReferrerCounts persisted ) {
MutableReferrerCounts mutable = persisted != null ? persisted.mutable() : ReferrerCounts.createMutable();
for (Set sourceKeys : addedStrong.values()) {
for (NodeKey key : sourceKeys) {
mutable.addStrong(key, 1 );
}
}
for (Set sourceKeys : addedWeak.values()) {
for (NodeKey key : sourceKeys) {
mutable.addWeak(key, 1 );
}
}
for (Set sourceKeys : removedStrong.values()) {
for (NodeKey key : sourceKeys) {
mutable.addStrong(key, -1 );
}
}
for (Set sourceKeys : removedWeak.values()) {
for (NodeKey key : sourceKeys) {
mutable.addWeak(key, -1 );
}
}
return mutable.freeze();
}
public boolean isEmpty () {
return addedWeak.isEmpty() && removedWeak.isEmpty() && addedStrong.isEmpty() && removedStrong.isEmpty();
}
@Override
public String toString () {
return getString(new StringBuilder());
}
public String getString ( StringBuilder sb ) {
sb.append("ReferrerChanges: " );
if (!addedStrong.isEmpty()) {
sb.append(" addedStrong=" ).append(addedStrong);
}
if (!addedWeak.isEmpty()) {
sb.append(" addedWeak=" ).append(addedWeak);
}
if (!removedWeak.isEmpty()) {
sb.append(" removedWeak=" ).append(removedWeak);
}
if (!removedStrong.isEmpty()) {
sb.append(" removedStrong=" ).append(removedStrong);
}
return sb.toString();
}
}
protected static class PermissionChanges implements MutableCachedNode .PermissionChanges {
private final Set removedPrincipals;
private final Set addedPrincipals;
protected PermissionChanges () {
this .removedPrincipals = new HashSet<>();
this .addedPrincipals = new HashSet<>();
}
protected void principalAdded (String principalName) {
this .removedPrincipals.remove(principalName);
this .addedPrincipals.add(principalName);
}
protected void principalRemoved (String principalName) {
this .addedPrincipals.remove(principalName);
this .removedPrincipals.add(principalName);
}
@Override
public long addedPrincipalsCount () {
return addedPrincipals.size();
}
@Override
public long removedPrincipalsCount () {
return removedPrincipals.size();
}
}
protected class DeepCopy {
protected final WritableSessionCache targetCache;
protected final SessionNode targetNode;
protected final SessionCache sourceCache;
protected final CachedNode sourceNode;
protected final Path startingPathInSource;
protected final PropertyFactory propertyFactory;
protected final String targetWorkspaceKey;
protected final Map linkedPlaceholdersToOriginal = new HashMap();
protected final Map sourceToTargetKeys = new HashMap();
protected final Map> sourceKeyToReferenceProperties = new HashMap>();
protected final DocumentStore documentStore;
protected final String systemWorkspaceKey;
protected final Connectors connectors;
protected final ValueFactories valueFactories;
protected DeepCopy ( SessionNode targetNode,
WritableSessionCache cache,
CachedNode sourceNode,
SessionCache sourceCache,
String systemWorkspaceKey,
Connectors connectors ) {
this .targetCache = cache;
this .targetNode = targetNode;
this .sourceCache = sourceCache;
this .sourceNode = sourceNode;
this .startingPathInSource = sourceNode.getPath(sourceCache);
this .propertyFactory = this .targetCache.getContext().getPropertyFactory();
this .targetWorkspaceKey = targetNode.getKey().getWorkspaceKey();
this .documentStore = ((WorkspaceCache)sourceCache.getWorkspace()).documentStore();
this .systemWorkspaceKey = systemWorkspaceKey;
this .connectors = connectors;
this .valueFactories = this .targetCache.context().getValueFactories();
}
public Map getSourceToTargetKeys () {
return sourceToTargetKeys;
}
public void execute () {
doPhase1(this .targetNode, this .sourceNode);
doPhase2();
resolveReferences();
}
protected void doPhase1 ( MutableCachedNode targetNode,
CachedNode sourceNode ) {
final NodeKey sourceKey = sourceNode.getKey();
final NodeKey targetKey = targetNode.getKey();
sourceToTargetKeys.put(sourceKey, targetKey);
if (shouldProcessSourceKey(sourceKey)) {
copyProperties(targetNode, sourceNode);
}
for (ChildReference sourceChildReference : sourceNode.getChildReferences(sourceCache)) {
NodeKey childKey = sourceChildReference.getKey();
if (!shouldProcessSourceKey(childKey)) {
continue ;
}
CachedNode sourceChild = sourceCache.getNode(childKey);
NodeKey parentSourceKey = sourceChild.getParentKeyInAnyWorkspace(sourceCache);
Name sourceChildName = sourceChildReference.getName();
if (sourceKey.equals(parentSourceKey)) {
boolean isExternal = !childKey.getSourceKey().equalsIgnoreCase(sourceCache.getRootKey().getSourceKey());
MutableCachedNode childCopy = null ;
String projectionAlias = sourceChildName.getString();
if (isExternal && connectors.hasExternalProjection(projectionAlias, childKey.toString())) {
targetNode.addFederatedSegment(childKey.toString(), projectionAlias);
childCopy = targetCache.mutable(childKey);
} else {
ChildReferences targetNodeChildReferences = targetNode.getChildReferences(targetCache);
ChildReference targetChildSameSegment = targetNodeChildReferences.getChild(sourceChildReference.getSegment());
if (targetChildSameSegment != null && !sourceToTargetKeys.containsValue(targetChildSameSegment.getKey())) {
childCopy = targetCache.mutable(targetChildSameSegment.getKey());
if (!isExternal) {
targetNode.removeChild(targetCache, targetChildSameSegment.getKey());
targetCache.destroy(targetChildSameSegment.getKey());
childCopy = null ;
}
}
if (childCopy == null ) {
String childCopyPreferredKey = documentStore.newDocumentKey(targetKey.toString(), sourceChildName,
sourceChild.getPrimaryType(sourceCache));
NodeKey newKey = createTargetKeyFor(childKey, targetKey, childCopyPreferredKey);
childCopy = targetNode.createChild(targetCache, newKey, sourceChildName, null );
}
}
doPhase1(childCopy, sourceChild);
} else {
Path sourceChildPath = sourceChild.getPath(sourceCache);
NodeKey newKey = null ;
if (sourceChildPath.isAtOrBelow(startingPathInSource)) {
newKey = sourceToTargetKeys.get(childKey);
if (newKey == null ) {
CachedNode nodeInOtherWorkspace = targetCache.getNode(childKey);
if (nodeInOtherWorkspace != null ) {
newKey = childKey;
} else {
NodeKey placeholderKey = createTargetKeyFor(childKey, targetKey, null );
String childCopyPreferredKey = documentStore.newDocumentKey(targetKey.toString(),
sourceChildName,
sourceChild.getPrimaryType(sourceCache));
newKey = createTargetKeyFor(childKey, targetKey, childCopyPreferredKey);
sourceToTargetKeys.put(childKey, newKey);
targetNode.createChild(targetCache, placeholderKey, sourceChildName, null );
linkedPlaceholdersToOriginal.put(placeholderKey, newKey);
continue ;
}
}
} else {
newKey = sourceChildReference.getKey();
}
targetNode.linkChild(targetCache, newKey, sourceChildName);
}
}
}
protected NodeKey createTargetKeyFor ( NodeKey sourceKey,
NodeKey parentKeyInTarget,
String preferredKey ) {
NodeKey newKey = sourceToTargetKeys.get(sourceKey);
if (newKey != null ) {
return newKey;
} else if (!StringUtil.isBlank(preferredKey)) {
return new NodeKey(preferredKey);
} else {
return parentKeyInTarget.withRandomId();
}
}
protected void doPhase2 () {
RuntimeException firstException = null ;
Iterator> entryIterator = linkedPlaceholdersToOriginal.entrySet().iterator();
while (entryIterator.hasNext()) {
Map.Entry entry = entryIterator.next();
try {
NodeKey placeholderKey = entry.getKey();
NodeKey linkableKey = entry.getValue();
CachedNode placeholder = targetCache.getNode(placeholderKey);
NodeKey parentKey = placeholder.getParentKey(targetCache);
MutableCachedNode parent = targetCache.mutable(parentKey);
if (parent.linkChild(targetCache, linkableKey, placeholder.getName(targetCache))) {
parent.reorderChild(targetCache, linkableKey, placeholderKey);
}
parent.removeChild(targetCache, placeholderKey);
entryIterator.remove();
} catch (RuntimeException e) {
if (firstException == null ) firstException = e;
}
}
if (firstException != null ) {
throw firstException;
}
}
protected void resolveReferences () {
for (Map.Entry entry : sourceToTargetKeys.entrySet()) {
NodeKey sourceKey = entry.getKey();
NodeKey targetKey = entry.getValue();
Set referenceProperties = sourceKeyToReferenceProperties.get(sourceKey);
if (referenceProperties == null ) {
continue ;
}
MutableCachedNode targetNode = targetCache.mutable(targetKey);
for (Property property : referenceProperties) {
List resolvedReferences = new ArrayList();
for (Iterator valuesIterator = property.getValues(); valuesIterator.hasNext();) {
Reference reference = (Reference)valuesIterator.next();
resolvedReferences.add(resolveReference(sourceKey, targetKey, property.getName(), reference));
}
Property updatedProperty = property.isMultiple() ? propertyFactory.create(property.getName(),
resolvedReferences) : propertyFactory.create(property.getName(),
resolvedReferences.get(0 ));
targetNode.setProperty(targetCache, updatedProperty);
}
}
}
private Reference resolveReference ( NodeKey sourceNodeKey,
NodeKey targetNodeKey,
Name propertyName,
Reference referenceInSource ) {
String referenceStringValue = referenceInSource.getString();
NodeKey referenceInSourceKey = null ;
if (referenceInSource instanceof NodeKeyReference) {
referenceInSourceKey = ((NodeKeyReference)referenceInSource).getNodeKey();
} else if (NodeKey.isValidFormat(referenceStringValue)) {
referenceInSourceKey = new NodeKey(referenceStringValue);
} else {
referenceInSourceKey = sourceNodeKey.withId(referenceStringValue);
}
if (referenceInSourceKey.getWorkspaceKey().equals(systemWorkspaceKey)) {
return referenceInSource;
}
NodeKey referenceInTargetKey = sourceToTargetKeys.get(referenceInSourceKey);
if (referenceInTargetKey == null ) {
referenceInTargetKey = referenceInSourceKey.withWorkspaceKey(targetNodeKey.getWorkspaceKey());
boolean resolvableInTargetWorkspace = targetCache.getNode(referenceInTargetKey) != null ;
boolean resolvableInSourceWorkspace = sourceCache.getNode(referenceInSourceKey) != null ;
if (!resolvableInTargetWorkspace && resolvableInSourceWorkspace) {
throw new WrappedException(
new RepositoryException(
JcrI18n.cannotCopyOrCloneReferenceOutsideGraph.text(propertyName,
referenceInSourceKey,
startingPathInSource)));
} else if (!resolvableInSourceWorkspace && !referenceInSource.isWeak() && !referenceInSource.isSimple()) {
throw new WrappedException(
new RepositoryException(
JcrI18n.cannotCopyOrCloneCorruptReference.text(propertyName,
referenceInSourceKey)));
}
}
if (referenceInSource.isSimple()) {
return valueFactories.getSimpleReferenceFactory().create(referenceInTargetKey, referenceInSource.isForeign());
} else if (referenceInSource.isWeak()) {
return valueFactories.getWeakReferenceFactory().create(referenceInTargetKey, referenceInSource.isForeign());
} else {
return valueFactories.getReferenceFactory().create(referenceInTargetKey, referenceInSource.isForeign());
}
}
protected void copyProperties ( MutableCachedNode targetNode,
CachedNode sourceNode ) {
NodeKey sourceNodeKey = sourceNode.getKey();
for (Iterator propertyIterator = sourceNode.getProperties(sourceCache); propertyIterator.hasNext();) {
Property property = propertyIterator.next();
if (property.isReference() || property.isSimpleReference()) {
Set referenceProperties = sourceKeyToReferenceProperties.get(sourceNodeKey);
if (referenceProperties == null ) {
referenceProperties = new HashSet();
sourceKeyToReferenceProperties.put(sourceNodeKey, referenceProperties);
}
referenceProperties.add(property);
} else if (property.getName().equals(JcrLexicon.UUID)) {
copyUUIDProperty(property, targetNode, sourceNode);
} else {
boolean sourceExternal = !sourceNodeKey.getSourceKey().equals(sourceCache.getRootKey().getSourceKey());
boolean targetInternal = targetNode.getKey().getSourceKey().equals(targetCache.getRootKey().getSourceKey());
if (sourceExternal && targetInternal && property.getFirstValue() instanceof ExternalBinaryValue) {
Property newProperty = null ;
if (property.isMultiple()) {
List values = new ArrayList(property.size());
for (Object value : property) {
values.add(convertToInternalBinaryValue(value));
}
newProperty = propertyFactory.create(property.getName(), values.iterator());
} else if (property.isSingle()) {
Object value = convertToInternalBinaryValue(property.getFirstValue());
newProperty = propertyFactory.create(property.getName(), value);
}
if (newProperty != null ) targetNode.setProperty(targetCache, newProperty);
} else {
targetNode.setProperty(targetCache, property);
}
}
}
}
protected Object convertToInternalBinaryValue ( Object value ) {
if (value instanceof ExternalBinaryValue) {
try {
return valueFactories.getBinaryFactory().create(((ExternalBinaryValue)value).getStream());
} catch (RepositoryException e) {
throw new WrappedException(e);
}
}
return value;
}
protected void copyUUIDProperty ( Property sourceProperty,
MutableCachedNode targetNode,
CachedNode sourceNode ) {
String targetUUID = JcrSession.nodeIdentifier(targetNode.getKey(), targetCache.getRootKey());
targetNode.setProperty(targetCache, propertyFactory.create(sourceProperty.getName(), targetUUID));
}
@Override
public String toString () {
return getOperationName() + " '"
+ this .startingPathInSource.getString(sourceCache.getContext().getNamespaceRegistry()) + "' in workspace '"
+ sourceCache.getWorkspace().toString() + "' into '"
+ this .targetNode.getPath(targetCache).getString(targetCache.getContext().getNamespaceRegistry())
+ "' in workspace '" + targetCache.getWorkspace().toString() + "'" ;
}
protected String getOperationName () {
return "Copy" ;
}
protected boolean shouldProcessSourceKey ( NodeKey sourceKey ) {
return !sourceKey.equals(sourceCache.getRootKey())
&& !sourceKey.getWorkspaceKey().equalsIgnoreCase(systemWorkspaceKey);
}
}
protected class DeepClone extends DeepCopy {
protected DeepClone ( SessionNode targetNode,
WritableSessionCache cache,
CachedNode sourceNode,
SessionCache sourceCache,
String systemWorkspaceKey,
Connectors connectors ) {
super (targetNode, cache, sourceNode, sourceCache, systemWorkspaceKey, connectors);
}
@Override
protected void copyProperties ( MutableCachedNode targetNode,
CachedNode sourceNode ) {
targetNode.removeAllProperties(targetCache);
super .copyProperties(targetNode, sourceNode);
}
@Override
protected void copyUUIDProperty ( Property sourceProperty,
MutableCachedNode targetNode,
CachedNode sourceNode ) {
targetNode.setProperty(targetCache, sourceProperty);
}
@Override
protected NodeKey createTargetKeyFor ( NodeKey sourceKey,
NodeKey parentKeyInTarget,
String preferredKey ) {
return !StringUtil.isBlank(preferredKey) ? new NodeKey(preferredKey) : parentKeyInTarget.withId(sourceKey.getIdentifier());
}
@Override
protected String getOperationName () {
return "Clone" ;
}
}
}