/** * Convert the value into an offset into the table for this browse * * @return the focus value to use * @throws BrowseException */ private int getOffsetForValue(String value) throws BrowseException { // get the table name. We don't really need to care about whether we are in a // community or collection at this point. This is only for full or second // level browse, so there is no need to worry about distinct value browsing String tableName = browseIndex.getTableName(); // we need to make sure that we select from the correct column. If the sort option // is the 0th option then we use sort_value, but if it is one of the others we have // to select from that column instead. Otherwise, we end up missing the focus value // to do comparisons in other columns. The use of the focus value needs to be consistent // across the browse SortOption so = scope.getSortOption(); if (so == null || so.getNumber() == 0) { if (browseIndex.getSortOption() != null) so = browseIndex.getSortOption(); } String col = "sort_1"; if (so.getNumber() > 0) { col = "sort_" + Integer.toString(so.getNumber()); } // now get the DAO to do the query for us, returning the highest // string value in the given column in the given table for the // item (I think) return dao.doOffsetQuery(col, value, scope.isAscending()); }
/** * Return the focus value. * * @return the focus value to use * @throws BrowseException */ private String getJumpToValue() throws BrowseException { log.debug(LogManager.getHeader(context, "get_focus_value", "")); // if the focus is by value, just return it if (scope.hasJumpToValue()) { log.debug( LogManager.getHeader( context, "get_focus_value_return", "return=" + scope.getJumpToValue())); return scope.getJumpToValue(); } // if the focus is to start with, then we need to return the value of the starts with if (scope.hasStartsWith()) { log.debug( LogManager.getHeader( context, "get_focus_value_return", "return=" + scope.getStartsWith())); return scope.getStartsWith(); } // since the focus is not by value, we need to obtain it // get the id of the item to focus on int id = scope.getJumpToItem(); // get the table name. We don't really need to care about whether we are in a // community or collection at this point. This is only for full or second // level browse, so there is no need to worry about distinct value browsing String tableName = browseIndex.getTableName(); // we need to make sure that we select from the correct column. If the sort option // is the 0th option then we use sort_value, but if it is one of the others we have // to select from that column instead. Otherwise, we end up missing the focus value // to do comparisons in other columns. The use of the focus value needs to be consistent // across the browse SortOption so = scope.getSortOption(); if (so == null || so.getNumber() == 0) { if (browseIndex.getSortOption() != null) { so = browseIndex.getSortOption(); } } String col = "sort_1"; if (so.getNumber() > 0) { col = "sort_" + Integer.toString(so.getNumber()); } // now get the DAO to do the query for us, returning the highest // string value in the given column in the given table for the // item (I think) String max = dao.doMaxQuery(col, tableName, id); log.debug(LogManager.getHeader(context, "get_focus_value_return", "return=" + max)); return max; }
/** * Convert the value into an offset into the table for this browse * * @return the focus value to use * @throws BrowseException */ private int getOffsetForDistinctValue(String value) throws BrowseException { if (!browseIndex.isMetadataIndex()) throw new IllegalArgumentException( "getOffsetForDistinctValue called when not a metadata index"); // get the table name. We don't really need to care about whether we are in a // community or collection at this point. This is only for full or second // level browse, so there is no need to worry about distinct value browsing String tableName = browseIndex.getTableName(); // now get the DAO to do the query for us, returning the highest // string value in the given column in the given table for the // item (I think) return dao.doDistinctOffsetQuery("sort_value", value, scope.isAscending()); }
/** * Convert the value into an offset into the table for this browse * * @return the focus value to use * @throws BrowseException */ private int getOffsetForDistinctValue(String value) throws BrowseException { if (!browseIndex.isMetadataIndex()) { throw new IllegalArgumentException( "getOffsetForDistinctValue called when not a metadata index"); } // now get the DAO to do the query for us, returning the highest // string value in the given column in the given table for the // item (I think) return dao.doDistinctOffsetQuery("sort_value", value, scope.isAscending()); }
/** * Convert the value into an offset into the table for this browse * * @return the focus value to use * @throws BrowseException */ private int getOffsetForValue(String value) throws BrowseException { // we need to make sure that we select from the correct column. If the sort option // is the 0th option then we use sort_value, but if it is one of the others we have // to select from that column instead. Otherwise, we end up missing the focus value // to do comparisons in other columns. The use of the focus value needs to be consistent // across the browse SortOption so = scope.getSortOption(); if (so == null || so.getNumber() == 0) { if (browseIndex.getSortOption() != null) { so = browseIndex.getSortOption(); } } String col = "sort_1"; if (so.getNumber() > 0) { col = "sort_" + Integer.toString(so.getNumber()); } // now get the DAO to do the query for us, returning the highest // string value in the given column in the given table for the // item (I think) return dao.doOffsetQuery(col, value, scope.isAscending()); }
/** * Get the position of the current start point of the browse in the field of total objects * relevant to this browse. This is integrally related to how results are presented in pages to * the User Interface. The argument tells us whether this is a distinct browse or not, as this has * an impact on how results are calculated * * @param distinct is this a distinct browse * @return the offset of the first result from the start of the set * @throws SQLException * @throws BrowseException */ private int getPosition(boolean distinct) throws SQLException, BrowseException { log.debug(LogManager.getHeader(context, "get_position", "distinct=" + distinct)); // if there isn't a focus then we are at the start if (dao.getJumpToValue() == null) { log.debug(LogManager.getHeader(context, "get_position_return", "return=0")); return 0; } // get the table name that we are going to be getting our data from String tableName = browseIndex.getTableName(distinct, scope.inCommunity(), scope.inCollection()); // ensure that the select is set to "*" String[] select = {"*"}; dao.setCountValues(select); // FIXME: it would be nice to have a good way of doing this in the DAO // now reset all of the fields that we don't want to have constraining // our count, storing them locally to reinstate later boolean isAscending = dao.isAscending(); boolean useEquals = dao.useEqualsComparator(); String orderField = dao.getOrderField(); int limit = dao.getLimit(); int offset = dao.getOffset(); // reverse the direction of the query, and remove the equal comparator // (if it exists), as well as nullifying a few other unnecessary parameters dao.setAscending(!isAscending); dao.setEqualsComparator(false); dao.setOrderField(null); dao.setLimit(-1); dao.setOffset(-1); // perform the query and get the result int count = dao.doCountQuery(); // now put back the values we removed for this method dao.setAscending(isAscending); dao.setEqualsComparator(useEquals); dao.setOrderField(orderField); dao.setLimit(limit); dao.setOffset(offset); log.debug(LogManager.getHeader(context, "get_position_return", "return=" + count)); return count; }
/* (non-Javadoc) * @see org.dspace.browse.BrowseDAO#setTable(java.lang.String) */ public void setTable(String table) { this.table = table; // FIXME Rather than assume from the browse table, join the query to item to get the correct // values // Check to see if this is the withdrawn browse index - if it is, // we need to set the flags appropriately for when we create the BrowseItems if (table.equals(BrowseIndex.getWithdrawnBrowseIndex().getTableName())) { itemsInArchive = false; itemsWithdrawn = true; } else { itemsInArchive = true; itemsWithdrawn = false; } this.rebuildQuery = true; }
/** * Perform a standard browse, which will return a BrowseInfo object that represents the results * for the current page, the total number of results, the range, and information to construct * previous and next links on any web page * * @param bs the scope of the browse * @return the results of the browse * @throws BrowseException */ public BrowseInfo browse(BrowserScope bs) throws BrowseException { log.debug(LogManager.getHeader(context, "browse", "")); // first, load the browse scope into the object this.scope = bs; // since we use it so much, get the browse index out of the // scope and store as a member browseIndex = scope.getBrowseIndex(); // now make the decision as to how to browse if (browseIndex.isMetadataIndex() && !scope.isSecondLevel()) { // this is a single value browse type that has not gone to // the second level (i.e. authors, not items by a given author) return browseByValue(scope); } else { // this is the full browse type or a browse that has gone to // the second level return browseByItem(scope); } }
/** * Obtain the sort option * * @return the sort option * @throws BrowseException */ public SortOption getSortOption() throws BrowseException { // If a sortOption hasn't been set, work out the default if (sortOption == null) { // We need a browse index first though if (browseIndex != null) { // If a sorting hasn't been specified, and it's a metadata browse if (sortBy <= 0 && browseIndex.isMetadataIndex()) { // Create a dummy sortOption for the metadata sort String dataType = browseIndex.getDataType(); String type = ("date".equals(dataType) ? "date" : "text"); sortOption = new SortOption( 0, browseIndex.getName(), browseIndex.getMetadata(), type, browseIndex.getDefaultOrder()); } else { // If a sorting hasn't been specified if (sortBy <= 0) { // Get the sort option from the index sortOption = browseIndex.getSortOption(); if (sortOption == null) { // No sort option, so default to the first one defined in the config for (SortOption so : SortOption.getSortOptions()) { sortOption = so; break; } } } else { // A sorting has been specified, so get it from the configured sort columns for (SortOption so : SortOption.getSortOptions()) { if (so.getNumber() == sortBy) sortOption = so; } } } } } return sortOption; }
/** * Utility method for obtaining a string representation of the browse. This is useful only for * debug */ public String toString() { try { StringBuffer sb = new StringBuffer(); // calculate the range for display String from = Integer.toString(overallPosition + 1); String to = Integer.toString(overallPosition + results.size()); String of = Integer.toString(total); // report on the positional information of the browse sb.append("BrowseInfo String Representation: "); sb.append("Browsing " + from + " to " + to + " of " + of + " "); // insert the information about which index sb.append( "in index: " + browseIndex.getName() + " (data type: " + browseIndex.getDataType() + ", display type: " + browseIndex.getDisplayType() + ") "); sb.append("||"); // report on the browse scope container String container = "all of DSpace"; DSpaceObject theContainer = null; if (inCollection()) { container = "collection"; theContainer = this.collection; } else if (inCommunity()) { container = "community"; theContainer = this.community; } String containerID = "no id available/necessary"; if (theContainer != null) { containerID = Integer.toString(theContainer.getID()) + " (" + theContainer.getHandle() + ")"; } sb.append("Browsing in " + container + ": " + containerID); sb.append("||"); // load the item list display configuration ItemListConfig config = new ItemListConfig(); // some information about the columns to be displayed if (browseIndex.isItemIndex()) { sb.append("Listing over " + Integer.toString(config.numCols()) + " columns: "); for (int k = 1; k <= config.numCols(); k++) { if (k > 1) { sb.append(","); } String[] meta = config.getMetadata(k); sb.append(meta[0] + "." + meta[1] + "." + meta[2]); } if (value != null) { sb.append(" on value: ").append(value); } if (isStartsWith()) { sb.append(" sort column starting with: ").append(focus); } else if (hasFocus()) { sb.append(" sort column focus: ").append(focus); } } else if (browseIndex.isMetadataIndex()) { sb.append("Listing single column: ").append(browseIndex.getMetadata()); if (isStartsWith()) { sb.append(" sort column starting with: ").append(focus); } else if (hasFocus()) { sb.append(" sort column focus: ").append(focus); } } sb.append("||"); // some information about how the data is sorted String direction = (ascending ? "ASC" : "DESC"); sb.append( "Sorting by: " + sortOption.getMetadata() + " " + direction + " (option " + Integer.toString(sortOption.getNumber()) + ")"); sb.append("||"); // output the results if (browseIndex.isMetadataIndex() && !isSecondLevel()) { sb.append(valueListingString()); } else if (browseIndex.isItemIndex() || isSecondLevel()) { sb.append(fullListingString(config)); } sb.append("||"); // tell us what the next and previous values are going to be sb.append("Top of next page: "); if (hasNextPage()) { sb.append("offset: ").append(Integer.toString(this.nextOffset)); } else { sb.append("n/a"); } sb.append(";"); sb.append("Top of previous page: "); if (hasPrevPage()) { sb.append("offset: ").append(Integer.toString(this.prevOffset)); } else { sb.append("n/a"); } sb.append("||"); return sb.toString(); } catch (SQLException e) { return e.getMessage(); } catch (BrowseException e) { return e.getMessage(); } }
/** * Browse the archive by single values (such as the name of an author). This produces a BrowseInfo * object that contains Strings as the results of the browse * * @param bs the scope of the browse * @return the results of the browse * @throws BrowseException */ private BrowseInfo browseByValue(BrowserScope bs) throws BrowseException { log.info(LogManager.getHeader(context, "browse_by_value", "focus=" + bs.getJumpToValue())); try { // get the table name that we are going to be getting our data from // this is the distinct table constrained to either community or collection dao.setTable(browseIndex.getDistinctTableName()); // remind the DAO that this is a distinct value browse, so it knows what sort // of query to build dao.setDistinct(true); // tell the browse query whether we are ascending or descending on the value dao.setAscending(scope.isAscending()); // set our constraints on community or collection if (scope.inCollection() || scope.inCommunity()) { // Scoped browsing of distinct metadata requires the mapping // table to be specified. dao.setFilterMappingTables(null, browseIndex.getMapTableName()); if (scope.inCollection()) { Collection col = (Collection) scope.getBrowseContainer(); dao.setContainerTable("collection2item"); dao.setContainerIDField("collection_id"); dao.setContainerID(col.getID()); } else if (scope.inCommunity()) { Community com = (Community) scope.getBrowseContainer(); dao.setContainerTable("communities2item"); dao.setContainerIDField("community_id"); dao.setContainerID(com.getID()); } } // this is the total number of results in answer to the query int total = getTotalResults(true); // set the ordering field (there is only one option) dao.setOrderField("sort_value"); // assemble the focus clause if we are to have one // it will look like one of the following // - sort_value < myvalue // = sort_1 > myvalue dao.setJumpToField("sort_value"); int offset = scope.getOffset(); String rawFocusValue = null; if (offset < 1 && scope.hasJumpToValue() || scope.hasStartsWith()) { String focusValue = getJumpToValue(); // store the value to tell the Browse Info object which value we are browsing on rawFocusValue = focusValue; // make sure the incoming value is normalised focusValue = normalizeJumpToValue(focusValue); offset = getOffsetForDistinctValue(focusValue); } // assemble the offset and limit dao.setOffset(offset); dao.setLimit(scope.getResultsPerPage()); // Holder for the results List<String[]> results = null; // Does this browse have any contents? if (total > 0) { // now run the query results = dao.doValueQuery(); // now, if we don't have any results, we are at the end of the browse. This will // be because a starts_with value has been supplied for which we don't have // any items. if (results.size() == 0) { // In this case, we will calculate a new offset for the last page of results offset = total - scope.getResultsPerPage(); if (offset < 0) { offset = 0; } // And rerun the query dao.setOffset(offset); results = dao.doValueQuery(); } } else { // No records, so make an empty list results = new ArrayList<String[]>(); } // construct the BrowseInfo object to pass back BrowseInfo browseInfo = new BrowseInfo(results, offset, total, offset); if (offset + scope.getResultsPerPage() < total) { browseInfo.setNextOffset(offset + scope.getResultsPerPage()); } if (offset - scope.getResultsPerPage() > -1) { browseInfo.setPrevOffset(offset - scope.getResultsPerPage()); } // add the browse index to the Browse Info browseInfo.setBrowseIndex(browseIndex); // set the sort option for the Browse Info browseInfo.setSortOption(scope.getSortOption()); // tell the Browse Info which way we are sorting browseInfo.setAscending(scope.isAscending()); // tell the Browse Info which level of browse we are at browseInfo.setBrowseLevel(scope.getBrowseLevel()); // set the browse value if there is one browseInfo.setFocus(rawFocusValue); // tell the browse info if it is working from a starts with parameter browseInfo.setStartsWith(scope.hasStartsWith()); // tell the browse info what the container for the browse was if (scope.inCollection() || scope.inCommunity()) { browseInfo.setBrowseContainer(scope.getBrowseContainer()); } browseInfo.setResultsPerPage(scope.getResultsPerPage()); return browseInfo; } catch (SQLException e) { log.error("caught exception: ", e); throw new BrowseException(e); } }
/** * Browse the archive by the full item browse mechanism. This produces a BrowseInfo object which * contains full BrowseItem objects as its result set. * * @param bs the scope of the browse * @return the results of the browse * @throws BrowseException */ private BrowseInfo browseByItem(BrowserScope bs) throws BrowseException { log.info(LogManager.getHeader(context, "browse_by_item", "")); try { // get the table name that we are going to be getting our data from dao.setTable(browseIndex.getTableName()); // tell the browse query whether we are ascending or descending on the value dao.setAscending(scope.isAscending()); // assemble the value clause String rawValue = null; if (scope.hasFilterValue() && scope.isSecondLevel()) { String value = scope.getFilterValue(); rawValue = value; // make sure the incoming value is normalised value = OrderFormat.makeSortString( value, scope.getFilterValueLang(), scope.getBrowseIndex().getDataType()); dao.setAuthorityValue(scope.getAuthorityValue()); // set the values in the Browse Query if (scope.isSecondLevel()) { dao.setFilterValueField("value"); dao.setFilterValue(rawValue); } else { dao.setFilterValueField("sort_value"); dao.setFilterValue(value); } dao.setFilterValuePartial(scope.getFilterValuePartial()); // to apply the filtering, we need the distinct and map tables for the index dao.setFilterMappingTables( browseIndex.getDistinctTableName(), browseIndex.getMapTableName()); } // define a clause for the WHERE clause which will allow us to constrain // our browse to a specified community or collection if (scope.inCollection() || scope.inCommunity()) { if (scope.inCollection()) { Collection col = (Collection) scope.getBrowseContainer(); dao.setContainerTable("collection2item"); dao.setContainerIDField("collection_id"); dao.setContainerID(col.getID()); } else if (scope.inCommunity()) { Community com = (Community) scope.getBrowseContainer(); dao.setContainerTable("communities2item"); dao.setContainerIDField("community_id"); dao.setContainerID(com.getID()); } } // this is the total number of results in answer to the query int total = getTotalResults(); // assemble the ORDER BY clause String orderBy = browseIndex.getSortField(scope.isSecondLevel()); if (scope.getSortBy() > 0) { orderBy = "sort_" + Integer.toString(scope.getSortBy()); } dao.setOrderField(orderBy); int offset = scope.getOffset(); String rawFocusValue = null; if (offset < 1 && (scope.hasJumpToItem() || scope.hasJumpToValue() || scope.hasStartsWith())) { // We need to convert these to an offset for the actual browse query. // First, get a value that we can look up in the ordering field rawFocusValue = getJumpToValue(); // make sure the incoming value is normalised String focusValue = normalizeJumpToValue(rawFocusValue); log.debug("browsing using focus: " + focusValue); // Convert the focus value into an offset offset = getOffsetForValue(focusValue); } dao.setOffset(offset); // assemble the LIMIT clause dao.setLimit(scope.getResultsPerPage()); // Holder for the results List<BrowseItem> results = null; // Does this browse have any contents? if (total > 0) { // now run the query results = dao.doQuery(); // now, if we don't have any results, we are at the end of the browse. This will // be because a starts_with value has been supplied for which we don't have // any items. if (results.size() == 0) { // In this case, we will calculate a new offset for the last page of results offset = total - scope.getResultsPerPage(); if (offset < 0) { offset = 0; } // And rerun the query dao.setOffset(offset); results = dao.doQuery(); } } else { // No records, so make an empty list results = new ArrayList<BrowseItem>(); } // construct the BrowseInfo object to pass back // BrowseInfo browseInfo = new BrowseInfo(results, position, total, offset); BrowseInfo browseInfo = new BrowseInfo(results, offset, total, offset); if (offset + scope.getResultsPerPage() < total) { browseInfo.setNextOffset(offset + scope.getResultsPerPage()); } if (offset - scope.getResultsPerPage() > -1) { browseInfo.setPrevOffset(offset - scope.getResultsPerPage()); } // add the browse index to the Browse Info browseInfo.setBrowseIndex(browseIndex); // set the sort option for the Browse Info browseInfo.setSortOption(scope.getSortOption()); // tell the Browse Info which way we are sorting browseInfo.setAscending(scope.isAscending()); // tell the Browse Info which level of browse we are at browseInfo.setBrowseLevel(scope.getBrowseLevel()); // set the browse value if there is one browseInfo.setValue(rawValue); // set the browse authority key if there is one browseInfo.setAuthority(scope.getAuthorityValue()); // set the focus value if there is one browseInfo.setFocus(rawFocusValue); if (scope.hasJumpToItem()) { browseInfo.setFocusItem(scope.getJumpToItem()); } // tell the browse info if it is working from a starts with parameter browseInfo.setStartsWith(scope.hasStartsWith()); // tell the browse info what the container for the browse was if (scope.inCollection() || scope.inCommunity()) { browseInfo.setBrowseContainer(scope.getBrowseContainer()); } browseInfo.setResultsPerPage(scope.getResultsPerPage()); browseInfo.setEtAl(scope.getEtAl()); return browseInfo; } catch (SQLException e) { log.error("caught exception: ", e); throw new BrowseException(e); } }
/** * Perform a limited browse, which only returns the results requested, without any extraneous * information. To perform a full browse, use BrowseEngine.browse() above. This supports Item * browse only, and does not currently support focus or values. This method is used, for example, * to generate the Recently Submitted Items results. * * @param bs the scope of the browse * @return the results of the browse */ public BrowseInfo browseMini(BrowserScope bs) throws BrowseException { log.info(LogManager.getHeader(context, "browse_mini", "")); // load the scope into the object this.scope = bs; // since we use it so much, get the browse index out of the // scope and store as a member browseIndex = scope.getBrowseIndex(); // get the table name that we are going to be getting our data from dao.setTable(browseIndex.getTableName()); // tell the browse query whether we are ascending or descending on the value dao.setAscending(scope.isAscending()); // define a clause for the WHERE clause which will allow us to constrain // our browse to a specified community or collection if (scope.inCollection() || scope.inCommunity()) { if (scope.inCollection()) { Collection col = (Collection) scope.getBrowseContainer(); dao.setContainerTable("collection2item"); dao.setContainerIDField("collection_id"); dao.setContainerID(col.getID()); } else if (scope.inCommunity()) { Community com = (Community) scope.getBrowseContainer(); dao.setContainerTable("communities2item"); dao.setContainerIDField("community_id"); dao.setContainerID(com.getID()); } } dao.setOffset(scope.getOffset()); dao.setLimit(scope.getResultsPerPage()); // assemble the ORDER BY clause String orderBy = browseIndex.getSortField(scope.isSecondLevel()); if (scope.getSortBy() > 0) { orderBy = "sort_" + Integer.toString(scope.getSortBy()); } dao.setOrderField(orderBy); // now run the query List<BrowseItem> results = dao.doQuery(); // construct the mostly empty BrowseInfo object to pass back BrowseInfo browseInfo = new BrowseInfo(results, 0, scope.getResultsPerPage(), 0); // add the browse index to the Browse Info browseInfo.setBrowseIndex(browseIndex); // set the sort option for the Browse Info browseInfo.setSortOption(scope.getSortOption()); // tell the Browse Info which way we are sorting browseInfo.setAscending(scope.isAscending()); // tell the browse info what the container for the browse was if (scope.inCollection() || scope.inCommunity()) { browseInfo.setBrowseContainer(scope.getBrowseContainer()); } browseInfo.setResultsPerPage(scope.getResultsPerPage()); browseInfo.setEtAl(scope.getEtAl()); return browseInfo; }