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