PageRenderTime 133ms CodeModel.GetById 23ms app.highlight 95ms RepoModel.GetById 0ms app.codeStats 1ms

/tags/3.1.0/src/org/getopt/luke/Luke.java

http://luke.googlecode.com/
Java | 4726 lines | 4239 code | 178 blank | 309 comment | 988 complexity | ed6ce736556028291fd87ef78bffcd8e MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/*
   2 * Licensed to the Apache Software Foundation (ASF) under one or more
   3 * contributor license agreements.  See the NOTICE file distributed with
   4 * this work for additional information regarding copyright ownership.
   5 * The ASF licenses this file to You under the Apache License, Version 2.0
   6 * (the "License"); you may not use this file except in compliance with
   7 * the License.  You may obtain a copy of the License at
   8 *
   9 *     http://www.apache.org/licenses/LICENSE-2.0
  10 *
  11 * Unless required by applicable law or agreed to in writing, software
  12 * distributed under the License is distributed on an "AS IS" BASIS,
  13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 * See the License for the specific language governing permissions and
  15 * limitations under the License.
  16 */
  17package org.getopt.luke;
  18
  19import java.awt.Color;
  20import java.awt.Font;
  21import java.awt.GraphicsEnvironment;
  22import java.awt.Toolkit;
  23import java.awt.datatransfer.Clipboard;
  24import java.awt.datatransfer.ClipboardOwner;
  25import java.awt.datatransfer.StringSelection;
  26import java.awt.datatransfer.Transferable;
  27import java.io.*;
  28import java.lang.reflect.Constructor;
  29import java.lang.reflect.Method;
  30import java.text.DecimalFormat;
  31import java.text.NumberFormat;
  32import java.util.*;
  33import java.util.Map.Entry;
  34import java.util.zip.GZIPOutputStream;
  35
  36import javax.swing.JFileChooser;
  37import javax.swing.UIManager;
  38
  39import org.apache.lucene.LucenePackage;
  40import org.apache.lucene.analysis.*;
  41import org.apache.lucene.analysis.payloads.PayloadHelper;
  42import org.apache.lucene.analysis.standard.StandardAnalyzer;
  43import org.apache.lucene.document.DateTools;
  44import org.apache.lucene.document.Document;
  45import org.apache.lucene.document.Field;
  46import org.apache.lucene.document.NumberTools;
  47import org.apache.lucene.document.Field.Index;
  48import org.apache.lucene.document.Field.Store;
  49import org.apache.lucene.index.*;
  50import org.apache.lucene.index.IndexReader.FieldOption;
  51import org.apache.lucene.index.IndexWriter.MaxFieldLength;
  52import org.apache.lucene.misc.SweetSpotSimilarity;
  53import org.apache.lucene.queryParser.QueryParser;
  54import org.apache.lucene.search.*;
  55import org.apache.lucene.search.BooleanClause.Occur;
  56import org.apache.lucene.search.payloads.PayloadNearQuery;
  57import org.apache.lucene.search.payloads.PayloadTermQuery;
  58import org.apache.lucene.search.similar.MoreLikeThis;
  59import org.apache.lucene.search.spans.SpanFirstQuery;
  60import org.apache.lucene.search.spans.SpanNearQuery;
  61import org.apache.lucene.search.spans.SpanNotQuery;
  62import org.apache.lucene.search.spans.SpanOrQuery;
  63import org.apache.lucene.search.spans.SpanQuery;
  64import org.apache.lucene.search.spans.SpanTermQuery;
  65import org.apache.lucene.search.spans.Spans;
  66import org.apache.lucene.store.*;
  67import org.apache.lucene.util.NumericUtils;
  68import org.apache.lucene.util.Version;
  69import org.apache.lucene.xmlparser.CoreParser;
  70import org.apache.lucene.xmlparser.CorePlusExtensionsParser;
  71import org.getopt.luke.DocReconstructor.Reconstructed;
  72import org.getopt.luke.decoders.BinaryDecoder;
  73import org.getopt.luke.decoders.DateDecoder;
  74import org.getopt.luke.decoders.Decoder;
  75import org.getopt.luke.decoders.NumDoubleDecoder;
  76import org.getopt.luke.decoders.NumFloatDecoder;
  77import org.getopt.luke.decoders.NumIntDecoder;
  78import org.getopt.luke.decoders.NumLongDecoder;
  79import org.getopt.luke.decoders.OldDateFieldDecoder;
  80import org.getopt.luke.decoders.OldNumberToolsDecoder;
  81import org.getopt.luke.decoders.StringDecoder;
  82import org.getopt.luke.plugins.ScriptingPlugin;
  83import org.getopt.luke.xmlQuery.XmlQueryParserFactory;
  84import org.getopt.luke.xmlQuery.CorePlusExtensionsParserFactory;
  85
  86import thinlet.FrameLauncher;
  87import thinlet.Thinlet;
  88
  89/**
  90 * This class allows you to browse a <a href="jakarta.apache.org/lucene">Lucene
  91 * </a> index in several ways - by document, by term, by query, and by most
  92 * frequent terms.
  93 * 
  94 * @author Andrzej Bialecki
  95 *  
  96 */
  97public class Luke extends Thinlet implements ClipboardOwner {
  98
  99  private Directory dir = null;
 100  String pName = null;
 101  private IndexReader ir = null;
 102  private IndexSearcher is = null;
 103  private boolean slowAccess = false;
 104  private Collection<String> fn = null;
 105  private String[] idxFields = null;
 106  private HashMap<String, FieldTermCount> termCounts = new HashMap<String, FieldTermCount>();
 107  private List<LukePlugin> plugins = new ArrayList<LukePlugin>();
 108  private Object errorDlg = null;
 109  private Object infoDlg = null;
 110  private Object statmsg = null;
 111  private Object slowstatus = null;
 112  private Object slowmsg = null;
 113  private Analyzer stdAnalyzer = new StandardAnalyzer(Version.LUCENE_CURRENT);
 114  private Analyzer analyzer = null;
 115  //private QueryParser qp = null;
 116  private boolean readOnly = false;
 117  private boolean ram = false;
 118  private boolean keepCommits = false;
 119  private boolean multi = false;
 120  private int tiiDiv = 1;
 121  private IndexCommit currentCommit = null;
 122  private Similarity similarity = null;
 123  private Object lastST;
 124  private HashMap<String, Decoder> decoders = new HashMap<String, Decoder>();
 125  private Decoder defDecoder = new StringDecoder();
 126  
 127  /** Default salmon theme. */
 128  public static final int THEME_DEFAULT     = 0;
 129  /** Gray theme. */
 130  public static final int THEME_GRAY        = 1;
 131  /** Sandstone theme. */
 132  public static final int THEME_SANDSTONE   = 2;
 133  /** Sky blue theme. */
 134  public static final int THEME_SKY         = 3;
 135  /** Navy blue reverse theme. */
 136  public static final int THEME_NAVY        = 4;
 137  
 138  /** Theme color contants. */
 139  public int[][] themes = {
 140          {0xece9d0, 0x000000, 0xf5f4f0, 0x919b9a, 0xb0b0b0, 0xeeeeee, 0xb9b9b9, 0xff8080, 0xc5c5dd}, // default
 141          {0xe6e6e6, 0x000000, 0xffffff, 0x909090, 0xb0b0b0, 0xededed, 0xb9b9b9, 0x89899a, 0xc5c5dd}, // gray
 142          {0xeeeecc, 0x000000, 0xffffff, 0x999966, 0xb0b096, 0xededcb, 0xcccc99, 0xcc6600, 0xffcc66}, // sandstone
 143          {0xf0f0ff, 0x0000a0, 0xffffff, 0x8080ff, 0xb0b0b0, 0xededed, 0xb0b0ff, 0xff0000, 0xfde0e0}, // sky
 144          {0x6375d6, 0xffffff, 0x7f8fdd, 0xd6dff5, 0x9caae5, 0x666666, 0x003399, 0xff3333, 0x666666}  // navy
 145  };
 146
 147  private int numTerms = 0;
 148  private static boolean exitOnDestroy = false;
 149  private Class[] analyzers = null;
 150
 151  private String baseDir = null;
 152
 153  private Class[] defaultAnalyzers = { SimpleAnalyzer.class, StandardAnalyzer.class, StopAnalyzer.class,
 154      WhitespaceAnalyzer.class };
 155
 156  private static final String MSG_NOINDEX = "FAILED: No index, or index is closed. Reopen it.";
 157  private static final String MSG_READONLY = "FAILED: Read-Only index.";
 158  private static final String MSG_CONV_ERROR = "Some values could not be properly represented in this format. " + 
 159                      "They are marked in grey and presented as a hex dump.";
 160
 161  /** Default constructor, loads preferences, initializes plugins and GUI. */ 
 162  public Luke() {
 163    super();
 164    Prefs.load();
 165    try {
 166      UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
 167    } catch (Exception e) {}
 168    setTheme(Prefs.getInteger(Prefs.P_THEME, THEME_DEFAULT));
 169    String fontName = Prefs.getProperty(Prefs.P_FONT_NAME, "SansSerif");
 170    String fontSize = Prefs.getProperty(Prefs.P_FONT_SIZE, "12.0");
 171    float fsize = 12.0f;
 172    try {
 173      fsize = Float.parseFloat(fontSize);
 174    } catch (Exception e) {};
 175    Font f = new Font(fontName, Font.PLAIN, (int)fsize);
 176    setFont(f);
 177    addComponent(this, "/xml/luke.xml", null, null);
 178    errorDlg = addComponent(null, "/xml/error.xml", null, null);
 179    infoDlg = addComponent(null, "/xml/info.xml", null, null);
 180    statmsg = find("statmsg");
 181    slowstatus = find("slowstat");
 182    slowmsg = find(slowstatus, "slowmsg");
 183    // populate analyzers
 184    try {
 185      Class[] an = ClassFinder.getInstantiableSubclasses(Analyzer.class);
 186      if (an == null || an.length == 0) {
 187        analyzers = defaultAnalyzers;
 188      } else {
 189        HashSet<Class> uniq = new HashSet<Class>(Arrays.asList(an));
 190        analyzers = (Class[])uniq.toArray(new Class[uniq.size()]);
 191      }
 192      Object cbType = find("cbType");
 193      populateAnalyzers(cbType);
 194    } catch (Exception e) {
 195      e.printStackTrace();
 196    }
 197    loadPlugins();
 198    
 199  }
 200
 201  /**
 202   * Set color theme for the UI.
 203   * @param which one of the predefined themes. For custom themes use {@link Thinlet#setColors(int, int, int, int, int, int, int, int, int)}.
 204   */
 205  public void setTheme(int which) {
 206    if (which < 0 || which >= themes.length) which = THEME_DEFAULT;
 207    int[] t = themes[which];
 208    setColors(t[0], t[1], t[2], t[3], t[4], t[5], t[6], t[7], t[8]);
 209    Prefs.setProperty(Prefs.P_THEME, which + "");
 210  }
 211  
 212  /**
 213   * Action handler to select color theme.
 214   * @param menu
 215   */
 216  public void actionTheme(Object menu) {
 217    String which = (String)getProperty(menu, "t");
 218    int t = THEME_DEFAULT;
 219    try {
 220      t = Integer.parseInt(which);
 221    } catch (Exception e) {};
 222    setTheme(t);
 223  }
 224  
 225  /**
 226   * Populate a combobox with the current list of analyzers.
 227   * @param combo
 228   */
 229  public void populateAnalyzers(Object combo) {
 230    removeAll(combo);
 231    String[] aNames = new String[analyzers.length];
 232    for (int i = 0; i < analyzers.length; i++) {
 233      aNames[i] = analyzers[i].getName();
 234    }
 235    Arrays.sort(aNames);
 236    for (int i = 0; i < aNames.length; i++) {
 237      Object choice = create("choice");
 238      setString(choice, "text", aNames[i]);
 239      add(combo, choice);
 240      if (i == 0) {
 241        setString(combo, "text", aNames[i]);
 242      }
 243    }
 244    int lastAnalyzerIdx = 0;
 245    String lastAnalyzer = Prefs.getProperty(Prefs.P_ANALYZER);
 246    if (lastAnalyzer != null) lastAnalyzerIdx = getIndex(combo, lastAnalyzer);
 247    if (lastAnalyzerIdx < 0) lastAnalyzerIdx = 0;
 248    setInteger(combo, "selected", lastAnalyzerIdx);
 249  }
 250
 251  /**
 252   * Return an array of available Analyzer implementations.
 253   * @return
 254   */
 255  public Class[] getAnalyzers() {
 256    return analyzers;
 257  }
 258
 259  /**
 260   * Loads plugins. Plugins are first searched from the CLASSPATH, and then from a
 261   * plugin list contained in a resource file "/.plugins". The "/.plugins" resource file
 262   * has a simple format - one fully qualified class name per line. Blank lines and
 263   * lines starting with '#' are ignored.
 264   */
 265  private void loadPlugins() {
 266    List pluginClasses = new ArrayList();
 267    // try to find all plugins
 268    try {
 269      Class classes[] = ClassFinder.getInstantiableSubclasses(LukePlugin.class);
 270      if (classes != null && classes.length > 0) {
 271        pluginClasses.addAll(Arrays.asList(classes));
 272      }
 273    } catch (Exception e) {
 274      e.printStackTrace();
 275    }
 276    // load plugins declared in the ".plugins" file
 277    try {
 278      InputStream is = getClass().getResourceAsStream("/.plugins");
 279      if (is != null) {
 280        BufferedReader br = new BufferedReader(new InputStreamReader(is));
 281        String line = null;
 282        while ((line = br.readLine()) != null) {
 283          if (line.startsWith("#")) continue;
 284          if (line.trim().equals("")) continue;
 285          try {
 286            Class clazz = Class.forName(line.trim());
 287            if (clazz.getSuperclass().equals(LukePlugin.class) && !pluginClasses.contains(clazz)) {
 288              pluginClasses.add(clazz);
 289            }
 290          } catch (Throwable x) {
 291            //
 292          }
 293        }
 294      }
 295    } catch (Exception e) {
 296      e.printStackTrace();
 297    }
 298    try {
 299      StringBuffer errors = new StringBuffer("Unable to load some plugins:");
 300      boolean failures = false;
 301      for (int i = 0; i < pluginClasses.size(); i++) {
 302        try {
 303          LukePlugin plugin = (LukePlugin) ((Class) pluginClasses.get(i)).getConstructor(new Class[0]).newInstance(
 304                  new Object[0]);
 305          String xul = plugin.getXULName();
 306          if (xul == null) continue;
 307          Object ui = parse(xul, plugin);
 308          plugin.setApplication(this);
 309          plugin.setMyUi(ui);
 310          plugins.add(plugin);
 311        } catch (Exception e) {
 312          failures = true;
 313          e.printStackTrace();
 314          errors.append("\n" + pluginClasses.get(i).toString());
 315        }
 316      }
 317      if (failures) {
 318        errorMsg(errors.toString());
 319      }
 320    } catch (Exception e) {
 321      e.printStackTrace();
 322      errorMsg(e.toString());
 323    }
 324    if (plugins.size() == 0) return;
 325    initPlugins();
 326  }
 327
 328  /**
 329   * Create UI for a single plugin.
 330   * @param tabs parent tabbedpane
 331   * @param plugin plugin instance
 332   */
 333  private void addPluginTab(Object tabs, LukePlugin plugin) {
 334    Object tab = create("tab");
 335    setColor(tab, "foreground", new Color(0x006000));
 336    setString(tab, "text", plugin.getPluginName());
 337    setFont(tab, getFont().deriveFont(Font.BOLD));
 338    add(tabs, tab);
 339    Object panel = create("panel");
 340    setInteger(panel, "gap", 2);
 341    setInteger(panel, "weightx", 1);
 342    setInteger(panel, "weighty", 1);
 343    setChoice(panel, "halign", "fill");
 344    setChoice(panel, "valign", "fill");
 345    setInteger(panel, "columns", 1);
 346    add(tab, panel);
 347    Object infobar = create("panel");
 348    setInteger(infobar, "gap", 8);
 349    setInteger(infobar, "top", 2);
 350    setInteger(infobar, "bottom", 2);
 351    setInteger(infobar, "weightx", 1);
 352    setChoice(infobar, "halign", "fill");
 353    setColor(infobar, "background", new Color(0xc0f0c0));
 354    add(panel, infobar);
 355    Object label = create("label");
 356    setString(label, "text", plugin.getPluginInfo());
 357    add(infobar, label);
 358    Object link = create("button");
 359    setChoice(link, "type", "link");
 360    setString(link, "text", plugin.getPluginHome());
 361    putProperty(link, "url", plugin.getPluginHome());
 362    setMethod(link, "action", "goUrl(this)", infobar, this);
 363    add(infobar, link);
 364    add(panel, create("separator"));
 365    add(panel, plugin.getMyUi());
 366  }
 367
 368  /**
 369   * Return the list of active plugin instances.
 370   * @return
 371   */
 372  public List getPlugins() {
 373    return Collections.unmodifiableList(plugins);
 374  }
 375  
 376  /**
 377   * Get an already instantiated plugin, or null if such plugin was
 378   * not loaded on startup.
 379   * @param className fully qualified plugin classname
 380   * @return
 381   */
 382  public LukePlugin getPlugin(String className) {
 383    for (int i = 0; i < plugins.size(); i++) {
 384      Object plugin = plugins.get(i);
 385      if (plugin.getClass().getName().equals(className))
 386        return (LukePlugin)plugin;
 387    }
 388    return null;
 389  }
 390  
 391  Thread statusThread = null;
 392  long lastUpdate = 0;
 393  long statusSleep = 0;
 394  
 395  /**
 396   * Display a message on the status bar for 5 seconds.
 397   * @param msg message to display. Too long messages will be truncated by the UI.
 398   */
 399  public void showStatus(final String msg) {
 400    if (statusThread != null && statusThread.isAlive()) {
 401      setString(statmsg, "text", msg);
 402      statusSleep = 5000;
 403    } else {
 404      statusThread = new Thread() {
 405        public void run() {
 406          statusSleep = 5000;
 407          setString(statmsg, "text", msg);
 408          while (statusSleep > 0) {
 409            try {
 410              sleep(500);
 411            } catch (Exception e) {};
 412            statusSleep -= 500;
 413          }
 414          setString(statmsg, "text", "");
 415        }
 416      };
 417      statusThread.start();
 418    }
 419  }
 420  
 421  /**
 422   * As {@link #showStatus(String)} but also sets the "Last search time" label.
 423   * @param msg
 424   */
 425  public void showSearchStatus(String msg) {
 426    setString(lastST, "text", msg);
 427    showStatus(msg);
 428  }
 429  
 430  long lastSlowUpdate = 0L;
 431  long lastSlowCounter = 0L;
 432  Thread slowThread = null;
 433  long slowSleep = 0;
 434  
 435  public void showSlowStatus(final String msg, final long counter) {
 436    if (slowThread != null && slowThread.isAlive()) {
 437      lastSlowCounter += counter;
 438      setString(slowmsg, "text", msg + " " + lastSlowCounter);
 439      slowSleep = 5000;
 440    } else {
 441      slowThread = new Thread() {
 442        public void run() {
 443          slowSleep = 5000;
 444          lastSlowCounter = counter;
 445          setBoolean(slowstatus, "visible", true);
 446          setString(slowmsg, "text", msg + " " + lastSlowCounter);
 447          while (slowSleep > 0) {
 448            try {
 449              sleep(500);
 450            } catch (Exception e) {};
 451            slowSleep -= 500;
 452          }
 453          setString(slowmsg, "text", "");
 454          setBoolean(slowstatus, "visible", false);
 455        }
 456      };
 457      slowThread.start();
 458    }
 459  }
 460
 461  /**
 462   * Add a Thinlet component from XUL file.
 463   * @param parent add the new component to this parent 
 464   * @param compView path to the XUL resource
 465   * @param handlerStr fully qualified classname of the handler to instantiate,
 466   * or null if the current class will become the handler
 467   * @param argv if not null, these arguments will be passed to the
 468   * appropriate constructor.
 469   * @return
 470   */
 471  public Object addComponent(Object parent, String compView, String handlerStr, Object[] argv) {
 472    Object res = null;
 473    Object handler = null;
 474    try {
 475      if (handlerStr != null) {
 476        if (argv == null) {
 477          handler = Class.forName(handlerStr).getConstructor(new Class[] { Thinlet.class }).newInstance(
 478                  new Object[] { this });
 479        } else {
 480          handler = Class.forName(handlerStr).getConstructor(new Class[] { Thinlet.class, Object[].class })
 481                  .newInstance(new Object[] { this, argv });
 482        }
 483      }
 484      if (handler != null) {
 485        res = parse(compView, handler);
 486      } else res = parse(compView);
 487      if (parent != null) {
 488        if (parent instanceof Thinlet)
 489          add(res);
 490        else add(parent, res);
 491      }
 492      return res;
 493    } catch (Exception exc) {
 494      exc.printStackTrace();
 495      errorMsg(exc.getMessage());
 496      return null;
 497    }
 498  }
 499
 500  /**
 501   * Show a modal error dialog with OK button.
 502   * @param msg error message
 503   */
 504  public void errorMsg(String msg) {
 505    Object fMsg = find(errorDlg, "msg");
 506    setString(fMsg, "text", msg);
 507    add(errorDlg);
 508  }
 509
 510  /**
 511   * Show a modal info dialog with OK button.
 512   * @param msg info message
 513   */
 514  public void infoMsg(String msg) {
 515    Object fMsg = find(infoDlg, "msg");
 516    setString(fMsg, "text", msg);
 517    add(infoDlg);
 518  }
 519
 520  /**
 521   * Show an "Open Index" dialog.
 522   *
 523   */
 524  public void actionOpen() {
 525    Object dialog = addComponent(this, "/xml/lukeinit.xml", null, null);
 526    Object path = find(dialog, "path");
 527    if (this.baseDir != null)
 528      setString(path, "text", this.baseDir);
 529    else setString(path, "text", System.getProperty("user.dir"));
 530  }
 531
 532  /**
 533   * Browse for a directory, and put the selection result in the
 534   * indicated widget.
 535   * @param path Thinlet widget to put the result
 536   */
 537  public void openBrowse(Object path) {
 538    JFileChooser fd = new JFileChooser();
 539    fd.setDialogType(JFileChooser.OPEN_DIALOG);
 540    fd.setDialogTitle("Select Index directory");
 541    fd.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
 542    fd.setFileHidingEnabled(false);
 543    String strPath = getString(path, "text");
 544    if (strPath != null) strPath.trim();
 545    if (strPath != null && strPath.length() > 0)
 546      fd.setCurrentDirectory(new File(strPath));
 547    else if (this.baseDir != null)
 548      fd.setCurrentDirectory(new File(this.baseDir));
 549    else fd.setCurrentDirectory(new File(System.getProperty("user.dir")));
 550    int res = fd.showOpenDialog(this);
 551    File iDir = null;
 552    if (res == JFileChooser.APPROVE_OPTION) iDir = fd.getSelectedFile();
 553    if (iDir != null && iDir.exists()) {
 554      if (!iDir.isDirectory()) iDir = iDir.getParentFile();
 555      setString(path, "text", iDir.toString());
 556    }
 557  }
 558
 559  
 560  /**
 561   * Select an output file name, and put the selection result in the
 562   * indicated widget.
 563   * @param path Thinlet widget to put the result
 564   */
 565  public void saveBrowse(Object path, Object startButton) {
 566    JFileChooser fd = new JFileChooser();
 567    fd.setDialogType(JFileChooser.SAVE_DIALOG);
 568    fd.setDialogTitle("Select Output File");
 569    fd.setFileSelectionMode(JFileChooser.FILES_ONLY);
 570    fd.setFileHidingEnabled(false);
 571    String strPath = getString(path, "text");
 572    if (strPath != null) strPath.trim();
 573    if (strPath != null && strPath.length() > 0)
 574      fd.setCurrentDirectory(new File(strPath));
 575    else if (this.baseDir != null)
 576      fd.setCurrentDirectory(new File(this.baseDir));
 577    else fd.setCurrentDirectory(new File(System.getProperty("user.dir")));
 578    int res = fd.showSaveDialog(this);
 579    setBoolean(startButton, "enabled", false);
 580    File iFile = null;
 581    if (res == JFileChooser.APPROVE_OPTION) iFile = fd.getSelectedFile();
 582    if (iFile != null) {
 583      setString(path, "text", iFile.toString());
 584      setBoolean(startButton, "enabled", true);
 585    }
 586  }
 587
 588  
 589  /**
 590   * Initialize MRU list of indexes in the open index dialog.
 591   * @param dialog
 592   */
 593  public void setupInit(Object dialog) {
 594    Object path = find(dialog, "path");
 595    syncMRU(path);
 596  }
 597
 598  /**
 599   * Attempt to load the index with parameters specified in the dialog.
 600   * <p>NOTE: this method is invoked from the UI. If you need to open an index
 601   * programmatically, you should use {@link #openIndex(String, boolean, boolean, boolean)} instead.</p>
 602   * @param dialog UI dialog with parameters
 603   */
 604  public void openOk(Object dialog) {
 605    Object path = find(dialog, "path");
 606    pName = getString(path, "text").trim();
 607
 608    boolean force = getBoolean(find(dialog, "force"), "selected");
 609    boolean noReader = getBoolean(find(dialog, "cbNoReader"), "selected");
 610    tiiDiv = 1;
 611    try {
 612      tiiDiv = Integer.parseInt(getString(find(dialog, "tiiDiv"), "text"));
 613    } catch (Exception e) {
 614      e.printStackTrace();
 615    }
 616    Object dirImpl = getSelectedItem(find(dialog, "dirImpl"));
 617    String dirClass = null;
 618    if (dirImpl == null) {
 619      dirClass = FSDirectory.class.getName();
 620    } else {
 621      String name = getString(dirImpl, "name");
 622      if (name == null) {
 623        dirClass = getString(dirImpl, "text");
 624      } else {
 625        if (name.equals("fs")) {
 626          dirClass = FSDirectory.class.getName();
 627        } else if (name.equals("mmap")) {
 628          dirClass = MMapDirectory.class.getName();
 629        } else if (name.equals("niofs")) {
 630          dirClass = NIOFSDirectory.class.getName();
 631        }
 632      }
 633    }
 634    if (pName == null || pName.trim().equals("")) {
 635      errorMsg("Invalid path.");
 636      return;
 637    }
 638    readOnly = getBoolean(find(dialog, "ro"), "selected");
 639    ram = getBoolean(find(dialog, "ram"), "selected");
 640    keepCommits = getBoolean(find(dialog, "cbKeepCommits"), "selected");
 641    slowAccess = getBoolean(find(dialog, "cbSlowIO"), "selected");
 642    decoders.clear();
 643    currentCommit = null;
 644    Prefs.addToMruList(pName);
 645    syncMRU(path);
 646    remove(dialog);
 647    if (noReader) {
 648      removeAll();
 649      addComponent(this, "/xml/luke.xml", null, null);
 650      try {
 651        Directory d = openDirectory(dirClass, pName, false);
 652        if (IndexReader.indexExists(d)) {
 653          throw new Exception("there is no valid Lucene index in this directory.");
 654        }
 655        dir = d;
 656        initOverview();
 657        infoMsg("There is no IndexReader - most actions are disabled. " +
 658            "You can open IndexReader from current Directory using 'Re-Open'");
 659      } catch (Exception e) {
 660        errorMsg("ERROR: " + e.toString());
 661      }
 662    } else {
 663      openIndex(pName, force, dirClass, readOnly, ram, keepCommits, null, tiiDiv);
 664    }
 665  }
 666  
 667  public void actionClose() {
 668    if (ir != null) {
 669      try {
 670        if (is != null) is.close();
 671        ir.close();
 672        if (dir != null) dir.close();
 673      } catch (Exception e) {
 674        e.printStackTrace();
 675        errorMsg("Close failed: " + e.getMessage());
 676      }
 677    }
 678    ir = null;
 679    dir = null;
 680    is = null;
 681    removeAll();
 682    addComponent(this, "/xml/luke.xml", null, null);
 683    initPlugins();
 684  }
 685  
 686  public void actionCommit() {
 687    if (ir == null) {
 688      showStatus(MSG_NOINDEX);
 689      return;
 690    }
 691    if (readOnly) {
 692      showStatus(MSG_READONLY);
 693      return;
 694    }
 695    if (!IndexGate.hasChanges(ir)) {
 696      showStatus("No changes - commit ignored.");
 697      return;
 698    }
 699    Object dialog = addComponent(this, "/xml/commit.xml", null, null);
 700    Map userData = ir.getCommitUserData();
 701    TreeMap ud = new TreeMap(userData);
 702    putProperty(dialog, "userData", ud);
 703    _showUserData(dialog);
 704  }
 705  
 706  private void _showUserData(Object dialog) {
 707    Object table = find(dialog, "data");
 708    removeAll(table);
 709    Map<Object,Object> ud = (Map)getProperty(dialog, "userData");
 710    for (Entry e : ud.entrySet()) {
 711      Object row = create("row");
 712      putProperty(row, "key", e.getKey());
 713      add(table, row);
 714      Object cell = create("cell");
 715      setString(cell, "text", e.getKey().toString());
 716      add(row, cell);
 717      cell = create("cell");
 718      setString(cell, "text", e.getValue().toString());
 719      add(row, cell);
 720    }
 721  }
 722  
 723  public void putUserData(Object dialog) {
 724    Object key = find(dialog, "key");
 725    Object value = find(dialog, "value");
 726    String k = getString(key, "text");
 727    String v = getString(value, "text");
 728    if (k.equals("")) {
 729      showStatus("Cannot add empty key.");
 730      return;
 731    }    
 732    Map<Object,Object> ud = (Map)getProperty(dialog, "userData");
 733    ud.put(k, v);
 734    _showUserData(dialog);
 735  }
 736  
 737  public void deleteUserData(Object dialog) {
 738    Object table = find(dialog, "data");
 739    Map ud = (Map)getProperty(dialog, "userData");
 740    Object[] rows = getSelectedItems(table);
 741    if (rows == null || rows.length == 0) {
 742      return;
 743    }
 744    for (Object row : rows) {
 745      Object key = getProperty(row, "key");
 746      ud.remove(key);
 747    }
 748    _showUserData(dialog);
 749  }
 750  
 751  public void commitUserData(Object dialog) {
 752    Map userData = (Map)getProperty(dialog, "userData");
 753    remove(dialog);
 754    try {
 755      ir.flush(userData);
 756      initOverview();
 757      showFiles(dir, Collections.EMPTY_LIST);
 758    } catch (Exception e) {
 759      errorMsg("Error: " + e.toString());
 760    }
 761  }
 762  
 763  public void actionReopen() {
 764    if (dir == null) {
 765      return;
 766    }
 767    openIndex(pName, false, dir.getClass().getName(), readOnly, ram,
 768        keepCommits, currentCommit, tiiDiv);
 769  }
 770  /**
 771   * Open indicated index and re-initialize all GUI and plugins.
 772   * @param pName path to index
 773   * @param force if true, and the index is locked, unlock it first. If false, and
 774   * the index is locked, an error will be reported.
 775   * @param readOnly open in read-only mode, and disallow modifications.
 776   */
 777  public void openIndex(String name, boolean force, String dirImpl, boolean ro,
 778      boolean ramdir, boolean keepCommits, IndexCommit point, int tiiDivisor) {
 779    pName = name;
 780    readOnly = ro;
 781    removeAll();
 782    File baseFileDir = new File(name);
 783    this.baseDir = baseFileDir.toString();
 784    addComponent(this, "/xml/luke.xml", null, null);
 785    statmsg = find("statmsg");
 786    if (dir != null) {
 787      try {
 788        if (ir != null) ir.close();
 789      } catch (Exception e) {}
 790      ;
 791      try {
 792        if (dir != null) dir.close();
 793      } catch (Exception e) {}
 794      ;
 795    }
 796    ArrayList<Directory> dirs = new ArrayList<Directory>();
 797    try {
 798      Directory d = openDirectory(dirImpl, pName, false);
 799      if (IndexWriter.isLocked(d)) {
 800        if (ro) {
 801          errorMsg("Index is locked and Read-Only. Open for read-write and 'Force unlock'.");
 802          d.close();
 803          d = null;
 804          return;
 805        }
 806        if (force) {
 807          IndexWriter.unlock(d);
 808        } else {
 809          errorMsg("Index is locked. Try 'Force unlock' when opening.");
 810          d.close();
 811          d = null;
 812          return;
 813        }
 814      }
 815      boolean existsSingle = false;
 816      try {
 817        existsSingle = IndexReader.indexExists(d);
 818      } catch (Exception e) {
 819        //
 820      }
 821      
 822      if (!existsSingle) { // try multi
 823        File[] files = baseFileDir.listFiles();
 824        for (File f : files) {
 825          if (f.isFile()) {
 826            continue;
 827          }
 828          Directory d1 = openDirectory(dirImpl, f.toString(), false);
 829          if (IndexWriter.isLocked(d1)) {
 830            if (ro) {
 831              errorMsg("Index is locked and Read-Only. Open for read-write and 'Force unlock'.");
 832              d1.close();
 833              d1 = null;
 834              return;
 835            }
 836            if (force) {
 837              IndexWriter.unlock(d1);
 838            } else {
 839              errorMsg("Index is locked. Try 'Force unlock' when opening.");
 840              d1.close();
 841              d1 = null;
 842              return;
 843            }
 844          }
 845          existsSingle = false;
 846          try {
 847            existsSingle = IndexReader.indexExists(d1);
 848          } catch (Exception e) {};
 849          if (!existsSingle) {
 850            d1.close();
 851            continue;
 852          }
 853          dirs.add(d1);
 854        }
 855      } else {
 856        dirs.add(d);
 857      }
 858      
 859      if (dirs.size() == 0) {
 860        errorMsg("No valid directory at the location, try another location.");
 861        return;
 862      }
 863
 864      if (ramdir) {
 865        showStatus("Loading index into RAMDirectory ...");
 866        Directory dir1 = new RAMDirectory();
 867        IndexWriter iw1 = new IndexWriter(dir1, new SimpleAnalyzer(), MaxFieldLength.UNLIMITED);
 868        iw1.addIndexesNoOptimize((Directory[])dirs.toArray(new Directory[dirs.size()]));
 869        iw1.close();
 870        showStatus("RAMDirectory loading done!");
 871        dir.close();
 872        dir = dir1;
 873      }
 874      IndexDeletionPolicy policy;
 875      if (keepCommits) {
 876        policy = new KeepAllIndexDeletionPolicy();
 877      } else {
 878        policy = new KeepLastIndexDeletionPolicy();
 879      }
 880      ArrayList<IndexReader> readers = new ArrayList<IndexReader>();
 881      for (Directory dd : dirs) {
 882        IndexReader reader;
 883        if (tiiDivisor > 1) {
 884          reader = IndexReader.open(dd, policy, ro, tiiDivisor);
 885        } else {
 886          reader = IndexReader.open(dd, policy, ro);
 887        }
 888        readers.add(reader);
 889      }
 890      if (readers.size() == 1) {
 891        ir = readers.get(0);
 892        dir = ir.directory();
 893      } else {
 894        ir = new MultiReader((IndexReader[])readers.toArray(new IndexReader[readers.size()]));
 895      }
 896      is = new IndexSearcher(ir);
 897      // XXX 
 898      slowAccess = false;
 899      initOverview();
 900      initPlugins();
 901      showStatus("Index successfully open.");
 902    } catch (Exception e) {
 903      e.printStackTrace();
 904      errorMsg(e.getMessage());
 905      return;
 906    }
 907  }
 908
 909  /**
 910   * Open a single directory.
 911   * @param dirImpl fully-qualified class name of Directory implementation,
 912   * or "FSDirectory" for {@link FSDirectory}
 913   * @param file index directory
 914   * @param create if true, create a new directory
 915   * @return directory implementation
 916   */
 917  Class defaultDirImpl = null;
 918  
 919  public Directory openDirectory(String dirImpl, String file, boolean create) throws Exception {
 920    File f = new File(file);
 921    if (!f.exists()) {
 922      throw new Exception("Index directory doesn't exist.");
 923    }
 924    Directory res = null;
 925    if (dirImpl == null || dirImpl.equals(FSDirectory.class.getName())) {
 926      return FSDirectory.open(f);
 927    }
 928    try {
 929      Class implClass = Class.forName(dirImpl);
 930      Constructor<Directory> constr = implClass.getConstructor(File.class);
 931      res = constr.newInstance(f);
 932    } catch (Throwable e) {
 933      errorMsg("Invalid directory implementation class: " + dirImpl + " " + e);
 934      return null;
 935    }
 936    if (res != null) return res;
 937    // fall-back to FSDirectory.
 938    if (res == null) return FSDirectory.open(f);
 939    return null;
 940  }
 941  
 942  /**
 943   * Indicates whether I/O access should be optimized because
 944   * the index is on a slow medium (e.g. remote).
 945   * @return true if I/O access is costly and should be minimized
 946   */
 947  public boolean isSlowAccess() {
 948    return slowAccess;
 949  }
 950  
 951  /**
 952   * Set whether the I/O access to this index is costly and
 953   * should be minimized.
 954   */
 955  public void setSlowAccess(boolean slowAccess) {
 956    this.slowAccess = slowAccess;
 957    if (slowAccess) {
 958      
 959    }
 960  }
 961
 962  /**
 963   * Initialize plugins. This method should always be called when a new index is open.
 964   *
 965   */
 966  public void initPlugins() {
 967    Object pluginsTabs = find("pluginsTabs");
 968    removeAll(pluginsTabs);
 969    for (int i = 0; i < plugins.size(); i++) {
 970      LukePlugin plugin = (LukePlugin) plugins.get(i);
 971      addPluginTab(pluginsTabs, plugin);
 972      plugin.setDirectory(dir);
 973      plugin.setIndexReader(ir);
 974      try {
 975        plugin.init();
 976      } catch (Exception e) {
 977        e.printStackTrace();
 978        showStatus("PLUGIN ERROR: " + e.getMessage());
 979      }
 980    }
 981  }
 982
 983  /**
 984   * Initialize index overview and other GUI elements. This method is called always
 985   * when a new index is open.
 986   *
 987   */
 988  private void initOverview() {
 989    try {
 990      courier = new Font("Courier", getFont().getStyle(), getFont().getSize());
 991      lastST = find("lastST");
 992      setBoolean(find("bReload"), "enabled", true);
 993      setBoolean(find("bClose"), "enabled", true);
 994      setBoolean(find("bCommit"), "enabled", true);
 995      Object cbType = find("cbType");
 996      populateAnalyzers(cbType);
 997      Object pOver = find("pOver");
 998      Object iName = find("idx");
 999      String idxName;
1000      if (pName.length() > 40) {
1001        idxName = pName.substring(0, 10) + "..." + pName.substring(pName.length() - 27);
1002      } else {
1003        idxName = pName;
1004      }
1005      setString(iName, "text", idxName + (readOnly ? " (R)" : ""));
1006      iName = find(pOver, "iName");
1007      setString(iName, "text", pName + (readOnly ? " (Read-Only)" : ""));
1008      Object dirImpl = find("dirImpl");
1009      String implName = "N/A";
1010      if (dir == null) {
1011        if (ir != null) {
1012          implName = "N/A (reader is " + ir.getClass().getName() + ")";
1013        }
1014      } else {
1015        implName = dir.getClass().getName();
1016      }
1017      setString(dirImpl, "text", implName);
1018      Object fileSize = find("iFileSize");
1019      long totalFileSize = Util.calcTotalFileSize(pName, dir);
1020      setString(fileSize, "text", Util.normalizeSize(totalFileSize) + Util.normalizeUnit(totalFileSize));
1021      if (ir == null) {
1022        return;
1023      }      
1024      // we need IndexReader from now on
1025      Object iMod = find(pOver, "iMod");
1026      String modText = "N/A";
1027      if (dir != null) {
1028        modText = new Date(IndexReader.lastModified(dir)).toString();
1029      }
1030      setString(iMod, "text", modText);
1031      Object iDocs = find(pOver, "iDocs");
1032      String numdocs = String.valueOf(ir.numDocs());
1033      setString(iDocs, "text", numdocs);
1034      iDocs = find("iDocs1");
1035      setString(iDocs, "text", String.valueOf(ir.maxDoc() - 1));
1036      Object iFields = find(pOver, "iFields");
1037      fn = ir.getFieldNames(IndexReader.FieldOption.ALL);
1038      if (fn.size() == 0) {
1039        showStatus("Empty index.");
1040      }
1041      showFiles(dir, null);
1042      showCommits();
1043      final Object fList = find(pOver, "fList");
1044      final Object defFld = find("defFld");
1045      final Object fCombo = find("fCombo");
1046      TreeSet<String> fields = new TreeSet<String>(fn);
1047      idxFields = (String[])fields.toArray(new String[fields.size()]);
1048      setString(iFields, "text", String.valueOf(idxFields.length));
1049      final Object iTerms = find(pOver, "iTerms");
1050      if (!slowAccess) {
1051        Thread t = new Thread() {
1052          public void run() {
1053            Object r = create("row");
1054            Object cell = create("cell");
1055            add(r, cell);
1056            add(fList, r);
1057            setBoolean(cell, "enabled", false);
1058            setString(cell, "text", "..wait..");
1059            termCounts.clear();
1060            FieldTermCount ftc = null;
1061            try {
1062              TermEnum te = ir.terms();
1063              numTerms = 0;
1064              while (te.next()) {
1065                Term currTerm = te.term();
1066                if (ftc == null) {
1067                  // initialize
1068                  ftc = new FieldTermCount();
1069                  ftc.fieldname = currTerm.field();
1070                  termCounts.put(ftc.fieldname, ftc);
1071                }
1072                if (ftc.fieldname == currTerm.field()) {
1073                  ftc.termCount++;
1074                } else {
1075                  ftc = new FieldTermCount();
1076                  ftc.fieldname = currTerm.field();
1077                  ftc.termCount++;
1078                  termCounts.put(ftc.fieldname, ftc);
1079                }
1080                numTerms++;
1081              }
1082              te.close();
1083              setString(iTerms, "text", String.valueOf(numTerms));
1084              initFieldList(fList, fCombo, defFld);
1085            } catch (Exception e) {
1086              showStatus("ERROR: can't count terms per field");
1087            }
1088          }
1089        };
1090        t.start();
1091      } else {
1092        setString(iTerms, "text", "N/A");        
1093        initFieldList(fList, fCombo, defFld);
1094      }
1095      Object iDel = find(pOver, "iDelOpt");
1096      String sDel = ir.hasDeletions() ? "Yes (" + ir.numDeletedDocs() + ")" : "No";
1097      String sDelOpt = sDel + " / " +
1098                      (ir.isOptimized() ? "Yes" : "No");
1099      setString(iDel, "text", sDelOpt);
1100      Object iVer = find(pOver, "iVer");
1101      String verText = "N/A";
1102      if (dir != null) {
1103        verText = Long.toHexString(IndexReader.getCurrentVersion(dir));
1104      }
1105      setString(iVer, "text", verText);
1106      Object iFormat = find(pOver, "iFormat");
1107      Object iCaps = find(pOver, "iCaps");
1108      String formatText = "N/A";
1109      String formatCaps = "N/A";
1110      if (dir != null) {
1111        int format = IndexGate.getIndexFormat(dir);
1112        IndexGate.FormatDetails formatDetails = IndexGate.getFormatDetails(format);
1113        formatText = format + " (" + formatDetails.genericName + ")";
1114        formatCaps = formatDetails.capabilities;
1115      }
1116      setString(iFormat, "text", formatText);
1117      setString(iCaps, "text", formatCaps);
1118      Object iTiiDiv = find(pOver, "iTiiDiv");
1119      String divText = "N/A";
1120      // not available in Lucene 3.0
1121//      try {
1122//        divText = String.valueOf(ir.getTermInfosIndexDivisor());
1123//      } catch (UnsupportedOperationException uoe) {
1124//      }
1125      setString(iTiiDiv, "text", divText);
1126      Object iCommit = find(pOver, "iCommit");
1127      String commitText = "N/A";
1128      try {
1129        IndexCommit commit = ir.getIndexCommit();
1130        commitText = commit.getSegmentsFileName() + " (" +
1131          new Date(commit.getTimestamp()).toString() + ")";
1132      } catch (UnsupportedOperationException uoe) {
1133      }
1134      setString(iCommit, "text", commitText);
1135      Object iUser = find(pOver, "iUser");
1136      String userData = null;
1137      try {
1138        Map userDataMap = ir.getCommitUserData();
1139        if (userDataMap != null && !userDataMap.isEmpty()) {
1140          userData = ir.getCommitUserData().toString();
1141        } else {
1142          userData = "--";
1143        }
1144      } catch (UnsupportedOperationException uoe) {
1145        userData = "(not supported)";
1146      }
1147      setString(iUser, "text", userData);
1148      final Object nTerms = find("nTerms");
1149      if (!slowAccess) {
1150        Thread t = new Thread() {
1151          public void run() {
1152            actionTopTerms(nTerms);
1153          }
1154        };
1155        t.start();
1156      }
1157    } catch (Exception e) {
1158      e.printStackTrace();
1159      errorMsg(e.getMessage());
1160    }
1161  }
1162
1163  private void initFieldList(Object fList, Object fCombo, Object defFld) {
1164    removeAll(fList);
1165    removeAll(fCombo);
1166    removeAll(defFld);
1167    setString(fCombo, "text", idxFields[0]);
1168    setString(defFld, "text", idxFields[0]);
1169    NumberFormat intCountFormat = NumberFormat.getIntegerInstance();
1170    NumberFormat percentFormat = NumberFormat.getNumberInstance();
1171    intCountFormat.setGroupingUsed(true);
1172    percentFormat.setMaximumFractionDigits(2);
1173    // sort by names now
1174    for (String s : idxFields) {
1175      Object row = create("row");
1176      putProperty(row, "fName", s);
1177      add(fList, row);
1178      Object cell = create("cell");
1179      setString(cell, "text", s);
1180      add(row, cell);
1181      cell = create("cell");
1182      FieldTermCount ftc = termCounts.get(s);
1183      if (ftc != null) {
1184        long cnt = ftc.termCount;
1185        setString(cell, "text", intCountFormat.format(cnt));
1186        setChoice(cell, "alignment", "right");
1187        add(row, cell);
1188        float pcent = (float)(cnt * 100) / (float)numTerms;
1189        cell = create("cell");
1190        setString(cell, "text", percentFormat.format(pcent) + " %");
1191        setChoice(cell, "alignment", "right");
1192        add(row, cell);
1193      } else {
1194        setString(cell, "text", "0");
1195        setChoice(cell, "alignment", "right");
1196        add(row, cell);
1197        cell = create("cell");
1198        setString(cell, "text", "0.00 %");
1199        setChoice(cell, "alignment", "right");
1200        add(row, cell);
1201      }
1202      cell = create("cell");
1203      setChoice(cell, "alignment", "right");
1204      Decoder dec = decoders.get(s);
1205      if (dec == null) dec = defDecoder;
1206      setString(cell, "text", dec.toString());
1207      add(row, cell);
1208      // populate combos
1209      Object choice = create("choice");
1210      add(fCombo, choice);
1211      setString(choice, "text", s);
1212      putProperty(choice, "fName", s);
1213      choice = create("choice");
1214      add(defFld, choice);
1215      setString(choice, "text", s);
1216      putProperty(choice, "fName", s);
1217    }
1218    setString(find("defFld"), "text", idxFields[0]);
1219    // Remove columns
1220    Object header = get(find("sTable"), "header");
1221    removeAll(header);
1222    Object c = create("column");
1223    setString(c, "text", "#");
1224    setInteger(c, "width", 40);
1225    add(header, c);
1226    c = create("column");
1227    setString(c, "text", "Score");
1228    setInteger(c, "width", 50);
1229    add(header, c);
1230    c = create("column");
1231    setString(c, "text", "Doc. Id");
1232    setInteger(c, "width", 60);
1233    add(header, c);
1234    for (int j = 0; j < idxFields.length; j++) {
1235      c = create("column");
1236      setString(c, "text", idxFields[j]);
1237      add(header, c);
1238    }
1239  }
1240  
1241  private void showCommits() throws Exception {
1242    Object commitsTable = find("commitsTable");
1243    removeAll(commitsTable);
1244    if (dir == null) {
1245      Object row = create("row");
1246      Object cell = create("cell");
1247      setString(cell, "text", "<not available>");
1248      setBoolean(cell, "enabled", false);
1249      add(row, cell);
1250      add(commitsTable, row);
1251      return;
1252    }
1253    Collection commits = IndexReader.listCommits(dir);
1254    // commits are ordered from oldest to newest ?
1255    Iterator it = commits.iterator();
1256    int rowNum = 0;
1257    while (it.hasNext()) {
1258      IndexCommit commit = (IndexCommit)it.next();
1259      // figure out the name of the segment files
1260      Collection files = commit.getFileNames();
1261      Iterator itf = files.iterator();
1262      Object row = create("row");
1263      boolean enabled = rowNum < commits.size() - 1;
1264      Color color = null;
1265      rowNum++;
1266      add(commitsTable, row);
1267      putProperty(row, "commit", commit);
1268      if (enabled) {
1269        putProperty(row, "commitDeletable", Boolean.TRUE);
1270      }
1271      Object cell = create("cell");
1272      setString(cell, "text", String.valueOf(commit.getGeneration()));
1273      add(row, cell);
1274      cell = create("cell");
1275      char[] flags = new char[]{'-', '-'};
1276      if (commit.isDeleted()) flags[0] = 'D';
1277      if (commit.isOptimized()) flags[1] = 'O';
1278      setString(cell, "text", new String(flags));
1279      setFont(cell, "font", courier);
1280      setChoice(cell, "alignment", "center");
1281      add(row, cell);
1282      cell = create("cell");
1283      setString(cell, "text", Long.toHexString(commit.getVersion()));
1284      add(row, cell);
1285      cell = create("cell");
1286      setString(cell, "text", new Date(commit.getTimestamp()).toString());
1287      add(row, cell);
1288      cell = create("cell");
1289      Map userData = commit.getUserData();
1290      if (userData != null && !userData.isEmpty()) {
1291        setString(cell, "text", userData.toString());
1292      } else {
1293        setString(cell, "text", "--");
1294      }
1295      add(row, cell);
1296    }
1297  }
1298  
1299  public void showCommitFiles(Object commitTable) throws Exception {
1300    List commits = new ArrayList();
1301    Object[] rows = getSelectedItems(commitTable);
1302    if (rows == null || rows.length == 0) {
1303      showFiles(dir, commits);
1304      return;
1305    }
1306    for (int i = 0; i < rows.length; i++) {
1307      IndexCommit commit = (IndexCommit)getProperty(rows[i], "commit");
1308      if (commit != null) {
1309        commits.add(commit);
1310      }
1311    }
1312    showFiles(dir, commits);
1313  }
1314
1315  private void showFiles(Directory dir, List commits) throws Exception {
1316    Object filesTable = find("filesTable");
1317    if (dir == null) {
1318      removeAll(filesTable);
1319      Object row = create("row");
1320      Object cell = create("cell");
1321      setString(cell, "text", "<not available>");
1322      setBoolean(cell, "enabled", false);
1323      add(row, cell);
1324      add(filesTable, row);
1325      return;
1326    }
1327    String[] physFiles = dir.listAll();
1328    List<String> files = new ArrayList();
1329    if (commits != null && commits.size() > 0) {
1330      for (int i = 0; i < commits.size(); i++) {
1331        IndexCommit commit = (IndexCommit)commits.get(i);
1332        files.addAll(commit.getFileNames());
1333      }
1334    } else {
1335      files.addAll(Arrays.asList(physFiles));
1336    }
1337    Collections.sort(files);
1338    List segs = getIndexFileNames(dir);
1339    List dels = getIndexDeletableNames(dir);
1340    removeAll(filesTable);
1341    for (int i = 0; i < files.size(); i++) {
1342      String fileName = files.get(i);
1343      String pathName;
1344      if (pName.endsWith(File.separator)) {
1345        pathName = pName;
1346      } else {
1347        pathName = pName + File.separator;
1348      }
1349      File file = new File(pathName + fileName);
1350      Object row = create("row");
1351      Object nameCell = create("cell");
1352      setString(nameCell, "text", fileName);
1353      add(row, nameCell);
1354      Object sizeCell = create("cell");
1355      setString(sizeCell, "text", Util.normalizeSize(file.length()));
1356      setChoice(sizeCell, "alignment", "right");
1357      add(row, sizeCell);
1358      Object unitCell = create("cell");
1359      setString(unitCell, "text", Util.normalizeUnit(file.length()));
1360      add(row, unitCell);
1361      boolean deletable = dels.contains(fileName.intern());
1362      String inuse = getFileFunction(fileName);
1363      Object delCell = create("cell");
1364      setString(delCell, "text", deletable ? "YES" : "-");
1365      add(row, delCell);
1366      Object inuseCell = create("cell");
1367      setString(inuseCell, "text", inuse);
1368      add(row, inuseCell);
1369      add(filesTable, row);
1370    }
1371  }
1372  
1373  private String getFileFunction(String file) {
1374    String res = IndexGate.getFileFunction(file);
1375    if (res == null) {
1376      res = "YES";
1377    }
1378    return res;
1379  }
1380
1381  private void syncMRU(Object path) {
1382    removeAll(path);
1383    for (Iterator iter = Prefs.getMruList().iterator(); iter.hasNext();) {
1384      String element = (String) iter.next();
1385      Object choice = create("choice");
1386      setString(choice, "text", element);
1387      add(path, choice);
1388    }
1389  }
1390
1391  /**
1392   * Update the list of top terms.
1393   * @param nTerms Thinlet widget containing the number of top terms to show
1394   */
1395  public void actionTopTerms(Object nTerms) {
1396    if (ir == null) {
1397      showStatus(MSG_NOINDEX);
1398      return;
1399    }
1400    String sndoc = getString(nTerms, "text");
1401    int nd = 50;
1402    try {
1403      nd = Integer.parseInt(sndoc);
1404    } catch (Exception e) {}
1405    final int ndoc = nd;
1406    Object[] fields = getSelectedItems(find("fList"));
1407    String[] flds = null;
1408    if (fields == null || fields.length == 0) {
1409      flds = idxFields;
1410    } else {
1411      flds = new String[fields.length];
1412      for (int i = 0; i < fields.length; i++) {
1413        flds[i] = (String) getProperty(fields[i], "fName");
1414      }
1415    }
1416    final String[] fflds = flds;
1417    SlowThread st = new SlowThread(this) {
1418      public void execute() {
1419        try {
1420          TermInfo[] tis = HighFreqTerms.getHighFreqTerms(ir, null, ndoc + 1, fflds);
1421          Object table = find("tTable");
1422          removeAll(table);
1423          if (tis == null || tis.length == 0) {
1424            Object row = create("row");
1425            Object cell = create("cell");
1426            add(row, cell);
1427            cell = create("cell");
1428            add(row, cell);
1429            cell = create("cell");
1430            add(row, cell);
1431            cell = create("cell");
1432            setBoolean(cell, "enabled", false);
1433            setString(cell, "text", "No Results");
1434            add(row, cell);
1435            add(table, row);
1436            return;
1437          }
1438          for (int i = 0; i < tis.length; i++) {
1439            Object row = create("row");
1440            add(table, row);
1441            putProperty(row, "term", tis[i].term);
1442            putProperty(row, "ti", tis[i]);
1443            Object cell = create("cell");
1444            setChoice(cell, "alignment", "right");
1445            setString(cell, "text", String.valueOf(i + 1));
1446            add(row, cell);
1447            cell = create("cell");
1448            setChoice(cell, "alignment", "right");
1449            setString(cell, "text", String.valueOf(tis[i].docFreq) + "  ");
1450            add(row, cell);
1451            cell = create("cell");
1452            setString(cell, "text", tis[i].term.field());
1453            add(row, cell);
1454            cell = create("cell");
1455            Decoder dec = decoders.get(tis[i].term.field());
1456            if (dec == null) dec = defDecoder;
1457            String s;
1458            try {
1459              s = dec.decodeTerm(tis[i].term.field(), tis[i].term.text());
1460            } catch (Throwable e) {
1461              s = tis[i].term.text();
1462              setColor(cell, "foreground", Color.RED);
1463            }
1464            setString(cell, "text", "  " + s);
1465            add(row, cell);
1466          }
1467        } catch (Exception e) {
1468          e.printStackTrace();
1469          errorMsg(e.getMessage());
1470        }
1471      }
1472    };
1473    if (slowAccess) {
1474      st.start();
1475    } else {
1476      st.execute();
1477    }
1478  }
1479  
1480  public void clipTopTerms(Object tTable) {
1481    Object[] rows = getItems(tTable);
1482    StringBuffer sb = new StringBuffer();
1483    for (int i = 0; i < rows.length; i++) {
1484      TermInfo ti = (Term

Large files files are truncated, but you can click here to view the full file