EMMA Coverage Report (generated Sun May 02 20:42:29 CEST 2010)
[all classes][hu.netmind.beankeeper.type.impl]

COVERAGE SUMMARY FOR SOURCE FILE [MapImpl.java]

nameclass, %method, %block, %line, %
MapImpl.java100% (6/6)81%  (52/64)89%  (1967/2206)86%  (331.7/387)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class MapImpl$MapImplEntrySetIterator$MapImplEntry100% (1/1)67%  (4/6)51%  (38/75)53%  (10/19)
MapImpl$MapImplEntrySetIterator$MapImplEntry (MapImpl$MapImplEntrySetIterator... 0%   (0/1)0%   (0/13)0%   (0/4)
setValue (Object): Object 0%   (0/1)0%   (0/24)0%   (0/5)
MapImpl$MapImplEntrySetIterator$MapImplEntry (MapImpl$MapImplEntrySetIterator... 100% (1/1)100% (16/16)100% (4/4)
MapImpl$MapImplEntrySetIterator$MapImplEntry (MapImpl$MapImplEntrySetIterator... 100% (1/1)100% (16/16)100% (4/4)
getKey (): Object 100% (1/1)100% (3/3)100% (1/1)
getValue (): Object 100% (1/1)100% (3/3)100% (1/1)
     
class MapImpl$SimpleMapEntry100% (1/1)75%  (3/4)70%  (19/27)67%  (6/9)
setValue (Object): Object 0%   (0/1)0%   (0/8)0%   (0/3)
MapImpl$SimpleMapEntry (Map$Entry): void 100% (1/1)100% (13/13)100% (4/4)
getKey (): Object 100% (1/1)100% (3/3)100% (1/1)
getValue (): Object 100% (1/1)100% (3/3)100% (1/1)
     
class MapImpl$MapImplEntrySetIterator100% (1/1)83%  (5/6)84%  (156/186)85%  (35/41)
access$1202 (MapImpl$MapImplEntrySetIterator, int): int 0%   (0/1)0%   (0/5)0%   (0/1)
read (): void 100% (1/1)80%  (89/111)83%  (20/24)
remove (): void 100% (1/1)92%  (34/37)83%  (5/6)
MapImpl$MapImplEntrySetIterator (MapImpl): void 100% (1/1)100% (21/21)100% (6/6)
hasNext (): boolean 100% (1/1)100% (3/3)100% (1/1)
next (): Object 100% (1/1)100% (9/9)100% (3/3)
     
class MapImpl$MapImplEntrySet100% (1/1)43%  (3/7)85%  (270/318)76%  (37.4/49)
clear (): void 0%   (0/1)0%   (0/4)0%   (0/2)
remove (Object): boolean 0%   (0/1)0%   (0/11)0%   (0/1)
size (): int 0%   (0/1)0%   (0/4)0%   (0/1)
toString (): String 0%   (0/1)0%   (0/5)0%   (0/1)
contains (Object): boolean 100% (1/1)91%  (258/282)84%  (35.4/42)
MapImpl$MapImplEntrySet (MapImpl): void 100% (1/1)100% (6/6)100% (1/1)
iterator (): Iterator 100% (1/1)100% (6/6)100% (1/1)
     
class MapImpl100% (1/1)92%  (33/36)93%  (1419/1530)91%  (229.3/253)
hashCode (): int 0%   (0/1)0%   (0/3)0%   (0/1)
retainAll (Object): boolean 0%   (0/1)0%   (0/56)0%   (0/12)
toString (): String 0%   (0/1)0%   (0/4)0%   (0/1)
get (Object): Object 100% (1/1)84%  (113/135)84%  (16/19)
remove (Object): Object 100% (1/1)87%  (41/47)89%  (8/9)
equals (Object): boolean 100% (1/1)91%  (21/23)75%  (3/4)
containsKey (Object): boolean 100% (1/1)96%  (189/196)96%  (31.6/33)
containsValue (Object): boolean 100% (1/1)97%  (307/318)94%  (50.6/54)
<static initializer> 100% (1/1)100% (4/4)100% (1/1)
MapImpl (): void 100% (1/1)100% (27/27)100% (10/10)
access$000 (MapImpl): ObjectTracker 100% (1/1)100% (3/3)100% (1/1)
access$100 (MapImpl): Map 100% (1/1)100% (3/3)100% (1/1)
access$1000 (MapImpl): QueryService 100% (1/1)100% (3/3)100% (1/1)
access$1100 (MapImpl): int 100% (1/1)100% (3/3)100% (1/1)
access$1108 (MapImpl): int 100% (1/1)100% (8/8)100% (1/1)
access$1300 (MapImpl): ClassTracker 100% (1/1)100% (3/3)100% (1/1)
access$200 (MapImpl): Map 100% (1/1)100% (3/3)100% (1/1)
access$300 (MapImpl): List 100% (1/1)100% (3/3)100% (1/1)
access$400 (MapImpl): TransactionTracker 100% (1/1)100% (3/3)100% (1/1)
access$500 (MapImpl): String 100% (1/1)100% (3/3)100% (1/1)
access$600 (MapImpl): ClassInfo 100% (1/1)100% (3/3)100% (1/1)
access$700 (MapImpl): SchemaManager 100% (1/1)100% (3/3)100% (1/1)
access$800 (MapImpl): Object 100% (1/1)100% (3/3)100% (1/1)
access$900 (MapImpl): TimeControl 100% (1/1)100% (3/3)100% (1/1)
addAll (Object): boolean 100% (1/1)100% (6/6)100% (2/2)
clear (): void 100% (1/1)100% (28/28)100% (7/7)
entrySet (): Set 100% (1/1)100% (5/5)100% (1/1)
getItemClassName (): String 100% (1/1)100% (4/4)100% (1/1)
getLastSerial (): Long 100% (1/1)100% (3/3)100% (1/1)
getParent (): Object 100% (1/1)100% (3/3)100% (1/1)
hasChanged (): boolean 100% (1/1)100% (15/15)100% (1/1)
init (ClassInfo, Object, String, String, Long, TimeControl): void 100% (1/1)100% (32/32)100% (9/9)
put (Object, Object): Object 100% (1/1)100% (110/110)100% (15/15)
reload (): void 100% (1/1)100% (141/141)100% (17/17)
save (Transaction, Long, Set, Set, Set, List): void 100% (1/1)100% (311/311)100% (53/53)
size (): int 100% (1/1)100% (12/12)100% (1/1)
     
class MapImpl$ObjectWrapper100% (1/1)80%  (4/5)93%  (65/70)88%  (14/16)
getIdentifier (): Long 0%   (0/1)0%   (0/3)0%   (0/1)
equals (Object): boolean 100% (1/1)91%  (21/23)80%  (4/5)
MapImpl$ObjectWrapper (MapImpl, Object): void 100% (1/1)100% (26/26)100% (6/6)
getObject (): Object 100% (1/1)100% (3/3)100% (1/1)
hashCode (): int 100% (1/1)100% (15/15)100% (3/3)

1/**
2 * Copyright (C) 2007 NetMind Consulting Bt.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 3 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 */
18 
19package hu.netmind.beankeeper.type.impl;
20 
21import java.util.*;
22import hu.netmind.beankeeper.parser.*;
23import hu.netmind.beankeeper.type.event.*;
24import hu.netmind.beankeeper.query.LazyList;
25import hu.netmind.beankeeper.model.*;
26import hu.netmind.beankeeper.transaction.Transaction;
27import hu.netmind.beankeeper.transaction.TransactionTracker;
28import hu.netmind.beankeeper.db.Limits;
29import hu.netmind.beankeeper.db.SearchResult;
30import hu.netmind.beankeeper.serial.Serial;
31import hu.netmind.beankeeper.db.Database;
32import hu.netmind.beankeeper.object.ObjectTracker;
33import hu.netmind.beankeeper.query.QueryService;
34import hu.netmind.beankeeper.schema.SchemaManager;
35import org.apache.log4j.Logger;
36 
37/**
38 * Custom map implementation based on lazy lists. Map is <strong>not</strong>
39 * thread-safe.
40 * @author Brautigam Robert
41 * @version Revision: $Revision$
42 */
43public class MapImpl extends AbstractMap implements Container
44{
45   private static Logger logger = Logger.getLogger(MapImpl.class);
46   
47   private List originalList;
48   private TimeControl originalTimeControl;
49   private ClassInfo parentInfo;
50   private Object parent;
51   private String parentAttributeName;
52   private Long lastSerial;
53   private ContainerItemClass itemClass;
54 
55   private Map addedItems;
56   private Map removedItems;
57   private boolean cleared = false;
58   private int modCount = 0;
59 
60   private ClassTracker classTracker = null; // Injected
61   private QueryService queryService = null; // Injected
62   private Database database = null; // Injected
63   private ObjectTracker objectTracker = null; // Injected
64   private TransactionTracker transactionTracker = null; // Injected
65   private SchemaManager schemaManager = null; // Injected
66   
67   /**
68    * Initialize with a default list.
69    */
70   public void init(ClassInfo classInfo, Object obj, 
71         String attributeName, String itemClassName, Long lastSerial, TimeControl timeControl)
72   {
73      this.originalList=null;
74      this.originalTimeControl=new TimeControl(timeControl);
75      this.parentInfo=classInfo;
76      this.parent=obj;
77      this.parentAttributeName=attributeName;
78      this.lastSerial=lastSerial;
79      this.itemClass=new ContainerItemClass(schemaManager,itemClassName);
80      // Model
81      reload();
82   }
83 
84   public Object getParent()
85   {
86      return parent;
87   }
88 
89   public String getItemClassName()
90   {
91      return itemClass.getItemClassName();
92   }
93 
94   public void reload()
95   {
96      // Initialize internal change structures
97      modCount++;
98      cleared = false;
99      addedItems = new HashMap();
100      removedItems = new HashMap();
101      // Load list
102      if ( getItemClassName() == null )
103      {
104         // No list
105         originalList = new ArrayList();
106      } else {
107         originalList = queryService.find(
108               "find item("+getItemClassName()+")"+
109               " where persistence_container_parent("+parentInfo.getSourceEntry().getFullName()+")"+
110               "."+parentAttributeName+" contains item and persistence_container_parent = ?",
111               new Object[] { parent }, originalTimeControl,null);
112         // Modify the select statements.
113         String subTableName = schemaManager.getTableName(
114               parentInfo.getAttributeClassEntry(parentAttributeName),parentAttributeName);
115         LazyList lazy = (LazyList) originalList;
116         for ( int i=0; i<lazy.getStmts().size(); i++ )
117         {
118            QueryStatement stmt = (QueryStatement) lazy.getStmts().get(i);
119            // First search for the table term of the subtable
120            Expression expr = stmt.getQueryExpression();
121            TableTerm subTableTerm = expr.getTableTerm(subTableName);
122            // Add this term to selected terms
123            stmt.getSelectTerms().add(new ReferenceTerm(subTableTerm,"container_key"));
124            // Add the correct order by
125            stmt.getOrderByList().add(0,new OrderBy(new ReferenceTerm(subTableTerm,"container_key"),OrderBy.ASCENDING));
126            // Correct static string
127            stmt.setStaticRepresentation(stmt.getStaticRepresentation()+" and mapkey");
128         }
129      }
130   }
131 
132   /**
133    * Returns whether the container changes internally since last save().
134    */
135   public boolean hasChanged()
136   {
137      return (addedItems.size()>0) || (removedItems.size()>0) || (cleared);
138   }
139 
140   /**
141    * Save the container to database.
142    */
143   public void save(Transaction transaction, Long currentSerial, Set waitingObjects, 
144         Set saveTables, Set removeTables, List events)
145   {
146      ClassEntry attributeClassEntry = parentInfo.getAttributeClassEntry(parentAttributeName);
147      String attributeTableName = schemaManager.getTableName(attributeClassEntry,parentAttributeName);
148      // If the list was cleared before, then clear the current values
149      if ( cleared )
150      {
151         HashMap keyAttributes = new HashMap();
152         keyAttributes.put("persistence_id",objectTracker.getIdentifier(parent));
153         keyAttributes.put("persistence_end",Serial.getMaxSerial().getValue());
154         keyAttributes.put("persistence_txend",Serial.getMaxSerial().getValue());
155         HashMap removeAttributes = new HashMap();
156         removeAttributes.put("persistence_txend",currentSerial);
157         removeAttributes.put("persistence_txendid",transaction.getSerial());
158         database.save(transaction, attributeTableName, keyAttributes,removeAttributes);
159         removeTables.add(attributeTableName);
160         // Notify listeners
161         events.add(new ClearedContainerEvent(parent,parentAttributeName));
162      }
163      // Remove removed items first
164      Iterator removedItemsIterator = removedItems.entrySet().iterator();
165      while ( removedItemsIterator.hasNext() )
166      {
167         Map.Entry entry = (Map.Entry) removedItemsIterator.next();
168         // Remove object from the list
169         HashMap keyAttributes = new HashMap();
170         keyAttributes.put("persistence_id",objectTracker.getIdentifier(parent));
171         keyAttributes.put("persistence_end",Serial.getMaxSerial().getValue());
172         keyAttributes.put("persistence_txend",Serial.getMaxSerial().getValue());
173         keyAttributes.put("container_key",entry.getKey().toString());
174         HashMap removeAttributes = new HashMap();
175         removeAttributes.put("persistence_txend",currentSerial);
176         removeAttributes.put("persistence_txendid",transaction.getSerial());
177         database.save(transaction, attributeTableName, keyAttributes,removeAttributes);
178         removeTables.add(attributeTableName);
179         // Notify listeners
180         events.add(new RemovedItemEvent(parent,parentAttributeName,new SimpleMapEntry(entry)));
181      }
182      // Add added items
183      Iterator addedItemsIterator = addedItems.entrySet().iterator();
184      while ( addedItemsIterator.hasNext() )
185      {
186         Map.Entry entry = (Map.Entry) addedItemsIterator.next();
187         ObjectWrapper wrapper = (ObjectWrapper) entry.getValue();
188         Object value = wrapper.getObject();
189         // Item does not exist then put it in the save list
190         if ( ! objectTracker.exists(value) )
191            waitingObjects.add(objectTracker.getWrapper(value));
192         // Add item
193         HashMap itemAttributes = new HashMap();
194         itemAttributes.put("persistence_id",objectTracker.getIdentifier(parent));
195         itemAttributes.put("persistence_start",Serial.getMaxSerial().getValue());
196         itemAttributes.put("persistence_end",Serial.getMaxSerial().getValue());
197         itemAttributes.put("persistence_txendid",new Long(0));
198         itemAttributes.put("persistence_txstartid",transaction.getSerial());
199         itemAttributes.put("persistence_txstart",currentSerial);
200         itemAttributes.put("persistence_txend",Serial.getMaxSerial().getValue());
201         itemAttributes.put("container_key",entry.getKey().toString());
202         itemAttributes.put("value",objectTracker.getIdentifier(value));
203         database.insert(transaction, attributeTableName, itemAttributes);
204         saveTables.add(attributeTableName);
205         // Notify listeners
206         logger.debug("adding item event to attribute: "+parentAttributeName+", entry: "+entry);
207         events.add(new AddedItemEvent(parent,parentAttributeName,new SimpleMapEntry(entry)));
208      }
209      // Reload the changed list. Note, that the list should not
210      // be referenced until the Store.save() operation completes, because
211      // the list will not contain nonexisting object currently.
212      originalTimeControl = new TimeControl(currentSerial,transaction.getSerial(),true);
213      lastSerial = currentSerial;
214   }
215 
216   /**
217    * Get the serial number of last modification.
218    */
219   public Long getLastSerial()
220   {
221      return lastSerial;
222   }
223 
224   /**
225    * Retain all container elements inside the other container.
226    */
227   public boolean retainAll(Object c)
228   {
229      // Go through all elements, if it's not contained in c,
230      // then remove (using the iterator!)
231      boolean changed = false;
232      Iterator iterator = entrySet().iterator();
233      if ( logger.isDebugEnabled() )
234         logger.debug("map retain all: "+toString()+" from "+c.toString());
235      while ( iterator.hasNext() )
236      {
237         Map.Entry entry = (Map.Entry) iterator.next();
238         if ( ! ((Map)c).containsKey(entry.getKey()) )
239         {
240            logger.debug("trying to remove key: "+entry.getKey());
241            iterator.remove();
242            changed=true;
243         }
244      }
245      return changed;
246   }
247 
248   /**
249    * Add all items in the other container.
250    */
251   public boolean addAll(Object c)
252   {
253      putAll((Map)c);
254      return true;
255   }
256 
257   /*
258    * Map implementation.
259    */
260 
261   public void clear()
262   {
263      // Clear content and mark cleared flag
264      cleared = true;
265      addedItems = new HashMap();
266      removedItems = new HashMap();
267      originalList = new ArrayList();
268      itemClass.clear();
269      modCount++;
270   }
271 
272   /**
273    * Return whether this map contains the specified key.
274    */
275   public boolean containsKey(Object key)
276   {
277      // If the item is removed, then it is not contained.
278      if ( removedItems.containsKey(key) )
279         return false;
280      // If it is added, then it is contained.
281      if ( addedItems.containsKey(key) )
282         return true;
283      // Else, check the list
284      if ( (originalList instanceof LazyList) && (!((LazyList) originalList).isIterationCheap()) )
285      {
286         // This means, that the backing lazy list would page if we
287         // were to iterate. So instead, we run a specific query for
288         // the given id.
289         Transaction tx = transactionTracker.getTransaction(TransactionTracker.TX_REQUIRED);
290         tx.begin();
291         try
292         {
293            String listTableName = schemaManager.getTableName(
294                  parentInfo.getAttributeClassEntry(parentAttributeName),parentAttributeName);
295            TableTerm listTableTerm = new TableTerm(listTableName,null);
296            Expression listExpression = new Expression();
297            listExpression.add(new ReferenceTerm(listTableTerm,"persistence_id"));
298            listExpression.add("=");
299            listExpression.add(new ConstantTerm(objectTracker.getIdentifier(parent)));
300            listExpression.add("and");
301            listExpression.add(new ReferenceTerm(listTableTerm,"container_key"));
302            listExpression.add("=");
303            listExpression.add(new ConstantTerm(key.toString()));
304            listExpression.add("and");
305            originalTimeControl.apply(listExpression,listTableTerm);
306            // Execute. If there is a hit, then object exists
307            QueryStatement stmt = new QueryStatement(listTableTerm,listExpression,null);
308            stmt.setTimeControl(originalTimeControl);
309            stmt.setStaticRepresentation("FIND "+listTableTerm+" WHERE persistence_id = "+
310                  objectTracker.getIdentifier(parent)+" and mapkey = "+key);
311            SearchResult result = queryService.find(stmt,new Limits(0,0,-1));
312            // Check
313            if ( result.getResultSize() > 0 )
314               return true; // Object exists
315         } finally {
316            tx.commit();
317         }
318      } else {
319         // Set is small, so iterate
320         for ( int i=0; i<originalList.size(); i++ )
321         {
322            Map obj = (Map) originalList.get(i);
323            if ( key.equals(obj.get("container_key")) )
324               return true;
325         }
326      }
327      // Fall through
328      return false;
329   }
330 
331   /**
332    * Returns whether this map contains one or more of the 
333    * specified value.
334    */
335   public boolean containsValue(Object value)
336   {
337      if ( value == null )
338         return false;
339      ClassTracker.ClassType type = classTracker.getType(value.getClass());
340      ObjectWrapper wrapper = new ObjectWrapper(value);
341      Long id = objectTracker.getIdentifier(value);
342      if ( id == null )
343         return false; // Can not contain value with no id
344      // If the item is removed, then it is not contained.
345      if ( removedItems.containsValue(wrapper) )
346         return false;
347      // If it is added, then it is contained.
348      if ( addedItems.containsValue(wrapper) )
349         return true;
350      // Else, check the list
351      if ( (originalList instanceof LazyList) && (!((LazyList) originalList).isIterationCheap()) )
352      {
353         // This means, that the backing lazy list would page if we
354         // were to iterate. So instead, we run a specific query for
355         // the given id.
356         Transaction tx = transactionTracker.getTransaction(TransactionTracker.TX_REQUIRED);
357         tx.begin();
358         try
359         {
360            ClassInfo itemInfo = classTracker.getClassInfo(value.getClass(),value);
361            String itemTableName = schemaManager.getTableName(itemInfo.getSourceEntry());
362            TableTerm itemTableTerm = new TableTerm(itemTableName,null);
363            String listTableName = schemaManager.getTableName(
364                  parentInfo.getAttributeClassEntry(parentAttributeName),parentAttributeName);
365            TableTerm listTableTerm = new TableTerm(listTableName,null);
366            Expression listExpression = new Expression();
367            listExpression.add(new ReferenceTerm(listTableTerm,"persistence_id"));
368            listExpression.add("=");
369            listExpression.add(new ConstantTerm(objectTracker.getIdentifier(parent)));
370            listExpression.add("and");
371            if ( type != ClassTracker.ClassType.TYPE_PRIMITIVE )
372            {
373               listExpression.add(new ReferenceTerm(listTableTerm,"value"));
374               listExpression.add("=");
375               listExpression.add(new ConstantTerm(id));
376            } else {
377               listExpression.add(new ReferenceTerm(itemTableTerm,"value"));
378               listExpression.add("=");
379               listExpression.add(new ConstantTerm(value));
380            }
381            listExpression.add("and");
382            listExpression.add(new ReferenceTerm(itemTableTerm,"persistence_id"));
383            listExpression.add("=");
384            listExpression.add(new ReferenceTerm(listTableTerm,"value"));
385            listExpression.add("and");
386            originalTimeControl.apply(listExpression,itemTableTerm);
387            listExpression.add("and");
388            originalTimeControl.apply(listExpression,listTableTerm);
389            // Execute. If there is a hit, then object exists
390            QueryStatement stmt = new QueryStatement(listTableTerm,listExpression,null);
391            stmt.getSpecifiedTerms().add(new SpecifiedTableTerm(itemTableTerm));
392            stmt.setTimeControl(originalTimeControl);
393            stmt.setStaticRepresentation("FIND "+listTableTerm+" WHERE persistence_id = "+objectTracker.getIdentifier(parent)+" and value = "+id);
394            SearchResult result = queryService.find(stmt,new Limits(0,0,-1));
395            // Check
396            if ( result.getResultSize() > 0 )
397               return true; // Object exists
398         } finally {
399            tx.commit();
400         }
401      } else {
402         // Set is small, so iterate
403         for ( int i=0; i<originalList.size(); i++ )
404         {
405            Map obj = (Map) originalList.get(i);
406            if ( ((type==ClassTracker.ClassType.TYPE_PRIMITIVE) && (value.equals(obj.get("object")))) ||
407                 ((type!=ClassTracker.ClassType.TYPE_PRIMITIVE) && 
408                  (id.equals(objectTracker.getIdentifier(obj.get("object"))))
409                 ) )
410               return true;
411         }
412      }
413      // Fall through
414      return false;
415   }
416 
417   /**
418    * Returns a set view of this map.
419    */
420   public Set entrySet()
421   {
422      return new MapImplEntrySet();
423   }
424 
425   /**
426    * This hash code invokes the parent, which is ok becaue the equals method
427    * does the same thing as the superclass', just more efficiently.
428    */
429   public int hashCode()
430   {
431      return super.hashCode();
432   }
433 
434   /**
435    * Determine whether map equals another map in content.
436    */
437   public boolean equals(Object obj)
438   {
439      // If it is not a map, then it surely does not equal.
440      if ( ! (obj instanceof Map) )
441         return false;
442      // If they are the same size, and all items equal, then the
443      // maps equal.
444      Map c = (Map) obj;
445      return (size()==c.size()) && (entrySet().containsAll(c.entrySet()));
446   }
447 
448   /**
449    * Get the value for a mapkey.
450    */
451   public Object get(Object key)
452   {
453      // If contained in the added list, return that
454      ObjectWrapper wrapper = (ObjectWrapper) addedItems.get(key);
455      if ( wrapper != null )
456         return wrapper.getObject();
457      // If contained in the remove list, then the result is null.
458      if ( removedItems.containsKey(key) )
459         return null;
460      // Not found until now, so check the list
461      if ( (originalList instanceof LazyList) && (!((LazyList) originalList).isIterationCheap()) )
462      {
463         if ( logger.isDebugEnabled() )
464            logger.debug("there are many entries, so finding with select: "+key);
465         // The list is too large, so select the item separately
466         List result = queryService.find(
467               "find item("+getItemClassName()+")"+
468               " where persistence_container_parent("+parentInfo.getSourceEntry().getFullName()+")"+
469               "."+parentAttributeName+"['"+key.toString()+"']=item and persistence_container_parent = ?",
470               new Object[] { parent }, originalTimeControl,null);
471         if ( result.isEmpty() )
472            return null;
473         return result.get(0);               
474      } else {
475         if ( logger.isDebugEnabled() )
476            logger.debug("iterating to find item: "+key);
477         // List is small, so iterate
478         for ( int i=0; i<originalList.size(); i++ )
479         {
480            Map obj = (Map) originalList.get(i);
481            if ( key.equals(obj.get("container_key")) )
482               return obj.get("object"); // Found key
483         }
484      }
485      // Fall through
486      return null; // Not found
487   }
488 
489   /**
490    * Put an item into the map.
491    */
492   public Object put(Object key, Object value)
493   {
494      // Check value validity
495      if ( value == null )
496         throw new IllegalArgumentException("map implementation not accepting null values.");
497      ClassTracker.ClassType type = classTracker.getType(value.getClass());
498      if ( (type != ClassTracker.ClassType.TYPE_OBJECT) && (type != ClassTracker.ClassType.TYPE_PRIMITIVE) )
499         throw new IllegalArgumentException("map only handles object or primitive types, but was: "+value+" ("+value.getClass().getName()+")");
500      // Determine the old value if there was one
501      Object oldValue = get(key);
502      // If the old value is the same, then skip
503      if ( (oldValue!=null) && (objectTracker.getIdentifier(oldValue)!=null) &&
504            (objectTracker.getIdentifier(oldValue).equals(objectTracker.getIdentifier(value))) )
505         return oldValue;
506      // Remove the key if there is an oldvalue
507      if ( oldValue != null )
508         remove(key);
509      // Then add the entry, now that is it not contained
510      itemClass.updateItemClassName(originalList,value.getClass(),addedItems.size()==0);
511      addedItems.put(key,new ObjectWrapper(value));
512      // Modification took place
513      modCount++;
514      // Return with the old value of the key
515      logger.debug("added key: "+key);
516      return oldValue;
517   }
518 
519   /**
520    * Remove a key from the map.
521    */
522   public Object remove(Object key)
523   {
524      // Get the old value
525      Object oldValue = get(key);
526      if ( oldValue == null )
527         return null; // Not contained, so no op.
528      // Determine whether key is in the added list. If it is,
529      // then simply remove it, so it will not be added. Else, add to
530      // the removed list.
531      if ( addedItems.containsKey(key) )
532         addedItems.remove(key);
533      else
534         removedItems.put(key,new ObjectWrapper(oldValue));
535      // Modification took place
536      modCount++;
537      // Return with old value
538      logger.debug("removed key: "+key);
539      return oldValue;
540   }
541 
542   public int size()
543   {
544      return originalList.size() - removedItems.size() + addedItems.size();
545   }
546 
547   public String toString()
548   {
549      return originalList.toString();
550   }
551 
552   public class MapImplEntrySet extends AbstractSet
553   {
554      public void clear()
555      {
556         MapImpl.this.clear();
557      }
558 
559      public boolean contains(Object o)
560      {
561         Map.Entry entry = (Map.Entry) o;
562         ObjectWrapper wrapper = new ObjectWrapper(entry.getValue());
563         Long id = objectTracker.getIdentifier(entry.getValue());
564         if ( id == null )
565            return false; // Can not contain value with no id
566         // If the item is removed, then it is not contained.
567         if ( removedItems.containsKey(entry.getKey()) )
568            return false;
569         // If it is added, then it is contained and is the same value.
570         if ( addedItems.containsKey(entry.getKey()) )
571            return addedItems.get(entry.getKey()).equals(wrapper);
572         // Else, check the list
573         if ( (originalList instanceof LazyList) && (!((LazyList) originalList).isIterationCheap()) )
574         {
575            // This means, that the backing lazy list would page if we
576            // were to iterate. So instead, we run a specific query for
577            // the given id.
578            Transaction tx = transactionTracker.getTransaction(TransactionTracker.TX_REQUIRED);
579            tx.begin();
580            try
581            {
582               String listTableName = schemaManager.getTableName(
583                     parentInfo.getAttributeClassEntry(parentAttributeName),parentAttributeName);
584               TableTerm listTableTerm = new TableTerm(listTableName,null);
585               Expression listExpression = new Expression();
586               listExpression.add(new ReferenceTerm(listTableTerm,"persistence_id"));
587               listExpression.add("=");
588               listExpression.add(new ConstantTerm(objectTracker.getIdentifier(parent)));
589               listExpression.add("and");
590               listExpression.add(new ReferenceTerm(listTableTerm,"value"));
591               listExpression.add("=");
592               listExpression.add(new ConstantTerm(id));
593               listExpression.add("and");
594               listExpression.add(new ReferenceTerm(listTableTerm,"container_key"));
595               listExpression.add("=");
596               listExpression.add(new ConstantTerm(entry.getKey()));
597               listExpression.add("and");
598               originalTimeControl.apply(listExpression,listTableTerm);
599               // Execute. If there is a hit, then object exists
600               QueryStatement stmt = new QueryStatement(listTableTerm,listExpression,null);
601               stmt.setTimeControl(originalTimeControl);
602               stmt.setStaticRepresentation("FIND "+listTableTerm+" WHERE persistence_id = "+
603                     objectTracker.getIdentifier(parent)+" and value = "+id+", mapkey = "+entry.getKey());
604               SearchResult result = queryService.find(stmt,new Limits(0,0,-1));
605               // Check
606               if ( result.getResultSize() > 0 )
607                  return true; // Object exists
608            } finally {
609               tx.commit();
610            }
611         } else {
612            // Set is small, so iterate
613            for ( int i=0; i<originalList.size(); i++ )
614            {
615               Map obj = (Map) originalList.get(i);
616               if ( obj.get("container_key").equals(entry.getKey()) )
617                  return (id.equals(objectTracker.getIdentifier(obj.get("object"))));
618            }
619         }
620         // Fall through
621         return false;
622      }
623 
624      public Iterator iterator()
625      {
626         return new MapImplEntrySetIterator();
627      }
628 
629      public boolean remove(Object o)
630      {
631         return MapImpl.this.remove( ((Map.Entry) o).getKey() ) != null;
632      }
633 
634      public int size()
635      {
636         return MapImpl.this.size();
637      }
638 
639      public String toString()
640      {
641         return originalList.toString();
642      }
643   }
644 
645   public class MapImplEntrySetIterator implements Iterator
646   {
647      private int ownModCount;
648      private int index;
649      private boolean hasNext;
650      private Map.Entry next;
651      private Map.Entry current;
652      private Iterator addedIterator;
653 
654      public MapImplEntrySetIterator()
655      {
656         index = 0;
657         ownModCount=modCount;
658         addedIterator = addedItems.entrySet().iterator();
659         // preread
660         read();
661      }
662 
663      private void read()
664      {
665         // Ensure that no modification took place
666         if ( ownModCount != modCount )
667            throw new java.util.ConcurrentModificationException("map was modified while iterating");
668         // Check whether there are elements left in the list
669         while ( index<originalList.size() )
670         {
671            Map obj = (Map) originalList.get(index);
672            index++; // Next
673            Object key = obj.get("container_key");
674            if ( ! removedItems.containsKey(key) )
675            {
676               // This is a valid item, because it is not removed
677               hasNext = true;
678               next = new MapImplEntry(obj);
679               return;
680            } 
681            if ( addedItems.containsKey(key) )
682            {
683               // This is a valid item, because it's key is here, but
684               // it was altered with another value. In this case we
685               // place the pair here, in it's original place.
686               hasNext = true;
687               next = new MapImplEntry(key,(ObjectWrapper)addedItems.get(key));
688               return;
689            } 
690         }
691         // If there were no elements in the original list, check through
692         // the added items. Note, that if the added key was already iterated
693         // through in the original list, that key has to be skipped here.
694         if ( addedIterator.hasNext() )
695         {
696            Map.Entry entry = (Map.Entry) addedIterator.next();
697            if ( ! removedItems.containsKey(entry.getKey()) )
698            {
699               // The key is not removed, meaning it is really a new key,
700               // which was not contained in the original list.
701               hasNext = true;
702               next = new MapImplEntry(entry);
703               return;
704            }
705         }
706         // Fall through
707         hasNext = false;
708         next = null;
709      }
710 
711      public void remove()
712      {
713         // Remove the current item
714         if ( index <= originalList.size() )
715         {
716            // The current item is in the original list, not in the
717            // added list, insert into the removed list.
718            removedItems.put(current.getKey(),new ObjectWrapper(current.getValue()));
719         } else {
720            // The current item is in the added list, so remove from it.
721            addedIterator.remove();
722            // Modify the modcount, because the whole set changed
723         }
724         modCount++;
725         ownModCount = modCount; // The modcount changed, but we're safe
726      }
727 
728      public boolean hasNext()
729      {
730         return hasNext;
731      }
732 
733      public Object next()
734      {
735         current = next;
736         read(); // Pre-read next
737         return current;
738      }
739 
740      public class MapImplEntry implements Map.Entry
741      {
742         private Object key;
743         private Object value;
744 
745         public MapImplEntry(Map obj)
746         {
747            key = obj.get("container_key");
748            value = obj.get("object");
749         }
750 
751         public MapImplEntry(Map.Entry entry)
752         {
753            key = entry.getKey();
754            value = ((ObjectWrapper) entry.getValue()).getObject();
755         }
756 
757         public MapImplEntry(Object key, ObjectWrapper wrapper)
758         {
759            this.key=key;
760            this.value=wrapper.getObject();
761         }
762         
763         public Object getKey()
764         {
765            return key;
766         }
767 
768         public Object getValue()
769         {
770            return value;
771         }
772 
773         public Object setValue(Object value)
774         {
775            // This will modify the map, but we're ok, because
776            // it won't hurt our iteration.
777            Object oldValue = this.value;
778            put(key,value);
779            this.value=value;
780            ownModCount=modCount;
781            return oldValue;
782         }
783      }
784   }
785 
786   public static class SimpleMapEntry implements Map.Entry
787   {
788      private Object key;
789      private Object value;
790      
791      public SimpleMapEntry(Map.Entry entry)
792      {
793         this.key=entry.getKey();
794         this.value=((ObjectWrapper) entry.getValue()).getObject();
795      }
796 
797      public Object getKey()
798      {
799         return key;
800      }
801 
802      public Object getValue()
803      {
804         return value;
805      }
806 
807      public Object setValue(Object value)
808      {
809         Object oldValue = this.value;
810         this.value=value;
811         return oldValue;
812      }
813   }
814 
815   public class ObjectWrapper
816   {
817      private Object obj;
818      private Long id;
819      private ClassTracker.ClassType type;
820 
821      public ObjectWrapper(Object obj)
822      {
823         this.obj=obj;
824         objectTracker.registerObject(obj);
825         this.id=objectTracker.getIdentifier(obj);
826         this.type=classTracker.getType(obj.getClass());
827      }
828 
829      public Long getIdentifier()
830      {
831         return id;
832      }
833 
834      public Object getObject()
835      {
836         return obj;
837      }
838 
839      public int hashCode()
840      {
841         if ( type == ClassTracker.ClassType.TYPE_PRIMITIVE )
842            return obj.hashCode();
843         else
844            return (int) (id.longValue()>>32);
845      }
846 
847      public boolean equals(Object rhs)
848      {
849         if ( ! (rhs instanceof ObjectWrapper) )
850            return false;
851         if ( type == ClassTracker.ClassType.TYPE_PRIMITIVE )
852            return obj.equals( ((ObjectWrapper)rhs).obj );
853         else
854            return id.equals(((ObjectWrapper) rhs).id);
855      }
856 
857   }
858}
859 

[all classes][hu.netmind.beankeeper.type.impl]
EMMA 2.0.5312debian (C) Vladimir Roubtsov