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

COVERAGE SUMMARY FOR SOURCE FILE [ListImpl.java]

nameclass, %method, %block, %line, %
ListImpl.java100% (4/4)87%  (46/53)90%  (2745/3063)93%  (531.2/574)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class ListImpl$ChangeEntry100% (1/1)67%  (6/9)53%  (34/64)71%  (12/17)
setChange (int): void 0%   (0/1)0%   (0/4)0%   (0/2)
setItem (ListImpl$ObjectWrapper): void 0%   (0/1)0%   (0/4)0%   (0/2)
toString (): String 0%   (0/1)0%   (0/22)0%   (0/1)
ListImpl$ChangeEntry (ListImpl, int, int, ListImpl$ObjectWrapper, boolean): void 100% (1/1)100% (18/18)100% (6/6)
getChange (): int 100% (1/1)100% (3/3)100% (1/1)
getItem (): ListImpl$ObjectWrapper 100% (1/1)100% (3/3)100% (1/1)
getOffset (): int 100% (1/1)100% (3/3)100% (1/1)
getReindex (): boolean 100% (1/1)100% (3/3)100% (1/1)
setOffset (int): void 100% (1/1)100% (4/4)100% (2/2)
     
class ListImpl$ObjectWrapper100% (1/1)80%  (8/10)86%  (118/138)86%  (31/36)
getIdentifier (): Long 0%   (0/1)0%   (0/3)0%   (0/1)
hashCode (): int 0%   (0/1)0%   (0/15)0%   (0/3)
equals (Object): boolean 100% (1/1)95%  (40/42)90%  (9/10)
ListImpl$ObjectWrapper (ListImpl, Object): void 100% (1/1)100% (29/29)100% (7/7)
ListImpl$ObjectWrapper (ListImpl, int, Object): void 100% (1/1)100% (32/32)100% (8/8)
getIndex (): int 100% (1/1)100% (3/3)100% (1/1)
getObject (): Object 100% (1/1)100% (3/3)100% (1/1)
getOriginalIndex (): long 100% (1/1)100% (3/3)100% (1/1)
setIndex (int): void 100% (1/1)100% (4/4)100% (2/2)
setOriginalIndex (long): void 100% (1/1)100% (4/4)100% (2/2)
     
class ListImpl100% (1/1)94%  (30/32)91%  (2561/2827)94%  (482.2/514)
hashCode (): int 0%   (0/1)0%   (0/3)0%   (0/1)
retainAll (Object): boolean 0%   (0/1)0%   (0/5)0%   (0/1)
remove (int): Object 100% (1/1)60%  (32/53)78%  (7/9)
postSelect (Map, List, Limits): boolean 100% (1/1)63%  (26/41)83%  (5/6)
add (int, Object): void 100% (1/1)68%  (159/235)87%  (32.9/38)
get (int): Object 100% (1/1)82%  (9/11)75%  (3/4)
addChange (int, int, ListImpl$ObjectWrapper, boolean): void 100% (1/1)88%  (142/162)91%  (31/34)
equals (Object): boolean 100% (1/1)89%  (47/53)81%  (13/16)
searchInternal (Object, ArrayList): Long 100% (1/1)89%  (257/288)93%  (42.6/46)
preSelect (Map, QueryStatement, List, Limits, Limits): QueryStatement 100% (1/1)89%  (229/256)93%  (40/43)
save (Transaction, Long, Set, Set, Set, List): void 100% (1/1)95%  (691/731)97%  (128/132)
lastIndexOf (Object): int 100% (1/1)95%  (223/234)94%  (35.7/38)
indexOf (Object): int 100% (1/1)97%  (224/231)97%  (37/38)
contains (Object): boolean 100% (1/1)98%  (84/86)93%  (14/15)
<static initializer> 100% (1/1)100% (4/4)100% (1/1)
ListImpl (): void 100% (1/1)100% (24/24)100% (9/9)
access$000 (ListImpl): ObjectTracker 100% (1/1)100% (3/3)100% (1/1)
access$100 (ListImpl): ClassTracker 100% (1/1)100% (3/3)100% (1/1)
addAll (Object): boolean 100% (1/1)100% (5/5)100% (1/1)
clear (): void 100% (1/1)100% (33/33)100% (8/8)
getInternal (int): ListImpl$ObjectWrapper 100% (1/1)100% (114/114)100% (31/31)
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)
preIndexing (Map, int): int 100% (1/1)100% (2/2)100% (1/1)
reload (): void 100% (1/1)100% (152/152)100% (19/19)
remove (Object): boolean 100% (1/1)100% (15/15)100% (5/5)
set (int, Object): Object 100% (1/1)100% (10/10)100% (3/3)
size (): int 100% (1/1)100% (12/12)100% (1/1)
toString (): String 100% (1/1)100% (4/4)100% (1/1)
     
class ListImpl$1100% (1/1)100% (2/2)94%  (32/34)86%  (6/7)
compare (Object, Object): int 100% (1/1)93%  (26/28)83%  (5/6)
ListImpl$1 (ListImpl): void 100% (1/1)100% (6/6)100% (1/1)

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.query.LazyList;
23import hu.netmind.beankeeper.query.LazyListHooks;
24import hu.netmind.beankeeper.common.StoreException;
25import hu.netmind.beankeeper.object.ObjectTracker;
26import hu.netmind.beankeeper.parser.*;
27import hu.netmind.beankeeper.query.QueryService;
28import hu.netmind.beankeeper.type.event.*;
29import hu.netmind.beankeeper.model.*;
30import hu.netmind.beankeeper.transaction.Transaction;
31import hu.netmind.beankeeper.transaction.TransactionTracker;
32import hu.netmind.beankeeper.db.Limits;
33import hu.netmind.beankeeper.db.Database;
34import hu.netmind.beankeeper.db.SearchResult;
35import hu.netmind.beankeeper.serial.Serial;
36import hu.netmind.beankeeper.schema.SchemaManager;
37import org.apache.log4j.Logger;
38 
39/**
40 * Custom list implementation based on lazy lists. List is <strong>not</strong>
41 * thread-safe.
42 * @author Brautigam Robert
43 * @version Revision: $Revision$
44 */
45public class ListImpl extends AbstractList implements Container, LazyListHooks
46{
47   private static Logger logger = Logger.getLogger(ListImpl.class);
48   private static final long INTERVAL = Integer.MAX_VALUE;
49   private static final long INT_BITS = 32;
50   
51   private List originalList;
52   private TimeControl originalTimeControl;
53   private ClassInfo parentInfo;
54   private Object parent;
55   private String parentAttributeName;
56   private Long lastSerial;
57   private ContainerItemClass itemClass;
58 
59   private ArrayList changes; // Change entries of index layout
60   private LinkedList addedItems;
61   private LinkedList removedItems;
62   private boolean cleared = false;
63 
64   private ClassTracker classTracker = null; // Injected
65   private ObjectTracker objectTracker = null; // Injected
66   private Database database = null; // Injected
67   private QueryService queryService = null; // Injected
68   private TransactionTracker transactionTracker = null; // Injected
69   private SchemaManager schemaManager = null; // Injected
70   
71   /**
72    * Initialize with a default list.
73    */
74   public void init(ClassInfo classInfo, Object obj, 
75         String attributeName, String itemClassName, Long lastSerial, TimeControl timeControl)
76   {
77      this.originalList=null;
78      this.originalTimeControl=new TimeControl(timeControl);
79      this.parentInfo=classInfo;
80      this.parent=obj;
81      this.parentAttributeName=attributeName;
82      this.lastSerial=lastSerial;
83      this.itemClass=new ContainerItemClass(schemaManager,itemClassName);
84      // Model
85      reload();
86   }
87 
88   public Object getParent()
89   {
90      return parent;
91   }
92 
93   public String getItemClassName()
94   {
95      return itemClass.getItemClassName();
96   }
97 
98   public void reload()
99   {
100      // Clear model
101      modCount++;
102      changes = new ArrayList();
103      addedItems = new LinkedList();
104      removedItems = new LinkedList();
105      cleared = false;
106      // Reload list
107      if ( getItemClassName() == null )
108      {
109         originalList = new ArrayList();
110      } else {
111         originalList = queryService.find(
112               "find item("+getItemClassName()+")"+
113               " where persistence_container_parent("+parentInfo.getSourceEntry().getFullName()+")"+
114               "."+parentAttributeName+" contains item and persistence_container_parent = ?",
115               new Object[] { parent }, originalTimeControl,null);
116         // Modify the select statements.
117         String subTableName = schemaManager.getTableName(
118               parentInfo.getAttributeClassEntry(parentAttributeName),parentAttributeName);
119         LazyList lazy = (LazyList) originalList;
120         lazy.setHooks(this);
121         for ( int i=0; (lazy.getStmts()!=null) && (i<lazy.getStmts().size()); i++ )
122         {
123            QueryStatement stmt = (QueryStatement) lazy.getStmts().get(i);
124            // First search for the table term of the subtable
125            Expression expr = stmt.getQueryExpression();
126            TableTerm subTableTerm = expr.getTableTerm(subTableName);
127            // Add this term to selected terms
128            stmt.getSelectTerms().add(new ReferenceTerm(subTableTerm,"container_index"));
129            // Add the correct order by
130            stmt.getOrderByList().add(0,new OrderBy(new ReferenceTerm(subTableTerm,"container_index"),OrderBy.ASCENDING));
131            // Correct static string
132            stmt.setStaticRepresentation(stmt.getStaticRepresentation()+" and index");
133         }
134      }
135   }
136 
137   public int preIndexing(Map session, int startIndex)
138   {
139      // Always use all statements
140      return 0;
141   }
142   
143   /**
144    * This method generates a sub-page select statement which will select
145    * the next items. If this is a linear iteration, then we already
146    * know the previous index to select from, if not, we have to
147    * select the list 'index' just before the page start.
148    */
149   public QueryStatement preSelect(Map session, QueryStatement stmt, List previousList, 
150         Limits limits, Limits pageLimits)
151   {
152      QueryStatement result = stmt;
153      if ( logger.isDebugEnabled() )
154         logger.debug("list impl pre select is running, limits is: "+limits+", page limits: "+pageLimits);
155      // Determining the index this query should start with.
156      Long lastIndex = (Long) session.get("lastIndex");
157      if ( (pageLimits.getOffset() > 0) && (lastIndex==null) )
158      {
159         // If the offset is greater than null, then we need
160         // to determine the last item's index from the previous
161         // page.
162         if ( previousList.size() > 0 )
163         {
164            // We're lucky, this is a linear iteration, so we can
165            // determine the last index by checking the last item
166            // in the previous page
167            lastIndex = (Long) ((Map) previousList.get(previousList.size()-1)).get("container_index");
168         } else {
169            if ( logger.isDebugEnabled() )
170               logger.debug("preselect is running pre-selecting query.");
171            // Well, no luck here. We must select the last index.
172            String subTableName = schemaManager.getTableName(
173                  parentInfo.getAttributeClassEntry(parentAttributeName),parentAttributeName);
174            TableTerm subTableTerm = new TableTerm(subTableName,null);
175            ArrayList selectTerms = new ArrayList();
176            selectTerms.add(new ReferenceTerm(subTableTerm,"container_index"));
177            Expression expr = new Expression();
178            expr.add(new ReferenceTerm(subTableTerm,"persistence_id"));
179            expr.add("=");
180            expr.add(new ConstantTerm(objectTracker.getIdentifier(parent)));
181            expr.add("and");
182            originalTimeControl.apply(expr,subTableTerm);
183            ArrayList orderBys = new ArrayList();
184            orderBys.add(new OrderBy(new ReferenceTerm(subTableTerm,"container_index"),OrderBy.ASCENDING));
185            QueryStatement indexStmt = new QueryStatement(selectTerms,expr,orderBys,null,null);
186            Transaction tx = transactionTracker.getTransaction(TransactionTracker.TX_REQUIRED);
187            tx.begin();
188            try
189            {
190               SearchResult qresult = database.search(tx,indexStmt,
191                     new Limits((int)(pageLimits.getOffset()-1),1,0));
192               lastIndex = (Long) ((Map) qresult.getResult().get(0)).get("container_index");
193            } finally {
194               tx.commit();
195            }
196         }
197         if ( logger.isDebugEnabled() )
198            logger.debug("pre select determined previous index to be: "+lastIndex);
199         session.put("lastIndex",lastIndex);
200      }
201      // Insert explicit conditions on the 'index' field, if the last index
202      // is known (because then we got to select only greater indexes).
203      // If the index is not given, the database will start from the lowest
204      // index automatically, because of the sql order by.
205      if ( lastIndex != null )
206      {
207         result = stmt.deepCopy();
208         result.setStaticRepresentation(result.getStaticRepresentation()+" and index > "+lastIndex);
209         // Get the index's reference term
210         ReferenceTerm indexTerm = ((OrderBy) result.getOrderByList().get(0)).getReferenceTerm();
211         // Insert new condition
212         Expression expr = result.getQueryExpression();
213         expr.add("and");
214         expr.add(indexTerm);
215         expr.add(">");
216         expr.add(new ConstantTerm(lastIndex));
217      }
218      // Offset is always 0, because we alter the select to include
219      // just the target items
220      limits.setOffset(0);
221      // Always get full result set, so limit is always on max. This
222      // is because we can not know in advance, whether the following
223      // sub-page contains items which by ordering belong to the
224      // final list or not. So we have to select all sub-pages
225      // with max count, and order and truncate them in the end.
226      limits.setLimit(pageLimits.getLimit());
227      // Return statement
228      return result;
229   }
230 
231   /**
232    * The result list is now unordered, because the result was assembled
233    * from possibly multiple query results. So we need to order by index.
234    */
235   public boolean postSelect(Map session, List list, Limits limits)
236   {
237      if ( logger.isDebugEnabled() )
238         logger.debug("post select running, size is: "+list.size()+", limits: "+limits);
239      // Order the result by index ascending
240      Collections.sort(list,new Comparator()
241            {
242               public int compare(Object o1, Object o2)
243               {
244                  long diff = ((Long) ((Map) o1).get("container_index")).longValue() -
245                         ((Long) ((Map) o2).get("container_index")).longValue();
246                  if ( diff < 0 )
247                     return -1;
248                  else if ( diff > 0 )
249                     return +1;
250                  return 0;
251               }
252            } );
253      // At this point, the list can be larger than specified by limits,
254      // because it is possibly assembled from multiple sub-pages which
255      // were all selected with batchsize. We simply truncate the list.
256      // The items at the end are sure to be _not_ part of the final
257      // result anyway.
258      if ( limits.getLimit() < list.size() )
259         list.subList((int)limits.getLimit(),list.size()).clear();
260      // Override always
261      return true;
262   }
263 
264   /**
265    * Returns whether the container changes internally since last save().
266    */
267 
268   public boolean hasChanged()
269   {
270      return (addedItems.size()>0) || (removedItems.size()>0) || (cleared);
271   }
272 
273   /**
274    * Save the container to database.
275    */
276   public void save(Transaction transaction, Long currentSerial, Set waitingObjects, 
277         Set saveTables, Set removeTables, List events)
278   {
279      ClassEntry attributeClassEntry = parentInfo.getAttributeClassEntry(parentAttributeName);
280      String attributeTableName = schemaManager.getTableName(attributeClassEntry,parentAttributeName);
281      // If the list was cleared before, then clear the current values
282      if ( cleared )
283      {
284         logger.debug("clearing list...");
285         HashMap keyAttributes = new HashMap();
286         keyAttributes.put("persistence_id",objectTracker.getIdentifier(parent));
287         keyAttributes.put("persistence_end",Serial.getMaxSerial().getValue());
288         keyAttributes.put("persistence_txend",Serial.getMaxSerial().getValue());
289         HashMap removeAttributes = new HashMap();
290         removeAttributes.put("persistence_txend",currentSerial);
291         removeAttributes.put("persistence_txendid",transaction.getSerial());
292         database.save(transaction, attributeTableName, keyAttributes,removeAttributes);
293         removeTables.add(attributeTableName);
294         // Notify listeners
295         events.add(new ClearedContainerEvent(parent,parentAttributeName));
296      }
297      // Remove removed items first
298      if ( logger.isDebugEnabled() )
299         logger.debug("removing "+removedItems.size()+" items from list...");
300      Iterator removedItemsIterator = removedItems.iterator();
301      while ( removedItemsIterator.hasNext() )
302      {
303         ObjectWrapper wrapper = (ObjectWrapper) removedItemsIterator.next();
304         Object obj = wrapper.getObject();
305         // Remove object from the list
306         HashMap keyAttributes = new HashMap();
307         keyAttributes.put("persistence_id",objectTracker.getIdentifier(parent));
308         keyAttributes.put("persistence_end",Serial.getMaxSerial().getValue());
309         keyAttributes.put("persistence_txend",Serial.getMaxSerial().getValue());
310         keyAttributes.put("container_index",new Long(wrapper.getOriginalIndex()));
311         HashMap removeAttributes = new HashMap();
312         removeAttributes.put("persistence_txend",currentSerial);
313         removeAttributes.put("persistence_txendid",transaction.getSerial());
314         database.save(transaction, attributeTableName, keyAttributes,removeAttributes);
315         removeTables.add(attributeTableName);
316         // Notify listeners
317         events.add(new RemovedItemEvent(parent,parentAttributeName,obj));
318      }
319      // Re-index list items. This only occurs, if an item addition
320      // would get the index of an already existing item. In this case, 
321      // all subsequent items are re-indexed. So find the first reindexing
322      // point
323      ChangeEntry reindexEntry = null;
324      int changeIndex = 0;
325      while ( (changeIndex<changes.size()) && (reindexEntry==null) )
326      {
327         ChangeEntry current = (ChangeEntry) changes.get(changeIndex);
328         if ( (current.getChange()==ChangeEntry.ADDED) && (current.getReindex()) )
329            reindexEntry = current;
330         changeIndex++;
331      }
332      if ( reindexEntry != null )
333      {
334         changeIndex--; // Back one step, if found
335         // Found a reindex entry
336         if ( logger.isDebugEnabled() )
337            logger.debug("re-indexing range, change: "+reindexEntry);
338         // All old items' indexes between this change and the next 
339         // are to be re-indexed. Get all the entries after the reindexing
340         // item.
341         TableTerm subTableTerm = new TableTerm(attributeTableName,null);
342         ArrayList selectTerms = new ArrayList();
343         selectTerms.add(new ReferenceTerm(subTableTerm,"container_index"));
344         selectTerms.add(new ReferenceTerm(subTableTerm,"value"));
345         Expression expr = new Expression();
346         expr.add(new ReferenceTerm(subTableTerm,"persistence_id"));
347         expr.add("=");
348         expr.add(new ConstantTerm(objectTracker.getIdentifier(parent)));
349         expr.add("and");
350         expr.add(new ReferenceTerm(subTableTerm,"container_index"));
351         expr.add(">=");
352         expr.add(new ConstantTerm(new Long(reindexEntry.getItem().getOriginalIndex())));
353         expr.add("and");
354         originalTimeControl.apply(expr,subTableTerm);
355         ArrayList orderBys = new ArrayList();
356         orderBys.add(new OrderBy(new ReferenceTerm(subTableTerm,"container_index"),OrderBy.ASCENDING));
357         QueryStatement stmt = new QueryStatement(selectTerms,expr,orderBys,null,null);
358         SearchResult result = database.search(transaction,stmt,null);
359         // Go through each item entry and modify it (first close the old
360         // one, and create a new entry with new index)
361         // Note, that because this is low-level, the paging mechanism
362         // is questionable. We depend here on the fetchsize parameter
363         // of the jdbc driver, so it is assumed, that large result sets
364         // can be read.
365         // The cycle reads the resultset, and the changeset in parallel,
366         // each is pre-read.
367         // Implementation note: On first iteration, the reindexEntry is
368         // always selected, because it has the lowest index.
369         long newIndex = reindexEntry.getItem().getOriginalIndex()+INTERVAL-1;
370         ChangeEntry currentEntry = reindexEntry;
371         int resultIndex = 0;
372         Map currentResult = null;
373         if ( resultIndex < result.getResult().size() )
374            currentResult = (Map) result.getResult().get(resultIndex);
375         while ( (currentResult!=null) || (currentEntry!=null) )
376         {
377            // Determine whether the next item is a change, or a database
378            // result.
379            if ( (currentResult!=null) && 
380                  ( (currentEntry==null) || 
381                    ( ((Long)currentResult.get("container_index")).longValue() 
382                      < currentEntry.getItem().getOriginalIndex() ) ))
383            {
384               // The database result entry has a smaller index than
385               // the change (if there is a change), this means, the
386               // next index belongs to this database entry.
387               Long index = (Long) currentResult.get("container_index");
388               Long value = (Long) currentResult.get("value");
389               // Close last occurence of index
390               HashMap keyAttributes = new HashMap();
391               keyAttributes.put("persistence_id",objectTracker.getIdentifier(parent));
392               keyAttributes.put("persistence_end",Serial.getMaxSerial().getValue());
393               keyAttributes.put("persistence_txend",Serial.getMaxSerial().getValue());
394               keyAttributes.put("container_index",index);
395               HashMap removeAttributes = new HashMap();
396               removeAttributes.put("persistence_txend",currentSerial);
397               removeAttributes.put("persistence_txendid",transaction.getSerial());
398               database.save(transaction, attributeTableName, keyAttributes,removeAttributes);
399               removeTables.add(attributeTableName);
400               // Insert new version
401               HashMap itemAttributes = new HashMap();
402               itemAttributes.put("persistence_id",objectTracker.getIdentifier(parent));
403               itemAttributes.put("persistence_start",Serial.getMaxSerial().getValue());
404               itemAttributes.put("persistence_end",Serial.getMaxSerial().getValue());
405               itemAttributes.put("persistence_txendid",new Long(0));
406               itemAttributes.put("persistence_txstartid",transaction.getSerial());
407               itemAttributes.put("persistence_txstart",currentSerial);
408               itemAttributes.put("persistence_txend",Serial.getMaxSerial().getValue());
409               itemAttributes.put("value",value);
410               itemAttributes.put("container_index",new Long(newIndex));
411               database.insert(transaction, attributeTableName, itemAttributes);
412               saveTables.add(attributeTableName);
413               // Pre-read the next result
414               resultIndex++;
415               if ( resultIndex < result.getResult().size() )
416                  currentResult = (Map) result.getResult().get(resultIndex);
417               else
418                  currentResult=null;
419            } else {
420               // This branch is active if an ADDED (yet non existing)
421               // item is next. In this case we must re-index the entry
422               // which will be already created with this new index later.
423               currentEntry.getItem().setOriginalIndex(newIndex);
424               // Pre-read next entry
425               changeIndex++;
426               while ( (changeIndex<changes.size()) &&
427                     ( ((ChangeEntry)changes.get(changeIndex)).getChange()!=ChangeEntry.ADDED) )
428                  changeIndex++;
429               if ( changeIndex < changes.size() )
430                  currentEntry = (ChangeEntry) changes.get(changeIndex);
431               else
432                  currentEntry=null;
433            }
434            // Increment index
435            newIndex += INTERVAL;
436         }
437      }
438      // Add added items
439      if ( logger.isDebugEnabled() )
440         logger.debug("adding "+addedItems.size()+" items to list...");
441      Iterator addedItemsIterator = addedItems.iterator();
442      while ( addedItemsIterator.hasNext() )
443      {
444         ObjectWrapper wrapper = (ObjectWrapper) addedItemsIterator.next();
445         Object obj = wrapper.getObject();
446         // Item does not exist then put it in the save list
447         if ( ! objectTracker.exists(obj) )
448            waitingObjects.add(objectTracker.getWrapper(obj));
449         // Add item
450         HashMap itemAttributes = new HashMap();
451         itemAttributes.put("persistence_id",objectTracker.getIdentifier(parent));
452         itemAttributes.put("persistence_start",Serial.getMaxSerial().getValue());
453         itemAttributes.put("persistence_end",Serial.getMaxSerial().getValue());
454         itemAttributes.put("persistence_txendid",new Long(0));
455         itemAttributes.put("persistence_txstartid",transaction.getSerial());
456         itemAttributes.put("persistence_txstart",currentSerial);
457         itemAttributes.put("persistence_txend",Serial.getMaxSerial().getValue());
458         itemAttributes.put("container_index",new Long(wrapper.getOriginalIndex()));
459         itemAttributes.put("value",objectTracker.getIdentifier(obj));
460         database.insert(transaction, attributeTableName, itemAttributes);
461         saveTables.add(attributeTableName);
462         // Notify listeners
463         events.add(new AddedItemEvent(parent,parentAttributeName,obj));
464      }
465      // Reload the changed list. Note, that the list should not
466      // be referenced until the Store.save() operation completes, because
467      // the list currently might contain nonexisting objects, which will
468      // be inserted _after_ this code.
469      originalTimeControl = new TimeControl(currentSerial,transaction.getSerial(),true);
470      lastSerial = currentSerial;
471   }
472 
473   /**
474    * Get the serial number of last modification.
475    */
476   public Long getLastSerial()
477   {
478      return lastSerial;
479   }
480 
481   public boolean retainAll(Object c)
482   {
483      return retainAll( (Collection) c );
484   }
485 
486   public boolean addAll(Object c)
487   {
488      return addAll( (Collection) c );
489   }
490 
491   /*
492    * List implementation.
493    */
494   
495   /**
496    * Add modification to this list.
497    */
498   private void addChange(int index, int change, ObjectWrapper item, boolean reindex)
499   {
500      // Search for the correct entry. The correct entry is the
501      // one which is concerned with at least one index higher item.
502      int topIndex = 0;
503      int prevTopIndex = 0;
504      int changesIndex = 0;
505      ChangeEntry nextEntry = null;
506      while ( (topIndex <= index) && (changesIndex<changes.size()) )
507      {
508         nextEntry = (ChangeEntry) changes.get(changesIndex);
509         changesIndex++;
510         // Determine the index of interest of this entry
511         prevTopIndex = topIndex;
512         topIndex+=nextEntry.getOffset();
513         if ( nextEntry.getChange() == ChangeEntry.ADDED )
514            topIndex++;
515      }
516      // Add modification to changes list
517      ChangeEntry previousEntry = null;
518      if ( changesIndex > 1 )
519         previousEntry = (ChangeEntry) changes.get(changesIndex-2);
520      ChangeEntry newEntry = null;
521      if ( topIndex <= index )
522      {
523         // This means, that there is no next entry, this is the last.
524         newEntry = new ChangeEntry(index-topIndex,change,item,reindex);
525         nextEntry = null;
526      } else {
527         // There are changes after this index, so adjust offsets.
528         newEntry = new ChangeEntry(index-prevTopIndex,change,item,reindex);
529         nextEntry.setOffset(nextEntry.getOffset()-(index-prevTopIndex));
530         changesIndex--;
531      }
532      if ( (newEntry.getOffset()==0) && (newEntry.getChange()==ChangeEntry.REMOVED) &&
533            (previousEntry!=null) && (previousEntry.getChange()==ChangeEntry.ADDED) )
534      {
535         // The new entry is a REMOVED entry, and the previous one is an
536         // ADDED on the same index. This means, the added item was removed
537         // even before save, so we remove both.
538         changes.remove(changesIndex-2);
539         if ( nextEntry != null )
540         {
541            // We must re-calculate the next entry's offset, with
542            // it's previous 2 entries removed
543            nextEntry.setOffset(nextEntry.getOffset()+newEntry.getOffset()+previousEntry.getOffset());
544         }
545      } else {
546         // The new entry must be added
547         changes.add(changesIndex,newEntry);
548         // With the new entry, the indexes after this change changed, so
549         // adjust
550         int diff = 0;
551         if ( newEntry.getChange() == ChangeEntry.ADDED )
552            diff = +1;
553         else
554            diff = -1;
555         for ( int i=changesIndex+1; i<changes.size(); i++ )
556         {
557            ObjectWrapper ow = ((ChangeEntry) changes.get(i)).getItem();
558            ow.setIndex(item.getIndex()+1);
559         }
560      }
561   }
562 
563   public void add(int index, Object item)
564   {
565      if ( logger.isDebugEnabled() )
566         logger.debug("list impl adding item at index: "+index+", item: "+item);
567      // Check item validity
568      if ( item == null )
569         throw new IllegalArgumentException("list does not accept null values.");
570      ClassTracker.ClassType type = classTracker.getType(item.getClass());
571      if ( (type != ClassTracker.ClassType.TYPE_OBJECT) && (type != ClassTracker.ClassType.TYPE_PRIMITIVE) )
572         throw new IllegalArgumentException("list only handles object or primitive types, but was: "+item+" ("+item.getClass().getName()+")");
573      // Determine whether it is in the removed list
574      ObjectWrapper wrapper = new ObjectWrapper(index,item);
575      boolean reindex = false;
576      if ( removedItems.contains(wrapper) )
577      {
578         // It was removed once, so remove remove entry
579         removedItems.remove(wrapper);
580      } else {
581         // Also, calculate insert index, aka. 'original index'.
582         // Algorithm: get the previous item, and the one we're
583         // inserting at, if both exist, calculate the middle.
584         // If either side do not exist then grow with INTERVAL.
585         ObjectWrapper prev = null;
586         if ( index > 0 )
587            prev = getInternal(index-1);
588         ObjectWrapper under = null;
589         if ( index < size() ) // Here we must calculate size, shame
590            under = getInternal(index);
591         if ( (prev!=null) && (under!=null) )
592         {
593            // This means, there is a previous index and a next one,
594            // try to get between them if possible.
595            if ( under.getOriginalIndex()-prev.getOriginalIndex() < 2 )
596            {
597               // No place between them, so we must give the new entry
598               // the index of 'under', and mark the change for reindexing.
599               wrapper.setOriginalIndex(under.getOriginalIndex());
600               reindex=true;
601            } else {
602               // We have luck, we don't have to reindex the whole
603               // list.
604               wrapper.setOriginalIndex( (under.getOriginalIndex()+prev.getOriginalIndex())/2 );
605            }
606         } else if ( (prev==null) && (under!=null) ) {
607            // This means we insert at the beginning (no previous item)
608            if ( Long.MIN_VALUE + INTERVAL > under.getOriginalIndex() )
609               throw new StoreException("sorry, list ran out of useful indexes on the beginning, the first index is: "+under.getOriginalIndex());
610            wrapper.setOriginalIndex( under.getOriginalIndex() - INTERVAL );
611         } else if ( (prev!=null) && (under==null) ) {
612            // The insert occured at the end
613            if ( Long.MAX_VALUE - INTERVAL < prev.getOriginalIndex() )
614               throw new StoreException("sorry, list ran out of useful indexes on the end, the last index is: "+prev.getOriginalIndex());
615            wrapper.setOriginalIndex( prev.getOriginalIndex() + INTERVAL );
616         } else {
617            // There are no items in the list yet, start at 0
618            wrapper.setOriginalIndex(0);
619         }
620         // Add the item
621         addedItems.add(wrapper);  
622      }
623      // Ensure item exists
624      if ( logger.isDebugEnabled() )
625         logger.debug("adding list item to list: "+index+", object: "+item);
626      itemClass.updateItemClassName(originalList,item.getClass(),addedItems.size()==0);
627      addChange(index,ChangeEntry.ADDED,wrapper,reindex);
628      modCount++;
629   }
630 
631   /**
632    * Clear all entries from the list.
633    */
634   public void clear()
635   {
636      // Clear content and mark cleared flag
637      cleared = true;
638      addedItems = new LinkedList();
639      removedItems = new LinkedList();
640      originalList = new ArrayList();
641      changes = new ArrayList();
642      itemClass.clear();
643      modCount++;
644   }
645 
646   private Long searchInternal(Object item, ArrayList order)
647   {
648      if ( item == null )
649         return null;
650      ClassTracker.ClassType type = classTracker.getType(item.getClass());
651      Long id = objectTracker.getIdentifier(item);
652      Transaction tx = transactionTracker.getTransaction(TransactionTracker.TX_REQUIRED);
653      tx.begin();
654      try
655      {
656         ClassInfo itemInfo = classTracker.getClassInfo(item.getClass(),item);
657         String itemTableName = schemaManager.getTableName(itemInfo.getSourceEntry());
658         TableTerm itemTableTerm = new TableTerm(itemTableName,null);
659         String listTableName = schemaManager.getTableName(
660               parentInfo.getAttributeClassEntry(parentAttributeName),parentAttributeName);
661         TableTerm listTableTerm = new TableTerm(listTableName,null);
662         Expression listExpression = new Expression();
663         listExpression.add(new ReferenceTerm(listTableTerm,"persistence_id"));
664         listExpression.add("=");
665         listExpression.add(new ConstantTerm(objectTracker.getIdentifier(parent)));
666         listExpression.add("and");
667         if ( type != ClassTracker.ClassType.TYPE_PRIMITIVE )
668         {
669            listExpression.add(new ReferenceTerm(listTableTerm,"value"));
670            listExpression.add("=");
671            listExpression.add(new ConstantTerm(id));
672         } else {
673            listExpression.add(new ReferenceTerm(itemTableTerm,"value"));
674            listExpression.add("=");
675            listExpression.add(new ConstantTerm(item));
676         }
677         listExpression.add("and");
678         listExpression.add(new ReferenceTerm(itemTableTerm,"persistence_id"));
679         listExpression.add("=");
680         listExpression.add(new ReferenceTerm(listTableTerm,"value"));
681         listExpression.add("and");
682         originalTimeControl.apply(listExpression,itemTableTerm);
683         listExpression.add("and");
684         originalTimeControl.apply(listExpression,listTableTerm);
685         // Execute. If there is a hit, then object exists
686         QueryStatement stmt = new QueryStatement(listTableTerm,listExpression,order);
687         stmt.getSpecifiedTerms().add(new SpecifiedTableTerm(itemTableTerm));
688         stmt.setTimeControl(originalTimeControl);
689         stmt.setStaticRepresentation("FIND "+listTableTerm+" WHERE persistence_id = "+
690               objectTracker.getIdentifier(parent)+" and value = "+id+", order="+order);
691         SearchResult result = queryService.find(stmt,new Limits(0,1,0));
692         // Check
693         if ( result.getResult().size() > 0 )
694         {
695            Long index = (Long) ((Map)result.getResult().get(0)).get("container_index");
696            if ( logger.isDebugEnabled() )
697               logger.debug("search internal result index is: "+index+", for item: "+item);
698            return index;
699         }
700      } finally {
701         tx.commit();
702      }
703         if ( logger.isDebugEnabled() )
704            logger.debug("search internal did not found the item given: "+item);
705      return null;
706   }
707 
708   public boolean contains(Object item)
709   {
710      if ( item == null )
711         return false;
712      ClassTracker.ClassType type = classTracker.getType(item.getClass());
713      // Check the list
714      Long id = objectTracker.getIdentifier(item);
715      ObjectWrapper wrapper = new ObjectWrapper(item);
716      // If it is added, then it is contained.
717      if ( addedItems.contains(wrapper) )
718         return true;
719      // Check the list
720      if ( (originalList instanceof LazyList) && (!((LazyList) originalList).isIterationCheap()) )
721      {
722         // This means, that the backing lazy list would page if we
723         // were to iterate. So instead, we run a specific query for
724         // the given id.
725         Long index = searchInternal(item,null);
726         return index != null;
727      } else {
728         // Set is small, so iterate
729         for ( int i=0; i<originalList.size(); i++ )
730         {
731            Object obj = ((Map)originalList.get(i)).get("object");
732            if ( ((type==ClassTracker.ClassType.TYPE_PRIMITIVE) && (item.equals(obj))) ||
733                 ((type!=ClassTracker.ClassType.TYPE_PRIMITIVE) && (objectTracker.getIdentifier(obj).equals(id))) )
734               return true;
735         }
736      }
737      // Fall through
738      return false;
739   }
740 
741   /**
742    * This hash code invokes the parent, which is ok becaue the equals method
743    * does the same thing as the superclass', just more efficiently.
744    */
745   public int hashCode()
746   {
747      return super.hashCode();
748   }
749 
750   /**
751    * Determine whether list equals another list in content.
752    */
753   public boolean equals(Object obj)
754   {
755      // If it is not a collection, then it surely does not equal.
756      if ( ! (obj instanceof List) )
757         return false;
758      if ( obj == this ) 
759         return true;  // They equals if they are the same object
760      List list = (List) obj;
761      if ( list.size() != size() )
762         return false;  // They don't equals if size differs
763      // They only equals if the items equals one-by-one
764      Iterator oneIterator = iterator();
765      Iterator twoIterator = list.iterator();
766      while ( oneIterator.hasNext() && twoIterator.hasNext() )
767      {
768         Object one = oneIterator.next();
769         Object two = twoIterator.next();
770         if ( ! objectTracker.getIdentifier(one).equals(
771               objectTracker.getIdentifier(two)) )
772            return false; // The two items do not equal
773      }
774      return true; // All items were equal
775   }
776 
777   public Object get(int index)
778   {
779      ObjectWrapper wrapper = getInternal(index);
780      if ( wrapper == null )
781         return null;
782      return wrapper.getObject();
783   }
784   
785   private ObjectWrapper getInternal(int index)
786   {
787      try
788      {
789         // Search for the correct entry.
790         int topIndex = 0;
791         int lastTopIndex = 0;
792         int lastBottomIndex = 0;
793         int bottomIndex = 0;
794         int changesIndex = 0;
795         ChangeEntry exactEntry = null;
796         while ( (topIndex <= index) && (changesIndex<changes.size()) )
797         {
798            ChangeEntry nextEntry = (ChangeEntry) changes.get(changesIndex);
799            changesIndex++;
800            // Determine the index of interest of this entry
801            lastTopIndex = topIndex;
802            lastBottomIndex = bottomIndex;
803            topIndex+=nextEntry.getOffset();
804            bottomIndex+=nextEntry.getOffset();
805            // If this entry is the exact index, then remember
806            if ( topIndex == index )
807               exactEntry = nextEntry;
808            // Modify top and bottom indexes
809            if ( nextEntry.getChange() == ChangeEntry.ADDED )
810               topIndex++;
811            else
812               bottomIndex++;
813         }
814         if ( topIndex <= index )
815            bottomIndex=bottomIndex+(index-topIndex);
816         else
817            bottomIndex=lastBottomIndex+(index-lastTopIndex);
818         // If the last exact match was an ADD, then that is the
819         // result, else the backing list is used.
820         if ( (exactEntry!=null) && (exactEntry.getChange()==ChangeEntry.ADDED) )
821         {
822            return exactEntry.getItem();
823         } else {
824            Object obj = ((Map)originalList.get(bottomIndex)).get("object");
825            ObjectWrapper result = new ObjectWrapper(index,obj);
826            result.setOriginalIndex(((Long)((Map)originalList.get(bottomIndex)).get("container_index")).longValue());
827            return result;
828         }
829      } catch ( IndexOutOfBoundsException e ) {
830         logger.error("array index out-of-bound",e);
831         throw e;
832      }
833   }
834 
835   public int indexOf(Object item)
836   {
837      if ( item == null )
838         return -1;
839      ClassTracker.ClassType type = classTracker.getType(item.getClass());
840      Long itemId = objectTracker.getIdentifier(item);
841      if ( (itemId == null) && (type!=ClassTracker.ClassType.TYPE_PRIMITIVE) )
842         return -1; // Object does not have identifier, it is not contained.
843      if ( (originalList instanceof LazyList) && (!((LazyList) originalList).isIterationCheap()) )
844      {
845         // The list is large, do a select to determine index
846         Transaction tx = transactionTracker.getTransaction(TransactionTracker.TX_REQUIRED);
847         tx.begin();
848         try
849         {
850            String listTableName = schemaManager.getTableName(
851                  parentInfo.getAttributeClassEntry(parentAttributeName),parentAttributeName);
852            TableTerm listTableTerm = new TableTerm(listTableName,null);
853            ArrayList order = new ArrayList();
854            order.add(new OrderBy(new ReferenceTerm(listTableTerm,"container_index"),OrderBy.ASCENDING));
855            Long oldIndex = searchInternal(item,order);
856            // Check
857            if ( oldIndex != null )
858            {
859               // Determine how many indexes are before this index
860               Expression listExpression = new Expression();
861               listExpression.add(new ReferenceTerm(listTableTerm,"persistence_id"));
862               listExpression.add("=");
863               listExpression.add(new ConstantTerm(objectTracker.getIdentifier(parent)));
864               listExpression.add("and");
865               listExpression.add(new ReferenceTerm(listTableTerm,"container_index"));
866               listExpression.add("<");
867               listExpression.add(new ConstantTerm(oldIndex));
868               listExpression.add("and");
869               originalTimeControl.apply(listExpression,listTableTerm);
870               QueryStatement stmt = new QueryStatement(listTableTerm,listExpression,null);
871               stmt.setTimeControl(originalTimeControl);
872               stmt.setStaticRepresentation("FIND "+listTableTerm+" WHERE persistence_id = "+
873                     objectTracker.getIdentifier(parent)+" and container_index < "+oldIndex);
874               SearchResult result = queryService.find(stmt,new Limits(0,0,-1));
875               return (int) result.getResultSize();
876            }
877         } finally {
878            tx.commit();
879         }
880      } else {
881         // Iterate and find the object
882         for ( int i=0; i<size(); i++ )
883         {
884            Object obj = get(i);
885            if ( ((type==ClassTracker.ClassType.TYPE_PRIMITIVE) && (item.equals(obj))) ||
886                 ((type!=ClassTracker.ClassType.TYPE_PRIMITIVE) && (objectTracker.getIdentifier(obj).equals(itemId))) )
887               return i;
888         }
889      }
890      return -1;
891   }
892 
893   public int lastIndexOf(Object item)
894   {
895      if ( item == null )
896         return -1;
897      ClassTracker.ClassType type = classTracker.getType(item.getClass());
898      Long itemId = objectTracker.getIdentifier(item);
899      if ( (itemId == null) && (type!=ClassTracker.ClassType.TYPE_PRIMITIVE) )
900         return -1; // Object does not have identifier, it is not contained.
901      if ( (originalList instanceof LazyList) && (!((LazyList) originalList).isIterationCheap()) )
902      {
903         // The list is large, do a select to determine index
904         Transaction tx = transactionTracker.getTransaction(TransactionTracker.TX_REQUIRED);
905         tx.begin();
906         try
907         {
908            String listTableName = schemaManager.getTableName(
909                  parentInfo.getAttributeClassEntry(parentAttributeName),parentAttributeName);
910            TableTerm listTableTerm = new TableTerm(listTableName,null);
911            ArrayList order = new ArrayList();
912            order.add(new OrderBy(new ReferenceTerm(listTableTerm,"container_index"),OrderBy.DESCENDING));
913            Long oldIndex = searchInternal(item,order);
914            // Check
915            if ( oldIndex != null )
916            {
917               // Determine how many indexes are before this index
918               Expression listExpression = new Expression();
919               listExpression.add(new ReferenceTerm(listTableTerm,"persistence_id"));
920               listExpression.add("=");
921               listExpression.add(new ConstantTerm(objectTracker.getIdentifier(parent)));
922               listExpression.add("and");
923               listExpression.add(new ReferenceTerm(listTableTerm,"container_index"));
924               listExpression.add("<");
925               listExpression.add(new ConstantTerm(oldIndex));
926               listExpression.add("and");
927               originalTimeControl.apply(listExpression,listTableTerm);
928               QueryStatement stmt = new QueryStatement(listTableTerm,listExpression,null);
929               stmt.setTimeControl(originalTimeControl);
930               stmt.setStaticRepresentation("FIND "+listTableTerm+" WHERE persistence_id = "+
931                     objectTracker.getIdentifier(parent)+" and container_index < "+oldIndex);
932               SearchResult result = queryService.find(stmt,new Limits(0,0,-1));
933               return (int) result.getResultSize();
934            }
935         } finally {
936            tx.commit();
937         }
938      } else {
939         // Iterate and find the object
940         for ( int i=size(); i>0; i++ )
941         {
942            Object obj = get(i-1);
943            if ( ((type==ClassTracker.ClassType.TYPE_PRIMITIVE) && (item.equals(obj))) ||
944                 ((type!=ClassTracker.ClassType.TYPE_PRIMITIVE) && (objectTracker.getIdentifier(obj).equals(itemId))) )
945               return i-1;
946         }
947      }
948      return -1;
949   }
950 
951   public Object remove(int index)
952   {
953      // Get the object at the given index
954      ObjectWrapper wrapper = getInternal(index);
955      // Object is present, determine, whether it is in the added list
956      if ( addedItems.contains(wrapper) )
957      {
958         // It was added, so remove added entry
959         addedItems.remove(wrapper);
960      } else {
961         // It was not yet added, so add
962         removedItems.add(wrapper);
963      }
964      // Insert modification entry
965      if ( logger.isDebugEnabled() )
966         logger.debug("removed index: "+index+", object: "+wrapper.getObject());
967      addChange(index,ChangeEntry.REMOVED,wrapper,false);
968      modCount++;
969      return wrapper.getObject();
970   }
971 
972   public boolean remove(Object item)
973   {
974      int index = indexOf(item);
975      if ( index == -1 )
976         return false;
977      remove(index);
978      return true;
979   }
980 
981   public Object set(int index, Object item)
982   {
983      // Simply remove first, then add to the same position
984      Object oldItem = remove(index);
985      add(index,item);
986      return oldItem;
987   }
988 
989   public int size()
990   {
991      return originalList.size() - removedItems.size() + addedItems.size();
992   }
993   
994   public String toString()
995   {
996      return originalList.toString();
997   }
998 
999   private class ChangeEntry
1000   {
1001      public static final int ADDED = 1;
1002      public static final int REMOVED = 2;
1003      
1004      private int offset;
1005      private int change;
1006      private boolean reindex;
1007      private ObjectWrapper item;
1008 
1009      public ChangeEntry(int offset, int change, ObjectWrapper item, boolean reindex)
1010      {
1011         this.offset=offset;
1012         this.change=change;
1013         this.item=item;
1014         this.reindex=reindex;
1015      }
1016 
1017      public boolean getReindex()
1018      {
1019         return reindex;
1020      }
1021      
1022      public String toString()
1023      {
1024         return "[Change: "+change+", offset: "+offset+", item: "+item+"]";
1025      }
1026 
1027      public int getOffset()
1028      {
1029         return offset;
1030      }
1031      public void setOffset(int offset)
1032      {
1033         this.offset=offset;
1034      }
1035 
1036      public int getChange()
1037      {
1038         return change;
1039      }
1040      public void setChange(int change)
1041      {
1042         this.change=change;
1043      }
1044 
1045      public ObjectWrapper getItem()
1046      {
1047         return item;
1048      }
1049      public void setItem(ObjectWrapper item)
1050      {
1051         this.item=item;
1052      }
1053   }
1054   
1055   public class ObjectWrapper
1056   {
1057      private Object obj;
1058      private Long id;
1059      private long originalIndex;
1060      private int index;
1061      private boolean indexGiven;
1062      private ClassTracker.ClassType type;
1063 
1064      public ObjectWrapper(int index, Object obj)
1065      {
1066         this.indexGiven=true;
1067         this.index=index;
1068         this.obj=obj;
1069         objectTracker.registerObject(obj);
1070         this.id=objectTracker.getIdentifier(obj);
1071         this.type=classTracker.getType(obj.getClass());
1072      }
1073 
1074      public ObjectWrapper(Object obj)
1075      {
1076         this.indexGiven=false;
1077         this.obj=obj;
1078         objectTracker.registerObject(obj);
1079         this.id=objectTracker.getIdentifier(obj);
1080         this.type=classTracker.getType(obj.getClass());
1081      }
1082 
1083      public int getIndex()
1084      {
1085         return index;
1086      }
1087 
1088      public void setIndex(int index)
1089      {
1090         this.index=index;
1091      }
1092 
1093      public Long getIdentifier()
1094      {
1095         return id;
1096      }
1097 
1098      public Object getObject()
1099      {
1100         return obj;
1101      }
1102 
1103      public int hashCode()
1104      {
1105         if ( type == ClassTracker.ClassType.TYPE_PRIMITIVE )
1106            return obj.hashCode();
1107         else
1108            return (int) (id.longValue()>>INT_BITS);
1109      }
1110 
1111      public boolean equals(Object rhs)
1112      {
1113         if ( ! (rhs instanceof ObjectWrapper) )
1114            return false;
1115         if ( type == ClassTracker.ClassType.TYPE_PRIMITIVE )
1116         {
1117            if ( ! obj.equals( ((ObjectWrapper)rhs).obj ) )
1118               return false;
1119         } else {
1120            if ( ! id.equals(((ObjectWrapper) rhs).id) )
1121               return false;
1122         }
1123         if ( indexGiven )
1124            return index == ((ObjectWrapper) rhs).index;
1125         return true;
1126      }
1127 
1128      public long getOriginalIndex()
1129      {
1130         return originalIndex;
1131      }
1132      public void setOriginalIndex(long originalIndex)
1133      {
1134         this.originalIndex=originalIndex;
1135      }
1136 
1137   }
1138}
1139 

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