/home/wpollock1/public_html/restricted/Java2/SearchEngine/com/wpollock/searchengine/FileListModel.java
package com.wpollock.searchengine;
import static com.wpollock.searchengine.Main.*;
import static com.wpollock.searchengine.MaintenanceWindow.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.*;
import javax.swing.table.AbstractTableModel;
/** The status of files added to the index.
* TODO: Internationalize the strings, just to show how.
*/
enum FileStatus { OKAY("Indexed"),
MISSING("File not found"),
NEEDS_UPDATE("File changed since last indexed");
private String displayText = null;
private FileStatus ( String text ) { displayText = text; }
@Override
public String toString () {
return displayText;
}
}
/** This class represents the list of indexed files. A simple List would work,
* but using a TableModel allows one to easily display the files in a JTable.
* (It also shows how to create and use a JTable.)
*
* @author Wayne Pollock
*/
class FileListModel extends AbstractTableModel {
private static final long serialVersionUID = 1L;
private String[] columnNames = { "File Name", "Status" };
private static FileListModel model = null;
static java.util.List<FileItem> fileList = new ArrayList<>();
FileListModel () {
model = this; // Save a reference so others can get at it.
// The data of the model is read from the saved file at startup
// TODO: Adjust column widths
}
/** This is a Singleton model.
* Note this method is not thread-safe.
* @return The file list model used by this application.
* (This is the list of currently indexed files.)
*/
public static FileListModel getModel() {
if ( model == null )
model = new FileListModel();
return model;
}
/** Get file name for docID.
*
* @param docID The ID of the file
* @return The canonical absolute pathname for the file.
*/
public String getFileName ( long docID ) {
for ( FileItem file : fileList ) {
if ( file.fileID == docID )
return file.fileName;
}
return null; // document not found
}
/** Returns a Set of the docIDs of any selected files from the JTable.
* @return The Set of docIDs, which may be empty.
*/
Set<Long> getSelectedFiles() {
Set<Long> selectedFiles = new HashSet<>();
int [] selectedRows = fileTable.getSelectedRows();
for ( int row : selectedRows ) {
selectedFiles.add( fileList.get(row).fileID );
}
return selectedFiles;
}
/** Adds a new file to the file list (model). If successful,
* Main.nextID is incremented by one. (TODO: Move that to this file?)
*
* @param fileName Absolute Pathname of the file to add.
* @return the DocID of the newly added file.
*/
public long addFile ( String fileName ) {
long docID = Main.nextID;
++Main.nextID;
File file = new File( fileName );
long modTimeStamp = file.lastModified();
if ( modTimeStamp == 0L ) // File doesn't exist or can't be opened
throw new IllegalArgumentException(
"Can't access file \"" + fileName + "\"." );
FileItem fi = new FileItem( docID, fileName, modTimeStamp );
fileList.add(fi);
fireTableDataChanged(); // Notify JTable to redraw itself
// TODO: This should use swingUtilities.invokeAndWait:
Main.numItemsIndexed.setText( "" + fileList.size() );
MaintenanceWindow.numItemsIndexed.setText( "" + fileList.size() );
return docID;
}
public void removeFile ( long docID ) {
// Find FileItem in List with this docID:
Iterator<FileItem> it = fileList.iterator();
while ( it.hasNext() ) {
FileItem file = it.next();
if ( file.fileID == docID ) {
it.remove();
break;
}
}
fireTableDataChanged(); // Notify JTable to redraw itself
// TODO: This should use swingUtilities.invokeAndWait:
Main.numItemsIndexed.setText( "" + fileList.size() );
MaintenanceWindow.numItemsIndexed.setText( "" + fileList.size() );
}
/** Finds the docID of a given filename. Note, if item isn't found,
* the caller will get an exception from auto-unboxing a null. TODO:
* have method return long, and throw exception if name not found.
*
* @param fileName The name of the file to search for.
* @return The docID of the given file, or null if the file isn't found.
*/
public Long getDocID ( String fileName ) {
for ( FileItem file : fileList ) {
if ( file.fileName.equals(fileName) )
return new Long(file.fileID);
}
return null; // document not found
}
public boolean contains ( String fileName ) {
for ( FileItem fi : fileList ) {
if ( fi.fileName.equals(fileName) )
return true;
}
return false;
}
/** Finds all document IDs in the index.
*
* @return Set<Long> of all docIDs
*/
public Set<Long> getAllDocIDs () {
Set<Long> results = new TreeSet<>();
for ( FileItem file : fileList )
results.add( file.fileID );
return results;
}
void updateFileModTime ( long docID ) {
// Find the fileItem with the specified docID:
Iterator<FileItem> it = fileList.iterator();
while (it.hasNext() ) {
FileItem fi = it.next();
if ( fi.fileID == docID ) {
fi.modificationTime = new File( fi.fileName ).lastModified();
break;
}
}
fireTableDataChanged(); // Notify JTable to redraw itself
}
void saveIndexToFile () {
// Determine absolute pathname of file in user's home directory:
File indexFile = new File( Main.indexFileName );
// Create and initialize new file if missing:
if ( ! indexFile.exists() )
try { indexFile.createNewFile(); }
catch ( Exception e ) { // Can't create UTF-8 file in user's home
e.printStackTrace(); // Should never happen
}
PrintWriter file = null;
try { // TODO: Convert to Java 7's Try-with-Resources
file = new PrintWriter( new OutputStreamWriter(
new FileOutputStream( indexFile ), FILE_ENCODING ) );
// Print file header:
file.println( FILE_HEADER ); // File header and version
file.println( Main.nextID ); // Next FileID number to use
// Add file list:
for ( FileItem fi : fileList ) {
file.println( fi.fileID + "\t" + fi.fileName
+ "\t" + fi.modificationTime );
}
file.println(); // Add a separator line.
// Add index data:
for ( String word : IndexUtils.invertedIndex.keySet() ) {
Set<DocPos> dataSet = IndexUtils.invertedIndex.get( word );
file.print( word );
for ( DocPos dp : dataSet ) {
file.print( " " + dp.docID + "," + dp.pos );
}
file.println();
}
file.close();
} catch (Exception e) { //
e.printStackTrace();
System.exit(0);
}
}
// Remaining methods are standard for any DocumentModels:
@Override
public int getColumnCount() {
return columnNames.length;
}
@Override
public int getRowCount() {
return fileList.size(); // data.length;
}
@Override
public String getColumnName(int col) {
return columnNames[col];
}
@Override
public Object getValueAt(int row, int col) {
FileItem fileData = fileList.get(row);
switch ( col ) {
case 0: return fileData.fileName;
case 1: // To determine the status, must check if file exists and
// its mod time:
File file = new File( fileData.fileName );
FileStatus status = FileStatus.OKAY;
if ( ! file.exists() ) status = FileStatus.MISSING;
else if ( file.lastModified() > fileData.modificationTime )
status = FileStatus.NEEDS_UPDATE;
return status;
}
return null;
}
@Override
public Class<?> getColumnClass ( int col ) {
return String.class;
}
@Override
public boolean isCellEditable(int row, int col) {
return false;
}
}