blob: 3b9f550e4dcd9b74d939d41f5dd3f06c7647e434 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2019 Xored Software Inc and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Xored Software Inc - initial API and implementation and/or initial documentation
*******************************************************************************/
package org.eclipse.rcptt.internal.core.model.index;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.rcptt.core.model.index.IQ7IndexConstants;
import org.eclipse.rcptt.core.model.search.SearchPattern.IKeyQuery;
import org.eclipse.rcptt.internal.core.RcpttPlugin;
import org.eclipse.rcptt.util.FileUtil;
public class Index {
private static final String CURRENT_VERSION = "INDEX_ID_1.0.3";
// private static final int N_THREADS = 8;
// private IPath path;
public ReadWriteMonitor monitor;
private File indexFile;
private Map<String, Entry> docToEnties = new HashMap<String, Entry>();
private Map<String, String> pathToIDCache = new HashMap<String, String>();
private Map<String, List<String>> idToPathCache = new HashMap<String, List<String>>();
private Map<String, String> pathToNameCache = new HashMap<String, String>();
private int changes = 0;
private IPath path;
private static class Entry {
Map<String, List<String>> keys = new HashMap<String, List<String>>();
String path;
long timestamp = -1;
public void addKey(String key, String value) {
List<String> list = keys.get(key);
if (list == null) {
list = new ArrayList<String>();
keys.put(key, list);
}
if( !list.contains(value)) {
list.add(value);
}
}
}
public Index(IPath fullPath) {
this.path = fullPath;
this.monitor = new ReadWriteMonitor();
// this.path = fullPath;
indexFile = RcpttPlugin.getDefault().getStateLocation().append("index")
.append(FileUtil.getID(fullPath.toString()) + ".index")
.toFile();
indexFile.getParentFile().mkdirs();
}
public synchronized void load() {
this.docToEnties.clear();
if (indexFile.exists()) {
DataInputStream din = null;
try {
din = new DataInputStream(new BufferedInputStream(
new FileInputStream(indexFile)));
String id = din.readUTF();
if (id.equals(CURRENT_VERSION)) {
readIndex_1_0(din);
}
} catch (IOException e) {
RcpttPlugin.log(e);
} finally {
FileUtil.safeClose(din);
}
}
}
private void readIndex_1_0(DataInputStream din) throws IOException {
int entriesCount = din.readInt();
for (int i = 0; i < entriesCount; i++) {
Entry entry = new Entry();
entry.path = din.readUTF();
entry.timestamp = din.readLong();
int keysCount = din.readInt();
for (int j = 0; j < keysCount; j++) {
String key = din.readUTF();
List<String> values = new ArrayList<String>();
int valuesCount = din.readInt();
for (int k = 0; k < valuesCount; k++) {
values.add(din.readUTF());
}
entry.keys.put(key, values);
if (IQ7IndexConstants.ID.equals(key) && values.size() == 1) {
pathToIDCache.put(entry.path, values.get(0));
updateIdToPath(entry.path, values.get(0));
}
if (IQ7IndexConstants.NAME.equals(key) && values.size() == 1) {
pathToNameCache.put(entry.path, values.get(0));
}
}
docToEnties.put(entry.path, entry);
}
}
private void writeIndex_1_0(DataOutputStream dout) throws IOException {
dout.writeUTF(CURRENT_VERSION);
List<String> keySet = new ArrayList<String>(docToEnties.keySet());
int entriesCount = docToEnties.size();
dout.writeInt(entriesCount);
for (int i = 0; i < entriesCount; i++) {
Entry entry = docToEnties.get(keySet.get(i));
dout.writeUTF(entry.path);
dout.writeLong(entry.timestamp);
int keysCount = entry.keys.size();
dout.writeInt(keysCount);
List<String> eKeys = new ArrayList<String>(entry.keys.keySet());
for (int j = 0; j < keysCount; j++) {
String key = eKeys.get(j);
dout.writeUTF(key);
List<String> values = entry.keys.get(key);
int valuesCount = values.size();
dout.writeInt(valuesCount);
for (int k = 0; k < valuesCount; k++) {
dout.writeUTF(values.get(k));
}
}
}
}
public synchronized String[] queryDocumentNames() {
Set<String> names = docToEnties.keySet();
return names.toArray(new String[names.size()]);
}
public synchronized void remove(String path) {
changes++;
docToEnties.remove(path);
String id = pathToIDCache.remove(path);
pathToNameCache.remove(path);
List<String> list = idToPathCache.get(id);
if (list != null) {
list.remove(path);
if (list.isEmpty()) {
idToPathCache.remove(id);
}
}
}
public synchronized void save() throws IOException {
if (changes == 0 && indexFile.exists()) {
return;
}
if (docToEnties.isEmpty()) {
if (indexFile.exists()) {
indexFile.delete();
}
}
DataOutputStream dout = null;
try {
dout = new DataOutputStream(new BufferedOutputStream(
new FileOutputStream(indexFile)));
writeIndex_1_0(dout);
} finally {
FileUtil.safeClose(dout);
}
changes = 0;
}
public synchronized void dispose() {
docToEnties.clear();
if (indexFile.exists()) {
indexFile.delete();
}
}
public synchronized void addKey(String path, String key, String value) {
if (changes >= 2500) {
try {
save();
} catch (IOException e) {
RcpttPlugin.log(e);
}
}
changes++;
getEntry(path).addKey(key, value);
if (IQ7IndexConstants.ID.equals(key)) {
pathToIDCache.put(path, value);
updateIdToPath(path, value);
}
if (IQ7IndexConstants.NAME.equals(key)) {
pathToNameCache.put(path, value);
}
}
private void updateIdToPath(String path, String id) {
List<String> list = idToPathCache.get(id);
if (list == null) {
list = new ArrayList<String>();
idToPathCache.put(id, list);
}
if (!list.contains(path)) {
list.add(path);
}
}
private Entry getEntry(String path) {
Entry entry = docToEnties.get(path);
if (entry == null) {
entry = new Entry();
entry.path = path;
docToEnties.put(path, entry);
}
return entry;
}
public synchronized void updateModificationstamp(String path, long stamp) {
changes++;
getEntry(path).timestamp = stamp;
}
public synchronized long getModificationStamp(String path) {
return getEntry(path).timestamp;
}
public synchronized List<QueryResult> queryIDs(String id) {
List<QueryResult> result = new ArrayList<QueryResult>();
List<String> list = idToPathCache.get(id);
if (list == null) {
return result;
}
for (String path : list) {
result.add(new QueryResult(path, IQ7IndexConstants.ID, id));
}
return result;
}
public synchronized String queryID(String path) {
return pathToIDCache.get(path);
}
public synchronized String queryName(String path) {
return pathToNameCache.get(path);
}
public synchronized List<QueryResult> query(final String[] keys,
final IKeyQuery pattern, IProgressMonitor monitor) {
// ExecutorService executor = Executors.newFixedThreadPool(N_THREADS);
final List<QueryResult> results = new ArrayList<QueryResult>();
for (final Map.Entry<String, Entry> entry : docToEnties.entrySet()) {
if (monitor.isCanceled()) {
throw new OperationCanceledException();
}
// executor.execute(new Runnable() {
// public void run() {
Entry value = entry.getValue();
for (String k : keys) {
List<String> list = value.keys.get(k);
if (list != null) {
for (String val : list) {
if (pattern.accept(k, val)) {
synchronized (results) {
results.add(new QueryResult(entry.getKey(), k,
val));
}
}
}
}
}
// }
// });
}
// executor.shutdown();
// while (!executor.isTerminated()) {
// try {
// Thread.sleep(50);
// } catch (Throwable e) {
// // ignore
// }
// }
return results;
}
public IPath getPath() {
return path;
}
public synchronized Map<String, List<String>> query(String documentName) {
Entry entry = getEntry(documentName);
Map<String, List<String>> copy = new HashMap<String, List<String>>();
Set<Map.Entry<String, List<String>>> entrySet = entry.keys.entrySet();
for (Map.Entry<String, List<String>> entryValue : entrySet) {
copy.put(entryValue.getKey(),
new ArrayList<String>(entryValue.getValue()));
}
return copy;
}
}