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


0001:        /*
0002:
0003:        This software is OSI Certified Open Source Software.
0004:        OSI Certified is a certification mark of the Open Source Initiative.
0005:
0006:        The license (Mozilla version 1.0) can be read at the MMBase site.
0007:        See http://www.MMBase.org/license
0008:
0009:         */
0010:        package org.mmbase.module.core;
0011:
0012:        import java.util.*;
0013:        import java.util.Map.Entry;
0014:
0015:        import org.mmbase.module.corebuilders.*;
0016:        import org.mmbase.core.CoreField;
0017:        import org.mmbase.bridge.Field;
0018:        import org.mmbase.core.util.Fields;
0019:        import org.mmbase.util.functions.*;
0020:        import org.mmbase.datatypes.*;
0021:        import org.mmbase.storage.search.*;
0022:        import org.mmbase.storage.search.implementation.*;
0023:        import org.mmbase.storage.search.legacy.ConstraintParser;
0024:        import org.mmbase.util.QueryConvertor;
0025:        import org.mmbase.util.logging.*;
0026:
0027:        /**
0028:         * The builder for {@link ClusterNode clusternodes}.
0029:         * <p>
0030:         * Provides these methods to retrieve clusternodes:
0031:         * <ul>
0032:         *      <li>{@link #getClusterNodes(SearchQuery)}
0033:         *          to retrieve clusternodes using a <code>SearchQuery</code> (recommended).
0034:         *      <li>{@link #getMultiLevelSearchQuery(List,List,String,List,String,List,List,int)}
0035:         *            as a convenience method to create a <code>SearchQuery</code>
0036:         *      <li>{@link #searchMultiLevelVector(List,List,String,List,String,List,List,int)}
0037:         *            to retrieve clusternodes using a constraint string.
0038:         * </ul>
0039:         * <p>
0040:         * Individual nodes in a 'cluster' node can be retrieved by calling the node's
0041:         * {@link MMObjectNode#getNodeValue(String) getNodeValue()} method, using
0042:         * the builder name (or step alias) as argument.
0043:         *
0044:         * @todo XXXX. This 'builder' is actually singleton (only one instance is created).  It does
0045:         *             therefore not support getFields, so this is more or less hacked in bridge.  Perhaps in 'core' a
0046:         *             similar approach as now in birdge must be taken, so no ClusterBuilder, but only Virtual builders,
0047:         *             one for every query result.
0048:         *
0049:         *
0050:         * @author Rico Jansen
0051:         * @author Pierre van Rooden
0052:         * @author Rob van Maris
0053:         * @version $Id: ClusterBuilder.java,v 1.93 2008/02/03 17:33:57 nklasens Exp $
0054:         * @see ClusterNode
0055:         */
0056:        public class ClusterBuilder extends VirtualBuilder {
0057:
0058:            /**
0059:             * Search for all valid relations.
0060:             * When searching relations, return both relations from source to deastination and from destination to source,
0061:             * provided there is an allowed relation in that directon.
0062:             * @deprecated use {@link RelationStep#DIRECTIONS_BOTH}
0063:             *             In future versions of MMBase (1.8 and up) this will be the default value
0064:             */
0065:            public static final int SEARCH_BOTH = RelationStep.DIRECTIONS_BOTH;
0066:
0067:            /**
0068:             * Search for destinations,
0069:             * When searching relations, return only relations from source to deastination.
0070:             * @deprecated use {@link RelationStep#DIRECTIONS_DESTINATION}
0071:             */
0072:            public static final int SEARCH_DESTINATION = RelationStep.DIRECTIONS_DESTINATION;
0073:
0074:            /**
0075:             * Seach for sources.
0076:             * When searching a multilevel, return only relations from destination to source, provided directionality allows
0077:             * @deprecated use {@link RelationStep#DIRECTIONS_SOURCE}
0078:             */
0079:            public static final int SEARCH_SOURCE = RelationStep.DIRECTIONS_SOURCE;
0080:
0081:            /**
0082:             * Search for all relations.  When searching a multilevel, return both relations from source to
0083:             * deastination and from destination to source.  Allowed relations are not checked - ALL
0084:             * relations are used. This makes more inefficient queries, but it is not really wrong.
0085:             * @deprecated use {@link RelationStep#DIRECTIONS_ALL}
0086:             */
0087:            public static final int SEARCH_ALL = RelationStep.DIRECTIONS_ALL;
0088:
0089:            /**
0090:             * Search for either destination or source.
0091:             * When searching a multilevel, return either relations from source to destination OR from destination to source.
0092:             * The returned set is decided through the typerel tabel. However, if both directions ARE somehow supported, the
0093:             * system only returns source to destination relations.
0094:             * This is the default value (for compatibility purposes).
0095:             * @deprecated use {@link RelationStep#DIRECTIONS_EITHER}.
0096:             *             In future versions of MMBase (1.8 and up) the default value will be
0097:             *             {@link RelationStep#DIRECTIONS_BOTH}
0098:             */
0099:            public static final int SEARCH_EITHER = RelationStep.DIRECTIONS_EITHER;
0100:
0101:            // logging variable
0102:            private static final Logger log = Logging
0103:                    .getLoggerInstance(ClusterBuilder.class);
0104:
0105:            /**
0106:             * Creates <code>ClusterBuilder</code> instance.
0107:             * Must be called from the MMBase class.
0108:             * @param m the MMbase cloud creating the node
0109:             * @scope package
0110:             */
0111:            public ClusterBuilder(MMBase m) {
0112:                super (m, "clusternodes");
0113:            }
0114:
0115:            /**
0116:             * Translates a string to a search direction constant.
0117:             *
0118:             * @since MMBase-1.6
0119:             */
0120:            public static int getSearchDir(String search) {
0121:                if (search == null) {
0122:                    return RelationStep.DIRECTIONS_EITHER;
0123:                }
0124:                return org.mmbase.bridge.util.Queries
0125:                        .getRelationStepDirection(search);
0126:            }
0127:
0128:            /**
0129:             * Translates a search direction constant to a string.
0130:             *
0131:             * @since MMBase-1.6
0132:             */
0133:            public static String getSearchDirString(int search) {
0134:                if (search == RelationStep.DIRECTIONS_DESTINATION) {
0135:                    return "DESTINATION";
0136:                } else if (search == RelationStep.DIRECTIONS_SOURCE) {
0137:                    return "SOURCE";
0138:                } else if (search == RelationStep.DIRECTIONS_BOTH) {
0139:                    return "BOTH";
0140:                } else if (search == RelationStep.DIRECTIONS_ALL) {
0141:                    return "ALL";
0142:                } else {
0143:                    return "EITHER";
0144:                }
0145:            }
0146:
0147:            /**
0148:             * Get a new node, using this builder as its parent.
0149:             * The new node is a cluster node.
0150:             * Unlike most other nodes, a cluster node does not have a number,
0151:             * owner, or otype fields.
0152:             * @param owner The administrator creating the new node (ignored).
0153:             * @return A newly initialized <code>VirtualNode</code>.
0154:             */
0155:            public MMObjectNode getNewNode(String owner) {
0156:                throw new UnsupportedOperationException(
0157:                        "One cannot create new ClusterNodes");
0158:            }
0159:
0160:            /**
0161:             * What should a GUI display for this node.
0162:             * This version displays the contents of the 'name' field(s) that were retrieved.
0163:             * XXX: should be changed to something better
0164:             * @param node The node to display
0165:             * @return the display of the node as a <code>String</code>
0166:             */
0167:            public String getGUIIndicator(MMObjectNode node) {
0168:                // Return "name"-field when available.
0169:                String s = node.getStringValue("name");
0170:                if (s != null) {
0171:                    return s;
0172:                }
0173:
0174:                // Else "name"-fields of contained nodes.
0175:                StringBuilder sb = new StringBuilder();
0176:                for (Entry<String, Object> entry : node.getValues().entrySet()) {
0177:                    String key = entry.getKey();
0178:                    if (key.endsWith(".name")) {
0179:                        if (s.length() != 0) {
0180:                            sb.append(", ");
0181:                        }
0182:                        sb.append(entry.getValue());
0183:                    }
0184:                }
0185:                if (sb.length() > 15) {
0186:                    return sb.substring(0, 12) + "...";
0187:                } else {
0188:                    return sb.toString();
0189:                }
0190:            }
0191:
0192:            /**
0193:             * What should a GUI display for this node/field combo.
0194:             * For a multilevel node, the builder tries to determine
0195:             * the original builder of a field, and invoke the method using
0196:             * that builder.
0197:             *
0198:             * @param node The node to display
0199:             * @param pars Parameters, see {@link MMObjectBuilder#GUI_PARAMETERS}
0200:             * @return the display of the node's field as a <code>String</code>, null if not specified
0201:             */
0202:            public String getGUIIndicator(MMObjectNode node, Parameters pars) {
0203:
0204:                if (node == null)
0205:                    throw new RuntimeException(
0206:                            "Tried to get GUIIndicator for  " + pars
0207:                                    + " with NULL node");
0208:
0209:                ClusterNode clusterNode = (ClusterNode) node;
0210:
0211:                String field = pars.getString(Parameter.FIELD);
0212:                if (field == null) {
0213:                    return super .getGUIIndicator(node, pars);
0214:                } else {
0215:                    int pos = field.indexOf('.');
0216:                    if (pos != -1) {
0217:                        String bulName = getTrueTableName(field.substring(0,
0218:                                pos));
0219:                        MMObjectNode n = clusterNode.getRealNode(bulName);
0220:                        if (n != null) {
0221:                            MMObjectBuilder bul = n.getBuilder();
0222:                            if (bul != null) {
0223:                                // what are we trying here?
0224:                                String fieldName = field.substring(pos + 1);
0225:                                Parameters newPars = new Parameters(pars
0226:                                        .getDefinition(), pars);
0227:                                newPars.set(Parameter.FIELD, fieldName);
0228:                                newPars.set("stringvalue", null);
0229:                                org.mmbase.bridge.Node bnode = pars
0230:                                        .get(Parameter.NODE);
0231:                                if (bnode != null) {
0232:                                    newPars.set(Parameter.NODE, bnode
0233:                                            .getNodeValue(bulName));
0234:                                }
0235:                                newPars.set(Parameter.CORENODE, n);
0236:                                return bul.guiFunction
0237:                                        .getFunctionValue(newPars);
0238:                            }
0239:                        }
0240:                    }
0241:                    return super .getGUIIndicator(node, pars);
0242:                }
0243:            }
0244:
0245:            /**
0246:             * Determines the builder part of a specified field.
0247:             * @param fieldName the name of the field
0248:             * @return the name of the field's builder
0249:             */
0250:            public String getBuilderNameFromField(String fieldName) {
0251:                int pos = fieldName.indexOf(".");
0252:                if (pos != -1) {
0253:                    String bulName = fieldName.substring(0, pos);
0254:                    return getTrueTableName(bulName);
0255:                }
0256:                return "";
0257:            }
0258:
0259:            /**
0260:             * Determines the fieldname part of a specified field (without the builder name).
0261:             * @param fieldname the name of the field
0262:             * @return the name of the field without its builder
0263:             */
0264:            public static String getFieldNameFromField(String fieldname) {
0265:                int pos = fieldname.indexOf(".");
0266:                if (pos != -1) {
0267:                    fieldname = fieldname.substring(pos + 1);
0268:                }
0269:                return fieldname;
0270:            }
0271:
0272:            /**
0273:             * Return a field.
0274:             * @param fieldName the requested field's name
0275:             * @return the field
0276:             */
0277:            public FieldDefs getField(String fieldName) {
0278:                String builderName = getBuilderNameFromField(fieldName);
0279:                if (builderName.length() > 0) {
0280:                    MMObjectBuilder bul = mmb.getBuilder(builderName);
0281:                    if (bul == null) {
0282:                        throw new RuntimeException("No builder with name '"
0283:                                + builderName + "' found");
0284:                    }
0285:                    return bul.getField(getFieldNameFromField(fieldName));
0286:                } else {
0287:                    //
0288:                    MMObjectBuilder bul = mmb
0289:                            .getBuilder(getTrueTableName(fieldName));
0290:                    if (bul != null) {
0291:                        return new FieldDefs(fieldName, Field.TYPE_NODE, -1,
0292:                                Field.STATE_VIRTUAL,
0293:                                org.mmbase.datatypes.DataTypes
0294:                                        .getDataType("node"));
0295:                    }
0296:                }
0297:                return null;
0298:            }
0299:
0300:            public List<CoreField> getFields(int order) {
0301:                throw new UnsupportedOperationException(
0302:                        "Cluster-nodes can have any field.");
0303:            }
0304:
0305:            public Collection<CoreField> getFields() {
0306:                throw new UnsupportedOperationException(
0307:                        "Cluster-nodes can have any field.");
0308:            }
0309:
0310:            /**
0311:             * @since MMBase-1.8
0312:             */
0313:            public Map<String, CoreField> getFields(MMObjectNode node) {
0314:                Map<String, CoreField> ret = new HashMap<String, CoreField>();
0315:                Iterator<String> i = node.getValues().keySet().iterator();
0316:                DataType<? extends Object> nodeType = DataTypes
0317:                        .getDataType("node");
0318:                while (i.hasNext()) {
0319:                    String name = i.next();
0320:                    int pos = name.indexOf(".");
0321:                    if (pos != -1) {
0322:                        String builderName = name.substring(0, pos);
0323:                        if (!ret.containsKey(builderName)) {
0324:                            CoreField fd = Fields.createField(builderName,
0325:                                    Field.TYPE_NODE, Field.TYPE_UNKNOWN,
0326:                                    Field.STATE_VIRTUAL, nodeType);
0327:                            ret.put(builderName, fd);
0328:                        }
0329:                    }
0330:                    ret.put(name, getField(name));
0331:                }
0332:                return ret;
0333:            }
0334:
0335:            /**
0336:             * Same as {@link #searchMultiLevelVector(List,List,String,List,String,List,List,int)
0337:             * searchMultiLevelVector(snodes, fields, pdistinct, tables, where, orderVec, direction, RelationStep.DIRECTIONS_EITHER)},
0338:             * where <code>snodes</code> contains just the number specified by <code>snode</code>.
0339:             *
0340:             * @see #searchMultiLevelVector(List, List, String, List, String, List, List, List)
0341:             */
0342:            public Vector<MMObjectNode> searchMultiLevelVector(int snode,
0343:                    Vector<String> fields, String pdistinct,
0344:                    Vector<String> tables, String where,
0345:                    Vector<String> orderVec, Vector<String> direction) {
0346:
0347:                List<String> v = new ArrayList<String>();
0348:                v.add("" + snode);
0349:                return searchMultiLevelVector(v, fields, pdistinct, tables,
0350:                        where, orderVec, direction,
0351:                        RelationStep.DIRECTIONS_EITHER);
0352:            }
0353:
0354:            /**
0355:             * Same as {@link #searchMultiLevelVector(List,List,String,List,String,List,List,int)
0356:             * searchMultiLevelVector(snodes, fields, pdistinct, tables, where, orderVec, direction, RelationStep.DIRECTIONS_EITHER)}.
0357:             *
0358:             * @see #searchMultiLevelVector(List,List,String,List,String,List,List,int)
0359:             */
0360:            public Vector<MMObjectNode> searchMultiLevelVector(
0361:                    Vector<String> snodes, Vector<String> fields,
0362:                    String pdistinct, Vector<String> tables, String where,
0363:                    Vector<String> orderVec, Vector<String> direction) {
0364:                return searchMultiLevelVector(snodes, fields, pdistinct,
0365:                        tables, where, orderVec, direction,
0366:                        RelationStep.DIRECTIONS_EITHER);
0367:            }
0368:
0369:            /**
0370:             * Return all the objects that match the searchkeys.
0371:             * The constraint must be in one of the formats specified by {@link
0372:             * org.mmbase.util.QueryConvertor#setConstraint(BasicSearchQuery,String)
0373:             * QueryConvertor#setConstraint()}.
0374:             *
0375:             * @param snodes The numbers of the nodes to start the search with. These have to be present in the first table
0376:             *      listed in the tables parameter.
0377:             * @param fields The fieldnames to return. This should include the name of the builder. Fieldnames without a builder prefix are ignored.
0378:             *      Fieldnames are accessible in the nodes returned in the same format (i.e. with manager indication) as they are specified in this parameter.
0379:             *      Examples: 'people.lastname'
0380:             * @param pdistinct 'YES' indicates the records returned need to be distinct. Any other value indicates double values can be returned.
0381:             * @param tables The builder chain. A list containing builder names.
0382:             *      The search is formed by following the relations between successive builders in the list. It is possible to explicitly supply
0383:             *      a relation builder by placing the name of the builder between two builders to search.
0384:             *      Example: company,people or typedef,authrel,people.
0385:             * @param where The constraint, must be in one of the formats specified by {@link
0386:             *        org.mmbase.util.QueryConvertor#setConstraint(BasicSearchQuery,String)
0387:             *        QueryConvertor#setConstraint()}.
0388:             *        E.g. "WHERE news.title LIKE '%MMBase%' AND news.title > 100"
0389:             * @param sortFields the fieldnames on which you want to sort.
0390:             * @param directions A list of values containing, for each field in the order parameter, a value indicating whether the sort is
0391:             *      ascending (<code>UP</code>) or descending (<code>DOWN</code>). If less values are syupplied then there are fields in order,
0392:             *      the first value in the list is used for the remaining fields. Default value is <code>'UP'</code>.
0393:             * @param searchDir Specifies in which direction relations are to be
0394:             *      followed, this must be one of the values defined by this class.
0395:             * @return a <code>Vector</code> containing all matching nodes
0396:             * @deprecated use {@link #searchMultiLevelVector(List snodes, List fields, String pdistinct, List tables, String where,
0397:             *               List orderVec, List directions, List searchDirs)}
0398:             */
0399:            public Vector<MMObjectNode> searchMultiLevelVector(
0400:                    List<String> snodes, List<String> fields, String pdistinct,
0401:                    List<String> tables, String where, List<String> sortFields,
0402:                    List<String> directions, int searchDir) {
0403:                List<Integer> searchDirs = new ArrayList<Integer>();
0404:                searchDirs.add(searchDir);
0405:                return searchMultiLevelVector(snodes, fields, pdistinct,
0406:                        tables, where, sortFields, directions, searchDirs);
0407:            }
0408:
0409:            /**
0410:             * Return all the objects that match the searchkeys.
0411:             * The constraint must be in one of the formats specified by {@link
0412:             * org.mmbase.util.QueryConvertor#setConstraint(BasicSearchQuery,String)
0413:             * QueryConvertor#setConstraint()}.
0414:             *
0415:             * @param snodes The numbers of the nodes to start the search with. These have to be present in the first table
0416:             *      listed in the tables parameter.
0417:             * @param fields The fieldnames to return. This should include the name of the builder. Fieldnames without a builder prefix are ignored.
0418:             *      Fieldnames are accessible in the nodes returned in the same format (i.e. with manager indication) as they are specified in this parameter.
0419:             *      Examples: 'people.lastname'
0420:             * @param pdistinct 'YES' indicates the records returned need to be distinct. Any other value indicates double values can be returned.
0421:             * @param tables The builder chain. A list containing builder names.
0422:             *      The search is formed by following the relations between successive builders in the list. It is possible to explicitly supply
0423:             *      a relation builder by placing the name of the builder between two builders to search.
0424:             *      Example: company,people or typedef,authrel,people.
0425:             * @param where The constraint, must be in one of the formats specified by {@link
0426:             *        org.mmbase.util.QueryConvertor#setConstraint(BasicSearchQuery,String)
0427:             *        QueryConvertor#setConstraint()}.
0428:             *        E.g. "WHERE news.title LIKE '%MMBase%' AND news.title > 100"
0429:             * @param sortFields the fieldnames on which you want to sort.
0430:             * @param directions A list of values containing, for each field in the order parameter, a value indicating whether the sort is
0431:             *      ascending (<code>UP</code>) or descending (<code>DOWN</code>). If less values are syupplied then there are fields in order,
0432:             *      the first value in the list is used for the remaining fields. Default value is <code>'UP'</code>.
0433:             * @param searchDirs Specifies in which direction relations are to be followed. You can specify a direction for each
0434:             *      relation in the path. If you specify less directions than there are relations, the last specified direction is used
0435:             *      for the remaining relations. If you specify an empty list the default direction is BOTH.
0436:             * @return a <code>Vector</code> containing all matching nodes
0437:             */
0438:            public Vector<MMObjectNode> searchMultiLevelVector(
0439:                    List<String> snodes, List<String> fields, String pdistinct,
0440:                    List<String> tables, String where, List<String> sortFields,
0441:                    List<String> directions, List<Integer> searchDirs) {
0442:                // Try to handle using the SearchQuery framework.
0443:                try {
0444:                    SearchQuery query = getMultiLevelSearchQuery(snodes,
0445:                            fields, pdistinct, tables, where, sortFields,
0446:                            directions, searchDirs);
0447:                    List<MMObjectNode> clusterNodes = getClusterNodes(query);
0448:                    return new Vector<MMObjectNode>(clusterNodes);
0449:                } catch (Exception e) {
0450:                    log.error(e.getMessage(), e);
0451:                    return null;
0452:                }
0453:            }
0454:
0455:            /**
0456:             * Executes query, returns results as {@link ClusterNode clusternodes} or MMObjectNodes if the
0457:             * query is a Node-query.
0458:             *
0459:             * @param query The query.
0460:             * @return The clusternodes.
0461:             * @throws org.mmbase.storage.search.SearchQueryException
0462:             *         When an exception occurred while retrieving the results.
0463:             * @since MMBase-1.7
0464:             * @see org.mmbase.storage.search.SearchQueryHandler#getNodes
0465:             */
0466:            public List<MMObjectNode> getClusterNodes(SearchQuery query)
0467:                    throws SearchQueryException {
0468:
0469:                // TODO (later): implement maximum set by maxNodesFromQuery?
0470:                // Execute query, return results.
0471:
0472:                return mmb.getSearchQueryHandler().getNodes(query, this );
0473:
0474:            }
0475:
0476:            /**
0477:             * Returns the name part of a tablename.
0478:             * The name part is the table name minus the numeric digit appended
0479:             * to a name (if appliable).
0480:             * @param table name of the original table
0481:             * @return A <code>String</code> containing the table name
0482:             */
0483:            private String getTableName(String table) {
0484:                int end = table.length();
0485:                if (end == 0)
0486:                    throw new IllegalArgumentException("Table name too short '"
0487:                            + table + "'");
0488:                while (Character.isDigit(table.charAt(end - 1)))
0489:                    --end;
0490:                return table.substring(0, end);
0491:            }
0492:
0493:            /**
0494:             * Returns the name part of a tablename, and convert it to a buidler name.
0495:             * This will catch specifying a rolename in stead of a builder name when using relations.
0496:             * @param table name of the original table
0497:             * @return A <code>String</code> containing the table name
0498:             */
0499:            private String getTrueTableName(String table) {
0500:                String tab = getTableName(table);
0501:                int rnumber = mmb.getRelDef().getNumberByName(tab);
0502:                if (rnumber != -1) {
0503:                    return mmb.getRelDef().getBuilderName(rnumber);
0504:                } else {
0505:                    return tab;
0506:                }
0507:            }
0508:
0509:            /**
0510:             * Get text from a blob field.
0511:             * The text is cut if it is to long.
0512:             * @param fieldname name of the field
0513:             * @param number number of the object in the table
0514:             * @return a <code>String</code> containing the contents of a field as text
0515:             */
0516:            public String getShortedText(String fieldname, int number) {
0517:                String buildername = getBuilderNameFromField(fieldname);
0518:                if (buildername.length() > 0) {
0519:                    MMObjectBuilder bul = mmb.getMMObject(buildername);
0520:                    return bul.getShortedText(getFieldNameFromField(fieldname),
0521:                            bul.getNode(number));
0522:                }
0523:                return null;
0524:            }
0525:
0526:            /**
0527:             * Get binary data of a database blob field.
0528:             * The data is cut if it is to long.
0529:             * @param fieldname name of the field
0530:             * @param number number of the object in the table
0531:             * @return an array of <code>byte</code> containing the contents of a field as text
0532:             */
0533:            public byte[] getShortedByte(String fieldname, int number) {
0534:                String buildername = getBuilderNameFromField(fieldname);
0535:                if (buildername.length() > 0) {
0536:                    MMObjectBuilder bul = mmb.getMMObject(buildername);
0537:                    return bul.getShortedByte(getFieldNameFromField(fieldname),
0538:                            bul.getNode(number));
0539:                }
0540:                return null;
0541:            }
0542:
0543:            /**
0544:             * Creates search query that selects all the objects that match the
0545:             * searchkeys.
0546:             * The constraint must be in one of the formats specified by {@link
0547:             * org.mmbase.util.QueryConvertor#setConstraint(BasicSearchQuery,String)
0548:             * QueryConvertor#setConstraint()}.
0549:             *
0550:             * @param snodes <code>null</code> or a list of numbers
0551:             *        of nodes to start the search with.
0552:             *        These have to be present in the first table listed in the
0553:             *        tables parameter.
0554:             * @param fields List of fieldnames to return.
0555:             *        These should be formatted as <em>stepalias.field</em>,
0556:             *        e.g. 'people.lastname'
0557:             * @param pdistinct 'YES' if the records returned need to be
0558:             *        distinct (ignoring case).
0559:             *        Any other value indicates double values can be returned.
0560:             * @param tables The builder chain, a list containing builder names.
0561:             *        The search is formed by following the relations between
0562:             *        successive builders in the list.
0563:             *        It is possible to explicitly supply a relation builder by
0564:             *        placing the name of the builder between two builders to search.
0565:             *        Example: company,people or typedef,authrel,people.
0566:             * @param where The constraint, must be in one of the formats specified by {@link
0567:             *        org.mmbase.util.QueryConvertor#setConstraint(BasicSearchQuery,String)
0568:             *        QueryConvertor#setConstraint()}.
0569:             *        E.g. "WHERE news.title LIKE '%MMBase%' AND news.title > 100"
0570:             * @param sortFields <code>null</code> or a list of  fieldnames on which you want to sort.
0571:             * @param directions <code>null</code> or a list of values containing, for each field in the
0572:             *        <code>sortFields</code> parameter, a value indicating whether the sort is
0573:             *        ascending (<code>UP</code>) or descending (<code>DOWN</code>).
0574:             *        If less values are supplied then there are fields in order,
0575:             *        the first value in the list is used for the remaining fields.
0576:             *        Default value is <code>'UP'</code>.
0577:             * @param searchDir Specifies in which direction relations are to be
0578:             *        followed, this must be one of the values defined by this class.
0579:             * @deprecated use {@link #getMultiLevelSearchQuery(List snodes, List fields, String pdistinct, List tables, String where,
0580:             *               List orderVec, List directions, int searchDir)}
0581:             * @return the resulting search query.
0582:             * @since MMBase-1.7
0583:             */
0584:            public BasicSearchQuery getMultiLevelSearchQuery(
0585:                    List<String> snodes, List<String> fields, String pdistinct,
0586:                    List<String> tables, String where, List<String> sortFields,
0587:                    List<String> directions, int searchDir) {
0588:                List<Integer> searchDirs = new ArrayList<Integer>();
0589:                searchDirs.add(searchDir);
0590:                return getMultiLevelSearchQuery(snodes, fields, pdistinct,
0591:                        tables, where, sortFields, directions, searchDirs);
0592:            }
0593:
0594:            /**
0595:             * Creates search query that selects all the objects that match the
0596:             * searchkeys.
0597:             * The constraint must be in one of the formats specified by {@link
0598:             * org.mmbase.util.QueryConvertor#setConstraint(BasicSearchQuery,String)
0599:             * QueryConvertor#setConstraint()}.
0600:             *
0601:             * @param snodes <code>null</code> or a list of numbers
0602:             *        of nodes to start the search with.
0603:             *        These have to be present in the first table listed in the
0604:             *        tables parameter.
0605:             * @param fields List of fieldnames to return.
0606:             *        These should be formatted as <em>stepalias.field</em>,
0607:             *        e.g. 'people.lastname'
0608:             * @param pdistinct 'YES' if the records returned need to be
0609:             *        distinct (ignoring case).
0610:             *        Any other value indicates double values can be returned.
0611:             * @param tables The builder chain, a list containing builder names.
0612:             *        The search is formed by following the relations between
0613:             *        successive builders in the list.
0614:             *        It is possible to explicitly supply a relation builder by
0615:             *        placing the name of the builder between two builders to search.
0616:             *        Example: company,people or typedef,authrel,people.
0617:             * @param where The constraint, must be in one of the formats specified by {@link
0618:             *        org.mmbase.util.QueryConvertor#setConstraint(BasicSearchQuery,String)
0619:             *        QueryConvertor#setConstraint()}.
0620:             *        E.g. "WHERE news.title LIKE '%MMBase%' AND news.title > 100"
0621:             * @param sortFields <code>null</code> or a list of  fieldnames on which you want to sort.
0622:             * @param directions <code>null</code> or a list of values containing, for each field in the
0623:             *        <code>sortFields</code> parameter, a value indicating whether the sort is
0624:             *        ascending (<code>UP</code>) or descending (<code>DOWN</code>).
0625:             *        If less values are supplied then there are fields in order,
0626:             *        the first value in the list is used for the remaining fields.
0627:             *        Default value is <code>'UP'</code>.
0628:             * @param searchDirs Specifies in which direction relations are to be
0629:             *        followed, this must be one of the values defined by this class.
0630:             * @return the resulting search query.
0631:             * @since MMBase-1.7
0632:             */
0633:            public BasicSearchQuery getMultiLevelSearchQuery(
0634:                    List<String> snodes, List<String> fields, String pdistinct,
0635:                    List<String> tables, String where, List<String> sortFields,
0636:                    List<String> directions, List<Integer> searchDirs) {
0637:
0638:                // Create the query.
0639:                BasicSearchQuery query = new BasicSearchQuery();
0640:
0641:                // Set the distinct property.
0642:                boolean distinct = pdistinct != null
0643:                        && pdistinct.equalsIgnoreCase("YES");
0644:                query.setDistinct(distinct);
0645:
0646:                // Get ALL tables (including missing reltables)
0647:                Map<String, Integer> roles = new HashMap<String, Integer>();
0648:                Map<String, BasicStepField> fieldsByAlias = new HashMap<String, BasicStepField>();
0649:                Map<String, BasicStep> stepsByAlias = addSteps(query, tables,
0650:                        roles, !distinct, fieldsByAlias);
0651:
0652:                // Add fields.
0653:                Iterator<String> iFields = fields.iterator();
0654:                while (iFields.hasNext()) {
0655:                    String field = iFields.next();
0656:                    addFields(query, field, stepsByAlias, fieldsByAlias);
0657:                }
0658:
0659:                // Add sortorders.
0660:                addSortOrders(query, sortFields, directions, fieldsByAlias);
0661:
0662:                // Supporting more then 1 source node or no source node at all
0663:                // Note that node number -1 is seen as no source node
0664:                if (snodes != null && snodes.size() > 0) {
0665:                    Integer nodeNumber = -1;
0666:
0667:                    // Copy list, so the original list is not affected.
0668:                    List<Integer> snodeNumbers = new ArrayList<Integer>();
0669:
0670:                    // Go trough the whole list of strings (each representing
0671:                    // either a nodenumber or an alias), convert all to Integer objects.
0672:                    // from last to first,,... since we want snode to be the one that
0673:                    // contains the first..
0674:                    for (int i = snodes.size() - 1; i >= 0; i--) {
0675:                        String str = snodes.get(i);
0676:                        try {
0677:                            nodeNumber = Integer.valueOf(str);
0678:                        } catch (NumberFormatException e) {
0679:                            // maybe it was not an integer, hmm lets look in OAlias
0680:                            // table then
0681:                            nodeNumber = mmb.getOAlias().getNumber(str);
0682:                            if (nodeNumber.intValue() < 0) {
0683:                                nodeNumber = 0;
0684:                            }
0685:                        }
0686:                        snodeNumbers.add(nodeNumber);
0687:                    }
0688:
0689:                    Step nodesStep = getNodesStep(query.getSteps(), nodeNumber
0690:                            .intValue());
0691:
0692:                    if (nodesStep == null) {
0693:                        // specified a node which is not of the type of one of the steps.
0694:                        // take as default the 'first' step (which will make the result empty, compatible with 1.6, bug #6440).
0695:                        nodesStep = query.getSteps().get(0);
0696:                    }
0697:
0698:                    Iterator<Integer> iNodeNumbers = snodeNumbers.iterator();
0699:                    while (iNodeNumbers.hasNext()) {
0700:                        Integer number = iNodeNumbers.next();
0701:                        nodesStep.addNode(number.intValue());
0702:                    }
0703:                }
0704:
0705:                addRelationDirections(query, searchDirs, roles);
0706:
0707:                // Add constraints.
0708:                // QueryConverter supports the old formats for backward compatibility.
0709:                QueryConvertor.setConstraint(query, where);
0710:
0711:                return query;
0712:            }
0713:
0714:            /**
0715:             * Creates a full chain of steps, adds these to the specified query.
0716:             * This includes adding necessary relation tables when not explicitly
0717:             * specified, and generating unique table aliases where necessary.
0718:             * Optionally adds "number"-fields for all tables in the original chain.
0719:             *
0720:             * @param query The searchquery.
0721:             * @param tables The original chain of tables.
0722:             * @param roles Map of tablenames mapped to <code>Integer</code> values,
0723:             *        representing the nodenumber of a corresponing RelDef node.
0724:             *        This method adds entries for table aliases that specify a role,
0725:             *        e.g. "related" or "related2".
0726:             * @param includeAllReference Indicates if the "number"-fields must
0727:             *        included in the query for all tables in the original chain.
0728:             * @param fieldsByAlias Map, mapping aliases (fieldname prefixed by table
0729:             *        alias) to the stepfields in the query. An entry is added for
0730:             *        each stepfield added to the query.
0731:             * @return Map, maps original table names to steps.
0732:             * @since MMBase-1.7
0733:             */
0734:            // package access!
0735:            Map<String, BasicStep> addSteps(BasicSearchQuery query,
0736:                    List<String> tables, Map<String, Integer> roles,
0737:                    boolean includeAllReference,
0738:                    Map<String, BasicStepField> fieldsByAlias) {
0739:
0740:                Map<String, BasicStep> stepsByAlias = new HashMap<String, BasicStep>(); // Maps original table names to steps.
0741:                Set<String> tableAliases = new HashSet<String>(); // All table aliases that are in use.
0742:
0743:                Iterator<String> iTables = tables.iterator();
0744:                if (iTables.hasNext()) {
0745:                    // First table.
0746:                    String tableName = iTables.next();
0747:                    MMObjectBuilder bul = getBuilder(tableName, roles);
0748:                    String tableAlias = getUniqueTableAlias(tableName,
0749:                            tableAliases, tables);
0750:                    BasicStep step = query.addStep(bul);
0751:                    step.setAlias(tableAlias);
0752:                    stepsByAlias.put(tableName, step);
0753:                    if (includeAllReference) {
0754:                        // Add number field.
0755:                        addField(query, step, "number", fieldsByAlias);
0756:                    }
0757:                }
0758:                while (iTables.hasNext()) {
0759:                    String tableName2 = iTables.next();
0760:                    MMObjectBuilder bul2 = getBuilder(tableName2, roles);
0761:                    BasicRelationStep relation;
0762:                    BasicStep step2;
0763:                    String tableName;
0764:                    if (bul2 instanceof  InsRel) {
0765:                        // Explicit relation step.
0766:                        tableName = tableName2;
0767:                        InsRel bul = (InsRel) bul2;
0768:                        tableName2 = iTables.next();
0769:                        bul2 = getBuilder(tableName2, roles);
0770:                        relation = query.addRelationStep(bul, bul2);
0771:                        step2 = (BasicStep) relation.getNext();
0772:
0773:                        // MM: setting aliases used to be _inside_ the includeAllReference-if.
0774:                        // but I don't see how that would make sense. Trying a while like this.
0775:                        relation.setAlias(tableName);
0776:                        step2.setAlias(tableName2);
0777:                        if (includeAllReference) {
0778:                            // Add number fields.
0779:                            addField(query, relation, "number", fieldsByAlias);
0780:                            addField(query, step2, "number", fieldsByAlias);
0781:                        }
0782:                        if (log.isDebugEnabled()) {
0783:                            log.debug("Created a relation step " + relation
0784:                                    + " (explicit)" + roles);
0785:                        }
0786:                    } else {
0787:                        // Not a relation, relation step is implicit.
0788:                        tableName = "insrel";
0789:                        InsRel bul = mmb.getInsRel();
0790:                        relation = query.addRelationStep(bul, bul2);
0791:                        step2 = (BasicStep) relation.getNext();
0792:                        step2.setAlias(tableName2); //see above
0793:                        if (includeAllReference) {
0794:                            // Add number field.
0795:                            addField(query, step2, "number", fieldsByAlias);
0796:                        }
0797:                        if (log.isDebugEnabled()) {
0798:                            log.debug("Created a relation step " + relation
0799:                                    + " (implicit)");
0800:                        }
0801:                    }
0802:                    String tableAlias = getUniqueTableAlias(tableName,
0803:                            tableAliases, tables);
0804:                    String tableAlias2 = getUniqueTableAlias(tableName2,
0805:                            tableAliases, tables);
0806:                    if (!tableName.equals(tableAlias)) {
0807:                        roles.put(tableAlias, roles.get(tableName));
0808:                    }
0809:                    relation.setAlias(tableAlias);
0810:                    step2.setAlias(tableAlias2);
0811:                    stepsByAlias.put(tableAlias, relation);
0812:                    stepsByAlias.put(tableAlias2, step2);
0813:                }
0814:                return stepsByAlias;
0815:            }
0816:
0817:            /**
0818:             * Gets builder corresponding to the specified table alias.
0819:             * This amounts to removing the optionally appended digit from the table
0820:             * alias, and interpreting the result as either a tablename or a relation
0821:             * role.
0822:             *
0823:             * @param tableAlias The table alias.
0824:             *        Must be tablename or relation role, optionally appended
0825:             *        with a digit, e.g. images, images3, related and related4.
0826:             * @param roles Map of tablenames mapped to <code>Integer</code> values,
0827:             *        representing the nodenumber of a corresponing RelDef node.
0828:             *        This method adds entries for table aliases that specify a role,
0829:             *        e.g. "related" or "related2".
0830:             * @return The builder.
0831:             * @since MMBase-1.7
0832:             */
0833:            // package access!
0834:            MMObjectBuilder getBuilder(String tableAlias,
0835:                    Map<String, Integer> roles) {
0836:                String tableName = getTableName(tableAlias);
0837:                // check builder - should throw exception if builder doesn't exist ?
0838:                MMObjectBuilder bul = null;
0839:                try {
0840:                    bul = mmb.getBuilder(tableName);
0841:                } catch (BuilderConfigurationException e) {
0842:                }
0843:
0844:                if (bul == null) {
0845:                    // check if it is a role name. if so, use the builder of the
0846:                    // rolename and store a filter on rnumber.
0847:                    int rnumber = mmb.getRelDef().getNumberByName(tableName);
0848:                    if (rnumber == -1) {
0849:                        String msg = "Specified builder " + tableName
0850:                                + " does not exist.";
0851:                        log.error(msg);
0852:                        throw new IllegalArgumentException(msg);
0853:                    } else {
0854:                        bul = mmb.getRelDef().getBuilder(rnumber); // relation builder
0855:                        roles.put(tableAlias, rnumber);
0856:                    }
0857:                } else if (bul instanceof  InsRel) {
0858:                    int rnumber = mmb.getRelDef().getNumberByName(tableName);
0859:                    if (rnumber != -1) {
0860:                        roles.put(tableAlias, rnumber);
0861:                    }
0862:                }
0863:                if (log.isDebugEnabled()) {
0864:                    log.debug("Resolved table alias \"" + tableAlias
0865:                            + "\" to builder \"" + bul.getTableName() + "\"");
0866:                }
0867:                return bul;
0868:            }
0869:
0870:            /**
0871:             * Returns unique table alias, must be tablename/rolename, optionally
0872:             * appended with a digit.
0873:             * Tests the provided table alias for uniqueness, generates alternative
0874:             * table alias if the provided alias is already in use.
0875:             *
0876:             * @param tableAlias The table alias.
0877:             * @param tableAliases The table aliases that are already in use. The
0878:             *        resulting table alias is added to this collection.
0879:             * @param originalAliases The originally supplied aliases - generated
0880:             *        aliases should not match any of these.
0881:             * @return The resulting table alias.
0882:             * @since MMBase-1.7
0883:             */
0884:            // package access!
0885:            String getUniqueTableAlias(String tableAlias,
0886:                    Set<String> tableAliases, Collection<String> originalAliases) {
0887:
0888:                // If provided alias is not unique, try alternatives,
0889:                // skipping alternatives that are already in originalAliases.
0890:                if (tableAliases.contains(tableAlias)) {
0891:                    tableName = getTableName(tableAlias);
0892:
0893:                    tableAlias = tableName;
0894:                    char ch = '0';
0895:                    while (originalAliases.contains(tableAlias)
0896:                            || tableAliases.contains(tableAlias)) {
0897:                        // Can't create more than 11 aliases for same tablename.
0898:                        if (ch > '9') {
0899:                            throw new IndexOutOfBoundsException(
0900:                                    "Failed to create unique table alias, because there "
0901:                                            + "are already 11 aliases for this tablename: \""
0902:                                            + tableName + "\"");
0903:                        }
0904:                        tableAlias = tableName + ch;
0905:                        ch++;
0906:                    }
0907:                }
0908:
0909:                // Unique table alias: add to collection, return as result.
0910:                tableAliases.add(tableAlias);
0911:                return tableAlias;
0912:            }
0913:
0914:            /**
0915:             * Retrieves fieldnames from an expression, and adds these to a search
0916:             * query.
0917:             * The expression may be either a fieldname or a a functionname with a
0918:             * (commaseparated) parameterlist between parenthesis
0919:             * (parameters being expressions themselves).
0920:             * <p>
0921:             * Fieldnames must be formatted as <em>stepalias.field</em>.
0922:             *
0923:             * @param query The query.
0924:             * @param expression The expression.
0925:             * @param stepsByAlias Map, mapping step aliases to the steps in the query.
0926:             * @param fieldsByAlias Map, mapping field aliases (fieldname prefixed by
0927:             *        table alias) to the stepfields in the query.
0928:             *        An entry is added for each stepfield added to the query.
0929:             * @since MMBase-1.7
0930:             */
0931:            // package access!
0932:            void addFields(BasicSearchQuery query, String expression,
0933:                    Map<String, BasicStep> stepsByAlias,
0934:                    Map<String, BasicStepField> fieldsByAlias) {
0935:
0936:                // TODO RvM: stripping functions is this (still) necessary?.
0937:                // Strip function(s).
0938:                int pos1 = expression.indexOf('(');
0939:                int pos2 = expression.indexOf(')');
0940:                if (pos1 != -1 ^ pos2 != -1) {
0941:                    // Parenthesis do not match.
0942:                    throw new IllegalArgumentException(
0943:                            "Parenthesis do not match in expression: \""
0944:                                    + expression + "\"");
0945:                } else if (pos1 != -1) {
0946:                    // Function parameter list containing subexpression(s).
0947:                    String parameters = expression.substring(pos1 + 1, pos2);
0948:                    Iterator<String> iParameters = getFunctionParameters(
0949:                            parameters).iterator();
0950:                    while (iParameters.hasNext()) {
0951:                        String parameter = iParameters.next();
0952:                        addFields(query, parameter, stepsByAlias, fieldsByAlias);
0953:                    }
0954:                } else if (!Character.isDigit(expression.charAt(0))) {
0955:                    int pos = expression.indexOf('.');
0956:                    if (pos < 1 || pos == (expression.length() - 1)) {
0957:                        throw new IllegalArgumentException(
0958:                                "Invalid fieldname: \"" + expression + "\"");
0959:                    }
0960:                    int bracketOffset = (expression.startsWith("[") && expression
0961:                            .endsWith("]")) ? 1 : 0;
0962:                    String stepAlias = expression.substring(0 + bracketOffset,
0963:                            pos);
0964:                    String fieldName = expression.substring(pos + 1
0965:                            - bracketOffset);
0966:
0967:                    BasicStep step = stepsByAlias.get(stepAlias);
0968:                    if (step == null) {
0969:                        throw new IllegalArgumentException(
0970:                                "Invalid step alias: \"" + stepAlias
0971:                                        + "\" in fields list");
0972:                    }
0973:                    addField(query, step, fieldName, fieldsByAlias);
0974:                }
0975:            }
0976:
0977:            /**
0978:             * Adds field to a search query, unless it is already added.
0979:             *
0980:             * @param query The query.
0981:             * @param step The non-null step corresponding to the field.
0982:             * @param fieldName The fieldname.
0983:             * @param fieldsByAlias Map, mapping field aliases (fieldname prefixed by
0984:             *        table alias) to the stepfields in the query.
0985:             *        An entry is added for each stepfield added to the query.
0986:             * @since MMBase-1.7
0987:             */
0988:            private void addField(BasicSearchQuery query, BasicStep step,
0989:                    String fieldName, Map<String, BasicStepField> fieldsByAlias) {
0990:
0991:                // Fieldalias = stepalias.fieldname.
0992:                // This value is used to store the field in fieldsByAlias.
0993:                // The actual alias of the field is not set.
0994:                String fieldAlias = step.getAlias() + "." + fieldName;
0995:                if (fieldsByAlias.containsKey(fieldAlias)) {
0996:                    // Added already.
0997:                    return;
0998:                }
0999:
1000:                MMObjectBuilder builder = mmb.getBuilder(step.getTableName());
1001:                CoreField fieldDefs = builder.getField(fieldName);
1002:                if (fieldDefs == null) {
1003:                    throw new IllegalArgumentException(
1004:                            "Not a known field of builder "
1005:                                    + step.getTableName() + ": \"" + fieldName
1006:                                    + "\"");
1007:                }
1008:
1009:                // Add the stepfield.
1010:                BasicStepField stepField = query.addField(step, fieldDefs);
1011:                fieldsByAlias.put(fieldAlias, stepField);
1012:            }
1013:
1014:            /**
1015:             * Adds sorting orders to a search query.
1016:             *
1017:             * @param query The query.
1018:             * @param fieldNames The fieldnames prefixed by the table aliases.
1019:             * @param directions The corresponding sorting directions ("UP"/"DOWN").
1020:             * @param fieldsByAlias Map, mapping field aliases (fieldname prefixed by
1021:             *        table alias) to the stepfields in the query.
1022:             * @since MMBase-1.7
1023:             */
1024:            // package visibility!
1025:            void addSortOrders(BasicSearchQuery query, List<String> fieldNames,
1026:                    List<String> directions,
1027:                    Map<String, BasicStepField> fieldsByAlias) {
1028:
1029:                // Test if fieldnames are specified.
1030:                if (fieldNames == null || fieldNames.size() == 0) {
1031:                    return;
1032:                }
1033:
1034:                int defaultSortOrder = SortOrder.ORDER_ASCENDING;
1035:                if (directions != null && directions.size() != 0) {
1036:                    if (directions.get(0).trim().equalsIgnoreCase("DOWN")) {
1037:                        defaultSortOrder = SortOrder.ORDER_DESCENDING;
1038:                    }
1039:                }
1040:
1041:                Iterator<String> iFieldNames = fieldNames.iterator();
1042:                Iterator<String> iDirections = directions.iterator();
1043:                while (iFieldNames.hasNext()) {
1044:                    String fieldName = iFieldNames.next();
1045:                    StepField field = fieldsByAlias.get(fieldName);
1046:                    if (field == null) {
1047:                        // Field has not been added.
1048:                        field = ConstraintParser.getField(fieldName, query
1049:                                .getSteps());
1050:                    }
1051:                    if (field == null) {
1052:                        throw new IllegalArgumentException(
1053:                                "Invalid fieldname: \"" + fieldName + "\"");
1054:                    }
1055:
1056:                    // Add sort order.
1057:                    BasicSortOrder sortOrder = query.addSortOrder(field); // ascending
1058:
1059:                    // Change direction if needed.
1060:                    if (iDirections.hasNext()) {
1061:                        String direction = iDirections.next();
1062:                        if (direction.trim().equalsIgnoreCase("DOWN")) {
1063:                            sortOrder.setDirection(SortOrder.ORDER_DESCENDING);
1064:                        } else if (!direction.trim().equalsIgnoreCase("UP")) {
1065:                            throw new IllegalArgumentException(
1066:                                    "Parameter directions contains an invalid value ("
1067:                                            + direction
1068:                                            + "), should be UP or DOWN.");
1069:                        }
1070:
1071:                    } else {
1072:                        sortOrder.setDirection(defaultSortOrder);
1073:                    }
1074:                }
1075:            }
1076:
1077:            /**
1078:             * Gets first step from list, that corresponds to the builder
1079:             * of a specified node - or one of its parentbuilders.
1080:             *
1081:             * @param steps The steps.
1082:             * @param nodeNumber The number identifying the node.
1083:             * @return The step, or <code>null</code> when not found.
1084:             * @since MMBase-1.7
1085:             */
1086:            // package visibility!
1087:            Step getNodesStep(List<Step> steps, int nodeNumber) {
1088:                if (nodeNumber < 0) {
1089:                    return null;
1090:                }
1091:
1092:                MMObjectNode node = getNode(nodeNumber);
1093:                if (node == null) {
1094:                    return null;
1095:                }
1096:
1097:                MMObjectBuilder builder = node.parent;
1098:                Step result = null;
1099:                do {
1100:                    // Find step corresponding to builder.
1101:                    Iterator<Step> iSteps = steps.iterator();
1102:                    while (iSteps.hasNext() && result == null) {
1103:                        Step step = iSteps.next();
1104:                        if (step.getTableName().equals(builder.tableName)) { // should inheritance not be considered?
1105:                            // Found.
1106:                            result = step;
1107:                        }
1108:                    }
1109:                    // Not found, then try again with parentbuilder.
1110:                    builder = builder.getParentBuilder();
1111:                } while (builder != null && result == null);
1112:
1113:                return result;
1114:            }
1115:
1116:            /**
1117:             * Adds relation directions.
1118:             *
1119:             * @param query The search query.
1120:             * @param searchDirs Specifies in which direction relations are to be followed. You can specify a direction for each
1121:             *      relation in the path. If you specify less directions than there are relations, the last specified direction is used
1122:             *      for the remaining relations. If you specify an empty list the default direction is BOTH.
1123:             * @param roles Map of tablenames mapped to <code>Integer</code> values,
1124:             *        representing the nodenumber of the corresponing RelDef node.
1125:             * @since MMBase-1.7
1126:             */
1127:            // package visibility!
1128:            void addRelationDirections(BasicSearchQuery query,
1129:                    List<Integer> searchDirs, Map<String, Integer> roles) {
1130:
1131:                Iterator<Step> iSteps = query.getSteps().iterator();
1132:                Iterator<Integer> iSearchDirs = searchDirs.iterator();
1133:                int searchDir = RelationStep.DIRECTIONS_BOTH;
1134:
1135:                if (!iSteps.hasNext())
1136:                    return; // nothing to be done.
1137:                Step sourceStep = iSteps.next();
1138:                Step destinationStep = null;
1139:
1140:                while (iSteps.hasNext()) {
1141:                    if (destinationStep != null) {
1142:                        sourceStep = destinationStep;
1143:                    }
1144:                    BasicRelationStep relationStep = (BasicRelationStep) iSteps
1145:                            .next();
1146:                    destinationStep = iSteps.next();
1147:                    if (iSearchDirs.hasNext())
1148:                        searchDir = iSearchDirs.next().intValue();
1149:
1150:                    // FIXME this cast to BasicStep is ugly and should not be here in a clean implementation
1151:
1152:                    // Determine typedef number of the source-type.
1153:                    int sourceType = ((BasicStep) sourceStep).getBuilder()
1154:                            .getObjectType();
1155:                    // Determine the typedef number of the destination-type.
1156:                    int destinationType = ((BasicStep) destinationStep)
1157:                            .getBuilder().getObjectType();
1158:
1159:                    // Determine reldef number of the role.
1160:                    Integer role = roles.get(relationStep.getAlias());
1161:
1162:                    int roleInt;
1163:                    if (role != null) {
1164:                        roleInt = role.intValue();
1165:                        relationStep.setRole(role);
1166:                    } else {
1167:                        roleInt = -1;
1168:                    }
1169:
1170:                    if (!mmb.getTypeRel().optimizeRelationStep(relationStep,
1171:                            sourceType, destinationType, roleInt, searchDir)) {
1172:                        if (searchDir != RelationStep.DIRECTIONS_SOURCE
1173:                                && searchDir != RelationStep.DIRECTIONS_DESTINATION) {
1174:                            log
1175:                                    .warn("No relation defined between "
1176:                                            + sourceStep.getTableName()
1177:                                            + " and "
1178:                                            + destinationStep.getTableName()
1179:                                            + " using "
1180:                                            + relationStep
1181:                                            + " with direction(s) "
1182:                                            + getSearchDirString(searchDir)
1183:                                            + ". Searching in 'destination' direction now, but perhaps the query should be fixed, because this should always result nothing.");
1184:                        } else {
1185:                            log
1186:                                    .warn("No relation defined between "
1187:                                            + sourceStep.getTableName()
1188:                                            + " and "
1189:                                            + destinationStep.getTableName()
1190:                                            + " using "
1191:                                            + relationStep
1192:                                            + " with direction(s) "
1193:                                            + getSearchDirString(searchDir)
1194:                                            + ". Trying anyway, but perhaps the query should be fixed, because this should always result nothing.");
1195:                        }
1196:                        log.warn(Logging.applicationStacktrace());
1197:                    }
1198:
1199:                }
1200:            }
1201:
1202:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.