/*
* HelpIndex.java - Index for help searching feature
* :tabSize=4:indentSize=4:noTabs=false:
* :folding=explicit:collapseFolds=1:
*
* Copyright (C) 2002 Slava Pestov
*
* 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 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.gjt.sp.jedit.help;
//{{{ Imports
import java.io.*;
import java.net.*;
import java.util.zip.*;
import java.util.*;
import org.gjt.sp.jedit.io.*;
import org.gjt.sp.jedit.*;
import org.gjt.sp.util.Log;
//}}}
class HelpIndex
{
//{{{ HelpIndex constructor
public HelpIndex()
{
words = new HashMap<String, Object>();
files = new ArrayList<HelpFile>();
ignoreWord("a");
ignoreWord("an");
ignoreWord("and");
ignoreWord("are");
ignoreWord("as");
ignoreWord("be");
ignoreWord("by");
ignoreWord("can");
ignoreWord("do");
ignoreWord("for");
ignoreWord("from");
ignoreWord("how");
ignoreWord("i");
ignoreWord("if");
ignoreWord("in");
ignoreWord("is");
ignoreWord("it");
ignoreWord("not");
ignoreWord("of");
ignoreWord("on");
ignoreWord("or");
ignoreWord("s");
ignoreWord("that");
ignoreWord("the");
ignoreWord("this");
ignoreWord("to");
ignoreWord("will");
ignoreWord("with");
ignoreWord("you");
} //}}}
/* //{{{ HelpIndex constructor
public HelpIndex(String fileListPath, String wordIndexPath)
{
this();
} //}}} */
//{{{ indexEditorHelp() method
/**
* Indexes all available help, including the jEdit user's guide, FAQ,]
* and plugin documentation.
*/
public void indexEditorHelp()
{
try
{
String jEditHome = jEdit.getJEditHome();
if(jEditHome != null)
{
indexDirectory(MiscUtilities.constructPath(jEditHome,"doc","users-guide"));
indexDirectory(MiscUtilities.constructPath(jEditHome,"doc","FAQ"));
indexDirectory(MiscUtilities.constructPath(jEditHome,"doc","whatsnew"));
}
}
catch(Throwable e)
{
Log.log(Log.ERROR,this,"Error indexing editor help");
Log.log(Log.ERROR,this,e);
}
PluginJAR[] jars = jEdit.getPluginJARs();
for (PluginJAR jar : jars)
{
try
{
indexJAR(jar.getZipFile());
}
catch (Throwable e)
{
Log.log(Log.ERROR, this, "Error indexing JAR: " + jar.getPath());
Log.log(Log.ERROR, this, e);
}
}
Log.log(Log.DEBUG,this,"Indexed " + words.size() + " words");
} //}}}
//{{{ indexDirectory() method
/**
* Indexes all HTML and text files in the specified directory.
* @param dir The directory
*/
public void indexDirectory(String dir) throws Exception
{
String[] files = VFSManager.getFileVFS()
._listDirectory(null,dir,"*.{html,txt}",true,null);
for (String file : files)
indexURL(file);
} //}}}
//{{{ indexJAR() method
/**
* Indexes all HTML and text files in the specified JAR file.
* @param jar The JAR file
*/
public void indexJAR(ZipFile jar) throws Exception
{
Enumeration e = jar.entries();
while(e.hasMoreElements())
{
ZipEntry entry = (ZipEntry)e.nextElement();
String name = entry.getName();
String lname = name.toLowerCase();
if(lname.endsWith(".html") || lname.endsWith(".txt") )
{
// only works for jEdit plugins
String url = "jeditresource:/" +
MiscUtilities.getFileName(jar.getName())
+ "!/" + name;
Log.log(Log.DEBUG,this,url);
indexStream(jar.getInputStream(entry),url);
}
}
} //}}}
//{{{ indexURL() method
/**
* Reads the specified HTML file and adds all words defined therein to the
* index.
* @param path The HTML file's URL or path
*/
public void indexURL(String path) throws Exception
{
URL url;
if (MiscUtilities.isURL(path))
url = new URL(path);
else
{
File f = new File(path);
url = f.toURI().toURL();
}
InputStream _in;
_in = url.openStream();
indexStream(_in, url.toString());
} //}}}
//{{{ lookupWord() method
public Word lookupWord(String word)
{
Object o = words.get(word);
if(o == IGNORE)
return null;
else
return (Word)o;
} //}}}
//{{{ getFile() method
public HelpFile getFile(int index)
{
return files.get(index);
} //}}}
//{{{ Private members
// used to mark words to ignore (see constructor for the list)
private static Object IGNORE = new Object();
private Map<String, Object> words;
private List<HelpFile> files;
//{{{ ignoreWord() method
private void ignoreWord(String word)
{
words.put(word,IGNORE);
} //}}}
//{{{ indexStream() method
/**
* Reads the specified HTML file and adds all words defined therein to the
* index.
* @param _in The input stream
* @param fileName The file
*/
private void indexStream(InputStream _in, String fileName)
throws Exception
{
HelpFile file = new HelpFile(fileName);
files.add(file);
int index = files.size() - 1;
StringBuilder titleText = new StringBuilder();
BufferedReader in = new BufferedReader(
new InputStreamReader(_in));
try
{
StringBuilder word = new StringBuilder();
boolean insideTag = false;
boolean insideEntity = false;
boolean title = false;
int c;
while((c = in.read()) != -1)
{
char ch = (char)c;
if(insideTag)
{
if(ch == '>')
{
if(word.toString().equals("title"))
title = true;
insideTag = false;
word.setLength(0);
}
else
word.append(ch);
}
else if(insideEntity)
{
if(ch == ';')
insideEntity = false;
}
else if(ch == '<')
{
if(title)
title = false;
if(word.length() != 0)
{
addWord(word.toString(),index,title);
word.setLength(0);
}
insideTag = true;
}
else if(ch == '&')
insideEntity = true;
else if(title)
titleText.append(ch);
else if(!Character.isLetterOrDigit(ch))
{
if(word.length() != 0)
{
addWord(word.toString(),index,title);
word.setLength(0);
}
}
else
word.append(ch);
}
}
finally
{
in.close();
}
if(titleText.length() == 0)
file.title = fileName;
else
file.title = titleText.toString();
} //}}}
//{{{ addWord() method
private void addWord(String word, int file, boolean title)
{
word = word.toLowerCase();
Object o = words.get(word);
if(o == IGNORE)
return;
if(o == null)
words.put(word,new Word(word,file,title));
else
((Word)o).addOccurrence(file,title);
} //}}}
//}}}
//{{{ Word class
static class Word
{
// how much an occurrence in the title is worth
static final int TITLE_OCCUR = 10;
// the word
String word;
// files it occurs in
int occurCount = 0;
Occurrence[] occurrences;
Word(String word, int file, boolean title)
{
this.word = word;
occurrences = new Occurrence[5];
addOccurrence(file,title);
}
void addOccurrence(int file, boolean title)
{
for(int i = 0; i < occurCount; i++)
{
if(occurrences[i].file == file)
{
occurrences[i].count += (title ? TITLE_OCCUR : 1);
return;
}
}
if(occurCount >= occurrences.length)
{
Occurrence[] newOccur = new Occurrence[occurrences.length * 2];
System.arraycopy(occurrences,0,newOccur,0,occurCount);
occurrences = newOccur;
}
occurrences[occurCount++] = new Occurrence(file,title);
}
static class Occurrence
{
int file;
int count;
Occurrence(int file, boolean title)
{
this.file = file;
this.count = (title ? TITLE_OCCUR : 1);
}
}
} //}}}
//{{{ HelpFile class
static class HelpFile
{
String file;
String title;
HelpFile(String file)
{
this.file = file;
}
public String toString()
{
return title;
}
public boolean equals(Object o)
{
if(o instanceof HelpFile)
return ((HelpFile)o).file.equals(file);
else
return false;
}
} //}}}
}