1 10 package org.mmbase.module.core; 11 12 import java.util.*; 13 import org.mmbase.module.corebuilders.*; 14 import org.mmbase.core.CoreField; 15 import org.mmbase.bridge.Field; 16 import org.mmbase.core.util.Fields; 17 import org.mmbase.util.functions.*; 18 import org.mmbase.datatypes.*; 19 import org.mmbase.storage.search.*; 20 import org.mmbase.storage.search.implementation.*; 21 import org.mmbase.storage.search.legacy.ConstraintParser; 22 import org.mmbase.util.QueryConvertor; 23 import org.mmbase.util.logging.*; 24 25 26 55 public class ClusterBuilder extends VirtualBuilder { 56 57 64 public static final int SEARCH_BOTH = RelationStep.DIRECTIONS_BOTH; 65 66 71 public static final int SEARCH_DESTINATION = RelationStep.DIRECTIONS_DESTINATION; 72 73 78 public static final int SEARCH_SOURCE = RelationStep.DIRECTIONS_SOURCE; 79 80 86 public static final int SEARCH_ALL = RelationStep.DIRECTIONS_ALL; 87 88 98 public static final int SEARCH_EITHER = RelationStep.DIRECTIONS_EITHER; 99 100 private static final Logger log= Logging.getLoggerInstance(ClusterBuilder.class); 102 103 109 public ClusterBuilder(MMBase m) { 110 super(m, "clusternodes"); 111 } 112 113 118 public static int getSearchDir(String search) { 119 if (search == null) { 120 return RelationStep.DIRECTIONS_EITHER; 121 } 122 return org.mmbase.bridge.util.Queries.getRelationStepDirection(search); 123 } 124 125 130 public static String getSearchDirString(int search) { 131 if (search == RelationStep.DIRECTIONS_DESTINATION) { 132 return "DESTINATION"; 133 } else if (search == RelationStep.DIRECTIONS_SOURCE) { 134 return "SOURCE"; 135 } else if (search == RelationStep.DIRECTIONS_BOTH) { 136 return "BOTH"; 137 } else if (search == RelationStep.DIRECTIONS_ALL) { 138 return "ALL"; 139 } else { 140 return "EITHER"; 141 } 142 } 143 144 152 public MMObjectNode getNewNode(String owner) { 153 throw new UnsupportedOperationException ("One cannot create new ClusterNodes"); 154 } 155 156 163 public String getGUIIndicator(MMObjectNode node) { 164 String s = node.getStringValue("name"); 166 if (s != null) { 167 return s; 168 } 169 170 StringBuffer sb = new StringBuffer (); 172 for (Iterator i= node.getValues().entrySet().iterator(); i.hasNext();) { 173 Map.Entry entry = (Map.Entry)i.next(); 174 String key = (String ) entry.getKey(); 175 if (key.endsWith(".name")) { 176 if (s.length() != 0) { 177 sb.append(", "); 178 } 179 sb.append(entry.getValue()); 180 } 181 } 182 if (sb.length() > 15) { 183 return sb.substring(0, 12) + "..."; 184 } else { 185 return sb.toString(); 186 } 187 } 188 189 199 protected String getGUIIndicator(MMObjectNode node, Parameters pars) { 200 201 if (node == null) throw new RuntimeException ("Tried to get GUIIndicator for " + pars + " with NULL node"); 202 203 ClusterNode clusterNode = (ClusterNode) node; 204 205 String field = pars.getString(Parameter.FIELD); 206 if (field == null) { 207 return super.getGUIIndicator(node, pars); 208 } else { 209 int pos = field.indexOf('.'); 210 if (pos != -1) { 211 String bulName = getTrueTableName(field.substring(0, pos)); 212 MMObjectNode n = clusterNode.getRealNode(bulName); 213 if (n != null) { 214 MMObjectBuilder bul= n.getBuilder(); 215 if (bul != null) { 216 String fieldName = field.substring(pos + 1); 218 Parameters newPars = new Parameters(pars.getDefinition(), pars); 219 newPars.set(Parameter.FIELD, fieldName); 220 newPars.set("stringvalue", null); 221 org.mmbase.bridge.Node bnode = (org.mmbase.bridge.Node) pars.get(Parameter.NODE); 222 if (bnode != null) { 223 newPars.set(Parameter.NODE, bnode.getNodeValue(bulName)); 224 } 225 newPars.set(Parameter.CORENODE, n); 226 return (String ) bul.guiFunction.getFunctionValue(newPars); 227 } 228 } 229 } 230 return super.getGUIIndicator(node, pars); 231 } 232 } 233 234 239 public String getBuilderNameFromField(String fieldName) { 240 int pos = fieldName.indexOf("."); 241 if (pos != -1) { 242 String bulName = fieldName.substring(0, pos); 243 return getTrueTableName(bulName); 244 } 245 return ""; 246 } 247 248 253 public static String getFieldNameFromField(String fieldname) { 254 int pos= fieldname.indexOf("."); 255 if (pos != -1) { 256 fieldname = fieldname.substring(pos + 1); 257 } 258 return fieldname; 259 } 260 261 266 public FieldDefs getField(String fieldName) { 267 String builderName = getBuilderNameFromField(fieldName); 268 if (builderName.length() > 0) { 269 MMObjectBuilder bul = mmb.getBuilder(builderName); 270 if (bul == null) { 271 throw new RuntimeException ("No builder with name '" + builderName + "' found"); 272 } 273 return bul.getField(getFieldNameFromField(fieldName)); 274 } else { 275 MMObjectBuilder bul = mmb.getBuilder(getTrueTableName(fieldName)); 277 if (bul != null) { 278 return new FieldDefs(fieldName, FieldDefs.TYPE_NODE, -1, FieldDefs.STATE_VIRTUAL, org.mmbase.datatypes.DataTypes.getDataType("node")); 279 } 280 } 281 return null; 282 } 283 284 public List getFields(int order) { 285 throw new UnsupportedOperationException ("Cluster-nodes can have any field."); 286 } 287 public Collection getFields() { 288 throw new UnsupportedOperationException ("Cluster-nodes can have any field."); 289 } 290 291 294 public Map getFields(MMObjectNode node) { 295 Map ret = new HashMap(); 296 Iterator i = node.getValues().keySet().iterator(); 297 DataType nodeType = DataTypes.getDataType("node"); 298 while (i.hasNext()) { 299 String name = (String ) i.next(); 300 int pos = name.indexOf("."); 301 if (pos != -1) { 302 String builderName = name.substring(0, pos); 303 if (! ret.containsKey(builderName)) { 304 CoreField fd = Fields.createField(builderName, Field.TYPE_NODE, Field.TYPE_UNKNOWN, Field.STATE_VIRTUAL, nodeType); 305 ret.put(builderName, fd); 306 } 307 } 308 ret.put(name, getField(name)); 309 } 310 return ret; 311 } 312 313 320 public Vector searchMultiLevelVector( 321 int snode, 322 Vector fields, 323 String pdistinct, 324 Vector tables, 325 String where, 326 Vector orderVec, 327 Vector direction) { 328 Vector v= new Vector(); 329 v.addElement("" + snode); 330 return searchMultiLevelVector(v, fields, pdistinct, tables, where, orderVec, direction, RelationStep.DIRECTIONS_EITHER); 331 } 332 333 339 public Vector searchMultiLevelVector( 340 Vector snodes, 341 Vector fields, 342 String pdistinct, 343 Vector tables, 344 String where, 345 Vector orderVec, 346 Vector direction) { 347 return searchMultiLevelVector(snodes, fields, pdistinct, tables, where, orderVec, direction, RelationStep.DIRECTIONS_EITHER); 348 } 349 350 380 public Vector searchMultiLevelVector(List snodes, List fields, String pdistinct, List tables, String where, List sortFields, 381 List directions, int searchDir) { 382 List searchDirs = new ArrayList(); 383 searchDirs.add(new Integer (searchDir)); 384 return searchMultiLevelVector(snodes, fields, pdistinct, tables, where, sortFields, directions, searchDirs); 385 } 386 387 416 public Vector searchMultiLevelVector(List snodes, List fields, String pdistinct, List tables, String where, List sortFields, 417 List directions, List searchDirs) { 418 try { 420 SearchQuery query = getMultiLevelSearchQuery(snodes, fields, pdistinct, tables, where, sortFields, directions, searchDirs); 421 List clusterNodes = getClusterNodes(query); 422 return new Vector(clusterNodes); 423 } catch (Exception e) { 424 log.error(e + Logging.stackTrace(e)); 425 return null; 426 } 427 } 428 429 440 public List getClusterNodes(SearchQuery query) throws SearchQueryException { 441 442 445 return mmb.getSearchQueryHandler().getNodes(query, this); 446 447 } 448 449 456 private String getTableName(String table) { 457 int end = table.length() ; 458 if (end == 0) throw new IllegalArgumentException ("Table name too short '" + table + "'"); 459 while (Character.isDigit(table.charAt(end -1))) --end; 460 return table.substring(0, end ); 461 } 462 463 469 private String getTrueTableName(String table) { 470 String tab = getTableName(table); 471 int rnumber = mmb.getRelDef().getNumberByName(tab); 472 if (rnumber != -1) { 473 return mmb.getRelDef().getBuilderName(new Integer (rnumber)); 474 } else { 475 return tab; 476 } 477 } 478 479 486 public String getShortedText(String fieldname, int number) { 487 String buildername= getBuilderNameFromField(fieldname); 488 if (buildername.length() > 0) { 489 MMObjectBuilder bul= mmb.getMMObject(buildername); 490 return bul.getShortedText(getFieldNameFromField(fieldname), bul.getNode(number)); 491 } 492 return null; 493 } 494 495 502 public byte[] getShortedByte(String fieldname, int number) { 503 String buildername= getBuilderNameFromField(fieldname); 504 if (buildername.length() > 0) { 505 MMObjectBuilder bul= mmb.getMMObject(buildername); 506 return bul.getShortedByte(getFieldNameFromField(fieldname), bul.getNode(number)); 507 } 508 return null; 509 } 510 511 552 public BasicSearchQuery getMultiLevelSearchQuery(List snodes, List fields, String pdistinct, List tables, String where, 553 List sortFields, List directions, int searchDir) { 554 List searchDirs = new ArrayList(); 555 searchDirs.add(new Integer (searchDir)); 556 return getMultiLevelSearchQuery(snodes, fields, pdistinct, tables, where, sortFields, directions, searchDirs); 557 } 558 559 598 public BasicSearchQuery getMultiLevelSearchQuery(List snodes, List fields, String pdistinct, List tables, String where, 599 List sortFields, List directions, List searchDirs) { 600 601 BasicSearchQuery query= new BasicSearchQuery(); 603 604 boolean distinct= pdistinct != null && pdistinct.equalsIgnoreCase("YES"); 606 query.setDistinct(distinct); 607 608 Map roles= new HashMap(); 610 Map fieldsByAlias= new HashMap(); 611 Map stepsByAlias= addSteps(query, tables, roles, !distinct, fieldsByAlias); 612 613 Iterator iFields= fields.iterator(); 615 while (iFields.hasNext()) { 616 String field = (String ) iFields.next(); 617 addFields(query, field, stepsByAlias, fieldsByAlias); 618 } 619 620 addSortOrders(query, sortFields, directions, fieldsByAlias); 622 623 if (snodes != null && snodes.size() > 0) { 626 Integer nodeNumber= new Integer (-1); 627 628 snodes= new ArrayList(snodes); 630 631 for (int i= snodes.size() - 1; i >= 0; i--) { 636 String str= (String )snodes.get(i); 637 try { 638 nodeNumber= new Integer (str); 639 } catch (NumberFormatException e) { 640 nodeNumber= new Integer (mmb.getOAlias().getNumber(str)); 643 if (nodeNumber.intValue() < 0) { 644 nodeNumber= new Integer (0); 645 } 646 } 647 snodes.set(i, nodeNumber); 648 } 649 650 BasicStep nodesStep= getNodesStep(query.getSteps(), nodeNumber.intValue()); 651 652 if (nodesStep == null) { 653 nodesStep = (BasicStep) query.getSteps().get(0); 656 } 657 658 Iterator iNodeNumbers= snodes.iterator(); 659 while (iNodeNumbers.hasNext()) { 660 Integer number= (Integer )iNodeNumbers.next(); 661 nodesStep.addNode(number.intValue()); 662 } 663 } 664 665 addRelationDirections(query, searchDirs, roles); 666 667 QueryConvertor.setConstraint(query, where); 670 671 return query; 672 } 673 674 694 Map addSteps(BasicSearchQuery query, List tables, Map roles, boolean includeAllReference, Map fieldsByAlias) { 696 697 Map stepsByAlias= new HashMap(); Set tableAliases= new HashSet(); 700 Iterator iTables= tables.iterator(); 701 if (iTables.hasNext()) { 702 String tableName= (String )iTables.next(); 704 MMObjectBuilder bul= getBuilder(tableName, roles); 705 String tableAlias= getUniqueTableAlias(tableName, tableAliases, tables); 706 BasicStep step= query.addStep(bul); 707 step.setAlias(tableAlias); 708 stepsByAlias.put(tableName, step); 709 if (includeAllReference) { 710 addField(query, step, "number", fieldsByAlias); 712 } 713 } 714 while (iTables.hasNext()) { 715 String tableName2 = (String )iTables.next(); 716 MMObjectBuilder bul2 = getBuilder(tableName2, roles); 717 BasicRelationStep relation; 718 BasicStep step2; 719 String tableName; 720 if (bul2 instanceof InsRel) { 721 tableName = tableName2; 723 InsRel bul = (InsRel)bul2; 724 tableName2 = (String )iTables.next(); 725 bul2 = getBuilder(tableName2, roles); 726 relation = query.addRelationStep(bul, bul2); 727 step2 = (BasicStep)relation.getNext(); 728 729 relation.setAlias(tableName); 732 step2.setAlias(tableName2); 733 if (includeAllReference) { 734 addField(query, relation, "number", fieldsByAlias); 736 addField(query, step2, "number", fieldsByAlias); 737 } 738 if (log.isDebugEnabled()) { 739 log.debug("Created a relation step " + relation + " (explicit)" + roles); 740 } 741 } else { 742 tableName = "insrel"; 744 InsRel bul = mmb.getInsRel(); 745 relation = query.addRelationStep(bul, bul2); 746 step2 = (BasicStep)relation.getNext(); 747 step2.setAlias(tableName2); if (includeAllReference) { 749 addField(query, step2, "number", fieldsByAlias); 751 } 752 if (log.isDebugEnabled()) { 753 log.debug("Created a relation step " + relation + " (implicit)"); 754 } 755 } 756 String tableAlias = getUniqueTableAlias(tableName, tableAliases, tables); 757 String tableAlias2 = getUniqueTableAlias(tableName2, tableAliases, tables); 758 if (! tableName.equals(tableAlias)) { 759 roles.put(tableAlias, roles.get(tableName)); 760 } 761 relation.setAlias(tableAlias); 762 step2.setAlias(tableAlias2); 763 stepsByAlias.put(tableAlias, relation); 764 stepsByAlias.put(tableAlias2, step2); 765 } 766 return stepsByAlias; 767 } 768 769 785 MMObjectBuilder getBuilder(String tableAlias, Map roles) { 787 String tableName= getTableName(tableAlias); 788 MMObjectBuilder bul= null; 790 try { 791 bul= mmb.getBuilder(tableName); 792 } catch (BuilderConfigurationException e) {} 793 794 if (bul == null) { 795 int rnumber= mmb.getRelDef().getNumberByName(tableName); 798 if (rnumber == -1) { 799 String msg= "Specified builder " + tableName + " does not exist."; 800 log.error(msg); 801 throw new IllegalArgumentException (msg); 802 } else { 803 bul = mmb.getRelDef().getBuilder(rnumber); roles.put(tableAlias, new Integer (rnumber)); 805 } 806 } else if (bul instanceof InsRel) { 807 int rnumber= mmb.getRelDef().getNumberByName(tableName); 808 if (rnumber != -1) { 809 roles.put(tableAlias, new Integer (rnumber)); 810 } 811 } 812 if (log.isDebugEnabled()) { 813 log.debug("Resolved table alias \"" + tableAlias + "\" to builder \"" + bul.getTableName() + "\""); 814 } 815 return bul; 816 } 817 818 832 String getUniqueTableAlias(String tableAlias, Set tableAliases, Collection originalAliases) { 834 835 if (tableAliases.contains(tableAlias)) { 838 tableName= getTableName(tableAlias); 839 840 tableAlias= tableName; 841 char ch= '0'; 842 while (originalAliases.contains(tableAlias) || tableAliases.contains(tableAlias)) { 843 if (ch > '9') { 845 throw new IndexOutOfBoundsException ("Failed to create unique table alias, because there " 846 + "are already 11 aliases for this tablename: \"" + tableName + "\""); 847 } 848 tableAlias = tableName + ch; 849 ch++; 850 } 851 } 852 853 tableAliases.add(tableAlias); 855 return tableAlias; 856 } 857 858 875 void addFields(BasicSearchQuery query, String expression, Map stepsByAlias, Map fieldsByAlias) { 877 878 int pos1= expression.indexOf('('); 881 int pos2= expression.indexOf(')'); 882 if (pos1 != -1 ^ pos2 != -1) { 883 throw new IllegalArgumentException ("Parenthesis do not match in expression: \"" + expression + "\""); 885 } else if (pos1 != -1) { 886 String parameters= expression.substring(pos1 + 1, pos2); 888 Iterator iParameters= getFunctionParameters(parameters).iterator(); 889 while (iParameters.hasNext()) { 890 String parameter= (String )iParameters.next(); 891 addFields(query, parameter, stepsByAlias, fieldsByAlias); 892 } 893 } else if (!Character.isDigit(expression.charAt(0))) { 894 int pos= expression.indexOf('.'); 895 if (pos < 1 || pos == (expression.length() - 1)) { 896 throw new IllegalArgumentException ("Invalid fieldname: \"" + expression + "\""); 897 } 898 int bracketOffset = (expression.startsWith("[") && expression.endsWith("]")) ? 1 : 0; 899 String stepAlias= expression.substring(0 + bracketOffset, pos); 900 String fieldName= expression.substring(pos + 1 - bracketOffset); 901 902 BasicStep step = (BasicStep)stepsByAlias.get(stepAlias); 903 if (step == null) { 904 throw new IllegalArgumentException ("Invalid step alias: \"" + stepAlias + "\" in fields list"); 905 } 906 addField(query, step, fieldName, fieldsByAlias); 907 } 908 } 909 910 921 private void addField(BasicSearchQuery query, BasicStep step, String fieldName, Map fieldsByAlias) { 922 923 String fieldAlias= step.getAlias() + "." + fieldName; 927 if (fieldsByAlias.containsKey(fieldAlias)) { 928 return; 930 } 931 932 MMObjectBuilder builder= mmb.getBuilder(step.getTableName()); 933 CoreField fieldDefs= builder.getField(fieldName); 934 if (fieldDefs == null) { 935 throw new IllegalArgumentException ("Not a known field of builder " + step.getTableName() + ": \"" + fieldName + "\""); 936 } 937 938 BasicStepField stepField= query.addField(step, fieldDefs); 940 fieldsByAlias.put(fieldAlias, stepField); 941 } 942 943 953 void addSortOrders(BasicSearchQuery query, List fieldNames, List directions, Map fieldsByAlias) { 955 956 if (fieldNames == null || fieldNames.size() == 0) { 958 return; 959 } 960 961 int defaultSortOrder= SortOrder.ORDER_ASCENDING; 962 if (directions != null && directions.size() != 0) { 963 if (((String )directions.get(0)).trim().equalsIgnoreCase("DOWN")) { 964 defaultSortOrder= SortOrder.ORDER_DESCENDING; 965 } 966 } 967 968 Iterator iFieldNames= fieldNames.iterator(); 969 Iterator iDirections= directions.iterator(); 970 while (iFieldNames.hasNext()) { 971 String fieldName= (String )iFieldNames.next(); 972 StepField field= (BasicStepField)fieldsByAlias.get(fieldName); 973 if (field == null) { 974 field= ConstraintParser.getField(fieldName, query.getSteps()); 976 } 977 if (field == null) { 978 throw new IllegalArgumentException ("Invalid fieldname: \"" + fieldName + "\""); 979 } 980 981 BasicSortOrder sortOrder= query.addSortOrder(field); 984 if (iDirections.hasNext()) { 986 String direction= (String )iDirections.next(); 987 if (direction.trim().equalsIgnoreCase("DOWN")) { 988 sortOrder.setDirection(SortOrder.ORDER_DESCENDING); 989 } else if (!direction.trim().equalsIgnoreCase("UP")) { 990 throw new IllegalArgumentException ("Parameter directions contains an invalid value ("+direction+"), should be UP or DOWN."); 991 } 992 993 } else { 994 sortOrder.setDirection(defaultSortOrder); 995 } 996 } 997 } 998 999 1008 BasicStep getNodesStep(List steps, int nodeNumber) { 1010 if (nodeNumber < 0) { 1011 return null; 1012 } 1013 1014 MMObjectNode node= getNode(nodeNumber); 1015 if (node == null) { 1016 return null; 1017 } 1018 1019 MMObjectBuilder builder = node.parent; 1020 BasicStep result = null; 1021 do { 1022 Iterator iSteps= steps.iterator(); 1024 while (iSteps.hasNext() && result == null) { 1025 BasicStep step= (BasicStep)iSteps.next(); 1026 if (step.getTableName().equals(builder.tableName)) { result = step; 1029 } 1030 } 1031 builder = builder.getParentBuilder(); 1033 } while (builder != null && result == null); 1034 1035 1040 1041 return result; 1042 } 1043 1044 1045 1056 void addRelationDirections(BasicSearchQuery query, List searchDirs, Map roles) { 1058 1059 Iterator iSteps = query.getSteps().iterator(); 1060 Iterator iSearchDirs = searchDirs.iterator(); 1061 int searchDir = RelationStep.DIRECTIONS_BOTH; 1062 1063 if (! iSteps.hasNext()) return; BasicStep sourceStep = (BasicStep)iSteps.next(); 1065 BasicStep destinationStep = null; 1066 1067 while (iSteps.hasNext()) { 1068 if (destinationStep != null) { 1069 sourceStep = destinationStep; 1070 } 1071 BasicRelationStep relationStep= (BasicRelationStep)iSteps.next(); 1072 destinationStep= (BasicStep)iSteps.next(); 1073 if (iSearchDirs.hasNext()) searchDir = ((Integer )iSearchDirs.next()).intValue(); 1074 1075 int sourceType = sourceStep.getBuilder().getObjectType(); 1077 1078 Integer role = (Integer ) roles.get(relationStep.getAlias()); 1080 1081 int destinationType = destinationStep.getBuilder().getObjectType(); 1083 1084 int roleInt; 1085 if (role != null) { 1086 roleInt = role.intValue(); 1087 relationStep.setRole(role); 1088 } else { 1089 roleInt = -1; 1090 } 1091 1092 if (!mmb.getTypeRel().optimizeRelationStep(relationStep, sourceType, destinationType, roleInt, searchDir)) { 1093 if (searchDir != RelationStep.DIRECTIONS_SOURCE && searchDir != RelationStep.DIRECTIONS_DESTINATION) { 1094 log.warn("No relation defined between " + sourceStep.getTableName() + " and " + destinationStep.getTableName() + " using " + relationStep + " with direction(s) " + getSearchDirString(searchDir) + ". Searching in 'destination' direction now, but perhaps the query should be fixed, because this should always result nothing."); 1095 } else { 1096 log.warn("No relation defined between " + sourceStep.getTableName() + " and " + destinationStep.getTableName() + " using " + relationStep + " with direction(s) " + getSearchDirString(searchDir) + ". Trying anyway, but perhaps the query should be fixed, because this should always result nothing."); 1097 } 1098 log.warn(Logging.applicationStacktrace()); 1099 } 1100 1101 } 1102 } 1103 1104} 1105
| Popular Tags
|