Source Code Cross Referenced for ListImpl.java in  » Database-ORM » beankeeper » hu » netmind » persistence » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Database ORM » beankeeper » hu.netmind.persistence 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /**
0002:         * Copyright (C) 2007 NetMind Consulting Bt.
0003:         *
0004:         * This library is free software; you can redistribute it and/or
0005:         * modify it under the terms of the GNU Lesser General Public
0006:         * License as published by the Free Software Foundation; either
0007:         * version 3 of the License, or (at your option) any later version.
0008:         *
0009:         * This library is distributed in the hope that it will be useful,
0010:         * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011:         * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012:         * Lesser General Public License for more details.
0013:         *
0014:         * You should have received a copy of the GNU Lesser General Public
0015:         * License along with this library; if not, write to the Free Software
0016:         * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
0017:         */package hu.netmind.persistence;
0018:
0019:        import java.util.*;
0020:        import hu.netmind.persistence.parser.*;
0021:        import hu.netmind.persistence.event.*;
0022:        import org.apache.log4j.Logger;
0023:
0024:        /**
0025:         * Custom list implementation based on lazy lists. List is <strong>not</strong>
0026:         * thread-safe.
0027:         * @author Brautigam Robert
0028:         * @version Revision: $Revision$
0029:         */
0030:        public class ListImpl extends AbstractList implements  Container,
0031:                LazyListHooks {
0032:            private static Logger logger = Logger.getLogger(ListImpl.class);
0033:            private static final long INTERVAL = Integer.MAX_VALUE;
0034:
0035:            private StoreContext context;
0036:            private List originalList;
0037:            private TimeControl originalTimeControl;
0038:            private ClassInfo parentInfo;
0039:            private Object parent;
0040:            private String parentAttributeName;
0041:            private Long lastSerial;
0042:            private ContainerItemClass itemClass;
0043:
0044:            private Vector changes; // Change entries of index layout
0045:            private LinkedList addedItems;
0046:            private LinkedList removedItems;
0047:            private boolean cleared = false;
0048:
0049:            /**
0050:             * Initialize with a default list.
0051:             */
0052:            public void init(StoreContext context, ClassInfo classInfo,
0053:                    Object obj, String attributeName, String itemClassName,
0054:                    Long lastSerial, TimeControl timeControl) {
0055:                this .context = context;
0056:                this .originalList = null;
0057:                this .originalTimeControl = new TimeControl(timeControl);
0058:                this .parentInfo = classInfo;
0059:                this .parent = obj;
0060:                this .parentAttributeName = attributeName;
0061:                this .lastSerial = lastSerial;
0062:                this .itemClass = new ContainerItemClass(itemClassName);
0063:                // Model
0064:                reload();
0065:            }
0066:
0067:            public String getItemClassName() {
0068:                return itemClass.getItemClassName();
0069:            }
0070:
0071:            public void reload() {
0072:                // Clear model
0073:                modCount++;
0074:                changes = new Vector();
0075:                addedItems = new LinkedList();
0076:                removedItems = new LinkedList();
0077:                cleared = false;
0078:                // Reload list
0079:                if (getItemClassName() == null) {
0080:                    originalList = new Vector();
0081:                } else {
0082:                    originalList = context.getStore().find(
0083:                            "find item(" + getItemClassName() + ")"
0084:                                    + " where parent("
0085:                                    + parentInfo.getSourceEntry().getFullName()
0086:                                    + ")" + "." + parentAttributeName
0087:                                    + " contains item and parent = ?",
0088:                            new Object[] { parent }, originalTimeControl, null);
0089:                    // Modify the select statements.
0090:                    String subTableName = parentInfo
0091:                            .getSubTableName(parentAttributeName);
0092:                    LazyList lazy = (LazyList) originalList;
0093:                    lazy.setHooks(this );
0094:                    for (int i = 0; i < lazy.getStmts().size(); i++) {
0095:                        QueryStatement stmt = (QueryStatement) lazy.getStmts()
0096:                                .get(i);
0097:                        // First search for the table term of the subtable
0098:                        Expression expr = stmt.getQueryExpression();
0099:                        TableTerm subTableTerm = expr
0100:                                .getTableTerm(subTableName);
0101:                        // Add this term to selected terms
0102:                        stmt.getSelectTerms().add(
0103:                                new ReferenceTerm(subTableTerm,
0104:                                        "container_index"));
0105:                        // Add the correct order by
0106:                        stmt.getOrderByList().add(
0107:                                0,
0108:                                new OrderBy(new ReferenceTerm(subTableTerm,
0109:                                        "container_index"), OrderBy.ASCENDING));
0110:                        // Correct static string
0111:                        stmt.setStaticRepresentation(stmt
0112:                                .getStaticRepresentation()
0113:                                + " and index");
0114:                    }
0115:                }
0116:            }
0117:
0118:            public int preIndexing(Map session, int startIndex) {
0119:                // Always use all statements
0120:                return 0;
0121:            }
0122:
0123:            /**
0124:             * This method generates a sub-page select statement which will select
0125:             * the next items. If this is a linear iteration, then we already
0126:             * know the previous index to select from, if not, we have to
0127:             * select the list 'index' just before the page start.
0128:             */
0129:            public QueryStatement preSelect(Map session, QueryStatement stmt,
0130:                    List previousList, Limits limits, Limits pageLimits) {
0131:                QueryStatement result = stmt;
0132:                if (logger.isDebugEnabled())
0133:                    logger.debug("list impl pre select is running, limits is: "
0134:                            + limits + ", page limits: " + pageLimits);
0135:                // Determining the index this query should start with.
0136:                Long lastIndex = (Long) session.get("lastIndex");
0137:                if ((pageLimits.getOffset() > 0) && (lastIndex == null)) {
0138:                    // If the offset is greater than null, then we need
0139:                    // to determine the last item's index from the previous
0140:                    // page.
0141:                    if (previousList.size() > 0) {
0142:                        // We're lucky, this is a linear iteration, so we can
0143:                        // determine the last index by checking the last item
0144:                        // in the previous page
0145:                        lastIndex = (Long) ((Map) previousList.get(previousList
0146:                                .size() - 1)).get("container_index");
0147:                    } else {
0148:                        if (logger.isDebugEnabled())
0149:                            logger
0150:                                    .debug("preselect is running pre-selecting query.");
0151:                        // Well, no luck here. We must select the last index.
0152:                        String subTableName = parentInfo
0153:                                .getSubTableName(parentAttributeName);
0154:                        TableTerm subTableTerm = new TableTerm(subTableName,
0155:                                null);
0156:                        Vector selectTerms = new Vector();
0157:                        selectTerms.add(new ReferenceTerm(subTableTerm,
0158:                                "container_index"));
0159:                        Expression expr = new Expression();
0160:                        expr.add(new ReferenceTerm(subTableTerm,
0161:                                "persistence_id"));
0162:                        expr.add("=");
0163:                        expr.add(new ConstantTerm(new Long(context
0164:                                .getObjectTracker().getIdentifier(parent))));
0165:                        expr.add("and");
0166:                        originalTimeControl.apply(expr, subTableTerm);
0167:                        Vector orderBys = new Vector();
0168:                        orderBys.add(new OrderBy(new ReferenceTerm(
0169:                                subTableTerm, "container_index"),
0170:                                OrderBy.ASCENDING));
0171:                        QueryStatement indexStmt = new QueryStatement(
0172:                                selectTerms, expr, orderBys);
0173:                        Transaction tx = context.getTransactionTracker()
0174:                                .getTransaction(TransactionTracker.TX_REQUIRED);
0175:                        tx.begin();
0176:                        try {
0177:                            SearchResult qresult = context.getDatabase()
0178:                                    .search(
0179:                                            tx,
0180:                                            indexStmt,
0181:                                            new Limits((int) (pageLimits
0182:                                                    .getOffset() - 1), 1, 0));
0183:                            lastIndex = (Long) ((Map) qresult.getResult()
0184:                                    .get(0)).get("container_index");
0185:                        } finally {
0186:                            tx.commit();
0187:                        }
0188:                    }
0189:                    if (logger.isDebugEnabled())
0190:                        logger
0191:                                .debug("pre select determined previous index to be: "
0192:                                        + lastIndex);
0193:                    session.put("lastIndex", lastIndex);
0194:                }
0195:                // Insert explicit conditions on the 'index' field, if the last index
0196:                // is known (because then we got to select only greater indexes).
0197:                // If the index is not given, the database will start from the lowest
0198:                // index automatically, because of the sql order by.
0199:                if (lastIndex != null) {
0200:                    result = stmt.deepCopy();
0201:                    result.setStaticRepresentation(result
0202:                            .getStaticRepresentation()
0203:                            + " and index > " + lastIndex);
0204:                    // Get the index's reference term
0205:                    ReferenceTerm indexTerm = ((OrderBy) result
0206:                            .getOrderByList().get(0)).getReferenceTerm();
0207:                    // Insert new condition
0208:                    Expression expr = result.getQueryExpression();
0209:                    expr.add("and");
0210:                    expr.add(indexTerm);
0211:                    expr.add(">");
0212:                    expr.add(new ConstantTerm(lastIndex));
0213:                }
0214:                // Offset is always 0, because we alter the select to include
0215:                // just the target items
0216:                limits.setOffset(0);
0217:                // Always get full result set, so limit is always on max. This
0218:                // is because we can not know in advance, whether the following
0219:                // sub-page contains items which by ordering belong to the
0220:                // final list or not. So we have to select all sub-pages
0221:                // with max count, and order and truncate them in the end.
0222:                limits.setLimit(pageLimits.getLimit());
0223:                // Return statement
0224:                return result;
0225:            }
0226:
0227:            /**
0228:             * The result list is now unordered, because the result was assembled
0229:             * from possibly multiple query results. So we need to order by index.
0230:             */
0231:            public boolean postSelect(Map session, List list, Limits limits) {
0232:                if (logger.isDebugEnabled())
0233:                    logger.debug("post select running, size is: " + list.size()
0234:                            + ", limits: " + limits);
0235:                // Order the result by index ascending
0236:                Collections.sort(list, new Comparator() {
0237:                    public int compare(Object o1, Object o2) {
0238:                        long diff = ((Long) ((Map) o1).get("container_index"))
0239:                                .longValue()
0240:                                - ((Long) ((Map) o2).get("container_index"))
0241:                                        .longValue();
0242:                        if (diff < 0)
0243:                            return -1;
0244:                        else if (diff > 0)
0245:                            return +1;
0246:                        return 0;
0247:                    }
0248:                });
0249:                // At this point, the list can be larger than specified by limits,
0250:                // because it is possibly assembled from multiple sub-pages which
0251:                // were all selected with batchsize. We simply truncate the list.
0252:                // The items at the end are sure to be _not_ part of the final
0253:                // result anyway.
0254:                if (limits.getLimit() < list.size())
0255:                    list.subList((int) limits.getLimit(), list.size()).clear();
0256:                // Override always
0257:                return true;
0258:            }
0259:
0260:            /**
0261:             * Returns whether the container changes internally since last save().
0262:             */
0263:
0264:            public boolean hasChanged() {
0265:                return (addedItems.size() > 0) || (removedItems.size() > 0)
0266:                        || (cleared);
0267:            }
0268:
0269:            /**
0270:             * Save the container to database.
0271:             */
0272:            public void save(Transaction transaction, Long currentSerial,
0273:                    Set waitingObjects, List events) {
0274:                // If the list was cleared before, then clear the current values
0275:                if (cleared) {
0276:                    logger.debug("clearing list...");
0277:                    HashMap keyAttributes = new HashMap();
0278:                    keyAttributes.put("persistence_id", new Long(context
0279:                            .getObjectTracker().getIdentifier(parent)));
0280:                    keyAttributes.put("persistence_end", new Long(
0281:                            DateSerialUtil.getMaxSerial()));
0282:                    keyAttributes.put("persistence_txend", new Long(
0283:                            DateSerialUtil.getMaxSerial()));
0284:                    HashMap removeAttributes = new HashMap();
0285:                    removeAttributes.put("persistence_txend", currentSerial);
0286:                    removeAttributes.put("persistence_txendid", transaction
0287:                            .getSerial());
0288:                    context.getDatabase().save(transaction,
0289:                            parentInfo.getSubTableName(parentAttributeName),
0290:                            keyAttributes, removeAttributes);
0291:                    transaction.addRemoveTable(parentInfo
0292:                            .getSubTableName(parentAttributeName));
0293:                    // Notify listeners
0294:                    events.add(new ClearedContainerEvent(parent,
0295:                            parentAttributeName));
0296:                }
0297:                // Remove removed items first
0298:                if (logger.isDebugEnabled())
0299:                    logger.debug("removing " + removedItems.size()
0300:                            + " items from list...");
0301:                Iterator removedItemsIterator = removedItems.iterator();
0302:                while (removedItemsIterator.hasNext()) {
0303:                    ObjectWrapper wrapper = (ObjectWrapper) removedItemsIterator
0304:                            .next();
0305:                    Object obj = wrapper.getObject();
0306:                    // Remove object from the list
0307:                    HashMap keyAttributes = new HashMap();
0308:                    keyAttributes.put("persistence_id", new Long(context
0309:                            .getObjectTracker().getIdentifier(parent)));
0310:                    keyAttributes.put("persistence_end", new Long(
0311:                            DateSerialUtil.getMaxSerial()));
0312:                    keyAttributes.put("persistence_txend", new Long(
0313:                            DateSerialUtil.getMaxSerial()));
0314:                    keyAttributes.put("container_index", new Long(wrapper
0315:                            .getOriginalIndex()));
0316:                    HashMap removeAttributes = new HashMap();
0317:                    removeAttributes.put("persistence_txend", currentSerial);
0318:                    removeAttributes.put("persistence_txendid", transaction
0319:                            .getSerial());
0320:                    context.getDatabase().save(transaction,
0321:                            parentInfo.getSubTableName(parentAttributeName),
0322:                            keyAttributes, removeAttributes);
0323:                    transaction.addRemoveTable(parentInfo
0324:                            .getSubTableName(parentAttributeName));
0325:                    // Notify listeners
0326:                    events.add(new RemovedItemEvent(parent,
0327:                            parentAttributeName, obj));
0328:                }
0329:                // Re-index list items. This only occurs, if an item addition
0330:                // would get the index of an already existing item. In this case, 
0331:                // all subsequent items are re-indexed. So find the first reindexing
0332:                // point
0333:                ChangeEntry reindexEntry = null;
0334:                int changeIndex = 0;
0335:                while ((changeIndex < changes.size()) && (reindexEntry == null)) {
0336:                    ChangeEntry current = (ChangeEntry) changes
0337:                            .get(changeIndex);
0338:                    if ((current.getChange() == ChangeEntry.ADDED)
0339:                            && (current.getReindex()))
0340:                        reindexEntry = current;
0341:                    changeIndex++;
0342:                }
0343:                if (reindexEntry != null) {
0344:                    changeIndex--; // Back one step, if found
0345:                    // Found a reindex entry
0346:                    if (logger.isDebugEnabled())
0347:                        logger.debug("re-indexing range, change: "
0348:                                + reindexEntry);
0349:                    // All old items' indexes between this change and the next 
0350:                    // are to be re-indexed. Get all the entries after the reindexing
0351:                    // item.
0352:                    String subTableName = parentInfo
0353:                            .getSubTableName(parentAttributeName);
0354:                    TableTerm subTableTerm = new TableTerm(subTableName, null);
0355:                    Vector selectTerms = new Vector();
0356:                    selectTerms.add(new ReferenceTerm(subTableTerm,
0357:                            "container_index"));
0358:                    selectTerms.add(new ReferenceTerm(subTableTerm, "value"));
0359:                    Expression expr = new Expression();
0360:                    expr.add(new ReferenceTerm(subTableTerm, "persistence_id"));
0361:                    expr.add("=");
0362:                    expr.add(new ConstantTerm(new Long(context
0363:                            .getObjectTracker().getIdentifier(parent))));
0364:                    expr.add("and");
0365:                    expr
0366:                            .add(new ReferenceTerm(subTableTerm,
0367:                                    "container_index"));
0368:                    expr.add(">=");
0369:                    expr.add(new ConstantTerm(new Long(reindexEntry.getItem()
0370:                            .getOriginalIndex())));
0371:                    expr.add("and");
0372:                    originalTimeControl.apply(expr, subTableTerm);
0373:                    Vector orderBys = new Vector();
0374:                    orderBys.add(new OrderBy(new ReferenceTerm(subTableTerm,
0375:                            "container_index"), OrderBy.ASCENDING));
0376:                    QueryStatement stmt = new QueryStatement(selectTerms, expr,
0377:                            orderBys);
0378:                    SearchResult result = context.getDatabase().search(
0379:                            transaction, stmt, null);
0380:                    // Go through each item entry and modify it (first close the old
0381:                    // one, and create a new entry with new index)
0382:                    // Note, that because this is low-level, the paging mechanism
0383:                    // is questionable. We depend here on the fetchsize parameter
0384:                    // of the jdbc driver, so it is assumed, that large result sets
0385:                    // can be read.
0386:                    // The cycle reads the resultset, and the changeset in parallel,
0387:                    // each is pre-read.
0388:                    // Implementation note: On first iteration, the reindexEntry is
0389:                    // always selected, because it has the lowest index.
0390:                    long newIndex = reindexEntry.getItem().getOriginalIndex()
0391:                            + INTERVAL - 1;
0392:                    ChangeEntry currentEntry = reindexEntry;
0393:                    int resultIndex = 0;
0394:                    Map currentResult = null;
0395:                    if (resultIndex < result.getResult().size())
0396:                        currentResult = (Map) result.getResult().get(
0397:                                resultIndex);
0398:                    while ((currentResult != null) || (currentEntry != null)) {
0399:                        // Determine whether the next item is a change, or a database
0400:                        // result.
0401:                        if ((currentResult != null)
0402:                                && ((currentEntry == null) || (((Long) currentResult
0403:                                        .get("container_index")).longValue() < currentEntry
0404:                                        .getItem().getOriginalIndex()))) {
0405:                            // The database result entry has a smaller index than
0406:                            // the change (if there is a change), this means, the
0407:                            // next index belongs to this database entry.
0408:                            Long index = (Long) currentResult
0409:                                    .get("container_index");
0410:                            Long value = (Long) currentResult.get("value");
0411:                            // Close last occurence of index
0412:                            HashMap keyAttributes = new HashMap();
0413:                            keyAttributes.put("persistence_id", new Long(
0414:                                    context.getObjectTracker().getIdentifier(
0415:                                            parent)));
0416:                            keyAttributes.put("persistence_end", new Long(
0417:                                    DateSerialUtil.getMaxSerial()));
0418:                            keyAttributes.put("persistence_txend", new Long(
0419:                                    DateSerialUtil.getMaxSerial()));
0420:                            keyAttributes.put("container_index", index);
0421:                            HashMap removeAttributes = new HashMap();
0422:                            removeAttributes.put("persistence_txend",
0423:                                    currentSerial);
0424:                            removeAttributes.put("persistence_txendid",
0425:                                    transaction.getSerial());
0426:                            context
0427:                                    .getDatabase()
0428:                                    .save(
0429:                                            transaction,
0430:                                            parentInfo
0431:                                                    .getSubTableName(parentAttributeName),
0432:                                            keyAttributes, removeAttributes);
0433:                            transaction.addRemoveTable(parentInfo
0434:                                    .getSubTableName(parentAttributeName));
0435:                            // Insert new version
0436:                            HashMap itemAttributes = new HashMap();
0437:                            itemAttributes.put("persistence_id", new Long(
0438:                                    context.getObjectTracker().getIdentifier(
0439:                                            parent)));
0440:                            itemAttributes.put("persistence_start", new Long(
0441:                                    DateSerialUtil.getMaxSerial()));
0442:                            itemAttributes.put("persistence_end", new Long(
0443:                                    DateSerialUtil.getMaxSerial()));
0444:                            itemAttributes.put("persistence_txendid", new Long(
0445:                                    0));
0446:                            itemAttributes.put("persistence_txstartid",
0447:                                    transaction.getSerial());
0448:                            itemAttributes.put("persistence_txstart",
0449:                                    currentSerial);
0450:                            itemAttributes.put("persistence_txend", new Long(
0451:                                    DateSerialUtil.getMaxSerial()));
0452:                            itemAttributes.put("value", value);
0453:                            itemAttributes.put("container_index", new Long(
0454:                                    newIndex));
0455:                            context
0456:                                    .getDatabase()
0457:                                    .insert(
0458:                                            transaction,
0459:                                            parentInfo
0460:                                                    .getSubTableName(parentAttributeName),
0461:                                            itemAttributes);
0462:                            transaction.addSaveTable(parentInfo
0463:                                    .getSubTableName(parentAttributeName));
0464:                            // Pre-read the next result
0465:                            resultIndex++;
0466:                            if (resultIndex < result.getResult().size())
0467:                                currentResult = (Map) result.getResult().get(
0468:                                        resultIndex);
0469:                            else
0470:                                currentResult = null;
0471:                        } else {
0472:                            // This branch is active if an ADDED (yet non existing)
0473:                            // item is next. In this case we must re-index the entry
0474:                            // which will be already created with this new index later.
0475:                            currentEntry.getItem().setOriginalIndex(newIndex);
0476:                            // Pre-read next entry
0477:                            changeIndex++;
0478:                            while ((changeIndex < changes.size())
0479:                                    && (((ChangeEntry) changes.get(changeIndex))
0480:                                            .getChange() != ChangeEntry.ADDED))
0481:                                changeIndex++;
0482:                            if (changeIndex < changes.size())
0483:                                currentEntry = (ChangeEntry) changes
0484:                                        .get(changeIndex);
0485:                            else
0486:                                currentEntry = null;
0487:                        }
0488:                        // Increment index
0489:                        newIndex += INTERVAL;
0490:                    }
0491:                }
0492:                // Add added items
0493:                if (logger.isDebugEnabled())
0494:                    logger.debug("adding " + addedItems.size()
0495:                            + " items to list...");
0496:                Iterator addedItemsIterator = addedItems.iterator();
0497:                while (addedItemsIterator.hasNext()) {
0498:                    ObjectWrapper wrapper = (ObjectWrapper) addedItemsIterator
0499:                            .next();
0500:                    Object obj = wrapper.getObject();
0501:                    // Item does not exist then put it in the save list
0502:                    if (!context.getObjectTracker().exists(obj))
0503:                        waitingObjects.add(context.getObjectTracker()
0504:                                .getWrapper(obj));
0505:                    // Add item
0506:                    HashMap itemAttributes = new HashMap();
0507:                    itemAttributes.put("persistence_id", new Long(context
0508:                            .getObjectTracker().getIdentifier(parent)));
0509:                    itemAttributes.put("persistence_start", new Long(
0510:                            DateSerialUtil.getMaxSerial()));
0511:                    itemAttributes.put("persistence_end", new Long(
0512:                            DateSerialUtil.getMaxSerial()));
0513:                    itemAttributes.put("persistence_txendid", new Long(0));
0514:                    itemAttributes.put("persistence_txstartid", transaction
0515:                            .getSerial());
0516:                    itemAttributes.put("persistence_txstart", currentSerial);
0517:                    itemAttributes.put("persistence_txend", new Long(
0518:                            DateSerialUtil.getMaxSerial()));
0519:                    itemAttributes.put("container_index", new Long(wrapper
0520:                            .getOriginalIndex()));
0521:                    itemAttributes.put("value", new Long(context
0522:                            .getObjectTracker().getIdentifier(obj)));
0523:                    context.getDatabase().insert(transaction,
0524:                            parentInfo.getSubTableName(parentAttributeName),
0525:                            itemAttributes);
0526:                    transaction.addSaveTable(parentInfo
0527:                            .getSubTableName(parentAttributeName));
0528:                    // Notify listeners
0529:                    events.add(new AddedItemEvent(parent, parentAttributeName,
0530:                            obj));
0531:                }
0532:                // Reload the changed list. Note, that the list should not
0533:                // be referenced until the Store.save() operation completes, because
0534:                // the list currently might contain nonexisting objects, which will
0535:                // be inserted _after_ this code.
0536:                originalTimeControl = new TimeControl(currentSerial,
0537:                        transaction.getSerial(), true);
0538:                lastSerial = currentSerial;
0539:            }
0540:
0541:            /**
0542:             * Get the serial number of last modification.
0543:             */
0544:            public Long getLastSerial() {
0545:                return lastSerial;
0546:            }
0547:
0548:            public boolean retainAll(Object c) {
0549:                return retainAll((Collection) c);
0550:            }
0551:
0552:            public boolean addAll(Object c) {
0553:                return addAll((Collection) c);
0554:            }
0555:
0556:            /*
0557:             * List implementation.
0558:             */
0559:
0560:            /**
0561:             * Add modification to this list.
0562:             */
0563:            private void addChange(int index, int change, ObjectWrapper item,
0564:                    boolean reindex) {
0565:                // Search for the correct entry. The correct entry is the
0566:                // one which is concerned with at least one index higher item.
0567:                int topIndex = 0;
0568:                int prevTopIndex = 0;
0569:                int changesIndex = 0;
0570:                ChangeEntry nextEntry = null;
0571:                while ((topIndex <= index) && (changesIndex < changes.size())) {
0572:                    nextEntry = (ChangeEntry) changes.get(changesIndex);
0573:                    changesIndex++;
0574:                    // Determine the index of interest of this entry
0575:                    prevTopIndex = topIndex;
0576:                    topIndex += nextEntry.getOffset();
0577:                    if (nextEntry.getChange() == ChangeEntry.ADDED)
0578:                        topIndex++;
0579:                }
0580:                // Add modification to changes list
0581:                ChangeEntry previousEntry = null;
0582:                if (changesIndex > 1)
0583:                    previousEntry = (ChangeEntry) changes.get(changesIndex - 2);
0584:                ChangeEntry newEntry = null;
0585:                if (topIndex <= index) {
0586:                    // This means, that there is no next entry, this is the last.
0587:                    newEntry = new ChangeEntry(index - topIndex, change, item,
0588:                            reindex);
0589:                    nextEntry = null;
0590:                } else {
0591:                    // There are changes after this index, so adjust offsets.
0592:                    newEntry = new ChangeEntry(index - prevTopIndex, change,
0593:                            item, reindex);
0594:                    nextEntry.setOffset(nextEntry.getOffset()
0595:                            - (index - prevTopIndex));
0596:                    changesIndex--;
0597:                }
0598:                if ((newEntry.getOffset() == 0)
0599:                        && (newEntry.getChange() == ChangeEntry.REMOVED)
0600:                        && (previousEntry != null)
0601:                        && (previousEntry.getChange() == ChangeEntry.ADDED)) {
0602:                    // The new entry is a REMOVED entry, and the previous one is an
0603:                    // ADDED on the same index. This means, the added item was removed
0604:                    // even before save, so we remove both.
0605:                    changes.remove(changesIndex - 2);
0606:                    if (nextEntry != null) {
0607:                        // We must re-calculate the next entry's offset, with
0608:                        // it's previous 2 entries removed
0609:                        nextEntry.setOffset(nextEntry.getOffset()
0610:                                + newEntry.getOffset()
0611:                                + previousEntry.getOffset());
0612:                    }
0613:                } else {
0614:                    // The new entry must be added
0615:                    changes.add(changesIndex, newEntry);
0616:                    // With the new entry, the indexes after this change changed, so
0617:                    // adjust
0618:                    int diff = 0;
0619:                    if (newEntry.getChange() == ChangeEntry.ADDED)
0620:                        diff = +1;
0621:                    else
0622:                        diff = -1;
0623:                    for (int i = changesIndex + 1; i < changes.size(); i++) {
0624:                        ObjectWrapper ow = ((ChangeEntry) changes.get(i))
0625:                                .getItem();
0626:                        ow.setIndex(item.getIndex() + 1);
0627:                    }
0628:                }
0629:            }
0630:
0631:            public void add(int index, Object item) {
0632:                if (logger.isDebugEnabled())
0633:                    logger.debug("list impl adding item at index: " + index
0634:                            + ", item: " + item);
0635:                // Check item validity
0636:                if (item == null)
0637:                    throw new IllegalArgumentException(
0638:                            "list does not accept null values.");
0639:                int type = context.getClassTracker().getType(item.getClass());
0640:                if ((type != ClassTracker.TYPE_OBJECT)
0641:                        && (type != ClassTracker.TYPE_PRIMITIVE))
0642:                    throw new IllegalArgumentException(
0643:                            "list only handles object or primitive types, but was: "
0644:                                    + item + " (" + item.getClass().getName()
0645:                                    + ")");
0646:                // Determine whether it is in the removed list
0647:                ObjectWrapper wrapper = new ObjectWrapper(index, item);
0648:                boolean reindex = false;
0649:                if (removedItems.contains(wrapper)) {
0650:                    // It was removed once, so remove remove entry
0651:                    removedItems.remove(wrapper);
0652:                } else {
0653:                    // Also, calculate insert index, aka. 'original index'.
0654:                    // Algorithm: get the previous item, and the one we're
0655:                    // inserting at, if both exist, calculate the middle.
0656:                    // If either side do not exist then grow with INTERVAL.
0657:                    ObjectWrapper prev = null;
0658:                    if (index > 0)
0659:                        prev = getInternal(index - 1);
0660:                    ObjectWrapper under = null;
0661:                    if (index < size()) // Here we must calculate size, shame
0662:                        under = getInternal(index);
0663:                    if ((prev != null) && (under != null)) {
0664:                        // This means, there is a previous index and a next one,
0665:                        // try to get between them if possible.
0666:                        if (under.getOriginalIndex() - prev.getOriginalIndex() < 2) {
0667:                            // No place between them, so we must give the new entry
0668:                            // the index of 'under', and mark the change for reindexing.
0669:                            wrapper.setOriginalIndex(under.getOriginalIndex());
0670:                            reindex = true;
0671:                        } else {
0672:                            // We have luck, we don't have to reindex the whole
0673:                            // list.
0674:                            wrapper
0675:                                    .setOriginalIndex((under.getOriginalIndex() + prev
0676:                                            .getOriginalIndex()) / 2);
0677:                        }
0678:                    } else if ((prev == null) && (under != null)) {
0679:                        // This means we insert at the beginning (no previous item)
0680:                        if (Long.MIN_VALUE + INTERVAL > under
0681:                                .getOriginalIndex())
0682:                            throw new StoreException(
0683:                                    "sorry, list ran out of useful indexes on the beginning, the first index is: "
0684:                                            + under.getOriginalIndex());
0685:                        wrapper.setOriginalIndex(under.getOriginalIndex()
0686:                                - INTERVAL);
0687:                    } else if ((prev != null) && (under == null)) {
0688:                        // The insert occured at the end
0689:                        if (Long.MAX_VALUE - INTERVAL < prev.getOriginalIndex())
0690:                            throw new StoreException(
0691:                                    "sorry, list ran out of useful indexes on the end, the last index is: "
0692:                                            + prev.getOriginalIndex());
0693:                        wrapper.setOriginalIndex(prev.getOriginalIndex()
0694:                                + INTERVAL);
0695:                    } else {
0696:                        // There are no items in the list yet, start at 0
0697:                        wrapper.setOriginalIndex(0);
0698:                    }
0699:                    // Add the item
0700:                    addedItems.add(wrapper);
0701:                }
0702:                // Ensure item exists
0703:                if (logger.isDebugEnabled())
0704:                    logger.debug("adding list item to list: " + index
0705:                            + ", object: " + item);
0706:                itemClass.updateItemClassName(originalList, item.getClass(),
0707:                        addedItems.size() == 0);
0708:                addChange(index, ChangeEntry.ADDED, wrapper, reindex);
0709:                modCount++;
0710:            }
0711:
0712:            /**
0713:             * Clear all entries from the list.
0714:             */
0715:            public void clear() {
0716:                // Clear content and mark cleared flag
0717:                cleared = true;
0718:                addedItems = new LinkedList();
0719:                removedItems = new LinkedList();
0720:                originalList = new Vector();
0721:                changes = new Vector();
0722:                itemClass.clear();
0723:                modCount++;
0724:            }
0725:
0726:            private Long searchInternal(Object item, Vector order) {
0727:                if (item == null)
0728:                    return null;
0729:                int type = context.getClassTracker().getType(item.getClass());
0730:                long id = context.getObjectTracker().getIdentifier(item);
0731:                Transaction tx = context.getTransactionTracker()
0732:                        .getTransaction(TransactionTracker.TX_REQUIRED);
0733:                tx.begin();
0734:                try {
0735:                    ClassInfo itemInfo = context.getClassTracker()
0736:                            .getClassInfo(item.getClass(), item);
0737:                    String itemTableName = itemInfo.getTableName(itemInfo
0738:                            .getSourceEntry());
0739:                    TableTerm itemTableTerm = new TableTerm(itemTableName, null);
0740:                    String listTableName = parentInfo
0741:                            .getSubTableName(parentAttributeName);
0742:                    TableTerm listTableTerm = new TableTerm(listTableName, null);
0743:                    Expression listExpression = new Expression();
0744:                    listExpression.add(new ReferenceTerm(listTableTerm,
0745:                            "persistence_id"));
0746:                    listExpression.add("=");
0747:                    listExpression.add(new ConstantTerm(new Long(context
0748:                            .getObjectTracker().getIdentifier(parent))));
0749:                    listExpression.add("and");
0750:                    if (type != ClassTracker.TYPE_PRIMITIVE) {
0751:                        listExpression.add(new ReferenceTerm(listTableTerm,
0752:                                "value"));
0753:                        listExpression.add("=");
0754:                        listExpression.add(new ConstantTerm(new Long(id)));
0755:                    } else {
0756:                        listExpression.add(new ReferenceTerm(itemTableTerm,
0757:                                "value"));
0758:                        listExpression.add("=");
0759:                        listExpression.add(new ConstantTerm(item));
0760:                    }
0761:                    listExpression.add("and");
0762:                    listExpression.add(new ReferenceTerm(itemTableTerm,
0763:                            "persistence_id"));
0764:                    listExpression.add("=");
0765:                    listExpression
0766:                            .add(new ReferenceTerm(listTableTerm, "value"));
0767:                    listExpression.add("and");
0768:                    originalTimeControl.apply(listExpression, itemTableTerm);
0769:                    listExpression.add("and");
0770:                    originalTimeControl.apply(listExpression, listTableTerm);
0771:                    // Execute. If there is a hit, then object exists
0772:                    QueryStatement stmt = new QueryStatement(listTableTerm,
0773:                            listExpression, order);
0774:                    stmt.setTimeControl(originalTimeControl);
0775:                    stmt.setStaticRepresentation("FIND " + listTableTerm
0776:                            + " WHERE persistence_id = "
0777:                            + context.getObjectTracker().getIdentifier(parent)
0778:                            + " and value = " + id + ", order=" + order);
0779:                    SearchResult result = context.getStore().find(tx, stmt,
0780:                            new Limits(0, 1, 0));
0781:                    // Check
0782:                    if (result.getResult().size() > 0) {
0783:                        Long index = (Long) ((Map) result.getResult().get(0))
0784:                                .get("container_index");
0785:                        if (logger.isDebugEnabled())
0786:                            logger.debug("search internal result index is: "
0787:                                    + index + ", for item: " + item);
0788:                        return index;
0789:                    }
0790:                } finally {
0791:                    tx.commit();
0792:                }
0793:                if (logger.isDebugEnabled())
0794:                    logger
0795:                            .debug("search internal did not found the item given: "
0796:                                    + item);
0797:                return null;
0798:            }
0799:
0800:            public boolean contains(Object item) {
0801:                if (item == null)
0802:                    return false;
0803:                int type = context.getClassTracker().getType(item.getClass());
0804:                // Check the list
0805:                long id = context.getObjectTracker().getIdentifier(item);
0806:                ObjectWrapper wrapper = new ObjectWrapper(item);
0807:                // If it is added, then it is contained.
0808:                if (addedItems.contains(wrapper))
0809:                    return true;
0810:                // Check the list
0811:                if (originalList.size() > LazyList.BATCH_SIZE) {
0812:                    // This means, that the backing lazy list would page if we
0813:                    // were to iterate. So instead, we run a specific query for
0814:                    // the given id.
0815:                    Long index = searchInternal(item, null);
0816:                    return index != null;
0817:                } else {
0818:                    // Set is small, so iterate
0819:                    for (int i = 0; i < originalList.size(); i++) {
0820:                        Object obj = ((Map) originalList.get(i)).get("object");
0821:                        if (((type == ClassTracker.TYPE_PRIMITIVE) && (item
0822:                                .equals(obj)))
0823:                                || ((type != ClassTracker.TYPE_PRIMITIVE) && (context
0824:                                        .getObjectTracker().getIdentifier(obj) == id)))
0825:                            return true;
0826:                    }
0827:                }
0828:                // Fall through
0829:                return false;
0830:            }
0831:
0832:            /**
0833:             * Determine whether list equals another list in content.
0834:             */
0835:            public boolean equals(Object obj) {
0836:                // If it is not a collection, then it surely does not equal.
0837:                if (!(obj instanceof  List))
0838:                    return false;
0839:                if (obj == this )
0840:                    return true; // They equals if they are the same object
0841:                List list = (List) obj;
0842:                if (list.size() != size())
0843:                    return false; // They don't equals if size differs
0844:                // They only equals if the items equals one-by-one
0845:                Iterator oneIterator = iterator();
0846:                Iterator twoIterator = list.iterator();
0847:                while (oneIterator.hasNext() && twoIterator.hasNext()) {
0848:                    Object one = oneIterator.next();
0849:                    Object two = twoIterator.next();
0850:                    if (context.getObjectTracker().getIdentifier(one) != context
0851:                            .getObjectTracker().getIdentifier(two))
0852:                        return false; // The two items do not equal
0853:                }
0854:                return true; // All items were equal
0855:            }
0856:
0857:            public Object get(int index) {
0858:                ObjectWrapper wrapper = getInternal(index);
0859:                if (wrapper == null)
0860:                    return null;
0861:                return wrapper.getObject();
0862:            }
0863:
0864:            private ObjectWrapper getInternal(int index) {
0865:                try {
0866:                    // Search for the correct entry.
0867:                    int topIndex = 0;
0868:                    int lastTopIndex = 0;
0869:                    int lastBottomIndex = 0;
0870:                    int bottomIndex = 0;
0871:                    int changesIndex = 0;
0872:                    ChangeEntry exactEntry = null;
0873:                    while ((topIndex <= index)
0874:                            && (changesIndex < changes.size())) {
0875:                        ChangeEntry nextEntry = (ChangeEntry) changes
0876:                                .get(changesIndex);
0877:                        changesIndex++;
0878:                        // Determine the index of interest of this entry
0879:                        lastTopIndex = topIndex;
0880:                        lastBottomIndex = bottomIndex;
0881:                        topIndex += nextEntry.getOffset();
0882:                        bottomIndex += nextEntry.getOffset();
0883:                        // If this entry is the exact index, then remember
0884:                        if (topIndex == index)
0885:                            exactEntry = nextEntry;
0886:                        // Modify top and bottom indexes
0887:                        if (nextEntry.getChange() == ChangeEntry.ADDED)
0888:                            topIndex++;
0889:                        else
0890:                            bottomIndex++;
0891:                    }
0892:                    if (topIndex <= index)
0893:                        bottomIndex = bottomIndex + (index - topIndex);
0894:                    else
0895:                        bottomIndex = lastBottomIndex + (index - lastTopIndex);
0896:                    // If the last exact match was an ADD, then that is the
0897:                    // result, else the backing list is used.
0898:                    if ((exactEntry != null)
0899:                            && (exactEntry.getChange() == ChangeEntry.ADDED)) {
0900:                        return exactEntry.getItem();
0901:                    } else {
0902:                        Object obj = ((Map) originalList.get(bottomIndex))
0903:                                .get("object");
0904:                        ObjectWrapper result = new ObjectWrapper(index, obj);
0905:                        result.setOriginalIndex(((Long) ((Map) originalList
0906:                                .get(bottomIndex)).get("container_index"))
0907:                                .longValue());
0908:                        return result;
0909:                    }
0910:                } catch (IndexOutOfBoundsException e) {
0911:                    logger.error("array index out-of-bound", e);
0912:                    throw e;
0913:                }
0914:            }
0915:
0916:            public int indexOf(Object item) {
0917:                if (item == null)
0918:                    return -1;
0919:                int type = context.getClassTracker().getType(item.getClass());
0920:                long itemId = context.getObjectTracker().getIdentifier(item);
0921:                if ((itemId == 0) && (type != ClassTracker.TYPE_PRIMITIVE))
0922:                    return -1; // Object does not have identifier, it is not contained.
0923:                if (originalList.size() > LazyList.BATCH_SIZE) {
0924:                    // The list is large, do a select to determine index
0925:                    Transaction tx = context.getTransactionTracker()
0926:                            .getTransaction(TransactionTracker.TX_REQUIRED);
0927:                    tx.begin();
0928:                    try {
0929:                        String listTableName = parentInfo
0930:                                .getSubTableName(parentAttributeName);
0931:                        TableTerm listTableTerm = new TableTerm(listTableName,
0932:                                null);
0933:                        Vector order = new Vector();
0934:                        order.add(new OrderBy(new ReferenceTerm(listTableTerm,
0935:                                "container_index"), OrderBy.ASCENDING));
0936:                        Long oldIndex = searchInternal(item, order);
0937:                        // Check
0938:                        if (oldIndex != null) {
0939:                            // Determine how many indexes are before this index
0940:                            Expression listExpression = new Expression();
0941:                            listExpression.add(new ReferenceTerm(listTableTerm,
0942:                                    "persistence_id"));
0943:                            listExpression.add("=");
0944:                            listExpression.add(new ConstantTerm(new Long(
0945:                                    context.getObjectTracker().getIdentifier(
0946:                                            parent))));
0947:                            listExpression.add("and");
0948:                            listExpression.add(new ReferenceTerm(listTableTerm,
0949:                                    "container_index"));
0950:                            listExpression.add("<");
0951:                            listExpression.add(new ConstantTerm(oldIndex));
0952:                            listExpression.add("and");
0953:                            originalTimeControl.apply(listExpression,
0954:                                    listTableTerm);
0955:                            QueryStatement stmt = new QueryStatement(
0956:                                    listTableTerm, listExpression, null);
0957:                            stmt.setTimeControl(originalTimeControl);
0958:                            stmt.setStaticRepresentation("FIND "
0959:                                    + listTableTerm
0960:                                    + " WHERE persistence_id = "
0961:                                    + context.getObjectTracker().getIdentifier(
0962:                                            parent) + " and container_index < "
0963:                                    + oldIndex);
0964:                            SearchResult result = context.getStore().find(tx,
0965:                                    stmt, new Limits(0, 0, -1));
0966:                            return (int) result.getResultSize();
0967:                        }
0968:                    } finally {
0969:                        tx.commit();
0970:                    }
0971:                } else {
0972:                    // Iterate and find the object
0973:                    for (int i = 0; i < size(); i++) {
0974:                        Object obj = get(i);
0975:                        if (((type == ClassTracker.TYPE_PRIMITIVE) && (item
0976:                                .equals(obj)))
0977:                                || ((type != ClassTracker.TYPE_PRIMITIVE) && (context
0978:                                        .getObjectTracker().getIdentifier(obj) == itemId)))
0979:                            return i;
0980:                    }
0981:                }
0982:                return -1;
0983:            }
0984:
0985:            public int lastIndexOf(Object item) {
0986:                if (item == null)
0987:                    return -1;
0988:                int type = context.getClassTracker().getType(item.getClass());
0989:                long itemId = context.getObjectTracker().getIdentifier(item);
0990:                if ((itemId == 0) && (type != ClassTracker.TYPE_PRIMITIVE))
0991:                    return -1; // Object does not have identifier, it is not contained.
0992:                if (originalList.size() > LazyList.BATCH_SIZE) {
0993:                    // The list is large, do a select to determine index
0994:                    Transaction tx = context.getTransactionTracker()
0995:                            .getTransaction(TransactionTracker.TX_REQUIRED);
0996:                    tx.begin();
0997:                    try {
0998:                        String listTableName = parentInfo
0999:                                .getSubTableName(parentAttributeName);
1000:                        TableTerm listTableTerm = new TableTerm(listTableName,
1001:                                null);
1002:                        Vector order = new Vector();
1003:                        order.add(new OrderBy(new ReferenceTerm(listTableTerm,
1004:                                "container_index"), OrderBy.DESCENDING));
1005:                        Long oldIndex = searchInternal(item, order);
1006:                        // Check
1007:                        if (oldIndex != null) {
1008:                            // Determine how many indexes are before this index
1009:                            Expression listExpression = new Expression();
1010:                            listExpression.add(new ReferenceTerm(listTableTerm,
1011:                                    "persistence_id"));
1012:                            listExpression.add("=");
1013:                            listExpression.add(new ConstantTerm(new Long(
1014:                                    context.getObjectTracker().getIdentifier(
1015:                                            parent))));
1016:                            listExpression.add("and");
1017:                            listExpression.add(new ReferenceTerm(listTableTerm,
1018:                                    "container_index"));
1019:                            listExpression.add("<");
1020:                            listExpression.add(new ConstantTerm(oldIndex));
1021:                            listExpression.add("and");
1022:                            originalTimeControl.apply(listExpression,
1023:                                    listTableTerm);
1024:                            QueryStatement stmt = new QueryStatement(
1025:                                    listTableTerm, listExpression, null);
1026:                            stmt.setTimeControl(originalTimeControl);
1027:                            stmt.setStaticRepresentation("FIND "
1028:                                    + listTableTerm
1029:                                    + " WHERE persistence_id = "
1030:                                    + context.getObjectTracker().getIdentifier(
1031:                                            parent) + " and container_index < "
1032:                                    + oldIndex);
1033:                            SearchResult result = context.getStore().find(tx,
1034:                                    stmt, new Limits(0, 0, -1));
1035:                            return (int) result.getResultSize();
1036:                        }
1037:                    } finally {
1038:                        tx.commit();
1039:                    }
1040:                } else {
1041:                    // Iterate and find the object
1042:                    for (int i = size(); i > 0; i++) {
1043:                        Object obj = get(i - 1);
1044:                        if (((type == ClassTracker.TYPE_PRIMITIVE) && (item
1045:                                .equals(obj)))
1046:                                || ((type != ClassTracker.TYPE_PRIMITIVE) && (context
1047:                                        .getObjectTracker().getIdentifier(obj) == itemId)))
1048:                            return i - 1;
1049:                    }
1050:                }
1051:                return -1;
1052:            }
1053:
1054:            public Object remove(int index) {
1055:                // Get the object at the given index
1056:                ObjectWrapper wrapper = getInternal(index);
1057:                // Object is present, determine, whether it is in the added list
1058:                if (addedItems.contains(wrapper)) {
1059:                    // It was added, so remove added entry
1060:                    addedItems.remove(wrapper);
1061:                } else {
1062:                    // It was not yet added, so add
1063:                    removedItems.add(wrapper);
1064:                }
1065:                // Insert modification entry
1066:                if (logger.isDebugEnabled())
1067:                    logger.debug("removed index: " + index + ", object: "
1068:                            + wrapper.getObject());
1069:                addChange(index, ChangeEntry.REMOVED, wrapper, false);
1070:                modCount++;
1071:                return wrapper.getObject();
1072:            }
1073:
1074:            public boolean remove(Object item) {
1075:                int index = indexOf(item);
1076:                if (index == -1)
1077:                    return false;
1078:                remove(index);
1079:                return true;
1080:            }
1081:
1082:            public Object set(int index, Object item) {
1083:                // Simply remove first, then add to the same position
1084:                Object oldItem = remove(index);
1085:                add(index, item);
1086:                return oldItem;
1087:            }
1088:
1089:            public int size() {
1090:                return originalList.size() - removedItems.size()
1091:                        + addedItems.size();
1092:            }
1093:
1094:            public String toString() {
1095:                return originalList.toString();
1096:            }
1097:
1098:            private class ChangeEntry {
1099:                public static final int ADDED = 1;
1100:                public static final int REMOVED = 2;
1101:
1102:                private int offset;
1103:                private int change;
1104:                private boolean reindex;
1105:                private ObjectWrapper item;
1106:
1107:                public ChangeEntry(int offset, int change, ObjectWrapper item,
1108:                        boolean reindex) {
1109:                    this .offset = offset;
1110:                    this .change = change;
1111:                    this .item = item;
1112:                    this .reindex = reindex;
1113:                }
1114:
1115:                public boolean getReindex() {
1116:                    return reindex;
1117:                }
1118:
1119:                public String toString() {
1120:                    return "[Change: " + change + ", offset: " + offset
1121:                            + ", item: " + item + "]";
1122:                }
1123:
1124:                public int getOffset() {
1125:                    return offset;
1126:                }
1127:
1128:                public void setOffset(int offset) {
1129:                    this .offset = offset;
1130:                }
1131:
1132:                public int getChange() {
1133:                    return change;
1134:                }
1135:
1136:                public void setChange(int change) {
1137:                    this .change = change;
1138:                }
1139:
1140:                public ObjectWrapper getItem() {
1141:                    return item;
1142:                }
1143:
1144:                public void setItem(ObjectWrapper item) {
1145:                    this .item = item;
1146:                }
1147:            }
1148:
1149:            public class ObjectWrapper {
1150:                private Object obj;
1151:                private long id;
1152:                private long originalIndex;
1153:                private int index;
1154:                private boolean indexGiven;
1155:                private int type;
1156:
1157:                public ObjectWrapper(int index, Object obj) {
1158:                    this .indexGiven = true;
1159:                    this .index = index;
1160:                    this .obj = obj;
1161:                    context.getObjectTracker().registerObject(obj, 0);
1162:                    this .id = context.getObjectTracker().getIdentifier(obj);
1163:                    this .type = context.getClassTracker().getType(
1164:                            obj.getClass());
1165:                }
1166:
1167:                public ObjectWrapper(Object obj) {
1168:                    this .indexGiven = false;
1169:                    this .obj = obj;
1170:                    context.getObjectTracker().registerObject(obj, 0);
1171:                    this .id = context.getObjectTracker().getIdentifier(obj);
1172:                    this .type = context.getClassTracker().getType(
1173:                            obj.getClass());
1174:                }
1175:
1176:                public int getIndex() {
1177:                    return index;
1178:                }
1179:
1180:                public void setIndex(int index) {
1181:                    this .index = index;
1182:                }
1183:
1184:                public long getIdentifier() {
1185:                    return id;
1186:                }
1187:
1188:                public Object getObject() {
1189:                    return obj;
1190:                }
1191:
1192:                public int hashCode() {
1193:                    if (type == ClassTracker.TYPE_PRIMITIVE)
1194:                        return obj.hashCode();
1195:                    else
1196:                        return (int) (id >> 32);
1197:                }
1198:
1199:                public boolean equals(Object rhs) {
1200:                    if (!(rhs instanceof  ObjectWrapper))
1201:                        return false;
1202:                    if (type == ClassTracker.TYPE_PRIMITIVE) {
1203:                        if (!obj.equals(((ObjectWrapper) rhs).obj))
1204:                            return false;
1205:                    } else {
1206:                        if (id != ((ObjectWrapper) rhs).id)
1207:                            return false;
1208:                    }
1209:                    if (indexGiven)
1210:                        return index == ((ObjectWrapper) rhs).index;
1211:                    return true;
1212:                }
1213:
1214:                public long getOriginalIndex() {
1215:                    return originalIndex;
1216:                }
1217:
1218:                public void setOriginalIndex(long originalIndex) {
1219:                    this.originalIndex = originalIndex;
1220:                }
1221:
1222:            }
1223:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.