blob: ba485943605b11917709539de50243dbcfdb3acf [file] [log] [blame]
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2009-2010 Sun Microsystems, Inc. All rights reserved.
*
* 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. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun 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 2009-2010 Sun Microsystems, Inc.
*/
package org.netbeans.modules.jackpot30.indexing.index;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.zip.DataFormatException;
import org.apache.lucene.analysis.KeywordAnalyzer;
import org.apache.lucene.document.CompressionTools;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.FieldSelector;
import org.apache.lucene.document.FieldSelectorResult;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.PhraseQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
import org.netbeans.api.annotations.common.NullAllowed;
import org.netbeans.modules.jackpot30.remoting.api.RemoteIndex;
import org.netbeans.modules.jackpot30.remoting.api.WebUtilities;
import org.netbeans.modules.java.hints.providers.spi.HintDescription.AdditionalQueryConstraints;
import org.netbeans.modules.java.hints.spiimpl.pm.BulkSearch;
import org.netbeans.modules.java.hints.spiimpl.pm.BulkSearch.BulkPattern;
import org.netbeans.modules.parsing.lucene.support.Convertor;
import org.netbeans.modules.parsing.lucene.support.Index;
import org.netbeans.modules.parsing.lucene.support.Index.Status;
import org.netbeans.modules.parsing.lucene.support.IndexManager;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.util.Exceptions;
/**
*
* @author lahvac
*/
public abstract class IndexQuery {
public abstract Collection<? extends String> findCandidates(BulkPattern pattern) throws IOException;
public abstract Map<String, Map<String, Integer>> findCandidatesWithFrequencies(BulkPattern pattern) throws IOException;
public static Map<String, Map<String, Integer>> performLocalQuery(Index index, final BulkPattern pattern, final boolean withFrequencies) throws IOException, InterruptedException, ParseException {
final Map<String, Map<String, Integer>> result = new HashMap<String, Map<String, Integer>>();
index.query(new ArrayList<Object>(), new Convertor<Document, Object>() {
@Override public Object convert(Document doc) {
try {
ByteArrayInputStream in = new ByteArrayInputStream(CompressionTools.decompress(doc.getField("languageEncoded").getBinaryValue()));
try {
Map<String, Integer> freqs;
boolean matches;
if (withFrequencies) {
freqs = BulkSearch.getDefault().matchesWithFrequencies(in, pattern, new AtomicBoolean());
matches = !freqs.isEmpty();
} else {
freqs = null;
matches = BulkSearch.getDefault().matches(in, new AtomicBoolean(), pattern);
}
if (matches) {
result.put(doc.getField("languagePath").stringValue(), freqs);
}
} finally {
in.close();
}
} catch (DataFormatException ex) {
throw new IllegalStateException(ex);
} catch (IOException ex) {
throw new IllegalStateException(ex);
}
return null;
}
}, new FieldSelector() {
public FieldSelectorResult accept(String string) {
return "languageEncoded".equals(string) || "languagePath".equals(string) ? FieldSelectorResult.LOAD : FieldSelectorResult.NO_LOAD;
}
}, null, query(pattern));
return result;
}
private static Query query(BulkPattern pattern) throws ParseException {
BooleanQuery result = new BooleanQuery();
for (int cntr = 0; cntr < pattern.getIdentifiers().size(); cntr++) {
assert !pattern.getRequiredContent().get(cntr).isEmpty();
BooleanQuery emb = new BooleanQuery();
for (List<String> c : pattern.getRequiredContent().get(cntr)) {
if (c.isEmpty()) continue;
PhraseQuery pq = new PhraseQuery();
for (String s : c) {
pq.add(new Term("languageContent", s));
}
emb.add(pq, BooleanClause.Occur.MUST);
}
AdditionalQueryConstraints additionalConstraints = pattern.getAdditionalConstraints().get(cntr);
if (additionalConstraints != null && !additionalConstraints.requiredErasedTypes.isEmpty()) {
BooleanQuery constraintsQuery = new BooleanQuery();
constraintsQuery.add(new TermQuery(new Term("languageAttributed", "false")), BooleanClause.Occur.SHOULD);
BooleanQuery constr = new BooleanQuery();
for (String tc : additionalConstraints.requiredErasedTypes) {
constr.add(new TermQuery(new Term("languageErasedTypes", tc)), BooleanClause.Occur.MUST);
}
constraintsQuery.add(constr, BooleanClause.Occur.SHOULD);
emb.add(constraintsQuery, BooleanClause.Occur.MUST);
}
result.add(emb, BooleanClause.Occur.SHOULD);
}
return result;
}
private static final class LocalIndexQuery extends IndexQuery {
private final @NullAllowed File cacheDir;
public LocalIndexQuery(@NullAllowed File cacheDir) {
this.cacheDir = cacheDir;
}
public Collection<? extends String> findCandidates(BulkPattern pattern) throws IOException {
return findCandidates(pattern, false).keySet();
}
public Map<String, Map<String, Integer>> findCandidatesWithFrequencies(BulkPattern pattern) throws IOException {
return findCandidates(pattern, true);
}
private Map<String, Map<String, Integer>> findCandidates(BulkPattern pattern, boolean withFrequencies) throws IOException {
Index index = IndexManager.createIndex(cacheDir, new KeywordAnalyzer());
if (index.getStatus(true) != Status.VALID) {
return Collections.emptyMap();
}
try {
return performLocalQuery(index, pattern, withFrequencies);
} catch (InterruptedException ex) {
throw new IOException(ex);
} catch (ParseException ex) {
throw new IOException(ex);
} finally {
index.close();
}
}
}
private static final class RemoteIndexQuery extends IndexQuery {
private final RemoteIndex idx;
public RemoteIndexQuery(RemoteIndex idx) {
this.idx = idx;
}
@Override
public Collection<? extends String> findCandidates(BulkPattern pattern) throws IOException {
try {
StringBuilder patterns = new StringBuilder();
for (String p : pattern.getPatterns()) {
patterns.append(p);
patterns.append(";;");
}
URI u = new URI(idx.remote.toExternalForm() + "?path=" + WebUtilities.escapeForQuery(idx.remoteSegment) + "&pattern=" + WebUtilities.escapeForQuery(patterns.toString()));
return new ArrayList<String>(WebUtilities.requestStringArrayResponse(u));
} catch (URISyntaxException ex) {
//XXX: better handling?
Exceptions.printStackTrace(ex);
return Collections.emptyList();
}
}
@Override
public Map<String, Map<String, Integer>> findCandidatesWithFrequencies(BulkPattern pattern) throws IOException {
throw new UnsupportedOperationException("Not supported yet.");
}
}
public static IndexQuery open(URL sourceRoot) throws IOException {
FileObject cacheFO = Indexer.resolveCacheFolder(sourceRoot).getFileObject(Indexer.INDEX_NAME);
File cache = cacheFO != null ? FileUtil.toFile(cacheFO) : null;
return new LocalIndexQuery(cache);
}
public static IndexQuery remote(RemoteIndex idx) {
return new RemoteIndexQuery(idx);
}
}