/*
* Copyright (C) 2014 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.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.DerDesaPlugin.DerMetadataHandler;
import cz.cas.lib.proarc.common.object.DerDesaPlugin.DerRawDisseminationHandler;
import cz.cas.lib.proarc.common.object.model.DatastreamEditorType;
import cz.cas.lib.proarc.common.object.model.MetaModel;
import cz.cas.lib.proarc.desa.nomenclature.Nomenclatures;
import cz.cas.lib.proarc.nsesss2.Dokument;
import cz.cas.lib.proarc.nsesss2.NsesssConstants;
import cz.cas.lib.proarc.nsesss2.NsesssUtils;
import cz.cas.lib.proarc.nsesss2.Spis;
import cz.cas.lib.proarc.nsesss2.TEvidencniUdajeDokumentu;
import cz.cas.lib.proarc.nsesss2.TEvidencniUdajeSpisu;
import cz.cas.lib.proarc.nsesss2.TIdentifikace;
import cz.cas.lib.proarc.nsesss2.TIdentifikator;
import cz.cas.lib.proarc.nsesss2.TPopis;
import cz.cas.lib.proarc.nsesss2.mapping.NsesssMapper;
import cz.cas.lib.proarc.nsesss2.mapping.NsesssMapper.SubjektExterni;
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.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.transform.Source;
/**
* Support of the DES DESA model. The plugin is used to provide list of all
* DES models but each model gets custom subclass to handle different data type
* but to reuse common stuff.
*
* @author Jan Pokorsky
*/
public class DesDesaPlugin implements DigitalObjectPlugin {
public static final String ID = "desa-des";
/** {@link Spis} */
public static final String MODEL_FOLDER = "model:desFolder";
/** {@link Dokument} */
public static final String MODEL_INTERNAL_RECORD = "model:desInternalRecord";
/** {@link Dokument} */
public static final String MODEL_EXTERNAL_RECORD = "model:desExternalRecord";
public static final String MODEL_FILE = "model:desFile";
@Override
public String getId() {
return ID;
}
@Override
public Collection<MetaModel> getModel() {
ArrayList<MetaModel> models = new ArrayList<MetaModel>();
DesNsesssDesaPlugin nsesssPlugin = new DesNsesssDesaPlugin();
models.add(new MetaModel(
MODEL_FOLDER, true, null,
Arrays.asList(new ElementType("File", "en"), new ElementType("Spis", "cs")),
NsesssConstants.NS,
"proarc.metadata.editor.nsesss.desFolder",
nsesssPlugin,
EnumSet.of(DatastreamEditorType.MODS, DatastreamEditorType.NOTE,
DatastreamEditorType.CHILDREN, DatastreamEditorType.ATM)
));
models.add(new MetaModel(
MODEL_INTERNAL_RECORD, true, null,
Arrays.asList(new ElementType("Internal Record", "en"), new ElementType("Vlastní dokument", "cs")),
NsesssConstants.NS,
"proarc.metadata.editor.nsesss.desInternalRecord",
nsesssPlugin,
EnumSet.of(DatastreamEditorType.MODS, DatastreamEditorType.NOTE,
DatastreamEditorType.PARENT, DatastreamEditorType.CHILDREN,
DatastreamEditorType.ATM)
));
models.add(new MetaModel(
MODEL_EXTERNAL_RECORD, true, null,
Arrays.asList(new ElementType("External Record", "en"), new ElementType("Doručený dokument", "cs")),
NsesssConstants.NS,
"proarc.metadata.editor.nsesss.desExternalRecord",
nsesssPlugin,
EnumSet.of(DatastreamEditorType.MODS, DatastreamEditorType.NOTE,
DatastreamEditorType.PARENT, DatastreamEditorType.CHILDREN,
DatastreamEditorType.ATM)
));
models.add(new MetaModel(
MODEL_FILE, null, true,
Arrays.asList(new ElementType("DES Component", "en"), new ElementType("DES Komponenta", "cs")),
DcConstants.NS_OAIDC, "proarc.metadata.editor.dc.desFile",
new DesDcDesaPlugin(),
EnumSet.of(DatastreamEditorType.MODS, DatastreamEditorType.NOTE,
DatastreamEditorType.PARENT,
DatastreamEditorType.MEDIA, DatastreamEditorType.ATM)
));
return models;
}
@Override
public <T extends HasDataHandler> T getHandlerProvider(Class<T> type) {
return type.isInstance(this) ? type.cast(this): null;
}
@Override
public List<ValueMap> getValueMaps(ValueMap.Context context) {
try {
AppConfiguration appConfig = AppConfigurationFactory.getInstance().defaultInstance();
DesaServices desaServices = appConfig.getDesaServices();
DesaConfiguration dc = desaServices.findConfigurationWithModel(
MODEL_EXTERNAL_RECORD, MODEL_FILE, MODEL_FOLDER, MODEL_INTERNAL_RECORD);
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);
}
}
/**
* Handles NSESSS2 format.
*/
private static class DesNsesssDesaPlugin extends DesDesaPlugin implements HasMetadataHandler<Object> {
@Override
public MetadataHandler<Object> createMetadataHandler(DigitalObjectHandler handler) {
return new DesMetadataHandler(handler);
}
}
/**
* Handles DC format.
*/
private static class DesDcDesaPlugin extends DesDesaPlugin implements HasMetadataHandler<OaiDcType>, HasDisseminationHandler {
@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);
}
return ddh;
}
@Override
public MetadataHandler<OaiDcType> createMetadataHandler(DigitalObjectHandler handler) {
return new DerMetadataHandler(handler);
}
}
public static class DesMetadataHandler implements MetadataHandler<Object> {
private final DigitalObjectHandler handler;
private final FedoraObject fobject;
private final XmlStreamEditor editor;
public DesMetadataHandler(DigitalObjectHandler handler) {
this.handler = handler;
this.fobject = handler.getFedoraObject();
this.editor = fobject.getEditor(FoxmlUtils.inlineProfile(
DESCRIPTION_DATASTREAM_ID, NsesssConstants.NS, DESCRIPTION_DATASTREAM_LABEL));
}
@Override
public void setMetadata(DescriptionMetadata<Object> data, String message) throws DigitalObjectException {
Object metadata = data.getData();
if (metadata == null) {
String modelId = handler.relations().getModel();
metadata = createDefaultMetadata(modelId);
}
write(metadata, data.getTimestamp(), message);
}
@Override
public void setMetadataAsJson(DescriptionMetadata<String> jsonData, String message) throws DigitalObjectException {
String json = jsonData.getData();
Object metadata;
if (json == null) {
String modelId = handler.relations().getModel();
metadata = createDefaultMetadata(modelId);
} else {
ObjectMapper jsMapper = JsonUtils.defaultObjectMapper();
try {
DesObjectWrapper wrapper = jsMapper.readValue(json, DesObjectWrapper.class);
metadata = wrapper.getSpis() != null
? mapFromJson(wrapper.getSpis())
: mapFromJson(wrapper.getDokument());
} catch (Exception ex) {
throw new DigitalObjectException(fobject.getPid(), null, editor.getProfile().getDsID(), null, ex);
}
}
write(metadata, jsonData.getTimestamp(), message);
}
@Override
public void setMetadataAsXml(DescriptionMetadata<String> xmlData, String message) throws DigitalObjectException {
String xml = xmlData.getData();
Object metadata;
if (xml == null) {
String modelId = handler.relations().getModel();
metadata = createDefaultMetadata(modelId);
} else {
metadata = NsesssUtils.unmarshal(xml, getType());
}
write(metadata, xmlData.getTimestamp(), message);
}
@Override
public DescriptionMetadata<Object> getMetadata() throws DigitalObjectException {
Source src = editor.read();
if (src == null) {
// return null or default?
throw new DigitalObjectException(fobject.getPid(),
null, DESCRIPTION_DATASTREAM_ID,
"datastream not initialized!", null);
}
Object metadata = NsesssUtils.unmarshal(src, getType());
DescriptionMetadata<Object> dm = new DescriptionMetadata<Object>();
dm.setPid(fobject.getPid());
dm.setTimestamp(editor.getLastModified());
// dm.setEditor(editorId);
dm.setData(metadata);
return dm;
}
@Override
public DescriptionMetadata<String> getMetadataAsXml() throws DigitalObjectException {
DescriptionMetadata<Object> dm = getMetadata();
Object metadata = dm.getData();
DescriptionMetadata<String> result = new DescriptionMetadata<String>();
result.setPid(dm.getPid());
result.setBatchId(dm.getBatchId());
String xml = NsesssUtils.toXml(metadata, true);
result.setData(xml);
return result;
}
@Override
@SuppressWarnings("unchecked")
public <O> DescriptionMetadata<O> getMetadataAsJsonObject(String mappingId) throws DigitalObjectException {
DescriptionMetadata<Object> dm = getMetadata();
Object metadata = dm.getData();
if (metadata instanceof Spis) {
dm.setData(new DesObjectWrapper((Spis) metadata));
} else if (metadata instanceof Dokument) {
dm.setData(new DesObjectWrapper(mapToJson((Dokument) metadata)));
}
return (DescriptionMetadata<O>) dm;
}
public static Dokument mapToJson(Dokument d) {
NsesssMapper mapper = new NsesssMapper();
d = mapper.replaceTSubjektExterni(d);
return d;
}
/**
* Gets unique ID for NSESSS object.
* @return ID
*/
private String nsesssId() {
// prefix uuid to comply with NCName syntax; cannot start with a number
return "ID_" + FoxmlUtils.pidAsUuid(fobject.getPid());
}
public Spis mapFromJson(Spis s) {
s.setID(nsesssId());
NsesssMapper mapper = new NsesssMapper();
mapper.fillZdroj(s);
mapper.fillDisposalDate(s);
return s;
}
public Dokument mapFromJson(Dokument d) {
d.setID(nsesssId());
NsesssMapper mapper = new NsesssMapper();
mapper.fillZdroj(d);
mapper.fillDisposalDate(d);
return d;
}
private Class<?> getType() throws DigitalObjectException {
String modelId = handler.relations().getModel();
if (MODEL_FOLDER.equals(modelId)) {
return Spis.class;
} else if (MODEL_INTERNAL_RECORD.equals(modelId)) {
return Dokument.class;
} else if (MODEL_EXTERNAL_RECORD.equals(modelId)) {
return Dokument.class;
}
throw new DigitalObjectException(fobject.getPid(), null,
DESCRIPTION_DATASTREAM_ID, "Unknown model: " + modelId, null);
}
private Object createDefaultMetadata(String modelId) throws DigitalObjectException {
NsesssMapper mapper = new NsesssMapper();
if (MODEL_FOLDER.equals(modelId)) {
return mapper.fillDefaults(NsesssUtils.defaultSpis(), nsesssId());
} else if (MODEL_INTERNAL_RECORD.equals(modelId)) {
return mapper.fillDefaults(NsesssUtils.defaultInternalDokument(), true, nsesssId());
} else if (MODEL_EXTERNAL_RECORD.equals(modelId)) {
return mapper.fillDefaults(NsesssUtils.defaultExternalDocument(), false, nsesssId());
}
throw new DigitalObjectException(fobject.getPid(), null,
DESCRIPTION_DATASTREAM_ID, "Unknown model: " + modelId, null);
}
private void write(Object metadata, long timestamp, String message) throws DigitalObjectException {
// DESCRIPTION stream
EditorResult result = editor.createResult();
NsesssUtils.marshal(result, metadata, false);
editor.write(result, timestamp, message);
if (metadata instanceof Spis) {
writeSpis((Spis) metadata, message);
} else if (metadata instanceof Dokument) {
writeDokument((Dokument) metadata, message);
} else {
throw new UnsupportedOperationException();
}
}
private void writeSpis(Spis record, String message) throws DigitalObjectException {
String objectId = null;
//LABEL
String label = null;
TEvidencniUdajeSpisu evidencniUdaje = record.getEvidencniUdaje();
if (evidencniUdaje != null) {
label = findIdentifier(evidencniUdaje.getIdentifikace());
}
if (label == null || label.isEmpty()) {
label = "?";
} else {
objectId = label;
}
fobject.setLabel(label);
//DC
DcStreamEditor dcEditor = handler.objectMetadata();
DublinCoreRecord dcr = dcEditor.read();
OaiDcType dc = new OaiDcType();
if (objectId != null) {
dc.getIdentifiers().add(new ElementType(objectId, null));
}
String title = findSpisNazev(record);
if (title != null && title.isEmpty()) {
dc.getTitles().add(new ElementType(title, null));
}
dcr.setDc(dc);
dcEditor.write(handler, dcr, message);
}
private void writeDokument(Dokument document, String message) throws DigitalObjectException {
String objectId = null;
//LABEL
String label = null;
TEvidencniUdajeDokumentu evidencniUdaje = document.getEvidencniUdaje();
if (evidencniUdaje != null) {
label = findIdentifier(evidencniUdaje.getIdentifikace());
}
if (label == null || label.isEmpty()) {
label = "?";
} else {
objectId = label;
}
fobject.setLabel(label);
//DC
DcStreamEditor dcEditor = handler.objectMetadata();
DublinCoreRecord dcr = dcEditor.read();
OaiDcType dc = new OaiDcType();
if (objectId != null) {
dc.getIdentifiers().add(new ElementType(objectId, null));
}
String title = findDokumentNazev(document);
if (title != null && title.isEmpty()) {
dc.getTitles().add(new ElementType(title, null));
}
dcr.setDc(dc);
dcEditor.write(handler, dcr, message);
}
private static String findSpisNazev(Spis spis) {
TEvidencniUdajeSpisu evidencniUdaje = spis.getEvidencniUdaje();
if (evidencniUdaje != null) {
TPopis popis = evidencniUdaje.getPopis();
if (popis != null) {
String nazev = popis.getNazev();
return nazev;
}
}
return null;
}
private static String findDokumentNazev(Dokument d) {
TEvidencniUdajeDokumentu evidencniUdaje = d.getEvidencniUdaje();
if (evidencniUdaje != null) {
TPopis popis = evidencniUdaje.getPopis();
if (popis != null) {
String nazev = popis.getNazev();
return nazev;
}
}
return null;
}
private static String findIdentifier(TIdentifikace tIdentifikace) {
if (tIdentifikace != null) {
List<TIdentifikator> identifikator = tIdentifikace.getIdentifikator();
if (!identifikator.isEmpty()) {
return identifikator.get(0).getValue();
}
}
return null;
}
}
/** The helper to serialize/deserialize generic NSESSS2 in JSON format. */
@XmlSeeAlso(value = SubjektExterni.class)
public static class DesObjectWrapper {
@XmlElement(name = "Spis")
private Spis spis;
@XmlElement(name = "Dokument")
private Dokument dokument;
public DesObjectWrapper() {
}
public DesObjectWrapper(Dokument dokument) {
this.dokument = dokument;
}
public DesObjectWrapper(Spis spis) {
this.spis = spis;
}
public Spis getSpis() {
return spis;
}
public void setSpis(Spis spis) {
this.spis = spis;
}
public Dokument getDokument() {
return dokument;
}
public void setDokument(Dokument dokument) {
this.dokument = dokument;
}
}
}