MdStreamCache extraStreams)
throws MetsException, PackageValidationException, CrosswalkException, AuthorizeException, SQLException, IOException
{
// Create the METS manifest in memory
Mets mets = new Mets();
String identifier = "DB-ID-" + dso.getID();
if(dso.getHandle()!=null)
{
identifier = dso.getHandle().replace('/', '-');
}
// this ID should be globally unique (format: DSpace_[objType]_[handle with slash replaced with a dash])
mets.setID("DSpace_" + Constants.typeText[dso.getType()] + "_" + identifier);
// identifies the object described by this document
mets.setOBJID(makePersistentID(dso));
mets.setTYPE(getObjectTypeString(dso));
// this is the signature by which the ingester will recognize
// a document it can expect to interpret.
mets.setPROFILE(getProfile());
MetsHdr metsHdr = makeMetsHdr(context, dso, params);
if (metsHdr != null)
{
mets.getContent().add(metsHdr);
}
// add DMD sections
// Each type element MAY be either just a MODS-and-crosswalk name, OR
// a combination "MODS-name:crosswalk-name" (e.g. "DC:qDC").
String dmdTypes[] = getDmdTypes(context, dso, params);
// record of ID of each dmdsec to make DMDID in structmap.
String dmdId[] = new String[dmdTypes.length];
for (int i = 0; i < dmdTypes.length; ++i)
{
MdSec dmdSec = makeMdSec(context, dso, DmdSec.class, dmdTypes[i], params, extraStreams);
if (dmdSec != null)
{
mets.getContent().add(dmdSec);
dmdId[i] = dmdSec.getID();
}
}
// add object-wide technical/source MD segments, get ID string:
// Put that ID in ADMID of first div in structmap.
String objectAMDID = addAmdSec(context, dso, params, mets, extraStreams);
// Create simple structMap: initial div represents the Object's
// contents, its children are e.g. Item bitstreams (content only),
// Collection's members, or Community's members.
StructMap structMap = new StructMap();
structMap.setID(gensym("struct"));
structMap.setTYPE("LOGICAL");
structMap.setLABEL("DSpace Object");
Div div0 = new Div();
div0.setID(gensym("div"));
div0.setTYPE("DSpace Object Contents");
structMap.getContent().add(div0);
// fileSec is optional, let object type create it if needed.
FileSec fileSec = null;
// Item-specific manifest - license, bitstreams as Files, etc.
if (dso.getType() == Constants.ITEM)
{
// this tags file ID and group identifiers for bitstreams.
String bitstreamIDstart = "bitstream_";
Item item = (Item)dso;
// how to handle unauthorized bundle/bitstream:
String unauth = (params == null) ? null : params.getProperty("unauthorized");
// fileSec - all non-metadata bundles go into fileGrp,
// and each bitstream therein into a file.
// Create the bitstream-level techMd and div's for structmap
// at the same time so we can connect the IDREFs to IDs.
fileSec = new FileSec();
Bundle[] bundles = item.getBundles();
for (int i = 0; i < bundles.length; i++)
{
if (!includeBundle(bundles[i]))
{
continue;
}
// unauthorized bundle?
// NOTE: This must match the logic in disseminate()
if (!AuthorizeManager.authorizeActionBoolean(context,
bundles[i], Constants.READ))
{
if (unauth != null &&
(unauth.equalsIgnoreCase("skip")))
{
continue;
}
else
{
throw new AuthorizeException("Not authorized to read Bundle named \"" + bundles[i].getName() + "\"");
}
}
Bitstream[] bitstreams = bundles[i].getBitstreams();
// Create a fileGrp, USE = permuted Bundle name
FileGrp fileGrp = new FileGrp();
String bName = bundles[i].getName();
if ((bName != null) && !bName.equals(""))
{
fileGrp.setUSE(bundleToFileGrp(bName));
}
// add technical metadata for a bundle
String techBundID = addAmdSec(context, bundles[i], params, mets, extraStreams);
if (techBundID != null)
{
fileGrp.setADMID(techBundID);
}
// watch for primary bitstream
int primaryBitstreamID = -1;
boolean isContentBundle = false;
if ((bName != null) && bName.equals("ORIGINAL"))
{
isContentBundle = true;
primaryBitstreamID = bundles[i].getPrimaryBitstreamID();
}
// For each bitstream, add to METS manifest
for (int bits = 0; bits < bitstreams.length; bits++)
{
// Check for authorization. Handle unauthorized
// bitstreams to match the logic in disseminate(),
// i.e. "unauth=zero" means include a 0-length bitstream,
// "unauth=skip" means to ignore it (and exclude from
// manifest).
boolean auth = AuthorizeManager.authorizeActionBoolean(context,
bitstreams[bits], Constants.READ);
if (!auth)
{
if (unauth != null && unauth.equalsIgnoreCase("skip"))
{
continue;
}
else if (!(unauth != null && unauth.equalsIgnoreCase("zero")))
{
throw new AuthorizeException("Not authorized to read Bitstream, SID=" + String.valueOf(bitstreams[bits].getSequenceID()));
}
}
String sid = String.valueOf(bitstreams[bits].getSequenceID());
String fileID = bitstreamIDstart + sid;
edu.harvard.hul.ois.mets.File file = new edu.harvard.hul.ois.mets.File();
file.setID(fileID);
file.setSEQ(bitstreams[bits].getSequenceID());
fileGrp.getContent().add(file);
// set primary bitstream in structMap
if (bitstreams[bits].getID() == primaryBitstreamID)
{
Fptr fptr = new Fptr();
fptr.setFILEID(fileID);
div0.getContent().add(0, fptr);
}
// if this is content, add to structmap too:
if (isContentBundle)
{
div0.getContent().add(makeFileDiv(fileID, getObjectTypeString(bitstreams[bits])));
}
/*
* If we're in THUMBNAIL or TEXT bundles, the bitstream is
* extracted text or a thumbnail, so we use the name to work
* out which bitstream to be in the same group as
*/
String groupID = "GROUP_" + bitstreamIDstart + sid;
if ((bundles[i].getName() != null)
&& (bundles[i].getName().equals("THUMBNAIL") ||
bundles[i].getName().startsWith("TEXT")))
{
// Try and find the original bitstream, and chuck the
// derived bitstream in the same group
Bitstream original = findOriginalBitstream(item,
bitstreams[bits]);
if (original != null)
{
groupID = "GROUP_" + bitstreamIDstart
+ original.getSequenceID();
}
}
file.setGROUPID(groupID);
file.setMIMETYPE(bitstreams[bits].getFormat().getMIMEType());
file.setSIZE(auth ? bitstreams[bits].getSize() : 0);
// Translate checksum and type to METS
String csType = bitstreams[bits].getChecksumAlgorithm();
String cs = bitstreams[bits].getChecksum();
if (auth && cs != null && csType != null)
{
try
{
file.setCHECKSUMTYPE(Checksumtype.parse(csType));
file.setCHECKSUM(cs);
}
catch (MetsException e)
{
log.warn("Cannot set bitstream checksum type="+csType+" in METS.");
}
}
// FLocat: point to location of bitstream contents.
FLocat flocat = new FLocat();
flocat.setLOCTYPE(Loctype.URL);
flocat.setXlinkHref(makeBitstreamURL(bitstreams[bits], params));
file.getContent().add(flocat);
// technical metadata for bitstream
String techID = addAmdSec(context, bitstreams[bits], params, mets, extraStreams);
if (techID != null)
{
file.setADMID(techID);
}
}
fileSec.getContent().add(fileGrp);
}
}
else if (dso.getType() == Constants.COLLECTION)
{
Collection collection = (Collection)dso;
ItemIterator ii = collection.getItems();
while (ii.hasNext())
{
//add a child <div> for each item in collection
Item item = ii.next();
Div childDiv = makeChildDiv(getObjectTypeString(item), item, params);
if(childDiv!=null)
{
div0.getContent().add(childDiv);
}
}
// add metadata & info for Template Item, if exists
Item templateItem = collection.getTemplateItem();
if(templateItem!=null)
{
String templateDmdId[] = new String[dmdTypes.length];
// index where we should add the first template item <dmdSec>.
// Index = number of <dmdSecs> already added + number of <metsHdr> = # of dmdSecs + 1
// (Note: in order to be a valid METS file, all dmdSecs must be before the 1st amdSec)
int dmdIndex = dmdTypes.length + 1;
//For each type of dmdSec specified,
// add a new dmdSec which contains the Template Item metadata
// (Note: Template Items are only metadata -- they have no content files)
for (int i = 0; i < dmdTypes.length; ++i)
{
MdSec templateDmdSec = makeMdSec(context, templateItem, DmdSec.class, dmdTypes[i], params, extraStreams);
if (templateDmdSec != null)
{
mets.getContent().add(dmdIndex, templateDmdSec);
dmdIndex++;
templateDmdId[i] = templateDmdSec.getID();
}
}
//Now add a child <div> in structMap to represent that Template Item
Div templateItemDiv = new Div();
templateItemDiv.setID(gensym("div"));
templateItemDiv.setTYPE(getObjectTypeString(templateItem) + TEMPLATE_TYPE_SUFFIX);
//Link up the dmdSec(s) for the Template Item to this <div>
StringBuilder templateDmdIds = new StringBuilder();
for (String currdmdId : templateDmdId)
{
templateDmdIds.append(" ").append(currdmdId);
}
templateItemDiv.setDMDID(templateDmdIds.substring(1));
//add this child <div> before the listing of normal Items
div0.getContent().add(0, templateItemDiv);
}
// add link to Collection Logo, if one exists
Bitstream logoBs = collection.getLogo();
if (logoBs != null)
{
fileSec = new FileSec();
addLogoBitstream(logoBs, fileSec, div0, params);
}
}
else if (dso.getType() == Constants.COMMUNITY)
{
// Subcommunities are directly under "DSpace Object Contents" <div>,
// but are labeled as Communities.
Community subcomms[] = ((Community)dso).getSubcommunities();
for (int i = 0; i < subcomms.length; ++i)
{
//add a child <div> for each subcommunity in this community
Div childDiv = makeChildDiv(getObjectTypeString(subcomms[i]), subcomms[i], params);
if(childDiv!=null)
{
div0.getContent().add(childDiv);
}
}
// Collections are also directly under "DSpace Object Contents" <div>,
// but are labeled as Collections.
Collection colls[] = ((Community)dso).getCollections();
for (int i = 0; i < colls.length; ++i)
{
//add a child <div> for each collection in this community
Div childDiv = makeChildDiv(getObjectTypeString(colls[i]), colls[i], params);
if(childDiv!=null)
{
div0.getContent().add(childDiv);
}
}
//add Community logo bitstream
Bitstream logoBs = ((Community)dso).getLogo();
if (logoBs != null)
{
fileSec = new FileSec();
addLogoBitstream(logoBs, fileSec, div0, params);
}
}
else if (dso.getType() == Constants.SITE)
{
// This is a site-wide <structMap>, which just lists the top-level
// communities. Each top level community is referenced by a div.
Community comms[] = Community.findAllTop(context);
for (int i = 0; i < comms.length; ++i)
{
//add a child <div> for each top level community in this site
Div childDiv = makeChildDiv(getObjectTypeString(comms[i]),
comms[i], params);
if(childDiv!=null)
{
div0.getContent().add(childDiv);
}
}
}
//Only add the <fileSec> to the METS file if it has content. A <fileSec> must have content.
if (fileSec != null && fileSec.getContent()!=null && !fileSec.getContent().isEmpty())
{
mets.getContent().add(fileSec);
}
mets.getContent().add(structMap);
// set links to metadata for object -- after type-specific
// code since that can add to the object metadata.
StringBuilder dmdIds = new StringBuilder();
for (String currdmdId : dmdId)