();
@@ -75,7 +75,7 @@
}
- static interface TermCollecting {
+ public static interface TermCollecting {
void attach (TermCollector collector);
}
diff --git a/parsing.lucene/src/org/netbeans/modules/parsing/lucene/layer.xml b/parsing.lucene/src/org/netbeans/modules/parsing/lucene/layer.xml
new file mode 100644
--- /dev/null
+++ b/parsing.lucene/src/org/netbeans/modules/parsing/lucene/layer.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/parsing.lucene/src/org/netbeans/modules/parsing/lucene/support/Convertor.java b/parsing.lucene/src/org/netbeans/modules/parsing/lucene/support/Convertor.java
new file mode 100644
--- /dev/null
+++ b/parsing.lucene/src/org/netbeans/modules/parsing/lucene/support/Convertor.java
@@ -0,0 +1,59 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2010 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2010 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.parsing.lucene.support;
+
+/**
+ * A convertor used by the {@link Index} to convert user types
+ * into or from lucene Documents, Queries, Terms.
+ * The interface allows isolation of user code from the lucene
+ * specific types.
+ * @author Tomas Zezula
+ */
+public interface Convertor {
+ /**
+ * Converts given object
+ * @param p the object to be converted
+ * @return the result of conversion
+ */
+ R convert (P p);
+}
diff --git a/parsing.lucene/src/org/netbeans/modules/parsing/lucene/support/Index.java b/parsing.lucene/src/org/netbeans/modules/parsing/lucene/support/Index.java
new file mode 100644
--- /dev/null
+++ b/parsing.lucene/src/org/netbeans/modules/parsing/lucene/support/Index.java
@@ -0,0 +1,149 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2010 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2010 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.parsing.lucene.support;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.FieldSelector;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.Query;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
+
+/**
+ * Lucene based index supporting queries and stores. The index instances
+ * do resource management (memory cache of used indexes, LRU cache of open
+ * file handles). The user is responsible for closing the index when it's no
+ * more used to keep the index consistent.
+ * @author Tomas Zezula
+ */
+public interface Index {
+
+ /**
+ * An exception thrown by {@link Index} when operation is called on
+ * a closed index.
+ */
+ public static final class IndexClosedException extends IOException {
+ }
+
+ /**
+ * Check if the index already exists
+ * @return true if the index already exists on disk.
+ */
+ boolean exists ();
+
+ /**
+ * Checks the validity of the index. The index is invalid when it's broken.
+ * @param tryOpen when true the {@link Index} does exact but more expensive check.
+ * @return true when {@link Index} is not broken
+ * @throws IOException in case of IO problem
+ */
+ boolean isValid (boolean tryOpen) throws IOException;
+
+ /**
+ * Queries the {@link Index} by given queries.
+ * @param result the {@link Collection} to store query results into
+ * @param convertor the {@link Convertor} used to convert lucene documents into the user objects added into the result
+ * @param selector the selector used to select document's fields which should be loaded, if null all fields are loaded
+ * @param cancel the {@link AtomicBoolean} used to cancel the index iteration by the caller. When set to true the iteration
+ * is stopped.
+ * @param queries the queries to be performed on the {@link Index}
+ * @throws IOException in case of IO problem
+ * @throws InterruptedException when query was canceled
+ */
+ void query (Collection super T> result, @NonNull Convertor super Document, T> convertor, @NullAllowed FieldSelector selector, @NullAllowed AtomicBoolean cancel, @NonNull Query... queries) throws IOException, InterruptedException;
+
+ /**
+ * Queries the {@link Index} by given queries. In addition to documents it also collects the terms which matched the queries.
+ * @param result the {@link Collection} to store query results into
+ * @param convertor the {@link Convertor} used to convert lucene documents into the user objects added into the result
+ * @param termConvertor the {@link Convertor} used to convert lucene terms into the user objects added into the result
+ * @param selector the selector used to select document's fields which should be loaded, if null all fields are loaded
+ * @param cancel the {@link AtomicBoolean} used to cancel the index iteration by the caller. When set to true the iteration
+ * is stopped.
+ * @param queries the queries to be performed on the {@link Index}
+ * @throws IOException in case of IO problem
+ * @throws InterruptedException when query was canceled
+ */
+ void queryDocTerms(Map super T, Set> result, @NonNull Convertor super Document, T> convertor, @NonNull Convertor super Term, S> termConvertor,@NullAllowed FieldSelector selector, @NullAllowed AtomicBoolean cancel, @NonNull Query... queries) throws IOException, InterruptedException;
+
+ /**
+ * Queries the {@link Index}'s b-tree for terms starting by the start term and accepted by the filter.
+ * @param result the {@link Collection} to store results into
+ * @param start the first term to start the b-tree iteration with, if null the iteration start on the first term.
+ * @param filter converting the terms into the user objects which are added into the result or null to skeep them.
+ * The filter can stop the iteration by throwing the {@link StoppableConvertor.Stop}.
+ * @param cancel the {@link AtomicBoolean} used to cancel the index iteration by the caller. When set to true the iteration
+ * is stopped.
+ * @throws IOException in case of IO problem
+ * @throws InterruptedException when query was canceled
+ */
+ void queryTerms(@NonNull Collection super T> result, @NullAllowed Term start, @NonNull StoppableConvertor filter, @NullAllowed AtomicBoolean cancel) throws IOException, InterruptedException;
+
+ /**
+ * Updates the {@link Index} by adding the toAdd objects and deleting toDelete objects.
+ * @param toAdd the objects to be added into the index
+ * @param toDelete the objects to be removed from the index
+ * @param docConvertor the {@link Convertor} used to convert toAdd objects into lucene's Documents which are stored into the {@link Index}
+ * @param queryConvertor the {@link Convertor} used to convert toDelete objects into lucene's Queries used to delete the documents from {@link Index}
+ * @param optimize if true the index is optimized. The optimized index is faster for queries but optimize takes significant time.
+ * @throws IOException in case of IO problem
+ */
+ void store (@NonNull Collection toAdd, @NonNull Collection toDelete, @NonNull Convertor super T, ? extends Document> docConvertor, @NonNull Convertor super S, ? extends Query> queryConvertor, boolean optimize) throws IOException;
+
+ /**
+ * Completely deletes the {@link Index}
+ * @throws IOException in case of IO problem
+ */
+ void clear () throws IOException;
+
+ /**
+ * Closes the {@link Index}
+ * @throws IOException in case of IO problem
+ */
+ void close () throws IOException;
+}
diff --git a/parsing.lucene/src/org/netbeans/modules/parsing/lucene/support/IndexManager.java b/parsing.lucene/src/org/netbeans/modules/parsing/lucene/support/IndexManager.java
new file mode 100644
--- /dev/null
+++ b/parsing.lucene/src/org/netbeans/modules/parsing/lucene/support/IndexManager.java
@@ -0,0 +1,175 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2010 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2010 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.parsing.lucene.support;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.Callable;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import org.apache.lucene.analysis.Analyzer;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.modules.masterfs.providers.ProvidedExtensions;
+import org.netbeans.modules.parsing.lucene.IndexFactory;
+import org.netbeans.modules.parsing.lucene.LuceneIndexFactory;
+import org.openide.util.Parameters;
+
+/**
+ * The {@link IndexManager} controls access to {@link Index} instances and acts
+ * as an {@link Index} factory.
+ *
+ * @author Tomas Zezula
+ */
+public final class IndexManager {
+
+ private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
+
+ static IndexFactory factory = new LuceneIndexFactory(); //Unit tests overrides the factory
+
+ private IndexManager() {}
+
+
+ /**
+ * The action to be performed under the {@link IndexManager}'s lock
+ */
+ public static interface Action {
+
+ /**
+ * The action
+ * @return the action result
+ * @throws IOException
+ * @throws InterruptedException
+ */
+ public R run () throws IOException, InterruptedException;
+ }
+
+
+ /**
+ * Runs the given action under {@link IndexManager}'s write lock.
+ * @param action the action to be performed.
+ * @return the result of the action
+ * @throws IOException when the action throws {@link IOException}
+ * @throws InterruptedException when the action throws {@link InterruptedException}
+ */
+ public static R writeAccess (final Action action) throws IOException, InterruptedException {
+ assert action != null;
+ lock.writeLock().lock();
+ try {
+ return ProvidedExtensions.priorityIO(new Callable() {
+ @Override
+ public R call() throws Exception {
+ return action.run();
+ }
+ });
+ } catch (IOException ioe) {
+ //rethrow ioe
+ throw ioe;
+ } catch (InterruptedException ie) {
+ //rethrow ioe
+ throw ie;
+ } catch (RuntimeException re) {
+ //rethrow ioe
+ throw re;
+ } catch (Exception e) {
+ throw new IOException(e);
+ } finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+ /**
+ * Runs the given action under {@link IndexManager}'s read lock.
+ * @param action the action to be performed.
+ * @return the result of the action
+ * @throws IOException when the action throws {@link IOException}
+ * @throws InterruptedException when the action throws {@link InterruptedException}
+ */
+ public static R readAccess (final Action action) throws IOException, InterruptedException {
+ assert action != null;
+ lock.readLock().lock();
+ try {
+ return ProvidedExtensions.priorityIO(new Callable() {
+ @Override
+ public R call() throws Exception {
+ return action.run();
+ }
+ });
+ } catch (IOException ioe) {
+ //rethrow ioe
+ throw ioe;
+ } catch (InterruptedException ie) {
+ //rethrow ioe
+ throw ie;
+ } catch (RuntimeException re) {
+ //rethrow ioe
+ throw re;
+ } catch (Exception e) {
+ throw new IOException(e);
+ } finally {
+ lock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Checks if the caller thread holds the {@link IndexManager}'s write lock
+ * @return true when the caller holds the lock
+ */
+ public static boolean holdsWriteLock () {
+ return lock.isWriteLockedByCurrentThread();
+ }
+
+ /**
+ * Creates a new {@link Index} for given folder with given lucene Analyzer.
+ * The returned {@link Index} is not cached, next call with the same arguments returns a different instance
+ * of {@link Index}. The caller is responsible to cache the returned {@link Index}.
+ * @param cacheFolder the folder in which the index is stored
+ * @param analyzer the lucene Analyzer used to split fields into tokens.
+ * @return the created {@link Index}
+ * @throws IOException in case of IO problem.
+ */
+ public static Index createIndex(final @NonNull File cacheFolder, final @NonNull Analyzer analyzer) throws IOException {
+ Parameters.notNull("cacheFolder", cacheFolder); //NOI18N
+ Parameters.notNull("analyzer", analyzer); //NOI18N
+ return factory.createIndex(cacheFolder, analyzer);
+ }
+
+}
diff --git a/parsing.lucene/src/org/netbeans/modules/parsing/lucene/support/Queries.java b/parsing.lucene/src/org/netbeans/modules/parsing/lucene/support/Queries.java
new file mode 100644
--- /dev/null
+++ b/parsing.lucene/src/org/netbeans/modules/parsing/lucene/support/Queries.java
@@ -0,0 +1,635 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2010 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2010 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.parsing.lucene.support;
+
+import java.io.IOException;
+import java.util.BitSet;
+import java.util.regex.Pattern;
+import org.apache.lucene.document.FieldSelector;
+import org.apache.lucene.document.FieldSelectorResult;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermDocs;
+import org.apache.lucene.index.TermEnum;
+import org.apache.lucene.search.BooleanClause;
+import org.apache.lucene.search.BooleanClause.Occur;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.DocIdSet;
+import org.apache.lucene.search.Filter;
+import org.apache.lucene.search.FilteredQuery;
+import org.apache.lucene.search.FilteredTermEnum;
+import org.apache.lucene.search.MatchAllDocsQuery;
+import org.apache.lucene.search.PrefixQuery;
+import org.apache.lucene.search.PrefixTermEnum;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.util.OpenBitSet;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.modules.parsing.lucene.TermCollector;
+import org.openide.util.Parameters;
+
+/**
+ * A factory class for creating queries and filed selectors
+ * @author Tomas Zezula
+ */
+public final class Queries {
+
+ /**
+ * Encodes a type of the query used by {@link Queries#createQuery}
+ * and {@link Queries#createTermCollectingQuery}
+ */
+ public enum QueryKind {
+ /**
+ * The created query looks for exact match with given text
+ */
+ EXACT,
+
+ /**
+ * The given text is a prefix of requested value.
+ */
+ PREFIX,
+
+ /**
+ * The given text is a case insensitive prefix of requested value.
+ */
+ CASE_INSENSITIVE_PREFIX,
+
+ /**
+ * The given text is treated as camel case pattern used to match the value.
+ */
+ CAMEL_CASE,
+
+ /**
+ * The given text is treated as case insensitive camel case pattern used to match the value.
+ */
+ CASE_INSENSITIVE_CAMEL_CASE,
+
+ /**
+ * The given text is a regular expression used to match the value.
+ */
+ REGEXP,
+
+ /**
+ * The given text is a case insensitive regular expression used to match the value.
+ */
+ CASE_INSENSITIVE_REGEXP;
+ }
+
+ /**
+ * Creates a standard lucene query querying the index for
+ * documents having indexed field containing the given value
+ * @param fieldName the name of the field
+ * @param caseInsensitiveFieldName the name of the field containing the case insensitive value
+ * @param value the value to search for
+ * @param kind the type of query, {@link Queries.QueryKind}
+ * @return the created query
+ */
+ public static Query createQuery (
+ final @NonNull String fieldName,
+ final @NonNull String caseInsensitiveFieldName,
+ final @NonNull String value,
+ final @NonNull QueryKind kind) {
+ Parameters.notNull("fieldName", fieldName); //NOI18N
+ Parameters.notNull("caseInsensitiveFieldName", caseInsensitiveFieldName); //NOI18N
+ Parameters.notNull("value", value); //NOI18N
+ Parameters.notNull("kind", kind); //NOI18N
+ return createQueryImpl(fieldName, caseInsensitiveFieldName, value, kind, new StandardQueryFactory());
+ }
+
+ /**
+ * Creates an extended lucene query querying the index for
+ * documents having indexed field containing the given value.
+ * This query is required by the {@link Index#queryDocTerms} method,
+ * in addition to matching documents the query also collects the matched terms.
+ * @param fieldName the name of the field
+ * @param caseInsensitiveFieldName the name of the field containing the case insensitive value
+ * @param value the value to search for
+ * @param kind the type of query {@link Queries.QueryKind}
+ * @return the created query
+ */
+ public static Query createTermCollectingQuery(
+ final @NonNull String fieldName,
+ final @NonNull String caseInsensitiveFieldName,
+ final @NonNull String value,
+ final @NonNull QueryKind kind) {
+ Parameters.notNull("fieldName", fieldName); //NOI18N
+ Parameters.notNull("caseInsensitiveFieldName", caseInsensitiveFieldName); //NOI18N
+ Parameters.notNull("value", value); //NOI18N
+ Parameters.notNull("kind", kind); //NOI18N
+ return createQueryImpl(fieldName, caseInsensitiveFieldName, value, kind, new TCQueryFactory());
+ }
+
+ /**
+ * Creates a FieldSelector loading the given fields.
+ * @param fieldsToLoad the fields to be loaded into the document.
+ * @return the created FieldSelector
+ */
+ public static FieldSelector createFieldSelector(final @NonNull String... fieldsToLoad) {
+ return new FieldSelectorImpl(fieldsToLoad);
+ }
+
+
+ //
+ private static Query createQueryImpl(
+ final @NonNull String fieldName,
+ final @NonNull String caseInsensitiveFieldName,
+ final @NonNull String value,
+ final @NonNull QueryKind kind,
+ final @NonNull QueryFactory f) {
+ switch (kind) {
+ case EXACT:
+ return f.createTermQuery(fieldName, value);
+ case PREFIX:
+ if (value.length() == 0) {
+ return f.createAllDocsQuery(fieldName);
+ }
+ else {
+ return f.createPrefixQuery(fieldName, value);
+ }
+ case CASE_INSENSITIVE_PREFIX:
+ if (value.length() == 0) {
+ return f.createAllDocsQuery(caseInsensitiveFieldName);
+ }
+ else {
+ return f.createPrefixQuery(caseInsensitiveFieldName, value.toLowerCase());
+ }
+ case CAMEL_CASE:
+ if (value.length() == 0) {
+ throw new IllegalArgumentException ();
+ } else {
+ return f.createRegExpQuery(fieldName,createCamelCaseRegExp(value, true), true);
+ }
+ case CASE_INSENSITIVE_REGEXP:
+ if (value.length() == 0) {
+ throw new IllegalArgumentException ();
+ } else {
+ return f.createRegExpQuery(caseInsensitiveFieldName, value.toLowerCase(), false);
+ }
+ case REGEXP:
+ if (value.length() == 0) {
+ throw new IllegalArgumentException ();
+ } else {
+ return f.createRegExpQuery(fieldName, value, true);
+ }
+ case CASE_INSENSITIVE_CAMEL_CASE:
+ if (value.length() == 0) {
+ //Special case (all) handle in different way
+ return f.createAllDocsQuery(caseInsensitiveFieldName);
+ }
+ else {
+ final Query pq = f.createPrefixQuery(caseInsensitiveFieldName, value.toLowerCase());
+ final Query fq = f.createRegExpQuery(caseInsensitiveFieldName, createCamelCaseRegExp(value, false), false);
+ final BooleanQuery result = f.createBooleanQuery();
+ result.add(pq, Occur.SHOULD);
+ result.add(fq, Occur.SHOULD);
+ return result;
+ }
+ default:
+ throw new UnsupportedOperationException (kind.toString());
+ }
+ }
+
+ private static String createCamelCaseRegExp(final String camel, final boolean caseSensitive) {
+ final StringBuilder sb = new StringBuilder();
+ int lastIndex = 0;
+ int index;
+ do {
+ index = findNextUpper(camel, lastIndex + 1);
+ String token = camel.substring(lastIndex, index == -1 ? camel.length(): index);
+ sb.append(Pattern.quote(caseSensitive ? token : token.toLowerCase()));
+ sb.append( index != -1 ? "[\\p{javaLowerCase}\\p{Digit}_\\$]*" : ".*"); // NOI18N
+ lastIndex = index;
+ } while(index != -1);
+ return sb.toString();
+ }
+
+ private static int findNextUpper(String text, int offset ) {
+ for( int i = offset; i < text.length(); i++ ) {
+ if ( Character.isUpperCase(text.charAt(i)) ) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private static abstract class DocumentVisitor {
+
+ public void generate(IndexReader reader, TermEnum enumerator) throws IOException {
+ final int[] docs = new int[32];
+ final int[] freqs = new int[32];
+ final TermDocs termDocs = reader.termDocs();
+ try {
+ do {
+ final Term term = enumerator.term();
+ if (term == null) {
+ break;
+ }
+ termDocs.seek(term);
+ while (true) {
+ final int count = termDocs.read(docs, freqs);
+ if (count != 0) {
+ for (int i = 0; i < count; i++) {
+ visit(term, docs[i]);
+ }
+ } else {
+ break;
+ }
+ }
+ } while (enumerator.next());
+ } finally {
+ termDocs.close();
+ }
+ }
+
+ abstract public void visit(Term term, int doc);
+ }
+
+ private static abstract class TCFilter extends Filter {
+ public abstract void attach (TermCollector collector);
+ }
+
+ private static abstract class AbstractTCFilter extends TCFilter {
+
+ private TermCollector termCollector;
+
+ @Override
+ public final BitSet bits(IndexReader reader) throws IOException {
+ final FilteredTermEnum enumerator = getTermEnum(reader);
+ try {
+ final BitSet bitSet = new BitSet(reader.maxDoc());
+ new DocumentVisitor() {
+ @Override
+ public void visit(Term term, int doc) {
+ bitSet.set(doc);
+ if (termCollector != null) {
+ termCollector.add(doc, term);
+ }
+ }
+ }.generate(reader, enumerator);
+ return bitSet;
+ } finally {
+ enumerator.close();
+ }
+ }
+
+ @Override
+ public final DocIdSet getDocIdSet(IndexReader reader) throws IOException {
+ final FilteredTermEnum enumerator = getTermEnum(reader);
+ try {
+ // if current term in enum is null, the enum is empty -> shortcut
+ if (enumerator.term() == null) {
+ return DocIdSet.EMPTY_DOCIDSET;
+ }
+ // else fill into a OpenBitSet
+ final OpenBitSet bitSet = new OpenBitSet(reader.maxDoc());
+ new DocumentVisitor() {
+ @Override
+ public void visit(Term term, int doc) {
+ bitSet.set(doc);
+ if (termCollector != null) {
+ termCollector.add(doc, term);
+ }
+ }
+ }.generate(reader, enumerator);
+ return bitSet;
+ } finally {
+ enumerator.close();
+ }
+ }
+
+ @Override
+ public final void attach(final TermCollector tc) {
+ this.termCollector = tc;
+ }
+
+ protected abstract FilteredTermEnum getTermEnum(IndexReader reader) throws IOException;
+
+ }
+
+ private static class RegexpTermEnum extends FilteredTermEnum {
+
+ private final String fieldName;
+ private final String startPrefix;
+ private final Pattern pattern;
+ private boolean endEnum;
+
+ public RegexpTermEnum(
+ final IndexReader in,
+ final String fieldName,
+ final Pattern pattern,
+ final String startPrefix) throws IOException {
+ final Term term = new Term(fieldName,startPrefix);
+ this.fieldName = term.field();
+ this.pattern = pattern;
+ this.startPrefix = startPrefix;
+ setEnum(in.terms(term));
+ }
+
+ @Override
+ protected boolean termCompare(Term term) {
+ if (fieldName == term.field()) {
+ String searchText = term.text();
+ if (searchText.startsWith(startPrefix)) {
+ return pattern.matcher(term.text()).matches();
+ }
+ }
+ endEnum = true;
+ return false;
+ }
+
+ @Override
+ public float difference() {
+ return 1.0f;
+ }
+
+ @Override
+ protected boolean endEnum() {
+ return endEnum;
+ }
+ }
+
+ private static class RegexpFilter extends AbstractTCFilter {
+
+ private final String fieldName;
+ private final String startPrefix;
+ private final Pattern pattern;
+
+ public RegexpFilter(final String fieldName, final String regexp, final boolean caseSensitive) {
+ this.fieldName = fieldName;
+ this.pattern = caseSensitive ? Pattern.compile(regexp) : Pattern.compile(regexp, Pattern.CASE_INSENSITIVE);
+ this.startPrefix = getStartText(regexp);
+ }
+
+ protected FilteredTermEnum getTermEnum(final @NonNull IndexReader reader) throws IOException {
+ return new RegexpTermEnum(reader, fieldName, pattern, startPrefix);
+ }
+
+ private static String getStartText(final String regexp) {
+ if (!Character.isJavaIdentifierStart(regexp.charAt(0))) {
+ return ""; //NOI18N
+ }
+ final StringBuilder startBuilder = new StringBuilder ();
+ startBuilder.append(regexp.charAt(0));
+ for (int i=1; i
+
+}
diff --git a/parsing.lucene/src/org/netbeans/modules/parsing/lucene/support/StoppableConvertor.java b/parsing.lucene/src/org/netbeans/modules/parsing/lucene/support/StoppableConvertor.java
new file mode 100644
--- /dev/null
+++ b/parsing.lucene/src/org/netbeans/modules/parsing/lucene/support/StoppableConvertor.java
@@ -0,0 +1,67 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2010 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2010 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.parsing.lucene.support;
+
+/**
+ * A convertor used by the {@link Index#queryTerms} to convert lucene Terms
+ * into user types.
+ * The interface allows isolation of user code from the lucene
+ * specific types.
+ * @author Tomas Zezula
+ */
+public interface StoppableConvertor {
+
+ /**
+ * The exception used by the convertor to stop the iteration.
+ */
+ public static final class Stop extends Exception {};
+
+ /**
+ * Converts given object
+ * @param p the object to be converted
+ * @return the result of conversion
+ * @throws Stop to stop the index iteration
+ */
+ R convert (P param) throws Stop;
+
+}
diff --git a/parsing.api/test/unit/src/org/netbeans/modules/parsing/impl/indexing/lucene/util/LRUCacheTest.java b/parsing.lucene/test/unit/src/org/netbeans/modules/parsing/lucene/LRUCacheTest.java
rename from parsing.api/test/unit/src/org/netbeans/modules/parsing/impl/indexing/lucene/util/LRUCacheTest.java
rename to parsing.lucene/test/unit/src/org/netbeans/modules/parsing/lucene/LRUCacheTest.java
--- a/parsing.api/test/unit/src/org/netbeans/modules/parsing/impl/indexing/lucene/util/LRUCacheTest.java
+++ b/parsing.lucene/test/unit/src/org/netbeans/modules/parsing/lucene/LRUCacheTest.java
@@ -40,7 +40,7 @@
* Portions Copyrighted 2009 Sun Microsystems, Inc.
*/
-package org.netbeans.modules.parsing.impl.indexing.lucene.util;
+package org.netbeans.modules.parsing.lucene;
import java.util.HashSet;
import java.util.Set;
@@ -81,6 +81,7 @@
}
private static class TestEvictionPolicy implements EvictionPolicy {
+ @Override
public boolean shouldEvict(int size, Integer key, Evictable value) {
return size > 10;
}
@@ -94,6 +95,7 @@
this.value = i;
}
+ @Override
public void evicted() {
used.remove(value);
}
diff --git a/parsing.lucene/test/unit/src/org/netbeans/modules/parsing/lucene/LuceneIndexTest.java b/parsing.lucene/test/unit/src/org/netbeans/modules/parsing/lucene/LuceneIndexTest.java
new file mode 100644
--- /dev/null
+++ b/parsing.lucene/test/unit/src/org/netbeans/modules/parsing/lucene/LuceneIndexTest.java
@@ -0,0 +1,202 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2010 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2010 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.parsing.lucene;
+
+/**
+ *
+ * @author Tomas Zezula
+ */
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.apache.lucene.analysis.KeywordAnalyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.store.Directory;
+import org.netbeans.junit.NbTestCase;
+import org.netbeans.modules.parsing.lucene.support.Convertor;
+
+/**
+ *
+ * @author Tomas Zezula
+ */
+public class LuceneIndexTest extends NbTestCase {
+
+ public LuceneIndexTest (String testName) {
+ super (testName);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ this.clearWorkDir();
+ //Prepare indeces
+ }
+
+ public void testIsValid() throws Exception {
+ final File wd = getWorkDir();
+ final File cache = new File(wd,"cache");
+ cache.mkdirs();
+ final LuceneIndex index = LuceneIndex.create(cache, new KeywordAnalyzer());
+ //Empty index => invalid
+ assertFalse(index.isValid(true));
+
+ clearValidityCache(index);
+ List refs = new ArrayList();
+ refs.add("A");
+ Set toDel = new HashSet();
+ index.store(
+ refs,
+ toDel,
+ new StrToDocConvertor("resources"),
+ new StrToQueryCovertor("resource"),
+ true);
+ //Existing index => valid
+ assertTrue(index.isValid(true));
+ assertTrue(cache.listFiles().length>0);
+
+ clearValidityCache(index);
+ createLock(index);
+ //Index with orphan lock => invalid
+ assertFalse(index.isValid(true));
+ assertTrue(cache.listFiles().length==0);
+
+ refs.add("B");
+ clearValidityCache(index);
+ index.store(
+ refs,
+ toDel,
+ new StrToDocConvertor("resources"),
+ new StrToQueryCovertor("resource"),
+ true);
+ assertTrue(index.isValid(true));
+ assertTrue(cache.listFiles().length>0);
+
+ //Broken index => invalid
+ clearValidityCache(index);
+ File bt = null;
+ for (File file : cache.listFiles()) {
+ if (file.getName().endsWith(".cfs")) {
+ bt = file;
+ break;
+ }
+ }
+ assertNotNull(bt);
+ FileOutputStream out = new FileOutputStream(bt);
+ try {
+ out.write(new byte[] {0,0,0,0,0,0,0,0,0,0}, 0, 10);
+ } finally {
+ out.close();
+ }
+ assertFalse(index.isValid(true));
+ assertTrue(cache.listFiles().length==0);
+
+ }
+
+
+ private void createLock(final LuceneIndex index) throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException, IOException {
+ final Class li = LuceneIndex.class;
+ final java.lang.reflect.Field dirCache = li.getDeclaredField("dirCache"); //NOI18N
+ dirCache.setAccessible(true);
+ Object o = dirCache.get(index);
+ final java.lang.reflect.Field directory = o.getClass().getDeclaredField("fsDir"); //NOI18N
+ directory.setAccessible(true);
+ Directory dir = (Directory) directory.get(o);
+ dir.makeLock("test").obtain(); //NOI18N
+ }
+
+
+ private void clearValidityCache(final LuceneIndex index) throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException, IOException {
+ final Class li = LuceneIndex.class;
+ final java.lang.reflect.Field dirCache = li.getDeclaredField("dirCache"); //NOI18N
+ dirCache.setAccessible(true);
+ Object o = dirCache.get(index);
+ final java.lang.reflect.Field reader = o.getClass().getDeclaredField("reader");
+ reader.setAccessible(true);
+ IndexReader r = (IndexReader) reader.get(o);
+ if (r != null) {
+ r.close();
+ }
+ reader.set(o,null);
+ }
+
+ private static class StrToDocConvertor implements Convertor{
+
+ private final String name;
+
+ public StrToDocConvertor(final String name) {
+ this.name = name;
+ }
+
+ @Override
+ public Document convert(final String p) {
+ final Document doc = new Document();
+ doc.add(new Field(name, p, Field.Store.YES, Field.Index.ANALYZED));
+ return doc;
+ }
+ }
+
+ private static class StrToQueryCovertor implements Convertor {
+
+ private final String name;
+
+ public StrToQueryCovertor(final String name) {
+ this.name = name;
+ }
+
+ @Override
+ public Query convert(String p) {
+ return new TermQuery(new Term(name, p));
+ }
+ }
+
+}
+
diff --git a/parsing.lucene/test/unit/src/org/netbeans/modules/parsing/lucene/support/IndexManagerTestUtilities.java b/parsing.lucene/test/unit/src/org/netbeans/modules/parsing/lucene/support/IndexManagerTestUtilities.java
new file mode 100644
--- /dev/null
+++ b/parsing.lucene/test/unit/src/org/netbeans/modules/parsing/lucene/support/IndexManagerTestUtilities.java
@@ -0,0 +1,65 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2010 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2010 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.parsing.lucene.support;
+
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.modules.parsing.lucene.IndexFactory;
+import org.openide.util.Parameters;
+
+/**
+ *
+ * @author Tomas Zezula
+ */
+public final class IndexManagerTestUtilities {
+ private IndexManagerTestUtilities(){}
+
+ public static IndexFactory getIndexFactory() {
+ return IndexManager.factory;
+ }
+
+ public static void setIndexFactory(final @NonNull IndexFactory factory) {
+ Parameters.notNull("factory",factory); //NOI18N
+ IndexManager.factory = factory;
+ }
+
+}