001/**
002 *   GRANITE DATA SERVICES
003 *   Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S.
004 *
005 *   This file is part of the Granite Data Services Platform.
006 *
007 *                               ***
008 *
009 *   Community License: GPL 3.0
010 *
011 *   This file is free software: you can redistribute it and/or modify
012 *   it under the terms of the GNU General Public License as published
013 *   by the Free Software Foundation, either version 3 of the License,
014 *   or (at your option) any later version.
015 *
016 *   This file is distributed in the hope that it will be useful, but
017 *   WITHOUT ANY WARRANTY; without even the implied warranty of
018 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
019 *   GNU General Public License for more details.
020 *
021 *   You should have received a copy of the GNU General Public License
022 *   along with this program. If not, see <http://www.gnu.org/licenses/>.
023 *
024 *                               ***
025 *
026 *   Available Commercial License: GraniteDS SLA 1.0
027 *
028 *   This is the appropriate option if you are creating proprietary
029 *   applications and you are not prepared to distribute and share the
030 *   source code of your application under the GPL v3 license.
031 *
032 *   Please visit http://www.granitedataservices.com/license for more
033 *   details.
034 */
035package org.granite.client.tide.data.spi;
036
037import java.util.HashSet;
038import java.util.IdentityHashMap;
039import java.util.LinkedList;
040import java.util.List;
041import java.util.Map;
042import java.util.Set;
043
044import org.granite.client.tide.data.Conflicts;
045import org.granite.client.tide.data.EntityManager;
046import org.granite.client.tide.server.ServerSession;
047
048/**
049 * @author William DRAI
050 */
051public class MergeContext {
052    
053    private static ThreadLocal<Map<EntityManager, MergeContext>> mergeContext = new ThreadLocal<Map<EntityManager, MergeContext>>() {
054        @Override
055        protected Map<EntityManager, MergeContext> initialValue() {
056            return new IdentityHashMap<EntityManager, MergeContext>();
057        }
058    };
059    
060    private final EntityManager entityManager;
061        private final DirtyCheckContext dirtyCheckContext;
062    private IdentityHashMap<Object, Object> entityCache = null;
063    private LinkedList<Object> mergeStack = new LinkedList<Object>();
064    
065    private String externalDataSessionId = null;
066    private EntityManager sourceEntityManager = null;
067    private ServerSession serverSession = null;
068    private boolean mergeUpdate = false;
069    private boolean merging = false;
070    private Set<Object> versionChangeCache = null;
071    private boolean resolvingConflict = false;
072    private boolean skipDirtyCheck = false;
073    private Conflicts mergeConflicts = null;
074    private boolean uninitializing = false;
075    
076    
077    public static MergeContext get(EntityManager entityManager) {
078        return mergeContext.get().get(entityManager);
079    }
080    
081    public static void destroy(EntityManager entityManager) {
082        mergeContext.get().remove(entityManager);
083    }
084    
085    
086    public MergeContext(EntityManager entityManager, DirtyCheckContext dirtyCheckContext, ServerSession serverSession) {
087        this.entityManager = entityManager;
088        this.dirtyCheckContext = dirtyCheckContext;
089        this.serverSession = serverSession;
090        mergeContext.get().put(entityManager, this);
091    }
092
093    
094    public void initMerge() {
095        if (this.entityCache == null) {
096            this.entityCache = new IdentityHashMap<Object, Object>();
097            this.mergeUpdate = true;
098        }
099    }
100    
101    public void clear() {
102                this.entityCache = null;
103        this.mergeConflicts = null;
104        this.versionChangeCache = null;
105        this.resolvingConflict = false;
106                this.uninitializing = false;
107        this.merging = false;
108        this.mergeUpdate = false;
109    }
110
111    public void addConflict(Object localEntity, Object receivedEntity, List<String> properties) {
112        if (this.mergeConflicts == null)
113            this.mergeConflicts = new Conflicts(this.entityManager);
114
115        this.mergeConflicts.addConflict(localEntity, receivedEntity, properties);
116    }
117
118    public void initMergeConflicts() {
119        this.entityCache = null;
120        this.versionChangeCache = null;
121        this.resolvingConflict = false;
122    }
123
124    public void checkConflictsResolved() {
125        if (this.mergeConflicts != null && this.mergeConflicts.isAllResolved())
126            this.mergeConflicts = null;
127    }
128    
129    public boolean isResolvingConflict() {
130        return this.resolvingConflict;
131    }
132    
133    public void setResolvingConflict(boolean resolvingConflict) {
134        this.resolvingConflict = resolvingConflict;
135    }
136
137    public Conflicts getMergeConflicts() {
138        return this.mergeConflicts;
139    }
140        
141        public Map<?, ?> getEntityCache() {
142                return this.entityCache;
143        }
144        
145        public IdentityHashMap<Object, Object> saveEntityCache() {
146                IdentityHashMap<Object, Object> entityCache = this.entityCache;
147                this.entityCache = new IdentityHashMap<Object, Object>();
148                return entityCache;
149        }
150        public void restoreEntityCache(IdentityHashMap<Object, Object> entityCache) {
151                this.entityCache = entityCache;
152        }       
153
154    public String getExternalDataSessionId() {
155        return this.externalDataSessionId;
156    }
157
158    public void setExternalDataSessionId(String externalDataSessionId) {
159        this.externalDataSessionId = externalDataSessionId;
160    }
161    
162    public void setServerSession(ServerSession serverSession) {
163        this.serverSession = serverSession;
164    }
165    
166    public ServerSession getServerSession() {
167        return serverSession;
168    }
169
170    public void setSourceEntityManager(EntityManager sourceEntityManager) {
171        this.sourceEntityManager = sourceEntityManager;
172    }
173    
174    public EntityManager getSourceEntityManager() {
175        return this.sourceEntityManager;
176    }
177    
178    public boolean isMergeUpdate() {
179        return this.mergeUpdate;
180    }
181    
182    public void setMergeUpdate(boolean mergeUpdate) {
183        this.mergeUpdate = mergeUpdate;
184    }
185
186    public boolean isMerging() {
187        return this.merging;
188    }
189    
190    public void setMerging(boolean merging) {
191        this.merging = merging;
192    }
193
194    public boolean isSkipDirtyCheck() {
195        return this.skipDirtyCheck;
196    }
197    
198    public void setSkipDirtyCheck(boolean skipDirtyCheck) {
199        this.skipDirtyCheck = skipDirtyCheck;
200    }
201    
202    public Object getFromCache(Object obj) {
203        if (this.entityCache == null)
204            return null;
205        return this.entityCache.get(obj);
206    }
207    
208    public void pushMerge(Object obj, Object dest) {
209        pushMerge(obj, dest, true);
210    }
211    public void pushMerge(Object obj, Object dest, boolean push) {
212        if (this.entityCache != null)
213            this.entityCache.put(obj, dest);
214        if (push)
215                this.mergeStack.push(dest);
216    }
217        public Object getCachedMerge(Object obj) {
218                return this.entityCache.get(obj);
219        }
220        public Object popMerge() {
221                return this.mergeStack.pop();
222        }
223        public Object getCurrentMerge() {
224                return this.mergeStack.peek();
225        }
226        public void setCurrentMerge(Object merge) {
227                this.mergeStack.set(0, merge);
228        }
229        public int getMergeStackSize() {
230                return this.mergeStack.size();
231        }
232        
233        public Object getSavedProperties(Object object) {
234                return dirtyCheckContext.getSavedProperties(object);
235        }
236        
237        public Object getCachedObject(Object object) {
238                return entityManager.getCachedObject(object, true);
239        }
240        
241        public Object[] getOwnerEntity(Object entity) {
242                return entityManager.getOwnerEntity(entity);
243        }
244        
245        public boolean isUnsaved(Object object) {
246                return dirtyCheckContext.isUnsaved(object);
247        }
248    
249    public void clearCache() {
250        this.entityCache = null;
251    }
252    
253    private Set<Object> getVersionChangeCache() {
254        if (this.versionChangeCache == null)
255            this.versionChangeCache = new HashSet<Object>();
256        return this.versionChangeCache;
257    }
258    
259    public void markVersionChanged(Object obj) {
260        getVersionChangeCache().add(obj);
261    }
262    
263    public boolean hasVersionChanged(Object obj) {
264        return this.versionChangeCache != null ? this.versionChangeCache.contains(obj) : false;
265    }
266
267    public void setUninitializing(boolean uninitializing) {
268        this.uninitializing = uninitializing;
269    }
270
271    public boolean isUninitializing() {
272        return this.uninitializing;
273    }
274
275    public boolean isUninitializeAllowed() {
276        return this.entityManager.isUninitializeAllowed();
277    }
278}