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