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