diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..6270ca8
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,54 @@
+
+ 4.0.0
+ se.kb
+ oai4j
+ jar
+ 1.0
+ oai4j
+ http://maven.apache.org
+
+
+
+ junit
+ junit
+ 4.1
+ test
+
+
+ org.oclc
+ oaiharvester2
+ 0.1.10
+
+
+ dom4j
+ dom4j
+ 1.6.1
+
+
+ log4j
+ log4j
+ 1.2.14
+
+
+ jaxen
+ jaxen
+ 1.1.1
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 2.0
+
+
+ 1.5
+
+
+
+
+
+
diff --git a/src/main/java/se/kb/oai/OAIException.java b/src/main/java/se/kb/oai/OAIException.java
new file mode 100644
index 0000000..20835ed
--- /dev/null
+++ b/src/main/java/se/kb/oai/OAIException.java
@@ -0,0 +1,34 @@
+/*
+ * Created on 17 Aug 2007
+ *
+ * Copyright (C) 2007 Royal Library of Sweden.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+package se.kb.oai;
+
+public class OAIException extends Exception {
+
+ private static final long serialVersionUID = 5926653436917245659L;
+
+ public OAIException() {
+ super();
+ }
+
+ public OAIException(Exception e) {
+ super(e);
+ }
+
+}
diff --git a/src/main/java/se/kb/oai/ore/AggregateBase.java b/src/main/java/se/kb/oai/ore/AggregateBase.java
new file mode 100644
index 0000000..2268c53
--- /dev/null
+++ b/src/main/java/se/kb/oai/ore/AggregateBase.java
@@ -0,0 +1,62 @@
+package se.kb.oai.ore;
+
+import java.net.URI;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.dom4j.QName;
+
+public abstract class AggregateBase {
+
+ protected URI id;
+ protected List types;
+ protected List metadata;
+
+ public AggregateBase(URI id) {
+ this.id = id;
+ this.types = new LinkedList();
+ this.metadata = new LinkedList();
+ }
+
+ public URI getId() {
+ return id;
+ }
+
+ public void setId(URI id) {
+ this.id = id;
+ }
+
+ public List getTypes() {
+ return types;
+ }
+
+ public void setTypes(List types) {
+ this.types = types;
+ }
+
+ public void addType(Type type) {
+ types.add(type);
+ }
+
+ public List getMetadata() {
+ return metadata;
+ }
+
+ public List getMetadata(QName name) {
+ List list = new LinkedList();
+ for (Metadata meta : metadata) {
+ if (meta.getName().equals(name)) {
+ list.add(meta);
+ }
+ }
+ return list;
+ }
+
+ public void setMetadata(List metadata) {
+ this.metadata = metadata;
+ }
+
+ public void addMetadata(Metadata meta) {
+ metadata.add(meta);
+ }
+}
diff --git a/src/main/java/se/kb/oai/ore/AggregatedResource.java b/src/main/java/se/kb/oai/ore/AggregatedResource.java
new file mode 100644
index 0000000..a127270
--- /dev/null
+++ b/src/main/java/se/kb/oai/ore/AggregatedResource.java
@@ -0,0 +1,34 @@
+package se.kb.oai.ore;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+public class AggregatedResource extends AggregateBase {
+
+ public AggregatedResource(String id) throws URISyntaxException {
+ this(new URI(id));
+ }
+
+ public AggregatedResource(URI id) {
+ super(id);
+ }
+
+ public InputStream getContent() throws IOException {
+ return id.toURL().openStream();
+ }
+ public String getContentAsString() throws IOException {
+ InputStream in = getContent();
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ byte[] bytes = new byte[4 * 1024];
+ int read = in.read(bytes);
+ while (read != -1) {
+ out.write(bytes, 0, read);
+ read = in.read(bytes);
+ }
+ in.close();
+ return new String(out.toByteArray());
+ }
+}
diff --git a/src/main/java/se/kb/oai/ore/Aggregation.java b/src/main/java/se/kb/oai/ore/Aggregation.java
new file mode 100644
index 0000000..901224e
--- /dev/null
+++ b/src/main/java/se/kb/oai/ore/Aggregation.java
@@ -0,0 +1,53 @@
+package se.kb.oai.ore;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.LinkedList;
+import java.util.List;
+
+public class Aggregation extends AggregateBase {
+
+ private List resources;
+
+ public Aggregation(String id) throws URISyntaxException {
+ this(new URI(id));
+ }
+
+ public Aggregation(URI id) {
+ super(id);
+ this.resources = new LinkedList();
+ }
+
+ public int numberOfResources() {
+ return resources.size();
+ }
+
+ public List getResources() {
+ return resources;
+ }
+
+ public AggregatedResource getResource(int index) {
+ return resources.get(index);
+ }
+
+ public AggregatedResource getResource(String id) throws URISyntaxException {
+ return getResource(new URI(id));
+ }
+
+ public AggregatedResource getResource(URI id) {
+ for (AggregatedResource resource : resources) {
+ if (resource.getId().equals(id)) {
+ return resource;
+ }
+ }
+ return null;
+ }
+
+ public void setResources(List resources) {
+ this.resources = resources;
+ }
+
+ public void addResource(AggregatedResource resource) {
+ resources.add(resource);
+ }
+}
diff --git a/src/main/java/se/kb/oai/ore/Metadata.java b/src/main/java/se/kb/oai/ore/Metadata.java
new file mode 100644
index 0000000..46ea2ce
--- /dev/null
+++ b/src/main/java/se/kb/oai/ore/Metadata.java
@@ -0,0 +1,40 @@
+package se.kb.oai.ore;
+
+import static se.kb.oai.ore.OREConstants.*;
+
+import org.dom4j.QName;
+
+public class Metadata {
+
+ public enum Namespace { DC, DCTERMS }
+
+ private QName qname;
+ private String value;
+
+ public Metadata(Namespace ns, String name, String value) {
+ this.qname = getName(ns, name);
+ this.value = value;
+ }
+
+ public QName getName() {
+ return qname;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public static QName getName(Namespace ns, String name) {
+ switch (ns) {
+ case DC:
+ return new QName(name, DC_NS);
+ case DCTERMS:
+ return new QName(name, DCTERMS_NS);
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/se/kb/oai/ore/OREConstants.java b/src/main/java/se/kb/oai/ore/OREConstants.java
new file mode 100644
index 0000000..5b7fbb7
--- /dev/null
+++ b/src/main/java/se/kb/oai/ore/OREConstants.java
@@ -0,0 +1,22 @@
+package se.kb.oai.ore;
+
+import org.dom4j.Namespace;
+
+public class OREConstants {
+
+ public static final String ATOM_NS_PREFIX = "atom";
+ public static final String ATOM_NS_URI = "http://www.w3.org/2005/Atom";
+ public static final Namespace ATOM_NS = new Namespace(ATOM_NS_PREFIX, ATOM_NS_URI);
+
+ public static final String DC_NS_PREFIX = "dc";
+ public static final String DC_NS_URI = "http://purl.org/dc/";
+ public static final Namespace DC_NS = new Namespace(DC_NS_PREFIX, DC_NS_URI);
+
+ public static final String DCTERMS_NS_PREFIX = "dcterms";
+ public static final String DCTERMS_NS_URI = "http://purl.org/dc/terms/";
+ public static final Namespace DCTERMS_NS = new Namespace(DCTERMS_NS_PREFIX, DCTERMS_NS_URI);
+
+ public static final String RDF_NS_PREFIX = "rdf";
+ public static final String RDF_NS_URI = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+ public static final Namespace RDF_NS = new Namespace(RDF_NS_PREFIX, RDF_NS_URI);
+}
diff --git a/src/main/java/se/kb/oai/ore/ResourceMap.java b/src/main/java/se/kb/oai/ore/ResourceMap.java
new file mode 100644
index 0000000..b82eae3
--- /dev/null
+++ b/src/main/java/se/kb/oai/ore/ResourceMap.java
@@ -0,0 +1,72 @@
+package se.kb.oai.ore;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Date;
+
+public class ResourceMap {
+
+ private URI id;
+ private String creator;
+ private String rights;
+ private Date created;
+ private Date modified;
+ private Aggregation aggregation;
+
+ public ResourceMap(String id) throws URISyntaxException {
+ this(new URI(id));
+ }
+
+ public ResourceMap(URI id) {
+ this.id = id;
+ this.aggregation = new Aggregation(id.resolve("#aggregation"));
+ }
+
+ public URI getId() {
+ return id;
+ }
+
+ public void setId(URI id) {
+ this.id = id;
+ }
+
+ public String getCreator() {
+ return creator;
+ }
+
+ public void setCreator(String creator) {
+ this.creator = creator;
+ }
+
+ public String getRights() {
+ return rights;
+ }
+
+ public void setRights(String rights) {
+ this.rights = rights;
+ }
+
+ public Date getCreated() {
+ return created;
+ }
+
+ public void setCreated(Date created) {
+ this.created = created;
+ }
+
+ public Date getModified() {
+ return modified;
+ }
+
+ public void setModified(Date modified) {
+ this.modified = modified;
+ }
+
+ public Aggregation getAggregation() {
+ return aggregation;
+ }
+
+ public void setAggregation(Aggregation aggregation) {
+ this.aggregation = aggregation;
+ }
+}
diff --git a/src/main/java/se/kb/oai/ore/ResourceMapFactory.java b/src/main/java/se/kb/oai/ore/ResourceMapFactory.java
new file mode 100644
index 0000000..ad448d9
--- /dev/null
+++ b/src/main/java/se/kb/oai/ore/ResourceMapFactory.java
@@ -0,0 +1,22 @@
+package se.kb.oai.ore;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+
+import org.dom4j.Element;
+
+import se.kb.oai.OAIException;
+
+public interface ResourceMapFactory {
+
+ public ResourceMap newResourceMap(String uri) throws URISyntaxException;
+
+ public ResourceMap newResourceMap(URI uri);
+
+ public ResourceMap getResourceMap(URL url) throws OAIException;
+
+ public ResourceMap getResourceMap(String url) throws OAIException;
+
+ public ResourceMap getResourceMap(Element root) throws OAIException;
+}
diff --git a/src/main/java/se/kb/oai/ore/ResourceMapSerializer.java b/src/main/java/se/kb/oai/ore/ResourceMapSerializer.java
new file mode 100644
index 0000000..4d33a4a
--- /dev/null
+++ b/src/main/java/se/kb/oai/ore/ResourceMapSerializer.java
@@ -0,0 +1,15 @@
+package se.kb.oai.ore;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.dom4j.Element;
+
+public interface ResourceMapSerializer {
+
+ public void serializeToFile(File file, ResourceMap map) throws IOException;
+
+ public String serializeToString(ResourceMap map) throws IOException;
+
+ public Element serializeToXml(ResourceMap map);
+}
diff --git a/src/main/java/se/kb/oai/ore/Type.java b/src/main/java/se/kb/oai/ore/Type.java
new file mode 100644
index 0000000..4361d43
--- /dev/null
+++ b/src/main/java/se/kb/oai/ore/Type.java
@@ -0,0 +1,14 @@
+package se.kb.oai.ore;
+
+public class Type {
+
+ private String value;
+
+ public Type(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+}
diff --git a/src/main/java/se/kb/oai/ore/impl/AtomConstants.java b/src/main/java/se/kb/oai/ore/impl/AtomConstants.java
new file mode 100644
index 0000000..2c6accc
--- /dev/null
+++ b/src/main/java/se/kb/oai/ore/impl/AtomConstants.java
@@ -0,0 +1,47 @@
+package se.kb.oai.ore.impl;
+
+import java.text.SimpleDateFormat;
+
+import org.dom4j.QName;
+
+import se.kb.oai.ore.OREConstants;
+
+public class AtomConstants extends OREConstants {
+
+ public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
+
+ public static final String COLON = ":";
+ public static final String SLASH = "/";
+
+ public static final QName FEED = new QName("feed", ATOM_NS);
+ public static final QName ID = new QName("id", ATOM_NS);
+ public static final QName LINK = new QName("link", ATOM_NS);
+ public static final QName TITLE = new QName("title", ATOM_NS);
+ public static final QName AUTHOR = new QName("author", ATOM_NS);
+ public static final QName NAME = new QName("name", ATOM_NS);
+ public static final QName RIGHTS = new QName("rights", ATOM_NS);
+ public static final QName PUBLISHED = new QName("published", ATOM_NS);
+ public static final QName UPDATED = new QName("updated", ATOM_NS);
+ public static final QName CATEGORY = new QName("category", ATOM_NS);
+ public static final QName ICON = new QName("icon", ATOM_NS);
+ public static final QName ENTRY = new QName("entry", ATOM_NS);
+ public static final QName TYPE = new QName("type", RDF_NS);
+
+ public static final String SCHEME = "scheme";
+ public static final String TERM = "term";
+ public static final String LABEL = "label";
+ public static final String REL = "rel";
+ public static final String HREF = "href";
+
+ public static final String ORE_TERMS_URL = "http://www.openarchives.org/ore/terms/";
+ public static final String ORE_TERMS_REM_URL = "http://www.openarchives.org/ore/terms/ResourceMap";
+ public static final String ICON_URL = "http://www.openarchives.org/ore/logos/ore_icon.png";
+
+ public static final String ID_XPATH = ATOM_NS_PREFIX + COLON + LINK.getName() + "[@rel='self']/@href";
+ public static final String CREATOR_XPATH = ATOM_NS_PREFIX + COLON + AUTHOR.getName() + SLASH + ATOM_NS_PREFIX + COLON + NAME.getName();
+ public static final String MODIFIED_XPATH = ATOM_NS_PREFIX + COLON + UPDATED.getName();
+ public static final String CREATED_XPATH = ATOM_NS_PREFIX + COLON + PUBLISHED.getName();
+ public static final String RIGHTS_XPATH = ATOM_NS_PREFIX + COLON + RIGHTS.getName();
+ public static final String ENTRY_XPATH = ATOM_NS_PREFIX + COLON + ENTRY.getName();
+ public static final String ENTRY_ID_XPATH = ATOM_NS_PREFIX + COLON + LINK.getName() + "[@rel='alternate']/@href";
+}
diff --git a/src/main/java/se/kb/oai/ore/impl/AtomFactory.java b/src/main/java/se/kb/oai/ore/impl/AtomFactory.java
new file mode 100644
index 0000000..e495b48
--- /dev/null
+++ b/src/main/java/se/kb/oai/ore/impl/AtomFactory.java
@@ -0,0 +1,88 @@
+package se.kb.oai.ore.impl;
+
+import java.util.List;
+
+import org.dom4j.Element;
+import org.dom4j.Node;
+
+import se.kb.oai.OAIException;
+import se.kb.oai.ore.AggregateBase;
+import se.kb.oai.ore.AggregatedResource;
+import se.kb.oai.ore.Metadata;
+import se.kb.oai.ore.Metadata.Namespace;
+import se.kb.oai.ore.ResourceMap;
+import se.kb.oai.ore.Type;
+import se.kb.xml.XPathWrapper;
+
+import static se.kb.oai.ore.OREConstants.ATOM_NS_PREFIX;
+import static se.kb.oai.ore.OREConstants.ATOM_NS_URI;
+import static se.kb.oai.ore.OREConstants.DCTERMS_NS_PREFIX;
+import static se.kb.oai.ore.OREConstants.DCTERMS_NS_URI;
+import static se.kb.oai.ore.OREConstants.DC_NS_PREFIX;
+import static se.kb.oai.ore.OREConstants.DC_NS_URI;
+import static se.kb.oai.ore.OREConstants.RDF_NS_PREFIX;
+import static se.kb.oai.ore.OREConstants.RDF_NS_URI;
+import static se.kb.oai.ore.impl.AtomConstants.*;
+
+public class AtomFactory extends FactoryBase {
+
+ @Override
+ public ResourceMap getResourceMap(Element feed) throws OAIException {
+ try {
+ XPathWrapper xpath = createXPath(feed);
+
+ ResourceMap map = new ResourceMap(xpath.valueOf(ID_XPATH));
+ map.setCreator(xpath.valueOf(CREATOR_XPATH));
+ map.setModified(DATE_FORMAT.parse(xpath.valueOf(MODIFIED_XPATH)));
+
+ String rights = xpath.valueOf(RIGHTS_XPATH);
+ if (notEmpty(rights))
+ map.setRights(rights);
+
+ String created = xpath.valueOf(CREATED_XPATH);
+ if (notEmpty(created))
+ map.setCreated(DATE_FORMAT.parse(created));
+
+ addTypesAndMetadata(map.getAggregation(), xpath.selectNodes("*"));
+
+ for (Node node : xpath.selectNodes(ENTRY_XPATH)) {
+ xpath = createXPath(node);
+ AggregatedResource resource = new AggregatedResource(xpath.valueOf(ENTRY_ID_XPATH));
+ addTypesAndMetadata(resource, xpath.selectNodes("*"));
+ map.getAggregation().addResource(resource);
+ }
+
+ return map;
+ } catch (Exception e) {
+ throw new OAIException(e);
+ }
+ }
+
+ private void addTypesAndMetadata(AggregateBase aggregate, List nodes) {
+ for (Node node : nodes) {
+ Element element = (Element) node;
+ if (element.getNamespace().equals(DC_NS)) {
+ aggregate.addMetadata((new Metadata(Namespace.DC, element.getName(), element.getText())));
+ }
+ else if (element.getNamespace().equals(DCTERMS_NS)) {
+ aggregate.addMetadata((new Metadata(Namespace.DCTERMS, element.getName(), element.getText())));
+ }
+ else if (element.getNamespace().equals(RDF_NS) && element.getName().equals("type")) {
+ aggregate.addType(new Type(element.getText()));
+ }
+ }
+ }
+
+ private XPathWrapper createXPath(Node node) {
+ XPathWrapper xpath = new XPathWrapper(node);
+ xpath.addNamespace(ATOM_NS_PREFIX, ATOM_NS_URI);
+ xpath.addNamespace(RDF_NS_PREFIX, RDF_NS_URI);
+ xpath.addNamespace(DC_NS_PREFIX, DC_NS_URI);
+ xpath.addNamespace(DCTERMS_NS_PREFIX, DCTERMS_NS_URI);
+ return xpath;
+ }
+
+ private boolean notEmpty(String string) {
+ return (string != null && string.trim().length() > 0);
+ }
+}
diff --git a/src/main/java/se/kb/oai/ore/impl/AtomSerializer.java b/src/main/java/se/kb/oai/ore/impl/AtomSerializer.java
new file mode 100644
index 0000000..82c30f0
--- /dev/null
+++ b/src/main/java/se/kb/oai/ore/impl/AtomSerializer.java
@@ -0,0 +1,127 @@
+package se.kb.oai.ore.impl;
+
+import java.util.List;
+
+import org.dom4j.DocumentHelper;
+import org.dom4j.Element;
+
+import se.kb.oai.ore.AggregatedResource;
+import se.kb.oai.ore.Metadata;
+import se.kb.oai.ore.ResourceMap;
+import se.kb.oai.ore.Type;
+
+import static se.kb.oai.ore.impl.AtomConstants.*;
+
+public class AtomSerializer extends SerializerBase {
+
+ @Override
+ public Element serializeToXml(ResourceMap map) {
+ Element feed = DocumentHelper.createElement(FEED);
+ feed.addNamespace(ATOM_NS_PREFIX, ATOM_NS_URI);
+ feed.addNamespace(RDF_NS_PREFIX, RDF_NS_URI);
+ feed.addNamespace(DC_NS_PREFIX, DC_NS_URI);
+ feed.addNamespace(DCTERMS_NS_PREFIX, DCTERMS_NS_URI);
+
+ Element id = DocumentHelper.createElement(ID);
+ id.setText(createAtomId());
+ feed.add(id);
+
+ Element link = DocumentHelper.createElement(LINK);
+ link.addAttribute(REL, "self");
+ link.addAttribute("type", "application/atom+xml");
+ link.addAttribute(HREF, map.getId().toString());
+ feed.add(link);
+
+ Element title = DocumentHelper.createElement(TITLE);
+ title.setText("Resource Map " + map.getId().toString());
+ feed.add(title);
+
+ Element author = DocumentHelper.createElement(AUTHOR);
+ Element name = DocumentHelper.createElement(NAME);
+ name.setText(map.getCreator());
+ author.add(name);
+ feed.add(author);
+
+ if (map.getRights() != null) {
+ Element rights = DocumentHelper.createElement(RIGHTS);
+ rights.setText(map.getRights());
+ feed.add(rights);
+ }
+
+ if (map.getCreated() != null) {
+ Element published = DocumentHelper.createElement(PUBLISHED);
+ published.setText(DATE_FORMAT.format(map.getCreated()));
+ feed.add(published);
+ }
+
+ Element updated = DocumentHelper.createElement(UPDATED);
+ updated.setText(DATE_FORMAT.format(map.getModified()));
+ feed.add(updated);
+
+ Element category = DocumentHelper.createElement(CATEGORY);
+ category.addAttribute(SCHEME, ORE_TERMS_URL);
+ category.addAttribute(TERM, ORE_TERMS_REM_URL);
+ category.addAttribute(LABEL, "Resource Map");
+ feed.add(category);
+
+ Element icon = DocumentHelper.createElement(ICON);
+ icon.setText(ICON_URL);
+ feed.add(icon);
+
+ link = DocumentHelper.createElement(LINK);
+ link.addAttribute(REL, "describes");
+ link.addAttribute(HREF, map.getAggregation().getId().toString());
+ feed.add(link);
+
+ addTypes(feed, map.getAggregation().getTypes());
+ addMetadata(feed, map.getAggregation().getMetadata());
+
+ for (AggregatedResource resource : map.getAggregation().getResources()) {
+ Element entry = DocumentHelper.createElement(ENTRY);
+
+ id = DocumentHelper.createElement(ID);
+ id.setText(createAtomId());
+ entry.add(id);
+
+ title = DocumentHelper.createElement(TITLE);
+ title.setText("Aggregated Resource " + resource.getId().toString());
+ entry.add(title);
+
+ updated = DocumentHelper.createElement(UPDATED);
+ updated.setText(DATE_FORMAT.format(map.getModified()));
+ entry.add(updated);
+
+ link = DocumentHelper.createElement(LINK);
+ link.addAttribute(REL, "alternate");
+ link.addAttribute(HREF, resource.getId().toString());
+ entry.add(link);
+
+ addTypes(entry, resource.getTypes());
+ addMetadata(entry, resource.getMetadata());
+
+ feed.add(entry);
+ }
+
+ return feed;
+ }
+
+ private void addTypes(Element target, List list) {
+ for (Type type : list) {
+ Element element = DocumentHelper.createElement(TYPE);
+ element.setText(type.getValue());
+ target.add(element);
+ }
+ }
+
+ private void addMetadata(Element target, List list) {
+ for (Metadata metadata : list) {
+ Element element = DocumentHelper.createElement(metadata.getName());
+ element.setText(metadata.getValue());
+ target.add(element);
+ }
+ }
+
+ protected String createAtomId() {
+ return "urn:uid:" + (int) (Math.random() * 1000);
+ }
+}
diff --git a/src/main/java/se/kb/oai/ore/impl/FactoryBase.java b/src/main/java/se/kb/oai/ore/impl/FactoryBase.java
new file mode 100644
index 0000000..b2ca50e
--- /dev/null
+++ b/src/main/java/se/kb/oai/ore/impl/FactoryBase.java
@@ -0,0 +1,44 @@
+package se.kb.oai.ore.impl;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+
+import org.dom4j.Document;
+import org.dom4j.Element;
+import org.dom4j.io.SAXReader;
+
+import se.kb.oai.OAIException;
+import se.kb.oai.ore.ResourceMap;
+import se.kb.oai.ore.ResourceMapFactory;
+
+public abstract class FactoryBase implements ResourceMapFactory {
+
+ public ResourceMap newResourceMap(String uri) throws URISyntaxException {
+ return new ResourceMap(uri);
+ }
+
+ public ResourceMap newResourceMap(URI uri) {
+ return new ResourceMap(uri);
+ }
+
+ public ResourceMap getResourceMap(URL url) throws OAIException {
+ try {
+ SAXReader reader = new SAXReader();
+ Document document = reader.read(url);
+ return getResourceMap(document.getRootElement());
+ } catch (Exception e) {
+ throw new OAIException(e);
+ }
+ }
+
+ public ResourceMap getResourceMap(String url) throws OAIException {
+ try {
+ return getResourceMap(new URL(url));
+ } catch (Exception e) {
+ throw new OAIException(e);
+ }
+ }
+
+ public abstract ResourceMap getResourceMap(Element root) throws OAIException;
+}
\ No newline at end of file
diff --git a/src/main/java/se/kb/oai/ore/impl/SerializerBase.java b/src/main/java/se/kb/oai/ore/impl/SerializerBase.java
new file mode 100644
index 0000000..95896e2
--- /dev/null
+++ b/src/main/java/se/kb/oai/ore/impl/SerializerBase.java
@@ -0,0 +1,26 @@
+package se.kb.oai.ore.impl;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.dom4j.Element;
+
+import se.kb.oai.ore.ResourceMap;
+import se.kb.oai.ore.ResourceMapSerializer;
+import se.kb.xml.XMLUtils;
+
+public abstract class SerializerBase implements ResourceMapSerializer {
+
+ public void serializeToFile(File file, ResourceMap map) throws IOException {
+ FileOutputStream out = new FileOutputStream(file);
+ XMLUtils.writeXmlTo(serializeToXml(map), out);
+ out.close();
+ }
+
+ public String serializeToString(ResourceMap map) throws IOException {
+ return XMLUtils.xmlToString(serializeToXml(map));
+ }
+
+ public abstract Element serializeToXml(ResourceMap map);
+}
diff --git a/src/main/java/se/kb/oai/pmh/Header.java b/src/main/java/se/kb/oai/pmh/Header.java
new file mode 100644
index 0000000..8db4326
--- /dev/null
+++ b/src/main/java/se/kb/oai/pmh/Header.java
@@ -0,0 +1,65 @@
+/*
+ * Created on 17 Aug 2007
+ *
+ * Copyright (C) 2007 Royal Library of Sweden.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+package se.kb.oai.pmh;
+
+
+import static se.kb.oai.pmh.ResponseBase.*;
+
+import java.util.List;
+import java.util.LinkedList;
+
+import org.dom4j.Node;
+
+import se.kb.xml.XPathWrapper;
+
+public class Header {
+
+ private static final String IDENTIFIER_XPATH = "oai:identifier";
+ private static final String DATESTAMP_XPATH = "oai:datestamp";
+ private static final String SETSPEC_XPATH = "oai:setSpec";
+
+ private String identifier;
+ private String datestamp;
+ private List setSpecs;
+
+ public Header(Node node) {
+ XPathWrapper xpath = new XPathWrapper(node);
+ xpath.addNamespace(OAI_NS_PREFIX, OAI_NS_URI);
+
+ this.identifier = xpath.valueOf(IDENTIFIER_XPATH);
+ this.datestamp = xpath.valueOf(DATESTAMP_XPATH);
+ this.setSpecs = new LinkedList();
+ for(Node spec : xpath.selectNodes(SETSPEC_XPATH)) {
+ setSpecs.add(spec.getText());
+ }
+ }
+
+ public String getIdentifier() {
+ return identifier;
+ }
+
+ public String getDatestamp() {
+ return datestamp;
+ }
+
+ public List getSetSpecs() {
+ return setSpecs;
+ }
+}
diff --git a/src/main/java/se/kb/oai/pmh/Identification.java b/src/main/java/se/kb/oai/pmh/Identification.java
new file mode 100644
index 0000000..325360f
--- /dev/null
+++ b/src/main/java/se/kb/oai/pmh/Identification.java
@@ -0,0 +1,108 @@
+/*
+ * Created on 17 Aug 2007
+ *
+ * Copyright (C) 2007 Royal Library of Sweden.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+package se.kb.oai.pmh;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.dom4j.Element;
+import org.dom4j.Node;
+import org.w3c.dom.Document;
+
+public class Identification extends ResponseBase {
+
+ private static final String REPOSITORYNAME_XPATH = "oai:Identify/oai:repositoryName";
+ private static final String BASEURL_XPATH = "oai:Identify/oai:baseURL";
+ private static final String PROTOCOLVERSION_XPATH = "oai:Identify/oai:protocolVersion";
+ private static final String EARLIESTDATESTAMP_XPATH = "oai:Identify/oai:earliestDatestamp";
+ private static final String DELETEDRECORD_XPATH = "oai:Identify/oai:deletedRecord";
+ private static final String GRANULARITY_XPATH = "oai:Identify/oai:granularity";
+ private static final String COMPRESSION_XPATH = "oai:Identify/oai:compression";
+ private static final String DESCRIPTION_XPATH = "oai:Identify/oai:description/*";
+ private static final String ADMINEMAIL_XPATH = "oai:Identify/oai:adminEmail";
+
+ private String repositoryName;
+ private String baseUrl;
+ private String protocolVersion;
+ private String earliestDatestamp;
+ private String deletedRecord;
+ private String granularity;
+ private String compression;
+ private List descriptions;
+ private List adminEmails;
+
+ public Identification(Document document) throws PMHErrorResponseException {
+ super(document);
+
+ this.repositoryName = xpath.valueOf(REPOSITORYNAME_XPATH);
+ this.baseUrl = xpath.valueOf(BASEURL_XPATH);
+ this.protocolVersion = xpath.valueOf(PROTOCOLVERSION_XPATH);
+ this.earliestDatestamp = xpath.valueOf(EARLIESTDATESTAMP_XPATH);
+ this.deletedRecord = xpath.valueOf(DELETEDRECORD_XPATH);
+ this.granularity = xpath.valueOf(GRANULARITY_XPATH);
+ this.compression = xpath.valueOf(COMPRESSION_XPATH);
+
+ this.descriptions = new LinkedList();
+ for (Node description: xpath.selectNodes(DESCRIPTION_XPATH)) {
+ descriptions.add((Element) description);
+ }
+
+ this.adminEmails = new LinkedList();
+ for(Node adminEmail : xpath.selectNodes(ADMINEMAIL_XPATH)) {
+ adminEmails.add(adminEmail.getText());
+ }
+ }
+
+ public List getAdminEmails() {
+ return adminEmails;
+ }
+
+ public String getBaseUrl() {
+ return baseUrl;
+ }
+
+ public String getCompression() {
+ return compression;
+ }
+
+ public String getDeletedRecord() {
+ return deletedRecord;
+ }
+
+ public List getDescriptions() {
+ return descriptions;
+ }
+
+ public String getEarliestDatestamp() {
+ return earliestDatestamp;
+ }
+
+ public String getGranularity() {
+ return granularity;
+ }
+
+ public String getProtocolVersion() {
+ return protocolVersion;
+ }
+
+ public String getRepositoryName() {
+ return repositoryName;
+ }
+}
diff --git a/src/main/java/se/kb/oai/pmh/IdentifiersList.java b/src/main/java/se/kb/oai/pmh/IdentifiersList.java
new file mode 100644
index 0000000..bd32c95
--- /dev/null
+++ b/src/main/java/se/kb/oai/pmh/IdentifiersList.java
@@ -0,0 +1,40 @@
+package se.kb.oai.pmh;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.dom4j.Node;
+import org.w3c.dom.Document;
+
+public class IdentifiersList extends ResponseBase {
+
+ private static final String HEADER_XPATH = "oai:ListIdentifiers/oai:header";
+
+ private List headers;
+
+ public IdentifiersList(Document document) throws PMHErrorResponseException {
+ super(document);
+
+ this.headers = new LinkedList();
+ for(Node node : xpath.selectNodes(HEADER_XPATH)) {
+ headers.add(new Header(node));
+ }
+ }
+
+ public int size() {
+ return headers.size();
+ }
+
+ public List asList() {
+ return headers;
+ }
+
+ public ResumptionToken getResumptionToken() {
+ if (super.resumptionToken == null
+ || super.resumptionToken.getId() == null
+ || super.resumptionToken.getId().length() == 0)
+ return null;
+
+ return super.resumptionToken;
+ }
+}
diff --git a/src/main/java/se/kb/oai/pmh/MetadataFormat.java b/src/main/java/se/kb/oai/pmh/MetadataFormat.java
new file mode 100644
index 0000000..d8a7792
--- /dev/null
+++ b/src/main/java/se/kb/oai/pmh/MetadataFormat.java
@@ -0,0 +1,58 @@
+/*
+ * Created on 17 Aug 2007
+ *
+ * Copyright (C) 2007 Royal Library of Sweden.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+package se.kb.oai.pmh;
+
+import static se.kb.oai.pmh.ResponseBase.*;
+
+import org.dom4j.Node;
+
+import se.kb.xml.XPathWrapper;
+
+public class MetadataFormat {
+
+ private static final String PREFIX_XPATH = "oai:metadataPrefix";
+ private static final String SCHEMA_XPATH = "oai:schema";
+ private static final String NAMESPACE_XPATH = "oai:metadataNamespace";
+
+ private String prefix;
+ private String schema;
+ private String namespace;
+
+ public MetadataFormat(Node node) {
+ XPathWrapper xpath = new XPathWrapper(node);
+ xpath.addNamespace(OAI_NS_PREFIX, OAI_NS_URI);
+
+ this.prefix = xpath.valueOf(PREFIX_XPATH);
+ this.schema = xpath.valueOf(SCHEMA_XPATH);
+ this.namespace = xpath.valueOf(NAMESPACE_XPATH);
+ }
+
+ public String getPrefix() {
+ return prefix;
+ }
+
+ public String getSchema() {
+ return schema;
+ }
+
+ public String getNamespace() {
+ return namespace;
+ }
+}
diff --git a/src/main/java/se/kb/oai/pmh/MetadataFormatsList.java b/src/main/java/se/kb/oai/pmh/MetadataFormatsList.java
new file mode 100644
index 0000000..95f7443
--- /dev/null
+++ b/src/main/java/se/kb/oai/pmh/MetadataFormatsList.java
@@ -0,0 +1,50 @@
+/*
+ * Created on 17 Aug 2007
+ *
+ * Copyright (C) 2007 Royal Library of Sweden.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+package se.kb.oai.pmh;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.dom4j.Node;
+import org.w3c.dom.Document;
+
+public class MetadataFormatsList extends ResponseBase {
+
+ private static final String METADATAFORMAT_XPATH = "oai:ListMetadataFormats/oai:metadataFormat";
+
+ private List metadataFormats;
+
+ public MetadataFormatsList(Document document) throws PMHErrorResponseException {
+ super(document);
+
+ this.metadataFormats = new LinkedList();
+ for(Node metadataFormat : xpath.selectNodes(METADATAFORMAT_XPATH)) {
+ metadataFormats.add(new MetadataFormat(metadataFormat));
+ }
+ }
+
+ public int size() {
+ return metadataFormats.size();
+ }
+
+ public List asList() {
+ return metadataFormats;
+ }
+}
diff --git a/src/main/java/se/kb/oai/pmh/OaiPmhServer.java b/src/main/java/se/kb/oai/pmh/OaiPmhServer.java
new file mode 100644
index 0000000..5c514b5
--- /dev/null
+++ b/src/main/java/se/kb/oai/pmh/OaiPmhServer.java
@@ -0,0 +1,148 @@
+package se.kb.oai.pmh;
+
+import java.net.URL;
+
+import se.kb.oai.OAIException;
+
+import ORG.oclc.oai.harvester2.verb.GetRecord;
+import ORG.oclc.oai.harvester2.verb.Identify;
+import ORG.oclc.oai.harvester2.verb.ListIdentifiers;
+import ORG.oclc.oai.harvester2.verb.ListMetadataFormats;
+import ORG.oclc.oai.harvester2.verb.ListRecords;
+import ORG.oclc.oai.harvester2.verb.ListSets;
+
+/**
+ * Class that acts as a facade for an OAI-PMH server.
+ *
+ * Has methods that corresponds to the different verbs in the OAI-PMH
+ * specification and that will return appropriate objects based on
+ * the response.
+ *
+ * @author oskar
+ */
+public class OaiPmhServer {
+
+ private String baseurl;
+
+ /**
+ * Creates an OaiPmhServer with the given base URL.
+ *
+ * @param url Base URL that points to an OAI-PMH server.
+ */
+ public OaiPmhServer(String url) {
+ this.baseurl = url;
+ }
+
+ /**
+ * Creates an OaiPmhServer with the given base URL.
+ *
+ * @param url Base URL that points to an OAI-PMH server.
+ */
+ public OaiPmhServer(URL url) {
+ this(url.toString());
+ }
+
+ public String getBaseUrl() {
+ return baseurl;
+ }
+
+ public Record getRecord(String identifier, String metadataPrefix) throws OAIException {
+ try {
+ GetRecord getRecord = new GetRecord(baseurl, identifier, metadataPrefix);
+ return new Record(getRecord.getDocument());
+ } catch (PMHErrorResponseException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new OAIException(e);
+ }
+ }
+
+ public Identification identify() throws OAIException {
+ try {
+ Identify identify = new Identify(baseurl);
+ return new Identification(identify.getDocument());
+ } catch (PMHErrorResponseException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new OAIException(e);
+ }
+ }
+
+ public IdentifiersList listIdentifiers(String metadataPrefix) throws OAIException {
+ return listIdentifiers(null, null, null, metadataPrefix);
+ }
+
+ public IdentifiersList listIdentifiers(String from, String until, String set, String metadataPrefix) throws OAIException {
+ try {
+ ListIdentifiers list = new ListIdentifiers(baseurl, from, until, set, metadataPrefix);
+ return new IdentifiersList(list.getDocument());
+ } catch (PMHErrorResponseException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new OAIException(e);
+ }
+ }
+
+ public IdentifiersList listIdentifiers(ResumptionToken resumptionToken) throws OAIException {
+ try {
+ ListIdentifiers list = new ListIdentifiers(baseurl, resumptionToken.getId());
+ return new IdentifiersList(list.getDocument());
+ } catch (PMHErrorResponseException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new OAIException(e);
+ }
+ }
+
+ public RecordsList listRecords(String metadataPrefix) throws OAIException {
+ return listRecords(null, null, null, metadataPrefix);
+ }
+
+ public RecordsList listRecords(String from, String until, String set, String metadataPrefix) throws OAIException {
+ try {
+ ListRecords list = new ListRecords(baseurl, from, until, set, metadataPrefix);
+ return new RecordsList(list.getDocument());
+ } catch (PMHErrorResponseException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new OAIException(e);
+ }
+ }
+
+ public RecordsList listRecords(ResumptionToken resumptionToken) throws OAIException {
+ try {
+ ListRecords list = new ListRecords(baseurl, resumptionToken.getId());
+ return new RecordsList(list.getDocument());
+ } catch (PMHErrorResponseException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new OAIException(e);
+ }
+ }
+
+ public MetadataFormatsList listMetadataFormats() throws OAIException {
+ return listMetadataFormats(null);
+ }
+
+ public MetadataFormatsList listMetadataFormats(String identifier) throws OAIException {
+ try {
+ ListMetadataFormats list = new ListMetadataFormats(baseurl, identifier);
+ return new MetadataFormatsList(list.getDocument());
+ } catch (PMHErrorResponseException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new OAIException(e);
+ }
+ }
+
+ public SetsList listSets() throws OAIException {
+ try {
+ ListSets list = new ListSets(baseurl);
+ return new SetsList(list.getDocument());
+ } catch (PMHErrorResponseException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new OAIException(e);
+ }
+ }
+}
diff --git a/src/main/java/se/kb/oai/pmh/PMHErrorResponseException.java b/src/main/java/se/kb/oai/pmh/PMHErrorResponseException.java
new file mode 100644
index 0000000..4f0ca02
--- /dev/null
+++ b/src/main/java/se/kb/oai/pmh/PMHErrorResponseException.java
@@ -0,0 +1,34 @@
+package se.kb.oai.pmh;
+
+import se.kb.oai.OAIException;
+
+public class PMHErrorResponseException extends OAIException {
+
+ private static final long serialVersionUID = -2010182612617642664L;
+
+ public static final String BAD_ARGUMENT = "badArgument";
+ public static final String BAD_RESUMPTION_TOKEN = "badResumptionToken";
+ public static final String BAD_VERB = "badVerb";
+ public static final String CANNOT_DISSEMINATE_FORMAT = "cannotDisseminateFormat";
+ public static final String ID_DOES_NOT_EXIST = "idDoesNotExist";
+ public static final String NO_RECORDS_MATCH = "noRecordsMatch";
+ public static final String NO_METADATA_FORMATS = "noMetadataFormats";
+ public static final String NO_SET_HIERARCHY = "noSetHierarchy";
+
+ private String code;
+ private String message;
+
+ public PMHErrorResponseException(String code, String message) {
+ super();
+ this.code = code;
+ this.message = message;
+ }
+
+ public String getCode() {
+ return code;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+}
diff --git a/src/main/java/se/kb/oai/pmh/Record.java b/src/main/java/se/kb/oai/pmh/Record.java
new file mode 100644
index 0000000..11758cb
--- /dev/null
+++ b/src/main/java/se/kb/oai/pmh/Record.java
@@ -0,0 +1,76 @@
+/*
+ * Created on 17 Aug 2007
+ *
+ * Copyright (C) 2007 Royal Library of Sweden.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+package se.kb.oai.pmh;
+
+import java.io.IOException;
+
+import org.dom4j.Element;
+import org.dom4j.Node;
+import org.w3c.dom.Document;
+
+import se.kb.xml.XMLUtils;
+import se.kb.xml.XPathWrapper;
+
+public class Record extends ResponseBase {
+
+ private static final String RECORD_XPATH = "oai:GetRecord/oai:record";
+ private static final String HEADER_XPATH = "oai:header";
+ private static final String METADATA_XPATH = "oai:metadata/*";
+ private static final String ABOUT_XPATH = "oai:about/*";
+
+ private Header header;
+ private Element metadata;
+ private Element about;
+
+ public Record(Document document) throws PMHErrorResponseException {
+ this(document, null);
+ }
+
+ public Record(Document document, Node record) throws PMHErrorResponseException {
+ super(document);
+
+ if (record == null)
+ this.xpath = new XPathWrapper(xpath.selectSingleNode(RECORD_XPATH));
+ else
+ this.xpath = new XPathWrapper(record);
+ xpath.addNamespace(OAI_NS_PREFIX, OAI_NS_URI);
+
+ Node headerNode = xpath.selectSingleNode(HEADER_XPATH);
+ this.header = new Header(headerNode);
+ this.metadata = xpath.selectSingleElement(METADATA_XPATH);
+ this.about = xpath.selectSingleElement(ABOUT_XPATH);
+ }
+
+ public Header getHeader() {
+ return header;
+ }
+
+ public Element getMetadata() {
+ return metadata;
+ }
+
+ public String getMetadataAsString() throws IOException {
+ return XMLUtils.xmlToString(getMetadata());
+ }
+
+ public Element getAbout() {
+ return about;
+ }
+}
diff --git a/src/main/java/se/kb/oai/pmh/RecordsList.java b/src/main/java/se/kb/oai/pmh/RecordsList.java
new file mode 100644
index 0000000..c7978b5
--- /dev/null
+++ b/src/main/java/se/kb/oai/pmh/RecordsList.java
@@ -0,0 +1,59 @@
+/*
+ * Created on 17 Aug 2007
+ *
+ * Copyright (C) 2007 Royal Library of Sweden.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+package se.kb.oai.pmh;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.dom4j.Node;
+import org.w3c.dom.Document;
+
+public class RecordsList extends ResponseBase {
+
+ private static final String RECORD_XPATH = "oai:ListRecords/oai:record";
+
+ private List records;
+
+ public RecordsList(Document document) throws PMHErrorResponseException {
+ super(document);
+
+ this.records = new LinkedList();
+ for (Node record : xpath.selectNodes(RECORD_XPATH)) {
+ records.add(new Record(document, record));
+ }
+ }
+
+ public int size() {
+ return records.size();
+ }
+
+ public List asList() {
+ return records;
+ }
+
+ public ResumptionToken getResumptionToken() {
+ if (super.resumptionToken == null
+ || super.resumptionToken.getId() == null
+ || super.resumptionToken.getId().length() == 0)
+ return null;
+
+ return super.resumptionToken;
+ }
+}
diff --git a/src/main/java/se/kb/oai/pmh/ResponseBase.java b/src/main/java/se/kb/oai/pmh/ResponseBase.java
new file mode 100644
index 0000000..ba895a3
--- /dev/null
+++ b/src/main/java/se/kb/oai/pmh/ResponseBase.java
@@ -0,0 +1,68 @@
+/*
+ * Created on 17 Aug 2007
+ *
+ * Copyright (C) 2007 Royal Library of Sweden.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+package se.kb.oai.pmh;
+
+import org.dom4j.Element;
+import org.dom4j.io.DOMReader;
+import org.w3c.dom.Document;
+
+import se.kb.xml.XPathWrapper;
+
+public abstract class ResponseBase {
+
+ public static final String OAI_NS_PREFIX = "oai";
+ public static final String OAI_NS_URI = "http://www.openarchives.org/OAI/2.0/";
+
+ private static final String RESPONSEDATE_XPATH = "oai:responseDate";
+ private static final String RESUMPTIONTOKEN_XPATH = "*/oai:resumptionToken";
+ private static final String ERROR_XPATH = "oai:error";
+
+
+ protected Document response;
+ protected XPathWrapper xpath;
+ protected String responseDate;
+ protected ResumptionToken resumptionToken;
+
+ public ResponseBase(Document document) throws PMHErrorResponseException {
+ DOMReader reader = new DOMReader();
+ Element root = reader.read(document).getRootElement();
+
+ this.xpath = new XPathWrapper(root);
+ xpath.addNamespace(OAI_NS_PREFIX, OAI_NS_URI);
+ this.response = document;
+ this.responseDate = xpath.valueOf(RESPONSEDATE_XPATH);
+
+ Element token = xpath.selectSingleElement(RESUMPTIONTOKEN_XPATH);
+ this.resumptionToken = token != null ? new ResumptionToken(token) : null;
+
+ Element error = xpath.selectSingleElement(ERROR_XPATH);
+ if (error != null) {
+ throw new PMHErrorResponseException(error.attributeValue("code"), error.getTextTrim());
+ }
+ }
+
+ public Document getResponse() {
+ return response;
+ }
+
+ public String getResponseDate() {
+ return responseDate;
+ }
+}
diff --git a/src/main/java/se/kb/oai/pmh/ResumptionToken.java b/src/main/java/se/kb/oai/pmh/ResumptionToken.java
new file mode 100644
index 0000000..d550442
--- /dev/null
+++ b/src/main/java/se/kb/oai/pmh/ResumptionToken.java
@@ -0,0 +1,41 @@
+/*
+ * Created on 17 Aug 2007
+ *
+ * Copyright (C) 2007 Royal Library of Sweden.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+package se.kb.oai.pmh;
+
+import org.dom4j.Element;
+
+public class ResumptionToken {
+
+ private String id;
+ private String expirationDate;
+
+ public ResumptionToken(Element element) {
+ this.id = element.getTextTrim();
+ this.expirationDate = element.attributeValue("expirationDate");
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public String getExpirationDate() {
+ return expirationDate;
+ }
+}
diff --git a/src/main/java/se/kb/oai/pmh/Set.java b/src/main/java/se/kb/oai/pmh/Set.java
new file mode 100644
index 0000000..f9e88b8
--- /dev/null
+++ b/src/main/java/se/kb/oai/pmh/Set.java
@@ -0,0 +1,65 @@
+/*
+ * Created on 17 Aug 2007
+ *
+ * Copyright (C) 2007 Royal Library of Sweden.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+package se.kb.oai.pmh;
+
+import static se.kb.oai.pmh.ResponseBase.*;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.dom4j.Element;
+import org.dom4j.Node;
+
+import se.kb.xml.XPathWrapper;
+
+public class Set {
+
+ private static final String SPEC_XPATH = "oai:setSpec";
+ private static final String NAME_XPATH = "oai:setName";
+ private static final String DESCRIPTION_XPATH = "oai:setDescription/*";
+
+ private String spec;
+ private String name;
+ private List descriptions;
+
+ public Set(Node node) {
+ XPathWrapper xpath = new XPathWrapper(node);
+ xpath.addNamespace(OAI_NS_PREFIX, OAI_NS_URI);
+
+ this.spec = xpath.valueOf(SPEC_XPATH);
+ this.name = xpath.valueOf(NAME_XPATH);
+ this.descriptions = new LinkedList();
+ for (Node description : xpath.selectNodes(DESCRIPTION_XPATH)) {
+ descriptions.add((Element) description);
+ }
+ }
+
+ public String getSpec() {
+ return spec;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public List getDescriptions() {
+ return descriptions;
+ }
+}
diff --git a/src/main/java/se/kb/oai/pmh/SetsList.java b/src/main/java/se/kb/oai/pmh/SetsList.java
new file mode 100644
index 0000000..4a927f1
--- /dev/null
+++ b/src/main/java/se/kb/oai/pmh/SetsList.java
@@ -0,0 +1,50 @@
+/*
+ * Created on 17 Aug 2007
+ *
+ * Copyright (C) 2007 Royal Library of Sweden.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+package se.kb.oai.pmh;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.dom4j.Node;
+import org.w3c.dom.Document;
+
+public class SetsList extends ResponseBase {
+
+ private static final String SET_XPATH = "oai:ListSets/oai:set";
+
+ private List sets;
+
+ public SetsList(Document document) throws PMHErrorResponseException {
+ super(document);
+
+ this.sets = new LinkedList();
+ for (Node set : xpath.selectNodes(SET_XPATH)) {
+ sets.add(new Set(set));
+ }
+ }
+
+ public int size() {
+ return sets.size();
+ }
+
+ public List asList() {
+ return sets;
+ }
+}
diff --git a/src/main/java/se/kb/xml/XMLUtils.java b/src/main/java/se/kb/xml/XMLUtils.java
new file mode 100644
index 0000000..f8c4972
--- /dev/null
+++ b/src/main/java/se/kb/xml/XMLUtils.java
@@ -0,0 +1,32 @@
+package se.kb.xml;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+
+import org.dom4j.Element;
+import org.dom4j.io.OutputFormat;
+import org.dom4j.io.XMLWriter;
+
+public class XMLUtils {
+
+ public static final String ENCODING = "UTF-8";
+
+ public static void writeXmlTo(Element element, OutputStream stream) throws IOException {
+ OutputStreamWriter writer = new OutputStreamWriter(stream, ENCODING);
+ OutputFormat format = OutputFormat.createPrettyPrint();
+ format.setEncoding(ENCODING);
+
+ XMLWriter xmlwriter = new XMLWriter(writer, format);
+ xmlwriter.write(element);
+ xmlwriter.flush();
+ writer.flush();
+ }
+
+ public static String xmlToString(Element xml) throws IOException {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ writeXmlTo(xml, stream);
+ return stream.toString(ENCODING);
+ }
+}
diff --git a/src/main/java/se/kb/xml/XPathWrapper.java b/src/main/java/se/kb/xml/XPathWrapper.java
new file mode 100644
index 0000000..d560ae6
--- /dev/null
+++ b/src/main/java/se/kb/xml/XPathWrapper.java
@@ -0,0 +1,74 @@
+/*
+ * Created on 17 Aug 2007
+ *
+ * Copyright (C) 2007 Royal Library of Sweden.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+package se.kb.xml;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.dom4j.DocumentHelper;
+import org.dom4j.Element;
+import org.dom4j.Node;
+import org.dom4j.XPath;
+
+public class XPathWrapper {
+
+ private Node node;
+ private Map namespaces;
+
+ public XPathWrapper(Node node) {
+ this(node, new HashMap());
+ }
+
+ public XPathWrapper(Node node, Map namespaces) {
+ this.node = node;
+ this.namespaces = namespaces;
+ }
+
+ public void addNamespace(String prefix, String uri) {
+ namespaces.put(prefix, uri);
+ }
+
+ public Node selectSingleNode(String xpathExpression) {
+ XPath xpath = createXPath(xpathExpression);
+ return xpath.selectSingleNode(node);
+ }
+
+ @SuppressWarnings("unchecked")
+ public List selectNodes(String xpathExpression) {
+ XPath xpath = createXPath(xpathExpression);
+ return xpath.selectNodes(node);
+ }
+
+ public Element selectSingleElement(String xpathExpression) {
+ return (Element) selectSingleNode(xpathExpression);
+ }
+
+ public String valueOf(String xpathExpression) {
+ XPath xpath =createXPath(xpathExpression);
+ return xpath.valueOf(node);
+ }
+
+ private XPath createXPath(String xpathExpression) {
+ XPath xpath = DocumentHelper.createXPath(xpathExpression);
+ xpath.setNamespaceURIs(namespaces);
+ return xpath;
+ }
+}
diff --git a/src/test/java/se/kb/oai/ore/ORETest.java b/src/test/java/se/kb/oai/ore/ORETest.java
new file mode 100644
index 0000000..89cf242
--- /dev/null
+++ b/src/test/java/se/kb/oai/ore/ORETest.java
@@ -0,0 +1,21 @@
+package se.kb.oai.ore;
+
+import static org.junit.Assert.*;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.junit.Test;
+
+/**
+ * Unit test for simple App.
+ */
+public class ORETest {
+
+ @Test
+ public void testConstructor() throws URISyntaxException
+ {
+ ResourceMap map = new ResourceMap(new URI("http://test.kb.se/rem/"));
+ assertEquals("http://test.kb.se/rem/#aggregation", map.getAggregation().getId().toString());
+ }
+}