/** * Licensed to The Apereo Foundation under one or more contributor license * agreements. See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * * The Apereo Foundation licenses this file to you under the Educational * Community License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of the License * at: * * http://opensource.org/licenses/ecl2.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. * */ package org.opencastproject.metadata.dublincore; import org.opencastproject.util.IoSupport; import org.apache.commons.io.IOUtils; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.DOMImplementation; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathFactory; /** * Simple class that enables storage of {@link DublinCoreCatalog} list and serializing into xml or json string. * */ public class DublinCoreCatalogList { /** Array storing Dublin cores */ private List<DublinCoreCatalog> catalogList = new LinkedList<DublinCoreCatalog>(); private long totalCatalogCount = 0; private static final Logger logger = LoggerFactory.getLogger(DublinCoreCatalogList.class); /** * Initialize with the given catalog list. * * @param catalogs * the catalogs to initialize this list with. * @param totalCount * the total count of catalogs that match the creating query of this list must be >= catalogs.size */ public DublinCoreCatalogList(List<DublinCoreCatalog> catalogs, long totalCount) { if (totalCount < catalogs.size()) throw new IllegalArgumentException("total count is less than the number of catalogs passed"); catalogList.addAll(catalogs); totalCatalogCount = totalCount; } /** * Returns list of Dublin Core currently stored * * @return List of {@link DublinCoreCatalog}s */ public List<DublinCoreCatalog> getCatalogList() { return new LinkedList<DublinCoreCatalog>(catalogList); } /** * Get the total number of catalogs matching the creating query. Is >= {@link #size()}. * * @return int totalCatalogCount */ public long getTotalCount() { return totalCatalogCount; } /** * Return the number of contained catalogs. */ public long size() { return catalogList.size(); } /** * Serializes list to XML. * * @return serialized array as XML string * @throws IOException * if serialization cannot be properly performed */ public String getResultsAsXML() throws IOException { try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); DOMImplementation impl = builder.getDOMImplementation(); Document doc = impl.createDocument(null, null, null); Element root = doc.createElement("dublincorelist"); root.setAttribute("totalCount", String.valueOf(totalCatalogCount)); doc.appendChild(root); for (DublinCoreCatalog series : catalogList) { Node node = doc.importNode(series.toXml().getDocumentElement(), true); root.appendChild(node); } Transformer tf = TransformerFactory.newInstance().newTransformer(); DOMSource xmlSource = new DOMSource(doc); StringWriter out = new StringWriter(); tf.transform(xmlSource, new StreamResult(out)); return out.toString(); } catch (Exception e) { throw new IOException(e); } } /** * Parses an XML or JSON string to an dublin core catalog list. * * @param dcString * the XML or JSON string * @throws IOException * if there is a problem parsing the XML or JSON */ public static DublinCoreCatalogList parse(String dcString) throws IOException { List<DublinCoreCatalog> catalogs = new ArrayList<DublinCoreCatalog>(); if (dcString.startsWith("{")) { JSONObject json; try { json = (JSONObject) new JSONParser().parse(dcString); long totalCount = Long.parseLong((String) json.get("totalCount")); JSONArray catalogsArray = (JSONArray) json.get("catalogs"); for (Object catalog : catalogsArray) { catalogs.add(DublinCoreJsonFormat.read((JSONObject) catalog)); } return new DublinCoreCatalogList(catalogs, totalCount); } catch (Exception e) { throw new IllegalStateException("Unable to load dublin core catalog list, json parsing failed.", e); } } else { // XML InputStream is = null; try { DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); docBuilderFactory.setNamespaceAware(true); DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder(); is = IOUtils.toInputStream(dcString, "UTF-8"); Document document = docBuilder.parse(is); XPath xPath = XPathFactory.newInstance().newXPath(); Number totalCount = (Number) xPath.evaluate("/*[local-name() = 'dublincorelist']/@totalCount", document, XPathConstants.NUMBER); NodeList nodes = (NodeList) xPath .evaluate("//*[local-name() = 'dublincore']", document, XPathConstants.NODESET); for (int i = 0; i < nodes.getLength(); i++) { InputStream nodeIs = null; try { nodeIs = nodeToString(nodes.item(i)); catalogs.add(DublinCoreXmlFormat.read(nodeIs)); } finally { IoSupport.closeQuietly(nodeIs); } } return new DublinCoreCatalogList(catalogs, totalCount.longValue()); } catch (Exception e) { throw new IOException(e); } finally { IoSupport.closeQuietly(is); } } } /** * Serialize a node to an input stream * * @param node * the node to serialize * @return the serialized input stream */ private static InputStream nodeToString(Node node) { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); try { Transformer t = TransformerFactory.newInstance().newTransformer(); t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); t.setOutputProperty(OutputKeys.INDENT, "yes"); t.transform(new DOMSource(node), new StreamResult(outputStream)); return new ByteArrayInputStream(outputStream.toByteArray()); } catch (TransformerException te) { logger.warn("nodeToString Transformer Exception", te); } return null; } /** * Serializes list to JSON array string. * * @return serialized array as json array string */ @SuppressWarnings("unchecked") public String getResultsAsJson() { JSONObject jsonObj = new JSONObject(); JSONArray jsonArray = new JSONArray(); for (DublinCoreCatalog catalog : catalogList) { jsonArray.add(DublinCoreJsonFormat.writeJsonObject((DublinCoreCatalog) catalog)); } jsonObj.put("totalCount", String.valueOf(totalCatalogCount)); jsonObj.put("catalogs", jsonArray); return jsonObj.toJSONString(); } }