package org.greenstone.gsdl3.gs3build.metadata; import java.util.List; import java.util.ArrayList; import java.util.Iterator; import java.io.PrintWriter; import java.sql.SQLException; import java.sql.Statement; import java.sql.ResultSet; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.Text; import org.greenstone.gsdl3.gs3build.doctypes.DocumentInterface; import org.greenstone.gsdl3.gs3build.util.XMLTools; import org.greenstone.gsdl3.gs3build.database.*; /** * Support the descriptive metadata of a given document * * The descriptive metadata can occur multiple times, so this 'wrapper' is * in fact a holder for a number of individual metadata sets. Individual * metadata groups are found in METSNamespace items. * * */ import org.greenstone.gsdl3.gs3build.util.MultiMap; public class METSDescriptive { MultiMap map; String ID; String name; public METSDescriptive(String ID, String name) { this.map = new MultiMap(); this.ID = ID; this.name = name; } public String getID() { return this.ID; } public String getName() { return this.name; } public void addNamespace(METSNamespace namespace) { this.map.put(namespace.getName(), namespace); } public METSNamespace getNamespace(String namespace) { return (METSNamespace) this.map.get(namespace); } /** * Get the version of a metadata namespace that can be used for * adding new values to the document. Remember, each namespace may * occur more than once, but only one of the occurrences can be 'open'. * * @param String the name of the namespace */ public METSNamespace getOpenNamespace(String namespace) { if (this.map.getAll(namespace) == null) { return null; } Iterator namespaces = ((List) this.map.getAll(namespace)).iterator(); while (namespaces.hasNext()) { METSNamespace namespaceData = (METSNamespace) namespaces.next(); if (namespaceData.isEditable()){ return namespaceData; } } return null; } /** * Add a piece of metadata to this document. Existing values are preserved. * * @param String the namespace in which the metadata occurs * @param String the label of the metadata * @param String the value of the metadata */ public void addMetadata(String namespace, String label, String value) { METSNamespace namespaceData = this.getOpenNamespace(namespace); if (namespaceData == null || namespaceData.isEditable() == false) { namespaceData = NamespaceFactory.initNamespace(namespace); this.addNamespace(namespaceData); } namespaceData.addMetadata(label, value); } /** * Add a piece of metadata to this document. Existing values in editable * parts of the document's metadata are destroyed. * * @param String the namespace in which the metadata occurs * @param String the label of the metadata * @param String the value of the metadata */ public void setMetadata(String namespace, String label, String value) { METSNamespace namespaceData = this.getOpenNamespace(namespace); if (namespaceData == null || namespaceData.isEditable() == false) { namespaceData = NamespaceFactory.initNamespace(namespace); this.addNamespace(namespaceData); } namespaceData.setMetadata(label, value); } /** * Remove the metadata for a particular label. * * @param String the namespace in which the metadata occurs * @param String the label of the metadata */ public void removeMetadata(String namespace, String label) { METSNamespace namespaceData = this.getOpenNamespace(namespace); if (namespaceData != null && namespaceData.isEditable() == true) { namespaceData.removeMetadata(label); } } /** * Fetch a List of the values matching the given label in * the requisite namespace. * * @param String namespace * @param String label of metadata to match * * @return List the corresponding values found. */ public List getMetadata(String namespace, String label) { // Simple case - no metadata if (!this.map.containsKey(namespace)){ return null; } // if there's just one instance of the requisite namespace, // then again it is a simple case if (this.map.getAll(namespace).size() == 1){ return this.getNamespace(namespace).getMetadata(label); } // otherwise, step through and accumulate List accumulatedValues = new ArrayList(); Iterator namespaceIterator = this.map.getAll(namespace).iterator(); while (namespaceIterator.hasNext()){ METSNamespace localNamespace = (METSNamespace) namespaceIterator.next(); List localList = localNamespace.getMetadata(label); if (localList != null && localList.size() > 0){ accumulatedValues.addAll(localList); } } // if the result of the accumulation is empty, return a null item if (accumulatedValues.size() == 0){ return null; } return accumulatedValues; } protected void parse_xmlData(METSNamespace namespace, Element element) { if (namespace==null) { System.err.println("namespace is null!!!!"); } NodeList children = element.getChildNodes(); for (int c = 0; c < children.getLength(); c++) { if (children.item(c).getNodeType() != org.w3c.dom.Node.ELEMENT_NODE) { continue; } Element child = (Element) children.item(c); String childName = child.getNodeName(); // if (childName.equals("gsdl2:Metadata")) { if (childName.equals("gsdl3:Metadata")) { String metaName = child.getAttribute("name"); Text text = (Text) child.getFirstChild(); String metaValue = text.getData(); addMetadata(namespace.getName(),metaName, metaValue); } } } public void parse_mdWrap(Element element) { // deal with mets:mdWrap METSNamespace namespace = NamespaceFactory.parseXML(element); NodeList children = element.getChildNodes(); for (int c = 0; c < children.getLength(); c ++) { if (children.item(c).getNodeType() != org.w3c.dom.Node.ELEMENT_NODE) { continue; } Element child = (Element) children.item(c); String childName = child.getNodeName(); if (childName.equals("mets:xmlData")) { parse_xmlData(namespace, child); } else { System.err.println("Warning: unrecognised tag " + childName + "in mets:mdWrap"); } } } /** * Parse an XML Element as a METS Descriptive Metadata section * * @param Element the XML element which represents the dmdSec itself */ public static METSDescriptive parseXML(Element element) { // Note: no parsing of attributes required, we just move onto // the metadata namespaces/sections themselves // deal with mets:dmdSec String ID = element.getAttribute("ID"); String label = element.getAttribute("GROUPID"); METSDescriptive thisDescriptive = new METSDescriptive(ID, label); //thisDescriptive = new METSDescriptive(ID, label); NodeList children = element.getChildNodes(); for (int c = 0; c < children.getLength(); c ++) { if (children.item(c).getNodeType() != org.w3c.dom.Node.ELEMENT_NODE) { continue; } Element child = (Element) children.item(c); String childName = child.getNodeName(); if (childName.equals("mets:mdRef")) { //for External Descriptive Metadata METSNamespace namespace = NamespaceFactory.parseXML(element); System.err.println("*** mets::mdRef does not appear to be implemented yet"); } else if (childName.equals("mets:mdWrap")) { thisDescriptive.parse_mdWrap(child); } else { // TODO: raise an error! System.out.println("Error: METSDescriptive: unrecognised tag "+childName); } } return thisDescriptive; } /** * Write the document metadata to a PrintWriter in METS * XML format. * * @param PrintWriter the writer to use for output */ public void write(PrintWriter output) { String tag = XMLTools.getOpenTag("mets", "dmdSec"); tag = XMLTools.addAttribute(tag, "ID", this.ID); tag = XMLTools.addAttribute(tag, "GROUPID", this.name); output.println(tag); Iterator keys = this.map.keySet().iterator(); // get the keys one by one while (keys.hasNext()){ Object nextKey = keys.next(); if (nextKey == null){ continue; } String key = nextKey.toString(); // get every copy of the current namespace name - namespaces may // occur more than once, so this is a List Iterator namespaces = this.map.getAll(key).iterator(); // namespaces will write themselves... while (namespaces.hasNext()){ METSNamespace namespace = (METSNamespace) namespaces.next(); namespace.write(output); } } tag = XMLTools.getCloseTag("mets", "dmdSec"); output.println(tag); } public void copy_metadata(METSDescriptive dst) { Iterator keys = this.map.keySet().iterator(); // get the keys one by one while (keys.hasNext()) { Object nextKey = keys.next(); if (nextKey == null) { continue; } String key = nextKey.toString(); // get every copy of the current namespace name - namespaces may // occur more than once, so this is a List Iterator namespaces = this.map.getAll(key).iterator(); while (namespaces.hasNext()) { METSNamespace namespace = (METSNamespace) namespaces.next(); // for each metadata name Iterator mdnIter = namespace.getMetadataNames(); while (mdnIter.hasNext()) { String md_name = mdnIter.next().toString(); List values = getMetadata(key, md_name); if (values != null) { Iterator valueIter = values.iterator(); while (valueIter.hasNext()) { String value = valueIter.next().toString(); dst.addMetadata("gsdl3",md_name,value); } } } } } } public boolean writeSQL(DocumentInterface document, GS3SQLConnection connection) { // check if this node is in the int sqlId = -1; // System.out.println("Writing " + connection.toString()); GS3SQLSelect select = new GS3SQLSelect("metadata"); select.addField("*"); GS3SQLWhereItem whereItem = new GS3SQLWhereItem("MetaID", "=", this.ID); GS3SQLWhereItem item = new GS3SQLWhereItem("DocID", "=", document.getID().toString()); GS3SQLWhere where = new GS3SQLWhere(whereItem); where.add(item); select.setWhere(where); try { Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery(select.toString()); GS3SQLAction action; if (resultSet.first()) { // the node already exists - no need to do anything as such GS3SQLUpdate update = new GS3SQLUpdate("metadata"); update.setWhere(where); action = update; } else { // Set result set to null, just in case first() didn't work above... // It is a new node and needs writing action = new GS3SQLInsert("metadata"); action.addValue("DocID", document.getID().toString()); action.addValue("MetaID", this.ID); } // always set the group identifier action.addValue("GroupID", this.name); // execute the action as required statement.execute(action.toString()); // get the resultSet again // now execute the select statement again to get the new identifier // 'MetadataRef' // System.out.println(select.toString()); resultSet = statement.executeQuery(select.toString()); if (resultSet.first()) { // get the reference for this item... sqlId = resultSet.getInt("MetadataRef"); } statement.close(); } catch (SQLException sql){ System.err.println("METSDescriptive.writeSQL(): "+sql); } if (sqlId == -1) { return false; } Iterator keys = this.map.keySet().iterator(); // get the keys one by one while (keys.hasNext()){ Object nextKey = keys.next(); if (nextKey == null){ continue; } String key = nextKey.toString(); // get every current namespace - namespaces may // occur more than once, so this is a List Iterator namespaces = this.map.getAll(key).iterator(); // namespaces will write themselves... while (namespaces.hasNext()){ METSNamespace namespace = (METSNamespace) namespaces.next(); if (!namespace.writeSQL(sqlId, connection)){ return false; } } } return true; } public static METSDescriptive readSQL(DocumentInterface document, GS3SQLConnection connection, ResultSet resultSet) { try { String ID = resultSet.getString("MetaID"); String name = resultSet.getString("GroupID"); // create the metadata block object METSDescriptive descriptive = new METSDescriptive(ID, name); // get its metadata reference to retrieve namespaces int metadataRef = resultSet.getInt("MetadataRef"); // query the database for matching namespaces for this metadata block GS3SQLSelect select = new GS3SQLSelect("namespaces"); select.addField("*"); GS3SQLWhereItem whereItem = new GS3SQLWhereItem("MetadataRef", "=", Integer.toString(metadataRef), GS3SQLField.INTEGER_TYPE); GS3SQLWhere where = new GS3SQLWhere(whereItem); select.setWhere(where); Statement statement = connection.createStatement(); ResultSet namespaceSet = statement.executeQuery(select.toString()); // parse through the namespaces, calling the namespace class to create instances as it will if (namespaceSet.first()) { do { METSNamespace namespace = METSNamespace.readSQL(connection, namespaceSet); if (namespace != null) { descriptive.addNamespace(namespace); } else { System.err.println("Null namespace output"); } } while (namespaceSet.next()); } statement.close(); return descriptive; } catch (SQLException sqlEx){ System.err.println("METSDescriptive.readSQL(): "+sqlEx); System.exit(1); } return null; } }