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;
}
}