Source Code Cross Referenced for StorageConnector.java in  » Database-ORM » MMBase » org » mmbase » core » util » 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 » MMBase » org.mmbase.core.util 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:
003:        This software is OSI Certified Open Source Software.
004:        OSI Certified is a certification mark of the Open Source Initiative.
005:
006:        The license (Mozilla version 1.0) can be read at the MMBase site.
007:        See http://www.MMBase.org/license
008:
009:         */
010:        package org.mmbase.core.util;
011:
012:        import java.util.*;
013:
014:        import org.mmbase.bridge.Field;
015:        import org.mmbase.bridge.NodeQuery;
016:        import org.mmbase.cache.*;
017:        import org.mmbase.core.CoreField;
018:        import org.mmbase.module.core.*;
019:        import org.mmbase.storage.*;
020:        import org.mmbase.storage.util.Index;
021:        import org.mmbase.storage.search.*;
022:        import org.mmbase.storage.search.implementation.*;
023:        import org.mmbase.util.QueryConvertor;
024:        import org.mmbase.util.logging.Logger;
025:        import org.mmbase.util.logging.Logging;
026:
027:        /**
028:         * A StorageConnector object is associated with a specific builder.
029:         * It provides methods for loading nodes from the cloud (using the search query classes),
030:         * either indivbidual nodes or nodelists.
031:         *
032:         * @since MMBase-1.8
033:         * @author Pierre van Rooden
034:         * @version $Id: StorageConnector.java,v 1.19 2007/08/21 14:53:10 michiel Exp $
035:         */
036:        public class StorageConnector {
037:
038:            /**
039:             * Max length of a query, informix = 32.0000 so we assume a bit less for other databases (???).
040:             */
041:            private static final int MAX_QUERY_SIZE = 20000;
042:
043:            private static final Logger log = Logging
044:                    .getLoggerInstance(StorageConnector.class);
045:
046:            /**
047:             * Determines whether the cache need be refreshed.
048:             * Seems useless, as this value is never changed (always true)
049:             * @see #processSearchResults
050:             */
051:            /*
052:             public static final boolean REPLACE_CACHE = true;
053:             */
054:
055:            /**
056:             * Whenever a list should always return the correct types of nodes
057:             * old behaviour is not...
058:             * This is needed, when you want for example use the following code:
059:             * <pre>
060:             * MMObjectNode node = MMObjectBuilder.getNode(123);
061:             * Enumeration relations = node.getRelations("posrel");
062:             * while(enumeration.hasNext()) {
063:             *   MMObjectNode posrel = (MMObjectNode) enumeration.getElement();
064:             *   int pos = posrel.getIntValue("pos");
065:             * }
066:             * </pre>
067:             * When the return of correct node types is the following code has to be used..
068:             * <pre>
069:             * MMObjectNode node = MMObjectBuilder.getNode(123);
070:             * Enumeration relations = node.getRelations("posrel");
071:             * while(enumeration.hasNext()) {
072:             *   MMObjectNode posrel = (MMObjectNode) enumeration.getElement();
073:             *   // next lines is needed when the return of correct nodes is not true
074:             *   posrel = posrel.parent.getNode(posrel.getNumber());
075:             *   // when the line above is skipped, the value of pos will always be -1
076:             *   int pos = posrel.getIntValue("pos");
077:             * }
078:             * </pre>
079:             * Maybe this should be fixed in some otherway,.. but when we want to use the inheritance  you
080:             * _really_ need this thing turned into true.
081:             */
082:            /*
083:             private static boolean CORRECT_NODE_TYPES = true;
084:             */
085:
086:            /**
087:             * Maximum number of nodes to return on a query (-1 means no limit, and is also the default)
088:             */
089:            protected int maxNodesFromQuery = -1;
090:
091:            /**
092:             * @javadoc
093:             */
094:            protected final MMObjectBuilder builder;
095:
096:            // indices for the storage layer
097:            private Map<String, Index> indices = new HashMap<String, Index>();
098:
099:            /**
100:             * @javadoc
101:             */
102:            public StorageConnector(MMObjectBuilder builder) {
103:                this .builder = builder;
104:            }
105:
106:            /**
107:             * Determine the number of objects in this table.
108:             * @return The number of entries in the table.
109:             */
110:            public int size() {
111:                try {
112:                    return builder.getMMBase().getStorageManager()
113:                            .size(builder);
114:                } catch (StorageException se) {
115:                    log.error(se.getMessage());
116:                    return -1;
117:                }
118:            }
119:
120:            /**
121:             * Check whether the table is accessible.
122:             * In general, this means the table does not exist. Please note that this routine may
123:             * also return false if the table is inaccessible due to insufficient rights.
124:             * @return <code>true</code> if the table is accessible, <code>false</code> otherwise.
125:             */
126:            public boolean created() {
127:                try {
128:                    return builder.getMMBase().getStorageManager().exists(
129:                            builder);
130:                } catch (StorageException se) {
131:                    log.error(se.getMessage() + Logging.stackTrace(se));
132:                    return false;
133:                }
134:            }
135:
136:            public Map<String, Index> getIndices() {
137:                return indices;
138:            }
139:
140:            public void addIndex(Index index) {
141:                if (index != null && index.getParent() == builder) {
142:                    indices.put(index.getName(), index);
143:                }
144:            }
145:
146:            public void addIndices(List<Index> indexList) {
147:                if (indexList != null) {
148:                    for (Index i : indexList) {
149:                        addIndex(i);
150:                    }
151:                }
152:            }
153:
154:            public Index getIndex(String key) {
155:                return indices.get(key);
156:            }
157:
158:            public synchronized Index createIndex(String key) {
159:                Index index = getIndex(key);
160:                if (index == null) {
161:                    index = new Index(builder, key);
162:                    indices.put(key, index);
163:                }
164:                return index;
165:            }
166:
167:            public void addToIndex(String key, Field field) {
168:                createIndex(key).add(field);
169:            }
170:
171:            public void removeFromIndex(String key, Field field) {
172:                Index index = createIndex(key);
173:                if (index != null) {
174:                    index.remove(field);
175:                }
176:            }
177:
178:            public boolean isInIndex(String key, Field field) {
179:                Index index = getIndex(key);
180:                return index != null && index.contains(field);
181:            }
182:
183:            // retrieve nodes
184:            /**
185:             * Retrieves a node based on it's number (a unique key).
186:             * @todo when something goes wrong, the method currently catches the exception and returns null.
187:             *       It should actually throw a NotFoundException instead.
188:             * @param number The number of the node to search for
189:             * @param useCache If false, a fresh copy is returned.
190:             * @return <code>null</code> if the node does not exist, the key is invalid,or a
191:             *       <code>MMObjectNode</code> containing the contents of the requested node.
192:             */
193:            public MMObjectNode getNode(final int number, final boolean useCache)
194:                    throws StorageException {
195:                if (log.isDebugEnabled()) {
196:                    log.trace("Getting node with number " + number);
197:                }
198:                if (number < 0) {
199:                    throw new IllegalArgumentException(
200:                            "Tried to obtain node from builder '"
201:                                    + builder.getTableName()
202:                                    + "' with an illegal number = " + number);
203:                }
204:                MMObjectNode node = null;
205:
206:                Integer numberValue = Integer.valueOf(number);
207:                // try cache if indicated to do so
208:                node = builder.getNodeFromCache(numberValue);
209:                if (node != null) {
210:                    log.trace("Found in cache!");
211:                    if (useCache) {
212:                        return node;
213:                    } else {
214:                        return new MMObjectNode(node);
215:                    }
216:                }
217:
218:                MMBase mmb = builder.getMMBase();
219:                // not in cache. We are going to put it in.
220:                // retrieve node's objecttype
221:                MMObjectBuilder nodeBuilder = getBuilderForNode(number);
222:                // use storage factory if present
223:                log.debug("Getting node from storage");
224:                node = mmb.getStorageManager().getNode(nodeBuilder, number);
225:                if (nodeBuilder == mmb.getInsRel()
226:                        && node.getOType() != nodeBuilder.getObjectType()) {
227:                    // the builder was unknown en we falled back to insrel. 
228:                    // Perhaps it would have been better to fall back to object?
229:                    if (node.getNumber() <= 0) {
230:                        node = mmb.getStorageManager().getNode(
231:                                mmb.getRootBuilder(), number);
232:                    }
233:                }
234:                // store in cache if indicated to do so
235:                if (useCache) {
236:                    if (log.isDebugEnabled()) {
237:                        log.debug("Caching node from storage" + node);
238:                    }
239:                    node = builder.safeCache(numberValue, node);
240:                }
241:                if (log.isDebugEnabled()) {
242:                    log.debug("Returning " + node);
243:                }
244:                if (useCache) {
245:                    return node;
246:                } else {
247:                    return new MMObjectNode(node);
248:                }
249:            }
250:
251:            private final Set<Integer> warnedBuilders = new HashSet<Integer>();
252:
253:            public MMObjectBuilder getBuilderForNode(final int number) {
254:                MMBase mmb = builder.getMMBase();
255:                MMObjectBuilder nodeBuilder = builder;
256:                int nodeType = getNodeType(number);
257:                if (nodeType < 0) {
258:                    // the node does not exists, which according to javadoc should return null
259:                    throw new StorageNotFoundException(
260:                            "Cannot determine node type of node with number ="
261:                                    + number);
262:                }
263:                // if the type is not for the current builder, determine the real builder
264:                if (nodeType != builder.getNumber()) {
265:                    if (log.isDebugEnabled()) {
266:                        log.debug(" " + nodeType + "!=" + builder.getNumber());
267:                    }
268:                    String builderName = mmb.getTypeDef().getValue(nodeType);
269:                    if (builderName == null) {
270:                        log
271:                                .error("The nodetype name of node #"
272:                                        + number
273:                                        + " could not be found (nodetype # "
274:                                        + nodeType
275:                                        + "), taking '"
276:                                        + builder.getTableName()
277:                                        + "' (more errors of this kind are logged on debug)");
278:                        builderName = builder.getTableName();
279:                    }
280:                    nodeBuilder = mmb.getBuilder(builderName);
281:                    if (nodeBuilder == null) {
282:                        if (builderName.endsWith("rel")) {
283:                            nodeBuilder = mmb.getInsRel();
284:                        } else {
285:                            nodeBuilder = mmb.getRootBuilder();
286:                        }
287:                        if (!warnedBuilders.contains(nodeType)) {
288:                            log.warn("Builder " + builderName + "(" + nodeType
289:                                    + ") is not loaded, taking "
290:                                    + nodeBuilder.getTableName());
291:                            warnedBuilders.add(nodeType);
292:                        }
293:                        log.debug("Node #" + number + "'s builder "
294:                                + builderName + "(" + nodeType
295:                                + ") is not loaded. Taking "
296:                                + nodeBuilder.getTableName());
297:
298:                    }
299:                }
300:                return nodeBuilder;
301:            }
302:
303:            /**
304:             * Retrieves an object's type. If necessary, the type is added to the cache.
305:             * @todo when something goes wrong, the method currently catches the exception and returns -1.
306:             *       It should actually throw a NotFoundException instead.
307:             * @param number The number of the node to search for
308:             * @return an <code>int</code> value which is the object type (otype) of the node.
309:             */
310:            public int getNodeType(int number) throws StorageException {
311:                if (number < 0) {
312:                    throw new IllegalArgumentException(
313:                            "node number was invalid (" + number + " < 0)");
314:                } else {
315:                    return builder.getMMBase().getStorageManager().getNodeType(
316:                            number);
317:                }
318:            }
319:
320:            // Search and query methods on a table
321:
322:            /**
323:             * Convert virtual nodes to real nodes based on their otype
324:             *
325:             * Normally a multirelations-search will return virtual nodes. These nodes
326:             * will only contain values which where specified in the field-vector.
327:             * This method will make real nodes of those virtual nodes.
328:             *
329:             * @param virtuals containing virtual nodes
330:             * @return List containing real nodes, directly from this Builders
331:             */
332:            public List<MMObjectNode> getNodes(Collection<MMObjectNode> virtuals)
333:                    throws SearchQueryException {
334:                List<MMObjectNode> result = new ArrayList<MMObjectNode>();
335:
336:                int numbersSize = 0;
337:                NodeSearchQuery query = new NodeSearchQuery(builder);
338:                BasicStep step = (BasicStep) query.getSteps().get(0); // casting is ugly !!
339:
340:                List<Integer> subResult = new ArrayList<Integer>();
341:
342:                for (MMObjectNode node : virtuals) {
343:                    // check if this node is already in cache
344:                    Integer number = node.getNumber();
345:                    if (builder.isNodeCached(number)) {
346:                        result.add(builder.getNodeFromCache(number));
347:                        // else seek it with a search on builder in db
348:                    } else {
349:                        numbersSize += ("," + number).length();
350:                        subResult.add(number);
351:                        step.addNode(number.intValue());
352:                    }
353:
354:                    if (numbersSize > MAX_QUERY_SIZE) {
355:                        addSubResult(query, subResult, result);
356:                        query = new NodeSearchQuery(builder);
357:                        step = (BasicStep) query.getSteps().get(0);
358:                        numbersSize = 0;
359:                        subResult.clear();
360:                    }
361:                }
362:
363:                // now that we have a comma seperated string of numbers, we can
364:                // the search with a where-clause containing this list
365:                if (numbersSize > 0) {
366:                    addSubResult(query, subResult, result);
367:                } // else everything from cache
368:
369:                // check that we didnt loose any nodes
370:                assert assertSizes(virtuals, result);
371:
372:                return result;
373:            }
374:
375:            /**
376:             * @param query Query with nodestep with added nodes.
377:             * @param subResult List of Integer
378:             * @param result    List to which the real nodes must be added.
379:             * @since MMBase-1.8.2
380:             */
381:            protected void addSubResult(final NodeSearchQuery query,
382:                    final List<Integer> subResult,
383:                    final List<MMObjectNode> result)
384:                    throws SearchQueryException {
385:                List<MMObjectNode> rawNodes = getRawNodes(query, true);
386:                // convert this list to a map, for easy reference when filling result.
387:                // would the creation of this Map not somehow be avoidable?
388:                Map<Integer, MMObjectNode> rawMap = new HashMap<Integer, MMObjectNode>();
389:                for (MMObjectNode n : rawNodes) {
390:                    rawMap.put(n.getNumber(), n);
391:                }
392:                for (Integer n : subResult) {
393:                    result.add(rawMap.get(n));
394:                }
395:            }
396:
397:            /**
398:             * @since MMBase-1.8.2
399:             */
400:            protected boolean assertSizes(Collection<MMObjectNode> virtuals,
401:                    Collection<MMObjectNode> result) {
402:                if (virtuals.size() != result.size()) {
403:                    log.error(" virtuals " + virtuals + " result " + result);
404:                    return false;
405:                } else {
406:                    return true;
407:                }
408:            }
409:
410:            /**
411:             * Counts number of nodes matching a specified constraint.
412:             * The constraint is specified by a query that selects nodes of
413:             * a specified type, which must be the nodetype corresponding
414:             * to this builder.
415:             *
416:             * @param query The query.
417:             * @return The number of nodes.
418:             * @throws IllegalArgumentException when an invalid argument is supplied.
419:             * @throws SearchQueryException when failing to retrieve the data.
420:             */
421:            public int count(SearchQuery query) throws SearchQueryException {
422:                // Test if nodetype corresponds to builder.
423:                verifyBuilderQuery(query);
424:
425:                // Wrap in modifiable query, replace fields by one count field.
426:                ModifiableQuery modifiedQuery = new ModifiableQuery(query);
427:                Step step = query.getSteps().get(0);
428:                CoreField numberField = builder
429:                        .getField(MMObjectBuilder.FIELD_NUMBER);
430:                AggregatedField field = new BasicAggregatedField(step,
431:                        numberField, AggregatedField.AGGREGATION_TYPE_COUNT);
432:                List<StepField> newFields = new ArrayList<StepField>(1);
433:                newFields.add(field);
434:                modifiedQuery.setFields(newFields);
435:
436:                AggregatedResultCache cache = AggregatedResultCache.getCache();
437:
438:                List<MMObjectNode> results = cache.get(modifiedQuery);
439:                if (results == null) {
440:                    // Execute query, return result.
441:                    results = builder.getMMBase().getSearchQueryHandler()
442:                            .getNodes(
443:                                    modifiedQuery,
444:                                    new ResultBuilder(builder.getMMBase(),
445:                                            modifiedQuery));
446:                    cache.put(modifiedQuery, results);
447:                }
448:                ResultNode result = (ResultNode) results.get(0);
449:                return result.getIntValue(MMObjectBuilder.FIELD_NUMBER);
450:            }
451:
452:            private void verifyBuilderQuery(SearchQuery query) {
453:                String builderName = null;
454:                if (query instanceof  NodeQuery) {
455:                    builderName = ((NodeQuery) query).getNodeManager()
456:                            .getName();
457:                } else if (query instanceof  NodeSearchQuery) {
458:                    builderName = ((NodeSearchQuery) query).getBuilder()
459:                            .getTableName();
460:                }
461:                if (builderName != null
462:                        && !builderName.equals(builder.getTableName())) {
463:                    throw new IllegalArgumentException("Query passed runs on '"
464:                            + builderName + "' but was passed to '"
465:                            + builder.getTableName() + "'");
466:                }
467:            }
468:
469:            /**
470:             * Returns the Cache which should be used for the result of a certain query. The current
471:             * implementation only makes the distinction between queries for the 'related nodes caches' and
472:             * for the 'node list caches'. Multilevel queries are not done here, so are at the moment not
473:             * anticipated.
474:             * 
475:             * It returns a Map rather then a Cache. The idea behind this is that if in the future a
476:             * query-result can be in more than one cache, a kind of 'chained map' can be returned, to
477:             * reflect that.
478:             * @todo Perhaps other usefull parameters like query-duration and query-result could be added
479:             * (in that case searching a result should certainly returns such a chained map, because then of
480:             * course you don't have those).
481:             */
482:            protected QueryResultCache getCache(SearchQuery query) {
483:                List<Step> steps = query.getSteps();
484:                if (steps.size() == 3) {
485:                    Step step0 = steps.get(0);
486:                    Collection<Integer> nodes = step0.getNodes();
487:                    if (nodes != null && nodes.size() == 1) {
488:                        return RelatedNodesCache.getCache();
489:                    }
490:                }
491:                return NodeListCache.getCache();
492:
493:            }
494:
495:            /**
496:             * Returns nodes matching a specified constraint.
497:             * The constraint is specified by a query that selects nodes of
498:             * a specified type, which must be the nodetype corresponding
499:             * to this builder.
500:             *
501:             * Cache is used, but not filled (because this function is used to calculate subresults)
502:             *
503:             * @param query The query.
504:             * @param useCache if true, the querycache is used
505:             * @return The nodes.
506:             * @throws IllegalArgumentException When the nodetype specified
507:             *         by the query is not the nodetype corresponding to this builder.
508:             */
509:            private List<MMObjectNode> getRawNodes(SearchQuery query,
510:                    boolean useCache) throws SearchQueryException {
511:                // Test if nodetype corresponds to builder.
512:                verifyBuilderQuery(query);
513:                List<MMObjectNode> results = useCache ? getCache(query).get(
514:                        query) : null;
515:
516:                // if unavailable, obtain from storage
517:                if (results == null) {
518:                    log.debug("result list is null, getting from storage");
519:                    results = builder.getMMBase().getSearchQueryHandler()
520:                            .getNodes(query, builder);
521:                } else {
522:                    if (log.isDebugEnabled()) {
523:                        log.debug("Found from cache"
524:                                + getCache(query).getName() + " " + results);
525:                    }
526:                }
527:                return results;
528:            }
529:
530:            /**
531:             * Returns nodes matching a specified constraint.
532:             * The constraint is specified by a query that selects nodes of
533:             * a specified type, which must be the nodetype corresponding
534:             * to this builder.
535:             *
536:             * @param query The query.
537:             * @return The nodes.
538:             * @throws IllegalArgumentException When the nodetype specified
539:             *         by the query is not the nodetype corresponding to this builder.
540:             */
541:            public List<MMObjectNode> getNodes(SearchQuery query)
542:                    throws SearchQueryException {
543:                return getNodes(query, true);
544:            }
545:
546:            /**
547:             * Returns nodes matching a specified constraint.
548:             * The constraint is specified by a query that selects nodes of
549:             * a specified type, which must be the nodetype corresponding
550:             * to this builder.
551:             *
552:             * @param query The query.
553:             * @param useCache if true, the querycache is used
554:             * @return The nodes.
555:             * @throws IllegalArgumentException When the nodetype specified
556:             *         by the query is not the nodetype corresponding to this builder.
557:             */
558:            public List<MMObjectNode> getNodes(SearchQuery query,
559:                    boolean useCache) throws SearchQueryException {
560:                List<MMObjectNode> results = getRawNodes(query, useCache);
561:                // TODO (later): implement maximum set by maxNodesFromQuery?
562:                // Perform necessary postprocessing.
563:                processSearchResults(results);
564:                if (useCache) {
565:                    getCache(query).put(query, results);
566:                }
567:                return results;
568:            }
569:
570:            /**
571:             * Returns all the nodes from the associated builder.
572:             * @return The nodes.
573:             */
574:            public List<MMObjectNode> getNodes() throws SearchQueryException {
575:                return getNodes(new NodeSearchQuery(builder));
576:            }
577:
578:            /**
579:             * Performs some necessary postprocessing on nodes retrieved from a
580:             * search query.
581:             * This consists of the following actions:
582:             * <ul>
583:             * <li>Stores retrieved nodes in the node cache, or
584:             * <li>Replace partially retrieved nodes in the result by complete nodes.
585:             *     Nodes are partially retrieved when their type is a inheriting type
586:             *     of this builder's type, having additional fields. For these nodes
587:             *     additional queries are performed to retrieve the complete nodes.
588:             * <li>Removes nodes with invalid node number from the result.
589:             * </ul>
590:             *
591:             * @param results The nodes. After returning, partially retrieved nodes
592:             *        in the result are replaced <em>in place</em> by complete nodes.
593:             */
594:            private void processSearchResults(List<MMObjectNode> results) {
595:                Map<Integer, Set<MMObjectNode>> convert = new HashMap<Integer, Set<MMObjectNode>>();
596:                int convertCount = 0;
597:                int convertedCount = 0;
598:                int cacheGetCount = 0;
599:                int cachePutCount = 0;
600:
601:                ListIterator<MMObjectNode> resultsIterator = results
602:                        .listIterator();
603:                while (resultsIterator.hasNext()) {
604:                    MMObjectNode node = resultsIterator.next();
605:                    Integer number = node.getNumber();
606:                    if (number.intValue() < 0) {
607:                        // never happened to me, and never should!
608:                        log
609:                                .error("invalid node found, node number was invalid:"
610:                                        + node.getNumber()
611:                                        + ", storage invalid?");
612:                        // dont know what to do with this node,...
613:                        // remove it from the results, continue to the next one!
614:                        resultsIterator.remove();
615:                        continue;
616:                    }
617:
618:                    boolean fromCache = false;
619:                    // only active when builder loaded (oType != -1)
620:                    // maybe we got the wrong node typeback, if so
621:                    // try to retrieve the correct node from the cache first
622:                    int oType = builder.getNumber();
623:                    if (oType != -1 && oType != node.getOType()) {
624:                        // try to retrieve the correct node from the
625:                        // nodecache
626:                        MMObjectNode cachedNode = builder
627:                                .getNodeFromCache(number);
628:                        if (cachedNode != null) {
629:                            node = cachedNode;
630:                            resultsIterator.set(node);
631:                            fromCache = true;
632:                            cacheGetCount++;
633:                        } else {
634:                            // add this node to the list of nodes that still need to
635:                            // be converted..
636:                            // we dont request the builder here, for this we need the
637:                            // typedef table, which could generate an additional query..
638:                            Integer nodeType = node.getOType();
639:                            Set<MMObjectNode> nodes = convert.get(nodeType);
640:                            // create an new entry for the type, if not yet there...
641:                            if (nodes == null) {
642:                                nodes = new HashSet<MMObjectNode>();
643:                                convert.put(nodeType, nodes);
644:                            }
645:                            nodes.add(node);
646:                            convertCount++;
647:                        }
648:                        /*
649:                         } else if (oType == node.getOType()) {
650:                         MMObjectNode oldNode = builder.getNodeFromCache(number);
651:                         // when we want to use cache also for new found nodes
652:                         // and cache may not be replaced, use the one from the
653:                         // cache..
654:                         if(!REPLACE_CACHE && oldNode != null) {
655:                         node = oldNode;
656:                         resultsIterator.set(node);
657:                         fromCache = true;
658:                         cacheGetCount++;
659:                         }
660:                         } else {
661:                         // skipping everything, our builder hasnt been started yet...
662:                         */
663:                    }
664:
665:                    // we can add the node to the cache _if_
666:                    // it was not from cache already, and it
667:                    // is of the correct type..
668:                    if (!fromCache && oType == node.getOType()) {
669:                        // can someone tell me what this has to do?
670:                        // clear the changed signal
671:                        node.clearChanged(); // huh?
672:                        node = builder.safeCache(number, node);
673:                        cachePutCount++;
674:                    }
675:                }
676:
677:                if (/* CORRECT_NODE_TYPES && */convert.size() > 0) {
678:                    // retieve the nodes from the builders....
679:                    // and put them into one big hashmap (integer/node)
680:                    // after that replace all the nodes in result, that
681:                    // were invalid.
682:                    Map<Integer, MMObjectNode> convertedNodes = new HashMap<Integer, MMObjectNode>();
683:
684:                    // process all the different types (builders)
685:                    for (Map.Entry<Integer, Set<MMObjectNode>> typeEntry : convert
686:                            .entrySet()) {
687:                        int nodeType = typeEntry.getKey();
688:                        Set<MMObjectNode> nodes = typeEntry.getValue();
689:                        MMObjectNode typedefNode;
690:                        try {
691:                            typedefNode = getNode(nodeType, true);
692:                        } catch (Exception e) {
693:                            log
694:                                    .error("Exception during conversion of nodelist to right types.  Nodes ("
695:                                            + nodes
696:                                            + ") of current type "
697:                                            + nodeType
698:                                            + " will be skipped. Probably the storage is inconsistent. Message: "
699:                                            + e.getMessage());
700:
701:                            continue;
702:                        }
703:                        if (typedefNode == null) {
704:                            typedefNode = getNode(MMBase.getMMBase()
705:                                    .getBuilder("object").getNumber(), true);
706:                            log.error("Could not find typedef node #"
707:                                    + nodeType + " taking " + typedefNode
708:                                    + " in stead");
709:                        }
710:                        String tableName = typedefNode.getBuilder()
711:                                .getTableName();
712:                        if (!tableName.equals("typedef")) {
713:                            typedefNode = getNode(MMBase.getMMBase()
714:                                    .getBuilder("object").getNumber(), true);
715:                            log.error("The type of node '" + nodeType
716:                                    + "' is not typedef (but '" + tableName
717:                                    + "'). This is an error. Taking '"
718:                                    + typedefNode + "' in stead.");
719:                        }
720:
721:                        MMObjectBuilder conversionBuilder = builder.getMMBase()
722:                                .getBuilder(typedefNode.getStringValue("name"));
723:                        if (conversionBuilder == null) {
724:                            // maybe it is not active?
725:                            log
726:                                    .error("Could not find builder with name:"
727:                                            + typedefNode
728:                                                    .getStringValue("name")
729:                                            + " refered by node #"
730:                                            + typedefNode.getNumber()
731:                                            + ", is it active? Taking object builder in stead.");
732:                            conversionBuilder = MMBase.getMMBase().getBuilder(
733:                                    "object");
734:                        }
735:                        try {
736:                            for (MMObjectNode current : conversionBuilder
737:                                    .getStorageConnector().getNodes(nodes)) {
738:                                if (current == null) {
739:                                    log.warn("Found a node which is NULL!");
740:                                    continue;
741:                                }
742:                                convertedNodes
743:                                        .put(current.getNumber(), current);
744:                            }
745:                        } catch (SearchQueryException sqe) {
746:                            log.error(sqe.getMessage(), sqe);
747:                            // no nodes
748:                        }
749:                    }
750:
751:                    // insert all the corrected nodes that were found into the list..
752:                    for (int i = 0; i < results.size(); i++) {
753:                        MMObjectNode current = results.get(i);
754:                        Integer number = current.getNumber();
755:                        if (convertedNodes.containsKey(number)) {
756:                            // converting the node...
757:                            results.set(i, convertedNodes.get(number));
758:                            convertedCount++;
759:                        }
760:                        current = results.get(i);
761:                        assert current.getNumber() >= 0;
762:                    }
763:                } else if (convert.size() != 0) {
764:                    log.warn("we still need to convert " + convertCount
765:                            + " of the " + results.size() + " nodes"
766:                            + "(number of different types:" + convert.size()
767:                            + ")");
768:                }
769:                if (log.isDebugEnabled()) {
770:                    log.debug("retrieved " + results.size()
771:                            + " nodes, converted " + convertedCount
772:                            + " of the " + convertCount + " invalid nodes("
773:                            + convert.size() + " types, " + cacheGetCount
774:                            + " from cache, " + cachePutCount + " to cache)");
775:                }
776:            }
777:
778:            /**
779:             * Creates search query that retrieves nodes matching a specified
780:             * constraint.
781:             *
782:             * @param where The constraint, can be a SQL where-clause, a MMNODE
783:             *        expression, an altavista-formatted expression, empty or
784:             *        <code>null</code>.
785:             * @return The query.
786:             * @since MMBase-1.7
787:             */
788:            public NodeSearchQuery getSearchQuery(String where) {
789:                NodeSearchQuery query;
790:
791:                if (where != null && where.startsWith("MMNODE ")) {
792:                    // MMNODE expression.
793:                    query = convertMMNodeSearch2Query(where);
794:                } else {
795:                    query = new NodeSearchQuery(builder);
796:                    QueryConvertor.setConstraint(query, where);
797:                }
798:
799:                return query;
800:            }
801:
802:            /**
803:             * Creates query based on an MMNODE expression.
804:             *
805:             * @deprecated MMNODE expressions are deprecated, scan only?
806:             * @param expr The MMNODE expression.
807:             * @return The query.
808:             * @throws IllegalArgumentException when an invalid argument is supplied.
809:             */
810:            private NodeSearchQuery convertMMNodeSearch2Query(String expr) {
811:                NodeSearchQuery query = new NodeSearchQuery(builder);
812:                BasicCompositeConstraint constraints = new BasicCompositeConstraint(
813:                        CompositeConstraint.LOGICAL_AND);
814:                String logicalOperator = null;
815:
816:                // Strip leading string "MMNODE " from expression, parse
817:                // fieldexpressions and logical operators.
818:                // (legacy: eol characters '\n' and '\r' are interpreted as "AND NOT")
819:                StringTokenizer tokenizer = new StringTokenizer(expr
820:                        .substring(7), "+-\n\r", true);
821:                while (tokenizer.hasMoreTokens()) {
822:                    String fieldExpression = tokenizer.nextToken();
823:
824:                    // Remove prefix if present (example episodes.title==).
825:                    int pos = fieldExpression.indexOf('.');
826:                    if (pos != -1) {
827:                        fieldExpression = fieldExpression.substring(pos + 1);
828:                    }
829:
830:                    // Break up field expression in fieldname, comparison operator
831:                    // and value.
832:                    pos = fieldExpression.indexOf('=');
833:                    if (pos != -1 && fieldExpression.length() > pos + 2) {
834:                        String fieldName = fieldExpression.substring(0, pos);
835:                        char comparison = fieldExpression.charAt(pos + 1);
836:                        String value = fieldExpression.substring(pos + 2);
837:
838:                        // Add corresponding constraint to constraints.
839:                        CoreField field = builder.getField(fieldName);
840:                        if (field == null) {
841:                            throw new IllegalArgumentException(
842:                                    "Invalid MMNODE expression: " + expr);
843:                        }
844:                        StepField stepField = query.getField(field);
845:                        BasicConstraint constraint = parseFieldPart(stepField,
846:                                comparison, value);
847:                        constraints.addChild(constraint);
848:
849:                        // Set to inverse if preceded by a logical operator that is
850:                        // not equal to "+".
851:                        if (logicalOperator != null
852:                                && !logicalOperator.equals("+")) {
853:                            constraint.setInverse(true);
854:                        }
855:                    } else {
856:                        // Invalid expression.
857:                        throw new IllegalArgumentException(
858:                                "Invalid MMNODE expression: " + expr);
859:                    }
860:
861:                    // Read next logical operator.
862:                    if (tokenizer.hasMoreTokens()) {
863:                        logicalOperator = tokenizer.nextToken();
864:                    }
865:                }
866:
867:                List<Constraint> childs = constraints.getChilds();
868:                if (childs.size() == 1) {
869:                    query.setConstraint(childs.get(0));
870:                } else if (childs.size() > 1) {
871:                    query.setConstraint(constraints);
872:                }
873:                return query;
874:            }
875:
876:            /**
877:             * Creates a {@link org.mmbase.storage.search.FieldCompareConstraint
878:             * FieldCompareConstraint}, based on parts of a field expression in a
879:             * MMNODE expression.
880:             *
881:             * @deprecated MMNODE expressions are deprecated
882:             * @param field The field
883:             * @param comparison The second character of the comparison operator.
884:             * @param strValue The value to compare with, represented as
885:             *        <code>String<code>.
886:             * @return The constraint.
887:             */
888:            private BasicFieldValueConstraint parseFieldPart(StepField field,
889:                    char comparison, String strValue) {
890:
891:                Object value = strValue;
892:
893:                // For numberical fields, convert string representation to Double.
894:                if (field.getType() != Field.TYPE_STRING
895:                        && field.getType() != Field.TYPE_XML
896:                        && field.getType() != Field.TYPE_UNKNOWN) {
897:                    // backwards comp fix. This is needed for the scan editors.
898:                    int length = strValue.length();
899:                    if (strValue.charAt(0) == '*'
900:                            && strValue.charAt(length - 1) == '*') {
901:                        strValue = strValue.substring(1, length - 1);
902:                    }
903:                    value = Double.valueOf(strValue);
904:                }
905:
906:                BasicFieldValueConstraint constraint = new BasicFieldValueConstraint(
907:                        field, value);
908:
909:                switch (comparison) {
910:                case '=':
911:                case 'E':
912:                    // EQUAL (string field)
913:                    if (field.getType() == Field.TYPE_STRING
914:                            || field.getType() == Field.TYPE_XML) {
915:                        // Strip first and last character of value, when
916:                        // equal to '*'.
917:                        String str = (String) value;
918:                        int length = str.length();
919:                        if (str.charAt(0) == '*'
920:                                && str.charAt(length - 1) == '*') {
921:                            value = str.substring(1, length - 1);
922:                        }
923:
924:                        // Convert to LIKE comparison with wildchard characters
925:                        // before and after (legacy).
926:                        constraint.setValue('%' + (String) value + '%');
927:                        constraint.setCaseSensitive(false);
928:                        constraint.setOperator(FieldCompareConstraint.LIKE);
929:
930:                        // EQUAL (numerical field)
931:                    } else {
932:                        constraint.setOperator(FieldCompareConstraint.EQUAL);
933:                    }
934:                    break;
935:
936:                case 'N':
937:                    constraint.setOperator(FieldCompareConstraint.NOT_EQUAL);
938:                    break;
939:
940:                case 'G':
941:                    constraint.setOperator(FieldCompareConstraint.GREATER);
942:                    break;
943:
944:                case 'g':
945:                    constraint
946:                            .setOperator(FieldCompareConstraint.GREATER_EQUAL);
947:                    break;
948:
949:                case 'S':
950:                    constraint.setOperator(FieldCompareConstraint.LESS);
951:                    break;
952:
953:                case 's':
954:                    constraint.setOperator(FieldCompareConstraint.LESS_EQUAL);
955:                    break;
956:
957:                default:
958:                    throw new IllegalArgumentException(
959:                            "Invalid comparison character: '" + comparison
960:                                    + "'");
961:                }
962:                return constraint;
963:            }
964:
965:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.