/*
* 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.catalog;
import cz.cas.lib.proarc.common.config.CatalogConfiguration;
import cz.cas.lib.proarc.common.mods.ModsUtils;
import cz.cas.lib.proarc.common.xml.Transformers;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.bind.JAXB;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamSource;
import org.w3c.dom.Element;
/**
* Aleph X Server metadata provider
*
* @author Jan Pokorsky
*/
public final class AlephXServer implements BibliographicCatalog {
public static final String TYPE = "AlephXServer";
private static final Logger LOG = Logger.getLogger(AlephXServer.class.getName());
private final Transformers transformers = new Transformers();
private final URI server;
public static AlephXServer get(CatalogConfiguration c) {
if (c == null || !TYPE.equals(c.getType())) {
return null;
}
String url = c.getUrl();
if (url != null) {
try {
return new AlephXServer(url);
} catch (MalformedURLException ex) {
LOG.log(Level.SEVERE, c.toString(), ex);
} catch (URISyntaxException ex) {
LOG.log(Level.SEVERE, c.toString(), ex);
}
}
return null;
}
public AlephXServer(URI uri) {
this.server = uri;
}
public AlephXServer(String url) throws URISyntaxException, MalformedURLException {
// parse with URL that offers better error notification
this(new URL(url).toURI());
}
public List<MetadataItem> find(String fieldName, String value) throws TransformerException, IOException {
return find(fieldName, value, null);
}
@Override
public List<MetadataItem> find(String fieldName, String value, Locale locale) throws TransformerException, IOException {
Criteria criteria = Criteria.get(fieldName, value);
if (value == null) {
return Collections.emptyList();
}
InputStream is = fetchEntries(criteria);
FindResponse found = createFindResponse(is);
if (found == null || found.getEntryCount() < 1) {
return Collections.emptyList();
}
is = fetchDetails(found);
return createDetailResponse(is, locale);
}
List<MetadataItem> createDetailResponse(InputStream is, Locale locale) throws TransformerException {
try {
StreamSource fixedOaiMarc = (StreamSource) transformers.transform(new StreamSource(is), Transformers.Format.AlephOaiMarcFix);
// StringBuilder sb = new StringBuilder();
// fixedOaiMarc = (StreamSource) transformers.dump(fixedOaiMarc, sb);
// String toString = sb.toString();
// fixedOaiMarc = (StreamSource) transformers.dump2Temp(fixedOaiMarc, "1AlephOaiMarcFix.xml");
DetailResponse details = JAXB.unmarshal(fixedOaiMarc.getInputStream(), DetailResponse.class);
if (details == null) {
return Collections.emptyList();
}
List<MetadataItem> result = new ArrayList<MetadataItem>();
for (DetailResponse.Record record : details.getRecords()) {
Element oaiMarc = record.getOaiMarc();
DOMSource domSource = new DOMSource(oaiMarc);
MetadataItem item;
try {
item = createResponse(record.getEntry(), domSource, locale);
result.add(item);
} catch (UnsupportedEncodingException ex) {
LOG.log(Level.SEVERE, null, ex);
}
}
return result;
} finally {
try {
is.close();
} catch (IOException ex) {
LOG.log(Level.SEVERE, null, ex);
}
}
}
FindResponse createFindResponse(InputStream is) {
try {
return JAXB.unmarshal(is, AlephXServer.FindResponse.class);
} finally {
try {
is.close();
} catch (IOException ex) {
LOG.log(Level.SEVERE, null, ex);
}
}
}
private MetadataItem createResponse(int entryIdx, Source source, Locale locale)
throws TransformerException, UnsupportedEncodingException {
// StringBuilder sb = new StringBuilder();
// source = transformers.dump(source, sb);
// String toString = sb.toString();
// source = transformers.dump2Temp(source, "2AlephOaiMarcFixedElement.xml");
Source marcxmlSrc = transformers.transform(source, Transformers.Format.OaimarcAsMarc21slim);
// marcxmlSrc = transformers.dump2Temp(marcxmlSrc, "3OaimarcAsMarc21slim.xml");
byte[] modsBytes = transformers.transformAsBytes(
marcxmlSrc, Transformers.Format.MarcxmlAsMods3);
// try {
// FileOutputStream tmp = new FileOutputStream("/tmp/aleph/4mods.xml");
// tmp.write(modsBytes);
// tmp.close();
// } catch (Exception ex) {
// Logger.getLogger(AlephXServer.class.getName()).log(Level.SEVERE, null, ex);
// }
byte[] modsHtmlBytes = modsAsHtmlBytes(new StreamSource(new ByteArrayInputStream(modsBytes)), locale);
byte[] modsTitleBytes = transformers.transformAsBytes(
new StreamSource(new ByteArrayInputStream(modsBytes)),
Transformers.Format.ModsAsTitle);
// try {
// FileOutputStream tmp = new FileOutputStream("/tmp/aleph/5title.txt");
// tmp.write(modsTitleBytes);
// tmp.close();
// } catch (Exception ex) {
// Logger.getLogger(AlephXServer.class.getName()).log(Level.SEVERE, null, ex);
// }
return new MetadataItem(entryIdx, new String(modsBytes, "UTF-8"),
new String(modsHtmlBytes, "UTF-8"), new String(modsTitleBytes, "UTF-8"));
}
private byte[] modsAsHtmlBytes(Source source, Locale locale) throws TransformerException {
byte[] modsHtmlBytes = transformers.transformAsBytes(
source, Transformers.Format.ModsAsHtml, ModsUtils.modsAsHtmlParameters(locale));
return modsHtmlBytes;
}
private InputStream fetchEntries(Criteria criteria) throws MalformedURLException, IOException {
URL alephFind = setQuery(server, criteria.toUrlParams(), true).toURL();
return alephFind.openStream();
}
private InputStream fetchDetails(FindResponse found) throws MalformedURLException, IOException {
String number = found.getNumber();
int entryCount = found.getEntryCount();
entryCount = Math.min(10, entryCount);
String entries = (entryCount == 1) ? "1" : "1-" + entryCount;
String query = String.format("op=present&set_number=%s&set_entry=%s", number, entries);
URL alephDetails = setQuery(server, query, false).toURL();
return new BufferedInputStream(alephDetails.openStream());
}
static URI setQuery(URI u, String newQuery, boolean add) throws MalformedURLException {
String query = u.getQuery();
query = (query == null || !add) ? newQuery : query + '&' + newQuery;
try {
return new URI(u.getScheme(), u.getUserInfo(), u.getHost(),
u.getPort(), u.getPath(), query, u.getFragment());
} catch (URISyntaxException ex) {
MalformedURLException mex = new MalformedURLException(ex.getMessage());
mex.initCause(ex);
throw mex;
}
}
static final class Criteria {
enum Field {
BARCODE("barcode", "bar"), CCNB("ccnb", "cnb"),
ISSN("issn", "ssn"), ISBN("isbn", "sbn"), SGINATURE("signature", "sg");
private final String alephKeyword;
private final String keyword;
private Field(String keyword, String alephKeyword) {
this.keyword = keyword;
this.alephKeyword = alephKeyword;
}
public String getAlephKeyword() {
return alephKeyword;
}
public String getKeyword() {
return keyword;
}
public static Field fromString(String keyword) {
for (Field field : values()) {
if (field.getKeyword().equals(keyword)) {
return field;
}
}
return null;
}
}
private String value;
private Criteria.Field field;
public Criteria(String value, Criteria.Field field) {
this.value = value;
this.field = field;
}
public String toUrlParams() {
String url = String.format("op=find&request=%s=%s",
field.getAlephKeyword(), value);
return url;
}
public static Criteria get(String fieldName, String value) {
if (value == null || value.trim().length() == 0) {
return null;
}
Field field = Field.fromString(fieldName);
return field == null ? null : new Criteria(value, field);
}
}
@XmlRootElement(name = "present")
public static class DetailResponse {
@XmlElement
private List<Record> record;
public DetailResponse() {
}
public List<Record> getRecords() {
return record;
}
public static class Record {
@XmlElement(name = "record_header")
private Header header;
@XmlElement
private Metadata metadata;
public Record() {
}
public Header getHeader() {
return header;
}
public Metadata getMetadata() {
return metadata;
}
public int getEntry() {
return header.getEntry();
}
public Element getOaiMarc() {
return metadata.getOaiMarc();
}
public static class Header {
@XmlElement(name = "set_entry")
private int entry;
public Header() {
}
public int getEntry() {
return entry;
}
}
}
public static class Metadata {
// @XmlElement(name = "oai_marc")
@XmlAnyElement(lax=false)
private Element oaiMarc;
public Metadata() {
}
public Element getOaiMarc() {
return oaiMarc;
}
}
}
@XmlRootElement(name = "find")
public static class FindResponse {
@XmlElement(name = "set_number")
private String number;
@XmlElement(name = "no_records")
private int recordCount;
@XmlElement(name = "no_entries")
private int entryCount;
public FindResponse() {
}
public int getEntryCount() {
return entryCount;
}
public String getNumber() {
return number;
}
public int getRecordCount() {
return recordCount;
}
}
}