/** *######################################################################### * * 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); } } }