/* * Copyright (C) 2015 Jan Pokorsky * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package cz.cas.lib.proarc.common.export.archive; import com.yourmediashelf.fedora.generated.foxml.DatastreamType; import com.yourmediashelf.fedora.generated.foxml.DigitalObject; import cz.cas.lib.proarc.common.device.DeviceRepository; import cz.cas.lib.proarc.common.dublincore.DcStreamEditor; import cz.cas.lib.proarc.common.export.archive.PackageBuilder.MdType; import cz.cas.lib.proarc.common.fedora.DigitalObjectException; import cz.cas.lib.proarc.common.fedora.FedoraObject; import cz.cas.lib.proarc.common.fedora.FoxmlUtils; import cz.cas.lib.proarc.common.fedora.LocalStorage; import cz.cas.lib.proarc.common.fedora.LocalStorage.LocalObject; import cz.cas.lib.proarc.common.fedora.RemoteStorage; import cz.cas.lib.proarc.common.fedora.RemoteStorage.RemoteObject; import cz.cas.lib.proarc.common.fedora.relation.RelationEditor; import cz.cas.lib.proarc.common.mods.ModsStreamEditor; import cz.cas.lib.proarc.common.object.DigitalObjectCrawler; import cz.cas.lib.proarc.common.object.DigitalObjectElement; import cz.cas.lib.proarc.common.object.DigitalObjectHandler; import cz.cas.lib.proarc.common.object.ReadonlyDisseminationHandler; import java.io.File; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import org.w3c.dom.Element; /** * Processes a path of digital objects to build a package. * * @author Jan Pokorsky */ public class ArchiveObjectProcessor { private final DigitalObjectCrawler crawler; private final LocalStorage ls = new LocalStorage(); private PackageBuilder builder; private final File targetFolder; private final HashSet<String> devicePids = new HashSet<String>(); public ArchiveObjectProcessor(DigitalObjectCrawler crawler, File targetFolder) { this.crawler = crawler; this.targetFolder = targetFolder; } /** * Processes a path of digital objects to build a package. * @param objectPath a leaf to root list of objects. * @throws DigitalObjectException a failure */ public void process(List<DigitalObjectElement> objectPath) throws DigitalObjectException { builder = new PackageBuilder(targetFolder); DigitalObjectElement entry = objectPath.get(0); DigitalObjectHandler handler = entry.getHandler(); LocalObject lobj = getLocalObject(handler.getFedoraObject()); builder.prepare(objectPath, lobj); processParents(objectPath); processObject(1, objectPath, lobj); builder.build(); } private void processParents(List<DigitalObjectElement> objectPath) throws DigitalObjectException { for (int i = objectPath.size() - 1; i >= 1 ; i--) { DigitalObjectElement elm = objectPath.get(i); LocalObject elmCache = getLocalObject(elm.getHandler().getFedoraObject()); processDatastreams(1, objectPath.subList(i, objectPath.size()), elmCache, new RelationEditor(elmCache)); } } private void processObject(int siblingIdx, List<DigitalObjectElement> objectPath, LocalObject cache) throws DigitalObjectException { DigitalObjectElement entry = objectPath.get(0); RelationEditor relsEditor = new RelationEditor(cache); processDatastreams(siblingIdx, objectPath, cache, relsEditor); List<String> members = relsEditor.getMembers(); if (!members.isEmpty()) { // read children with single query List<DigitalObjectElement> children = crawler.getChildren(entry.getPid()); processChildren(objectPath, children); } } private void processDatastreams( int siblingIdx, List<DigitalObjectElement> objectPath, LocalObject cache, RelationEditor relsEditor ) throws DigitalObjectException { DigitalObjectElement elm = objectPath.get(0); DigitalObjectElement parentElm = objectPath.size() <= 1 ? null : objectPath.get(1); DigitalObjectHandler handler = elm.getHandler(); builder.addObject(siblingIdx, elm, parentElm); for (DatastreamType dt : cache.getDigitalObject().getDatastream()) { String dsId = dt.getID(); if (ModsStreamEditor.DATASTREAM_ID.equals(dsId)) { // XXX might not be mods! It should rather go to fileGrp. builder.addStreamAsMdSec(siblingIdx, dt, cache.getPid(), elm.getModelId(), MdType.MODS); } else if (DcStreamEditor.DATASTREAM_ID.equals(dsId)) { Element dcElm = dt.getDatastreamVersion().get(0).getXmlContent().getAny().get(0); FoxmlUtils.fixFoxmlDc(dcElm); builder.addStreamAsMdSec(siblingIdx, dt, cache.getPid(), elm.getModelId(), MdType.DC); } else if (RelationEditor.DATASTREAM_ID.equals(dsId)) { processDevice(relsEditor.getDevice(), cache.getPid()); builder.addStreamAsFile(siblingIdx, dt, cache.getPid(), elm.getModelId(), null); } else if (FoxmlUtils.DS_AUDIT_ID.equals(dsId)) { builder.addStreamAsFile(siblingIdx, dt, cache.getPid(), elm.getModelId(), null); } else { builder.addStreamAsFile(siblingIdx, dt, cache.getPid(), elm.getModelId(), handler.dissemination(dsId)); } } builder.addFoxmlAsFile(siblingIdx, elm, cache); } private void processDevice(String devicePid, String objPid) throws DigitalObjectException { if (devicePid == null) { return ; } boolean contains = devicePids.contains(devicePid); if (!contains) { devicePids.add(devicePid); RemoteStorage remoteStorage = RemoteStorage.getInstance(); RemoteObject ro = remoteStorage.find(devicePid); LocalObject cache = getLocalObject(ro); builder.addDevice(cache); final int deviceIdx = devicePids.size(); final String modelId = DeviceRepository.METAMODEL_ID; builder.addFoxmlAsFile(deviceIdx, modelId, cache); DigitalObject dobj = cache.getDigitalObject(); // write dc DatastreamType dcDs = FoxmlUtils.findDatastream(dobj, DcStreamEditor.DATASTREAM_ID); Element dcElm = dcDs.getDatastreamVersion().get(0).getXmlContent().getAny().get(0); FoxmlUtils.fixFoxmlDc(dcElm); builder.addStreamAsMdSec(deviceIdx, dcDs, devicePid, modelId, MdType.DC); // write description builder.addStreamAsFile(deviceIdx, FoxmlUtils.findDatastream(dobj, DeviceRepository.DESCRIPTION_DS_ID), devicePid, modelId, new ReadonlyDisseminationHandler(ro, DeviceRepository.DESCRIPTION_DS_ID)); // write audit builder.addStreamAsFile(deviceIdx, FoxmlUtils.findDatastream(dobj, FoxmlUtils.DS_AUDIT_ID), devicePid, modelId, null); // write rels-ext builder.addStreamAsFile(deviceIdx, FoxmlUtils.findDatastream(dobj, RelationEditor.DATASTREAM_ID), devicePid, modelId, null); } } private void processChildren( List<DigitalObjectElement> objectPath, List<DigitalObjectElement> children ) throws DigitalObjectException { int i = 1; for (DigitalObjectElement child : children) { LocalObject lObj = getLocalObject(child.getHandler().getFedoraObject()); ArrayList<DigitalObjectElement> childPath = new ArrayList<DigitalObjectElement>(objectPath.size() + 1); childPath.add(child); childPath.addAll(objectPath); processObject(i++, childPath, lObj); } } private LocalObject getLocalObject(FedoraObject fo) throws DigitalObjectException { // get FOXML copy and query it locally if (fo instanceof LocalObject) { return (LocalObject) fo; } String foxml = fo.asText(); DigitalObject dobj = FoxmlUtils.unmarshal(foxml, DigitalObject.class); return ls.create(dobj); } }