package org.greenstone.gsdl3.gs3build.classifier;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.greenstone.gsdl3.gs3build.doctypes.DocumentID;
import org.greenstone.gsdl3.gs3build.util.GS3SQLConnection;
import org.greenstone.gsdl3.gs3build.database.*;
public abstract class AbstractHierarchyNode
{
String prefix; // a basic prefix used in this hierarchy
String descriptor; // the textual descriptor used on screen or long-hand
String name; // the index number, letter assignment or other item
// used to identify the position of the item in the
// hierarchy
String id; // an identifier used by the GLI for maintenance
// purposes; this plays no active role in the
// rebuilding process (at the moment)
List childNodes; // the child classification nodes of this node
List childDocs; // the child documents of this node
List matches; // the other metadata values that may be matched
// against the classifier
AbstractHierarchyNode parent; // the parent of the node
class AbstractHierarchyDocument
{ DocumentID documentId;
String sortKey;
public AbstractHierarchyDocument(DocumentID documentId, String sortKey)
{ this.documentId = documentId;
this.sortKey = sortKey;
}
public DocumentID getID()
{ return this.documentId;
}
public String toString()
{ return this.sortKey;
}
}
/**
* Simple node
*/
public AbstractHierarchyNode()
{ this.descriptor = "";
this.name = "";
this.id = "";
this.prefix = "";
this.childNodes = new ArrayList();
this.childDocs = new ArrayList();
this.parent = null;
this.matches = new ArrayList();
}
public AbstractHierarchyNode(String prefix, String name, String id, String descriptor)
{ this.descriptor = descriptor;
this.name = name;
this.id = id;
this.prefix = prefix;
this.childNodes = new ArrayList();
this.childDocs = new ArrayList();
this.parent = null;
this.matches = new ArrayList();
this.matches.add(this.id);
this.matches.add(this.name);
}
public void addChild(AbstractHierarchyNode child)
{ this.childNodes.add(child);
child.setParent(this);
/**
if (this.id == null) {
System.out.println(child.id.toString() + " added to root");
}
else {
System.out.println(child.id.toString() + " added to " + this.id);
}
*/
}
public boolean add(AbstractHierarchyNode child)
{ if (this.id != null && this.id.length() > 0 &&
!child.id.startsWith(this.id + "."))
{ return false;
}
Iterator subNodes = this.childNodes.iterator();
while (subNodes.hasNext()) {
AbstractHierarchyNode subNode = (AbstractHierarchyNode) subNodes.next();
if (subNode.add(child)) {
return true;
}
}
this.addChild(child);
return true;
}
public void addDocument(DocumentID document, String sortKey)
{ this.childDocs.add(new AbstractHierarchyDocument(document, sortKey));
if (sortKey != null) {
int first = 0;
int last = this.childDocs.size() - 1;
while (first != last) {
int at = (first + last) / 2;
if (this.childDocs.get(at) == null) {
last = at;
continue;
}
if (this.childDocs.get(at).toString().compareTo(sortKey) > 0) {
last = at;
}
else {
first = at + 1;
}
}
Object newItem = this.childDocs.get(this.childDocs.size() - 1);
last = this.childDocs.size() - 1;
while (last > first) {
this.childDocs.set(last, this.childDocs.get(last-1));
last --;
}
this.childDocs.set(first, newItem);
}
}
public void setParent(AbstractHierarchyNode parent)
{ this.parent = parent;
}
public AbstractHierarchyNode getParent()
{ return this.parent;
}
public String getParentId()
{
if (this.id == null) {
return "";
}
int dotAt = this.id.lastIndexOf('.');
if (dotAt < 0) {
return "";
}
return this.id.substring(0, dotAt);
}
public void setDescriptor(String descriptor)
{ this.descriptor = descriptor;
}
public void setID(String id)
{ this.id = id;
this.matches.add(id);
}
public String getID()
{ return this.id;
}
public int noOfChildDocs()
{ return this.childDocs.size();
}
public int noOfLeafDocs()
{ int total = this.childDocs.size();
Iterator iterator = this.childNodes.iterator();
while (iterator.hasNext()) {
AbstractHierarchyNode childNode = (AbstractHierarchyNode) iterator.next();
total += childNode.noOfLeafDocs();
}
return total;
}
/**
* Set the name for this hierarchy node - i.e. its brief description
*
* @param String
the new node name.
*/
public void setName(String name)
{ this.name = name;
}
/**
* Get the name for this hierarchy node - i.e. its brief description
*
* @return String
the node's name.
*/
public String getName()
{ return this.name;
}
/**
* Add another string which can be used to describe this hierarchy node - i.e. one which if
* found in a document would indicate that that document belongs to this hierarchy node.
*
* @param String
the string to match.
*/
public void addMatch(String match)
{ this.matches.add(match);
}
/**
* Check if a given string matches any of the values given for this hierarchy node...
*
* @param String
the descriptive string to be compared against the hierarchy
* @return boolean
true
whether the string matches this hierarchy
*/
public boolean isMatch (String toMatch)
{ Iterator thisMatch = this.matches.iterator();
while (thisMatch.hasNext())
{ String thisMatchText = thisMatch.next().toString();
if (thisMatchText.equals(toMatch))
{ return true;
}
}
return false;
}
/**
* Take a document, and find the classifications that it matches against in
* the current hierarchy.
*
* @param DocumentID
the id of the document being classified
* @param List
the values against which the classifier should
* test for the document being a match - i.e. the pertinent document
* property values.
* @param ClassifierObserverInterface
* object modifies the document with information about the
* classifications that it fell within.
*/
abstract public void getClassifications(DocumentID documentID, List values, String sortKey,
ClassifierObserverInterface observer);
public boolean writeSQL(GS3SQLConnection connection)
{
int classifyRef;
GS3SQLAction action = null;
GS3SQLSelect select;
GS3SQLInsert insert;
// Get own full id
String fullId = this.id.length() > 0 ? this.prefix+"."+this.id : this.prefix;
// check for an existing instance of this classifier
select = new GS3SQLSelect("classifiers");
select.addField("ClassifyRef");
GS3SQLWhereItem whereItem = new GS3SQLWhereItem("ClassifyID", "=", fullId);
GS3SQLWhere where = new GS3SQLWhere(whereItem);
select.setWhere(where);
connection.execute(select.toString());
// update or insert the classifier as required
try {
ResultSet results = connection.getResultSet();
if (results != null && results.first()) {
GS3SQLUpdate update = new GS3SQLUpdate("classifiers");
update.setWhere(where);
action = update;
classifyRef = results.getInt("ClassifyRef");
}
else {
insert = new GS3SQLInsert("classifiers");
if (this.id == null || this.id.length() == 0) {
insert.addValue("ParentID", "");
}
else {
String parentId = this.getParentId();
if (parentId.length() > 0) {
insert.addValue("ParentID", this.prefix+"."+this.getParentId());
}
else {
insert.addValue("ParentID", this.prefix);
}
}
action = insert;
}
action.addValue("ClassifyID", fullId);
action.addValue("Name", this.name);
action.addValue("Description", this.descriptor);
String tailId = "0";
if (this.id.length() > 0) {
int lastDot = this.id.lastIndexOf('.');
if (lastDot >= 0) {
tailId = this.id.substring(lastDot+1);
}
else {
tailId = this.id;
}
}
action.addValue("ClassifyOrder", tailId, GS3SQLField.INTEGER_TYPE);
action.addValue("NumLeafDocs", Integer.toString(this.noOfLeafDocs()), GS3SQLField.INTEGER_TYPE);
connection.execute(action.toString());
classifyRef = -1;
}
catch (SQLException sqlEx) {
if (action == null) {
System.err.println(sqlEx);
}
else {
System.err.println(sqlEx + " " + action.toString());
}
return false;
}
// get the ClassifyRef if we don't already have it (have done a
// insert action above)...
if (classifyRef == -1) {
connection.execute(select.toString());
try {
ResultSet results = connection.getResultSet();
if (results == null || !results.first()) {
return false;
}
classifyRef = results.getInt("ClassifyRef");
}
catch (SQLException sqlEx) {
System.err.println(sqlEx);
return false;
}
}
else {
// TODO: clear 'dead' child classifications
// delete child documents
GS3SQLDelete delete = new GS3SQLDelete("classdocuments");
delete.setWhere(where);
connection.execute(delete.toString());
}
// post the child nodes...
Iterator iterator = this.childNodes.iterator();
while (iterator.hasNext()) {
AbstractHierarchyNode childNode = (AbstractHierarchyNode) iterator.next();
if (!childNode.writeSQL(connection)) {
System.out.println("Failed to write " );
return false;
}
}
// note the child documents...
iterator = this.childDocs.iterator();
int order = 1;
while (iterator.hasNext()) {
AbstractHierarchyDocument hierarchyDoc = (AbstractHierarchyDocument) iterator.next();
DocumentID docId = hierarchyDoc.getID();
insert = new GS3SQLInsert("classdocuments");
insert.addValue("ClassifyRef", Integer.toString(classifyRef), GS3SQLField.INTEGER_TYPE);
insert.addValue("DocID", docId.toString());
insert.addValue("DocOrder", Integer.toString(order), GS3SQLField.INTEGER_TYPE);
connection.execute(insert.toString());
order ++;
}
return true;
}
}