/*******************************************************************************
* Copyright (c) 2000, 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* MetaMatrix, Inc - repackaging and updates for use as a metadata store
*******************************************************************************/
package org.teiid.designer.core.index;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.HashMap;
import org.teiid.core.designer.util.CharOperation;
import org.teiid.core.designer.util.LRUCache;
import org.teiid.designer.legacy.Messages;
/**
* This input is used for reading indexes saved using a BlocksIndexOutput.
*
* @since 8.0
*/
public class BlocksIndexInput extends IndexInput {
public static final int CACHE_SIZE = 16; // Cache 16 blocks of 8K each, for a cache size of 128K
protected FileListBlock currentFileListBlock;
protected int currentFileListBlockNum;
protected int currentIndexBlockNum;
protected IndexBlock currentIndexBlock;
private RandomAccessFile raf;
protected File indexFile;
protected LRUCache blockCache;
protected IndexSummary summary;
public BlocksIndexInput( File inputFile ) {
this.indexFile = inputFile;
blockCache = new LRUCache(CACHE_SIZE);
}
/**
* @see IndexInput#clearCache()
*/
@Override
public void clearCache() {
blockCache = new LRUCache(CACHE_SIZE);
}
/**
* @see IndexInput#close()
*/
@Override
public void close() throws IOException {
if (isOpen()) {
// System.out.println(" Closing Index File: " + this.indexFile.getName());
summary = null;
setOpen(false);
if (raf != null) raf.close();
}
}
/**
* @see IndexInput#getCurrentFile()
*/
@Override
public IndexedFile getCurrentFile() throws IOException {
if (!hasMoreFiles()) return null;
IndexedFile file = null;
if ((file = currentFileListBlock.getFile(filePosition)) == null) {
currentFileListBlockNum = summary.getBlockNumForFileNum(filePosition);
currentFileListBlock = getFileListBlock(currentFileListBlockNum);
file = currentFileListBlock.getFile(filePosition);
}
return file;
}
/**
* Returns the entry corresponding to the given word.
*/
protected WordEntry getEntry( char[] word ) throws IOException {
int blockNum = summary.getBlockNumForWord(word);
if (blockNum == -1) return null;
IndexBlock block = getIndexBlock(blockNum);
return block.findExactEntry(word);
}
/**
* Returns the FileListBlock with the given number.
*/
protected FileListBlock getFileListBlock( int blockNum ) throws IOException {
Integer key = new Integer(blockNum);
Block block = (Block)blockCache.get(key);
if (block != null && block instanceof FileListBlock) return (FileListBlock)block;
FileListBlock fileListBlock = new FileListBlock(IIndexConstants.BLOCK_SIZE);
fileListBlock.read(raf, blockNum);
blockCache.put(key, fileListBlock);
return fileListBlock;
}
/**
* Returns the IndexBlock (containing words) with the given number.
*/
protected IndexBlock getIndexBlock( int blockNum ) throws IOException {
Integer key = new Integer(blockNum);
Block block = (Block)blockCache.get(key);
if (block != null && block instanceof IndexBlock) return (IndexBlock)block;
IndexBlock indexBlock = new GammaCompressedIndexBlock(IIndexConstants.BLOCK_SIZE);
indexBlock.read(raf, blockNum);
blockCache.put(key, indexBlock);
return indexBlock;
}
/**
* @see IndexInput#getIndexedFile(int)
*/
@Override
public IndexedFile getIndexedFile( int fileNum ) throws IOException {
int blockNum = summary.getBlockNumForFileNum(fileNum);
if (blockNum == -1) return null;
FileListBlock block = getFileListBlock(blockNum);
return block.getFile(fileNum);
}
/**
* @see IndexInput#getIndexedFile(IDocument)
*/
@Override
public IndexedFile getIndexedFile( IDocument document ) throws java.io.IOException {
setFirstFile();
String name = document.getName();
while (hasMoreFiles()) {
IndexedFile file = getCurrentFile();
String path = file.getPath();
if (path.equals(name)) return file;
moveToNextFile();
}
return null;
}
/**
* Returns the list of numbers of files containing the given word.
*/
protected int[] getMatchingFileNumbers( char[] word ) throws IOException {
int blockNum = summary.getBlockNumForWord(word);
if (blockNum == -1) return new int[0];
IndexBlock block = getIndexBlock(blockNum);
WordEntry entry = block.findExactEntry(word);
return entry == null ? new int[0] : entry.getRefs();
}
/**
* @see IndexInput#getNumFiles()
*/
@Override
public int getNumFiles() {
return summary.getNumFiles();
}
/**
* @see IndexInput#getNumWords()
*/
@Override
public int getNumWords() {
return summary.getNumWords();
}
/**
* @see IndexInput#getSource()
*/
@Override
public Object getSource() {
return indexFile;
}
/**
* Initialises the blocksIndexInput
*/
protected void init() throws IOException {
clearCache();
setFirstFile();
setFirstWord();
}
/**
* @see IndexInput#moveToNextFile()
*/
@Override
public void moveToNextFile() {
filePosition++;
}
/**
* @see IndexInput#moveToNextWordEntry()
*/
@Override
public void moveToNextWordEntry() throws IOException {
wordPosition++;
if (!hasMoreWords()) {
return;
}
// if end of the current block, we load the next one.
boolean endOfBlock = !currentIndexBlock.nextEntry(currentWordEntry);
if (endOfBlock) {
currentIndexBlock = getIndexBlock(++currentIndexBlockNum);
currentIndexBlock.nextEntry(currentWordEntry);
}
}
/**
* @see IndexInput#open()
*/
@Override
public void open() throws IOException {
if (!isOpen()) {
raf = new SafeRandomAccessFile(indexFile, "r"); //$NON-NLS-1$
String sig = raf.readUTF();
if (!sig.equals(IIndexConstants.SIGNATURE)) throw new IOException(Messages.wrongFormat); //$NON-NLS-1$
int summaryBlockNum = raf.readInt();
raf.seek(summaryBlockNum * (long)IIndexConstants.BLOCK_SIZE);
summary = new IndexSummary();
summary.read(raf);
init();
setOpen(true);
}
}
/**
* @see IndexInput#query(String)
*/
public IQueryResult[] query( String word ) throws IOException {
open();
int[] fileNums = getMatchingFileNumbers(word.toCharArray());
int size = fileNums.length;
IQueryResult[] files = new IQueryResult[size];
for (int i = 0; i < size; ++i) {
files[i] = getIndexedFile(fileNums[i]);
}
return files;
}
/**
* Overloaded the method in BlocksIndexInput to allow a user to specify if the query should be case sensitive.
*
* @param pattern
* @param isCaseSensitive
* @return
* @throws IOException
*/
public IEntryResult[] queryEntriesMatching( char[] pattern,
boolean isCaseSensitive ) throws IOException {
open();
if (pattern == null || pattern.length == 0) return null;
int[] blockNums = null;
int firstStar = indexOf('*', pattern);
switch (firstStar) {
case -1:
WordEntry entry = getEntry(pattern);
if (entry == null) return null;
return new IEntryResult[] {new EntryResult(entry.getWord(), entry.getRefs())};
case 0:
blockNums = summary.getAllBlockNums();
break;
default:
char[] prefix = new char[firstStar];
System.arraycopy(pattern, 0, prefix, 0, firstStar);
blockNums = summary.getBlockNumsForPrefix(prefix);
}
if (blockNums == null || blockNums.length == 0) return null;
IEntryResult[] entries = new IEntryResult[5];
int count = 0;
for (int i = 0, max = blockNums.length; i < max; i++) {
IndexBlock block = getIndexBlock(blockNums[i]);
block.reset();
WordEntry entry = new WordEntry();
while (block.nextEntry(entry)) {
// Switch argument order to fix bug in BlocksIndexInput
if (CharOperation.match(pattern, entry.getWord(), isCaseSensitive)) {
if (count == entries.length) {
System.arraycopy(entries, 0, entries = new IEntryResult[count * 2], 0, count);
}
entries[count++] = new EntryResult(entry.getWord(), entry.getRefs());
}
}
}
if (count != entries.length) {
System.arraycopy(entries, 0, entries = new IEntryResult[count], 0, count);
}
return entries;
}
static final int indexOf( char toBeFound,
char[] array ) {
for (int i = 0; i < array.length; i++)
if (toBeFound == array[i]) return i;
return -1;
}
@Override
public IEntryResult[] queryEntriesPrefixedBy( char[] prefix ) throws IOException {
open();
int blockLoc = summary.getFirstBlockLocationForPrefix(prefix);
if (blockLoc < 0) return null;
IEntryResult[] entries = new IEntryResult[5];
int count = 0;
while (blockLoc >= 0) {
IndexBlock block = getIndexBlock(summary.getBlockNum(blockLoc));
block.reset();
boolean found = false;
WordEntry entry = new WordEntry();
while (block.nextEntry(entry)) {
if (CharOperation.prefixEquals(prefix, entry.getWord(), true)) {
if (count == entries.length) {
System.arraycopy(entries, 0, entries = new IEntryResult[count * 2], 0, count);
}
entries[count++] = new EntryResult(entry.getWord(), entry.getRefs());
found = true;
} else {
if (found) break;
}
}
/* consider next block ? */
blockLoc = summary.getNextBlockLocationForPrefix(prefix, blockLoc);
}
if (count == 0) return null;
if (count != entries.length) {
System.arraycopy(entries, 0, entries = new IEntryResult[count], 0, count);
}
return entries;
}
/**
* Overloaded the method in BlocksIndexInput to allow a user to specify if the query should be case sensitive.
*/
public IEntryResult[] queryEntriesPrefixedBy( char[] prefix,
boolean isCaseSensitive ) throws IOException {
open();
int blockLoc = summary.getFirstBlockLocationForPrefix(prefix);
if (blockLoc < 0) return null;
IEntryResult[] entries = new IEntryResult[5];
int count = 0;
while (blockLoc >= 0) {
IndexBlock block = getIndexBlock(summary.getBlockNum(blockLoc));
block.reset();
boolean found = false;
WordEntry entry = new WordEntry();
while (block.nextEntry(entry)) {
if (CharOperation.prefixEquals(prefix, entry.getWord(), isCaseSensitive)) {
if (count == entries.length) {
System.arraycopy(entries, 0, entries = new IEntryResult[count * 2], 0, count);
}
entries[count++] = new EntryResult(entry.getWord(), entry.getRefs());
found = true;
} else {
if (found) break;
}
}
/* consider next block ? */
blockLoc = summary.getNextBlockLocationForPrefix(prefix, blockLoc);
}
if (count == 0) return null;
if (count != entries.length) {
System.arraycopy(entries, 0, entries = new IEntryResult[count], 0, count);
}
return entries;
}
@Override
public IQueryResult[] queryFilesReferringToPrefix( char[] prefix ) throws IOException {
open();
int blockLoc = summary.getFirstBlockLocationForPrefix(prefix);
if (blockLoc < 0) return null;
// each filename must be returned already once
HashMap<Integer, IndexedFile> fileMatches = new HashMap<Integer, IndexedFile>();
int count = 0;
while (blockLoc >= 0) {
IndexBlock block = getIndexBlock(summary.getBlockNum(blockLoc));
block.reset();
boolean found = false;
WordEntry entry = new WordEntry();
while (block.nextEntry(entry)) {
if (CharOperation.prefixEquals(prefix, entry.getWord(), true)) {
int[] refs = entry.getRefs();
for (int i = 0, max = refs.length; i < max; i++) {
int ref = refs[i];
Integer key = Integer.valueOf(ref);
if (!fileMatches.containsKey(key)) {
count++;
fileMatches.put(key, getIndexedFile(ref));
}
}
found = true;
} else {
if (found) break;
}
}
/* consider next block ? */
blockLoc = summary.getNextBlockLocationForPrefix(prefix, blockLoc);
}
/* extract indexed files */
IQueryResult[] files = fileMatches.values().toArray(new IQueryResult[count]);
return files;
}
/**
* @see IndexInput#queryInDocumentNames(String)
*/
@Override
public IQueryResult[] queryInDocumentNames( String word ) throws IOException {
open();
ArrayList matches = new ArrayList();
setFirstFile();
while (hasMoreFiles()) {
IndexedFile file = getCurrentFile();
if (file.getPath().indexOf(word) != -1) matches.add(file);
moveToNextFile();
}
IQueryResult[] match = new IQueryResult[matches.size()];
matches.toArray(match);
return match;
}
/**
* @see IndexInput#setFirstFile()
*/
@Override
protected void setFirstFile() throws IOException {
filePosition = 1;
if (getNumFiles() > 0) {
currentFileListBlockNum = summary.getBlockNumForFileNum(1);
currentFileListBlock = getFileListBlock(currentFileListBlockNum);
}
}
/**
* @see IndexInput#setFirstWord()
*/
@Override
protected void setFirstWord() throws IOException {
wordPosition = 1;
if (getNumWords() > 0) {
currentIndexBlockNum = summary.getFirstWordBlockNum();
currentIndexBlock = getIndexBlock(currentIndexBlockNum);
currentWordEntry = new WordEntry();
currentIndexBlock.reset();
currentIndexBlock.nextEntry(currentWordEntry);
}
}
}