1 40 package org.dspace.browse; 41 42 import java.sql.Connection ; 43 import java.sql.PreparedStatement ; 44 import java.sql.ResultSet ; 45 import java.sql.SQLException ; 46 import java.text.MessageFormat ; 47 import java.util.ArrayList ; 48 import java.util.Collections ; 49 import java.util.HashMap ; 50 import java.util.Iterator ; 51 import java.util.List ; 52 import java.util.Map ; 53 import java.util.SortedMap ; 54 import java.util.StringTokenizer ; 55 import java.util.TreeMap ; 56 import java.util.WeakHashMap ; 57 58 import org.apache.log4j.Logger; 59 import org.dspace.content.Collection; 60 import org.dspace.content.Community; 61 import org.dspace.content.DCValue; 62 import org.dspace.content.Item; 63 import org.dspace.content.ItemComparator; 64 import org.dspace.content.ItemIterator; 65 import org.dspace.core.ConfigurationManager; 66 import org.dspace.core.Context; 67 import org.dspace.storage.rdbms.DatabaseManager; 68 import org.dspace.storage.rdbms.TableRow; 69 70 77 public class Browse 78 { 79 static final int AUTHORS_BROWSE = 0; 81 82 static final int ITEMS_BY_TITLE_BROWSE = 1; 83 84 static final int ITEMS_BY_AUTHOR_BROWSE = 2; 85 86 static final int ITEMS_BY_DATE_BROWSE = 3; 87 88 static final int SUBJECTS_BROWSE = 4; 89 90 static final int ITEMS_BY_SUBJECT_BROWSE = 5; 91 92 93 94 private static Logger log = Logger.getLogger(Browse.class); 95 96 99 private Browse() 100 { 101 } 102 103 118 public static BrowseInfo getAuthors(BrowseScope scope) throws SQLException 119 { 120 scope.setBrowseType(AUTHORS_BROWSE); 121 scope.setAscending(true); 122 scope.setSortByTitle(null); 123 124 return doBrowse(scope); 125 } 126 141 public static BrowseInfo getSubjects(BrowseScope scope) throws SQLException 142 { 143 scope.setBrowseType(SUBJECTS_BROWSE); 144 scope.setAscending(true); 145 scope.setSortByTitle(null); 146 147 return doBrowse(scope); 148 } 149 150 165 public static BrowseInfo getItemsByTitle(BrowseScope scope) 166 throws SQLException 167 { 168 scope.setBrowseType(ITEMS_BY_TITLE_BROWSE); 169 scope.setAscending(true); 170 scope.setSortByTitle(null); 171 172 return doBrowse(scope); 173 } 174 175 209 public static BrowseInfo getItemsByDate(BrowseScope scope, 210 boolean oldestfirst) throws SQLException 211 { 212 scope.setBrowseType(ITEMS_BY_DATE_BROWSE); 213 scope.setAscending(oldestfirst); 214 scope.setSortByTitle(null); 215 216 return doBrowse(scope); 217 } 218 219 240 public static BrowseInfo getItemsByAuthor(BrowseScope scope, 241 boolean sortByTitle) throws SQLException 242 { 243 if (!scope.hasFocus()) 244 { 245 throw new IllegalArgumentException ( 246 "Must specify an author for getItemsByAuthor"); 247 } 248 249 if (!(scope.getFocus() instanceof String )) 250 { 251 throw new IllegalArgumentException ( 252 "The focus for getItemsByAuthor must be a String"); 253 } 254 255 scope.setBrowseType(ITEMS_BY_AUTHOR_BROWSE); 256 scope.setAscending(true); 257 scope.setSortByTitle(sortByTitle ? Boolean.TRUE : Boolean.FALSE); 258 scope.setTotalAll(); 259 260 return doBrowse(scope); 261 } 262 263 284 public static BrowseInfo getItemsBySubject(BrowseScope scope, 285 boolean sortByTitle) throws SQLException 286 { 287 if (!scope.hasFocus()) 288 { 289 throw new IllegalArgumentException ( 290 "Must specify a subject for getItemsBySubject"); 291 } 292 293 if (!(scope.getFocus() instanceof String )) 294 { 295 throw new IllegalArgumentException ( 296 "The focus for getItemsBySubject must be a String"); 297 } 298 299 scope.setBrowseType(ITEMS_BY_SUBJECT_BROWSE); 300 scope.setAscending(true); 301 scope.setSortByTitle(sortByTitle ? Boolean.TRUE : Boolean.FALSE); 302 scope.setTotalAll(); 303 304 return doBrowse(scope); 305 } 306 307 316 public static List getLastSubmitted(BrowseScope scope) throws SQLException 317 { 318 Context context = scope.getContext(); 319 String sql = getLastSubmittedQuery(scope); 320 321 if (log.isDebugEnabled()) 322 { 323 log.debug("SQL for last submitted is \"" + sql + "\""); 324 } 325 326 List results = DatabaseManager.query(context, sql).toList(); 327 328 return getLastSubmittedResults(context, results); 329 } 330 331 337 private static String getLastSubmittedQuery(BrowseScope scope) 338 { 339 String table = getLastSubmittedTable(scope); 340 341 String query = "SELECT * FROM " + table 342 + getScopeClause(scope, "where") 343 + " ORDER BY date_accessioned DESC"; 344 345 if (!scope.hasNoLimit()) 346 { 347 if ("oracle".equals(ConfigurationManager.getProperty("db.name"))) 348 { 349 query = "SELECT * FROM (" + query + ") WHERE ROWNUM <=" 352 + scope.getTotal(); 353 } 354 else 355 { 356 query = query + " LIMIT " + scope.getTotal(); 358 } 359 } 360 361 return query; 362 } 363 364 371 private static String getLastSubmittedTable(BrowseScope scope) 372 { 373 if (scope.isCommunityScope()) 374 { 375 return "CommunityItemsByDateAccession"; 376 } 377 else if (scope.isCollectionScope()) 378 { 379 return "CollectionItemsByDateAccession"; 380 } 381 382 return "ItemsByDateAccessioned"; 383 } 384 385 393 private static List getLastSubmittedResults(Context context, List results) 394 throws SQLException 395 { 396 if ((results == null) || (results.isEmpty())) 397 { 398 return Collections.EMPTY_LIST; 399 } 400 401 List items = new ArrayList (); 402 403 for (Iterator iterator = results.iterator(); iterator.hasNext();) 406 { 407 TableRow row = (TableRow) iterator.next(); 408 Item item = Item.find(context, row.getIntColumn("item_id")); 409 items.add(item); 410 } 411 412 return items; 413 } 414 415 419 429 public static void itemRemoved(Context context, int id) throws SQLException 430 { 431 String sql = "delete from {0} where item_id = " + id; 432 433 String [] browseTables = BrowseTables.tables(); 434 435 for (int i = 0; i < browseTables.length; i++) 436 { 437 String query = MessageFormat.format(sql, 438 new String [] { browseTables[i] }); 439 DatabaseManager.updateQuery(context, query); 440 } 441 } 442 443 459 public static void itemChanged(Context context, Item item) 460 throws SQLException 461 { 462 itemRemoved(context, item.getID()); 466 467 if (!item.isArchived()) 468 { 469 return; 470 } 471 472 itemAdded(context, item); 473 } 474 475 485 public static void itemAdded(Context context, Item item) 486 throws SQLException 487 { 488 Community[] parents = item.getCommunities(); 490 491 for (int j = 0; j < parents.length; j++) 492 { 493 TableRow row = DatabaseManager.create(context, "Communities2Item"); 494 row.setColumn("item_id", item.getID()); 495 row.setColumn("community_id", parents[j].getID()); 496 DatabaseManager.update(context, row); 497 } 498 499 501 String dateField = ConfigurationManager.getProperty("webui.browse.index.date"); 503 if (dateField == null) 504 { 505 dateField = "dc.date.issued"; 506 } 507 508 String titleField = ConfigurationManager.getProperty("webui.browse.index.title"); 509 if (titleField == null) 510 { 511 titleField = "dc.title"; 512 } 513 514 String authorField = ConfigurationManager.getProperty("webui.browse.index.author"); 515 if (authorField == null) 516 { 517 authorField = "dc.contributor.*"; 518 } 519 520 String subjectField = ConfigurationManager.getProperty("webui.browse.index.subject"); 521 if (subjectField == null) 522 { 523 subjectField = "dc.subject.*"; 524 } 525 526 DCValue[] titleArray = getMetadataField(item, titleField); 528 DCValue[] dateArray = getMetadataField(item, dateField); 529 DCValue[] authorArray = getMetadataField(item, authorField); 530 DCValue[] subjectArray = getMetadataField(item, subjectField); 531 532 Map table2dc = new HashMap (); 534 table2dc.put("ItemsByTitle", titleArray); 535 table2dc.put("ItemsByAuthor", authorArray); 536 table2dc.put("ItemsByDate", dateArray); 537 table2dc.put("ItemsByDateAccessioned", item.getDC("date", 538 "accessioned", Item.ANY)); 539 table2dc.put("ItemsBySubject", subjectArray); 540 541 for (Iterator iterator = table2dc.keySet().iterator(); iterator 542 .hasNext();) 543 { 544 String table = (String ) iterator.next(); 545 DCValue[] dc = (DCValue[]) table2dc.get(table); 546 547 for (int i = 0; i < dc.length; i++) 548 { 549 TableRow row = DatabaseManager.create(context, table); 550 row.setColumn("item_id", item.getID()); 551 552 String value = dc[i].value; 553 554 if ("ItemsByDateAccessioned".equals(table)) 555 { 556 row.setColumn("date_accessioned", value); 557 } 558 else if ("ItemsByDate".equals(table)) 559 { 560 row.setColumn("date_issued", value); 561 } 562 else if ("ItemsByAuthor".equals(table)) 563 { 564 row.setColumn("author", value); 567 row.setColumn("sort_author", value.toLowerCase()); 568 } 569 else if ("ItemsByTitle".equals(table)) 570 { 571 String title = NormalizedTitle.normalize(value, 572 dc[i].language); 573 row.setColumn("title", value); 574 row.setColumn("sort_title", title.toLowerCase()); 575 } 576 else if ("ItemsBySubject".equals(table)) 577 { 578 row.setColumn("subject", value); 579 row.setColumn("sort_subject", value.toLowerCase()); 580 } 581 582 583 DatabaseManager.update(context, row); 584 } 585 } 586 } 587 588 597 public static int indexAll(Context context) throws SQLException 598 { 599 indexRemoveAll(context); 600 601 int count = 0; 602 ItemIterator iterator = Item.findAll(context); 603 604 while (iterator.hasNext()) 605 { 606 itemAdded(context, iterator.next()); 607 count++; 608 } 609 610 return count; 611 } 612 613 622 public static int indexRemoveAll(Context context) throws SQLException 623 { 624 int total = 0; 625 626 String [] browseTables = BrowseTables.tables(); 627 628 for (int i = 0; i < browseTables.length; i++) 629 { 630 String sql = "delete from " + browseTables[i]; 631 total += DatabaseManager.updateQuery(context, sql); 632 } 633 634 return total; 635 } 636 637 641 648 public static String getNormalizedTitle(String title, String lang) 649 { 650 return NormalizedTitle.normalize(title, lang); 651 } 652 653 657 private static DCValue[] getMetadataField(Item item, String md) 658 { 659 StringTokenizer dcf = new StringTokenizer (md, "."); 660 661 String [] tokens = { "", "", "" }; 662 int i = 0; 663 while(dcf.hasMoreTokens()) 664 { 665 tokens[i] = dcf.nextToken().toLowerCase().trim(); 666 i++; 667 } 668 String schema = tokens[0]; 669 String element = tokens[1]; 670 String qualifier = tokens[2]; 671 672 DCValue[] values; 673 if ("*".equals(qualifier)) 674 { 675 values = item.getMetadata(schema, element, Item.ANY, Item.ANY); 676 } 677 else if ("".equals(qualifier)) 678 { 679 values = item.getMetadata(schema, element, null, Item.ANY); 680 } 681 else 682 { 683 values = item.getMetadata(schema, element, qualifier, Item.ANY); 684 } 685 686 return values; 687 } 688 689 696 private static BrowseInfo doBrowse(BrowseScope scope) throws SQLException 697 { 698 BrowseInfo cachedInfo = BrowseCache.get(scope); 700 701 if (cachedInfo != null) 702 { 703 return cachedInfo; 704 } 705 706 String itemValue = getItemValue(scope); 709 List results = new ArrayList (); 710 results.addAll(getResultsBeforeFocus(scope, itemValue)); 711 712 int beforeFocus = results.size(); 713 results.addAll(getResultsAfterFocus(scope, itemValue, beforeFocus)); 714 715 int total = countTotalInIndex(scope, results.size()); 718 int matches = countMatches(scope, itemValue, total, results.size()); 719 720 if (log.isDebugEnabled()) 721 { 722 log.debug("Number of matches " + matches); 723 } 724 725 int position = getPosition(total, matches, beforeFocus); 726 727 sortResults(scope, results); 728 729 BrowseInfo info = new BrowseInfo(results, position, total, 730 beforeFocus); 731 732 logInfo(info); 733 734 BrowseCache.add(scope, info); 735 736 return info; 737 } 738 739 755 protected static String getItemValue(BrowseScope scope) throws SQLException 756 { 757 if (!scope.focusIsItem()) 758 { 759 return null; 760 } 761 762 PreparedStatement statement = null; 763 ResultSet results = null; 764 765 try 766 { 767 String tablename = BrowseTables.getTable(scope); 768 String column = BrowseTables.getValueColumn(scope); 769 770 String itemValueQuery = new StringBuffer ().append("select ") 771 .append("max(").append(column).append(") from ").append( 772 tablename).append(" where ").append(" item_id = ") 773 .append(scope.getFocusItemId()).append( 774 getScopeClause(scope, "and")).toString(); 775 776 statement = createStatement(scope, itemValueQuery); 777 results = statement.executeQuery(); 778 779 String itemValue = results.next() ? results.getString(1) : null; 780 781 if (log.isDebugEnabled()) 782 { 783 log.debug("Subquery value is " + itemValue); 784 } 785 786 return itemValue; 787 } 788 finally 789 { 790 if (statement != null) 791 { 792 statement.close(); 793 } 794 795 if (results != null) 796 { 797 results.close(); 798 } 799 } 800 } 801 802 813 protected static List getResultsBeforeFocus(BrowseScope scope, 814 String itemValue) throws SQLException 815 { 816 if (!scope.hasFocus()) 818 { 819 return Collections.EMPTY_LIST; 820 } 821 822 if (scope.getNumberBefore() == 0) 824 { 825 return Collections.EMPTY_LIST; 826 } 827 828 if (scope.getBrowseType() == ITEMS_BY_AUTHOR_BROWSE 832 || scope.getBrowseType() == ITEMS_BY_SUBJECT_BROWSE) 833 { 834 return Collections.EMPTY_LIST; 835 } 836 837 PreparedStatement statement = createSql(scope, itemValue, false, false); 838 839 List qresults = DatabaseManager.queryPrepared(statement).toList(); 840 int numberDesired = scope.getNumberBefore(); 841 List results = getResults(scope, qresults, numberDesired); 842 843 if (!results.isEmpty()) 844 { 845 Collections.reverse(results); 846 } 847 848 return results; 849 } 850 851 863 protected static List getResultsAfterFocus(BrowseScope scope, 864 String itemValue, int count) throws SQLException 865 { 866 if (scope.getTotal() == 0) 868 { 869 return Collections.EMPTY_LIST; 870 } 871 872 PreparedStatement statement = createSql(scope, itemValue, true, false); 873 874 List qresults = DatabaseManager.queryPrepared(statement).toList(); 875 876 int numberDesired = -1; 879 880 if (!scope.hasNoLimit()) 881 { 882 numberDesired = Math.max(scope.getTotal() - count, 0); 883 } 884 885 return getResults(scope, qresults, numberDesired); 886 } 887 888 899 protected static int countTotalInIndex(BrowseScope scope, 900 int numberOfResults) throws SQLException 901 { 902 int browseType = scope.getBrowseType(); 903 904 if ((browseType == ITEMS_BY_AUTHOR_BROWSE) 909 && (scope.hasNoLimit() || (scope.getTotal() > numberOfResults))) 910 { 911 return numberOfResults; 912 } 913 914 PreparedStatement statement = null; 915 Object obj = scope.getScope(); 916 917 try 918 { 919 String table = BrowseTables.getTable(scope); 920 921 StringBuffer buffer = new StringBuffer ().append("select count(") 922 .append(getTargetColumns(scope)).append(") from ").append( 923 table); 924 925 boolean hasWhere = false; 926 927 if (browseType == ITEMS_BY_AUTHOR_BROWSE) 928 { 929 hasWhere = true; 930 buffer.append(" where sort_author = ?"); 931 } 932 933 if (browseType == ITEMS_BY_SUBJECT_BROWSE) 934 { 935 hasWhere = true; 936 buffer.append(" where sort_subject = ?"); 937 } 938 939 String connector = hasWhere ? "and" : "where"; 940 String sql = buffer.append(getScopeClause(scope, connector)) 941 .toString(); 942 943 if (log.isDebugEnabled()) 944 { 945 log.debug("Total sql: \"" + sql + "\""); 946 } 947 948 statement = createStatement(scope, sql); 949 950 if (browseType == ITEMS_BY_AUTHOR_BROWSE) 951 { 952 statement.setString(1, (String ) scope.getFocus()); 953 } 954 955 if (browseType == ITEMS_BY_SUBJECT_BROWSE) 956 { 957 statement.setString(1, (String ) scope.getFocus()); 958 } 959 960 return getIntValue(statement); 961 } 962 finally 963 { 964 if (statement != null) 965 { 966 statement.close(); 967 } 968 } 969 } 970 971 984 protected static int countMatches(BrowseScope scope, String itemValue, 985 int totalInIndex, int numberOfResults) throws SQLException 986 { 987 if (numberOfResults == totalInIndex) 989 { 990 return totalInIndex; 991 } 992 993 if ((!scope.hasFocus()) && scope.isAllDSpaceScope()) 998 { 999 return totalInIndex; 1000 } 1001 1002 PreparedStatement statement = null; 1003 try { 1004 statement = createSql(scope, itemValue, true, true); 1005 return getIntValue(statement); 1006 } 1007 finally { 1008 if(statement != null) { 1009 try { 1010 statement.close(); 1011 } 1012 catch(SQLException e) { 1013 log.error("Problem releasing statement", e); 1014 } 1015 } 1016 } 1017 } 1018 1019 private static int getPosition(int total, int matches, int beforeFocus) 1020 { 1021 if (total == matches) 1023 { 1024 return 0; 1025 } 1026 1027 return total - matches - beforeFocus; 1028 } 1029 1030 1037 private static void sortResults(BrowseScope scope, List results) 1038 { 1039 if ((scope.getBrowseType() != ITEMS_BY_AUTHOR_BROWSE) 1041 && (scope.getBrowseType() != ITEMS_BY_SUBJECT_BROWSE)) 1042 { 1043 return; 1044 } 1045 1046 ItemComparator ic = scope.getSortByTitle().booleanValue() ? new ItemComparator( 1047 "title", null, Item.ANY, true) 1048 : new ItemComparator("date", "issued", Item.ANY, true); 1049 1050 Collections.sort(results, ic); 1051 } 1052 1053 1066 private static List getResults(BrowseScope scope, List results, int max) 1067 throws SQLException 1068 { 1069 if (results == null) 1070 { 1071 return Collections.EMPTY_LIST; 1072 } 1073 1074 List theResults = new ArrayList (); 1075 boolean hasLimit = !scope.hasNoLimit(); 1076 boolean isAuthorsBrowse = scope.getBrowseType() == AUTHORS_BROWSE; 1077 boolean isSubjectsBrowse = scope.getBrowseType() == SUBJECTS_BROWSE; 1078 1079 for (Iterator iterator = results.iterator(); iterator.hasNext();) 1080 { 1081 TableRow row = (TableRow) iterator.next(); 1082 Object theValue = null; 1083 1084 if (isAuthorsBrowse) 1085 theValue = (Object ) row.getStringColumn("author"); 1086 else if (isSubjectsBrowse) 1087 theValue = (Object ) row.getStringColumn("subject"); 1088 else 1089 theValue = (Object ) new Integer (row.getIntColumn("item_id")); 1090 1091 if (theValue == null) 1093 { 1094 continue; 1095 } 1096 1097 if (hasLimit && (theResults.size() >= max)) 1099 { 1100 break; 1101 } 1102 1103 theResults.add(theValue); 1104 1105 if (log.isDebugEnabled()) 1106 { 1107 log.debug("Adding result " + theValue); 1108 } 1109 } 1110 1111 return (isAuthorsBrowse||isSubjectsBrowse) 1112 ? theResults : toItems(scope.getContext(), theResults); 1113 } 1114 1115 1132 private static PreparedStatement createSql(BrowseScope scope, 1133 String subqueryValue, boolean after, boolean isCount) 1134 throws SQLException 1135 { 1136 String sqli = createSqlInternal(scope, subqueryValue, isCount); 1137 String sql = formatSql(scope, sqli, subqueryValue, after); 1138 PreparedStatement statement = createStatement(scope, sql); 1139 1140 if (scope.hasFocus()) 1142 { 1143 String value = subqueryValue; 1144 if (value == null && scope.getFocus() instanceof String ) 1145 { 1146 value = (String )scope.getFocus(); 1147 } 1148 1149 statement.setString(1, value); 1150 1151 if (subqueryValue != null) 1153 { 1154 statement.setString(2, value); 1155 } 1156 } 1157 1158 if (log.isDebugEnabled()) 1159 { 1160 log.debug("Created SQL \"" + sql + "\""); 1161 } 1162 1163 return statement; 1164 } 1165 1166 1175 private static String createSqlInternal(BrowseScope scope, 1176 String itemValue, boolean isCount) 1177 { 1178 String tablename = BrowseTables.getTable(scope); 1179 String column = BrowseTables.getValueColumn(scope); 1180 1181 int browseType = scope.getBrowseType(); 1182 1183 StringBuffer sqlb = new StringBuffer (); 1184 sqlb.append("select "); 1185 sqlb.append(isCount ? "count(" : ""); 1186 sqlb.append(getTargetColumns(scope)); 1187 1188 1192 if ((browseType == AUTHORS_BROWSE) && !isCount) 1193 { 1194 sqlb.append(",sort_author"); 1195 } 1196 1197 if ((browseType == SUBJECTS_BROWSE) && !isCount) 1198 { 1199 sqlb.append(",sort_subject"); 1200 } 1201 1202 sqlb.append(isCount ? ")" : ""); 1203 1204 sqlb.append(" from (SELECT DISTINCT * "); 1205 1206 sqlb.append(" from "); 1207 sqlb.append(tablename); 1208 sqlb.append(" ) distinct_view"); 1209 1210 boolean addedWhereClause = false; 1217 1218 if (scope.hasFocus()) 1219 { 1220 String subquery = null; 1221 1222 if (scope.focusIsItem()) 1223 { 1224 subquery = new StringBuffer ().append(" or ( ").append(column) 1225 .append(" = ? and item_id {0} ").append( 1226 scope.getFocusItemId()).append(")").toString(); 1227 } 1228 1229 if (log.isDebugEnabled()) 1230 { 1231 log.debug("Subquery is \"" + subquery + "\""); 1232 } 1233 1234 sqlb.append(" where ").append("(").append(column).append(" {1} ") 1235 .append("?").append(scope.focusIsItem() ? subquery : "") 1236 .append(")"); 1237 1238 addedWhereClause = true; 1239 } 1240 1241 String connector = addedWhereClause ? " and " : " where "; 1242 sqlb.append(getScopeClause(scope, connector)); 1243 1244 if (isCount) 1246 { 1247 return sqlb.toString(); 1248 } 1249 1250 sqlb 1252 .append(" order by ") 1253 .append(column) 1254 .append("{2}") 1255 .append( 1256 ((scope.focusIsString() && (scope.getBrowseType() != ITEMS_BY_DATE_BROWSE)) 1257 || (scope.getBrowseType() == AUTHORS_BROWSE) || (scope 1258 .getBrowseType() == SUBJECTS_BROWSE)) ? "" 1259 : ", item_id{2}"); 1260 1261 String myquery = sqlb.toString(); 1262 1263 if (!scope.hasNoLimit()) 1265 { 1266 if ("oracle".equals(ConfigurationManager.getProperty("db.name"))) 1267 { 1268 myquery = "SELECT * FROM (" + myquery 1269 + ") WHERE ROWNUM <= {3} "; 1270 } 1271 else 1272 { 1273 myquery = myquery + " LIMIT {3} "; 1275 } 1276 } 1277 1278 return myquery; 1279 } 1280 1281 1293 private static String formatSql(BrowseScope scope, String sql, 1294 String subqueryValue, boolean after) 1295 { 1296 boolean before = !after; 1297 int browseType = scope.getBrowseType(); 1298 boolean ascending = scope.getAscending(); 1299 int numberDesired = before ? scope.getNumberBefore() : scope.getTotal(); 1300 1301 String beforeOperator = "<"; 1304 String afterOperator = ">="; 1305 1306 if (browseType == ITEMS_BY_AUTHOR_BROWSE) 1308 { 1309 afterOperator = "="; 1310 } 1311 1312 if (browseType == ITEMS_BY_SUBJECT_BROWSE) 1313 { 1314 afterOperator = "="; 1315 } 1316 1317 if (subqueryValue != null) 1320 { 1321 beforeOperator = "<"; 1322 afterOperator = ">"; 1323 } 1324 1325 if (!ascending) 1326 { 1327 beforeOperator = ">"; 1328 afterOperator = "<="; 1329 } 1330 1331 if (browseType == ITEMS_BY_DATE_BROWSE) 1332 { 1333 if (!ascending) 1334 { 1335 beforeOperator = ">"; 1336 afterOperator = "<"; 1337 } 1338 else 1339 { 1340 beforeOperator = "<"; 1341 afterOperator = ">"; 1342 } 1343 } 1344 1345 String beforeSubqueryOperator = "<"; 1346 String afterSubqueryOperator = ">="; 1347 1348 if (browseType == ITEMS_BY_AUTHOR_BROWSE 1350 || browseType == ITEMS_BY_SUBJECT_BROWSE) 1351 { 1352 afterSubqueryOperator = "="; 1353 } 1354 1355 if (!ascending) 1356 { 1357 beforeSubqueryOperator = ">"; 1358 afterSubqueryOperator = "<="; 1359 } 1360 1361 String order = before ? " desc" : ""; 1362 1363 if (!ascending) 1364 { 1365 order = before ? "" : " desc"; 1366 } 1367 1368 List args = new ArrayList (); 1372 1373 args.add(before ? beforeSubqueryOperator : afterSubqueryOperator); 1374 args.add(before ? beforeOperator : afterOperator); 1375 args.add(order); 1376 args.add(new Integer (numberDesired)); 1377 1378 return MessageFormat.format(sql, args.toArray()); 1379 } 1380 1381 1386 private static void logInfo(BrowseInfo info) 1387 { 1388 if (!log.isDebugEnabled()) 1389 { 1390 return; 1391 } 1392 1393 log.debug("Number of Results: " + info.getResultCount() 1394 + " Overall position: " + info.getOverallPosition() + " Total " 1395 + info.getTotal() + " Offset " + info.getOffset()); 1396 1397 int lastIndex = (info.getOverallPosition() + info.getResultCount()); 1398 boolean noresults = (info.getTotal() == 0) 1399 || (info.getResultCount() == 0); 1400 1401 if (noresults) 1402 { 1403 log.debug("Got no results"); 1404 } 1405 1406 log.debug("Got results: " + info.getOverallPosition() + " to " 1407 + lastIndex + " out of " + info.getTotal()); 1408 } 1409 1410 1417 private static String getTargetColumns(BrowseScope scope) 1418 { 1419 int browseType = scope.getBrowseType(); 1420 1421 if (browseType == AUTHORS_BROWSE) 1422 return "distinct author"; 1423 else if (browseType == SUBJECTS_BROWSE) 1424 return "distinct subject"; 1425 else 1426 return "*"; 1427 } 1428 1429 1453 static String getScopeClause(BrowseScope scope, String connector) 1454 { 1455 if (scope.isAllDSpaceScope()) 1456 { 1457 return ""; 1458 } 1459 1460 boolean isCommunity = scope.isCommunityScope(); 1461 Object obj = scope.getScope(); 1462 1463 int id = (isCommunity) ? ((Community) obj).getID() : ((Collection) obj) 1464 .getID(); 1465 1466 String column = (isCommunity) ? "community_id" : "collection_id"; 1467 1468 return new StringBuffer ().append(" ").append(connector).append(" ") 1469 .append(column).append(" = ").append(id).toString(); 1470 } 1471 1472 1483 private static PreparedStatement createStatement(BrowseScope scope, 1484 String sql) throws SQLException 1485 { 1486 Connection connection = scope.getContext().getDBConnection(); 1487 1488 return connection.prepareStatement(sql); 1489 } 1490 1491 1501 private static int getIntValue(PreparedStatement statement) 1502 throws SQLException 1503 { 1504 ResultSet results = null; 1505 1506 try 1507 { 1508 results = statement.executeQuery(); 1509 1510 return results.next() ? results.getInt(1) : (-1); 1511 } 1512 finally 1513 { 1514 if (results != null) 1515 { 1516 results.close(); 1517 } 1518 } 1519 } 1520 1521 1532 private static List toItems(Context context, List ids) throws SQLException 1533 { 1534 List results = new ArrayList (); 1536 1537 for (Iterator iterator = ids.iterator(); iterator.hasNext();) 1538 { 1539 Integer id = (Integer ) iterator.next(); 1540 Item item = Item.find(context, id.intValue()); 1541 1542 if (item != null) 1543 { 1544 results.add(item); 1545 } 1546 } 1547 1548 return results; 1549 } 1550} 1551 1552class NormalizedTitle 1553{ 1554 private static String [] STOP_WORDS = new String [] { "A", "An", "The" }; 1555 1556 1563 public static String normalize(String title, String lang) 1564 { 1565 if (lang == null) 1566 { 1567 return title; 1568 } 1569 1570 return (lang.startsWith("en")) ? normalizeEnglish(title) : title; 1571 } 1572 1573 1585 public static String normalizeEnglish(String oldtitle) 1586 { 1587 if (oldtitle == null) 1589 { 1590 return null; 1591 } 1592 1593 if (oldtitle.length() == 0) 1594 { 1595 return oldtitle; 1596 } 1597 1598 String title = oldtitle.toLowerCase(); 1600 1601 int startAt = firstWhitespace(title); 1604 boolean modified = (startAt != 0); 1605 boolean usedStopWord = false; 1606 String stop = null; 1607 1608 for (int i = 0; i < STOP_WORDS.length; i++) 1610 { 1611 stop = STOP_WORDS[i]; 1612 1613 int stoplen = stop.length(); 1614 1615 boolean found = title.toLowerCase().startsWith(stop.toLowerCase(), 1618 startAt) 1619 && ( title.length() >= (startAt + stoplen + 1)) && 1622 Character.isWhitespace(title.charAt(startAt + stoplen)); 1624 1625 if (found) 1626 { 1627 modified = true; 1628 usedStopWord = true; 1629 1630 startAt += stoplen; 1631 1632 int firstw = firstWhitespace(title, startAt); 1634 1635 if (firstw != 0) 1636 { 1637 startAt = firstw; 1638 } 1639 1640 break; 1642 } 1643 } 1644 1645 if (!modified) 1647 { 1648 return title; 1649 } 1650 1651 if (!usedStopWord) 1653 { 1654 return title.substring(startAt); 1655 } 1656 1657 return new StringBuffer (title.substring(startAt)).append(", ").append( 1659 stop).toString(); 1660 } 1661 1662 1668 private static int firstWhitespace(String title) 1669 { 1670 return firstWhitespace(title, 0); 1671 } 1672 1673 1680 private static int firstWhitespace(char[] title) 1681 { 1682 return firstWhitespace(title, 0); 1683 } 1684 1685 1693 private static int firstWhitespace(String title, int startAt) 1694 { 1695 return firstWhitespace(title.toCharArray(), startAt); 1696 } 1697 1698 1706 private static int firstWhitespace(char[] title, int startAt) 1707 { 1708 int first = 0; 1709 1710 for (int j = startAt; j < title.length; j++) 1711 { 1712 if (!Character.isLetterOrDigit(title[j])) 1715 { 1716 first = j + 1; 1717 1718 continue; 1719 } 1720 1721 break; 1722 } 1723 1724 return first; 1725 } 1726} 1727 1728class BrowseCache 1729{ 1730 private static Map tableMax = new HashMap (); 1731 1732 private static Map tableSize = new HashMap (); 1733 1734 1735 private static Logger log = Logger.getLogger(BrowseCache.class); 1736 1737 private static Map cache = new WeakHashMap (); 1738 1739 private static SortedMap dateCache = new TreeMap (); 1744 1745 private static final int CACHE_MAXIMUM = 30; 1746 1747 1753 public static BrowseInfo get(BrowseScope key) 1754 { 1755 if (log.isDebugEnabled()) 1756 { 1757 log 1758 .debug("Checking browse cache with " + cache.size() 1759 + " objects"); 1760 } 1761 1762 BrowseInfo cachedInfo = (BrowseInfo) cache.get(key); 1763 1764 try 1765 { 1766 if (getMaximum(key) == -1) 1768 { 1769 updateIndexData(key); 1770 } 1771 1772 if (cachedInfo == null) 1773 { 1774 if (log.isDebugEnabled()) 1775 { 1776 log.debug("Not in browse cache"); 1777 } 1778 1779 return null; 1780 } 1781 1782 if (indexHasChanged(key)) 1790 { 1791 if (log.isDebugEnabled()) 1792 { 1793 log.debug("Index has changed"); 1794 } 1795 1796 cache.remove(key); 1797 1798 return null; 1799 } 1800 } 1801 catch (SQLException sqle) 1802 { 1803 if (log.isDebugEnabled()) 1804 { 1805 log.debug("Caught SQLException: " + sqle, sqle); 1806 } 1807 1808 return null; 1809 } 1810 1811 if (log.isDebugEnabled()) 1813 { 1814 log.debug("Using cached browse"); 1815 } 1816 1817 cachedInfo.setCached(true); 1818 1819 return cachedInfo; 1820 } 1821 1822 1829 public static boolean indexHasChanged(BrowseScope key) throws SQLException 1830 { 1831 Context context = null; 1832 1833 try 1834 { 1835 context = new Context(); 1836 1837 TableRow results = countAndMax(context, key); 1838 long count = -1; 1839 int max = -1; 1840 1841 if (results != null) 1842 { 1843 if ("oracle".equals(ConfigurationManager.getProperty("db.name"))) 1845 { 1846 count = results.getIntColumn("count"); 1847 } 1848 else { 1850 count = results.getLongColumn("count"); 1851 } 1852 max = results.getIntColumn("max"); 1853 } 1854 1855 context.complete(); 1856 1857 if ((count == getCount(key)) && (max == getMaximum(key))) 1859 { 1860 return false; 1861 } 1862 1863 setMaximum(key, max); 1865 setCount(key, count); 1866 1867 return true; 1869 } 1870 catch (SQLException sqle) 1871 { 1872 if (context != null) 1873 { 1874 context.abort(); 1875 } 1876 1877 throw sqle; 1878 } 1879 } 1880 1881 1887 public static void updateIndexData(BrowseScope key) 1888 { 1889 Context context = null; 1890 1891 try 1892 { 1893 context = new Context(); 1894 1895 TableRow results = countAndMax(context, key); 1896 long count = -1; 1897 int max = -1; 1898 1899 if (results != null) 1900 { 1901 if ("oracle".equals(ConfigurationManager.getProperty("db.name"))) 1903 { 1904 count = results.getIntColumn("count"); 1905 } 1906 else { 1908 count = results.getLongColumn("count"); 1909 } 1910 max = results.getIntColumn("max"); 1911 } 1912 1913 context.complete(); 1914 1915 setMaximum(key, max); 1916 setCount(key, count); 1917 } 1918 catch (Exception e) 1919 { 1920 if (context != null) 1921 { 1922 context.abort(); 1923 } 1924 1925 e.printStackTrace(); 1926 } 1927 } 1928 1929 1937 public static TableRow countAndMax(Context context, BrowseScope scope) 1938 throws SQLException 1939 { 1940 String sql = new StringBuffer ().append( 1946 "select count({0}) as count, max({0}) as max from ").append( 1947 BrowseTables.getTable(scope)).append( 1948 Browse.getScopeClause(scope, "where")).toString(); 1949 1950 String countColumn = BrowseTables.getIndexColumn(scope); 1952 Object [] args = new Object [] { countColumn, countColumn }; 1953 String SQL = MessageFormat.format(sql, args); 1954 1955 if (log.isDebugEnabled()) 1957 { 1958 log.debug("Running SQL to check whether index has changed: \"" 1959 + SQL + "\""); 1960 } 1961 1962 return DatabaseManager.querySingle(context, SQL); 1963 } 1964 1965 1971 public static void add(BrowseScope key, BrowseInfo info) 1972 { 1973 if (info.getResultCount() == 0) 1976 { 1977 return; 1978 } 1979 1980 cache.put(key.clone(), info); 1984 1985 cleanDateCache(); 1987 1988 dateCache.put(new java.util.Date (), key); 1990 } 1991 1992 1995 private static void cleanDateCache() 1996 { 1997 synchronized (dateCache) 1998 { 1999 if (dateCache.size() < CACHE_MAXIMUM) 2001 { 2002 return; 2003 } 2004 2005 dateCache.remove(dateCache.firstKey()); 2007 } 2008 } 2009 2010 2016 private static int getMaximum(BrowseScope scope) 2017 { 2018 String table = BrowseTables.getTable(scope); 2019 Integer value = (Integer ) tableMax.get(table); 2020 2021 return (value == null) ? (-1) : value.intValue(); 2022 } 2023 2024 private static long getCount(BrowseScope scope) 2025 { 2026 String table = BrowseTables.getTable(scope); 2027 Long value = (Long ) tableSize.get(table); 2028 2029 return (value == null) ? (-1) : value.longValue(); 2030 } 2031 2032 private static void setMaximum(BrowseScope scope, int max) 2033 { 2034 String table = BrowseTables.getTable(scope); 2035 tableMax.put(table, new Integer (max)); 2036 } 2037 2038 private static void setCount(BrowseScope scope, long count) 2039 { 2040 String table = BrowseTables.getTable(scope); 2041 tableSize.put(table, new Long (count)); 2042 } 2043} 2044 2045 2052class BrowseTables 2053{ 2054 private static final String [] BROWSE_TABLES = new String [] { 2055 "Communities2Item", "ItemsByAuthor", "ItemsByDate", 2056 "ItemsByDateAccessioned", "ItemsByTitle", "ItemsBySubject" }; 2057 2058 2064 public static String [] tables() 2065 { 2066 return BROWSE_TABLES; 2067 } 2068 2069 2075 public static String getTable(BrowseScope scope) 2076 { 2077 int browseType = scope.getBrowseType(); 2078 boolean isCommunity = scope.isCommunityScope(); 2079 boolean isCollection = scope.isCollectionScope(); 2080 2081 if ((browseType == Browse.AUTHORS_BROWSE) 2082 || (browseType == Browse.ITEMS_BY_AUTHOR_BROWSE)) 2083 { 2084 if (isCommunity) 2085 { 2086 return "CommunityItemsByAuthor"; 2087 } 2088 2089 if (isCollection) 2090 { 2091 return "CollectionItemsByAuthor"; 2092 } 2093 2094 return "ItemsByAuthor"; 2095 } 2096 2097 if (browseType == Browse.ITEMS_BY_TITLE_BROWSE) 2098 { 2099 if (isCommunity) 2100 { 2101 return "CommunityItemsByTitle"; 2102 } 2103 2104 if (isCollection) 2105 { 2106 return "CollectionItemsByTitle"; 2107 } 2108 2109 return "ItemsByTitle"; 2110 } 2111 2112 if (browseType == Browse.ITEMS_BY_DATE_BROWSE) 2113 { 2114 if (isCommunity) 2115 { 2116 return "CommunityItemsByDate"; 2117 } 2118 2119 if (isCollection) 2120 { 2121 return "CollectionItemsByDate"; 2122 } 2123 2124 return "ItemsByDate"; 2125 } 2126 2127 if ((browseType == Browse.SUBJECTS_BROWSE) 2128 || (browseType == Browse.ITEMS_BY_SUBJECT_BROWSE)) 2129 { 2130 if (isCommunity) 2131 { 2132 return "CommunityItemsBySubject"; 2133 } 2134 2135 if (isCollection) 2136 { 2137 return "CollectionItemsBySubject"; 2138 } 2139 2140 return "ItemsBySubject"; 2141 } 2142 2143 throw new IllegalArgumentException ( 2144 "No table for browse and scope combination"); 2145 } 2146 2147 2153 public static String getIndexColumn(BrowseScope scope) 2154 { 2155 int browseType = scope.getBrowseType(); 2156 2157 if (browseType == Browse.AUTHORS_BROWSE) 2158 { 2159 return "items_by_author_id"; 2160 } 2161 2162 if (browseType == Browse.ITEMS_BY_AUTHOR_BROWSE) 2163 { 2164 return "items_by_author_id"; 2165 } 2166 2167 if (browseType == Browse.ITEMS_BY_DATE_BROWSE) 2168 { 2169 return "items_by_date_id"; 2170 } 2171 2172 if (browseType == Browse.ITEMS_BY_TITLE_BROWSE) 2173 { 2174 return "items_by_title_id"; 2175 } 2176 2177 if (browseType == Browse.SUBJECTS_BROWSE) 2178 { 2179 return "items_by_subject_id"; 2180 } 2181 2182 if (browseType == Browse.ITEMS_BY_SUBJECT_BROWSE) 2183 { 2184 return "items_by_subject_id"; 2185 } 2186 2187 throw new IllegalArgumentException ("Unknown browse type: " + browseType); 2188 } 2189 2190 2197 public static String getValueColumn(BrowseScope scope) 2198 { 2199 int browseType = scope.getBrowseType(); 2200 2201 if (browseType == Browse.AUTHORS_BROWSE) 2202 { 2203 return "sort_author"; 2204 } 2205 2206 if (browseType == Browse.ITEMS_BY_AUTHOR_BROWSE) 2207 { 2208 return "sort_author"; 2209 } 2210 2211 if (browseType == Browse.ITEMS_BY_DATE_BROWSE) 2212 { 2213 return "date_issued"; 2214 } 2215 2216 if (browseType == Browse.ITEMS_BY_TITLE_BROWSE) 2218 { 2219 return "sort_title"; 2220 } 2221 2222 if (browseType == Browse.SUBJECTS_BROWSE) 2223 { 2224 return "sort_subject"; 2225 } 2226 2227 if (browseType == Browse.ITEMS_BY_SUBJECT_BROWSE) 2228 { 2229 return "sort_subject"; 2230 } 2231 2232 throw new IllegalArgumentException ("Unknown browse type: " + browseType); 2233 } 2234} 2235 | Popular Tags |