001/*
002 * (C) Copyright 2014 Nuxeo SA (http://nuxeo.com/) and others.
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 *
016 * Contributors:
017 *     Florent Guillaume
018 */
019package org.nuxeo.ecm.core.storage.dbs;
020
021import static org.nuxeo.ecm.core.storage.dbs.DBSDocument.KEY_ID;
022import static org.nuxeo.ecm.core.storage.dbs.DBSDocument.KEY_NAME;
023import static org.nuxeo.ecm.core.storage.dbs.DBSDocument.KEY_PARENT_ID;
024import static org.nuxeo.ecm.core.storage.dbs.DBSDocument.KEY_PRIMARY_TYPE;
025import static org.nuxeo.ecm.core.storage.dbs.DBSDocument.KEY_VERSION_SERIES_ID;
026
027import java.io.Serializable;
028
029import org.nuxeo.ecm.core.model.Document;
030import org.nuxeo.ecm.core.storage.State.StateDiff;
031import org.nuxeo.ecm.core.storage.StateHelper;
032import org.nuxeo.ecm.core.storage.State;
033
034/**
035 * Implementation of a {@link Document} state for Document-Based Storage.
036 * <p>
037 * It wraps a {@link State}, together with a dirty flag.
038 *
039 * @since 5.9.4
040 */
041public class DBSDocumentState {
042
043    /**
044     * The current state.
045     */
046    protected State state;
047
048    /**
049     * When non-null, the original state (otherwise the state hasn't been modified).
050     */
051    protected State originalState;
052
053    /**
054     * Constructs an empty state.
055     */
056    public DBSDocumentState() {
057        state = new State();
058        originalState = null;
059    }
060
061    /**
062     * Constructs a document state from the copy of an existing base state.
063     */
064    public DBSDocumentState(State base) {
065        state = StateHelper.deepCopy(base);
066        originalState = null;
067    }
068
069    /**
070     * This must be called if we're about to directly change the internal state.
071     */
072    public void markDirty() {
073        if (originalState == null) {
074            originalState = StateHelper.deepCopy(state);
075        }
076    }
077
078    /**
079     * Checks if the document state has been changed since its construction or the last call to {@link #setNotDirty}.
080     */
081    public boolean isDirty() {
082        return originalState != null;
083    }
084
085    public void setNotDirty() {
086        originalState = null;
087        StateHelper.resetDeltas(state);
088    }
089
090    /**
091     * Gets the state. If the caller changes the state, it must also call {@link #dirty} to inform this object that the
092     * state is dirtied.
093     */
094    public State getState() {
095        return state;
096    }
097
098    /**
099     * Gets a diff of what changed since this document state was read from database or saved.
100     *
101     * @return {@code null} if there was no change, or a {@link StateDiff}
102     */
103    public StateDiff getStateChange() {
104        if (originalState == null) {
105            return null;
106        }
107        StateDiff diff = StateHelper.diff(originalState, state);
108        if (diff.isEmpty()) {
109            return null;
110        }
111        return diff;
112    }
113
114    /**
115     * Gets the original state for this, needed when creating an undo log.
116     *
117     * @return a state that must not be modified
118     * @since 7.4
119     */
120    public State getOriginalState() {
121        return originalState == null ? state : originalState;
122    }
123
124    public Serializable get(String key) {
125        return state.get(key);
126    }
127
128    public void put(String key, Serializable value) {
129        markDirty();
130        state.put(key, value);
131    }
132
133    public boolean containsKey(String key) {
134        return state.get(key) != null;
135    }
136
137    public String getId() {
138        return (String) get(KEY_ID);
139    }
140
141    public String getParentId() {
142        return (String) get(KEY_PARENT_ID);
143    }
144
145    public String getName() {
146        return (String) get(KEY_NAME);
147    }
148
149    public String getPrimaryType() {
150        return (String) get(KEY_PRIMARY_TYPE);
151    }
152
153    public String getVersionSeriesId() {
154        return (String) get(KEY_VERSION_SERIES_ID);
155    }
156
157    @Override
158    public String toString() {
159        return getClass().getSimpleName() + '(' + (isDirty() ? "dirty," : "") + state.toString() + ')';
160    }
161
162}