Source Code Cross Referenced for Queries.java in  » Database-ORM » MMBase » org » mmbase » bridge » 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.bridge.util 
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:
0011:        package org.mmbase.bridge.util;
0012:
0013:        import java.util.*;
0014:
0015:        import org.mmbase.bridge.*;
0016:        import org.mmbase.bridge.implementation.BasicQuery;
0017:        import org.mmbase.module.core.ClusterBuilder;
0018:        import org.mmbase.module.core.MMBase;
0019:        import org.mmbase.storage.search.*;
0020:        import org.mmbase.storage.search.legacy.ConstraintParser;
0021:        import org.mmbase.util.*;
0022:        import org.mmbase.util.logging.*;
0023:
0024:        /**
0025:         * This class contains various utility methods for manipulating and creating query objects.
0026:         * Most essential methods are available on the Query object itself, but too specific or legacy-ish
0027:         * methods are put here.
0028:         *
0029:         * @author Michiel Meeuwissen
0030:         * @version $Id: Queries.java,v 1.96 2008/02/29 11:00:05 michiel Exp $
0031:         * @see  org.mmbase.bridge.Query
0032:         * @since MMBase-1.7
0033:         */
0034:        abstract public class Queries {
0035:
0036:            public static final int OPERATOR_BETWEEN = -1; // not a FieldCompareConstraint (numeric)
0037:            public static final int OPERATOR_IN = 10000; // not a FieldCompareConstraint (non numeric)
0038:            public static final int OPERATOR_NULL = 10001; // FieldIsNullConstraint
0039:
0040:            private static final Logger log = Logging
0041:                    .getLoggerInstance(Queries.class);
0042:
0043:            /**
0044:             * Translates a string to a search direction constant. If the string is <code>null</code> then
0045:             * 'BOTH' is returned.
0046:             * @param search string representation of the searchdir constant
0047:             * @return Searchdir constant as in {@link RelationStep}
0048:             * @see ClusterBuilder#getSearchDir The same function, only with another return value if String is <code>null</code>
0049:             */
0050:            public static int getRelationStepDirection(String search) {
0051:                if (search == null) {
0052:                    return RelationStep.DIRECTIONS_BOTH;
0053:                }
0054:                search = search.toUpperCase();
0055:                if ("DESTINATION".equals(search)) {
0056:                    return RelationStep.DIRECTIONS_DESTINATION;
0057:                } else if ("SOURCE".equals(search)) {
0058:                    return RelationStep.DIRECTIONS_SOURCE;
0059:                } else if ("BOTH".equals(search)) {
0060:                    return RelationStep.DIRECTIONS_BOTH;
0061:                } else if ("ALL".equals(search)) {
0062:                    return RelationStep.DIRECTIONS_ALL;
0063:                } else if ("EITHER".equals(search)) {
0064:                    return RelationStep.DIRECTIONS_EITHER;
0065:                } else {
0066:                    throw new BridgeException(
0067:                            "'"
0068:                                    + search
0069:                                    + "' cannot be converted to a relation-step direction constant");
0070:                }
0071:            }
0072:
0073:            /**
0074:             * Creates a Query object using arguments for {@link Cloud#getList(String, String, String, String, String, String, String, boolean)}
0075:             * (this function is of course implemented using this utility). This is useful to convert (legacy) code which uses
0076:             * getList, but you want to use new Query features without rewriting the complete thing.
0077:             *
0078:             * It can also be simply handy to specify things as Strings.
0079:             *
0080:             * @param cloud
0081:             * @param startNodes
0082:             * @param nodePath
0083:             * @param fields
0084:             * @param constraints
0085:             * @param orderby
0086:             * @param directions
0087:             * @param searchDir
0088:             * @param distinct
0089:             * @return New query object
0090:             * @todo Should this method be part of Cloud itself?
0091:             */
0092:            public static Query createQuery(Cloud cloud, String startNodes,
0093:                    String nodePath, String fields, String constraints,
0094:                    String orderby, String directions, String searchDir,
0095:                    boolean distinct) {
0096:
0097:                // the bridge test case say that you may also specifiy empty string (why?)
0098:                if ("".equals(startNodes) || "-1".equals(startNodes)) {
0099:                    startNodes = null;
0100:                }
0101:                if ("".equals(fields)) {
0102:                    fields = null;
0103:                }
0104:                if ("".equals(constraints)) {
0105:                    constraints = null;
0106:                }
0107:                if ("".equals(searchDir)) {
0108:                    searchDir = null;
0109:                }
0110:                if ("".equals(orderby)) {
0111:                    orderby = null;
0112:                }
0113:
0114:                if ("".equals(directions)) {
0115:                    directions = null;
0116:                }
0117:                // check invalid search command
0118:                Encode encoder = new Encode("ESCAPE_SINGLE_QUOTE");
0119:                // if(startNodes != null) startNodes = encoder.encode(startNodes);
0120:                // if(nodePath != null) nodePath = encoder.encode(nodePath);
0121:                // if(fields != null) fields = encoder.encode(fields);
0122:                if (orderby != null) {
0123:                    orderby = encoder.encode(orderby);
0124:                }
0125:                if (directions != null) {
0126:                    directions = encoder.encode(directions);
0127:                }
0128:                if (searchDir != null) {
0129:                    searchDir = encoder.encode(searchDir);
0130:                }
0131:                if (constraints != null) {
0132:                    constraints = ConstraintParser
0133:                            .convertClauseToDBS(constraints);
0134:                    if (!ConstraintParser.validConstraints(constraints)) {
0135:                        throw new BridgeException("invalid constraints:"
0136:                                + constraints);
0137:                    }
0138:                    if (!constraints.substring(0, 5).equalsIgnoreCase("WHERE")) {
0139:                        /// WHERE is used in org.mmbase.util.QueryConvertor
0140:                        constraints = "WHERE " + constraints;
0141:                    }
0142:                }
0143:
0144:                // create query object
0145:                //TODO: remove this code... classes under org.mmbase.bridge.util must not use the core
0146:
0147:                // getMultilevelSearchQuery must perhaps move to a utility container org.mmbase.storage.search.Queries or so.
0148:
0149:                ClusterBuilder clusterBuilder = MMBase.getMMBase()
0150:                        .getClusterBuilder();
0151:                int search = -1;
0152:                if (searchDir != null) {
0153:                    search = ClusterBuilder.getSearchDir(searchDir);
0154:                }
0155:
0156:                List<String> snodes = StringSplitter.split(startNodes);
0157:                List<String> tables = StringSplitter.split(nodePath);
0158:                List<String> f = StringSplitter.split(fields);
0159:                List<String> orderVec = StringSplitter.split(orderby);
0160:                List<String> d = StringSplitter.split(directions);
0161:                try {
0162:                    // pitty that we can't use cloud.createQuery for this.
0163:                    // but all essential methods are on ClusterBuilder
0164:                    // XXX need casting here, something's wrong!!!
0165:                    Query query = new BasicQuery(cloud, clusterBuilder
0166:                            .getMultiLevelSearchQuery(snodes, f,
0167:                                    distinct ? "YES" : "NO", tables,
0168:                                    constraints, orderVec, d, search));
0169:                    return query;
0170:                } catch (IllegalArgumentException iae) {
0171:                    throw new BridgeException(iae.getMessage()
0172:                            + ". (arguments: startNodes='" + startNodes
0173:                            + "', path='" + nodePath + "', fields='" + fields
0174:                            + "', constraints='" + constraints + "' orderby='"
0175:                            + orderby + "', directions='" + directions
0176:                            + "', searchdir='" + searchDir + "')", iae);
0177:                }
0178:            }
0179:
0180:            /**
0181:             * Adds a 'legacy' constraint to the query, i.e. constraint(s) represented
0182:             * by a string. Alreading existing constraints remain ('AND' is used).
0183:             *
0184:             * @param query query to add constraint to
0185:             * @param constraints string representation of constraints
0186:             * @return The new constraint, or null if nothing changed added.
0187:             */
0188:            public static Constraint addConstraints(Query query,
0189:                    String constraints) {
0190:                if (constraints == null || constraints.equals("")) {
0191:                    return null;
0192:                }
0193:
0194:                // (Try to) parse constraints string to Constraint object.
0195:                Constraint newConstraint = new ConstraintParser(query)
0196:                        .toConstraint(constraints);
0197:                addConstraint(query, newConstraint);
0198:                return newConstraint;
0199:            }
0200:
0201:            /**
0202:             * Adds a Constraint to the already present constraint (with AND).
0203:             * @param query query to add the constraint to
0204:             * @param newConstraint constraint to add
0205:             * @return The new constraint.
0206:             */
0207:            public static Constraint addConstraint(Query query,
0208:                    Constraint newConstraint) {
0209:                if (newConstraint == null) {
0210:                    return null;
0211:                }
0212:
0213:                Constraint constraint = query.getConstraint();
0214:
0215:                if (constraint != null) {
0216:                    log.debug("compositing constraint");
0217:                    Constraint compConstraint = query.createConstraint(
0218:                            constraint, CompositeConstraint.LOGICAL_AND,
0219:                            newConstraint);
0220:                    query.setConstraint(compConstraint);
0221:                } else {
0222:                    query.setConstraint(newConstraint);
0223:                }
0224:                return newConstraint;
0225:            }
0226:
0227:            /**
0228:             * Creates a operator constant for use by createConstraint
0229:             * @param s String representation of operator
0230:             * @return FieldCompareConstraint operator constant
0231:             * @see #createConstraint(Query, String, int, Object)
0232:             * @see #createConstraint(Query, String, int, Object, Object, boolean)
0233:             */
0234:            public static int getOperator(String s) {
0235:                String op = s.toUpperCase();
0236:                // first: determine operator:
0237:                if (op.equals("<") || op.equals("LESS") || op.equals("LT")) {
0238:                    return FieldCompareConstraint.LESS;
0239:                } else if (op.equals("<=") || op.equals("LESS_EQUAL")
0240:                        || op.equals("LE")) {
0241:                    return FieldCompareConstraint.LESS_EQUAL;
0242:                } else if (op.equals("=") || op.equals("EQUAL")
0243:                        || op.equals("") || op.equals("EQ")) {
0244:                    return FieldCompareConstraint.EQUAL;
0245:                } else if (op.equals("!=") || op.equals("NOT_EQUAL")
0246:                        || op.equals("NE")) {
0247:                    return FieldCompareConstraint.NOT_EQUAL;
0248:                } else if (op.equals(">") || op.equals("GREATER")
0249:                        || op.equals("GT")) {
0250:                    return FieldCompareConstraint.GREATER;
0251:                } else if (op.equals(">=") || op.equals("GREATER_EQUAL")
0252:                        || op.equals("GE")) {
0253:                    return FieldCompareConstraint.GREATER_EQUAL;
0254:                } else if (op.equals("LIKE")) {
0255:                    return FieldCompareConstraint.LIKE;
0256:                } else if (op.equals("BETWEEN")) {
0257:                    return OPERATOR_BETWEEN;
0258:                } else if (op.equals("IN")) {
0259:                    return OPERATOR_IN;
0260:                } else if (op.equals("NULL")) {
0261:                    return OPERATOR_NULL;
0262:                    //} else if (op.equals("~") || op.equals("REGEXP")) {
0263:                    //  return FieldCompareConstraint.REGEXP;
0264:                } else {
0265:                    throw new BridgeException(
0266:                            "Unknown Field Compare Operator '" + op + "'");
0267:                }
0268:            }
0269:
0270:            /**
0271:             * Creates a part constant for use by createConstraint
0272:             * @param s String representation of a datetime part
0273:             * @return FieldValueDateConstraint part constant
0274:             * @see #createConstraint(Query, String, int, Object, Object, boolean, int)
0275:             */
0276:            public static int getDateTimePart(String s) {
0277:                String sPart = s.toUpperCase();
0278:                if (sPart.equals("")) {
0279:                    return -1;
0280:                } else if (sPart.equals("CENTURY")) {
0281:                    return FieldValueDateConstraint.CENTURY;
0282:                } else if (sPart.equals("YEAR")) {
0283:                    return FieldValueDateConstraint.YEAR;
0284:                } else if (sPart.equals("QUARTER")) {
0285:                    return FieldValueDateConstraint.QUARTER;
0286:                } else if (sPart.equals("MONTH")) {
0287:                    return FieldValueDateConstraint.MONTH;
0288:                } else if (sPart.equals("WEEK")) {
0289:                    return FieldValueDateConstraint.WEEK;
0290:                } else if (sPart.equals("DAYOFYEAR")) {
0291:                    return FieldValueDateConstraint.DAY_OF_YEAR;
0292:                } else if (sPart.equals("DAY") || sPart.equals("DAYOFMONTH")) {
0293:                    return FieldValueDateConstraint.DAY_OF_MONTH;
0294:                } else if (sPart.equals("DAYOFWEEK")) {
0295:                    return FieldValueDateConstraint.DAY_OF_WEEK;
0296:                } else if (sPart.equals("HOUR")) {
0297:                    return FieldValueDateConstraint.HOUR;
0298:                } else if (sPart.equals("MINUTE")) {
0299:                    return FieldValueDateConstraint.MINUTE;
0300:                } else if (sPart.equals("SECOND")) {
0301:                    return FieldValueDateConstraint.SECOND;
0302:                } else if (sPart.equals("MILLISECOND")) {
0303:                    return FieldValueDateConstraint.MILLISECOND;
0304:                } else {
0305:                    throw new BridgeException("Unknown datetime part '" + sPart
0306:                            + "'");
0307:                }
0308:            }
0309:
0310:            /**
0311:             * Used in implementation of createConstraint
0312:             * @param stringValue string representation of a number
0313:             * @return Number object
0314:             * @throws BridgeException when failed to convert the string
0315:             */
0316:            protected static Number getNumberValue(String stringValue)
0317:                    throws BridgeException {
0318:                if (stringValue == null)
0319:                    return null;
0320:                try {
0321:                    return Integer.valueOf(stringValue);
0322:                } catch (NumberFormatException e) {
0323:                    try {
0324:                        return Double.valueOf(stringValue);
0325:                    } catch (NumberFormatException e2) {
0326:                        if (stringValue.equalsIgnoreCase("true")) {
0327:                            return Integer.valueOf(1);
0328:                        } else if (stringValue.equalsIgnoreCase("false")) {
0329:                            return Integer.valueOf(0);
0330:                        }
0331:                        throw new BridgeException(
0332:                                "Operator requires number value ('"
0333:                                        + stringValue + "' is not)");
0334:                    }
0335:                }
0336:            }
0337:
0338:            /**
0339:             * Used in implementation of createConstraint
0340:             * @param fieldType Field Type constant (@link Field)
0341:             * @param operator Compare operator
0342:             * @param value value to convert
0343:             * @return new Compare value
0344:             */
0345:            protected static Object getCompareValue(int fieldType,
0346:                    int operator, Object value) {
0347:                return getCompareValue(fieldType, operator, value, -1, null);
0348:            }
0349:
0350:            /**
0351:             * Used in implementation of createConstraint
0352:             * @param fieldType Field Type constant (@link Field)
0353:             * @param operator Compare operator
0354:             * @param value value to convert
0355:             * @param cloud The cloud may be used to pass locale sensitive properties which may be needed for comparisions (locales, timezones)
0356:             * @return new Compare value
0357:             * @since MMBase-1.8.2
0358:             */
0359:            protected static Object getCompareValue(int fieldType,
0360:                    int operator, Object value, int datePart, Cloud cloud) {
0361:                if (operator == OPERATOR_IN) {
0362:                    SortedSet<Object> set;
0363:                    if (value instanceof  SortedSet) {
0364:                        set = (SortedSet<Object>) value;
0365:                    } else if (value instanceof  NodeList) {
0366:                        set = new TreeSet<Object>();
0367:                        for (Node node : ((NodeList) value)) {
0368:                            set.add(getCompareValue(fieldType,
0369:                                    FieldCompareConstraint.EQUAL, node
0370:                                            .getNumber()));
0371:                        }
0372:                    } else if (value instanceof  Collection) {
0373:                        set = new TreeSet<Object>();
0374:                        for (Object o : ((Collection) value)) {
0375:                            set.add(getCompareValue(fieldType,
0376:                                    FieldCompareConstraint.EQUAL, o));
0377:                        }
0378:                    } else {
0379:                        set = new TreeSet<Object>();
0380:                        if (!(value == null || value.equals(""))) {
0381:                            set.add(getCompareValue(fieldType,
0382:                                    FieldCompareConstraint.EQUAL, value));
0383:                        }
0384:                    }
0385:                    return set;
0386:                }
0387:                switch (fieldType) {
0388:                case Field.TYPE_STRING:
0389:                    return value == null ? null : Casting.toString(value);
0390:                case Field.TYPE_INTEGER:
0391:                case Field.TYPE_FLOAT:
0392:                case Field.TYPE_LONG:
0393:                case Field.TYPE_DOUBLE:
0394:                case Field.TYPE_NODE:
0395:                    if (value instanceof  Number) {
0396:                        return value;
0397:                    } else {
0398:                        return getNumberValue(value == null ? null : Casting
0399:                                .toString(value));
0400:                    }
0401:                case Field.TYPE_DATETIME:
0402:                    //TimeZone     tz = cloud == null ? null : (TimeZone) cloud.getProperty("org.mmbase.timezone");
0403:                    if (datePart > -1) {
0404:                        return Casting.toInteger(value);
0405:                    } else {
0406:                        return Casting.toDate(value);
0407:                    }
0408:                case Field.TYPE_BOOLEAN:
0409:                    return Casting.toBoolean(value) ? Boolean.TRUE
0410:                            : Boolean.FALSE;
0411:                default:
0412:                    return value;
0413:                }
0414:            }
0415:
0416:            /**
0417:             * Defaulting version of {@link #createConstraint(Query, String, int, Object, Object, boolean, int)}.
0418:             * Casesensitivity defaults to false, value2 to null (so 'BETWEEN' cannot be used), datePart set to -1 (so no date part comparison)
0419:             * @param query      The query to create the constraint for
0420:             * @param fieldName  The field to create the constraint on (as a string, so it can include the step), e.g. 'news.number'
0421:             * @param operator   The operator to use. This constant can be produces from a string using {@link #getOperator(String)}.
0422:             * @param value      The value to compare with, which must be of the right type. If field is number it might also be an alias.
0423:             * @return The new constraint, or <code>null</code> it by chance the specified arguments did not lead to a new actual constraint (e.g. if value is an empty set)
0424:             */
0425:            public static Constraint createConstraint(Query query,
0426:                    String fieldName, int operator, Object value) {
0427:                return createConstraint(query, fieldName, operator, value,
0428:                        null, false, -1);
0429:            }
0430:
0431:            /**
0432:             * Defaulting version of {@link #createConstraint(Query, String, int, Object, Object, boolean, int)}.
0433:             * DatePart set to -1 (so no date part comparison)
0434:             * @param query      The query to create the constraint for
0435:             * @param fieldName  The field to create the constraint on (as a string, so it can include the step), e.g. 'news.number'
0436:             * @param operator   The operator to use. This constant can be produces from a string using {@link #getOperator(String)}.
0437:             * @param value      The value to compare with, which must be of the right type. If field is number it might also be an alias.
0438:             * @param value2     The other value (only relevant if operator is BETWEEN, the only terniary operator)
0439:             * @param caseSensitive  Whether it should happen case sensitively (not relevant for number fields)
0440:             * @return The new constraint, or <code>null</code> it by chance the specified arguments did not lead to a new actual constraint (e.g. if value is an empty set)
0441:             */
0442:            public static Constraint createConstraint(Query query,
0443:                    String fieldName, int operator, Object value,
0444:                    Object value2, boolean caseSensitive) {
0445:                return createConstraint(query, fieldName, operator, value,
0446:                        value2, caseSensitive, -1);
0447:            }
0448:
0449:            /**
0450:             * Creates a constraint smartly, depending on the type of the field, the value is cast to the
0451:             * right type, and the right type of constraint is created.
0452:             * This is used in taglib implementation, but could be useful more generally.
0453:             *
0454:             * @param query      The query to create the constraint for
0455:             * @param fieldName  The field to create the constraint on (as a string, so it can include the step), e.g. 'news.number'
0456:             * @param operator   The operator to use. This constant can be produces from a string using {@link #getOperator(String)}.
0457:             * @param value      The value to compare with, which must be of the right type. If field is number it might also be an alias.
0458:             * @param value2     The other value (only relevant if operator is BETWEEN, the only terniary operator)
0459:             * @param caseSensitive  Whether it should happen case sensitively (not relevant for number fields)
0460:             * @param datePart       The part of a DATETIME value that is to be checked
0461:             * @return The new constraint, or <code>null</code> it by chance the specified arguments did not lead to a new actual constraint (e.g. if value is an empty set)
0462:             */
0463:            public static Constraint createConstraint(final Query query,
0464:                    final String fieldName, final int operator,
0465:                    final Object originalValue, final Object value2,
0466:                    final boolean caseSensitive, final int datePart) {
0467:
0468:                Object value = originalValue;
0469:                StepField stepField = query.createStepField(fieldName);
0470:                if (stepField == null) {
0471:                    throw new BridgeException(
0472:                            "Could not create stepfield with '" + fieldName
0473:                                    + "'");
0474:                }
0475:
0476:                Cloud cloud = query.getCloud();
0477:                FieldConstraint newConstraint;
0478:
0479:                if (value instanceof  StepField) {
0480:                    newConstraint = query.createConstraint(stepField, operator,
0481:                            (StepField) value);
0482:                } else if (operator == OPERATOR_NULL || value == null) {
0483:                    newConstraint = query.createConstraint(stepField);
0484:                } else {
0485:                    Field field = cloud.getNodeManager(
0486:                            stepField.getStep().getTableName()).getField(
0487:                            stepField.getFieldName());
0488:                    int fieldType = field.getType();
0489:
0490:                    if (fieldName.equals("number")
0491:                            || fieldType == Field.TYPE_NODE) {
0492:                        if (value instanceof  String) { // it might be an alias!
0493:                            if (cloud.hasNode((String) value)) {
0494:                                Node node = cloud.getNode((String) value);
0495:                                value = Integer.valueOf(node.getNumber());
0496:                            } else {
0497:                                value = -1; // non existing node number. Integer.parseInt((String) value);
0498:                            }
0499:                        } else if (value instanceof  Collection) { // or even more aliases!
0500:                            Collection col = (Collection) value;
0501:                            value = new ArrayList();
0502:                            List<Object> list = (List<Object>) value;
0503:                            for (Object v : col) {
0504:                                if (v instanceof  Number) {
0505:                                    list.add(v);
0506:                                } else {
0507:                                    String s = Casting.toString(v);
0508:                                    if (cloud.hasNode(s)) {
0509:                                        Node node = cloud.getNode(s);
0510:                                        list.add(node.getNumber());
0511:                                    } else {
0512:                                        list.add(-1);
0513:                                    }
0514:
0515:                                }
0516:                            }
0517:
0518:                        }
0519:                    }
0520:                    if (operator != OPERATOR_IN) { // should the elements of the collection then not be cast?
0521:
0522:                        if (fieldType == Field.TYPE_XML) {
0523:                            // XML's are treated as String in the query-handler so, let's anticipate that here...
0524:                            // a bit of a hack, perhaps we need something like a 'searchCast' or so.
0525:                            value = Casting.toString(value);
0526:                        } else {
0527:                            Object castedValue = field.getDataType().cast(
0528:                                    value, null, field);
0529:                            if (castedValue == null && value != null
0530:                                    && fieldType == Field.TYPE_NODE) {
0531:                                // non existing node-number, like e.g. -1 are csated to null,
0532:                                // but that is incorrect when e..g the operator is GREATER
0533:                                castedValue = Casting.toInteger(value);
0534:                            }
0535:                            value = castedValue;
0536:
0537:                        }
0538:                    }
0539:
0540:                    Object compareValue = getCompareValue(fieldType, operator,
0541:                            value, datePart, cloud);
0542:
0543:                    if (log.isDebugEnabled()) {
0544:                        log.debug(" " + originalValue + " -> " + value + " -> "
0545:                                + compareValue);
0546:                    }
0547:
0548:                    if (operator > 0 && operator < OPERATOR_IN) {
0549:                        if (fieldType == Field.TYPE_DATETIME && datePart > -1) {
0550:                            newConstraint = query.createConstraint(stepField,
0551:                                    operator, compareValue, datePart);
0552:                        } else {
0553:                            if (operator == FieldCompareConstraint.EQUAL
0554:                                    && compareValue == null) {
0555:                                newConstraint = query
0556:                                        .createConstraint(stepField);
0557:                            } else {
0558:                                newConstraint = query.createConstraint(
0559:                                        stepField, operator, compareValue);
0560:                            }
0561:                        }
0562:                    } else {
0563:                        if (fieldType == Field.TYPE_DATETIME && datePart > -1) {
0564:                            throw new RuntimeException(
0565:                                    "Cannot apply IN or BETWEEN to a partial date field");
0566:                        }
0567:                        switch (operator) {
0568:                        case OPERATOR_BETWEEN:
0569:                            Object compareValue2 = getCompareValue(fieldType,
0570:                                    operator, value2);
0571:                            newConstraint = query.createConstraint(stepField,
0572:                                    compareValue, compareValue2);
0573:                            break;
0574:                        case OPERATOR_IN:
0575:                            newConstraint = query.createConstraint(stepField,
0576:                                    (SortedSet) compareValue);
0577:                            break;
0578:                        default:
0579:                            throw new RuntimeException(
0580:                                    "Unknown value for operation " + operator);
0581:                        }
0582:                    }
0583:                }
0584:                query.setCaseSensitive(newConstraint, caseSensitive);
0585:                return newConstraint;
0586:
0587:            }
0588:
0589:            /**
0590:             * Takes a Constraint of a query, and takes al constraints on 'sourceStep' of it, and copies
0591:             * those Constraints to the given step of the receiving query.
0592:             *
0593:             * Constraints on different steps then the given 'sourceStep' are ignored. CompositeConstraints
0594:             * cause recursion and would work too (but same limitation are valid for the childs).
0595:             *
0596:             * @param c          The constrain to be copied (for example the result of sourceQuery.getConstraint()).
0597:             * @param sourceStep The step in the 'source' query.
0598:             * @param query      The receiving query
0599:             * @param step       The step of the receiving query which must 'receive' the sort orders.
0600:             * @since MMBase-1.7.1
0601:             * @see   org.mmbase.storage.search.implementation.BasicSearchQuery#copyConstraint Functions are similar
0602:             * @throws IllegalArgumentException If the given constraint is not compatible with the given step.
0603:             * @throws UnsupportedOperationException If CompareFieldsConstraints or LegacyConstraints are encountered.
0604:             * @return The new constraint or null
0605:             */
0606:            public static Constraint copyConstraint(Constraint c,
0607:                    Step sourceStep, Query query, Step step) {
0608:                if (c == null)
0609:                    return null;
0610:
0611:                if (c instanceof  CompositeConstraint) {
0612:                    CompositeConstraint constraint = (CompositeConstraint) c;
0613:                    List<Constraint> constraints = new ArrayList<Constraint>();
0614:                    for (Constraint child : constraint.getChilds()) {
0615:                        Constraint cons = copyConstraint(child, sourceStep,
0616:                                query, step);
0617:                        if (cons != null)
0618:                            constraints.add(cons);
0619:                    }
0620:                    int size = constraints.size();
0621:                    if (size == 0)
0622:                        return null;
0623:                    if (size == 1)
0624:                        return constraints.get(0);
0625:                    Iterator<Constraint> i = constraints.iterator();
0626:                    int op = constraint.getLogicalOperator();
0627:                    Constraint newConstraint = query.createConstraint(i.next(),
0628:                            op, i.next());
0629:                    while (i.hasNext()) {
0630:                        newConstraint = query.createConstraint(newConstraint,
0631:                                op, i.next());
0632:                    }
0633:                    query.setInverse(newConstraint, constraint.isInverse());
0634:                    return newConstraint;
0635:                } else if (c instanceof  CompareFieldsConstraint) {
0636:                    throw new UnsupportedOperationException(
0637:                            "Cannot copy comparison between fields"); // at least not from different steps
0638:                }
0639:
0640:                FieldConstraint fieldConstraint = (FieldConstraint) c;
0641:                if (!fieldConstraint.getField().getStep().equals(sourceStep))
0642:                    return null; // constraint is not for the request step, so don't copy.
0643:
0644:                StepField field = query.createStepField(step, fieldConstraint
0645:                        .getField().getFieldName());
0646:
0647:                FieldConstraint newConstraint;
0648:                if (c instanceof  FieldValueConstraint) {
0649:                    newConstraint = query.createConstraint(field,
0650:                            ((FieldValueConstraint) c).getOperator(),
0651:                            ((FieldValueConstraint) c).getValue());
0652:                } else if (c instanceof  FieldNullConstraint) {
0653:                    newConstraint = query.createConstraint(field);
0654:                } else if (c instanceof  FieldValueBetweenConstraint) {
0655:                    FieldValueBetweenConstraint constraint = (FieldValueBetweenConstraint) c;
0656:                    try {
0657:                        newConstraint = query.createConstraint(field,
0658:                                constraint.getLowerLimit(), constraint
0659:                                        .getUpperLimit());
0660:                    } catch (NumberFormatException e) {
0661:                        newConstraint = query.createConstraint(field,
0662:                                constraint.getLowerLimit(), constraint
0663:                                        .getUpperLimit());
0664:                    }
0665:                } else if (c instanceof  FieldValueInConstraint) {
0666:                    FieldValueInConstraint constraint = (FieldValueInConstraint) c;
0667:
0668:                    // sigh
0669:                    SortedSet<Object> set = new TreeSet<Object>();
0670:                    int type = field.getType();
0671:                    for (Object value : constraint.getValues()) {
0672:                        switch (type) {
0673:                        case Field.TYPE_INTEGER:
0674:                        case Field.TYPE_LONG:
0675:                        case Field.TYPE_NODE:
0676:                            value = Long.valueOf(Casting.toLong(value));
0677:                            break;
0678:                        case Field.TYPE_FLOAT:
0679:                        case Field.TYPE_DOUBLE:
0680:                            value = Double.valueOf(Casting.toDouble(value));
0681:                            break;
0682:                        case Field.TYPE_DATETIME:
0683:                            value = new Date((long) 1000
0684:                                    * Integer.parseInt("" + value));
0685:                            break;
0686:                        default:
0687:                            log.debug("Unknown type " + type);
0688:                            break;
0689:                        }
0690:                        set.add(value);
0691:                    }
0692:                    newConstraint = query.createConstraint(field, set);
0693:                } else if (c instanceof  LegacyConstraint) {
0694:                    throw new UnsupportedOperationException(
0695:                            "Cannot copy legacy constraint to other step");
0696:                } else {
0697:                    throw new RuntimeException("Could not copy constraint " + c);
0698:                }
0699:                query.setInverse(newConstraint, fieldConstraint.isInverse());
0700:                query.setCaseSensitive(newConstraint, fieldConstraint
0701:                        .isCaseSensitive());
0702:                return newConstraint;
0703:
0704:            }
0705:
0706:            /**
0707:             * Copies SortOrders to a given step of another query. SortOrders which do not sort the given
0708:             * 'sourceStep' are ignored.
0709:             * @param sortOrders A list of SortOrders (for example the result of sourceQuery.getSortOrders()).
0710:             * @param sourceStep The step in the 'source' query.
0711:             * @param query      The receiving query
0712:             * @param step       The step of the receiving query which must 'receive' the sort orders.
0713:             * @since MMBase-1.7.1
0714:
0715:             */
0716:            public static void copySortOrders(List<SortOrder> sortOrders,
0717:                    Step sourceStep, Query query, Step step) {
0718:                for (SortOrder sortOrder : sortOrders) {
0719:                    StepField sourceField = sortOrder.getField();
0720:                    if (!sourceField.getStep().equals(sourceStep))
0721:                        continue; // for another step
0722:                    if (sortOrder instanceof  DateSortOrder) {
0723:                        query.addSortOrder(query.createStepField(step,
0724:                                sourceField.getFieldName()), sortOrder
0725:                                .getDirection(), sortOrder.isCaseSensitive(),
0726:                                ((DateSortOrder) sortOrder).getPart());
0727:                    } else {
0728:                        query.addSortOrder(query.createStepField(step,
0729:                                sourceField.getFieldName()), sortOrder
0730:                                .getDirection(), sortOrder.isCaseSensitive());
0731:                    }
0732:                }
0733:            }
0734:
0735:            /**
0736:             * Converts a String to a SortOrder constant
0737:             * @param dir string representation of direction of sortorder
0738:             * @return SortOrder constant
0739:             * @since MMBase-1.7.1
0740:             */
0741:            public static int getSortOrder(String dir) {
0742:                dir = dir.toUpperCase();
0743:                if (dir.equals("")) {
0744:                    return SortOrder.ORDER_ASCENDING;
0745:                } else if (dir.equals("DOWN")) {
0746:                    return SortOrder.ORDER_DESCENDING;
0747:                } else if (dir.equals("UP")) {
0748:                    return SortOrder.ORDER_ASCENDING;
0749:                } else if (dir.equals("ASCENDING")) {
0750:                    return SortOrder.ORDER_ASCENDING;
0751:                } else if (dir.equals("DESCENDING")) {
0752:                    return SortOrder.ORDER_DESCENDING;
0753:                } else {
0754:                    throw new BridgeException("Unknown sort-order '" + dir
0755:                            + "'");
0756:                }
0757:            }
0758:
0759:            /**
0760:             * Adds sort orders to the query, using two strings. Like in 'getList' of Cloud. Several tag-attributes need this.
0761:             * @param query query to add the sortorders to
0762:             * @param sorted string with comma-separated fields
0763:             * @param directions string with comma-separated directions
0764:             *
0765:             * @todo implement for normal query.
0766:             * @return The new sort orders
0767:             */
0768:            public static List<SortOrder> addSortOrders(Query query,
0769:                    String sorted, String directions) {
0770:                // following code was copied from MMObjectBuilder.setSearchQuery (bit ugly)
0771:                if (sorted == null) {
0772:                    return query.getSortOrders().subList(0, 0);
0773:                }
0774:                if (directions == null) {
0775:                    directions = "";
0776:                }
0777:                List<SortOrder> list = query.getSortOrders();
0778:                int initialSize = list.size();
0779:
0780:                StringTokenizer sortedTokenizer = new StringTokenizer(sorted,
0781:                        ",");
0782:                StringTokenizer directionsTokenizer = new StringTokenizer(
0783:                        directions, ",");
0784:
0785:                while (sortedTokenizer.hasMoreTokens()) {
0786:                    String fieldName = sortedTokenizer.nextToken().trim();
0787:                    int dot = fieldName.indexOf('.');
0788:
0789:                    StepField sf;
0790:                    if (dot == -1 && query instanceof  NodeQuery) {
0791:                        NodeManager nodeManager = ((NodeQuery) query)
0792:                                .getNodeManager();
0793:                        sf = ((NodeQuery) query).getStepField(nodeManager
0794:                                .getField(fieldName));
0795:                    } else {
0796:                        sf = query.createStepField(fieldName);
0797:                    }
0798:
0799:                    int dir = SortOrder.ORDER_ASCENDING;
0800:                    if (directionsTokenizer.hasMoreTokens()) {
0801:                        String direction = directionsTokenizer.nextToken()
0802:                                .trim();
0803:                        dir = getSortOrder(direction);
0804:                    }
0805:                    query.addSortOrder(sf, dir);
0806:                }
0807:
0808:                return list.subList(initialSize, list.size());
0809:            }
0810:
0811:            /**
0812:             * Returns substring of given string without the leading digits (used in 'paths')
0813:             * @param complete string with leading digits
0814:             * @return string with digits removed
0815:             */
0816:            public static String removeDigits(String complete) {
0817:                int end = complete.length() - 1;
0818:                while (Character.isDigit(complete.charAt(end))) {
0819:                    --end;
0820:                }
0821:                return complete.substring(0, end + 1);
0822:            }
0823:
0824:            /**
0825:             * Adds path of steps to an existing query. The query may contain steps already. Per step also
0826:             * the 'search direction' may be specified.
0827:             * @param query extend this query
0828:             * @param path create steps from this path
0829:             * @param searchDirs add steps with these relation directions
0830:             * @return The new steps.
0831:             */
0832:            public static List<Step> addPath(Query query, String path,
0833:                    String searchDirs) {
0834:                if (path == null || path.equals("")) {
0835:                    return query.getSteps().subList(0, 0);
0836:                }
0837:                if (searchDirs == null) {
0838:                    searchDirs = "";
0839:                }
0840:
0841:                List<Step> list = query.getSteps();
0842:                int initialSize = list.size();
0843:
0844:                StringTokenizer pathTokenizer = new StringTokenizer(path, ",");
0845:                StringTokenizer searchDirsTokenizer = new StringTokenizer(
0846:                        searchDirs, ",");
0847:
0848:                Cloud cloud = query.getCloud();
0849:
0850:                if (query.getSteps().size() == 0) { // if no steps yet, first step must be added with addStep
0851:                    String completeFirstToken = pathTokenizer.nextToken()
0852:                            .trim();
0853:                    String firstToken = removeDigits(completeFirstToken);
0854:                    //if (cloud.hasRole(firstToken)) {
0855:                    // you cannot start with a role.., should we throw exception?
0856:                    // naa, the following code will throw exception that node type does not exist.
0857:                    //}
0858:                    Step step = query.addStep(cloud.getNodeManager(firstToken));
0859:                    if (!firstToken.equals(completeFirstToken)) {
0860:                        query.setAlias(step, completeFirstToken);
0861:                    }
0862:                }
0863:
0864:                String searchDir = null; // outside the loop, so defaulting to previous searchDir
0865:                while (pathTokenizer.hasMoreTokens()) {
0866:                    String completeToken = pathTokenizer.nextToken().trim();
0867:                    String token = removeDigits(completeToken);
0868:
0869:                    if (searchDirsTokenizer.hasMoreTokens()) {
0870:                        searchDir = searchDirsTokenizer.nextToken();
0871:                    }
0872:
0873:                    if (cloud.hasRole(token) && pathTokenizer.hasMoreTokens()) {
0874:                        if (cloud.hasNodeManager(token)) {
0875:                            // Ambigious path element '" + token + "', is both a role and a nodemanager
0876:                            // This is pretty common though. E.g. 'posrel'.
0877:                        }
0878:                        String nodeManagerAlias = pathTokenizer.nextToken()
0879:                                .trim();
0880:                        String nodeManagerName = removeDigits(nodeManagerAlias);
0881:                        NodeManager nodeManager = cloud
0882:                                .getNodeManager(nodeManagerName);
0883:                        RelationStep relationStep = query.addRelationStep(
0884:                                nodeManager, token, searchDir);
0885:
0886:                        /// make it possible to postfix with numbers manually
0887:                        if (!cloud.hasRole(completeToken)) {
0888:                            query.setAlias(relationStep, completeToken);
0889:                        }
0890:                        if (!nodeManagerName.equals(nodeManagerAlias)) {
0891:                            Step next = relationStep.getNext();
0892:                            query.setAlias(next, nodeManagerAlias);
0893:                        }
0894:                    } else {
0895:                        NodeManager nodeManager = cloud.getNodeManager(token);
0896:                        RelationStep step = query.addRelationStep(nodeManager,
0897:                                null /* role */, searchDir);
0898:                        if (!completeToken.equals(nodeManager.getName())) {
0899:                            Step next = step.getNext();
0900:                            query.setAlias(next, completeToken);
0901:                        }
0902:                    }
0903:                }
0904:                if (searchDirsTokenizer.hasMoreTokens()) {
0905:                    throw new BridgeException("Too many search directions ("
0906:                            + path + "/" + searchDirs + ")");
0907:                }
0908:                return list.subList(initialSize, list.size());
0909:            }
0910:
0911:            /**
0912:             * Adds a number of fields. Fields is represented as a comma separated string.
0913:             * @param query The query where the fields should be added to
0914:             * @param fields a comma separated string of fields
0915:             * @return The new stepfields
0916:             */
0917:            public static List<StepField> addFields(Query query, String fields) {
0918:                List<StepField> result = new ArrayList<StepField>();
0919:                if (fields == null || fields.equals("")) {
0920:                    return result;
0921:                }
0922:
0923:                for (String fieldName : StringSplitter.split(fields)) {
0924:                    result.add(query.addField(fieldName));
0925:                }
0926:                return result;
0927:
0928:            }
0929:
0930:            /**
0931:             * Add startNodes to the first step with the correct type to the given query. The nodes are identified
0932:             * by a String, which could be prefixed with a step-alias, if you want to add the nodes to
0933:             * another then this found step.
0934:             *
0935:             * Furthermore may the nodes by identified by their alias, if they have one.
0936:             * @param query query to add the startnodes
0937:             * @param startNodes start nodes
0938:             *
0939:             * @see org.mmbase.module.core.ClusterBuilder#getMultiLevelSearchQuery(List, List, String, List, String, List, List, int)
0940:             * (this is essentially a 'bridge' version of the startnodes part)
0941:             */
0942:            public static void addStartNodes(Query query, String startNodes) {
0943:                if (startNodes == null || "".equals(startNodes)
0944:                        || "-1".equals(startNodes)) {
0945:                    return;
0946:                }
0947:
0948:                Step firstStep = null; // the 'default' step to which nodes are added. It is the first step which corresponds with the type of the first node.
0949:
0950:                for (String nodeAlias : StringSplitter.split(startNodes)) {
0951:                    // can be a string, prefixed with the step alias.
0952:                    Step step; // the step to which the node must be added (defaults to 'firstStep').
0953:                    String nodeNumber; // a node number or perhaps also a node alias.
0954:                    {
0955:                        int dot = nodeAlias.indexOf('.'); // this feature is not in core. It should be considered experimental
0956:                        if (dot == -1) {
0957:                            step = firstStep;
0958:                            nodeNumber = nodeAlias;
0959:                        } else {
0960:                            step = query.getStep(nodeAlias.substring(0, dot));
0961:                            nodeNumber = nodeAlias.substring(dot + 1);
0962:                        }
0963:                    }
0964:
0965:                    if (firstStep == null) { // firstStep not yet determined, do that now.
0966:                        Node node;
0967:                        try {
0968:                            node = query.getCloud().getNode(nodeNumber);
0969:                        } catch (NotFoundException nfe) { // alias with dot?
0970:                            node = query.getCloud().getNode(nodeAlias);
0971:                        }
0972:                        NodeManager nodeManager = node.getNodeManager();
0973:                        for (Step queryStep : query.getSteps()) {
0974:                            NodeManager queryNodeManager = query.getCloud()
0975:                                    .getNodeManager(queryStep.getTableName());
0976:                            if (queryNodeManager.equals(nodeManager)
0977:                                    || queryNodeManager.getDescendants()
0978:                                            .contains(nodeManager)) {
0979:                                // considering inheritance. ClusterBuilder is not doing that, but I think it is a bug.
0980:                                firstStep = queryStep;
0981:                                break;
0982:                            }
0983:                        }
0984:                        if (firstStep == null) {
0985:                            // odd..
0986:                            // See also org.mmbase.module.core.ClusterBuilder#getMultiLevelSearchQuery
0987:                            // specified a node which is not of the type of one of the steps.
0988:                            // take as default the 'first' step (which will make the result empty, compatible with 1.6, bug #6440).
0989:                            firstStep = query.getSteps().get(0);
0990:                        }
0991:                    }
0992:
0993:                    if (step == null) {
0994:                        step = firstStep;
0995:                    }
0996:
0997:                    try {
0998:                        try {
0999:                            query.addNode(step, Integer.parseInt(nodeNumber));
1000:                        } catch (NumberFormatException nfe) {
1001:                            query.addNode(step, query.getCloud().getNode(
1002:                                    nodeNumber)); // node was specified by alias.
1003:                        }
1004:                    } catch (NotFoundException nnfe) {
1005:                        query
1006:                                .addNode(step, query.getCloud().getNode(
1007:                                        nodeAlias)); // perhas an alias containing a dot?
1008:                    }
1009:                }
1010:            }
1011:
1012:            /**
1013:             * Takes the query, and does a count with the same constraints (so ignoring 'offset' and 'max')
1014:             * @param query query as base for the count
1015:             * @return number of results
1016:             */
1017:            public static int count(Query query) {
1018:                Cloud cloud = query.getCloud();
1019:                Query count = query.aggregatingClone();
1020:                int type = query.isDistinct() ? AggregatedField.AGGREGATION_TYPE_COUNT_DISTINCT
1021:                        : AggregatedField.AGGREGATION_TYPE_COUNT;
1022:
1023:                String resultName;
1024:                if (query instanceof  NodeQuery) {
1025:                    // all fields are present of the node-step, so, we could use the number field simply.
1026:                    resultName = "number";
1027:                    NodeQuery nq = (NodeQuery) query;
1028:                    count.addAggregatedField(nq.getNodeStep(), nq
1029:                            .getNodeManager().getField(resultName), type);
1030:                } else {
1031:                    List<StepField> fields = query.getFields();
1032:                    if (fields.size() == 0) { // for non-distinct queries always the number fields would be available
1033:                        throw new IllegalArgumentException(
1034:                                "Cannot count queries with less than one field: "
1035:                                        + query);
1036:                    }
1037:
1038:                    if (query.isDistinct() && fields.size() > 1) {
1039:                        // aha hmm. Well, we also find it ok if all fields are of one step, and 'number' is present
1040:                        resultName = null;
1041:                        Step step = null;
1042:                        for (StepField sf : fields) {
1043:                            if (step == null) {
1044:                                step = sf.getStep();
1045:                            } else {
1046:                                if (!step.equals(sf.getStep())) {
1047:                                    throw new UnsupportedOperationException(
1048:                                            "Cannot count distinct queries with fields of more than one step. Current fields: "
1049:                                                    + fields);
1050:                                }
1051:                            }
1052:                            if (sf.getFieldName().equals("number")) {
1053:                                resultName = sf.getFieldName();
1054:                            }
1055:                        }
1056:                        if (resultName == null) {
1057:                            throw new UnsupportedOperationException(
1058:                                    "Cannot count distinct queries with more than one field if 'number' field is missing. Current fields: "
1059:                                            + fields);
1060:                        }
1061:                        count
1062:                                .addAggregatedField(step, cloud.getNodeManager(
1063:                                        step.getTableName()).getField(
1064:                                        resultName), type);
1065:                    } else {
1066:                        // simply take this one field
1067:                        StepField sf = fields.get(0);
1068:                        Step step = sf.getStep();
1069:                        resultName = sf.getFieldName();
1070:                        count
1071:                                .addAggregatedField(step, cloud.getNodeManager(
1072:                                        step.getTableName()).getField(
1073:                                        resultName), type);
1074:                    }
1075:                }
1076:                NodeList r = cloud.getList(count);
1077:                if (r.size() != 1)
1078:                    throw new RuntimeException("Count query " + query
1079:                            + " did not give one result but " + r);
1080:                Node result = r.getNode(0);
1081:                return result.getIntValue(resultName);
1082:            }
1083:
1084:            /**
1085:             * @since MMBase-1.8
1086:             */
1087:            protected static Object aggregate(Query query, StepField field,
1088:                    int type) {
1089:                Cloud cloud = query.getCloud();
1090:                Query aggregate = query.aggregatingClone();
1091:                String resultName = field.getFieldName();
1092:                Step step = field.getStep();
1093:                aggregate.addAggregatedField(step, cloud.getNodeManager(
1094:                        step.getTableName()).getField(resultName), type);
1095:                NodeList r = cloud.getList(aggregate);
1096:                if (r.size() != 1)
1097:                    throw new RuntimeException("Aggregated query " + query
1098:                            + " did not give one result but " + r);
1099:                Node result = r.getNode(0);
1100:                return result.getValue(resultName);
1101:            }
1102:
1103:            /**
1104:             * @since MMBase-1.8
1105:             */
1106:            public static Object min(Query query, StepField field) {
1107:                return aggregate(query, field,
1108:                        AggregatedField.AGGREGATION_TYPE_MIN);
1109:            }
1110:
1111:            /**
1112:             * @since MMBase-1.8
1113:             */
1114:            public static Object max(Query query, StepField field) {
1115:                return aggregate(query, field,
1116:                        AggregatedField.AGGREGATION_TYPE_MAX);
1117:            }
1118:
1119:            /**
1120:             * Searches a list of Steps for a step with a certain name. (alias or tableName)
1121:             * @param steps steps to search through
1122:             * @param stepAlias alias to search for
1123:             * @return The Step if found, otherwise null
1124:             * @throws ClassCastException if list does not contain only Steps
1125:             */
1126:            public static Step searchStep(List<Step> steps, String stepAlias) {
1127:                if (log.isDebugEnabled()) {
1128:                    log.debug("Searching '" + stepAlias + "' in " + steps);
1129:                }
1130:                // first try aliases
1131:                for (Step step : steps) {
1132:                    if (stepAlias.equals(step.getAlias())) {
1133:                        return step;
1134:                    }
1135:                }
1136:                // if no aliases found, try table names
1137:                for (Step step : steps) {
1138:                    if (stepAlias.equals(step.getTableName())) {
1139:                        return step;
1140:                    }
1141:                }
1142:                return null;
1143:            }
1144:
1145:            /**
1146:             * Returns the NodeQuery returning the given Node. This query itself is not very useful, because
1147:             * you already have its result (the node), but it is convenient as a base query for many other
1148:             * goals.
1149:             *
1150:             * If the node is uncommited, it cannot be queried, and the node query returning all nodes from
1151:             * the currect type will be returned.
1152:             *
1153:             * @param node Node to create the query from
1154:             * @return A new NodeQuery object
1155:             */
1156:            public static NodeQuery createNodeQuery(Node node) {
1157:                NodeManager nm = node.getNodeManager();
1158:                NodeQuery query = node.getCloud().createNodeQuery(); // use the version which can accept more steps
1159:                Step step = query.addStep(nm);
1160:                query.setNodeStep(step);
1161:                if (!node.isNew()) {
1162:                    query.addNode(step, node);
1163:                }
1164:                return query;
1165:            }
1166:
1167:            /**
1168:             * Returns a query to find the nodes related to the given node.
1169:             * @param node start node
1170:             * @param otherNodeManager node manager on the other side of the relation
1171:             * @param role role of the relation
1172:             * @param direction direction of the relation
1173:             * @return A new NodeQuery object
1174:             */
1175:            public static NodeQuery createRelatedNodesQuery(Node node,
1176:                    NodeManager otherNodeManager, String role, String direction) {
1177:                NodeQuery query = createNodeQuery(node);
1178:                if (otherNodeManager == null)
1179:                    otherNodeManager = node.getCloud().getNodeManager("object");
1180:                RelationStep step = query.addRelationStep(otherNodeManager,
1181:                        role, direction);
1182:                query.setNodeStep(step.getNext());
1183:                return query;
1184:            }
1185:
1186:            /**
1187:             * Returns a query to find the relations nodes of the given node.
1188:             * @param node start node
1189:             * @param otherNodeManager node manager on the other side of the relation
1190:             * @param role role of the relation
1191:             * @param direction direction of the relation
1192:             * @return A new NodeQuery object
1193:             */
1194:            public static NodeQuery createRelationNodesQuery(Node node,
1195:                    NodeManager otherNodeManager, String role, String direction) {
1196:                NodeQuery query = createNodeQuery(node);
1197:                if (otherNodeManager == null)
1198:                    otherNodeManager = node.getCloud().getNodeManager("object");
1199:                RelationStep step = query.addRelationStep(otherNodeManager,
1200:                        role, direction);
1201:                query.setNodeStep(step);
1202:                return query;
1203:            }
1204:
1205:            /**
1206:             * Returns a query to find the relations nodes between two given nodes.
1207:             *
1208:             * To test <em>whether</em> to nodes are related you can use e.g.:
1209:             * <code>
1210:             *  if (Queries.count(Queries.createRelationNodesQuery(node1, node2, "posrel", null)) > 0) {
1211:             *    ..
1212:             *  }
1213:             * </code>
1214:             * @param node start node
1215:             * @param otherNode node on the other side of the relation
1216:             * @param role role of the relation
1217:             * @param direction direction of the relation
1218:             * @return A new NodeQuery object
1219:             * @since MMBase-1.8
1220:             */
1221:            public static NodeQuery createRelationNodesQuery(Node node,
1222:                    Node otherNode, String role, String direction) {
1223:                NodeQuery query = createNodeQuery(node);
1224:                NodeManager otherNodeManager = otherNode.getNodeManager();
1225:                RelationStep step = query.addRelationStep(otherNodeManager,
1226:                        role, direction);
1227:                Step nextStep = step.getNext();
1228:                query.addNode(nextStep, otherNode.getNumber());
1229:                query.setNodeStep(step);
1230:                return query;
1231:            }
1232:
1233:            /**
1234:             * Queries a list of cluster nodes, using a {@link org.mmbase.bridge.NodeQuery} (so al fields of
1235:             * one step are available), plus some fields of the relation step.  The actual node can be got
1236:             * from the node cache by doing a {@link org.mmbase.bridge.Node#getNodeValue} with the {@link
1237:             * org.mmbase.bridge.NodeList#NODESTEP_PROPERTY} property.  The fields of the relation can be got by
1238:             * prefixing their names by the role and a dot (as normal in multilevel results).
1239:             * @param node start node
1240:             * @param otherNodeManager node manager on the other side of the relation
1241:             * @param role role of the relation
1242:             * @param direction direction of the relation
1243:             * @param relationFields  Comma separated string of fields which must be queried from the relation step
1244:             * @param sortOrders      Comma separated string of fields of sortorders, or the empty string or <code>null</code>
1245:             *                        So, this methods is targeted at the use of 'posrel' and similar fields, because sorting on other fields isn't possible right now.
1246:             * @since MMBase-1.8
1247:             * @todo  EXPERIMENTAL
1248:             */
1249:            public static NodeList getRelatedNodes(Node node,
1250:                    NodeManager otherNodeManager, String role,
1251:                    String direction, String relationFields, String sortOrders) {
1252:                NodeQuery q = Queries.createRelatedNodesQuery(node,
1253:                        otherNodeManager, role, direction);
1254:                addRelationFields(q, role, relationFields, sortOrders);
1255:                return q.getCloud().getList(q);
1256:            }
1257:
1258:            /**
1259:             * @since MMBase-1.8
1260:             */
1261:            public static NodeQuery addRelationFields(NodeQuery q, String role,
1262:                    String relationFields, String sortOrders) {
1263:                List<String> list = StringSplitter.split(relationFields);
1264:                List<String> orders = StringSplitter.split(sortOrders);
1265:                Iterator<String> j = orders.iterator();
1266:                for (String fieldName : list) {
1267:                    StepField sf = q.addField(role + "." + fieldName);
1268:                    if (j.hasNext()) {
1269:                        String so = j.next();
1270:                        q.addSortOrder(sf, getSortOrder(so));
1271:                    }
1272:                }
1273:                return q;
1274:            }
1275:
1276:            /**
1277:             * Add a sortorder (DESCENDING) on al the'number' fields of the query, on which there is not yet a
1278:             * sortorder. This ensures that the query result is ordered uniquely.
1279:             * @param q query to change
1280:             * @return The changed Query
1281:             */
1282:            public static Query sortUniquely(final Query q) {
1283:                List<Step> steps = null;
1284:
1285:                // remove the ones which are already sorted
1286:                for (SortOrder sortOrder : q.getSortOrders()) {
1287:                    if (sortOrder.getField().getFieldName().equals("number")) {
1288:                        Step step = sortOrder.getField().getStep();
1289:                        if (steps == null) {
1290:                            // instantiate new ArrayList only if really necessary
1291:                            steps = new ArrayList<Step>(q.getSteps());
1292:                        }
1293:                        steps.remove(step);
1294:                    }
1295:                }
1296:                if (steps == null) {
1297:                    steps = q.getSteps();
1298:                }
1299:                // add sort order on the remaining ones:
1300:                for (Step step : steps) {
1301:                    StepField sf = q.createStepField(step, "number");
1302:                    if (sf == null) {
1303:                        throw new RuntimeException(
1304:                                "Create stepfield for 'number' field returned null!");
1305:                    }
1306:                    q.addSortOrder(sf, SortOrder.ORDER_DESCENDING);
1307:                }
1308:                return q;
1309:            }
1310:
1311:            /**
1312:             * Make sure all sorted fields are queried
1313:             * @since MMBase-1.8
1314:             */
1315:            public static Query addSortedFields(Query q) {
1316:                List<StepField> fields = q.getFields();
1317:                for (SortOrder order : q.getSortOrders()) {
1318:                    StepField field = order.getField();
1319:                    Step s = field.getStep();
1320:                    StepField sf = q.createStepField(s, q.getCloud()
1321:                            .getNodeManager(s.getTableName()).getField(
1322:                                    field.getFieldName()));
1323:                    if (!fields.contains(sf)) {
1324:                        q.addField(s, q.getCloud().getNodeManager(
1325:                                s.getTableName())
1326:                                .getField(field.getFieldName()));
1327:                    }
1328:                }
1329:                return q;
1330:            }
1331:
1332:            /**
1333:             * Obtains a value for the field of a sortorder from a given node.
1334:             * Used to set constraints based on sortorder.
1335:             * @since MMBase-1.8
1336:             */
1337:            public static Object getSortOrderFieldValue(Node node,
1338:                    SortOrder sortOrder) {
1339:                String fieldName = sortOrder.getField().getFieldName();
1340:                if (node == null)
1341:                    throw new IllegalArgumentException("No node given");
1342:                Object value = node.getValue(fieldName);
1343:                if (value == null) {
1344:                    Step step = sortOrder.getField().getStep();
1345:                    String pref = step.getAlias();
1346:                    if (pref == null) {
1347:                        pref = step.getTableName();
1348:                    }
1349:                    value = node.getValue(pref + "." + fieldName);
1350:
1351:                }
1352:                if (value instanceof  Node) {
1353:                    value = ((Node) value).getNumber();
1354:                }
1355:                return value;
1356:            }
1357:
1358:            /**
1359:             * Compare tho nodes, with a SortOrder. This determins where a certain node is smaller or bigger than a certain other node, with respect to some SortOrder.
1360:             * This is used by {@link #compare(Node, Node, List)}
1361:             *
1362:             * If node2 is only 'longer' then node1, but otherwise equal, then it is bigger.
1363:             *
1364:             * @since MMBase-1.8
1365:             */
1366:            public static int compare(Node node1, Node node2,
1367:                    SortOrder sortOrder) {
1368:                return compare(getSortOrderFieldValue(node1, sortOrder),
1369:                        getSortOrderFieldValue(node2, sortOrder), sortOrder);
1370:
1371:            }
1372:
1373:            /**
1374:             * @since MMBase-1.8
1375:             */
1376:            public static int compare(Object value, Object value2,
1377:                    SortOrder sortOrder) {
1378:                int result;
1379:                // compare values - if they differ, detemrine whether
1380:                // they are bigger or smaller and return the result
1381:                // remaining fields are not of interest ionce a difference is found
1382:                if (value == null) {
1383:                    if (value2 != null) {
1384:                        return 1;
1385:                    } else {
1386:                        result = 0;
1387:                    }
1388:                } else if (value2 == null) {
1389:                    return -1;
1390:                } else {
1391:                    // compare the results
1392:                    try {
1393:                        result = ((Comparable<Object>) value).compareTo(value2);
1394:                    } catch (ClassCastException cce) {
1395:                        // This should not occur, and indicates very odd values are being sorted on (i.e. byte arrays).
1396:                        // warn and ignore this sortorder
1397:                        log.warn("Cannot compare values " + value + " and "
1398:                                + value2 + " in sortorder field "
1399:                                + sortOrder.getField().getFieldName()
1400:                                + " in step "
1401:                                + sortOrder.getField().getStep().getAlias());
1402:                        result = 0;
1403:                    }
1404:                }
1405:                // if the order of this field is descending,
1406:                // then the result of the comparison is the reverse (the node is 'greater' if the value is 'less' )
1407:                if (sortOrder.getDirection() == SortOrder.ORDER_DESCENDING) {
1408:                    result = -result;
1409:                }
1410:                return result;
1411:            }
1412:
1413:            /**
1414:             * Does a field-by-field compare of two Node objects, on the fields used to order the nodes.
1415:             * This is used to determine whether a node comes after or before another, in a certain query result.
1416:             *
1417:             * @return -1 if node1 is smaller than node 2, 0 if both nodes are equals, and +1 is node 1 is greater than node 2.
1418:             * @since MMBase-1.8
1419:             */
1420:            public static int compare(Node node1, Node node2,
1421:                    List<SortOrder> sortOrders) {
1422:                if (node1 == null)
1423:                    return -1;
1424:                if (node2 == null)
1425:                    return +1;
1426:                int result = 0;
1427:                Iterator<SortOrder> i = sortOrders.iterator();
1428:                while (result == 0 && i.hasNext()) {
1429:                    SortOrder order = i.next();
1430:                    result = compare(node1, node2, order);
1431:                }
1432:                // if all fields match - return 0 as if equal
1433:                return result;
1434:            }
1435:
1436:            public static void main(String[] argv) {
1437:                System.out
1438:                        .println(ConstraintParser
1439:                                .convertClauseToDBS("(([cpsettings.status]='[A]' OR [cpsettings.status]='I') AND [users.account] != '') and (lower([users.account]) LIKE '%t[est%' OR lower([users.email]) LIKE '%te]st%' OR lower([users.firstname]) LIKE '%t[e]st%' OR lower([users.lastname]) LIKE '%]test%')"));
1440:            }
1441:
1442:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.