From 5404261268711a74291bab8adf08ef07df991b64 Mon Sep 17 00:00:00 2001 From: peter Date: Tue, 22 May 2012 18:11:42 +0200 Subject: [PATCH 01/26] IDEA-86160 Constant conditions & exceptions: no highlighting when one is expected --- .../dataFlow/ControlFlowAnalyzer.java | 41 ++++++++----------- .../fixture/StringTernaryAlwaysTrue.java | 12 ++++++ .../DataFlowInspectionFixtureTest.java | 1 + 3 files changed, 31 insertions(+), 23 deletions(-) create mode 100644 java/java-tests/testData/inspection/dataFlow/fixture/StringTernaryAlwaysTrue.java diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java index f51ac1972979..51e886a9d872 100644 --- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java @@ -1075,38 +1075,33 @@ class ControlFlowAnalyzer extends JavaElementVisitor { startElement(expression); DfaValue dfaValue = myFactory.create(expression); - if (dfaValue != null) { - addInstruction(new PushInstruction(dfaValue, expression)); - } - else { - PsiExpression condition = expression.getCondition(); + PsiExpression condition = expression.getCondition(); - PsiExpression thenExpression = expression.getThenExpression(); - PsiExpression elseExpression = expression.getElseExpression(); + PsiExpression thenExpression = expression.getThenExpression(); + PsiExpression elseExpression = expression.getElseExpression(); - final int elseOffset = elseExpression == null ? getEndOffset(expression) - 1 : getStartOffset(elseExpression); - if (thenExpression != null) { - condition.accept(this); - generateBoxingUnboxingInstructionFor(condition, PsiType.BOOLEAN); - PsiType type = expression.getType(); - addInstruction(new ConditionalGotoInstruction(elseOffset, true, condition)); - thenExpression.accept(this); - generateBoxingUnboxingInstructionFor(thenExpression,type); + final int elseOffset = elseExpression == null ? getEndOffset(expression) - 1 : getStartOffset(elseExpression); + if (thenExpression != null) { + condition.accept(this); + generateBoxingUnboxingInstructionFor(condition, PsiType.BOOLEAN); + PsiType type = expression.getType(); + addInstruction(new ConditionalGotoInstruction(elseOffset, true, condition)); + thenExpression.accept(this); + generateBoxingUnboxingInstructionFor(thenExpression,type); - addInstruction(new GotoInstruction(getEndOffset(expression))); + addInstruction(new GotoInstruction(getEndOffset(expression))); - if (elseExpression != null) { - elseExpression.accept(this); - generateBoxingUnboxingInstructionFor(elseExpression,type); - } - else { - pushUnknown(); - } + if (elseExpression != null) { + elseExpression.accept(this); + generateBoxingUnboxingInstructionFor(elseExpression,type); } else { pushUnknown(); } } + else { + pushUnknown(); + } finishElement(expression); } diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/StringTernaryAlwaysTrue.java b/java/java-tests/testData/inspection/dataFlow/fixture/StringTernaryAlwaysTrue.java new file mode 100644 index 000000000000..93255d9a79b8 --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/StringTernaryAlwaysTrue.java @@ -0,0 +1,12 @@ +class Foo { + public void foo() { + final boolean flag = true; + + bar(flag ? "a" : "b", + flag ? new String[]{"aa"} : new String[]{"bb"} + ); + } + + void bar(String b, String[] a) { + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionFixtureTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionFixtureTest.java index b8c5012f562e..768679bdb501 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionFixtureTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionFixtureTest.java @@ -62,5 +62,6 @@ public class DataFlowInspectionFixtureTest extends JavaCodeInsightFixtureTestCas public void testIDEA84489() throws Throwable { doTest(); } public void testComparingToNotNullShouldNotAffectNullity() throws Throwable { doTest(); } + public void testStringTernaryAlwaysTrue() throws Throwable { doTest(); } } From 575423b51cd0331e817195ea1351335604d278c2 Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 23 May 2012 10:41:23 +0200 Subject: [PATCH 02/26] IDEA-86317 Getting a revision from Perforce when a file is not added to a changelist hangs the UI --- .../vcs/history/FileHistoryPanelImpl.java | 59 +++++++++---------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistoryPanelImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistoryPanelImpl.java index 3bbaed4fbef4..af08b7c08ae5 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistoryPanelImpl.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistoryPanelImpl.java @@ -21,6 +21,7 @@ import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.command.CommandProcessor; +import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.fileEditor.OpenFileDescriptor; @@ -1023,7 +1024,7 @@ public class FileHistoryPanelImpl extends PanelWithActionsAndCloseButton { new Task.Backgroundable(project, VcsBundle.message("show.diff.progress.title")) { @Override public void run(@NotNull ProgressIndicator indicator) { - LocalHistoryAction action = file != null ? startLocalHistoryAction(revision) : LocalHistoryAction.NULL; + final LocalHistoryAction action = file != null ? startLocalHistoryAction(revision) : LocalHistoryAction.NULL; final byte[] revisionContent; try { revisionContent = VcsHistoryUtil.loadRevisionContent(revision); @@ -1049,39 +1050,37 @@ public class FileHistoryPanelImpl extends PanelWithActionsAndCloseButton { return; } - try { - UIUtil.invokeAndWaitIfNeeded(new Runnable() { - @Override public void run() { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - public void run() { - CommandProcessor.getInstance().executeCommand(project, new Runnable() { - public void run() { - if (file != null && !file.isWritable()) { - if (ReadonlyStatusHandler.getInstance(project).ensureFilesWritable(file).hasReadonlyFiles()) { - return; - } - } + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + try { + new WriteCommandAction.Simple(project) { + @Override + protected void run() throws Throwable { + if (file != null && + !file.isWritable() && + ReadonlyStatusHandler.getInstance(project).ensureFilesWritable(file).hasReadonlyFiles()) { + return; + } - try { - write(revisionContent); - } catch (IOException e) { - Messages.showMessageDialog(VcsBundle.message("message.text.cannot.save.content", e.getLocalizedMessage()), - VcsBundle.message( "message.title.get.revision.content"), Messages.getErrorIcon()); - } - } - }, createGetActionTitle(revision), null); + try { + write(revisionContent); + } + catch (IOException e) { + Messages.showMessageDialog(VcsBundle.message("message.text.cannot.save.content", e.getLocalizedMessage()), + VcsBundle.message("message.title.get.revision.content"), Messages.getErrorIcon()); + } } - }); + }.execute(); + if (file != null) { + VcsDirtyScopeManager.getInstance(project).fileDirty(file); + } + } + finally { + action.finish(); } - }); - - if (file != null) { - VcsDirtyScopeManager.getInstance(project).fileDirty(file); } - } - finally { - action.finish(); - } + }); } }.queue(); } From 7ef272a323c19aff988ab99aae4997fc564ccf57 Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 23 May 2012 12:46:48 +0200 Subject: [PATCH 03/26] less garbage when checking intention applicability on every caret move --- .../intention/impl/config/IntentionActionWrapper.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/config/IntentionActionWrapper.java b/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/config/IntentionActionWrapper.java index 685db4510e68..37a1d3b89ef1 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/config/IntentionActionWrapper.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/config/IntentionActionWrapper.java @@ -32,6 +32,7 @@ public class IntentionActionWrapper implements IntentionAction { private IntentionAction myDelegate; private final String[] myCategories; private final IntentionActionBean myExtension; + private String myFullFamilyName; public IntentionActionWrapper(final IntentionActionBean extension, String[] categories) { myExtension = extension; @@ -60,13 +61,13 @@ public class IntentionActionWrapper implements IntentionAction { return getDelegate().startInWriteAction(); } + @NotNull public String getFullFamilyName(){ - if (myCategories != null) { - return StringUtil.join(myCategories, "/") + "/" + getFamilyName(); - } - else { - return getFamilyName(); + String result = myFullFamilyName; + if (result == null) { + myFullFamilyName = result = myCategories != null ? StringUtil.join(myCategories, "/") + "/" + getFamilyName() : getFamilyName(); } + return result; } public synchronized IntentionAction getDelegate() { From f6414038977d740ab8486ac2c0aa2b1bb53eb83a Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 23 May 2012 13:15:11 +0200 Subject: [PATCH 04/26] less garbage during editor painting --- .../editor/markup/AttributesFlyweight.java | 59 ++++++++++++++++++- 1 file changed, 56 insertions(+), 3 deletions(-) diff --git a/platform/platform-api/src/com/intellij/openapi/editor/markup/AttributesFlyweight.java b/platform/platform-api/src/com/intellij/openapi/editor/markup/AttributesFlyweight.java index b6ea12c0257f..10ce05c95d97 100644 --- a/platform/platform-api/src/com/intellij/openapi/editor/markup/AttributesFlyweight.java +++ b/platform/platform-api/src/com/intellij/openapi/editor/markup/AttributesFlyweight.java @@ -29,7 +29,45 @@ import java.awt.*; public class AttributesFlyweight { private final int myHashCode; - private static final StripedLockConcurrentHashMap entries = new StripedLockConcurrentHashMap(); + private static final StripedLockConcurrentHashMap entries = new StripedLockConcurrentHashMap(); + private static final ThreadLocal ourKey = new ThreadLocal(); + + private static class FlyweightKey { + Color foreground; + Color background; + @JdkConstants.FontStyle int fontType; + Color effectColor; + EffectType effectType; + Color errorStripeColor; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof FlyweightKey)) return false; + + FlyweightKey key = (FlyweightKey)o; + + if (fontType != key.fontType) return false; + if (background != null ? !background.equals(key.background) : key.background != null) return false; + if (effectColor != null ? !effectColor.equals(key.effectColor) : key.effectColor != null) return false; + if (effectType != key.effectType) return false; + if (errorStripeColor != null ? !errorStripeColor.equals(key.errorStripeColor) : key.errorStripeColor != null) return false; + if (foreground != null ? !foreground.equals(key.foreground) : key.foreground != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = foreground != null ? foreground.hashCode() : 0; + result = 31 * result + (background != null ? background.hashCode() : 0); + result = 31 * result + fontType; + result = 31 * result + (effectColor != null ? effectColor.hashCode() : 0); + result = 31 * result + (effectType != null ? effectType.hashCode() : 0); + result = 31 * result + (errorStripeColor != null ? errorStripeColor.hashCode() : 0); + return result; + } + } @NotNull public static AttributesFlyweight create(Color foreground, @@ -38,8 +76,23 @@ public class AttributesFlyweight { Color effectColor, EffectType effectType, Color errorStripeColor) { - AttributesFlyweight key = new AttributesFlyweight(foreground, background, fontType, effectColor, effectType, errorStripeColor); - return ConcurrencyUtil.cacheOrGet(entries, key, key); + FlyweightKey key = ourKey.get(); + if (key == null) { + ourKey.set(key = new FlyweightKey()); + } + key.foreground = foreground; + key.background = background; + key.fontType = fontType; + key.effectColor = effectColor; + key.effectType = effectType; + key.errorStripeColor = errorStripeColor; + + AttributesFlyweight flyweight = entries.get(key); + if (flyweight != null) { + return flyweight; + } + + return ConcurrencyUtil.cacheOrGet(entries, key, new AttributesFlyweight(foreground, background, fontType, effectColor, effectType, errorStripeColor)); } private final Color myForeground; From b6d2b5fa370d0d3989d91168bf898980e922af66 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Wed, 23 May 2012 13:29:37 +0400 Subject: [PATCH 05/26] [git] Diagnostics for the non-Git root case. GitRepositoryManager#getRepositoryForFile: if the vcs is not Git, return null, but add logging information. That way, when an assertion in the calling code fails, we can possibly see that it's because of incorrect VCS configuration. --- .../src/git4idea/repo/GitRepositoryManagerImpl.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/plugins/git4idea/src/git4idea/repo/GitRepositoryManagerImpl.java b/plugins/git4idea/src/git4idea/repo/GitRepositoryManagerImpl.java index e0ee250f464e..c8129a70249f 100644 --- a/plugins/git4idea/src/git4idea/repo/GitRepositoryManagerImpl.java +++ b/plugins/git4idea/src/git4idea/repo/GitRepositoryManagerImpl.java @@ -23,6 +23,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.vcs.AbstractVcs; import com.intellij.openapi.vcs.ProjectLevelVcsManager; +import com.intellij.openapi.vcs.VcsRoot; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ArrayUtil; import git4idea.GitUtil; @@ -97,9 +98,13 @@ public class GitRepositoryManagerImpl extends AbstractProjectComponent implement @Override @Nullable public GitRepository getRepositoryForFile(@NotNull VirtualFile file) { - final VirtualFile vcsRoot = myVcsManager.getVcsRootFor(file); + final VcsRoot vcsRoot = myVcsManager.getVcsRootObjectFor(file); if (vcsRoot == null) { return null; } - return getRepositoryForRoot(vcsRoot); + if (!myVcs.equals(vcsRoot.vcs)) { + LOG.info(String.format("getRepositoryForFile returned non-Git (%s) root for file %s", vcsRoot.vcs.getDisplayName(), file)); + return null; + } + return getRepositoryForRoot(vcsRoot.path); } @Override From 261858723a8a6976e9bad44a54f11d9122640a5d Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Wed, 23 May 2012 15:44:54 +0400 Subject: [PATCH 06/26] [git] deprecate GitUtil.getGitRoot... methods, change in GitChangeProvider Ground: GitUtil.getGitRootOrNull is expensive, because it walks through a file system, which may be a hotspot when called from GitChangesCollector with thousand of dirty files. There is no need to get the actual .git root: we can get the VCS root (defined in the Settings), because we notify user about Git roots problems anyway. Changes: Mark GitUtil.getGitRoot... methods as deprecated. Change the code in GitChangesCollector to use ProjectLevelVcsManager#getVcsRootFor. --- plugins/git4idea/src/git4idea/GitUtil.java | 21 ++++++++++ .../git4idea/status/GitChangeProvider.java | 20 ++++++---- .../git4idea/status/GitChangesCollector.java | 40 ++++++++++--------- .../status/GitNewChangesCollector.java | 10 ++--- .../status/GitOldChangesCollector.java | 20 ++++------ 5 files changed, 69 insertions(+), 42 deletions(-) diff --git a/plugins/git4idea/src/git4idea/GitUtil.java b/plugins/git4idea/src/git4idea/GitUtil.java index 41db3cd4f414..4c4879a483ab 100644 --- a/plugins/git4idea/src/git4idea/GitUtil.java +++ b/plugins/git4idea/src/git4idea/GitUtil.java @@ -298,6 +298,9 @@ public class GitUtil { * @return git root for the file * @throws IllegalArgumentException if the file is not under git * @throws VcsException if the file is not under git + * + * @deprecated because uses the java.io.File. + * @use GitRepositoryManager#getRepositoryForFile(). */ public static VirtualFile getGitRoot(final FilePath filePath) throws VcsException { VirtualFile root = getGitRootOrNull(filePath); @@ -312,7 +315,11 @@ public class GitUtil { * * @param filePath a file path * @return git root for the file or null if the file is not under git + * + * @deprecated because uses the java.io.File. + * @use GitRepositoryManager#getRepositoryForFile(). */ + @Deprecated @Nullable public static VirtualFile getGitRootOrNull(final FilePath filePath) { return getGitRootOrNull(filePath.getIOFile()); @@ -322,6 +329,11 @@ public class GitUtil { return file != null && file.exists() && file.isDirectory() && new File(file, DOT_GIT).exists(); } + /** + * @deprecated because uses the java.io.File. + * @use GitRepositoryManager#getRepositoryForFile(). + */ + @Deprecated @Nullable public static VirtualFile getGitRootOrNull(final File file) { File root = file; @@ -337,6 +349,9 @@ public class GitUtil { * @param file the file to check * @return git root for the file * @throws VcsException if the file is not under git + * + * @deprecated because uses the java.io.File. + * @use GitRepositoryManager#getRepositoryForFile(). */ public static VirtualFile getGitRoot(@NotNull final VirtualFile file) throws VcsException { final VirtualFile root = gitRootOrNull(file); @@ -353,6 +368,9 @@ public class GitUtil { * * @param file the file to check * @return git root for the file or null if the file is not not under Git + * + * @deprecated because uses the java.io.File. + * @use GitRepositoryManager#getRepositoryForFile(). */ @Nullable public static VirtualFile gitRootOrNull(final VirtualFile file) { @@ -376,6 +394,9 @@ public class GitUtil { * @param project the project * @param vcs the git Vcs * @return the list of the roots + * + * @deprecated because uses the java.io.File. + * @use GitRepositoryManager#getRepositoryForFile(). */ @NotNull public static List getGitRoots(Project project, GitVcs vcs) throws VcsException { diff --git a/plugins/git4idea/src/git4idea/status/GitChangeProvider.java b/plugins/git4idea/src/git4idea/status/GitChangeProvider.java index ab5754660478..94b3e69e7e47 100644 --- a/plugins/git4idea/src/git4idea/status/GitChangeProvider.java +++ b/plugins/git4idea/src/git4idea/status/GitChangeProvider.java @@ -62,6 +62,11 @@ public class GitChangeProvider implements ChangeProvider { final ChangelistBuilder builder, final ProgressIndicator progress, final ChangeListManagerGate addGate) throws VcsException { + final GitVcs vcs = GitVcs.getInstance(myProject); + if (vcs == null) { + // already disposed or not yet initialized => ignoring + return; + } final Collection affected = dirtyScope.getAffectedContentRootsWithCheck(); if (dirtyScope.getAffectedContentRoots().size() != affected.size()) { @@ -78,8 +83,10 @@ public class GitChangeProvider implements ChangeProvider { myFileDocumentManager, myVcsManager); for (VirtualFile root : roots) { GitChangesCollector collector = isNewGitChangeProviderAvailable() - ? GitNewChangesCollector.collect(myProject, myGit, myChangeListManager, dirtyScope, root) - : GitOldChangesCollector.collect(myProject, myChangeListManager, dirtyScope, root); + ? GitNewChangesCollector.collect(myProject, myGit, myChangeListManager, myVcsManager, + vcs, dirtyScope, root) + : GitOldChangesCollector.collect(myProject, myChangeListManager, myVcsManager, + vcs, dirtyScope, root); holder.changed(collector.getChanges()); for (Change file : collector.getChanges()) { builder.processChange(file, GitVcs.getKey()); @@ -90,11 +97,10 @@ public class GitChangeProvider implements ChangeProvider { } holder.feedBuilder(builder); } - } catch (VcsException e) {// most probably the error happened because git is not configured - final GitVcs vcs = GitVcs.getInstance(myProject); - if (vcs != null) { - vcs.getExecutableValidator().showNotificationOrThrow(e); - } + } + catch (VcsException e) { + // most probably the error happened because git is not configured + vcs.getExecutableValidator().showNotificationOrThrow(e); } } diff --git a/plugins/git4idea/src/git4idea/status/GitChangesCollector.java b/plugins/git4idea/src/git4idea/status/GitChangesCollector.java index 92058bc189d0..902ddea321c7 100644 --- a/plugins/git4idea/src/git4idea/status/GitChangesCollector.java +++ b/plugins/git4idea/src/git4idea/status/GitChangesCollector.java @@ -17,13 +17,12 @@ package git4idea.status; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.vcs.FilePath; -import com.intellij.openapi.vcs.FilePathImpl; +import com.intellij.openapi.vcs.*; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vcs.changes.ChangeListManager; +import com.intellij.openapi.vcs.changes.ContentRevision; import com.intellij.openapi.vcs.changes.VcsDirtyScope; import com.intellij.openapi.vfs.VirtualFile; -import git4idea.GitUtil; import org.jetbrains.annotations.NotNull; import java.io.File; @@ -37,18 +36,21 @@ import java.util.*; * @author Kirill Likhodedov */ abstract class GitChangesCollector { - protected final Project myProject; - protected final VirtualFile myVcsRoot; - private final ChangeListManager myChangeListManager; - private final VcsDirtyScope myDirtyScope; + @NotNull protected final Project myProject; + @NotNull protected final VirtualFile myVcsRoot; + + @NotNull private final VcsDirtyScope myDirtyScope; + @NotNull private final ChangeListManager myChangeListManager; + @NotNull private final ProjectLevelVcsManager myVcsManager; + @NotNull private AbstractVcs myVcs; - GitChangesCollector(@NotNull Project project, - @NotNull ChangeListManager changeListManager, - @NotNull VcsDirtyScope dirtyScope, - @NotNull VirtualFile vcsRoot) { + GitChangesCollector(@NotNull Project project, @NotNull ChangeListManager changeListManager, @NotNull ProjectLevelVcsManager vcsManager, + @NotNull AbstractVcs vcs, @NotNull VcsDirtyScope dirtyScope, @NotNull VirtualFile vcsRoot) { myProject = project; myChangeListManager = changeListManager; + myVcsManager = vcsManager; + myVcs = vcs; myDirtyScope = dirtyScope; myVcsRoot = vcsRoot; } @@ -86,11 +88,13 @@ abstract class GitChangesCollector { case NEW: case DELETED: case MOVED: - if (c.getAfterRevision() != null) { - addToPaths(c.getAfterRevision().getFile(), allPaths); + ContentRevision afterRevision = c.getAfterRevision(); + if (afterRevision != null) { + addToPaths(afterRevision.getFile(), allPaths); } - if (c.getBeforeRevision() != null) { - addToPaths(c.getBeforeRevision().getFile(), allPaths); + ContentRevision beforeRevision = c.getBeforeRevision(); + if (beforeRevision != null) { + addToPaths(beforeRevision.getFile(), allPaths); } case MODIFICATION: default: @@ -114,9 +118,9 @@ abstract class GitChangesCollector { } protected void addToPaths(FilePath pathToAdd, List paths) { - File file = pathToAdd.getIOFile(); - if (myVcsRoot.equals(GitUtil.getGitRootOrNull(file))) { - paths.add(file.getPath()); + VcsRoot fileRoot = myVcsManager.getVcsRootObjectFor(pathToAdd); + if (fileRoot != null && fileRoot.vcs != null && fileRoot.vcs.equals(myVcs) && myVcsRoot.equals(fileRoot.path)) { + paths.add(pathToAdd.getPath()); } } diff --git a/plugins/git4idea/src/git4idea/status/GitNewChangesCollector.java b/plugins/git4idea/src/git4idea/status/GitNewChangesCollector.java index 983e493813cf..6d1afc991059 100644 --- a/plugins/git4idea/src/git4idea/status/GitNewChangesCollector.java +++ b/plugins/git4idea/src/git4idea/status/GitNewChangesCollector.java @@ -18,9 +18,7 @@ package git4idea.status; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vcs.FilePath; -import com.intellij.openapi.vcs.FileStatus; -import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.*; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vcs.changes.ChangeListManager; import com.intellij.openapi.vcs.changes.ContentRevision; @@ -70,8 +68,9 @@ class GitNewChangesCollector extends GitChangesCollector { */ @NotNull static GitNewChangesCollector collect(@NotNull Project project, @NotNull Git git, @NotNull ChangeListManager changeListManager, + @NotNull ProjectLevelVcsManager vcsManager, @NotNull AbstractVcs vcs, @NotNull VcsDirtyScope dirtyScope, @NotNull VirtualFile vcsRoot) throws VcsException { - return new GitNewChangesCollector(project, git, changeListManager, dirtyScope, vcsRoot); + return new GitNewChangesCollector(project, git, changeListManager, vcsManager, vcs, dirtyScope, vcsRoot); } @Override @@ -87,9 +86,10 @@ class GitNewChangesCollector extends GitChangesCollector { } private GitNewChangesCollector(@NotNull Project project, @NotNull Git git, @NotNull ChangeListManager changeListManager, + @NotNull ProjectLevelVcsManager vcsManager, @NotNull AbstractVcs vcs, @NotNull VcsDirtyScope dirtyScope, @NotNull VirtualFile vcsRoot) throws VcsException { - super(project, changeListManager, dirtyScope, vcsRoot); + super(project, changeListManager, vcsManager, vcs, dirtyScope, vcsRoot); myGit = git; myRepository = GitUtil.getRepositoryManager(myProject).getRepositoryForRoot(vcsRoot); diff --git a/plugins/git4idea/src/git4idea/status/GitOldChangesCollector.java b/plugins/git4idea/src/git4idea/status/GitOldChangesCollector.java index 52ed9458f353..518efac8ca8d 100644 --- a/plugins/git4idea/src/git4idea/status/GitOldChangesCollector.java +++ b/plugins/git4idea/src/git4idea/status/GitOldChangesCollector.java @@ -16,9 +16,7 @@ package git4idea.status; import com.intellij.openapi.project.Project; -import com.intellij.openapi.vcs.FilePath; -import com.intellij.openapi.vcs.FileStatus; -import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.*; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vcs.changes.ChangeListManager; import com.intellij.openapi.vcs.changes.ContentRevision; @@ -104,11 +102,10 @@ class GitOldChangesCollector extends GitChangesCollector { * This may be lengthy. */ @NotNull - static GitOldChangesCollector collect(@NotNull Project project, - @NotNull ChangeListManager changeListManager, - @NotNull VcsDirtyScope dirtyScope, - @NotNull VirtualFile vcsRoot) throws VcsException { - return new GitOldChangesCollector(project, changeListManager, dirtyScope, vcsRoot); + static GitOldChangesCollector collect(@NotNull Project project, @NotNull ChangeListManager changeListManager, + @NotNull ProjectLevelVcsManager vcsManager, @NotNull AbstractVcs vcs, + @NotNull VcsDirtyScope dirtyScope, @NotNull VirtualFile vcsRoot) throws VcsException { + return new GitOldChangesCollector(project, changeListManager, vcsManager, vcs, dirtyScope, vcsRoot); } @NotNull @@ -123,11 +120,10 @@ class GitOldChangesCollector extends GitChangesCollector { return myChanges; } - private GitOldChangesCollector(@NotNull Project project, - @NotNull ChangeListManager changeListManager, - @NotNull VcsDirtyScope dirtyScope, + private GitOldChangesCollector(@NotNull Project project, @NotNull ChangeListManager changeListManager, + @NotNull ProjectLevelVcsManager vcsManager, @NotNull AbstractVcs vcs, @NotNull VcsDirtyScope dirtyScope, @NotNull VirtualFile vcsRoot) throws VcsException { - super(project, changeListManager, dirtyScope, vcsRoot); + super(project, changeListManager, vcsManager, vcs, dirtyScope, vcsRoot); updateIndex(); collectUnmergedAndUnversioned(); collectDiffChanges(); From 1b02ad2fc38294cfcb61fca189069f4d677c797d Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Wed, 23 May 2012 15:46:25 +0400 Subject: [PATCH 07/26] [git] Don't ignore exceptions. There are no checked exceptions thrown in GitChangesCollector#dirtyPaths. Don't explicitly ignore unchecked ones as well. --- .../git4idea/status/GitChangesCollector.java | 37 ++++++++----------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/plugins/git4idea/src/git4idea/status/GitChangesCollector.java b/plugins/git4idea/src/git4idea/status/GitChangesCollector.java index 902ddea321c7..0d7869471475 100644 --- a/plugins/git4idea/src/git4idea/status/GitChangesCollector.java +++ b/plugins/git4idea/src/git4idea/status/GitChangesCollector.java @@ -82,29 +82,24 @@ abstract class GitChangesCollector { } if (includeChanges) { - try { - for (Change c : myChangeListManager.getChangesIn(myVcsRoot)) { - switch (c.getType()) { - case NEW: - case DELETED: - case MOVED: - ContentRevision afterRevision = c.getAfterRevision(); - if (afterRevision != null) { - addToPaths(afterRevision.getFile(), allPaths); - } - ContentRevision beforeRevision = c.getBeforeRevision(); - if (beforeRevision != null) { - addToPaths(beforeRevision.getFile(), allPaths); - } - case MODIFICATION: - default: - // do nothing - } + for (Change c : myChangeListManager.getChangesIn(myVcsRoot)) { + switch (c.getType()) { + case NEW: + case DELETED: + case MOVED: + ContentRevision afterRevision = c.getAfterRevision(); + if (afterRevision != null) { + addToPaths(afterRevision.getFile(), allPaths); + } + ContentRevision beforeRevision = c.getBeforeRevision(); + if (beforeRevision != null) { + addToPaths(beforeRevision.getFile(), allPaths); + } + case MODIFICATION: + default: + // do nothing } } - catch (Exception t) { - // ignore exceptions - } } removeCommonParents(allPaths); From 08333eac553e16e9717f8013328ef4179367d501 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Wed, 23 May 2012 16:10:07 +0400 Subject: [PATCH 08/26] Split leaf/composite Javadoc element --- .../java/JavaSpacePropertyProcessor.java | 13 ++++----- .../psi/impl/source/tree/JavaASTFactory.java | 5 ++-- .../lang/java/parser/JavadocParser.java | 12 ++++---- .../PsiJavaCodeReferenceElementImpl.java | 4 +-- .../javadoc/CorePsiDocTagValueImpl.java | 4 ++- .../impl/source/javadoc/PsiDocParamRef.java | 11 ++----- .../impl/source/javadoc/PsiDocTagImpl.java | 29 ++++++++----------- .../java => javadoc}/PsiInlineDocTagImpl.java | 25 +++++++--------- .../impl/source/tree/CoreJavaASTFactory.java | 5 ++-- .../impl/source/tree/JavaDocElementType.java | 9 +++--- .../parser-full/javadocParsing/Exception0.txt | 2 +- .../parser-full/javadocParsing/InlineTag1.txt | 2 +- .../parser-full/javadocParsing/InlineTag2.txt | 2 +- .../parser-full/javadocParsing/InlineTag3.txt | 2 +- .../javadocParsing/LinkPlainTag1.txt | 2 +- .../javadocParsing/LinkPlainTag2.txt | 2 +- .../parser-full/javadocParsing/LinkTag1.txt | 2 +- .../parser-full/javadocParsing/LinkTag2.txt | 2 +- .../parser-full/javadocParsing/LinkTag3.txt | 2 +- .../parser-full/javadocParsing/LinkTag4.txt | 2 +- .../parser-full/javadocParsing/LinkTag5.txt | 2 +- .../parser-full/javadocParsing/LinkTag6.txt | 2 +- .../parser-full/javadocParsing/SeeTag10.txt | 2 +- .../parser-full/javadocParsing/SeeTag11.txt | 2 +- .../parser-full/javadocParsing/SeeTag12.txt | 2 +- .../parser-full/javadocParsing/SeeTag14.txt | 2 +- .../parser-full/javadocParsing/SeeTag15.txt | 2 +- .../parser-full/javadocParsing/SeeTag16.txt | 2 +- .../parser-full/javadocParsing/SeeTag3.txt | 2 +- .../parser-full/javadocParsing/SeeTag5.txt | 2 +- .../parser-full/javadocParsing/SeeTag9.txt | 6 ++-- .../psi/parser-full/javadocParsing/Tag2.txt | 2 +- .../psi/parser-full/javadocParsing/Tag5.txt | 2 +- .../psi/formatter/java/JavaFormatterTest.java | 19 ++++++++++-- 34 files changed, 94 insertions(+), 92 deletions(-) rename java/java-psi-impl/src/com/intellij/psi/impl/source/{tree/java => javadoc}/PsiInlineDocTagImpl.java (81%) diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java b/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java index 8da301fb80c6..c1ab84ce0bb0 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java @@ -125,13 +125,13 @@ public class JavaSpacePropertyProcessor extends JavaElementVisitor { if (node == null) { return true; } + final IElementType type = node.getElementType(); - if (type == JavaDocTokenType.DOC_TAG_VALUE_TOKEN) { + if (type == JavaDocElementType.DOC_TAG_VALUE_ELEMENT) { return PsiTreeUtil.getParentOfType(parent, PsiDocMethodOrFieldRef.class) != null; } - return type == JavaDocElementType.DOC_COMMENT || type == JavaDocElementType.DOC_TAG - || type == JavaDocElementType.DOC_INLINE_TAG; + return type == JavaDocElementType.DOC_COMMENT || type == JavaDocElementType.DOC_TAG || type == JavaDocElementType.DOC_INLINE_TAG; } private void init(final ASTNode child) { @@ -1096,9 +1096,8 @@ public class JavaSpacePropertyProcessor extends JavaElementVisitor { else if (myRole1 == ChildRole.OPERATION_SIGN) { createSpaceInCode(mySettings.SPACE_AROUND_UNARY_OPERATOR); } - else if (myChild1.getElementType() == JavaDocTokenType.DOC_TAG_VALUE_TOKEN - && myChild2.getElementType() == JavaDocTokenType.DOC_TAG_VALUE_TOKEN) - { + else if ((myType1 == JavaDocTokenType.DOC_TAG_VALUE_TOKEN || myType1 == JavaDocElementType.DOC_TAG_VALUE_ELEMENT) && + (myType2 == JavaDocTokenType.DOC_TAG_VALUE_TOKEN || myType2 == JavaDocElementType.DOC_TAG_VALUE_ELEMENT)) { createSpaceInCode(true); } else if (myRole1 == ChildRole.COMMA) { @@ -1516,7 +1515,7 @@ public class JavaSpacePropertyProcessor extends JavaElementVisitor { } @Override public void visitDocTag(PsiDocTag tag) { - if (myChild1.getElementType() == JavaDocTokenType.DOC_TAG_NAME && myChild2.getElementType() == JavaDocTokenType.DOC_TAG_VALUE_TOKEN) { + if (myType1 == JavaDocTokenType.DOC_TAG_NAME && myType2 == JavaDocElementType.DOC_TAG_VALUE_ELEMENT) { myResult = Spacing.createSpacing(1, 1, 0, false, 0); } } diff --git a/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaASTFactory.java b/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaASTFactory.java index 9549f72b7ed1..20f7ab1e2f44 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaASTFactory.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaASTFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2012 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. @@ -22,10 +22,9 @@ import com.intellij.psi.tree.IElementType; * @author max */ public class JavaASTFactory extends CoreJavaASTFactory { - @Override public CompositeElement createComposite(final IElementType type) { - if (type == DOC_TAG_VALUE_TOKEN) { + if (type == DOC_TAG_VALUE_ELEMENT) { return new PsiDocTagValueImpl(); } diff --git a/java/java-psi-impl/src/com/intellij/lang/java/parser/JavadocParser.java b/java/java-psi-impl/src/com/intellij/lang/java/parser/JavadocParser.java index b72e4a88d0d5..78abd7fd19e5 100644 --- a/java/java-psi-impl/src/com/intellij/lang/java/parser/JavadocParser.java +++ b/java/java-psi-impl/src/com/intellij/lang/java/parser/JavadocParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2012 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. @@ -147,7 +147,7 @@ public class JavadocParser { final PsiBuilder.Marker tagValue = builder.mark(); builder.remapCurrentToken(JavaDocElementType.DOC_REFERENCE_HOLDER); builder.advanceLexer(); - tagValue.done(JavaDocTokenType.DOC_TAG_VALUE_TOKEN); + tagValue.done(JavaDocElementType.DOC_TAG_VALUE_ELEMENT); } else if (!isInline && tagName != null && tagName.equals(PARAM_TAG)) { parseSimpleTagValue(builder, true); @@ -187,7 +187,7 @@ public class JavadocParser { else { final PsiBuilder.Marker tagValue = builder.mark(); builder.advanceLexer(); - tagValue.done(JavaDocTokenType.DOC_TAG_VALUE_TOKEN); + tagValue.done(JavaDocElementType.DOC_TAG_VALUE_ELEMENT); } } @@ -217,7 +217,7 @@ public class JavadocParser { } } else if (tokenType == JavaDocTokenType.DOC_TAG_VALUE_RPAREN) { - subValue.done(JavaDocTokenType.DOC_TAG_VALUE_TOKEN); + subValue.done(JavaDocElementType.DOC_TAG_VALUE_ELEMENT); builder.advanceLexer(); refStart.done(JavaDocElementType.DOC_METHOD_OR_FIELD_REF); return; @@ -227,7 +227,7 @@ public class JavadocParser { } } - subValue.done(JavaDocTokenType.DOC_TAG_VALUE_TOKEN); + subValue.done(JavaDocElementType.DOC_TAG_VALUE_ELEMENT); } refStart.done(JavaDocElementType.DOC_METHOD_OR_FIELD_REF); @@ -238,7 +238,7 @@ public class JavadocParser { while (TAG_VALUES_SET.contains(getTokenType(builder))) { builder.advanceLexer(); } - tagValue.done(parameter ? JavaDocElementType.DOC_PARAMETER_REF : JavaDocTokenType.DOC_TAG_VALUE_TOKEN); + tagValue.done(parameter ? JavaDocElementType.DOC_PARAMETER_REF : JavaDocElementType.DOC_TAG_VALUE_ELEMENT); } @Nullable diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiJavaCodeReferenceElementImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiJavaCodeReferenceElementImpl.java index c4e790fa9485..f69fdf7127ea 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiJavaCodeReferenceElementImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiJavaCodeReferenceElementImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2012 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. @@ -99,7 +99,7 @@ public class PsiJavaCodeReferenceElementImpl extends CompositePsiElement impleme i == JavaElementType.THIS_EXPRESSION || i == JavaElementType.SUPER_EXPRESSION || i == JavaDocElementType.DOC_METHOD_OR_FIELD_REF || - i == JavaDocTokenType.DOC_TAG_VALUE_TOKEN || + i == JavaDocElementType.DOC_TAG_VALUE_ELEMENT || i == JavaElementType.REFERENCE_PARAMETER_LIST || i == JavaElementType.ANNOTATION) { if (isQualified()) { diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/javadoc/CorePsiDocTagValueImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/javadoc/CorePsiDocTagValueImpl.java index bf790cb612cc..d79afae2c1cc 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/javadoc/CorePsiDocTagValueImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/javadoc/CorePsiDocTagValueImpl.java @@ -21,6 +21,7 @@ import com.intellij.psi.JavaElementVisitor; import com.intellij.psi.PsiElementVisitor; import com.intellij.psi.impl.source.tree.ChildRole; import com.intellij.psi.impl.source.tree.CompositePsiElement; +import com.intellij.psi.impl.source.tree.JavaDocElementType; import com.intellij.psi.javadoc.PsiDocTagValue; import org.jetbrains.annotations.NotNull; @@ -29,7 +30,7 @@ import org.jetbrains.annotations.NotNull; */ public class CorePsiDocTagValueImpl extends CompositePsiElement implements PsiDocTagValue { public CorePsiDocTagValueImpl() { - super(JavaDocTokenType.DOC_TAG_VALUE_TOKEN); + super(JavaDocElementType.DOC_TAG_VALUE_ELEMENT); } @Override @@ -47,6 +48,7 @@ public class CorePsiDocTagValueImpl extends CompositePsiElement implements PsiDo if (child.getElementType() == JavaDocTokenType.DOC_TAG_VALUE_COMMA) { return ChildRole.COMMA; } + return super.getChildRole(child); } } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/javadoc/PsiDocParamRef.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/javadoc/PsiDocParamRef.java index 4fb485baf727..36f7c61047f5 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/javadoc/PsiDocParamRef.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/javadoc/PsiDocParamRef.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2012 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. @@ -18,11 +18,7 @@ package com.intellij.psi.impl.source.javadoc; import com.intellij.lang.ASTNode; import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; -import com.intellij.psi.impl.source.Constants; -import com.intellij.psi.impl.source.tree.CompositePsiElement; -import com.intellij.psi.impl.source.tree.Factory; -import com.intellij.psi.impl.source.tree.LeafElement; -import com.intellij.psi.impl.source.tree.SharedImplUtil; +import com.intellij.psi.impl.source.tree.*; import com.intellij.psi.infos.CandidateInfo; import com.intellij.psi.javadoc.PsiDocComment; import com.intellij.psi.javadoc.PsiDocTag; @@ -43,9 +39,8 @@ import java.util.Set; * @author mike */ public class PsiDocParamRef extends CompositePsiElement implements PsiDocTagValue { - public PsiDocParamRef() { - super(Constants.DOC_PARAMETER_REF); + super(JavaDocElementType.DOC_PARAMETER_REF); } @Override diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/javadoc/PsiDocTagImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/javadoc/PsiDocTagImpl.java index ae89a54207ee..9f2b59ec5538 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/javadoc/PsiDocTagImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/javadoc/PsiDocTagImpl.java @@ -16,13 +16,9 @@ package com.intellij.psi.impl.source.javadoc; import com.intellij.lang.ASTNode; -import com.intellij.psi.JavaElementVisitor; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiElementVisitor; -import com.intellij.psi.PsiReference; +import com.intellij.psi.*; import com.intellij.psi.impl.PsiImplUtil; import com.intellij.psi.impl.source.Constants; -import com.intellij.psi.impl.source.SourceTreeToPsiMap; import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry; import com.intellij.psi.impl.source.tree.ChildRole; import com.intellij.psi.impl.source.tree.CompositePsiElement; @@ -37,9 +33,10 @@ import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; public class PsiDocTagImpl extends CompositePsiElement implements PsiDocTag, Constants { - private static final TokenSet VALUE_BIT_SET = TokenSet.create( - JAVA_CODE_REFERENCE, DOC_TAG_VALUE_TOKEN, DOC_METHOD_OR_FIELD_REF, DOC_PARAMETER_REF, - DOC_COMMENT_DATA, DOC_INLINE_TAG, DOC_REFERENCE_HOLDER); + private static final TokenSet TAG_VALUE_BIT_SET = TokenSet.create( + DOC_TAG_VALUE_ELEMENT, DOC_METHOD_OR_FIELD_REF, DOC_PARAMETER_REF); + private static final TokenSet VALUE_BIT_SET = TokenSet.orSet(TAG_VALUE_BIT_SET, TokenSet.create( + DOC_TAG_VALUE_TOKEN, JAVA_CODE_REFERENCE, DOC_COMMENT_DATA, DOC_INLINE_TAG, DOC_REFERENCE_HOLDER)); public PsiDocTagImpl() { super(DOC_TAG); @@ -47,17 +44,17 @@ public class PsiDocTagImpl extends CompositePsiElement implements PsiDocTag, Con @Override public PsiDocComment getContainingComment() { - return (PsiDocComment)SourceTreeToPsiMap.treeElementToPsi(getTreeParent()); + return (PsiDocComment)getParent(); } @Override public PsiElement getNameElement() { - return findChildByRoleAsPsiElement(ChildRole.DOC_TAG_NAME); + return findPsiChildByType(DOC_TAG_NAME); } @Override public PsiDocTagValue getValueElement() { - return (PsiDocTagValue)findChildByRoleAsPsiElement(ChildRole.DOC_TAG_VALUE); + return (PsiDocTagValue)findPsiChildByType(TAG_VALUE_BIT_SET); } @Override @@ -65,6 +62,7 @@ public class PsiDocTagImpl extends CompositePsiElement implements PsiDocTag, Con return getChildrenAsPsiElements(VALUE_BIT_SET, PsiElement.ARRAY_FACTORY); } + @NotNull @Override public String getName() { if (getNameElement() == null) return ""; @@ -79,7 +77,7 @@ public class PsiDocTagImpl extends CompositePsiElement implements PsiDocTag, Con @Override public int getChildRole(ASTNode child) { - assert (child.getTreeParent() == this); + assert child.getTreeParent() == this : child.getTreeParent(); IElementType i = child.getElementType(); if (i == DOC_TAG_NAME) { return ChildRole.DOC_TAG_NAME; @@ -90,10 +88,7 @@ public class PsiDocTagImpl extends CompositePsiElement implements PsiDocTag, Con else if (i == DOC_COMMENT_LEADING_ASTERISKS) { return ChildRole.DOC_COMMENT_ASTERISKS; } - else if (i == DOC_TAG_VALUE_TOKEN) { - return ChildRole.DOC_TAG_VALUE; - } - else if (i == DOC_METHOD_OR_FIELD_REF || i == DOC_PARAMETER_REF) { + else if (TAG_VALUE_BIT_SET.contains(i)) { return ChildRole.DOC_TAG_VALUE; } else { @@ -104,7 +99,7 @@ public class PsiDocTagImpl extends CompositePsiElement implements PsiDocTag, Con @Override @NotNull public PsiReference[] getReferences() { - return ReferenceProvidersRegistry.getReferencesFromProviders(this, PsiDocTag.class); + return ReferenceProvidersRegistry.getReferencesFromProviders(this, PsiReferenceService.Hints.NO_HINTS); } @Override diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiInlineDocTagImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/javadoc/PsiInlineDocTagImpl.java similarity index 81% rename from java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiInlineDocTagImpl.java rename to java/java-psi-impl/src/com/intellij/psi/impl/source/javadoc/PsiInlineDocTagImpl.java index 5e6e534a1f78..38466a5f6cb1 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiInlineDocTagImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/javadoc/PsiInlineDocTagImpl.java @@ -13,10 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.intellij.psi.impl.source.tree.java; +package com.intellij.psi.impl.source.javadoc; import com.intellij.lang.ASTNode; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.psi.JavaElementVisitor; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementVisitor; @@ -36,11 +35,10 @@ import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; public class PsiInlineDocTagImpl extends CompositePsiElement implements PsiInlineDocTag, Constants { - private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.java.PsiInlineDocTagImpl"); - - private static final TokenSet VALUE_BIT_SET = TokenSet.create( - JAVA_CODE_REFERENCE, DOC_TAG_VALUE_TOKEN, DOC_METHOD_OR_FIELD_REF, DOC_COMMENT_DATA, DOC_INLINE_TAG, - DOC_REFERENCE_HOLDER, WHITE_SPACE, DOC_COMMENT_BAD_CHARACTER); + private static final TokenSet TAG_VALUE_BIT_SET = TokenSet.create( + DOC_TAG_VALUE_ELEMENT, DOC_METHOD_OR_FIELD_REF); + private static final TokenSet VALUE_BIT_SET = TokenSet.orSet(TAG_VALUE_BIT_SET, TokenSet.create( + JAVA_CODE_REFERENCE, DOC_TAG_VALUE_TOKEN, DOC_COMMENT_DATA, DOC_INLINE_TAG, DOC_REFERENCE_HOLDER, WHITE_SPACE, DOC_COMMENT_BAD_CHARACTER)); public PsiInlineDocTagImpl() { super(DOC_INLINE_TAG); @@ -57,7 +55,7 @@ public class PsiInlineDocTagImpl extends CompositePsiElement implements PsiInlin @Override public PsiElement getNameElement() { - return findChildByRoleAsPsiElement(ChildRole.DOC_TAG_NAME); + return findPsiChildByType(DOC_TAG_NAME); } @Override @@ -67,7 +65,7 @@ public class PsiInlineDocTagImpl extends CompositePsiElement implements PsiInlin @Override public PsiDocTagValue getValueElement() { - return (PsiDocTagValue)findChildByRoleAsPsiElement(ChildRole.DOC_TAG_VALUE); + return (PsiDocTagValue)findPsiChildByType(TAG_VALUE_BIT_SET); } @Override @@ -79,7 +77,7 @@ public class PsiInlineDocTagImpl extends CompositePsiElement implements PsiInlin @Override public int getChildRole(ASTNode child) { - LOG.assertTrue(child.getTreeParent() == this); + assert child.getTreeParent() == this : child.getTreeParent(); IElementType i = child.getElementType(); if (i == DOC_TAG_NAME) { return ChildRole.DOC_TAG_NAME; @@ -93,10 +91,7 @@ public class PsiInlineDocTagImpl extends CompositePsiElement implements PsiInlin else if (i == DOC_INLINE_TAG_END) { return ChildRole.DOC_INLINE_TAG_END; } - else if (i == DOC_TAG_VALUE_TOKEN) { - return ChildRole.DOC_TAG_VALUE; - } - else if (i == DOC_METHOD_OR_FIELD_REF) { + else if (TAG_VALUE_BIT_SET.contains(i)) { return ChildRole.DOC_TAG_VALUE; } else { @@ -124,4 +119,4 @@ public class PsiInlineDocTagImpl extends CompositePsiElement implements PsiInlin PsiImplUtil.setName(getNameElement(), name); return this; } -} \ No newline at end of file +} diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/CoreJavaASTFactory.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/CoreJavaASTFactory.java index c9b6e5552676..dc7b6e576871 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/CoreJavaASTFactory.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/CoreJavaASTFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2012 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. @@ -49,6 +49,7 @@ public class CoreJavaASTFactory extends ASTFactory implements Constants { return new PsiJavaTokenImpl(type, text); } else if (type instanceof IJavaDocElementType) { + assert type != DOC_TAG_VALUE_ELEMENT; return new PsiDocTokenImpl(type, text); } @@ -57,7 +58,7 @@ public class CoreJavaASTFactory extends ASTFactory implements Constants { @Override public CompositeElement createComposite(IElementType type) { - if (type == DOC_TAG_VALUE_TOKEN) { + if (type == DOC_TAG_VALUE_ELEMENT) { return new CorePsiDocTagValueImpl(); } return null; diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaDocElementType.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaDocElementType.java index 4c4776cb1653..71df6027483e 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaDocElementType.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaDocElementType.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2012 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. @@ -29,7 +29,7 @@ import com.intellij.psi.impl.source.javadoc.PsiDocCommentImpl; import com.intellij.psi.impl.source.javadoc.PsiDocMethodOrFieldRef; import com.intellij.psi.impl.source.javadoc.PsiDocParamRef; import com.intellij.psi.impl.source.javadoc.PsiDocTagImpl; -import com.intellij.psi.impl.source.tree.java.PsiInlineDocTagImpl; +import com.intellij.psi.impl.source.javadoc.PsiInlineDocTagImpl; import com.intellij.psi.tree.*; import com.intellij.psi.tree.java.IJavaDocElementType; import com.intellij.util.ReflectionUtil; @@ -70,6 +70,7 @@ public interface JavaDocElementType { IElementType DOC_INLINE_TAG = new JavaDocCompositeElementType("DOC_INLINE_TAG", PsiInlineDocTagImpl.class); IElementType DOC_METHOD_OR_FIELD_REF = new JavaDocCompositeElementType("DOC_METHOD_OR_FIELD_REF", PsiDocMethodOrFieldRef.class); IElementType DOC_PARAMETER_REF = new JavaDocCompositeElementType("DOC_PARAMETER_REF", PsiDocParamRef.class); + IElementType DOC_TAG_VALUE_ELEMENT = new IJavaDocElementType("DOC_TAG_VALUE_ELEMENT"); ILazyParseableElementType DOC_REFERENCE_HOLDER = new JavaDocLazyElementType("DOC_REFERENCE_HOLDER") { private final JavaParserUtil.ParserWrapper myParser = new JavaParserUtil.ParserWrapper() { @@ -135,6 +136,6 @@ public interface JavaDocElementType { }; TokenSet ALL_JAVADOC_ELEMENTS = TokenSet.create( - DOC_TAG, DOC_INLINE_TAG, DOC_METHOD_OR_FIELD_REF, DOC_PARAMETER_REF, DOC_REFERENCE_HOLDER, DOC_TYPE_HOLDER, DOC_COMMENT - ); + DOC_TAG, DOC_INLINE_TAG, DOC_METHOD_OR_FIELD_REF, DOC_PARAMETER_REF, DOC_TAG_VALUE_ELEMENT, + DOC_REFERENCE_HOLDER, DOC_TYPE_HOLDER, DOC_COMMENT); } diff --git a/java/java-tests/testData/psi/parser-full/javadocParsing/Exception0.txt b/java/java-tests/testData/psi/parser-full/javadocParsing/Exception0.txt index 9254cba2a145..b3935666fc28 100644 --- a/java/java-tests/testData/psi/parser-full/javadocParsing/Exception0.txt +++ b/java/java-tests/testData/psi/parser-full/javadocParsing/Exception0.txt @@ -25,7 +25,7 @@ PsiJavaFile:Exception0.java PsiDocTag:@throws PsiDocToken:DOC_TAG_NAME('@throws') PsiWhiteSpace(' ') - PsiElement(DOC_TAG_VALUE_TOKEN) + PsiElement(DOC_TAG_VALUE_ELEMENT) PsiElement(DOC_REFERENCE_HOLDER) PsiJavaCodeReferenceElement:java.lang.Exception PsiJavaCodeReferenceElement:java.lang diff --git a/java/java-tests/testData/psi/parser-full/javadocParsing/InlineTag1.txt b/java/java-tests/testData/psi/parser-full/javadocParsing/InlineTag1.txt index 983c2c305f61..1975d6a75cf5 100644 --- a/java/java-tests/testData/psi/parser-full/javadocParsing/InlineTag1.txt +++ b/java/java-tests/testData/psi/parser-full/javadocParsing/InlineTag1.txt @@ -15,7 +15,7 @@ PsiJavaFile:InlineTag1.java PsiDocToken:DOC_TAG_VALUE_SHARP_TOKEN('#') PsiDocToken:DOC_TAG_VALUE_TOKEN('somefunc') PsiDocToken:DOC_TAG_VALUE_LPAREN('(') - PsiElement(DOC_TAG_VALUE_TOKEN) + PsiElement(DOC_TAG_VALUE_ELEMENT) PsiElement(DOC_TYPE_HOLDER) PsiTypeElement:String PsiJavaCodeReferenceElement:String diff --git a/java/java-tests/testData/psi/parser-full/javadocParsing/InlineTag2.txt b/java/java-tests/testData/psi/parser-full/javadocParsing/InlineTag2.txt index 9bba0d6bc228..9165b8960367 100644 --- a/java/java-tests/testData/psi/parser-full/javadocParsing/InlineTag2.txt +++ b/java/java-tests/testData/psi/parser-full/javadocParsing/InlineTag2.txt @@ -15,7 +15,7 @@ PsiJavaFile:InlineTag2.java PsiDocToken:DOC_TAG_VALUE_SHARP_TOKEN('#') PsiDocToken:DOC_TAG_VALUE_TOKEN('somefunc') PsiDocToken:DOC_TAG_VALUE_LPAREN('(') - PsiElement(DOC_TAG_VALUE_TOKEN) + PsiElement(DOC_TAG_VALUE_ELEMENT) PsiElement(DOC_TYPE_HOLDER) PsiTypeElement:String PsiJavaCodeReferenceElement:String diff --git a/java/java-tests/testData/psi/parser-full/javadocParsing/InlineTag3.txt b/java/java-tests/testData/psi/parser-full/javadocParsing/InlineTag3.txt index e74ee40b22bd..fb591f4269c2 100644 --- a/java/java-tests/testData/psi/parser-full/javadocParsing/InlineTag3.txt +++ b/java/java-tests/testData/psi/parser-full/javadocParsing/InlineTag3.txt @@ -15,7 +15,7 @@ PsiJavaFile:InlineTag3.java PsiDocToken:DOC_TAG_VALUE_SHARP_TOKEN('#') PsiDocToken:DOC_TAG_VALUE_TOKEN('somefunc') PsiDocToken:DOC_TAG_VALUE_LPAREN('(') - PsiElement(DOC_TAG_VALUE_TOKEN) + PsiElement(DOC_TAG_VALUE_ELEMENT) PsiElement(DOC_TYPE_HOLDER) PsiTypeElement:String PsiJavaCodeReferenceElement:String diff --git a/java/java-tests/testData/psi/parser-full/javadocParsing/LinkPlainTag1.txt b/java/java-tests/testData/psi/parser-full/javadocParsing/LinkPlainTag1.txt index b1bae863f3b4..d7f6a22bf259 100644 --- a/java/java-tests/testData/psi/parser-full/javadocParsing/LinkPlainTag1.txt +++ b/java/java-tests/testData/psi/parser-full/javadocParsing/LinkPlainTag1.txt @@ -30,7 +30,7 @@ PsiJavaFile:LinkPlainTag1.java PsiDocToken:DOC_TAG_VALUE_SHARP_TOKEN('#') PsiDocToken:DOC_TAG_VALUE_TOKEN('equals') PsiDocToken:DOC_TAG_VALUE_LPAREN('(') - PsiElement(DOC_TAG_VALUE_TOKEN) + PsiElement(DOC_TAG_VALUE_ELEMENT) PsiElement(DOC_TYPE_HOLDER) PsiTypeElement:java.lang.Object PsiJavaCodeReferenceElement:java.lang.Object diff --git a/java/java-tests/testData/psi/parser-full/javadocParsing/LinkPlainTag2.txt b/java/java-tests/testData/psi/parser-full/javadocParsing/LinkPlainTag2.txt index ba779cc5e45a..79e2ec31d002 100644 --- a/java/java-tests/testData/psi/parser-full/javadocParsing/LinkPlainTag2.txt +++ b/java/java-tests/testData/psi/parser-full/javadocParsing/LinkPlainTag2.txt @@ -10,7 +10,7 @@ PsiJavaFile:LinkPlainTag2.java PsiDocTag:@linkplain PsiDocToken:DOC_TAG_NAME('@linkplain') PsiWhiteSpace(' ') - PsiElement(DOC_TAG_VALUE_TOKEN) + PsiElement(DOC_TAG_VALUE_ELEMENT) PsiDocToken:DOC_TAG_VALUE_TOKEN('java.lang.String') PsiDocToken:DOC_TAG_VALUE_SHARP_TOKEN('#') PsiDocToken:DOC_TAG_VALUE_TOKEN('equals') diff --git a/java/java-tests/testData/psi/parser-full/javadocParsing/LinkTag1.txt b/java/java-tests/testData/psi/parser-full/javadocParsing/LinkTag1.txt index 588adbae3d2e..f5fde4f3d333 100644 --- a/java/java-tests/testData/psi/parser-full/javadocParsing/LinkTag1.txt +++ b/java/java-tests/testData/psi/parser-full/javadocParsing/LinkTag1.txt @@ -30,7 +30,7 @@ PsiJavaFile:LinkTag1.java PsiDocToken:DOC_TAG_VALUE_SHARP_TOKEN('#') PsiDocToken:DOC_TAG_VALUE_TOKEN('equals') PsiDocToken:DOC_TAG_VALUE_LPAREN('(') - PsiElement(DOC_TAG_VALUE_TOKEN) + PsiElement(DOC_TAG_VALUE_ELEMENT) PsiElement(DOC_TYPE_HOLDER) PsiTypeElement:java.lang.Object PsiJavaCodeReferenceElement:java.lang.Object diff --git a/java/java-tests/testData/psi/parser-full/javadocParsing/LinkTag2.txt b/java/java-tests/testData/psi/parser-full/javadocParsing/LinkTag2.txt index 5bb0d66a89f3..af4cf7d2b185 100644 --- a/java/java-tests/testData/psi/parser-full/javadocParsing/LinkTag2.txt +++ b/java/java-tests/testData/psi/parser-full/javadocParsing/LinkTag2.txt @@ -10,7 +10,7 @@ PsiJavaFile:LinkTag2.java PsiDocTag:@link PsiDocToken:DOC_TAG_NAME('@link') PsiWhiteSpace(' ') - PsiElement(DOC_TAG_VALUE_TOKEN) + PsiElement(DOC_TAG_VALUE_ELEMENT) PsiDocToken:DOC_TAG_VALUE_TOKEN('java.lang.String') PsiDocToken:DOC_TAG_VALUE_SHARP_TOKEN('#') PsiDocToken:DOC_TAG_VALUE_TOKEN('equals') diff --git a/java/java-tests/testData/psi/parser-full/javadocParsing/LinkTag3.txt b/java/java-tests/testData/psi/parser-full/javadocParsing/LinkTag3.txt index 8a8940b8e0b3..b1ea51c9d1af 100644 --- a/java/java-tests/testData/psi/parser-full/javadocParsing/LinkTag3.txt +++ b/java/java-tests/testData/psi/parser-full/javadocParsing/LinkTag3.txt @@ -17,7 +17,7 @@ PsiJavaFile:LinkTag3.java PsiDocToken:DOC_TAG_VALUE_SHARP_TOKEN('#') PsiDocToken:DOC_TAG_VALUE_TOKEN('foo') PsiDocToken:DOC_TAG_VALUE_LPAREN('(') - PsiElement(DOC_TAG_VALUE_TOKEN) + PsiElement(DOC_TAG_VALUE_ELEMENT) PsiDocToken:DOC_TAG_VALUE_RPAREN(')') PsiDocToken:DOC_INLINE_TAG_END('}') diff --git a/java/java-tests/testData/psi/parser-full/javadocParsing/LinkTag4.txt b/java/java-tests/testData/psi/parser-full/javadocParsing/LinkTag4.txt index 93a0946316fc..a08d012cd2f2 100644 --- a/java/java-tests/testData/psi/parser-full/javadocParsing/LinkTag4.txt +++ b/java/java-tests/testData/psi/parser-full/javadocParsing/LinkTag4.txt @@ -16,7 +16,7 @@ PsiJavaFile:LinkTag4.java PsiDocToken:DOC_TAG_VALUE_SHARP_TOKEN('#') PsiDocToken:DOC_TAG_VALUE_TOKEN('foo') PsiDocToken:DOC_TAG_VALUE_LPAREN('(') - PsiElement(DOC_TAG_VALUE_TOKEN) + PsiElement(DOC_TAG_VALUE_ELEMENT) PsiDocToken:DOC_TAG_VALUE_RPAREN(')') PsiDocToken:DOC_INLINE_TAG_END('}') diff --git a/java/java-tests/testData/psi/parser-full/javadocParsing/LinkTag5.txt b/java/java-tests/testData/psi/parser-full/javadocParsing/LinkTag5.txt index 6fe4b9bb0d46..6aeebb155f30 100644 --- a/java/java-tests/testData/psi/parser-full/javadocParsing/LinkTag5.txt +++ b/java/java-tests/testData/psi/parser-full/javadocParsing/LinkTag5.txt @@ -15,7 +15,7 @@ PsiJavaFile:LinkTag5.java PsiDocToken:DOC_TAG_VALUE_SHARP_TOKEN('#') PsiDocToken:DOC_TAG_VALUE_TOKEN('foo') PsiDocToken:DOC_TAG_VALUE_LPAREN('(') - PsiElement(DOC_TAG_VALUE_TOKEN) + PsiElement(DOC_TAG_VALUE_ELEMENT) PsiDocToken:DOC_TAG_VALUE_RPAREN(')') PsiDocToken:DOC_INLINE_TAG_END('}') diff --git a/java/java-tests/testData/psi/parser-full/javadocParsing/LinkTag6.txt b/java/java-tests/testData/psi/parser-full/javadocParsing/LinkTag6.txt index 24d4ab93bb89..1cc176462b4e 100644 --- a/java/java-tests/testData/psi/parser-full/javadocParsing/LinkTag6.txt +++ b/java/java-tests/testData/psi/parser-full/javadocParsing/LinkTag6.txt @@ -15,7 +15,7 @@ PsiJavaFile:LinkTag6.java PsiDocToken:DOC_TAG_VALUE_SHARP_TOKEN('#') PsiDocToken:DOC_TAG_VALUE_TOKEN('foo') PsiDocToken:DOC_TAG_VALUE_LPAREN('(') - PsiElement(DOC_TAG_VALUE_TOKEN) + PsiElement(DOC_TAG_VALUE_ELEMENT) PsiDocToken:DOC_TAG_VALUE_RPAREN(')') PsiDocToken:DOC_INLINE_TAG_END('}') diff --git a/java/java-tests/testData/psi/parser-full/javadocParsing/SeeTag10.txt b/java/java-tests/testData/psi/parser-full/javadocParsing/SeeTag10.txt index 94bc09223507..95b2f7f607a3 100644 --- a/java/java-tests/testData/psi/parser-full/javadocParsing/SeeTag10.txt +++ b/java/java-tests/testData/psi/parser-full/javadocParsing/SeeTag10.txt @@ -29,7 +29,7 @@ PsiJavaFile:SeeTag10.java PsiDocToken:DOC_TAG_VALUE_SHARP_TOKEN('#') PsiDocToken:DOC_TAG_VALUE_TOKEN('equals') PsiDocToken:DOC_TAG_VALUE_LPAREN('(') - PsiElement(DOC_TAG_VALUE_TOKEN) + PsiElement(DOC_TAG_VALUE_ELEMENT) PsiElement(DOC_TYPE_HOLDER) PsiTypeElement:java.lang.Object PsiJavaCodeReferenceElement:java.lang.Object diff --git a/java/java-tests/testData/psi/parser-full/javadocParsing/SeeTag11.txt b/java/java-tests/testData/psi/parser-full/javadocParsing/SeeTag11.txt index fec603ea5b39..f58d76b72fd8 100644 --- a/java/java-tests/testData/psi/parser-full/javadocParsing/SeeTag11.txt +++ b/java/java-tests/testData/psi/parser-full/javadocParsing/SeeTag11.txt @@ -29,7 +29,7 @@ PsiJavaFile:SeeTag11.java PsiDocToken:DOC_TAG_VALUE_SHARP_TOKEN('#') PsiDocToken:DOC_TAG_VALUE_TOKEN('wait') PsiDocToken:DOC_TAG_VALUE_LPAREN('(') - PsiElement(DOC_TAG_VALUE_TOKEN) + PsiElement(DOC_TAG_VALUE_ELEMENT) PsiDocToken:DOC_TAG_VALUE_RPAREN(')') PsiWhiteSpace('\n ') diff --git a/java/java-tests/testData/psi/parser-full/javadocParsing/SeeTag12.txt b/java/java-tests/testData/psi/parser-full/javadocParsing/SeeTag12.txt index 6ef08071804c..ac4dd62c07d2 100644 --- a/java/java-tests/testData/psi/parser-full/javadocParsing/SeeTag12.txt +++ b/java/java-tests/testData/psi/parser-full/javadocParsing/SeeTag12.txt @@ -14,7 +14,7 @@ PsiJavaFile:SeeTag12.java PsiDocToken:DOC_TAG_VALUE_SHARP_TOKEN('#') PsiDocToken:DOC_TAG_VALUE_TOKEN('wait') PsiDocToken:DOC_TAG_VALUE_LPAREN('(') - PsiElement(DOC_TAG_VALUE_TOKEN) + PsiElement(DOC_TAG_VALUE_ELEMENT) PsiDocToken:DOC_TAG_VALUE_RPAREN(')') PsiWhiteSpace('\n ') diff --git a/java/java-tests/testData/psi/parser-full/javadocParsing/SeeTag14.txt b/java/java-tests/testData/psi/parser-full/javadocParsing/SeeTag14.txt index 9d930ec9c238..08f90474c3a7 100644 --- a/java/java-tests/testData/psi/parser-full/javadocParsing/SeeTag14.txt +++ b/java/java-tests/testData/psi/parser-full/javadocParsing/SeeTag14.txt @@ -29,7 +29,7 @@ PsiJavaFile:SeeTag14.java PsiDocToken:DOC_TAG_VALUE_SHARP_TOKEN('#') PsiDocToken:DOC_TAG_VALUE_TOKEN('append') PsiDocToken:DOC_TAG_VALUE_LPAREN('(') - PsiElement(DOC_TAG_VALUE_TOKEN) + PsiElement(DOC_TAG_VALUE_ELEMENT) PsiElement(DOC_TYPE_HOLDER) PsiTypeElement:char[] PsiTypeElement:char diff --git a/java/java-tests/testData/psi/parser-full/javadocParsing/SeeTag15.txt b/java/java-tests/testData/psi/parser-full/javadocParsing/SeeTag15.txt index a535a82cc44f..fa79b853c1a4 100644 --- a/java/java-tests/testData/psi/parser-full/javadocParsing/SeeTag15.txt +++ b/java/java-tests/testData/psi/parser-full/javadocParsing/SeeTag15.txt @@ -29,7 +29,7 @@ PsiJavaFile:SeeTag15.java PsiDocToken:DOC_TAG_VALUE_SHARP_TOKEN('#') PsiDocToken:DOC_TAG_VALUE_TOKEN('append') PsiDocToken:DOC_TAG_VALUE_LPAREN('(') - PsiElement(DOC_TAG_VALUE_TOKEN) + PsiElement(DOC_TAG_VALUE_ELEMENT) PsiElement(DOC_TYPE_HOLDER) PsiTypeElement:java.lang.Object PsiJavaCodeReferenceElement:java.lang.Object diff --git a/java/java-tests/testData/psi/parser-full/javadocParsing/SeeTag16.txt b/java/java-tests/testData/psi/parser-full/javadocParsing/SeeTag16.txt index d7f1d3d5157a..3d1fa26a8169 100644 --- a/java/java-tests/testData/psi/parser-full/javadocParsing/SeeTag16.txt +++ b/java/java-tests/testData/psi/parser-full/javadocParsing/SeeTag16.txt @@ -19,7 +19,7 @@ PsiJavaFile:SeeTag16.java PsiDocToken:DOC_TAG_VALUE_SHARP_TOKEN('#') PsiDocToken:DOC_TAG_VALUE_TOKEN('equals') PsiDocToken:DOC_TAG_VALUE_LPAREN('(') - PsiElement(DOC_TAG_VALUE_TOKEN) + PsiElement(DOC_TAG_VALUE_ELEMENT) PsiElement(DOC_TYPE_HOLDER) PsiTypeElement:int PsiKeyword:int('int') diff --git a/java/java-tests/testData/psi/parser-full/javadocParsing/SeeTag3.txt b/java/java-tests/testData/psi/parser-full/javadocParsing/SeeTag3.txt index a2a28c9a945a..f018c5af50c1 100644 --- a/java/java-tests/testData/psi/parser-full/javadocParsing/SeeTag3.txt +++ b/java/java-tests/testData/psi/parser-full/javadocParsing/SeeTag3.txt @@ -19,7 +19,7 @@ PsiJavaFile:SeeTag3.java PsiDocToken:DOC_TAG_VALUE_SHARP_TOKEN('#') PsiDocToken:DOC_TAG_VALUE_TOKEN('equals') PsiDocToken:DOC_TAG_VALUE_LPAREN('(') - PsiElement(DOC_TAG_VALUE_TOKEN) + PsiElement(DOC_TAG_VALUE_ELEMENT) PsiElement(DOC_TYPE_HOLDER) PsiTypeElement:Object PsiJavaCodeReferenceElement:Object diff --git a/java/java-tests/testData/psi/parser-full/javadocParsing/SeeTag5.txt b/java/java-tests/testData/psi/parser-full/javadocParsing/SeeTag5.txt index 977e65124359..9903e7c37d6b 100644 --- a/java/java-tests/testData/psi/parser-full/javadocParsing/SeeTag5.txt +++ b/java/java-tests/testData/psi/parser-full/javadocParsing/SeeTag5.txt @@ -29,7 +29,7 @@ PsiJavaFile:SeeTag5.java PsiDocToken:DOC_TAG_VALUE_SHARP_TOKEN('#') PsiDocToken:DOC_TAG_VALUE_TOKEN('wait') PsiDocToken:DOC_TAG_VALUE_LPAREN('(') - PsiElement(DOC_TAG_VALUE_TOKEN) + PsiElement(DOC_TAG_VALUE_ELEMENT) PsiElement(DOC_TYPE_HOLDER) PsiTypeElement:long PsiKeyword:long('long') diff --git a/java/java-tests/testData/psi/parser-full/javadocParsing/SeeTag9.txt b/java/java-tests/testData/psi/parser-full/javadocParsing/SeeTag9.txt index 75968a87205e..7b2b10a6a7ce 100644 --- a/java/java-tests/testData/psi/parser-full/javadocParsing/SeeTag9.txt +++ b/java/java-tests/testData/psi/parser-full/javadocParsing/SeeTag9.txt @@ -15,16 +15,16 @@ PsiJavaFile:SeeTag9.java PsiIdentifier:equals('equals') PsiReferenceParameterList - PsiElement(DOC_TAG_VALUE_TOKEN) + PsiElement(DOC_TAG_VALUE_ELEMENT) PsiDocToken:DOC_TAG_VALUE_LPAREN('(') PsiElement(DOC_REFERENCE_HOLDER) PsiKeyword:long('long') - PsiElement(DOC_TAG_VALUE_TOKEN) + PsiElement(DOC_TAG_VALUE_ELEMENT) PsiDocToken:DOC_TAG_VALUE_COMMA(',') PsiWhiteSpace(' ') PsiElement(DOC_REFERENCE_HOLDER) PsiKeyword:long('long') - PsiElement(DOC_TAG_VALUE_TOKEN) + PsiElement(DOC_TAG_VALUE_ELEMENT) PsiDocToken:DOC_TAG_VALUE_RPAREN(')') PsiWhiteSpace('\n ') PsiDocToken:DOC_COMMENT_END('*/') diff --git a/java/java-tests/testData/psi/parser-full/javadocParsing/Tag2.txt b/java/java-tests/testData/psi/parser-full/javadocParsing/Tag2.txt index 45c260fa5f51..83308bc7bd6b 100644 --- a/java/java-tests/testData/psi/parser-full/javadocParsing/Tag2.txt +++ b/java/java-tests/testData/psi/parser-full/javadocParsing/Tag2.txt @@ -23,7 +23,7 @@ PsiJavaFile:Tag2.java PsiDocTag:@return PsiDocToken:DOC_TAG_NAME('@return') PsiWhiteSpace(' ') - PsiElement(DOC_TAG_VALUE_TOKEN) + PsiElement(DOC_TAG_VALUE_ELEMENT) PsiDocToken:DOC_TAG_VALUE_TOKEN('value') PsiWhiteSpace(' ') PsiDocToken:DOC_COMMENT_DATA('value') diff --git a/java/java-tests/testData/psi/parser-full/javadocParsing/Tag5.txt b/java/java-tests/testData/psi/parser-full/javadocParsing/Tag5.txt index eb44d58a94e9..c6a9b80b47b6 100644 --- a/java/java-tests/testData/psi/parser-full/javadocParsing/Tag5.txt +++ b/java/java-tests/testData/psi/parser-full/javadocParsing/Tag5.txt @@ -10,7 +10,7 @@ PsiJavaFile:Tag5.java PsiDocTag:@ejb:bean PsiDocToken:DOC_TAG_NAME('@ejb:bean') PsiWhiteSpace(' ') - PsiElement(DOC_TAG_VALUE_TOKEN) + PsiElement(DOC_TAG_VALUE_ELEMENT) PsiDocToken:DOC_TAG_VALUE_TOKEN('this') PsiWhiteSpace(' ') PsiDocToken:DOC_COMMENT_DATA('is an EJB ') diff --git a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterTest.java b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterTest.java index 16fb8dd3c01c..63fcd6b60215 100644 --- a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterTest.java @@ -1,3 +1,18 @@ +/* + * Copyright 2000-2012 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.psi.formatter.java; import com.intellij.openapi.application.ApplicationManager; @@ -529,12 +544,12 @@ public class JavaFormatterTest extends AbstractJavaFormatterTest { public void testJavaDocLinksWithParameterNames() throws Exception { // See IDEADEV-8332 doTextTest("/**\n" + - "* @return if ( x1 == x1 ) then retrun {@link #cmp(String y1,int y2)}\n" + + "* @return if ( x1 == x1 ) then return {@link #cmp(String y1,int y2)}\n" + "* otherwise return {@link #cmp(int x1,int x2)}\n" + "*/\n" + "class X {\n" + "}\n", "/**\n" + - " * @return if ( x1 == x1 ) then retrun {@link #cmp(String y1, int y2)}\n" + + " * @return if ( x1 == x1 ) then return {@link #cmp(String y1, int y2)}\n" + " * otherwise return {@link #cmp(int x1, int x2)}\n" + " */\n" + "class X {\n" + From b6649c9c26862550811246c5c6d1f380588e597b Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Wed, 23 May 2012 14:09:45 +0200 Subject: [PATCH 09/26] support jdk 1.8 language level --- .../src/org/jetbrains/jps/idea/IdeaProjectLoader.groovy | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/jps/model/src/org/jetbrains/jps/idea/IdeaProjectLoader.groovy b/jps/model/src/org/jetbrains/jps/idea/IdeaProjectLoader.groovy index d40e9cc91e9b..6791338d53b7 100644 --- a/jps/model/src/org/jetbrains/jps/idea/IdeaProjectLoader.groovy +++ b/jps/model/src/org/jetbrains/jps/idea/IdeaProjectLoader.groovy @@ -632,6 +632,12 @@ public class IdeaProjectLoader { case "JDK_1_5": return "1.5" case "JDK_1_6": return "1.6" case "JDK_1_7": return "1.7" + case "JDK_1_8": return "1.8" + } + + final String prefix = "JDK_"; + if (imlPropertyText != null && imlPropertyText.startsWith(prefix)) { + return imlPropertyText.substring(prefix.length()).replace('_', '.'); } return "1.6" From 4003d12f285ac6f42b9c09ff6ce3f0832fc23cab Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 23 May 2012 14:28:22 +0200 Subject: [PATCH 10/26] relax test, extract MinusculeMatcher --- .../completion/PrefixMatchingWeigher.java | 3 +- .../lookup/impl/LookupCellRenderer.java | 3 +- .../ide/util/gotoByName/ChooseByNameBase.java | 4 +- .../DefaultChooseByNameItemProvider.java | 17 +- .../ui/speedSearch/SpeedSearchUtil.java | 6 +- .../intellij/ui/SpeedSearchComparator.java | 5 +- .../com/intellij/psi/util/NameUtilTest.java | 63 ++-- .../psi/codeStyle/MinusculeMatcher.java | 296 ++++++++++++++++++ .../com/intellij/psi/codeStyle/NameUtil.java | 289 +---------------- 9 files changed, 358 insertions(+), 328 deletions(-) create mode 100644 platform/util/src/com/intellij/psi/codeStyle/MinusculeMatcher.java diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/PrefixMatchingWeigher.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/PrefixMatchingWeigher.java index 5290bf1e5ee3..2df59cd5fa37 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/PrefixMatchingWeigher.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/PrefixMatchingWeigher.java @@ -18,6 +18,7 @@ package com.intellij.codeInsight.completion; import com.intellij.codeInsight.CodeInsightSettings; import com.intellij.codeInsight.completion.impl.CamelHumpMatcher; import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.psi.codeStyle.MinusculeMatcher; import com.intellij.psi.codeStyle.NameUtil; import org.jetbrains.annotations.NotNull; @@ -38,7 +39,7 @@ public class PrefixMatchingWeigher extends CompletionWeigher { final NameUtil.MatchingCaseSensitivity sensitivity = setting == CodeInsightSettings.NONE ? NameUtil.MatchingCaseSensitivity.NONE : setting == CodeInsightSettings.FIRST_LETTER ? NameUtil.MatchingCaseSensitivity.FIRST_LETTER : NameUtil.MatchingCaseSensitivity.ALL; - final NameUtil.MinusculeMatcher matcher = new NameUtil.MinusculeMatcher(CamelHumpMatcher.applyMiddleMatching(prefix), sensitivity); + final MinusculeMatcher matcher = new MinusculeMatcher(CamelHumpMatcher.applyMiddleMatching(prefix), sensitivity); int max = Integer.MIN_VALUE; for (String lookupString : item.getAllLookupStrings()) { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupCellRenderer.java b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupCellRenderer.java index 0a5e258a0c14..3e9b529ad131 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupCellRenderer.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupCellRenderer.java @@ -24,6 +24,7 @@ import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.editor.colors.EditorFontType; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.codeStyle.MinusculeMatcher; import com.intellij.psi.codeStyle.NameUtil; import com.intellij.ui.*; import com.intellij.ui.speedSearch.SpeedSearchUtil; @@ -255,7 +256,7 @@ public class LookupCellRenderer implements ListCellRenderer { final String prefix = myLookup.itemPattern(item); if (prefix.length() > 0) { - Iterable ranges = new NameUtil.MinusculeMatcher("*" + prefix, NameUtil.MatchingCaseSensitivity.NONE).matchingFragments(name); + Iterable ranges = new MinusculeMatcher("*" + prefix, NameUtil.MatchingCaseSensitivity.NONE).matchingFragments(name); if (ranges != null) { SimpleTextAttributes highlighted = new SimpleTextAttributes(style, selected ? SELECTED_PREFIX_FOREGROUND_COLOR : PREFIX_FOREGROUND_COLOR); diff --git a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNameBase.java b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNameBase.java index 393fd5000bd5..c3839457750b 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNameBase.java +++ b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNameBase.java @@ -46,6 +46,7 @@ import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.openapi.wm.WindowManager; import com.intellij.openapi.wm.ex.WindowManagerEx; import com.intellij.psi.PsiElement; +import com.intellij.psi.codeStyle.MinusculeMatcher; import com.intellij.psi.codeStyle.NameUtil; import com.intellij.psi.statistics.StatisticsInfo; import com.intellij.psi.statistics.StatisticsManager; @@ -1020,7 +1021,8 @@ public abstract class ChooseByNameBase { String text = EXTRA_ELEM.equals(modelElement) ? null : myModel.getFullName(modelElement); if (text != null) { String shortName = myModel.getElementName(modelElement); - int match = shortName != null && matcher instanceof NameUtil.MinusculeMatcher ? ((NameUtil.MinusculeMatcher)matcher).matchingDegree(shortName) : Integer.MIN_VALUE; + int match = shortName != null && matcher instanceof MinusculeMatcher + ? ((MinusculeMatcher)matcher).matchingDegree(shortName) : Integer.MIN_VALUE; int stats = StatisticsManager.getInstance().getUseCount(new StatisticsInfo(statContext, text)); if (stats > best || stats == best && match > bestMatch) { best = stats; diff --git a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/DefaultChooseByNameItemProvider.java b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/DefaultChooseByNameItemProvider.java index 89bb47436d43..7fbc2a2fdbd4 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/DefaultChooseByNameItemProvider.java +++ b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/DefaultChooseByNameItemProvider.java @@ -22,6 +22,7 @@ import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; +import com.intellij.psi.codeStyle.MinusculeMatcher; import com.intellij.psi.codeStyle.NameUtil; import com.intellij.psi.util.proximity.PsiProximityComparator; import com.intellij.util.Function; @@ -147,10 +148,10 @@ public class DefaultChooseByNameItemProvider implements ChooseByNameItemProvider if (name == null) return false; final List suspects = split(name, base); - final List> patternsAndMatchers = - ContainerUtil.map2List(split(qualifierPattern, base), new Function>() { + final List> patternsAndMatchers = + ContainerUtil.map2List(split(qualifierPattern, base), new Function>() { @Override - public Pair fun(String s) { + public Pair fun(String s) { return Pair.create(getNamePattern(base, s), buildPatternMatcher(getNamePattern(base, s))); } }); @@ -159,9 +160,9 @@ public class DefaultChooseByNameItemProvider implements ChooseByNameItemProvider try { patterns: - for (Pair patternAndMatcher : patternsAndMatchers) { + for (Pair patternAndMatcher : patternsAndMatchers) { final String pattern = patternAndMatcher.first; - final NameUtil.MinusculeMatcher matcher = patternAndMatcher.second; + final MinusculeMatcher matcher = patternAndMatcher.second; if (!pattern.isEmpty()) { for (int j = matchPosition; j < suspects.size() - 1; j++) { String suspect = suspects.get(j); @@ -204,7 +205,7 @@ public class DefaultChooseByNameItemProvider implements ChooseByNameItemProvider pattern = pattern.substring(1); } - final NameUtil.MinusculeMatcher matcher = buildPatternMatcher(pattern); + final MinusculeMatcher matcher = buildPatternMatcher(pattern); try { for (String name : names) { @@ -221,7 +222,7 @@ public class DefaultChooseByNameItemProvider implements ChooseByNameItemProvider } } - private static boolean matches(ChooseByNameBase base, String pattern, NameUtil.MinusculeMatcher matcher, String name) { + private static boolean matches(ChooseByNameBase base, String pattern, MinusculeMatcher matcher, String name) { boolean matches = false; if (name != null) { if (base.getModel() instanceof CustomMatcherModel) { @@ -236,7 +237,7 @@ public class DefaultChooseByNameItemProvider implements ChooseByNameItemProvider return matches; } - private static NameUtil.MinusculeMatcher buildPatternMatcher(String pattern) { + private static MinusculeMatcher buildPatternMatcher(String pattern) { return NameUtil.buildMatcher(pattern, NameUtil.MatchingCaseSensitivity.NONE); } diff --git a/platform/platform-api/src/com/intellij/ui/speedSearch/SpeedSearchUtil.java b/platform/platform-api/src/com/intellij/ui/speedSearch/SpeedSearchUtil.java index b75cd5db6fa3..8e3f0f5ba13e 100644 --- a/platform/platform-api/src/com/intellij/ui/speedSearch/SpeedSearchUtil.java +++ b/platform/platform-api/src/com/intellij/ui/speedSearch/SpeedSearchUtil.java @@ -18,7 +18,7 @@ package com.intellij.ui.speedSearch; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.registry.Registry; -import com.intellij.psi.codeStyle.NameUtil; +import com.intellij.psi.codeStyle.MinusculeMatcher; import com.intellij.ui.SimpleColoredComponent; import com.intellij.ui.SimpleTextAttributes; import com.intellij.util.Processor; @@ -65,12 +65,12 @@ public final class SpeedSearchUtil { final Matcher matcher, final Color selectedBg, final boolean selected) { - if (!(matcher instanceof NameUtil.MinusculeMatcher) || (Registry.is("ide.highlight.match.in.selected.only") && !selected)) { + if (!(matcher instanceof MinusculeMatcher) || (Registry.is("ide.highlight.match.in.selected.only") && !selected)) { component.append(text, attributes); return; } - final Iterable iterable = ((NameUtil.MinusculeMatcher)matcher).matchingFragments(text); + final Iterable iterable = ((MinusculeMatcher)matcher).matchingFragments(text); if (iterable != null) { final Color fg = attributes.getFgColor(); final int style = attributes.getStyle(); diff --git a/platform/platform-impl/src/com/intellij/ui/SpeedSearchComparator.java b/platform/platform-impl/src/com/intellij/ui/SpeedSearchComparator.java index 0f3b5dccdb64..31e2f6597412 100644 --- a/platform/platform-impl/src/com/intellij/ui/SpeedSearchComparator.java +++ b/platform/platform-impl/src/com/intellij/ui/SpeedSearchComparator.java @@ -17,6 +17,7 @@ package com.intellij.ui; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.codeStyle.MinusculeMatcher; import com.intellij.psi.codeStyle.NameUtil; import org.jetbrains.annotations.Nullable; @@ -24,7 +25,7 @@ import org.jetbrains.annotations.Nullable; * @author Konstantin Bulenkov */ public class SpeedSearchComparator { - private NameUtil.MinusculeMatcher myMinusculeMatcher; + private MinusculeMatcher myMinusculeMatcher; private String myRecentSearchText; private boolean myShouldMatchFromTheBeginning; private boolean myShouldMatchCamelCase; @@ -52,7 +53,7 @@ public class SpeedSearchComparator { if (!myShouldMatchFromTheBeginning && !pattern.startsWith("*")) { pattern = "*" + pattern; } - myMinusculeMatcher = new NameUtil.MinusculeMatcher(pattern, NameUtil.MatchingCaseSensitivity.NONE); + myMinusculeMatcher = new MinusculeMatcher(pattern, NameUtil.MatchingCaseSensitivity.NONE); } return myMinusculeMatcher.matchingFragments(text); } diff --git a/platform/platform-tests/testSrc/com/intellij/psi/util/NameUtilTest.java b/platform/platform-tests/testSrc/com/intellij/psi/util/NameUtilTest.java index 72ac2b2fccac..c2a329e9dcae 100644 --- a/platform/platform-tests/testSrc/com/intellij/psi/util/NameUtilTest.java +++ b/platform/platform-tests/testSrc/com/intellij/psi/util/NameUtilTest.java @@ -20,6 +20,7 @@ package com.intellij.psi.util; import com.intellij.openapi.util.TextRange; +import com.intellij.psi.codeStyle.MinusculeMatcher; import com.intellij.psi.codeStyle.NameUtil; import com.intellij.testFramework.PlatformTestUtil; import com.intellij.testFramework.UsefulTestCase; @@ -312,57 +313,57 @@ public class NameUtilTest extends UsefulTestCase { } public void testMinusculeFirstLetter() { - assertTrue(new NameUtil.MinusculeMatcher("WebLogic", NameUtil.MatchingCaseSensitivity.FIRST_LETTER).matches("WebLogic")); - assertFalse(new NameUtil.MinusculeMatcher("webLogic", NameUtil.MatchingCaseSensitivity.FIRST_LETTER).matches("WebLogic")); - assertTrue(new NameUtil.MinusculeMatcher("cL", NameUtil.MatchingCaseSensitivity.FIRST_LETTER).matches("class")); - assertTrue(new NameUtil.MinusculeMatcher("CL", NameUtil.MatchingCaseSensitivity.FIRST_LETTER).matches("Class")); - assertFalse(new NameUtil.MinusculeMatcher("abc", NameUtil.MatchingCaseSensitivity.FIRST_LETTER).matches("_abc")); + assertTrue(new MinusculeMatcher("WebLogic", NameUtil.MatchingCaseSensitivity.FIRST_LETTER).matches("WebLogic")); + assertFalse(new MinusculeMatcher("webLogic", NameUtil.MatchingCaseSensitivity.FIRST_LETTER).matches("WebLogic")); + assertTrue(new MinusculeMatcher("cL", NameUtil.MatchingCaseSensitivity.FIRST_LETTER).matches("class")); + assertTrue(new MinusculeMatcher("CL", NameUtil.MatchingCaseSensitivity.FIRST_LETTER).matches("Class")); + assertFalse(new MinusculeMatcher("abc", NameUtil.MatchingCaseSensitivity.FIRST_LETTER).matches("_abc")); } public void testMinusculeAllImportant() { - assertTrue(new NameUtil.MinusculeMatcher("WebLogic", NameUtil.MatchingCaseSensitivity.ALL).matches("WebLogic")); - assertFalse(new NameUtil.MinusculeMatcher("webLogic", NameUtil.MatchingCaseSensitivity.ALL).matches("weblogic")); - assertFalse(new NameUtil.MinusculeMatcher("FOO", NameUtil.MatchingCaseSensitivity.ALL).matches("foo")); - assertFalse(new NameUtil.MinusculeMatcher("foo", NameUtil.MatchingCaseSensitivity.ALL).matches("fOO")); - assertFalse(new NameUtil.MinusculeMatcher("Wl", NameUtil.MatchingCaseSensitivity.ALL).matches("WebLogic")); - assertTrue(new NameUtil.MinusculeMatcher("WL", NameUtil.MatchingCaseSensitivity.ALL).matches("WebLogic")); - assertFalse(new NameUtil.MinusculeMatcher("WL", NameUtil.MatchingCaseSensitivity.ALL).matches("Weblogic")); - assertFalse(new NameUtil.MinusculeMatcher("WL", NameUtil.MatchingCaseSensitivity.ALL).matches("weblogic")); - assertFalse(new NameUtil.MinusculeMatcher("webLogic", NameUtil.MatchingCaseSensitivity.ALL).matches("WebLogic")); - assertFalse(new NameUtil.MinusculeMatcher("Str", NameUtil.MatchingCaseSensitivity.ALL).matches("SomeThingRidiculous")); + assertTrue(new MinusculeMatcher("WebLogic", NameUtil.MatchingCaseSensitivity.ALL).matches("WebLogic")); + assertFalse(new MinusculeMatcher("webLogic", NameUtil.MatchingCaseSensitivity.ALL).matches("weblogic")); + assertFalse(new MinusculeMatcher("FOO", NameUtil.MatchingCaseSensitivity.ALL).matches("foo")); + assertFalse(new MinusculeMatcher("foo", NameUtil.MatchingCaseSensitivity.ALL).matches("fOO")); + assertFalse(new MinusculeMatcher("Wl", NameUtil.MatchingCaseSensitivity.ALL).matches("WebLogic")); + assertTrue(new MinusculeMatcher("WL", NameUtil.MatchingCaseSensitivity.ALL).matches("WebLogic")); + assertFalse(new MinusculeMatcher("WL", NameUtil.MatchingCaseSensitivity.ALL).matches("Weblogic")); + assertFalse(new MinusculeMatcher("WL", NameUtil.MatchingCaseSensitivity.ALL).matches("weblogic")); + assertFalse(new MinusculeMatcher("webLogic", NameUtil.MatchingCaseSensitivity.ALL).matches("WebLogic")); + assertFalse(new MinusculeMatcher("Str", NameUtil.MatchingCaseSensitivity.ALL).matches("SomeThingRidiculous")); } public void testMatchingFragments() { @NonNls String sample = "NoClassDefFoundException"; // 0 2 7 10 15 21 - assertOrderedEquals(new NameUtil.MinusculeMatcher("ncldfou*ion", NameUtil.MatchingCaseSensitivity.NONE).matchingFragments(sample), + assertOrderedEquals(new MinusculeMatcher("ncldfou*ion", NameUtil.MatchingCaseSensitivity.NONE).matchingFragments(sample), TextRange.from(0, 1), TextRange.from(2, 2), TextRange.from(7, 1), TextRange.from(10, 3), TextRange.from(21, 3)); sample = "doGet(HttpServletRequest, HttpServletResponse):void"; // 0 22 - assertOrderedEquals(new NameUtil.MinusculeMatcher("d*st", NameUtil.MatchingCaseSensitivity.NONE).matchingFragments(sample), + assertOrderedEquals(new MinusculeMatcher("d*st", NameUtil.MatchingCaseSensitivity.NONE).matchingFragments(sample), TextRange.from(0, 1), TextRange.from(22, 2)); - assertOrderedEquals(new NameUtil.MinusculeMatcher("doge*st", NameUtil.MatchingCaseSensitivity.NONE).matchingFragments(sample), + assertOrderedEquals(new MinusculeMatcher("doge*st", NameUtil.MatchingCaseSensitivity.NONE).matchingFragments(sample), TextRange.from(0, 4), TextRange.from(22, 2)); sample = "_test"; - assertOrderedEquals(new NameUtil.MinusculeMatcher("_", NameUtil.MatchingCaseSensitivity.NONE).matchingFragments(sample), + assertOrderedEquals(new MinusculeMatcher("_", NameUtil.MatchingCaseSensitivity.NONE).matchingFragments(sample), TextRange.from(0, 1)); - assertOrderedEquals(new NameUtil.MinusculeMatcher("_t", NameUtil.MatchingCaseSensitivity.NONE).matchingFragments(sample), + assertOrderedEquals(new MinusculeMatcher("_t", NameUtil.MatchingCaseSensitivity.NONE).matchingFragments(sample), TextRange.from(0, 2)); } public void testMatchingFragmentsSorted() { @NonNls String sample = "SWUPGRADEHDLRFSPR7TEST"; // 0 9 12 - assertOrderedEquals(new NameUtil.MinusculeMatcher("SWU*H*R", NameUtil.MatchingCaseSensitivity.NONE).matchingFragments(sample), + assertOrderedEquals(new MinusculeMatcher("SWU*H*R", NameUtil.MatchingCaseSensitivity.NONE).matchingFragments(sample), TextRange.from(0, 3), TextRange.from(9, 1), TextRange.from(12, 1)); } public void testPreferCapsMatching() { String sample = "getCurrentUser"; // 0 4 10 - assertOrderedEquals(new NameUtil.MinusculeMatcher("getCU", NameUtil.MatchingCaseSensitivity.NONE).matchingFragments(sample), + assertOrderedEquals(new MinusculeMatcher("getCU", NameUtil.MatchingCaseSensitivity.NONE).matchingFragments(sample), TextRange.from(0, 4), TextRange.from(10, 1)); } @@ -390,7 +391,7 @@ public class NameUtilTest extends UsefulTestCase { } public void testMeaningfulMatchingDegree() { - assertTrue(new NameUtil.MinusculeMatcher(" EUC-", NameUtil.MatchingCaseSensitivity.FIRST_LETTER).matchingDegree("x-EUC-TW") > Integer.MIN_VALUE); + assertTrue(new MinusculeMatcher(" EUC-", NameUtil.MatchingCaseSensitivity.FIRST_LETTER).matchingDegree("x-EUC-TW") > Integer.MIN_VALUE); } private static void assertPreference(@NonNls String pattern, @@ -403,7 +404,7 @@ public class NameUtilTest extends UsefulTestCase { @NonNls String less, @NonNls String more, NameUtil.MatchingCaseSensitivity sensitivity) { - NameUtil.MinusculeMatcher matcher = new NameUtil.MinusculeMatcher(pattern, sensitivity); + MinusculeMatcher matcher = new MinusculeMatcher(pattern, sensitivity); int iLess = matcher.matchingDegree(less); int iMore = matcher.matchingDegree(more); assertTrue(iLess + ">=" + iMore + "; " + less + ">=" + more, iLess < iMore); @@ -411,24 +412,24 @@ public class NameUtilTest extends UsefulTestCase { public void testPerformance() { @NonNls final String longName = "ThisIsAQuiteLongNameWithParentheses().Dots.-Minuses-_UNDERSCORES_digits239:colons:/slashes\\AndOfCourseManyLetters"; - final List matching = new ArrayList(); - final List nonMatching = new ArrayList(); + final List matching = new ArrayList(); + final List nonMatching = new ArrayList(); for (String s : CollectionFactory.ar("*", "*i", "*a", "*u", "T", "ti", longName, longName.substring(0, 20))) { - matching.add(new NameUtil.MinusculeMatcher(s, NameUtil.MatchingCaseSensitivity.NONE)); + matching.add(new MinusculeMatcher(s, NameUtil.MatchingCaseSensitivity.NONE)); } for (String s : CollectionFactory.ar("A", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "ta")) { - nonMatching.add(new NameUtil.MinusculeMatcher(s, NameUtil.MatchingCaseSensitivity.NONE)); + nonMatching.add(new MinusculeMatcher(s, NameUtil.MatchingCaseSensitivity.NONE)); } - PlatformTestUtil.startPerformanceTest("Matcher is slow", 1200, new ThrowableRunnable() { + PlatformTestUtil.startPerformanceTest("Matcher is slow", 1700, new ThrowableRunnable() { @Override public void run() { for (int i = 0; i < 100000; i++) { - for (NameUtil.MinusculeMatcher matcher : matching) { + for (MinusculeMatcher matcher : matching) { assertTrue(matcher.matches(longName)); } - for (NameUtil.MinusculeMatcher matcher : nonMatching) { + for (MinusculeMatcher matcher : nonMatching) { assertFalse(matcher.matches(longName)); } } diff --git a/platform/util/src/com/intellij/psi/codeStyle/MinusculeMatcher.java b/platform/util/src/com/intellij/psi/codeStyle/MinusculeMatcher.java new file mode 100644 index 000000000000..32074ac649f6 --- /dev/null +++ b/platform/util/src/com/intellij/psi/codeStyle/MinusculeMatcher.java @@ -0,0 +1,296 @@ +/* + * Copyright 2000-2012 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.psi.codeStyle; + +import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.containers.FList; +import com.intellij.util.text.CharArrayCharSequence; +import com.intellij.util.text.Matcher; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; + +/** +* @author peter +*/ +public class MinusculeMatcher implements Matcher { + private final char[] myPattern; + private final NameUtil.MatchingCaseSensitivity myOptions; + + public MinusculeMatcher(String pattern, NameUtil.MatchingCaseSensitivity options) { + myOptions = options; + myPattern = StringUtil.trimEnd(pattern, "* ").replaceAll(":", "\\*:").toCharArray(); + } + + @Nullable + private FList matchName(String name, int patternIndex, int nameIndex) { + if (patternIndex == myPattern.length) { + return FList.emptyList(); + } + if ('*' == myPattern[patternIndex]) { + return skipChars(name, patternIndex, nameIndex, true); + } + if (nameIndex == name.length()) { + return null; + } + + if ('.' == myPattern[patternIndex] && name.charAt(nameIndex) != '.') { + return skipChars(name, patternIndex, nameIndex, false); + } + + if (' ' == myPattern[patternIndex] && patternIndex != myPattern.length - 1) { + return skipWords(name, patternIndex, nameIndex); + } + + if ((patternIndex == 0 || patternIndex == 1 && myPattern[0] == ' ' && nameIndex == 0) && + myOptions != NameUtil.MatchingCaseSensitivity.NONE && name.charAt(nameIndex) != myPattern[patternIndex]) { + return null; + } + + if (NameUtil.isWordSeparator(name.charAt(nameIndex))) { + return skipSeparators(name, patternIndex, nameIndex); + } + + if (StringUtil.toLowerCase(name.charAt(nameIndex)) != StringUtil.toLowerCase(myPattern[patternIndex])) { + if (Character.isDigit(name.charAt(nameIndex)) && nameIndex > 0 || (name.charAt(nameIndex) == '.' && name.indexOf('.', nameIndex + 1) > 0)) { + return matchName(name, patternIndex, nameIndex + 1); + } + return null; + } + + if (myOptions == NameUtil.MatchingCaseSensitivity.ALL && name.charAt(nameIndex) != myPattern[patternIndex]) { + return null; + } + + int nextStart = NameUtil.nextWord(name, nameIndex); + + int lastUpper = NameUtil.isWordStart(myPattern[patternIndex]) ? 0 : -1; + + int i = 1; + while (true) { + if (patternIndex + i == myPattern.length || i + nameIndex == nextStart) { + break; + } + char p = myPattern[patternIndex + i]; + char w = name.charAt(i + nameIndex); + if (lastUpper == i - 1 && NameUtil.isWordStart(p) && myOptions != NameUtil.MatchingCaseSensitivity.ALL) { + if (p == w) { + lastUpper = i; + } + p = StringUtil.toLowerCase(p); + } + + if (myOptions != NameUtil.MatchingCaseSensitivity.ALL) { + w = StringUtil.toLowerCase(w); + } + if (w != p) { + break; + } + i++; + } + + if (isFinalSpaceMatch(name, patternIndex, nameIndex, nextStart, i)) { + return FList.emptyList().prepend(TextRange.from(nameIndex, i)); + } + + return matchAfterFragment(name, patternIndex, nameIndex, nextStart, lastUpper, i); + } + + private boolean isFinalSpaceMatch(String name, int patternIndex, int nameIndex, int nextStart, int i) { + return nextStart == name.length() && + patternIndex + i == myPattern.length - 1 && + ' ' == myPattern[patternIndex + i] && + (i == 1 && NameUtil.isWordStart(myPattern[patternIndex]) || i + nameIndex == name.length()); + } + + @Nullable + private FList matchAfterFragment(String name, int patternIndex, int nameIndex, int nextStart, int lastUpper, int matchLen) { + boolean star = patternIndex + matchLen < myPattern.length && myPattern[patternIndex + matchLen] == '*'; + if (lastUpper >= 0) { + FList ranges = matchName(name, patternIndex + lastUpper + 1, star && matchLen == lastUpper ? nameIndex + lastUpper : nextStart); + if (ranges != null) { + return prependRange(ranges, nameIndex, lastUpper + 1); + } + } + + int trial = matchLen; + while (trial > 0) { + FList ranges = matchName(name, patternIndex + trial, nextStart); + if (ranges != null) { + return prependRange(ranges, nameIndex, trial); + } + trial--; + } + + FList ranges = matchName(name, patternIndex + matchLen, nameIndex + matchLen); + if (ranges != null) { + return prependRange(ranges, nameIndex, matchLen); + } + return null; + } + + private static FList prependRange(FList ranges, int from, int length) { + TextRange head = ranges.getHead(); + if (head != null && head.getStartOffset() == from + length) { + return ranges.getTail().prepend(new TextRange(from, head.getEndOffset())); + } + return ranges.prepend(TextRange.from(from, length)); + } + + @Nullable + private FList skipSeparators(String name, int patternIndex, int nameIndex) { + int nextStart = NameUtil.nextWord(name, nameIndex); + assert nextStart - nameIndex == 1 : "'" + name + "'" + nameIndex + " " + nextStart; + char p = myPattern[patternIndex]; + if (NameUtil.isWordSeparator(p)) { + if (myOptions != NameUtil.MatchingCaseSensitivity.NONE && + nameIndex == 0 && name.length() > 1 && patternIndex + 1 < myPattern.length && + NameUtil.isWordSeparator(name.charAt(1)) && !NameUtil.isWordSeparator(myPattern[patternIndex + 1])) { + return null; + } + + final FList ranges = matchName(name, patternIndex + 1, nextStart); + if (ranges != null) { + return prependRange(ranges, nameIndex, 1); + } + + return null; + } + + return matchName(name, patternIndex, nextStart); + } + + @Nullable + private FList skipChars(String name, int patternIndex, int nameIndex, boolean maySkipNextChar) { + while ('*' == myPattern[patternIndex]) { + patternIndex++; + if (patternIndex == myPattern.length) { + return FList.emptyList(); + } + } + + String nextChar = String.valueOf(myPattern[patternIndex]); + boolean upper = Character.isUpperCase(myPattern[patternIndex]); + + int fromIndex = nameIndex; + while (true) { + int next = StringUtil.indexOfIgnoreCase(name, nextChar, fromIndex); + if (next < 0) { + break; + } + if (upper && next > 0 && !Character.isUpperCase(name.charAt(next))) { + fromIndex = next + 1; + continue; + } + + FList ranges = matchName(name, patternIndex, next); + if (ranges != null) { + return ranges; + } + if (!maySkipNextChar) { + return null; + } + fromIndex = next + 1; + } + return null; + } + + @Nullable + private FList skipWords(String name, int patternIndex, int nameIndex) { + while (' ' == myPattern[patternIndex]) { + patternIndex++; + if (patternIndex == myPattern.length) { + return null; + } + } + + if (nameIndex == 0 || NameUtil.isWordStart(name, nameIndex)) { + FList ranges = matchName(name, patternIndex, nameIndex); + if (ranges != null) { + return ranges; + } + } + + boolean separatorInPattern = NameUtil.isWordSeparator(myPattern[patternIndex]); + int fromIndex = nameIndex; + while (fromIndex < name.length()) { + int next = separatorInPattern ? name.indexOf(myPattern[patternIndex], fromIndex) : NameUtil.nextWord(name, fromIndex); + if (next < 0) { + break; + } + + FList ranges = matchName(name, patternIndex, next); + if (ranges != null) { + return ranges; + } + fromIndex = next; + if (separatorInPattern) { + fromIndex++; + } + } + return null; + } + + public int matchingDegree(String name) { + Iterable iterable = matchingFragments(name); + if (iterable == null) return Integer.MIN_VALUE; + + int fragmentCount = 0; + int matchingCase = 0; + CharArrayCharSequence seq = new CharArrayCharSequence(myPattern); + int p = -1; + TextRange first = null; + for (TextRange range : iterable) { + if (first == null) { + first = range; + } + for (int i = range.getStartOffset(); i < range.getEndOffset(); i++) { + char c = name.charAt(i); + p = StringUtil.indexOf(seq, c, p + 1, myPattern.length, false); + if (p < 0) { + break; + } + matchingCase += c == myPattern[p] ? 1 : 0; + } + fragmentCount++; + } + + int commonStart = 0; + while (commonStart < name.length() && commonStart < myPattern.length && name.charAt(commonStart) == myPattern[commonStart]) { + commonStart++; + } + + boolean prefixMatching = first != null && first.getStartOffset() == 0; + boolean middleWordStart = first != null && first.getStartOffset() > 0 && NameUtil.isWordStart(name, first.getStartOffset()); + + return -fragmentCount + matchingCase * 10 + commonStart + (prefixMatching ? 2 : middleWordStart ? 1 : 0) * 100; + } + + @Override + public boolean matches(String name) { + return matchingFragments(name) != null; + } + + @Nullable + public Iterable matchingFragments(String name) { + if (name.isEmpty()) { + return myPattern.length == 0 ? Collections.emptyList() : null; + } + + return matchName(name, 0, 0); + } +} diff --git a/platform/util/src/com/intellij/psi/codeStyle/NameUtil.java b/platform/util/src/com/intellij/psi/codeStyle/NameUtil.java index 1fd815e56922..5725e6460a48 100644 --- a/platform/util/src/com/intellij/psi/codeStyle/NameUtil.java +++ b/platform/util/src/com/intellij/psi/codeStyle/NameUtil.java @@ -15,20 +15,15 @@ */ package com.intellij.psi.codeStyle; -import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.util.text.StringUtilRt; import com.intellij.util.ArrayUtil; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.containers.FList; -import com.intellij.util.text.CharArrayCharSequence; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.ArrayList; -import java.util.Collections; import java.util.List; public class NameUtil { @@ -331,24 +326,24 @@ public class NameUtil { return suggestion; } - private static boolean isWordStart(String text, int i) { + static boolean isWordStart(String text, int i) { if (isWordStart(text.charAt(i))) { return true; } if (!Character.isLetterOrDigit(text.charAt(i))) { return false; } - if (i > 0 && (MinusculeMatcher.isWordSeparator(text.charAt(i - 1)) || text.charAt(i - 1) == '.')) { + if (i > 0 && (isWordSeparator(text.charAt(i - 1)) || text.charAt(i - 1) == '.')) { return true; } return false; } - private static boolean isWordStart(char p) { + static boolean isWordStart(char p) { return Character.isUpperCase(p) || Character.isDigit(p); } - private static int nextWord(String text, int start) { + static int nextWord(String text, int start) { if (!Character.isLetterOrDigit(text.charAt(start))) { return start + 1; } @@ -378,6 +373,10 @@ public class NameUtil { } } + static boolean isWordSeparator(char c) { + return Character.isWhitespace(c) || c == '_' || c == '-' || c == ':'; + } + /** * @deprecated use com.intellij.util.text.Matcher */ @@ -409,276 +408,4 @@ public class NameUtil { public enum MatchingCaseSensitivity { NONE, FIRST_LETTER, ALL } - - public static class MinusculeMatcher implements com.intellij.util.text.Matcher { - private final char[] myPattern; - private final MatchingCaseSensitivity myOptions; - - public MinusculeMatcher(String pattern, MatchingCaseSensitivity options) { - myOptions = options; - myPattern = StringUtil.trimEnd(pattern, "* ").replaceAll(":", "\\*:").toCharArray(); - } - - @Nullable - private FList matchName(String name, int patternIndex, int nameIndex) { - if (patternIndex == myPattern.length) { - return FList.emptyList(); - } - if ('*' == myPattern[patternIndex]) { - return skipChars(name, patternIndex, nameIndex, true); - } - if (nameIndex == name.length()) { - return null; - } - - if ('.' == myPattern[patternIndex] && name.charAt(nameIndex) != '.') { - return skipChars(name, patternIndex, nameIndex, false); - } - - if (' ' == myPattern[patternIndex] && patternIndex != myPattern.length - 1) { - return skipWords(name, patternIndex, nameIndex); - } - - if ((patternIndex == 0 || patternIndex == 1 && myPattern[0] == ' ' && nameIndex == 0) && - myOptions != MatchingCaseSensitivity.NONE && name.charAt(nameIndex) != myPattern[patternIndex]) { - return null; - } - - if (isWordSeparator(name.charAt(nameIndex))) { - return skipSeparators(name, patternIndex, nameIndex); - } - - if (StringUtil.toLowerCase(name.charAt(nameIndex)) != StringUtil.toLowerCase(myPattern[patternIndex])) { - if (Character.isDigit(name.charAt(nameIndex)) && nameIndex > 0 || (name.charAt(nameIndex) == '.' && name.indexOf('.', nameIndex + 1) > 0)) { - return matchName(name, patternIndex, nameIndex + 1); - } - return null; - } - - if (myOptions == MatchingCaseSensitivity.ALL && name.charAt(nameIndex) != myPattern[patternIndex]) { - return null; - } - - int nextStart = NameUtil.nextWord(name, nameIndex); - - int lastUpper = isWordStart(myPattern[patternIndex]) ? 0 : -1; - - int i = 1; - while (true) { - if (patternIndex + i == myPattern.length || i + nameIndex == nextStart) { - break; - } - char p = myPattern[patternIndex + i]; - char w = name.charAt(i + nameIndex); - if (lastUpper == i - 1 && isWordStart(p) && myOptions != MatchingCaseSensitivity.ALL) { - if (p == w) { - lastUpper = i; - } - p = StringUtil.toLowerCase(p); - } - - if (myOptions != MatchingCaseSensitivity.ALL) { - w = StringUtil.toLowerCase(w); - } - if (w != p) { - break; - } - i++; - } - - if (isFinalSpaceMatch(name, patternIndex, nameIndex, nextStart, i)) { - return FList.emptyList().prepend(TextRange.from(nameIndex, i)); - } - - return matchAfterFragment(name, patternIndex, nameIndex, nextStart, lastUpper, i); - } - - private boolean isFinalSpaceMatch(String name, int patternIndex, int nameIndex, int nextStart, int i) { - return nextStart == name.length() && - patternIndex + i == myPattern.length - 1 && - ' ' == myPattern[patternIndex + i] && - (i == 1 && isWordStart(myPattern[patternIndex]) || i + nameIndex == name.length()); - } - - @Nullable - private FList matchAfterFragment(String name, int patternIndex, int nameIndex, int nextStart, int lastUpper, int matchLen) { - boolean star = patternIndex + matchLen < myPattern.length && myPattern[patternIndex + matchLen] == '*'; - if (lastUpper >= 0) { - FList ranges = matchName(name, patternIndex + lastUpper + 1, star && matchLen == lastUpper ? nameIndex + lastUpper : nextStart); - if (ranges != null) { - return prependRange(ranges, nameIndex, lastUpper + 1); - } - } - - int trial = matchLen; - while (trial > 0) { - FList ranges = matchName(name, patternIndex + trial, nextStart); - if (ranges != null) { - return prependRange(ranges, nameIndex, trial); - } - trial--; - } - - FList ranges = matchName(name, patternIndex + matchLen, nameIndex + matchLen); - if (ranges != null) { - return prependRange(ranges, nameIndex, matchLen); - } - return null; - } - - private static FList prependRange(FList ranges, int from, int length) { - TextRange head = ranges.getHead(); - if (head != null && head.getStartOffset() == from + length) { - return ranges.getTail().prepend(new TextRange(from, head.getEndOffset())); - } - return ranges.prepend(TextRange.from(from, length)); - } - - private static boolean isWordSeparator(char c) { - return Character.isWhitespace(c) || c == '_' || c == '-' || c == ':'; - } - - @Nullable - private FList skipSeparators(String name, int patternIndex, int nameIndex) { - int nextStart = NameUtil.nextWord(name, nameIndex); - assert nextStart - nameIndex == 1 : "'" + name + "'" + nameIndex + " " + nextStart; - char p = myPattern[patternIndex]; - if (isWordSeparator(p)) { - if (myOptions != MatchingCaseSensitivity.NONE && - nameIndex == 0 && name.length() > 1 && patternIndex + 1 < myPattern.length && - isWordSeparator(name.charAt(1)) && !isWordSeparator(myPattern[patternIndex + 1])) { - return null; - } - - final FList ranges = matchName(name, patternIndex + 1, nextStart); - if (ranges != null) { - return prependRange(ranges, nameIndex, 1); - } - - return null; - } - - return matchName(name, patternIndex, nextStart); - } - - @Nullable - private FList skipChars(String name, int patternIndex, int nameIndex, boolean maySkipNextChar) { - while ('*' == myPattern[patternIndex]) { - patternIndex++; - if (patternIndex == myPattern.length) { - return FList.emptyList(); - } - } - - String nextChar = String.valueOf(myPattern[patternIndex]); - boolean upper = Character.isUpperCase(myPattern[patternIndex]); - - int fromIndex = nameIndex; - while (true) { - int next = StringUtil.indexOfIgnoreCase(name, nextChar, fromIndex); - if (next < 0) { - break; - } - if (upper && next > 0 && !Character.isUpperCase(name.charAt(next))) { - fromIndex = next + 1; - continue; - } - - FList ranges = matchName(name, patternIndex, next); - if (ranges != null) { - return ranges; - } - if (!maySkipNextChar) { - return null; - } - fromIndex = next + 1; - } - return null; - } - - @Nullable - private FList skipWords(String name, int patternIndex, int nameIndex) { - while (' ' == myPattern[patternIndex]) { - patternIndex++; - if (patternIndex == myPattern.length) { - return null; - } - } - - if (nameIndex == 0 || isWordStart(name, nameIndex)) { - FList ranges = matchName(name, patternIndex, nameIndex); - if (ranges != null) { - return ranges; - } - } - - boolean separatorInPattern = isWordSeparator(myPattern[patternIndex]); - int fromIndex = nameIndex; - while (fromIndex < name.length()) { - int next = separatorInPattern ? name.indexOf(myPattern[patternIndex], fromIndex) : nextWord(name, fromIndex); - if (next < 0) { - break; - } - - FList ranges = matchName(name, patternIndex, next); - if (ranges != null) { - return ranges; - } - fromIndex = next; - if (separatorInPattern) { - fromIndex++; - } - } - return null; - } - - public int matchingDegree(String name) { - Iterable iterable = matchingFragments(name); - if (iterable == null) return Integer.MIN_VALUE; - - int fragmentCount = 0; - int matchingCase = 0; - CharArrayCharSequence seq = new CharArrayCharSequence(myPattern); - int p = -1; - TextRange first = null; - for (TextRange range : iterable) { - if (first == null) { - first = range; - } - for (int i = range.getStartOffset(); i < range.getEndOffset(); i++) { - char c = name.charAt(i); - p = StringUtil.indexOf(seq, c, p + 1, myPattern.length, false); - if (p < 0) { - break; - } - matchingCase += c == myPattern[p] ? 1 : 0; - } - fragmentCount++; - } - - int commonStart = 0; - while (commonStart < name.length() && commonStart < myPattern.length && name.charAt(commonStart) == myPattern[commonStart]) { - commonStart++; - } - - boolean prefixMatching = first != null && first.getStartOffset() == 0; - boolean middleWordStart = first != null && first.getStartOffset() > 0 && isWordStart(name, first.getStartOffset()); - - return -fragmentCount + matchingCase * 10 + commonStart + (prefixMatching ? 2 : middleWordStart ? 1 : 0) * 100; - } - - @Override - public boolean matches(String name) { - return matchingFragments(name) != null; - } - - @Nullable - public Iterable matchingFragments(String name) { - if (name.isEmpty()) { - return myPattern.length == 0 ? Collections.emptyList() : null; - } - - return matchName(name, 0, 0); - } - } } From b3fd5fe2293e875c949703a57e7fc0eb8d534a0d Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Wed, 23 May 2012 14:29:14 +0200 Subject: [PATCH 11/26] IDEA-86476 UML toolbar layout/cosmetics --- .../openapi/actionSystem/impl/ActionToolbarImpl.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionToolbarImpl.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionToolbarImpl.java index 9d6da6f8d442..00ef9f89ad09 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionToolbarImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionToolbarImpl.java @@ -93,6 +93,7 @@ public class ActionToolbarImpl extends JPanel implements ActionToolbar { private final DefaultActionGroup mySecondaryActions = new DefaultActionGroup(); private boolean myMinimalMode; + private boolean myForceUseMacEnhancements; public ActionButton getSecondaryActionsButton() { return mySecondaryActionsButton; @@ -164,7 +165,11 @@ public class ActionToolbarImpl extends JPanel implements ActionToolbar { } private boolean doMacEnhancementsForMainToolbar() { - return (UIUtil.isUnderAquaLookAndFeel() && ActionPlaces.MAIN_TOOLBAR.equals(myPlace)); + return (UIUtil.isUnderAquaLookAndFeel() && (ActionPlaces.MAIN_TOOLBAR.equals(myPlace) || myForceUseMacEnhancements)); + } + + public void setForceUseMacEnhancements(boolean useMacEnhancements) { + myForceUseMacEnhancements = useMacEnhancements; } private boolean isInsideNavBar() { From 831df48803fa9a2dd86f3a0bc5313428b0c5c276 Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 23 May 2012 15:35:21 +0200 Subject: [PATCH 12/26] make 'Group by changelist' actually work in VCS Updated Files view (IDEA-85917) --- .../update/AbstractCommonUpdateAction.java | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/update/AbstractCommonUpdateAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/update/AbstractCommonUpdateAction.java index 2e8d8380f5e7..d0a300d97200 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/update/AbstractCommonUpdateAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/update/AbstractCommonUpdateAction.java @@ -49,7 +49,6 @@ import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.util.WaitForProgressToShow; -import com.intellij.util.messages.MessageBusConnection; import com.intellij.util.ui.OptionsDialog; import com.intellij.vcsUtil.VcsUtil; import org.jetbrains.annotations.NonNls; @@ -581,19 +580,14 @@ public abstract class AbstractCommonUpdateAction extends AbstractVcsAction { updateInfoTree.setBefore(myBefore); updateInfoTree.setAfter(myAfter); - // todo make temporal listener of changes reload - if (updateInfoTree != null) { - updateInfoTree.setCanGroupByChangeList(canGroupByChangelist(myVcsToVirtualFiles.keySet())); - final MessageBusConnection messageBusConnection = myProject.getMessageBus().connect(); - messageBusConnection.subscribe(CommittedChangesCache.COMMITTED_TOPIC, new CommittedChangesAdapter() { - public void incomingChangesUpdated(final List receivedChanges) { - if (receivedChanges != null) { - updateInfoTree.setChangeLists(receivedChanges); - messageBusConnection.disconnect(); - } + updateInfoTree.setCanGroupByChangeList(canGroupByChangelist(myVcsToVirtualFiles.keySet())); + myProject.getMessageBus().connect(updateInfoTree).subscribe(CommittedChangesCache.COMMITTED_TOPIC, new CommittedChangesAdapter() { + public void incomingChangesUpdated(final List receivedChanges) { + if (receivedChanges != null) { + updateInfoTree.setChangeLists(receivedChanges); } - }); - } + } + }); } public void onCancel() { From b186dded6170c9bd260b19d2c4bcb5782ee08741 Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 23 May 2012 15:36:09 +0200 Subject: [PATCH 13/26] IDEA-86471 DevKit: NPE in inspection --- plugins/devkit/src/dom/impl/IdeaPluginImpl.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/devkit/src/dom/impl/IdeaPluginImpl.java b/plugins/devkit/src/dom/impl/IdeaPluginImpl.java index 198bcd3eb31a..bb213490e9c2 100644 --- a/plugins/devkit/src/dom/impl/IdeaPluginImpl.java +++ b/plugins/devkit/src/dom/impl/IdeaPluginImpl.java @@ -26,6 +26,10 @@ import org.jetbrains.idea.devkit.dom.IdeaPlugin; public abstract class IdeaPluginImpl implements IdeaPlugin { public String getPluginId() { final XmlTag tag = getXmlTag(); + if (tag == null) { + return null; + } + final XmlTag idTag = tag.findFirstSubTag("id"); if (idTag != null) { return idTag.getValue().getTrimmedText(); From 61643f75e83e68e124df3d7d8d2c10e63859db69 Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 23 May 2012 16:17:06 +0200 Subject: [PATCH 14/26] rollback --- .../editor/markup/AttributesFlyweight.java | 59 +------------------ 1 file changed, 3 insertions(+), 56 deletions(-) diff --git a/platform/platform-api/src/com/intellij/openapi/editor/markup/AttributesFlyweight.java b/platform/platform-api/src/com/intellij/openapi/editor/markup/AttributesFlyweight.java index 10ce05c95d97..b6ea12c0257f 100644 --- a/platform/platform-api/src/com/intellij/openapi/editor/markup/AttributesFlyweight.java +++ b/platform/platform-api/src/com/intellij/openapi/editor/markup/AttributesFlyweight.java @@ -29,45 +29,7 @@ import java.awt.*; public class AttributesFlyweight { private final int myHashCode; - private static final StripedLockConcurrentHashMap entries = new StripedLockConcurrentHashMap(); - private static final ThreadLocal ourKey = new ThreadLocal(); - - private static class FlyweightKey { - Color foreground; - Color background; - @JdkConstants.FontStyle int fontType; - Color effectColor; - EffectType effectType; - Color errorStripeColor; - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof FlyweightKey)) return false; - - FlyweightKey key = (FlyweightKey)o; - - if (fontType != key.fontType) return false; - if (background != null ? !background.equals(key.background) : key.background != null) return false; - if (effectColor != null ? !effectColor.equals(key.effectColor) : key.effectColor != null) return false; - if (effectType != key.effectType) return false; - if (errorStripeColor != null ? !errorStripeColor.equals(key.errorStripeColor) : key.errorStripeColor != null) return false; - if (foreground != null ? !foreground.equals(key.foreground) : key.foreground != null) return false; - - return true; - } - - @Override - public int hashCode() { - int result = foreground != null ? foreground.hashCode() : 0; - result = 31 * result + (background != null ? background.hashCode() : 0); - result = 31 * result + fontType; - result = 31 * result + (effectColor != null ? effectColor.hashCode() : 0); - result = 31 * result + (effectType != null ? effectType.hashCode() : 0); - result = 31 * result + (errorStripeColor != null ? errorStripeColor.hashCode() : 0); - return result; - } - } + private static final StripedLockConcurrentHashMap entries = new StripedLockConcurrentHashMap(); @NotNull public static AttributesFlyweight create(Color foreground, @@ -76,23 +38,8 @@ public class AttributesFlyweight { Color effectColor, EffectType effectType, Color errorStripeColor) { - FlyweightKey key = ourKey.get(); - if (key == null) { - ourKey.set(key = new FlyweightKey()); - } - key.foreground = foreground; - key.background = background; - key.fontType = fontType; - key.effectColor = effectColor; - key.effectType = effectType; - key.errorStripeColor = errorStripeColor; - - AttributesFlyweight flyweight = entries.get(key); - if (flyweight != null) { - return flyweight; - } - - return ConcurrencyUtil.cacheOrGet(entries, key, new AttributesFlyweight(foreground, background, fontType, effectColor, effectType, errorStripeColor)); + AttributesFlyweight key = new AttributesFlyweight(foreground, background, fontType, effectColor, effectType, errorStripeColor); + return ConcurrencyUtil.cacheOrGet(entries, key, key); } private final Color myForeground; From b707520f09049087a1455036e335c52141eca358 Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 23 May 2012 16:23:44 +0200 Subject: [PATCH 15/26] less garbage during editor painting, take 2 --- .../editor/markup/AttributesFlyweight.java | 69 ++++++++++++++++++- 1 file changed, 66 insertions(+), 3 deletions(-) diff --git a/platform/platform-api/src/com/intellij/openapi/editor/markup/AttributesFlyweight.java b/platform/platform-api/src/com/intellij/openapi/editor/markup/AttributesFlyweight.java index b6ea12c0257f..05fa2cd09b82 100644 --- a/platform/platform-api/src/com/intellij/openapi/editor/markup/AttributesFlyweight.java +++ b/platform/platform-api/src/com/intellij/openapi/editor/markup/AttributesFlyweight.java @@ -29,7 +29,55 @@ import java.awt.*; public class AttributesFlyweight { private final int myHashCode; - private static final StripedLockConcurrentHashMap entries = new StripedLockConcurrentHashMap(); + private static final StripedLockConcurrentHashMap entries = new StripedLockConcurrentHashMap(); + private static final ThreadLocal ourKey = new ThreadLocal(); + + private static class FlyweightKey implements Cloneable { + Color foreground; + Color background; + @JdkConstants.FontStyle int fontType; + Color effectColor; + EffectType effectType; + Color errorStripeColor; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof FlyweightKey)) return false; + + FlyweightKey key = (FlyweightKey)o; + + if (fontType != key.fontType) return false; + if (background != null ? !background.equals(key.background) : key.background != null) return false; + if (effectColor != null ? !effectColor.equals(key.effectColor) : key.effectColor != null) return false; + if (effectType != key.effectType) return false; + if (errorStripeColor != null ? !errorStripeColor.equals(key.errorStripeColor) : key.errorStripeColor != null) return false; + if (foreground != null ? !foreground.equals(key.foreground) : key.foreground != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = foreground != null ? foreground.hashCode() : 0; + result = 31 * result + (background != null ? background.hashCode() : 0); + result = 31 * result + fontType; + result = 31 * result + (effectColor != null ? effectColor.hashCode() : 0); + result = 31 * result + (effectType != null ? effectType.hashCode() : 0); + result = 31 * result + (errorStripeColor != null ? errorStripeColor.hashCode() : 0); + return result; + } + + @Override + protected FlyweightKey clone() { + try { + return (FlyweightKey)super.clone(); + } + catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } + } + } @NotNull public static AttributesFlyweight create(Color foreground, @@ -38,8 +86,23 @@ public class AttributesFlyweight { Color effectColor, EffectType effectType, Color errorStripeColor) { - AttributesFlyweight key = new AttributesFlyweight(foreground, background, fontType, effectColor, effectType, errorStripeColor); - return ConcurrencyUtil.cacheOrGet(entries, key, key); + FlyweightKey key = ourKey.get(); + if (key == null) { + ourKey.set(key = new FlyweightKey()); + } + key.foreground = foreground; + key.background = background; + key.fontType = fontType; + key.effectColor = effectColor; + key.effectType = effectType; + key.errorStripeColor = errorStripeColor; + + AttributesFlyweight flyweight = entries.get(key); + if (flyweight != null) { + return flyweight; + } + + return ConcurrencyUtil.cacheOrGet(entries, key.clone(), new AttributesFlyweight(foreground, background, fontType, effectColor, effectType, errorStripeColor)); } private final Color myForeground; From 762cc566ce858c89b15921ffa6d980eab63db72a Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Wed, 23 May 2012 16:57:33 +0400 Subject: [PATCH 16/26] Typos --- .../platform-api/src/com/intellij/openapi/ui/Messages.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/platform/platform-api/src/com/intellij/openapi/ui/Messages.java b/platform/platform-api/src/com/intellij/openapi/ui/Messages.java index 367957385950..839eea53bd76 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/Messages.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/Messages.java @@ -716,7 +716,7 @@ public class Messages { } /** - * Shows dialog with given message and title, infomation icon {@link #getInformationIcon()} and OK button + * Shows dialog with given message and title, information icon {@link #getInformationIcon()} and OK button */ public static void showInfoMessage(Component component, String message, String title) { if (canShowMacSheetPanel()) { @@ -728,7 +728,7 @@ public class Messages { } /** - * Shows dialog with given message and title, infomation icon {@link #getInformationIcon()} and OK button + * Shows dialog with given message and title, information icon {@link #getInformationIcon()} and OK button */ public static void showInfoMessage(Project project, @Nls String message, @Nls String title) { if (canShowMacSheetPanel()) { @@ -740,7 +740,7 @@ public class Messages { } /** - * Shows dialog with given message and title, infomation icon {@link #getInformationIcon()} and OK button + * Shows dialog with given message and title, information icon {@link #getInformationIcon()} and OK button */ public static void showInfoMessage(String message, String title) { if (canShowMacSheetPanel()) { From d35f60d6ec1c0f6b94ee916087342941c1e263b2 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Wed, 23 May 2012 17:53:37 +0400 Subject: [PATCH 17/26] Fix chained assertion --- .../intellij/testFramework/HighlightTestInfo.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/platform/testFramework/src/com/intellij/testFramework/HighlightTestInfo.java b/platform/testFramework/src/com/intellij/testFramework/HighlightTestInfo.java index 9d12ebc4f484..407ae199627e 100644 --- a/platform/testFramework/src/com/intellij/testFramework/HighlightTestInfo.java +++ b/platform/testFramework/src/com/intellij/testFramework/HighlightTestInfo.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2012 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. @@ -36,6 +36,7 @@ public abstract class HighlightTestInfo implements Disposable { Disposer.register(parentDisposable, this); myPlace = parentDisposable.toString(); } + public HighlightTestInfo checkWarnings() { checkWarnings = true; return this; } public HighlightTestInfo checkWeakWarnings() { checkWeakWarnings = true; return this; } public HighlightTestInfo checkInfos() { checkInfos = true; return this; } @@ -43,10 +44,14 @@ public abstract class HighlightTestInfo implements Disposable { public HighlightTestInfo projectRoot(@NonNls @NotNull String root) { projectRoot = root; return this; } public HighlightTestInfo test() throws Exception { - doTest(); - tested = true; - Disposer.dispose(this); - return this; + try { + doTest(); + return this; + } + finally { + tested = true; + Disposer.dispose(this); + } } @Override From 7b1b6f06bc87155b1d6b1338da6e5a90bc010f71 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Wed, 23 May 2012 18:17:07 +0400 Subject: [PATCH 18/26] Fix test data for Linux --- .../ipp/opassign/assignment/ByteOpAssign.java | 23 +++++++++++++++++++ .../assignment/ByteOpAssign_after.java | 23 +++++++++++++++++++ .../ipp/opassign/assignment/ByteOpassign.java | 8 ------- .../assignment/ByteOpassign_after.java | 8 ------- .../opassign/assignment/DoubleOpAssign.java | 23 +++++++++++++++++++ .../assignment/DoubleOpAssign_after.java | 23 +++++++++++++++++++ .../opassign/assignment/DoubleOpassign.java | 8 ------- .../assignment/DoubleOpassign_after.java | 8 ------- .../assignment/StringOpAssign_after.java | 23 +++++++++++++++++++ .../assignment/StringOpassign_after.java | 8 ------- 10 files changed, 115 insertions(+), 40 deletions(-) create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/ByteOpAssign.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/ByteOpAssign_after.java delete mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/ByteOpassign.java delete mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/ByteOpassign_after.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/DoubleOpAssign.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/DoubleOpAssign_after.java delete mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/DoubleOpassign.java delete mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/DoubleOpassign_after.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/StringOpAssign_after.java delete mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/StringOpassign_after.java diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/ByteOpAssign.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/ByteOpAssign.java new file mode 100644 index 000000000000..ca45c9670725 --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/ByteOpAssign.java @@ -0,0 +1,23 @@ +/* + * Copyright 2000-2012 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.siyeh.ipp.opassign.assignment; + +class ByteOpassign { + + void foo(byte b) { + b += 1; + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/ByteOpAssign_after.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/ByteOpAssign_after.java new file mode 100644 index 000000000000..af4441527704 --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/ByteOpAssign_after.java @@ -0,0 +1,23 @@ +/* + * Copyright 2000-2012 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.siyeh.ipp.opassign.assignment; + +class ByteOpassign { + + void foo(byte b) { + b = (byte) (b + 1); + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/ByteOpassign.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/ByteOpassign.java deleted file mode 100644 index ce6c0daca40f..000000000000 --- a/plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/ByteOpassign.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.siyeh.ipp.opassign.assignment; - -class ByteOpassign { - - void foo(byte b) { - b += 1; - } -} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/ByteOpassign_after.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/ByteOpassign_after.java deleted file mode 100644 index e4daf05d38e4..000000000000 --- a/plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/ByteOpassign_after.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.siyeh.ipp.opassign.assignment; - -class ByteOpassign { - - void foo(byte b) { - b = (byte) (b + 1); - } -} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/DoubleOpAssign.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/DoubleOpAssign.java new file mode 100644 index 000000000000..2e8a0e5ba7a5 --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/DoubleOpAssign.java @@ -0,0 +1,23 @@ +/* + * Copyright 2000-2012 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.siyeh.ipp.opassign.assignment; + +class DoubleOpassign { + + void foo(double d) { + d += 0.5; + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/DoubleOpAssign_after.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/DoubleOpAssign_after.java new file mode 100644 index 000000000000..fe005f3c66d2 --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/DoubleOpAssign_after.java @@ -0,0 +1,23 @@ +/* + * Copyright 2000-2012 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.siyeh.ipp.opassign.assignment; + +class DoubleOpassign { + + void foo(double d) { + d = d + 0.5; + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/DoubleOpassign.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/DoubleOpassign.java deleted file mode 100644 index fb159c964e3b..000000000000 --- a/plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/DoubleOpassign.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.siyeh.ipp.opassign.assignment; - -class DoubleOpassign { - - void foo(double d) { - d += 0.5; - } -} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/DoubleOpassign_after.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/DoubleOpassign_after.java deleted file mode 100644 index fabb55a2a1c3..000000000000 --- a/plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/DoubleOpassign_after.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.siyeh.ipp.opassign.assignment; - -class DoubleOpassign { - - void foo(double d) { - d = d + 0.5; - } -} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/StringOpAssign_after.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/StringOpAssign_after.java new file mode 100644 index 000000000000..feab030b8d20 --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/StringOpAssign_after.java @@ -0,0 +1,23 @@ +/* + * Copyright 2000-2012 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.siyeh.ipp.opassign.assignment; + +class StringOpassign { + + void foo(String s) { + s = s + 1.0; + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/StringOpassign_after.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/StringOpassign_after.java deleted file mode 100644 index 5457c890258b..000000000000 --- a/plugins/IntentionPowerPak/test/com/siyeh/ipp/opassign/assignment/StringOpassign_after.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.siyeh.ipp.opassign.assignment; - -class StringOpassign { - - void foo(String s) { - s = s + 1.0; - } -} \ No newline at end of file From 7db94898149b2435d25f4fbd4b5074baac0cd21b Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 23 May 2012 17:10:56 +0200 Subject: [PATCH 19/26] [jeka] ignore empty encoding attribute --- .../jps/idea/IdeaProjectLoader.groovy | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/jps/model/src/org/jetbrains/jps/idea/IdeaProjectLoader.groovy b/jps/model/src/org/jetbrains/jps/idea/IdeaProjectLoader.groovy index 6791338d53b7..92521fb12f09 100644 --- a/jps/model/src/org/jetbrains/jps/idea/IdeaProjectLoader.groovy +++ b/jps/model/src/org/jetbrains/jps/idea/IdeaProjectLoader.groovy @@ -334,15 +334,17 @@ public class IdeaProjectLoader { def componentTag = getComponent(root, "Encoding"); if (componentTag == null) return; componentTag.file?.each {Node fileNode -> - def url = fileNode."@url"; - def charset = fileNode."@charset"; + String url = fileNode."@url"; + String charset = fileNode."@charset"; - if ("PROJECT".equals(url)) { - project.projectCharset = charset; - } - else { - def path = projectMacroExpander.expandMacros(IdeaProjectLoadingUtil.pathFromUrl(url)); - project.filePathToCharset[path] = charset; + if (!StringUtil.isEmptyOrSpaces(charset)) { + if ("PROJECT".equals(url)) { + project.projectCharset = charset; + } + else { + def path = projectMacroExpander.expandMacros(IdeaProjectLoadingUtil.pathFromUrl(url)); + project.filePathToCharset[FileUtil.toCanonicalPath(path)] = charset; + } } } } From fb6f640511700379d24767d309fc31bd39fc998c Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 23 May 2012 17:11:37 +0200 Subject: [PATCH 20/26] type casts may have side effects if their operands do --- .../DefaultCodeFragmentFactory.java | 2 +- .../completion/JavaCompletionUtil.java | 23 ++++++++++--------- ...erenceExpressionCompletionContributor.java | 6 +++-- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/DefaultCodeFragmentFactory.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/DefaultCodeFragmentFactory.java index bec0848383fb..053f4448e1d2 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/DefaultCodeFragmentFactory.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/DefaultCodeFragmentFactory.java @@ -79,7 +79,7 @@ public class DefaultCodeFragmentFactory extends CodeFragmentFactory { return null; } - if (parameters.getInvocationCount() <= 1 && JavaCompletionUtil.containsMethodCalls(expression)) { + if (parameters.getInvocationCount() <= 1 && JavaCompletionUtil.mayHaveSideEffects(expression)) { final CompletionService service = CompletionService.getCompletionService(); if (service.getAdvertisementText() == null && parameters.getInvocationCount() < 2) { service.setAdvertisementText("Invoke completion once more to see runtime type variants"); diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java index a97e1316b6ed..2e168b6291ab 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java @@ -677,18 +677,19 @@ public class JavaCompletionUtil { return ret; } - @Nullable - static PsiElement getQualifier(final PsiElement element) { - return element instanceof PsiJavaCodeReferenceElement ? ((PsiJavaCodeReferenceElement)element).getQualifier() : null; - } - - public static boolean containsMethodCalls(@Nullable final PsiElement qualifier) { - if (qualifier == null) return false; - if (qualifier instanceof PsiMethodCallExpression || qualifier instanceof PsiNewExpression) return true; - if (qualifier instanceof PsiArrayAccessExpression) { - return containsMethodCalls(((PsiArrayAccessExpression)qualifier).getArrayExpression()); + public static boolean mayHaveSideEffects(@Nullable final PsiElement element) { + if (element == null) return false; + if (element instanceof PsiMethodCallExpression || element instanceof PsiNewExpression) return true; + if (element instanceof PsiTypeCastExpression) { + return mayHaveSideEffects(((PsiTypeCastExpression)element).getOperand()); } - return containsMethodCalls(getQualifier(qualifier)); + if (element instanceof PsiArrayAccessExpression) { + return mayHaveSideEffects(((PsiArrayAccessExpression)element).getArrayExpression()); + } + if (element instanceof PsiJavaCodeReferenceElement) { + return mayHaveSideEffects(((PsiJavaCodeReferenceElement)element).getQualifier()); + } + return true; } public static void insertClassReference(@NotNull PsiClass psiClass, @NotNull PsiFile file, int offset) { diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/ReferenceExpressionCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/completion/ReferenceExpressionCompletionContributor.java index 454fcf02d7b1..b164555b8576 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/ReferenceExpressionCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/ReferenceExpressionCompletionContributor.java @@ -274,7 +274,9 @@ public class ReferenceExpressionCompletionContributor { } if (itemType == null) return; - final PsiElement qualifier = JavaCompletionUtil.getQualifier(reference.getElement()); + final PsiElement element1 = reference.getElement(); + final PsiElement qualifier = + element1 instanceof PsiJavaCodeReferenceElement ? ((PsiJavaCodeReferenceElement)element1).getQualifier() : null; final PsiType expectedType = parameters.getExpectedType(); if (!OBJECT_METHOD_PATTERN.accepts(object) || allowGetClass(object, parameters)) { if (parameters.getParameters().getInvocationCount() >= 3 || !itemType.equalsToText(CommonClassNames.JAVA_LANG_STRING)) { @@ -398,7 +400,7 @@ public class ReferenceExpressionCompletionContributor { } final String bracketSpace = getSpace(CodeStyleSettingsManager.getSettings(element.getProject()).SPACE_WITHIN_BRACKETS); - if (object instanceof PsiVariable && !JavaCompletionUtil.containsMethodCalls(qualifier)) { + if (object instanceof PsiVariable && !JavaCompletionUtil.mayHaveSideEffects(qualifier)) { final PsiVariable variable = (PsiVariable)object; addToArrayConversion(element, prefix, "new " + componentType.getCanonicalText() + From d10bfb6b7dba15683733fa6a5f8cad83822e0135 Mon Sep 17 00:00:00 2001 From: Dmitry Boulytchev Date: Wed, 23 May 2012 19:11:18 +0400 Subject: [PATCH 21/26] Mappings.differentiate has scattered into a bundle of classes and methods (for those whose eyes are not mounted wide enough :) (compile-server) --- .../ether/dependencyView/Mappings.java | 1642 +++++++++-------- 1 file changed, 875 insertions(+), 767 deletions(-) diff --git a/jps/model/src/org/jetbrains/ether/dependencyView/Mappings.java b/jps/model/src/org/jetbrains/ether/dependencyView/Mappings.java index be62585412c8..f90f00941c1f 100644 --- a/jps/model/src/org/jetbrains/ether/dependencyView/Mappings.java +++ b/jps/model/src/org/jetbrains/ether/dependencyView/Mappings.java @@ -86,7 +86,7 @@ public class Mappings { myChangedFiles = new TIntHashSet(DEFAULT_SET_CAPACITY, DEFAULT_SET_LOAD_FACTOR); myDeletedClasses = new HashSet(DEFAULT_SET_CAPACITY, DEFAULT_SET_LOAD_FACTOR); myDeltaIsTransient = base.myDeltaIsTransient; - myRootDir = new File(FileUtil.toSystemIndependentName(base.myRootDir.getAbsolutePath()) + File.separatorChar + "delta"); + myRootDir = new File(FileUtil.toSystemIndependentName(base.myRootDir.getAbsolutePath()) + File.separatorChar + "myDelta"); myContext = base.myContext; myInitName = myContext.get(""); myEmptyName = myContext.get(""); @@ -232,7 +232,6 @@ public class Mappings { private final List myPostPasses; - private void addPostPass(final PostPass p) { myPostPasses.add(p); } @@ -672,7 +671,7 @@ public class Mappings { final File theFile = new File(myContext.getValue(depFile)); if (filter == null || filter.accept(theFile)) { - affectedFiles.add(theFile); + affectedFiles.add(theFile); } } return true; @@ -846,773 +845,270 @@ public class Mappings { boolean accept(File file); } - public boolean differentiate(final Mappings delta, - final Collection removed, - final Collection filesToCompile, - final Collection compiledFiles, - final Collection affectedFiles, - final DependentFilesFilter filter, - @Nullable final Callbacks.ConstantAffectionResolver constantSearch) { - synchronized (myLock) { + private class Differential { + final int DESPERATE_MASK = Opcodes.ACC_STATIC | Opcodes.ACC_FINAL; - class DelayedWorks { - class Triple { - final int owner; - final FieldRepr field; - @Nullable - final Future affection; + final Mappings myDelta; + final Collection myRemoved; + final Collection myFilesToCompile; + final Collection myCompiledFiles; + final Collection myAffectedFiles; + final DependentFilesFilter myFilter; + @Nullable final Callbacks.ConstantAffectionResolver myConstantSearch; + final DelayedWorks myDelayedWorks; - private Triple(final int owner, final FieldRepr field, @Nullable final Future affection) { - this.owner = owner; - this.field = field; - this.affection = affection; - } + final Util myUpdated; + final Util mySelf; + final Util myOriginal; - Callbacks.ConstantAffection getAffection() { - try { - return affection != null ? affection.get() : Callbacks.ConstantAffection.EMPTY; - } - catch (Exception e) { - throw new RuntimeException(e); - } - } + private class DelayedWorks { + class Triple { + final int owner; + final FieldRepr field; + @Nullable + final Future affection; + + private Triple(final int owner, final FieldRepr field, @Nullable final Future affection) { + this.owner = owner; + this.field = field; + this.affection = affection; } - final Collection myQueue = new LinkedList(); - - void addConstantWork(final int ownerClass, final FieldRepr changedField, final boolean isRemoved, boolean accessChanged) { - final Future future; - if (constantSearch == null) { - future = null; + Callbacks.ConstantAffection getAffection() { + try { + return affection != null ? affection.get() : Callbacks.ConstantAffection.EMPTY; } - else { - final String className = myContext.getValue(ownerClass); - final String fieldName = myContext.getValue(changedField.name); - future = constantSearch.request(className.replace('/', '.'), fieldName, changedField.access, isRemoved, accessChanged); + catch (Exception e) { + throw new RuntimeException(e); } - myQueue.add(new Triple(ownerClass, changedField, future)); - } - - boolean doWork(final Collection affectedFiles) { - if (!myQueue.isEmpty()) { - debug("Starting delayed works."); - - for (final Triple t : myQueue) { - final Callbacks.ConstantAffection affection = t.getAffection(); - - debug("Class: ", t.owner); - debug("Field: ", t.field.name); - - if (!affection.isKnown()) { - debug("No external dependency information available."); - debug("Trying to soften non-incremental decision."); - if (!incrementalDecision(t.owner, t.field, affectedFiles, filter)) { - debug("No luck."); - debug("End of delayed work, returning false."); - return false; - } - } - else { - debug("External dependency information retrieved."); - affectedFiles.addAll(affection.getAffectedFiles()); - } - } - - debug("End of delayed work, returning true."); - } - return true; } } - final DelayedWorks works = new DelayedWorks(); + final Collection myQueue = new LinkedList(); - debug("Begin of Differentiate:"); + void addConstantWork(final int ownerClass, final FieldRepr changedField, final boolean isRemoved, boolean accessChanged) { + final Future future; + if (myConstantSearch == null) { + future = null; + } + else { + final String className = myContext.getValue(ownerClass); + final String fieldName = myContext.getValue(changedField.name); + future = myConstantSearch.request(className.replace('/', '.'), fieldName, changedField.access, isRemoved, accessChanged); + } + myQueue.add(new Triple(ownerClass, changedField, future)); + } - delta.runPostPasses(); - delta.compensateRemovedContent(filesToCompile); + boolean doWork(final Collection affectedFiles) { + if (!myQueue.isEmpty()) { + debug("Starting delayed works."); - final Util u = new Util(delta); - final Util self = new Util(this); - final Util o = new Util(); + for (final Triple t : myQueue) { + final Callbacks.ConstantAffection affection = t.getAffection(); - if (removed != null) { - for (String file : removed) { + debug("Class: ", t.owner); + debug("Field: ", t.field.name); + + if (!affection.isKnown()) { + debug("No external dependency information available."); + debug("Trying to soften non-incremental decision."); + if (!incrementalDecision(t.owner, t.field, affectedFiles, myFilter)) { + debug("No luck."); + debug("End of delayed work, returning false."); + return false; + } + } + else { + debug("External dependency information retrieved."); + affectedFiles.addAll(affection.getAffectedFiles()); + } + } + + debug("End of delayed work, returning true."); + } + return true; + } + } + + private class FileClasses { + final int fileName; + final Set fileClasses; + + FileClasses(int fileName, Collection fileClasses) { + this.fileName = fileName; + this.fileClasses = new HashSet(fileClasses); + } + } + + private class DiffState { + final public TIntHashSet dependants = new TIntHashSet(DEFAULT_SET_CAPACITY, DEFAULT_SET_LOAD_FACTOR); + + final public Set affectedUsages = new HashSet(); + final public Set annotationQuery = new HashSet(); + final public Map usageConstraints = new HashMap(); + + final Difference.Specifier classDiff; + + private DiffState(Difference.Specifier classDiff) { + this.classDiff = classDiff; + } + } + + private Differential(final Mappings delta, + final Collection removed, + final Collection filesToCompile, + final Collection compiledFiles, + final Collection affectedFiles, + final DependentFilesFilter filter, + @Nullable final Callbacks.ConstantAffectionResolver constantSearch) { + this.myDelta = delta; + this.myRemoved = removed; + this.myFilesToCompile = filesToCompile; + this.myCompiledFiles = compiledFiles; + this.myAffectedFiles = affectedFiles; + this.myFilter = filter; + this.myConstantSearch = constantSearch; + + myDelayedWorks = new DelayedWorks(); + + myUpdated = new Util(myDelta); + mySelf = new Util(Mappings.this); + myOriginal = new Util(); + } + + private void processDisappearedClasses() { + myDelta.runPostPasses(); + myDelta.compensateRemovedContent(myFilesToCompile); + + if (myRemoved != null) { + for (String file : myRemoved) { final Collection classes = mySourceFileToClasses.get(myContext.get(file)); if (classes != null) { for (ClassRepr c : classes) { debug("Affecting usages of removed class ", c.name); - u.affectAll(c.name, affectedFiles, filter); + myUpdated.affectAll(c.name, myAffectedFiles, myFilter); } } } } + } - class FileClasses { - final int fileName; - final Set fileClasses; + private void processAddedMethods (final DiffState state, final ClassRepr.Diff diff, final ClassRepr it) { + debug("Processing added methods: "); + for (final MethodRepr m : diff.methods().added()) { + debug("Method: ", m.name); - FileClasses(int fileName, Collection fileClasses) { - this.fileName = fileName; - this.fileClasses = new HashSet(fileClasses); + if (it.isAnnotation()) { + debug("Class is annotation, skipping method analysis"); + continue; } - } - final List newClasses = new ArrayList(); - delta.mySourceFileToClasses.forEachEntry(new TIntObjectProcedure>() { - @Override - public boolean execute(int fileName, Collection classes) { - newClasses.add(new FileClasses(fileName, classes)); - return true; + if ((it.access & Opcodes.ACC_INTERFACE) > 0 || + (it.access & Opcodes.ACC_ABSTRACT) > 0 || + (m.access & Opcodes.ACC_ABSTRACT) > 0) { + debug("Class is abstract, or is interface, or added method in abstract => affecting all subclasses"); + myUpdated.affectSubclasses(it.name, myAffectedFiles, state.affectedUsages, state.dependants, false); } - }); - for (final FileClasses compiledFile : newClasses) { - final int fileName = compiledFile.fileName; - final Set classes = compiledFile.fileClasses; - final Set pastClasses = (Set)mySourceFileToClasses.get(fileName); - final TIntHashSet dependants = new TIntHashSet(DEFAULT_SET_CAPACITY, DEFAULT_SET_LOAD_FACTOR); + TIntHashSet propagated = null; - final Set affectedUsages = new HashSet(); - final Set annotationQuery = new HashSet(); - final Map usageConstraints = new HashMap(); + if ((m.access & Opcodes.ACC_PRIVATE) == 0 && m.name != myInitName) { + final ClassRepr oldIt = getReprByName(it.name); - final Difference.Specifier classDiff = Difference.make(pastClasses, classes); + if (oldIt != null && mySelf.findOverridenMethods(m, oldIt).size() > 0) { - debug("Processing changed classes:"); - for (final Pair changed : classDiff.changed()) { - final ClassRepr it = changed.first; - final ClassRepr.Diff diff = (ClassRepr.Diff)changed.second; + } + else { + if (m.argumentTypes.length > 0) { + propagated = myUpdated.propagateMethodAccess(m.name, it.name); + debug("Conservative case on overriding methods, affecting method usages"); + myUpdated.affectMethodUsages(m, propagated, m.createMetaUsage(myContext, it.name), state.affectedUsages, state.dependants); + } + } + } - self.appendDependents(it, dependants); + if ((m.access & Opcodes.ACC_PRIVATE) == 0) { + final Collection> affectedMethods = myUpdated.findAllMethodsBySpecificity(m, it); + final MethodRepr.Predicate overrides = MethodRepr.equalByJavaRules(m); - delta.addChangedClass(it.name); - - debug("Changed: ", it.name); - - final int addedModifiers = diff.addedModifiers(); - - final boolean superClassChanged = (diff.base() & Difference.SUPERCLASS) > 0; - final boolean interfacesChanged = !diff.interfaces().unchanged(); - final boolean signatureChanged = (diff.base() & Difference.SIGNATURE) > 0; - - if (superClassChanged || interfacesChanged || signatureChanged) { - debug("Superclass changed: ", superClassChanged); - debug("Interfaces changed: ", interfacesChanged); - debug("Signature changed ", signatureChanged); - - final boolean extendsChanged = superClassChanged && !diff.extendsAdded(); - final boolean interfacesRemoved = interfacesChanged && !diff.interfaces().removed().isEmpty(); - - debug("Extends changed: ", extendsChanged); - debug("Interfaces removed: ", interfacesRemoved); - - u.affectSubclasses(it.name, affectedFiles, affectedUsages, dependants, extendsChanged || interfacesRemoved || signatureChanged); + if (propagated == null) { + propagated = myUpdated.propagateMethodAccess(m.name, it.name); } - if ((diff.addedModifiers() & Opcodes.ACC_INTERFACE) > 0 || (diff.removedModifiers() & Opcodes.ACC_INTERFACE) > 0) { - debug("Class-to-interface or interface-to-class conversion detected, added class usage to affected usages"); - affectedUsages.add(it.createUsage()); - } + final Collection lessSpecific = it.findMethods(myUpdated.lessSpecific(m)); - if (it.isAnnotation() && it.policy == RetentionPolicy.SOURCE) { - debug("Annotation, retention policy = SOURCE => a switch to non-incremental mode requested"); - if (!incrementalDecision(it.outerClassName, it, affectedFiles, filter)) { - debug("End of Differentiate, returning false"); - return false; + for (final MethodRepr mm : lessSpecific) { + if (!mm.equals(m)) { + debug("Found less specific method, affecting method usages"); + myUpdated.affectMethodUsages(mm, propagated, mm.createUsage(myContext, it.name), state.affectedUsages, state.dependants); } } - if ((addedModifiers & Opcodes.ACC_PROTECTED) > 0) { - debug("Introduction of 'protected' modifier detected, adding class usage + inheritance constraint to affected usages"); - final UsageRepr.Usage usage = it.createUsage(); + debug("Processing affected by specificity methods"); + for (final Pair p : affectedMethods) { + final MethodRepr mm = p.first; + final ClassRepr cc = p.second; - affectedUsages.add(usage); - usageConstraints.put(usage, u.new InheritanceConstraint(it.name)); - } + if (cc == myMockClass) { - if (diff.packageLocalOn()) { - debug("Introduction of 'package local' access detected, adding class usage + package constraint to affected usages"); - final UsageRepr.Usage usage = it.createUsage(); - - affectedUsages.add(usage); - usageConstraints.put(usage, u.new PackageConstraint(it.getPackageName())); - } - - if ((addedModifiers & Opcodes.ACC_FINAL) > 0 || (addedModifiers & Opcodes.ACC_PRIVATE) > 0) { - debug("Introduction of 'private' or 'final' modifier(s) detected, adding class usage to affected usages"); - affectedUsages.add(it.createUsage()); - } - - if ((addedModifiers & Opcodes.ACC_ABSTRACT) > 0 || (addedModifiers & Opcodes.ACC_STATIC) > 0) { - debug("Introduction of 'abstract' or 'static' modifier(s) detected, adding class new usage to affected usages"); - affectedUsages.add(UsageRepr.createClassNewUsage(myContext, it.name)); - } - - if (it.isAnnotation()) { - debug("Class is annotation, performing annotation-specific analysis"); - - if (diff.retentionChanged()) { - debug("Retention policy change detected, adding class usage to affected usages"); - affectedUsages.add(it.createUsage()); } else { - final Collection removedtargets = diff.targets().removed(); + final Option inheritorOf = mySelf.isInheritorOf(cc.name, it.name); - if (removedtargets.contains(ElemType.LOCAL_VARIABLE)) { - debug("Removed target contains LOCAL_VARIABLE => a switch to non-incremental mode requested"); - if (!incrementalDecision(it.outerClassName, it, affectedFiles, filter)) { - debug("End of Differentiate, returning false"); - return false; + debug("Method: ", mm.name); + debug("Class : ", cc.name); + + if (overrides.satisfy(mm) && inheritorOf.isValue() && inheritorOf.value()) { + debug("Current method overrides that found"); + + final int file = myClassToSourceFile.get(cc.name); + + if (file > 0) { + final String f = myContext.getValue(file); + debug("Affecting file ", f); + myAffectedFiles.add(new File(f)); } } - - if (!removedtargets.isEmpty()) { - debug("Removed some annotation targets, adding annotation query"); - annotationQuery.add((UsageRepr.AnnotationUsage)UsageRepr - .createAnnotationUsage(myContext, TypeRepr.createClassType(myContext, it.name), null, EnumSet.copyOf(removedtargets))); - } - - for (final MethodRepr m : diff.methods().added()) { - if (!m.hasValue()) { - debug("Added method with no default value: ", m.name); - debug("Adding class usage to affected usages"); - affectedUsages.add(it.createUsage()); - } - } - } - - debug("End of annotation-specific analysis"); - } - - debug("Processing added methods: "); - for (final MethodRepr m : diff.methods().added()) { - debug("Method: ", m.name); - - if (it.isAnnotation()) { - debug("Class is annotation, skipping method analysis"); - continue; - } - - if ((it.access & Opcodes.ACC_INTERFACE) > 0 || - (it.access & Opcodes.ACC_ABSTRACT) > 0 || - (m.access & Opcodes.ACC_ABSTRACT) > 0) { - debug("Class is abstract, or is interface, or added method in abstract => affecting all subclasses"); - u.affectSubclasses(it.name, affectedFiles, affectedUsages, dependants, false); - } - - TIntHashSet propagated = null; - - if ((m.access & Opcodes.ACC_PRIVATE) == 0 && m.name != myInitName) { - final ClassRepr oldIt = getReprByName(it.name); - - if (oldIt != null && self.findOverridenMethods(m, oldIt).size() > 0) { - - } else { - if (m.argumentTypes.length > 0) { - propagated = u.propagateMethodAccess(m.name, it.name); - debug("Conservative case on overriding methods, affecting method usages"); - u.affectMethodUsages(m, propagated, m.createMetaUsage(myContext, it.name), affectedUsages, dependants); - } - } - } + debug("Current method does not override that found"); - if ((m.access & Opcodes.ACC_PRIVATE) == 0) { - final Collection> affectedMethods = u.findAllMethodsBySpecificity(m, it); - final MethodRepr.Predicate overrides = MethodRepr.equalByJavaRules(m); + final TIntHashSet yetPropagated = mySelf.propagateMethodAccess(mm.name, it.name); - if (propagated == null) { - propagated = u.propagateMethodAccess(m.name, it.name); - } - - final Collection lessSpecific = it.findMethods(u.lessSpecific(m)); - - for (final MethodRepr mm : lessSpecific) { - if (!mm.equals(m)) { - debug("Found less specific method, affecting method usages"); - u.affectMethodUsages(mm, propagated, mm.createUsage(myContext, it.name), affectedUsages, dependants); - } - } - - debug("Processing affected by specificity methods"); - for (final Pair p : affectedMethods) { - final MethodRepr mm = p.first; - final ClassRepr cc = p.second; - - if (cc == myMockClass) { - - } - else { - final Option inheritorOf = self.isInheritorOf(cc.name, it.name); - - debug("Method: ", mm.name); - debug("Class : ", cc.name); - - if (overrides.satisfy(mm) && inheritorOf.isValue() && inheritorOf.value()) { - debug("Current method overrides that found"); - - final int file = myClassToSourceFile.get(cc.name); - - if (file > 0) { - final String f = myContext.getValue(file); - debug("Affecting file ", f); - affectedFiles.add(new File(f)); - } - } - else { - debug("Current method does not override that found"); - - final TIntHashSet yetPropagated = self.propagateMethodAccess(mm.name, it.name); - - if (inheritorOf.isValue() && inheritorOf.value()) { - final TIntHashSet deps = myClassToClassDependency.get(cc.name); - - if (deps != null) { - addAll(dependants, deps); - } - - u.affectMethodUsages(mm, yetPropagated, mm.createUsage(myContext, cc.name), affectedUsages, dependants); - } - - debug("Affecting method usages for that found"); - u.affectMethodUsages(mm, yetPropagated, mm.createUsage(myContext, it.name), affectedUsages, dependants); - } - } - } - - final TIntHashSet subClasses = getAllSubclasses(it.name); - - if (subClasses != null) { - subClasses.forEach(new TIntProcedure() { - @Override - public boolean execute(int subClass) { - final ClassRepr r = u.reprByName(subClass); - final int sourceFileName = myClassToSourceFile.get(subClass); - - if (r != null && sourceFileName > 0) { - final int outerClass = r.outerClassName; - - if (u.methodVisible(outerClass, m)) { - final String f = myContext.getValue(sourceFileName); - debug("Affecting file due to local overriding: ", f); - affectedFiles.add(new File(f)); - } - } - return true; - } - }); - } - } - } - debug("End of added methods processing"); - - debug("Processing removed methods:"); - for (final MethodRepr m : diff.methods().removed()) { - debug("Method ", m.name); - - final Collection> overridenMethods = u.findOverridenMethods(m, it); - final TIntHashSet propagated = u.propagateMethodAccess(m.name, it.name); - - if (overridenMethods.size() == 0) { - debug("No overridden methods found, affecting method usages"); - u.affectMethodUsages(m, propagated, m.createUsage(myContext, it.name), affectedUsages, dependants); - } - else { - boolean clear = true; - - loop: - for (final Pair overriden : overridenMethods) { - final MethodRepr mm = overriden.first; - - if (mm == myMockMethod || !mm.type.equals(m.type) || !empty(mm.signature) || !empty(m.signature)) { - clear = false; - break loop; - } - } - - if (!clear) { - debug("No clearly overridden methods found, affecting method usages"); - u.affectMethodUsages(m, propagated, m.createUsage(myContext, it.name), affectedUsages, dependants); - } - } - - final Collection> overriding = u.findOverridingMethods(m, it, false); - - for (final Pair p : overriding) { - final int fName = myClassToSourceFile.get(p.second.name); - debug("Affecting file by overriding: ", fName); - affectedFiles.add(new File(myContext.getValue(fName))); - } - - if ((m.access & Opcodes.ACC_ABSTRACT) == 0) { - propagated.forEach(new TIntProcedure() { - @Override - public boolean execute(int p) { - if (p != it.name) { - final ClassRepr s = u.reprByName(p); - - if (s != null) { - final Collection> overridenInS = u.findOverridenMethods(m, s); - - overridenInS.addAll(overridenMethods); - - boolean allAbstract = true; - boolean visited = false; - - for (final Pair pp : overridenInS) { - final ClassRepr cc = pp.second; - - if (cc == myMockClass) { - visited = true; - continue; - } - - if (cc.name == it.name) { - continue; - } - - visited = true; - allAbstract = ((pp.first.access & Opcodes.ACC_ABSTRACT) > 0) || ((cc.access & Opcodes.ACC_INTERFACE) > 0); - - if (!allAbstract) { - break; - } - } - - if (allAbstract && visited) { - final int source = myClassToSourceFile.get(p); - - if (source > 0) { - final String f = myContext.getValue(source); - debug( - "Removed method is not abstract & overrides some abstract method which is not then over-overriden in subclass ", - p); - debug("Affecting subclass source file ", f); - affectedFiles.add(new File(f)); - } - } - } - } - return true; - } - }); - } - } - debug("End of removed methods processing"); - - debug("Processing changed methods:"); - for (final Pair mr : diff.methods().changed()) { - final MethodRepr m = mr.first; - final MethodRepr.Diff d = (MethodRepr.Diff)mr.second; - final boolean throwsChanged = (d.exceptions().added().size() > 0) || (d.exceptions().changed().size() > 0); - - debug("Method: ", m.name); - - if (it.isAnnotation()) { - if (d.defaultRemoved()) { - debug("Class is annotation, default value is removed => adding annotation query"); - final TIntHashSet l = new TIntHashSet(DEFAULT_SET_CAPACITY, DEFAULT_SET_LOAD_FACTOR); - l.add(m.name); - annotationQuery.add((UsageRepr.AnnotationUsage)UsageRepr - .createAnnotationUsage(myContext, TypeRepr.createClassType(myContext, it.name), l, null)); - } - } - else if (d.base() != Difference.NONE || throwsChanged) { - final TIntHashSet propagated = u.propagateMethodAccess(m.name, it.name); - - boolean affected = false; - boolean constrained = false; - - final Set usages = new HashSet(); - - if (d.packageLocalOn()) { - debug("Method became package-local, affecting method usages outside the package"); - u.affectMethodUsages(m, propagated, m.createUsage(myContext, it.name), usages, dependants); - - for (final UsageRepr.Usage usage : usages) { - usageConstraints.put(usage, u.new InheritanceConstraint(it.name)); - } - - affectedUsages.addAll(usages); - affected = true; - constrained = true; - } - - if ((d.base() & Difference.TYPE) > 0 || (d.base() & Difference.SIGNATURE) > 0 || throwsChanged) { - if (!affected) { - debug("Return type, throws list or signature changed --- affecting method usages"); - u.affectMethodUsages(m, propagated, m.createUsage(myContext, it.name), usages, dependants); - affectedUsages.addAll(usages); - } - } - else if ((d.base() & Difference.ACCESS) > 0) { - if ((d.addedModifiers() & Opcodes.ACC_STATIC) > 0 || - (d.removedModifiers() & Opcodes.ACC_STATIC) > 0 || - (d.addedModifiers() & Opcodes.ACC_PRIVATE) > 0) { - if (!affected) { - debug("Added static or private specifier or removed static specifier --- affecting method usages"); - u.affectMethodUsages(m, propagated, m.createUsage(myContext, it.name), usages, dependants); - affectedUsages.addAll(usages); - } - - if ((d.addedModifiers() & Opcodes.ACC_STATIC) > 0) { - debug("Added static specifier --- affecting subclasses"); - u.affectSubclasses(it.name, affectedFiles, affectedUsages, dependants, false); - } - } - else { - if ((d.addedModifiers() & Opcodes.ACC_FINAL) > 0 || - (d.addedModifiers() & Opcodes.ACC_PUBLIC) > 0 || - (d.addedModifiers() & Opcodes.ACC_ABSTRACT) > 0) { - debug("Added final, public or abstract specifier --- affecting subclasses"); - u.affectSubclasses(it.name, affectedFiles, affectedUsages, dependants, false); - } - - if ((d.addedModifiers() & Opcodes.ACC_PROTECTED) > 0 && !((d.removedModifiers() & Opcodes.ACC_PRIVATE) > 0)) { - if (!constrained) { - debug("Added public or package-local method became protected --- affect method usages with protected constraint"); - if (!affected) { - u.affectMethodUsages(m, propagated, m.createUsage(myContext, it.name), usages, dependants); - affectedUsages.addAll(usages); - } - - for (final UsageRepr.Usage usage : usages) { - usageConstraints.put(usage, u.new InheritanceConstraint(it.name)); - } - } - } - } - } - } - } - debug("End of changed methods processing"); - - final int mask = Opcodes.ACC_STATIC | Opcodes.ACC_FINAL; - - debug("Processing added fields"); - for (final FieldRepr f : diff.fields().added()) { - debug("Field: ", f.name); - - final boolean fPrivate = (f.access & Opcodes.ACC_PRIVATE) > 0; - final boolean fProtected = (f.access & Opcodes.ACC_PROTECTED) > 0; - final boolean fPublic = (f.access & Opcodes.ACC_PUBLIC) > 0; - final boolean fPLocal = !fPrivate && !fProtected && !fPublic; - - if (!fPrivate) { - final TIntHashSet subClasses = getAllSubclasses(it.name); - subClasses.forEach(new TIntProcedure() { - @Override - public boolean execute(int subClass) { - final ClassRepr r = u.reprByName(subClass); - final int sourceFileName = myClassToSourceFile.get(subClass); - - if (r != null && sourceFileName > 0) { - if (r.isLocal) { - debug( - "Affecting local subclass (introduced field can potentially hide surrounding method parameters/local variables): ", - sourceFileName); - affectedFiles.add(new File(myContext.getValue(sourceFileName))); - } - else { - final int outerClass = r.outerClassName; - - if (!empty(outerClass) && u.fieldVisible(outerClass, f)) { - debug("Affecting inner subclass (introduced field can potentially hide surrounding class fields): ", - sourceFileName); - affectedFiles.add(new File(myContext.getValue(sourceFileName))); - } - } - } - - debug("Affecting field usages referenced from subclass ", subClass); - final TIntHashSet propagated = u.propagateFieldAccess(f.name, subClass); - u.affectFieldUsages(f, propagated, f.createUsage(myContext, subClass), affectedUsages, dependants); - - final TIntHashSet deps = myClassToClassDependency.get(subClass); + if (inheritorOf.isValue() && inheritorOf.value()) { + final TIntHashSet deps = myClassToClassDependency.get(cc.name); if (deps != null) { - addAll(dependants, deps); - } - return true; - } - }); - } - - final Collection> overridden = u.findOverridenFields(f, it); - - for (final Pair p : overridden) { - final FieldRepr ff = p.first; - final ClassRepr cc = p.second; - - final boolean ffPrivate = (ff.access & Opcodes.ACC_PRIVATE) > 0; - final boolean ffProtected = (ff.access & Opcodes.ACC_PROTECTED) > 0; - final boolean ffPublic = (ff.access & Opcodes.ACC_PUBLIC) > 0; - final boolean ffPLocal = Difference.isPackageLocal(ff.access); - - if (!ffPrivate) { - final TIntHashSet propagated = o.propagateFieldAccess(ff.name, cc.name); - final Set localUsages = new HashSet(); - - debug("Affecting usages of overridden field in class ", cc.name); - u.affectFieldUsages(ff, propagated, ff.createUsage(myContext, cc.name), localUsages, dependants); - - if (fPrivate || (fPublic && (ffPublic || ffPLocal)) || (fProtected && ffProtected) || (fPLocal && ffPLocal)) { - - } - else { - Util.UsageConstraint constaint; - - if ((ffProtected && fPublic) || (fProtected && ffPublic) || (ffPLocal && fProtected)) { - constaint = u.new NegationConstraint(u.new InheritanceConstraint(cc.name)); - } - else if (ffPublic && ffPLocal) { - constaint = u.new NegationConstraint(u.new PackageConstraint(cc.getPackageName())); - } - else { - constaint = u.new IntersectionConstraint(u.new NegationConstraint(u.new InheritanceConstraint(cc.name)), - u.new NegationConstraint(u.new PackageConstraint(cc.getPackageName()))); + addAll(state.dependants, deps); } - for (final UsageRepr.Usage usage : localUsages) { - usageConstraints.put(usage, constaint); - } + myUpdated + .affectMethodUsages(mm, yetPropagated, mm.createUsage(myContext, cc.name), state.affectedUsages, state.dependants); } - affectedUsages.addAll(localUsages); + debug("Affecting method usages for that found"); + myUpdated + .affectMethodUsages(mm, yetPropagated, mm.createUsage(myContext, it.name), state.affectedUsages, state.dependants); } } } - debug("End of added fields processing"); - debug("Processing removed fields:"); - for (final FieldRepr f : diff.fields().removed()) { - debug("Field: ", f.name); + final TIntHashSet subClasses = getAllSubclasses(it.name); - if ((f.access & Opcodes.ACC_PRIVATE) == 0 && (f.access & mask) == mask && f.hasValue()) { - debug("Field had value and was (non-private) final static => a switch to non-incremental mode requested"); - if (constantSearch != null) { - works.addConstantWork(it.name, f, true, false); - } - else { - if (!incrementalDecision(it.name, f, affectedFiles, filter)) { - debug("End of Differentiate, returning false"); - return false; - } - } - } - - final TIntHashSet propagated = u.propagateFieldAccess(f.name, it.name); - u.affectFieldUsages(f, propagated, f.createUsage(myContext, it.name), affectedUsages, dependants); - } - debug("End of removed fields processing"); - - debug("Processing changed fields:"); - for (final Pair f : diff.fields().changed()) { - final Difference d = f.second; - final FieldRepr field = f.first; - - debug("Field: ", field.name); - - if ((field.access & Opcodes.ACC_PRIVATE) == 0 && (field.access & mask) == mask) { - final int changedModifiers = d.addedModifiers() | d.removedModifiers(); - final boolean harmful = (changedModifiers & (Opcodes.ACC_STATIC | Opcodes.ACC_FINAL)) > 0; - final boolean accessChanged = (changedModifiers & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED)) > 0; - final boolean valueChanged = (d.base() & Difference.VALUE) > 0 && d.hadValue(); - - if (harmful || valueChanged || (accessChanged && !d.weakedAccess())) { - debug("Inline field changed it's access or value => a switch to non-incremental mode requested"); - if (constantSearch != null) { - works.addConstantWork(it.name, field, false, accessChanged); - } - else { - if (!incrementalDecision(it.name, field, affectedFiles, filter)) { - debug("End of Differentiate, returning false"); - return false; - } - } - } - } - - if (d.base() != Difference.NONE) { - final TIntHashSet propagated = u.propagateFieldAccess(field.name, it.name); - - if ((d.base() & Difference.TYPE) > 0 || (d.base() & Difference.SIGNATURE) > 0) { - debug("Type or signature changed --- affecting field usages"); - u.affectFieldUsages(field, propagated, field.createUsage(myContext, it.name), affectedUsages, dependants); - } - else if ((d.base() & Difference.ACCESS) > 0) { - if ((d.addedModifiers() & Opcodes.ACC_STATIC) > 0 || - (d.removedModifiers() & Opcodes.ACC_STATIC) > 0 || - (d.addedModifiers() & Opcodes.ACC_PRIVATE) > 0 || - (d.addedModifiers() & Opcodes.ACC_VOLATILE) > 0) { - debug("Added/removed static modifier or added private/volatile modifier --- affecting field usages"); - u.affectFieldUsages(field, propagated, field.createUsage(myContext, it.name), affectedUsages, dependants); - } - else { - boolean affected = false; - final Set usages = new HashSet(); - - if ((d.addedModifiers() & Opcodes.ACC_FINAL) > 0) { - debug("Added final modifier --- affecting field assign usages"); - u.affectFieldUsages(field, propagated, field.createAssignUsage(myContext, it.name), usages, dependants); - affectedUsages.addAll(usages); - affected = true; - } - - if ((d.removedModifiers() & Opcodes.ACC_PUBLIC) > 0) { - debug("Removed public modifier, affecting field usages with appropriate constraint"); - if (!affected) { - u.affectFieldUsages(field, propagated, field.createUsage(myContext, it.name), usages, dependants); - affectedUsages.addAll(usages); - } - - for (final UsageRepr.Usage usage : usages) { - if ((d.addedModifiers() & Opcodes.ACC_PROTECTED) > 0) { - usageConstraints.put(usage, u.new InheritanceConstraint(it.name)); - } - else { - usageConstraints.put(usage, u.new PackageConstraint(it.getPackageName())); - } - } - } - } - } - } - } - debug("End of changed fields processing"); - } - debug("End of changed classes processing"); - - debug("Processing removed classes:"); - for (final ClassRepr c : classDiff.removed()) { - delta.addDeletedClass(c); - self.appendDependents(c, dependants); - debug("Adding usages of class ", c.name); - affectedUsages.add(c.createUsage()); - } - debug("End of removed classes processing."); - - debug("Processing added classes:"); - for (final ClassRepr c : classDiff.added()) { - debug("Class name: ", c.name); - delta.addChangedClass(c.name); - - final TIntHashSet depClasses = myClassToClassDependency.get(c.name); - - if (depClasses != null) { - depClasses.forEach(new TIntProcedure() { + if (subClasses != null) { + subClasses.forEach(new TIntProcedure() { @Override - public boolean execute(int depClass) { - final int fName = myClassToSourceFile.get(depClass); + public boolean execute(int subClass) { + final ClassRepr r = myUpdated.reprByName(subClass); + final int sourceFileName = myClassToSourceFile.get(subClass); - if (fName > 0) { - final String f = myContext.getValue(fName); - final File theFile = new File(f); + if (r != null && sourceFileName > 0) { + final int outerClass = r.outerClassName; - if (filter.accept(theFile)) { - debug("Adding dependent file ", f); - affectedFiles.add(theFile); + if (myUpdated.methodVisible(outerClass, m)) { + final String f = myContext.getValue(sourceFileName); + debug("Affecting file due to local overriding: ", f); + myAffectedFiles.add(new File(f)); } } return true; @@ -1620,92 +1116,704 @@ public class Mappings { }); } } - debug("End of added classes processing."); + } + debug("End of added methods processing"); + } - debug("Checking dependent files:"); - final TIntHashSet dependentFiles = new TIntHashSet(DEFAULT_SET_CAPACITY, DEFAULT_SET_LOAD_FACTOR); + private void processRemovedMethods (final DiffState state, final ClassRepr.Diff diff, final ClassRepr it) { + debug("Processing removed methods:"); + for (final MethodRepr m : diff.methods().removed()) { + debug("Method ", m.name); - dependants.forEach(new TIntProcedure() { - @Override - public boolean execute(int depClass) { - final int file = myClassToSourceFile.get(depClass); - if (file > 0) { - dependentFiles.add(file); + final Collection> overridenMethods = myUpdated.findOverridenMethods(m, it); + final TIntHashSet propagated = myUpdated.propagateMethodAccess(m.name, it.name); + + if (overridenMethods.size() == 0) { + debug("No overridden methods found, affecting method usages"); + myUpdated.affectMethodUsages(m, propagated, m.createUsage(myContext, it.name), state.affectedUsages, state.dependants); + } + else { + boolean clear = true; + + loop: + for (final Pair overriden : overridenMethods) { + final MethodRepr mm = overriden.first; + + if (mm == myMockMethod || !mm.type.equals(m.type) || !empty(mm.signature) || !empty(m.signature)) { + clear = false; + break loop; } - return true; - } - }); - - - filewise: - for (final int depFile : dependentFiles.toArray()) { // todo: avoid toArray()? - final File theFile = new File(myContext.getValue(depFile)); - - if (affectedFiles.contains(theFile) || compiledFiles.contains(theFile)) { - continue filewise; } - debug("Dependent file: ", depFile); - final Collection depClusters = mySourceFileToUsages.get(depFile); - if (depClusters != null) { - for (final UsageRepr.Cluster depCluster : depClusters) { - final Set depUsages = depCluster.getUsages(); - if (depUsages == null) { - continue; - } - final Set usages = new HashSet(depUsages); + if (!clear) { + debug("No clearly overridden methods found, affecting method usages"); + myUpdated.affectMethodUsages(m, propagated, m.createUsage(myContext, it.name), state.affectedUsages, state.dependants); + } + } - usages.retainAll(affectedUsages); + final Collection> overriding = myUpdated.findOverridingMethods(m, it, false); - if (!usages.isEmpty()) { - for (UsageRepr.Usage usage : usages) { - final Util.UsageConstraint constraint = usageConstraints.get(usage); + for (final Pair p : overriding) { + final int fName = myClassToSourceFile.get(p.second.name); + debug("Affecting file by overriding: ", fName); + myAffectedFiles.add(new File(myContext.getValue(fName))); + } - if (constraint == null) { - debug("Added file with no constraints"); - affectedFiles.add(theFile); - continue filewise; + if ((m.access & Opcodes.ACC_ABSTRACT) == 0) { + propagated.forEach(new TIntProcedure() { + @Override + public boolean execute(int p) { + if (p != it.name) { + final ClassRepr s = myUpdated.reprByName(p); + + if (s != null) { + final Collection> overridenInS = myUpdated.findOverridenMethods(m, s); + + overridenInS.addAll(overridenMethods); + + boolean allAbstract = true; + boolean visited = false; + + for (final Pair pp : overridenInS) { + final ClassRepr cc = pp.second; + + if (cc == myMockClass) { + visited = true; + continue; + } + + if (cc.name == it.name) { + continue; + } + + visited = true; + allAbstract = ((pp.first.access & Opcodes.ACC_ABSTRACT) > 0) || ((cc.access & Opcodes.ACC_INTERFACE) > 0); + + if (!allAbstract) { + break; + } } - else { - final TIntHashSet residenceClasses = depCluster.getResidence(usage); - for (final int residentName : residenceClasses.toArray()) { - if (constraint.checkResidence(residentName)) { - debug("Added file with satisfied constraint"); - affectedFiles.add(theFile); - continue filewise; - } + + if (allAbstract && visited) { + final int source = myClassToSourceFile.get(p); + + if (source > 0) { + final String f = myContext.getValue(source); + debug( + "Removed method is not abstract & overrides some abstract method which is not then over-overriden in subclass ", + p); + debug("Affecting subclass source file ", f); + myAffectedFiles.add(new File(f)); } } } } + return true; + } + }); + } + } + debug("End of removed methods processing"); + } - if (annotationQuery.size() > 0) { - final Collection annotationUsages = mySourceFileToAnnotationUsages.get(depFile); + private void processChangedMethods (final DiffState state, final ClassRepr.Diff diff, final ClassRepr it) { + debug("Processing changed methods:"); + for (final Pair mr : diff.methods().changed()) { + final MethodRepr m = mr.first; + final MethodRepr.Diff d = (MethodRepr.Diff)mr.second; + final boolean throwsChanged = (d.exceptions().added().size() > 0) || (d.exceptions().changed().size() > 0); - for (final UsageRepr.Usage usage : annotationUsages) { - for (final UsageRepr.AnnotationUsage query : annotationQuery) { - if (query.satisfies(usage)) { - debug("Added file due to annotation query"); - affectedFiles.add(theFile); + debug("Method: ", m.name); + + if (it.isAnnotation()) { + if (d.defaultRemoved()) { + debug("Class is annotation, default value is removed => adding annotation query"); + final TIntHashSet l = new TIntHashSet(DEFAULT_SET_CAPACITY, DEFAULT_SET_LOAD_FACTOR); + l.add(m.name); + state.annotationQuery.add((UsageRepr.AnnotationUsage)UsageRepr + .createAnnotationUsage(myContext, TypeRepr.createClassType(myContext, it.name), l, null)); + } + } + else if (d.base() != Difference.NONE || throwsChanged) { + final TIntHashSet propagated = myUpdated.propagateMethodAccess(m.name, it.name); + + boolean affected = false; + boolean constrained = false; + + final Set usages = new HashSet(); + + if (d.packageLocalOn()) { + debug("Method became package-local, affecting method usages outside the package"); + myUpdated.affectMethodUsages(m, propagated, m.createUsage(myContext, it.name), usages, state.dependants); + + for (final UsageRepr.Usage usage : usages) { + state.usageConstraints.put(usage, myUpdated.new InheritanceConstraint(it.name)); + } + + state.affectedUsages.addAll(usages); + affected = true; + constrained = true; + } + + if ((d.base() & Difference.TYPE) > 0 || (d.base() & Difference.SIGNATURE) > 0 || throwsChanged) { + if (!affected) { + debug("Return type, throws list or signature changed --- affecting method usages"); + myUpdated.affectMethodUsages(m, propagated, m.createUsage(myContext, it.name), usages, state.dependants); + state.affectedUsages.addAll(usages); + } + } + else if ((d.base() & Difference.ACCESS) > 0) { + if ((d.addedModifiers() & Opcodes.ACC_STATIC) > 0 || + (d.removedModifiers() & Opcodes.ACC_STATIC) > 0 || + (d.addedModifiers() & Opcodes.ACC_PRIVATE) > 0) { + if (!affected) { + debug("Added static or private specifier or removed static specifier --- affecting method usages"); + myUpdated.affectMethodUsages(m, propagated, m.createUsage(myContext, it.name), usages, state.dependants); + state.affectedUsages.addAll(usages); + } + + if ((d.addedModifiers() & Opcodes.ACC_STATIC) > 0) { + debug("Added static specifier --- affecting subclasses"); + myUpdated.affectSubclasses(it.name, myAffectedFiles, state.affectedUsages, state.dependants, false); + } + } + else { + if ((d.addedModifiers() & Opcodes.ACC_FINAL) > 0 || + (d.addedModifiers() & Opcodes.ACC_PUBLIC) > 0 || + (d.addedModifiers() & Opcodes.ACC_ABSTRACT) > 0) { + debug("Added final, public or abstract specifier --- affecting subclasses"); + myUpdated.affectSubclasses(it.name, myAffectedFiles, state.affectedUsages, state.dependants, false); + } + + if ((d.addedModifiers() & Opcodes.ACC_PROTECTED) > 0 && !((d.removedModifiers() & Opcodes.ACC_PRIVATE) > 0)) { + if (!constrained) { + debug("Added public or package-local method became protected --- affect method usages with protected constraint"); + if (!affected) { + myUpdated.affectMethodUsages(m, propagated, m.createUsage(myContext, it.name), usages, state.dependants); + state.affectedUsages.addAll(usages); + } + + for (final UsageRepr.Usage usage : usages) { + state.usageConstraints.put(usage, myUpdated.new InheritanceConstraint(it.name)); + } + } + } + } + } + } + } + debug("End of changed methods processing"); + } + + private boolean processAddedFields (final DiffState state, final ClassRepr.Diff diff, final ClassRepr it) { + debug("Processing added fields"); + + for (final FieldRepr f : diff.fields().added()) { + debug("Field: ", f.name); + + final boolean fPrivate = (f.access & Opcodes.ACC_PRIVATE) > 0; + final boolean fProtected = (f.access & Opcodes.ACC_PROTECTED) > 0; + final boolean fPublic = (f.access & Opcodes.ACC_PUBLIC) > 0; + final boolean fPLocal = !fPrivate && !fProtected && !fPublic; + + if (!fPrivate) { + final TIntHashSet subClasses = getAllSubclasses(it.name); + subClasses.forEach(new TIntProcedure() { + @Override + public boolean execute(int subClass) { + final ClassRepr r = myUpdated.reprByName(subClass); + final int sourceFileName = myClassToSourceFile.get(subClass); + + if (r != null && sourceFileName > 0) { + if (r.isLocal) { + debug( + "Affecting local subclass (introduced field can potentially hide surrounding method parameters/local variables): ", + sourceFileName); + myAffectedFiles.add(new File(myContext.getValue(sourceFileName))); + } + else { + final int outerClass = r.outerClassName; + + if (!empty(outerClass) && myUpdated.fieldVisible(outerClass, f)) { + debug("Affecting inner subclass (introduced field can potentially hide surrounding class fields): ", + sourceFileName); + myAffectedFiles.add(new File(myContext.getValue(sourceFileName))); + } + } + } + + debug("Affecting field usages referenced from subclass ", subClass); + final TIntHashSet propagated = myUpdated.propagateFieldAccess(f.name, subClass); + myUpdated.affectFieldUsages(f, propagated, f.createUsage(myContext, subClass), state.affectedUsages, state.dependants); + + final TIntHashSet deps = myClassToClassDependency.get(subClass); + + if (deps != null) { + addAll(state.dependants, deps); + } + return true; + } + }); + } + + final Collection> overridden = myUpdated.findOverridenFields(f, it); + + for (final Pair p : overridden) { + final FieldRepr ff = p.first; + final ClassRepr cc = p.second; + + final boolean ffPrivate = (ff.access & Opcodes.ACC_PRIVATE) > 0; + final boolean ffProtected = (ff.access & Opcodes.ACC_PROTECTED) > 0; + final boolean ffPublic = (ff.access & Opcodes.ACC_PUBLIC) > 0; + final boolean ffPLocal = Difference.isPackageLocal(ff.access); + + if (!ffPrivate) { + final TIntHashSet propagated = myOriginal.propagateFieldAccess(ff.name, cc.name); + final Set localUsages = new HashSet(); + + debug("Affecting usages of overridden field in class ", cc.name); + myUpdated.affectFieldUsages(ff, propagated, ff.createUsage(myContext, cc.name), localUsages, state.dependants); + + if (fPrivate || (fPublic && (ffPublic || ffPLocal)) || (fProtected && ffProtected) || (fPLocal && ffPLocal)) { + + } + else { + Util.UsageConstraint constaint; + + if ((ffProtected && fPublic) || (fProtected && ffPublic) || (ffPLocal && fProtected)) { + constaint = myUpdated.new NegationConstraint(myUpdated.new InheritanceConstraint(cc.name)); + } + else if (ffPublic && ffPLocal) { + constaint = myUpdated.new NegationConstraint(myUpdated.new PackageConstraint(cc.getPackageName())); + } + else { + constaint = + myUpdated.new IntersectionConstraint(myUpdated.new NegationConstraint(myUpdated.new InheritanceConstraint(cc.name)), + myUpdated.new NegationConstraint( + myUpdated.new PackageConstraint(cc.getPackageName()))); + } + + for (final UsageRepr.Usage usage : localUsages) { + state.usageConstraints.put(usage, constaint); + } + } + + state.affectedUsages.addAll(localUsages); + } + } + } + debug("End of added fields processing"); + + return true; + } + + private boolean processRemovedFields (final DiffState state, final ClassRepr.Diff diff, final ClassRepr it) { + debug("Processing removed fields:"); + + for (final FieldRepr f : diff.fields().removed()) { + debug("Field: ", f.name); + + if ((f.access & Opcodes.ACC_PRIVATE) == 0 && (f.access & DESPERATE_MASK) == DESPERATE_MASK && f.hasValue()) { + debug("Field had value and was (non-private) final static => a switch to non-incremental mode requested"); + if (myConstantSearch != null) { + myDelayedWorks.addConstantWork(it.name, f, true, false); + } + else { + if (!incrementalDecision(it.name, f, myAffectedFiles, myFilter)) { + debug("End of Differentiate, returning false"); + return false; + } + } + } + + final TIntHashSet propagated = myUpdated.propagateFieldAccess(f.name, it.name); + myUpdated.affectFieldUsages(f, propagated, f.createUsage(myContext, it.name), state.affectedUsages, state.dependants); + } + debug("End of removed fields processing"); + + return true; + } + + private boolean processChangedFields (final DiffState state, final ClassRepr.Diff diff, final ClassRepr it) { + debug("Processing changed fields:"); + + for (final Pair f : diff.fields().changed()) { + final Difference d = f.second; + final FieldRepr field = f.first; + + debug("Field: ", field.name); + + if ((field.access & Opcodes.ACC_PRIVATE) == 0 && (field.access & DESPERATE_MASK) == DESPERATE_MASK) { + final int changedModifiers = d.addedModifiers() | d.removedModifiers(); + final boolean harmful = (changedModifiers & (Opcodes.ACC_STATIC | Opcodes.ACC_FINAL)) > 0; + final boolean accessChanged = (changedModifiers & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED)) > 0; + final boolean valueChanged = (d.base() & Difference.VALUE) > 0 && d.hadValue(); + + if (harmful || valueChanged || (accessChanged && !d.weakedAccess())) { + debug("Inline field changed it's access or value => a switch to non-incremental mode requested"); + if (myConstantSearch != null) { + myDelayedWorks.addConstantWork(it.name, field, false, accessChanged); + } + else { + if (!incrementalDecision(it.name, field, myAffectedFiles, myFilter)) { + debug("End of Differentiate, returning false"); + return false; + } + } + } + } + + if (d.base() != Difference.NONE) { + final TIntHashSet propagated = myUpdated.propagateFieldAccess(field.name, it.name); + + if ((d.base() & Difference.TYPE) > 0 || (d.base() & Difference.SIGNATURE) > 0) { + debug("Type or signature changed --- affecting field usages"); + myUpdated.affectFieldUsages(field, propagated, field.createUsage(myContext, it.name), state.affectedUsages, state.dependants); + } + else if ((d.base() & Difference.ACCESS) > 0) { + if ((d.addedModifiers() & Opcodes.ACC_STATIC) > 0 || + (d.removedModifiers() & Opcodes.ACC_STATIC) > 0 || + (d.addedModifiers() & Opcodes.ACC_PRIVATE) > 0 || + (d.addedModifiers() & Opcodes.ACC_VOLATILE) > 0) { + debug("Added/removed static modifier or added private/volatile modifier --- affecting field usages"); + myUpdated + .affectFieldUsages(field, propagated, field.createUsage(myContext, it.name), state.affectedUsages, state.dependants); + } + else { + boolean affected = false; + final Set usages = new HashSet(); + + if ((d.addedModifiers() & Opcodes.ACC_FINAL) > 0) { + debug("Added final modifier --- affecting field assign usages"); + myUpdated.affectFieldUsages(field, propagated, field.createAssignUsage(myContext, it.name), usages, state.dependants); + state.affectedUsages.addAll(usages); + affected = true; + } + + if ((d.removedModifiers() & Opcodes.ACC_PUBLIC) > 0) { + debug("Removed public modifier, affecting field usages with appropriate constraint"); + if (!affected) { + myUpdated.affectFieldUsages(field, propagated, field.createUsage(myContext, it.name), usages, state.dependants); + state.affectedUsages.addAll(usages); + } + + for (final UsageRepr.Usage usage : usages) { + if ((d.addedModifiers() & Opcodes.ACC_PROTECTED) > 0) { + state.usageConstraints.put(usage, myUpdated.new InheritanceConstraint(it.name)); + } + else { + state.usageConstraints.put(usage, myUpdated.new PackageConstraint(it.getPackageName())); + } + } + } + } + } + } + } + debug("End of changed fields processing"); + + return true; + } + + private boolean processChangedClasses(final DiffState state) { + debug("Processing changed classes:"); + for (final Pair changed : state.classDiff.changed()) { + final ClassRepr it = changed.first; + final ClassRepr.Diff diff = (ClassRepr.Diff)changed.second; + + mySelf.appendDependents(it, state.dependants); + + myDelta.addChangedClass(it.name); + + debug("Changed: ", it.name); + + final int addedModifiers = diff.addedModifiers(); + + final boolean superClassChanged = (diff.base() & Difference.SUPERCLASS) > 0; + final boolean interfacesChanged = !diff.interfaces().unchanged(); + final boolean signatureChanged = (diff.base() & Difference.SIGNATURE) > 0; + + if (superClassChanged || interfacesChanged || signatureChanged) { + debug("Superclass changed: ", superClassChanged); + debug("Interfaces changed: ", interfacesChanged); + debug("Signature changed ", signatureChanged); + + final boolean extendsChanged = superClassChanged && !diff.extendsAdded(); + final boolean interfacesRemoved = interfacesChanged && !diff.interfaces().removed().isEmpty(); + + debug("Extends changed: ", extendsChanged); + debug("Interfaces removed: ", interfacesRemoved); + + myUpdated.affectSubclasses(it.name, myAffectedFiles, state.affectedUsages, state.dependants, + extendsChanged || interfacesRemoved || signatureChanged); + } + + if ((diff.addedModifiers() & Opcodes.ACC_INTERFACE) > 0 || (diff.removedModifiers() & Opcodes.ACC_INTERFACE) > 0) { + debug("Class-to-interface or interface-to-class conversion detected, added class usage to affected usages"); + state.affectedUsages.add(it.createUsage()); + } + + if (it.isAnnotation() && it.policy == RetentionPolicy.SOURCE) { + debug("Annotation, retention policy = SOURCE => a switch to non-incremental mode requested"); + if (!incrementalDecision(it.outerClassName, it, myAffectedFiles, myFilter)) { + debug("End of Differentiate, returning false"); + return false; + } + } + + if ((addedModifiers & Opcodes.ACC_PROTECTED) > 0) { + debug("Introduction of 'protected' modifier detected, adding class usage + inheritance constraint to affected usages"); + final UsageRepr.Usage usage = it.createUsage(); + + state.affectedUsages.add(usage); + state.usageConstraints.put(usage, myUpdated.new InheritanceConstraint(it.name)); + } + + if (diff.packageLocalOn()) { + debug("Introduction of 'package local' access detected, adding class usage + package constraint to affected usages"); + final UsageRepr.Usage usage = it.createUsage(); + + state.affectedUsages.add(usage); + state.usageConstraints.put(usage, myUpdated.new PackageConstraint(it.getPackageName())); + } + + if ((addedModifiers & Opcodes.ACC_FINAL) > 0 || (addedModifiers & Opcodes.ACC_PRIVATE) > 0) { + debug("Introduction of 'private' or 'final' modifier(s) detected, adding class usage to affected usages"); + state.affectedUsages.add(it.createUsage()); + } + + if ((addedModifiers & Opcodes.ACC_ABSTRACT) > 0 || (addedModifiers & Opcodes.ACC_STATIC) > 0) { + debug("Introduction of 'abstract' or 'static' modifier(s) detected, adding class new usage to affected usages"); + state.affectedUsages.add(UsageRepr.createClassNewUsage(myContext, it.name)); + } + + if (it.isAnnotation()) { + debug("Class is annotation, performing annotation-specific analysis"); + + if (diff.retentionChanged()) { + debug("Retention policy change detected, adding class usage to affected usages"); + state.affectedUsages.add(it.createUsage()); + } + else { + final Collection removedtargets = diff.targets().removed(); + + if (removedtargets.contains(ElemType.LOCAL_VARIABLE)) { + debug("Removed target contains LOCAL_VARIABLE => a switch to non-incremental mode requested"); + if (!incrementalDecision(it.outerClassName, it, myAffectedFiles, myFilter)) { + debug("End of Differentiate, returning false"); + return false; + } + } + + if (!removedtargets.isEmpty()) { + debug("Removed some annotation targets, adding annotation query"); + state.annotationQuery.add((UsageRepr.AnnotationUsage)UsageRepr + .createAnnotationUsage(myContext, TypeRepr.createClassType(myContext, it.name), null, EnumSet.copyOf(removedtargets))); + } + + for (final MethodRepr m : diff.methods().added()) { + if (!m.hasValue()) { + debug("Added method with no default value: ", m.name); + debug("Adding class usage to affected usages"); + state.affectedUsages.add(it.createUsage()); + } + } + } + + debug("End of annotation-specific analysis"); + } + + processAddedMethods (state, diff, it); + processRemovedMethods (state, diff, it); + processChangedMethods (state, diff, it); + + if (!processAddedFields (state, diff, it)) { + return false; + } + + if (!processRemovedFields (state, diff, it)) { + return false; + } + + if (!processChangedFields (state, diff, it)) { + return false; + } + } + debug("End of changed classes processing"); + + return true; + } + + private void processRemovedClases(final DiffState state) { + debug("Processing removed classes:"); + for (final ClassRepr c : state.classDiff.removed()) { + myDelta.addDeletedClass(c); + mySelf.appendDependents(c, state.dependants); + debug("Adding usages of class ", c.name); + state.affectedUsages.add(c.createUsage()); + } + debug("End of removed classes processing."); + } + + private void processAddedClasses(final DiffState state) { + debug("Processing added classes:"); + for (final ClassRepr c : state.classDiff.added()) { + debug("Class name: ", c.name); + myDelta.addChangedClass(c.name); + + final TIntHashSet depClasses = myClassToClassDependency.get(c.name); + + if (depClasses != null) { + depClasses.forEach(new TIntProcedure() { + @Override + public boolean execute(int depClass) { + final int fName = myClassToSourceFile.get(depClass); + + if (fName > 0) { + final String f = myContext.getValue(fName); + final File theFile = new File(f); + + if (myFilter.accept(theFile)) { + debug("Adding dependent file ", f); + myAffectedFiles.add(theFile); + } + } + return true; + } + }); + } + } + debug("End of added classes processing."); + } + + private void calaulateAffectedFiles(final DiffState state) { + debug("Checking dependent files:"); + final TIntHashSet dependentFiles = new TIntHashSet(DEFAULT_SET_CAPACITY, DEFAULT_SET_LOAD_FACTOR); + + state.dependants.forEach(new TIntProcedure() { + @Override + public boolean execute(int depClass) { + final int file = myClassToSourceFile.get(depClass); + if (file > 0) { + dependentFiles.add(file); + } + return true; + } + }); + + filewise: + for (final int depFile : dependentFiles.toArray()) { // todo: avoid toArray()? + final File theFile = new File(myContext.getValue(depFile)); + + if (myAffectedFiles.contains(theFile) || myCompiledFiles.contains(theFile)) { + continue filewise; + } + + debug("Dependent file: ", depFile); + final Collection depClusters = mySourceFileToUsages.get(depFile); + if (depClusters != null) { + for (final UsageRepr.Cluster depCluster : depClusters) { + final Set depUsages = depCluster.getUsages(); + if (depUsages == null) { + continue; + } + final Set usages = new HashSet(depUsages); + + usages.retainAll(state.affectedUsages); + + if (!usages.isEmpty()) { + for (UsageRepr.Usage usage : usages) { + final Util.UsageConstraint constraint = state.usageConstraints.get(usage); + + if (constraint == null) { + debug("Added file with no constraints"); + myAffectedFiles.add(theFile); + continue filewise; + } + else { + final TIntHashSet residenceClasses = depCluster.getResidence(usage); + for (final int residentName : residenceClasses.toArray()) { + if (constraint.checkResidence(residentName)) { + debug("Added file with satisfied constraint"); + myAffectedFiles.add(theFile); continue filewise; } } } } } + + if (state.annotationQuery.size() > 0) { + final Collection annotationUsages = mySourceFileToAnnotationUsages.get(depFile); + + for (final UsageRepr.Usage usage : annotationUsages) { + for (final UsageRepr.AnnotationUsage query : state.annotationQuery) { + if (query.satisfies(usage)) { + debug("Added file due to annotation query"); + myAffectedFiles.add(theFile); + continue filewise; + } + } + } + } } } } - - if (removed != null) { - for (final String r : removed) { - affectedFiles.remove(new File(r)); - } - } - - debug("End of Differentiate."); - return works.doWork(affectedFiles); } + + boolean differentiate() { + synchronized (myLock) { + + debug("Begin of Differentiate:"); + + processDisappearedClasses(); + + final List newClasses = new ArrayList(); + myDelta.mySourceFileToClasses.forEachEntry(new TIntObjectProcedure>() { + @Override + public boolean execute(int fileName, Collection classes) { + newClasses.add(new FileClasses(fileName, classes)); + return true; + } + }); + + for (final FileClasses compiledFile : newClasses) { + final int fileName = compiledFile.fileName; + final Set classes = compiledFile.fileClasses; + final Set pastClasses = (Set)mySourceFileToClasses.get(fileName); + final DiffState state = new DiffState(Difference.make(pastClasses, classes)); + + if (!processChangedClasses(state)) { + return false; + } + + processRemovedClases(state); + processAddedClasses(state); + + calaulateAffectedFiles(state); + } + + if (myRemoved != null) { + for (final String r : myRemoved) { + myAffectedFiles.remove(new File(r)); + } + } + + debug("End of Differentiate."); + return myDelayedWorks.doWork(myAffectedFiles); + } + } + } + + public boolean differentiate + (final Mappings delta, + final Collection removed, + final Collection filesToCompile, + final Collection compiledFiles, + final Collection affectedFiles, + final DependentFilesFilter filter, + @Nullable final Callbacks.ConstantAffectionResolver constantSearch) { + return new Differential(delta, removed, filesToCompile, compiledFiles, affectedFiles, filter, constantSearch).differentiate(); } private void cleanupBackDependency(final int className, @Nullable Collection clusters, IntIntMultiMaplet buffer) { From 8fe579a90f000805cc7578069b36af166844c1e8 Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Wed, 23 May 2012 18:37:04 +0400 Subject: [PATCH 22/26] fix incorrect return from closure --- .../src/org/jetbrains/jps/android/AndroidFacet.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidFacet.groovy b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidFacet.groovy index d7d04ba87dab..7f9d3dddb6a9 100644 --- a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidFacet.groovy +++ b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidFacet.groovy @@ -92,7 +92,7 @@ class AndroidFacet extends Facet { } if (lookInContentRoot) { - module.contentRoots.each {String contentRoot -> + for (String contentRoot in module.contentRoots) { def absPath = FileUtil.toSystemIndependentName(contentRoot + relativePath) def f = new File(absPath) From f73a14592d950565b4c983ea755439bbfa1307b4 Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Wed, 23 May 2012 18:38:29 +0400 Subject: [PATCH 23/26] fix: stop if error occurs --- .../jetbrains/jps/android/AndroidSourceGeneratingBuilder.java | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidSourceGeneratingBuilder.java b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidSourceGeneratingBuilder.java index c982ef16a5ae..265845566c92 100644 --- a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidSourceGeneratingBuilder.java +++ b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidSourceGeneratingBuilder.java @@ -584,6 +584,7 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder { if (!FileUtil.moveDirWithContent(tmpOutputDir, aptOutputDirectory)) { context.processMessage(new CompilerMessage(ANDROID_APT_COMPILER, BuildMessage.Kind.ERROR, AndroidJpsBundle .message("android.jps.errors.cannot.move.content", tmpOutputDir.getPath(), aptOutputDirectory.getPath()))); + success = false; continue; } markDirtyRecursively(aptOutputDirectory, context, ANDROID_APT_COMPILER); From c19d25e40d468e36aa0775b433cba58371348401 Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Wed, 23 May 2012 19:08:10 +0400 Subject: [PATCH 24/26] android+maven: fix paths of apklib module --- .../android/facet/AndroidFacetConfiguration.java | 10 ++-------- .../org/jetbrains/android/facet/AndroidRootUtil.java | 12 ++++++++++++ .../android/maven/AndroidFacetImporterBase.java | 10 +++++++--- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/plugins/android/src/org/jetbrains/android/facet/AndroidFacetConfiguration.java b/plugins/android/src/org/jetbrains/android/facet/AndroidFacetConfiguration.java index 350ba76c09e4..254d6b90beb2 100644 --- a/plugins/android/src/org/jetbrains/android/facet/AndroidFacetConfiguration.java +++ b/plugins/android/src/org/jetbrains/android/facet/AndroidFacetConfiguration.java @@ -27,7 +27,6 @@ import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.util.DefaultJDOMExternalizer; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.WriteExternalException; -import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import org.jdom.Element; @@ -97,15 +96,10 @@ public class AndroidFacetConfiguration implements FacetConfiguration { } public void init(@NotNull Module module, @NotNull String baseDirectoryPath) { - String moduleDirPath = AndroidRootUtil.getModuleDirPath(module); - if (moduleDirPath == null) { + final String s = AndroidRootUtil.getPathRelativeToModuleDir(module, baseDirectoryPath); + if (s == null || s.length() == 0) { return; } - if (moduleDirPath.equals(baseDirectoryPath)) { - return; - } - - String s = FileUtil.getRelativePath(moduleDirPath, baseDirectoryPath, '/'); GEN_FOLDER_RELATIVE_PATH_APT = '/' + s + GEN_FOLDER_RELATIVE_PATH_APT; GEN_FOLDER_RELATIVE_PATH_AIDL = '/' + s + GEN_FOLDER_RELATIVE_PATH_AIDL; diff --git a/plugins/android/src/org/jetbrains/android/facet/AndroidRootUtil.java b/plugins/android/src/org/jetbrains/android/facet/AndroidRootUtil.java index c24eed85f9a6..6eeb95037884 100644 --- a/plugins/android/src/org/jetbrains/android/facet/AndroidRootUtil.java +++ b/plugins/android/src/org/jetbrains/android/facet/AndroidRootUtil.java @@ -514,4 +514,16 @@ public class AndroidRootUtil { String moduleDirPath = getModuleDirPath(facet.getModule()); return moduleDirPath != null ? FileUtil.toSystemDependentName(moduleDirPath + path) : null; } + + @Nullable + public static String getPathRelativeToModuleDir(@NotNull Module module, @NotNull String path) { + String moduleDirPath = getModuleDirPath(module); + if (moduleDirPath == null) { + return null; + } + if (moduleDirPath.equals(path)) { + return ""; + } + return FileUtil.getRelativePath(moduleDirPath, path, '/'); + } } diff --git a/plugins/android/src/org/jetbrains/android/maven/AndroidFacetImporterBase.java b/plugins/android/src/org/jetbrains/android/maven/AndroidFacetImporterBase.java index 2eaa9968d45e..7aa809d188e1 100644 --- a/plugins/android/src/org/jetbrains/android/maven/AndroidFacetImporterBase.java +++ b/plugins/android/src/org/jetbrains/android/maven/AndroidFacetImporterBase.java @@ -389,9 +389,13 @@ public abstract class AndroidFacetImporterBase extends FacetImporter 0 ? '/' + s + '/' : "/"; + configuration.RES_FOLDER_RELATIVE_PATH = s + AndroidMavenUtil.APK_LIB_ARTIFACT_RES_DIR; + configuration.LIBS_FOLDER_RELATIVE_PATH = s + AndroidMavenUtil.APK_LIB_ARTIFACT_NATIVE_LIBS_DIR; + configuration.MANIFEST_FILE_RELATIVE_PATH = s + AndroidMavenUtil.APK_LIB_ARTIFACT_MANIFEST_FILE; + } importSdkAndDependenciesForApklibArtifact(project, rootModelAdapter, apklibModuleModel, modelsProvider, mavenTree, artifact, mavenProject2ModuleName); From a49feab3e2ef37fbac5dc03fa862cddfbe9e138f Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Wed, 23 May 2012 19:31:14 +0400 Subject: [PATCH 25/26] Logging; cleanup --- .../integration/FileListeningTest.java | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/platform/platform-tests/testSrc/com/intellij/history/integration/FileListeningTest.java b/platform/platform-tests/testSrc/com/intellij/history/integration/FileListeningTest.java index f5cbf86aac8a..2d65ec17ac8f 100644 --- a/platform/platform-tests/testSrc/com/intellij/history/integration/FileListeningTest.java +++ b/platform/platform-tests/testSrc/com/intellij/history/integration/FileListeningTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2012 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. @@ -72,7 +72,7 @@ public class FileListeningTest extends IntegrationTestCase { LocalFileSystem.getInstance().refresh(false); List changes = getVcs().getChangeListInTests().getChangesInTests().get(0).getChanges(); - assertEquals(4, changes.size()); + assertEquals(changes.toString(), 4, changes.size()); List actual = new SmartList(); for (Change each : changes) { actual.add(((StructuralChange)each).getPath()); @@ -100,7 +100,7 @@ public class FileListeningTest extends IntegrationTestCase { VirtualFile f = createFile("file.txt"); assertEquals(2, getRevisionsFor(f).size()); - f.rename(null, "file2.txt"); + f.rename(this, "file2.txt"); assertEquals(3, getRevisionsFor(f).size()); } @@ -125,7 +125,7 @@ public class FileListeningTest extends IntegrationTestCase { addFileListenerDuring(l, new RunnableAdapter() { @Override public void doRun() throws IOException { - f.rename(null, "new.txt"); + f.rename(this, "new.txt"); } }); @@ -139,7 +139,7 @@ public class FileListeningTest extends IntegrationTestCase { VirtualFile f = createFile("file.hprof"); assertEquals(before, getRevisionsFor(myRoot).size()); - f.rename(null, "file.txt"); + f.rename(this, "file.txt"); assertEquals(before + 1, getRevisionsFor(myRoot).size()); assertEquals(2, getRevisionsFor(f).size()); @@ -151,7 +151,7 @@ public class FileListeningTest extends IntegrationTestCase { VirtualFile f = createFile("file.txt"); assertEquals(before + 1, getRevisionsFor(myRoot).size()); - f.rename(null, "file.hprof"); + f.rename(this, "file.hprof"); assertEquals(before + 2, getRevisionsFor(myRoot).size()); } @@ -161,7 +161,7 @@ public class FileListeningTest extends IntegrationTestCase { VirtualFile f = createFile(FILTERED_DIR_NAME); assertEquals(before, getRevisionsFor(myRoot).size()); - f.rename(null, "not_filtered"); + f.rename(this, "not_filtered"); assertEquals(before + 1, getRevisionsFor(myRoot).size()); assertEquals(2, getRevisionsFor(f).size()); @@ -173,7 +173,7 @@ public class FileListeningTest extends IntegrationTestCase { VirtualFile f = createDirectory("not_filtered"); assertEquals(before + 1, getRevisionsFor(myRoot).size()); - f.rename(null, FILTERED_DIR_NAME); + f.rename(this, FILTERED_DIR_NAME); assertEquals(before + 2, getRevisionsFor(myRoot).size()); } @@ -188,7 +188,7 @@ public class FileListeningTest extends IntegrationTestCase { assertEquals(4, getRevisionsFor(f).size()); } - public void testIgnoringROStstusChangeForUnversionedFiles() throws Exception { + public void testIgnoringROStatusChangeForUnversionedFiles() throws Exception { int before = getRevisionsFor(myRoot).size(); VirtualFile f = createFile("f.hprof"); @@ -202,7 +202,7 @@ public class FileListeningTest extends IntegrationTestCase { int before = getRevisionsFor(myRoot).size(); - f.delete(null); + f.delete(this); assertEquals(before + 1, getRevisionsFor(myRoot).size()); } @@ -210,24 +210,26 @@ public class FileListeningTest extends IntegrationTestCase { int before = getRevisionsFor(myRoot).size(); VirtualFile f = createDirectory(FILTERED_DIR_NAME); - f.delete(null); + f.delete(this); assertEquals(before, getRevisionsFor(myRoot).size()); } public void testDeletionDoesNotVersionIgnoredFilesRecursively() throws Exception { String dir1 = createDirectoryExternally("dir"); - String f1 = createFileExternally("dir/f.txt"); + createFileExternally("dir/f.txt"); createFileExternally("dir/f.class"); createFileExternally("dir/subdir/f.txt"); - String dir2 = createDirectoryExternally("dir/subdir/subdir2"); - String f2 = createFileExternally("dir/subdir/subdir2/f.txt"); + createDirectoryExternally("dir/subdir/subdir2"); + createFileExternally("dir/subdir/subdir2/f.txt"); LocalFileSystem.getInstance().refresh(false); addExcludedDir(myRoot.getPath() + "/dir/subdir"); addContentRoot(myRoot.getPath() + "/dir/subdir/subdir2"); - LocalFileSystem.getInstance().findFileByPath(dir1).delete(this); + final VirtualFile vDir1 = LocalFileSystem.getInstance().findFileByPath(dir1); + assertNotNull(dir1, vDir1); + vDir1.delete(this); List changes = getVcs().getChangeListInTests().getChangesInTests().get(0).getChanges(); assertEquals(1, changes.size()); From c1df50bd7792b3b54988b37d83831753bda3a88c Mon Sep 17 00:00:00 2001 From: Alexander Lobas Date: Wed, 23 May 2012 19:36:27 +0400 Subject: [PATCH 26/26] Show Javadoc for properties --- .../android/designer/model/RadFragment.java | 19 ++-- .../model/layout/relative/CenterProperty.java | 5 +- .../layout/relative/RadRelativeLayout.java | 8 +- .../propertyTable/AttributeProperty.java | 9 +- .../propertyTable/CustomViewProperty.java | 3 +- .../designer/propertyTable/FlagProperty.java | 9 +- .../propertyTable/IncludeLayoutProperty.java | 3 +- .../designer/propertyTable/JavadocParser.java | 86 +++++++++++++++++++ .../propertyTable/actions/ShowJavadoc.java | 13 ++- .../src/messages/DesignerBundle.properties | 2 +- 10 files changed, 140 insertions(+), 17 deletions(-) create mode 100644 plugins/android-designer/src/com/intellij/android/designer/propertyTable/JavadocParser.java diff --git a/plugins/android-designer/src/com/intellij/android/designer/model/RadFragment.java b/plugins/android-designer/src/com/intellij/android/designer/model/RadFragment.java index 953db64cab8b..7a3ff434924f 100644 --- a/plugins/android-designer/src/com/intellij/android/designer/model/RadFragment.java +++ b/plugins/android-designer/src/com/intellij/android/designer/model/RadFragment.java @@ -17,6 +17,7 @@ package com.intellij.android.designer.model; import com.intellij.android.designer.propertyTable.FragmentProperty; import com.intellij.android.designer.propertyTable.IdProperty; +import com.intellij.android.designer.propertyTable.JavadocParser; import com.intellij.android.designer.propertyTable.editors.ChooseClassDialog; import com.intellij.android.designer.propertyTable.editors.ResourceEditor; import com.intellij.designer.model.RadComponent; @@ -45,15 +46,17 @@ public class RadFragment extends RadViewComponent implements IConfigurableCompon setValue(fragment); } } - }, "Supply the name of the fragment class to instantiate."); + }, JavadocParser.build("name", "Supply the name of the fragment class to instantiate.")); private static final Property TAG_PROPERTY = new FragmentProperty("tag", new TextEditor(), - "Use device-admin as the root tag of the XML resource that\n" + - "describes a\n" + - " {@link android.app.admin.DeviceAdminReceiver}, which is\n" + - " referenced from its\n" + - " {@link android.app.admin.DeviceAdminReceiver#DEVICE_ADMIN_META_DATA}\n" + - " meta-data entry. Described here are the attributes that can be\n" + - " included in that tag."); + JavadocParser.build( + "tag", + "Use device-admin as the root tag of the XML resource that\n" + + "describes a\n" + + " {@link android.app.admin.DeviceAdminReceiver}, which is\n" + + " referenced from its\n" + + " {@link android.app.admin.DeviceAdminReceiver#DEVICE_ADMIN_META_DATA}\n" + + " meta-data entry. Described here are the attributes that can be\n" + + " included in that tag.")); private static final String NAME_KEY = "fragment.name"; @Override diff --git a/plugins/android-designer/src/com/intellij/android/designer/model/layout/relative/CenterProperty.java b/plugins/android-designer/src/com/intellij/android/designer/model/layout/relative/CenterProperty.java index a087b5511066..41c8ab813f2e 100644 --- a/plugins/android-designer/src/com/intellij/android/designer/model/layout/relative/CenterProperty.java +++ b/plugins/android-designer/src/com/intellij/android/designer/model/layout/relative/CenterProperty.java @@ -25,6 +25,7 @@ import com.intellij.designer.propertyTable.renderers.LabelPropertyRenderer; import com.intellij.openapi.application.ApplicationManager; import com.intellij.psi.xml.XmlTag; import com.intellij.util.ArrayUtil; +import com.intellij.android.designer.propertyTable.JavadocParser; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -35,6 +36,8 @@ public class CenterProperty extends Property { private static final String[] COMBO_ITEMS = {"horizontal", "vertical", "both"}; private static final String[] ATTR_ITEMS = {"android:layout_centerHorizontal", "android:layout_centerVertical", "android:layout_centerInParent"}; + private static final String JAVA_DOC = + JavadocParser.build("layout:centerInParent", "Set centers this child horizontally or vertically within its parent."); private final PropertyRenderer myRenderer = new LabelPropertyRenderer(null); private final PropertyEditor myEditor = new StringsComboEditor(COMBO_ITEMS); @@ -116,6 +119,6 @@ public class CenterProperty extends Property { @Override public String getJavadocText() { - return "Set centers this child horizontally or vertically within its parent."; + return JAVA_DOC; } } \ No newline at end of file diff --git a/plugins/android-designer/src/com/intellij/android/designer/model/layout/relative/RadRelativeLayout.java b/plugins/android-designer/src/com/intellij/android/designer/model/layout/relative/RadRelativeLayout.java index 555ee8f16ad4..7130485d77f7 100644 --- a/plugins/android-designer/src/com/intellij/android/designer/model/layout/relative/RadRelativeLayout.java +++ b/plugins/android-designer/src/com/intellij/android/designer/model/layout/relative/RadRelativeLayout.java @@ -22,6 +22,7 @@ import com.intellij.android.designer.designSurface.layout.relative.RelativeDecor import com.intellij.android.designer.model.PropertyParser; import com.intellij.android.designer.model.RadViewLayoutWithData; import com.intellij.android.designer.model.layout.actions.AllGravityAction; +import com.intellij.android.designer.propertyTable.JavadocParser; import com.intellij.designer.componentTree.TreeEditOperation; import com.intellij.designer.designSurface.*; import com.intellij.designer.designSurface.selection.ResizeSelectionDecorator; @@ -54,7 +55,9 @@ public class RadRelativeLayout extends RadViewLayoutWithData implements ILayoutD @Override public void configureProperties(List properties) { CompoundProperty alignComponent = - new CompoundProperty("layout:alignComponent", "Positions the edges of this view to the edges of the another view."); + new CompoundProperty("layout:alignComponent", + JavadocParser + .build("layout:alignComponent", "Positions the edges of this view to the edges of the another view.")); PropertyParser.moveProperties(properties, alignComponent, "layout:alignTop", "top:top", "layout:below", "top:bottom", @@ -68,7 +71,8 @@ public class RadRelativeLayout extends RadViewLayoutWithData implements ILayoutD properties.add(alignComponent); CompoundProperty alignParent = - new CompoundProperty("layout:alignParent", "Positions the edges of this view to the edges of the parent."); + new CompoundProperty("layout:alignParent", + JavadocParser.build("layout:alignParent", "Positions the edges of this view to the edges of the parent.")); PropertyParser.moveProperties(properties, alignParent, "layout:alignParentTop", "top", "layout:alignParentLeft", "left", diff --git a/plugins/android-designer/src/com/intellij/android/designer/propertyTable/AttributeProperty.java b/plugins/android-designer/src/com/intellij/android/designer/propertyTable/AttributeProperty.java index f768db303a3c..46bdbda27916 100644 --- a/plugins/android-designer/src/com/intellij/android/designer/propertyTable/AttributeProperty.java +++ b/plugins/android-designer/src/com/intellij/android/designer/propertyTable/AttributeProperty.java @@ -41,6 +41,7 @@ public class AttributeProperty extends Property { protected final AttributeDefinition myDefinition; private final PropertyRenderer myRenderer; private final PropertyEditor myEditor; + private String myJavadocText; public AttributeProperty(@NotNull String name, @NotNull AttributeDefinition definition) { this(null, name, definition); @@ -137,6 +138,12 @@ public class AttributeProperty extends Property { @Override public String getJavadocText() { - return myDefinition.getDocValue(); + if (myJavadocText == null) { + String javadocText = myDefinition.getDocValue(); + if (javadocText != null) { + myJavadocText = JavadocParser.build(getName(), javadocText); + } + } + return myJavadocText; } } \ No newline at end of file diff --git a/plugins/android-designer/src/com/intellij/android/designer/propertyTable/CustomViewProperty.java b/plugins/android-designer/src/com/intellij/android/designer/propertyTable/CustomViewProperty.java index 8d02639ad0b0..17e4d2c2f96a 100644 --- a/plugins/android-designer/src/com/intellij/android/designer/propertyTable/CustomViewProperty.java +++ b/plugins/android-designer/src/com/intellij/android/designer/propertyTable/CustomViewProperty.java @@ -37,6 +37,7 @@ import java.util.List; * @author Alexander Lobas */ public class CustomViewProperty extends Property { + private static final String JAVA_DOC = JavadocParser.build("view:class", "The fully qualified name of the class."); private final PropertyRenderer myRenderer = new LabelPropertyRenderer(null); private final PropertyEditor myEditor = new ResourceEditor(null, Collections.emptySet(), null) { @Override @@ -122,6 +123,6 @@ public class CustomViewProperty extends Property { @Override public String getJavadocText() { - return "The fully qualified name of the class."; + return JAVA_DOC; } } \ No newline at end of file diff --git a/plugins/android-designer/src/com/intellij/android/designer/propertyTable/FlagProperty.java b/plugins/android-designer/src/com/intellij/android/designer/propertyTable/FlagProperty.java index 200478958a73..461cfea30707 100644 --- a/plugins/android-designer/src/com/intellij/android/designer/propertyTable/FlagProperty.java +++ b/plugins/android-designer/src/com/intellij/android/designer/propertyTable/FlagProperty.java @@ -40,6 +40,7 @@ public class FlagProperty extends Property implements IPropert private final PropertyRenderer myRenderer = new LabelPropertyRenderer(null); protected final AttributeDefinition myDefinition; protected final List> myOptions = new ArrayList>(); + private String myJavadocText; public FlagProperty(@NotNull String name, @NotNull AttributeDefinition definition) { super(null, name); @@ -163,7 +164,13 @@ public class FlagProperty extends Property implements IPropert @Override public String getJavadocText() { - return myDefinition.getDocValue(); + if (myJavadocText == null) { + String javadocText = myDefinition.getDocValue(); + if (javadocText != null) { + myJavadocText = JavadocParser.build(getName(), javadocText); + } + } + return myJavadocText; } ////////////////////////////////////////////////////////////////////////////////////////// diff --git a/plugins/android-designer/src/com/intellij/android/designer/propertyTable/IncludeLayoutProperty.java b/plugins/android-designer/src/com/intellij/android/designer/propertyTable/IncludeLayoutProperty.java index 1835911adbfb..7d3a9f789577 100644 --- a/plugins/android-designer/src/com/intellij/android/designer/propertyTable/IncludeLayoutProperty.java +++ b/plugins/android-designer/src/com/intellij/android/designer/propertyTable/IncludeLayoutProperty.java @@ -41,6 +41,7 @@ public class IncludeLayoutProperty extends Property { public static ResourceType[] TYPES = new ResourceType[]{ResourceType.LAYOUT}; private static final Set FORMATS = EnumSet.of(AttributeFormat.Reference); public static final Property INSTANCE = new IncludeLayoutProperty(); + private static final String JAVA_DOC = JavadocParser.build(NAME, "Reference to the layout file you wish to include."); private final PropertyRenderer myRenderer = new ResourceRenderer(FORMATS); private final PropertyEditor myEditor = new ResourceEditor(TYPES, FORMATS, null); @@ -91,6 +92,6 @@ public class IncludeLayoutProperty extends Property { @Override public String getJavadocText() { - return "Reference to the layout file you wish to include."; + return JAVA_DOC; } } \ No newline at end of file diff --git a/plugins/android-designer/src/com/intellij/android/designer/propertyTable/JavadocParser.java b/plugins/android-designer/src/com/intellij/android/designer/propertyTable/JavadocParser.java new file mode 100644 index 000000000000..422675316b7e --- /dev/null +++ b/plugins/android-designer/src/com/intellij/android/designer/propertyTable/JavadocParser.java @@ -0,0 +1,86 @@ +/* + * Copyright 2000-2012 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.android.designer.propertyTable; + +import com.intellij.codeInsight.documentation.DocumentationManager; +import org.jetbrains.annotations.NotNull; + +/** + * @author Alexander Lobas + */ +public class JavadocParser { + private static final String[] TAGS = {"{see ", "{@see ", "{@link "}; + + @NotNull + public static String build(@NotNull String title, @NotNull String javadoc) { + StringBuilder buffer = new StringBuilder(); + + buffer.append(""); + buffer.append("

").append(title).append(" - "); + + for (String tag : TAGS) { + javadoc = convertLink(javadoc, tag); + } + + javadoc = javadoc.replaceAll("", ""); + javadoc = javadoc.replaceAll("", ""); + + buffer.append(javadoc); + + return buffer.append("").toString(); + } + + private static String convertLink(String javadoc, String tag) { + StringBuilder buffer = new StringBuilder(); + int length = javadoc.length(); + int start = 0; + + while (true) { + int index = javadoc.indexOf(tag, start); + + if (index == -1) { + buffer.append(javadoc.substring(start, length)); + break; + } + else { + buffer.append(javadoc.substring(start, index)); + + int linkStart = index + tag.length(); + int end = javadoc.indexOf('}', linkStart); + String linkValue = javadoc.substring(linkStart, end).trim(); + String href; + String text; + + int spaceIndex = linkValue.indexOf(' '); + if (spaceIndex != -1) { + href = linkValue.substring(0, spaceIndex); + text = linkValue.substring(spaceIndex + 1); + } + else { + href = text = linkValue; + } + + text = text.replace('#', '.'); + + DocumentationManager.createHyperlink(buffer, href, text, true); + + start = end + 1; + } + } + + return buffer.toString(); + } +} \ No newline at end of file diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/actions/ShowJavadoc.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/actions/ShowJavadoc.java index c11a585ba6dc..2f2b7da84e3a 100644 --- a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/actions/ShowJavadoc.java +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/actions/ShowJavadoc.java @@ -28,11 +28,13 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.util.ActionCallback; +import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.IconLoader; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.ui.awt.RelativePoint; +import com.intellij.ui.popup.NotLookupOrSearchCondition; import java.awt.*; @@ -85,13 +87,22 @@ public class ShowJavadoc extends AnAction implements IPropertyTableAction { callback.doWhenProcessed(new Runnable() { public void run() { - final JBPopup hint = + JBPopup hint = JBPopupFactory.getInstance().createComponentPopupBuilder(component, component) + .setRequestFocusCondition(project, NotLookupOrSearchCondition.INSTANCE) + .setProject(project) .setDimensionServiceKey(project, DocumentationManager.JAVADOC_LOCATION_AND_SIZE, false) .setResizable(true) .setMovable(true) .setRequestFocus(true) .setTitle(DesignerBundle.message("designer.properties.javadoc.title", property.getName())) + .setCancelCallback(new Computable() { + @Override + public Boolean compute() { + Disposer.dispose(component); + return Boolean.TRUE; + } + }) .createPopup(); component.setHint(hint); Disposer.register(hint, component); diff --git a/plugins/ui-designer/ui-designer-new/src/messages/DesignerBundle.properties b/plugins/ui-designer/ui-designer-new/src/messages/DesignerBundle.properties index f105d74fd4cf..aeb0e5ad1812 100644 --- a/plugins/ui-designer/ui-designer-new/src/messages/DesignerBundle.properties +++ b/plugins/ui-designer/ui-designer-new/src/messages/DesignerBundle.properties @@ -8,7 +8,7 @@ command.set.property.value=Set Property Value designer.properties.title=Properties designer.properties.show.expert=Show expert properties designer.properties.show.javadoc=Show Documentation -designer.properties.javadoc.title=Documentation for {0} property +designer.properties.javadoc.title=Documentation for {0} property designer.properties.restore_default=Restore default value designer.properties.column1=Property designer.properties.column2=Value