Merge remote-tracking branch 'origin/master'

This commit is contained in:
Dmitry Trofimov
2013-07-12 16:31:53 +02:00
360 changed files with 13934 additions and 2197 deletions

View File

@@ -83,6 +83,7 @@ def layoutFull(String home, String targetDirectory, String patchedDescriptorDir
"InspectionGadgetsAnalysis",
"InspectionGadgetsPlugin",
"IntentionPowerPackPlugin",
"generate-tostring",
].flatten()
ant.patternset(id: "resources.included") {
@@ -251,7 +252,6 @@ public def layoutCommunityPlugins(String home) {
module("ant-jps-plugin")
}
}
layoutPlugin("ToString", "generate-tostring", "toString")
layoutPlugin("uiDesigner", "ui-designer", "uiDesigner") {
dir("jps") {
jar("ui-designer-jps-plugin.jar") {
@@ -718,8 +718,6 @@ def layout_core(String home, String target) {
module("extensions")
module("java-psi-api")
module("java-psi-impl")
module("analysis-api")
module("analysis-impl")
}
jar("annotations.jar") {

View File

@@ -0,0 +1,273 @@
package com.intellij.jarFinder;
import com.intellij.codeInsight.AttachSourcesProvider;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationType;
import com.intellij.openapi.application.AccessToken;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.roots.LibraryOrderEntry;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.roots.ui.configuration.PathUIUtils;
import com.intellij.openapi.util.ActionCallback;
import com.intellij.openapi.vfs.JarFileSystem;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.util.SystemProperties;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.net.HttpConfigurable;
import com.intellij.util.net.NetUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.*;
import java.net.HttpURLConnection;
import java.util.*;
import java.util.regex.Pattern;
/**
* @author Sergey Evdokimov
*/
public class InternetAttachSourceProvider implements AttachSourcesProvider {
private static final Logger LOG = Logger.getInstance("#com.intellij.jarFinder.SonatypeAttachSourceProvider");
private static final Pattern ARTIFACT_IDENTIFIER = Pattern.compile("[A-Za-z0-9\\.\\-_]+");
@Nullable
protected static VirtualFile getJarByPsiFile(PsiFile psiFile) {
VirtualFile virtualFile = psiFile.getVirtualFile();
if (virtualFile == null) return null;
VirtualFile jar = JarFileSystem.getInstance().getVirtualFileForJar(psiFile.getVirtualFile());
if (jar == null || !jar.getName().endsWith(".jar")) return null;
return jar;
}
@NotNull
@Override
public Collection<AttachSourcesAction> getActions(List<LibraryOrderEntry> orderEntries, final PsiFile psiFile) {
VirtualFile jar = getJarByPsiFile(psiFile);
if (jar == null) return Collections.emptyList();
final String jarName = jar.getNameWithoutExtension();
int index = jarName.lastIndexOf('-');
if (index == -1) return Collections.emptyList();
final String version = jarName.substring(index + 1);
final String artifactId = jarName.substring(0, index);
if (!ARTIFACT_IDENTIFIER.matcher(version).matches() || !ARTIFACT_IDENTIFIER.matcher(artifactId).matches()) {
return Collections.emptyList();
}
final Set<Library> libraries = new HashSet<Library>();
for (LibraryOrderEntry orderEntry : orderEntries) {
ContainerUtil.addIfNotNull(libraries, orderEntry.getLibrary());
}
if (libraries.isEmpty()) return Collections.emptyList();
final String sourceFileName = jarName + "-sources.jar";
for (Library library : libraries) {
for (VirtualFile file : library.getFiles(OrderRootType.SOURCES)) {
if (file.getPath().contains(sourceFileName)) {
if (isRootInExistingFile(file)) {
return Collections.emptyList(); // Sources already attached, but source-jar doesn't contain current class.
}
}
}
}
final File libSourceDir = getLibrarySourceDir();
final File sourceFile = new File(libSourceDir, sourceFileName);
if (sourceFile.exists()) {
return Collections.<AttachSourcesAction>singleton(new LightAttachSourcesAction() {
@Override
public String getName() {
return "Attach downloaded source";
}
@Override
public String getBusyText() {
return getName();
}
@Override
public ActionCallback perform(List<LibraryOrderEntry> orderEntriesContainingFile) {
attachSourceJar(sourceFile, libraries);
return new ActionCallback.Done();
}
});
}
return Collections.<AttachSourcesAction>singleton(new LightAttachSourcesAction() {
@Override
public String getName() {
return "Search in internet...";
}
@Override
public String getBusyText() {
return "Searching...";
}
@Override
public ActionCallback perform(List<LibraryOrderEntry> orderEntriesContainingFile) {
final Task task = new Task.Modal(psiFile.getProject(), "Searching source...", true) {
// Don't move initialization of searchers to static context of top level class, to avoid unnecessary initialization of searcher's classes
private SourceSearcher[] mySearchers = new SourceSearcher[]{new MavenCentralSourceSearcher(), new SonatypeSourceSearcher()};
@Override
public void run(@NotNull final ProgressIndicator indicator) {
String artifactUrl = null;
for (SourceSearcher searcher : mySearchers) {
try {
artifactUrl = searcher.findSourceJar(indicator, artifactId, version);
}
catch (SourceSearchException e) {
showMessage("Downloading failed", e.getMessage(), NotificationType.ERROR);
continue;
}
if (artifactUrl != null) break;
}
if (artifactUrl == null) {
showMessage("Source not found", "Sources for: " + jarName + ".jar not found", NotificationType.WARNING);
return;
}
libSourceDir.mkdirs();
if (!libSourceDir.exists()) {
showMessage("Downloading failed", "Failed to create directory to store sources: " + libSourceDir, NotificationType.ERROR);
return;
}
try {
HttpURLConnection urlConnection = HttpConfigurable.getInstance().openHttpConnection(artifactUrl);
int contentLength = urlConnection.getContentLength();
File tmpDownload = File.createTempFile("download", ".tmp", libSourceDir);
OutputStream out = new BufferedOutputStream(new FileOutputStream(tmpDownload));
try {
InputStream in = urlConnection.getInputStream();
indicator.setText("Downloading sources...");
indicator.setIndeterminate(false);
try {
NetUtils.copyStreamContent(indicator, in, out, contentLength);
}
finally {
in.close();
}
}
finally {
out.close();
}
if (!sourceFile.exists()) {
if (!tmpDownload.renameTo(sourceFile)) {
LOG.warn("Failed to rename file " + tmpDownload + " to " + sourceFileName);
}
}
}
catch (IOException e) {
showMessage("Downloading failed", "Connection problem. See log for more details.", NotificationType.ERROR);
}
}
@Override
public void onSuccess() {
attachSourceJar(sourceFile, libraries);
}
private void showMessage(final String title, final String message, final NotificationType notificationType) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
new Notification("Source searcher",
title,
message,
notificationType)
.notify(getProject());
}
});
}
};
task.queue();
return new ActionCallback.Done();
}
});
}
private static boolean isRootInExistingFile(VirtualFile root) {
if (root.getFileSystem() instanceof JarFileSystem) {
VirtualFile jar = JarFileSystem.getInstance().getVirtualFileForJar(root);
if (jar == null) return false;
jar.refresh(false, false);
return root.isValid();
}
return true;
}
public static void attachSourceJar(@NotNull File sourceJar, @NotNull Collection<Library> libraries) {
AccessToken accessToken = WriteAction.start();
try {
VirtualFile srcFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(sourceJar);
if (srcFile == null) return;
VirtualFile jarRoot = JarFileSystem.getInstance().getJarRootForLocalFile(srcFile);
if (jarRoot == null) return;
VirtualFile[] roots = PathUIUtils.scanAndSelectDetectedJavaSourceRoots(null, new VirtualFile[]{jarRoot});
if (roots.length == 0) {
roots = new VirtualFile[]{jarRoot};
}
for (Library library : libraries) {
Library.ModifiableModel model = library.getModifiableModel();
List<VirtualFile> alreadyExistingFiles = Arrays.asList(model.getFiles(OrderRootType.SOURCES));
for (VirtualFile root : roots) {
if (!alreadyExistingFiles.contains(root)) {
model.addRoot(root, OrderRootType.SOURCES);
}
}
model.commit();
}
}
finally {
accessToken.finish();
}
}
public static File getLibrarySourceDir() {
String path = System.getProperty("idea.library.source.dir");
if (path != null) {
return new File(path);
}
return new File(SystemProperties.getUserHome(), ".ideaLibSources");
}
}

View File

@@ -51,6 +51,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
private Stack<CatchDescriptor> myCatchStack;
private DfaValue myRuntimeException;
private DfaValue myError;
private PsiType myNpe;
ControlFlowAnalyzer(final DfaValueFactory valueFactory) {
myFactory = valueFactory;
@@ -62,6 +63,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
GlobalSearchScope scope = codeFragment.getResolveScope();
myRuntimeException = myFactory.getNotNullFactory().create(PsiType.getJavaLangRuntimeException(manager, scope));
myError = myFactory.getNotNullFactory().create(PsiType.getJavaLangError(manager, scope));
myNpe = JavaPsiFacade.getElementFactory(manager.getProject()).createTypeByFQClassName(JAVA_LANG_NULL_POINTER_EXCEPTION, scope);
myFields = new HashSet<DfaVariableValue>();
myCatchStack = new Stack<CatchDescriptor>();
myPassNumber = 1;
@@ -642,6 +644,13 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
if (exception != null) {
exception.accept(this);
addInstruction(new DupInstruction());
addInstruction(new PushInstruction(myFactory.getConstFactory().getNull(), null));
addInstruction(new BinopInstruction(JavaTokenType.EQEQ, null, statement.getProject()));
ConditionalGotoInstruction gotoInstruction = new ConditionalGotoInstruction(-1, true, null);
addInstruction(gotoInstruction);
addThrowCode(myNpe);
gotoInstruction.setOffset(myCurrentFlow.getInstructionCount());
addThrowCode(exception.getType());
}
@@ -1019,7 +1028,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
@Nullable
private static IElementType substituteBinaryOperation(IElementType op, PsiType type) {
if (JavaTokenType.PLUS == op && (type == null || !type.equalsToText(CommonClassNames.JAVA_LANG_STRING))) {
if (JavaTokenType.PLUS == op && (type == null || !type.equalsToText(JAVA_LANG_STRING))) {
return null;
}
return op;
@@ -1266,7 +1275,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
if (expressions.length == 1 && method instanceof PsiMethod &&
"equals".equals(((PsiMethod)method).getName()) && parameters.length == 1 &&
parameters[0].getType().equalsToText(CommonClassNames.JAVA_LANG_OBJECT) &&
parameters[0].getType().equalsToText(JAVA_LANG_OBJECT) &&
PsiType.BOOLEAN.equals(((PsiMethod)method).getReturnType())) {
addInstruction(new PushInstruction(myFactory.getConstFactory().getFalse(), null));
addInstruction(new SwapInstruction());
@@ -1438,7 +1447,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
addInstruction(new PopInstruction());
}
}
addInstruction(new MethodCallInstruction(expression, (DfaValue)null));
addInstruction(new MethodCallInstruction(expression, null));
}
else {
final PsiExpressionList args = expression.getArgumentList();
@@ -1455,7 +1464,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
}
}
addInstruction(new MethodCallInstruction(expression, (DfaValue)null));
addInstruction(new MethodCallInstruction(expression, null));
if (!myCatchStack.isEmpty()) {
addMethodThrows(ctr);

View File

@@ -34,6 +34,9 @@
<orderEntry type="module" module-name="jps-model-impl" />
<orderEntry type="module" module-name="java-analysis-impl" exported="" />
<orderEntry type="module" module-name="external-system-api" />
<orderEntry type="library" name="asm4" level="project" />
<orderEntry type="library" name="Guava" level="project" />
<orderEntry type="library" name="Xerces" level="project" />
</component>
<component name="copyright">
<Base>

View File

@@ -362,6 +362,7 @@ public class JavaCompletionUtil {
PsiElement ctx = createContextWithXxxVariable(element, composite);
javaReference = (PsiReferenceExpression) JavaPsiFacade.getElementFactory(element.getProject()).createExpressionFromText("xxx.xxx", ctx);
qualifierType = runtimeQualifier;
processor.setQualifierType(qualifierType);
}
javaReference.processVariants(processor);

View File

@@ -0,0 +1,94 @@
package com.intellij.codeInsight.completion.methodChains;
import com.intellij.openapi.util.text.StringUtilRt;
import com.intellij.psi.CommonClassNames;
import com.intellij.psi.PsiPrimitiveType;
import com.intellij.psi.PsiType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashSet;
import java.util.Set;
/**
* @author Dmitry Batkovich
*/
public final class ChainCompletionStringUtil {
private ChainCompletionStringUtil() {}
public static boolean isPrimitiveOrArray(final @Nullable String typeQName) {
return typeQName != null && (typeQName.endsWith("[]") || PRIMITIVES_NAMES.contains(typeQName));
}
/**
* CAUTION: isPrimitiveOrArrayOfPrimitives("java.lang.String") == true,
* isPrimitiveOrArrayOfPrimitives("java.lang.Object") == true
* isPrimitiveOrArrayOfPrimitives("java.lang.Class") == true
*/
public static boolean isPrimitiveOrArrayOfPrimitives(final String typeQName) {
if (typeQName == null) {
return false;
}
return PRIMITIVES_NAMES.contains(deleteArraySigns(typeQName));
}
public static boolean isShortNamePrimitiveOrArrayOfPrimitives(final @Nullable String shortName) {
if (shortName == null) {
return false;
}
return PRIMITIVES_SHORT_NAMES.contains(deleteArraySigns(shortName));
}
private static String deleteArraySigns(final @NotNull String typeName) {
String nameWithoutArraySign = typeName;
while (nameWithoutArraySign.endsWith("[]")) {
nameWithoutArraySign = nameWithoutArraySign.substring(0, nameWithoutArraySign.length() - 2);
}
return nameWithoutArraySign;
}
private static final Set<String> PRIMITIVES_NAMES = new HashSet<String>();
static {
fillPrimitivesNames(PsiType.BOOLEAN);
fillPrimitivesNames(PsiType.INT);
fillPrimitivesNames(PsiType.LONG);
fillPrimitivesNames(PsiType.DOUBLE);
fillPrimitivesNames(PsiType.FLOAT);
fillPrimitivesNames(PsiType.SHORT);
fillPrimitivesNames(PsiType.CHAR);
fillPrimitivesNames(PsiType.BYTE);
fillPrimitivesNames(PsiType.VOID);
PRIMITIVES_NAMES.add(CommonClassNames.JAVA_LANG_STRING);
PRIMITIVES_NAMES.add(CommonClassNames.JAVA_LANG_OBJECT);
PRIMITIVES_NAMES.add(CommonClassNames.JAVA_LANG_CLASS);
}
private static void fillPrimitivesNames(final PsiPrimitiveType type) {
PRIMITIVES_NAMES.add(type.getBoxedTypeName());
PRIMITIVES_NAMES.add(type.getCanonicalText());
}
private static final Set<String> PRIMITIVES_SHORT_NAMES = new HashSet<String>();
static {
fillPrimitivesShortNames(PsiType.BOOLEAN);
fillPrimitivesShortNames(PsiType.INT);
fillPrimitivesShortNames(PsiType.LONG);
fillPrimitivesShortNames(PsiType.DOUBLE);
fillPrimitivesShortNames(PsiType.FLOAT);
fillPrimitivesShortNames(PsiType.SHORT);
fillPrimitivesShortNames(PsiType.CHAR);
fillPrimitivesShortNames(PsiType.BYTE);
fillPrimitivesShortNames(PsiType.VOID);
PRIMITIVES_SHORT_NAMES.add(StringUtilRt.getShortName(CommonClassNames.JAVA_LANG_STRING));
PRIMITIVES_SHORT_NAMES.add(StringUtilRt.getShortName(CommonClassNames.JAVA_LANG_OBJECT));
PRIMITIVES_SHORT_NAMES.add(StringUtilRt.getShortName(CommonClassNames.JAVA_LANG_CLASS));
}
private static void fillPrimitivesShortNames(final PsiPrimitiveType type) {
PRIMITIVES_SHORT_NAMES.add(StringUtilRt.getShortName(type.getBoxedTypeName()));
PRIMITIVES_SHORT_NAMES.add(type.getCanonicalText());
}
}

View File

@@ -0,0 +1,20 @@
package com.intellij.codeInsight.completion.methodChains;
/**
* @author Dmitry Batkovich
*/
public final class Constants {
private Constants() {
}
/**
* magic numbers
*/
public static final int SINGLETON_MAGIC_RATIO = 100;
public static final int SINGLETON_MAGIC_RATIO2 = 5;
public static final int CHAIN_SEARCH_MAGIC_RATIO = 12;
}

View File

@@ -0,0 +1,33 @@
package com.intellij.codeInsight.completion.methodChains.completion;
import com.intellij.codeInsight.completion.CompletionInitializationContext;
import com.intellij.patterns.ElementPattern;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.tree.java.PsiMethodCallExpressionImpl;
import static com.intellij.patterns.PsiJavaPatterns.psiElement;
import static com.intellij.patterns.StandardPatterns.or;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public final class CompletionContributorPatternUtil {
private CompletionContributorPatternUtil() {}
@SuppressWarnings("unchecked")
public static ElementPattern<PsiElement> patternForVariableAssignment() {
final ElementPattern<PsiElement> patternForParent = or(psiElement().withText(CompletionInitializationContext.DUMMY_IDENTIFIER_TRIMMED)
.afterSiblingSkipping(psiElement(PsiWhiteSpace.class),
psiElement(PsiJavaToken.class).withText("=")));
return psiElement().withParent(patternForParent).withSuperParent(2, or(psiElement(PsiAssignmentExpression.class),
psiElement(PsiLocalVariable.class)
.inside(PsiDeclarationStatement.class)))
.inside(PsiMethod.class);
}
public static ElementPattern<PsiElement> patternForMethodParameter() {
return psiElement().withSuperParent(3, PsiMethodCallExpressionImpl.class);
}
}

View File

@@ -0,0 +1,206 @@
package com.intellij.codeInsight.completion.methodChains.completion;
import com.intellij.codeInsight.completion.*;
import com.intellij.codeInsight.completion.methodChains.ChainCompletionStringUtil;
import com.intellij.codeInsight.completion.methodChains.completion.context.ChainCompletionContext;
import com.intellij.codeInsight.completion.methodChains.completion.context.ContextUtil;
import com.intellij.codeInsight.completion.methodChains.search.ChainsSearcher;
import com.intellij.codeInsight.completion.methodChains.search.MethodChainsSearchService;
import com.intellij.codeInsight.completion.methodChains.search.MethodsChain;
import com.intellij.codeInsight.completion.methodChains.search.MethodsChainLookupRangingHelper;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputIndexer;
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.patterns.ElementPattern;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.DirectClassInheritorsSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ProcessingContext;
import com.intellij.util.Processor;
import com.intellij.util.SmartList;
import com.intellij.util.containers.FactoryMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import static com.intellij.patterns.PsiJavaPatterns.or;
/**
* @author Dmitry Batkovich
*/
public class MethodsChainsCompletionContributor extends CompletionContributor {
public static final int INVOCATIONS_THRESHOLD = 3;
private final static int MAX_SEARCH_RESULT_SIZE = 20;
private final static int MAX_CHAIN_SIZE = 4;
private final static int FILTER_RATIO = 10;
@Override
public void fillCompletionVariants(final CompletionParameters parameters, final CompletionResultSet result) {
if (parameters.getInvocationCount() >= INVOCATIONS_THRESHOLD && CompilerOutputIndexer.getInstance(parameters.getPosition().getProject()).isEnabled()) {
super.fillCompletionVariants(parameters, result);
if (ApplicationManager.getApplication().isUnitTestMode()) {
result.stopHere();
}
}
}
@SuppressWarnings("unchecked")
public MethodsChainsCompletionContributor() {
final ElementPattern<PsiElement> pattern =
or(CompletionContributorPatternUtil.patternForMethodParameter(), CompletionContributorPatternUtil.patternForVariableAssignment());
extend(CompletionType.BASIC, pattern, new CompletionProvider<CompletionParameters>() {
@Override
protected void addCompletions(final @NotNull CompletionParameters parameters,
final ProcessingContext context,
final @NotNull CompletionResultSet result) {
final ChainCompletionContext completionContext = extractContext(parameters);
if (completionContext == null) return;
final String targetClassQName = completionContext.getTargetQName();
final Set<String> contextTypesKeysSet = completionContext.getContextTypes();
final Set<String> contextRelevantTypes = new HashSet<String>(contextTypesKeysSet.size() + 1);
for (final String type : contextTypesKeysSet) {
if (!ChainCompletionStringUtil.isPrimitiveOrArrayOfPrimitives(type)) {
contextRelevantTypes.add(type);
}
}
contextRelevantTypes.remove(targetClassQName);
final List<LookupElement> foundedElements = searchForLookups(targetClassQName, contextRelevantTypes, completionContext);
result.addAllElements(foundedElements);
}
});
}
private static List<LookupElement> searchForLookups(final String targetClassQName,
final Set<String> contextRelevantTypes,
final ChainCompletionContext completionContext) {
final MethodChainsSearchService searchService = new MethodChainsSearchService(completionContext.getProject());
final List<MethodsChain> searchResult =
searchChains(targetClassQName, contextRelevantTypes, MAX_SEARCH_RESULT_SIZE, MAX_CHAIN_SIZE, completionContext, searchService);
if (searchResult.size() < MAX_SEARCH_RESULT_SIZE) {
final PsiClass aClass = JavaPsiFacade.getInstance(completionContext.getProject())
.findClass(targetClassQName, GlobalSearchScope.allScope(completionContext.getProject()));
if (aClass != null) {
DirectClassInheritorsSearch.search(aClass).forEach(new Processor<PsiClass>() {
@Override
public boolean process(final PsiClass psiClass) {
final String inheritorQName = psiClass.getQualifiedName();
if (!StringUtil.isEmpty(inheritorQName)) {
final List<MethodsChain> inheritorFilteredSearchResult = new SmartList<MethodsChain>();
//noinspection ConstantConditions
for (final MethodsChain chain : searchChains(inheritorQName, contextRelevantTypes, MAX_SEARCH_RESULT_SIZE, MAX_CHAIN_SIZE,
completionContext, searchService)) {
boolean insert = true;
for (final MethodsChain baseChain : searchResult) {
if (baseChain.weakContains(chain)) {
insert = false;
break;
}
}
if (insert) {
inheritorFilteredSearchResult.add(chain);
}
}
searchResult.addAll(inheritorFilteredSearchResult);
}
return true;
}
});
}
}
return MethodsChainLookupRangingHelper.chainsToWeightableLookupElements(filterTailAndGetSumLastMethodOccurrence(searchResult),
completionContext);
}
@SuppressWarnings("unchecked")
@Nullable
private static ChainCompletionContext extractContext(final CompletionParameters parameters) {
final PsiElement parent = PsiTreeUtil
.getParentOfType(parameters.getPosition(), PsiAssignmentExpression.class, PsiLocalVariable.class, PsiMethodCallExpression.class);
if (parent == null) {
return null;
}
if (parent instanceof PsiAssignmentExpression) {
return tryExtractContextFromAssignment((PsiAssignmentExpression)parent);
}
if (parent instanceof PsiLocalVariable) {
final PsiLocalVariable localVariable = (PsiLocalVariable)parent;
return ContextUtil.createContext(localVariable.getType(), localVariable.getName(),
PsiTreeUtil.getParentOfType(parent, PsiDeclarationStatement.class));
}
final PsiMethod method = ((PsiMethodCallExpression)parent).resolveMethod();
if (method == null) return null;
final PsiExpression expression = PsiTreeUtil.getParentOfType(parameters.getPosition(), PsiExpression.class);
final PsiExpressionList expressionList = PsiTreeUtil.getParentOfType(parameters.getPosition(), PsiExpressionList.class);
if (expressionList == null) return null;
final int exprPosition = Arrays.asList(expressionList.getExpressions()).indexOf(expression);
final PsiParameter[] methodParameters = method.getParameterList().getParameters();
if (exprPosition < methodParameters.length) {
final PsiParameter methodParameter = methodParameters[exprPosition];
return ContextUtil
.createContext(methodParameter.getType(), null, PsiTreeUtil.getParentOfType(expression, PsiDeclarationStatement.class));
}
return null;
}
@Nullable
private static ChainCompletionContext tryExtractContextFromAssignment(final PsiAssignmentExpression assignmentExpression) {
final PsiType type = assignmentExpression.getLExpression().getType();
final PsiIdentifier identifier = PsiTreeUtil.getChildOfType(assignmentExpression.getLExpression(), PsiIdentifier.class);
if (identifier == null) return null;
final String identifierText = identifier.getText();
return ContextUtil.createContext(type, identifierText, assignmentExpression);
}
private static List<MethodsChain> filterTailAndGetSumLastMethodOccurrence(final List<MethodsChain> chains) {
int maxWeight = 0;
for (final MethodsChain chain : chains) {
final int chainWeight = chain.getChainWeight();
if (chainWeight > maxWeight) {
maxWeight = chainWeight;
}
}
final List<MethodsChain> filteredResult = new ArrayList<MethodsChain>();
for (final MethodsChain chain : chains) {
final int chainWeight = chain.getChainWeight();
if (chainWeight * FILTER_RATIO >= maxWeight) {
filteredResult.add(chain);
}
}
return filteredResult;
}
private static List<MethodsChain> searchChains(final String targetQName,
final Set<String> contextVarsQNames,
final int maxResultSize,
final int maxChainSize,
final ChainCompletionContext context,
final MethodChainsSearchService searchService) {
return ChainsSearcher.search(searchService, targetQName, contextVarsQNames, maxResultSize, maxChainSize,
createNotDeprecatedMethodsResolver(JavaPsiFacade.getInstance(context.getProject()),
context.getResolveScope()),
context.getExcludedQNames(), context.getContextMethodName());
}
private static FactoryMap<MethodIncompleteSignature, PsiMethod[]> createNotDeprecatedMethodsResolver(final JavaPsiFacade javaPsiFacade,
final GlobalSearchScope scope) {
return new FactoryMap<MethodIncompleteSignature, PsiMethod[]>() {
@Nullable
@Override
protected PsiMethod[] create(final MethodIncompleteSignature signature) {
return signature.resolveNotDeprecated(javaPsiFacade, scope);
}
};
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed 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.intellij.codeInsight.completion.methodChains.completion;
import com.intellij.codeInsight.completion.CompletionLocation;
import com.intellij.codeInsight.completion.CompletionWeigher;
import com.intellij.codeInsight.completion.methodChains.completion.lookup.WeightableChainLookupElement;
import com.intellij.codeInsight.completion.methodChains.search.ChainRelevance;
import com.intellij.codeInsight.lookup.LookupElement;
import org.jetbrains.annotations.NotNull;
/**
* @author Dmitry Batkovich
*/
public class MethodsChainsWeigher extends CompletionWeigher {
@Override
public Comparable weigh(@NotNull final LookupElement element, @NotNull final CompletionLocation location) {
if (element instanceof WeightableChainLookupElement) {
return ((WeightableChainLookupElement)element).getChainRelevance();
}
return ChainRelevance.LOWEST;
}
}

View File

@@ -0,0 +1,156 @@
package com.intellij.codeInsight.completion.methodChains.completion.context;
import com.intellij.codeInsight.completion.methodChains.search.CachedRelevantStaticMethodSearcher;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.NotNullLazyValue;
import com.intellij.openapi.util.UserDataHolder;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiVariable;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* @author Dmitry Batkovich
*/
public class ChainCompletionContext {
private final NotNullLazyValue<String> myContextMethodName = new NotNullLazyValue<String>() {
@NotNull
@Override
protected String compute() {
return myContextMethod.getName();
}
};
private final PsiMethod myContextMethod;
private final String myTargetQName;
private final Set<String> myContainingClassQNames;
private final MultiMap<String, PsiVariable> myContextVars;
private final MultiMap<String, PsiMethod> myContainingClassGetters;
private final MultiMap<String, ContextRelevantVariableGetter> myContextVarsGetters;
private final Map<String, PsiVariable> myStringVars;
private final CachedRelevantStaticMethodSearcher myStaticMethodSearcher;
private final Set<String> myExcludedQNames;
private final GlobalSearchScope myResolveScope;
private final Project myProject;
private final NotNullLazyValue<Set<String>> contextTypesQNames = new NotNullLazyValue<Set<String>>() {
@SuppressWarnings("unchecked")
@NotNull
@Override
protected Set<String> compute() {
return unionToHashSet(myContainingClassQNames, myContextVars.keySet(), myContainingClassGetters.keySet(),
myContextVarsGetters.keySet());
}
};
public Set<String> getExcludedQNames() {
return myExcludedQNames;
}
ChainCompletionContext(final PsiMethod contextMethod,
final String targetQName,
final Set<String> containingClassQNames,
final MultiMap<String, PsiVariable> contextVars,
final MultiMap<String, PsiMethod> containingClassGetters,
final MultiMap<String, ContextRelevantVariableGetter> contextVarsGetters,
final Map<String, PsiVariable> stringVars,
final Set<String> excludedQNames,
final Project project,
final GlobalSearchScope resolveScope) {
myContextMethod = contextMethod;
myTargetQName = targetQName;
myContainingClassQNames = containingClassQNames;
myContextVars = contextVars;
myContainingClassGetters = containingClassGetters;
myContextVarsGetters = contextVarsGetters;
myStringVars = stringVars;
myExcludedQNames = excludedQNames;
myResolveScope = resolveScope;
myProject = project;
myStaticMethodSearcher = new CachedRelevantStaticMethodSearcher(project, resolveScope);
}
public PsiMethod getContextMethod() {
return myContextMethod;
}
public String getContextMethodName() {
return myContextMethodName.getValue();
}
public String getTargetQName() {
return myTargetQName;
}
@Nullable
public PsiVariable findRelevantStringInContext(@Nullable final String stringParamName) {
if (stringParamName == null) {
return null;
}
for (final Map.Entry<String, PsiVariable> e : myStringVars.entrySet()) {
if (ChainCompletionContextStringUtil.isSimilar(e.getKey(), stringParamName)) {
return e.getValue();
}
}
return null;
}
public Set<String> getContainingClassQNames() {
return myContainingClassQNames;
}
public Collection<PsiVariable> getVariables(final String typeQName) {
return myContextVars.get(typeQName);
}
public Collection<PsiMethod> getContainingClassMethods(final String typeQName) {
return myContainingClassGetters.get(typeQName);
}
public Collection<ContextRelevantStaticMethod> getRelevantStaticMethods(final String typeQName, final int weight) {
return myStaticMethodSearcher.getRelevantStaticMethods(typeQName, weight, this);
}
public Collection<ContextRelevantVariableGetter> getRelevantVariablesGetters(final String typeQName) {
return myContextVarsGetters.get(typeQName);
}
public Collection<?> getContextRefElements(final String typeQName) {
final Collection<PsiVariable> variables = getVariables(typeQName);
final Collection<PsiMethod> containingClassMethods = getContainingClassMethods(typeQName);
final Collection<UserDataHolder> refElements = new ArrayList<UserDataHolder>(variables.size() + containingClassMethods.size());
refElements.addAll(variables);
refElements.addAll(containingClassMethods);
for (final ContextRelevantVariableGetter contextRelevantVariableGetter : getRelevantVariablesGetters(typeQName)) {
refElements.add(contextRelevantVariableGetter.createLookupElement());
}
return refElements;
}
public boolean contains(@Nullable final String typeQualifierName) {
return typeQualifierName != null && contextTypesQNames.getValue().contains(typeQualifierName);
}
public Set<String> getContextTypes() {
return contextTypesQNames.getValue();
}
public GlobalSearchScope getResolveScope() {
return myResolveScope;
}
public Project getProject() {
return myProject;
}
private static <T> HashSet<T> unionToHashSet(final Collection<T>... collections) {
final HashSet<T> res = new HashSet<T>();
for (final Collection<T> set : collections) {
res.addAll(set);
}
return res;
}
}

View File

@@ -0,0 +1,36 @@
package com.intellij.codeInsight.completion.methodChains.completion.context;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.NotNull;
/**
* @author Dmitry Batkovich
*/
class ChainCompletionContextStringUtil {
private ChainCompletionContextStringUtil(){}
private final static int COMMON_PART_MIN_LENGTH = 3;
public static boolean isSimilar(@NotNull final String varName,
@NotNull final String parameterName) {
final String sanitizedParamName = sanitizedToLowerCase(parameterName);
if (StringUtil.commonPrefix(varName, sanitizedParamName).length() >= COMMON_PART_MIN_LENGTH) {
return true;
}
final String suffix = StringUtil.commonSuffix(varName, sanitizedParamName);
return suffix.length() >= COMMON_PART_MIN_LENGTH;
}
@NotNull
public static String sanitizedToLowerCase(@NotNull final String name) {
final StringBuilder result = new StringBuilder();
for (int i = 0; i < name.length(); i++) {
final char ch = name.charAt(i);
if (Character.isLetter(ch)) {
result.append(Character.toLowerCase(ch));
}
}
return result.toString();
}
}

View File

@@ -0,0 +1,45 @@
package com.intellij.codeInsight.completion.methodChains.completion.context;
import com.intellij.codeInsight.completion.methodChains.completion.lookup.sub.StaticMethodSubLookupElement;
import com.intellij.codeInsight.completion.methodChains.completion.lookup.sub.SubLookupElement;
import com.intellij.codeInsight.completion.methodChains.completion.lookup.sub.VariableSubLookupElement;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiVariable;
import gnu.trove.TIntObjectHashMap;
import gnu.trove.TIntObjectProcedure;
import org.jetbrains.annotations.Nullable;
/**
* @author Dmitry Batkovich
*/
public class ContextRelevantStaticMethod {
private final PsiMethod psiMethod;
@Nullable
private final TIntObjectHashMap<SubLookupElement> parameters;
public ContextRelevantStaticMethod(final PsiMethod psiMethod, @Nullable final TIntObjectHashMap<PsiVariable> parameters) {
this.psiMethod = psiMethod;
if (parameters == null) {
this.parameters = null;
} else {
this.parameters = new TIntObjectHashMap<SubLookupElement>(parameters.size());
parameters.forEachEntry(new TIntObjectProcedure<PsiVariable>() {
@SuppressWarnings("ConstantConditions")
@Override
public boolean execute(final int pos, final PsiVariable var) {
ContextRelevantStaticMethod.this.parameters.put(pos, new VariableSubLookupElement(var));
return false;
}
});
}
}
private SubLookupElement cachedLookupElement;
public SubLookupElement createLookupElement() {
if (cachedLookupElement == null) {
cachedLookupElement = new StaticMethodSubLookupElement(psiMethod, parameters);
}
return cachedLookupElement;
}
}

View File

@@ -0,0 +1,31 @@
package com.intellij.codeInsight.completion.methodChains.completion.context;
import com.intellij.codeInsight.completion.JavaChainLookupElement;
import com.intellij.codeInsight.completion.JavaMethodCallElement;
import com.intellij.codeInsight.completion.methodChains.completion.lookup.sub.GetterLookupSubLookupElement;
import com.intellij.codeInsight.completion.methodChains.completion.lookup.sub.SubLookupElement;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.VariableLookupItem;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiVariable;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public class ContextRelevantVariableGetter {
private final PsiVariable myVariable;
private final PsiMethod myMethod;
public ContextRelevantVariableGetter(final PsiVariable variable, final PsiMethod method) {
myVariable = variable;
myMethod = method;
}
public SubLookupElement createSubLookupElement() {
return new GetterLookupSubLookupElement(myVariable.getName(), myMethod.getName());
}
public LookupElement createLookupElement() {
return new JavaChainLookupElement(new VariableLookupItem(myVariable), new JavaMethodCallElement(myMethod));
}
}

View File

@@ -0,0 +1,245 @@
package com.intellij.codeInsight.completion.methodChains.completion.context;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.SmartList;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* @author Dmitry Batkovich
*/
public class ContextUtil {
@Nullable
public static ChainCompletionContext createContext(final @Nullable PsiType variableType,
final @Nullable String variableName,
final @Nullable PsiElement containingElement) {
if (variableType == null || containingElement == null) {
return null;
}
if (variableType instanceof PsiClassType) {
final PsiClass aClass = ((PsiClassType)variableType).resolve();
if (aClass != null) {
if (aClass.hasTypeParameters()) {
return null;
}
}
else {
return null;
}
}
final String targetQName = variableType.getCanonicalText();
if (targetQName == null || targetQName.endsWith("[]")) {
return null;
}
final PsiMethod method = PsiTreeUtil.getParentOfType(containingElement, PsiMethod.class);
if (method == null) {
return null;
}
final PsiClass aClass = method.getContainingClass();
if (aClass == null) {
return null;
}
final Set<String> containingClassQNames = resolveSupersNamesRecursively(aClass);
final List<PsiVariable> contextVars = new SmartList<PsiVariable>();
for (final PsiField field : aClass.getFields()) {
final PsiClass containingClass = field.getContainingClass();
if (containingClass != null) {
if ((field.hasModifierProperty(PsiModifier.PUBLIC) ||
field.hasModifierProperty(PsiModifier.PROTECTED) ||
((field.hasModifierProperty(PsiModifier.PRIVATE) || field.hasModifierProperty(PsiModifier.PACKAGE_LOCAL)) &&
aClass.isEquivalentTo(containingClass))) && !field.getName().equals(variableName)) {
contextVars.add(field);
}
}
}
Collections.addAll(contextVars, method.getParameterList().getParameters());
final PsiCodeBlock methodBody = method.getBody();
assert methodBody != null;
boolean processMethodTail = false;
final List<PsiElement> afterElements = new ArrayList<PsiElement>();
for (final PsiElement element : methodBody.getChildren()) {
if (element.isEquivalentTo(containingElement)) {
if (variableType instanceof PsiClassType) {
processMethodTail = true;
continue;
}
else {
break;
}
}
if (element instanceof PsiDeclarationStatement) {
if (processMethodTail) {
afterElements.add(element);
}
else {
for (final PsiElement declaredElement : ((PsiDeclarationStatement)element).getDeclaredElements()) {
if (declaredElement instanceof PsiLocalVariable &&
(variableName == null || !variableName.equals(((PsiLocalVariable)declaredElement).getName()))) {
contextVars.add((PsiVariable)declaredElement);
}
}
}
}
}
final Set<String> excludedQNames = processMethodTail
? generateExcludedQNames(afterElements, ((PsiClassType)variableType).resolve(), variableName,
contextVars)
: Collections.<String>emptySet();
final List<PsiMethod> contextMethods = new ArrayList<PsiMethod>();
for (final PsiMethod psiMethod : aClass.getMethods()) {
if ((psiMethod.hasModifierProperty(PsiModifier.PROTECTED) || psiMethod.hasModifierProperty(PsiModifier.PRIVATE)) &&
psiMethod.getParameterList().getParametersCount() == 0) {
contextMethods.add(psiMethod);
}
}
return create(method, targetQName, contextVars, contextMethods, containingClassQNames, containingElement.getProject(),
containingElement.getResolveScope(), excludedQNames);
}
private static Set<String> generateExcludedQNames(final List<PsiElement> tailElements,
final @Nullable PsiClass psiClass,
final @Nullable String varName,
final List<PsiVariable> contextVars) {
if (psiClass == null) {
return Collections.emptySet();
}
final String classQName = psiClass.getQualifiedName();
if (classQName == null) {
return Collections.emptySet();
}
final Set<String> excludedQNames = new HashSet<String>();
if (!tailElements.isEmpty()) {
final Set<String> contextVarTypes = new HashSet<String>();
final Map<String, PsiVariable> contextVarNamesToVar = new HashMap<String, PsiVariable>();
for (final PsiVariable var : contextVars) {
contextVarTypes.add(var.getType().getCanonicalText());
contextVarNamesToVar.put(var.getName(), var);
}
for (final PsiElement element : tailElements) {
final Collection<PsiMethodCallExpression> methodCallExpressions =
PsiTreeUtil.findChildrenOfType(element, PsiMethodCallExpression.class);
for (final PsiMethodCallExpression methodCallExpression : methodCallExpressions) {
final PsiExpressionList args = methodCallExpression.getArgumentList();
final PsiMethod resolvedMethod = methodCallExpression.resolveMethod();
if (resolvedMethod != null) {
final PsiType returnType = resolvedMethod.getReturnType();
if (returnType != null) {
final String returnTypeAsString = returnType.getCanonicalText();
for (final PsiExpression expression : args.getExpressions()) {
final String qVarName = expression.getText();
if (qVarName != null) {
if (contextVarNamesToVar.containsKey(qVarName) || qVarName.equals(varName)) {
excludedQNames.add(returnTypeAsString);
}
}
}
if (!contextVarTypes.contains(returnTypeAsString)) {
excludedQNames.add(returnTypeAsString);
}
}
}
}
}
}
return excludedQNames;
}
@Nullable
private static ChainCompletionContext create(final PsiMethod contextMethod,
final String targetQName,
final List<PsiVariable> contextVars,
final List<PsiMethod> contextMethods,
final Set<String> containingClassQNames,
final Project project,
final GlobalSearchScope resolveScope,
final Set<String> excludedQNames) {
final MultiMap<String, PsiVariable> classQNameToVariable = new MultiMap<String, PsiVariable>();
final MultiMap<String, PsiMethod> containingClassGetters = new MultiMap<String, PsiMethod>();
final MultiMap<String, ContextRelevantVariableGetter> contextVarsGetters = new MultiMap<String, ContextRelevantVariableGetter>();
final Map<String, PsiVariable> stringVars = new HashMap<String, PsiVariable>();
for (final PsiMethod method : contextMethods) {
final String returnTypeQName = method.getReturnType().getCanonicalText();
if (returnTypeQName != null) {
containingClassGetters.putValue(returnTypeQName, method);
}
}
for (final PsiVariable var : contextVars) {
final PsiType type = var.getType();
final Set<String> classQNames = new HashSet<String>();
if (type instanceof PsiClassType) {
if (JAVA_LANG_STRING_SHORT_NAME.equals(((PsiClassType)type).getClassName())) {
final String varName = var.getName();
if (varName != null) {
stringVars.put(ChainCompletionContextStringUtil.sanitizedToLowerCase(varName), var);
continue;
}
}
final PsiClass aClass = ((PsiClassType)type).resolve();
if (aClass != null) {
final String classQName = type.getCanonicalText();
if (!targetQName.equals(classQName)) {
classQNames.add(classQName);
classQNames.addAll(resolveSupersNamesRecursively(aClass));
for (final PsiMethod method : aClass.getAllMethods()) {
if (method.getParameterList().getParametersCount() == 0 && method.getName().startsWith("get")) {
final String getterReturnTypeQName = method.getReturnType().getCanonicalText();
if (getterReturnTypeQName != null) {
contextVarsGetters.putValue(getterReturnTypeQName, new ContextRelevantVariableGetter(var, method));
}
}
}
}
}
}
else {
final String classQName = type.getCanonicalText();
if (classQName != null) {
classQNames.add(classQName);
}
}
for (final String qName : classQNames) {
classQNameToVariable.putValue(qName, var);
}
}
return new ChainCompletionContext(contextMethod, targetQName, containingClassQNames, classQNameToVariable, containingClassGetters,
contextVarsGetters, stringVars, excludedQNames, project, resolveScope);
}
@NotNull
private static Set<String> resolveSupersNamesRecursively(@Nullable final PsiClass psiClass) {
final Set<String> result = new HashSet<String>();
if (psiClass != null) {
for (final PsiClass superClass : psiClass.getSupers()) {
final String qualifiedName = superClass.getQualifiedName();
if (!CommonClassNames.JAVA_LANG_OBJECT.equals(qualifiedName)) {
if (qualifiedName != null) {
result.add(qualifiedName);
}
result.addAll(resolveSupersNamesRecursively(superClass));
}
}
}
return result;
}
private final static String JAVA_LANG_STRING_SHORT_NAME = StringUtil.getShortName(CommonClassNames.JAVA_LANG_STRING);
}

View File

@@ -0,0 +1,53 @@
package com.intellij.codeInsight.completion.methodChains.completion.lookup;
import com.intellij.codeInsight.completion.methodChains.completion.lookup.sub.SubLookupElement;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.psi.PsiKeyword;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiModifier;
import com.intellij.psi.PsiParameter;
import gnu.trove.TIntObjectHashMap;
import org.jetbrains.annotations.Nullable;
/**
* @author Dmitry Batkovich
*/
public final class ChainCompletionLookupElementUtil {
private ChainCompletionLookupElementUtil() {
}
public static LookupElement createLookupElement(final PsiMethod method,
final @Nullable TIntObjectHashMap<SubLookupElement> replaceElements) {
if (method.isConstructor()) {
//noinspection ConstantConditions
return LookupElementBuilder.create(String.format("%s %s", PsiKeyword.NEW, method.getContainingClass().getName()));
} else if (method.hasModifierProperty(PsiModifier.STATIC)) {
return new ChainCompletionMethodCallLookupElement(method, replaceElements, false, true);
} else {
return new ChainCompletionMethodCallLookupElement(method, replaceElements);
}
}
public static String fillMethodParameters(final PsiMethod method, @Nullable final TIntObjectHashMap<SubLookupElement> replaceElements) {
final TIntObjectHashMap<SubLookupElement> notNullReplaceElements = replaceElements == null ?
new TIntObjectHashMap<SubLookupElement>(0) :
replaceElements;
final PsiParameter[] parameters = method.getParameterList().getParameters();
final StringBuilder sb = new StringBuilder();
for (int i = 0; i < parameters.length; i++) {
if (i != 0) {
sb.append(", ");
}
final PsiParameter parameter = parameters[i];
final SubLookupElement replaceElement = notNullReplaceElements.get(i);
if (replaceElement != null) {
sb.append(replaceElement.getInsertString());
} else {
sb.append(parameter.getName());
}
}
return sb.toString();
}
}

View File

@@ -0,0 +1,88 @@
package com.intellij.codeInsight.completion.methodChains.completion.lookup;
import com.intellij.codeInsight.completion.InsertionContext;
import com.intellij.codeInsight.completion.JavaMethodCallElement;
import com.intellij.codeInsight.completion.StaticallyImportable;
import com.intellij.codeInsight.completion.methodChains.completion.lookup.sub.SubLookupElement;
import com.intellij.codeInsight.lookup.AutoCompletionPolicy;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiJavaFile;
import com.intellij.psi.PsiMethod;
import gnu.trove.TIntObjectHashMap;
import gnu.trove.TObjectProcedure;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Dmitry Batkovich
*/
public class ChainCompletionMethodCallLookupElement extends JavaMethodCallElement implements StaticallyImportable {
public static final String PROP_METHODS_CHAIN_COMPLETION_AUTO_COMPLETION = "methods.chain.completion.autoCompletion";
private final PsiMethod myMethod;
@Nullable
private final TIntObjectHashMap<SubLookupElement> myReplaceElements;
private final boolean myMergedOverloads;
public ChainCompletionMethodCallLookupElement(final PsiMethod method,
final @Nullable TIntObjectHashMap<SubLookupElement> replaceElements,
final boolean shouldImportStatic,
final boolean mergedOverloads) {
super(method, shouldImportStatic, mergedOverloads);
myMethod = method;
myReplaceElements = replaceElements;
myMergedOverloads = mergedOverloads;
configureAutoCompletionPolicy();
}
public ChainCompletionMethodCallLookupElement(final PsiMethod method,
final @Nullable TIntObjectHashMap<SubLookupElement> replaceElements) {
super(method);
myMethod = method;
myReplaceElements = replaceElements;
myMergedOverloads = true;
configureAutoCompletionPolicy();
}
private void configureAutoCompletionPolicy() {
if (ApplicationManager.getApplication().isUnitTestMode()) {
if (PropertiesComponent.getInstance(myMethod.getProject()).getBoolean(PROP_METHODS_CHAIN_COMPLETION_AUTO_COMPLETION, false)) {
setAutoCompletionPolicy(AutoCompletionPolicy.GIVE_CHANCE_TO_OVERWRITE);
}
}
}
@Override
public void handleInsert(final InsertionContext context) {
super.handleInsert(context);
if (!myMergedOverloads || isUniqueMethod(myMethod)) {
context.commitDocument();
context.getDocument()
.insertString(context.getTailOffset() - 1, ChainCompletionLookupElementUtil.fillMethodParameters(myMethod, myReplaceElements));
final PsiFile file = context.getFile();
assert file instanceof PsiJavaFile;
final PsiJavaFile javaFile = (PsiJavaFile)file;
if (myReplaceElements != null) {
myReplaceElements.forEachValue(new TObjectProcedure<SubLookupElement>() {
@Override
public boolean execute(final SubLookupElement subLookupElement) {
subLookupElement.doImport(javaFile);
return true;
}
});
}
context.commitDocument();
context.getEditor().getCaretModel().moveToOffset(context.getTailOffset());
context.commitDocument();
}
}
private static boolean isUniqueMethod(@NotNull final PsiMethod method) {
final PsiClass containingClass = method.getContainingClass();
return containingClass == null || containingClass.findMethodsByName(method.getName(), true).length == 1;
}
}

View File

@@ -0,0 +1,79 @@
package com.intellij.codeInsight.completion.methodChains.completion.lookup;
import com.intellij.codeInsight.completion.InsertionContext;
import com.intellij.codeInsight.lookup.LookupElementPresentation;
import com.intellij.codeInsight.lookup.LookupItem;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.SuggestedNameInfo;
import com.intellij.psi.codeStyle.VariableKind;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public class ChainCompletionNewVariableLookupElement extends LookupItem<PsiClass> {
private final PsiClass psiClass;
private final String newVarName;
public ChainCompletionNewVariableLookupElement(final PsiClass psiClass, final String newVarName) {
super(psiClass, newVarName);
this.newVarName = newVarName;
this.psiClass = psiClass;
}
public static ChainCompletionNewVariableLookupElement create(final PsiClass psiClass) {
final Project project = psiClass.getProject();
final SuggestedNameInfo suggestedNameInfo = JavaCodeStyleManager.getInstance(project).suggestVariableName(VariableKind.LOCAL_VARIABLE, null, null, JavaPsiFacade .getElementFactory( project).createType(psiClass));
return new ChainCompletionNewVariableLookupElement(psiClass, chooseLongest(suggestedNameInfo.names));
}
@Override
public void handleInsert(final InsertionContext context) {
final PsiFile file = context.getFile();
((PsiJavaFile) file).importClass(psiClass);
final PsiStatement statement = PsiTreeUtil.getParentOfType(file.findElementAt(context.getEditor().getCaretModel().getOffset()), PsiStatement.class);
final PsiCodeBlock codeBlock = PsiTreeUtil.getParentOfType(statement, PsiCodeBlock.class);
assert codeBlock != null;
final Project project = context.getProject();
new WriteCommandAction.Simple(project, file) {
@Override
protected void run() throws Throwable {
codeBlock.addBefore(
JavaPsiFacade.getElementFactory(
project).
createStatementFromText(String.format("%s %s = null;", psiClass.getName(), newVarName), null), statement);
}
}.execute();
PsiDocumentManager.getInstance(context.getProject()).doPostponedOperationsAndUnblockDocument(context.getDocument());
}
@NotNull
@Override
public String getLookupString() {
return newVarName;
}
@Override
public void renderElement(final LookupElementPresentation presentation) {
super.renderElement(presentation);
presentation.setItemText(newVarName);
}
private static String chooseLongest(final String[] names) {
String longestWord = names[0];
int maxLength = longestWord.length();
for (int i = 1; i < names.length; i++) {
final int length = names[i].length();
if (length > maxLength) {
maxLength = length;
longestWord = names[i];
}
}
return longestWord;
}
}

View File

@@ -0,0 +1,22 @@
package com.intellij.codeInsight.completion.methodChains.completion.lookup;
import com.intellij.codeInsight.completion.methodChains.search.ChainRelevance;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementDecorator;
import org.jetbrains.annotations.NotNull;
/**
* @author Dmitry Batkovich
*/
public final class WeightableChainLookupElement extends LookupElementDecorator<LookupElement> {
private final ChainRelevance myChainRelevance;
public WeightableChainLookupElement(final @NotNull LookupElement delegate, final ChainRelevance relevance) {
super(delegate);
myChainRelevance = relevance;
}
public ChainRelevance getChainRelevance() {
return myChainRelevance;
}
}

View File

@@ -0,0 +1,35 @@
package com.intellij.codeInsight.completion.methodChains.completion.lookup.sub;
import com.intellij.psi.PsiJavaFile;
import org.jetbrains.annotations.Nullable;
/**
* @author Dmitry Batkovich
*/
public class GetterLookupSubLookupElement implements SubLookupElement {
private final String myVariableName;
private final String myMethodName;
public GetterLookupSubLookupElement(final String methodName) {
this(null, methodName);
}
public GetterLookupSubLookupElement(@Nullable final String variableName, final String methodName) {
myVariableName = variableName;
myMethodName = methodName;
}
@Override
public void doImport(final PsiJavaFile javaFile) {
}
@Override
public String getInsertString() {
final StringBuilder sb = new StringBuilder();
if (myVariableName != null) {
sb.append(myVariableName).append(".");
}
sb.append(myMethodName).append("()");
return sb.toString();
}
}

View File

@@ -0,0 +1,51 @@
package com.intellij.codeInsight.completion.methodChains.completion.lookup.sub;
import com.intellij.codeInsight.completion.methodChains.completion.lookup.ChainCompletionLookupElementUtil;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiJavaFile;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiModifier;
import gnu.trove.TIntObjectHashMap;
import gnu.trove.TObjectProcedure;
import org.jetbrains.annotations.Nullable;
/**
* @author Dmitry Batkovich
*/
public class StaticMethodSubLookupElement implements SubLookupElement {
private final PsiMethod myMethod;
private final TIntObjectHashMap<SubLookupElement> myReplaceElements;
public StaticMethodSubLookupElement(final PsiMethod method, @Nullable final TIntObjectHashMap<SubLookupElement> replaceElements) {
assert method.hasModifierProperty(PsiModifier.STATIC);
myReplaceElements = replaceElements;
myMethod = method;
}
@Override
public void doImport(final PsiJavaFile javaFile) {
final PsiClass containingClass = myMethod.getContainingClass();
if (containingClass != null) {
if (javaFile.findImportReferenceTo(containingClass) == null) {
javaFile.importClass(containingClass);
}
}
if (myReplaceElements != null) {
myReplaceElements.forEachValue(new TObjectProcedure<SubLookupElement>() {
@Override
public boolean execute(final SubLookupElement subLookupElement) {
subLookupElement.doImport(javaFile);
return false;
}
});
}
}
@Override
public String getInsertString() {
//noinspection ConstantConditions
return String.format("%s.%s(%s)", myMethod.getContainingClass().getName(), myMethod.getName(),
ChainCompletionLookupElementUtil.fillMethodParameters(myMethod, myReplaceElements));
}
}

View File

@@ -0,0 +1,13 @@
package com.intellij.codeInsight.completion.methodChains.completion.lookup.sub;
import com.intellij.psi.PsiJavaFile;
/**
* @author Dmitry Batkovich
*/
public interface SubLookupElement {
void doImport(final PsiJavaFile javaFile);
String getInsertString();
}

View File

@@ -0,0 +1,25 @@
package com.intellij.codeInsight.completion.methodChains.completion.lookup.sub;
import com.intellij.psi.PsiJavaFile;
import com.intellij.psi.PsiVariable;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public class VariableSubLookupElement implements SubLookupElement {
private final String myVarName;
public VariableSubLookupElement(final PsiVariable variable) {
myVarName = variable.getName();
}
@Override
public void doImport(final PsiJavaFile javaFile) {
}
@Override
public String getInsertString() {
return myVarName;
}
}

View File

@@ -0,0 +1,36 @@
package com.intellij.codeInsight.completion.methodChains.search;
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature;
import com.intellij.openapi.project.Project;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.search.GlobalSearchScope;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public class CachedNotDeprecatedMethodsResolver {
private final Map<MethodIncompleteSignature, PsiMethod[]> myResolveLocalCache = new HashMap<MethodIncompleteSignature, PsiMethod[]>();
private final JavaPsiFacade myJavaPsiFacade;
private final GlobalSearchScope myScope;
public CachedNotDeprecatedMethodsResolver(final Project project, final GlobalSearchScope scope) {
myScope = scope;
myJavaPsiFacade = JavaPsiFacade.getInstance(project);
}
@NotNull
public PsiMethod[] resolveNotDeprecated(@NotNull final MethodIncompleteSignature methodInvocation) {
final PsiMethod[] cached = myResolveLocalCache.get(methodInvocation);
if (cached != null) {
return cached;
}
final PsiMethod[] methods = methodInvocation.resolveNotDeprecated(myJavaPsiFacade, myScope);
myResolveLocalCache.put(methodInvocation, methods);
return methods;
}
}

View File

@@ -0,0 +1,122 @@
package com.intellij.codeInsight.completion.methodChains.search;
import com.intellij.codeInsight.completion.methodChains.ChainCompletionStringUtil;
import com.intellij.codeInsight.completion.methodChains.completion.context.ChainCompletionContext;
import com.intellij.codeInsight.completion.methodChains.completion.context.ContextRelevantStaticMethod;
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature;
import com.intellij.compilerOutputIndex.impl.MethodsUsageIndex;
import com.intellij.compilerOutputIndex.impl.UsageIndexValue;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* @author Dmitry Batkovich
*/
public class CachedRelevantStaticMethodSearcher {
private final HashMap<MethodIncompleteSignature, PsiMethod> myCachedResolveResults = new HashMap<MethodIncompleteSignature, PsiMethod>();
private final MethodsUsageIndex myIndex;
private final JavaPsiFacade myJavaPsiFacade;
private final GlobalSearchScope myAllScope;
private final GlobalSearchScope myResolveScope;
public CachedRelevantStaticMethodSearcher(final Project project, final GlobalSearchScope resolveScope) {
myIndex = MethodsUsageIndex.getInstance(project);
myJavaPsiFacade = JavaPsiFacade.getInstance(project);
myAllScope = GlobalSearchScope.allScope(project);
myResolveScope = resolveScope;
}
@NotNull
public List<ContextRelevantStaticMethod> getRelevantStaticMethods(final String resultQualifiedClassName,
final int minOccurrence,
final ChainCompletionContext completionContext) {
if (resultQualifiedClassName == null ||
ChainCompletionStringUtil.isPrimitiveOrArrayOfPrimitives(resultQualifiedClassName) ||
completionContext.getTargetQName().equals(resultQualifiedClassName)) {
return Collections.emptyList();
}
final TreeSet<UsageIndexValue> indexValues = myIndex.getValues(resultQualifiedClassName);
if (indexValues != null) {
int occurrences = 0;
final List<ContextRelevantStaticMethod> relevantMethods = new ArrayList<ContextRelevantStaticMethod>();
for (final UsageIndexValue indexValue : extractStaticMethods(indexValues)) {
final MethodIncompleteSignature methodInvocation = indexValue.getMethodIncompleteSignature();
final PsiMethod method;
if (myCachedResolveResults.containsKey(methodInvocation)) {
method = myCachedResolveResults.get(methodInvocation);
}
else {
final PsiMethod[] methods = methodInvocation.resolveNotDeprecated(myJavaPsiFacade, myAllScope);
method = MethodChainsSearchUtil
.getMethodWithMinNotPrimitiveParameters(methods, Collections.singleton(completionContext.getTargetQName()));
myCachedResolveResults.put(methodInvocation, method);
if (method == null) {
return Collections.emptyList();
}
}
if (method == null) {
return Collections.emptyList();
}
if (method.hasModifierProperty(PsiModifier.PUBLIC)) {
if (isMethodValid(method, completionContext, resultQualifiedClassName)) {
occurrences += indexValue.getOccurrences();
if (myResolveScope.contains(method.getContainingFile().getVirtualFile())) {
relevantMethods.add(new ContextRelevantStaticMethod(method, null));
}
if (occurrences >= minOccurrence) {
return relevantMethods;
}
}
}
}
}
return Collections.emptyList();
}
private static List<UsageIndexValue> extractStaticMethods(final TreeSet<UsageIndexValue> indexValues) {
final List<UsageIndexValue> relevantStaticMethods = new SmartList<UsageIndexValue>();
for (final UsageIndexValue indexValue : indexValues) {
if (indexValue.getMethodIncompleteSignature().isStatic()) {
relevantStaticMethods.add(indexValue);
}
}
return relevantStaticMethods;
}
private static boolean isMethodValid(final @Nullable PsiMethod method,
final ChainCompletionContext completionContext,
final String targetTypeShortName) {
if (method == null) return false;
for (final PsiParameter parameter : method.getParameterList().getParameters()) {
final PsiType type = parameter.getType();
final String shortClassName = typeAsString(type);
if (targetTypeShortName.equals(shortClassName)) return false;
if (!ChainCompletionStringUtil.isShortNamePrimitiveOrArrayOfPrimitives(shortClassName) &&
!completionContext.contains(type.getCanonicalText())) {
return false;
}
}
return true;
}
@Nullable
public static String typeAsString(final PsiType type) {
if (type instanceof PsiClassType)
return ((PsiClassType) type).getClassName();
else if (type instanceof PsiPrimitiveType)
return type.getCanonicalText();
else if (type instanceof PsiArrayType) {
final String componentTypeAsString = typeAsString(((PsiArrayType) type).getComponentType());
if (componentTypeAsString == null) return null;
return String.format("%s[]", componentTypeAsString);
}
return null;
}
}

View File

@@ -0,0 +1,83 @@
package com.intellij.codeInsight.completion.methodChains.search;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.TestOnly;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public class ChainRelevance implements Comparable<ChainRelevance> {
public static final ChainRelevance LOWEST = new ChainRelevance(Integer.MAX_VALUE, 0, Integer.MAX_VALUE, Integer.MAX_VALUE, false, false);
private final int myChainSize;
private final int myLastMethodOccurrences;
private final int myUnreachableParametersCount;
private final int myNotMatchedStringVars;
private final boolean myHasCallingVariableInContext;
private final boolean myFirstMethodStatic;
public ChainRelevance(final int chainSize,
final int lastMethodOccurrences,
final int unreachableParametersCount,
final int notMatchedStringVars,
final boolean hasCallingVariableInContext,
final boolean firstMethodStatic) {
myChainSize = chainSize;
myLastMethodOccurrences = lastMethodOccurrences;
myUnreachableParametersCount = unreachableParametersCount;
myNotMatchedStringVars = notMatchedStringVars;
myHasCallingVariableInContext = hasCallingVariableInContext;
myFirstMethodStatic = firstMethodStatic;
}
@TestOnly
public boolean hasCallingVariableInContext() {
return myHasCallingVariableInContext;
}
@TestOnly
public int getChainSize() {
return myChainSize;
}
@TestOnly
public int getLastMethodOccurrences() {
return myLastMethodOccurrences;
}
@TestOnly
public int getUnreachableParametersCount() {
return myUnreachableParametersCount;
}
@TestOnly
public int getNotMatchedStringVars() {
return myNotMatchedStringVars;
}
@TestOnly
public boolean isFirstMethodStatic() {
return myFirstMethodStatic;
}
@Override
public int compareTo(@NotNull final ChainRelevance that) {
if (myFirstMethodStatic && !that.myFirstMethodStatic) {
return -1;
}
if (that.myFirstMethodStatic && !myFirstMethodStatic) {
return 1;
}
if (myHasCallingVariableInContext && !that.myHasCallingVariableInContext) {
return 1;
}
if (that.myHasCallingVariableInContext && !myHasCallingVariableInContext) {
return -1;
}
int sub = myLastMethodOccurrences - that.myLastMethodOccurrences;
if (sub != 0) return sub;
sub = myUnreachableParametersCount - that.myUnreachableParametersCount;
if (sub != 0) return -sub;
return 0;
}
}

View File

@@ -0,0 +1,206 @@
package com.intellij.codeInsight.completion.methodChains.search;
import com.intellij.codeInsight.completion.methodChains.Constants;
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature;
import com.intellij.compilerOutputIndex.impl.UsageIndexValue;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.psi.PsiMethod;
import com.intellij.util.containers.FactoryMap;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* @author Dmitry Batkovich
*/
public class ChainsSearcher {
public static List<MethodsChain> search(final MethodChainsSearchService searchService,
final String targetQName,
final Set<String> contextQNames,
final int maxResultSize,
final int pathMaximalLength,
final FactoryMap<MethodIncompleteSignature, PsiMethod[]> resolver,
final String contextMethodName) {
return search(searchService, targetQName, contextQNames, maxResultSize, pathMaximalLength, resolver,
Collections.<String>singleton(targetQName), contextMethodName);
}
public static List<MethodsChain> search(final MethodChainsSearchService searchService,
final String targetQName,
final Set<String> contextQNames,
final int maxResultSize,
final int pathMaximalLength,
final FactoryMap<MethodIncompleteSignature, PsiMethod[]> resolver,
final Set<String> excludedParamsTypesQNames,
final String contextMethodName) {
final SearchInitializer initializer = createInitializer(targetQName, resolver, searchService, excludedParamsTypesQNames);
final ArrayList<MethodsChain> methodsChains = new ArrayList<MethodsChain>(maxResultSize);
final MethodsChain firstBestMethodsChain =
search(searchService, initializer, contextQNames, Collections.<String>emptySet(), pathMaximalLength, resolver, targetQName,
excludedParamsTypesQNames, contextMethodName);
if (firstBestMethodsChain != null) {
methodsChains.add(firstBestMethodsChain);
Set<Set<String>> excludedCombinations = MethodsChain.edgeCombinations(Collections.<Set<String>>emptySet(), firstBestMethodsChain);
while (methodsChains.size() <= maxResultSize) {
final Set<Set<String>> localExcludedCombinations = excludedCombinations;
boolean allLocalsIsNull = true;
final int beforeStepChainsCount = methodsChains.size();
for (final Set<String> excludedEdges : localExcludedCombinations) {
final MethodsChain local =
search(searchService, initializer, contextQNames, excludedEdges, pathMaximalLength, resolver, targetQName,
excludedParamsTypesQNames, contextMethodName);
if (local != null) {
allLocalsIsNull = false;
}
else {
continue;
}
boolean add = true;
for (int i = 0; i < methodsChains.size(); i++) {
final MethodsChain chain = methodsChains.get(i);
final MethodsChain.CompareResult compareResult = MethodsChain.compare(local, chain);
if (compareResult == MethodsChain.CompareResult.EQUAL || compareResult == MethodsChain.CompareResult.RIGHT_CONTAINS_LEFT) {
add = false;
break;
}
else if (compareResult == MethodsChain.CompareResult.LEFT_CONTAINS_RIGHT) {
methodsChains.set(i, local);
add = false;
break;
}
}
if (add) {
methodsChains.add(local);
if (methodsChains.size() >= maxResultSize) {
return methodsChains;
}
excludedCombinations = MethodsChain.edgeCombinations(excludedCombinations, local);
}
}
if (allLocalsIsNull || beforeStepChainsCount == methodsChains.size()) {
return methodsChains;
}
}
}
return methodsChains;
}
private static SearchInitializer createInitializer(final String targetQName,
final FactoryMap<MethodIncompleteSignature, PsiMethod[]> context,
final MethodChainsSearchService searchService,
final Set<String> excludedParamsTypesQNames) {
return new SearchInitializer(searchService.getMethods(targetQName), context, targetQName, excludedParamsTypesQNames);
}
@Nullable
private static MethodsChain search(final MethodChainsSearchService searchService,
final SearchInitializer initializer,
final Set<String> toSet,
final Set<String> excludedEdgeNames,
final int pathMaximalLength,
final FactoryMap<MethodIncompleteSignature, PsiMethod[]> resolver,
final String targetQName,
final Set<String> excludedParamsTypesQNames,
final String contextMethodName) {
final Set<String> allExcludedNames = MethodChainsSearchUtil.unionToHashSet(excludedParamsTypesQNames, targetQName);
ProgressManager.checkCanceled();
final SearchInitializer.InitResult initResult = initializer.init(excludedEdgeNames, toSet, searchService, contextMethodName);
final Map<MethodIncompleteSignature, MethodsChain> knownDistance = initResult.getChains();
final PriorityQueue<WeightAware<MethodIncompleteSignature>> q =
new PriorityQueue<WeightAware<MethodIncompleteSignature>>(initResult.getVertexes());
MethodsChain result = initResult.getCurrentBestTargetChain();
int maxWeight = 0;
for (final MethodsChain methodsChain : knownDistance.values()) {
if (methodsChain.getChainWeight() > maxWeight) {
maxWeight = methodsChain.getChainWeight();
}
}
final WeightAware<MethodIncompleteSignature> maxVertex = q.peek();
final int maxDistance;
if (maxVertex != null) {
maxDistance = maxVertex.getWeight();
}
else {
return null;
}
while (!q.isEmpty()) {
final WeightAware<MethodIncompleteSignature> currentVertex = q.poll();
final int currentVertexDistance = currentVertex.getWeight();
if (currentVertexDistance * Constants.CHAIN_SEARCH_MAGIC_RATIO < maxDistance) {
return result;
}
final MethodIncompleteSignature currentVertexUnderlying = currentVertex.getUnderlying();
final MethodsChain currentVertexMethodsChain = knownDistance.get(currentVertexUnderlying);
if (currentVertexDistance != currentVertexMethodsChain.getChainWeight()) {
continue;
}
final SortedSet<UsageIndexValue> bigrams = searchService.getBigram(currentVertexUnderlying);
int bigramsSumWeight = 0;
int maxUpdatedWeight = 0;
for (final UsageIndexValue indexValue : bigrams) {
final MethodIncompleteSignature vertex = indexValue.getMethodIncompleteSignature();
final int occurrences = indexValue.getOccurrences();
bigramsSumWeight += occurrences;
final boolean canBeResult = vertex.isStatic() || toSet.contains(vertex.getOwner());
if (!vertex.getOwner().equals(targetQName) || canBeResult) {
final int vertexDistance = Math.min(currentVertexDistance, occurrences);
final MethodsChain knownVertexMethodsChain = knownDistance.get(vertex);
if ((knownVertexMethodsChain == null || knownVertexMethodsChain.getChainWeight() < vertexDistance) &&
(result == null || result.getChainWeight() < vertexDistance)) {
if (occurrences * Constants.CHAIN_SEARCH_MAGIC_RATIO >= currentVertexMethodsChain.getChainWeight()) {
final MethodIncompleteSignature methodInvocation = indexValue.getMethodIncompleteSignature();
final PsiMethod[] psiMethods = resolver.get(methodInvocation);
if (psiMethods.length != 0 && MethodChainsSearchUtil.checkParametersForTypesQNames(psiMethods, allExcludedNames)) {
final MethodsChain newBestMethodsChain = currentVertexMethodsChain.addEdge(psiMethods);
if (canBeResult) {
result = newBestMethodsChain;
}
else if (newBestMethodsChain.size() < pathMaximalLength - 1) {
maxUpdatedWeight = Math.max(maxUpdatedWeight, newBestMethodsChain.getChainWeight());
q.add(new WeightAware<MethodIncompleteSignature>(indexValue.getMethodIncompleteSignature(),
newBestMethodsChain.getChainWeight()));
}
knownDistance.put(vertex, newBestMethodsChain);
}
}
else if (!allExcludedNames.contains(currentVertexMethodsChain.getFirstQualifierClass().getQualifiedName()) &&
searchService.isSingleton(currentVertexMethodsChain.getFirstQualifierClass(), contextMethodName) &&
(searchService.isRelevantMethodForNotOverriden(currentVertexMethodsChain.getFirstQualifierClass().getQualifiedName(),
currentVertexMethodsChain.getOneOfFirst().getName()) ||
searchService.isRelevantMethodForField(currentVertexMethodsChain.getFirstQualifierClass().getQualifiedName(),
currentVertexMethodsChain.getOneOfFirst().getName()))) {
result = currentVertexMethodsChain;
}
}
}
}
//if ((result == null || maxUpdatedWeight * Constants.CHAIN_SEARCH_MAGIC_RATIO2 <= bigramsSumWeight)
// && bigramsSumWeight * Constants.CHAIN_SEARCH_MAGIC_RATIO >= currentVertexMethodsChain.getChainWeight()) {
// return currentVertexMethodsChain;
//}
if ((currentVertexMethodsChain.isStaticChain() ||
!allExcludedNames.contains(currentVertexMethodsChain.getFirstQualifierClass().getQualifiedName())) &&
bigramsSumWeight * Constants.CHAIN_SEARCH_MAGIC_RATIO <= currentVertexDistance &&
(result == null || result.getChainWeight() < currentVertexDistance) &&
(currentVertexMethodsChain.isStaticChain() ||
searchService.isSingleton(currentVertexMethodsChain.getFirstQualifierClass(), contextMethodName) &&
(searchService.isRelevantMethodForNotOverriden(currentVertexMethodsChain.getFirstQualifierClass().getQualifiedName(),
currentVertexMethodsChain.getOneOfFirst().getName()) ||
searchService.isRelevantMethodForField(currentVertexMethodsChain.getFirstQualifierClass().getQualifiedName(),
currentVertexMethodsChain.getOneOfFirst().getName())))) {
result = currentVertexMethodsChain;
}
}
if (result != null && result.getChainWeight() * Constants.CHAIN_SEARCH_MAGIC_RATIO >= maxWeight) {
return result;
}
return null;
}
}

View File

@@ -0,0 +1,93 @@
package com.intellij.codeInsight.completion.methodChains.search;
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature;
import com.intellij.compilerOutputIndex.impl.MethodsUsageIndex;
import com.intellij.compilerOutputIndex.impl.UsageIndexValue;
import com.intellij.compilerOutputIndex.impl.bigram.BigramMethodsUsageIndex;
import com.intellij.compilerOutputIndex.impl.callingLocation.MethodNameAndQualifier;
import com.intellij.codeInsight.completion.methodChains.search.service.OverridenMethodsService;
import com.intellij.codeInsight.completion.methodChains.search.service.SingletonService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiClass;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public class MethodChainsSearchService {
private final static SortedSet EMPTY_SORTED_SET = new TreeSet();
private final MethodsUsageIndex myMethodsUsageIndex;
private final BigramMethodsUsageIndex myBigramMethodsUsageIndex;
private final SingletonService mySingletonService;
private final OverridenMethodsService myOverridenMethodsService;
private final Project myProject;
private final Map<String, Boolean> mySingletonLocalCache;
public MethodChainsSearchService(final Project project) {
myOverridenMethodsService = new OverridenMethodsService(project);
myMethodsUsageIndex = MethodsUsageIndex.getInstance(project);
myBigramMethodsUsageIndex = BigramMethodsUsageIndex.getInstance(project);
mySingletonService = new SingletonService(project);
myProject = project;
mySingletonLocalCache = new HashMap<String, Boolean>();
mySingletonLocalCache.put(null, false);
}
public Project getProject() {
return myProject;
}
@NotNull
@SuppressWarnings("unchecked")
public SortedSet<UsageIndexValue> getBigram(final MethodIncompleteSignature methodIncompleteSignature) {
final TreeSet<UsageIndexValue> value = myBigramMethodsUsageIndex.getValues(methodIncompleteSignature);
if (value != null) {
return value;
}
return EMPTY_SORTED_SET;
}
@NotNull
@SuppressWarnings("unchecked")
public SortedSet<UsageIndexValue> getMethods(final String targetQName) {
final TreeSet<UsageIndexValue> value = myMethodsUsageIndex.getValues(targetQName);
if (value != null) {
return value;
}
return EMPTY_SORTED_SET;
}
public boolean isSingleton(@NotNull final PsiClass psiClass, final String contextMethodName) {
return isSingleton(psiClass.getQualifiedName(), contextMethodName);
}
public boolean isSingleton(@Nullable final String typeQName, final String methodName) {
Boolean isSingleton = mySingletonLocalCache.get(typeQName);
if (isSingleton == null) {
isSingleton = mySingletonService.isSingleton(typeQName, methodName);
mySingletonLocalCache.put(typeQName, isSingleton);
}
return isSingleton;
}
public boolean isRelevantMethodForField(@NotNull final String className, @NotNull final String methodName) {
final Pair<Integer, Integer> occurrences =
myOverridenMethodsService.getMethodUsageInFieldContext(new MethodNameAndQualifier(methodName, className));
return occurrences.getFirst() > occurrences.getSecond();
}
public boolean isRelevantMethodForNotOverriden(@NotNull final String className, @NotNull final String methodName) {
final Pair<Integer, Integer> occurrences =
myOverridenMethodsService.getMethodsUsageInOverridenContext(new MethodNameAndQualifier(methodName, className));
return occurrences.getFirst() < occurrences.getSecond();
}
}

View File

@@ -0,0 +1,80 @@
package com.intellij.codeInsight.completion.methodChains.search;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiParameter;
import com.intellij.psi.PsiParameterList;
import com.intellij.psi.PsiPrimitiveType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* @author Dmitry Batkovich
*/
public final class MethodChainsSearchUtil {
private MethodChainsSearchUtil() {
}
@Nullable
public static PsiMethod getMethodWithMinNotPrimitiveParameters(final @NotNull PsiMethod[] methods,
final Set<String> excludedParamsQNames) {
PsiMethod minMethod = null;
int minParametersCount = Integer.MAX_VALUE;
for (final PsiMethod method : methods) {
final PsiParameterList parameterList = method.getParameterList();
boolean doContinue = false;
int parametersCount = parameterList.getParametersCount();
for (final PsiParameter p : parameterList.getParameters()) {
if (!(p.getType() instanceof PsiPrimitiveType)) {
if (excludedParamsQNames.contains(p.getType().getCanonicalText())) {
doContinue = true;
break;
}
parametersCount++;
}
}
if (doContinue) {
continue;
}
if (parametersCount < minParametersCount) {
if (parametersCount == 0) {
return method;
}
minParametersCount = parametersCount;
minMethod = method;
}
}
return minMethod;
}
public static boolean checkParametersForTypesQNames(final PsiMethod[] psiMethods, final Set<String> excludedTypesQNames) {
if (psiMethods.length == 0) {
return true;
}
for (final PsiMethod method : psiMethods) {
boolean hasTargetInParams = false;
for (final PsiParameter param : method.getParameterList().getParameters()) {
final String paramType = param.getType().getCanonicalText();
if (excludedTypesQNames.contains(paramType)) {
hasTargetInParams = true;
break;
}
}
if (!hasTargetInParams) {
return true;
}
}
return false;
}
public static <T> HashSet<T> unionToHashSet(final Collection<T> collection, final T... items) {
final HashSet<T> result = new HashSet<T>();
result.addAll(collection);
Collections.addAll(result, items);
return result;
}
}

View File

@@ -0,0 +1,160 @@
package com.intellij.codeInsight.completion.methodChains.search;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import static com.intellij.util.containers.ContainerUtil.reverse;
/**
* @author Dmitry Batkovich
*/
public class MethodsChain {
private final List<PsiMethod[]> myRevertedPath;
private final int myWeight;
public MethodsChain(final PsiMethod[] methods, final int weight) {
this(ContainerUtil.<PsiMethod[]>newArrayList(methods), weight);
}
public MethodsChain(final List<PsiMethod[]> revertedPath, final int weight) {
myRevertedPath = revertedPath;
myWeight = weight;
}
public int size() {
return myRevertedPath.size();
}
public boolean isStaticChain() {
return myRevertedPath.get(myRevertedPath.size() - 1)[0].hasModifierProperty(PsiModifier.STATIC);
}
@Nullable
public PsiClass getFirstQualifierClass() {
return myRevertedPath.isEmpty() ? null : myRevertedPath.get(myRevertedPath.size() - 1)[0].getContainingClass();
}
@Nullable
public PsiMethod getOneOfFirst() {
return (myRevertedPath.isEmpty() || myRevertedPath.get(0).length == 0) ? null : myRevertedPath.get(myRevertedPath.size() - 1)[0];
}
public List<PsiMethod[]> getPath() {
return reverse(myRevertedPath);
}
public int getChainWeight() {
return myWeight;
}
public MethodsChain addEdge(final PsiMethod[] psiMethods) {
final List<PsiMethod[]> newRevertedPath = new ArrayList<PsiMethod[]>(myRevertedPath.size() + 1);
newRevertedPath.addAll(myRevertedPath);
newRevertedPath.add(psiMethods);
return new MethodsChain(newRevertedPath, myWeight);
}
/**
* checking only method names
*/
public boolean weakContains(final MethodsChain otherChain) {
if (otherChain.myRevertedPath.isEmpty()) {
return true;
}
if (myRevertedPath.isEmpty()) {
return false;
}
final Iterator<PsiMethod[]> otherChainIterator = otherChain.myRevertedPath.iterator();
String otherChainCurrentName = otherChainIterator.next()[0].getName();
boolean checkingStarted = false;
for (final PsiMethod[] methods : myRevertedPath) {
final String thisCurrentName = methods[0].getName();
if (!checkingStarted && thisCurrentName.equals(otherChainCurrentName)) {
checkingStarted = true;
}
if (checkingStarted) {
if (otherChainIterator.hasNext()) {
otherChainCurrentName = otherChainIterator.next()[0].getName();
if (!otherChainCurrentName.equals(thisCurrentName)) {
return false;
}
} else {
return false;
}
}
}
return !otherChainIterator.hasNext();
}
@Override
public String toString() {
return StringUtil.join(myRevertedPath, "<-");
}
public static Set<Set<String>> edgeCombinations(final Set<Set<String>> oldCombinations,
final MethodsChain methodsChain) {
if (oldCombinations.isEmpty()) {
final Set<Set<String>> result = new HashSet<Set<String>>(methodsChain.myRevertedPath.size());
for (final PsiMethod[] e : methodsChain.myRevertedPath) {
final Set<String> set = new HashSet<String>();
set.add(e[0].getName());
result.add(set);
}
return result;
} else {
final Set<Set<String>> newTail = new HashSet<Set<String>>(oldCombinations.size() * methodsChain.size());
for (final PsiMethod[] e : methodsChain.myRevertedPath) {
final String methodName = e[0].getName();
for (final Set<String> tailSet : oldCombinations) {
final Set<String> newSet = new HashSet<String>(tailSet);
newSet.add(methodName);
if (!oldCombinations.contains(newSet)) {
newTail.add(newSet);
}
}
}
return newTail;
}
}
@SuppressWarnings("ConstantConditions")
public static CompareResult compare(final MethodsChain left, final MethodsChain right) {
if (left.size() == 0) {
return CompareResult.RIGHT_CONTAINS_LEFT;
}
if (right.size() == 0) {
return CompareResult.LEFT_CONTAINS_RIGHT;
}
final Iterator<PsiMethod[]> leftIterator = left.myRevertedPath.iterator();
final Iterator<PsiMethod[]> rightIterator = right.myRevertedPath.iterator();
final PsiManager psiManager = PsiManager.getInstance(left.getFirstQualifierClass().getProject());
while (leftIterator.hasNext() && rightIterator.hasNext()) {
final PsiMethod thisNext = leftIterator.next()[0];
final PsiMethod thatNext = rightIterator.next()[0];
if (((thisNext.isConstructor() != thatNext.isConstructor()))
|| !thisNext.getName().equals(thatNext.getName())
|| !psiManager.areElementsEquivalent(thisNext.getContainingClass(), thatNext.getContainingClass())) {
return CompareResult.NOT_EQUAL;
}
}
if (leftIterator.hasNext() && !rightIterator.hasNext()) {
return CompareResult.LEFT_CONTAINS_RIGHT;
}
if (!leftIterator.hasNext() && rightIterator.hasNext()) {
return CompareResult.RIGHT_CONTAINS_LEFT;
}
return CompareResult.EQUAL;
}
public enum CompareResult {
LEFT_CONTAINS_RIGHT,
RIGHT_CONTAINS_LEFT,
EQUAL,
NOT_EQUAL
}
}

View File

@@ -0,0 +1,230 @@
package com.intellij.codeInsight.completion.methodChains.search;
import com.intellij.codeInsight.NullableNotNullManager;
import com.intellij.codeInsight.completion.JavaChainLookupElement;
import com.intellij.codeInsight.completion.methodChains.ChainCompletionStringUtil;
import com.intellij.codeInsight.completion.methodChains.completion.context.ChainCompletionContext;
import com.intellij.codeInsight.completion.methodChains.completion.context.ContextRelevantStaticMethod;
import com.intellij.codeInsight.completion.methodChains.completion.context.ContextRelevantVariableGetter;
import com.intellij.codeInsight.completion.methodChains.completion.lookup.ChainCompletionNewVariableLookupElement;
import com.intellij.codeInsight.completion.methodChains.completion.lookup.WeightableChainLookupElement;
import com.intellij.codeInsight.completion.methodChains.completion.lookup.sub.GetterLookupSubLookupElement;
import com.intellij.codeInsight.completion.methodChains.completion.lookup.sub.SubLookupElement;
import com.intellij.codeInsight.completion.methodChains.completion.lookup.sub.VariableSubLookupElement;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.VariableLookupItem;
import com.intellij.psi.*;
import com.intellij.util.containers.ContainerUtil;
import gnu.trove.TIntObjectHashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static com.intellij.codeInsight.completion.methodChains.completion.lookup.ChainCompletionLookupElementUtil.createLookupElement;
import static com.intellij.psi.CommonClassNames.JAVA_LANG_STRING;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public class MethodsChainLookupRangingHelper {
public static List<LookupElement> chainsToWeightableLookupElements(final List<MethodsChain> chains,
final ChainCompletionContext context) {
final List<LookupElement> lookupElements = new ArrayList<LookupElement>(chains.size());
for (final MethodsChain chain : chains) {
final LookupElement lookupElement = chainToWeightableLookupElement(chain, context);
if (lookupElement != null) {
lookupElements.add(lookupElement);
}
}
return lookupElements;
}
@SuppressWarnings("ConstantConditions")
@Nullable
private static WeightableChainLookupElement chainToWeightableLookupElement(final MethodsChain chain,
final ChainCompletionContext context) {
final int chainSize = chain.size();
assert chainSize != 0;
final int lastMethodWeight = chain.getChainWeight();
int unreachableParametersCount = 0;
int notMatchedStringVars = 0;
Boolean isFirstMethodStatic = null;
Boolean hasCallingVariableInContext = null;
LookupElement chainLookupElement = null;
final NullableNotNullManager nullableNotNullManager = NullableNotNullManager.getInstance(context.getProject());
for (final PsiMethod[] psiMethods : chain.getPath()) {
final PsiMethod method =
MethodChainsSearchUtil.getMethodWithMinNotPrimitiveParameters(psiMethods, Collections.singleton(context.getTargetQName()));
if (method == null) {
return null;
}
if (isFirstMethodStatic == null) {
isFirstMethodStatic = psiMethods[0].hasModifierProperty(PsiModifier.STATIC);
}
final MethodProcResult procResult =
processMethod(method, context, lastMethodWeight, chainLookupElement == null, nullableNotNullManager);
if (procResult == null) {
return null;
}
if (hasCallingVariableInContext == null) {
hasCallingVariableInContext = procResult.hasCallingVariableInContext();
}
unreachableParametersCount += procResult.getUnreachableParametersCount();
notMatchedStringVars += procResult.getNotMatchedStringVars();
chainLookupElement = chainLookupElement == null
? procResult.getLookupElement()
: new JavaChainLookupElement(chainLookupElement, procResult.getLookupElement());
}
final ChainRelevance relevance = new ChainRelevance(chainSize,
lastMethodWeight,
unreachableParametersCount,
notMatchedStringVars,
hasCallingVariableInContext,
isFirstMethodStatic);
return new WeightableChainLookupElement(chainLookupElement, relevance);
}
private static MethodProcResult processMethod(@NotNull final PsiMethod method,
final ChainCompletionContext context,
final int weight,
final boolean isHeadMethod,
final NullableNotNullManager nullableNotNullManager) {
int unreachableParametersCount = 0;
int notMatchedStringVars = 0;
boolean hasCallingVariableInContext = false;
final PsiParameterList parameterList = method.getParameterList();
final TIntObjectHashMap<SubLookupElement> parametersMap = new TIntObjectHashMap<SubLookupElement>(parameterList.getParametersCount());
final PsiParameter[] parameters = parameterList.getParameters();
for (int i = 0; i < parameters.length; i++) {
final PsiParameter parameter = parameters[i];
final String typeQName = parameter.getType().getCanonicalText();
if (typeQName != null) {
if (JAVA_LANG_STRING.equals(typeQName)) {
final PsiVariable relevantStringVar = context.findRelevantStringInContext(parameter.getName());
if (relevantStringVar == null) {
notMatchedStringVars++;
}
else {
parametersMap.put(i, new VariableSubLookupElement(relevantStringVar));
}
}
else if (!ChainCompletionStringUtil.isPrimitiveOrArrayOfPrimitives(typeQName)) {
final Collection<PsiVariable> contextVariables = context.getVariables(typeQName);
final PsiVariable contextVariable = ContainerUtil.getFirstItem(contextVariables, null);
if (contextVariable != null) {
if (contextVariables.size() == 1) parametersMap.put(i, new VariableSubLookupElement(contextVariable));
continue;
}
final Collection<ContextRelevantVariableGetter> relevantVariablesGetters = context.getRelevantVariablesGetters(typeQName);
final ContextRelevantVariableGetter contextVariableGetter = ContainerUtil.getFirstItem(relevantVariablesGetters, null);
if (contextVariableGetter != null) {
if (relevantVariablesGetters.size() == 1) parametersMap.put(i, contextVariableGetter.createSubLookupElement());
continue;
}
final Collection<PsiMethod> containingClassMethods = context.getContainingClassMethods(typeQName);
final PsiMethod contextRelevantGetter = ContainerUtil.getFirstItem(containingClassMethods, null);
if (contextRelevantGetter != null) {
if (containingClassMethods.size() == 1) parametersMap.put(i, new GetterLookupSubLookupElement(method.getName()));
continue;
}
final ContextRelevantStaticMethod contextRelevantStaticMethod =
ContainerUtil.getFirstItem(context.getRelevantStaticMethods(typeQName, weight), null);
if (contextRelevantStaticMethod != null) {
//
// In most cases it is not really relevant
//
//parametersMap.put(i, contextRelevantStaticMethod.createLookupElement());
continue;
}
if (!nullableNotNullManager.isNullable(parameter, true)) {
unreachableParametersCount++;
}
}
}
}
final LookupElement lookupElement;
if (isHeadMethod) {
if (method.hasModifierProperty(PsiModifier.STATIC)) {
lookupElement = createLookupElement(method, parametersMap);
}
else if (method.isConstructor()) {
return null;
}
else {
final PsiClass containingClass = method.getContainingClass();
final String classQName = containingClass.getQualifiedName();
if (classQName == null) return null;
final Object e = ContainerUtil.getFirstItem(context.getContextRefElements(classQName), null);
if (e != null) {
final LookupElement firstChainElement;
if (e instanceof PsiVariable) {
hasCallingVariableInContext = true;
firstChainElement = new VariableLookupItem((PsiVariable)e);
}
else if (e instanceof PsiMethod) {
firstChainElement = createLookupElement((PsiMethod)e, null);
}
else if (e instanceof LookupElement) {
firstChainElement = (LookupElement)e;
}
else {
throw new AssertionError();
}
lookupElement = new JavaChainLookupElement(firstChainElement, createLookupElement(method, parametersMap));
}
else lookupElement = context.getContainingClassQNames().contains(classQName)
? createLookupElement(method, parametersMap)
: new JavaChainLookupElement(ChainCompletionNewVariableLookupElement.create(containingClass),
createLookupElement(method, parametersMap));
}
}
else {
lookupElement = createLookupElement(method, parametersMap);
}
return new MethodProcResult(lookupElement, unreachableParametersCount, notMatchedStringVars, hasCallingVariableInContext);
}
private static class MethodProcResult {
private final LookupElement myMethodLookup;
private final int myUnreachableParametersCount;
private final int myNotMatchedStringVars;
private final boolean myHasCallingVariableInContext;
private MethodProcResult(final LookupElement methodLookup,
final int unreachableParametersCount,
final int notMatchedStringVars,
final boolean hasCallingVariableInContext) {
myMethodLookup = methodLookup;
myUnreachableParametersCount = unreachableParametersCount;
myNotMatchedStringVars = notMatchedStringVars;
myHasCallingVariableInContext = hasCallingVariableInContext;
}
private boolean hasCallingVariableInContext() {
return myHasCallingVariableInContext;
}
private LookupElement getLookupElement() {
return myMethodLookup;
}
private int getUnreachableParametersCount() {
return myUnreachableParametersCount;
}
private int getNotMatchedStringVars() {
return myNotMatchedStringVars;
}
}
}

View File

@@ -0,0 +1,128 @@
package com.intellij.codeInsight.completion.methodChains.search;
import com.intellij.codeInsight.completion.methodChains.Constants;
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature;
import com.intellij.compilerOutputIndex.impl.UsageIndexValue;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiModifier;
import com.intellij.util.containers.FactoryMap;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public class SearchInitializer {
private final List<WeightAware<MethodIncompleteSignature>> myVertexes;
private final LinkedHashMap<MethodIncompleteSignature, MethodsChain> myChains;
private final Map<MethodIncompleteSignature, Integer> myOccurrencesMap;
private final FactoryMap<MethodIncompleteSignature, PsiMethod[]> myResolver;
public SearchInitializer(final SortedSet<UsageIndexValue> indexValues,
final FactoryMap<MethodIncompleteSignature, PsiMethod[]> resolver,
final String targetQName,
final Set<String> excludedParamsTypesQNames) {
myResolver = resolver;
final int size = indexValues.size();
myVertexes = new ArrayList<WeightAware<MethodIncompleteSignature>>(size);
myChains = new LinkedHashMap<MethodIncompleteSignature, MethodsChain>(size);
myOccurrencesMap = new HashMap<MethodIncompleteSignature, Integer>(size);
add(indexValues, MethodChainsSearchUtil.unionToHashSet(excludedParamsTypesQNames, targetQName));
}
private void add(final Collection<UsageIndexValue> indexValues, final Set<String> excludedParamsTypesQNames) {
int bestOccurrences = -1;
for (final UsageIndexValue indexValue : indexValues) {
if (add(indexValue, excludedParamsTypesQNames)) {
final int occurrences = indexValue.getOccurrences();
if (bestOccurrences == -1) {
bestOccurrences = occurrences;
} else if (bestOccurrences > occurrences * Constants.CHAIN_SEARCH_MAGIC_RATIO) {
return;
}
}
}
}
private boolean add(final UsageIndexValue indexValue, final Set<String> excludedParamsTypesQNames) {
final MethodIncompleteSignature methodInvocation = indexValue.getMethodIncompleteSignature();
final PsiMethod[] psiMethods = myResolver.get(methodInvocation);
if (psiMethods.length != 0 && MethodChainsSearchUtil.checkParametersForTypesQNames(psiMethods, excludedParamsTypesQNames)) {
final int occurrences = indexValue.getOccurrences();
final MethodsChain methodsChain = new MethodsChain(psiMethods, occurrences);
myChains.put(methodInvocation, methodsChain);
myVertexes.add(new WeightAware<MethodIncompleteSignature>(methodInvocation, occurrences));
myOccurrencesMap.put(methodInvocation, occurrences);
return true;
}
return false;
}
public InitResult init(final Set<String> excludedEdgeNames,
final Set<String> contextQNames,
final MethodChainsSearchService searchService,
final String contextMethodName) {
final int size = myVertexes.size();
int bestOccurrences = 0;
MethodsChain bestTargetMethodChain = null;
final List<WeightAware<MethodIncompleteSignature>> initedVertexes = new ArrayList<WeightAware<MethodIncompleteSignature>>(size);
final LinkedHashMap<MethodIncompleteSignature, MethodsChain> initedChains = new LinkedHashMap<MethodIncompleteSignature, MethodsChain>(size);
final Iterator<Map.Entry<MethodIncompleteSignature, MethodsChain>> chainsIterator = myChains.entrySet().iterator();
for (final WeightAware<MethodIncompleteSignature> vertex : myVertexes) {
final Map.Entry<MethodIncompleteSignature, MethodsChain> chainEntry = chainsIterator.next();
final MethodIncompleteSignature method = vertex.getUnderlying();
if (!excludedEdgeNames.contains(method.getName())) {
initedVertexes.add(vertex);
final MethodsChain methodsChain = chainEntry.getValue();
initedChains.put(chainEntry.getKey(), methodsChain);
if (contextQNames.contains(method.getOwner())) {
final Integer occurrences = myOccurrencesMap.get(method);
if (occurrences > bestOccurrences) {
final PsiMethod oneOfFirst = methodsChain.getOneOfFirst();
if (oneOfFirst != null && oneOfFirst.hasModifierProperty(PsiModifier.STATIC)) {
bestTargetMethodChain = methodsChain;
bestOccurrences = occurrences;
continue;
}
final PsiClass firstQualifierClass = methodsChain.getFirstQualifierClass();
if (firstQualifierClass != null && (searchService.isSingleton(firstQualifierClass, contextMethodName)
|| contextQNames.contains(firstQualifierClass.getQualifiedName()))) {
bestTargetMethodChain = methodsChain;
bestOccurrences = occurrences;
}
}
}
}
}
return new InitResult(initedVertexes, initedChains, bestTargetMethodChain);
}
public static class InitResult {
private final List<WeightAware<MethodIncompleteSignature>> myVertexes;
private final LinkedHashMap<MethodIncompleteSignature, MethodsChain> myChains;
private final MethodsChain myCurrentBestTargetChain;
private InitResult(final List<WeightAware<MethodIncompleteSignature>> vertexes,
final LinkedHashMap<MethodIncompleteSignature, MethodsChain> chains,
final @Nullable MethodsChain currentBestTargetChain) {
this.myVertexes = vertexes;
this.myChains = chains;
this.myCurrentBestTargetChain = currentBestTargetChain;
}
public List<WeightAware<MethodIncompleteSignature>> getVertexes() {
return myVertexes;
}
public LinkedHashMap<MethodIncompleteSignature, MethodsChain> getChains() {
return myChains;
}
@Nullable
public MethodsChain getCurrentBestTargetChain() {
return myCurrentBestTargetChain;
}
}
}

View File

@@ -0,0 +1,29 @@
package com.intellij.codeInsight.completion.methodChains.search;
import org.jetbrains.annotations.NotNull;
/**
* @author Dmitry Batkovich
*/
public class WeightAware<V> implements Comparable<WeightAware<V>> {
private final V myUnderlying;
private final int myWeight;
public WeightAware(final V underlying, final int weight) {
myUnderlying = underlying;
myWeight = weight;
}
public V getUnderlying() {
return myUnderlying;
}
public int getWeight() {
return myWeight;
}
@Override
public int compareTo(@NotNull final WeightAware<V> that) {
return -getWeight() + that.getWeight();
}
}

View File

@@ -0,0 +1,65 @@
package com.intellij.codeInsight.completion.methodChains.search.service;
import com.google.common.collect.Multiset;
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature;
import com.intellij.compilerOutputIndex.impl.callingLocation.CallingLocation;
import com.intellij.compilerOutputIndex.impl.callingLocation.MethodCallingLocationIndex;
import com.intellij.compilerOutputIndex.impl.callingLocation.MethodNameAndQualifier;
import com.intellij.compilerOutputIndex.impl.callingLocation.VariableType;
import com.intellij.compilerOutputIndex.impl.quickInheritance.QuickInheritanceIndex;
import com.intellij.compilerOutputIndex.impl.quickInheritance.QuickMethodsIndex;
import com.intellij.compilerOutputIndex.impl.quickInheritance.QuickOverrideUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
/**
* @author Dmitry Batkovich
*/
public class OverridenMethodsService {
private final QuickMethodsIndex myQuickMethodsIndex;
private final QuickInheritanceIndex myQuickInheritanceIndex;
private final MethodCallingLocationIndex myMethodCallingLocationIndex;
public OverridenMethodsService(final Project project) {
myQuickInheritanceIndex = QuickInheritanceIndex.getInstance(project);
myQuickMethodsIndex = QuickMethodsIndex.getInstance(project);
myMethodCallingLocationIndex = MethodCallingLocationIndex.getInstance(project);
}
public boolean isMethodOverriden(final String classQName, final String methodName) {
return QuickOverrideUtil.isMethodOverriden(classQName, methodName, myQuickInheritanceIndex, myQuickMethodsIndex);
}
public Pair<Integer, Integer> getMethodsUsageInOverridenContext(final MethodNameAndQualifier method) {
final Multiset<MethodIncompleteSignature> locationsAsParam = myMethodCallingLocationIndex.getLocationsAsParam(method);
int overridenOccurrences = 0;
int nonOverridenOccurrences = 0;
for (final Multiset.Entry<MethodIncompleteSignature> e : locationsAsParam.entrySet()) {
final MethodIncompleteSignature sign = e.getElement();
final boolean methodOverriden = isMethodOverriden(sign.getOwner(), sign.getName());
if (methodOverriden) {
overridenOccurrences++;
}
else {
nonOverridenOccurrences++;
}
}
return Pair.create(overridenOccurrences, nonOverridenOccurrences);
}
public Pair<Integer, Integer> getMethodUsageInFieldContext(final MethodNameAndQualifier method) {
int asField = 0;
int notField = 0;
for (final CallingLocation callingLocation : myMethodCallingLocationIndex.getAllLocations(method)) {
if (callingLocation.getVariableType().equals(VariableType.FIELD)) {
asField++;
}
else {
notField++;
}
}
return Pair.create(asField, notField);
}
}

View File

@@ -0,0 +1,89 @@
package com.intellij.codeInsight.completion.methodChains.search.service;
import com.intellij.codeInsight.completion.methodChains.Constants;
import com.intellij.compilerOutputIndex.impl.singleton.MethodShortSignatureWithWeight;
import com.intellij.compilerOutputIndex.impl.singleton.ParamsInMethodOccurrencesIndex;
import com.intellij.compilerOutputIndex.impl.singleton.TwinVariablesIndex;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.CommonClassNames;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiClass;
import com.intellij.psi.search.GlobalSearchScope;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author Dmitry Batkovich
*/
public class SingletonService {
private final TwinVariablesIndex myTwinVariablesIndex;
private final ParamsInMethodOccurrencesIndex myParamsInMethodOccurrencesIndex;
private final GlobalSearchScope myAllScope;
private final JavaPsiFacade myJavaPsiFacade;
private final Map<String, Boolean> myLocalCache;
public SingletonService(final Project project) {
myTwinVariablesIndex = TwinVariablesIndex.getInstance(project);
myParamsInMethodOccurrencesIndex = ParamsInMethodOccurrencesIndex.getInstance(project);
myAllScope = GlobalSearchScope.allScope(project);
myJavaPsiFacade = JavaPsiFacade.getInstance(project);
myLocalCache = new HashMap<String, Boolean>();
myLocalCache.put(null, false);
}
public boolean isSingleton(@Nullable final String typeQName, final @NotNull String contextMethodName) {
final Boolean isSingletonCached = myLocalCache.get(typeQName);
if (isSingletonCached == null) {
assert typeQName != null;
final PsiClass aClass = myJavaPsiFacade.findClass(typeQName, myAllScope);
if (aClass == null) {
myLocalCache.put(typeQName, false);
return false;
}
for (final PsiClass psiClass : aClass.getInterfaces()) {
final String qualifiedName = psiClass.getQualifiedName();
if (CommonClassNames.JAVA_LANG_OBJECT.equals(qualifiedName) || !isSingleton(qualifiedName, contextMethodName)) {
myLocalCache.put(typeQName, false);
return false;
}
}
final boolean isSingleton = hasTwinsFeature(typeQName) && isSuitableTypeFor(typeQName, contextMethodName);
myLocalCache.put(typeQName, isSingleton);
return isSingleton;
}
return isSingletonCached;
}
public boolean hasTwinsFeature(final String typeQName) {
final List<Integer> twinInfo = myTwinVariablesIndex.getTwinInfo(typeQName);
if (twinInfo.isEmpty()) {
return false;
}
int ones = 0;
for (final int i : twinInfo) {
if (i == 1) {
ones++;
}
}
return (twinInfo.size() - ones) * Constants.SINGLETON_MAGIC_RATIO < twinInfo.size();
}
private boolean isSuitableTypeFor(final String typeName, final String methodName) {
final Pair<List<MethodShortSignatureWithWeight>, Integer> parameterOccurrences =
myParamsInMethodOccurrencesIndex.getParameterOccurrences(typeName);
if (parameterOccurrences.getSecond() == 0) {
return true;
}
final List<MethodShortSignatureWithWeight> contextMethods = parameterOccurrences.getFirst();
final MethodShortSignatureWithWeight last = contextMethods.get(contextMethods.size() - 1);
return last.getMethodShortSignature().getName().equals(methodName) || last.getWeight() * Constants.SINGLETON_MAGIC_RATIO2 <= parameterOccurrences.getSecond();
}
}

View File

@@ -95,8 +95,7 @@ public class JavaCompletionProcessor extends BaseScopeProcessor implements Eleme
}
}
else if (qualifier != null) {
myQualifierType = qualifier.getType();
myQualifierClass = PsiUtil.resolveClassInType(myQualifierType);
setQualifierType(qualifier.getType());
if (myQualifierType == null && qualifier instanceof PsiJavaCodeReferenceElement) {
final PsiElement target = ((PsiJavaCodeReferenceElement)qualifier).resolve();
if (target instanceof PsiClass) {
@@ -256,6 +255,11 @@ public class JavaCompletionProcessor extends BaseScopeProcessor implements Eleme
return false;
}
public void setQualifierType(@Nullable PsiType qualifierType) {
myQualifierType = qualifierType;
myQualifierClass = PsiUtil.resolveClassInClassTypeOnly(qualifierType);
}
@Nullable
public PsiType getQualifierType() {
return myQualifierType;

View File

@@ -260,7 +260,7 @@ public class GenerateMembersUtil {
try {
final PsiMethod resultMethod = createMethod(factory, sourceMethod, target);
copyDocComment(sourceMethod, resultMethod);
copyDocComment(sourceMethod, resultMethod, factory);
copyModifiers(sourceMethod.getModifierList(), resultMethod.getModifierList());
final PsiSubstitutor collisionResolvedSubstitutor =
substituteTypeParameters(factory, target, sourceMethod.getTypeParameterList(), resultMethod.getTypeParameterList(), substitutor, sourceMethod);
@@ -305,7 +305,8 @@ public class GenerateMembersUtil {
for (PsiTypeParameter typeParam : sourceTypeParameterList.getTypeParameters()) {
final PsiTypeParameter substitutedTypeParam = substituteTypeParameter(factory, typeParam, substitutor, sourceMethod);
final PsiTypeParameter resolvedTypeParam = resolveTypeParametersCollision(factory, sourceTypeParameterList, target, substitutedTypeParam, substitutor);
final PsiTypeParameter resolvedTypeParam = resolveTypeParametersCollision(factory, sourceTypeParameterList, target,
substitutedTypeParam, substitutor);
targetTypeParameterList.add(resolvedTypeParam);
if (substitutedTypeParam != resolvedTypeParam) {
substitutionMap.put(typeParam, factory.createType(resolvedTypeParam));
@@ -328,7 +329,7 @@ public class GenerateMembersUtil {
return newTypeParameter;
}
}
return typeParam;
return factory.createTypeParameter(typeParam.getName(), typeParam.getSuperTypes());
}
@NotNull
@@ -383,8 +384,8 @@ public class GenerateMembersUtil {
@NotNull PsiSubstitutor substitutor, PsiElement target) {
PsiParameter[] parameters = sourceParameterList.getParameters();
UniqueNameGenerator generator = new UniqueNameGenerator();
for (int i = 0; i < parameters.length; i++) {
PsiParameter parameter = parameters[i];
for (PsiParameter parameter : parameters) {
final PsiType parameterType = parameter.getType();
final PsiType substituted = substituteType(substitutor, parameterType, (PsiMethod)parameter.getDeclarationScope());
@NonNls String paramName = parameter.getName();
@@ -394,14 +395,20 @@ public class GenerateMembersUtil {
isBaseNameGenerated = false;
}
if (paramName == null || isBaseNameGenerated && !isSubstituted && isBaseNameGenerated(codeStyleManager, parameterType, paramName)) {
if (paramName == null ||
isBaseNameGenerated && !isSubstituted && isBaseNameGenerated(codeStyleManager, parameterType, paramName) ||
!factory.isValidParameterName(paramName)) {
String[] names = codeStyleManager.suggestVariableName(VariableKind.PARAMETER, null, null, substituted).names;
if (names.length > 0) {
paramName = generator.generateUniqueName(names[0]);
}
else {
paramName = generator.generateUniqueName("p");
}
}
else if (!generator.value(paramName)) {
paramName = generator.generateUniqueName(paramName);
}
if (paramName == null) paramName = "p" + i;
generator.addExistingName(paramName);
final PsiParameter newParameter = factory.createParameter(paramName, substituted, target);
copyOrReplaceModifierList(parameter, newParameter);
@@ -419,12 +426,12 @@ public class GenerateMembersUtil {
}
}
private static void copyDocComment(PsiMethod source, PsiMethod target) {
private static void copyDocComment(PsiMethod source, PsiMethod target, JVMElementFactory factory) {
final PsiElement navigationElement = source.getNavigationElement();
if (navigationElement instanceof PsiDocCommentOwner) {
final PsiDocComment docComment = ((PsiDocCommentOwner)navigationElement).getDocComment();
if (docComment != null) {
target.addAfter(docComment, null);
target.addAfter(factory.createDocCommentFromText(docComment.getText()), null);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 0-2 JetBrains s.r.o.
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -119,34 +119,22 @@ public class OverrideImplementUtil extends OverrideImplementExploreUtil {
return !superClass.isInterface();
}
@NotNull
private static List<PsiMethod> overrideOrImplementMethod(PsiClass aClass,
PsiMethod method,
PsiSubstitutor substitutor,
boolean toCopyJavaDoc,
boolean insertOverrideIfPossible) throws IncorrectOperationException {
return overrideOrImplementMethod(aClass, method, substitutor, createDefaultDecorator(aClass, method, toCopyJavaDoc, insertOverrideIfPossible));
}
public static List<PsiMethod> overrideOrImplementMethod(PsiClass aClass,
PsiMethod method,
PsiSubstitutor substitutor,
Consumer<PsiMethod> decorator) throws IncorrectOperationException {
PsiMethod method,
PsiSubstitutor substitutor,
boolean toCopyJavaDoc,
boolean insertOverrideIfPossible) throws IncorrectOperationException {
if (!method.isValid() || !substitutor.isValid()) return Collections.emptyList();
List<PsiMethod> results = new ArrayList<PsiMethod>();
for (final MethodImplementor implementor : getImplementors()) {
final PsiMethod[] prototypes = implementor.createImplementationPrototypes(aClass, method);
if (implementor.isBodyGenerated()) {
ContainerUtil.addAll(results, prototypes);
}
else {
for (PsiMethod prototype : prototypes) {
decorator.consume(prototype);
results.add(prototype);
}
for (PsiMethod prototype : prototypes) {
implementor.createDecorator(aClass, method, toCopyJavaDoc, insertOverrideIfPossible).consume(prototype);
results.add(prototype);
}
}
if (results.isEmpty()) {
PsiMethod method1 = GenerateMembersUtil.substituteGenericMethod(method, substitutor, aClass);
@@ -163,6 +151,7 @@ public class OverrideImplementUtil extends OverrideImplementExploreUtil {
defaultValue.getParent().deleteChildRange(defaultKeyword, defaultValue);
}
}
Consumer<PsiMethod> decorator = createDefaultDecorator(aClass, method, toCopyJavaDoc, insertOverrideIfPossible);
decorator.consume(result);
results.add(result);
}

View File

@@ -51,6 +51,10 @@ public class MagicCompletionContributor extends CompletionContributor {
PsiElement pos = parameters.getPosition();
MagicConstantInspection.AllowedValues allowedValues = null;
if (JavaCompletionData.AFTER_DOT.accepts(pos)) {
return;
}
if (IN_METHOD_CALL_ARGUMENT.accepts(pos)) {
PsiCall call = PsiTreeUtil.getParentOfType(pos, PsiCall.class);
if (!(call instanceof PsiExpression)) return;

View File

@@ -0,0 +1,50 @@
package com.intellij.compilerOutputIndex.api.descriptor;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.KeyDescriptor;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public class ArrayListKeyDescriptor<E> implements KeyDescriptor<List<E>> {
private final DataExternalizer<E> myDataExternalizer;
public ArrayListKeyDescriptor(final DataExternalizer<E> dataExternalizer) {
myDataExternalizer = dataExternalizer;
}
@Override
public void save(final DataOutput out, final List<E> list) throws IOException {
out.writeInt(list.size());
for (final E element : list) {
myDataExternalizer.save(out, element);
}
}
@Override
public ArrayList<E> read(final DataInput in) throws IOException {
final int size = in.readInt();
final ArrayList<E> list = new ArrayList<E>(size);
for (int i = 0; i < size; i++) {
list.add(myDataExternalizer.read(in));
}
return list;
}
@Override
public int getHashCode(final List<E> value) {
return value.hashCode();
}
@Override
public boolean isEqual(final List<E> val1, final List<E> val2) {
return val1.equals(val2);
}
}

View File

@@ -0,0 +1,54 @@
package com.intellij.compilerOutputIndex.api.descriptor;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.KeyDescriptor;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* @author Dmitry Batkovich
*/
public class HashMapKeyDescriptor<K, V> implements KeyDescriptor<Map<K, V>> {
private final DataExternalizer<K> myKeyDataExternalizer;
private final DataExternalizer<V> myValueDataExternalizer;
public HashMapKeyDescriptor(final DataExternalizer<K> keyDataExternalizer, final DataExternalizer<V> valueDataExternalizer) {
myKeyDataExternalizer = keyDataExternalizer;
myValueDataExternalizer = valueDataExternalizer;
}
@Override
public void save(final DataOutput out, final Map<K, V> map) throws IOException {
final int size = map.size();
out.writeInt(size);
for (final Map.Entry<K, V> e : map.entrySet()) {
myKeyDataExternalizer.save(out, e.getKey());
myValueDataExternalizer.save(out, e.getValue());
}
}
@Override
public Map<K, V> read(final DataInput in) throws IOException {
final int size = in.readInt();
final HashMap<K, V> map = new HashMap<K, V>(size);
for (int i = 0; i < size; i++) {
map.put(myKeyDataExternalizer.read(in), myValueDataExternalizer.read(in));
}
return map;
}
@Override
public int getHashCode(final Map<K, V> map) {
return map.hashCode();
}
@Override
public boolean isEqual(final Map<K, V> val1, final Map<K, V> val2) {
return val1.equals(val2);
}
}

View File

@@ -0,0 +1,54 @@
package com.intellij.compilerOutputIndex.api.descriptor;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.KeyDescriptor;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
/**
* @author Dmitry Batkovich
*/
public class HashSetKeyDescriptor<K> implements KeyDescriptor<Set<K>> {
private final DataExternalizer<K> keyDataExternalizer;
public HashSetKeyDescriptor(final DataExternalizer<K> keyDataExternalizer) {
this.keyDataExternalizer = keyDataExternalizer;
}
@Override
public void save(final DataOutput out, final Set<K> set) throws IOException {
out.writeInt(set.size());
for (final K k : set) {
keyDataExternalizer.save(out, k);
}
}
@Override
public HashSet<K> read(final DataInput in) throws IOException {
final int size = in.readInt();
final HashSet<K> set = new HashSet<K>(size);
for (int i = 0; i < size; i++) {
set.add(keyDataExternalizer.read(in));
}
return set;
}
public static <K> HashSetKeyDescriptor<K> of(final DataExternalizer<K> keyDataExternalizer) {
return new HashSetKeyDescriptor<K>(keyDataExternalizer);
}
@Override
public int getHashCode(final Set<K> value) {
return value.hashCode();
}
@Override
public boolean isEqual(final Set<K> val1, final Set<K> val2) {
return val1.equals(val2);
}
}

View File

@@ -0,0 +1,129 @@
package com.intellij.compilerOutputIndex.api.fs;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.asm4.Opcodes;
import org.jetbrains.asm4.Type;
import java.util.ArrayList;
import java.util.List;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public final class AsmUtil implements Opcodes {
private AsmUtil() {}
public static boolean isStaticMethodDeclaration(final int access) {
return (access & Opcodes.ACC_STATIC) != 0;
}
public static boolean isStaticMethodInvocation(final int opcode) {
return opcode == Opcodes.INVOKESTATIC;
}
public static String getQualifiedClassName(final String name) {
return asJavaInnerClassQName(Type.getObjectType(name).getClassName());
}
public static String getReturnType(final String desc) {
return asJavaInnerClassQName(Type.getReturnType(desc).getClassName());
}
public static String[] getQualifiedClassNames(final String[] classNames, final String... yetAnotherClassNames) {
final List<String> qualifiedClassNames = new ArrayList<String>(classNames.length + yetAnotherClassNames.length);
for (final String className : classNames) {
qualifiedClassNames.add(getQualifiedClassName(className));
}
for (final String className : yetAnotherClassNames) {
if (className != null) {
qualifiedClassNames.add(getQualifiedClassName(className));
}
}
return ArrayUtil.toStringArray(qualifiedClassNames);
}
public static String[] getParamsTypes(final String desc) {
final Type[] types = Type.getArgumentTypes(desc);
final String[] typesAsString = new String[types.length];
for (int i = 0; i < types.length; i++) {
typesAsString[i] = types[i].getClassName();
}
return typesAsString;
}
@Nullable
public static String getSignature(final PsiMethod psiMethod) {
final PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
final StringBuilder sb = new StringBuilder();
sb.append("(");
for (final PsiParameter p : parameters) {
final String desc = getDescriptor(p);
if (desc == null) {
return null;
}
sb.append(desc);
}
sb.append(")");
final String desc = getDescriptor(psiMethod.getReturnType());
if (desc == null) {
return null;
}
sb.append(desc);
return sb.toString();
}
@Nullable
private static String getDescriptor(final PsiParameter parameter) {
return getDescriptor(parameter.getType());
}
@Nullable
private static String getDescriptor(@Nullable final PsiType type) {
if (type == null) {
return null;
}
if (type instanceof PsiPrimitiveType) {
final PsiPrimitiveType primitiveType = (PsiPrimitiveType) type;
if (PsiType.INT.equals(primitiveType)) {
return "I";
} else if (primitiveType.equals(PsiType.VOID)) {
return "V";
} else if (primitiveType.equals(PsiType.BOOLEAN)) {
return "Z";
} else if (primitiveType.equals(PsiType.BYTE)) {
return "B";
} else if (primitiveType.equals(PsiType.CHAR)) {
return "C";
} else if (primitiveType.equals(PsiType.SHORT)) {
return "S";
} else if (primitiveType.equals(PsiType.DOUBLE)) {
return "D";
} else if (primitiveType.equals(PsiType.FLOAT)) {
return "F";
} else /* if (primitiveType.equals(PsiType.LONG)) */ {
return "J";
}
} else if (type instanceof PsiArrayType) {
return "[" + getDescriptor(((PsiArrayType) type).getComponentType());
} else {
final PsiClassType classType = (PsiClassType) type;
final PsiClass aClass = classType.resolve();
if (aClass == null) {
return null;
}
final String qName = aClass.getQualifiedName();
if (qName == null) {
return null;
}
return "L" + StringUtil.replace(qName, ".", "/") + ";";
}
}
private static String asJavaInnerClassQName(final String byteCodeClassQName) {
return StringUtil.replaceChar(byteCodeClassQName, '$', '.');
}
}

View File

@@ -0,0 +1,64 @@
package com.intellij.compilerOutputIndex.api.fs;
import com.intellij.openapi.compiler.CompilerPaths;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Consumer;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.util.HashSet;
import java.util.Set;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public final class CompilerOutputFilesUtil {
private CompilerOutputFilesUtil() {}
public final static String CLASS_FILES_SUFFIX = ".class";
public static void iterateProjectClassFiles(@NotNull final Project project, @NotNull final Consumer<File> fileConsumer) {
for (final Module module : ModuleManager.getInstance(project).getModules()) {
iterateModuleClassFiles(module, fileConsumer);
}
}
public static void iterateModuleClassFiles(@NotNull final Module module, @NotNull final Consumer<File> fileConsumer) {
final VirtualFile moduleOutputDirectory = CompilerPaths.getModuleOutputDirectory(module, false);
if (moduleOutputDirectory == null) {
return;
}
final String canonicalPath = moduleOutputDirectory.getCanonicalPath();
if (canonicalPath == null) {
return;
}
final File root = new File(canonicalPath);
iterateClassFilesOverRoot(root, fileConsumer);
}
public static void iterateClassFilesOverRoot(@NotNull final File file, final Consumer<File> fileConsumer) {
iterateClassFilesOverRoot(file, fileConsumer, new HashSet<File>());
}
private static void iterateClassFilesOverRoot(@NotNull final File file, final Consumer<File> fileConsumer, final Set<File> visited) {
if (file.isDirectory()) {
final File[] files = file.listFiles();
if (files != null) {
for (final File childFile : files) {
if (visited.add(childFile)) {
iterateClassFilesOverRoot(childFile.getAbsoluteFile(), fileConsumer, visited);
}
}
}
}
else {
if (file.getName().endsWith(CLASS_FILES_SUFFIX)) {
fileConsumer.consume(file);
}
}
}
}

View File

@@ -0,0 +1,53 @@
package com.intellij.compilerOutputIndex.api.fs;
import com.intellij.openapi.project.Project;
import com.intellij.util.Consumer;
import java.io.File;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public interface FileVisitorService {
interface Visitor {
void visit(File file);
}
void visit(final Consumer<File> visitor);
class ProjectClassFiles implements FileVisitorService {
private final Project myProject;
public ProjectClassFiles(final Project project) {
myProject = project;
}
@Override
public void visit(final Consumer<File> visitor) {
CompilerOutputFilesUtil.iterateProjectClassFiles(myProject, visitor);
}
}
class DirectoryClassFiles implements FileVisitorService {
private final File myDir;
public DirectoryClassFiles(final File dir) {
if (!dir.isDirectory()) {
throw new IllegalArgumentException();
}
myDir = dir;
}
@Override
public void visit(final Consumer<File> visitor) {
//noinspection ConstantConditions
for (final File file : myDir.listFiles()) {
if (file.getName().endsWith(CompilerOutputFilesUtil.CLASS_FILES_SUFFIX)) {
visitor.consume(file);
}
}
}
}
}

View File

@@ -0,0 +1,145 @@
package com.intellij.compilerOutputIndex.api.indexer;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Factory;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.indexing.*;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.KeyDescriptor;
import com.intellij.util.io.PersistentHashMap;
import org.jetbrains.asm4.ClassReader;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import static com.intellij.util.indexing.IndexInfrastructure.*;
/**
* @author Dmitry Batkovich
*/
public abstract class CompilerOutputBaseIndex<K, V> {
public final static ExtensionPointName<CompilerOutputBaseIndex> EXTENSION_POINT_NAME =
ExtensionPointName.create("com.intellij.java.compilerOutputIndex");
private final static Logger LOG = Logger.getInstance(CompilerOutputBaseIndex.class);
private final KeyDescriptor<K> myKeyDescriptor;
private final DataExternalizer<V> myValueExternalizer;
protected volatile MapReduceIndex<K, V, ClassReader> myIndex;
private volatile Project myProject;
public CompilerOutputBaseIndex(final KeyDescriptor<K> keyDescriptor, final DataExternalizer<V> valueExternalizer) {
myKeyDescriptor = keyDescriptor;
myValueExternalizer = valueExternalizer;
}
public final boolean init(final Project project) {
myProject = project;
final MapReduceIndex<K, V, ClassReader> index;
final Ref<Boolean> rewriteIndex = new Ref<Boolean>(false);
try {
final ID<K, V> indexId = getIndexId();
if (!IndexInfrastructure.getIndexRootDir(indexId).exists()) {
rewriteIndex.set(true);
}
final File storageFile = IndexInfrastructure.getStorageFile(indexId);
final MapIndexStorage<K, V> indexStorage = new MapIndexStorage<K, V>(storageFile, myKeyDescriptor, myValueExternalizer, 1024);
index = new MapReduceIndex<K, V, ClassReader>(indexId, getIndexer(), indexStorage);
index.setInputIdToDataKeysIndex(new Factory<PersistentHashMap<Integer, Collection<K>>>() {
@Override
public PersistentHashMap<Integer, Collection<K>> create() {
Exception failCause = null;
for (int attempts = 0; attempts < 2; attempts++) {
try {
return FileBasedIndexImpl.createIdToDataKeysIndex(indexId, myKeyDescriptor, new MemoryIndexStorage<K, V>(indexStorage));
}
catch (IOException e) {
failCause = e;
FileUtil.delete(IndexInfrastructure.getInputIndexStorageFile(getIndexId()));
rewriteIndex.set(true);
}
}
throw new RuntimeException("couldn't create index", failCause);
}
});
final File versionFile = getVersionFile(indexId);
if (versionFile.exists()) {
if (versionDiffers(versionFile, getVersion())) {
rewriteVersion(versionFile, getVersion());
rewriteIndex.set(true);
try {
LOG.info("clearing index for updating index version");
index.clear();
}
catch (StorageException e) {
LOG.error("couldn't clear index for reinitializing");
throw new RuntimeException(e);
}
}
}
else if (versionFile.createNewFile()) {
rewriteVersion(versionFile, getVersion());
rewriteIndex.set(true);
}
else {
LOG.error(String.format("problems while access to index version file to index %s ", indexId));
}
}
catch (IOException e) {
LOG.error("couldn't initialize index");
throw new RuntimeException(e);
}
myIndex = index;
return rewriteIndex.get();
}
protected abstract ID<K, V> getIndexId();
protected abstract int getVersion();
protected abstract DataIndexer<K, V, ClassReader> getIndexer();
public final void projectClosed() {
if (myIndex != null) {
try {
myIndex.flush();
}
catch (StorageException ignored) {
}
myIndex.dispose();
}
}
public void update(final int id, final ClassReader classReader) {
try {
myIndex.update(id, classReader);
}
catch (StorageException e) {
throw new RuntimeException(e);
}
}
public void clear() {
try {
myIndex.clear();
}
catch (StorageException e) {
throw new RuntimeException(e);
}
}
protected final ID<K, V> generateIndexId(final String indexName) {
return CompilerOutputIndexUtil.generateIndexId(indexName, myProject);
}
protected final ID<K, V> generateIndexId(final Class aClass) {
final String className = StringUtil.getShortName(aClass);
return generateIndexId(StringUtil.trimEnd(className, "Index"));
}
}

View File

@@ -0,0 +1,21 @@
package com.intellij.compilerOutputIndex.api.indexer;
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature;
import com.intellij.openapi.project.Project;
import com.intellij.util.indexing.ID;
/**
* @author Dmitry Batkovich
*/
public final class CompilerOutputIndexUtil {
private CompilerOutputIndexUtil() {}
public static <K, V> ID<K, V> generateIndexId(final String indexName, final Project project) {
return ID.create(String.format("compilerOutputIndex.%s.%d", indexName, Math.abs(project.getBasePath().hashCode())));
}
public static boolean isSetterOrConstructorMethodName(final String methodName) {
return MethodIncompleteSignature.CONSTRUCTOR_METHOD_NAME.equals(methodName) || methodName.startsWith("set");
}
}

View File

@@ -0,0 +1,359 @@
package com.intellij.compilerOutputIndex.api.indexer;
import com.intellij.compilerOutputIndex.api.fs.CompilerOutputFilesUtil;
import com.intellij.compilerOutputIndex.api.fs.FileVisitorService;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.compiler.CompileContext;
import com.intellij.openapi.compiler.CompileTask;
import com.intellij.openapi.compiler.CompilerManager;
import com.intellij.openapi.components.AbstractProjectComponent;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.registry.RegistryValue;
import com.intellij.openapi.util.registry.RegistryValueListener;
import com.intellij.util.Consumer;
import com.intellij.util.indexing.ID;
import com.intellij.util.indexing.IndexInfrastructure;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.EnumeratorStringDescriptor;
import com.intellij.util.io.PersistentEnumeratorDelegate;
import com.intellij.util.io.PersistentHashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import org.jetbrains.asm4.ClassReader;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author Dmitry Batkovich
*/
public class CompilerOutputIndexer extends AbstractProjectComponent {
private final static Logger LOG = Logger.getInstance(CompilerOutputIndexer.class);
public final static String REGISTRY_KEY = "completion.enable.relevant.method.chain.suggestions";
public final static String TITLE = "Compiler output indexer in progress...";
private volatile CompilerOutputBaseIndex[] myIndexes;
private volatile Map<String, CompilerOutputBaseIndex> myIndexTypeQNameToIndex;
private volatile PersistentHashMap<String, Long> myFileTimestampsIndex;
private volatile PersistentEnumeratorDelegate<String> myFileEnumerator;
private volatile boolean myInitialized = false;
private final Lock myLock = new ReentrantLock();
private final AtomicBoolean myInProgress = new AtomicBoolean(false);
private AtomicBoolean myEnabled = new AtomicBoolean(false);
public static CompilerOutputIndexer getInstance(final Project project) {
return project.getComponent(CompilerOutputIndexer.class);
}
protected CompilerOutputIndexer(final Project project) {
super(project);
}
public boolean isEnabled() {
return myEnabled.get();
}
private ID<String, Long> getFileTimestampsIndexId() {
return CompilerOutputIndexUtil.generateIndexId("ProjectCompilerOutputClassFilesTimestamps", myProject);
}
@Override
public final void projectOpened() {
Registry.get(REGISTRY_KEY).addListener(new RegistryValueListener.Adapter() {
@Override
public void afterValueChanged(final RegistryValue value) {
myEnabled.set(value.asBoolean());
if (myEnabled.get()) {
doEnable();
}
}
}, ApplicationManager.getApplication());
myEnabled = new AtomicBoolean(Registry.is(REGISTRY_KEY) || ApplicationManager.getApplication().isUnitTestMode());
if (myEnabled.get()) {
doEnable();
}
}
private void doEnable() {
if (!myInitialized) {
myIndexes = Extensions.getExtensions(CompilerOutputBaseIndex.EXTENSION_POINT_NAME, myProject);
myIndexTypeQNameToIndex = new HashMap<String, CompilerOutputBaseIndex>();
boolean needReindex = false;
for (final CompilerOutputBaseIndex index : myIndexes) {
if (index.init(myProject)) {
needReindex = true;
}
myIndexTypeQNameToIndex.put(index.getClass().getCanonicalName(), index);
}
initTimestampIndex(needReindex);
try {
myFileEnumerator = new PersistentEnumeratorDelegate<String>(
IndexInfrastructure.getStorageFile(CompilerOutputIndexUtil.generateIndexId("compilerOutputIndexFileId.enum", myProject)),
new EnumeratorStringDescriptor(), 2048);
}
catch (IOException e) {
throw new RuntimeException(e);
}
CompilerManager.getInstance(myProject).addAfterTask(new CompileTask() {
@Override
public boolean execute(final CompileContext context) {
if (myEnabled.get() && myInProgress.compareAndSet(false, true)) {
myLock.lock();
try {
context.getProgressIndicator().setText("Compiler output indexing in progress");
for (final Module module : context.getCompileScope().getAffectedModules()) {
CompilerOutputFilesUtil.iterateModuleClassFiles(module, new Consumer<File>() {
@Override
public void consume(final File file) {
try {
doIndexing(file, context.getProgressIndicator());
}
catch (ProcessCanceledException e0) {
throw e0;
}
catch (RuntimeException e) {
LOG.error(e);
}
}
});
}
}
finally {
myLock.unlock();
myInProgress.set(false);
}
}
return true;
}
});
if (needReindex) {
reindexAllProjectInBackground();
}
myInitialized = true;
}
}
private void initTimestampIndex(final boolean needReindex) {
if (needReindex) {
FileUtil.delete(IndexInfrastructure.getIndexRootDir(getFileTimestampsIndexId()));
}
for (int attempts = 0; attempts < 2; attempts++) {
try {
myFileTimestampsIndex = new PersistentHashMap<String, Long>(IndexInfrastructure.getStorageFile(getFileTimestampsIndexId()),
new EnumeratorStringDescriptor(), new DataExternalizer<Long>() {
@Override
public void save(final DataOutput out, final Long value) throws IOException {
out.writeLong(value);
}
@Override
public Long read(final DataInput in) throws IOException {
return in.readLong();
}
});
}
catch (IOException e) {
FileUtil.delete(IndexInfrastructure.getIndexRootDir(getFileTimestampsIndexId()));
}
if (myFileTimestampsIndex != null) {
return;
}
}
throw new RuntimeException("Timestamps index not initialized");
}
public void reindex(final FileVisitorService visitorService, @NotNull final ProgressIndicator indicator) {
myLock.lock();
try {
indicator.setText(TITLE);
visitorService.visit(new Consumer<File>() {
@Override
public void consume(final File file) {
try {
doIndexing(file, indicator);
}
catch (ProcessCanceledException e0) {
throw e0;
}
catch (RuntimeException e) {
LOG.error(e);
}
}
});
}
finally {
myLock.unlock();
}
}
public void reindexAllProjectInBackground() {
if (myInProgress.compareAndSet(false, true)) {
ProgressManager.getInstance().run(new Task.Backgroundable(myProject, TITLE) {
@Override
public void onCancel() {
myIndexTypeQNameToIndex.clear();
myInProgress.set(false);
}
@Override
public void onSuccess() {
myInProgress.set(false);
}
@Override
public void run(@NotNull final ProgressIndicator indicator) {
reindexAllProject(indicator);
}
});
}
}
public void reindexAllProject(@NotNull final ProgressIndicator indicator) {
reindex(new FileVisitorService.ProjectClassFiles(myProject), indicator);
}
@SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
private void doIndexing(@NotNull final File file, @NotNull final ProgressIndicator indicator) {
final String filePath;
try {
filePath = file.getCanonicalPath();
}
catch (IOException e) {
LOG.error(e);
return;
}
indicator.setText2(filePath);
final Long timestamp = getTimestamp(filePath);
ProgressManager.checkCanceled();
final long currentTimeStamp = file.lastModified();
if (timestamp == null || timestamp != currentTimeStamp) {
putTimestamp(filePath, currentTimeStamp);
final ClassReader reader;
InputStream is = null;
try {
is = new FileInputStream(file);
reader = new ClassReader(is);
}
catch (IOException e) {
removeTimestamp(filePath);
return;
}
finally {
if (is != null) {
try {
is.close();
}
catch (IOException ignored) {
}
}
}
try {
final int id = myFileEnumerator.enumerate(filePath);
for (final CompilerOutputBaseIndex index : myIndexes) {
index.update(id, reader);
}
}
catch (RuntimeException e) {
LOG.error(String.format("can't index file: %s", file.getAbsolutePath()), e);
}
catch (IOException e) {
LOG.error(String.format("can't index file: %s", file.getAbsolutePath()), e);
}
}
}
public void clear() {
try {
myFileTimestampsIndex.close();
}
catch (IOException e) {
throw new RuntimeException(e);
}
initTimestampIndex(true);
for (final CompilerOutputBaseIndex index : myIndexes) {
index.clear();
}
}
private void removeTimestamp(final String fileId) {
try {
myFileTimestampsIndex.remove(fileId);
}
catch (IOException e) {
LOG.error(e);
}
}
@Nullable
private Long getTimestamp(final String fileName) {
try {
return myFileTimestampsIndex.get(fileName);
}
catch (IOException e) {
LOG.error(e);
return 0L;
}
}
private void putTimestamp(final String fileName, final long timestamp) {
try {
myFileTimestampsIndex.put(fileName, timestamp);
}
catch (IOException e) {
LOG.error(e);
}
}
@Override
public void projectClosed() {
if (myInitialized) {
for (final CompilerOutputBaseIndex index : myIndexes) {
index.projectClosed();
}
try {
myFileTimestampsIndex.close();
myFileEnumerator.close();
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@TestOnly
public void removeIndexes() {
for (final CompilerOutputBaseIndex index : myIndexes) {
FileUtil.delete(IndexInfrastructure.getIndexRootDir(index.getIndexId()));
}
FileUtil.delete(IndexInfrastructure.getIndexRootDir(getFileTimestampsIndexId()));
}
@SuppressWarnings("unchecked")
public <T extends CompilerOutputBaseIndex> T getIndex(final Class<T> tClass) {
final CompilerOutputBaseIndex index = myIndexTypeQNameToIndex.get(tClass.getCanonicalName());
if (index == null) {
throw new RuntimeException(String.format("index class with name %s not found", tClass.getName()));
}
return (T)index;
}
}

View File

@@ -0,0 +1,116 @@
package com.intellij.compilerOutputIndex.impl;
import com.intellij.codeInsight.completion.methodChains.ChainCompletionStringUtil;
import com.intellij.compilerOutputIndex.api.fs.AsmUtil;
import org.jetbrains.asm4.ClassReader;
import org.jetbrains.asm4.ClassVisitor;
import org.jetbrains.asm4.MethodVisitor;
import org.jetbrains.asm4.Opcodes;
import java.util.ArrayList;
import java.util.List;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public class ClassFileData {
private final List<MethodData> myMethodDatas;
public ClassFileData(final ClassReader classReader) {
this(classReader, true);
}
public ClassFileData(final ClassReader classReader, final boolean checkForPrimitiveReturn) {
myMethodDatas = new ArrayList<MethodData>();
classReader.accept(new ClassVisitor(Opcodes.ASM4) {
@Override
public MethodVisitor visitMethod(final int access,
final String name,
final String desc,
final String signature,
final String[] exceptions) {
final MethodDataAccumulator methodDataAccumulator = new MethodDataAccumulator(checkForPrimitiveReturn);
myMethodDatas.add(methodDataAccumulator.getMethodData());
return methodDataAccumulator;
}
}, Opcodes.ASM4);
}
public List<MethodData> getMethodDatas() {
return myMethodDatas;
}
public static class MethodData {
private final List<MethodInsnSignature> myMethodInsnSignatures = new ArrayList<MethodInsnSignature>();
private void addSign(final MethodInsnSignature signature) {
myMethodInsnSignatures.add(signature);
}
public List<MethodInsnSignature> getMethodInsnSignatures() {
return myMethodInsnSignatures;
}
}
private static class MethodDataAccumulator extends MethodVisitor {
private final MethodData myMethodData = new MethodData();
private final boolean myCheckForPrimitiveReturn;
public MethodDataAccumulator(final boolean checkForPrimitiveReturn) {
super(Opcodes.ASM4);
myCheckForPrimitiveReturn = checkForPrimitiveReturn;
}
private MethodData getMethodData() {
return myMethodData;
}
@Override
public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc) {
if (MethodIncompleteSignature.CONSTRUCTOR_METHOD_NAME.equals(name)) {
return;
}
final String ownerClassName = AsmUtil.getQualifiedClassName(owner);
if (ChainCompletionStringUtil.isPrimitiveOrArrayOfPrimitives(ownerClassName)) {
return;
}
if (myCheckForPrimitiveReturn) {
final String returnType = AsmUtil.getReturnType(desc);
if (ChainCompletionStringUtil.isPrimitiveOrArrayOfPrimitives(returnType)) {
return;
}
}
myMethodData.addSign(new MethodInsnSignature(opcode, owner, name, desc));
}
}
public static class MethodInsnSignature {
private final int myOpcode;
private final String myOwner;
private final String myName;
private final String myDesc;
private MethodInsnSignature(final int opcode, final String owner, final String name, final String desc) {
myOpcode = opcode;
myOwner = owner;
myName = name;
myDesc = desc;
}
public int getOpcode() {
return myOpcode;
}
public String getOwner() {
return myOwner;
}
public String getName() {
return myName;
}
public String getDesc() {
return myDesc;
}
}
}

View File

@@ -0,0 +1,47 @@
package com.intellij.compilerOutputIndex.impl;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset;
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputBaseIndex;
import com.intellij.util.indexing.StorageException;
import com.intellij.util.indexing.ValueContainer;
import com.intellij.util.io.KeyDescriptor;
import java.util.TreeSet;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public abstract class CompilerOutputBaseGramsIndex<K> extends CompilerOutputBaseIndex<K, Multiset<MethodIncompleteSignature>> {
protected CompilerOutputBaseGramsIndex(final KeyDescriptor<K> keyDescriptor) {
super(keyDescriptor, new GuavaHashMultiSetExternalizer<MethodIncompleteSignature>(MethodIncompleteSignature.createKeyDescriptor()));
}
public TreeSet<UsageIndexValue> getValues(final K key) {
try {
final ValueContainer<Multiset<MethodIncompleteSignature>> valueContainer = myIndex.getData(key);
final Multiset<MethodIncompleteSignature> rawValues = HashMultiset.create();
valueContainer.forEach(new ValueContainer.ContainerAction<Multiset<MethodIncompleteSignature>>() {
@Override
public boolean perform(final int id, final Multiset<MethodIncompleteSignature> values) {
for (final Multiset.Entry<MethodIncompleteSignature> entry : values.entrySet()) {
rawValues.add(entry.getElement(), entry.getCount());
}
return true;
}
});
return rawValuesToValues(rawValues);
} catch (StorageException e) {
throw new RuntimeException();
}
}
private static TreeSet<UsageIndexValue> rawValuesToValues(final Multiset<MethodIncompleteSignature> rawValues) {
final TreeSet<UsageIndexValue> values = new TreeSet<UsageIndexValue>();
for (final Multiset.Entry<MethodIncompleteSignature> entry : rawValues.entrySet()) {
values.add(new UsageIndexValue(entry.getElement(), entry.getCount()));
}
return values;
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed 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.intellij.compilerOutputIndex.impl;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset;
import com.intellij.util.io.DataExternalizer;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.Set;
/**
* @author Dmitry Batkovich
*/
public class GuavaHashMultiSetExternalizer<K> implements DataExternalizer<Multiset<K>> {
private final DataExternalizer<K> myKeyDataExternalizer;
public GuavaHashMultiSetExternalizer(final DataExternalizer<K> keyDataExternalizer) {
myKeyDataExternalizer = keyDataExternalizer;
}
@Override
public void save(final DataOutput out, final Multiset<K> multiset) throws IOException {
final Set<Multiset.Entry<K>> entries = multiset.entrySet();
out.writeInt(entries.size());
for (final Multiset.Entry<K> entry : entries) {
myKeyDataExternalizer.save(out, entry.getElement());
out.writeInt(entry.getCount());
}
}
@Override
public Multiset<K> read(final DataInput in) throws IOException {
final int size = in.readInt();
final Multiset<K> multiset = HashMultiset.create(size);
for (int i = 0; i < size; i++) {
multiset.add(myKeyDataExternalizer.read(in), in.readInt());
}
return multiset;
}
}

View File

@@ -0,0 +1,183 @@
package com.intellij.compilerOutputIndex.impl;
import com.intellij.openapi.util.Condition;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.io.EnumeratorStringDescriptor;
import com.intellij.util.io.KeyDescriptor;
import org.jetbrains.annotations.NotNull;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public class MethodIncompleteSignature {
public static final String CONSTRUCTOR_METHOD_NAME = "<init>";
@NotNull
private final String myOwner;
@NotNull
private final String myReturnType;
@NotNull
private final String myName;
private final boolean myStatic;
public MethodIncompleteSignature(@NotNull final String owner,
@NotNull final String returnType,
@NotNull final String name,
final boolean aStatic) {
myOwner = owner;
myReturnType = returnType;
myName = name;
myStatic = aStatic;
}
public static MethodIncompleteSignature constructor(@NotNull final String className) {
return new MethodIncompleteSignature(className, className, CONSTRUCTOR_METHOD_NAME, true);
}
@NotNull
public String getOwner() {
return myOwner;
}
@NotNull
public String getReturnType() {
return myReturnType;
}
@NotNull
public String getName() {
return myName;
}
public boolean isStatic() {
return myStatic;
}
@NotNull
public PsiMethod[] resolveNotDeprecated(final JavaPsiFacade javaPsiFacade, final GlobalSearchScope scope) {
return notDeprecated(resolve(javaPsiFacade, scope));
}
@NotNull
public PsiMethod[] resolve(final JavaPsiFacade javaPsiFacade, final GlobalSearchScope scope) {
if (CONSTRUCTOR_METHOD_NAME.equals(getName())) {
return PsiMethod.EMPTY_ARRAY;
}
final PsiClass aClass = javaPsiFacade.findClass(getOwner(), scope);
if (aClass == null) {
return PsiMethod.EMPTY_ARRAY;
}
final PsiMethod[] methods = aClass.findMethodsByName(getName(), true);
final List<PsiMethod> filtered = new ArrayList<PsiMethod>(methods.length);
for (final PsiMethod method : methods) {
if (method.hasModifierProperty(PsiModifier.STATIC) == isStatic()) {
final PsiType returnType = method.getReturnType();
if (returnType != null && returnType.equalsToText(getReturnType())) {
filtered.add(method);
}
}
}
return filtered.toArray(new PsiMethod[filtered.size()]);
}
public static KeyDescriptor<MethodIncompleteSignature> createKeyDescriptor() {
final EnumeratorStringDescriptor stringDescriptor = new EnumeratorStringDescriptor();
return new KeyDescriptor<MethodIncompleteSignature>() {
@Override
public void save(final DataOutput out, final MethodIncompleteSignature value) throws IOException {
stringDescriptor.save(out, value.getOwner());
stringDescriptor.save(out, value.getReturnType());
stringDescriptor.save(out, value.getName());
out.writeBoolean(value.isStatic());
}
@Override
public MethodIncompleteSignature read(final DataInput in) throws IOException {
return new MethodIncompleteSignature(stringDescriptor.read(in), stringDescriptor.read(in), stringDescriptor.read(in),
in.readBoolean());
}
@Override
public int getHashCode(final MethodIncompleteSignature value) {
return value.hashCode();
}
@Override
public boolean isEqual(final MethodIncompleteSignature val1, final MethodIncompleteSignature val2) {
return val1.equals(val2);
}
};
}
@NotNull
private static PsiMethod[] notDeprecated(@NotNull final PsiMethod[] methods) {
final List<PsiMethod> filtered = ContainerUtil.filter(methods, NOT_DEPRECATED_CONDITION);
return filtered.toArray(new PsiMethod[filtered.size()]);
}
private final static Condition<PsiMethod> NOT_DEPRECATED_CONDITION = new Condition<PsiMethod>() {
@Override
public boolean value(final PsiMethod method) {
return !method.isDeprecated();
}
};
public final static Comparator<MethodIncompleteSignature> COMPARATOR = new Comparator<MethodIncompleteSignature>() {
@Override
public int compare(final MethodIncompleteSignature o1, final MethodIncompleteSignature o2) {
int sub = o1.getOwner().compareTo(o2.getOwner());
if (sub != 0) {
return sub;
}
sub = o1.getName().compareTo(o2.getName());
if (sub != 0) {
return sub;
}
sub = o1.getReturnType().compareTo(o2.getReturnType());
if (sub != 0) {
return sub;
}
if (o1.isStatic() && !o2.isStatic()) {
return 1;
}
if (o2.isStatic() && !o1.isStatic()) {
return -1;
}
return 0;
}
};
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final MethodIncompleteSignature that = (MethodIncompleteSignature)o;
if (myStatic != that.myStatic) return false;
if (!myName.equals(that.myName)) return false;
if (!myOwner.equals(that.myOwner)) return false;
if (!myReturnType.equals(that.myReturnType)) return false;
return true;
}
@Override
public int hashCode() {
int result = myOwner.hashCode();
result = 31 * result + myReturnType.hashCode();
result = 31 * result + myName.hashCode();
result = 31 * result + (myStatic ? 1 : 0);
return result;
}
}

View File

@@ -0,0 +1,44 @@
package com.intellij.compilerOutputIndex.impl;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public class MethodIncompleteSignatureChain {
private final List<MethodIncompleteSignature> myMethodIncompleteSignatures;
public MethodIncompleteSignatureChain(final List<MethodIncompleteSignature> methodIncompleteSignatures) {
myMethodIncompleteSignatures = methodIncompleteSignatures;
}
public List<MethodIncompleteSignature> list() {
return myMethodIncompleteSignatures;
}
public boolean isEmpty() {
return myMethodIncompleteSignatures.isEmpty();
}
@Nullable
public MethodIncompleteSignature getFirstInvocation() {
final int size = myMethodIncompleteSignatures.size();
return size == 0 ? null : myMethodIncompleteSignatures.get(0);
}
@Nullable
public MethodIncompleteSignature getLastInvocation() {
final int size = myMethodIncompleteSignatures.size();
return size == 0 ? null : myMethodIncompleteSignatures.get(size -1);
}
public int size() {
return myMethodIncompleteSignatures.size();
}
public MethodIncompleteSignature get(final int index) {
return myMethodIncompleteSignatures.get(index);
}
}

View File

@@ -0,0 +1,91 @@
package com.intellij.compilerOutputIndex.impl;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset;
import com.intellij.compilerOutputIndex.api.fs.AsmUtil;
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputIndexer;
import com.intellij.openapi.project.Project;
import com.intellij.util.indexing.DataIndexer;
import com.intellij.util.indexing.ID;
import com.intellij.util.indexing.StorageException;
import com.intellij.util.io.EnumeratorStringDescriptor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.asm4.ClassReader;
import org.jetbrains.asm4.Opcodes;
import java.util.HashMap;
import java.util.Map;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public class MethodsUsageIndex extends CompilerOutputBaseGramsIndex<String> {
public static MethodsUsageIndex getInstance(final Project project) {
return CompilerOutputIndexer.getInstance(project).getIndex(MethodsUsageIndex.class);
}
public MethodsUsageIndex() {
super(new EnumeratorStringDescriptor());
}
@Override
protected DataIndexer<String, Multiset<MethodIncompleteSignature>, ClassReader> getIndexer() {
return new DataIndexer<String, Multiset<MethodIncompleteSignature>, ClassReader>() {
@NotNull
@Override
public Map<String, Multiset<MethodIncompleteSignature>> map(final ClassReader inputData) {
final Map<String, Multiset<MethodIncompleteSignature>> map = new HashMap<String, Multiset<MethodIncompleteSignature>>();
for (final ClassFileData.MethodData data : new ClassFileData(inputData).getMethodDatas()) {
for (final ClassFileData.MethodInsnSignature ms : data.getMethodInsnSignatures()) {
final String ownerClassName = AsmUtil.getQualifiedClassName(ms.getOwner());
final String returnType = AsmUtil.getReturnType(ms.getDesc());
if (MethodIncompleteSignature.CONSTRUCTOR_METHOD_NAME.equals(ms.getName())) {
addToIndex(map, ownerClassName, MethodIncompleteSignature.constructor(ownerClassName));
}
else {
final boolean isStatic = ms.getOpcode() == Opcodes.INVOKESTATIC;
if (!ownerClassName.equals(returnType) || isStatic) {
addToIndex(map, returnType, new MethodIncompleteSignature(ownerClassName, returnType, ms.getName(), isStatic));
}
}
}
}
return map;
}
};
}
public void clear() {
try {
myIndex.clear();
}
catch (StorageException e) {
throw new RuntimeException(e);
}
}
@Override
protected ID<String, Multiset<MethodIncompleteSignature>> getIndexId() {
return generateIndexId(MethodsUsageIndex.class);
}
@Override
protected int getVersion() {
return 0;
}
private static void addToIndex(final Map<String, Multiset<MethodIncompleteSignature>> map,
final String key,
final MethodIncompleteSignature mi) {
Multiset<MethodIncompleteSignature> occurrences = map.get(key);
if (occurrences == null) {
occurrences = HashMultiset.create();
map.put(key, occurrences);
}
occurrences.add(mi);
}
}

View File

@@ -0,0 +1,69 @@
package com.intellij.compilerOutputIndex.impl;
import com.intellij.util.io.DataExternalizer;
import org.jetbrains.annotations.NotNull;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
/**
* @author Dmitry Batkovich
*/
public class UsageIndexValue implements Comparable<UsageIndexValue> {
private final int myOccurrences;
private final MethodIncompleteSignature myMethodIncompleteSignature;
public UsageIndexValue(final MethodIncompleteSignature signature, final int occurrences) {
myOccurrences = occurrences;
myMethodIncompleteSignature = signature;
}
public int getOccurrences() {
return myOccurrences;
}
public MethodIncompleteSignature getMethodIncompleteSignature() {
return myMethodIncompleteSignature;
}
public static DataExternalizer<UsageIndexValue> createDataExternalizer() {
final DataExternalizer<MethodIncompleteSignature> methodInvocationDataExternalizer = MethodIncompleteSignature.createKeyDescriptor();
return new DataExternalizer<UsageIndexValue>() {
@Override
public void save(final DataOutput out, final UsageIndexValue value) throws IOException {
methodInvocationDataExternalizer.save(out, value.myMethodIncompleteSignature);
out.writeInt(value.myOccurrences);
}
@Override
public UsageIndexValue read(final DataInput in) throws IOException {
return new UsageIndexValue(methodInvocationDataExternalizer.read(in), in.readInt());
}
};
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final UsageIndexValue that = (UsageIndexValue)o;
return myOccurrences == that.myOccurrences && myMethodIncompleteSignature.equals(that.myMethodIncompleteSignature);
}
@Override
public int hashCode() {
int result = myOccurrences;
result = 31 * result + myMethodIncompleteSignature.hashCode();
return result;
}
@Override
public int compareTo(@NotNull final UsageIndexValue that) {
final int sub = -myOccurrences + that.myOccurrences;
if (sub != 0) return sub;
return MethodIncompleteSignature.COMPARATOR.compare(myMethodIncompleteSignature, that.myMethodIncompleteSignature);
}
}

View File

@@ -0,0 +1,22 @@
package com.intellij.compilerOutputIndex.impl.bigram;
import com.intellij.openapi.util.Pair;
import org.jetbrains.annotations.NotNull;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public class Bigram<E> extends Pair<E, E> {
public Bigram(@NotNull final E first, @NotNull final E second) {
super(first, second);
}
public Bigram<E> swap() {
return new Bigram<E>(second, first);
}
@Override
public String toString() {
return String.format("%s - %s", first, second);
}
}

View File

@@ -0,0 +1,86 @@
package com.intellij.compilerOutputIndex.impl.bigram;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset;
import com.intellij.compilerOutputIndex.api.fs.AsmUtil;
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputIndexer;
import com.intellij.compilerOutputIndex.impl.ClassFileData;
import com.intellij.compilerOutputIndex.impl.CompilerOutputBaseGramsIndex;
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature;
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignatureChain;
import com.intellij.openapi.project.Project;
import com.intellij.util.SmartList;
import com.intellij.util.indexing.DataIndexer;
import com.intellij.util.indexing.ID;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.asm4.ClassReader;
import org.jetbrains.asm4.Opcodes;
import java.util.*;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public class BigramMethodsUsageIndex extends CompilerOutputBaseGramsIndex<MethodIncompleteSignature> {
public static BigramMethodsUsageIndex getInstance(final Project project) {
return CompilerOutputIndexer.getInstance(project).getIndex(BigramMethodsUsageIndex.class);
}
public BigramMethodsUsageIndex() {
super(MethodIncompleteSignature.createKeyDescriptor());
}
@Override
protected ID<MethodIncompleteSignature, Multiset<MethodIncompleteSignature>> getIndexId() {
return generateIndexId(BigramMethodsUsageIndex.class);
}
@Override
protected int getVersion() {
return 0;
}
@Override
protected DataIndexer<MethodIncompleteSignature, Multiset<MethodIncompleteSignature>, ClassReader> getIndexer() {
//
// not fair way, but works fast
//
return new DataIndexer<MethodIncompleteSignature,Multiset<MethodIncompleteSignature>,ClassReader>() {
@NotNull
@Override
public Map<MethodIncompleteSignature, Multiset<MethodIncompleteSignature>> map(final ClassReader inputData) {
final Map<MethodIncompleteSignature, Multiset<MethodIncompleteSignature>> map =
new HashMap<MethodIncompleteSignature, Multiset<MethodIncompleteSignature>>();
for (final ClassFileData.MethodData data : new ClassFileData(inputData).getMethodDatas()) {
final SimpleBigramsExtractor extractor = new SimpleBigramsExtractor(new SimpleBigramsExtractor.BigramMethodIncompleteSignatureProcessor() {
@Override
public void process(final Bigram<MethodIncompleteSignature> bigram) {
final MethodIncompleteSignature secondGram = bigram.getSecond();
Multiset<MethodIncompleteSignature> occurrences = map.get(secondGram);
if (occurrences == null) {
occurrences = HashMultiset.create();
map.put(secondGram, occurrences);
}
occurrences.add(bigram.getFirst());
}
});
for (final ClassFileData.MethodInsnSignature ms : data.getMethodInsnSignatures()) {
final List<MethodIncompleteSignature> methodInvocations = new SmartList<MethodIncompleteSignature>();
final String ownerClassName = AsmUtil.getQualifiedClassName(ms.getOwner());
final String returnType = AsmUtil.getReturnType(ms.getDesc());
if (ms.getName().equals(MethodIncompleteSignature.CONSTRUCTOR_METHOD_NAME)) {
methodInvocations.add(MethodIncompleteSignature.constructor(ownerClassName));
}
else {
methodInvocations.add(new MethodIncompleteSignature(ownerClassName, returnType, ms.getName(), ms.getOpcode() == Opcodes.INVOKESTATIC));
}
extractor.addChain(new MethodIncompleteSignatureChain(methodInvocations));
}
}
return map;
}
};
}
}

View File

@@ -0,0 +1,55 @@
package com.intellij.compilerOutputIndex.impl.bigram;
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature;
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignatureChain;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* @author Dmitry Batkovich
*/
class SimpleBigramsExtractor {
private final Map<String, MethodIncompleteSignature> myHolder = new HashMap<String, MethodIncompleteSignature>();
private final BigramMethodIncompleteSignatureProcessor myProcessor;
public SimpleBigramsExtractor(final BigramMethodIncompleteSignatureProcessor processor) {
myProcessor = processor;
}
public void addChain(final MethodIncompleteSignatureChain chain) {
if (chain.isEmpty()) {
return;
}
final MethodIncompleteSignature firstInvocation = chain.getFirstInvocation();
assert firstInvocation != null;
final MethodIncompleteSignature head = firstInvocation.isStatic() ? null : myHolder.get(firstInvocation.getOwner());
for (final Bigram<MethodIncompleteSignature> bigram : toBigrams(head, chain)) {
myProcessor.process(bigram);
}
final MethodIncompleteSignature lastInvocation = chain.getLastInvocation();
assert lastInvocation != null;
myHolder.put(lastInvocation.getReturnType(), lastInvocation);
}
private static Collection<Bigram<MethodIncompleteSignature>> toBigrams(final @Nullable MethodIncompleteSignature head,
final @NotNull MethodIncompleteSignatureChain chain) {
MethodIncompleteSignature currentLast = null;
if (head != null) {
currentLast = head;
}
final List<Bigram<MethodIncompleteSignature>> bigrams = new ArrayList<Bigram<MethodIncompleteSignature>>(chain.size());
for (final MethodIncompleteSignature current : chain.list()) {
if (currentLast != null) {
bigrams.add(new Bigram<MethodIncompleteSignature>(currentLast, current));
}
currentLast = current;
}
return bigrams;
}
public interface BigramMethodIncompleteSignatureProcessor {
void process(Bigram<MethodIncompleteSignature> bigram);
}
}

View File

@@ -0,0 +1,71 @@
package com.intellij.compilerOutputIndex.impl.callingLocation;
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.KeyDescriptor;
import org.jetbrains.annotations.NotNull;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public class CallingLocation {
@NotNull
private final MethodIncompleteSignature myMethodIncompleteSignature;
@NotNull
private final VariableType myVariableType;
public CallingLocation(@NotNull final MethodIncompleteSignature methodIncompleteSignature, @NotNull final VariableType variableType) {
myMethodIncompleteSignature = methodIncompleteSignature;
myVariableType = variableType;
}
@NotNull
public MethodIncompleteSignature getMethodIncompleteSignature() {
return myMethodIncompleteSignature;
}
@NotNull
public VariableType getVariableType() {
return myVariableType;
}
public static DataExternalizer<CallingLocation> createDataExternalizer() {
final KeyDescriptor<MethodIncompleteSignature> methodIncompleteSignatureKeyDescriptor = MethodIncompleteSignature.createKeyDescriptor();
return new DataExternalizer<CallingLocation>() {
@Override
public void save(final DataOutput out, final CallingLocation value) throws IOException {
methodIncompleteSignatureKeyDescriptor.save(out, value.getMethodIncompleteSignature());
VariableType.KEY_DESCRIPTOR.save(out, value.getVariableType());
}
@Override
public CallingLocation read(final DataInput in) throws IOException {
return new CallingLocation(methodIncompleteSignatureKeyDescriptor.read(in), VariableType.KEY_DESCRIPTOR.read(in));
}
};
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final CallingLocation that = (CallingLocation)o;
if (!myMethodIncompleteSignature.equals(that.myMethodIncompleteSignature)) return false;
if (myVariableType != that.myVariableType) return false;
return true;
}
@Override
public int hashCode() {
int result = myMethodIncompleteSignature.hashCode();
result = 31 * result + myVariableType.hashCode();
return result;
}
}

View File

@@ -0,0 +1,157 @@
package com.intellij.compilerOutputIndex.impl.callingLocation;
import com.intellij.codeInsight.completion.methodChains.ChainCompletionStringUtil;
import com.intellij.compilerOutputIndex.api.fs.AsmUtil;
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.asm4.*;
import org.jetbrains.asm4.commons.AnalyzerAdapter;
import org.jetbrains.asm4.commons.JSRInlinerAdapter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public class MethodCallingLocationExtractor {
private MethodCallingLocationExtractor() {
}
public static Map<MethodNameAndQualifier, List<CallingLocation>> extract(final ClassReader classReader) {
final MyClassVisitor classVisitor = new MyClassVisitor();
classReader.accept(classVisitor, ClassReader.EXPAND_FRAMES);
return classVisitor.getExtractedMethodsCallings();
}
private static class MyClassVisitor extends ClassVisitor {
public MyClassVisitor() {
super(Opcodes.ASM4);
}
private final Map<MethodNameAndQualifier, List<CallingLocation>> myExtractedMethodsCallings =
new HashMap<MethodNameAndQualifier, List<CallingLocation>>();
private String myClassName;
private String myRawClassName;
private Map<MethodNameAndQualifier, List<CallingLocation>> getExtractedMethodsCallings() {
return myExtractedMethodsCallings;
}
@Override
public void visit(final int version,
final int access,
final String className,
final String signature,
final String superName,
final String[] interfaces) {
myRawClassName = className;
myClassName = AsmUtil.getQualifiedClassName(className);
}
@Nullable
@Override
public FieldVisitor visitField(final int access, final String name, final String desc, final String signature, final Object value) {
return null;
}
@Nullable
@Override
public MethodVisitor visitMethod(final int access,
final String name,
final String desc,
final String signature,
final String[] exceptions) {
if (name.charAt(0) == '<') {
return null;
}
final boolean isStaticMethod = AsmUtil.isStaticMethodDeclaration(access);
if (isStaticMethod) {
return null;
}
@SuppressWarnings("UnnecessaryLocalVariable") final String methodName = name;
final String[] methodParams = AsmUtil.getParamsTypes(desc);
final MethodIncompleteSignature currentMethodSignature =
new MethodIncompleteSignature(myClassName, AsmUtil.getReturnType(desc), methodName, isStaticMethod);
return new JSRInlinerAdapter(new AnalyzerAdapter(Opcodes.ASM4, myRawClassName, access, name, desc, null) {
private final Map<Integer, Variable> myFieldsAndParamsPositionInStack = new HashMap<Integer, Variable>();
@Override
public void visitInsn(final int opcode) {
super.visitInsn(opcode);
}
@Override
public void visitFieldInsn(final int opcode, final String owner, final String name, final String desc) {
boolean onThis = false;
if (stack != null && opcode == Opcodes.GETFIELD && !ChainCompletionStringUtil.isPrimitiveOrArray(AsmUtil.getReturnType(desc))) {
final Object objectRef = stack.get(stack.size() - 1);
if (objectRef instanceof String && objectRef.equals(myRawClassName)) {
onThis = true;
}
}
super.visitFieldInsn(opcode, owner, name, desc);
if (onThis) {
final int index = stack.size() - 1;
final Object marker = stack.get(index);
myFieldsAndParamsPositionInStack.put(index, new Variable(marker, VariableType.FIELD));
}
}
@Override
public void visitVarInsn(final int opcode, final int varIndex) {
super.visitVarInsn(opcode, varIndex);
if (stack != null && opcode == Opcodes.ALOAD &&
varIndex > 0 &&
varIndex <= methodParams.length &&
!ChainCompletionStringUtil.isPrimitiveOrArray(methodParams[varIndex - 1])) {
final int stackPos = stack.size() - 1;
myFieldsAndParamsPositionInStack.put(stackPos, new Variable(stack.get(stackPos), VariableType.METHOD_PARAMETER));
}
}
@Override
public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc) {
if (stack != null && opcode != Opcodes.INVOKESTATIC && !methodName.startsWith("<")) {
final int index = stack.size() - 1 - AsmUtil.getParamsTypes(desc).length;
final Object stackValue = stack.get(index);
final Variable variable = myFieldsAndParamsPositionInStack.get(index);
if (variable != null && variable.getMarker() == stackValue /*equality by reference is not mistake*/) {
final CallingLocation callingLocation = new CallingLocation(currentMethodSignature, variable.getVariableType());
final MethodNameAndQualifier invokedMethod = new MethodNameAndQualifier(name, AsmUtil.getQualifiedClassName(owner));
List<CallingLocation> callingLocations = myExtractedMethodsCallings.get(invokedMethod);
if (callingLocations == null) {
callingLocations = new ArrayList<CallingLocation>();
myExtractedMethodsCallings.put(invokedMethod, callingLocations);
}
callingLocations.add(callingLocation);
}
}
super.visitMethodInsn(opcode, owner, name, desc);
}
}, access, name, desc, signature, exceptions);
}
}
private static class Variable {
private final Object myMarker;
private final VariableType myVariableType;
private Variable(final Object marker, final VariableType variableType) {
myMarker = marker;
myVariableType = variableType;
}
private Object getMarker() {
return myMarker;
}
private VariableType getVariableType() {
return myVariableType;
}
}
}

View File

@@ -0,0 +1,94 @@
package com.intellij.compilerOutputIndex.impl.callingLocation;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset;
import com.intellij.compilerOutputIndex.api.descriptor.ArrayListKeyDescriptor;
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputBaseIndex;
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputIndexer;
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature;
import com.intellij.openapi.project.Project;
import com.intellij.util.indexing.DataIndexer;
import com.intellij.util.indexing.ID;
import com.intellij.util.indexing.StorageException;
import com.intellij.util.indexing.ValueContainer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.asm4.ClassReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author Dmitry Batkovich
*/
public class MethodCallingLocationIndex extends CompilerOutputBaseIndex<MethodNameAndQualifier, List<CallingLocation>> {
public static MethodCallingLocationIndex getInstance(final Project project) {
return CompilerOutputIndexer.getInstance(project).getIndex(MethodCallingLocationIndex.class);
}
public MethodCallingLocationIndex() {
super(MethodNameAndQualifier.createKeyDescriptor(),
new ArrayListKeyDescriptor<CallingLocation>(CallingLocation.createDataExternalizer()));
}
@Override
protected ID<MethodNameAndQualifier, List<CallingLocation>> getIndexId() {
return generateIndexId(MethodCallingLocationIndex.class);
}
@Override
protected int getVersion() {
return 0;
}
public List<CallingLocation> getAllLocations(final MethodNameAndQualifier methodNameAndQualifier) {
try {
final List<CallingLocation> result = new ArrayList<CallingLocation>();
myIndex.getData(methodNameAndQualifier).forEach(new ValueContainer.ContainerAction<List<CallingLocation>>() {
@Override
public boolean perform(final int id, final List<CallingLocation> values) {
result.addAll(values);
return true;
}
});
return result;
}
catch (StorageException e) {
throw new RuntimeException(e);
}
}
public Multiset<MethodIncompleteSignature> getLocationsAsParam(final MethodNameAndQualifier methodNameAndQualifier) {
final Multiset<MethodIncompleteSignature> result = HashMultiset.create();
try {
myIndex.getData(methodNameAndQualifier).forEach(new ValueContainer.ContainerAction<List<CallingLocation>>() {
@Override
public boolean perform(final int id, final List<CallingLocation> values) {
for (final CallingLocation value : values) {
if (value.getVariableType().equals(VariableType.METHOD_PARAMETER)) {
result.add(value.getMethodIncompleteSignature());
}
}
return true;
}
});
}
catch (StorageException e) {
throw new RuntimeException(e);
}
return result;
}
@Override
protected DataIndexer<MethodNameAndQualifier, List<CallingLocation>, ClassReader> getIndexer() {
return new DataIndexer<MethodNameAndQualifier, List<CallingLocation>, ClassReader>() {
@NotNull
@Override
public Map<MethodNameAndQualifier, List<CallingLocation>> map(final ClassReader inputData) {
return MethodCallingLocationExtractor.extract(inputData);
}
};
}
}

View File

@@ -0,0 +1,81 @@
package com.intellij.compilerOutputIndex.impl.callingLocation;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.EnumeratorStringDescriptor;
import com.intellij.util.io.KeyDescriptor;
import org.jetbrains.annotations.NotNull;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
/**
* @author Dmitry Batkovich
*/
public class MethodNameAndQualifier {
@NotNull
private final String myMethodName;
@NotNull
private final String myQualifierName;
public MethodNameAndQualifier(@NotNull final String methodName, @NotNull final String qualifierName) {
myMethodName = methodName;
myQualifierName = qualifierName;
}
@NotNull
public String getMethodName() {
return myMethodName;
}
@NotNull
public String getQualifierName() {
return myQualifierName;
}
public static KeyDescriptor<MethodNameAndQualifier> createKeyDescriptor() {
final DataExternalizer<String> stringDataExternalizer = new EnumeratorStringDescriptor();
return new KeyDescriptor<MethodNameAndQualifier>() {
@Override
public void save(final DataOutput out, final MethodNameAndQualifier value) throws IOException {
stringDataExternalizer.save(out, value.myMethodName);
stringDataExternalizer.save(out, value.myQualifierName);
}
@Override
public MethodNameAndQualifier read(final DataInput in) throws IOException {
return new MethodNameAndQualifier(stringDataExternalizer.read(in), stringDataExternalizer.read(in));
}
@Override
public int getHashCode(final MethodNameAndQualifier value) {
return value.hashCode();
}
@Override
public boolean isEqual(final MethodNameAndQualifier val1, final MethodNameAndQualifier val2) {
return val1.equals(val2);
}
};
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final MethodNameAndQualifier that = (MethodNameAndQualifier)o;
if (!myMethodName.equals(that.myMethodName)) return false;
if (!myQualifierName.equals(that.myQualifierName)) return false;
return true;
}
@Override
public int hashCode() {
int result = myMethodName.hashCode();
result = 31 * result + myQualifierName.hashCode();
return result;
}
}

View File

@@ -0,0 +1,15 @@
package com.intellij.compilerOutputIndex.impl.callingLocation;
import com.intellij.util.io.EnumDataDescriptor;
import com.intellij.util.io.KeyDescriptor;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public enum VariableType {
FIELD,
METHOD_PARAMETER,
OTHER;
public static final KeyDescriptor<VariableType> KEY_DESCRIPTOR = new EnumDataDescriptor<VariableType>(VariableType.class);
}

View File

@@ -0,0 +1,96 @@
package com.intellij.compilerOutputIndex.impl.quickInheritance;
import com.intellij.compilerOutputIndex.api.fs.AsmUtil;
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputBaseIndex;
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputIndexer;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.CommonClassNames;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.indexing.DataIndexer;
import com.intellij.util.indexing.ID;
import com.intellij.util.indexing.StorageException;
import com.intellij.util.indexing.ValueContainer;
import com.intellij.util.io.EnumeratorStringDescriptor;
import com.intellij.compilerOutputIndex.api.descriptor.HashSetKeyDescriptor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.asm4.ClassReader;
import org.jetbrains.asm4.ClassVisitor;
import org.jetbrains.asm4.Opcodes;
import org.jetbrains.asm4.Type;
import java.util.*;
/**
* @author Dmitry Batkovich
*/
public class QuickInheritanceIndex extends CompilerOutputBaseIndex<String, Set<String>> {
public static QuickInheritanceIndex getInstance(final Project project) {
return CompilerOutputIndexer.getInstance(project).getIndex(QuickInheritanceIndex.class);
}
public QuickInheritanceIndex() {
super(new EnumeratorStringDescriptor(), new HashSetKeyDescriptor<String>(new EnumeratorStringDescriptor()));
}
@Override
protected ID<String, Set<String>> getIndexId() {
return generateIndexId(QuickInheritanceIndex.class);
}
protected Set<String> getSupers(final String classQName) {
try {
final ValueContainer<Set<String>> valueContainer = myIndex.getData(classQName);
final Ref<Set<String>> setRef = Ref.create();
valueContainer.forEach(new ValueContainer.ContainerAction<Set<String>>() {
@Override
public boolean perform(final int id, final Set<String> value) {
setRef.set(value);
return false;
}
});
final Set<String> supers = setRef.get();
if (supers == null) {
return Collections.emptySet();
}
return supers;
}
catch (StorageException e) {
throw new RuntimeException(e);
}
}
@Override
protected int getVersion() {
return 0;
}
@Override
protected DataIndexer<String, Set<String>, ClassReader> getIndexer() {
return new DataIndexer<String, Set<String>, ClassReader>() {
@NotNull
@Override
public Map<String, Set<String>> map(final ClassReader inputData) {
final Map<String, Set<String>> map = new HashMap<String, Set<String>>();
inputData.accept(new ClassVisitor(Opcodes.ASM4) {
@Override
public void visit(final int version,
final int access,
final String name,
final String signature,
final String superName,
final String[] interfaces) {
final String className = Type.getObjectType(name).getClassName();
if (className != null) {
final HashSet<String> value = ContainerUtil.newHashSet(AsmUtil.getQualifiedClassNames(interfaces, superName));
value.remove(CommonClassNames.JAVA_LANG_OBJECT);
map.put(className, value);
}
}
}, Opcodes.ASM4);
return map;
}
};
}
}

View File

@@ -0,0 +1,103 @@
package com.intellij.compilerOutputIndex.impl.quickInheritance;
import com.intellij.compilerOutputIndex.api.fs.AsmUtil;
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputBaseIndex;
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputIndexer;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.util.indexing.DataIndexer;
import com.intellij.util.indexing.ID;
import com.intellij.util.indexing.StorageException;
import com.intellij.util.indexing.ValueContainer;
import com.intellij.util.io.EnumeratorStringDescriptor;
import com.intellij.compilerOutputIndex.api.descriptor.HashSetKeyDescriptor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.asm4.ClassReader;
import org.jetbrains.asm4.ClassVisitor;
import org.jetbrains.asm4.MethodVisitor;
import org.jetbrains.asm4.Opcodes;
import java.util.*;
/**
* @author Dmitry Batkovich
*/
public class QuickMethodsIndex extends CompilerOutputBaseIndex<String, Set<String>> {
public static QuickMethodsIndex getInstance(final Project project) {
return CompilerOutputIndexer.getInstance(project).getIndex(QuickMethodsIndex.class);
}
public QuickMethodsIndex() {
super(new EnumeratorStringDescriptor(), new HashSetKeyDescriptor<String>(new EnumeratorStringDescriptor()));
}
@Override
protected ID<String, Set<String>> getIndexId() {
return generateIndexId(QuickMethodsIndex.class);
}
@Override
protected int getVersion() {
return 0;
}
protected Set<String> getMethodsNames(final String classQName) {
final Ref<Set<String>> methodsRef = Ref.create();
try {
myIndex.getData(classQName).forEach(new ValueContainer.ContainerAction<Set<String>>() {
@Override
public boolean perform(final int id, final Set<String> value) {
methodsRef.set(value);
return true;
}
});
final Set<String> methods = methodsRef.get();
return methods == null ? Collections.<String>emptySet() : methods;
}
catch (StorageException e) {
throw new RuntimeException(e);
}
}
@Override
protected DataIndexer<String, Set<String>, ClassReader> getIndexer() {
return new DataIndexer<String, Set<String>, ClassReader>() {
@NotNull
@Override
public Map<String, Set<String>> map(final ClassReader inputData) {
final Map<String, Set<String>> map = new HashMap<String, Set<String>>();
inputData.accept(new ClassVisitor(Opcodes.ASM4) {
private String myClassName;
private final HashSet<String> myMethodNames = new HashSet<String>();
@Override
public void visit(final int i, final int i2, final String name, final String s2, final String s3, final String[] strings) {
myClassName = AsmUtil.getQualifiedClassName(name);
}
@Override
public void visitEnd() {
map.put(myClassName, myMethodNames);
}
@Nullable
@Override
public MethodVisitor visitMethod(final int access,
final String name,
final String desc,
final String sign,
final String[] exceptions) {
if ((access & Opcodes.ACC_STATIC) == 0) {
myMethodNames.add(name);
}
return null;
}
}, Opcodes.ASM4);
return map;
}
};
}
}

View File

@@ -0,0 +1,23 @@
package com.intellij.compilerOutputIndex.impl.quickInheritance;
/**
* @author Dmitry Batkovich
*/
public final class QuickOverrideUtil {
private QuickOverrideUtil() {}
public static boolean isMethodOverriden(final String classQName, final String methodName,
final QuickInheritanceIndex quickInheritanceIndex,
final QuickMethodsIndex quickMethodsIndex) {
for (final String aSuper : quickInheritanceIndex.getSupers(classQName)) {
if (quickMethodsIndex.getMethodsNames(aSuper).contains(methodName)) {
return true;
}
if (isMethodOverriden(aSuper, methodName, quickInheritanceIndex, quickMethodsIndex)) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,71 @@
package com.intellij.compilerOutputIndex.impl.singleton;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.EnumeratorStringDescriptor;
import org.jetbrains.annotations.NotNull;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
/**
* @author Dmitry Batkovich
*/
public class MethodShortSignature {
@NotNull
private final String myName;
@NotNull
private final String mySignature; //in raw asm type
public MethodShortSignature(final @NotNull String name, final @NotNull String signature) {
myName = name;
mySignature = signature;
}
@NotNull
public String getName() {
return myName;
}
@NotNull
public String getSignature() {
return mySignature;
}
public static DataExternalizer<MethodShortSignature> createDataExternalizer() {
final EnumeratorStringDescriptor stringDescriptor = new EnumeratorStringDescriptor();
return new DataExternalizer<MethodShortSignature>() {
@Override
public void save(final DataOutput out, final MethodShortSignature value) throws IOException {
stringDescriptor.save(out, value.getName());
stringDescriptor.save(out, value.getSignature());
}
@Override
public MethodShortSignature read(final DataInput in) throws IOException {
return new MethodShortSignature(stringDescriptor.read(in), stringDescriptor.read(in));
}
};
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final MethodShortSignature that = (MethodShortSignature) o;
if (!myName.equals(that.myName)) return false;
if (!mySignature.equals(that.mySignature)) return false;
return true;
}
@Override
public int hashCode() {
int result = myName.hashCode();
result = 31 * result + mySignature.hashCode();
return result;
}
}

View File

@@ -0,0 +1,53 @@
package com.intellij.compilerOutputIndex.impl.singleton;
import org.jetbrains.annotations.NotNull;
import java.util.Comparator;
/**
* @author Dmitry Batkovich
*/
public class MethodShortSignatureWithWeight {
private final MethodShortSignature myMethodShortSignature;
private final int myWeight;
public MethodShortSignatureWithWeight(final MethodShortSignature methodShortSignature, final int weight) {
myMethodShortSignature = methodShortSignature;
myWeight = weight;
}
public MethodShortSignature getMethodShortSignature() {
return myMethodShortSignature;
}
public int getWeight() {
return myWeight;
}
public static Comparator<MethodShortSignatureWithWeight> COMPARATOR = new Comparator<MethodShortSignatureWithWeight>() {
@Override
public int compare(final MethodShortSignatureWithWeight o1, final MethodShortSignatureWithWeight o2) {
return o1.getWeight() - o2.getWeight();
}
} ;
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final MethodShortSignatureWithWeight that = (MethodShortSignatureWithWeight) o;
if (myWeight != that.myWeight) return false;
if (!myMethodShortSignature.equals(that.myMethodShortSignature)) return false;
return true;
}
@Override
public int hashCode() {
int result = myMethodShortSignature.hashCode();
result = 31 * result + myWeight;
return result;
}
}

View File

@@ -0,0 +1,116 @@
package com.intellij.compilerOutputIndex.impl.singleton;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset;
import com.intellij.compilerOutputIndex.api.fs.AsmUtil;
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputBaseIndex;
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputIndexUtil;
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputIndexer;
import com.intellij.compilerOutputIndex.impl.GuavaHashMultiSetExternalizer;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.util.indexing.DataIndexer;
import com.intellij.util.indexing.ID;
import com.intellij.util.indexing.StorageException;
import com.intellij.util.indexing.ValueContainer;
import com.intellij.util.io.EnumeratorStringDescriptor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.asm4.*;
import java.util.*;
/**
* @author Dmitry Batkovich
*/
public class ParamsInMethodOccurrencesIndex extends CompilerOutputBaseIndex<String, Multiset<MethodShortSignature>> {
public static ParamsInMethodOccurrencesIndex getInstance(final Project project) {
return CompilerOutputIndexer.getInstance(project).getIndex(ParamsInMethodOccurrencesIndex.class);
}
public ParamsInMethodOccurrencesIndex() {
super(new EnumeratorStringDescriptor(), new GuavaHashMultiSetExternalizer<MethodShortSignature>(MethodShortSignature.createDataExternalizer()));
}
@Override
protected ID<String, Multiset<MethodShortSignature>> getIndexId() {
return generateIndexId(ParamsInMethodOccurrencesIndex.class);
}
@Override
protected int getVersion() {
return 0;
}
public Pair<List<MethodShortSignatureWithWeight>, Integer> getParameterOccurrences(final String parameterTypeName) {
try {
final Multiset<MethodShortSignature> resultAsMultiset = HashMultiset.create();
final ValueContainer<Multiset<MethodShortSignature>> valueContainer = myIndex.getData(parameterTypeName);
valueContainer.forEach(new ValueContainer.ContainerAction<Multiset<MethodShortSignature>>() {
@Override
public boolean perform(final int id, final Multiset<MethodShortSignature> localMap) {
for (final Multiset.Entry<MethodShortSignature> e : localMap.entrySet()) {
resultAsMultiset.add(e.getElement(), e.getCount());
}
return true;
}
});
final List<MethodShortSignatureWithWeight> result = new ArrayList<MethodShortSignatureWithWeight>(resultAsMultiset.elementSet().size());
int sumWeight = 0;
for (final Multiset.Entry<MethodShortSignature> e : resultAsMultiset.entrySet()) {
sumWeight += e.getCount();
result.add(new MethodShortSignatureWithWeight(e.getElement(), e.getCount()));
}
Collections.sort(result, MethodShortSignatureWithWeight.COMPARATOR);
return Pair.create(result, sumWeight);
} catch (StorageException e) {
throw new RuntimeException(e);
}
}
@Override
protected DataIndexer<String, Multiset<MethodShortSignature>, ClassReader> getIndexer() {
return new DataIndexer<String, Multiset<MethodShortSignature>, ClassReader>() {
@NotNull
@Override
public Map<String, Multiset<MethodShortSignature>> map(final ClassReader inputData) {
final Map<String, Multiset<MethodShortSignature>> result = new HashMap<String, Multiset<MethodShortSignature>>();
inputData.accept(new ClassVisitor(Opcodes.ASM4) {
@Nullable
@Override
public MethodVisitor visitMethod(final int i, final String name, final String desc, final String signature, final String[] exception) {
if (CompilerOutputIndexUtil.isSetterOrConstructorMethodName(name)) {
return null;
}
final String[] parameters = AsmUtil.getParamsTypes(desc);
final MethodShortSignature thisMethodShortSignature = new MethodShortSignature(name, desc);
for (final String parameter : parameters) {
Multiset<MethodShortSignature> methods = result.get(parameter);
if (methods == null) {
methods = HashMultiset.create();
result.put(parameter, methods);
}
methods.add(thisMethodShortSignature);
}
return new MethodVisitor(Opcodes.ASM4) {
@Override
public void visitLocalVariable(final String s, final String desc, final String signature, final Label label, final Label label2, final int i) {
final String varType = AsmUtil.getQualifiedClassName(desc);
Multiset<MethodShortSignature> methods = result.get(varType);
if (methods == null) {
methods = HashMultiset.create();
result.put(varType, methods);
}
methods.add(thisMethodShortSignature);
}
};
}
}, Opcodes.ASM4);
return result;
}
};
}
}

View File

@@ -0,0 +1,137 @@
package com.intellij.compilerOutputIndex.impl.singleton;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset;
import com.intellij.codeInsight.completion.methodChains.ChainCompletionStringUtil;
import com.intellij.compilerOutputIndex.api.descriptor.ArrayListKeyDescriptor;
import com.intellij.compilerOutputIndex.api.fs.AsmUtil;
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputBaseIndex;
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputIndexUtil;
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputIndexer;
import com.intellij.openapi.project.Project;
import com.intellij.util.indexing.DataIndexer;
import com.intellij.util.indexing.ID;
import com.intellij.util.indexing.StorageException;
import com.intellij.util.indexing.ValueContainer;
import com.intellij.util.io.EnumeratorIntegerDescriptor;
import com.intellij.util.io.EnumeratorStringDescriptor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.asm4.*;
import java.util.*;
/**
* @author Dmitry Batkovich
*/
public class TwinVariablesIndex extends CompilerOutputBaseIndex<String, List<Integer>> {
public static TwinVariablesIndex getInstance(final Project project) {
return CompilerOutputIndexer.getInstance(project).getIndex(TwinVariablesIndex.class);
}
public TwinVariablesIndex() {
super(new EnumeratorStringDescriptor(), new ArrayListKeyDescriptor<Integer>(EnumeratorIntegerDescriptor.INSTANCE));
}
@Override
protected ID<String, List<Integer>> getIndexId() {
return generateIndexId(TwinVariablesIndex.class);
}
@Override
protected int getVersion() {
return 0;
}
@NotNull
public List<Integer> getTwinInfo(final String typeQName) {
try {
final ValueContainer<List<Integer>> valueContainer = myIndex.getData(typeQName);
final List<Integer> result = new ArrayList<Integer>(valueContainer.size());
valueContainer.forEach(new ValueContainer.ContainerAction<List<Integer>>() {
@Override
public boolean perform(final int id, final List<Integer> value) {
result.addAll(value);
return true;
}
});
return result;
}
catch (StorageException e) {
throw new RuntimeException(e);
}
}
@Override
protected DataIndexer<String, List<Integer>, ClassReader> getIndexer() {
return new DataIndexer<String, List<Integer>, ClassReader>() {
@NotNull
@Override
public Map<String, List<Integer>> map(final ClassReader inputData) {
final Map<String, List<Integer>> map = new HashMap<String, List<Integer>>();
inputData.accept(new ClassVisitor(Opcodes.ASM4) {
@Nullable
@Override
public MethodVisitor visitMethod(final int access,
final String name,
final String desc,
final String signature,
final String[] exceptions) {
if (CompilerOutputIndexUtil.isSetterOrConstructorMethodName(name)) {
return null;
}
final Multiset<String> myTypesOccurrences = HashMultiset.create();
final String[] paramsTypes = AsmUtil.getParamsTypes(desc);
Collections.addAll(myTypesOccurrences, paramsTypes);
return new MethodVisitor(Opcodes.ASM4) {
private final Set<String> myLocalVarNames = new HashSet<String>();
@SuppressWarnings("unchecked")
@Override
public void visitEnd() {
for (final Multiset.Entry<String> e: myTypesOccurrences.entrySet()) {
final String key = e.getElement();
if (!ChainCompletionStringUtil.isPrimitiveOrArrayOfPrimitives(key)) {
List<Integer> values = map.get(key);
if (values == null) {
values = new ArrayList<Integer>();
map.put(key, values);
}
values.add(e.getCount());
}
}
}
private final Set<String> myUsedReadFieldsIndex = new HashSet<String>();
@Override
public void visitFieldInsn(final int opcode, final String owner, final String name, final String desc) {
final String fieldTypeQName = AsmUtil.getReturnType(desc);
if ((opcode == Opcodes.GETSTATIC || opcode == Opcodes.GETFIELD)) {
if (myUsedReadFieldsIndex.add(owner + name)) {
myTypesOccurrences.add(fieldTypeQName);
}
}
}
@Override
public void visitLocalVariable(final String name,
final String desc,
final String signature,
final Label start,
final Label end,
final int index) {
if (index > paramsTypes.length && myLocalVarNames.add(name)) {
final String type = AsmUtil.getReturnType(desc);
myTypesOccurrences.add(type);
}
}
};
}
}, Opcodes.ASM4);
return map;
}
};
}
}

View File

@@ -0,0 +1,301 @@
package com.intellij.jarFinder;
import com.intellij.codeInsight.daemon.impl.quickfix.OrderEntryFix;
import com.intellij.codeInsight.hint.HintManager;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.application.AccessToken;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileChooser.FileChooser;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.Iconable;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.CommonClassNames;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.ui.components.JBList;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.NotNullFunction;
import com.intellij.util.PlatformIcons;
import com.intellij.util.download.DownloadableFileDescription;
import com.intellij.util.download.DownloadableFileService;
import org.apache.xerces.parsers.DOMParser;
import org.jetbrains.annotations.NotNull;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import javax.swing.*;
import java.io.File;
import java.io.IOException;
import java.util.*;
/**
* @author Konstantin Bulenkov
*/
public abstract class FindJarFix<T extends PsiElement> implements IntentionAction, Iconable {
private static final String CLASS_ROOT_URL = "http://findjar.com/class/";
private static final String CLASS_PAGE_EXT = ".html";
private static final String SERVICE_URL = "http://findjar.com";
private static final String LINK_TAG_NAME = "a";
private static final String LINK_ATTR_NAME = "href";
protected final T myRef;
protected final Module myModule;
protected JComponent myEditorComponent;
public FindJarFix(T ref) {
myRef = ref;
myModule = ModuleUtil.findModuleForPsiElement(ref);
}
@NotNull
@Override
public String getText() {
return "Find jar on web";
}
@NotNull
@Override
public String getFamilyName() {
return "Family name";
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
return myRef.isValid()
&& JavaPsiFacade.getInstance(project).findClass(CommonClassNames.JAVA_LANG_OBJECT, file.getResolveScope()) != null
&& myModule != null
&& isFqnsOk(project, getPossibleFqns(myRef));
}
private static boolean isFqnsOk(Project project, List<String> fqns) {
if (fqns.isEmpty()) return false;
final JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
final GlobalSearchScope scope = GlobalSearchScope.allScope(project);
for (String fqn : fqns) {
if (facade.findClass(fqn, scope) != null) return false;
}
return true;
}
@Override
public void invoke(@NotNull Project project, final Editor editor, final PsiFile file) throws IncorrectOperationException {
final List<String> fqns = getPossibleFqns(myRef);
myEditorComponent = editor.getComponent();
if (fqns.size() > 1) {
final JBList listOfFqns = new JBList(fqns);
JBPopupFactory.getInstance()
.createListPopupBuilder(listOfFqns)
.setTitle("Select Qualified Name")
.setItemChoosenCallback(new Runnable() {
@Override
public void run() {
final Object value = listOfFqns.getSelectedValue();
if (value instanceof String) {
findJarsForFqn(((String)value), editor);
}
}
}).createPopup().showInBestPositionFor(editor);
}
else if (fqns.size() == 1) {
findJarsForFqn(fqns.get(0), editor);
}
}
private void findJarsForFqn(final String fqn, final Editor editor) {
final Map<String, String> libs = new HashMap<String, String>();
final Runnable runnable = new Runnable() {
public void run() {
try {
final DOMParser parser = new DOMParser();
parser.parse(CLASS_ROOT_URL + fqn.replace('.', '/') + CLASS_PAGE_EXT);
final Document doc = parser.getDocument();
if (doc != null) {
final NodeList links = doc.getElementsByTagName(LINK_TAG_NAME);
for (int i = 0; i < links.getLength(); i++) {
final Node link = links.item(i);
final String libName = link.getTextContent();
final NamedNodeMap attributes = link.getAttributes();
if (attributes != null) {
final Node href = attributes.getNamedItem(LINK_ATTR_NAME);
if (href != null) {
final String pathToJar = href.getTextContent();
if (pathToJar != null && (pathToJar.startsWith("/jar/") || pathToJar.startsWith("/class/../"))) {
libs.put(libName, SERVICE_URL + pathToJar);
}
}
}
}
}
}
catch (IOException ignore) {//
}
catch (SAXException e) {//
}
}
};
final Task.Modal task = new Task.Modal(editor.getProject(), "Looking for libraries", true) {
@Override
public void run(@NotNull ProgressIndicator indicator) {
indicator.setIndeterminate(true);
runnable.run();
}
@Override
public void onSuccess() {
super.onSuccess();
if (libs.isEmpty()) {
HintManager.getInstance().showInformationHint(editor, "No libraries found for '" + fqn + "'");
} else {
final ArrayList<String> variants = new ArrayList<String>(libs.keySet());
Collections.sort(variants, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
final JBList libNames = new JBList(variants);
libNames.installCellRenderer(new NotNullFunction<Object, JComponent>() {
@NotNull
@Override
public JComponent fun(Object o) {
return new JLabel(o.toString(), PlatformIcons.JAR_ICON, SwingConstants.LEFT);
}
});
if (libs.size() == 1) {
final String jarName = libs.keySet().iterator().next();
final String url = libs.get(jarName);
initiateDownload(url, jarName);
} else {
JBPopupFactory.getInstance()
.createListPopupBuilder(libNames)
.setTitle("Select a jar file")
.setItemChoosenCallback(new Runnable() {
@Override
public void run() {
final Object value = libNames.getSelectedValue();
if (value instanceof String) {
final String jarName = (String)value;
final String url = libs.get(jarName);
if (url != null) {
initiateDownload(url, jarName);
}
}
}
})
.createPopup().showInBestPositionFor(editor);
}
}
}
};
ProgressManager.getInstance().run(task);
}
private void initiateDownload(String url, String jarName) {
DOMParser parser = new DOMParser();
try {
parser.parse(url);
final Document doc = parser.getDocument();
if (doc != null) {
final NodeList links = doc.getElementsByTagName(LINK_TAG_NAME);
if (links != null) {
for (int i = 0; i < links.getLength(); i++) {
final Node item = links.item(i);
if (item != null) {
final NamedNodeMap attributes = item.getAttributes();
if (attributes != null) {
final Node link = attributes.getNamedItem(LINK_ATTR_NAME);
if (link != null) {
final String jarUrl = link.getTextContent();
if (jarUrl != null && jarUrl.endsWith(jarName)) {
downloadJar(jarUrl, jarName);
}
}
}
}
}
}
}
}
catch (SAXException e) {//
}
catch (IOException e) {//
}
}
private void downloadJar(String jarUrl, String jarName) {
final Project project = myModule.getProject();
final String dirPath = PropertiesComponent.getInstance(project).getValue("findjar.last.used.dir");
VirtualFile toSelect = dirPath == null ? null : LocalFileSystem.getInstance().findFileByIoFile(new File(dirPath));
final VirtualFile file = FileChooser.chooseFile(FileChooserDescriptorFactory.createSingleFolderDescriptor(), project, toSelect);
if (file != null) {
PropertiesComponent.getInstance(project).setValue("findjar.last.used.dir", file.getPath());
final DownloadableFileService downloader = DownloadableFileService.getInstance();
final DownloadableFileDescription description = downloader.createFileDescription(jarUrl, jarName);
final VirtualFile[] jars = downloader.createDownloader(Arrays.asList(description), project, myEditorComponent, jarName)
.toDirectory(file.getPath()).download();
if (jars != null && jars.length == 1) {
AccessToken token = WriteAction.start();
try {
OrderEntryFix.addJarToRoots(jars[0].getPresentableUrl(), myModule, myRef);
}
finally {
token.finish();
}
}
}
}
protected abstract Collection<String> getFqns(@NotNull T ref);
protected List<String> getPossibleFqns(T ref) {
Collection<String> fqns = getFqns(ref);
List<String> res = new ArrayList<String>(fqns.size());
for (String fqn : fqns) {
if (fqn.startsWith("java.") || fqn.startsWith("javax.swing.")) {
continue;
}
final int index = fqn.lastIndexOf('.');
if (index == -1) {
continue;
}
final String className = fqn.substring(index + 1);
if (className.length() == 0 || Character.isLowerCase(className.charAt(0))) {
continue;
}
res.add(fqn);
}
return res;
}
@Override
public boolean startInWriteAction() {
return false;
}
@Override
public Icon getIcon(int flags) {
return PlatformIcons.WEB_ICON;
}
}

View File

@@ -0,0 +1,22 @@
package com.intellij.jarFinder;
import com.intellij.codeInsight.daemon.QuickFixActionRegistrar;
import com.intellij.codeInsight.quickfix.UnresolvedReferenceQuickFixProvider;
import com.intellij.psi.PsiJavaCodeReferenceElement;
import org.jetbrains.annotations.NotNull;
/**
* @author Konstantin Bulenkov
*/
public class FindJarQuickFixProvider extends UnresolvedReferenceQuickFixProvider<PsiJavaCodeReferenceElement> {
@Override
public void registerFixes(PsiJavaCodeReferenceElement ref, QuickFixActionRegistrar registrar) {
registrar.register(new JavaFindJarFix(ref));
}
@NotNull
@Override
public Class<PsiJavaCodeReferenceElement> getReferenceClass() {
return PsiJavaCodeReferenceElement.class;
}
}

View File

@@ -0,0 +1,78 @@
package com.intellij.jarFinder;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.PsiImportStaticStatementImpl;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* @author Sergey Evdokimov
*/
public class JavaFindJarFix extends FindJarFix<PsiQualifiedReferenceElement> {
public JavaFindJarFix(PsiQualifiedReferenceElement ref) {
super(ref);
}
@Override
protected Collection<String> getFqns(@NotNull PsiQualifiedReferenceElement ref) {
final PsiImportStatementBase importStatement = PsiTreeUtil.getParentOfType(ref.getElement(), PsiImportStatementBase.class);
//from static imports
if (importStatement != null) {
if (importStatement instanceof PsiImportStatement) {
final String importFQN = ((PsiImportStatement)importStatement).getQualifiedName();
if (importFQN != null && !importFQN.endsWith("*")) {
return Collections.singleton(importFQN);
}
}
else if (importStatement instanceof PsiImportStaticStatementImpl) {
final PsiJavaCodeReferenceElement classRef = ((PsiImportStaticStatementImpl)importStatement).getClassReference();
if (classRef != null) {
final String importFQN = classRef.getQualifiedName();
if (importFQN != null) {
return Collections.singleton(importFQN);
}
}
}
return Collections.emptyList();
}
final PsiElement qualifier = ref.getQualifier();
if (qualifier instanceof PsiQualifiedReference) {
//PsiQualifiedReference r = (PsiQualifiedReference)qualifier;
//TODO[kb] get fqn from expressions like org.unresolvedPackage.MyClass.staticMethodCall(...);
return Collections.emptyList();
}
final String className = ref.getReferenceName();
PsiFile file = ref.getContainingFile().getOriginalFile();
if (className != null && file instanceof PsiJavaFile) {
final PsiImportList importList = ((PsiJavaFile)file).getImportList();
if (importList != null) {
final PsiImportStatementBase statement = importList.findSingleImportStatement(className);
if (statement instanceof PsiImportStatement) {
final String importFQN = ((PsiImportStatement)statement).getQualifiedName();
if (importFQN != null) {
return Collections.singleton(importFQN);
}
}
else {
List<String> res = new ArrayList<String>();
// iterate through *
for (PsiImportStatementBase imp : importList.getAllImportStatements()) {
if (imp.isOnDemand() && imp instanceof PsiImportStatement) {
res.add(((PsiImportStatement)imp).getQualifiedName() + "." + className);
}
}
return res;
}
}
}
return Collections.emptyList();
}
}

View File

@@ -0,0 +1,70 @@
package com.intellij.jarFinder;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.xpath.XPath;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.util.List;
/**
* @author Sergey Evdokimov
*/
public class MavenCentralSourceSearcher extends SourceSearcher {
private static final Logger LOG = Logger.getInstance(MavenCentralSourceSearcher.class);
@Nullable
@Override
protected String findSourceJar(@NotNull ProgressIndicator indicator,
@NotNull String artifactId,
@NotNull String version) throws SourceSearchException {
try {
indicator.setIndeterminate(true);
indicator.setText("Connecting to http://search.maven.org");
indicator.checkCanceled();
String url = "http://search.maven.org/solrsearch/select?rows=3&wt=xml&q=a:%22" + artifactId + "%22%20AND%20v:%22" + version + "%22%20AND%20l:%22sources%22";
Document document = readDocumentCancelable(indicator, url);
indicator.checkCanceled();
List<Element> artifactList = (List<Element>)XPath.newInstance("/response/result/doc/str[@name='g']").selectNodes(document);
if (artifactList.isEmpty()) {
return null;
}
Element element;
if (artifactList.size() == 1) {
element = artifactList.get(0);
}
else {
// TODO handle
return null;
}
String groupId = element.getValue();
String downloadUrl = "http://search.maven.org/remotecontent?filepath=" + groupId.replace('.', '/') + '/' + artifactId + '/' + version + '/' + artifactId + '-' + version + "-sources.jar";
return downloadUrl;
}
catch (JDOMException e) {
LOG.warn(e);
throw new SourceSearchException("Failed to parse response from server. See log for more details.");
}
catch (IOException e) {
indicator.checkCanceled(); // Cause of IOException may be canceling of operation.
LOG.warn(e);
throw new SourceSearchException("Connection problem. See log for more details.");
}
}
}

View File

@@ -0,0 +1,87 @@
package com.intellij.jarFinder;
import com.intellij.notification.NotificationType;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.util.Pair;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.xpath.XPath;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.util.List;
/**
* @author Sergey Evdokimov
*/
public class SonatypeSourceSearcher extends SourceSearcher {
private static final Logger LOG = Logger.getInstance(SonatypeSourceSearcher.class);
@Nullable
@Override
public String findSourceJar(@NotNull final ProgressIndicator indicator, @NotNull String artifactId, @NotNull String version)
throws SourceSearchException {
try {
indicator.setIndeterminate(true);
indicator.setText("Connecting to https://oss.sonatype.org");
indicator.checkCanceled();
String url = "https://oss.sonatype.org/service/local/lucene/search?collapseresults=true&c=sources&a=" + artifactId + "&v=" + version;
Document document = readDocumentCancelable(indicator, url);
indicator.checkCanceled();
List<Element> artifactList = (List<Element>)XPath.newInstance("/searchNGResponse/data/artifact").selectNodes(document);
if (artifactList.isEmpty()) {
return null;
}
Element element;
if (artifactList.size() == 1) {
element = artifactList.get(0);
}
else {
// TODO handle
return null;
}
List<Element> artifactHintList =
(List<Element>)XPath.newInstance("artifactHits/artifactHit/artifactLinks/artifactLink/classifier[text()='sources']/../../..")
.selectNodes(element);
if (artifactHintList.isEmpty()) {
return null;
}
String groupId = element.getChildTextTrim("groupId");
String repositoryId = artifactHintList.get(0).getChildTextTrim("repositoryId");
String downloadUrl = "https://oss.sonatype.org/service/local/artifact/maven/redirect?r=" +
repositoryId +
"&g=" +
groupId +
"&a=" +
artifactId +
"&v=" +
version +
"&e=jar&c=sources";
return downloadUrl;
}
catch (JDOMException e) {
LOG.warn(e);
throw new SourceSearchException("Failed to parse response from server. See log for more details.");
}
catch (IOException e) {
indicator.checkCanceled(); // Cause of IOException may be canceling of operation.
LOG.warn(e);
throw new SourceSearchException("Connection problem. See log for more details.");
}
}
}

View File

@@ -0,0 +1,77 @@
package com.intellij.jarFinder;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.util.net.HttpConfigurable;
import org.jdom.Document;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
/**
* @author Sergey Evdokimov
*/
public abstract class SourceSearcher {
/**
* @param indicator
* @param artifactId
* @param version
* @return groupId of found artifact and url.
*/
@Nullable
protected abstract String findSourceJar(@NotNull final ProgressIndicator indicator, @NotNull String artifactId, @NotNull String version) throws SourceSearchException;
protected static Document readDocumentCancelable(final ProgressIndicator indicator, String url) throws JDOMException, IOException {
final HttpURLConnection urlConnection = HttpConfigurable.getInstance().openHttpConnection(url);
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
//noinspection InfiniteLoopStatement
while (true) {
if (indicator.isCanceled()) {
urlConnection.disconnect();
}
//noinspection BusyWait
Thread.sleep(100);
}
}
catch (InterruptedException ignored) {
}
}
});
t.start();
try {
urlConnection.setRequestProperty("accept", "application/xml");
InputStream inputStream = urlConnection.getInputStream();
try {
return new SAXBuilder().build(inputStream);
}
finally {
inputStream.close();
}
}
finally {
t.interrupt();
}
}
}
class SourceSearchException extends Exception {
SourceSearchException(String message) {
super(message);
}
}

View File

@@ -27,6 +27,7 @@ import com.intellij.util.indexing.FileBasedIndex;
import com.intellij.util.io.DataInputOutputUtil;
import com.intellij.util.messages.MessageBus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.DataInputStream;
import java.io.DataOutputStream;
@@ -42,7 +43,7 @@ public class JavaLanguageLevelPusher implements FilePropertyPusher<LanguageLevel
}
@Override
public void initExtra(Project project, MessageBus bus, Engine languageLevelUpdater) {
public void initExtra(@NotNull Project project, @NotNull MessageBus bus, @NotNull Engine languageLevelUpdater) {
// nothing
}
@@ -64,24 +65,24 @@ public class JavaLanguageLevelPusher implements FilePropertyPusher<LanguageLevel
}
@Override
public LanguageLevel getImmediateValue(Project project, VirtualFile file) {
public LanguageLevel getImmediateValue(@NotNull Project project, @Nullable VirtualFile file) {
return null;
}
@Override
public LanguageLevel getImmediateValue(Module module) {
public LanguageLevel getImmediateValue(@NotNull Module module) {
return LanguageLevelUtil.getEffectiveLanguageLevel(module);
}
@Override
public boolean acceptsFile(VirtualFile file) {
public boolean acceptsFile(@NotNull VirtualFile file) {
return false;
}
private static final FileAttribute PERSISTENCE = new FileAttribute("language_level_persistence", 2, true);
@Override
public void persistAttribute(VirtualFile fileOrDir, @NotNull LanguageLevel level) throws IOException {
public void persistAttribute(@NotNull VirtualFile fileOrDir, @NotNull LanguageLevel level) throws IOException {
final DataInputStream iStream = PERSISTENCE.readAttribute(fileOrDir);
if (iStream != null) {
try {
@@ -105,6 +106,6 @@ public class JavaLanguageLevelPusher implements FilePropertyPusher<LanguageLevel
}
@Override
public void afterRootsChanged(Project project) {
public void afterRootsChanged(@NotNull Project project) {
}
}

View File

@@ -15,16 +15,21 @@
*/
package com.intellij.psi.impl.source.codeStyle.javadoc;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.lang.ASTNode;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.LineTokenizer;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.impl.source.SourceTreeToPsiMap;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
@@ -59,7 +64,7 @@ public class CommentFormatter {
processElementComment(psiElement);
}
private void processElementComment(PsiElement psiElement) {
private void processElementComment(@Nullable PsiElement psiElement) {
if (psiElement instanceof PsiClass) {
String newCommentText = formatClassComment((PsiClass)psiElement);
replaceDocComment(newCommentText, (PsiDocCommentOwner)psiElement);
@@ -77,7 +82,7 @@ public class CommentFormatter {
}
}
private void replaceDocComment(String newCommentText, final PsiDocCommentOwner psiDocCommentOwner) {
private void replaceDocComment(@Nullable String newCommentText, @NotNull final PsiDocCommentOwner psiDocCommentOwner) {
final PsiDocComment oldComment = psiDocCommentOwner.getDocComment();
if (newCommentText != null) newCommentText = stripSpaces(newCommentText);
if (newCommentText == null || oldComment == null || newCommentText.equals(oldComment.getText())) {
@@ -99,7 +104,7 @@ public class CommentFormatter {
private static String stripSpaces(String text) {
String[] lines = LineTokenizer.tokenize(text.toCharArray(), false);
StringBuffer buf = new StringBuffer(text.length());
StringBuilder buf = new StringBuilder(text.length());
for (int i = 0; i < lines.length; i++) {
if (i > 0) buf.append('\n');
buf.append(rTrim(lines[i]));
@@ -117,7 +122,7 @@ public class CommentFormatter {
}
@Nullable
private String formatClassComment(PsiClass psiClass) {
private String formatClassComment(@NotNull PsiClass psiClass) {
final String info = getOrigCommentInfo(psiClass);
if (info == null) return null;
@@ -126,7 +131,7 @@ public class CommentFormatter {
}
@Nullable
private String formatMethodComment(PsiMethod psiMethod) {
private String formatMethodComment(@NotNull PsiMethod psiMethod) {
final String info = getOrigCommentInfo(psiMethod);
if (info == null) return null;
@@ -135,7 +140,7 @@ public class CommentFormatter {
}
@Nullable
private String formatFieldComment(PsiField psiField) {
private String formatFieldComment(@NotNull PsiField psiField) {
final String info = getOrigCommentInfo(psiField);
if (info == null) return null;
@@ -149,8 +154,9 @@ public class CommentFormatter {
* @param element the specified element
* @return text chunk
*/
@Nullable
private static String getOrigCommentInfo(PsiDocCommentOwner element) {
StringBuffer sb = new StringBuffer();
StringBuilder sb = new StringBuilder();
PsiElement e = element.getFirstChild();
if (!(e instanceof PsiComment)) {
// no comments for this element
@@ -188,39 +194,38 @@ public class CommentFormatter {
}
/**
* For the specified element returns its indentation
*
* @param element the specified element
* @return indentation as string
* Computes indentation of PsiClass, PsiMethod and PsiField elements after formatting
* @param element PsiClass or PsiMethod or PsiField
* @return indentation size
*/
private static String getIndent(PsiElement element) {
PsiElement e = element.getFirstChild();
PsiWhiteSpace lastWS = null;
for (; ; e = e.getNextSibling()) {
if (e instanceof PsiWhiteSpace) {
lastWS = (PsiWhiteSpace)e;
}
else if (e instanceof PsiComment) {
lastWS = null;
}
else {
private int getIndentSpecial(@NotNull PsiElement element) {
assert(element instanceof PsiClass ||
element instanceof PsiField ||
element instanceof PsiMethod);
int indentSize = mySettings.getIndentSize(JavaFileType.INSTANCE);
boolean doNotIndentTopLevelClassMembers = mySettings.getCommonSettings(JavaLanguage.INSTANCE).DO_NOT_INDENT_TOP_LEVEL_CLASS_MEMBERS;
int indent = 0;
PsiClass top = PsiUtil.getTopLevelClass(element);
while (top != null && !element.isEquivalentTo(top)) {
if (doNotIndentTopLevelClassMembers && element.getParent().isEquivalentTo(top)) {
break;
}
element = element.getParent();
indent += indentSize;
}
e = lastWS == null ? element.getPrevSibling() : lastWS;
if (!(e instanceof PsiWhiteSpace)) return "";
PsiWhiteSpace ws = (PsiWhiteSpace)e;
String t = ws.getText();
int l = t.length();
int i = l;
while (--i >= 0) {
char ch = t.charAt(i);
if (ch == '\n' || ch == '\r') break;
}
if (i < 0) return t;
i++;
if (i == l) return "";
return t.substring(i);
return indent;
}
/**
* Used while formatting javadocs. We need precise element indentation after formatting to wrap comments correctly.
* Used only for PsiClass, PsiMethod and PsiFields.
* @return indent which would be used for the given element when it's formatted according to the current code style settings
*/
@NotNull
private String getIndent(@NotNull PsiElement element) {
return StringUtil.repeatSymbol(' ', getIndentSpecial(element));
}
}

View File

@@ -17,7 +17,6 @@ package com.intellij.psi.impl.source.resolve.reference.impl.providers;
import com.intellij.codeInsight.completion.JavaLookupElementBuilder;
import com.intellij.codeInsight.completion.scope.JavaCompletionProcessor;
import com.intellij.codeInsight.daemon.QuickFixProvider;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.daemon.impl.quickfix.OrderEntryFix;
import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixAction;
@@ -68,7 +67,7 @@ import java.util.Map;
/**
* @author peter
*/
public class JavaClassReference extends GenericReference implements PsiJavaReference, QuickFixProvider, LocalQuickFixProvider {
public class JavaClassReference extends GenericReference implements PsiJavaReference, LocalQuickFixProvider {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.resolve.reference.impl.providers.JavaClassReference");
protected final int myIndex;
private TextRange myRange;
@@ -472,11 +471,6 @@ public class JavaClassReference extends GenericReference implements PsiJavaRefer
return new JavaResolveResult[]{javaResolveResult};
}
@Override
public void registerQuickfix(HighlightInfo info, PsiReference reference) {
registerFixes(info);
}
@Nullable
private List<? extends LocalQuickFix> registerFixes(final HighlightInfo info) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
package com.intellij.psi;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NonNls;
@@ -135,7 +136,7 @@ public interface JVMElementFactory {
/**
* Creates new reference element by type
* @param type the type for which reference element should be creted
* @param type the type for which reference element should be created
* @return the reference element
*/
@NotNull
@@ -264,4 +265,45 @@ public interface JVMElementFactory {
*/
@NotNull
PsiClassType createTypeByFQClassName(@NotNull @NonNls String qName, @NotNull GlobalSearchScope resolveScope);
/**
* Creates doc comment from text
*/
@NotNull
PsiDocComment createDocCommentFromText(@NotNull String text);
/**
* Checks whether name is a valid class name in the current language
* @param name name to checks
* @return true if name is a valid name
*/
boolean isValidClassName(@NotNull String name);
/**
* Checks whether name is a valid method name in the current language
* @param name name to checks
* @return true if name is a valid name
*/
boolean isValidMethodName(@NotNull String name);
/**
* Checks whether name is a valid parameter name in the current language
* @param name name to checks
* @return true if name is a valid name
*/
boolean isValidParameterName(@NotNull String name);
/**
* Checks whether name is a valid field name in the current language
* @param name name to checks
* @return true if name is a valid name
*/
boolean isValidFieldName(@NotNull String name);
/**
* Checks whether name is a valid local variable name in the current language
* @param name name to checks
* @return true if name is a valid name
*/
boolean isValidLocalVariableName(@NotNull String name);
}

View File

@@ -11,7 +11,6 @@
<orderEntry type="module" module-name="core-impl" exported="" />
<orderEntry type="module" module-name="resources-en" />
<orderEntry type="library" name="asm4" level="project" />
<orderEntry type="module" module-name="indexing-api" />
</component>
</module>

View File

@@ -182,7 +182,8 @@ public class PsiElementFactoryImpl extends PsiJavaParserFacadeImpl implements Ps
public PsiTypeParameter createTypeParameter(String name, PsiClassType[] superTypes) {
@NonNls StringBuilder builder = new StringBuilder();
builder.append("public <").append(name);
if (superTypes.length > 1) {
if (superTypes.length > 1 ||
superTypes.length == 1 && !superTypes[0].equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) {
builder.append(" extends ");
for (PsiClassType type : superTypes) {
if (type.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) continue;
@@ -755,4 +756,33 @@ public class PsiElementFactoryImpl extends PsiJavaParserFacadeImpl implements Ps
GeneratedMarkerVisitor.markGenerated(catchSection);
return catchSection;
}
@Override
public boolean isValidClassName(@NotNull String name) {
return isIdentifier(name);
}
@Override
public boolean isValidMethodName(@NotNull String name) {
return isIdentifier(name);
}
@Override
public boolean isValidParameterName(@NotNull String name) {
return isIdentifier(name);
}
@Override
public boolean isValidFieldName(@NotNull String name) {
return isIdentifier(name);
}
@Override
public boolean isValidLocalVariableName(@NotNull String name) {
return isIdentifier(name);
}
private boolean isIdentifier(@NotNull String name) {
return JavaPsiFacade.getInstance(myManager.getProject()).getNameHelper().isIdentifier(name);
}
}

View File

@@ -46,7 +46,15 @@ public class SourceUtil {
@Override
public void visitLeaf(LeafElement leaf) {
if (!REF_FILTER.contains(leaf.getElementType())) {
buffer.append(leaf.getText());
String leafText = leaf.getText();
if (buffer.length() > 0 && leafText.length() > 0 && Character.isJavaIdentifierPart(leafText.charAt(0))) {
char lastInBuffer = buffer.charAt(buffer.length() - 1);
if (lastInBuffer == '?' || Character.isJavaIdentifierPart(lastInBuffer)) {
buffer.append(" ");
}
}
buffer.append(leafText);
}
}

View File

@@ -0,0 +1,31 @@
import java.jang.String;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
class PsiElement {
PsiFile getContainingFile() {
return null;
}
}
class PsiFile {
VirtualFile getVirtualFile() {
return null;
}
}
class VirtualFile {}
public class TestCompletion {
PsiElement e;
public void method() {
VirtualFile vf = <caret>
}
}

View File

@@ -0,0 +1,30 @@
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public class TestIndex {
public void statMethod(PsiElement e) {
e.getContainingFile().getVirtualFile();
e.getContainingFile().getVirtualFile();
e.getContainingFile().getVirtualFile();
e.getContainingFile().getVirtualFile();
e.getContainingFile().getVirtualFile();
e.getContainingFile().getVirtualFile();
e.getContainingFile().getVirtualFile();
e.getContainingFile().getVirtualFile();
}
}
class PsiElement {
PsiFile getContainingFile() {
return null;
}
}
class PsiFile {
VirtualFile getVirtualFile() {
return null;
}
}
class VirtualFile {}

View File

@@ -0,0 +1,36 @@
import java.jang.String;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
interface PsiFile extends PsiElement {
}
interface PsiElement {
PsiElement findElementAt(int position);
}
class PsiManager {
static PsiManager getInstance(Project p) {
return null;
}
PsiFile findFile(VirtualFile vf) {
return null;
}
}
interface VirtualFile {}
interface Project {}
public class TestCompletion {
public void method() {
PsiElement e = <caret>
}
}

Some files were not shown because too many files have changed in this diff Show More