1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.opencastproject.oaipmh.server;
22
23 import static org.opencastproject.oaipmh.OaiPmhConstants.OAI_2_0_SCHEMA_LOCATION;
24 import static org.opencastproject.oaipmh.OaiPmhConstants.OAI_2_0_XML_NS;
25 import static org.opencastproject.oaipmh.OaiPmhUtil.toUtcSecond;
26
27 import org.opencastproject.mediapackage.EName;
28 import org.opencastproject.metadata.dublincore.DublinCore;
29 import org.opencastproject.metadata.dublincore.DublinCoreCatalog;
30 import org.opencastproject.metadata.dublincore.DublinCoreValue;
31 import org.opencastproject.oaipmh.OaiPmhConstants;
32 import org.opencastproject.oaipmh.persistence.OaiPmhDatabaseException;
33 import org.opencastproject.oaipmh.persistence.SearchResult;
34 import org.opencastproject.oaipmh.persistence.SearchResultItem;
35 import org.opencastproject.oaipmh.util.XmlGen;
36
37 import org.w3c.dom.Element;
38 import org.w3c.dom.Node;
39
40 import java.util.ArrayList;
41 import java.util.Arrays;
42 import java.util.Collections;
43 import java.util.Date;
44 import java.util.List;
45 import java.util.Optional;
46
47 import javax.xml.XMLConstants;
48
49
50
51
52 public abstract class OaiXmlGen extends XmlGen {
53
54 protected OaiPmhRepository repository;
55
56
57
58
59 public OaiXmlGen(OaiPmhRepository repository) {
60 super(Optional.of(OaiPmhConstants.OAI_2_0_XML_NS));
61 this.repository = repository;
62 }
63
64
65
66
67 Element oai(Node... nodes) {
68 final List<Node> combined = new ArrayList<>();
69 combined.add(schemaLocation(OAI_2_0_SCHEMA_LOCATION));
70 combined.add($eTxt("responseDate", OAI_2_0_XML_NS, toUtcSecond(new Date())));
71 Collections.addAll(combined, nodes);
72 return $e("OAI-PMH",
73 Collections.singletonList(ns("xsi", XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI)),
74 combined);
75 }
76
77
78
79
80 Element dc(Node... nodes) {
81 List<Node> combined = new ArrayList<Node>(Arrays.asList(nodes));
82 combined.add(schemaLocation(OaiPmhConstants.OAI_DC_SCHEMA_LOCATION));
83 return $e("oai_dc:dc", OaiPmhConstants.OAI_DC_XML_NS,
84 Arrays.asList(ns("dc", "http://purl.org/dc/elements/1.1/"),
85 ns("xsi", XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI)),
86 combined);
87 }
88
89
90
91
92 @SuppressWarnings("unchecked") Element dc(final SearchResultItem item, Optional<String> set) {
93 try {
94 return getDublincoreElement(item.getEpisodeDublinCore());
95 } catch (OaiPmhDatabaseException ex) {
96 return dc($e("dc:identifier", $txtBlank(item.getId())));
97 }
98 }
99
100
101
102
103 private Element getDublincoreElement(DublinCoreCatalog dc) {
104 List<Node> nodes = new ArrayList<Node>();
105 nodes.addAll(getDublinCoreNodes(dc, DublinCore.PROPERTY_TITLE));
106 nodes.addAll(getDublinCoreNodes(dc, DublinCore.PROPERTY_CREATOR));
107 nodes.addAll(getDublinCoreNodes(dc, DublinCore.PROPERTY_SUBJECT));
108 nodes.addAll(getDublinCoreNodes(dc, DublinCore.PROPERTY_DESCRIPTION));
109 nodes.addAll(getDublinCoreNodes(dc, DublinCore.PROPERTY_PUBLISHER));
110 nodes.addAll(getDublinCoreNodes(dc, DublinCore.PROPERTY_CONTRIBUTOR));
111 nodes.addAll(getDublinCoreNodes(dc, DublinCore.PROPERTY_TYPE));
112 nodes.addAll(getDublinCoreNodes(dc, DublinCore.PROPERTY_FORMAT));
113 nodes.addAll(getDublinCoreNodes(dc, DublinCore.PROPERTY_IDENTIFIER));
114 nodes.addAll(getDublinCoreNodes(dc, DublinCore.PROPERTY_SOURCE));
115 nodes.addAll(getDublinCoreNodes(dc, DublinCore.PROPERTY_LANGUAGE));
116 nodes.addAll(getDublinCoreNodes(dc, DublinCore.PROPERTY_RELATION));
117 nodes.addAll(getDublinCoreNodes(dc, DublinCore.PROPERTY_COVERAGE));
118 nodes.addAll(getDublinCoreNodes(dc, DublinCore.PROPERTY_LICENSE));
119 return dc(nodes.toArray(new Node[nodes.size()]));
120 }
121
122 private List<Node> getDublinCoreNodes(DublinCoreCatalog dc, EName eName) {
123 List<Node> nodes = new ArrayList<Node>();
124
125 List<DublinCoreValue> values = dc.get(eName);
126 for (DublinCoreValue dcValue : values) {
127 Element element = $e("dc:" + eName.getLocalName(), $langNode(dcValue.getLanguage()), $txt(dcValue.getValue()));
128 nodes.add(element);
129 }
130 return nodes;
131 }
132
133
134
135
136 Node resumptionToken(final Optional<String> resumptionToken, final String metadataPrefix, final SearchResult result,
137 Date until, Optional<String> set) {
138
139 final Optional<Optional<String>> token;
140 if (result.size() == result.getLimit()) {
141 SearchResultItem lastResult = result.getItems().get((int) (result.size() - 1));
142
143 token = Optional.of(Optional.of(repository.saveQuery(new ResumableQuery(metadataPrefix, lastResult.getModificationDate(),
144 until, set))));
145 } else if (resumptionToken.isPresent()) {
146
147 token = Optional.of(Optional.<String>empty());
148 } else {
149 token = Optional.empty();
150 }
151
152 return $e(
153 "resumptionToken",
154 token.flatMap(inner -> inner.map(this::$txt)).orElseGet(this::nodeZero)
155 );
156 }
157
158
159
160
161 Element record(final SearchResultItem item, final Node metadata) {
162 if (item.isDeleted()) {
163 return $e("record", header(item));
164 } else {
165 return $e("record", header(item), $e("metadata", metadata));
166 }
167 }
168
169
170
171
172 Element metadataFormat(MetadataFormat f) {
173 return $e("metadataFormat", $eTxt("metadataPrefix", f.getPrefix()), $eTxt("schema", f.getSchema().toString()),
174 $eTxt("metadataNamespace", f.getNamespace().toString()));
175 }
176
177
178
179
180 Node metadataPrefixAttr(Params p) {
181 return $aSome("metadataPrefix", p.getMetadataPrefix());
182 }
183
184
185
186
187 Element header(final SearchResultItem item) {
188
189
190
191 Element header = $e("header",
192 $eTxt("identifier", item.getId()),
193 $eTxt("datestamp", repository.toSupportedGranularity(item.getModificationDate())));
194 if (item.isDeleted()) {
195 header.setAttribute("status", "deleted");
196 }
197 for (String setSpec: item.getSetSpecs()) {
198 header.appendChild($eTxt("setSpec", setSpec));
199 }
200 return header;
201 }
202
203
204
205
206 protected List<Node> merge(Node[] a, Node... b) {
207 List<Node> merge = new ArrayList<Node>(a.length + b.length);
208 java.util.Collections.addAll(merge, a);
209 java.util.Collections.addAll(merge, b);
210 return merge;
211 }
212 }