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