/*
* Copyright (C) 2012 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.fedora.relation;
import com.yourmediashelf.fedora.generated.management.DatastreamProfile;
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.RemoteStorage.RemoteObject;
import cz.cas.lib.proarc.common.fedora.XmlStreamEditor;
import cz.cas.lib.proarc.common.fedora.XmlStreamEditor.EditorResult;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.xml.transform.Source;
import org.w3c.dom.Element;
/**
* RDF relations editor.
*
* @see <a href='https://wiki.duraspace.org/display/FEDORA35/Digital+Object+Relationships'>
* Digital Object Relationships</a>
* @see <a href='https://wiki.duraspace.org/display/FEDORA35/Resource+Index'>
* Resource Index</a>
* @author Jan Pokorsky
*/
public final class RelationEditor {
public static final String DATASTREAM_ID = "RELS-EXT";
public static final String DATASTREAM_FORMAT_URI = "info:fedora/fedora-system:FedoraRELSExt-1.0";
public static final String DATASTREAM_LABEL = "RDF Statements about this object";
private final XmlStreamEditor editor;
private final FedoraObject fobject;
private Rdf relsExt;
public RelationEditor(FedoraObject fobject) {
this.fobject = fobject;
this.editor = fobject.getEditor(profile());
}
public static DatastreamProfile profile() {
return FoxmlUtils.inlineProfile(DATASTREAM_ID, DATASTREAM_FORMAT_URI, DATASTREAM_LABEL);
}
public long getLastModified() throws DigitalObjectException {
return editor.getLastModified();
}
/**
* @param model PID of the model
*/
public void setModel(String model) throws DigitalObjectException {
Rdf rdf = getRdf();
rdf.getDescription().setModel(RdfRelation.fromPid(model));
}
/**
* @return PID of the model
*/
public String getModel() throws DigitalObjectException {
Rdf rdf = getRdf();
return RdfRelation.toPid(rdf.getDescription().getModel());
}
/**
* @param device PID of the device
*/
public void setDevice(String device) throws DigitalObjectException {
Rdf rdf = getRdf();
rdf.getDescription().setDevice(RdfRelation.fromPid(device));
}
/**
* @return PID of the device
*/
public String getDevice() throws DigitalObjectException {
Rdf rdf = getRdf();
return RdfRelation.toPid(rdf.getDescription().getDevice());
}
/**
* @param filename filename of the imported digital content
*/
public void setImportFile(String filename) throws DigitalObjectException {
getRdf().getDescription().setImportFile(filename);
}
/**
*
* @return filename of the imported digital content
*/
public String getImportFile() throws DigitalObjectException {
return getRdf().getDescription().getImportFile();
}
/**
* Sets some identifier of the export action.
* @param result e.g. SIP ID or folder
* @throws DigitalObjectException failure
*/
public void setExportResult(String result) throws DigitalObjectException {
getRdf().getDescription().setHasExport(result);
}
/**
* Gets an export identifier.
* @return the identifier
* @throws DigitalObjectException failure
*/
public String getExportResult() throws DigitalObjectException {
return getRdf().getDescription().getHasExport();
}
/**
* Relations defining object hierarchy graph.
*
* @return list of PIDs.
*/
public List<String> getMembers() throws DigitalObjectException {
Rdf rdf = getRdf();
return relationAsPid(rdf.getDescription().getMemberRelations());
}
/**
* Sets relations defining object hierarchy graph.
* Relations should be ordered for each model.
*
* @param members list of PIDs
*/
public void setMembers(List<String> members) throws DigitalObjectException {
Rdf rdf = getRdf();
List<RdfRelation> oldies = rdf.getDescription().getMemberRelations();
oldies.clear();
oldies.addAll(pidAsRelation(members));
}
/**
* Relations defining the reverse object hierarchy graph (isMemberOf).
*
* @return list of PIDs of objects, where the object is a member.
*/
public Collection<String> getMembership() throws DigitalObjectException {
Rdf rdf = getRdf();
return relationAsPid(rdf.getDescription().getMembershipRelations());
}
/**
* Sets relations defining the reverse object hierarchy graph (isMemberOf).
*
* @param subjects list of PIDs of objects, where the object is a member.
*/
public void setMembership(Collection<String> subjects) throws DigitalObjectException {
Rdf rdf = getRdf();
List<RdfRelation> oldies = rdf.getDescription().getMembershipRelations();
oldies.clear();
oldies.addAll(pidAsRelation(subjects));
}
/**
* Relations defining ownership of the object.
*
* @return list of PIDs of owners
*/
public Collection<String> getOwners() throws DigitalObjectException {
Rdf rdf = getRdf();
return relationAsPid(rdf.getDescription().getOwners());
}
/**
* Sets relations defining the reverse object hierarchy graph (isMemberOf).
*
* @param owners list of PIDs of objects, where the object is a member.
*/
public void setOwners(Collection<String> owners) throws DigitalObjectException {
Rdf rdf = getRdf();
List<RdfRelation> oldies = rdf.getDescription().getOwners();
oldies.clear();
oldies.addAll(pidAsRelation(owners));
}
/**
* Gets unrecognized relations.
*
* @return list of relations
*/
public List<Element> getRelations() throws DigitalObjectException {
Rdf rdf = getRdf();
List<Element> elms = rdf.getDescription().getRelations();
return new ArrayList<Element>(elms);
}
/**
* Sets relations unrecognized by RelationEditor.
* <b>Do not use for members, model, ...</b>
*
* @param elms list of custom relations
*/
public void setRelations(List<Element> elms) throws DigitalObjectException {
Rdf rdf = getRdf();
List<Element> relations = rdf.getDescription().getRelations();
relations.clear();
relations.addAll(elms);
}
/**
* Replaces all relations.
* @param rdf the relations
*/
public void setRdf(Rdf rdf) {
this.relsExt = rdf;
}
/**
* Prepares updates for {@link FedoraObject#flush() }
* @param timestamp timestamp
*/
public void write(long timestamp, String message) throws DigitalObjectException {
EditorResult result = editor.createResult();
Relations.marshal(result, relsExt, false);
editor.write(result, timestamp, message);
}
private Rdf getRdf() throws DigitalObjectException {
if (relsExt != null) {
return relsExt;
}
Source source = editor.read();
if (source == null) {
if (fobject instanceof RemoteObject) {
// it should never arise; broken Fedora?
throw new DigitalObjectException(fobject.getPid(), "missing RELS-EXT!");
}
relsExt = new Rdf(fobject.getPid());
} else {
relsExt = Relations.unmarshal(source, Rdf.class);
}
return relsExt;
}
/**
* Converts a list of RDF relation IDS to the list of PIDs.
*/
public static List<String> relationAsPid(List<RdfRelation> relations) {
ArrayList<String> result = new ArrayList<String>(relations.size());
for (RdfRelation relation : relations) {
result.add(RdfRelation.toPid(relation));
}
return result;
}
private static List<RdfRelation> pidAsRelation(Collection<String> pids) {
ArrayList<RdfRelation> relations = new ArrayList<RdfRelation>(pids.size());
for (String pid : pids) {
relations.add(RdfRelation.fromPid(pid));
}
return relations;
}
}