/**
*#########################################################################
*
* A component of the Gatherer application, part of the Greenstone digital
* library suite from the New Zealand Digital Library Project at the
* University of Waikato, New Zealand.
*
*
*
* Author: John Thompson, Greenstone Digital Library, University of Waikato
*
*
*
* Copyright (C) 1999 New Zealand Digital Library Project
*
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*########################################################################
*/
package org.greenstone.gatherer.shell;
import java.awt.Component;
import java.util.ArrayList;
import org.greenstone.gatherer.Configuration;
import org.greenstone.gatherer.DebugStream;
import org.greenstone.gatherer.Dictionary;
import org.greenstone.gatherer.Gatherer;
import org.greenstone.gatherer.cdm.CollectionDesignManager;
import org.greenstone.gatherer.gui.GProgressBar;
import org.greenstone.gatherer.util.StaticStrings;
/** This implementation of GShellProgressMonitor is designed to parse and translate the progress of a buildcol.pl call.
* @author John Thompson, Greenstone Digital Library, University of Waikato
* @version 2.3
*/
public class GBuildProgressMonitor
implements GShellProgressMonitor {
/** The minimum value = 0 */
static final private int MIN = 0;
/** In order to get a smoothish progress bar, set the maximum to a large number = 1000000 */
static final private int MAX = 1000000;
/** The various states the build progress state machine can be in, starting at the base state. */
static final int BASE = -1;
static final int BUILD = 0;
static final int COMPRESSTEXT = 10;
static final int COMPRESSTEXT_COLLECTTEXTSTATS = 11;
static final int COMPRESSTEXT_CREATINGCOMPRESS = 12;
static final int COMPRESSTEXT_COMPRESSINGTEXT = 13;
static final int INDEX = 20;
static final int INDEX_CREATINGINDEXDIC = 21;
static final int INDEX_INVERTINGTEXT = 22;
static final int INDEX_CREATETHEWEIGHTS = 23;
static final int INDEX_CREATESTEMMEDDIC = 24;
static final int INDEX_CREATINGSTEMINDX = 25;
static final int CREATEINFODATA = 30;
static final int PHIND = 40;
static final int PHIND_EXTRACTINGVOCAB = 41;
static final int PHIND_EXTRACTINGPHRASE = 42;
static final int PHIND_SORTANDRENUMBER = 43;
static final int PHIND_PHRASEDATABASES = 44;
static final int PHIND_WORDLEVELINDEXES = 45;
static final int PHIND_DOCINFODATABASES = 46;
static final int CREATINGAUXILARY = 50;
/** Now the language independant sentinel strings. */
static final String BADARGUMENT_ELEMENT = "BadArgument";
static final String BADARGUMENTVALUE_ELEMENT = "BadArgumentValue";
static final String BADCLASSIFIER_ELEMENT = "BadClassifier";
static final String BADPLUGIN_ELEMENT = "BadPlugin";
static final String BUILD_ELEMENT = "Build";
static final String COLLECTTEXTSTATS_VALUE = "CollectTextStats";
static final String COMPRESSINGTEXT_VALUE = "CompressingText";
static final String COMPRESSTEXT_VALUE = "CompressText";
static final String CREATINGAUXILARY_VALUE = "CreatingAuxilary";
static final String CREATINGCOMPRESS_VALUE = "CreatingCompress";
static final String CREATINGINDEXDIC_VALUE = "CreatingIndexDic";
static final String CREATINGSTEMINDX_VALUE = "CreatingStemIndx";
static final String CREATEINFODATA_VALUE = "CreateInfoData";
static final String CREATESTEMMEDDIC_VALUE = "CreateStemmedDic";
static final String CREATETHEWEIGHTS_VALUE = "CreateTheWeights";
static final String DOCINFODATABASES_VALUE = "DocInfoDatabases";
static final String EXTRACTINGPHRASE_VALUE = "ExtractingPhrase";
static final String EXTRACTINGVOCAB_VALUE = "ExtractingVocab";
static final String FATALERROR_ELEMENT = "FatalError";
static final String INDEX_VALUE = "Index";
static final String INVERTINGTEXT_VALUE = "InvertingText";
static final String NAME_ATTRIBUTE = "name";
static final String PHASE_ELEMENT = "Phase";
static final String PHIND_VALUE = "Phind";
static final String PHRASEDATABASES_VALUE = "PhraseDatabases";
static final String SKIPCREATINGCOMP_VALUE = "SkipCreatingComp";
static final String SORTANDRENUMBER_VALUE = "SortAndRenumber";
static final String SOURCE_ATTRIBUTE = "source";
static final String STAGE_ELEMENT = "Stage";
static final String WARNING_ELEMENT = "Warning";
static final String WORDLEVELINDEXES_VALUE = "WordLevelIndexes";
static final private String ARGUMENT_ATTRIBUTE = "a";
static final private String CLASSIFIER_ATTRIBUTE = "c";
static final private String PLUGIN_ATTRIBUTE = "p";
/** Indicates if the GUI has asked the process this object monitors to stop. */
private boolean stop = false;
/** The current number of stages we have completed. */
private int current_stages = 0;
/** The number of stages we are expecting to complete. */
private int expected_stages = 0;
private int threshold = Configuration.LIBRARIAN_MODE;
/** The current state we are in. The state will change depending on the next flag recieved. */
private int state = BASE;
/** The progress bar this monitor updates. */
private GProgressBar progress_bar;
/** A progress bar that is shared between this this listener and the Import monitor. */
private GProgressBar shared_progress_bar;
/** Construct a new GBuildProgressMonitor. */
public GBuildProgressMonitor(GProgressBar shared_progress_bar) {
this.shared_progress_bar = shared_progress_bar;
progress_bar = new GProgressBar();
progress_bar.setMaximum(MAX);
progress_bar.setMinimum(MIN);
progress_bar.setString(null);
progress_bar.setStringPainted(true);
setValue(MIN);
}
/** Method to register a new progress bar with this monitor.
* @param progress_bar The new GProgressBar.
*/
public void addProgressBar(GProgressBar progress_bar) {
this.progress_bar = progress_bar;
progress_bar.setMaximum(MAX);
progress_bar.setMinimum(MIN);
progress_bar.setStringPainted(false);
progress_bar.setString(null);
setValue(MIN);
}
/** Determine the script exit value according to the progress monitor. This gets around a problem where several script failures actually result with a successful exit value.
* @return A int with a value of zero if and only if the script was successful.
*/
public int exitValue() {
if(state == BASE) {
return 0;
}
return 1;
}
/** Method to retrieve whatever control is being used as the progress indicator. Usually a GProgressBar but there may be others implemented later.
* @return A Component on which the progress of the process is being displayed.
*/
public Component getProgress() {
return progress_bar;
}
public GProgressBar getSharedProgress() {
return shared_progress_bar;
}
public void messageOnProgressBar(String message)
{
if (message!=null && !message.equals("")) {
progress_bar.setString(message);
shared_progress_bar.setString(message);
}
else {
progress_bar.setString(null);
shared_progress_bar.setString(null);
}
}
/** Method to determine the state of the stop flag, which may be set by the visual component that created this monitor.
* @return A boolean indicating if the process has been asked to stop.
*/
public synchronized boolean hasSignalledStop() {
return stop;
}
/** Inform the progress bar that it should programatically increment progress by one step. */
public void increment() {
// There is no reason this should be called for a build.
}
/** This method is used to 'feed in' a line of text captured from the process.
* @param queue a queue which at the moment should contain a single GShellEvent
*/
public void process(ArrayList queue) {
GShellEvent event = (GShellEvent) queue.get(0);
// Remove 'build.pl> ' bit
String line = event.getMessage();
line = line.substring(line.indexOf(StaticStrings.GREATER_THAN_CHARACTER) + 1);
line = line.trim();
// We are only really interested in processing pseudo-XML tags
if(line.startsWith(StaticStrings.LESS_THAN_CHARACTER) && line.endsWith(StaticStrings.GREATER_THAN_CHARACTER)) {
// All events are vetoed
event.veto();
// Create a new element from it
GShellElement element = new GShellElement(line);
// Now change states within the state machine as appropriate. A state change may subsequently incur a progress update.
String name = null;
String value = null;
switch(state) {
case BASE:
// The only thing we would expect is a build message
name = element.getElementName();
if(name.equals(BUILD_ELEMENT)) {
progress_bar.setIndeterminate(false);
state = BUILD;
// Produce a lower detail message if required
if(Configuration.getMode() <= threshold && !stop) {
queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.Build.BuildBegun1"), event.getStatus()));
}
}
else {
// Unknown command, go indeterminate
DebugStream.println("Unknown name: " + name);
progress_bar.setIndeterminate(true);
}
name = null;
break;
case BUILD:
// The only thing we would expect is a stage element or a
// bad classifier arg name
name = element.getElementName();
if(name.equals(STAGE_ELEMENT)) {
progress_bar.setIndeterminate(false);
// Now we determine what stage we are moving to
value = element.getAttribute(NAME_ATTRIBUTE);
if(value.equals(COMPRESSTEXT_VALUE)) {
state = COMPRESSTEXT;
if(Configuration.getMode() <= threshold) {
queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.Build.CompressText"), event.getStatus()));
}
}
else if(value.equals(INDEX_VALUE)) {
state = INDEX;
if(Configuration.getMode() <= threshold) {
String args[] = new String[1];
args[0] = element.getAttribute(SOURCE_ATTRIBUTE);
queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.Build.Index", args), event.getStatus()));
args = null;
}
}
else if(value.equals(CREATEINFODATA_VALUE)) {
state = CREATEINFODATA;
if(Configuration.getMode() <= threshold) {
queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.Build.InfoDatabase"), event.getStatus()));
}
}
else if(value.equals(PHIND_VALUE)) {
state = PHIND;
if(Configuration.getMode() <= threshold) {
queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.Build.Phind"), event.getStatus()));
}
}
else if(value.equals(CREATINGAUXILARY_VALUE)) {
state = CREATINGAUXILARY;
if(Configuration.getMode() <= threshold) {
queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.Build.Auxilary"), event.getStatus()));
}
}
else {
// Unknown command, go indeterminate
DebugStream.println("Unknown value: " + value);
progress_bar.setIndeterminate(true);
}
value = null;
}
// The build end message indicates a successfulish build.
else if(name.equals(BUILD_ELEMENT) && element.isClosed()) {
progress_bar.setIndeterminate(false);
setValue(MAX);
state = BASE;
if(Configuration.getMode() <= threshold) {
queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.Build.BuildComplete1"), event.getStatus()));
}
}
// We had a bad argument to a plugin/classifier
else if (name.equals(BADARGUMENT_ELEMENT)) {
if(Configuration.getMode() <= threshold) {
String args[] = new String[1];
args[0] = element.getAttribute(ARGUMENT_ATTRIBUTE);
queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.BadArgument", args), event.getStatus()));
args = null;
}
}
// We had a bad argument value to a plugin/clasifier
else if (name.equals(BADARGUMENTVALUE_ELEMENT)) {
if(Configuration.getMode() <= threshold) {
String args[] = new String[1];
args[0] = element.getAttribute(ARGUMENT_ATTRIBUTE);
queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.BadArgumentValue", args), event.getStatus()));
args = null;
}
}
// And this one tellsa us the plugin
else if (name.equals(BADPLUGIN_ELEMENT)) {
if(Configuration.getMode() <= threshold) {
String args[] = new String[1];
args[0] = element.getAttribute(PLUGIN_ATTRIBUTE);
queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.BadPluginOptions", args), event.getStatus()));
args = null;
}
}
// or the classifier
else if (name.equals(BADCLASSIFIER_ELEMENT)) {
if(Configuration.getMode() <= threshold) {
String args[] = new String[1];
args[0] = element.getAttribute(CLASSIFIER_ATTRIBUTE);
queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.BadClassifierOptions", args), event.getStatus()));
args = null;
}
}
else {
// Unknown command, go indeterminate
DebugStream.println("Unknown name: " + name);
progress_bar.setIndeterminate(true);
}
name = null;
break;
case COMPRESSTEXT:
// We are either dealing with a phase, aa fatal error or an end stage
name = element.getElementName();
if(name.equals(PHASE_ELEMENT)) {
// There are three phases within compressing text (or of four possible ones)
value = element.getAttribute(NAME_ATTRIBUTE);
if(value.equals(COLLECTTEXTSTATS_VALUE)) {
// Nothing to do
}
else if(value.equals(CREATINGCOMPRESS_VALUE) || value.equals(SKIPCREATINGCOMP_VALUE)) {
// One third complete
progress_bar.setIndeterminate(false);
setValue((int)(((double)current_stages + 0.3d) * MAX) / expected_stages);
}
else if(value.equals(COMPRESSINGTEXT_VALUE)) {
// Two thirds complete
progress_bar.setIndeterminate(false);
setValue((int)(((double)current_stages + 0.6d) * MAX) / expected_stages);
}
else {
// Unknown command, go indeterminate
DebugStream.println("Unknown value: " + value);
progress_bar.setIndeterminate(true);
}
value = null;
}
// Note that a fatal error is always followed by an end-stage, but not an end build. Thus the final state will be build (not base) and calls to exitValue() with indicate failure.
else if(name.equals(FATALERROR_ELEMENT)) {
// On simplier modes produce a meaningful message
queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.Build.FatalError"), event.getStatus()));
}
else if(name.equals(STAGE_ELEMENT) && element.isClosed()) {
// Increase progress so that stage is complete.
current_stages++;
progress_bar.setIndeterminate(false);
setValue((current_stages * MAX) / expected_stages);
state = BUILD;
}
else {
// Unknown command, go indeterminate
DebugStream.println("Unknown name: " + name);
progress_bar.setIndeterminate(true);
}
name = null;
break;
case INDEX:
/** @todo - If on lower modes output a meaningful message. */
name = element.getElementName();
if(name.equals(PHASE_ELEMENT)) {
value = element.getAttribute(NAME_ATTRIBUTE);
if(value.equals(CREATINGINDEXDIC_VALUE)) {
// Nothing to do
}
else if(value.equals(INVERTINGTEXT_VALUE)) {
// One fifth complete
progress_bar.setIndeterminate(false);
setValue((int)(((double)current_stages + 0.2d) * MAX) / expected_stages);
}
else if(value.equals(CREATETHEWEIGHTS_VALUE)) {
// Two fifths complete
progress_bar.setIndeterminate(false);
setValue((int)(((double)current_stages + 0.4d) * MAX) / expected_stages);
}
else if(value.equals(CREATESTEMMEDDIC_VALUE)) {
// Three fifths complete
progress_bar.setIndeterminate(false);
setValue((int)(((double)current_stages + 0.6d) * MAX) / expected_stages);
}
else if(value.equals(CREATINGSTEMINDX_VALUE)) {
// Four fifths complete
progress_bar.setIndeterminate(false);
setValue((int)(((double)current_stages + 0.8d) * MAX) / expected_stages);
}
else {
// Unknown command, go indeterminate
DebugStream.println("Unknown value: " + value);
progress_bar.setIndeterminate(true);
}
value = null;
}
else if(name.equals(WARNING_ELEMENT)) {
/** @todo - If on lower modes output a meaningful message. */
}
// Note that a fatal error is always followed by an end-stage, but not an end build. Thus the final state will be build (not base) and calls to exitValue() with indicate failure.
else if(name.equals(FATALERROR_ELEMENT)) {
// On lower modes output a meaningful message.
}
else if(name.equals(STAGE_ELEMENT) && element.isClosed()) {
// Increase progress so that stage is complete.
current_stages++;
progress_bar.setIndeterminate(false);
setValue((current_stages * MAX) / expected_stages);
state = BUILD;
}
else {
// Unknown command, go indeterminate
DebugStream.println("Unknown name: " + name);
progress_bar.setIndeterminate(true);
}
name = null;
break;
case PHIND:
name = element.getElementName();
if(name.equals(PHASE_ELEMENT)) {
value = element.getAttribute(NAME_ATTRIBUTE);
if(value.equals(EXTRACTINGVOCAB_VALUE)) {
// Nothing to do
}
else if(value.equals(EXTRACTINGPHRASE_VALUE)) {
// One sixth complete
progress_bar.setIndeterminate(false);
setValue((int)(((double)current_stages + 0.16d) * MAX) / expected_stages);
}
else if(value.equals(SORTANDRENUMBER_VALUE)) {
// Two sixths complete
progress_bar.setIndeterminate(false);
setValue((int)(((double)current_stages + 0.33d) * MAX) / expected_stages);
}
else if(value.equals(PHRASEDATABASES_VALUE)) {
// Three sixths complete
progress_bar.setIndeterminate(false);
setValue((int)(((double)current_stages + 0.5d) * MAX) / expected_stages);
}
else if(value.equals(WORDLEVELINDEXES_VALUE)) {
// Four sixths complete
progress_bar.setIndeterminate(false);
setValue((int)(((double)current_stages + 0.66d) * MAX) / expected_stages);
}
else if(value.equals(DOCINFODATABASES_VALUE)) {
// Five sixths complete
progress_bar.setIndeterminate(false);
setValue((int)(((double)current_stages + 0.83d) * MAX) / expected_stages);
}
else {
// Unknown command, go indeterminate
DebugStream.println("Unknown value: " + value);
progress_bar.setIndeterminate(true);
}
value = null;
}
else if(name.equals(STAGE_ELEMENT) && element.isClosed()) {
// Increase progress so that stage is complete.
current_stages++;
progress_bar.setIndeterminate(false);
setValue((current_stages * MAX) / expected_stages);
state = BUILD;
}
else {
// Unknown command, go indeterminate
DebugStream.println("Unknown name: " + name);
progress_bar.setIndeterminate(true);
}
name = null;
break;
case CREATEINFODATA:
case CREATINGAUXILARY:
name = element.getElementName();
if(name.equals(STAGE_ELEMENT) && element.isClosed()) {
// Another stage complete
current_stages++;
progress_bar.setIndeterminate(false);
setValue((current_stages * MAX) / expected_stages);
state = BUILD;
}
else {
// Unknown command, go indeterminate
DebugStream.println("Unknown name: " + name);
progress_bar.setIndeterminate(true);
}
name = null;
break;
default:
// Unknown command, go indeterminate
DebugStream.println("Unknown name: " + name);
progress_bar.setIndeterminate(true);
}
}
// If we are dealing with lower detail modes then veto any other messages. We do this here as the progress monitors are garuenteed to recieve this message before anything else
else if(Configuration.getMode() <= threshold) {
event.veto();
}
}
public void reset() {
current_stages = 0;
expected_stages = 0;
progress_bar.setIndeterminate(false);
progress_bar.setString(null);
setValue(MIN);
state = BASE;
progress_bar.updateUI();
}
public void saving() {
}
public void setSharedProgressBar(GProgressBar shared_progress_bar) {
this.shared_progress_bar = shared_progress_bar;
}
/** Since the creator of this process monitor is actually in the GUI, this class provides the simpliest way to send a cancel process message between the two.
* @param state The desired state of the stop flag as a boolean.
*/
public synchronized void setStop(boolean state) {
this.stop = state;
progress_bar.setIndeterminate(false);
}
/** This method resets this monitor to the start, reseting the process parsing and progress bar.
* TODO Everthing.
*/
public void start() {
stop = false;
setValue(MIN);
expected_stages = 3; // Always compress text, create info database and auxiliary creation
// Retrieve cdm.
CollectionDesignManager cdm = Gatherer.c_man.getCollection().cdm;
// If we are using MGPP the number of indexes is always 1
if(cdm.index_manager.isMGPP()) {
expected_stages = expected_stages + 1;
} else if (cdm.index_manager.isLucene()) {
// lucene no of indexes == number of levels
int num_levels = cdm.index_manager.getNumLevels();
if (num_levels == 0) num_levels = 1;
expected_stages = expected_stages + num_levels;
}
else {
int num_indexes = cdm.index_manager.getNumIndexes();
int num_partitions = cdm.subcollectionindex_manager.getSize();
if(num_partitions > 0) {
num_indexes = num_indexes * num_partitions;
}
int num_languages = cdm.language_manager.getSize();
if(num_languages > 0) {
num_indexes = num_indexes * num_languages;
}
expected_stages = expected_stages + num_indexes;
}
// And if we have a phind classifier, we have one further index
if(cdm.classifier_manager.isPhindClassifierAssigned()) {
expected_stages = expected_stages + 1;
}
cdm = null;
}
/** This method indicates the process is complete.
* TODO Everthing.
*/
public void stop() {
progress_bar.setIndeterminate(false);
setValue(MAX);
}
private void setValue(int value) {
progress_bar.setValue(value);
// Incrementing the shared progress bar is a little more problematic as we may very well be trying to set it to MIN but instead set it to halfway if we're not careful.
if(value > MIN) {
shared_progress_bar.setValue(MAX + value);
}
}
}