/* * ItemAdapter.java * * Version: $Revision: 3705 $ * * Date: $Date: 2009-04-11 18:02:24 +0100 (Sat, 11 Apr 2009) $ * * Copyright (c) 2002-2005, Hewlett-Packard Company and Massachusetts * Institute of Technology. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of the Hewlett-Packard Company nor the name of the * Massachusetts Institute of Technology nor the names of their * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. */ package org.dspace.app.xmlui.objectmanager; import org.dspace.app.xmlui.wing.AttributeMap; import org.dspace.app.xmlui.wing.WingException; import org.dspace.authorize.AuthorizeException; import org.dspace.content.Bitstream; import org.dspace.content.Bundle; import org.dspace.content.DCValue; import org.dspace.content.Item; import org.dspace.content.crosswalk.CrosswalkException; import org.dspace.content.crosswalk.DisseminationCrosswalk; import org.dspace.core.ConfigurationManager; import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.eperson.EPerson; import org.dspace.eperson.Group; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.output.SAXOutputter; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.XMLReaderFactory; import java.io.IOException; import java.io.InputStream; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import org.dspace.content.DSpaceObject; import uk.ac.jorum.licence.ItemLicence; import uk.ac.jorum.licence.LicenceController; /** * This is an adapter which translate a DSpace item into a METS document * following the DSpace METS profile, err well mostly. At least if you use * the proper configuration it will be fully complaint with the profile, * however this adapter will allow you to configure it to be incorrect. * * When we are configured to be non-complaint with the profile the MET's * profile is changed to reflect the diviation. The DSpace profile states * that metadata should be given in MODS format. However you can configure * this adapter to use any metadata crosswalk. When that case is detected we * change the profile to say that we are divating from the standard profile * and it lists what metadata has been added. * * There are four parts to an item's METS document: descriptive metadata, * file section, structural map, and extra sections. * * @author Scott Phillips */ public class ItemAdapter extends AbstractAdapter { /** The item this METS adapter represents */ private Item item; /** List of bitstreams which should be publicaly viewable */ private List<Bitstream> contentBitstreams = new ArrayList<Bitstream>(); /** The primary bitstream, or null if none specified */ private Bitstream primaryBitstream; /** A space seperated list of descriptive metadata sections */ private StringBuffer dmdSecIDS; /** A space seperated list of administrative metadata sections (for item)*/ private StringBuffer amdSecIDS; /** A hashmap of all Files and their corresponding space separated list of administrative metadata sections */ private HashMap<String,StringBuffer> fileAmdSecIDs = new HashMap<String,StringBuffer>(); /** Current DSpace context **/ private Context dspaceContext; /** * Construct a new ItemAdapter * * @param item * The DSpace item to adapt. * @param contextPath * The contextpath for this webapplication. */ public ItemAdapter(Context context, Item item,String contextPath) { super(contextPath); this.item = item; this.dspaceContext = context; } /** Return the item */ public Item getItem() { return this.item; } /** * * * * Required abstract methods * * * */ /** * Return the URL of this item in the interface */ protected String getMETSOBJID() { if (item.getHandle() != null) return contextPath+"/handle/" + item.getHandle(); return null; } /** * @return Return the URL for editing this item */ protected String getMETSOBJEDIT() { return contextPath+"/admin/item?itemID=" + item.getID(); } /** * Return the item's handle as the METS ID */ protected String getMETSID() { if (item.getHandle() == null) return "item:"+item.getID(); else return "hdl:"+item.getHandle(); } /** * Return the official METS SIP Profile. */ protected String getMETSProfile() throws WingException { return "DSPACE METS SIP Profile 1.0"; } /** * Return a helpfull label that this is a DSpace Item. */ protected String getMETSLabel() { return "DSpace Item"; } /** * Return a unique id for a bitstream. */ protected String getFileID(Bitstream bitstream) { return "file_" + bitstream.getID(); } /** * Return a group id for a bitstream. */ protected String getGroupFileID(Bitstream bitstream) { return "group_file_" + bitstream.getID(); } /** * Return a techMD id for a bitstream. */ protected String getAmdSecID(String admSecName, String mdType, DSpaceObject dso) { if (dso.getType() == Constants.BITSTREAM) return admSecName + "_" + getFileID((Bitstream)dso) + "_" + mdType; else return admSecName + "_" + dso.getID() + "_" + mdType; } /** * Render the METS descriptive section. This will create a new metadata * section for each crosswalk configured. Futher more, a special check * has been aded that will add mods descriptive metadata if it is * available in DSpace. * * Example: * <dmdSec> * <mdWrap MDTYPE="MODS"> * <xmlData> * ... content from the crosswalk ... * </xmlDate> * </mdWrap> * </dmdSec */ protected void renderDescriptiveSection() throws WingException, SAXException, CrosswalkException, IOException, SQLException { AttributeMap attributes; String groupID = getGenericID("group_dmd_"); dmdSecIDS = new StringBuffer(); // Add DIM descriptive metadata if it was requested or if no metadata types // were specified. Further more since this is the default type we also use a // faster rendering method that the crosswalk API. if(dmdTypes.size() == 0 || dmdTypes.contains("DIM")) { // Metadata element's ID String dmdID = getGenericID("dmd_"); // Keep track of all descriptive sections dmdSecIDS.append("" + dmdID); //////////////////////////////// // Start a metadata wrapper attributes = new AttributeMap(); attributes.put("ID", dmdID); attributes.put("GROUPID", groupID); startElement(METS, "dmdSec", attributes); //////////////////////////////// // Start a metadata wrapper attributes = new AttributeMap(); attributes.put("MDTYPE","OTHER"); attributes.put("OTHERMDTYPE", "DIM"); startElement(METS,"mdWrap",attributes); // //////////////////////////////// // Start the xml data startElement(METS,"xmlData"); // /////////////////////////////// // Start the DIM element attributes = new AttributeMap(); attributes.put("dspaceType", Constants.typeText[item.getType()]); if (item.isWithdrawn()) attributes.put("withdrawn", "y"); startElement(DIM,"dim",attributes); DCValue[] dcvs = item.getMetadata(Item.ANY, Item.ANY, Item.ANY, Item.ANY); for (DCValue dcv : dcvs) { // /////////////////////////////// // Field element for each metadata field. attributes = new AttributeMap(); attributes.put("mdschema",dcv.schema); attributes.put("element", dcv.element); if (dcv.qualifier != null) attributes.put("qualifier", dcv.qualifier); if (dcv.language != null) attributes.put("language", dcv.language); startElement(DIM,"field",attributes); sendCharacters(dcv.value); endElement(DIM,"field"); } // If the licence restricts viewing to certain groups then add a dcterms.accessRights element. if (isUserAuthorised()) { attributes = new AttributeMap(); attributes.put("mdschema","dc"); attributes.put("element", "accessRights"); startElement(DIM,"field",attributes); sendCharacters("viewAuthorised"); endElement(DIM, "field"); } // /////////////////////////////// // End the DIM element endElement(DIM,"dim"); // //////////////////////////////// // End elements endElement(METS,"xmlData"); endElement(METS,"mdWrap"); endElement(METS, "dmdSec"); } // Add any extra crosswalks that may configured. for (String dmdType : dmdTypes) { // If DIM was requested then it was generated above without using // the crosswalk API. So we can skip this one. if ("DIM".equals(dmdType)) continue; DisseminationCrosswalk crosswalk = getDisseminationCrosswalk(dmdType); if (crosswalk == null) continue; String dmdID = getGenericID("dmd_"); // Add our id to the list. dmdSecIDS.append(" " + dmdID); //////////////////////////////// // Start a metadata wrapper attributes = new AttributeMap(); attributes.put("ID", dmdID); attributes.put("GROUPID", groupID); startElement(METS, "dmdSec", attributes); //////////////////////////////// // Start a metadata wrapper attributes = new AttributeMap(); if (isDefinedMETStype(dmdType)) { attributes.put("MDTYPE", dmdType); } else { attributes.put("MDTYPE","OTHER"); attributes.put("OTHERMDTYPE", dmdType); } startElement(METS,"mdWrap",attributes); // //////////////////////////////// // Start the xml data startElement(METS,"xmlData"); // /////////////////////////////// // Send the actual XML content try { Element dissemination = crosswalk.disseminateElement(item); SAXFilter filter = new SAXFilter(contentHandler, lexicalHandler, namespaces); // Allow the basics for XML filter.allowElements().allowIgnorableWhitespace().allowCharacters().allowCDATA().allowPrefixMappings(); SAXOutputter outputter = new SAXOutputter(); outputter.setContentHandler(filter); outputter.setLexicalHandler(filter); outputter.output(dissemination); } catch (JDOMException jdome) { throw new WingException(jdome); } catch (AuthorizeException ae) { // just ignore the authorize exception and continue on with //out parsing the xml document. } // //////////////////////////////// // End elements endElement(METS,"xmlData"); endElement(METS,"mdWrap"); endElement(METS, "dmdSec"); } // Check to see if there is an in-line MODS document // stored as a bitstream. If there is then we should also // include these metadata in our METS document. However // we don't really know what the document describes, so we // but it in it's own dmd group. Boolean include = ConfigurationManager.getBooleanProperty("xmlui.bitstream.mods"); if (include && dmdTypes.contains("MODS")) { // Generate a second group id for any extra metadata added. String groupID2 = getGenericID("group_dmd_"); Bundle[] bundles = item.getBundles("METADATA"); for (Bundle bundle : bundles) { Bitstream bitstream = bundle.getBitstreamByName("MODS.xml"); if (bitstream == null) continue; String dmdID = getGenericID("dmd_"); //////////////////////////////// // Start a metadata wrapper attributes = new AttributeMap(); attributes.put("ID", dmdID); attributes.put("GROUPID", groupID2); startElement(METS, "dmdSec", attributes); //////////////////////////////// // Start a metadata wrapper attributes = new AttributeMap(); attributes.put("MDTYPE", "MODS"); startElement(METS,"mdWrap",attributes); // //////////////////////////////// // Start the xml data startElement(METS,"xmlData"); // /////////////////////////////// // Send the actual XML content SAXFilter filter = new SAXFilter(contentHandler, lexicalHandler, namespaces); // Allow the basics for XML filter.allowElements().allowIgnorableWhitespace().allowCharacters().allowCDATA().allowPrefixMappings(); XMLReader reader = XMLReaderFactory.createXMLReader(); reader.setContentHandler(filter); reader.setProperty("http://xml.org/sax/properties/lexical-handler", filter); try { InputStream is = bitstream.retrieve(); reader.parse(new InputSource(is)); } catch (AuthorizeException ae) { // just ignore the authorize exception and continue on with //out parsing the xml document. } // //////////////////////////////// // End elements endElement(METS,"xmlData"); endElement(METS,"mdWrap"); endElement(METS, "dmdSec"); } } } /* Does the licence permit the user to view this item ? */ private boolean isUserAuthorised() throws SQLException, IOException { boolean authorised = false; ItemLicence itemLicence = null; try { itemLicence = LicenceController.getItemLicence(item); } catch (AuthorizeException e) { // Generally not a good thing just ignoring an Exception like this, but its not actually thrown anywhere // down the chain and I don't want to change all the classes up the chain to cope with a faulty declaration. return false; } if (itemLicence != null) { Group[] authorisedGroups = itemLicence.authorisedGroupsForViewing(); if (authorisedGroups.length == 0) { authorised = true; } else { // Check to see if the current user belongs to one of the groups for (Group g : authorisedGroups) { if (Group.isMember(dspaceContext, g.getID())) { authorised = true; break; } } } } return authorised; } /** * Render the METS administrative section. * * Example: * <amdSec> * <mdWrap MDTYPE="OTHER" OTHERMDTYPE="METSRights"> * <xmlData> * ... content from the crosswalk ... * </xmlDate> * </mdWrap> * </amdSec> */ protected void renderAdministrativeSection() throws WingException, SAXException, CrosswalkException, IOException, SQLException { AttributeMap attributes; String groupID; //Only create an <amdSec>, if we have amdTypes (or sub-sections) specified... // (this keeps our METS file small, by default, and hides all our admin metadata) if(amdTypes.size() > 0) { //////////////////////////////// // Start an administrative wrapper // Administrative element's ID String amdID = getGenericID("amd_"); attributes = new AttributeMap(); attributes.put("ID", amdID); startElement(METS, "amdSec", attributes); groupID = getGenericID("group_amd_"); attributes.put("GROUPID", groupID); } // For each administrative metadata section specified for (String amdSecName : amdTypes.keySet()) { //get a list of metadata crosswalks which will be used to build // this administrative metadata section List<String> mdTypes = amdTypes.get(amdSecName); // For each crosswalk for (String mdType : mdTypes) { //get our dissemination crosswalk DisseminationCrosswalk crosswalk = getDisseminationCrosswalk(mdType); //skip, if we cannot find this crosswalk in config file if (crosswalk == null) continue; //First, check if this crosswalk can handle disseminating Item-level Administrative metadata if(crosswalk.canDisseminate(item)) { //Since this crosswalk works with items, first render a section for entire item renderAmdSubSection(amdSecName, mdType, crosswalk, item); } //Next, we'll try and render Bitstream-level administrative metadata // (Although, we're only rendering this metadata for the bundles specified) List<Bundle> bundles = findEnabledBundles(); for (Bundle bundle : bundles) { Bitstream[] bitstreams = bundle.getBitstreams(); //Create a sub-section of <amdSec> for each bitstream in bundle for(Bitstream bitstream : bitstreams) { //Only render the section if crosswalk works with bitstreams if(crosswalk.canDisseminate(bitstream)) { renderAmdSubSection(amdSecName, mdType, crosswalk, bitstream); } }//end for each bitstream }//end for each bundle }//end for each crosswalk }//end for each amdSec if(amdTypes.size() > 0) { ////////////////////////////////// // End administrative section endElement(METS,"amdSec"); } } /** * Render a sub-section of the administrative metadata section. * Valid sub-sections include: techMD, rightsMD, sourceMD, digiprovMD * * Example: * <techMD> * <mdWrap MDTYPE="PREMIS"> * <xmlData> * [PREMIS content ... ] * </xmlData> * </mdWrap> * </techMD> * * @param amdSecName Name of administrative metadata section * @param mdType Type of metadata section (e.g. PREMIS) * @param crosswalk The DisseminationCrosswalk to use to generate this section * @param dso The current DSpace object to use the crosswalk on */ protected void renderAmdSubSection(String amdSecName, String mdType, DisseminationCrosswalk crosswalk, DSpaceObject dso) throws WingException, SAXException, CrosswalkException, IOException, SQLException { ///////////////////////////////// // Start administrative metadata section wrapper String amdSecID = getAmdSecID(amdSecName, mdType, dso); AttributeMap attributes = new AttributeMap(); attributes.put("ID", amdSecID); startElement(METS, amdSecName, attributes); //If this is a bitstream if (dso.getType() == Constants.BITSTREAM) { // Add this to our list of each file's administrative section IDs String fileID = getFileID((Bitstream) dso); if(fileAmdSecIDs.containsKey(fileID)) fileAmdSecIDs.get(fileID).append(" " + amdSecID); else fileAmdSecIDs.put(fileID, new StringBuffer(amdSecID)); }//else if an Item else if (dso.getType() == Constants.ITEM) { //Add this to our list of item's administrative section IDs if(amdSecIDS==null) amdSecIDS = new StringBuffer(amdSecID); else amdSecIDS.append(" " + amdSecID); } //////////////////////////////// // Start a metadata wrapper attributes = new AttributeMap(); if (isDefinedMETStype(mdType)) { attributes.put("MDTYPE", mdType); } else { attributes.put("MDTYPE","OTHER"); attributes.put("OTHERMDTYPE", mdType); } startElement(METS,"mdWrap",attributes); ////////////////////////////////// // Start the xml data startElement(METS,"xmlData"); ///////////////////////////////// // Send the actual XML content, // using the PREMIS crosswalk for each bitstream try { Element dissemination = crosswalk.disseminateElement(dso); SAXFilter filter = new SAXFilter(contentHandler, lexicalHandler, namespaces); // Allow the basics for XML filter.allowElements().allowIgnorableWhitespace().allowCharacters().allowCDATA().allowPrefixMappings(); SAXOutputter outputter = new SAXOutputter(); outputter.setContentHandler(filter); outputter.setLexicalHandler(filter); outputter.output(dissemination); } catch (JDOMException jdome) { throw new WingException(jdome); } catch (AuthorizeException ae) { // just ignore the authorize exception and continue on with //out parsing the xml document. } // //////////////////////////////// // End elements endElement(METS,"xmlData"); endElement(METS,"mdWrap"); endElement(METS,amdSecName); } /** * Render the METS file section. This will contain a list of all bitstreams in the * item. Each bundle, even those that are not typically displayed will be listed. * * Example: * <fileSec> * <fileGrp USE="CONTENT"> * <file ... > * <fLocate ... > * </file> * </fileGrp> * <fileGrp USE="TEXT"> * <file ... > * <fLocate ... > * </file> * </fileGrp> * </fileSec> */ protected void renderFileSection() throws SQLException, SAXException { AttributeMap attributes; // ////////////////////// // Start a new file section startElement(METS,"fileSec"); // Check if the user is requested a specific bundle or // the all bundles. List<Bundle> bundles = findEnabledBundles(); // Loop over all requested bundles for (Bundle bundle : bundles) { // Use the bitstream's name as the use parameter unless we // are the original bundle. In this case rename it to // content. String use = bundle.getName(); boolean isContentBundle = false; // remember the content bundle. boolean isDerivedBundle = false; if ("ORIGINAL".equals(use)) { use = "CONTENT"; isContentBundle = true; } if ("TEXT".equals(bundle.getName()) || "THUMBNAIL".equals(bundle.getName())) { isDerivedBundle = true; } // /////////////////// // Start bundle's file group attributes = new AttributeMap(); attributes.put("USE", use); startElement(METS,"fileGrp",attributes); for (Bitstream bitstream : bundle.getBitstreams()) { // ////////////////////////////// // Determine the file's IDs String fileID = getFileID(bitstream); Bitstream originalBitstream = null; if (isDerivedBundle) originalBitstream = findOriginalBitstream(item, bitstream); String groupID = getGroupFileID((originalBitstream == null) ? bitstream : originalBitstream ); //Check if there were administrative metadata sections corresponding to this file String admIDs = null; if(fileAmdSecIDs.containsKey(fileID)) admIDs = fileAmdSecIDs.get(fileID).toString(); // Render the actual file & flocate elements. renderFile(item, bitstream, fileID, groupID, admIDs); // Remember all the viewable content bitstreams for later in the // structMap. if (isContentBundle) { contentBitstreams.add(bitstream); if (bundle.getPrimaryBitstreamID() == bitstream.getID()) primaryBitstream = bitstream; } } // /////////////////// // End the bundle's file group endElement(METS,"fileGrp"); } // ////////////////////// // End the file section endElement(METS,"fileSec"); } /** * Render the item's structural map. This includes a list of * content bitstreams, those are bistreams that are typicaly * viewable by the end user. * * Examlpe: * <structMap TYPE="LOGICAL" LABEL="DSpace"> * <div TYPE="DSpace Item" DMDID="space seperated list of ids"> * <fptr FILEID="primary bitstream"/> * ... a div for each content bitstream. * </div> * </structMap> */ protected void renderStructureMap() throws SQLException, SAXException { AttributeMap attributes; // /////////////////////// // Start a new structure map attributes = new AttributeMap(); attributes.put("TYPE", "LOGICAL"); attributes.put("LABEL", "DSpace"); startElement(METS,"structMap",attributes); // //////////////////////////////// // Start the special first division attributes = new AttributeMap(); attributes.put("TYPE", "DSpace Item"); // add references to the Descriptive metadata if (dmdSecIDS != null) attributes.put("DMDID", dmdSecIDS.toString()); // add references to the Administrative metadata if (amdSecIDS != null) attributes.put("AMDID", amdSecIDS.toString()); startElement(METS,"div",attributes); // add a fptr pointer to the primary bitstream. if (primaryBitstream != null) { // //////////////////////////////// // Start & end a refrence to the primary bistream. attributes = new AttributeMap(); String fileID = getFileID(primaryBitstream); attributes.put("FILEID", fileID); startElement(METS,"fptr",attributes); endElement(METS,"fptr"); } for (Bitstream bitstream : contentBitstreams) { // //////////////////////////////////// // Start a div for each publicaly viewable bitstream attributes = new AttributeMap(); attributes.put("ID", getGenericID("div_")); attributes.put("TYPE", "DSpace Content Bitstream"); startElement(METS,"div",attributes); // //////////////////////////////// // Start a the actualy pointer to the bitstream attributes = new AttributeMap(); String fileID = getFileID(bitstream); attributes.put("FILEID", fileID); startElement(METS,"fptr",attributes); endElement(METS,"fptr"); // /////////////////////////////// // End the div endElement(METS,"div"); } // //////////////////////////////// // End the special first division endElement(METS,"div"); // /////////////////////// // End the structure map endElement(METS,"structMap"); } /** * Render any extra METS section. If the item contains a METS.xml document * then all of that document's sections are included in this document's * METS document. */ protected void renderExtraSections() throws SAXException, SQLException, IOException { Boolean include = ConfigurationManager.getBooleanProperty("xmlui.bitstream.mets"); if (!include) return; Bundle[] bundles = item.getBundles("METADATA"); for (Bundle bundle : bundles) { Bitstream bitstream = bundle.getBitstreamByName("METS.xml"); if (bitstream == null) continue; // /////////////////////////////// // Send the actual XML content try { SAXFilter filter = new SAXFilter(contentHandler, lexicalHandler, namespaces); // Allow the basics for XML filter.allowIgnorableWhitespace().allowCharacters().allowCDATA().allowPrefixMappings(); // Special option, only allow elements below the second level to pass through. This // will trim out the METS declaration and only leave the actual METS parts to be // included. filter.allowElements(1); XMLReader reader = XMLReaderFactory.createXMLReader(); reader.setContentHandler(filter); reader.setProperty("http://xml.org/sax/properties/lexical-handler", filter); reader.parse(new InputSource(bitstream.retrieve())); } catch (AuthorizeException ae) { // just ignore the authorize exception and continue on with //out parsing the xml document. } } } /** * Checks which Bundles of current item a user has requested. * If none specifically requested, then all Bundles are returned. * * @return List of enabled bundles */ protected List<Bundle> findEnabledBundles() throws SQLException { // Check if the user is requested a specific bundle or // the all bundles. List<Bundle> bundles; if (fileGrpTypes.size() == 0) bundles = Arrays.asList(item.getBundles()); else { bundles = new ArrayList<Bundle>(); for (String fileGrpType : fileGrpTypes) { for (Bundle newBundle : item.getBundles(fileGrpType)) { bundles.add(newBundle); } } } return bundles; } /** * For a bitstream that's a thumbnail or extracted text, find the * corresponding bitstream it was derived from, in the ORIGINAL bundle. * * @param item * the item we're dealing with * @param derived * the derived bitstream * * @return the corresponding original bitstream (or null) */ protected static Bitstream findOriginalBitstream(Item item,Bitstream derived) throws SQLException { // FIXME: this method is a copy of the one found below. However the // original method is protected so we can't use it here. I think that // perhaps this should be folded into the DSpace bitstream API. Untill // then a good final solution can be determined I am just going to copy // the method here. // // return org.dspace.content.packager.AbstractMetsDissemination // .findOriginalBitstream(item, derived); Bundle[] bundles = item.getBundles(); // Filename of original will be filename of the derived bitstream // minus the extension (last 4 chars - .jpg or .txt) String originalFilename = derived.getName().substring(0, derived.getName().length() - 4); // First find "original" bundle for (int i = 0; i < bundles.length; i++) { if ((bundles[i].getName() != null) && bundles[i].getName().equals("ORIGINAL")) { // Now find the corresponding bitstream Bitstream[] bitstreams = bundles[i].getBitstreams(); for (int bsnum = 0; bsnum < bitstreams.length; bsnum++) { if (bitstreams[bsnum].getName().equals(originalFilename)) { return bitstreams[bsnum]; } } } } // Didn't find it return null; } }