001/*
002 * ModeShape (http://www.modeshape.org)
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *       http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.modeshape.schematic.internal.delta;
017
018import java.util.Collection;
019import java.util.stream.Collectors;
020import org.modeshape.schematic.annotation.Immutable;
021import org.modeshape.schematic.document.Document;
022import org.modeshape.schematic.document.Path;
023import org.modeshape.schematic.internal.document.MutableDocument;
024
025/**
026 * An atomic operation for SchematicValueDelta.
027 * <p/>
028 * 
029 * @author (various)
030 */
031@Immutable
032public abstract class Operation {
033
034    protected final Path parentPath;
035    private final int hashCode;
036
037    protected Operation( Path parentPath,
038                         int hashCode ) {
039        this.parentPath = parentPath;
040        this.hashCode = hashCode;
041    }
042
043    @Override
044    public int hashCode() {
045        return hashCode;
046    }
047
048    public abstract void replay( MutableDocument delegate );
049
050    public abstract void rollback( MutableDocument delegate );
051
052    public Path getParentPath() {
053        return parentPath;
054    }
055
056    protected MutableDocument mutableParent( MutableDocument delegate ) {
057        MutableDocument parent = delegate;
058        Path parentPath = getParentPath();
059        for (String fieldName : parentPath) {
060            assert parent != null : "Unexpected to find path " + parentPath + " in " + delegate + ". Unable to apply operation "
061                                    + this;
062            parent = (MutableDocument)parent.getDocument(fieldName);
063        }
064        return parent;
065    }
066
067    @Override
068    public abstract Operation clone();
069
070    protected Object cloneValue( Object value ) {
071        if (value == null) return null;
072        if (value instanceof Document) return ((Document)value).clone();
073        if (value instanceof Collection) {
074            Collection<?> original = (Collection<?>)value;
075            Collection<Object> copy = original.stream().map(this::cloneValue).collect(Collectors.toList());
076            return copy;
077        }
078        // everything else should be immutable ...
079        return value;
080    }
081
082    protected boolean equalsIfNotNull( Object obj1,
083                                       Object obj2 ) {
084        if (obj1 == null) {
085            return obj2 == null;
086        }
087        return obj1.equals(obj2);
088    }
089
090}