/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.browse;
import java.sql.SQLException;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.Metadatum;
import org.dspace.content.DSpaceObject;
import org.dspace.content.Item;
import org.dspace.core.Context;
import org.dspace.sort.SortOption;
/**
* The results of a Browse, including all the contextual information about
* the query, as well as the results and associated information to create
* pageable navigation.
*
* @author Richard Jones
*/
public class BrowseInfo
{
/**
* The results of the browse.
* FIXME: Unable to generify due to mixed usage
*/
private List results;
/**
* The position of the first element of results within the Browse index.
* Positions begin with 0.
*/
private int overallPosition;
/**
* The position of the requested object within the results. Offsets begin
* with 0.
*/
private int offset;
/**
* The total number of items in the browse index.
*/
private int total;
/**
* True if this browse was cached.
*/
private boolean cached;
/** the browse index to which this pertains */
private BrowseIndex browseIndex;
/** the sort option being used */
private SortOption sortOption;
/** is the browse ascending or descending */
private boolean ascending;
/** what level of browse are we in? full and single front pages are 0, single value browse is 1 */
private int level = 0;
/** the value browsed upon */
private String value;
/** the authority key browsed upon */
private String authority;
/** is this a "starts_with" browse? */
private boolean startsWith = false;
/** Collection we are constrained to */
private Collection collection;
/** Community we are constrained to */
private Community community;
/** offset of the item at the top of the next page */
private int nextOffset = -1;
/** offset of the item at the top of the previous page */
private int prevOffset = -1;
/** the value upon which we are focusing */
private String focus;
/** number of results to display per page */
private int resultsPerPage = -1;
/** database id of the item upon which we are focusing */
private int focusItem = -1;
/** number of metadata elements to display before truncating using "et al" */
private int etAl = -1;
/**
* Constructor
* FIXME: Unable to generify due to mixed usage
*
* @param results
* A List of Browse results
* @param overallPosition
* The position of the first returned item in the overall index
* @param total
* The total number of items in the index
* @param offset
* The position of the requested item in the set of results
*/
public BrowseInfo(List results, int overallPosition, int total, int offset)
{
if (results == null)
{
throw new IllegalArgumentException("Null result list not allowed");
}
this.results = Collections.unmodifiableList(results);
this.overallPosition = overallPosition;
this.total = total;
this.offset = offset;
}
/**
* @return the number of metadata fields at which to truncate with "et al"
*/
public int getEtAl()
{
return etAl;
}
/**
* set the number of metadata fields at which to truncate with "et al"
*
* @param etAl
*/
public void setEtAl(int etAl)
{
this.etAl = etAl;
}
/**
* @return Returns the focusItem.
*/
public int getFocusItem()
{
return focusItem;
}
/**
* @param focusItem The focusItem to set.
*/
public void setFocusItem(int focusItem)
{
this.focusItem = focusItem;
}
/**
* Does this browse have an item focus (as opposed to one of: no focus,
* a value focus)
*
* @return true if item focus, false if not
*/
public boolean hasItemFocus()
{
if (focusItem == -1)
{
return false;
}
return true;
}
/**
* @return Returns the resultsPerPage.
*/
public int getResultsPerPage()
{
return resultsPerPage;
}
/**
* @param resultsPerPage The resultsPerPage to set.
*/
public void setResultsPerPage(int resultsPerPage)
{
this.resultsPerPage = resultsPerPage;
}
/**
* Is there a value associated with this browse
*
* @return true if a value, false if not
*/
public boolean hasValue()
{
if (this.value != null)
{
return true;
}
return false;
}
/**
* Is there an authority key associated with this browse
*
* @return true if an authority key, false if not
*/
public boolean hasAuthority()
{
if (this.authority != null)
{
return true;
}
return false;
}
/**
* Are there results for this browse, or was the result set empty?
*
* @return true if results, false if not
*/
public boolean hasResults()
{
if (results.size() > 0)
{
return true;
}
return false;
}
/**
* @param focus the value to focus the browse around
*/
public void setFocus(String focus)
{
this.focus = focus;
}
/**
* @return the value to focus the browse around
*/
public String getFocus()
{
return this.focus;
}
/**
* Set the DSpaceObject that is the container for this browse. If this
* is not of type Collection or Community, this method will throw an
* exception
*
* @param dso the container object; a Community or Collection
* @throws BrowseException
*/
public void setBrowseContainer(DSpaceObject dso)
throws BrowseException
{
if (dso instanceof Collection)
{
this.collection = (Collection) dso;
}
else if (dso instanceof Community)
{
this.community = (Community) dso;
}
else
{
throw new BrowseException("The container must be a community or a collection");
}
}
/**
* Obtain a DSpaceObject that represents the container object. This will be
* a Community or a Collection
*
* @return A DSpaceObject representing a Community or a Collection
*/
public DSpaceObject getBrowseContainer()
{
if (this.collection != null)
{
return this.collection;
}
if (this.community != null)
{
return this.community;
}
return null;
}
/**
* @param level the browse level
*/
public void setBrowseLevel(int level)
{
this.level = level;
}
/**
* @return the browse level
*/
public int getBrowseLevel()
{
return this.level;
}
/**
* @param offset the database id of the item at the top of the next page
*/
public void setNextOffset(int offset)
{
this.nextOffset = offset;
}
/**
* @return the database id of the item at the top of the next page
*/
public int getNextOffset()
{
return this.nextOffset;
}
/**
* @return Returns the ascending.
*/
public boolean isAscending()
{
return ascending;
}
/**
* @param ascending The ascending to set.
*/
public void setAscending(boolean ascending)
{
this.ascending = ascending;
}
/**
* @return Returns the browseIndex.
*/
public BrowseIndex getBrowseIndex()
{
return browseIndex;
}
/**
* @param browseIndex The browseIndex to set.
*/
public void setBrowseIndex(BrowseIndex browseIndex)
{
this.browseIndex = browseIndex;
}
/**
* @return Returns the prevItem.
*/
public int getPrevOffset()
{
return prevOffset > -1 ? prevOffset : 0;
}
/**
* @param prevOffset The prevOffset to set.
*/
public void setPrevOffset(int prevOffset)
{
this.prevOffset = prevOffset;
}
/**
* @return Returns the sortOption.
*/
public SortOption getSortOption()
{
return sortOption;
}
/**
* @param sortOption The sortOption to set.
*/
public void setSortOption(SortOption sortOption)
{
this.sortOption = sortOption;
}
/**
* @return Returns the startsWith.
*/
public boolean isStartsWith()
{
return startsWith;
}
/**
* @param startsWith The startsWith to set.
*/
public void setStartsWith(boolean startsWith)
{
this.startsWith = startsWith;
}
/**
* @return Returns the value.
*/
public String getValue()
{
return value;
}
/**
* @param value The value to set.
*/
public void setValue(String value)
{
this.value = value;
}
/**
* @return Returns the authority key.
*/
public String getAuthority()
{
return authority;
}
/**
* @param authority The authority key to set.
*/
public void setAuthority(String authority)
{
this.authority = authority;
}
/**
* is this a top level (0) browse? Examples of this are a full item
* browse or a single browse. Other browse types are considered
* second level (1)
*
* @return true if top level, false if not
*/
public boolean isTopLevel()
{
if (this.level == 0)
{
return true;
}
return false;
}
/**
* Is this a second level (1) browse? Examples of this are a single
* value browse (e.g. all items by a given author)
*
* @return true if second level, false if not
*/
public boolean isSecondLevel()
{
if (this.level == 1)
{
return true;
}
return false;
}
/**
* The results of the Browse. Each member of the list is either a String array
* (for the authors browse: first element the value, second element the authority key)
* or an {@link org.dspace.content.Item}(for the
* other browses).
*
* @return Result list. This list cannot be modified.
*/
public List getResults()
{
return results;
}
/**
* Return the results of the Browse as an array of String array.
* The first element (i.e. index 0) is the value, the second is the authority key
*
* @return The results of the Browse as a String array.
*/
public String[][] getStringResults()
{
return (String[][]) results.toArray(new String[results.size()][2]);
}
/**
* @deprecated
* @return an empty array of Item.
*/
public Item[] getItemResults()
{
return new Item[0];
}
/**
* Return the results of the Browse as an Item array.
*
* @return The results of the Browse as an Item array.
*/
public Item[] getItemResults(Context context)
throws BrowseException
{
try
{
BrowseItem[] bis = getBrowseItemResults();
Item[] items = new Item[bis.length];
for (int i = 0; i < bis.length; i++)
{
items[i] = Item.find(context, bis[i].getID());
}
return items;
}
catch (SQLException e)
{
throw new BrowseException(e);
}
}
/**
* Return the results of the Browse as a BrowseItem array
*
* @return the results of the browse as a BrowseItem array
*/
public BrowseItem[] getBrowseItemResults()
{
return (BrowseItem[]) results.toArray(new BrowseItem[results.size()]);
}
/**
* Return the number of results.
*
* @return The number of results.
*/
public int getResultCount()
{
return results.size();
}
/**
* Return the position of the results in index being browsed. This is 0 for
* the start of the index.
*
* @return The position of the results in index being browsed.
*/
public int getOverallPosition()
{
return overallPosition;
}
/**
* Return the total number of items in the index.
*
* @return The total number of items in the index.
*/
public int getTotal()
{
return total;
}
/**
* Return the position of the requested item or value in the set of results.
*
* @return The position of the requested item or value in the set of results
*/
public int getOffset()
{
return offset;
}
/**
* True if there are no previous results from the browse.
*
* @return True if there are no previous results from the browse
*/
public boolean isFirst()
{
return overallPosition == 0;
}
/**
* True if these are the last results from the browse.
*
* @return True if these are the last results from the browse
*/
public boolean isLast()
{
return (overallPosition + getResultCount()) == total;
}
/**
* True if this browse was cached.
*/
public boolean wasCached()
{
return cached;
}
/**
* Set whether this browse was cached.
*/
void setCached(boolean cached)
{
this.cached = cached;
}
/**
* are we browsing within a Community container?
*
* @return true if in community, false if not
*/
public boolean inCommunity()
{
if (this.community != null)
{
return true;
}
return false;
}
/**
* are we browsing within a Collection container
*
* @return true if in collection, false if not
*/
public boolean inCollection()
{
if (this.collection != null)
{
return true;
}
return false;
}
/**
* Are there further results for the browse that haven't been returned yet?
*
* @return true if next page, false if not
*/
public boolean hasNextPage()
{
if (nextOffset > -1)
{
return true;
}
return false;
}
/**
* Are there results prior to these that haven't been returned here?
*
* @return true if previous page, false if not
*/
public boolean hasPrevPage()
{
if (offset > 0)
{
return true;
}
return false;
}
/**
* Does this browse have a focus?
*
* @return true if focus, false if not
*/
public boolean hasFocus()
{
if ("".equals(focus) || focus == null)
{
return false;
}
return true;
}
/**
* Get an integer representing the number within the total set of results which
* marks the position of the first result in the current sub-set
*
* @return the start point of the browse page
*/
public int getStart()
{
return overallPosition + 1;
}
/**
* Get an integer representing the number within the total set of results which
* marks the position of the last result in the current sub-set
*
* @return the end point of the browse page
*/
public int getFinish()
{
return overallPosition + results.size();
}
/**
* 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();
}
}
/**
* A utility method for generating a string to represent a single item's
* entry in the browse
*
* @param config
* @return
* @throws SQLException
*/
private String fullListingString(ItemListConfig config)
throws SQLException
{
// report on all the results contained herein
StringBuffer sb = new StringBuffer();
Iterator itr = results.iterator();
while (itr.hasNext())
{
BrowseItem bi = (BrowseItem) itr.next();
if (bi == null)
{
sb.append("{{ NULL ITEM }}");
break;
}
sb.append("{{Item ID: " + Integer.toString(bi.getID()) + " :: ");
for (int j = 1; j <= config.numCols(); j++)
{
String[] md = config.getMetadata(j);
if (md == null)
{
sb.append("{{ NULL METADATA }}");
break;
}
Metadatum[] values = bi.getMetadata(md[0], md[1], md[2], Item.ANY);
StringBuffer value = new StringBuffer();
if (values != null)
{
for (int i = 0; i < values.length; i++)
{
if (i > 0)
{
value.append(",");
}
value.append(values[i].value);
}
}
else
{
value.append("-");
}
String metadata = "[" + md[0] + "." + md[1] + "." + md[2] + ":" + value.toString() + "]";
sb.append(metadata);
}
sb.append("}}");
}
return sb.toString();
}
/**
* A utility method for representing a single value in the browse
*
* @return
*/
private String valueListingString()
{
// report on all the results contained herein
StringBuffer sb = new StringBuffer();
Iterator itr = results.iterator();
while (itr.hasNext())
{
String theValue = (String) itr.next();
if (theValue == null)
{
sb.append("{{ NULL VALUE }}");
break;
}
sb.append("{{Value: " + theValue + "}}");
}
return sb.toString();
}
}