/*
* 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.mods.custom;
import cz.cas.lib.proarc.common.mods.custom.ArrayMapper.ArrayItem;
import cz.cas.lib.proarc.common.mods.custom.ArrayMapper.ItemMapper;
import cz.cas.lib.proarc.common.mods.custom.OriginInfoMapper.PublisherItem.Role;
import cz.cas.lib.proarc.mods.DateDefinition;
import cz.cas.lib.proarc.mods.IssuanceDefinition;
import cz.cas.lib.proarc.mods.ModsDefinition;
import cz.cas.lib.proarc.mods.ObjectFactory;
import cz.cas.lib.proarc.mods.OriginInfoDefinition;
import cz.cas.lib.proarc.mods.PlaceDefinition;
import cz.cas.lib.proarc.mods.PlaceTermDefinition;
import cz.cas.lib.proarc.mods.StringPlusLanguagePlusAuthority;
import cz.cas.lib.proarc.mods.StringPlusLanguagePlusSupplied;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlElement;
/**
* Maps OriginInfoType to publishers, printers and frequency item.
*
* Kramerius 4 defines frequency as an independent originInfo element.
* <p/>Aleph defines frequency inside the publisher's originInfo element.
*
* <p/>For now the implementation supports only Kramerius 4 layout.
*
* {@code mods/originInfo[@transliteration == null]/frequency}
*
* @author Jan Pokorsky
*/
final class OriginInfoMapper {
/**
* @see <a href='http://www.loc.gov/standards/mods/userguide/origininfo.html#issuance'>Guidelines for Use</a>
*/
public static final String ISSUANCE_CONTINUING = IssuanceDefinition.CONTINUING.value();
public static final String ISSUANCE_MONOGRAPHIC = IssuanceDefinition.MONOGRAPHIC.value();
private ArrayMapper<OriginInfoDefinition, OriginInfoItem> originsMap =
new ArrayMapper<>(new OriginInfoItemMapper());
public List<OriginInfoItem> map(ModsDefinition mods) {
List<OriginInfoDefinition> origins = mods.getOriginInfo();
return originsMap.map(origins);
}
public ModsDefinition map(ModsDefinition mods, List<PublisherItem> publishers, List<PublisherItem> printers, String issuance) {
// useful for monographs without frequencies
return mapImpl(mods, publishers, printers, null, issuance);
}
public ModsDefinition map(ModsDefinition mods, List<PublisherItem> publishers, List<PublisherItem> printers, List<String> frequencies, String issuance) {
return mapImpl(mods, publishers, printers, MapperUtils.noNull(frequencies), issuance);
}
private ModsDefinition mapImpl(ModsDefinition mods, List<PublisherItem> publishers, List<PublisherItem> printers, List<String> frequencies, String issuance) {
List<OriginInfoItem> oldItems = map(mods);
List<PublisherItem> others = filter(oldItems, true, Role.OTHER);
List<OriginInfoItem> origins = MapperUtils.<OriginInfoItem>mergeList(
cast(MapperUtils.noNull(publishers), Role.PUBLISHER),
cast(MapperUtils.noNull(printers), Role.PRINTER),
others
);
if (frequencies != null || issuance != null) {
PeriodicityItem periodicity = MapperUtils.findFirst(oldItems, PeriodicityItem.class);
if (periodicity == null) {
periodicity = new PeriodicityItem();
}
periodicity.setFrequencies(frequencies);
periodicity.setIssuance(issuance);
periodicity.ignoreMapping = false;
origins.add(0, periodicity);
}
return map(mods, origins);
}
public ModsDefinition map(ModsDefinition mods, List<OriginInfoItem> origins) {
List<OriginInfoDefinition> oldies = mods.getOriginInfo();
List<OriginInfoDefinition> news = originsMap.map(origins, oldies);
oldies.clear();
oldies.addAll(news);
return mods;
}
public static List<PublisherItem> filter(List<? extends OriginInfoItem> origins, boolean include, Role first, Role... rest) {
return filter(origins, EnumSet.of(first, rest), include);
}
public static List<PublisherItem> filter(List<? extends OriginInfoItem> origins, Set<Role> filter, boolean include) {
List<PublisherItem> result = new ArrayList<>();
for (OriginInfoItem origin : origins) {
if (!(origin instanceof PublisherItem)) {
continue;
}
PublisherItem publisher = (PublisherItem) origin;
boolean contains = filter.contains(publisher.getRole());
if ((include == true && include == contains) || (include == false && include == contains)) {
result.add(publisher);
}
}
return result;
}
public static List<String> getFreqencies(List<? extends OriginInfoItem> origins) {
PeriodicityItem item = MapperUtils.findFirst(origins, PeriodicityItem.class);
return item != null ? item.getFrequencies() : Collections.<String>emptyList();
}
public static String getIssuance(List<? extends OriginInfoItem> origins) {
PeriodicityItem item = MapperUtils.findFirst(origins, PeriodicityItem.class);
return item != null ? item.getIssuance() : null;
}
/**
* Ensures that items belongs to role.
*/
private static List<PublisherItem> cast(List<PublisherItem> items, Role role) {
for (PublisherItem item : items) {
item.setRole(role);
}
return items;
}
private static final class OriginInfoItemMapper implements ItemMapper<OriginInfoDefinition, OriginInfoItem> {
private ObjectFactory factory = new ObjectFactory();
@Override
public OriginInfoItem map(OriginInfoDefinition source) {
String transliteration = source.getTransliteration();
StringPlusLanguagePlusSupplied publisher = getPublisher(factory, source.getPublisher(), false);
if (transliteration == null && publisher == null) {
return readPeriodicity(source);
} else {
return readPublisher(source);
}
}
private PeriodicityItem readPeriodicity(OriginInfoDefinition source) {
PeriodicityItem result = new PeriodicityItem();
List<String> frequencies = source.getFrequency().stream()
.map(spl -> spl.getValue())
.collect(Collectors.toList());
Optional<IssuanceDefinition> issuance = source.getIssuance().stream().findFirst();
result.setIssuance(issuance.isPresent()? issuance.get().value() : null);
result.setFrequencies(frequencies);
return result;
}
private PublisherItem readPublisher(OriginInfoDefinition source) {
PublisherItem result = new PublisherItem();
Role role = Role.fromText(source.getTransliteration());
result.setRole(role);
if (role == Role.OTHER) {
return result;
}
Optional<StringPlusLanguagePlusSupplied> publisher = source.getPublisher().stream().findFirst();
result.setName(publisher.isPresent() ? publisher.get().getValue() : null);
DateDefinition date = getDate(factory, source, role, false);
result.setDate(date == null ? null : date.getValue());
PlaceTermDefinition place = getPlace(factory, source, false);
result.setPlace(place == null ? null : place.getValue());
return result;
}
@Override
public OriginInfoDefinition map(OriginInfoItem item, OriginInfoDefinition origin) {
if (item instanceof PublisherItem) {
origin = writePublisher((PublisherItem) item, origin);
} else if (item instanceof PeriodicityItem) {
origin = writePeriodicity((PeriodicityItem) item, origin);
}
return origin;
}
private OriginInfoDefinition writePeriodicity(PeriodicityItem item, OriginInfoDefinition origin) {
if (item.ignoreMapping) {
return origin;
}
if (origin == null) {
origin = factory.createOriginInfoDefinition();
}
origin.getFrequency().clear();
origin.getIssuance().clear();
for (String frequency : MapperUtils.noNull(item.getFrequencies())) {
StringPlusLanguagePlusAuthority spa = factory.createStringPlusLanguagePlusAuthority();
spa.setValue(frequency);
origin.getFrequency().add(spa);
}
origin.getIssuance().add(IssuanceDefinition.fromValue(item.getIssuance()));
return origin;
}
private OriginInfoDefinition writePublisher(PublisherItem item, OriginInfoDefinition origin) {
OriginInfoDefinition source = origin;
Role role = item.getRole();
if (role == Role.OTHER) {
return origin;
}
if (origin == null) {
source = factory.createOriginInfoDefinition();
source.setTransliteration(role.getText());
}
if (item.getName() != null) {
StringPlusLanguagePlusSupplied publisher = getPublisher(factory, source.getPublisher(), true);
publisher.setValue(item.getName());
} else {
StringPlusLanguagePlusSupplied publisher = getPublisher(factory, source.getPublisher(), false);
source.getPublisher().remove(publisher);
}
if (item.getPlace() != null) {
getPlace(factory, source, true).setValue(item.getPlace());
} else {
PlaceTermDefinition place = getPlace(factory, source, false);
if (place != null) {
place.setValue(null);
}
}
if (item.getDate() != null) {
getDate(factory, source, role, true).setValue(item.getDate());
} else {
DateDefinition date = getDate(factory, source, role, false);
if (date != null) {
date.setValue(null);
}
}
return source;
}
private static StringPlusLanguagePlusSupplied getPublisher(ObjectFactory factory, List<StringPlusLanguagePlusSupplied> publishers, boolean create) {
StringPlusLanguagePlusSupplied publisher = publishers.stream().findFirst().orElse(null);
if (create && publisher == null) {
publisher = factory.createStringPlusLanguagePlusSupplied();
publishers.add(publisher);
}
return publisher;
}
private static DateDefinition getDate(ObjectFactory factory, OriginInfoDefinition oid, Role role, boolean create) {
List<DateDefinition> dates = role == Role.PRINTER ? oid.getDateCreated() : oid.getDateIssued();
DateDefinition date = dates.stream().findFirst().orElse(null);
if (create && date == null) {
date = factory.createDateDefinition();
dates.add(date);
}
return date;
}
private static PlaceTermDefinition getPlace(ObjectFactory factory, OriginInfoDefinition oid, boolean create) {
PlaceDefinition place = oid.getPlace().stream().findFirst().orElse(null);
if (place == null) {
if (create) {
place = factory.createPlaceDefinition();
oid.getPlace().add(place);
} else {
return null;
}
}
if (place.getPlaceTerm().isEmpty()) {
if (create) {
place.getPlaceTerm().add(factory.createPlaceTermDefinition());
} else {
return null;
}
}
return place.getPlaceTerm().get(0);
}
}
@javax.xml.bind.annotation.XmlAccessorType(XmlAccessType.FIELD)
public static final class PublisherItem implements OriginInfoItem {
@XmlElement(name = ModsConstants.FIELD_PRINTER_PUBLISHER_NAME)
private String name;
@XmlElement(name = ModsConstants.FIELD_PRINTER_PUBLISHER_DATE)
private String date;
@XmlElement(name = ModsConstants.FIELD_PRINTER_PUBLISHER_PLACE)
private String place;
private transient Role role;
private Integer index;
public PublisherItem() {
}
PublisherItem(Integer index, Role role, String name, String date, String place) {
this.name = name;
this.date = date;
this.place = place;
this.role = role;
this.index = index;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = MapperUtils.normalize(date);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = MapperUtils.normalize(name);
}
public String getPlace() {
return place;
}
public void setPlace(String place) {
this.place = MapperUtils.normalize(place);
}
public Role getRole() {
return role;
}
public void setRole(Role role) {
this.role = role;
}
@Override
public Integer getArrayIndex() {
return index;
}
@Override
public void setArrayIndex(Integer index) {
this.index = index;
}
@Override
public String toString() {
return String.format("PublisherItem{index: %s, name: %s, date: %s, place: %s, role: %s}",
index, name, date, place, role);
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final PublisherItem other = (PublisherItem) obj;
if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
return false;
}
if ((this.date == null) ? (other.date != null) : !this.date.equals(other.date)) {
return false;
}
if ((this.place == null) ? (other.place != null) : !this.place.equals(other.place)) {
return false;
}
if (this.role != other.role) {
return false;
}
if (this.index != other.index && (this.index == null || !this.index.equals(other.index))) {
return false;
}
return true;
}
public enum Role {
PUBLISHER("publisher"), PRINTER("printer"), OTHER("PublisherMapper.Role.OTHER");
private String text;
private Role(String text) {
this.text = text;
}
public String getText() {
return text;
}
public static Role fromText(String s) {
if (s == null) {
// default
return PUBLISHER;
}
for (Role role : values()) {
if (role.getText().equals(s)) {
return role;
}
}
return OTHER;
}
}
}
static final class PeriodicityItem implements OriginInfoItem {
// transliteration == null
private List<String> frequencies;
private String issuance;
private Integer index;
private boolean ignoreMapping = true;
public PeriodicityItem() {
}
public PeriodicityItem(Integer index, List<String> frequencies, String issuance) {
this.frequencies = frequencies;
this.index = index;
this.issuance = issuance;
}
@Override
public Integer getArrayIndex() {
return index;
}
@Override
public void setArrayIndex(Integer index) {
this.index = index;
}
public List<String> getFrequencies() {
return frequencies;
}
public void setFrequencies(List<String> frequencies) {
this.frequencies = frequencies;
}
public String getIssuance() {
return issuance;
}
public void setIssuance(String issuance) {
this.issuance = issuance;
}
@Override
public String toString() {
return String.format("PeriodicityItem{index: %s, frequencies: %s, issuance: %s}", index, frequencies, issuance);
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final PeriodicityItem other = (PeriodicityItem) obj;
if (this.frequencies != other.frequencies && (this.frequencies == null || !this.frequencies.equals(other.frequencies))) {
return false;
}
if ((this.issuance == null) ? (other.issuance != null) : !this.issuance.equals(other.issuance)) {
return false;
}
if (this.index != other.index && (this.index == null || !this.index.equals(other.index))) {
return false;
}
return true;
}
}
public interface OriginInfoItem extends ArrayItem {
}
}