/* See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* Esri Inc. licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.esri.gpt.control.webharvest.engine;
import com.esri.gpt.framework.util.UuidUtil;
import com.esri.gpt.framework.util.Val;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.KeywordAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.store.AlreadyClosedException;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.store.NoLockFactory;
/**
* Source URI's array.
*/
class SourceUriArray implements Iterable<String[]> {
/** logger */
private static final Logger LOGGER = Logger.getLogger(SourceUriArray.class.getCanonicalName());
/** subfolder of the collections index */
private static final String SUBFOLDER = "OnFileStringCollection";
/** names */
private String [] names;
/** lucene directory */
private FSDirectory directory;
/** writer */
private IndexWriter writer;
/** reader */
private IndexReader reader;
/** folder */
private File folder;
/**
* Creates instance of the collection.
* @throws IOException if creating instance fails
*/
public SourceUriArray(String [] names) throws IOException {
this.names = names!=null? names: new String[]{};
// get new UUID
String name = UuidUtil.makeUuid();
// construct full path to the Lucene directory
String path = System.getProperty("java.io.tmpdir") + File.separator + SUBFOLDER + File.separator + name;
// create folder
folder = new File(path);
folder.mkdirs();
folder.deleteOnExit();
// create Lucene directory within the folder; it has to be no locking directory
directory = FSDirectory.open(folder,NoLockFactory.getNoLockFactory());
openForWriting();
}
/**
* Closes collection.
* @throws IOException if clossing collection fails
*/
public void close() throws IOException {
if (reader != null) {
reader.close();
reader = null;
}
if (writer != null) {
writer.close();
writer = null;
}
if (directory != null) {
try {
directory.close();
} catch (AlreadyClosedException ex) {}
}
if (folder != null) {
for (File f : folder.listFiles()) {
f.delete();
}
folder.delete();
folder = null;
}
}
/**
* Adds new string to the collection.
* @param values array of values
* @return <code>true</code> if adding values succeed
* @throws IOException if accessing index fails
*/
public void add(String [] values) throws IOException {
openForWriting();
// create document
Document doc = new Document();
for (int i = 0; i<names.length && i<values.length; i++) {
Field fld = new Field(names[i], values[i], Field.Store.YES, Field.Index.NOT_ANALYZED, Field.TermVector.YES);
doc.add(fld);
}
// save document
writer.addDocument(doc);
}
/**
* Removes string from the collection.
* @param name field name
* @param value field value
* @throws IOException if accessing index fails
*/
public void remove(String name, String value) throws IOException {
openForWriting();
// delete documents matching term
writer.deleteDocuments(new Term(name, value));
}
/**
* Provides iterator over the current collection of stored pairs.
* @return iterator
*/
@Override
public Iterator<String[]> iterator() {
return new PairIterator();
}
/**
* Opens collection for writing.
* @throws IOException if opening collection fails
*/
private void openForWriting() throws IOException {
if (writer == null) {
if (reader != null) {
reader.close();
reader = null;
}
// create writer used to store data
Analyzer analyzer = new KeywordAnalyzer();
writer = new IndexWriter(directory, analyzer, IndexWriter.MaxFieldLength.UNLIMITED);
}
}
/**
* Opens collection for reading
* @throws CorruptIndexException if index corrupted
* @throws IOException if opening collection fails
*/
private void openForReading() throws CorruptIndexException, IOException {
if (reader == null) {
if (writer != null) {
writer.commit();
writer.close();
writer = null;
}
// create reader used to search for data
reader = IndexReader.open(directory, true);
}
}
/**
* Iterator implementation.
*/
private class PairIterator implements Iterator<String[]> {
// current record index
private int index = -1;
// next document
private Document nextDoc;
public boolean hasNext() {
try {
openForReading();
if (nextDoc == null) {
while (index + 1 < reader.maxDoc()) {
index++;
if (!reader.isDeleted(index)) {
nextDoc = reader.document(index);
break;
}
}
}
if (nextDoc == null) {
reader.close();
}
return nextDoc != null;
} catch (Exception ex) {
LOGGER.log(Level.WARNING, "Error determining if there is more elemnts to iterate.", ex);
return false;
}
}
public String[] next() {
if (!hasNext()) {
throw new NoSuchElementException("No more elements.");
}
ArrayList<String> list = new ArrayList<String>();
for (int i=0; i<names.length; i++) {
list.add(Val.chkStr(nextDoc.get(names[i])));
}
nextDoc = null;
return list.toArray(new String[list.size()]);
}
public void remove() {
}
}
}