Package org.jboss.dna.graph.observe

Source Code of org.jboss.dna.graph.observe.NetChangeObserver$NetChangeDetails

/*
* JBoss DNA (http://www.jboss.org/dna)
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.  Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
* See the AUTHORS.txt file in the distribution for a full listing of
* individual contributors.
*
* JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
* is licensed to you under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* JBoss DNA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.dna.graph.observe;

import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import net.jcip.annotations.Immutable;
import net.jcip.annotations.NotThreadSafe;
import net.jcip.annotations.ThreadSafe;
import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.property.Name;
import org.jboss.dna.graph.property.Path;
import org.jboss.dna.graph.property.Property;
import org.jboss.dna.graph.request.ChangeRequest;
import org.jboss.dna.graph.request.CreateNodeRequest;
import org.jboss.dna.graph.request.DeleteBranchRequest;
import org.jboss.dna.graph.request.DeleteChildrenRequest;
import org.jboss.dna.graph.request.RemovePropertyRequest;
import org.jboss.dna.graph.request.SetPropertyRequest;
import org.jboss.dna.graph.request.UpdatePropertiesRequest;

/**
* A specialized {@link Observer} that figures out the net changes made during a single {@link Changes set of changes}. For
* example, if a property is updated and then updated again, the net change will be a single change. Or, if a node is created and
* then deleted, no net change will be observed.
*/
@ThreadSafe
public abstract class NetChangeObserver extends ChangeObserver {

    public enum ChangeType {
        NODE_ADDED,
        NODE_REMOVED,
        PROPERTY_ADDED,
        PROPERTY_REMOVED,
        PROPERTY_CHANGED;
    }

    protected NetChangeObserver() {
    }

    /**
     * {@inheritDoc}
     *
     * @see org.jboss.dna.graph.observe.ChangeObserver#notify(org.jboss.dna.graph.observe.Changes)
     */
    @Override
    public void notify( Changes changes ) {
        Map<String, Map<Location, NetChangeDetails>> detailsByLocationByWorkspace = new HashMap<String, Map<Location, NetChangeDetails>>();
        // Process each of the events, extracting the node path and property details for each ...
        for (ChangeRequest change : changes) {
            Location location = change.changedLocation();
            String workspace = change.changedWorkspace();

            // Find the NetChangeDetails for this node ...
            Map<Location, NetChangeDetails> detailsByLocation = detailsByLocationByWorkspace.get(workspace);
            NetChangeDetails details = null;
            if (detailsByLocation == null) {
                detailsByLocation = new TreeMap<Location, NetChangeDetails>();
                detailsByLocationByWorkspace.put(workspace, detailsByLocation);
                details = new NetChangeDetails();
                detailsByLocation.put(location, details);
            } else {
                details = detailsByLocation.get(location);
                if (details == null) {
                    details = new NetChangeDetails();
                    detailsByLocation.put(location, details);
                }
            }

            // Process the specific kind of change ...
            if (change instanceof CreateNodeRequest) {
                CreateNodeRequest create = (CreateNodeRequest)change;
                details.addEventType(ChangeType.NODE_ADDED);
                for (Property property : create) {
                    details.addProperty(property);
                }
            } else if (change instanceof UpdatePropertiesRequest) {
                UpdatePropertiesRequest update = (UpdatePropertiesRequest)change;
                for (Map.Entry<Name, Property> entry : update.properties().entrySet()) {
                    Property property = entry.getValue();
                    if (property != null) {
                        details.changeProperty(property);
                    } else {
                        details.removeProperty(entry.getKey());
                    }
                }
            } else if (change instanceof SetPropertyRequest) {
                SetPropertyRequest set = (SetPropertyRequest)change;
                details.changeProperty(set.property());
            } else if (change instanceof RemovePropertyRequest) {
                RemovePropertyRequest remove = (RemovePropertyRequest)change;
                details.removeProperty(remove.propertyName());
            } else if (change instanceof DeleteBranchRequest) {
                details.addEventType(ChangeType.NODE_REMOVED);
            } else if (change instanceof DeleteChildrenRequest) {
                DeleteChildrenRequest delete = (DeleteChildrenRequest)change;
                for (Location deletedChild : delete.getActualChildrenDeleted()) {
                    NetChangeDetails childDetails = detailsByLocation.get(location);
                    if (childDetails == null) {
                        childDetails = new NetChangeDetails();
                        detailsByLocation.put(deletedChild, childDetails);
                    }
                    childDetails.addEventType(ChangeType.NODE_REMOVED);
                }
            }
        }

        // Walk through the net changes ...
        String sourceName = changes.getSourceName();
        for (Map.Entry<String, Map<Location, NetChangeDetails>> byWorkspaceEntry : detailsByLocationByWorkspace.entrySet()) {
            String workspaceName = byWorkspaceEntry.getKey();
            // Iterate over the entries. Since we've used a TreeSet, we'll get these with the lower paths first ...
            for (Map.Entry<Location, NetChangeDetails> entry : byWorkspaceEntry.getValue().entrySet()) {
                Location location = entry.getKey();
                NetChangeDetails details = entry.getValue();
                notify(new NetChange(sourceName, workspaceName, location, details.getEventTypes(),
                                     details.getModifiedProperties(), details.getRemovedProperties()));
            }
        }
    }

    /**
     * Method that is called for each net change.
     *
     * @param change the net change; never null
     */
    protected abstract void notify( NetChange change );

    /**
     * A notification of changes to a node.
     */
    @Immutable
    public static class NetChange {

        private final String sourceName;
        private final String workspaceName;
        private final Location location;
        private final EnumSet<ChangeType> eventTypes;
        private final Set<Property> modifiedProperties;
        private final Set<Name> removedProperties;
        private final int hc;

        public NetChange( String sourceName,
                          String workspaceName,
                          Location location,
                          EnumSet<ChangeType> eventTypes,
                          Set<Property> modifiedProperties,
                          Set<Name> removedProperties ) {
            assert sourceName != null;
            assert workspaceName != null;
            assert location != null;
            this.sourceName = sourceName;
            this.workspaceName = workspaceName;
            this.location = location;
            this.hc = HashCode.compute(this.workspaceName, this.location);
            this.eventTypes = eventTypes;
            if (modifiedProperties == null) modifiedProperties = Collections.emptySet();
            if (removedProperties == null) removedProperties = Collections.emptySet();
            this.modifiedProperties = Collections.unmodifiableSet(modifiedProperties);
            this.removedProperties = Collections.unmodifiableSet(removedProperties);
        }

        /**
         * @return absolutePath
         */
        public Path getPath() {
            return this.location.getPath();
        }

        /**
         * @return repositorySourceName
         */
        public String getRepositorySourceName() {
            return this.sourceName;
        }

        /**
         * @return repositoryWorkspaceName
         */
        public String getRepositoryWorkspaceName() {
            return this.workspaceName;
        }

        /**
         * @return modifiedProperties
         */
        public Set<Property> getModifiedProperties() {
            return this.modifiedProperties;
        }

        /**
         * @return removedProperties
         */
        public Set<Name> getRemovedProperties() {
            return this.removedProperties;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public int hashCode() {
            return this.hc;
        }

        /**
         * Determine whether this net change includes all of the supplied types.
         *
         * @param jcrEventTypes the types to check for
         * @return true if all of the supplied events are included in this net change, or false otherwise
         */
        public boolean includesAllOf( ChangeType... jcrEventTypes ) {
            for (ChangeType jcrEventType : jcrEventTypes) {
                if (!this.eventTypes.contains(jcrEventType)) return false;
            }
            return true;
        }

        /**
         * Determine whether this net change includes any of the supplied types.
         *
         * @param jcrEventTypes the types to check for
         * @return true if any of the supplied events are included in this net change, or false otherwise
         */
        public boolean includes( ChangeType... jcrEventTypes ) {
            for (ChangeType jcrEventType : jcrEventTypes) {
                if (this.eventTypes.contains(jcrEventType)) return true;
            }
            return false;
        }

        public boolean isSameNode( NetChange that ) {
            if (that == this) return true;
            if (this.hc != that.hc) return false;
            if (!this.workspaceName.equals(that.workspaceName)) return false;
            if (!this.location.equals(that.location)) return false;
            return true;
        }

        /**
         * Determine whether this node change includes the setting of new value(s) for the supplied property.
         *
         * @param property the name of the property
         * @return true if the named property has a new value on this node, or false otherwise
         */
        public boolean isPropertyModified( String property ) {
            return this.modifiedProperties.contains(property);
        }

        /**
         * Determine whether this node change includes the removal of the supplied property.
         *
         * @param property the name of the property
         * @return true if the named property was removed from this node, or false otherwise
         */
        public boolean isPropertyRemoved( String property ) {
            return this.removedProperties.contains(property);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public boolean equals( Object obj ) {
            if (obj == this) return true;
            if (obj instanceof NetChange) {
                NetChange that = (NetChange)obj;
                if (!this.isSameNode(that)) return false;
                if (this.eventTypes != that.eventTypes) return false;
                return true;
            }
            return false;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public String toString() {
            return this.workspaceName + "=>" + this.location;
        }
    }

    /**
     * Internal utility class used in the computation of the net changes.
     */
    @NotThreadSafe
    private static class NetChangeDetails {

        private final Set<Property> modifiedProperties = new HashSet<Property>();
        private final Set<Name> removedProperties = new HashSet<Name>();
        private EnumSet<ChangeType> eventTypes = EnumSet.noneOf(ChangeType.class);

        protected NetChangeDetails() {
        }

        public void addEventType( ChangeType eventType ) {
            this.eventTypes.add(eventType);
        }

        public void addProperty( Property property ) {
            this.modifiedProperties.add(property);
            this.eventTypes.add(ChangeType.PROPERTY_ADDED);
        }

        public void changeProperty( Property property ) {
            this.modifiedProperties.add(property);
            this.eventTypes.add(ChangeType.PROPERTY_CHANGED);
        }

        public void removeProperty( Name propertyName ) {
            this.removedProperties.add(propertyName);
            this.eventTypes.add(ChangeType.PROPERTY_REMOVED);
        }

        /**
         * @return nodeAction
         */
        public EnumSet<ChangeType> getEventTypes() {
            return this.eventTypes;
        }

        /**
         * @return addedProperties
         */
        public Set<Property> getModifiedProperties() {
            return this.modifiedProperties;
        }

        /**
         * @return removedProperties
         */
        public Set<Name> getRemovedProperties() {
            return this.removedProperties;
        }
    }
}
TOP

Related Classes of org.jboss.dna.graph.observe.NetChangeObserver$NetChangeDetails

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.