001/*
002 * (C) Copyright 2014 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl-2.1.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Florent Guillaume
016 */
017package org.nuxeo.ecm.core.storage.dbs;
018
019import static org.nuxeo.ecm.core.storage.dbs.DBSDocument.KEY_FULLTEXT_BINARY;
020import static org.nuxeo.ecm.core.storage.dbs.DBSDocument.KEY_FULLTEXT_JOBID;
021import static org.nuxeo.ecm.core.storage.dbs.DBSDocument.KEY_FULLTEXT_SIMPLE;
022import static org.nuxeo.ecm.core.storage.dbs.DBSDocument.KEY_ID;
023import static org.nuxeo.ecm.core.storage.dbs.DBSDocument.KEY_NAME;
024import static org.nuxeo.ecm.core.storage.dbs.DBSDocument.KEY_PARENT_ID;
025import static org.nuxeo.ecm.core.storage.dbs.DBSDocument.KEY_PRIMARY_TYPE;
026import static org.nuxeo.ecm.core.storage.dbs.DBSDocument.KEY_VERSION_SERIES_ID;
027
028import java.io.Serializable;
029
030import org.nuxeo.ecm.core.model.Document;
031import org.nuxeo.ecm.core.storage.State.StateDiff;
032import org.nuxeo.ecm.core.storage.StateHelper;
033import org.nuxeo.ecm.core.storage.State;
034
035/**
036 * Implementation of a {@link Document} state for Document-Based Storage.
037 * <p>
038 * It wraps a {@link State}, together with a dirty flag.
039 *
040 * @since 5.9.4
041 */
042public class DBSDocumentState {
043
044    /**
045     * The current state.
046     */
047    protected State state;
048
049    /**
050     * When non-null, the original state (otherwise the state hasn't been modified).
051     */
052    protected State originalState;
053
054    /**
055     * Constructs an empty state.
056     */
057    public DBSDocumentState() {
058        state = new State();
059        originalState = null;
060    }
061
062    /**
063     * Constructs a document state from the copy of an existing base state.
064     */
065    public DBSDocumentState(State base) {
066        state = StateHelper.deepCopy(base);
067        originalState = null;
068    }
069
070    /**
071     * This must be called if we're about to directly change the internal state.
072     */
073    public void markDirty() {
074        if (originalState == null) {
075            originalState = StateHelper.deepCopy(state);
076        }
077    }
078
079    /**
080     * Checks if the document state has been changed since its construction or the last call to {@link #setNotDirty}.
081     */
082    public boolean isDirty() {
083        return originalState != null;
084    }
085
086    public boolean isDirtyIgnoringFulltext() {
087        StateDiff diff = getStateChange();
088        if (diff == null) {
089            return false;
090        }
091        diff.remove(KEY_FULLTEXT_SIMPLE);
092        diff.remove(KEY_FULLTEXT_BINARY);
093        diff.remove(KEY_FULLTEXT_JOBID);
094        return !diff.isEmpty();
095    }
096
097    public void setNotDirty() {
098        originalState = null;
099        StateHelper.resetDeltas(state);
100    }
101
102    /**
103     * Gets the state. If the caller changes the state, it must also call {@link #dirty} to inform this object that the
104     * state is dirtied.
105     */
106    public State getState() {
107        return state;
108    }
109
110    /**
111     * Gets a diff of what changed since this document state was read from database or saved.
112     *
113     * @return {@code null} if there was no change, or a {@link StateDiff}
114     */
115    public StateDiff getStateChange() {
116        if (originalState == null) {
117            return null;
118        }
119        StateDiff diff = StateHelper.diff(originalState, state);
120        if (diff.isEmpty()) {
121            return null;
122        }
123        return diff;
124    }
125
126    /**
127     * Gets the original state for this, needed when creating an undo log.
128     *
129     * @return a state that must not be modified
130     * @since 7.4
131     */
132    public State getOriginalState() {
133        return originalState == null ? state : originalState;
134    }
135
136    public Serializable get(String key) {
137        return state.get(key);
138    }
139
140    public void put(String key, Serializable value) {
141        markDirty();
142        state.put(key, value);
143    }
144
145    public boolean containsKey(String key) {
146        return state.get(key) != null;
147    }
148
149    public String getId() {
150        return (String) get(KEY_ID);
151    }
152
153    public String getParentId() {
154        return (String) get(KEY_PARENT_ID);
155    }
156
157    public String getName() {
158        return (String) get(KEY_NAME);
159    }
160
161    public String getPrimaryType() {
162        return (String) get(KEY_PRIMARY_TYPE);
163    }
164
165    public String getVersionSeriesId() {
166        return (String) get(KEY_VERSION_SERIES_ID);
167    }
168
169    @Override
170    public String toString() {
171        return getClass().getSimpleName() + '(' + (isDirty() ? "dirty," : "") + state.toString() + ')';
172    }
173
174}