/*
* Copyright (C) 2013 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.object;
import com.fasterxml.jackson.databind.ObjectMapper;
import cz.cas.lib.proarc.common.config.AppConfiguration;
import cz.cas.lib.proarc.common.config.AppConfigurationFactory;
import cz.cas.lib.proarc.common.dublincore.DcStreamEditor;
import cz.cas.lib.proarc.common.dublincore.DcStreamEditor.DublinCoreRecord;
import cz.cas.lib.proarc.common.dublincore.DcUtils;
import cz.cas.lib.proarc.common.export.desa.DesaServices;
import cz.cas.lib.proarc.common.export.desa.DesaServices.DesaConfiguration;
import cz.cas.lib.proarc.common.fedora.BinaryEditor;
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.XmlStreamEditor;
import cz.cas.lib.proarc.common.fedora.XmlStreamEditor.EditorResult;
import cz.cas.lib.proarc.common.json.JsonUtils;
import cz.cas.lib.proarc.common.object.model.DatastreamEditorType;
import cz.cas.lib.proarc.common.object.model.MetaModel;
import cz.cas.lib.proarc.common.user.Group;
import cz.cas.lib.proarc.common.user.UserProfile;
import cz.cas.lib.proarc.common.user.UserUtil;
import cz.cas.lib.proarc.desa.nomenclature.Nomenclatures;
import cz.cas.lib.proarc.oaidublincore.DcConstants;
import cz.cas.lib.proarc.oaidublincore.ElementType;
import cz.cas.lib.proarc.oaidublincore.OaiDcType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.xml.transform.Source;
/**
* Support of the DER DESA model.
*
* @author Jan Pokorsky
*/
public class DerDesaPlugin implements DigitalObjectPlugin,
HasMetadataHandler<OaiDcType>, HasDisseminationHandler {
/**
* The plugin ID.
*/
public static final String ID = "desa-der";
public static final String MODEL_FOLDER = "model:derFolder";
public static final String MODEL_DOCUMENT = "model:derDocument";
public static final String MODEL_FILE = "model:derFile";
@Override
public String getId() {
return ID;
}
@Override
public <T extends HasDataHandler> T getHandlerProvider(Class<T> type) {
return type.isInstance(this) ? type.cast(this): null;
}
@Override
public Collection<MetaModel> getModel() {
ArrayList<MetaModel> models = new ArrayList<MetaModel>();
models.add(new MetaModel(
MODEL_FOLDER, true, null,
Arrays.asList(new ElementType("Folder", "en"), new ElementType("Složka", "cs")),
DcConstants.NS_OAIDC, "proarc.metadata.editor.dc.derFolder",
this,
EnumSet.of(DatastreamEditorType.MODS, DatastreamEditorType.NOTE,
DatastreamEditorType.CHILDREN, DatastreamEditorType.ATM)
));
models.add(new MetaModel(
MODEL_DOCUMENT, true, null,
Arrays.asList(new ElementType("Document", "en"), new ElementType("Dokument", "cs")),
DcConstants.NS_OAIDC, "proarc.metadata.editor.dc.derDocument",
this,
EnumSet.of(DatastreamEditorType.MODS, DatastreamEditorType.NOTE,
DatastreamEditorType.PARENT, DatastreamEditorType.CHILDREN,
DatastreamEditorType.ATM)
));
models.add(new MetaModel(
MODEL_FILE, null, true,
Arrays.asList(new ElementType("DER Component", "en"), new ElementType("DER Komponenta", "cs")),
DcConstants.NS_OAIDC, "proarc.metadata.editor.dc.derFile",
this,
EnumSet.of(DatastreamEditorType.MODS, DatastreamEditorType.NOTE,
DatastreamEditorType.PARENT,
DatastreamEditorType.MEDIA, DatastreamEditorType.ATM)
));
return models;
}
@Override
public MetadataHandler<OaiDcType> createMetadataHandler(DigitalObjectHandler handler) {
return new DerMetadataHandler(handler);
}
@Override
public DisseminationHandler createDisseminationHandler(String dsId, DigitalObjectHandler handler) {
DefaultDisseminationHandler ddh = new DefaultDisseminationHandler(dsId, handler);
if (BinaryEditor.RAW_ID.equals(dsId)) {
return new DerRawDisseminationHandler(handler, ddh);
} else {
return ddh;
}
}
@Override
public List<ValueMap> getValueMaps(ValueMap.Context context) {
try {
AppConfiguration appConfig = AppConfigurationFactory.getInstance().defaultInstance();
DesaServices desaServices = appConfig.getDesaServices();
DesaConfiguration dc = desaServices.findConfigurationWithModel(MODEL_DOCUMENT, MODEL_FILE, MODEL_FOLDER);
if (dc != null) {
Nomenclatures nomenclatures = desaServices.getNomenclatures(dc, context.getUser());
return desaServices.getValueMap(nomenclatures, ID);
} else {
return Collections.emptyList();
}
} catch (Exception ex) {
throw new IllegalStateException("Cannot init value maps for " + ID + " plugin!", ex);
}
}
static class DerRawDisseminationHandler implements DisseminationHandler {
private final DisseminationHandler defaultHandler;
private final DigitalObjectHandler handler;
public DerRawDisseminationHandler(DigitalObjectHandler handler, DefaultDisseminationHandler defaultHandler) {
this.defaultHandler = defaultHandler;
this.handler = handler;
}
@Override
public Response getDissemination(Request httpRequest) throws DigitalObjectException {
return defaultHandler.getDissemination(httpRequest);
}
@Override
public void setDissemination(DisseminationInput input, String message) throws DigitalObjectException {
defaultHandler.setDissemination(input, message);
MetadataHandler<OaiDcType> metadata = handler.metadata();
DescriptionMetadata<OaiDcType> dm = metadata.getMetadata();
OaiDcType data = dm.getData();
List<ElementType> titles = data.getTitles();
titles.clear();
String filename = input.getFilename();
titles.add(new ElementType(filename, null));
List<ElementType> formats = data.getFormats();
formats.clear();
formats.add(new ElementType(input.getMime().toString(), null));
metadata.setMetadata(dm, message);
}
}
static class DerMetadataHandler implements MetadataHandler<OaiDcType> {
private final DigitalObjectHandler handler;
private final XmlStreamEditor editor;
private final FedoraObject object;
DerMetadataHandler(DigitalObjectHandler handler) {
this.handler = handler;
this.object = handler.getFedoraObject();
this.editor = object.getEditor(FoxmlUtils.inlineProfile(
DESCRIPTION_DATASTREAM_ID, DcConstants.NS_OAIDC, DESCRIPTION_DATASTREAM_LABEL));
}
@Override
public void setMetadata(DescriptionMetadata<OaiDcType> data, String message) throws DigitalObjectException {
OaiDcType dc = data.getData();
if (dc == null) {
dc = createDefautDc();
}
write(dc, data.getTimestamp(), message);
}
@Override
public void setMetadataAsJson(DescriptionMetadata<String> json, String message) throws DigitalObjectException {
String data = json.getData();
OaiDcType dc;
if (data == null) {
dc = createDefautDc();
} else {
ObjectMapper jsMapper = JsonUtils.defaultObjectMapper();
try {
dc = jsMapper.readValue(data, OaiDcType.class);
} catch (Exception ex) {
throw new DigitalObjectException(object.getPid(), null, editor.getProfile().getDsID(), null, ex);
}
}
write(dc, json.getTimestamp(), message);
}
@Override
public void setMetadataAsXml(DescriptionMetadata<String> xml, String message) throws DigitalObjectException {
OaiDcType dc = null;
if (xml.getData() != null) {
dc = DcUtils.unmarshal(xml.getData(), OaiDcType.class);
}
if (dc == null) {
dc = createDefautDc();
}
write(dc, xml.getTimestamp(), message);
}
private OaiDcType createDefautDc() throws DigitalObjectException {
OaiDcType dc = new OaiDcType();
DigitalObjectHandler parent = handler.getParameterParent();
String modelId = handler.relations().getModel();
if (MODEL_FOLDER.equals(modelId)) {
String identifier = getProducerCode() + "_";
dc.getIdentifiers().add(new ElementType(identifier, null));
dc.getTitles().add(new ElementType(identifier + '-', null));
} else if (MODEL_DOCUMENT.equals(modelId)) {
OaiDcType parentDc = getParentDcMetadata(parent);
if (parentDc != null) {
if (!parentDc.getIdentifiers().isEmpty()) {
String identifier = parentDc.getIdentifiers().get(0).getValue();
dc.getIdentifiers().add(new ElementType(identifier + '-', null));
dc.getSubjects().add(new ElementType(identifier, null));
}
if (!parentDc.getTitles().isEmpty()) {
String title = parentDc.getTitles().get(0).getValue()
+ '-';
dc.getTitles().add(new ElementType(title, null));
}
if (!parentDc.getTypes().isEmpty()) {
String type = parentDc.getTypes().get(0).getValue();
dc.getTypes().add(new ElementType(type, null));
}
}
}
return dc;
}
private void write(OaiDcType dc, long timestamp, String message) throws DigitalObjectException {
EditorResult result = editor.createResult();
// DO NOT include schemaLocation. Fedora validator does not accept it.
DcUtils.marshal(result, dc, false);
editor.write(result, timestamp, message);
// Label: spec requires identifier + title
StringBuilder label = new StringBuilder();
if (!dc.getIdentifiers().isEmpty()) {
label.append(dc.getIdentifiers().get(0).getValue());
}
if (!dc.getTitles().isEmpty()) {
if (label.length() > 0) {
label.append(' ');
}
label.append(dc.getTitles().get(0).getValue());
}
String objLabel = label.toString().trim();
object.setLabel(objLabel.isEmpty() ? "?" : objLabel);
// DC
DcStreamEditor dcEditor = handler.objectMetadata();
DublinCoreRecord dcr = dcEditor.read();
dcr.setDc(dc);
dcEditor.write(handler, dcr, message);
}
@Override
public DescriptionMetadata<OaiDcType> getMetadata() throws DigitalObjectException {
Source src = getDataAsSource();
DescriptionMetadata<OaiDcType> dm = new DescriptionMetadata<OaiDcType>();
dm.setPid(object.getPid());
dm.setTimestamp(editor.getLastModified());
// dm.setEditor(editorId);
OaiDcType dc = DcUtils.unmarshal(src, OaiDcType.class);
dm.setData(dc);
return dm;
}
@Override
public DescriptionMetadata<String> getMetadataAsXml() throws DigitalObjectException {
DescriptionMetadata<OaiDcType> dm = getMetadata();
OaiDcType dc = dm.getData();
DescriptionMetadata<String> result = new DescriptionMetadata<String>();
result.setPid(dm.getPid());
result.setBatchId(dm.getBatchId());
String xml = DcUtils.toXml(dc, true);
result.setData(xml);
return result;
}
@Override
@SuppressWarnings("unchecked")
public <O> DescriptionMetadata<O> getMetadataAsJsonObject(String mapping) throws DigitalObjectException {
return (DescriptionMetadata<O>) getMetadata();
}
private Source getDataAsSource() throws DigitalObjectException {
Source src = editor.read();
if (src == null) {
// it should never arise; it would need to create datastream again with default data
throw new DigitalObjectException(object.getPid(),
null, DESCRIPTION_DATASTREAM_ID,
"datastream not initialized!", null);
}
return src;
}
/**
* Gets parent DC.
* @param parent parent to query
* @return the description metadata or {@code null}
* @throws DigitalObjectException failure
*/
private static OaiDcType getParentDcMetadata(DigitalObjectHandler parent) throws DigitalObjectException {
if (parent != null) {
MetadataHandler<Object> metadataHandler = parent.metadata();
if (metadataHandler != null) {
Object metadata = metadataHandler.getMetadata().getData();
if (metadata instanceof OaiDcType) {
return (OaiDcType) metadata;
}
}
}
return null;
}
private String getProducerCode() {
String producerCode = "";
UserProfile user = handler.getParameterUser();
if (user == null) {
return producerCode;
}
Integer defaultGroupId = user.getDefaultGroup();
if (defaultGroupId != null) {
Group group = UserUtil.getDefaultManger().findGroup(defaultGroupId);
if (group != null && group.getRemoteName() != null) {
producerCode = group.getRemoteName();
}
}
return producerCode;
}
}
}