/* * ModeShape (http://www.modeshape.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.modeshape.jcr.federation; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import org.modeshape.schematic.document.Document; import org.modeshape.common.util.StringUtil; import org.modeshape.jcr.spi.federation.DocumentChanges; import org.modeshape.jcr.value.Name; /** * Default implementation of the {@link DocumentChanges} interface * * @author Horia Chiorean (hchiorea@redhat.com) */ @SuppressWarnings( "synthetic-access" ) public class FederatedDocumentChanges implements DocumentChanges { private final String documentId; private final Document document; private final FederatedPropertyChanges propertyChanges; private final FederatedMixinChanges mixinChanges; private final FederatedChildrenChanges childrenChanges; private final FederatedParentChanges parentChanges; private final FederatedReferrerChanges referrerChanges; protected FederatedDocumentChanges( String documentId, Document document ) { this.document = document; this.documentId = documentId; this.propertyChanges = new FederatedPropertyChanges(); this.mixinChanges = new FederatedMixinChanges(); this.childrenChanges = new FederatedChildrenChanges(); this.parentChanges = new FederatedParentChanges(); this.referrerChanges = new FederatedReferrerChanges(); } @Override public ChildrenChanges getChildrenChanges() { return childrenChanges; } @Override public Document getDocument() { return document; } @Override public String getDocumentId() { return documentId; } @Override public MixinChanges getMixinChanges() { return mixinChanges; } @Override public ParentChanges getParentChanges() { return parentChanges; } @Override public PropertyChanges getPropertyChanges() { return propertyChanges; } @Override public ReferrerChanges getReferrerChanges() { return referrerChanges; } protected void setPropertyChanges( Set<Name> sessionChangedProperties, Set<Name> sessionRemovedProperties ) { Set<Name> addedProperties = new HashSet<Name>(); // process the session properties to make the distinction between changed / added / removed for (Iterator<Name> changedPropertiesIterator = sessionChangedProperties.iterator(); changedPropertiesIterator.hasNext();) { Name changedPropertyName = changedPropertiesIterator.next(); // check if it's an add or a change if (!sessionRemovedProperties.contains(changedPropertyName)) { addedProperties.add(changedPropertyName); changedPropertiesIterator.remove(); } else { // it's a changed property, so clean up the removals sessionRemovedProperties.remove(changedPropertyName); } } propertyChanges.changed(sessionChangedProperties).removed(sessionRemovedProperties).added(addedProperties); } protected void setMixinChanges( Set<Name> addedMixins, Set<Name> removedMixins ) { mixinChanges.added(addedMixins).removed(removedMixins); } protected void setChildrenChanges( LinkedHashMap<String, Name> sessionAppendedChildren, Map<String, Name> sessionRenamedChildren, Set<String> sessionRemovedChildren, Map<String, LinkedHashMap<String, Name>> sessionChildrenInsertedBeforeAnotherChild ) { // the reordered children appear in the remove list as well, so we need to clean this up for (String orderedBefore : sessionChildrenInsertedBeforeAnotherChild.keySet()) { LinkedHashMap<String, Name> childrenMap = sessionChildrenInsertedBeforeAnotherChild.get(orderedBefore); for (String childId : childrenMap.keySet()) { sessionRemovedChildren.remove(childId); } } childrenChanges.appended(sessionAppendedChildren) .renamed(sessionRenamedChildren) .removed(sessionRemovedChildren) .insertedBeforeAnotherChild(sessionChildrenInsertedBeforeAnotherChild); } protected void setParentChanges( Set<String> addedParents, Set<String> removedParents, String newPrimaryParent ) { parentChanges.added(addedParents).removed(removedParents).newPrimaryParent(newPrimaryParent); } protected void setReferrerChanges( Set<String> addedWeakReferrers, Set<String> removedWeakReferrers, Set<String> addedStrongReferrers, Set<String> removedStrongReferrers ) { referrerChanges.addedWeak(addedWeakReferrers) .addedStrong(addedStrongReferrers) .removedStrong(removedStrongReferrers) .removedWeak(removedWeakReferrers); } protected class FederatedPropertyChanges implements PropertyChanges { private Set<Name> added = Collections.emptySet(); private Set<Name> changed = Collections.emptySet(); private Set<Name> removed = Collections.emptySet(); private FederatedPropertyChanges() { } @Override public boolean isEmpty() { return changed.isEmpty() && removed.isEmpty(); } @Override public Set<Name> getChanged() { return changed; } @Override public Set<Name> getRemoved() { return removed; } @Override public Set<Name> getAdded() { return added; } private FederatedPropertyChanges changed( final Set<Name> changed ) { if (changed != null) { this.changed = changed; } return this; } private FederatedPropertyChanges removed( final Set<Name> removed ) { if (removed != null) { this.removed = removed; } return this; } private PropertyChanges added( final Set<Name> added ) { this.added = added; return this; } } protected class FederatedMixinChanges implements MixinChanges { private Set<Name> added = Collections.emptySet(); private Set<Name> removed = Collections.emptySet(); private FederatedMixinChanges() { } @Override public boolean isEmpty() { return added.isEmpty() && removed.isEmpty(); } @Override public Set<Name> getAdded() { return added; } @Override public Set<Name> getRemoved() { return removed; } private FederatedMixinChanges added( final Set<Name> added ) { if (added != null) { this.added = added; } return this; } private MixinChanges removed( final Set<Name> removed ) { if (removed != null) { this.removed = removed; } return this; } } protected class FederatedChildrenChanges implements ChildrenChanges { private LinkedHashMap<String, Name> appended = new LinkedHashMap<String, Name>(); private Map<String, Name> renamed = Collections.emptyMap(); private Map<String, LinkedHashMap<String, Name>> insertedBeforeAnotherChild = Collections.emptyMap(); private Set<String> removed = Collections.emptySet(); private FederatedChildrenChanges() { } @Override public boolean isEmpty() { return appended.isEmpty() && renamed.isEmpty() && insertedBeforeAnotherChild.isEmpty() && removed.isEmpty(); } @Override public LinkedHashMap<String, Name> getAppended() { return appended; } @Override public Map<String, Name> getRenamed() { return renamed; } @Override public Map<String, LinkedHashMap<String, Name>> getInsertedBeforeAnotherChild() { return insertedBeforeAnotherChild; } @Override public Set<String> getRemoved() { return removed; } private FederatedChildrenChanges appended( final LinkedHashMap<String, Name> appended ) { if (appended != null) { this.appended = appended; } return this; } private FederatedChildrenChanges renamed( final Map<String, Name> renamed ) { if (renamed != null) { this.renamed = renamed; } return this; } private FederatedChildrenChanges insertedBeforeAnotherChild( final Map<String, LinkedHashMap<String, Name>> insertedBeforeAnotherChild ) { if (insertedBeforeAnotherChild != null) { this.insertedBeforeAnotherChild = insertedBeforeAnotherChild; } return this; } private FederatedChildrenChanges removed( final Set<String> removed ) { if (removed != null) { this.removed = removed; } return this; } } protected class FederatedParentChanges implements ParentChanges { private Set<String> added = Collections.emptySet(); private Set<String> removed = Collections.emptySet(); private String newPrimaryParent = null; @Override public boolean isEmpty() { return added.isEmpty() && removed.isEmpty() && StringUtil.isBlank(newPrimaryParent); } @Override public boolean hasNewPrimaryParent() { return !StringUtil.isBlank(newPrimaryParent); } @Override public Set<String> getAdded() { return added; } @Override public Set<String> getRemoved() { return removed; } @Override public String getNewPrimaryParent() { return newPrimaryParent; } private FederatedParentChanges added( final Set<String> added ) { if (added != null) { this.added = added; } return this; } private FederatedParentChanges removed( final Set<String> removed ) { if (removed != null) { this.removed = removed; } return this; } private ParentChanges newPrimaryParent( final String newPrimaryParent ) { this.newPrimaryParent = newPrimaryParent; return this; } } protected class FederatedReferrerChanges implements ReferrerChanges { private Set<String> addedWeak = Collections.emptySet(); private Set<String> removedWeak = Collections.emptySet(); private Set<String> addedStrong = Collections.emptySet(); private Set<String> removedStrong = Collections.emptySet(); @Override public boolean isEmpty() { return addedWeak.isEmpty() && removedWeak.isEmpty() && addedStrong.isEmpty() && removedStrong.isEmpty(); } @Override public Set<String> getAddedStrong() { return addedStrong; } @Override public Set<String> getAddedWeak() { return addedWeak; } @Override public Set<String> getRemovedStrong() { return removedStrong; } @Override public Set<String> getRemovedWeak() { return removedWeak; } private FederatedReferrerChanges addedStrong( final Set<String> addedStrong ) { if (addedStrong != null) { this.addedStrong = addedStrong; } return this; } private FederatedReferrerChanges addedWeak( final Set<String> addedWeak ) { if (addedWeak != null) { this.addedWeak = addedWeak; } return this; } private ReferrerChanges removedWeak( final Set<String> removedWeak ) { if (removedWeak != null) { this.removedWeak = removedWeak; } return this; } private FederatedReferrerChanges removedStrong( final Set<String> removedStrong ) { if (removedStrong != null) { this.removedStrong = removedStrong; } return this; } } }