From a1237c6ba5c8d8b04e0b25910269063292678216 Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Fri, 12 Mar 2010 14:41:42 +0300 Subject: [PATCH 01/93] diagnostics --- .../com/intellij/openapi/vfs/impl/VirtualFilePointerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerImpl.java b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerImpl.java index 697ce7a7cc9b..85a52d461d70 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerImpl.java @@ -152,7 +152,7 @@ public class VirtualFilePointerImpl extends UserDataHolderBase implements Virtua myLastUpdated = fsModCount; if (myFile == null) { - LOG.assertTrue(myUrl != null); + LOG.assertTrue(myUrl != null, "Both file & url are null"); myFile = myVirtualFileManager.findFileByUrl(myUrl); if (myFile != null) { myUrl = null; From 3c74e463529b358b47f5a416744898eb5d3c5007 Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Fri, 12 Mar 2010 14:50:05 +0300 Subject: [PATCH 02/93] add confirmation dialog in File Saver --- .../fileChooser/ex/FileSaverDialogImpl.java | 17 ++++++++++++++++- .../src/messages/UIBundle.properties | 2 ++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/fileChooser/ex/FileSaverDialogImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileChooser/ex/FileSaverDialogImpl.java index 7bd17500ab7e..c6a3ba190648 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileChooser/ex/FileSaverDialogImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileChooser/ex/FileSaverDialogImpl.java @@ -15,10 +15,11 @@ */ package com.intellij.openapi.fileChooser.ex; -import com.intellij.openapi.fileChooser.FileSystemTree; import com.intellij.openapi.fileChooser.FileSaverDescriptor; import com.intellij.openapi.fileChooser.FileSaverDialog; +import com.intellij.openapi.fileChooser.FileSystemTree; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.Messages; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileWrapper; import com.intellij.ui.DocumentAdapter; @@ -160,4 +161,18 @@ public class FileSaverDialogImpl extends FileChooserDialogImpl implements FileSa //double check. FileChooserFactoryImpl sets enable ok button super.setOKActionEnabled(isFileNameExist()); } + + @Override + protected void doOKAction() { + final File file = getFile(); + if (file != null && file.exists()) { + if (OK_EXIT_CODE != Messages.showYesNoDialog(this.getRootPane(), + UIBundle.message("file.chooser.save.dialog.confirmation", file.getName()), + UIBundle.message("file.chooser.save.dialog.confirmation.title"), + Messages.getWarningIcon())) { + return; + } + } + super.doOKAction(); + } } diff --git a/platform/platform-resources-en/src/messages/UIBundle.properties b/platform/platform-resources-en/src/messages/UIBundle.properties index 6fcceb24c518..8c4ebe8683aa 100644 --- a/platform/platform-resources-en/src/messages/UIBundle.properties +++ b/platform/platform-resources-en/src/messages/UIBundle.properties @@ -98,6 +98,8 @@ welcome.script.text.not.specified.message=Not specified file.chooser.default.title=Select Path file.chooser.save.dialog.default.title=Select File to Save +file.chooser.save.dialog.confirmation={0} is already exists.\nDo you want to replace it? +file.chooser.save.dialog.confirmation.title=Confirm Save as file.chooser.select.object.title=Select {0} delete.dialog.title=Delete file.chooser.could.not.erase.file.or.folder.error.messabe=Could not erase file or folder: {0} From 10dc44803dbe811319c262e5dd98ba8c549655cb Mon Sep 17 00:00:00 2001 From: irengrig Date: Fri, 12 Mar 2010 15:21:36 +0300 Subject: [PATCH 03/93] DEA-49056 (RE at com.intellij.openapi.editor.impl.EditorFactoryImpl.a) --- .../intellij/openapi/vcs/impl/ProjectLevelVcsManagerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/ProjectLevelVcsManagerImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/ProjectLevelVcsManagerImpl.java index 0d6ddf1fc974..3eb7739eda6d 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/ProjectLevelVcsManagerImpl.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/ProjectLevelVcsManagerImpl.java @@ -332,7 +332,7 @@ public void addMessageToConsoleWindow(final String message, final TextAttributes Content content = contentManager.findContent(displayName); if (content == null) { final EditorFactory editorFactory = EditorFactory.getInstance(); - final Editor editor = editorFactory.createViewer(editorFactory.createDocument("")); + final Editor editor = editorFactory.createViewer(editorFactory.createDocument(""), myProject); EditorSettings editorSettings = editor.getSettings(); editorSettings.setLineMarkerAreaShown(false); editorSettings.setIndentGuidesShown(false); From 4156030308a34c14dd8805e2670421f8d4f69002 Mon Sep 17 00:00:00 2001 From: Anton Makeev Date: Fri, 12 Mar 2010 15:45:19 +0300 Subject: [PATCH 04/93] Maven: suspend/resume conter bugfix --- .../idea/maven/utils/MavenMergingUpdateQueue.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenMergingUpdateQueue.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenMergingUpdateQueue.java index 5bef4d9c4bed..e553c7c2abfb 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenMergingUpdateQueue.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenMergingUpdateQueue.java @@ -86,16 +86,18 @@ public class MavenMergingUpdateQueue extends MergingUpdateQueue { } }, MavenMergingUpdateQueue.this); - if (CommandProcessor.getInstance().getCurrentCommand() != null) { - suspend(); - } - ProjectRootManager.getInstance(project).addModuleRootListener(new ModuleRootListener() { + boolean beforeCalled = false; + public void beforeRootsChange(ModuleRootEvent event) { suspend(); + beforeCalled = true; } public void rootsChanged(ModuleRootEvent event) { + if (!beforeCalled) return; + beforeCalled = false; + resume(); MavenMergingUpdateQueue.this.restartTimer(); } @@ -109,7 +111,6 @@ public class MavenMergingUpdateQueue extends MergingUpdateQueue { protected void run(Result result) throws Throwable { MessageBusConnection connection = project.getMessageBus().connect(MavenMergingUpdateQueue.this); connection.subscribe(DumbService.DUMB_MODE, new DumbService.DumbModeListener() { - public void enteredDumbMode() { suspend(); } @@ -118,6 +119,7 @@ public class MavenMergingUpdateQueue extends MergingUpdateQueue { resume(); } }); + if (DumbService.getInstance(project).isDumb()) { suspend(); } From 3949c74ed7f96cc6996ceff7690a0c838b087d4e Mon Sep 17 00:00:00 2001 From: Anton Makeev Date: Fri, 12 Mar 2010 15:46:24 +0300 Subject: [PATCH 05/93] Maven: action names --- ...=> GenerateAddManagedDependencyAction.java} | 6 +++--- .../src/main/resources/DomBundle.properties | 18 +++++++++--------- .../src/main/resources/META-INF/plugin.xml | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) rename plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/generate/{GenerateOverridingDependencyAction.java => GenerateAddManagedDependencyAction.java} (95%) diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/generate/GenerateOverridingDependencyAction.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/generate/GenerateAddManagedDependencyAction.java similarity index 95% rename from plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/generate/GenerateOverridingDependencyAction.java rename to plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/generate/GenerateAddManagedDependencyAction.java index 5e9ebb68ed12..cb316659e318 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/generate/GenerateOverridingDependencyAction.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/generate/GenerateAddManagedDependencyAction.java @@ -33,8 +33,8 @@ import org.jetbrains.idea.maven.utils.MavenIcons; import java.util.List; import java.util.Set; -public class GenerateOverridingDependencyAction extends GenerateDomElementAction { - public GenerateOverridingDependencyAction() { +public class GenerateAddManagedDependencyAction extends GenerateDomElementAction { + public GenerateAddManagedDependencyAction() { super(new MavenOverridingDependencyGenerateProvider(), MavenIcons.DEPENDENCY_ICON); } @@ -45,7 +45,7 @@ public class GenerateOverridingDependencyAction extends GenerateDomElementAction private static class MavenOverridingDependencyGenerateProvider extends MavenGenerateProvider { public MavenOverridingDependencyGenerateProvider() { - super(MavenDomBundle.message("override.dependency"), MavenDomDependency.class); + super(MavenDomBundle.message("generate.managed.dependency"), MavenDomDependency.class); } @Override diff --git a/plugins/maven/src/main/resources/DomBundle.properties b/plugins/maven/src/main/resources/DomBundle.properties index c2375fb9e889..3c266a92241c 100644 --- a/plugins/maven/src/main/resources/DomBundle.properties +++ b/plugins/maven/src/main/resources/DomBundle.properties @@ -3,7 +3,7 @@ inspection.name=Maven Model Inspection fix.create.module=Create Module fix.create.module.with.parent=Create Module with Parent fix.update.indices=Update Maven Indices -fix.parent.path=Fix relative path +fix.parent.path=Fix Relative Path fix.open.file=Open ''{0}'' intention.choose.file=Choose File @@ -17,15 +17,15 @@ generate.dependency.group=Dependency (group) generate.plugin.artifact=Plugin (artifact) generate.plugin.group=Plugin (group) -overriden.dependency.title=Navigate to managing dependency -overriding.dependency.title=Navigate to dependency usages -navigate.parent.dependency.title=Choose dependency to navigate +overriden.dependency.title=Navigate to Managing Dependency +overriding.dependency.title=Navigate to Dependency Usages +navigate.parent.dependency.title=Choose Dependency to Navigate to -parent.pom.title=Navigate to parent -children.poms.title=Navigate to children projects -navigate.children.poms.title=Choose project -dependencies.chooser.title=Choose dependency +parent.pom.title=Navigate to Parent +children.poms.title=Navigate to Children Projects +navigate.children.poms.title=Choose Project +dependencies.chooser.title=Choose Dependency all.dependencies=All Dependencies chooser.show.project.files=Project Files -override.dependency=Override Parent Dependency generate.dependency=Dependency +generate.managed.dependency=Add Managed Dependency diff --git a/plugins/maven/src/main/resources/META-INF/plugin.xml b/plugins/maven/src/main/resources/META-INF/plugin.xml index 522d5b8e485c..a65c7e76773d 100644 --- a/plugins/maven/src/main/resources/META-INF/plugin.xml +++ b/plugins/maven/src/main/resources/META-INF/plugin.xml @@ -288,7 +288,7 @@ + description="Assign shortcut to selected phase/goal"> @@ -355,7 +355,7 @@ - + From c95686bc7552bbd479c91438e21f0577cf78908e Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Fri, 12 Mar 2010 14:25:36 +0300 Subject: [PATCH 06/93] move FileSetTestCase to platform testFramework --- .../src/com/intellij/FileSetTestCase.java | 147 ++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 platform/testFramework/src/com/intellij/FileSetTestCase.java diff --git a/platform/testFramework/src/com/intellij/FileSetTestCase.java b/platform/testFramework/src/com/intellij/FileSetTestCase.java new file mode 100644 index 000000000000..86c846ea252e --- /dev/null +++ b/platform/testFramework/src/com/intellij/FileSetTestCase.java @@ -0,0 +1,147 @@ +/* + * Created by IntelliJ IDEA. + * User: user + * Date: Sep 22, 2002 + * Time: 2:45:20 PM + * To change template for new class use + * Code Style | Class Templates options (Tools | IDE Options). + */ +package com.intellij; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.testFramework.LightPlatformTestCase; +import com.intellij.util.ArrayUtil; +import junit.framework.TestSuite; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +public abstract class FileSetTestCase extends TestSuite { + private final File[] myFiles; + protected Project myProject; + + public FileSetTestCase(String path) { + File f = new File(path); + if (f.isDirectory()) { + myFiles = f.listFiles(); + } + else if (f.exists()) { + myFiles = new File[] {f}; + } + else { + throw new IllegalArgumentException("invalid path: " + path); + } + + + addAllTests(); + } + + protected void setUp() { + + } + + protected void tearDown() { + + } + + private void addAllTests() { + for (File file : myFiles) { + if (file.isFile()) { + addFileTest(file); + } + } + } + + public abstract String transform(String testName, String[] data) throws Exception; + + protected FileSetTestCase(File[] files) { + myFiles = files; + addAllTests(); + } + + public String getName() { + return getClass().getName(); + } + + private void addFileTest(File file) { + if (!StringUtil.startsWithChar(file.getName(), '_') && !"CVS".equals(file.getName())) { + final ActualTest t = new ActualTest(file); + addTest(t); + } + } + + private class ActualTest extends LightPlatformTestCase { + private final File myTestFile; + + public ActualTest(File testFile) { + myTestFile = testFile; + } + + protected void setUp() throws Exception { + super.setUp(); + FileSetTestCase.this.setUp(); + } + + protected void tearDown() throws Exception { + FileSetTestCase.this.tearDown(); + super.tearDown(); + } + + public int countTestCases() { + return 1; + } + + protected void runTest() throws Throwable { + String content = new String(FileUtil.loadFileText(myTestFile)); + assertNotNull(content); + + List input = new ArrayList(); + + int separatorIndex; + + content = StringUtil.replace(content, "\r", ""); + + while ((separatorIndex = content.indexOf("---")) >= 0) { + input.add(content.substring(0, separatorIndex)); + content = content.substring(separatorIndex); + while (StringUtil.startsWithChar(content, '-') || StringUtil.startsWithChar(content, '\n')) content = content.substring(1); + } + + String result = content; + + assertTrue("No data found in source file", input.size() > 0); + + while (StringUtil.startsWithChar(result, '-') || StringUtil.startsWithChar(result, '\n') || StringUtil.startsWithChar(result, '\r')) { + result = result.substring(1); + } + final String transformed; + FileSetTestCase.this.myProject = getProject(); + String testName = myTestFile.getName(); + final int dotIdx = testName.indexOf('.'); + if (dotIdx >= 0) { + testName = testName.substring(0, dotIdx); + } + + transformed = StringUtil.replace(transform(testName, ArrayUtil.toStringArray(input)), "\r", ""); + result = StringUtil.replace(result, "\r", ""); + + assertEquals(result.trim(),transformed.trim()); + } + + + public String toString() { + return myTestFile.getAbsolutePath() + " "; + } + + protected void resetAllFields() { + // Do nothing otherwise myTestFile will be nulled out before getName() is called. + } + + public String getName() { + return myTestFile.getAbsolutePath(); + } + } +} From fcea83251dbcf65aef8b3a34a7293bca3a7bf467 Mon Sep 17 00:00:00 2001 From: Ekaterina Shliakhovetskaja Date: Fri, 12 Mar 2010 15:56:39 +0300 Subject: [PATCH 07/93] Spellchecker: replace PAtriciaTrie with THashSet --- .../dictionary/UserDictionary.java | 35 +- .../spellchecker/engine/BaseSpellChecker.java | 8 +- .../intellij/spellchecker/trie/Action.java | 4 +- .../trie/CharSequenceKeyAnalyzer.java | 147 - .../spellchecker/trie/PatriciaTrie.java | 2376 ----------------- .../com/intellij/spellchecker/trie/Trie.java | 168 -- 6 files changed, 21 insertions(+), 2717 deletions(-) delete mode 100644 plugins/spellchecker/src/com/intellij/spellchecker/trie/CharSequenceKeyAnalyzer.java delete mode 100644 plugins/spellchecker/src/com/intellij/spellchecker/trie/PatriciaTrie.java delete mode 100644 plugins/spellchecker/src/com/intellij/spellchecker/trie/Trie.java diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/UserDictionary.java b/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/UserDictionary.java index 112372b6d664..3a8bd704072b 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/UserDictionary.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/UserDictionary.java @@ -16,19 +16,16 @@ package com.intellij.spellchecker.dictionary; import com.intellij.spellchecker.trie.Action; -import com.intellij.spellchecker.trie.CharSequenceKeyAnalyzer; -import com.intellij.spellchecker.trie.PatriciaTrie; -import com.intellij.spellchecker.trie.Trie; +import gnu.trove.THashSet; import org.jetbrains.annotations.Nullable; import java.util.Collection; -import java.util.Map; import java.util.Set; public class UserDictionary implements Dictionary { private String name; - private Trie trie = new PatriciaTrie(new CharSequenceKeyAnalyzer()); + private THashSet dictionary = new THashSet(); public UserDictionary(String name) { this.name = name; @@ -39,17 +36,17 @@ public class UserDictionary implements Dictionary { } public boolean contains(String word) { - return word != null && trie.containsKey(word); + return word != null && dictionary.contains(word); } @Nullable public Set getWords() { - return trie.keySet(); + return dictionary; } @Nullable public Set getEditableWords() { - return trie.keySet(); + return dictionary; } @Nullable @@ -58,7 +55,7 @@ public class UserDictionary implements Dictionary { } public void clear() { - trie.clear(); + dictionary.clear(); } @@ -66,14 +63,14 @@ public class UserDictionary implements Dictionary { if (word == null) { return; } - trie.put(word, word); + dictionary.add(word); } public void removeFromDictionary(String word) { if (word == null) { return; } - trie.remove(word); + dictionary.remove(word); } public void replaceAll(@Nullable Collection words) { @@ -91,7 +88,7 @@ public class UserDictionary implements Dictionary { } public boolean isEmpty() { - return (trie == null || trie.size() == 0); + return (dictionary == null || dictionary.size() == 0); } @Override @@ -106,12 +103,12 @@ public class UserDictionary implements Dictionary { } public void traverse(final Action action){ - trie.traverse(new PatriciaTrie.Cursor() { - public SelectStatus select(Map.Entry entry) { - action.run(entry); - return SelectStatus.CONTINUE; - } - }); + if (dictionary==null){ + return; + } + for (String s : dictionary) { + action.run(s); + } } @Override @@ -121,6 +118,6 @@ public class UserDictionary implements Dictionary { @Override public String toString() { - return "UserDictionary{" + "name='" + name + '\'' + ", words.count=" + trie.size() + '}'; + return "UserDictionary{" + "name='" + name + '\'' + ", words.count=" + dictionary.size() + '}'; } } diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/engine/BaseSpellChecker.java b/plugins/spellchecker/src/com/intellij/spellchecker/engine/BaseSpellChecker.java index 5af0d0b6cc25..29b9844f488c 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/engine/BaseSpellChecker.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/engine/BaseSpellChecker.java @@ -85,10 +85,10 @@ public class BaseSpellChecker implements SpellCheckerEngine { } final List suggestions = new ArrayList(); engineDictionary.traverse(new Action() { - public void run(Map.Entry entry) { - if (transformed.charAt(0) == entry.getKey().charAt(0)/* && Math.abs(mpw.length() - entry.getKey().length()) <= 1*/) { - final int distance = metrics.calculateMetrics(transformed, entry.getKey()); - suggestions.add(new Suggestion(entry.getKey(), distance)); + public void run(String entry) { + if (transformed.charAt(0) == entry.charAt(0)/* && Math.abs(mpw.length() - entry.getKey().length()) <= 1*/) { + final int distance = metrics.calculateMetrics(transformed, entry); + suggestions.add(new Suggestion(entry, distance)); } } }); diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/trie/Action.java b/plugins/spellchecker/src/com/intellij/spellchecker/trie/Action.java index 4391ff8ca6e2..a5b401680802 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/trie/Action.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/trie/Action.java @@ -15,9 +15,7 @@ */ package com.intellij.spellchecker.trie; -import java.util.Map; - public interface Action { - void run(Map.Entry entry); + void run(String entry); } diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/trie/CharSequenceKeyAnalyzer.java b/plugins/spellchecker/src/com/intellij/spellchecker/trie/CharSequenceKeyAnalyzer.java deleted file mode 100644 index fd08f8843dbd..000000000000 --- a/plugins/spellchecker/src/com/intellij/spellchecker/trie/CharSequenceKeyAnalyzer.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2005-2009 Roger Kapsi, Sam Berlin - * - * 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.spellchecker.trie; - - -/** - * Analyzes CharSequence keys with case sensitivity. With - * CharSequenceKeyAnalyzer you can - * compare, check prefix, and determine the index of a bit. - *

- * A typical use case for a CharSequenceKeyAnalyzer is with a - * {@link PatriciaTrie}. - *

- * PatriciaTrie<String, String> trie = new PatriciaTrie<String, String>(new CharSequenceKeyAnalyzer());
- * 

- * trie.put("Lime", "Lime"); - * trie.put("LimeWire", "LimeWire"); - * trie.put("LimeRadio", "LimeRadio"); - * trie.put("Lax", "Lax"); - * trie.put("Lake", "Lake"); - * trie.put("Lovely", "Lovely"); - *

- * System.out.println(trie.select("Lo")); - * System.out.println(trie.select("Lime")); - *

- * System.out.println(trie.getPrefixedBy("La").toString()); - *

- * Output: - * Lovely - * Lime - * {Lake=Lake, Lax=Lax} - *

- *

- * - * @author Sam Berlin - * @author Roger Kapsi - */ -public class CharSequenceKeyAnalyzer implements PatriciaTrie.KeyAnalyzer { - - private static final long serialVersionUID = -7032449491269434877L; - - private static final int[] BITS = createIntBitMask(16); - - public static final int[] createIntBitMask(int bitCount) { - int[] bits = new int[bitCount]; - for (int i = 0; i < bitCount; i++) { - bits[i] = 1 << (bitCount - i - 1); - } - return bits; - } - - public int length(CharSequence key) { - return (key != null ? key.length() * 16 : 0); - } - - public int bitIndex(CharSequence key, int keyOff, int keyLength, CharSequence found, int foundOff, int foundKeyLength) { - boolean allNull = true; - - if (keyOff % 16 != 0 || foundOff % 16 != 0 || keyLength % 16 != 0 || foundKeyLength % 16 != 0) { - throw new IllegalArgumentException("offsets & lengths must be at character boundaries"); - } - - int off1 = keyOff / 16; - int off2 = foundOff / 16; - int len1 = keyLength / 16 + off1; - int len2 = foundKeyLength / 16 + off2; - int length = Math.max(len1, len2); - - // Look at each character, and if they're different - // then figure out which bit makes the difference - // and return it. - char k = 0, f = 0; - for (int i = 0; i < length; i++) { - int kOff = i + off1; - int fOff = i + off2; - - if (kOff >= len1) { - k = 0; - } - else { - k = key.charAt(kOff); - } - - if (found == null || fOff >= len2) { - f = 0; - } - else { - f = found.charAt(fOff); - } - - if (k != f) { - int x = k ^ f; - return i * 16 + (Integer.numberOfLeadingZeros(x) - 16); - } - - if (k != 0) allNull = false; - - } - - if (allNull) { - return PatriciaTrie.KeyAnalyzer.NULL_BIT_KEY; - } - - return PatriciaTrie.KeyAnalyzer.EQUAL_BIT_KEY; - } - - public boolean isBitSet(CharSequence key, int keyLength, int bitIndex) { - if (key == null || bitIndex >= keyLength) { - return false; - } - - int index = bitIndex / BITS.length; - int bit = bitIndex - index * BITS.length; - return (key.charAt(index) & BITS[bit]) != 0; - } - - public int compare(CharSequence o1, CharSequence o2) { - return o1.toString().compareTo(o2.toString()); - } - - public int bitsPerElement() { - return 16; - } - - public boolean isPrefix(CharSequence prefix, int offset, int length, CharSequence key) { - if (offset % 16 != 0 || length % 16 != 0) throw new IllegalArgumentException("Cannot determine prefix outside of character boundaries"); - String s1 = prefix.subSequence(offset / 16, length / 16).toString(); - String s2 = key.toString(); - return s2.startsWith(s1); - } -} - diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/trie/PatriciaTrie.java b/plugins/spellchecker/src/com/intellij/spellchecker/trie/PatriciaTrie.java deleted file mode 100644 index 96dc77a8d24e..000000000000 --- a/plugins/spellchecker/src/com/intellij/spellchecker/trie/PatriciaTrie.java +++ /dev/null @@ -1,2376 +0,0 @@ -/* - * Copyright 2005-2009 Roger Kapsi, Sam Berlin - * - * 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.spellchecker.trie; - - -import java.io.Serializable; -import java.util.AbstractCollection; -import java.util.AbstractMap; -import java.util.AbstractSet; -import java.util.Collection; -import java.util.Comparator; -import java.util.ConcurrentModificationException; -import java.util.Iterator; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Set; -import java.util.SortedMap; - -/** - * A PATRICIA Trie. - *

- * PATRICIA = Practical Algorithm to Retrieve Information Coded in Alphanumeric - *

- * A PATRICIA Trie is a compressed Trie. Instead of storing all data at the - * edges of the Trie (and having empty internal nodes), PATRICIA stores data - * in every node. This allows for very efficient traversal, insert, delete, - * predecessor, successor, prefix, range, and 'select' operations. All operations - * are performed at worst in O(K) time, where K is the number of bits in the - * largest item in the tree. In practice, operations actually take O(A(K)) - * time, where A(K) is the average number of bits of all items in the tree. - *

- * Most importantly, PATRICIA requires very few comparisons to keys while - * doing any operation. While performing a lookup, each comparison - * (at most K of them, described above) will perform a single bit comparison - * against the given key, instead of comparing the entire key to another key. - *

- * The Trie can return operations in lexicographical order using the 'traverse', - * 'prefix', 'submap', or 'iterator' methods. The Trie can also scan for items - * that are 'bitwise' (using an XOR metric) by the 'select' method. Bitwise - * closeness is determined by the {@link KeyAnalyzer} returning true or - * false for a bit being set or not in a given key. - *

- * This PATRICIA Trie supports both variable length & fixed length keys. - * Some methods, such as getPrefixedBy(...) are suited only to - * variable length keys, whereas getPrefixedByBits(...) is suited - * to fixed-size keys. - *

- * Additionally see PATRICIA - * for more information. - *

- * Any methods here that take an Object may throw a - * ClassCastException if the method is expecting an instance of K - * (and it isn't K). - *

- *

- * PatriciaTrie<String, String> trie = new PatriciaTrie<String, String>
- * (new CharSequenceKeyAnalyzer());
- * 

- * trie.put("Lime", "Lime"); - * trie.put("LimeWire", "LimeWire"); - * trie.put("LimeRadio", "LimeRadio"); - * trie.put("Lax", "Lax"); - * trie.put("Lake", "Lake"); - * trie.put("Lovely", "Lovely"); - *

- * System.out.println(trie.select("Lo")); - * System.out.println(trie.select("Lime")); - *

- * System.out.println(trie.getPrefixedBy("La").toString()); - *

- * Output: - * Lovely - * Lime - * {Lake=Lake, Lax=Lax} - *

- *

- * - * @author Roger Kapsi - * @author Sam Berlin - */ -public class PatriciaTrie extends AbstractMap implements Trie, Serializable { - - private static final long serialVersionUID = 110232526181493307L; - - /** - * The root element of the Trie. - */ - private final TrieEntry root = new TrieEntry(null, null, -1); - - /** - * The current size (total number of elements) of the Trie. - */ - private int size = 0; - - /** - * The number of times this has been modified (to fail-fast the iterators). - */ - private transient int modCount = 0; - - /** - * The keyAnalyzer used to analyze bit values of keys. - */ - private final KeyAnalyzer keyAnalyzer; - - /** - * Constructs a new PatriciaTrie using the given keyAnalyzer. - */ - public PatriciaTrie(KeyAnalyzer keyAnalyzer) { - this.keyAnalyzer = keyAnalyzer; - } - - /** - * Returns the KeyAnalyzer that constructed the trie. - */ - public KeyAnalyzer getKeyAnalyzer() { - return keyAnalyzer; - } - - /** - * Returns the KeyAnalyzer as a comparator. - */ - public Comparator comparator() { - return keyAnalyzer; - } - - /** - * Clears the Trie (i.e. removes all elements). - */ - public void clear() { - root.key = null; - root.bitIndex = -1; - root.value = null; - - root.parent = null; - root.left = root; - root.right = null; - root.predecessor = root; - - size = 0; - incrementModCount(); - } - - /** - * Returns true if the Trie is empty - */ - public boolean isEmpty() { - return size == 0; - } - - /** - * Returns the number items in the Trie - */ - public int size() { - return size; - } - - /** - * Increments both the size and mod counter. - */ - private void incrementSize() { - size++; - incrementModCount(); - } - - /** - * Decrements the size and increments the mod counter. - */ - private void decrementSize() { - size--; - incrementModCount(); - } - - /** - * Increments the mod counter. - */ - private void incrementModCount() { - modCount++; - } - - /** - * Adds a new pair to the Trie and if a pair already - * exists it will be replaced. In the latter case it will return - * the old value. - */ - public V put(K key, V value) { - if (key == null) { - throw new NullPointerException("Key cannot be null"); - } - - int keyLength = length(key); - - // The only place to store a key with a length - // of zero bits is the root node - if (keyLength == 0) { - if (root.isEmpty()) { - incrementSize(); - } - else { - incrementModCount(); - } - return root.setKeyValue(key, value); - } - - TrieEntry found = getNearestEntryForKey(key, keyLength); - if (key.equals(found.key)) { - if (found.isEmpty()) { // <- must be the root - incrementSize(); - } - else { - incrementModCount(); - } - return found.setKeyValue(key, value); - } - - int bitIndex = bitIndex(key, found.key); - if (isValidBitIndex(bitIndex)) { // in 99.999...9% the case - /* NEW KEY+VALUE TUPLE */ - TrieEntry t = new TrieEntry(key, value, bitIndex); - addEntry(t, keyLength); - incrementSize(); - return null; - } - else if (isNullBitKey(bitIndex)) { - // A bits of the Key are zero. The only place to - // store such a Key is the root Node! - - /* NULL BIT KEY */ - if (root.isEmpty()) { - incrementSize(); - } - else { - incrementModCount(); - } - return root.setKeyValue(key, value); - - } - else if (isEqualBitKey(bitIndex)) { - // This is a very special and rare case. - - /* REPLACE OLD KEY+VALUE */ - if (found != root) { - incrementModCount(); - return found.setKeyValue(key, value); - } - } - - throw new IndexOutOfBoundsException("Failed to put: " + key + " -> " + value + ", " + bitIndex); - } - - /** - * Adds the given entry into the Trie. - */ - private TrieEntry addEntry(TrieEntry toAdd, int keyLength) { - TrieEntry current = root.left; - TrieEntry path = root; - while (true) { - if (current.bitIndex >= toAdd.bitIndex || current.bitIndex <= path.bitIndex) { - toAdd.predecessor = toAdd; - - if (!isBitSet(toAdd.key, keyLength, toAdd.bitIndex)) { - toAdd.left = toAdd; - toAdd.right = current; - } - else { - toAdd.left = current; - toAdd.right = toAdd; - } - - toAdd.parent = path; - if (current.bitIndex >= toAdd.bitIndex) { - current.parent = toAdd; - } - - // if we inserted an uplink, set the predecessor on it - if (current.bitIndex <= path.bitIndex) { - current.predecessor = toAdd; - } - - if (path == root || !isBitSet(toAdd.key, keyLength, path.bitIndex)) { - path.left = toAdd; - } - else { - path.right = toAdd; - } - return toAdd; - } - - path = current; - if (!isBitSet(toAdd.key, keyLength, current.bitIndex)) { - current = current.left; - } - else { - current = current.right; - } - } - } - - @Override - public Set> entrySet() { - Set> es = entrySet; - return (es != null ? es : (entrySet = new EntrySet())); - } - - /** - * Returns the Value whose Key equals our lookup Key - * or null if no such key exists. - */ - public V get(Object k) { - TrieEntry entry = getEntry(k); - return entry != null ? entry.getValue() : null; - } - - /** - * Returns the entry associated with the specified key in the - * PatriciaTrie. Returns null if the map contains no mapping - * for this key. - *

- * This may throw ClassCastException if the object is not of type K. - */ - TrieEntry getEntry(Object k) { - K key = asKey(k); - if (key == null) return null; - - int keyLength = length(key); - TrieEntry entry = getNearestEntryForKey(key, keyLength); - return !entry.isEmpty() && key.equals(entry.key) ? entry : null; - } - - /** - * Gets the key as a 'K'. - */ - @SuppressWarnings("unchecked") - protected final K asKey(Object key) { - try { - return (K)key; - } - catch (ClassCastException cce) { - // Because the type is erased, the cast & return are - // actually doing nothing, making this CCE impossible. - // However, it's still here on the off-chance it may - // work. - return null; - } - } - - /** - * Returns the nearest entry for a given key. This is useful - * for finding knowing if a given key exists (and finding the value - * for it), or for inserting the key. - *

- * The actual get implementation. This is very similar to - * selectR but with the exception that it might return the - * root Entry even if it's empty. - */ - private TrieEntry getNearestEntryForKey(K key, int keyLength) { - TrieEntry current = root.left; - TrieEntry path = root; - while (true) { - if (current.bitIndex <= path.bitIndex) return current; - - path = current; - if (!isBitSet(key, keyLength, current.bitIndex)) { - current = current.left; - } - else { - current = current.right; - } - } - } - - /** - * Returns the Value whose Key has the longest prefix - * in common with our lookup key. - */ - @SuppressWarnings("unchecked") - public V select(K key) { - int keyLength = length(key); - TrieEntry[] result = new TrieEntry[1]; - if (!selectR(root.left, -1, key, keyLength, result)) { - TrieEntry e = result[0]; - return e.getValue(); - } - return null; - } - - /** - * This is equivalent to the other selectR() method but without - * its overhead because we're selecting only one best matching - * Entry from the Trie. - */ - private boolean selectR(TrieEntry h, int bitIndex, final K key, final int keyLength, final TrieEntry[] result) { - - if (h.bitIndex <= bitIndex) { - // If we hit the root Node and it is empty - // we have to look for an alternative best - // matching node. - if (!h.isEmpty()) { - result[0] = h; - return false; - } - return true; - } - - if (!isBitSet(key, keyLength, h.bitIndex)) { - if (selectR(h.left, h.bitIndex, key, keyLength, result)) { - return selectR(h.right, h.bitIndex, key, keyLength, result); - } - } - else { - if (selectR(h.right, h.bitIndex, key, keyLength, result)) { - return selectR(h.left, h.bitIndex, key, keyLength, result); - } - } - return false; - } - - @SuppressWarnings("unchecked") - public Map.Entry select(K key, Cursor cursor) { - int keyLength = length(key); - TrieEntry[] result = new TrieEntry[]{null}; - selectR(root.left, -1, key, keyLength, cursor, result); - return result[0]; - } - - private boolean selectR(TrieEntry h, - int bitIndex, - final K key, - final int keyLength, - final Cursor cursor, - final TrieEntry[] result) { - - if (h.bitIndex <= bitIndex) { - if (!h.isEmpty()) { - Cursor.SelectStatus ret = cursor.select(h); - switch (ret) { - case REMOVE: - throw new UnsupportedOperationException("cannot remove during select"); - case EXIT: - result[0] = h; - return false; // exit - case REMOVE_AND_EXIT: - TrieEntry entry = new TrieEntry(h.getKey(), h.getValue(), -1); - result[0] = entry; - removeEntry(h); - return false; - case CONTINUE: - // fall through. - } - } - return true; // continue - } - - if (!isBitSet(key, keyLength, h.bitIndex)) { - if (selectR(h.left, h.bitIndex, key, keyLength, cursor, result)) { - return selectR(h.right, h.bitIndex, key, keyLength, cursor, result); - } - } - else { - if (selectR(h.right, h.bitIndex, key, keyLength, cursor, result)) { - return selectR(h.left, h.bitIndex, key, keyLength, cursor, result); - } - } - - return false; - } - - /** - * Returns a view of this Trie of all elements that are - * prefixed by the given key. - *

- * In a fixed-keysize Trie, this is essentially a 'get' operation. - *

- * For example, if the trie contains 'Lime', 'LimeWire', - * 'LimeRadio', 'Lax', 'Later', 'Lake', and 'Lovely', then - * a lookup of 'Lime' would return 'Lime', 'LimeRadio', and 'LimeWire'. - *

- * The view that this returns is optimized to have a very efficient - * Iterator. The firstKey, lastKey & size methods must iterate - * over all possible values in order to determine the results. This - * information is cached until the Patricia tree changes. All other - * methods (except Iterator) must compare the given key to the prefix - * to ensure that it is within the range of the view. The Iterator's - * remove method must also relocate the subtree that contains the - * prefixes if the entry holding the subtree is removed or changes. - * Changing the subtree takes O(K) time. - * - * @param key - */ - public SortedMap getPrefixedBy(K key) { - return getPrefixedByBits(key, 0, keyAnalyzer.length(key)); - } - - /** - * Returns a view of this Trie of all elements that are - * prefixed by the length of the key. - *

- * Fixed-keysize Tries will not support this operation - * (because all keys will be the same length). - *

- * For example, if the trie contains 'Lime', 'LimeWire', - * 'LimeRadio', 'Lax', 'Later', 'Lake', and 'Lovely', then - * a lookup of 'LimePlastics' with a length of 4 would - * return 'Lime', 'LimeRadio', and 'LimeWire'. - *

- * The view that this returns is optimized to have a very efficient - * Iterator. The firstKey, lastKey & size methods must iterate - * over all possible values in order to determine the results. This - * information is cached until the Patricia tree changes. All other - * methods (except Iterator) must compare the given key to the prefix - * to ensure that it is within the range of the view. The Iterator's - * remove method must also relocate the subtree that contains the - * prefixes if the entry holding the subtree is removed or changes. - * Changing the subtree takes O(K) time. - * - * @param key - * @param length - */ - public SortedMap getPrefixedBy(K key, int length) { - return getPrefixedByBits(key, 0, length * keyAnalyzer.bitsPerElement()); - } - - /** - * Returns a view of this Trie of all elements that are prefixed - * by the key, starting at the given offset and for the given length. - *

- * Fixed-keysize Tries will not support this operation - * (because all keys are the same length). - *

- * For example, if the trie contains 'Lime', 'LimeWire', - * 'LimeRadio', 'Lax', 'Later', 'Lake', and 'Lovely', then - * a lookup of 'The Lime Plastics' with an offset of 4 and a - * length of 4 would return 'Lime', 'LimeRadio', and 'LimeWire'. - *

- * The view that this returns is optimized to have a very efficient - * Iterator. The firstKey, lastKey & size methods must iterate - * over all possible values in order to determine the results. This - * information is cached until the Patricia tree changes. All other - * methods (except Iterator) must compare the given key to the prefix - * to ensure that it is within the range of the view. The Iterator's - * remove method must also relocate the subtree that contains the - * prefixes if the entry holding the subtree is removed or changes. - * Changing the subtree takes O(K) time. - * - * @param key - * @param offset - * @param length - */ - public SortedMap getPrefixedBy(K key, int offset, int length) { - return getPrefixedByBits(key, offset * keyAnalyzer.bitsPerElement(), length * keyAnalyzer.bitsPerElement()); - } - - /** - * Returns a view of this Trie of all elements that are prefixed - * by the number of bits in the given Key. - *

- * Fixed-keysize Tries can support this operation as a way to do - * lookups of partial keys. That is, if the Trie is storing IP - * addresses, you can lookup all addresses that begin with - * '192.168' by providing the key '192.168.X.X' and a length of 16 - * would return all addresses that begin with '192.168'. - *

- * The view that this returns is optimized to have a very efficient - * Iterator. The firstKey, lastKey & size methods must iterate - * over all possible values in order to determine the results. This - * information is cached until the Patricia tree changes. All other - * methods (except Iterator) must compare the given key to the prefix - * to ensure that it is within the range of the view. The Iterator's - * remove method must also relocate the subtree that contains the - * prefixes if the entry holding the subtree is removed or changes. - * Changing the subtree takes O(K) time. - * - * @param key - * @param bitLength - */ - public SortedMap getPrefixedByBits(K key, int bitLength) { - return getPrefixedByBits(key, 0, bitLength); - } - - /** - * Returns a view of this map, with entries containing only those that - * are prefixed by a value whose bits matches the bits between 'offset' - * and 'length' in the given key. - *

- * The view that this returns is optimized to have a very efficient - * Iterator. The firstKey, lastKey & size methods must iterate - * over all possible values in order to determine the results. This - * information is cached until the Patricia tree changes. All other - * methods (except Iterator) must compare the given key to the prefix - * to ensure that it is within the range of the view. The Iterator's - * remove method must also relocate the subtree that contains the - * prefixes if the entry holding the subtree is removed or changes. - * Changing the subtree takes O(K) time. - * - * @param key - * @param offset - * @param length - */ - private SortedMap getPrefixedByBits(K key, int offset, int length) { - int offsetLength = offset + length; - if (offsetLength > length(key)) { - throw new IllegalArgumentException(offset + " + " + length + " > " + length(key)); - } - - if (offsetLength == 0) return this; - - return new PrefixSubMap(key, offset, length); - } - - /** - * Returns true if this trie contains the specified Key - *

- * This may throw ClassCastException if the object is not - * of type K. - */ - public boolean containsKey(Object k) { - K key = asKey(k); - if (key == null) return false; - - int keyLength = length(key); - TrieEntry entry = getNearestEntryForKey(key, keyLength); - return !entry.isEmpty() && key.equals(entry.key); - } - - /** - * Returns true if this Trie contains the specified value. - */ - public boolean containsValue(Object o) { - for (V v : values()) { - if (valEquals(v, o)) return true; - } - return false; - } - - - /** - * Removes a Key from the Trie if one exists - *

- * This may throw ClassCastException if the object is not of type K. - * - * @param k the Key to delete - * @return Returns the deleted Value - */ - public V remove(Object k) { - K key = asKey(k); - if (key == null) return null; - - int keyLength = length(key); - TrieEntry current = root.left; - TrieEntry path = root; - while (true) { - if (current.bitIndex <= path.bitIndex) { - if (!current.isEmpty() && key.equals(current.key)) { - return removeEntry(current); - } - else { - return null; - } - } - - path = current; - if (!isBitSet(key, keyLength, current.bitIndex)) { - current = current.left; - } - else { - current = current.right; - } - } - } - - /** - * Removes a single entry from the Trie. - *

- * If we found a Key (Entry h) then figure out if it's - * an internal (hard to remove) or external Entry (easy - * to remove) - */ - private V removeEntry(TrieEntry h) { - if (h != root) { - if (h.isInternalNode()) { - removeInternalEntry(h); - } - else { - removeExternalEntry(h); - } - } - - decrementSize(); - return h.setKeyValue(null, null); - } - - /** - * Removes an external entry from the Trie. - *

- * If it's an external Entry then just remove it. - * This is very easy and straight forward. - */ - private void removeExternalEntry(TrieEntry h) { - if (h == root) { - throw new IllegalArgumentException("Cannot delete root Entry!"); - } - else if (!h.isExternalNode()) { - throw new IllegalArgumentException(h + " is not an external Entry!"); - } - - TrieEntry parent = h.parent; - TrieEntry child = (h.left == h) ? h.right : h.left; - - if (parent.left == h) { - parent.left = child; - } - else { - parent.right = child; - } - - // either the parent is changing, or the predecessor is changing. - if (child.bitIndex > parent.bitIndex) { - child.parent = parent; - } - else { - child.predecessor = parent; - } - - } - - /** - * Removes an internal entry from the Trie. - *

- * If it's an internal Entry then "good luck" with understanding - * this code. The Idea is essentially that Entry p takes Entry h's - * place in the trie which requires some re-wiring. - */ - private void removeInternalEntry(TrieEntry h) { - if (h == root) { - throw new IllegalArgumentException("Cannot delete root Entry!"); - } - else if (!h.isInternalNode()) { - throw new IllegalArgumentException(h + " is not an internal Entry!"); - } - - TrieEntry p = h.predecessor; - - // Set P's bitIndex - p.bitIndex = h.bitIndex; - - // Fix P's parent, predecessor and child Nodes - { - TrieEntry parent = p.parent; - TrieEntry child = (p.left == h) ? p.right : p.left; - - // if it was looping to itself previously, - // it will now be pointed from it's parent - // (if we aren't removing it's parent -- - // in that case, it remains looping to itself). - // otherwise, it will continue to have the same - // predecessor. - if (p.predecessor == p && p.parent != h) p.predecessor = p.parent; - - if (parent.left == p) { - parent.left = child; - } - else { - parent.right = child; - } - - if (child.bitIndex > parent.bitIndex) { - child.parent = parent; - } - } - ; - - // Fix H's parent and child Nodes - { - // If H is a parent of its left and right child - // then change them to P - if (h.left.parent == h) { - h.left.parent = p; - } - - if (h.right.parent == h) { - h.right.parent = p; - } - - // Change H's parent - if (h.parent.left == h) { - h.parent.left = p; - } - else { - h.parent.right = p; - } - } - ; - - // Copy the remaining fields from H to P - //p.bitIndex = h.bitIndex; - p.parent = h.parent; - p.left = h.left; - p.right = h.right; - - // Make sure that if h was pointing to any uplinks, - // p now points to them. - if (isValidUplink(p.left, p)) p.left.predecessor = p; - if (isValidUplink(p.right, p)) p.right.predecessor = p; - - } - - /** - * Returns the node lexicographically before the given node (or null if none). - *

- * This follows four simple branches: - * - If the uplink that returned us was a right uplink: - * - If predecessor's left is a valid uplink from predecessor, return it. - * - Else, follow the right path from the predecessor's left. - * - If the uplink that returned us was a left uplink: - * - Loop back through parents until we encounter a node where - * node != node.parent.left. - * - If node.parent.left is uplink from node.parent: - * - If node.parent.left is not root, return it. - * - If it is root & root isEmpty, return null. - * - If it is root & root !isEmpty, return root. - * - If node.parent.left is not uplink from node.parent: - * - Follow right path for first right child from node.parent.left - * - * @param start - */ - private TrieEntry previousEntry(TrieEntry start) { - if (start.predecessor == null) throw new IllegalArgumentException("must have come from somewhere!"); - - if (start.predecessor.right == start) { - if (isValidUplink(start.predecessor.left, start.predecessor)) { - return start.predecessor.left; - } - else { - return followRight(start.predecessor.left); - } - } - else { - TrieEntry node = start.predecessor; - while (node.parent != null && node == node.parent.left) node = node.parent; - if (node.parent == null) // can be null if we're looking up root. - { - return null; - } - if (isValidUplink(node.parent.left, node.parent)) { - if (node.parent.left == root) { - if (root.isEmpty()) { - return null; - } - else { - return root; - } - } - else { - return node.parent.left; - } - } - else { - return followRight(node.parent.left); - } - } - } - - /** - * Returns the entry lexicographically after the given entry. - * If the given entry is null, returns the first node. - */ - private TrieEntry nextEntry(TrieEntry node) { - if (node == null) { - return firstEntry(); - } - else { - return nextEntryImpl(node.predecessor, node, null); - } - } - - /** - * Returns the entry lexicographically after the given entry. - * If the given entry is null, returns the first node. - *

- * This will traverse only within the subtree. If the given node - * is not within the subtree, this will have undefined results. - */ - private TrieEntry nextEntryInSubtree(TrieEntry node, TrieEntry parentOfSubtree) { - if (node == null) { - return firstEntry(); - } - else { - return nextEntryImpl(node.predecessor, node, parentOfSubtree); - } - } - - /** - * Scans for the next node, starting at the specified point, and using 'previous' - * as a hint that the last node we returned was 'previous' (so we know not to return - * it again). If 'tree' is non-null, this will limit the search to the given tree. - *

- * The basic premise is that each iteration can follow the following steps: - *

- * 1) Scan all the way to the left. - * a) If we already started from this node last time, proceed to Step 2. - * b) If a valid uplink is found, use it. - * c) If the result is an empty node (root not set), break the scan. - * d) If we already returned the left node, break the scan. - *

- * 2) Check the right. - * a) If we already returned the right node, proceed to Step 3. - * b) If it is a valid uplink, use it. - * c) Do Step 1 from the right node. - *

- * 3) Back up through the parents until we encounter find a parent - * that we're not the right child of. - *

- * 4) If there's no right child of that parent, the iteration is finished. - * Otherwise continue to Step 5. - *

- * 5) Check to see if the right child is a valid uplink. - * a) If we already returned that child, proceed to Step 6. - * Otherwise, use it. - *

- * 6) If the right child of the parent is the parent itself, we've - * already found & returned the end of the Trie, so exit. - *

- * 7) Do Step 1 on the parent's right child. - */ - private TrieEntry nextEntryImpl(TrieEntry start, TrieEntry previous, TrieEntry tree) { - TrieEntry current = start; - - // Only look at the left if this was a recursive or - // the first check, otherwise we know we've already looked - // at the left. - if (previous == null || start != previous.predecessor) { - while (!current.left.isEmpty()) { - // stop traversing if we've already - // returned the left of this node. - if (previous == current.left) { - break; - } - - if (isValidUplink(current.left, current)) { - return current.left; - } - - current = current.left; - } - } - - // If there's no data at all, exit. - if (current.isEmpty()) { - return null; - } - - // If we've already returned the left, - // and the immediate right is null, - // there's only one entry in the Trie - // which is stored at the root. - // - // / ("") <-- root - // \_/ \ - // null <-- 'current' - // - if (current.right == null) return null; - - // If nothing valid on the left, try the right. - if (previous != current.right) { - // See if it immediately is valid. - if (isValidUplink(current.right, current)) { - return current.right; - } - - // Must search on the right's side if it wasn't initially valid. - return nextEntryImpl(current.right, previous, tree); - } - - // Neither left nor right are valid, find the first parent - // whose child did not come from the right & traverse it. - while (current == current.parent.right) { - // If we're going to traverse to above the subtree, stop. - if (current == tree) return null; - - current = current.parent; - } - - // If we're on the top of the subtree, we can't go any higher. - if (current == tree) return null; - - - // If there's no right, the parent must be root, so we're done. - if (current.parent.right == null) { - return null; - } - - - // If the parent's right points to itself, we've found one. - if (previous != current.parent.right && isValidUplink(current.parent.right, current.parent)) { - return current.parent.right; - } - - // If the parent's right is itself, there can't be any more nodes. - if (current.parent.right == current.parent) { - return null; - } - - // We need to traverse down the parent's right's path. - return nextEntryImpl(current.parent.right, previous, tree); - } - - /** - * Returns each entry as a string. - */ - public String toString() { - StringBuilder buffer = new StringBuilder(); - buffer.append("Trie[").append(size()).append("]={\n"); - for (Iterator> i = newEntryIterator(); i.hasNext();) { - buffer.append(" ").append(i.next().toString()).append("\n"); - } - buffer.append("}\n"); - return buffer.toString(); - } - - public Map.Entry traverse(Cursor cursor) { - TrieEntry entry = nextEntry(null); - while (entry != null) { - TrieEntry current = entry; - Cursor.SelectStatus ret = cursor.select(current); - entry = nextEntry(current); - switch (ret) { - case EXIT: - return current; - case REMOVE: - removeEntry(current); - break; // out of switch, stay in while loop - case REMOVE_AND_EXIT: - Map.Entry value = new TrieEntry(current.getKey(), current.getValue(), -1); - removeEntry(current); - return value; - case CONTINUE: // do nothing. - } - } - - return null; - } - - /** - * Returns true if 'next' is a valid uplink coming from 'from'. - */ - private boolean isValidUplink(TrieEntry next, TrieEntry from) { - return next != null && next.bitIndex <= from.bitIndex && !next.isEmpty(); - } - - /** - * Returns true if bitIndex is a valid index - */ - private static boolean isValidBitIndex(int bitIndex) { - return 0 <= bitIndex && bitIndex <= Integer.MAX_VALUE; - } - - /** - * Returns true if bitIndex is a NULL_BIT_KEY - */ - private static boolean isNullBitKey(int bitIndex) { - return bitIndex == KeyAnalyzer.NULL_BIT_KEY; - } - - /** - * Returns true if bitIndex is a EQUAL_BIT_KEY - */ - private static boolean isEqualBitKey(int bitIndex) { - return bitIndex == KeyAnalyzer.EQUAL_BIT_KEY; - } - - /** - * Returns the length of the key, or 0 if the key is null. - */ - private int length(K key) { - if (key == null) { - return 0; - } - - return keyAnalyzer.length(key); - } - - /** - * Returns whether or not the given bit on the - * key is set, or false if the key is null - */ - private boolean isBitSet(K key, int keyLength, int bitIndex) { - if (key == null) { // root's might be null! - return false; - } - return keyAnalyzer.isBitSet(key, keyLength, bitIndex); - } - - /** - * Utility method for calling - * keyAnalyzer.bitIndex(key, 0, length(key), foundKey, 0, length(foundKey)) - */ - private int bitIndex(K key, K foundKey) { - return keyAnalyzer.bitIndex(key, 0, length(key), foundKey, 0, length(foundKey)); - } - - /** - * The actual Trie nodes. - */ - private static class TrieEntry implements Map.Entry, Serializable { - - private static final long serialVersionUID = 4596023148184140013L; - - private K key; - private V value; - - /** - * The index this entry is comparing. - */ - private int bitIndex; - - /** - * The parent of this entry. - */ - private TrieEntry parent; - /** - * The left child of this entry. - */ - private TrieEntry left; - /** - * The right child of this entry. - */ - private TrieEntry right; - /** - * The entry who uplinks to this entry. - */ - private TrieEntry predecessor; - - private TrieEntry(K key, V value, int bitIndex) { - this.key = key; - this.value = value; - - this.bitIndex = bitIndex; - - this.parent = null; - this.left = this; - this.right = null; - this.predecessor = this; - } - - public boolean equals(Object o) { - if (o == this) { - return true; - } - else if (o instanceof Map.Entry) { - Map.Entry e = (Map.Entry)o; - Object k1 = getKey(); - Object k2 = e.getKey(); - if (k1 == k2 || (k1 != null && k1.equals(k2))) { - Object v1 = getValue(); - Object v2 = e.getValue(); - if (v1 == v2 || (v1 != null && v1.equals(v2))) return true; - } - return false; - } - else { - return false; - } - } - - /** - * Whether or not the entry is storing a key. - * Only the root can potentially be empty, all other - * nodes must have a key. - */ - public boolean isEmpty() { - return key == null; - } - - public K getKey() { - return key; - } - - public V getValue() { - return value; - } - - public V setValue(V value) { - V o = this.value; - this.value = value; - return o; - } - - /** - * Replaces the old key and value with the new ones. - * Returns the old vlaue. - */ - private V setKeyValue(K key, V value) { - this.key = key; - return setValue(value); - } - - /** - * Neither the left nor right child is a loopback - */ - private boolean isInternalNode() { - return left != this && right != this; - } - - /** - * Either the left or right child is a loopback - */ - private boolean isExternalNode() { - return !isInternalNode(); - } - - public String toString() { - StringBuilder buffer = new StringBuilder(); - - if (bitIndex == -1) { - buffer.append("RootEntry("); - } - else { - buffer.append("Entry("); - } - - buffer.append("key=").append(getKey()).append(" [").append(bitIndex).append("], "); - buffer.append("value=").append(getValue()).append(", "); - //buffer.append("bitIndex=").append(bitIndex).append(", "); - - if (parent != null) { - if (parent.bitIndex == -1) { - buffer.append("parent=").append("ROOT"); - } - else { - buffer.append("parent=").append(parent.getKey()).append(" [").append(parent.bitIndex).append("]"); - } - } - else { - buffer.append("parent=").append("null"); - } - buffer.append(", "); - - if (left != null) { - if (left.bitIndex == -1) { - buffer.append("left=").append("ROOT"); - } - else { - buffer.append("left=").append(left.getKey()).append(" [").append(left.bitIndex).append("]"); - } - } - else { - buffer.append("left=").append("null"); - } - buffer.append(", "); - - if (right != null) { - if (right.bitIndex == -1) { - buffer.append("right=").append("ROOT"); - } - else { - buffer.append("right=").append(right.getKey()).append(" [").append(right.bitIndex).append("]"); - } - } - else { - buffer.append("right=").append("null"); - } - buffer.append(", "); - - if (predecessor != null) { - if (predecessor.bitIndex == -1) { - buffer.append("predecessor=").append("ROOT"); - } - else { - buffer.append("predecessor=").append(predecessor.getKey()).append(" [").append(predecessor.bitIndex).append("]"); - } - } - - buffer.append(")"); - return buffer.toString(); - } - } - - /** - * Defines the interface to analyze {@link Trie} keys on a bit - * level. KeyAnalyzer's - * methods return the length of the key in bits, whether or not a bit is - * set, and bits per element in the key. - *

- * Additionally, a method determines if a key is a prefix of another key and - * returns the bit index where one key is different from another key (if - * the key and found key are equal than the return value is EQUAL_BIT_KEY). - *

- * KeyAnalyzer defines:
- * - * - * - *
NULL_BIT_KEYWhen key's bits are all zero
EQUAL_BIT_KEY When keys are the same
- */ - public static interface KeyAnalyzer extends Comparator, Serializable { - - /** - * Returned by bitIndex if key's bits are all 0 - */ - public static final int NULL_BIT_KEY = -1; - - /** - * Returned by bitIndex if key and found key are - * equal. This is a very very specific case and - * shouldn't happen on a regular basis - */ - public static final int EQUAL_BIT_KEY = -2; - - /** - * Returns the length of the Key in bits. - */ - public int length(K key); - - /** - * Returns whether or not a bit is set - */ - public boolean isBitSet(K key, int keyLength, int bitIndex); - - /** - * Returns the n-th different bit between key and found. - * This starts the comparison in key at 'keyStart' and goes - * for 'keyLength' bits, and compares to the found key - * starting at 'foundStart' and going for 'foundLength' bits. - */ - public int bitIndex(K key, int keyStart, int keyLength, K found, int foundStart, int foundLength); - - /** - * Returns the number of bits per element in the key. - * This is only useful for variable-length keys, such as Strings. - */ - public int bitsPerElement(); - - /** - * Determines whether or not the given prefix (from offset to length) - * is a prefix of the given key. - */ - public boolean isPrefix(K prefix, int offset, int length, K key); - } - - /** - * An iterator that stores a single TrieEntry. - */ - private class SingletonIterator implements Iterator> { - private final TrieEntry entry; - private int hit = 0; - - public SingletonIterator(TrieEntry entry) { - this.entry = entry; - } - - public boolean hasNext() { - return hit == 0; - } - - public Map.Entry next() { - if (hit != 0) throw new NoSuchElementException(); - hit++; - return entry; - } - - public void remove() { - if (hit != 1) throw new IllegalStateException(); - hit++; - PatriciaTrie.this.removeEntry(entry); - } - - } - - /** - * An iterator for the entries. - */ - private abstract class NodeIterator implements Iterator { - protected int expectedModCount = modCount; // For fast-fail - protected TrieEntry next; // the next node to return - protected TrieEntry current; // the current entry we're on - - // Starts iteration from the beginning. - protected NodeIterator() { - next = PatriciaTrie.this.nextEntry(null); - } - - // Starts iteration at the given entry. - protected NodeIterator(TrieEntry firstEntry) { - next = firstEntry; - } - - public boolean hasNext() { - return next != null; - } - - TrieEntry nextEntry() { - if (modCount != expectedModCount) throw new ConcurrentModificationException(); - TrieEntry e = next; - if (e == null) throw new NoSuchElementException(); - - next = findNext(e); - current = e; - return e; - } - - protected TrieEntry findNext(TrieEntry prior) { - return PatriciaTrie.this.nextEntry(prior); - } - - public void remove() { - if (current == null) throw new IllegalStateException(); - if (modCount != expectedModCount) throw new ConcurrentModificationException(); - - TrieEntry node = current; - current = null; - PatriciaTrie.this.removeEntry(node); - - expectedModCount = modCount; - } - } - - private class ValueIterator extends NodeIterator { - public V next() { - return nextEntry().value; - } - } - - private class KeyIterator extends NodeIterator { - public K next() { - return nextEntry().getKey(); - } - } - - private class EntryIterator extends NodeIterator> { - public Map.Entry next() { - return nextEntry(); - } - } - - /** - * An iterator for iterating over a prefix search. - */ - private class PrefixEntryIterator extends NodeIterator> { - // values to reset the subtree if we remove it. - protected final K prefix; - protected final int offset; - protected final int length; - protected boolean lastOne; - - protected TrieEntry subtree; // the subtree to search within - - // Starts iteration at the given entry & search only within the given subtree. - PrefixEntryIterator(TrieEntry startScan, K prefix, int offset, int length) { - subtree = startScan; - next = PatriciaTrie.this.followLeft(startScan); - this.prefix = prefix; - this.offset = offset; - this.length = length; - } - - public Map.Entry next() { - Map.Entry entry = nextEntry(); - if (lastOne) next = null; - return entry; - } - - @Override - protected TrieEntry findNext(TrieEntry prior) { - return PatriciaTrie.this.nextEntryInSubtree(prior, subtree); - } - - @Override - public void remove() { - // If the current entry we're removing is the subtree - // then we need to find a new subtree parent. - boolean needsFixing = false; - int bitIdx = subtree.bitIndex; - if (current == subtree) needsFixing = true; - - super.remove(); - - // If the subtree changed its bitIndex or we - // removed the old subtree, get a new one. - if (bitIdx != subtree.bitIndex || needsFixing) subtree = subtree(prefix, offset, length); - - // If the subtree's bitIndex is less than the - // length of our prefix, it's the last item - // in the prefix tree. - if (length >= subtree.bitIndex) lastOne = true; - } - - } - - /** - * An iterator for submaps. - */ - private class SubMapEntryIterator extends NodeIterator> { - private final K firstExcludedKey; - - SubMapEntryIterator(TrieEntry first, TrieEntry firstExcluded) { - super(first); - firstExcludedKey = (firstExcluded == null ? null : firstExcluded.key); - } - - public boolean hasNext() { - return next != null && next.key != firstExcludedKey; - } - - public Map.Entry next() { - if (next == null || next.key == firstExcludedKey) throw new NoSuchElementException(); - return nextEntry(); - } - } - - Iterator newKeyIterator() { - return new KeyIterator(); - } - - Iterator newValueIterator() { - return new ValueIterator(); - } - - Iterator> newEntryIterator() { - return new EntryIterator(); - } - - /** - * Each of these fields are initialized to contain an instance of the - * appropriate view the first time this view is requested. The views are - * stateless, so there's no reason to create more than one of each. - */ - private transient volatile Set keySet = null; - private transient volatile Collection values = null; - private transient volatile Set> entrySet = null; - - private class EntrySet extends AbstractSet> { - public Iterator> iterator() { - return newEntryIterator(); - } - - public boolean contains(Object o) { - if (!(o instanceof Map.Entry)) return false; - - TrieEntry candidate = getEntry(((Map.Entry)o).getKey()); - return candidate != null && candidate.equals(o); - } - - public boolean remove(Object o) { - int size = size(); - PatriciaTrie.this.remove(o); - return size != size(); - } - - public int size() { - return size; - } - - public void clear() { - PatriciaTrie.this.clear(); - } - } - - /** - * Returns a set view of the keys contained in this map. The set is - * backed by the map, so changes to the map are reflected in the set, and - * vice-versa. The set supports element removal, which removes the - * corresponding mapping from this map, via the Iterator.remove, - * Set.remove, removeAll, retainAll, and - * clear operations. It does not support the add or - * addAll operations. - * - * @return a set view of the keys contained in this map. - */ - public Set keySet() { - Set ks = keySet; - return (ks != null ? ks : (keySet = new KeySet())); - } - - private class KeySet extends AbstractSet { - public Iterator iterator() { - return newKeyIterator(); - } - - public int size() { - return size; - } - - public boolean contains(Object o) { - return containsKey(o); - } - - public boolean remove(Object o) { - int size = size(); - PatriciaTrie.this.remove(o); - return size != size(); - } - - public void clear() { - PatriciaTrie.this.clear(); - } - } - - /** - * Returns a collection view of the values contained in this map. The - * collection is backed by the map, so changes to the map are reflected in - * the collection, and vice-versa. The collection supports element - * removal, which removes the corresponding mapping from this map, via the - * Iterator.remove, Collection.remove, - * removeAll, retainAll, and clear operations. - * It does not support the add or addAll operations. - * - * @return a collection view of the values contained in this map. - */ - public Collection values() { - Collection vs = values; - return (vs != null ? vs : (values = new Values())); - } - - /** - * Test two values for equality. Works with null values. - */ - private static boolean valEquals(Object o1, Object o2) { - return (o1 == null ? o2 == null : o1.equals(o2)); - } - - private class Values extends AbstractCollection { - public Iterator iterator() { - return newValueIterator(); - } - - public int size() { - return size; - } - - public boolean contains(Object o) { - return containsValue(o); - } - - public void clear() { - PatriciaTrie.this.clear(); - } - - public boolean remove(Object o) { - for (Iterator i = iterator(); i.hasNext();) { - V v = i.next(); - if (valEquals(v, o)) { - i.remove(); - return true; - } - } - return false; - } - } - - /** - * Returns the first entry the Trie is storing. - *

- * This is implemented by going always to the left until - * we encounter a valid uplink. That uplink is the first key. - */ - private TrieEntry firstEntry() { - // if Trie is empty, no first node. - if (isEmpty()) return null; - - return followLeft(root); - } - - /** - * Goes left through the tree until it finds a valid node. - */ - private TrieEntry followLeft(TrieEntry node) { - while (true) { - TrieEntry child = node.left; - // if we hit root and it didn't have a node, go right instead. - if (child.isEmpty()) child = node.right; - - if (child.bitIndex <= node.bitIndex) return child; - - node = child; - } - } - - /** - * Returns the last entry the Trie is storing. - *

- * This is implemented by going always to the right until - * we encounter a valid uplink. That uplink is the last key. - */ - private TrieEntry lastEntry() { - return followRight(root.left); - } - - /** - * Traverses down the right path until it finds an uplink. - */ - protected TrieEntry followRight(TrieEntry node) { - // if Trie is empty, no last entry. - if (node.right == null) return null; - - // Go as far right as possible, until we encounter an uplink. - while (node.right.bitIndex > node.bitIndex) node = node.right; - - return node.right; - } - - public K firstKey() { - return firstEntry().getKey(); - } - - public SortedMap headMap(K toKey) { - return new SubMap(null, toKey); - } - - public K lastKey() { - TrieEntry entry = lastEntry(); - if (entry != null) { - return entry.getKey(); - } - else { - return null; - } - } - - - public SortedMap subMap(K fromKey, K toKey) { - return new SubMap(fromKey, toKey); - } - - public SortedMap tailMap(K fromKey) { - return new SubMap(fromKey, null); - } - - /** - * Returns an entry strictly higher than the given key, - * or null if no such entry exists. - */ - protected TrieEntry higherEntry(K key) { - // TODO: Cleanup so that we don't actually have to add/remove from the - // tree. (We do it here because there are other well-defined - // functions to perform the search.) - int keyLength = length(key); - - if (keyLength == 0) { - if (!root.isEmpty()) { - // If data in root, and more after -- return it. - if (size() > 1) { - return nextEntry(root); - } - else { // If no more after, no higher entry. - return null; - } - } - else { - // Root is empty & we want something after empty, return first. - return firstEntry(); - } - } - - TrieEntry found = getNearestEntryForKey(key, keyLength); - if (key.equals(found.key)) return nextEntry(found); - - int bitIndex = bitIndex(key, found.key); - if (isValidBitIndex(bitIndex)) { - TrieEntry added = new TrieEntry(key, null, bitIndex); - addEntry(added, keyLength); - incrementSize(); // must increment because remove will decrement - TrieEntry ceil = nextEntry(added); - removeEntry(added); - modCount -= 2; // we didn't really modify it. - return ceil; - } - else if (isNullBitKey(bitIndex)) { - if (!root.isEmpty()) { - return firstEntry(); - } - else if (size() > 1) { - return nextEntry(firstEntry()); - } - else { - return null; - } - } - else if (isEqualBitKey(bitIndex)) { - return nextEntry(found); - } - - // we should have exited above. - throw new IllegalStateException("invalid lookup: " + key); - } - - /** - * Returns a key-value mapping associated with the least key greater - * than or equal to the given key, or null if there is no such key. - */ - protected TrieEntry ceilingEntry(K key) { - // Basically: - // Follow the steps of adding an entry, but instead... - // - // - If we ever encounter a situation where we found an equal - // key, we return it immediately. - // - // - If we hit an empty root, return the first iterable item. - // - // - If we have to add a new item, we temporarily add it, - // find the successor to it, then remove the added item. - // - // These steps ensure that the returned value is either the - // entry for the key itself, or the first entry directly after - // the key. - - // TODO: Cleanup so that we don't actually have to add/remove from the - // tree. (We do it here because there are other well-defined - // functions to perform the search.) - int keyLength = length(key); - - if (keyLength == 0) { - if (!root.isEmpty()) { - return root; - } - else { - return firstEntry(); - } - } - - TrieEntry found = getNearestEntryForKey(key, keyLength); - if (key.equals(found.key)) return found; - - int bitIndex = bitIndex(key, found.key); - if (isValidBitIndex(bitIndex)) { - TrieEntry added = new TrieEntry(key, null, bitIndex); - addEntry(added, keyLength); - incrementSize(); // must increment because remove will decrement - TrieEntry ceil = nextEntry(added); - removeEntry(added); - modCount -= 2; // we didn't really modify it. - return ceil; - } - else if (isNullBitKey(bitIndex)) { - if (!root.isEmpty()) { - return root; - } - else { - return firstEntry(); - } - } - else if (isEqualBitKey(bitIndex)) { - return found; - } - - // we should have exited above. - throw new IllegalStateException("invalid lookup: " + key); - } - - /** - * Returns a key-value mapping associated with the greatest key - * strictly less than the given key, or null if there is no such key. - */ - protected TrieEntry lowerEntry(K key) { - // Basically: - // Follow the steps of adding an entry, but instead... - // - // - If we ever encounter a situation where we found an equal - // key, we return it's previousEntry immediately. - // - // - If we hit root (empty or not), return null. - // - // - If we have to add a new item, we temporarily add it, - // find the previousEntry to it, then remove the added item. - // - // These steps ensure that the returned value is always just before - // the key or null (if there was nothing before it). - - // TODO: Cleanup so that we don't actually have to add/remove from the - // tree. (We do it here because there are other well-defined - // functions to perform the search.) - int keyLength = length(key); - - if (keyLength == 0) { - return null; // there can never be anything before root. - } - - TrieEntry found = getNearestEntryForKey(key, keyLength); - if (key.equals(found.key)) return previousEntry(found); - - int bitIndex = bitIndex(key, found.key); - if (isValidBitIndex(bitIndex)) { - TrieEntry added = new TrieEntry(key, null, bitIndex); - addEntry(added, keyLength); - incrementSize(); // must increment because remove will decrement - TrieEntry prior = previousEntry(added); - removeEntry(added); - modCount -= 2; // we didn't really modify it. - return prior; - } - else if (isNullBitKey(bitIndex)) { - return null; - } - else if (isEqualBitKey(bitIndex)) { - return previousEntry(found); - } - - // we should have exited above. - throw new IllegalStateException("invalid lookup: " + key); - } - - /** - * Returns a key-value mapping associated with the greatest key - * less than or equal to the given key, or null if there is no such key. - */ - protected TrieEntry floorEntry(K key) { - // TODO: Cleanup so that we don't actually have to add/remove from the - // tree. (We do it here because there are other well-defined - // functions to perform the search.) - int keyLength = length(key); - - if (keyLength == 0) { - if (!root.isEmpty()) { - return root; - } - else { - return null; - } - } - - TrieEntry found = getNearestEntryForKey(key, keyLength); - if (key.equals(found.key)) return found; - - int bitIndex = bitIndex(key, found.key); - if (isValidBitIndex(bitIndex)) { - TrieEntry added = new TrieEntry(key, null, bitIndex); - addEntry(added, keyLength); - incrementSize(); // must increment because remove will decrement - TrieEntry floor = previousEntry(added); - removeEntry(added); - modCount -= 2; // we didn't really modify it. - return floor; - } - else if (isNullBitKey(bitIndex)) { - if (!root.isEmpty()) { - return root; - } - else { - return null; - } - } - else if (isEqualBitKey(bitIndex)) { - return found; - } - - // we should have exited above. - throw new IllegalStateException("invalid lookup: " + key); - } - - - /** - * Finds the subtree that contains the prefix. - *

- * This is very similar to getR but with the difference that - * we stop the lookup if h.bitIndex > keyLength. - */ - private TrieEntry subtree(K prefix, int offset, int length) { - TrieEntry current = root.left; - TrieEntry path = root; - while (true) { - if (current.bitIndex <= path.bitIndex || length < current.bitIndex) break; - - path = current; - if (!isBitSet(prefix, length + offset, current.bitIndex + offset)) { - current = current.left; - } - else { - current = current.right; - } - } - - // Make sure the entry is valid for a subtree. - TrieEntry entry = current.isEmpty() ? path : current; - - // If entry is root, it can't be empty. - if (entry.isEmpty()) return null; - - int offsetLength = offset + length; - - // if root && length of root is less than length of lookup, - // there's nothing. - // (this prevents returning the whole subtree if root has an empty - // string and we want to lookup things with "\0") - if (entry == root && length(entry.getKey()) < offsetLength) return null; - - // Found key's length-th bit differs from our key - // which means it cannot be the prefix... - if (isBitSet(prefix, offsetLength, offsetLength) != isBitSet(entry.key, length(entry.key), length)) { - return null; - } - - // ... or there are less than 'length' equal bits - int bitIndex = keyAnalyzer.bitIndex(prefix, offset, length, entry.key, 0, length(entry.getKey())); - if (bitIndex >= 0 && bitIndex < length) return null; - - return entry; - } - - /** - * A submap used for prefix views over the Trie. - */ - private class PrefixSubMap extends SubMap { - protected final K prefix; - protected final int offset; - protected final int length; - private transient int keyModCount = 0; - protected int size; - - PrefixSubMap(K prefix, int offset, int length) { - this.prefix = prefix; - this.offset = offset; - this.length = length; - fromInclusive = false; - } - - public K firstKey() { - fixup(); - TrieEntry e; - if (fromKey == null) { - e = firstEntry(); - } - else { - e = higherEntry(fromKey); - } - - K first = e != null ? e.getKey() : null; - if (e == null || !keyAnalyzer.isPrefix(prefix, offset, length, first)) throw new NoSuchElementException(); - return first; - } - - public K lastKey() { - fixup(); - TrieEntry e; - if (toKey == null) { - e = lastEntry(); - } - else { - e = lowerEntry(toKey); - } - - K last = e != null ? e.getKey() : null; - if (e == null || !keyAnalyzer.isPrefix(prefix, offset, length, last)) throw new NoSuchElementException(); - return last; - } - - protected boolean inRange(K key) { - return keyAnalyzer.isPrefix(prefix, offset, length, key); - } - - protected boolean inRange2(K key) { - return keyAnalyzer.isPrefix(prefix, offset, length, key); - } - - protected boolean inToRange(K key, boolean forceInclusive) { - return keyAnalyzer.isPrefix(prefix, offset, length, key); - } - - protected boolean inFromRange(K key, boolean forceInclusive) { - return keyAnalyzer.isPrefix(prefix, offset, length, key); - } - - private void fixup() { - // The trie has changed since we last - // found our toKey / fromKey - if (modCount != keyModCount) { - Iterator> iter = entrySet().iterator(); - size = 0; - - Map.Entry entry = null; - if (iter.hasNext()) { - entry = iter.next(); - size = 1; - } - - fromKey = entry == null ? null : entry.getKey(); - if (fromKey != null) { - TrieEntry prior = previousEntry((TrieEntry)entry); - fromKey = prior == null ? null : prior.getKey(); - } - - toKey = fromKey; - - while (iter.hasNext()) { - size++; - entry = iter.next(); - } - - toKey = entry == null ? null : entry.getKey(); - - if (toKey != null) { - entry = nextEntry((TrieEntry)entry); - toKey = entry == null ? null : entry.getKey(); - } - - keyModCount = modCount; - } - } - - protected Set> newSubMapEntrySet() { - return new PrefixEntrySetView(); - } - - private class PrefixEntrySetView extends SubMap.EntrySetView { - private TrieEntry prefixStart; - private int iterModCount = 0; - - public int size() { - fixup(); - return PrefixSubMap.this.size; - } - - @SuppressWarnings("unchecked") - public Iterator> iterator() { - if (modCount != iterModCount) { - prefixStart = subtree(prefix, offset, length); - iterModCount = modCount; - } - - if (prefixStart == null) { - return new EmptyIterator(); - } - else if (length >= prefixStart.bitIndex) { - return new SingletonIterator(prefixStart); - } - else { - return new PrefixEntryIterator(prefixStart, prefix, offset, length); - } - } - } - } - - private class SubMap extends AbstractMap implements SortedMap, java.io.Serializable { - - // TODO: add serialVersionUID - - /** - * The key to start from, null if the beginning. - */ - protected K fromKey; - - /** - * The key to end at, null if till the end. - */ - protected K toKey; - - /** - * Whether or not the 'from' is inclusive. - */ - protected boolean fromInclusive; - - /** - * Whether or not the 'to' is inclusive. - */ - protected boolean toInclusive; - - /** - * Constructs a blank SubMap -- this should ONLY be used - * by subclasses that wish to lazily construct their - * fromKey or toKey - */ - protected SubMap() { - } - - SubMap(K fromKey, K toKey) { - if (fromKey == null && toKey == null) throw new IllegalArgumentException("must have a from or to!"); - if (fromKey != null && toKey != null && keyAnalyzer.compare(fromKey, toKey) > 0) { - throw new IllegalArgumentException("fromKey > toKey"); - } - this.fromKey = fromKey; - this.toKey = toKey; - fromInclusive = true; - } - - public boolean isEmpty() { - return entrySet().isEmpty(); - } - - @SuppressWarnings("unchecked") - public boolean containsKey(Object key) { - return inRange((K)key) && PatriciaTrie.this.containsKey(key); - } - - @SuppressWarnings("unchecked") - public V remove(Object key) { - if (!inRange((K)key)) return null; - return PatriciaTrie.this.remove(key); - } - - @SuppressWarnings("unchecked") - public V get(Object key) { - if (!inRange((K)key)) return null; - return PatriciaTrie.this.get(key); - } - - public V put(K key, V value) { - if (!inRange(key)) throw new IllegalArgumentException("key out of range"); - return PatriciaTrie.this.put(key, value); - } - - public Comparator comparator() { - return keyAnalyzer; - } - - public K firstKey() { - TrieEntry e; - if (fromKey == null) { - e = firstEntry(); - } - else { - if (fromInclusive) { - e = ceilingEntry(fromKey); - } - else { - e = higherEntry(fromKey); - } - } - - K first = e != null ? e.getKey() : null; - if (e == null || toKey != null && !inToRange(first, false)) throw new NoSuchElementException(); - return first; - } - - public K lastKey() { - TrieEntry e; - if (toKey == null) { - e = lastEntry(); - } - else { - if (toInclusive) { - e = floorEntry(toKey); - } - else { - e = lowerEntry(toKey); - } - } - - K last = e != null ? e.getKey() : null; - if (e == null || fromKey != null && !inFromRange(last, false)) throw new NoSuchElementException(); - return last; - } - - private transient Set> entrySet; - - public Set> entrySet() { - if (entrySet == null) entrySet = newSubMapEntrySet(); - return entrySet; - } - - protected Set> newSubMapEntrySet() { - return new EntrySetView(); - } - - class EntrySetView extends AbstractSet> { - private transient int size = -1, sizeModCount; - - public int size() { - if (size == -1 || sizeModCount != PatriciaTrie.this.modCount) { - size = 0; - sizeModCount = PatriciaTrie.this.modCount; - Iterator i = iterator(); - while (i.hasNext()) { - size++; - i.next(); - } - } - return size; - } - - public boolean isEmpty() { - return !iterator().hasNext(); - } - - @SuppressWarnings("unchecked") - public boolean contains(Object o) { - if (!(o instanceof Map.Entry)) return false; - Map.Entry entry = (Map.Entry)o; - K key = entry.getKey(); - if (!inRange(key)) return false; - TrieEntry node = getEntry(key); - return node != null && valEquals(node.getValue(), entry.getValue()); - } - - @SuppressWarnings("unchecked") - public boolean remove(Object o) { - if (!(o instanceof Map.Entry)) return false; - Map.Entry entry = (Map.Entry)o; - K key = entry.getKey(); - if (!inRange(key)) return false; - TrieEntry node = getEntry(key); - if (node != null && valEquals(node.getValue(), entry.getValue())) { - removeEntry(node); - return true; - } - return false; - } - - public Iterator> iterator() { - return new SubMapEntryIterator((fromKey == null ? firstEntry() : ceilingEntry(fromKey)), - (toKey == null ? null : ceilingEntry(toKey))); - } - } - - public SortedMap subMap(K fromKey, K toKey) { - if (!inRange2(fromKey)) throw new IllegalArgumentException("fromKey out of range"); - if (!inRange2(toKey)) throw new IllegalArgumentException("toKey out of range"); - return new SubMap(fromKey, toKey); - } - - public SortedMap headMap(K toKey) { - if (!inRange2(toKey)) throw new IllegalArgumentException("toKey out of range"); - return new SubMap(fromKey, toKey); - } - - public SortedMap tailMap(K fromKey) { - if (!inRange2(fromKey)) throw new IllegalArgumentException("fromKey out of range"); - return new SubMap(fromKey, toKey); - } - - protected boolean inRange(K key) { - return (fromKey == null || inFromRange(key, false)) && (toKey == null || inToRange(key, false)); - } - - // This form allows the high endpoint (as well as all legit keys) - protected boolean inRange2(K key) { - return (fromKey == null || inFromRange(key, false)) && (toKey == null || inToRange(key, true)); - } - - protected boolean inToRange(K key, boolean forceInclusive) { - int ret = keyAnalyzer.compare(key, toKey); - if (toInclusive || forceInclusive) { - return ret <= 0; - } - else { - return ret < 0; - } - } - - protected boolean inFromRange(K key, boolean forceInclusive) { - int ret = keyAnalyzer.compare(key, fromKey); - if (fromInclusive || forceInclusive) { - return ret >= 0; - } - else { - return ret > 0; - } - } - } - - private static class EmptyIterator implements Iterator { - public boolean hasNext() { - return false; - } - - public Object next() { - throw new NoSuchElementException(); - } - - public void remove() { - throw new IllegalStateException(); - } - - } -} - diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/trie/Trie.java b/plugins/spellchecker/src/com/intellij/spellchecker/trie/Trie.java deleted file mode 100644 index 1f76191d4a3d..000000000000 --- a/plugins/spellchecker/src/com/intellij/spellchecker/trie/Trie.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright 2005-2009 Roger Kapsi, Sam Berlin - * - * 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.spellchecker.trie; - - -import java.util.Map; -import java.util.SortedMap; - -public interface Trie extends SortedMap { - - /** - * Returns a view of this Trie of all elements that are - * prefixed by the given key. - *

- * In a fixed-keysize Trie, this is essentially a 'get' operation. - *

- * For example, if the Trie contains 'Lime', 'LimeWire', - * 'LimeRadio', 'Lax', 'Later', 'Lake', and 'Lovely', then - * a lookup of 'Lime' would return 'Lime', 'LimeRadio', and 'LimeWire'. - */ - public SortedMap getPrefixedBy(K key); - - /** - * Returns a view of this Trie of all elements that are - * prefixed by the length of the key. - *

- * Fixed-keysize Tries will not support this operation - * (because all keys will be the same length). - *

- * For example, if the Trie contains 'Lime', 'LimeWire', - * 'LimeRadio', 'Lax', 'Later', 'Lake', and 'Lovely', then - * a lookup of 'LimePlastics' with a length of 4 would - * return 'Lime', 'LimeRadio', and 'LimeWire'. - */ - public SortedMap getPrefixedBy(K key, int length); - - /** - * Returns a view of this Trie of all elements that are prefixed - * by the key, starting at the given offset and for the given length. - *

- * Fixed-keysize Tries will not support this operation - * (because all keys are the same length). - *

- * For example, if the Trie contains 'Lime', 'LimeWire', - * 'LimeRadio', 'Lax', 'Later', 'Lake', and 'Lovely', then - * a lookup of 'The Lime Plastics' with an offset of 4 and a - * length of 4 would return 'Lime', 'LimeRadio', and 'LimeWire'. - */ - public SortedMap getPrefixedBy(K key, int offset, int length); - - /** - * Returns a view of this Trie of all elements that are prefixed - * by the number of bits in the given Key. - *

- * Fixed-keysize Tries can support this operation as a way to do - * lookups of partial keys. That is, if the Trie is storing IP - * addresses, you can lookup all addresses that begin with - * '192.168' by providing the key '192.168.X.X' and a length of 16 - * would return all addresses that begin with '192.168'. - */ - public SortedMap getPrefixedByBits(K key, int bitLength); - - /** - * Returns the value for the entry whose key is closest in a bitwise - * XOR metric to the given key. This is NOT lexicographic closeness. - * For example, given the keys:
- * D = 1000100
- * H = 1001000
- * L = 1001100
- *

- * If the Trie contained 'H' and 'L', a lookup of 'D' would return 'L', - * because the XOR distance between D & L is smaller than the XOR distance - * between D & H. - */ - public V select(K key); - - /** - * Iterates through the Trie, starting with the entry whose bitwise - * value is closest in an XOR metric to the given key. After the closest - * entry is found, the Trie will call select on that entry and continue - * calling select for each entry (traversing in order of XOR closeness, - * NOT lexicographically) until the cursor returns - * Cursor.SelectStatus.EXIT.
- * The cursor can return Cursor.SelectStatus.CONTINUE to - * continue traversing.
- * Cursor.SelectStatus.REMOVE_AND_EXIT is used to remove the current element - * and stop traversing. - *

- * Note: The {@link Cursor.SelectStatus#REMOVE} operation is not supported. - * - * @return The entry the cursor returned EXIT on, or null if it continued - * till the end. - */ - public Map.Entry select(K key, Cursor cursor); - - /** - * Traverses the Trie in lexicographical order. Cursor.select - * will be called on each entry.

- * The traversal will stop when the cursor returns Cursor.SelectStatus.EXIT.
- * Cursor.SelectStatus.CONTINUE is used to continue traversing.
- * Cursor.SelectStatus.REMOVE is used to remove the element that was - * selected and continue traversing.
- * Cursor.SelectStatus.REMOVE_AND_EXIT is used to remove the current element - * and stop traversing. - * - * @return The entry the cursor returned EXIT on, or null if it continued - * till the end. - */ - public Map.Entry traverse(Cursor cursor); - - /** - * An interface used by a {@link Trie}. A {@link Trie} selects items by - * closeness and passes the items to the Cursor. You can then - * decide what to do with the key-value pair and the return value - * from {@link #select(java.util.Map.Entry)} tells the Trie - * what to do next. - *

- * Cursor returns status/selection status might be: - * - * - * - * - * - * - *
Return ValueStatus
EXITFinish the Trie operation
CONTINUELook at the next element in the traversal
REMOVE_AND_EXITRemove the entry and stop iterating
REMOVERemove the entry and continue iterating
- *

- * Note: {@link Trie#select(Object, org.limewire.collection.Trie.Cursor)} does - * not support REMOVE. - * - * @param Key Type - * @param Key Value - */ - public static interface Cursor { - - /** - * Notification that the Trie is currently looking at the given entry. - * Return EXIT to finish the Trie operation, - * CONTINUE to look at the next entry, REMOVE - * to remove the entry and continue iterating, or - * REMOVE_AND_EXIT to remove the entry and stop iterating. - * Not all operations support REMOVE. - */ - public SelectStatus select(Map.Entry entry); - - /** - * The mode during selection. - */ - public static enum SelectStatus { - EXIT, CONTINUE, REMOVE, REMOVE_AND_EXIT; - } - } -} - From 6fa5e31de3f4e12387356d54aa35004a12ba1869 Mon Sep 17 00:00:00 2001 From: nik Date: Fri, 12 Mar 2010 16:06:35 +0300 Subject: [PATCH 08/93] add util.jar to classpath only if our UrlClassLoader is used as system classloader --- .../src/com/intellij/openapi/projectRoots/JdkUtil.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/platform/lang-api/src/com/intellij/openapi/projectRoots/JdkUtil.java b/platform/lang-api/src/com/intellij/openapi/projectRoots/JdkUtil.java index c13746244d7e..da262d36699d 100644 --- a/platform/lang-api/src/com/intellij/openapi/projectRoots/JdkUtil.java +++ b/platform/lang-api/src/com/intellij/openapi/projectRoots/JdkUtil.java @@ -162,9 +162,14 @@ public class JdkUtil { writer.close(); } + String classpath = PathUtil.getJarPathForClass(commandLineWrapper); + final Class ourUrlClassLoader = UrlClassLoader.class; + if (ourUrlClassLoader.getName().equals(parametersList.getPropertyValue("java.system.class.loader"))) { + classpath += File.pathSeparator + PathUtil.getJarPathForClass(ourUrlClassLoader); + } + commandLine.addParameter("-classpath"); - commandLine.addParameter(PathUtil.getJarPathForClass(commandLineWrapper) + File.pathSeparator + - PathUtil.getJarPathForClass(UrlClassLoader.class)); + commandLine.addParameter(classpath); } catch (IOException e) { LOG.error(e); From dd4d2f3ee5c56e93df8477ba5db0f3591b742aa0 Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Fri, 12 Mar 2010 16:13:07 +0300 Subject: [PATCH 09/93] http://ea.jetbrains.com/browser/ea_problems/15737 : PIEAE: CompositePsiElement.getContainingFile --- .../com/intellij/psi/impl/source/codeStyle/ImportHelper.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/ImportHelper.java b/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/ImportHelper.java index 7168c8a5213b..aeca7ad64065 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/ImportHelper.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/ImportHelper.java @@ -671,7 +671,10 @@ public class ImportHelper{ PsiElement currentFileResolveScope = resolveResult.getCurrentFileResolveScope(); if (!(currentFileResolveScope instanceof PsiImportStatementBase)) continue; - if (context != null && currentFileResolveScope instanceof JspxImportStatement && context != ((JspxImportStatement)currentFileResolveScope).getDeclarationFile()) { + if (context != null && + (!currentFileResolveScope.isValid() || + currentFileResolveScope instanceof JspxImportStatement && + context != ((JspxImportStatement)currentFileResolveScope).getDeclarationFile())) { continue; } From b1d1f8de6773b1a897430ac589647aab7acc01cf Mon Sep 17 00:00:00 2001 From: Constantine Plotnikov Date: Thu, 11 Mar 2010 16:21:53 +0300 Subject: [PATCH 10/93] git4idea: If ssh script were removed from temporary directory, it is recreated when next ssh operation is run. --- .../git4idea/src/org/jetbrains/git4idea/ssh/GitSSHService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/git4idea/src/org/jetbrains/git4idea/ssh/GitSSHService.java b/plugins/git4idea/src/org/jetbrains/git4idea/ssh/GitSSHService.java index 3522b2fc96c2..db8584376b4d 100644 --- a/plugins/git4idea/src/org/jetbrains/git4idea/ssh/GitSSHService.java +++ b/plugins/git4idea/src/org/jetbrains/git4idea/ssh/GitSSHService.java @@ -115,7 +115,7 @@ public class GitSSHService implements ApplicationComponent { */ @NotNull public synchronized File getScriptPath() throws IOException { - if (myScriptPath == null) { + if (myScriptPath == null || !myScriptPath.exists()) { ScriptGenerator generator = new ScriptGenerator(GIT_SSH_PREFIX, SSHMain.class); generator.addInternal(Integer.toString(myXmlRpcServer.getPortNumber())); generator.addClasses(XmlRpcClientLite.class, DecoderException.class); From aba8665f92c06feaecf3b50319e3160cf3689d53 Mon Sep 17 00:00:00 2001 From: Constantine Plotnikov Date: Fri, 12 Mar 2010 17:35:32 +0300 Subject: [PATCH 11/93] git4idea: Fixed change collector to detect unmerged files --- plugins/git4idea/src/git4idea/changes/ChangeCollector.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/git4idea/src/git4idea/changes/ChangeCollector.java b/plugins/git4idea/src/git4idea/changes/ChangeCollector.java index 58f637e1fc6e..06dac8e4d58a 100644 --- a/plugins/git4idea/src/git4idea/changes/ChangeCollector.java +++ b/plugins/git4idea/src/git4idea/changes/ChangeCollector.java @@ -304,7 +304,7 @@ class ChangeCollector { if ('M' == status) { sc.boundedToken('\t'); String file = GitUtil.unescapePath(sc.line()); - VirtualFile vFile = myVcsRoot.findFileByRelativePath(GitUtil.unescapePath(sc.line())); + VirtualFile vFile = myVcsRoot.findFileByRelativePath(file); if (GitUtil.gitRootOrNull(vFile) != myVcsRoot) { continue; } From ee6f85b2a0ac56c9759a4581ae8a03fbfd4b4d3c Mon Sep 17 00:00:00 2001 From: Oleg Shpynov Date: Fri, 12 Mar 2010 18:29:17 +0300 Subject: [PATCH 12/93] Python reaching defs fix leading to unbound local variable inspection false positives --- .../controlflow/ControlFlowUtil.java | 7 ++++- .../codeInsight/dataflow/DFAEngine.java | 31 +++---------------- .../intellij/codeInsight/dataflow/DFAMap.java | 26 ++++++++++++++-- .../codeInsight/dataflow/DfaInstance.java | 2 ++ 4 files changed, 37 insertions(+), 29 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/controlflow/ControlFlowUtil.java b/platform/lang-impl/src/com/intellij/codeInsight/controlflow/ControlFlowUtil.java index 0d690cb36dc7..5860786b79e9 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/controlflow/ControlFlowUtil.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/controlflow/ControlFlowUtil.java @@ -56,6 +56,11 @@ public class ControlFlowUtil { public void clear() { myIndex = -1; } + + @Override + public String toString() { + return "Stack(" + (myIndex + 1) + ") elements"; + } } public static int[] postOrder(Instruction[] flow) { @@ -74,7 +79,7 @@ public class ControlFlowUtil { while (!stack.isEmpty()) { final int num = stack.pop(); - result[num] = N++; + result[N++] = num; for (Instruction succ : flow[num].allSucc()) { final int succNum = succ.num(); if (!visited[succNum]) { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/dataflow/DFAEngine.java b/platform/lang-impl/src/com/intellij/codeInsight/dataflow/DFAEngine.java index 94457d42c0ac..4e30944ef4ae 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/dataflow/DFAEngine.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/dataflow/DFAEngine.java @@ -23,7 +23,7 @@ import java.util.*; public class DFAEngine { private static final Logger LOG = Logger.getInstance(DFAEngine.class.getName()); - private static final double TIME_LIMIT = 5*10e9; + private static final double TIME_LIMIT = 3*10e9; private final Instruction[] myFlow; @@ -60,8 +60,6 @@ public class DFAEngine { final boolean forward = myDfa.isForward(); final int[] order = ControlFlowUtil.postOrder(myFlow); -// Count limit for number of iterations per worklist - final int limit = getIterationLimit(forward); int dfaCount = 0; final long startTime = System.nanoTime(); @@ -76,8 +74,6 @@ public class DFAEngine { break; } - // Iteration count per one worklist - int count = 0; final Instruction instruction = myFlow[order[i]]; final int number = instruction.num(); @@ -87,13 +83,7 @@ public class DFAEngine { visited[number] = true; while (true) { - count++; - if (count > limit){ - if (LOG.isDebugEnabled()){ - LOG.debug("Iteration count exceeded on worklist"); - } - break; - } + dfaCount++; final Instruction currentInstruction = worklist.poll(); if (currentInstruction == null) { @@ -105,6 +95,9 @@ public class DFAEngine { final DFAMap joinedE = join(currentInstruction, info); final DFAMap newE = myDfa.fun(joinedE, currentInstruction); if (!mySemilattice.eq(newE, oldE)) { + if (LOG.isDebugEnabled()){ + LOG.debug("Number: " + currentNumber + " old: " + oldE.keySet() + " new: " + newE.keySet()); + } info.set(currentNumber, newE); for (Instruction next : getNext(currentInstruction)) { worklist.add(next); @@ -120,7 +113,6 @@ public class DFAEngine { } else { i--; } - dfaCount += count; } if (LOG.isDebugEnabled()){ LOG.debug("Done in: " + (System.nanoTime() - startTime)/10e6 + "ms. Ratio: " + dfaCount / myFlow.length); @@ -129,19 +121,6 @@ public class DFAEngine { } - /** - * Count limit for dfa number of iterations. - * Every node in dfa should be processed <= pred times * 2 - * Multiplier 2 is because of cycles. - */ - private int getIterationLimit(final boolean forward) { - int allPred = myFlow.length; - for (Instruction instruction : myFlow) { - allPred += forward ? instruction.allPred().size() : instruction.allSucc().size(); - } - return allPred * 2; - } - private DFAMap join(final Instruction instruction, final List> info) { final Iterable prev = myDfa.isForward() ? instruction.allPred() : instruction.allSucc(); final ArrayList> prevInfos = new ArrayList>(); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/dataflow/DFAMap.java b/platform/lang-impl/src/com/intellij/codeInsight/dataflow/DFAMap.java index bb57567e16e6..148a5942c3ec 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/dataflow/DFAMap.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/dataflow/DFAMap.java @@ -37,7 +37,7 @@ public class DFAMap { public DFAMap() { } - public DFAMap(DFAMap initialMap) { + private DFAMap(DFAMap initialMap) { myK = initialMap.myK; myV = initialMap.myV; myAll = initialMap.myAll == null ? null : new HashMap(initialMap.myAll); @@ -71,6 +71,7 @@ public class DFAMap { } } + @Nullable public V get(String key) { if (myAll != null) { return myAll.get(key); @@ -166,6 +167,27 @@ public class DFAMap { } public DFAMap asWritable() { - return this; + return new DFAMap(this); + } + + @Override + public String toString() { + if (this == ourEmptyMap){ + return "Empty Map"; + } + if (myAll != null){ + return myAll.toString(); + } + if (myK != null){ + return "{" + myK + "=" + myV + "}"; + } + return "Empty"; + } + + public Set keySet() { + if (myAll != null){ + return myAll.keySet(); + } + return myK != null ? Collections.singleton(myK) : Collections.emptySet(); } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/dataflow/DfaInstance.java b/platform/lang-impl/src/com/intellij/codeInsight/dataflow/DfaInstance.java index 716ec6989374..ce618834fe4b 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/dataflow/DfaInstance.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/dataflow/DfaInstance.java @@ -18,6 +18,8 @@ import com.intellij.codeInsight.controlflow.Instruction; import org.jetbrains.annotations.NotNull; public interface DfaInstance { + // Please ensure that E has correctly implemented equals method + // Invariant: fun must create new instance of DFAMap if modifies it DFAMap fun(DFAMap e, Instruction instruction); From cb44ca7d98b56e3753692956ca2189dc35c8881c Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Fri, 12 Mar 2010 18:18:38 +0300 Subject: [PATCH 13/93] show "val$..." synthetic fields on the same level as local vars --- .../debugger/ui/impl/watch/DebuggerTree.java | 15 +++++++++++++++ .../ui/impl/watch/FieldDescriptorImpl.java | 19 +++++++++++++++---- .../ui/tree/render/ClassRenderer.java | 13 +++++++------ 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java index c2a42eb2e3fb..081471a8ab9b 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java @@ -50,6 +50,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.SpeedSearchBase; import com.intellij.ui.TreeSpeedSearch; import com.sun.jdi.*; @@ -500,6 +501,20 @@ public abstract class DebuggerTree extends DebuggerTreeBase implements DataProvi descriptor = myNodeManager.getStaticDescriptor(stackDescriptor, type); } myChildren.add(myNodeManager.createNode(descriptor, evaluationContext)); + + if (thisObjectReference != null && evaluationContext.getDebugProcess().getVirtualMachineProxy().canGetSyntheticAttribute()) { + final ReferenceType thisRefType = thisObjectReference.referenceType(); + if (thisRefType instanceof ClassType) { + final ClassType clsType = (ClassType)thisRefType; + for (Field field : clsType.fields()) { + if (field.isSynthetic() && StringUtil.startsWith(field.name(), FieldDescriptorImpl.OUTER_LOCAL_VAR_FIELD_PREFIX)) { + final FieldDescriptorImpl fieldDescriptor = myNodeManager.getFieldDescriptor(stackDescriptor, thisObjectReference, field); + myChildren.add(myNodeManager.createNode(fieldDescriptor, evaluationContext)); + } + } + } + } + try { buildVariables(stackDescriptor, evaluationContext); if (NodeRendererSettings.getInstance().getClassRenderer().SORT_ASCENDING) { diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/FieldDescriptorImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/FieldDescriptorImpl.java index 521d30ba6ae8..12911d980dbb 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/FieldDescriptorImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/FieldDescriptorImpl.java @@ -39,6 +39,7 @@ import com.sun.jdi.*; import org.jetbrains.annotations.NotNull; public class FieldDescriptorImpl extends ValueDescriptorImpl implements FieldDescriptor{ + public static final String OUTER_LOCAL_VAR_FIELD_PREFIX = "val$"; private final Field myField; private final ObjectReference myObject; private Boolean myIsPrimitive = null; @@ -66,7 +67,7 @@ public class FieldDescriptorImpl extends ValueDescriptorImpl implements FieldDes final ReferenceType type = myField.declaringType(); final JavaPsiFacade facade = JavaPsiFacade.getInstance(project); final String fieldName = myField.name(); - if (fieldName.startsWith("val$")) { + if (fieldName.startsWith(OUTER_LOCAL_VAR_FIELD_PREFIX)) { // this field actually mirrors a local variable in the outer class String varName = fieldName.substring(fieldName.lastIndexOf('$') + 1); PsiElement element = PositionUtil.getContextElement(context); @@ -137,7 +138,17 @@ public class FieldDescriptorImpl extends ValueDescriptorImpl implements FieldDes } public String getName() { - return myField.name(); + final String fieldName = myField.name(); + return isOuterLocalVariableValue()? fieldName.substring(OUTER_LOCAL_VAR_FIELD_PREFIX.length()) : fieldName; + } + + public boolean isOuterLocalVariableValue() { + try { + return DebuggerUtils.isSynthetic(myField) && myField.name().startsWith(OUTER_LOCAL_VAR_FIELD_PREFIX); + } + catch (UnsupportedOperationException e) { + return false; + } } public String calcValueName() { @@ -166,10 +177,10 @@ public class FieldDescriptorImpl extends ValueDescriptorImpl implements FieldDes } else { //noinspection HardCodedStringLiteral - fieldName = "this." + getName(); + fieldName = isOuterLocalVariableValue()? getName() : "this." + getName(); } try { - return (PsiReferenceExpression)elementFactory.createExpressionFromText(fieldName, null); + return elementFactory.createExpressionFromText(fieldName, null); } catch (IncorrectOperationException e) { throw new EvaluateException(DebuggerBundle.message("error.invalid.field.name", getName()), e); diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ClassRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ClassRenderer.java index 0c33df0bf32a..36b3a41f4b9e 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ClassRenderer.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ClassRenderer.java @@ -21,6 +21,7 @@ import com.intellij.debugger.engine.DebuggerManagerThreadImpl; import com.intellij.debugger.engine.DebuggerUtils; import com.intellij.debugger.engine.evaluation.EvaluateException; import com.intellij.debugger.engine.evaluation.EvaluationContext; +import com.intellij.debugger.ui.impl.watch.FieldDescriptorImpl; import com.intellij.debugger.ui.impl.watch.MessageDescriptor; import com.intellij.debugger.ui.impl.watch.NodeManagerImpl; import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl; @@ -29,6 +30,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.DefaultJDOMExternalizer; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.WriteExternalException; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.JavaPsiFacade; import com.intellij.psi.PsiElementFactory; import com.intellij.psi.PsiExpression; @@ -162,15 +164,14 @@ public class ClassRenderer extends NodeRendererImpl{ builder.setChildren(children); } - private boolean shouldDisplay(TypeComponent component) { - if (!SHOW_SYNTHETICS && DebuggerUtils.isSynthetic(component)) { + private boolean shouldDisplay(Field field) { + final boolean isSynthetic = DebuggerUtils.isSynthetic(field); + if (!SHOW_SYNTHETICS && isSynthetic) { return false; } - if (!(component instanceof Field)) { - return true; + if (isSynthetic && StringUtil.startsWith(field.name(), FieldDescriptorImpl.OUTER_LOCAL_VAR_FIELD_PREFIX)) { + return false; } - final Field field = (Field)component; - if(!SHOW_STATIC && field.isStatic()) { return false; } From f77e01fbb02255077e3a1a63500f10bbea6e72db Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Fri, 12 Mar 2010 18:28:41 +0300 Subject: [PATCH 14/93] optimization --- .../src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java index 081471a8ab9b..82dcc22eee34 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java @@ -504,7 +504,7 @@ public abstract class DebuggerTree extends DebuggerTreeBase implements DataProvi if (thisObjectReference != null && evaluationContext.getDebugProcess().getVirtualMachineProxy().canGetSyntheticAttribute()) { final ReferenceType thisRefType = thisObjectReference.referenceType(); - if (thisRefType instanceof ClassType) { + if (thisRefType instanceof ClassType && thisRefType.name().contains("$")) { // makes sense for nested classes only final ClassType clsType = (ClassType)thisRefType; for (Field field : clsType.fields()) { if (field.isSynthetic() && StringUtil.startsWith(field.name(), FieldDescriptorImpl.OUTER_LOCAL_VAR_FIELD_PREFIX)) { From 0f8acf777ac4b4feddae94ebbe3e52a8d6cc6318 Mon Sep 17 00:00:00 2001 From: Oleg Shpynov Date: Fri, 12 Mar 2010 18:58:48 +0300 Subject: [PATCH 15/93] Restore DFA instruction count limit --- .../codeInsight/dataflow/DFAEngine.java | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/dataflow/DFAEngine.java b/platform/lang-impl/src/com/intellij/codeInsight/dataflow/DFAEngine.java index 4e30944ef4ae..65908b8d7466 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/dataflow/DFAEngine.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/dataflow/DFAEngine.java @@ -60,6 +60,8 @@ public class DFAEngine { final boolean forward = myDfa.isForward(); final int[] order = ControlFlowUtil.postOrder(myFlow); +// Count limit for number of iterations per worklist + final int limit = getIterationLimit(forward); int dfaCount = 0; final long startTime = System.nanoTime(); @@ -74,6 +76,8 @@ public class DFAEngine { break; } + // Iteration count per one worklist + int count = 0; final Instruction instruction = myFlow[order[i]]; final int number = instruction.num(); @@ -82,8 +86,17 @@ public class DFAEngine { worklist.add(instruction); visited[number] = true; + // It is essential to apply this check!!! + // This gives us more chances that resulting info will be closer to expected result + // Also it is used as indicator that "equals" method is implemented correctly in E while (true) { - dfaCount++; + count++; + if (count > limit){ + if (LOG.isDebugEnabled()){ + LOG.debug("Iteration count exceeded on worklist"); + } + break; + } final Instruction currentInstruction = worklist.poll(); if (currentInstruction == null) { @@ -113,6 +126,7 @@ public class DFAEngine { } else { i--; } + dfaCount += count; } if (LOG.isDebugEnabled()){ LOG.debug("Done in: " + (System.nanoTime() - startTime)/10e6 + "ms. Ratio: " + dfaCount / myFlow.length); @@ -121,6 +135,19 @@ public class DFAEngine { } + /** + * Count limit for dfa number of iterations. + * Every node in dfa should be processed <= pred times * 2 + * Multiplier 2 is because of cycles. + */ + private int getIterationLimit(final boolean forward) { + int allPred = myFlow.length; + for (Instruction instruction : myFlow) { + allPred += forward ? instruction.allPred().size() : instruction.allSucc().size(); + } + return allPred * 2; + } + private DFAMap join(final Instruction instruction, final List> info) { final Iterable prev = myDfa.isForward() ? instruction.allPred() : instruction.allSucc(); final ArrayList> prevInfos = new ArrayList>(); From 7b2b170bc005279dd6fafdcc12ec9d09fbef7c28 Mon Sep 17 00:00:00 2001 From: Anton Makeev Date: Fri, 12 Mar 2010 17:12:22 +0300 Subject: [PATCH 16/93] Maven: toolwindow toolbar cosmetics --- .../MavenProjectsNavigatorPanel.java | 9 ++-- .../actions/EditTemplateSettingsAction.java | 26 ---------- .../src/main/resources/META-INF/plugin.xml | 51 ++++++++----------- 3 files changed, 25 insertions(+), 61 deletions(-) delete mode 100644 plugins/maven/src/main/java/org/jetbrains/idea/maven/project/actions/EditTemplateSettingsAction.java diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/navigator/MavenProjectsNavigatorPanel.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/navigator/MavenProjectsNavigatorPanel.java index 8266dba43e00..a427248ae7a7 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/navigator/MavenProjectsNavigatorPanel.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/navigator/MavenProjectsNavigatorPanel.java @@ -56,13 +56,12 @@ public class MavenProjectsNavigatorPanel extends SimpleToolWindowPanel implement myTree = tree; final ActionManager actionManager = ActionManager.getInstance(); - ActionToolbar actionToolbar = actionManager.createActionToolbar("New Maven Toolbar", - (ActionGroup)actionManager.getAction("Maven.NavigatorToolbar"), + ActionToolbar actionToolbar = actionManager.createActionToolbar("Maven Navigator Toolbar", + (DefaultActionGroup)actionManager.getAction("Maven.NavigatorActionsToolbar"), true); - actionToolbar.setTargetComponent(tree); - JComponent toolbar = actionToolbar.getComponent(); - setToolbar(toolbar); + actionToolbar.setTargetComponent(tree); + setToolbar(actionToolbar.getComponent()); setContent(new JScrollPane(myTree)); myTree.addMouseListener(new PopupHandler() { diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/actions/EditTemplateSettingsAction.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/actions/EditTemplateSettingsAction.java deleted file mode 100644 index 5586e3aefa73..000000000000 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/actions/EditTemplateSettingsAction.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2000-2009 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 org.jetbrains.idea.maven.project.actions; - -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.project.ProjectManager; - -public class EditTemplateSettingsAction extends EditSettingsAction { - @Override - public void actionPerformed(AnActionEvent e) { - showSettingsFor(ProjectManager.getInstance().getDefaultProject()); - } -} \ No newline at end of file diff --git a/plugins/maven/src/main/resources/META-INF/plugin.xml b/plugins/maven/src/main/resources/META-INF/plugin.xml index a65c7e76773d..65f3f5ead48d 100644 --- a/plugins/maven/src/main/resources/META-INF/plugin.xml +++ b/plugins/maven/src/main/resources/META-INF/plugin.xml @@ -233,12 +233,6 @@ icon="/images/mavenSettings.png"> - - - - - - - - - + description="Assign shortcutto selected phase/goal"> @@ -296,7 +272,7 @@ - + @@ -308,13 +284,28 @@ - - - - + + + + From cfd08e439037bc456cb6918e50d6dd8562a27ae6 Mon Sep 17 00:00:00 2001 From: Anton Makeev Date: Fri, 12 Mar 2010 18:25:22 +0300 Subject: [PATCH 17/93] Maven: potential race condition fix --- .../maven/project/MavenProjectsManager.java | 56 ++++++++++--------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsManager.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsManager.java index 22b401b455a1..f73ccb86389c 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsManager.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsManager.java @@ -170,23 +170,25 @@ public class MavenProjectsManager extends SimpleProjectComponent } private void doInit(final boolean isNew) { - if (isInitialized.getAndSet(true)) return; + synchronized (isInitialized) { + if (isInitialized.getAndSet(true)) return; - initProjectsTree(!isNew); + initProjectsTree(!isNew); - initWorkers(); - listenForSettingsChanges(); - listenForProjectsTreeChanges(); + initWorkers(); + listenForSettingsChanges(); + listenForProjectsTreeChanges(); - MavenUtil.runWhenInitialized(myProject, new DumbAwareRunnable() { - public void run() { - if (!isUnitTestMode()) { - fireActivated(); - listenForExternalChanges(); + MavenUtil.runWhenInitialized(myProject, new DumbAwareRunnable() { + public void run() { + if (!isUnitTestMode()) { + fireActivated(); + listenForExternalChanges(); + } + scheduleUpdateAllProjects(isNew); } - scheduleUpdateAllProjects(isNew); - } - }); + }); + } } private void initProjectsTree(boolean tryToLoadExisting) { @@ -380,24 +382,26 @@ public class MavenProjectsManager extends SimpleProjectComponent @Override public void projectClosed() { - if (!isInitialized.getAndSet(false)) return; + synchronized (isInitialized) { + if (!isInitialized.getAndSet(false)) return; - Disposer.dispose(mySchedulesQueue); - Disposer.dispose(myImportingQueue); + Disposer.dispose(mySchedulesQueue); + Disposer.dispose(myImportingQueue); - myWatcher.stop(); + myWatcher.stop(); - myReadingProcessor.stop(); - myResolvingProcessor.stop(); - myPluginsResolvingProcessor.stop(); - myFoldersResolvingProcessor.stop(); - myArtifactsDownloadingProcessor.stop(); - myPostProcessor.stop(); + myReadingProcessor.stop(); + myResolvingProcessor.stop(); + myPluginsResolvingProcessor.stop(); + myFoldersResolvingProcessor.stop(); + myArtifactsDownloadingProcessor.stop(); + myPostProcessor.stop(); - myEmbeddersManager.release(); + myEmbeddersManager.release(); - if (isUnitTestMode()) { - FileUtil.delete(getProjectsTreesDir()); + if (isUnitTestMode()) { + FileUtil.delete(getProjectsTreesDir()); + } } } From 2bab41e7a5a266d90384995c92ecd2a4c0f10866 Mon Sep 17 00:00:00 2001 From: Anton Makeev Date: Fri, 12 Mar 2010 18:53:59 +0300 Subject: [PATCH 18/93] Maven: ivalid VirtualFile access fix + correctly registering postStartupActivity --- .../maven/navigator/MavenProjectsNavigatorPanel.java | 12 ++---------- .../org/jetbrains/idea/maven/utils/MavenUtil.java | 3 ++- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/navigator/MavenProjectsNavigatorPanel.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/navigator/MavenProjectsNavigatorPanel.java index a427248ae7a7..ea966e527f33 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/navigator/MavenProjectsNavigatorPanel.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/navigator/MavenProjectsNavigatorPanel.java @@ -113,18 +113,10 @@ public class MavenProjectsNavigatorPanel extends SimpleToolWindowPanel implement return null; } - private List extractProjectNodes() { - List result = new ArrayList(); - for (MavenProjectsStructure.ProjectNode each : getSelectedProjectNodes()) { - result.add(each.getMavenProject()); - } - return result.isEmpty() ? null : result; - } - private VirtualFile extractVirtualFile() { for (MavenProjectsStructure.MavenSimpleNode each : getSelectedNodes(MavenProjectsStructure.MavenSimpleNode.class)) { VirtualFile file = each.getVirtualFile(); - if (file != null) return file; + if (file != null && file.isValid()) return file; } final MavenProjectsStructure.ProjectNode projectNode = getContextProjectNode(); @@ -138,7 +130,7 @@ public class MavenProjectsNavigatorPanel extends SimpleToolWindowPanel implement final List files = new ArrayList(); for (MavenProjectsStructure.MavenSimpleNode each : getSelectedNodes(MavenProjectsStructure.MavenSimpleNode.class)) { VirtualFile file = each.getVirtualFile(); - if (file != null) files.add(file); + if (file != null && file.isValid()) files.add(file); } return files.isEmpty() ? null : VfsUtil.toVirtualFileArray(files); } diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenUtil.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenUtil.java index 48aa53d48e94..6001367c0b9e 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenUtil.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenUtil.java @@ -19,6 +19,7 @@ import com.intellij.codeInsight.template.TemplateManager; import com.intellij.codeInsight.template.impl.TemplateImpl; import com.intellij.ide.fileTemplates.FileTemplate; import com.intellij.ide.fileTemplates.FileTemplateManager; +import com.intellij.ide.startup.StartupManagerEx; import com.intellij.notification.Notification; import com.intellij.notification.NotificationType; import com.intellij.notification.Notifications; @@ -137,7 +138,7 @@ public class MavenUtil { return; } - if (!project.isInitialized()) { + if (!StartupManagerEx.getInstanceEx(project).postStartupActivityPassed()) { StartupManager.getInstance(project).registerPostStartupActivity(r); return; } From 71e16df447dae5a880b2d28985bd701f7bfb3134 Mon Sep 17 00:00:00 2001 From: Anton Makeev Date: Fri, 12 Mar 2010 20:21:53 +0300 Subject: [PATCH 19/93] CacheUpdate: invalid file access handling --- .../openapi/project/FileContentQueue.java | 92 +++++++++---------- 1 file changed, 43 insertions(+), 49 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/project/FileContentQueue.java b/platform/platform-impl/src/com/intellij/openapi/project/FileContentQueue.java index a8f3a6d48633..fd508a67f21b 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/FileContentQueue.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/FileContentQueue.java @@ -21,6 +21,8 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.vfs.InvalidVirtualFileAccessException; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.newvfs.persistent.PersistentFS; import org.jetbrains.annotations.NotNull; @@ -74,60 +76,12 @@ public class FileContentQueue { } private void put(VirtualFile file) throws InterruptedException { - final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); - FileContent content = new FileContent(file); if (file.isValid()) { - final long contentLength = content.getLength(); - boolean counterUpdated = false; - try { - if (contentLength < PersistentFS.MAX_INTELLISENSE_FILESIZE) { - synchronized (this) { - while (myTotalSize > SIZE_THRESHOLD) { - if (indicator != null) { - indicator.checkCanceled(); - } - wait(300); - } - myTotalSize += contentLength; - counterUpdated = true; - } - - content.getBytes(); // Reads the content bytes and caches them. - } - } - catch (IOException e) { - LOG.info(e); - if (counterUpdated) { - synchronized (this) { - myTotalSize -= contentLength; // revert size counter - notifyAll(); - } - } + if (!doLoadContent(content)) { content.setEmptyContent(); } - catch (ProcessCanceledException e) { - if (counterUpdated) { - synchronized (this) { - myTotalSize -= contentLength; // revert size counter - notifyAll(); - } - } - throw e; - } - catch (InterruptedException e) { - if (counterUpdated) { - synchronized (this) { - myTotalSize -= contentLength; // revert size counter - notifyAll(); - } - } - return; - } - catch (Throwable e) { - LOG.error(e); - } } else { content.setEmptyContent(); @@ -136,6 +90,46 @@ public class FileContentQueue { myQueue.put(content); } + private boolean doLoadContent(final FileContent content) throws InterruptedException { + final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); + final long contentLength = content.getLength(); + + boolean counterUpdated = false; + try { + if (contentLength < PersistentFS.MAX_INTELLISENSE_FILESIZE) { + synchronized (this) { + while (myTotalSize > SIZE_THRESHOLD) { + if (indicator != null) { + indicator.checkCanceled(); + } + wait(300); + } + myTotalSize += contentLength; + counterUpdated = true; + } + + content.getBytes(); // Reads the content bytes and caches them. + } + + return true; + } + catch (Throwable e) { + if (counterUpdated) { + synchronized (this) { + myTotalSize -= contentLength; // revert size counter + notifyAll(); + } + } + if (e instanceof ProcessCanceledException) throw (ProcessCanceledException)e; + if (e instanceof InterruptedException) throw (InterruptedException)e; + + if (e instanceof IOException || e instanceof InvalidVirtualFileAccessException) LOG.info(e); + else LOG.error(e); + + return false; + } + } + public FileContent take() { FileContent result; synchronized (this) { From 6b2cb0b31e0a6828d73145eb0e3096b0121282fe Mon Sep 17 00:00:00 2001 From: Gregory Shrago Date: Fri, 12 Mar 2010 20:08:45 +0300 Subject: [PATCH 20/93] important method flushAllUiUpdates added --- .../com/intellij/execution/console/LanguageConsoleImpl.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleImpl.java b/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleImpl.java index c299ce3522a3..deff67214367 100644 --- a/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleImpl.java @@ -214,6 +214,10 @@ public class LanguageConsoleImpl implements Disposable, TypeSafeDataProvider { myUiUpdateRunnable = uiUpdateRunnable; } + public void flushAllUiUpdates() { + myUpdateQueue.flush(); + } + public LightVirtualFile getHistoryFile() { return myHistoryFile; } From b9fad08e4a059cf975a34cfa2b4614d8297dd15a Mon Sep 17 00:00:00 2001 From: "Kirill.Safonov" Date: Fri, 12 Mar 2010 20:41:44 +0300 Subject: [PATCH 21/93] open directory dialog: respect 'show hidden files' option (WI-191) --- .../intellij/ide/actions/OpenProjectFileChooserDescriptor.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/platform/platform-impl/src/com/intellij/ide/actions/OpenProjectFileChooserDescriptor.java b/platform/platform-impl/src/com/intellij/ide/actions/OpenProjectFileChooserDescriptor.java index e527436d6d7a..8248da96c861 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/OpenProjectFileChooserDescriptor.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/OpenProjectFileChooserDescriptor.java @@ -19,6 +19,7 @@ import com.intellij.ide.highlighter.ProjectFileType; import com.intellij.ide.impl.ProjectUtil; import com.intellij.openapi.application.impl.ApplicationInfoImpl; import com.intellij.openapi.fileChooser.FileChooserDescriptor; +import com.intellij.openapi.fileChooser.FileElement; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.IconLoader; import com.intellij.openapi.vfs.VirtualFile; @@ -66,6 +67,7 @@ public class OpenProjectFileChooserDescriptor extends FileChooserDescriptor { } public boolean isFileVisible(final VirtualFile file, final boolean showHiddenFiles) { + if (!showHiddenFiles && FileElement.isFileHidden(file)) return false; return isProjectFile(file) || super.isFileVisible(file, showHiddenFiles) && file.isDirectory(); } From bd421507b542c59433a2fd470cc4d10665e732ab Mon Sep 17 00:00:00 2001 From: Maxim Shafirov Date: Fri, 12 Mar 2010 15:39:33 +0300 Subject: [PATCH 22/93] Avoid holding large char arrays for short strings. --- .../intellij/ide/ui/search/SearchableOptionsRegistrarImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/platform-impl/src/com/intellij/ide/ui/search/SearchableOptionsRegistrarImpl.java b/platform/platform-impl/src/com/intellij/ide/ui/search/SearchableOptionsRegistrarImpl.java index 6c29be7e3760..6f18ae4accee 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/search/SearchableOptionsRegistrarImpl.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/search/SearchableOptionsRegistrarImpl.java @@ -157,7 +157,7 @@ public class SearchableOptionsRegistrarImpl extends SearchableOptionsRegistrar { Set configs = myStorage.get(option); if (configs == null) { configs = new THashSet(3, 0.9f); - myStorage.put(option, configs); + myStorage.put(new String(option), configs); } configs.add(new OptionDescription(null, myIdentifierTable.intern(id), hit != null ? myIdentifierTable.intern(hit) : null, From b2537551f401746c23c368a64c9fba1400adc65e Mon Sep 17 00:00:00 2001 From: Maxim Shafirov Date: Fri, 12 Mar 2010 18:01:31 +0300 Subject: [PATCH 23/93] Optimization: don't need a stack trace for this control flow throwable. --- .../psi/scope/MethodProcessorSetupFailedException.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/java/java-impl/src/com/intellij/psi/scope/MethodProcessorSetupFailedException.java b/java/java-impl/src/com/intellij/psi/scope/MethodProcessorSetupFailedException.java index 77c7cce96c62..d15e175d384f 100644 --- a/java/java-impl/src/com/intellij/psi/scope/MethodProcessorSetupFailedException.java +++ b/java/java-impl/src/com/intellij/psi/scope/MethodProcessorSetupFailedException.java @@ -28,4 +28,10 @@ public class MethodProcessorSetupFailedException extends Exception{ public MethodProcessorSetupFailedException(@NonNls String message){ super(message); } + + @SuppressWarnings({"NonSynchronizedMethodOverridesSynchronizedMethod"}) + @Override + public Throwable fillInStackTrace() { + return this; + } } From aa8b8fce3f256d50be1413217927f9cb99856f85 Mon Sep 17 00:00:00 2001 From: Maxim Shafirov Date: Fri, 12 Mar 2010 21:03:03 +0300 Subject: [PATCH 24/93] Indices shall not be built in background when in headless mode. --- .../src/com/intellij/openapi/project/DumbServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.java b/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.java index 1d6fb06e5439..4e4a68361ce1 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.java @@ -111,7 +111,7 @@ public class DumbServiceImpl extends DumbService { } try { final int size = runner.queryNeededFiles(indicator); - if ((size + runner.getNumberOfPendingUpdateJobs(indicator)) < 50) { + if (application.isHeadlessEnvironment() || (size + runner.getNumberOfPendingUpdateJobs(indicator)) < 50) { // If not that many files found, process them on the spot, avoiding entering dumb mode // Consider number of pending tasks as well, becase they may take noticeable time to process even if the number of files is small if (size > 0) { From 15e613fb5a4c6e879fd4be57c349d94147b31a03 Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Fri, 12 Mar 2010 22:04:22 +0300 Subject: [PATCH 25/93] IDEA-52842 Unknonw HTML tag should not trigger for XML namespace mappings --- .../HtmlUnknownAttributeInspection.java | 8 ++++---- .../source/html/dtd/HtmlElementDescriptorImpl.java | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/xml/impl/src/com/intellij/codeInspection/htmlInspections/HtmlUnknownAttributeInspection.java b/xml/impl/src/com/intellij/codeInspection/htmlInspections/HtmlUnknownAttributeInspection.java index ad654d123812..8d6ffbfa17bc 100644 --- a/xml/impl/src/com/intellij/codeInspection/htmlInspections/HtmlUnknownAttributeInspection.java +++ b/xml/impl/src/com/intellij/codeInspection/htmlInspections/HtmlUnknownAttributeInspection.java @@ -16,8 +16,9 @@ package com.intellij.codeInspection.htmlInspections; -import com.intellij.codeInspection.ProblemsHolder; +import com.intellij.codeInsight.daemon.XmlErrorMessages; import com.intellij.codeInspection.ProblemHighlightType; +import com.intellij.codeInspection.ProblemsHolder; import com.intellij.lang.ASTNode; import com.intellij.openapi.diagnostic.Logger; import com.intellij.psi.PsiElement; @@ -30,7 +31,6 @@ import com.intellij.xml.XmlBundle; import com.intellij.xml.XmlElementDescriptor; import com.intellij.xml.impl.schema.AnyXmlElementDescriptor; import com.intellij.xml.util.XmlUtil; -import com.intellij.codeInsight.daemon.XmlErrorMessages; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -87,8 +87,8 @@ public class HtmlUnknownAttributeInspection extends HtmlUnknownTagInspection { XmlAttributeDescriptor attributeDescriptor = elementDescriptor.getAttributeDescriptor(attribute); final String name = attribute.getName(); - - if (attributeDescriptor == null) { + + if (attributeDescriptor == null && !attribute.isNamespaceDeclaration()) { if (!XmlUtil.attributeFromTemplateFramework(name, tag) && (!isCustomValuesEnabled() || !isCustomValue(name))) { final ASTNode node = attribute.getNode(); assert node != null; diff --git a/xml/impl/src/com/intellij/psi/impl/source/html/dtd/HtmlElementDescriptorImpl.java b/xml/impl/src/com/intellij/psi/impl/source/html/dtd/HtmlElementDescriptorImpl.java index d831839b0bc9..70ed9f06ff02 100644 --- a/xml/impl/src/com/intellij/psi/impl/source/html/dtd/HtmlElementDescriptorImpl.java +++ b/xml/impl/src/com/intellij/psi/impl/source/html/dtd/HtmlElementDescriptorImpl.java @@ -22,6 +22,8 @@ import com.intellij.xml.XmlAttributeDescriptor; import com.intellij.xml.XmlElementDescriptor; import com.intellij.xml.XmlNSDescriptor; import com.intellij.xml.impl.dtd.BaseXmlElementDescriptorImpl; +import com.intellij.xml.impl.schema.XmlNSDescriptorImpl; +import com.intellij.xml.util.XmlUtil; import java.util.HashMap; @@ -96,6 +98,18 @@ public class HtmlElementDescriptorImpl extends BaseXmlElementDescriptorImpl { if (!myCaseSensitive) attributeName = attributeName.toLowerCase(); XmlAttributeDescriptor descriptor = super.getAttributeDescriptor(attributeName, context); if (descriptor == null) descriptor = RelaxedHtmlFromSchemaElementDescriptor.getAttributeDescriptorFromFacelets(attributeName, context); + + if (descriptor == null) { + String prefix = XmlUtil.findPrefixByQualifiedName(attributeName); + + if ("xml".equals(prefix)) { // todo this is not technically correct dtd document references namespaces but we should handle it at least for xml stuff + XmlNSDescriptor nsdescriptor = context.getNSDescriptor(XmlUtil.XML_NAMESPACE_URI, true); + if (nsdescriptor instanceof XmlNSDescriptorImpl) { + descriptor = ((XmlNSDescriptorImpl)nsdescriptor).getAttribute( + XmlUtil.findLocalNameByQualifiedName(attributeName), XmlUtil.XML_NAMESPACE_URI, context); + } + } + } return descriptor; } From fd47197d65e2a30d4abcc7f8414dc527fe039008 Mon Sep 17 00:00:00 2001 From: Maxim Shafirov Date: Fri, 12 Mar 2010 21:35:21 +0300 Subject: [PATCH 26/93] Cancelling thread shall have higher priority. --- .../com/intellij/openapi/progress/impl/ProgressManagerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/progress/impl/ProgressManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/progress/impl/ProgressManagerImpl.java index 54fd5ed11639..d5e66819e3b0 100644 --- a/platform/platform-impl/src/com/intellij/openapi/progress/impl/ProgressManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/progress/impl/ProgressManagerImpl.java @@ -66,7 +66,7 @@ public class ProgressManagerImpl extends ProgressManager { } } }; - thread.setPriority(Thread.MIN_PRIORITY + 1); + thread.setPriority(Thread.MAX_PRIORITY - 1); thread.start(); } } From 8f86a318da6767d240651f02c48648dbfed5173f Mon Sep 17 00:00:00 2001 From: Maxim Shafirov Date: Fri, 12 Mar 2010 21:56:19 +0300 Subject: [PATCH 27/93] Checking for instantiations of an utility class while building a fix is too dare. --- ...ssWithoutPrivateConstructorInspection.java | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/classlayout/UtilityClassWithoutPrivateConstructorInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/classlayout/UtilityClassWithoutPrivateConstructorInspection.java index cc371bfd5a81..997a5792b9b1 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/classlayout/UtilityClassWithoutPrivateConstructorInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/classlayout/UtilityClassWithoutPrivateConstructorInspection.java @@ -18,6 +18,7 @@ package com.siyeh.ig.classlayout; import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.Messages; import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.search.GlobalSearchScope; @@ -70,18 +71,6 @@ public class UtilityClassWithoutPrivateConstructorInspection final PsiClass aClass = (PsiClass)infos[0]; final PsiMethod constructor = getNullArgConstructor(aClass); if (constructor == null) { - final Query query = - ReferencesSearch.search(aClass, aClass.getUseScope()); - for (PsiReference reference : query) { - if (reference == null) { - continue; - } - final PsiElement element = reference.getElement(); - final PsiElement parent = element.getParent(); - if (parent instanceof PsiNewExpression) { - return null; - } - } return new CreateEmptyPrivateConstructor(); } else { final Query query = @@ -113,6 +102,25 @@ public class UtilityClassWithoutPrivateConstructorInspection if (aClass == null) { return; } + final Query query = + ReferencesSearch.search(aClass, aClass.getUseScope()); + for (PsiReference reference : query) { + if (reference == null) { + continue; + } + final PsiElement element = reference.getElement(); + final PsiElement parent = element.getParent(); + if (parent instanceof PsiNewExpression) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + Messages.showInfoMessage(aClass.getProject(), + "Utility class has instantiations, private constructor will not be created", + "Can't generate constructor"); + } + }); + return; + } + } final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project); final PsiElementFactory factory = psiFacade.getElementFactory(); final PsiMethod constructor = factory.createConstructor(); @@ -253,4 +261,4 @@ public class UtilityClassWithoutPrivateConstructorInspection } return null; } -} \ No newline at end of file +} From 0bef5521c58b276277ab70d716a228be12437be6 Mon Sep 17 00:00:00 2001 From: Maxim Shafirov Date: Fri, 12 Mar 2010 21:59:02 +0300 Subject: [PATCH 28/93] Don't use regexp matching for such a simple case. --- .../ex/DescriptorProviderInspection.java | 4 +- .../openapi/util/text/StringUtil.java | 38 ++++++++++++++++++- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ex/DescriptorProviderInspection.java b/platform/lang-impl/src/com/intellij/codeInspection/ex/DescriptorProviderInspection.java index e894b14d3d2b..b56965123654 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ex/DescriptorProviderInspection.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ex/DescriptorProviderInspection.java @@ -26,6 +26,7 @@ import com.intellij.openapi.components.PathMacroManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.JDOMUtil; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.FileStatus; import com.intellij.psi.PsiElement; import com.intellij.util.ArrayUtil; @@ -39,7 +40,6 @@ import org.jetbrains.annotations.Nullable; import java.io.*; import java.util.*; -import java.util.regex.Matcher; /** * @author max @@ -263,7 +263,7 @@ public abstract class DescriptorProviderInspection extends InspectionTool implem @NonNls final String template = description.getDescriptionTemplate(); int line = description instanceof ProblemDescriptor ? ((ProblemDescriptor)description).getLineNumber() : -1; final String text = description instanceof ProblemDescriptor ? ((ProblemDescriptor)description).getPsiElement().getText() : ""; - @NonNls String problemText = template.replaceAll("#ref", Matcher.quoteReplacement(text)).replaceAll(" #loc ", " "); + @NonNls String problemText = StringUtil.replace(StringUtil.replace(template, "#ref", StringUtil.quoteReplacement(text)), " #loc ", " "); Element element = refEntity.getRefManager().export(refEntity, parentNode, line); @NonNls Element problemClassElement = new Element(InspectionsBundle.message("inspection.export.results.problem.element.tag")); diff --git a/platform/util/src/com/intellij/openapi/util/text/StringUtil.java b/platform/util/src/com/intellij/openapi/util/text/StringUtil.java index c6c97d59bc19..c5f438560ed2 100644 --- a/platform/util/src/com/intellij/openapi/util/text/StringUtil.java +++ b/platform/util/src/com/intellij/openapi/util/text/StringUtil.java @@ -17,7 +17,6 @@ package com.intellij.openapi.util.text; import com.intellij.CommonBundle; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.TextRange; import com.intellij.util.ArrayUtil; import com.intellij.util.Function; @@ -39,6 +38,9 @@ public class StringUtil { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.util.text.StringUtil"); @NonNls private static final String VOWELS = "aeiouy"; + private StringUtil() { + } + public static String replace(@NonNls @NotNull String text, @NonNls @NotNull String oldS, @NonNls @Nullable String newS) { return replace(text, oldS, newS, false); } @@ -488,6 +490,40 @@ public class StringUtil { return s.substring(1, s.length() - 1); } + /** + * This is just an optimized version of Matcher.quoteReplacement + */ + public static String quoteReplacement(String s) { + boolean needReplacements = false; + + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == '\\' || c == '$') { + needReplacements = true; + break; + } + } + + if (!needReplacements) return s; + + StringBuilder sb = new StringBuilder(s.length() * 6 / 5); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == '\\') { + sb.append('\\'); + sb.append('\\'); + } + else if (c == '$') { + sb.append('\\'); + sb.append('$'); + } + else { + sb.append(c); + } + } + return sb.toString(); + } + private static void unescapeStringCharacters(int length, @NotNull String s, @NotNull StringBuilder buffer) { boolean escaped = false; for (int idx = 0; idx < length; idx++) { From 19ecb1add7d5733db8203454f2969afe29b0adc5 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 12 Mar 2010 19:57:46 +0000 Subject: [PATCH 29/93] don't construct Map type, it'll fail --- .../groovy/lang/psi/impl/statements/params/GrParameterImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/params/GrParameterImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/params/GrParameterImpl.java index 5b3e5fa90e5f..938626ec586a 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/params/GrParameterImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/params/GrParameterImpl.java @@ -200,7 +200,7 @@ public class GrParameterImpl extends GrVariableImpl implements GrParameter { private static PsiType getEntryForMap(PsiType map, PsiElementFactory factory, PsiElement context) { PsiType key = PsiUtil.substituteTypeParameter(map, CommonClassNames.JAVA_UTIL_MAP, 0, true); PsiType value = PsiUtil.substituteTypeParameter(map, CommonClassNames.JAVA_UTIL_MAP, 1, true); - if (key != null && value != null) { + if (key != null && key != PsiType.NULL && value != null && value != PsiType.NULL) { return factory.createTypeFromText("java.util.Map.Entry<" + key.getCanonicalText() + ", " + value.getCanonicalText() + ">", context); } return null; From 005dfa99c007f69eaaab62fded57fdf744f17cee Mon Sep 17 00:00:00 2001 From: Gregory Shrago Date: Fri, 12 Mar 2010 23:09:16 +0300 Subject: [PATCH 30/93] run SQL Script action --- platform/platform-resources/src/idea/Keymap_Default.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/platform/platform-resources/src/idea/Keymap_Default.xml b/platform/platform-resources/src/idea/Keymap_Default.xml index da2b2722d684..e98812cd9aa6 100644 --- a/platform/platform-resources/src/idea/Keymap_Default.xml +++ b/platform/platform-resources/src/idea/Keymap_Default.xml @@ -942,6 +942,8 @@ + + From 137cbc661e52bc4bb9a06d06b4d74428341be863 Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Sat, 13 Mar 2010 17:09:51 +0300 Subject: [PATCH 31/93] faster reformat for javascript when document == psifile content --- .../psi/formatter/FormattingDocumentModelImpl.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/psi/formatter/FormattingDocumentModelImpl.java b/platform/lang-impl/src/com/intellij/psi/formatter/FormattingDocumentModelImpl.java index 84d0cf06351e..2afe6c2464ee 100644 --- a/platform/lang-impl/src/com/intellij/psi/formatter/FormattingDocumentModelImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/formatter/FormattingDocumentModelImpl.java @@ -26,6 +26,7 @@ import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; import com.intellij.psi.impl.PsiDocumentManagerImpl; import com.intellij.psi.impl.PsiToDocumentSynchronizer; +import org.jetbrains.annotations.NotNull; public class FormattingDocumentModelImpl implements FormattingDocumentModel{ @@ -56,7 +57,7 @@ public class FormattingDocumentModelImpl implements FormattingDocumentModel{ } - private static Document getDocumentToBeUsedFor(final PsiFile file) { + public static Document getDocumentToBeUsedFor(final PsiFile file) { final Project project = file.getProject(); final Document document = PsiDocumentManager.getInstance(project).getDocument(file); if (document == null) return null; @@ -91,4 +92,11 @@ public class FormattingDocumentModelImpl implements FormattingDocumentModel{ public PsiFile getFile() { return myFile; } + + public static boolean canUseDocumentModel(@NotNull Document document,@NotNull PsiFile file) { + PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(file.getProject()); + return !psiDocumentManager.isUncommited(document) && + !psiDocumentManager.isDocumentBlockedByPsi(document) && + file.getText().equals(document.getText()); + } } From 35cb4defeadbb620e3bc22a4b61dc3e103a361d2 Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Sat, 13 Mar 2010 21:59:42 +0300 Subject: [PATCH 32/93] do not resolve all injected references by highlighting visitor + less walkups when checking qualified name has necessary import --- .../impl/analysis/HighlightVisitorImpl.java | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java index 2f50f7adb3b5..1ab725bc535b 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java @@ -69,18 +69,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh private final Map> mySingleImportedClasses = new THashMap>(); private final Map> mySingleImportedFields = new THashMap>(); private PsiFile myFile; - private final PsiElementVisitor REGISTER_REFERENCES_VISITOR = new PsiRecursiveElementWalkingVisitor() { - @Override public void visitElement(PsiElement element) { - super.visitElement(element); - for (PsiReference reference : element.getReferences()) { - PsiElement resolved = reference.resolve(); - if (resolved instanceof PsiNamedElement) { - myRefCountHolder.registerLocallyReferenced((PsiNamedElement)resolved); - } - } - } - }; - + @SuppressWarnings({"UnusedDeclaration"}) //in plugin.xml public HighlightVisitorImpl(Project project) { this(JavaPsiFacade.getInstance(project).getResolveHelper()); @@ -111,14 +100,14 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh element.accept(this); if (myRefCountHolder != null) { - registerReferencesFromInjectedFragments(element); + instantiateInjections(element); } } - private void registerReferencesFromInjectedFragments(final PsiElement element) { + private void instantiateInjections(final PsiElement element) { InjectedLanguageUtil.enumerate(element, myFile, new PsiLanguageInjectionHost.InjectedPsiVisitor() { public void visit(@NotNull final PsiFile injectedPsi, @NotNull final List places) { - injectedPsi.accept(REGISTER_REFERENCES_VISITOR); + // no op } }, false); } From ca095a013d7f28269356b5158b102017e329d92b Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Sun, 14 Mar 2010 23:11:21 +0300 Subject: [PATCH 33/93] remove variable --- .../com/intellij/codeInsight/template/impl/TemplateImpl.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateImpl.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateImpl.java index dac35a95ccd9..dfb1b3c4ed07 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateImpl.java @@ -305,6 +305,10 @@ public class TemplateImpl extends Template implements SchemeElement { return variable; } + public void removeVariable(int i) { + myVariables.remove(i); + } + public int getVariableCount() { return myVariables.size(); } From 1b8c1e71addde96196ee6beced95c3b01e2b3ddc Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Sun, 14 Mar 2010 23:12:38 +0300 Subject: [PATCH 34/93] psi-based attribute predefining --- .../template/XmlZenCodingTemplate.java | 212 +++++++++++++----- 1 file changed, 154 insertions(+), 58 deletions(-) diff --git a/xml/impl/src/com/intellij/codeInsight/template/XmlZenCodingTemplate.java b/xml/impl/src/com/intellij/codeInsight/template/XmlZenCodingTemplate.java index 8da184ae3d46..bcf539d1ee4f 100644 --- a/xml/impl/src/com/intellij/codeInsight/template/XmlZenCodingTemplate.java +++ b/xml/impl/src/com/intellij/codeInsight/template/XmlZenCodingTemplate.java @@ -20,15 +20,18 @@ import com.intellij.codeInsight.template.impl.TemplateImpl; import com.intellij.lang.xml.XMLLanguage; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiFileFactory; import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.psi.xml.XmlTag; -import com.intellij.psi.xml.XmlToken; -import com.intellij.psi.xml.XmlTokenType; +import com.intellij.psi.xml.*; import com.intellij.util.containers.HashMap; import com.intellij.util.containers.HashSet; +import com.intellij.util.containers.IntArrayList; +import org.apache.xerces.util.XML11Char; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -62,6 +65,8 @@ public class XmlZenCodingTemplate implements CustomLiveTemplate { private static class MyTemplateToken extends MyToken { final String myKey; final List> myAttribute2Value; + TemplateImpl myTemplate; + String myTemplateString; MyTemplateToken(String key, List> attribute2value) { myKey = key; @@ -189,6 +194,65 @@ public class XmlZenCodingTemplate implements CustomLiveTemplate { return new MyTemplateToken(templateKey, attributes); } + private static boolean isXML11ValidQName(String str) { + final int colon = str.indexOf(':'); + if (colon == 0 || colon == str.length() - 1) { + return false; + } + if (colon > 0) { + final String prefix = str.substring(0, colon); + final String localPart = str.substring(colon + 1); + return XML11Char.isXML11ValidNCName(prefix) && XML11Char.isXML11ValidNCName(localPart); + } + return XML11Char.isXML11ValidNCName(str); + } + + private static boolean containsAttrsVar(TemplateImpl template) { + for (int i = 0; i < template.getVariableCount(); i++) { + String varName = template.getVariableNameAt(i); + if (ATTRS.equals(varName)) { + return true; + } + } + return false; + } + + private static void removeVariablesWhichHasNoSegment(TemplateImpl template) { + Set segments = new HashSet(); + for (int i = 0; i < template.getSegmentsCount(); i++) { + segments.add(template.getSegmentName(i)); + } + IntArrayList varsToRemove = new IntArrayList(); + for (int i = 0; i < template.getVariableCount(); i++) { + String varName = template.getVariableNameAt(i); + if (!segments.contains(varName)) { + varsToRemove.add(i); + } + } + for (int i = 0; i < varsToRemove.size(); i++) { + template.removeVariable(varsToRemove.get(i)); + } + } + + @Nullable + private static XmlTag parseXmlTagInTemplate(String templateString, Project project) { + XmlFile xmlFile = (XmlFile)PsiFileFactory.getInstance(project).createFileFromText("dummy.xml", templateString); + XmlDocument document = xmlFile.getDocument(); + return document == null ? null : document.getRootTag(); + } + + private static boolean generateTemplateAndAddToToken(MyTemplateToken token, CustomTemplateCallback callback) { + TemplateImpl template = callback.findApplicableTemplate(token.myKey); + assert template != null; + XmlTag tag = parseXmlTagInTemplate(template.getString(), callback.getProject()); + if (tag == null) { + return false; + } + token.myTemplate = template; + token.myTemplateString = template.getString(); + return true; + } + @Nullable private static List parse(@NotNull String text, @NotNull CustomTemplateCallback callback) { text += MARKER; @@ -208,13 +272,20 @@ public class XmlZenCodingTemplate implements CustomLiveTemplate { return null; } String prefix = getPrefix(key); - if (!callback.isLiveTemplateApplicable(prefix) && prefix.indexOf('<') >= 0 /*&& !XML11Char.isXML11ValidQName(prefix)*/) { + boolean applicable = callback.isLiveTemplateApplicable(prefix); + if (!applicable && !isXML11ValidQName(prefix)) { return null; } MyTemplateToken token = parseSelectors(key); if (token == null) { return null; } + if (applicable && token.myAttribute2Value.size() > 0) { + assert prefix.equals(token.myKey); + if (!generateTemplateAndAddToToken(token, callback)) { + return null; + } + } result.add(token); } result.add(i < n - 1 ? new MyOperationToken(c) : new MyMarkerToken()); @@ -353,45 +424,103 @@ public class XmlZenCodingTemplate implements CustomLiveTemplate { LOG.error("Input string was checked incorrectly during isApplicable() invokation"); } - @NotNull - private static String buildAttributesString(List> attribute2value, int numberInIteration) { + @Nullable + private static Map buildPredefinedValues(List> attribute2value, int numberInIteration) { StringBuilder result = new StringBuilder(); for (Iterator> it = attribute2value.iterator(); it.hasNext();) { Pair pair = it.next(); String name = pair.first; - String value = pair.second.replace(NUMBER_IN_ITERATION_PLACE_HOLDER, Integer.toString(numberInIteration + 1)); + String value = getValue(pair, numberInIteration); result.append(name).append("=\"").append(value).append('"'); if (it.hasNext()) { result.append(' '); } } - return result.toString(); - } - - private static boolean invokeTemplate(MyTemplateToken token, - final CustomTemplateCallback callback, - final TemplateInvokationListener listener, - int numberInIteration) { - String attributes = buildAttributesString(token.myAttribute2Value, numberInIteration); + String attributes = result.toString(); attributes = attributes.length() > 0 ? ' ' + attributes : null; Map predefinedValues = null; if (attributes != null) { predefinedValues = new HashMap(); predefinedValues.put(ATTRS, attributes); } + return predefinedValues; + } + + private static String getValue(Pair pair, int numberInIteration) { + return pair.second.replace(NUMBER_IN_ITERATION_PLACE_HOLDER, Integer.toString(numberInIteration + 1)); + } + + @Nullable + private static String addAttrsVar(TemplateImpl modifiedTemplate, XmlTag tag) { + String text = tag.getContainingFile().getText(); + PsiElement[] children = tag.getChildren(); + if (children.length >= 1 && + children[0] instanceof XmlToken && + ((XmlToken)children[0]).getTokenType() == XmlTokenType.XML_START_TAG_START) { + PsiElement beforeAttrs = children[0]; + if (children.length >= 2 && children[1] instanceof XmlToken && ((XmlToken)children[1]).getTokenType() == XmlTokenType.XML_NAME) { + beforeAttrs = children[1]; + } + TextRange range = beforeAttrs.getTextRange(); + if (range == null) { + return null; + } + int offset = range.getEndOffset(); + text = text.substring(0, offset) + " $ATTRS$" + text.substring(offset); + modifiedTemplate.addVariable(ATTRS, "", "", false); + return text; + } + return null; + } + + private static boolean invokeTemplate(MyTemplateToken token, + final CustomTemplateCallback callback, + final TemplateInvokationListener listener, + int numberInIteration) { + List> attr2value = new ArrayList>(token.myAttribute2Value); if (callback.isLiveTemplateApplicable(token.myKey)) { - if (attributes != null && !callback.templateContainsVars(token.myKey, ATTRS)) { - TemplateImpl newTemplate = generateTemplateWithAttributes(token.myKey, attributes, callback); - if (newTemplate != null) { - return callback.startTemplate(newTemplate, predefinedValues, listener); + if (token.myTemplate != null) { + TemplateImpl modifiedTemplate = token.myTemplate.copy(); + XmlTag tag = parseXmlTagInTemplate(token.myTemplateString, callback.getProject()); + assert tag != null; + for (Iterator> iterator = attr2value.iterator(); iterator.hasNext();) { + Pair pair = iterator.next(); + if (tag.getAttribute(pair.first) != null) { + tag.setAttribute(pair.first, getValue(pair, numberInIteration)); + iterator.remove(); + } } + String text = null; + if (!containsAttrsVar(modifiedTemplate) && attr2value.size() > 0) { + String textWithAttrs = addAttrsVar(modifiedTemplate, tag); + if (textWithAttrs != null) { + text = textWithAttrs; + } + else { + for (Iterator> iterator = attr2value.iterator(); iterator.hasNext();) { + Pair pair = iterator.next(); + tag.setAttribute(pair.first, getValue(pair, numberInIteration)); + iterator.remove(); + } + } + } + if (text == null) { + text = tag.getContainingFile().getText(); + } + modifiedTemplate.setString(text); + removeVariablesWhichHasNoSegment(modifiedTemplate); + Map predefinedValues = buildPredefinedValues(attr2value, numberInIteration); + return callback.startTemplate(modifiedTemplate, predefinedValues, listener); + } + else { + Map predefinedValues = buildPredefinedValues(attr2value, numberInIteration); + return callback.startTemplate(token.myKey, predefinedValues, listener); } - return callback.startTemplate(token.myKey, predefinedValues, listener); } else { TemplateImpl template = new TemplateImpl("", ""); template.addTextSegment('<' + token.myKey); - if (attributes != null) { + if (attr2value.size() > 0) { template.addVariable(ATTRS, "", "", false); template.addVariableSegment(ATTRS); } @@ -399,45 +528,11 @@ public class XmlZenCodingTemplate implements CustomLiveTemplate { template.addVariableSegment(TemplateImpl.END); template.addTextSegment(""); template.setToReformat(true); + Map predefinedValues = buildPredefinedValues(attr2value, numberInIteration); return callback.startTemplate(template, predefinedValues, listener); } } - private static int findPlaceToInsertAttrs(@NotNull TemplateImpl template) { - String s = template.getString(); - if (s.length() > 0) { - if (s.charAt(0) != '<') { - return -1; - } - int i = 1; - while (i < s.length() && !Character.isWhitespace(s.charAt(i)) && s.charAt(i) != '>') { - i++; - } - if (i == 1) { - return -1; - } - if (s.indexOf('>', i) >= i) { - return i; - } - } - return -1; - } - - @Nullable - private static TemplateImpl generateTemplateWithAttributes(String key, String attributes, CustomTemplateCallback callback) { - TemplateImpl template = callback.findApplicableTemplate(key); - assert template != null; - String templateString = template.getString(); - int offset = findPlaceToInsertAttrs(template); - if (offset >= 0) { - String newTemplateString = templateString.substring(0, offset) + attributes + templateString.substring(offset); - TemplateImpl newTemplate = template.copy(); - newTemplate.setString(newTemplateString); - return newTemplate; - } - return null; - } - /*private static boolean hasClosingTag(CharSequence text, CharSequence tagName, int offset, int rightBound) { if (offset + 1 < text.length() && text.charAt(offset) == '<' && text.charAt(offset + 1) == '/') { CharSequence closingTagName = parseTagName(text, offset + 2, rightBound); @@ -544,7 +639,7 @@ public class XmlZenCodingTemplate implements CustomLiveTemplate { } } }; - if (!invokeTemplate(templateToken, myCallback, listener, -1)) { + if (!invokeTemplate(templateToken, myCallback, listener, 0)) { return false; } templateToken = null; @@ -625,7 +720,7 @@ public class XmlZenCodingTemplate implements CustomLiveTemplate { } } }; - if (!invokeTemplate(templateToken, myCallback, listener, -1)) { + if (!invokeTemplate(templateToken, myCallback, listener, 0)) { return false; } return true; @@ -704,4 +799,5 @@ public class XmlZenCodingTemplate implements CustomLiveTemplate { return true; } } + } \ No newline at end of file From 082a98476f070baf30d9578a5325d0f1e580ace4 Mon Sep 17 00:00:00 2001 From: "Kirill.Safonov" Date: Mon, 15 Mar 2010 10:24:53 +0300 Subject: [PATCH 35/93] reworked deployment configurable - follow up --- .../src/com/intellij/openapi/ui/MasterDetailsComponent.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/platform/platform-api/src/com/intellij/openapi/ui/MasterDetailsComponent.java b/platform/platform-api/src/com/intellij/openapi/ui/MasterDetailsComponent.java index a1f8687aefda..7707e58c53d4 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/MasterDetailsComponent.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/MasterDetailsComponent.java @@ -777,6 +777,10 @@ public abstract class MasterDetailsComponent implements Configurable, Persistent public boolean isDisplayInBold() { return myDisplayInBold; } + + public void setDisplayInBold(boolean displayInBold) { + myDisplayInBold = displayInBold; + } } @SuppressWarnings({"ConstantConditions"}) From 5f8ebb7a192640822f58452e344a14710f18d801 Mon Sep 17 00:00:00 2001 From: nik Date: Mon, 15 Mar 2010 10:33:14 +0300 Subject: [PATCH 36/93] long error tooltips for artifacts truncated --- .../daemon/ProjectStructureProblemsHolderImpl.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureProblemsHolderImpl.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureProblemsHolderImpl.java index 60cc936ced23..82cb561b8fd8 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureProblemsHolderImpl.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureProblemsHolderImpl.java @@ -46,8 +46,14 @@ public class ProjectStructureProblemsHolderImpl implements ProjectStructureProbl try { buf.append(""); if (myProblemDescriptions != null) { + int problems = 0; for (ProjectStructureProblemDescription problemDescription : myProblemDescriptions) { buf.append(problemDescription.getMessage()).append("
"); + problems++; + if (problems >= 10 && myProblemDescriptions.size() > 12) { + buf.append(myProblemDescriptions.size() - problems).append(" more problems...
"); + break; + } } } buf.append(""); From 26c9c62451dbb9065899cde1688f60e3443b205c Mon Sep 17 00:00:00 2001 From: Anton Makeev Date: Mon, 15 Mar 2010 11:36:34 +0300 Subject: [PATCH 37/93] Maven: test fix --- .../idea/maven/dom/MavenParentCompletionAndResolutionTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/maven/src/test/java/org/jetbrains/idea/maven/dom/MavenParentCompletionAndResolutionTest.java b/plugins/maven/src/test/java/org/jetbrains/idea/maven/dom/MavenParentCompletionAndResolutionTest.java index 57f87ecdd220..ed546a5843d0 100644 --- a/plugins/maven/src/test/java/org/jetbrains/idea/maven/dom/MavenParentCompletionAndResolutionTest.java +++ b/plugins/maven/src/test/java/org/jetbrains/idea/maven/dom/MavenParentCompletionAndResolutionTest.java @@ -353,7 +353,7 @@ public class MavenParentCompletionAndResolutionTest extends MavenDomWithIndicesT " xxx" + ""); - IntentionAction i = getIntentionAtCaret("Fix relative path"); + IntentionAction i = getIntentionAtCaret("Fix Relative Path"); assertNotNull(i); myFixture.launchAction(i); From eb789b31e43258b2834e057940cdfae1fe9ae2fb Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 15 Mar 2010 09:18:20 +0000 Subject: [PATCH 38/93] allow to pass arrays as groovy vararg parameter --- .../plugins/groovy/lang/psi/impl/GrClosureSignatureUtil.java | 3 ++- .../plugins/groovy/lang/GroovyHighlightingTest.java | 1 + .../groovy/testdata/highlighting/ByteArrayArgument.groovy | 5 +++++ 3 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 plugins/groovy/testdata/highlighting/ByteArrayArgument.groovy diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrClosureSignatureUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrClosureSignatureUtil.java index 39d508ae4c76..1a1ceda96e20 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrClosureSignatureUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrClosureSignatureUtil.java @@ -56,9 +56,10 @@ public class GrClosureSignatureUtil { if (signature.isVarargs()) { if (isApplicable(params, args, params.length - 1, args.length, manager, scope)) return true; - PsiType lastType = params[params.length - 1].getType(); assert lastType instanceof PsiArrayType; + if (TypesUtil.isAssignableByMethodCallConversion(lastType, args[args.length - 1], manager, scope)) return true; + PsiType varargType = ((PsiArrayType)lastType).getComponentType(); for (int argCount = args.length - 1; argCount >= notOptional; argCount--) { diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.java b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.java index eb385138a5f9..f38280c2924c 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.java +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.java @@ -205,6 +205,7 @@ public class GroovyHighlightingTest extends LightCodeInsightFixtureTestCase { public void testStringAndGStringUpperBound() throws Exception {doTest();} public void testWithMethod() throws Exception {doTest();} + public void testByteArrayArgument() throws Exception {doTest();} public void testForLoopWithNestedEndlessLoop() throws Exception {doTest(new UnassignedVariableAccessInspection());} public void testIfIncrementElseReturn() throws Exception {doTest(new UnusedDefInspection()); } diff --git a/plugins/groovy/testdata/highlighting/ByteArrayArgument.groovy b/plugins/groovy/testdata/highlighting/ByteArrayArgument.groovy new file mode 100644 index 000000000000..e20f290d0fc9 --- /dev/null +++ b/plugins/groovy/testdata/highlighting/ByteArrayArgument.groovy @@ -0,0 +1,5 @@ +def foo(byte[] b) {} +def bar(byte[] b) { + foo(b) + foo(239) +} \ No newline at end of file From 39316f29d108ff8f981b53dc9d984e6aa9aa946d Mon Sep 17 00:00:00 2001 From: anna Date: Fri, 12 Mar 2010 13:39:59 +0300 Subject: [PATCH 39/93] fetch external javadoc for all elements with configured external urls (IDEA-52559) --- .../lang/java/JavaDocumentationProvider.java | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/java/java-impl/src/com/intellij/lang/java/JavaDocumentationProvider.java b/java/java-impl/src/com/intellij/lang/java/JavaDocumentationProvider.java index df905629b703..4cb54acf4a6b 100644 --- a/java/java-impl/src/com/intellij/lang/java/JavaDocumentationProvider.java +++ b/java/java-impl/src/com/intellij/lang/java/JavaDocumentationProvider.java @@ -549,18 +549,16 @@ public class JavaDocumentationProvider implements CodeDocumentationProvider, Ext @Nullable - private static String generateExternalJavadoc(final PsiElement element, String fromUrl, boolean checkCompiled, JavaDocExternalFilter filter) { - if (!checkCompiled || element instanceof PsiCompiledElement) { - try { - String externalDoc = filter.getExternalDocInfoForElement(fromUrl, element); - if (externalDoc != null && externalDoc.length() > 0) { - return externalDoc; - } - } - catch (Exception e) { - //try to generate some javadoc + private static String fetchExternalJavadoc(final PsiElement element, String fromUrl, JavaDocExternalFilter filter) { + try { + String externalDoc = filter.getExternalDocInfoForElement(fromUrl, element); + if (externalDoc != null && externalDoc.length() > 0) { + return externalDoc; } } + catch (Exception e) { + //try to generate some javadoc + } return null; } @@ -762,7 +760,7 @@ public class JavaDocumentationProvider implements CodeDocumentationProvider, Ext if (docURLs != null) { for (String docURL : docURLs) { try { - final String javadoc = generateExternalJavadoc(element, docURL, true, docFilter); + final String javadoc = fetchExternalJavadoc(element, docURL, docFilter); if (javadoc != null) return javadoc; } catch (IndexNotReadyException e) { From d81a4e50725defb6ee090570e6abb0921f03f569 Mon Sep 17 00:00:00 2001 From: anna Date: Fri, 12 Mar 2010 16:58:16 +0300 Subject: [PATCH 40/93] build searchable options respects configurables filters --- .../src/com/intellij/ide/ui/search/SearchUtil.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/ide/ui/search/SearchUtil.java b/platform/platform-impl/src/com/intellij/ide/ui/search/SearchUtil.java index cb28a0720c59..f2642492b77c 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/search/SearchUtil.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/search/SearchUtil.java @@ -16,13 +16,13 @@ package com.intellij.ide.ui.search; -import com.intellij.openapi.application.Application; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.options.Configurable; +import com.intellij.openapi.options.ConfigurableGroup; import com.intellij.openapi.options.MasterDetails; import com.intellij.openapi.options.SearchableConfigurable; -import com.intellij.openapi.options.ConfigurableGroup; import com.intellij.openapi.options.ex.GlassPanel; +import com.intellij.openapi.options.ex.IdeConfigurablesGroup; +import com.intellij.openapi.options.ex.ProjectConfigurablesGroup; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.JBPopupFactory; @@ -62,11 +62,8 @@ public class SearchUtil { } public static void processProjectConfigurables(Project project, HashMap> options) { - processConfigurables(project.getExtensions(Configurable.PROJECT_CONFIGURABLES), options); - processConfigurables(project.getComponents(Configurable.class), options); - final Application app = ApplicationManager.getApplication(); - processConfigurables(app.getExtensions(Configurable.APPLICATION_CONFIGURABLES), options); - processConfigurables(app.getComponents(Configurable.class), options); + processConfigurables(new ProjectConfigurablesGroup(project, false).getConfigurables(), options); + processConfigurables(new IdeConfigurablesGroup().getConfigurables(), options); } private static void processConfigurables(final Configurable[] configurables, From dd87a7dc4324b0df08771904a9839989ede79407 Mon Sep 17 00:00:00 2001 From: anna Date: Fri, 12 Mar 2010 17:32:12 +0300 Subject: [PATCH 41/93] conflict mnemonic changed (IDEA-52832 ) --- .../src/messages/RefactoringBundle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/platform-resources-en/src/messages/RefactoringBundle.properties b/platform/platform-resources-en/src/messages/RefactoringBundle.properties index 2ce788085ef2..909d679b0513 100644 --- a/platform/platform-resources-en/src/messages/RefactoringBundle.properties +++ b/platform/platform-resources-en/src/messages/RefactoringBundle.properties @@ -80,7 +80,7 @@ parameter.name.prompt=Pa&rameter name: no.class.name.specified=No class name specified no.parameter.name.specified=No parameter name specified pass.outer.class.instance.as.parameter=Pass &outer class' instance as a parameter -pull.up.members.to=&Pull up members of {0} to: +pull.up.members.to=P&ull up members of {0} to: members.to.be.pulled.up=Members to be pulled up make.abstract=Make abstract javadoc.for.abstracts=JavaDoc for abstracts From a4ed9bf8ab8d129dff62859cf4eb0c9c2d8b6268 Mon Sep 17 00:00:00 2001 From: anna Date: Mon, 15 Mar 2010 12:38:17 +0300 Subject: [PATCH 42/93] TeamCity inherits StringUtil: do not add private constructors --- .../util/src/com/intellij/openapi/util/text/StringUtil.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/platform/util/src/com/intellij/openapi/util/text/StringUtil.java b/platform/util/src/com/intellij/openapi/util/text/StringUtil.java index c5f438560ed2..ed99449c231a 100644 --- a/platform/util/src/com/intellij/openapi/util/text/StringUtil.java +++ b/platform/util/src/com/intellij/openapi/util/text/StringUtil.java @@ -34,13 +34,11 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.util.*; +//TeamCity inherits StringUtil: do not add private constructors!!! public class StringUtil { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.util.text.StringUtil"); @NonNls private static final String VOWELS = "aeiouy"; - private StringUtil() { - } - public static String replace(@NonNls @NotNull String text, @NonNls @NotNull String oldS, @NonNls @Nullable String newS) { return replace(text, oldS, newS, false); } From 023292c511ab2d27c1a24c2a972bc54e338bdd46 Mon Sep 17 00:00:00 2001 From: Maxim Medvedev Date: Mon, 15 Mar 2010 13:01:15 +0300 Subject: [PATCH 43/93] AE: GrClosureSignatureUtil.isSignatureApplicable --- .../parameterInfo/GroovyParameterInfoHandler.java | 9 ++++++++- .../lang/psi/dataFlow/types/TypesSemilattice.java | 12 ++++++------ .../lang/psi/impl/types/GrClosureSignatureImpl.java | 2 +- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parameterInfo/GroovyParameterInfoHandler.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parameterInfo/GroovyParameterInfoHandler.java index f3f788d6616a..c4b445113d5f 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parameterInfo/GroovyParameterInfoHandler.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parameterInfo/GroovyParameterInfoHandler.java @@ -315,7 +315,14 @@ public class GroovyParameterInfoHandler implements ParameterInfoHandler 0) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/types/TypesSemilattice.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/types/TypesSemilattice.java index c6ae0505d6b3..c85d8486118f 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/types/TypesSemilattice.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/types/TypesSemilattice.java @@ -36,7 +36,7 @@ public class TypesSemilattice implements Semilattice> { public Map join(ArrayList> ins) { if (ins.size() == 0) return new HashMap(); - + Map result = new HashMap(ins.get(0)); for (int i = 1; i < ins.size(); i++) { @@ -48,10 +48,9 @@ public class TypesSemilattice implements Semilattice> { if (result.containsKey(name)) { final PsiType t2 = result.get(name); if (t1 != null && t2 != null) { - if (t1.isAssignableFrom(t2)) result.put(name, t1); - else if (t2.isAssignableFrom(t1)) result.put(name, t2); - else result.put(name, TypesUtil.getLeastUpperBound(t1, t2, myManager)); - } else { + result.put(name, TypesUtil.getLeastUpperBound(t1, t2, myManager)); + } + else { result.put(name, null); } } @@ -71,7 +70,8 @@ public class TypesSemilattice implements Semilattice> { final PsiType t2 = e2.get(name); if (t1 == null || t2 == null) { if (t1 != null || t2 != null) return false; - } else { + } + else { if (!t1.equals(t2)) return false; } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/types/GrClosureSignatureImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/types/GrClosureSignatureImpl.java index d3e06d35bcec..9f1e46289497 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/types/GrClosureSignatureImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/types/GrClosureSignatureImpl.java @@ -40,7 +40,7 @@ public class GrClosureSignatureImpl implements GrClosureSignature { myParameters[i] = new GrClosureParameterImpl(parameters[i], substitutor); } if (length > 0) { - myIsVarargs = parameters[length - 1].isVarArgs() || myParameters[length - 1].getType() instanceof PsiArrayType; + myIsVarargs = /*parameters[length - 1].isVarArgs() ||*/ myParameters[length - 1].getType() instanceof PsiArrayType; } else { myIsVarargs = false; From 9de88eeabf58db9873d78cef05c357435a8617a3 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Fri, 12 Mar 2010 15:50:13 +0300 Subject: [PATCH 44/93] cleanup: lazy initialization --- .../src/com/intellij/psi/stubs/StubIndex.java | 10 ++-- .../DefaultScopesProvider.java | 44 +++++++++--------- .../util/indexing/FileBasedIndex.java | 11 ++--- .../openapi/fileTypes/FileTypeManager.java | 17 ++++--- .../openapi/vfs/newvfs/ManagingFS.java | 10 ++-- .../src/messages/IdeBundle.properties | 46 ------------------- .../actions/GroovyTemplatesFactory.java | 9 ++-- .../svn/history/MergeSourceDetailsAction.java | 10 ++-- 8 files changed, 56 insertions(+), 101 deletions(-) diff --git a/platform/lang-api/src/com/intellij/psi/stubs/StubIndex.java b/platform/lang-api/src/com/intellij/psi/stubs/StubIndex.java index 4b4034f373d6..c331e6e9441d 100644 --- a/platform/lang-api/src/com/intellij/psi/stubs/StubIndex.java +++ b/platform/lang-api/src/com/intellij/psi/stubs/StubIndex.java @@ -29,13 +29,13 @@ import org.jetbrains.annotations.NotNull; import java.util.Collection; public abstract class StubIndex { - private static StubIndex ourInstance = CachedSingletonsRegistry.markCachedField(StubIndex.class); + + private static class StubIndexHolder { + private static final StubIndex ourInstance = ApplicationManager.getApplication().getComponent(StubIndex.class); + } public static StubIndex getInstance() { - if (ourInstance == null) { - ourInstance = ApplicationManager.getApplication().getComponent(StubIndex.class); - } - return ourInstance; + return StubIndexHolder.ourInstance; } public abstract Collection get( diff --git a/platform/lang-impl/src/com/intellij/packageDependencies/DefaultScopesProvider.java b/platform/lang-impl/src/com/intellij/packageDependencies/DefaultScopesProvider.java index 1e1f0d67afb5..6a080f33fed4 100644 --- a/platform/lang-impl/src/com/intellij/packageDependencies/DefaultScopesProvider.java +++ b/platform/lang-impl/src/com/intellij/packageDependencies/DefaultScopesProvider.java @@ -32,7 +32,6 @@ import java.util.ArrayList; import java.util.List; public class DefaultScopesProvider implements CustomScopesProvider { - private static NamedScope myAllScope; private NamedScope myProblemsScope; private final Project myProject; @@ -55,27 +54,28 @@ public class DefaultScopesProvider implements CustomScopesProvider { return list; } + private static class NamedScopeHolder { + private static final NamedScope myAllScope = new NamedScope("All", new PackageSet() { + public boolean contains(final PsiFile file, final NamedScopesHolder holder) { + return true; + } + + public PackageSet createCopy() { + return this; + } + + public String getText() { + return FilePatternPackageSet.SCOPE_FILE + ":*//*"; + } + + public int getNodePriority() { + return 0; + } + }); + } + public static NamedScope getAllScope() { - if (myAllScope == null) { - myAllScope = new NamedScope("All", new PackageSet() { - public boolean contains(final PsiFile file, final NamedScopesHolder holder) { - return true; - } - - public PackageSet createCopy() { - return this; - } - - public String getText() { - return FilePatternPackageSet.SCOPE_FILE + ":*//*"; - } - - public int getNodePriority() { - return 0; - } - }); - } - return myAllScope; + return NamedScopeHolder.myAllScope; } public NamedScope getProblemsScope() { @@ -100,4 +100,4 @@ public class DefaultScopesProvider implements CustomScopesProvider { } return myProblemsScope; } -} \ No newline at end of file +} diff --git a/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndex.java b/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndex.java index 61dbf0e63879..4de538b56949 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndex.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndex.java @@ -287,13 +287,12 @@ public class FileBasedIndex implements ApplicationComponent { } } - private static FileBasedIndex ourInstance = CachedSingletonsRegistry.markCachedField(FileBasedIndex.class); - public static FileBasedIndex getInstance() { - if (ourInstance == null) { - ourInstance = ApplicationManager.getApplication().getComponent(FileBasedIndex.class); - } + private static class FileBasedIndexHolder { + private static final FileBasedIndex ourInstance = ApplicationManager.getApplication().getComponent(FileBasedIndex.class); + } - return ourInstance; + public static FileBasedIndex getInstance() { + return FileBasedIndexHolder.ourInstance; } /** diff --git a/platform/platform-api/src/com/intellij/openapi/fileTypes/FileTypeManager.java b/platform/platform-api/src/com/intellij/openapi/fileTypes/FileTypeManager.java index 9b8400aa8a10..873acb6bbcc4 100644 --- a/platform/platform-api/src/com/intellij/openapi/fileTypes/FileTypeManager.java +++ b/platform/platform-api/src/com/intellij/openapi/fileTypes/FileTypeManager.java @@ -17,7 +17,6 @@ package com.intellij.openapi.fileTypes; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.CachedSingletonsRegistry; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -31,18 +30,22 @@ import java.util.List; */ public abstract class FileTypeManager{ - private static FileTypeManager ourInstance = CachedSingletonsRegistry.markCachedField(FileTypeManager.class); + private static class FileTypeManagerHolder { + private static final FileTypeManager ourInstance = createFileTypeManager(); + } + /** * Returns the singleton instance of the FileTypeManager component. * * @return the instace of FileTypeManager */ public static FileTypeManager getInstance() { - if (ourInstance == null) { - Application app = ApplicationManager.getApplication(); - ourInstance = app != null ? app.getComponent(FileTypeManager.class) : new MockFileTypeManager(); - } - return ourInstance; + return FileTypeManagerHolder.ourInstance; + } + + private static FileTypeManager createFileTypeManager() { + Application app = ApplicationManager.getApplication(); + return app == null ? new MockFileTypeManager() : app.getComponent(FileTypeManager.class); } public abstract void registerFileType(@NotNull FileType type, @NotNull List defaultAssociations); diff --git a/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/ManagingFS.java b/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/ManagingFS.java index bc3122889a53..9793edf8447d 100644 --- a/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/ManagingFS.java +++ b/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/ManagingFS.java @@ -30,13 +30,13 @@ import java.io.DataOutputStream; import java.util.List; public abstract class ManagingFS implements FileSystemInterface { - private static ManagingFS ourInstance = CachedSingletonsRegistry.markCachedField(ManagingFS.class); + + private static class ManagingFSHolder { + private static final ManagingFS ourInstance = ApplicationManager.getApplication().getComponent(ManagingFS.class); + } public static ManagingFS getInstance() { - if (ourInstance == null) { - ourInstance = ApplicationManager.getApplication().getComponent(ManagingFS.class); - } - return ourInstance; + return ManagingFSHolder.ourInstance; } @Nullable diff --git a/platform/platform-resources-en/src/messages/IdeBundle.properties b/platform/platform-resources-en/src/messages/IdeBundle.properties index e45a14fc682f..c0cf915bbdad 100644 --- a/platform/platform-resources-en/src/messages/IdeBundle.properties +++ b/platform/platform-resources-en/src/messages/IdeBundle.properties @@ -1,4 +1,3 @@ -error.cannot.show.in.external.browser=Cannot show \"{0}\" in external browser title.cannot.start.browser=Cannot start browser error.malformed.url=Malformed url: {0} error.cannot.start.browser=Cannot start browser: {0} @@ -20,19 +19,15 @@ checkbox.transparent.native.to.ascii.conversion=Transparent native-to-ascii conv checkbox.autodetect.utf=Autodetect UTF-encoded files group.http.proxy=HTTP Proxy group.web.browser=Web Browser -radio.use.system.default.browser=Use system default browser -radio.use.specified.browser=Use group.general.options=General Options editbox.ignore.files.and.folders=Ignore files and folders: checkbox.confirm.application.exit=Confirm application exit -checkbox.use.cyclic.buffer.in.console=Limit run/debug consoles output to: label.inactive.timeout.sec= sec. search.in.the.background=Search in the background checkbox.save.files.automatically=Save files automatically if application is idle for checkbox.save.files.on.frame.deactivation=Save files on frame deactivation checkbox.synchronize.files.on.frame.activation=Synchronize files on frame activation checkbox.reopen.last.project.on.startup=Reopen last project on startup -label.kb= Kb. treenode.loading= loading... action.clear.list=_Clear List action.descriptor.action=Action: {0} @@ -59,17 +54,11 @@ button.remove=&Remove button.remove.all=Remove &All button.move.up=Move &Up button.move.down=Move &Down -column.bookmark=Bookmark column.description=Description -title.project.elements.bookmarks=Project Elements Bookmarks bookmark.file.X.line.Y={0}, line {1} button.view.source=&View Source -title.editor.bookmarks=Editor Bookmarks -bookmark.method.at.line={0}, {1}{2}, line {3} -action.show.bookmarks=_Show Bookmarks action.toggle.bookmark=Toggle _Bookmark action.toggle.bookmark.mnemonic=Toggle Bookmark With Mnemonic -action.set.bookmark=Set _Bookmark progress.deleting=Deleting select.in.commander=Commander errortree.information=Information: @@ -255,10 +244,7 @@ label.text.project.has.older.format=The project ''{0}'' has an older Old versions of project files will be saved to: ''{1}'' message.text.unlock.read.only.files=The following files are read only. IDEA will unlock them.
{0} error.message.cannot.make.files.writable=Cannot make the following files writable:\n{0} -label.text.project.was.succesfully.converted.old.version.was.saved.to.0=Your project was succesfully converted. \ -
Old versions of project files were saved to: ''{0}'' error.cannot.convert.project=Cannot convert project: {0} -error.some.file.is.corrupted.message=File ''{0}'' is corrupted:
{1} message.files.doesn.t.exists.0.so.the.corresponding.modules.won.t.be.converted.do.you.want.to.continue=The following files doesn''t exists:
\ {0}The corresponding modules won''t be converted. Do you want to continue? @@ -273,7 +259,6 @@ message.purchase.or.upgrade=To purchase {0}, refer to {1}.
To upgrade from a message.expiration.date=Expiration date: {0} message.educational.license=1-Year Educational License. {0} message.open.source.project.license=Open Source Project License. {0} -message.evaluation.expires=Evaluation expires: {0} message.non.commercial.use.only=Non-commercial use only message.personal.license=Personal License aboutbox.build.number=Build #{0} @@ -281,7 +266,6 @@ aboutbox.build.date=Built on {0} aboutbox.jdk=JDK: {0} aboutbox.vm=VM: {0} aboutbox.vendor=Vendor: {0} -message.license.you.will.need.upgrade=Your license key will expire on {0}.
You will need an upgrade to run {1}.
To learn more about upgrades, visit {2} title.warning=Warning message.upgrade.from.previous.required={0} requires an upgrade from previous versions.
You can purchase a license key from the JetBrains website at
{1} title.upgrade.needed=Upgrade Needed @@ -305,7 +289,6 @@ title.unable.to.save.data=Unable to Save Data link.click.here.to.license.server.info=More info link.purchase.commercial.license=To purchase a commercial license, please visit license.panel.current.license.description=The license will expire on {0,date,MMMM dd, yyyy} -license.panel.autodescovered.license.server.tile=Default license server license.panel.buildit.evaluation.expires.in=Remains {0} day(s) editbox.license.user.name=User name: editbox.license.license.key=License key: @@ -314,7 +297,6 @@ radio.license.data=Enter license &data radio.evaluate=&Evaluate for free for 30 days radio.default.license.server=&Default License Server action.activate.tool.window=Activate {0} window -action.change.splitter.orientations=Change Splitter O_rientations error.checkforupdates.connection.failed=Connection failed. Please check your network connection and try again. title.connection.error=Connection Error editbox.export.settings.to=Export settings to: @@ -336,7 +318,6 @@ action.create.annotation.type=Create @interface prompt.enter.annotation.type.name=Enter a new @interface name: title.new.annotation.type=New @interface title.cannot.create.annotation.type=Cannot Create @interface -progress.creating.annotation.type=Creating @interface {0}.{1} action.create.new.class=Create New Class action.create.new.filetype=Create New {0} action.description.create.new.file=Create New {0} @@ -363,10 +344,8 @@ progress.creating.package=Creating package {0}.{1} command.create.directory=Create directory command.create.package=Create package action.create.new.enum=Create New Enum -prompt.enter.new.enum.name=Enter a new enum name: title.new.enum=New Enum title.cannot.create.enum=Cannot Create Enum -progress.creating.enum=Creating enum {0}.{1} command.create.enum=Create enum action.create.new.file=Create New File prompt.enter.new.file.name=Enter a new file name: @@ -432,7 +411,6 @@ command.find.previous=Find Previous command.select.all=Select All message.no.targets.available=No targets available in this context title.popup.select.target=Select Target -title.popup.select.subtarget=Select in {0} title.popup.recent.files=Recent Files action.split.vertically=Split _Vertically action.split.horizontally=Split Hori_zontally @@ -452,8 +430,6 @@ action.toolwindow.hierarchy=Hierarch&y action.toolwindow.todo=TODO action.toolwindow.inspection=I&nspection action.toolwindow.favorites=Favorites -action.unsplit=Un_split -action.unsplit.all=U_nsplit All macro.classpath.entry=Entry in the classpath the element belongs to macro.project.classpath=Project's classpath macro.column.number=Column number @@ -633,7 +609,6 @@ node.todo.found.items={0} (found {1} {1,choice,1#item|2#items}) node.todo.items={0} ({1} {1, choice, 1#item|2#items}) title.appearance=Appearance group.window.options= Window Options -checkox.show.buttons.for.disabled.tool.windows=Show buttons for disabled tool windows checkbox.show.memory.indicator=Show memory indicator checkbox.show.tool.window.bars=Show tool window bars checkbox.show.tool.window.numbers=Show tool window numbers @@ -656,10 +631,6 @@ checkbox.use.antialiased.font.in.editor=Use antialiased font # this string must start with "IDEA" idea.default.look.and.feel=IDEA (4.5 default) error.cannot.set.look.and.feel=Cannot set {0} look and feel -action.add.customization.schema=Add customization scheme -customizations.schema.default=default -action.copy.customization.schema=Copy scheme -action.remove.customization.schema=Remove customization scheme error.adding.action.without.icon.to.toolbar=You are adding an action without icon to the toolbar. The default icon will be added to this action. title.unable.to.add.action.without.icon.to.toolbar=Unable to add action without icon to the toolbar error.please.specify.new.name.for.schema=Please, specify new name for scheme ''{0}''. @@ -670,13 +641,10 @@ action.choose.actions.to.add=Choose Actions To Add button.set.icon=&Set icon label.icon.path=Icon Path: button.edit.action.icon=Edit Action &Icon -editbox.schema.name=Scheme Name: -editbox.schema.description=Description: button.add.separator=Add &Separator button.move.up.u=Move &Up button.move.down.d=Move &Down button.add.action.after=Add a&fter... -customization.schema.default.description=Default unmodified customization scheme title.custom.actions.schemas=Custom Actions Schemes title.customizations=Menus and Toolbars label.choosebyname.no.matches.found=(no matches found) @@ -873,21 +841,12 @@ element.of.interface=of interface element.of.class=of class prompt.do.you.want.to.action_verb.the.method.from_class=Do you want to {0} the base {1,choice,1#method|2#methods}? jar.no.java.modules.in.project.error=There are no Java modules found in the project.\nOnly Java modules can be jarred. -jar.no.java.modules.in.project.title=No Modules To Jar Found jar.build.progress=Building jar {0}... -jar.build.success.message=Jar has been built in {0} -jar.build.cancelled=Jar creation has been cancelled jar.build.error.title=Error Creating Jar jar.build.progress.title=Building Jar -jar.build.processing.file.progress=Processing file {0} ... -error.message.jar.build.cannot.create.temporary.file.in.0=Cannot create temporary file in ''{0}'' -jar.build.cannot.overwrite.error=Cannot overwrite file ''{0}''. Copy has been saved to ''{1}''. -jar.build.module.presentable.name=Build Jars for module ''{0}'' jar.build.path=Jar file &path: jar.build.main.class=Main &class: jar.build.dialog.title=Build Jars -jar.build.save.title=Save Jar File -jar.build.main.class.title=Choose Main Class jar.build.class.not.found=Class ''{0}'' not found help.not.found.error=Help not found for {0} help.not.found.title=Help Not Found @@ -918,7 +877,6 @@ updates.check.period.on.startup=On every start updates.check.period.daily=Daily updates.check.period.weekly=Weekly updates.check.period.monthly=Monthly -updates.current.build.unknown=N/A updates.last.check.never=Never updates.settings.caption.1=$PRODUCT$ can automatically check for new and updated versions of itself, using your internet connection (when active). updates.settings.check.now.button=Check Now @@ -942,14 +900,11 @@ jar.build.on.make=Build jars on &make jar.build.modules.to.jar=Choose Modules to Jar jar.build.module.0.jar.settings=Module ''{0}'' Jar Settings jar.build.include.in.jar.file=Include in jar file: -jar.build.button=&Build search.textfield.title=&Search: select.in.scope=Scope scope.view.title=Scope select.in.title.project.view=Project View several.plugins.depend.on.0.continue.to.remove=Several plugins depend on {0}. Continue to remove? -project.view.combo.label=View &as: - child.tag.0.should.be.defined=''{0}'' child tag should be defined attribute.0.should.be.defined=''{0}'' attribute should be defined value.must.not.be.empty=Value must not be empty @@ -1060,7 +1015,6 @@ plugin.deleted.status.tooltip=Plugin will be deleted on next startup plugin.outdated.version.status.tooltip=Plugin has newer version plugin.download.status.tooltip=Plugin will be activated on next startup plugin.is.already.installed.status.tooltip=Plugin is installed -update.plugins.shutdown.action=Update and &Shutdown update.plugins.update.action=&Update fail.open.project.message=Unable to open project from ''{0}'' disabled.plugins.warning.message=

  • Plugin \"{0}\" won''t be able to load because required {2, choice, 1#plugin|2#plugins} {1} {2, choice, 1#is|2#are} disabled.
  • diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/actions/GroovyTemplatesFactory.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/actions/GroovyTemplatesFactory.java index 228fbddfeb83..960e458a0424 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/actions/GroovyTemplatesFactory.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/actions/GroovyTemplatesFactory.java @@ -40,13 +40,12 @@ public class GroovyTemplatesFactory implements FileTemplateGroupDescriptorFactor } } - private static GroovyTemplatesFactory myInstance = null; + private static class GroovyTemplatesFactoryHolder { + private static final GroovyTemplatesFactory myInstance = new GroovyTemplatesFactory(); + } public static GroovyTemplatesFactory getInstance() { - if (myInstance == null) { - myInstance = new GroovyTemplatesFactory(); - } - return myInstance; + return GroovyTemplatesFactoryHolder.myInstance; } private final ArrayList myCustomTemplates = new ArrayList(); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/MergeSourceDetailsAction.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/MergeSourceDetailsAction.java index e5a3dd32cebc..3eba3a97499f 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/MergeSourceDetailsAction.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/MergeSourceDetailsAction.java @@ -30,7 +30,6 @@ import javax.swing.*; import java.awt.event.KeyEvent; public class MergeSourceDetailsAction extends AnAction implements DumbAware { - private static Icon myIcon; @Override public void update(AnActionEvent e) { @@ -39,11 +38,12 @@ public class MergeSourceDetailsAction extends AnAction implements DumbAware { e.getPresentation().setEnabled(enabled(e)); } + private static class IconHolder { + private static final Icon myIcon = IconLoader.getIcon("/icons/mergeSourcesDetails.png"); + } + private Icon getIcon() { - if (myIcon == null) { - myIcon = IconLoader.getIcon("/icons/mergeSourcesDetails.png"); - } - return myIcon; + return IconHolder.myIcon; } public void registerSelf(final JComponent comp) { From fb47d0746cb17a41c9419a85273558e1bcaf6336 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Fri, 12 Mar 2010 17:38:25 +0300 Subject: [PATCH 45/93] cleanup --- .../com/intellij/compiler/impl/rmiCompiler/RmicCompiler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/rmiCompiler/RmicCompiler.java b/java/compiler/impl/src/com/intellij/compiler/impl/rmiCompiler/RmicCompiler.java index 75aef5a38dbf..04972a1e68f3 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/rmiCompiler/RmicCompiler.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/rmiCompiler/RmicCompiler.java @@ -110,7 +110,7 @@ public class RmicCompiler implements ClassPostProcessingCompiler{ } if (!VfsUtil.isAncestor(outputDir, outputClassFile, true)) { - LOG.assertTrue(false, outputClassFile.getPath() + " should be located under the output root " + outputDir.getPath()); + LOG.error(outputClassFile.getPath() + " should be located under the output root " + outputDir.getPath()); } final ProcessingItem item = createProcessingItem(module, outputClassFile, outputDir, From 6b55cf0b05dc99234ccda00f9b25b3da703dae42 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Fri, 12 Mar 2010 17:40:00 +0300 Subject: [PATCH 46/93] test fix: implement replaceString in DocumentWindow --- .../completion/DummyIdentifierPatcher.java | 5 +- .../injected/editor/DocumentWindowImpl.java | 47 +++++++++++++++++-- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/platform/lang-api/src/com/intellij/codeInsight/completion/DummyIdentifierPatcher.java b/platform/lang-api/src/com/intellij/codeInsight/completion/DummyIdentifierPatcher.java index b2d38b8f14b6..5f058270a5fb 100644 --- a/platform/lang-api/src/com/intellij/codeInsight/completion/DummyIdentifierPatcher.java +++ b/platform/lang-api/src/com/intellij/codeInsight/completion/DummyIdentifierPatcher.java @@ -32,8 +32,9 @@ public class DummyIdentifierPatcher extends FileCopyPatcher { public void patchFileCopy(@NotNull final PsiFile fileCopy, @NotNull final Document document, @NotNull final OffsetMap map) { if (StringUtil.isEmpty(myDummyIdentifier)) return; - document.replaceString(map.getOffset(CompletionInitializationContext.START_OFFSET), map.getOffset(CompletionInitializationContext.SELECTION_END_OFFSET), - myDummyIdentifier); + int startOffset = map.getOffset(CompletionInitializationContext.START_OFFSET); + int endOffset = map.getOffset(CompletionInitializationContext.SELECTION_END_OFFSET); + document.replaceString(startOffset, endOffset, myDummyIdentifier); } @Override diff --git a/platform/lang-impl/src/com/intellij/injected/editor/DocumentWindowImpl.java b/platform/lang-impl/src/com/intellij/injected/editor/DocumentWindowImpl.java index df80a4c384e5..7ea0829403ab 100644 --- a/platform/lang-impl/src/com/intellij/injected/editor/DocumentWindowImpl.java +++ b/platform/lang-impl/src/com/intellij/injected/editor/DocumentWindowImpl.java @@ -27,6 +27,7 @@ import com.intellij.openapi.editor.ex.*; import com.intellij.openapi.editor.impl.DocumentImpl; import com.intellij.openapi.editor.markup.MarkupModel; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.ProperTextRange; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.UserDataHolderBase; @@ -40,8 +41,8 @@ import org.jetbrains.annotations.Nullable; import java.beans.PropertyChangeListener; import java.util.ArrayList; -import java.util.List; import java.util.Collections; +import java.util.List; /** * @author Alexey @@ -254,9 +255,47 @@ public class DocumentWindowImpl extends UserDataHolderBase implements Disposable startOffset += perfixLength; endOffset -= suffixLength; s = s.subSequence(perfixLength, s.length() - suffixLength); - - deleteString(startOffset, endOffset); - insertString(startOffset, s); + + doReplaceString(startOffset, endOffset, s); + } + + private void doReplaceString(int startOffset, int endOffset, CharSequence s) { + assert intersectWithEditable(new TextRange(startOffset, startOffset)) != null; + assert intersectWithEditable(new TextRange(endOffset, endOffset)) != null; + + List> hostRangesToModify = new ArrayList>(myShreds.size()); + + int offset = startOffset; + int curRangeStart = 0; + for (int i = 0; i < myShreds.size(); i++) { + PsiLanguageInjectionHost.Shred shred = myShreds.get(i); + curRangeStart += shred.prefix.length(); + if (offset < curRangeStart) offset = curRangeStart; + RangeMarker hostRange = shred.getHostRangeMarker(); + if (!hostRange.isValid()) continue; + int hostRangeLength = hostRange.getEndOffset() - hostRange.getStartOffset(); + TextRange range = TextRange.from(curRangeStart, hostRangeLength); + if (range.contains(offset) || range.getEndOffset() == offset/* in case of inserting at the end*/) { + TextRange rangeToModify = new TextRange(offset, Math.min(range.getEndOffset(), endOffset)); + TextRange hostRangeToModify = rangeToModify.shiftRight(hostRange.getStartOffset() - curRangeStart); + CharSequence toReplace = i == myShreds.size() - 1 ? s : s.subSequence(0, Math.min(hostRangeToModify.getLength(), s.length())); + s = s.subSequence(toReplace.length(), s.length()); + hostRangesToModify.add(Pair.create(hostRangeToModify, toReplace)); + offset = rangeToModify.getEndOffset(); + } + curRangeStart += hostRangeLength; + curRangeStart += shred.suffix.length(); + if (curRangeStart >= endOffset) break; + } + + int delta = 0; + for (Pair pair : hostRangesToModify) { + TextRange hostRange = pair.getFirst(); + CharSequence replace = pair.getSecond(); + + myDelegate.replaceString(hostRange.getStartOffset() + delta, hostRange.getEndOffset() + delta, replace); + delta -= hostRange.getLength() - replace.length(); + } } public boolean isWritable() { From 1c53544c3c9d638358bb6abb4db8302584e65e82 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Mon, 15 Mar 2010 12:38:08 +0300 Subject: [PATCH 47/93] concurrency --- .../psi/impl/search/PsiSearchHelperImpl.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java index e24fdb3b6ad7..b0aa304c1cda 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java @@ -158,11 +158,11 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { return processElementsWithWord(occurenceProcessor, searchScope, identifier, UsageSearchContext.IN_COMMENTS, true); } - public boolean processElementsWithWord(@NotNull TextOccurenceProcessor processor, + public boolean processElementsWithWord(@NotNull final TextOccurenceProcessor processor, @NotNull SearchScope searchScope, - @NotNull String text, + @NotNull final String text, short searchContext, - boolean caseSensitively) { + final boolean caseSensitively) { if (text.length() == 0) { throw new IllegalArgumentException("Cannot search for elements with empty text"); } @@ -179,10 +179,11 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { PsiElement[] scopeElements = scope.getScope(); final boolean ignoreInjectedPsi = scope.isIgnoreInjectedPsi(); - for (final PsiElement scopeElement : scopeElements) { - if (!processElementsWithWordInScopeElement(scopeElement, processor, text, caseSensitively, ignoreInjectedPsi)) return false; - } - return true; + return JobUtil.invokeConcurrentlyUnderMyProgress(Arrays.asList(scopeElements), new Processor() { + public boolean process(PsiElement scopeElement) { + return processElementsWithWordInScopeElement(scopeElement, processor, text, caseSensitively, ignoreInjectedPsi); + } + }, "textsearch"); } } @@ -200,7 +201,7 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { }).booleanValue(); } - private boolean processElementsWithTextInGlobalScope(final TextOccurenceProcessor processor, + private boolean processElementsWithTextInGlobalScope(@NotNull final TextOccurenceProcessor processor, final GlobalSearchScope scope, final StringSearcher searcher, final short searchContext, From 176d4463b57b989c08129328cefcd8ff2b2ab6d0 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Mon, 15 Mar 2010 12:41:21 +0300 Subject: [PATCH 48/93] more reliable inspection --- .../daemon/impl/PostHighlightingPass.java | 8 +-- .../intellij/psi/search/PsiSearchHelper.java | 6 ++ .../com/intellij/psi/util/PsiTreeUtil.java | 14 ++--- .../psi/impl/search/PsiSearchHelperImpl.java | 9 +-- ...ticMethodOnlyUsedInOneClassInspection.java | 61 +++++-------------- ...eWithOnlyOneDirectInheritorInspection.java | 5 +- .../properties/UnusedPropertyInspection.java | 33 +++++----- 7 files changed, 54 insertions(+), 82 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/PostHighlightingPass.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/PostHighlightingPass.java index 7c60ae78f1fb..b4e59b8238af 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/PostHighlightingPass.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/PostHighlightingPass.java @@ -55,11 +55,11 @@ import com.intellij.profile.codeInspection.InspectionProjectProfileManager; import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.impl.PsiClassImplUtil; -import com.intellij.psi.impl.search.PsiSearchHelperImpl; import com.intellij.psi.impl.source.jsp.jspJava.JspxImportStatement; import com.intellij.psi.jsp.JspFile; import com.intellij.psi.jsp.JspSpiUtil; import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.search.PsiSearchHelper; import com.intellij.psi.search.SearchScope; import com.intellij.psi.search.searches.MethodReferencesSearch; import com.intellij.psi.search.searches.OverridingMethodsSearch; @@ -525,12 +525,12 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { // some classes may have references from within XML outside dependent modules, e.g. our actions if (member instanceof PsiClass) scope = scope.uniteWith(GlobalSearchScope.projectScope(myProject)); - PsiSearchHelperImpl.Result cheapEnough = ((PsiSearchHelperImpl)myFile.getManager().getSearchHelper()).isItCheapEnoughToSearch(myFile, name, scope); - if (cheapEnough == PsiSearchHelperImpl.Result.TOO_MANY_OCCURRENCES) return false; + PsiSearchHelper.SearchCostResult cheapEnough = myFile.getManager().getSearchHelper().isCheapEnoughToSearch(name, scope, myFile); + if (cheapEnough == PsiSearchHelper.SearchCostResult.TOO_MANY_OCCURRENCES) return false; //search usages if it cheap //if count is 0 there is no usages since we've called myRefCountHolder.isReferenced() before - if (cheapEnough == PsiSearchHelperImpl.Result.ZERO_OCCURRENCES && !canbeReferencedViaWeirdNames(member)) return true; + if (cheapEnough == PsiSearchHelper.SearchCostResult.ZERO_OCCURRENCES && !canbeReferencedViaWeirdNames(member)) return true; Query query = member instanceof PsiMethod ? MethodReferencesSearch.search((PsiMethod)member, scope, true) diff --git a/platform/lang-api/src/com/intellij/psi/search/PsiSearchHelper.java b/platform/lang-api/src/com/intellij/psi/search/PsiSearchHelper.java index a3d1183f9f90..341b8206908f 100644 --- a/platform/lang-api/src/com/intellij/psi/search/PsiSearchHelper.java +++ b/platform/lang-api/src/com/intellij/psi/search/PsiSearchHelper.java @@ -179,4 +179,10 @@ public interface PsiSearchHelper { @NotNull String text, short searchContext, boolean caseSensitive); + + SearchCostResult isCheapEnoughToSearch(@NotNull String name, @NotNull GlobalSearchScope scope, @Nullable PsiFile fileToIgnoreOccurencesIn); + + enum SearchCostResult { + ZERO_OCCURRENCES, FEW_OCCURRENCES, TOO_MANY_OCCURRENCES + } } diff --git a/platform/lang-api/src/com/intellij/psi/util/PsiTreeUtil.java b/platform/lang-api/src/com/intellij/psi/util/PsiTreeUtil.java index bbbea29c8583..b47dc9f8958f 100644 --- a/platform/lang-api/src/com/intellij/psi/util/PsiTreeUtil.java +++ b/platform/lang-api/src/com/intellij/psi/util/PsiTreeUtil.java @@ -28,6 +28,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; public class PsiTreeUtil { @@ -49,8 +50,8 @@ public class PsiTreeUtil { public static boolean isAncestor(@Nullable PsiElement ancestor, @NotNull PsiElement element, boolean strict) { if (ancestor == null) return false; // fast path to avoid loading tree - if ((ancestor instanceof StubBasedPsiElement && (((StubBasedPsiElement)ancestor).getStub() != null)) || - (element instanceof StubBasedPsiElement && ((StubBasedPsiElement)element).getStub() != null)) { + if (ancestor instanceof StubBasedPsiElement && ((StubBasedPsiElement)ancestor).getStub() != null || + element instanceof StubBasedPsiElement && ((StubBasedPsiElement)element).getStub() != null) { if (ancestor.getContainingFile() != element.getContainingFile()) return false; } @@ -345,7 +346,7 @@ public class PsiTreeUtil { @NotNull public static PsiElement[] collectElements(@Nullable PsiElement element, @NotNull PsiElementFilter filter) { - PsiElementProcessor.CollectFilteredElements processor = new PsiElementProcessor.CollectFilteredElements(filter); + PsiElementProcessor.CollectFilteredElements processor = new PsiElementProcessor.CollectFilteredElements(filter); processElements(element, processor); return processor.toArray(); } @@ -551,7 +552,8 @@ public class PsiTreeUtil { return false; } - public static PsiElement[] filterAncestors(PsiElement[] elements) { + @NotNull + public static PsiElement[] filterAncestors(@NotNull PsiElement[] elements) { if (LOG.isDebugEnabled()) { for (PsiElement element : elements) { LOG.debug("element = " + element); @@ -559,9 +561,7 @@ public class PsiTreeUtil { } ArrayList filteredElements = new ArrayList(); - for (PsiElement element : elements) { - filteredElements.add(element); - } + filteredElements.addAll(Arrays.asList(elements)); int previousSize; do { diff --git a/platform/lang-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java index b0aa304c1cda..e5747b88b7e1 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java @@ -425,10 +425,7 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { myManager.getCacheManager().processFilesWithWord(processor, word, UsageSearchContext.IN_STRINGS, scope, true); } - public static enum Result { - ZERO_OCCURRENCES, FEW_OCCURRENCES, TOO_MANY_OCCURRENCES - } - public Result isItCheapEnoughToSearch(@Nullable final PsiFile fileToIgnoreOccurencesIn, @NotNull String name, @NotNull GlobalSearchScope scope) { + public SearchCostResult isCheapEnoughToSearch(@NotNull String name, @NotNull GlobalSearchScope scope, @Nullable final PsiFile fileToIgnoreOccurencesIn) { final int[] count = {0}; if (!processFilesWithText(scope, UsageSearchContext.ANY, true, name, new Processor() { public boolean process(PsiFile file) { @@ -439,11 +436,11 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { } } })) { - return Result.TOO_MANY_OCCURRENCES; + return SearchCostResult.TOO_MANY_OCCURRENCES; } synchronized (count) { - return count[0] == 0 ? Result.ZERO_OCCURRENCES : Result.FEW_OCCURRENCES; + return count[0] == 0 ? SearchCostResult.ZERO_OCCURRENCES : SearchCostResult.FEW_OCCURRENCES; } } } diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/abstraction/StaticMethodOnlyUsedInOneClassInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/abstraction/StaticMethodOnlyUsedInOneClassInspection.java index 7713f9969ef8..d63719f8ecbf 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/abstraction/StaticMethodOnlyUsedInOneClassInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/abstraction/StaticMethodOnlyUsedInOneClassInspection.java @@ -39,7 +39,7 @@ import com.siyeh.ig.psiutils.ClassUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; public class StaticMethodOnlyUsedInOneClassInspection extends BaseInspection { @@ -105,10 +105,10 @@ public class StaticMethodOnlyUsedInOneClassInspection } public BaseInspectionVisitor buildVisitor() { - return new StaticmethodOnlyUsedInOneClassVisitor(); + return new StaticMethodOnlyUsedInOneClassVisitor(); } - private static class StaticmethodOnlyUsedInOneClassVisitor + private static class StaticMethodOnlyUsedInOneClassVisitor extends BaseInspectionVisitor { @Override public void visitMethod(PsiMethod method) { @@ -151,23 +151,16 @@ public class StaticMethodOnlyUsedInOneClassInspection } private static class UsageProcessor implements Processor { - - private PsiClass usageClass = null; + private final AtomicReference foundClass = new AtomicReference(); public boolean process(PsiReference reference) { ProgressManager.checkCanceled(); final PsiElement element = reference.getElement(); - final PsiClass usageClass = - ClassUtils.getContainingClass(element); - synchronized (this) { - if (this.usageClass != null && - !this.usageClass.equals(usageClass)) { - this.usageClass = null; - return false; - } - this.usageClass = usageClass; - } - return true; + PsiClass usageClass = ClassUtils.getContainingClass(element); + if (usageClass == null) return true; + if (foundClass.compareAndSet(null, usageClass)) return true; + PsiClass aClass = foundClass.get(); + return usageClass.getManager().areElementsEquivalent(aClass, usageClass); } /** @@ -183,42 +176,18 @@ public class StaticMethodOnlyUsedInOneClassInspection final String name = method.getName(); final GlobalSearchScope scope = GlobalSearchScope.allScope(method.getProject()); - final FindUsagesCostProcessor costProcessor = - new FindUsagesCostProcessor(); - searchHelper.processAllFilesWithWord(name, scope, - costProcessor, true); - if (costProcessor.isCostTooHigh()) { - return null; - } + if (searchHelper.isCheapEnoughToSearch(name, scope, null) + == PsiSearchHelper.SearchCostResult.TOO_MANY_OCCURRENCES) return null; progressManager.runProcess(new Runnable() { public void run() { final Query query = MethodReferencesSearch.search(method); - query.forEach(UsageProcessor.this); + if (!query.forEach(UsageProcessor.this)) { + foundClass.set(null); + } } }, null); - synchronized (this) { - return usageClass; - } - } - - private static class FindUsagesCostProcessor - implements Processor { - - private final AtomicInteger counter = new AtomicInteger(); - private volatile boolean costTooHigh = false; - - public boolean process(PsiFile psiFile) { - if (counter.incrementAndGet() >= 10) { - costTooHigh = true; - return false; - } - return true; - } - - public boolean isCostTooHigh() { - return costTooHigh; - } + return foundClass.get(); } } } diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/j2me/InterfaceWithOnlyOneDirectInheritorInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/j2me/InterfaceWithOnlyOneDirectInheritorInspection.java index 00b0c57e7f0a..42d104d30096 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/j2me/InterfaceWithOnlyOneDirectInheritorInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/j2me/InterfaceWithOnlyOneDirectInheritorInspection.java @@ -26,8 +26,6 @@ import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; import org.jetbrains.annotations.NotNull; -import java.util.Collection; - public class InterfaceWithOnlyOneDirectInheritorInspection extends BaseInspection { @@ -71,8 +69,7 @@ public class InterfaceWithOnlyOneDirectInheritorInspection ClassInheritorsSearch.search(aClass, searchScope, false).forEach(new PsiElementProcessorAdapter(processor)); } }, null); - final Collection collection = processor.getCollection(); - return collection.size() == 1; + return !processor.isOverflow(); } } } diff --git a/plugins/properties/src/com/intellij/lang/properties/UnusedPropertyInspection.java b/plugins/properties/src/com/intellij/lang/properties/UnusedPropertyInspection.java index 63720e08e1ec..c04f20f2333e 100644 --- a/plugins/properties/src/com/intellij/lang/properties/UnusedPropertyInspection.java +++ b/plugins/properties/src/com/intellij/lang/properties/UnusedPropertyInspection.java @@ -29,8 +29,8 @@ import com.intellij.openapi.progress.ProgressManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiReference; -import com.intellij.psi.impl.search.PsiSearchHelperImpl; import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.search.PsiSearchHelper; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.util.Processor; import com.intellij.util.SmartList; @@ -68,23 +68,26 @@ public class UnusedPropertyInspection extends PropertySuppressableInspectionBase original.setText(PropertiesBundle.message("searching.for.property.key.progress.text", property.getUnescapedKey())); } - PsiSearchHelperImpl.Result cheapEnough = ((PsiSearchHelperImpl)file.getManager().getSearchHelper()).isItCheapEnoughToSearch(file, property.getName(), searchScope); - if (cheapEnough == PsiSearchHelperImpl.Result.TOO_MANY_OCCURRENCES) return true; + String name = property.getName(); + if (name == null) return true; + PsiSearchHelper.SearchCostResult cheapEnough = file.getManager().getSearchHelper().isCheapEnoughToSearch(name, searchScope, file); + if (cheapEnough == PsiSearchHelper.SearchCostResult.TOO_MANY_OCCURRENCES) return true; - final PsiReference usage = cheapEnough == PsiSearchHelperImpl.Result.ZERO_OCCURRENCES ? null : + final PsiReference usage = cheapEnough == PsiSearchHelper.SearchCostResult.ZERO_OCCURRENCES ? null : ReferencesSearch.search(property, searchScope, false).findFirst(); - if (usage == null) { - final ASTNode propertyNode = property.getNode(); - assert propertyNode != null; + if (usage != null) { + return true; + } + final ASTNode propertyNode = property.getNode(); + assert propertyNode != null; - ASTNode[] nodes = propertyNode.getChildren(null); - PsiElement key = nodes.length == 0 ? property : nodes[0].getPsi(); - String description = PropertiesBundle.message("unused.property.problem.descriptor.name"); - ProblemDescriptor descriptor = manager.createProblemDescriptor(key, description, RemovePropertyLocalFix.INSTANCE, ProblemHighlightType.LIKE_UNUSED_SYMBOL, - isOnTheFly); - synchronized (descriptors) { - descriptors.add(descriptor); - } + ASTNode[] nodes = propertyNode.getChildren(null); + PsiElement key = nodes.length == 0 ? property : nodes[0].getPsi(); + String description = PropertiesBundle.message("unused.property.problem.descriptor.name"); + ProblemDescriptor descriptor = manager.createProblemDescriptor(key, description, RemovePropertyLocalFix.INSTANCE, ProblemHighlightType.LIKE_UNUSED_SYMBOL, + isOnTheFly); + synchronized (descriptors) { + descriptors.add(descriptor); } return true; From d2fba9894ad410d010f4e72c1eb95c2d02cc769c Mon Sep 17 00:00:00 2001 From: irengrig Date: Mon, 15 Mar 2010 13:38:09 +0300 Subject: [PATCH 49/93] VCS: diff in shelved changes, apply patch: lazy load contents --- .../actions/DiffRequestPresentableProxy.java | 62 +++++++++++++++++++ .../changes/patch/FilePatchInProgress.java | 30 +++++---- .../shelf/DiffShelvedChangesAction.java | 40 +++++++----- 3 files changed, 103 insertions(+), 29 deletions(-) create mode 100644 platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/DiffRequestPresentableProxy.java diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/DiffRequestPresentableProxy.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/DiffRequestPresentableProxy.java new file mode 100644 index 000000000000..74c6886fc7b5 --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/DiffRequestPresentableProxy.java @@ -0,0 +1,62 @@ +/* + * Copyright 2000-2010 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.openapi.vcs.changes.actions; + +import com.intellij.openapi.actionSystem.AnAction; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public abstract class DiffRequestPresentableProxy implements DiffRequestPresentable { + private DiffRequestPresentable myDelegate; + private List myCachedActions; + private MyResult myStepResult; + + @Nullable + protected abstract DiffRequestPresentable init(); + + @Nullable + private DiffRequestPresentable initRequest() { + if (myDelegate == null) { + myDelegate = init(); + } + return myDelegate; + } + + public List createActions(ShowDiffAction.DiffExtendUIFactory uiFactory) { + if (myCachedActions == null) { + myCachedActions = initRequest().createActions(uiFactory); + } + return myCachedActions; + } + + public MyResult step() { + final DiffRequestPresentable request = initRequest(); + if (request == null) { + return new MyResult(null, DiffPresentationReturnValue.removeFromList); + } + myStepResult = request.step(); + return myStepResult; + } + + public boolean haveStuff() { + final DiffRequestPresentable request = initRequest(); + if (request == null) { + return false; + } + return request.haveStuff(); + } +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/FilePatchInProgress.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/FilePatchInProgress.java index a785c66343d6..780784f9e287 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/FilePatchInProgress.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/FilePatchInProgress.java @@ -32,6 +32,7 @@ import com.intellij.openapi.vcs.changes.CurrentContentRevision; import com.intellij.openapi.vcs.changes.SimpleContentRevision; import com.intellij.openapi.vcs.changes.actions.ChangeDiffRequestPresentable; import com.intellij.openapi.vcs.changes.actions.DiffRequestPresentable; +import com.intellij.openapi.vcs.changes.actions.DiffRequestPresentableProxy; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.vcsUtil.VcsUtil; import org.jetbrains.annotations.NotNull; @@ -220,19 +221,24 @@ public class FilePatchInProgress implements Strippable { @Nullable public DiffRequestPresentable createDiffRequestPresentable(final Project project) { - if (myPatchInProgress.isConflictingChange()) { - final ApplyPatchForBaseRevisionTexts texts = ApplyPatchForBaseRevisionTexts - .create(project, myPatchInProgress.getCurrentBase(), new FilePathImpl(myPatchInProgress.getCurrentBase()), - myPatchInProgress.getPatch()); - if ((texts != null) && - (ApplyPatchStatus.SUCCESS.equals(texts.getStatus()) || ApplyPatchStatus.ALREADY_APPLIED.equals(texts.getStatus()))) { - return new MergedDiffRequestPresentable(project, texts, - myPatchInProgress.getCurrentBase(), myPatchInProgress.getPatch().getAfterVersionId()); + return new DiffRequestPresentableProxy() { + @Override + protected DiffRequestPresentable init() { + if (myPatchInProgress.isConflictingChange()) { + final ApplyPatchForBaseRevisionTexts texts = ApplyPatchForBaseRevisionTexts + .create(project, myPatchInProgress.getCurrentBase(), new FilePathImpl(myPatchInProgress.getCurrentBase()), + myPatchInProgress.getPatch()); + if ((texts != null) && + (ApplyPatchStatus.SUCCESS.equals(texts.getStatus()) || ApplyPatchStatus.ALREADY_APPLIED.equals(texts.getStatus()))) { + return new MergedDiffRequestPresentable(project, texts, + myPatchInProgress.getCurrentBase(), myPatchInProgress.getPatch().getAfterVersionId()); + } + return null; + } else { + return new ChangeDiffRequestPresentable(project, PatchChange.this); + } } - return null; - } else { - return new ChangeDiffRequestPresentable(project, this); - } + }; } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesAction.java index 8133ecc3c6b2..850be8768999 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesAction.java @@ -29,6 +29,7 @@ import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vcs.changes.ContentRevision; import com.intellij.openapi.vcs.changes.actions.ChangeDiffRequestPresentable; import com.intellij.openapi.vcs.changes.actions.DiffRequestPresentable; +import com.intellij.openapi.vcs.changes.actions.DiffRequestPresentableProxy; import com.intellij.openapi.vcs.changes.actions.ShowDiffAction; import com.intellij.openapi.vcs.changes.patch.ApplyPatchForBaseRevisionTexts; import com.intellij.openapi.vcs.changes.patch.MergedDiffRequestPresentable; @@ -51,7 +52,7 @@ public class DiffShelvedChangesAction extends AnAction implements DumbAware { } public static void showShelvedChangesDiff(final DataContext dc) { - Project project = PlatformDataKeys.PROJECT.getData(dc); + final Project project = PlatformDataKeys.PROJECT.getData(dc); ShelvedChangeList[] changeLists = ShelvedChangesViewManager.SHELVED_CHANGELIST_KEY.getData(dc); if (changeLists == null) { changeLists = ShelvedChangesViewManager.SHELVED_RECYCLED_CHANGELIST_KEY.getData(dc); @@ -68,7 +69,7 @@ public class DiffShelvedChangesAction extends AnAction implements DumbAware { int toSelectIdx = 0; final ArrayList diffRequestPresentables = new ArrayList(); - ApplyPatchContext context = new ApplyPatchContext(project.getBaseDir(), 0, false, false); + final ApplyPatchContext context = new ApplyPatchContext(project.getBaseDir(), 0, false, false); final PatchesPreloader preloader = new PatchesPreloader(); final List missing = new LinkedList(); @@ -76,7 +77,7 @@ public class DiffShelvedChangesAction extends AnAction implements DumbAware { final Change change = shelvedChange.getChange(project); final String beforePath = shelvedChange.getBeforePath(); try { - VirtualFile f = FilePatch.findPatchTarget(context, beforePath, shelvedChange.getAfterPath(), beforePath == null); + final VirtualFile f = FilePatch.findPatchTarget(context, beforePath, shelvedChange.getAfterPath(), beforePath == null); if ((f == null) || (! f.exists())) { if (beforePath != null) { missing.add(beforePath); @@ -84,22 +85,27 @@ public class DiffShelvedChangesAction extends AnAction implements DumbAware { continue; } - if (isConflictingChange(change)) { - TextFilePatch patch = preloader.getPatch(shelvedChange); - if (patch == null) continue; + diffRequestPresentables.add(new DiffRequestPresentableProxy() { + @Override + protected DiffRequestPresentable init() { + if (isConflictingChange(change)) { + TextFilePatch patch = preloader.getPatch(shelvedChange); + if (patch == null) return null; - final FilePath pathBeforeRename = context.getPathBeforeRename(f); + final FilePath pathBeforeRename = context.getPathBeforeRename(f); - final ApplyPatchForBaseRevisionTexts threeTexts = ApplyPatchForBaseRevisionTexts.create(project, f, pathBeforeRename, patch); - if ((threeTexts == null) || (threeTexts.getStatus() == null) || (ApplyPatchStatus.FAILURE.equals(threeTexts.getStatus()))) { - continue; + final ApplyPatchForBaseRevisionTexts threeTexts = ApplyPatchForBaseRevisionTexts.create(project, f, pathBeforeRename, patch); + if ((threeTexts == null) || (threeTexts.getStatus() == null) || (ApplyPatchStatus.FAILURE.equals(threeTexts.getStatus()))) { + return null; + } + + return new MergedDiffRequestPresentable(project, threeTexts, f, "Shelved Version"); + } + else { + return new ChangeDiffRequestPresentable(project, change); + } } - - diffRequestPresentables.add(new MergedDiffRequestPresentable(project, threeTexts, f, "Shelved Version")); - } - else { - diffRequestPresentables.add(new ChangeDiffRequestPresentable(project, change)); - } + }); } catch (IOException e) { continue; @@ -114,7 +120,7 @@ public class DiffShelvedChangesAction extends AnAction implements DumbAware { ChangesViewBalloonProblemNotifier.showMe(project, "Show Diff: Cannot find base for: " + StringUtil.join(missing, ",\n"), MessageType.WARNING); } - ShowDiffAction.showDiffImpl(project, diffRequestPresentables, toSelectIdx, ShowDiffAction.DiffExtendUIFactory.NONE, true); + ShowDiffAction.showDiffImpl(project, diffRequestPresentables, toSelectIdx, ShowDiffAction.DiffExtendUIFactory.NONE, false); } private static class PatchesPreloader { From f237995749166f1b3b5297781475ce435471b164 Mon Sep 17 00:00:00 2001 From: irengrig Date: Mon, 15 Mar 2010 13:56:58 +0300 Subject: [PATCH 50/93] VCS: apply patch, correctly select item in show diff according to grouping by folders toggle --- .../patch/ApplyPatchDifferentiatedDialog.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDifferentiatedDialog.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDifferentiatedDialog.java index 9b68b9efec02..1a840a924823 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDifferentiatedDialog.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDifferentiatedDialog.java @@ -15,6 +15,7 @@ */ package com.intellij.openapi.vcs.changes.patch; +import com.intellij.ide.util.PropertiesComponent; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.diff.impl.patch.FilePatch; import com.intellij.openapi.diff.impl.patch.PatchReader; @@ -817,8 +818,10 @@ public class ApplyPatchDifferentiatedDialog extends DialogWrapper { } private class MyShowDiff extends AnAction { + private final MyChangeComparator myMyChangeComparator; private MyShowDiff() { super("Show Diff", "Show Diff", IconLoader.getIcon("/actions/diff.png")); + myMyChangeComparator = new MyChangeComparator(); } public void update(AnActionEvent e) { @@ -828,7 +831,7 @@ public class ApplyPatchDifferentiatedDialog extends DialogWrapper { public void actionPerformed(AnActionEvent e) { if (myPatches.isEmpty() || (! myContainBasedChanges)) return; final List changes = getAllChanges(); - Collections.sort(changes, MyChangeComparator.getInstance()); + Collections.sort(changes, myMyChangeComparator); final List selectedChanges = myChangesTreeList.getSelectedChanges(); int selectedIdx = 0; @@ -852,14 +855,11 @@ public class ApplyPatchDifferentiatedDialog extends DialogWrapper { } } - private static class MyChangeComparator implements Comparator { - private static final MyChangeComparator ourInstance = new MyChangeComparator(); - - public static MyChangeComparator getInstance() { - return ourInstance; - } - + private class MyChangeComparator implements Comparator { public int compare(FilePatchInProgress.PatchChange o1, FilePatchInProgress.PatchChange o2) { + if (PropertiesComponent.getInstance(myProject).isTrueValue("ChangesBrowser.SHOW_FLATTEN")) { + return o1.getPatchInProgress().getIoCurrentBase().getName().compareTo(o2.getPatchInProgress().getIoCurrentBase().getName()); + } return o1.getPatchInProgress().getIoCurrentBase().compareTo(o2.getPatchInProgress().getIoCurrentBase()); } } From 94d5cf68ecdb0d8c7f8d10476405778c65013eb5 Mon Sep 17 00:00:00 2001 From: Alexey Pegov Date: Mon, 15 Mar 2010 14:40:09 +0300 Subject: [PATCH 51/93] IDEA-20943 Run/Debug Dialog is too big on 15" MacBook Pro --- .../impl/SingleConfigurationConfigurable.form | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/execution/impl/SingleConfigurationConfigurable.form b/platform/lang-impl/src/com/intellij/execution/impl/SingleConfigurationConfigurable.form index 3be743de1a37..12ac18a596a4 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/SingleConfigurationConfigurable.form +++ b/platform/lang-impl/src/com/intellij/execution/impl/SingleConfigurationConfigurable.form @@ -3,7 +3,7 @@ - + @@ -25,15 +25,6 @@ - - - - - - - - - @@ -79,6 +70,22 @@ + + + + + + + + + + + + + + + + From 76cedec032a94835b8c124663ebc7fb8a0b3a6f4 Mon Sep 17 00:00:00 2001 From: Maxim Medvedev Date: Mon, 15 Mar 2010 14:48:35 +0300 Subject: [PATCH 52/93] IDEA-52843: Cannot resolve Groovy boolean property in reading context --- .../lang/psi/impl/statements/GrFieldImpl.java | 22 ++++++--------- .../GrReferenceExpressionImpl.java | 5 ++-- .../typedef/members/GrMethodBaseImpl.java | 2 +- .../lang/psi/util/GroovyPropertyUtils.java | 27 +++++++++++++++---- .../lang/resolve/ResolvePropertyTest.groovy | 23 ++++++++++++++++ 5 files changed, 56 insertions(+), 23 deletions(-) diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrFieldImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrFieldImpl.java index e40babd90f56..10ef8c47d9bf 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrFieldImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrFieldImpl.java @@ -190,26 +190,20 @@ public class GrFieldImpl extends GrVariableBaseImpl implements GrFi final PsiClass clazz = getContainingClass(); name = GroovyPropertyUtils.capitalize(name); GrAccessorMethod getter1 = new GrAccessorMethodImpl(this, false, "get" + name); - if (hasContradictingMethods(getter1, clazz)) getter1 = null; + if (!hasContradictingMethods(getter1, clazz)) { + GrAccessorMethod getter2 = null; + if (PsiType.BOOLEAN.equals(getDeclaredType())) { + getter2 = new GrAccessorMethodImpl(this, false, "is" + name); + if (hasContradictingMethods(getter2, clazz)) getter2 = null; + } - GrAccessorMethod getter2 = null; - if (PsiType.BOOLEAN.equals(getDeclaredType())) { - getter2 = new GrAccessorMethodImpl(this, false, "is" + name); - if (hasContradictingMethods(getter2, clazz)) getter2 = null; - } - - if (getter1 != null || getter2 != null) { - if (getter1 != null && getter2 != null) { + if (getter2 != null) { myGetters = new GrAccessorMethod[]{getter1, getter2}; } - else if (getter1 != null) { + else { myGetters = new GrAccessorMethod[]{getter1}; } - else { - myGetters = new GrAccessorMethod[]{getter2}; - } } - } myGettersInitialized = true; diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java index 8f19b418b1f1..36b6584c70ed 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java @@ -443,13 +443,12 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl implements final boolean isLValue = PsiUtil.isLValue(refExpr); String[] names; names = isLValue ? GroovyPropertyUtils.suggestSettersName(name) : GroovyPropertyUtils.suggestGettersName(name); - List list = new ArrayList(); for (String getterName : names) { AccessorResolverProcessor accessorResolver = new AccessorResolverProcessor(getterName, refExpr, !isLValue); resolveImpl(refExpr, accessorResolver); - list.addAll(Arrays.asList(accessorResolver.getCandidates())); + final GroovyResolveResult[] candidates = accessorResolver.getCandidates(); + if (candidates.length > 0) return candidates; } - if (list.size() > 0) return list.toArray(new GroovyResolveResult[list.size()]); if (fieldCandidates.length > 0) return fieldCandidates; EnumSet kinds = refExpr.getParent() instanceof GrReferenceExpression diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/members/GrMethodBaseImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/members/GrMethodBaseImpl.java index 15384571999d..64ffa0d45b51 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/members/GrMethodBaseImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/members/GrMethodBaseImpl.java @@ -154,7 +154,7 @@ public abstract class GrMethodBaseImpl extends GroovyBaseEl private static final Function ourTypesCalculator = new NullableFunction() { public PsiType fun(GrMethod method) { PsiType nominal = getNominalType(method); - if (nominal != null && nominal.equals(PsiType.VOID)) return nominal; + if (PsiType.VOID.equals(nominal)) return nominal; PsiType inferred = getInferredType(method); if (nominal == null) { if (inferred == null) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/GroovyPropertyUtils.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/GroovyPropertyUtils.java index e8ede41cdbda..2cf7df077a28 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/GroovyPropertyUtils.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/GroovyPropertyUtils.java @@ -24,6 +24,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrAccessorMethod; +import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; import java.beans.Introspector; @@ -31,6 +32,9 @@ import java.beans.Introspector; * @author ilyas */ public class GroovyPropertyUtils { + private static final String IS_PREFIX = "is"; + private static final String GET_PREFIX = "get"; + private GroovyPropertyUtils() { } @@ -112,6 +116,11 @@ public class GroovyPropertyUtils { if (method == null || method.isConstructor()) return false; if (method.getParameterList().getParametersCount() != 0) return false; if (!isGetterName(method.getName())) return false; + if (method.getName().startsWith(IS_PREFIX) && + !CommonClassNames.JAVA_LANG_BOOLEAN + .equals(TypesUtil.boxPrimitiveType(method.getReturnType(), method.getManager(), method.getResolveScope()).getCanonicalText())) { + return false; + } return (propertyName == null || propertyName.equals(getPropertyNameByGetter(method))) && method.getReturnType() != PsiType.VOID; } @@ -132,10 +141,13 @@ public class GroovyPropertyUtils { } @NonNls String methodName = getterMethod.getName(); - if (methodName.startsWith("get") && methodName.length() > 3) { + if (methodName.startsWith(GET_PREFIX) && methodName.length() > 3) { return decapitalize(methodName.substring(3)); } - else if (methodName.startsWith("is") && methodName.length() > 2 && PsiType.BOOLEAN.equals(getterMethod.getReturnType())) { + else if (methodName.startsWith(IS_PREFIX) && + methodName.length() > 2 && + CommonClassNames.JAVA_LANG_BOOLEAN.equals(TypesUtil.boxPrimitiveType(getterMethod.getReturnType(), getterMethod.getManager(), + getterMethod.getResolveScope()).getCanonicalText())) { return decapitalize(methodName.substring(2)); } return methodName; @@ -163,13 +175,18 @@ public class GroovyPropertyUtils { } public static boolean isGetterName(@NotNull String name) { - if (name.startsWith("get") && name.length() > 3 && isUpperCase(name.charAt(3))) return true; - if (name.startsWith("is") && name.length() > 2 && isUpperCase(name.charAt(2))) return true; + if (name.startsWith(GET_PREFIX) && name.length() > 3 && isUpperCase(name.charAt(3))) return true; + if (name.startsWith(IS_PREFIX) && name.length() > 2 && isUpperCase(name.charAt(2))) return true; return false; } + /** + * Returns getter names in priority order + * @param name property name + * @return getter names + */ public static String[] suggestGettersName(@NotNull String name) { - return new String[]{"get"+capitalize(name), "is"+capitalize(name)}; + return new String[]{IS_PREFIX + capitalize(name), GET_PREFIX + capitalize(name)}; } public static String[] suggestSettersName(@NotNull String name) { diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolvePropertyTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolvePropertyTest.groovy index a9a472839db6..eabeba791a07 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolvePropertyTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolvePropertyTest.groovy @@ -326,4 +326,27 @@ print bar PsiElement resolved = ref.resolve(); assertTrue(resolved instanceof GrReferenceExpression); } + + public void testBooleanProperty() throws Exception { + myFixture.configureByText("Abc.groovy", """class A{ + boolean getFoo(){return true} + boolean isFoo(){return false} + } + print new A().foo"""); + def ref = findReference() + def resolved = ref.resolve(); + assertNotNull resolved + assertEquals resolved.getName(), "isFoo" + } + + public void testExplicitBooleanProperty() throws Exception { + myFixture.configureByText("Abc.groovy", """class A{ + boolean foo + } + print new A().foo"""); + def ref = findReference() + def resolved = ref.resolve(); + assertNotNull resolved + assertEquals resolved.getName(), "isFoo" + } } \ No newline at end of file From b221c6776dbdc19a9c79f8c09e46f31523c6057b Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Mon, 15 Mar 2010 14:56:03 +0300 Subject: [PATCH 53/93] removing empty changelists --- .../util/src/com/intellij/util/ui/UIUtil.java | 27 +++- .../openapi/vcs/VcsConfiguration.java | 1 + .../vcs/VcsShowConfirmationOption.java | 5 +- .../vcs/changes/ChangeListListener.java | 3 + .../vcs/changes/ChangeListManagerImpl.java | 40 ++++- .../vcs/changes/VcsConfirmationDialog.java | 86 ++++++++++ .../VcsGeneralConfigurationPanel.form | 153 ++++++++++-------- .../VcsGeneralConfigurationPanel.java | 21 ++- 8 files changed, 257 insertions(+), 79 deletions(-) create mode 100644 platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsConfirmationDialog.java diff --git a/platform/util/src/com/intellij/util/ui/UIUtil.java b/platform/util/src/com/intellij/util/ui/UIUtil.java index a70d4b8ab392..84fdb70b1581 100644 --- a/platform/util/src/com/intellij/util/ui/UIUtil.java +++ b/platform/util/src/com/intellij/util/ui/UIUtil.java @@ -42,10 +42,8 @@ import java.awt.*; import java.awt.event.*; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.util.*; import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.regex.Pattern; @@ -1064,5 +1062,28 @@ public class UIUtil { return g instanceof PrintGraphics; } + public static int getSelectedButton(ButtonGroup group) { + Enumeration enumeration = group.getElements(); + int i = 0; + while (enumeration.hasMoreElements()) { + AbstractButton button = enumeration.nextElement(); + if (group.isSelected(button.getModel())) { + return i; + } + i++; + } + return -1; + } + + public static void setSelectedButton(ButtonGroup group, int index) { + Enumeration enumeration = group.getElements(); + int i = 0; + while (enumeration.hasMoreElements()) { + AbstractButton button = enumeration.nextElement(); + group.setSelected(button.getModel(), index == i); + i++; + } + } + } diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/VcsConfiguration.java b/platform/vcs-api/src/com/intellij/openapi/vcs/VcsConfiguration.java index 63159e43c35c..59dc044c95c5 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/VcsConfiguration.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/VcsConfiguration.java @@ -63,6 +63,7 @@ public final class VcsConfiguration implements PersistentStateComponent public boolean PERFORM_ROLLBACK_IN_BACKGROUND = false; public volatile boolean CHECK_LOCALLY_CHANGED_CONFLICTS_IN_BACKGROUND = false; public VcsShowConfirmationOption.Value MOVE_TO_FAILED_COMMIT_CHANGELIST = VcsShowConfirmationOption.Value.SHOW_CONFIRMATION; + public VcsShowConfirmationOption.Value REMOVE_EMPTY_INACTIVE_CHANGELISTS = VcsShowConfirmationOption.Value.SHOW_CONFIRMATION; public boolean ENABLE_BACKGROUND_PROCESSES = false; public int CHANGED_ON_SERVER_INTERVAL = 60; diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/VcsShowConfirmationOption.java b/platform/vcs-api/src/com/intellij/openapi/vcs/VcsShowConfirmationOption.java index 4c43ad78a904..d077e7621ee4 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/VcsShowConfirmationOption.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/VcsShowConfirmationOption.java @@ -27,7 +27,10 @@ public interface VcsShowConfirmationOption { myId = id; } - + public int getId() { + return myId; + } + public String toString() { return String.valueOf(myId); } diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ChangeListListener.java b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ChangeListListener.java index 3c5398f44f58..3d30fcc55261 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ChangeListListener.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ChangeListListener.java @@ -21,6 +21,9 @@ import java.util.Collection; /** * @author max + * + * @see com.intellij.openapi.vcs.changes.ChangeListManager#addChangeListListener(ChangeListListener) + * @see com.intellij.openapi.vcs.changes.ChangeListManager#removeChangeListListener(ChangeListListener) */ public interface ChangeListListener extends EventListener { void changeListAdded(ChangeList list); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java index 6a201f6dda92..e94ded8095e9 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java @@ -107,7 +107,7 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec return (ChangeListManagerImpl) project.getComponent(ChangeListManager.class); } - public ChangeListManagerImpl(final Project project) { + public ChangeListManagerImpl(Project project, final VcsConfiguration config) { myProject = project; myChangesViewManager = ChangesViewManager.getInstance(myProject); myFileStatusManager = FileStatusManager.getInstance(myProject); @@ -120,6 +120,44 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec myModifier = new Modifier(myWorker, myDelayedNotificator); myConflictTracker = new ChangelistConflictTracker(project, this, myFileStatusManager, EditorNotifications.getInstance(project)); + + myListeners.addListener(new ChangeListAdapter() { + @Override + public void defaultListChanged(final ChangeList oldDefaultList, ChangeList newDefaultList) { + if (oldDefaultList instanceof LocalChangeList && + oldDefaultList.getChanges().isEmpty()) { + + SwingUtilities.invokeLater(new Runnable() { + public void run() { + switch (config.REMOVE_EMPTY_INACTIVE_CHANGELISTS) { + + case SHOW_CONFIRMATION: + VcsConfirmationDialog dialog = new VcsConfirmationDialog(myProject, new VcsShowConfirmationOption() { + public Value getValue() { + return config.REMOVE_EMPTY_INACTIVE_CHANGELISTS; + } + + public void setValue(Value value) { + config.REMOVE_EMPTY_INACTIVE_CHANGELISTS = value; + } + }, "The empty changelist is no longer active.
    " + + "Do you want to remove it?", "Remember my choice"); + dialog.show(); + if (!dialog.isOK()) { + return; + } + break; + case DO_NOTHING_SILENTLY: + return; + case DO_ACTION_SILENTLY: + break; + } + removeChangeList((LocalChangeList)oldDefaultList); + } + }); + } + } + }); } public void projectOpened() { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsConfirmationDialog.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsConfirmationDialog.java new file mode 100644 index 000000000000..def76962e97c --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsConfirmationDialog.java @@ -0,0 +1,86 @@ +/* + * Copyright 2000-2010 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.openapi.vcs.changes; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.vcs.VcsShowConfirmationOption; +import com.intellij.util.ui.OptionsDialog; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; + +/** + * @author Dmitry Avdeev + */ +public class VcsConfirmationDialog extends OptionsDialog { + + private final VcsShowConfirmationOption myOption; + private final String myMessage; + private final String myDoNotShowMessage; + + protected VcsConfirmationDialog(Project project, VcsShowConfirmationOption option, String message, String doNotShowMessage) { + super(project); + myOption = option; + myMessage = message; + myDoNotShowMessage = doNotShowMessage; + + init(); + } + + @Override + protected boolean isToBeShown() { + return myOption.getValue() == VcsShowConfirmationOption.Value.SHOW_CONFIRMATION; + } + + @Override + protected void setToBeShown(boolean value, boolean onOk) { + myOption.setValue(value ? VcsShowConfirmationOption.Value.SHOW_CONFIRMATION : onOk ? VcsShowConfirmationOption.Value.DO_ACTION_SILENTLY : VcsShowConfirmationOption.Value.DO_NOTHING_SILENTLY); + } + + @Override + protected boolean shouldSaveOptionsOnCancel() { + return true; + } + + @Override + protected JComponent createCenterPanel() { + JPanel panel = new JPanel(new BorderLayout()); + panel.add(new JLabel(myMessage)); + panel.add(new JLabel(Messages.getQuestionIcon())); + return panel; + } + + @Override + protected String getDoNotShowMessage() { + return myDoNotShowMessage; + } + + @Override + protected Action[] createActions() { + return new Action[] { + new AbstractAction("Yes") { + public void actionPerformed(ActionEvent e) { + doOKAction(); + } + }, new AbstractAction("No") { + public void actionPerformed(ActionEvent e) { + doCancelAction(); + } + }}; + } +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsGeneralConfigurationPanel.form b/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsGeneralConfigurationPanel.form index 21e3f625ca9f..6167748fc95a 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsGeneralConfigurationPanel.form +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsGeneralConfigurationPanel.form @@ -1,6 +1,6 @@
    - + @@ -8,24 +8,15 @@ - - - - - - - - - - + - + @@ -92,42 +83,88 @@ - + - - - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + - + - + - + - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -135,7 +172,7 @@ - + @@ -144,7 +181,7 @@ - + @@ -155,49 +192,22 @@ - + - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + @@ -211,5 +221,10 @@ + + + + + diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsGeneralConfigurationPanel.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsGeneralConfigurationPanel.java index 0ccc62d5fad6..fcfa0d99a237 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsGeneralConfigurationPanel.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsGeneralConfigurationPanel.java @@ -15,7 +15,6 @@ */ package com.intellij.openapi.vcs.configurable; -import com.intellij.openapi.application.ApplicationNamesInfo; import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.project.Project; @@ -60,8 +59,7 @@ public class VcsGeneralConfigurationPanel implements Configurable { private JPanel myAddConfirmationPanel; private JCheckBox myCbOfferToMoveChanges; private JComboBox myFailedCommitChangelistCombo; - private JLabel myRemoveConfirmationLabel; - private JLabel myAddConfirmationLabel; + private ButtonGroup myEmptyChangelistRemovingGroup; public VcsGeneralConfigurationPanel(final Project project) { @@ -93,8 +91,6 @@ public class VcsGeneralConfigurationPanel implements Configurable { myPromptsPanel.setSize(myPromptsPanel.getPreferredSize()); - myAddConfirmationLabel.setText(VcsBundle.message("add.confirmation.label.text", ApplicationNamesInfo.getInstance().getProductName())); - myRemoveConfirmationLabel.setText(VcsBundle.message("remove.confirmation.label.text", ApplicationNamesInfo.getInstance().getProductName())); } public void apply() throws ConfigurationException { @@ -103,6 +99,7 @@ public class VcsGeneralConfigurationPanel implements Configurable { settings.FORCE_NON_EMPTY_COMMENT = myForceNonEmptyComment.isSelected(); settings.OFFER_MOVE_TO_ANOTHER_CHANGELIST_ON_PARTIAL_COMMIT = myCbOfferToMoveChanges.isSelected(); + settings.REMOVE_EMPTY_INACTIVE_CHANGELISTS = getSelected(myEmptyChangelistRemovingGroup); settings.MOVE_TO_FAILED_COMMIT_CHANGELIST = getFailedCommitConfirm(); for (VcsShowOptionsSettingImpl setting : myPromptOptions.keySet()) { @@ -141,6 +138,15 @@ public class VcsGeneralConfigurationPanel implements Configurable { return VcsShowConfirmationOption.Value.DO_NOTHING_SILENTLY; } + private static VcsShowConfirmationOption.Value getSelected(ButtonGroup group) { + switch (UIUtil.getSelectedButton(group)) { + case 0: + return VcsShowConfirmationOption.Value.SHOW_CONFIRMATION; + case 1: + return VcsShowConfirmationOption.Value.DO_ACTION_SILENTLY; + } + return VcsShowConfirmationOption.Value.DO_NOTHING_SILENTLY; + } private ReadonlyStatusHandlerImpl getReadOnlyStatusHandler() { return ((ReadonlyStatusHandlerImpl)ReadonlyStatusHandler.getInstance(myProject)); @@ -155,6 +161,9 @@ public class VcsGeneralConfigurationPanel implements Configurable { if (settings.OFFER_MOVE_TO_ANOTHER_CHANGELIST_ON_PARTIAL_COMMIT != myCbOfferToMoveChanges.isSelected()){ return true; } + if (settings.REMOVE_EMPTY_INACTIVE_CHANGELISTS != getSelected(myEmptyChangelistRemovingGroup)){ + return true; + } if (!Comparing.equal(getFailedCommitConfirm(), settings.MOVE_TO_FAILED_COMMIT_CHANGELIST)) { return true; @@ -178,6 +187,8 @@ public class VcsGeneralConfigurationPanel implements Configurable { VcsConfiguration settings = VcsConfiguration.getInstance(myProject); myForceNonEmptyComment.setSelected(settings.FORCE_NON_EMPTY_COMMENT); myCbOfferToMoveChanges.setSelected(settings.OFFER_MOVE_TO_ANOTHER_CHANGELIST_ON_PARTIAL_COMMIT); + int id = settings.REMOVE_EMPTY_INACTIVE_CHANGELISTS.getId(); + UIUtil.setSelectedButton(myEmptyChangelistRemovingGroup, id == 0 ? 0 : id == 1 ? 2 : 1); myShowReadOnlyStatusDialog.setSelected(getReadOnlyStatusHandler().getState().SHOW_DIALOG); if (settings.MOVE_TO_FAILED_COMMIT_CHANGELIST == VcsShowConfirmationOption.Value.DO_ACTION_SILENTLY) { myFailedCommitChangelistCombo.setSelectedIndex(0); From f82ab4ac3ec539f42f2ba1ca8eb1019bc4257492 Mon Sep 17 00:00:00 2001 From: irengrig Date: Mon, 15 Mar 2010 15:13:39 +0300 Subject: [PATCH 54/93] VCS: patch builder -> text patch builder, method cleanup --- .../intellij/history/integration/patches/PatchCreator.java | 4 ++-- .../patch/{PatchBuilder.java => TextPatchBuilder.java} | 7 +++---- .../actions/RevertCommittedStuffAbstractAction.java | 4 ++-- .../vcs/changes/patch/CreatePatchCommitExecutor.java | 4 ++-- .../openapi/vcs/changes/shelf/ShelveChangesManager.java | 6 +++--- 5 files changed, 12 insertions(+), 13 deletions(-) rename platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/{PatchBuilder.java => TextPatchBuilder.java} (98%) diff --git a/platform/lvcs-impl/src/com/intellij/history/integration/patches/PatchCreator.java b/platform/lvcs-impl/src/com/intellij/history/integration/patches/PatchCreator.java index 7994f5961fa1..2f4dc002f43f 100644 --- a/platform/lvcs-impl/src/com/intellij/history/integration/patches/PatchCreator.java +++ b/platform/lvcs-impl/src/com/intellij/history/integration/patches/PatchCreator.java @@ -18,7 +18,7 @@ package com.intellij.history.integration.patches; import com.intellij.history.integration.IdeaGateway; import com.intellij.openapi.diff.impl.patch.FilePatch; -import com.intellij.openapi.diff.impl.patch.PatchBuilder; +import com.intellij.openapi.diff.impl.patch.TextPatchBuilder; import com.intellij.openapi.diff.impl.patch.UnifiedDiffWriter; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.Change; @@ -35,7 +35,7 @@ public class PatchCreator { throws IOException, VcsException { Writer writer = new OutputStreamWriter(new FileOutputStream(filePath)); try { - List patches = PatchBuilder.buildPatch(changes, gw.getBaseDir(), false, isReverse); + List patches = TextPatchBuilder.buildPatch(changes, gw.getBaseDir(), isReverse); String lineSeparator = CodeStyleSettingsManager.getInstance(gw.getProject()).getCurrentSettings().getLineSeparator(); UnifiedDiffWriter.write(patches, writer, lineSeparator); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchBuilder.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/TextPatchBuilder.java similarity index 98% rename from platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchBuilder.java rename to platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/TextPatchBuilder.java index 606fa0bf8363..42c364104b01 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchBuilder.java +++ b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/TextPatchBuilder.java @@ -44,11 +44,11 @@ import java.util.List; /** * @author yole */ -public class PatchBuilder { +public class TextPatchBuilder { private static final int CONTEXT_LINES = 3; @NonNls private static final String REVISION_NAME_TEMPLATE = "(revision {0})"; - private PatchBuilder() { + private TextPatchBuilder() { } private static void checkCanceled(final ProgressIndicator ind) { @@ -57,8 +57,7 @@ public class PatchBuilder { } } - public static List buildPatch(final Collection changes, final String basePath, final boolean allowRename, - final boolean reversePatch) throws VcsException { + public static List buildPatch(final Collection changes, final String basePath, final boolean reversePatch) throws VcsException { final ProgressIndicator ind = ProgressManager.getInstance().getProgressIndicator(); List result = new ArrayList(); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/RevertCommittedStuffAbstractAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/RevertCommittedStuffAbstractAction.java index 395b4cbe6919..fc17779c4742 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/RevertCommittedStuffAbstractAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/RevertCommittedStuffAbstractAction.java @@ -19,7 +19,7 @@ import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.diff.impl.patch.FilePatch; -import com.intellij.openapi.diff.impl.patch.PatchBuilder; +import com.intellij.openapi.diff.impl.patch.TextPatchBuilder; import com.intellij.openapi.diff.impl.patch.formove.PatchApplier; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; @@ -70,7 +70,7 @@ abstract class RevertCommittedStuffAbstractAction extends AnAction implements Du List patches; try { - patches = PatchBuilder.buildPatch(changesList, baseDir.getPresentableUrl(), false, true); + patches = TextPatchBuilder.buildPatch(changesList, baseDir.getPresentableUrl(), true); } catch (VcsException ex) { Messages.showErrorDialog(project, "Failed to revert changes: " + ex.getMessage(), VcsBundle.message("revert.changes.title")); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/CreatePatchCommitExecutor.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/CreatePatchCommitExecutor.java index 71d2d5312857..c6b80cebd11f 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/CreatePatchCommitExecutor.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/CreatePatchCommitExecutor.java @@ -22,7 +22,7 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.ProjectComponent; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diff.impl.patch.FilePatch; -import com.intellij.openapi.diff.impl.patch.PatchBuilder; +import com.intellij.openapi.diff.impl.patch.TextPatchBuilder; import com.intellij.openapi.diff.impl.patch.UnifiedDiffWriter; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; @@ -153,7 +153,7 @@ public class CreatePatchCommitExecutor implements CommitExecutor, ProjectCompone REVERSE_PATCH = myPanel.isReversePatch(); Writer writer = new OutputStreamWriter(new FileOutputStream(fileName)); try { - List patches = PatchBuilder.buildPatch(changes, myProject.getBaseDir().getPresentableUrl(), false, REVERSE_PATCH); + List patches = TextPatchBuilder.buildPatch(changes, myProject.getBaseDir().getPresentableUrl(), REVERSE_PATCH); final String lineSeparator = CodeStyleFacade.getInstance(myProject).getLineSeparator(); UnifiedDiffWriter.write(patches, writer, lineSeparator); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesManager.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesManager.java index 167e4c63e80a..d7d0066f6d3f 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesManager.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesManager.java @@ -30,9 +30,9 @@ import com.intellij.openapi.diff.impl.patch.*; import com.intellij.openapi.diff.impl.patch.formove.CustomBinaryPatchApplier; import com.intellij.openapi.diff.impl.patch.formove.PatchApplier; import com.intellij.openapi.options.StreamProvider; -import com.intellij.openapi.progress.ProgressManager; -import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.*; import com.intellij.openapi.util.io.FileUtil; @@ -170,7 +170,7 @@ public class ShelveChangesManager implements ProjectComponent, JDOMExternalizabl if (ind != null && ind.isCanceled()) { throw new ProcessCanceledException(); } - final List patches = PatchBuilder.buildPatch(textChanges, myProject.getBaseDir().getPresentableUrl(), true, false); + final List patches = TextPatchBuilder.buildPatch(textChanges, myProject.getBaseDir().getPresentableUrl(), false); if (ind != null && ind.isCanceled()) { throw new ProcessCanceledException(); } From f441f3c60003c4122557ea658ebf623f0201f59f Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Mon, 15 Mar 2010 15:33:35 +0300 Subject: [PATCH 55/93] removing empty changelists --- .../intellij/openapi/vcs/changes/ChangeListManagerImpl.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java index e94ded8095e9..fdb3459ee10b 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java @@ -124,7 +124,8 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec myListeners.addListener(new ChangeListAdapter() { @Override public void defaultListChanged(final ChangeList oldDefaultList, ChangeList newDefaultList) { - if (oldDefaultList instanceof LocalChangeList && + if (!ApplicationManager.getApplication().isUnitTestMode() && + oldDefaultList instanceof LocalChangeList && oldDefaultList.getChanges().isEmpty()) { SwingUtilities.invokeLater(new Runnable() { From 01cc2c247127b0048dd0ba387ba4dfeb161b1c25 Mon Sep 17 00:00:00 2001 From: Alexey Pegov Date: Mon, 15 Mar 2010 16:22:20 +0300 Subject: [PATCH 56/93] IDEA-52243 Copy global library to project level not working --- .../BaseLibrariesConfigurable.java | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/BaseLibrariesConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/BaseLibrariesConfigurable.java index 7d4f51e5a047..2d3079a3e5c0 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/BaseLibrariesConfigurable.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/BaseLibrariesConfigurable.java @@ -274,21 +274,21 @@ public abstract class BaseLibrariesConfigurable extends BaseStructureConfigurabl for (VirtualFile file : files) { if (mySaveAsCb.isSelected() && myPathTf.getText().trim().length() > 0) { final File copy = new File(new File(myPathTf.getText()), file.getName()); - if (copy.mkdirs()) { - try { - final File fromFile = VfsUtil.virtualToIoFile(file); - if (fromFile.isFile()) { - FileUtil.copy(fromFile, copy); - } else { - FileUtil.copyDir(fromFile, copy); - } - model.addRoot(LocalFileSystem.getInstance().refreshAndFindFileByIoFile(copy), type); + if (!copy.getParentFile().exists() && !copy.getParentFile().mkdirs()) continue; + try { + final File fromFile = VfsUtil.virtualToIoFile(file); + if (fromFile.isFile()) { + FileUtil.copy(fromFile, copy); + } else { + FileUtil.copyDir(fromFile, copy); } - catch (IOException e1) { - //skip - } - continue; + model.addRoot(LocalFileSystem.getInstance().refreshAndFindFileByIoFile(copy), type); } + catch (IOException e1) { + LOG.error(e1); + } + + continue; } model.addRoot(file, type); From 867fe538f73847e1ed50f07cc2df04f4fef52538 Mon Sep 17 00:00:00 2001 From: Anton Makeev Date: Mon, 15 Mar 2010 12:46:17 +0300 Subject: [PATCH 57/93] Maven: invalid file access fix --- .../org/jetbrains/idea/maven/project/MavenProjectsTree.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsTree.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsTree.java index 50f9d6a4258b..6c63d266c203 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsTree.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsTree.java @@ -669,7 +669,7 @@ public class MavenProjectsTree { } private long getFileTimestamp(VirtualFile file) { - if (file == null) return -1; + if (file == null || !file.isValid()) return -1; return file.getTimeStamp(); } From 5a03ad41471260c0f59d8fb9eafbf30c9e1fb8c7 Mon Sep 17 00:00:00 2001 From: Anton Makeev Date: Mon, 15 Mar 2010 13:36:36 +0300 Subject: [PATCH 58/93] Maven: cannot calculate relative path for module fix --- .../java/org/jetbrains/idea/maven/dom/MavenDomUtil.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/MavenDomUtil.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/MavenDomUtil.java index fa84377f670e..a9ebd1a1e336 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/MavenDomUtil.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/MavenDomUtil.java @@ -41,6 +41,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.maven.dom.model.*; import org.jetbrains.idea.maven.project.*; import org.jetbrains.idea.maven.utils.MavenConstants; +import org.jetbrains.idea.maven.utils.MavenLog; import org.jetbrains.idea.maven.vfs.MavenPropertiesVirtualFileSystem; import java.io.File; @@ -87,7 +88,11 @@ public class MavenDomUtil { } public static String calcRelativePath(VirtualFile parent, VirtualFile child) { - String result = FileUtil.getRelativePath(new File(parent.getPath()), new File(child.getPath())); + String result = FileUtil.getRelativePath(parent.getPath(), child.getPath(), '/'); + if (result == null) { + MavenLog.LOG.warn("cannot calculate relative path for\nparent: " + parent + "\nchild: " + child); + result = child.getPath(); + } return FileUtil.toSystemIndependentName(result); } From 5e7015f980b854771e09fc1d777fa6009362de35 Mon Sep 17 00:00:00 2001 From: Anton Makeev Date: Mon, 15 Mar 2010 14:09:58 +0300 Subject: [PATCH 59/93] F4: npe on navigation to invalid library doc references: assertion --- .../roots/ui/configuration/ProjectStructureConfigurable.java | 2 +- .../openapi/command/impl/DocumentReferenceByVirtualFile.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectStructureConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectStructureConfigurable.java index d4d47895b2c7..1003a570f891 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectStructureConfigurable.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectStructureConfigurable.java @@ -383,7 +383,7 @@ public class ProjectStructureConfigurable extends BaseConfigurable implements Se public ActionCallback select(@NotNull LibraryOrderEntry libraryOrderEntry, final boolean requestFocus) { final Library lib = libraryOrderEntry.getLibrary(); - if (lib != null && lib.getTable() == null) { + if (lib == null || lib.getTable() == null) { Place place = new Place().putPath(CATEGORY, myModulesConfig); place.putPath(BaseStructureConfigurable.TREE_OBJECT, libraryOrderEntry.getOwnerModule()); return navigateTo(place, requestFocus); diff --git a/platform/platform-impl/src/com/intellij/openapi/command/impl/DocumentReferenceByVirtualFile.java b/platform/platform-impl/src/com/intellij/openapi/command/impl/DocumentReferenceByVirtualFile.java index 25c33c45aa49..dc2c2643658f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/command/impl/DocumentReferenceByVirtualFile.java +++ b/platform/platform-impl/src/com/intellij/openapi/command/impl/DocumentReferenceByVirtualFile.java @@ -31,7 +31,7 @@ public class DocumentReferenceByVirtualFile implements DocumentReference { @Nullable public Document getDocument() { - assert myFile.isValid() : "should not be called on references to deleted files"; + assert myFile.isValid() : "should not be called on references to deleted file: " + myFile; return FileDocumentManager.getInstance().getDocument(myFile); } From e0d555550e3facb58c3b7df2da821cd78963d8c4 Mon Sep 17 00:00:00 2001 From: Anton Makeev Date: Mon, 15 Mar 2010 16:43:32 +0300 Subject: [PATCH 60/93] maven: delete button unmapped IDEA-27047 fix maven: startup activities fix reverted --- .../main/java/org/jetbrains/idea/maven/utils/MavenUtil.java | 3 +-- plugins/maven/src/main/resources/META-INF/plugin.xml | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenUtil.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenUtil.java index 6001367c0b9e..48aa53d48e94 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenUtil.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenUtil.java @@ -19,7 +19,6 @@ import com.intellij.codeInsight.template.TemplateManager; import com.intellij.codeInsight.template.impl.TemplateImpl; import com.intellij.ide.fileTemplates.FileTemplate; import com.intellij.ide.fileTemplates.FileTemplateManager; -import com.intellij.ide.startup.StartupManagerEx; import com.intellij.notification.Notification; import com.intellij.notification.NotificationType; import com.intellij.notification.Notifications; @@ -138,7 +137,7 @@ public class MavenUtil { return; } - if (!StartupManagerEx.getInstanceEx(project).postStartupActivityPassed()) { + if (!project.isInitialized()) { StartupManager.getInstance(project).registerPostStartupActivity(r); return; } diff --git a/plugins/maven/src/main/resources/META-INF/plugin.xml b/plugins/maven/src/main/resources/META-INF/plugin.xml index 65f3f5ead48d..581a7f640910 100644 --- a/plugins/maven/src/main/resources/META-INF/plugin.xml +++ b/plugins/maven/src/main/resources/META-INF/plugin.xml @@ -196,7 +196,6 @@ text="Remove Projects" description="Remove selected Maven projects from the project tree" icon="/general/remove.png"> -
    Date: Mon, 15 Mar 2010 17:04:02 +0300 Subject: [PATCH 61/93] Allow to update presentation by sub classes --- .../lang-impl/src/com/intellij/ide/actions/GotoActionBase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/ide/actions/GotoActionBase.java b/platform/lang-impl/src/com/intellij/ide/actions/GotoActionBase.java index 646cd246e267..0fda5957f06e 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/GotoActionBase.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/GotoActionBase.java @@ -48,7 +48,7 @@ public abstract class GotoActionBase extends AnAction { protected abstract void gotoActionPerformed(AnActionEvent e); - public final void update(final AnActionEvent event) { + public void update(final AnActionEvent event) { final Presentation presentation = event.getPresentation(); final DataContext dataContext = event.getDataContext(); final Project project = PlatformDataKeys.PROJECT.getData(dataContext); From 8fdf1439cd38d1a07e098e15b47b634f98a5d74c Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Mon, 15 Mar 2010 17:32:39 +0300 Subject: [PATCH 62/93] removing empty changelists --- .../intellij/openapi/vcs/changes/ChangeListManagerImpl.java | 3 ++- .../intellij/openapi/vcs/changes/VcsConfirmationDialog.java | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java index fdb3459ee10b..f013d7c3f93b 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java @@ -29,6 +29,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.*; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.*; import com.intellij.openapi.vcs.changes.conflicts.ChangelistConflictTracker; import com.intellij.openapi.vcs.changes.ui.CommitHelper; @@ -141,7 +142,7 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec public void setValue(Value value) { config.REMOVE_EMPTY_INACTIVE_CHANGELISTS = value; } - }, "The empty changelist is no longer active.
    " + + }, "The empty changelist '" + StringUtil.last(oldDefaultList.getName(), 30, true) + "' is no longer active.
    " + "Do you want to remove it?", "Remember my choice"); dialog.show(); if (!dialog.isOK()) { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsConfirmationDialog.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsConfirmationDialog.java index def76962e97c..547be42efa2b 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsConfirmationDialog.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsConfirmationDialog.java @@ -38,7 +38,7 @@ public class VcsConfirmationDialog extends OptionsDialog { myOption = option; myMessage = message; myDoNotShowMessage = doNotShowMessage; - + setTitle("Confirmation"); init(); } @@ -59,9 +59,9 @@ public class VcsConfirmationDialog extends OptionsDialog { @Override protected JComponent createCenterPanel() { - JPanel panel = new JPanel(new BorderLayout()); + JPanel panel = new JPanel(new BorderLayout(15, 0)); panel.add(new JLabel(myMessage)); - panel.add(new JLabel(Messages.getQuestionIcon())); + panel.add(new JLabel(Messages.getQuestionIcon()), BorderLayout.WEST); return panel; } From 089e3e7496d3189a030438db693229cd97be9324 Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Mon, 15 Mar 2010 17:38:12 +0300 Subject: [PATCH 63/93] removing empty changelists --- .../intellij/openapi/vcs/changes/ChangeListManagerImpl.java | 1 + .../intellij/openapi/vcs/changes/VcsConfirmationDialog.java | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java index f013d7c3f93b..e8674f1fa46b 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java @@ -127,6 +127,7 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec public void defaultListChanged(final ChangeList oldDefaultList, ChangeList newDefaultList) { if (!ApplicationManager.getApplication().isUnitTestMode() && oldDefaultList instanceof LocalChangeList && + !((LocalChangeList)oldDefaultList).isDefault() && oldDefaultList.getChanges().isEmpty()) { SwingUtilities.invokeLater(new Runnable() { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsConfirmationDialog.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsConfirmationDialog.java index 547be42efa2b..a18744763e05 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsConfirmationDialog.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsConfirmationDialog.java @@ -15,6 +15,7 @@ */ package com.intellij.openapi.vcs.changes; +import com.intellij.CommonBundle; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.vcs.VcsShowConfirmationOption; @@ -73,11 +74,11 @@ public class VcsConfirmationDialog extends OptionsDialog { @Override protected Action[] createActions() { return new Action[] { - new AbstractAction("Yes") { + new AbstractAction(CommonBundle.getYesButtonText()) { public void actionPerformed(ActionEvent e) { doOKAction(); } - }, new AbstractAction("No") { + }, new AbstractAction(CommonBundle.getNoButtonText()) { public void actionPerformed(ActionEvent e) { doCancelAction(); } From 72e29ef2eeaf32a10bcd3f87566f898b75caf069 Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Mon, 15 Mar 2010 17:59:34 +0300 Subject: [PATCH 64/93] removing empty changelists --- .../intellij/openapi/vcs/changes/ChangeListManagerImpl.java | 5 ++--- .../intellij/openapi/vcs/changes/VcsConfirmationDialog.java | 3 +++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java index e8674f1fa46b..106ddb7abee1 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java @@ -127,10 +127,9 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec public void defaultListChanged(final ChangeList oldDefaultList, ChangeList newDefaultList) { if (!ApplicationManager.getApplication().isUnitTestMode() && oldDefaultList instanceof LocalChangeList && - !((LocalChangeList)oldDefaultList).isDefault() && oldDefaultList.getChanges().isEmpty()) { - SwingUtilities.invokeLater(new Runnable() { + invokeAfterUpdate(new Runnable() { public void run() { switch (config.REMOVE_EMPTY_INACTIVE_CHANGELISTS) { @@ -157,7 +156,7 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec } removeChangeList((LocalChangeList)oldDefaultList); } - }); + }, InvokeAfterUpdateMode.SILENT, null, null); } } }); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsConfirmationDialog.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsConfirmationDialog.java index a18744763e05..149c6c9cb089 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsConfirmationDialog.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsConfirmationDialog.java @@ -75,6 +75,9 @@ public class VcsConfirmationDialog extends OptionsDialog { protected Action[] createActions() { return new Action[] { new AbstractAction(CommonBundle.getYesButtonText()) { + { + putValue(DEFAULT_ACTION, Boolean.TRUE); + } public void actionPerformed(ActionEvent e) { doOKAction(); } From 9b9f04e1ae4eaaf5fe3c1c76f7e8ca074ff85405 Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Mon, 15 Mar 2010 18:00:15 +0300 Subject: [PATCH 65/93] remove myTemplateString field --- .../intellij/codeInsight/template/XmlZenCodingTemplate.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/xml/impl/src/com/intellij/codeInsight/template/XmlZenCodingTemplate.java b/xml/impl/src/com/intellij/codeInsight/template/XmlZenCodingTemplate.java index bcf539d1ee4f..5e168aa6adb3 100644 --- a/xml/impl/src/com/intellij/codeInsight/template/XmlZenCodingTemplate.java +++ b/xml/impl/src/com/intellij/codeInsight/template/XmlZenCodingTemplate.java @@ -66,7 +66,6 @@ public class XmlZenCodingTemplate implements CustomLiveTemplate { final String myKey; final List> myAttribute2Value; TemplateImpl myTemplate; - String myTemplateString; MyTemplateToken(String key, List> attribute2value) { myKey = key; @@ -249,7 +248,6 @@ public class XmlZenCodingTemplate implements CustomLiveTemplate { return false; } token.myTemplate = template; - token.myTemplateString = template.getString(); return true; } @@ -481,7 +479,7 @@ public class XmlZenCodingTemplate implements CustomLiveTemplate { if (callback.isLiveTemplateApplicable(token.myKey)) { if (token.myTemplate != null) { TemplateImpl modifiedTemplate = token.myTemplate.copy(); - XmlTag tag = parseXmlTagInTemplate(token.myTemplateString, callback.getProject()); + XmlTag tag = parseXmlTagInTemplate(token.myTemplate.getString(), callback.getProject()); assert tag != null; for (Iterator> iterator = attr2value.iterator(); iterator.hasNext();) { Pair pair = iterator.next(); From 00d106d1359ecda29d33295bdd7400b6d0c95ea8 Mon Sep 17 00:00:00 2001 From: nik Date: Mon, 15 Mar 2010 17:35:43 +0300 Subject: [PATCH 66/93] possible NPE fixed --- .../impl/elements/ModuleOutputPackagingElement.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/ModuleOutputPackagingElement.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/ModuleOutputPackagingElement.java index 3f2083f9e12d..29d5c9d709b9 100644 --- a/java/compiler/impl/src/com/intellij/packaging/impl/elements/ModuleOutputPackagingElement.java +++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/ModuleOutputPackagingElement.java @@ -80,9 +80,12 @@ public class ModuleOutputPackagingElement extends PackagingElement Date: Mon, 15 Mar 2010 18:04:05 +0300 Subject: [PATCH 67/93] more logging --- .../src/com/intellij/compiler/impl/CompileDriver.java | 4 +++- .../packaging/impl/compiler/ArtifactCompileScope.java | 8 +++----- .../impl/compiler/IncrementalArtifactsCompiler.java | 10 ++++++++-- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java index 00b601bf69bf..aef725908db7 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java @@ -1703,6 +1703,9 @@ public class CompileDriver { if (context.getMessageCount(CompilerMessageCategory.ERROR) > 0) { return false; } + if (LOG.isDebugEnabled() && items.length > 0) { + LOG.debug("Start processing files by " + adapter.getCompiler().getDescription()); + } final CompileScope scope = context.getCompileScope(); final List toProcess = new ArrayList(); final Set allUrls = new HashSet(); @@ -1806,7 +1809,6 @@ public class CompileDriver { final VirtualFile file = aProcessed.getFile(); vFiles.add(file); if (LOG.isDebugEnabled()) { - LOG.debug("File processed by " + adapter.getCompiler().getDescription()); LOG.debug("\tFile processed " + file.getPresentableUrl() + "; ts=" + file.getTimeStamp()); } diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactCompileScope.java b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactCompileScope.java index 4702d30f3f31..38805aa9083c 100644 --- a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactCompileScope.java +++ b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactCompileScope.java @@ -31,10 +31,7 @@ import com.intellij.util.Processor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; +import java.util.*; /** * @author nik @@ -87,9 +84,10 @@ public class ArtifactCompileScope { Set artifacts = new HashSet(); final ArtifactManager artifactManager = ArtifactManager.getInstance(project); final Set modules = new HashSet(Arrays.asList(compileScope.getAffectedModules())); + final List allModules = Arrays.asList(ModuleManager.getInstance(project).getModules()); for (Artifact artifact : artifactManager.getArtifacts()) { if (artifact.isBuildOnMake()) { - if (modules.containsAll(Arrays.asList(ModuleManager.getInstance(project).getModules())) + if (modules.containsAll(allModules) || containsModuleOutput(artifact, modules, artifactManager)) { artifacts.add(artifact); } diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/IncrementalArtifactsCompiler.java b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/IncrementalArtifactsCompiler.java index 38d04612f2e8..770fc0989716 100644 --- a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/IncrementalArtifactsCompiler.java +++ b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/IncrementalArtifactsCompiler.java @@ -84,6 +84,9 @@ public class IncrementalArtifactsCompiler implements PackagingCompiler { final CompileContext context = builderContext.getCompileContext(); final Set artifactsToBuild = ArtifactCompileScope.getArtifactsToBuild(project, context.getCompileScope()); + if (LOG.isDebugEnabled()) { + LOG.debug("artifacts to build: " + artifactsToBuild); + } List additionalArtifacts = new ArrayList(); for (BuildParticipantProvider provider : BuildParticipantProvider.EXTENSION_POINT_NAME.getExtensions()) { for (Module module : ModuleManager.getInstance(project).getModules()) { @@ -93,6 +96,9 @@ public class IncrementalArtifactsCompiler implements PackagingCompiler { } } } + if (LOG.isDebugEnabled() && !additionalArtifacts.isEmpty()) { + LOG.debug("additional artifacts to build: " + additionalArtifacts); + } artifactsToBuild.addAll(additionalArtifacts); final List allArtifacts = new ArrayList(Arrays.asList(ArtifactManager.getInstance(project).getArtifacts())); @@ -120,7 +126,7 @@ public class IncrementalArtifactsCompiler implements PackagingCompiler { ArtifactPackagingProcessingItem[] allProcessingItems = collectItems(builderContext, context.getProject()); if (LOG.isDebugEnabled()) { - int num = Math.min(100, allProcessingItems.length); + int num = Math.min(5000, allProcessingItems.length); LOG.debug("All files (" + num + " of " + allProcessingItems.length + "):"); for (int i = 0; i < num; i++) { LOG.debug(allProcessingItems[i].getFile().getPath()); @@ -203,7 +209,7 @@ public class IncrementalArtifactsCompiler implements PackagingCompiler { final boolean testMode = ApplicationManager.getApplication().isUnitTestMode(); if (LOG.isDebugEnabled()) { - int num = Math.min(100, items.length); + int num = Math.min(200, items.length); LOG.debug("Files to process (" + num + " of " + items.length + "):"); for (int i = 0; i < num; i++) { LOG.debug(items[i].getFile().getPath()); From b23f393c5d160b2fae86ba79e55c954c8e58e0fb Mon Sep 17 00:00:00 2001 From: Anton Makeev Date: Mon, 15 Mar 2010 18:40:40 +0300 Subject: [PATCH 68/93] Maven: do not show settings.xml in ctrl+shift+N in project scope (IDEA-52825) --- .../idea/maven/utils/MavenGotoSettingsFileContibutor.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenGotoSettingsFileContibutor.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenGotoSettingsFileContibutor.java index 8f85811c785e..6ae9412af293 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenGotoSettingsFileContibutor.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenGotoSettingsFileContibutor.java @@ -32,6 +32,8 @@ import java.util.Set; public class MavenGotoSettingsFileContibutor implements ChooseByNameContributor, DumbAware { public String[] getNames(Project project, boolean includeNonProjectItems) { + if (!includeNonProjectItems) return ArrayUtil.EMPTY_STRING_ARRAY; + Set result = new THashSet(); for (VirtualFile each : getSettingsFiles(project)) { result.add(each.getName()); @@ -40,6 +42,8 @@ public class MavenGotoSettingsFileContibutor implements ChooseByNameContributor, } public NavigationItem[] getItemsByName(String name, String pattern, Project project, boolean includeNonProjectItems) { + if (!includeNonProjectItems) return NavigationItem.EMPTY_NAVIGATION_ITEM_ARRAY; + List result = new ArrayList(); for (VirtualFile each : getSettingsFiles(project)) { if (each.getName().equals(name)) { From ec64a0bad0d9da38564499e6640f715a9f00e265 Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Mon, 15 Mar 2010 18:42:56 +0300 Subject: [PATCH 69/93] removing empty changelists: mnemonic added --- .../platform-api/src/com/intellij/util/ui/DialogUtil.java | 7 ++++++- .../src/com/intellij/util/ui/OptionsDialog.java | 3 ++- .../openapi/vcs/changes/ChangeListManagerImpl.java | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/platform/platform-api/src/com/intellij/util/ui/DialogUtil.java b/platform/platform-api/src/com/intellij/util/ui/DialogUtil.java index a40134fee4f7..a09c07867f41 100644 --- a/platform/platform-api/src/com/intellij/util/ui/DialogUtil.java +++ b/platform/platform-api/src/com/intellij/util/ui/DialogUtil.java @@ -25,7 +25,12 @@ import javax.swing.*; * @since 5.1 */ public class DialogUtil { + public static void registerMnemonic(AbstractButton button) { + registerMnemonic(button, UIUtil.MNEMONIC); + } + + public static void registerMnemonic(AbstractButton button, char mn) { String text = button.getText(); if (text != null) { @@ -34,7 +39,7 @@ public class DialogUtil { int index = -1; for (int i = 0; i < text.length(); i++) { char ch = text.charAt(i); - if (ch != UIUtil.MNEMONIC) { + if (ch != mn) { realText.append(ch); } else if (i + 1 < text.length()) { diff --git a/platform/platform-api/src/com/intellij/util/ui/OptionsDialog.java b/platform/platform-api/src/com/intellij/util/ui/OptionsDialog.java index f1b84baca9c1..a3fd2f94eb6a 100644 --- a/platform/platform-api/src/com/intellij/util/ui/OptionsDialog.java +++ b/platform/platform-api/src/com/intellij/util/ui/OptionsDialog.java @@ -28,7 +28,7 @@ import java.awt.*; public abstract class OptionsDialog extends DialogWrapper { - private JCheckBox myCheckBoxDoNotShowDialog; + protected JCheckBox myCheckBoxDoNotShowDialog; protected String getDoNotShowMessage() { return CommonBundle.message("dialog.options.do.not.show"); @@ -68,6 +68,7 @@ public abstract class OptionsDialog extends DialogWrapper { final JPanel panel = addDoNotShowCheckBox(southPanel, myCheckBoxDoNotShowDialog); myCheckBoxDoNotShowDialog.setSelected(!isToBeShown()); + DialogUtil.registerMnemonic(myCheckBoxDoNotShowDialog, '&'); return panel; } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java index 106ddb7abee1..6b11ca594982 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java @@ -143,7 +143,7 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec config.REMOVE_EMPTY_INACTIVE_CHANGELISTS = value; } }, "The empty changelist '" + StringUtil.last(oldDefaultList.getName(), 30, true) + "' is no longer active.
    " + - "Do you want to remove it?", "Remember my choice"); + "Do you want to remove it?", "&Remember my choice"); dialog.show(); if (!dialog.isOK()) { return; From 9c6e028f289ac881226d099456cf7ce1970fa03d Mon Sep 17 00:00:00 2001 From: irengrig Date: Mon, 15 Mar 2010 18:45:51 +0300 Subject: [PATCH 70/93] VCS: patch creation separated from patch application --- .../historyIntegrTests/PatchingTestCase.java | 3 +- .../openapi/diff/impl/patch/PatchLine.java | 0 .../diff/impl/patch/BinaryFilePatch.java | 17 +- .../openapi/diff/impl/patch/FilePatch.java | 140 ---- .../openapi/diff/impl/patch/PatchHunk.java | 2 +- .../diff/impl/patch/TextFilePatch.java | 53 +- .../diff/impl/patch/TextPatchBuilder.java | 2 +- .../patch/apply/ApplyBinaryFilePatch.java | 38 + .../apply/ApplyBinaryShelvedFilePatch.java | 38 + .../diff/impl/patch/apply/ApplyFilePatch.java | 35 + .../impl/patch/apply/ApplyFilePatchBase.java | 199 +++++ .../patch/apply/ApplyFilePatchFactory.java | 49 ++ .../impl/patch/apply/ApplyTextFilePatch.java | 86 ++ .../formove/CustomBinaryPatchApplier.java | 5 +- .../diff/impl/patch/formove/PatchApplier.java | 36 +- .../impl/patch/formove/PathsVerifier.java | 28 +- .../RevertCommittedStuffAbstractAction.java | 3 +- .../vcs/changes/patch/ApplyPatchAction.java | 12 +- .../vcs/changes/patch/ApplyPatchDialog.form | 126 --- .../vcs/changes/patch/ApplyPatchDialog.java | 735 ------------------ .../patch/ApplyPatchForBaseRevisionTexts.java | 4 +- .../patch/LazyPatchContentRevision.java | 3 +- .../shelf/DiffShelvedChangesAction.java | 3 +- .../changes/shelf/ShelveChangesManager.java | 18 +- .../vcs/changes/shelf/ShelvedChange.java | 3 +- 25 files changed, 518 insertions(+), 1120 deletions(-) rename platform/{vcs-impl => util}/src/com/intellij/openapi/diff/impl/patch/PatchLine.java (100%) create mode 100644 platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyBinaryFilePatch.java create mode 100644 platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyBinaryShelvedFilePatch.java create mode 100644 platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyFilePatch.java create mode 100644 platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyFilePatchBase.java create mode 100644 platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyFilePatchFactory.java create mode 100644 platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyTextFilePatch.java delete mode 100644 platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDialog.form delete mode 100644 platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDialog.java diff --git a/platform/lvcs-impl/testSrc/com/intellij/historyIntegrTests/PatchingTestCase.java b/platform/lvcs-impl/testSrc/com/intellij/historyIntegrTests/PatchingTestCase.java index 6d89eb3df885..4b7be8efdcfd 100644 --- a/platform/lvcs-impl/testSrc/com/intellij/historyIntegrTests/PatchingTestCase.java +++ b/platform/lvcs-impl/testSrc/com/intellij/historyIntegrTests/PatchingTestCase.java @@ -16,6 +16,7 @@ package com.intellij.historyIntegrTests; +import com.intellij.openapi.diff.impl.patch.BinaryFilePatch; import com.intellij.openapi.diff.impl.patch.FilePatch; import com.intellij.openapi.diff.impl.patch.PatchReader; import com.intellij.openapi.diff.impl.patch.formove.PatchApplier; @@ -52,6 +53,6 @@ public abstract class PatchingTestCase extends IntegrationTestCase { patches.add(p); } - new PatchApplier(myProject, root, patches, null, null).execute(); + new PatchApplier(myProject, root, patches, null, null).execute(); } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchLine.java b/platform/util/src/com/intellij/openapi/diff/impl/patch/PatchLine.java similarity index 100% rename from platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchLine.java rename to platform/util/src/com/intellij/openapi/diff/impl/patch/PatchLine.java diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/BinaryFilePatch.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/BinaryFilePatch.java index 31ee275760f8..6fe7ff86f336 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/BinaryFilePatch.java +++ b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/BinaryFilePatch.java @@ -15,10 +15,6 @@ */ package com.intellij.openapi.diff.impl.patch; -import com.intellij.openapi.vfs.VirtualFile; - -import java.io.IOException; - /** * @author yole */ @@ -31,15 +27,6 @@ public class BinaryFilePatch extends FilePatch { myAfterContent = afterContent; } - protected void applyCreate(final VirtualFile newFile) throws IOException, ApplyPatchException { - newFile.setBinaryContent(myAfterContent); - } - - protected ApplyPatchStatus applyChange(final VirtualFile fileToPatch) throws IOException, ApplyPatchException { - fileToPatch.setBinaryContent(myAfterContent); - return ApplyPatchStatus.SUCCESS; - } - public boolean isNewFile() { return myBeforeContent == null; } @@ -47,4 +34,8 @@ public class BinaryFilePatch extends FilePatch { public boolean isDeletedFile() { return myAfterContent == null; } + + public byte[] getAfterContent() { + return myAfterContent; + } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/FilePatch.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/FilePatch.java index d33a87f8c292..b1f8fb3dd621 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/FilePatch.java +++ b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/FilePatch.java @@ -22,16 +22,7 @@ */ package com.intellij.openapi.diff.impl.patch; -import com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl; -import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vcs.FilePath; -import com.intellij.openapi.vcs.FilePathImpl; -import com.intellij.openapi.vfs.VirtualFile; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.IOException; public abstract class FilePatch { private String myBeforeName; @@ -86,137 +77,6 @@ public abstract class FilePatch { return StringUtil.join(components, skipDirs, components.length, "/"); } - public FilePath getTarget(final VirtualFile file) { - if (isNewFile()) { - return new FilePathImpl(file, getBeforeFileName(), false); - } - return new FilePathImpl(file); - } - - public ApplyPatchStatus apply(final VirtualFile fileToPatch, final ApplyPatchContext context, final Project project) throws IOException, ApplyPatchException { - context.addAffectedFile(getTarget(fileToPatch)); - return applyImpl(fileToPatch, project); - } - - public ApplyPatchStatus applyImpl(final VirtualFile fileToPatch, final Project project) throws IOException, ApplyPatchException { - if (isNewFile()) { - applyCreate(fileToPatch); - } - else if (isDeletedFile()) { - FileEditorManagerImpl.getInstance(project).closeFile(fileToPatch); - fileToPatch.delete(this); - } - else { - return applyChange(fileToPatch); - } - return ApplyPatchStatus.SUCCESS; - } - - protected abstract void applyCreate(VirtualFile newFile) throws IOException, ApplyPatchException; - protected abstract ApplyPatchStatus applyChange(VirtualFile fileToPatch) throws IOException, ApplyPatchException; - - @Nullable - public VirtualFile findFileToPatch(@NotNull ApplyPatchContext context) throws IOException { - return findPatchTarget(context, myBeforeName, myAfterName, isNewFile()); - } - - @Nullable - public static VirtualFile findPatchTarget(final ApplyPatchContext context, final String beforeName, final String afterName, - final boolean isNewFile) throws IOException { - VirtualFile file = null; - if (beforeName != null) { - file = findFileToPatchByName(context, beforeName, isNewFile); - } - if (file == null) { - file = findFileToPatchByName(context, afterName, isNewFile); - } - else if (context.isAllowRename() && afterName != null && !beforeName.equals(afterName)) { - String[] beforeNameComponents = beforeName.split("/"); - String[] afterNameComponents = afterName.split("/"); - if (!beforeNameComponents [beforeNameComponents.length-1].equals(afterNameComponents [afterNameComponents.length-1])) { - context.registerBeforeRename(file); - file.rename(FilePatch.class, afterNameComponents [afterNameComponents.length-1]); - context.addAffectedFile(file); - } - boolean needMove = (beforeNameComponents.length != afterNameComponents.length); - if (!needMove) { - needMove = checkPackageRename(context, beforeNameComponents, afterNameComponents); - } - if (needMove) { - VirtualFile moveTarget = findFileToPatchByComponents(context, afterNameComponents, afterNameComponents.length-1); - if (moveTarget == null) { - return null; - } - context.registerBeforeRename(file); - file.move(FilePatch.class, moveTarget); - context.addAffectedFile(file); - } - } - return file; - } - - private static boolean checkPackageRename(final ApplyPatchContext context, - final String[] beforeNameComponents, - final String[] afterNameComponents) { - int changedIndex = -1; - for(int i=context.getSkipTopDirs(); i lines = new ArrayList(); - Collections.addAll(lines, LineTokenizer.tokenize(text, false)); - ApplyPatchStatus result = null; - for(PatchHunk hunk: myHunks) { - result = ApplyPatchStatus.and(result, hunk.apply(lines)); - } - for(int i=0; i { + public ApplyBinaryFilePatch(BinaryFilePatch patch) { + super(patch); + } + + protected void applyCreate(final VirtualFile newFile) throws IOException, ApplyPatchException { + newFile.setBinaryContent(myPatch.getAfterContent()); + } + + protected ApplyPatchStatus applyChange(final VirtualFile fileToPatch) throws IOException, ApplyPatchException { + fileToPatch.setBinaryContent(myPatch.getAfterContent()); + return ApplyPatchStatus.SUCCESS; + } +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyBinaryShelvedFilePatch.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyBinaryShelvedFilePatch.java new file mode 100644 index 000000000000..076cf64dc1e7 --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyBinaryShelvedFilePatch.java @@ -0,0 +1,38 @@ +/* + * Copyright 2000-2010 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.openapi.diff.impl.patch.apply; + +import com.intellij.openapi.diff.impl.patch.ApplyPatchException; +import com.intellij.openapi.diff.impl.patch.ApplyPatchStatus; +import com.intellij.openapi.vcs.changes.shelf.ShelveChangesManager; +import com.intellij.openapi.vfs.VirtualFile; + +import java.io.IOException; + +public class ApplyBinaryShelvedFilePatch extends ApplyFilePatchBase { + public ApplyBinaryShelvedFilePatch(ShelveChangesManager.ShelvedBinaryFilePatch patch) { + super(patch); + } + + @Override + protected ApplyPatchStatus applyChange(VirtualFile fileToPatch) throws IOException, ApplyPatchException { + return null; + } + + @Override + protected void applyCreate(VirtualFile newFile) throws IOException, ApplyPatchException { + } +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyFilePatch.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyFilePatch.java new file mode 100644 index 000000000000..03e8a5639362 --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyFilePatch.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000-2010 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.openapi.diff.impl.patch.apply; + +import com.intellij.openapi.diff.impl.patch.ApplyPatchContext; +import com.intellij.openapi.diff.impl.patch.ApplyPatchException; +import com.intellij.openapi.diff.impl.patch.ApplyPatchStatus; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; + +public interface ApplyFilePatch { + ApplyPatchStatus apply(VirtualFile fileToPatch, ApplyPatchContext context, Project project) throws IOException, ApplyPatchException; + + ApplyPatchStatus applyImpl(VirtualFile fileToPatch, Project project) throws IOException, ApplyPatchException; + + @Nullable + VirtualFile findFileToPatch(@NotNull ApplyPatchContext context) throws IOException; +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyFilePatchBase.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyFilePatchBase.java new file mode 100644 index 000000000000..0b368a9e09ba --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyFilePatchBase.java @@ -0,0 +1,199 @@ +/* + * Copyright 2000-2010 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.openapi.diff.impl.patch.apply; + +import com.intellij.openapi.diff.impl.patch.*; +import com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.LineTokenizer; +import com.intellij.openapi.vcs.FilePath; +import com.intellij.openapi.vcs.FilePathImpl; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public abstract class ApplyFilePatchBase implements ApplyFilePatch { + protected final T myPatch; + + public ApplyFilePatchBase(T patch) { + myPatch = patch; + } + + public T getPatch() { + return myPatch; + } + + private FilePath getTarget(final VirtualFile file) { + if (myPatch.isNewFile()) { + return new FilePathImpl(file, myPatch.getBeforeFileName(), false); + } + return new FilePathImpl(file); + } + + public ApplyPatchStatus apply(final VirtualFile fileToPatch, final ApplyPatchContext context, final Project project) throws + IOException, ApplyPatchException { + context.addAffectedFile(getTarget(fileToPatch)); + return applyImpl(fileToPatch, project); + } + + public ApplyPatchStatus applyImpl(final VirtualFile fileToPatch, final Project project) throws IOException, ApplyPatchException { + if (myPatch.isNewFile()) { + applyCreate(fileToPatch); + } + else if (myPatch.isDeletedFile()) { + FileEditorManagerImpl.getInstance(project).closeFile(fileToPatch); + fileToPatch.delete(this); + } + else { + return applyChange(fileToPatch); + } + return ApplyPatchStatus.SUCCESS; + } + + protected abstract void applyCreate(VirtualFile newFile) throws IOException, ApplyPatchException; + @Nullable + protected abstract ApplyPatchStatus applyChange(VirtualFile fileToPatch) throws IOException, ApplyPatchException; + + @Nullable + public VirtualFile findFileToPatch(@NotNull ApplyPatchContext context) throws IOException { + return findPatchTarget(context, myPatch.getBeforeName(), myPatch.getAfterName(), myPatch.isNewFile()); + } + + @Nullable + public static VirtualFile findPatchTarget(final ApplyPatchContext context, final String beforeName, final String afterName, + final boolean isNewFile) throws IOException { + VirtualFile file = null; + if (beforeName != null) { + file = findFileToPatchByName(context, beforeName, isNewFile); + } + if (file == null) { + file = findFileToPatchByName(context, afterName, isNewFile); + } + else if (context.isAllowRename() && afterName != null && !beforeName.equals(afterName)) { + String[] beforeNameComponents = beforeName.split("/"); + String[] afterNameComponents = afterName.split("/"); + if (!beforeNameComponents [beforeNameComponents.length-1].equals(afterNameComponents [afterNameComponents.length-1])) { + context.registerBeforeRename(file); + file.rename(FilePatch.class, afterNameComponents [afterNameComponents.length-1]); + context.addAffectedFile(file); + } + boolean needMove = (beforeNameComponents.length != afterNameComponents.length); + if (!needMove) { + needMove = checkPackageRename(context, beforeNameComponents, afterNameComponents); + } + if (needMove) { + VirtualFile moveTarget = findFileToPatchByComponents(context, afterNameComponents, afterNameComponents.length-1); + if (moveTarget == null) { + return null; + } + context.registerBeforeRename(file); + file.move(FilePatch.class, moveTarget); + context.addAffectedFile(file); + } + } + return file; + } + + private static boolean checkPackageRename(final ApplyPatchContext context, + final String[] beforeNameComponents, + final String[] afterNameComponents) { + int changedIndex = -1; + for(int i=context.getSkipTopDirs(); i hunks = patch.getHunks(); + if (hunks.isEmpty()) { + return ApplyPatchStatus.SUCCESS; + } + List lines = new ArrayList(); + Collections.addAll(lines, LineTokenizer.tokenize(text, false)); + ApplyPatchStatus result = null; + for(PatchHunk hunk: hunks) { + result = ApplyPatchStatus.and(result, hunk.apply(lines)); + } + for(int i=0; i { + public ApplyTextFilePatch(final TextFilePatch patch) { + super(patch); + } + + @Nullable + protected ApplyPatchStatus applyChange(final VirtualFile fileToPatch) throws IOException, ApplyPatchException { + byte[] fileContents = fileToPatch.contentsToByteArray(); + CharSequence text = LoadTextUtil.getTextByBinaryPresentation(fileContents, fileToPatch); + StringBuilder newText = new StringBuilder(); + ApplyPatchStatus status = applyModifications(text, newText); + if (status != ApplyPatchStatus.ALREADY_APPLIED) { + final Document document = FileDocumentManager.getInstance().getDocument(fileToPatch); + if (document == null) { + throw new ApplyPatchException("Failed to set contents for updated file " + fileToPatch.getPath()); + } + document.setText(newText.toString()); + FileDocumentManager.getInstance().saveDocument(document); + } + return status; + } + + // todo taken to another place also, cheeeeeck + @Nullable + public ApplyPatchStatus applyModifications(final CharSequence text, final StringBuilder newText) throws ApplyPatchException { + final List hunks = myPatch.getHunks(); + if (hunks.size() == 0) { + return ApplyPatchStatus.SUCCESS; + } + List lines = new ArrayList(); + Collections.addAll(lines, LineTokenizer.tokenize(text, false)); + ApplyPatchStatus result = null; + for(PatchHunk hunk: hunks) { + result = ApplyPatchStatus.and(result, hunk.apply(lines)); + } + for(int i=0; i { @NotNull - ApplyPatchStatus apply(List> patches) throws IOException; + ApplyPatchStatus apply(List>> patches) throws IOException; @NotNull List getAppliedPatches(); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/formove/PatchApplier.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/formove/PatchApplier.java index eaa713eb261b..7ff2c143a0a0 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/formove/PatchApplier.java +++ b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/formove/PatchApplier.java @@ -21,6 +21,8 @@ import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.diff.impl.patch.ApplyPatchContext; import com.intellij.openapi.diff.impl.patch.ApplyPatchStatus; import com.intellij.openapi.diff.impl.patch.FilePatch; +import com.intellij.openapi.diff.impl.patch.apply.ApplyFilePatchBase; +import com.intellij.openapi.diff.impl.patch.apply.ApplyTextFilePatch; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.fileTypes.FileTypes; import com.intellij.openapi.fileTypes.ex.FileTypeChooser; @@ -52,25 +54,25 @@ import java.util.List; /** * for patches. for shelve. */ -public class PatchApplier { +public class PatchApplier { private final Project myProject; private final VirtualFile myBaseDirectory; private final List myPatches; - private final CustomBinaryPatchApplier myCustomForBinaries; + private final CustomBinaryPatchApplier myCustomForBinaries; private final LocalChangeList myTargetChangeList; private final List myRemainingPatches; - private final PathsVerifier myVerifier; + private final PathsVerifier myVerifier; public PatchApplier(final Project project, final VirtualFile baseDirectory, final List patches, - final LocalChangeList targetChangeList, final CustomBinaryPatchApplier customForBinaries) { + final LocalChangeList targetChangeList, final CustomBinaryPatchApplier customForBinaries) { myProject = project; myBaseDirectory = baseDirectory; myPatches = patches; myTargetChangeList = targetChangeList; myCustomForBinaries = customForBinaries; myRemainingPatches = new ArrayList(); - myVerifier = new PathsVerifier(myProject, myBaseDirectory, myPatches, new PathsVerifier.BaseMapper() { + myVerifier = new PathsVerifier(myProject, myBaseDirectory, myPatches, new PathsVerifier.BaseMapper() { @Nullable public VirtualFile getFile(FilePatch patch, String path) { return PathMerger.getFile(myBaseDirectory, path); @@ -137,7 +139,7 @@ public class PatchApplier { return; } - final List> textPatches = myVerifier.getTextPatches(); + final List> textPatches = myVerifier.getTextPatches(); if (! fileTypesAreOk(textPatches)) { return; } @@ -184,8 +186,8 @@ public class PatchApplier { } @Nullable - private ApplyPatchStatus actualApply(final PathsVerifier verifier) { - final List> textPatches = verifier.getTextPatches(); + private ApplyPatchStatus actualApply(final PathsVerifier verifier) { + final List> textPatches = verifier.getTextPatches(); final ApplyPatchContext context = new ApplyPatchContext(myBaseDirectory, 0, true, true); ApplyPatchStatus status = null; @@ -195,7 +197,7 @@ public class PatchApplier { if (myCustomForBinaries == null) { status = applyList(verifier.getBinaryPatches(), context, status); } else { - final List> binaryPatches = verifier.getBinaryPatches(); + final List>> binaryPatches = verifier.getBinaryPatches(); ApplyPatchStatus patchStatus = myCustomForBinaries.apply(binaryPatches); final List appliedPatches = myCustomForBinaries.getAppliedPatches(); moveForCustomBinaries(binaryPatches, appliedPatches); @@ -211,24 +213,24 @@ public class PatchApplier { return status; } - private void moveForCustomBinaries(final List> patches, + private void moveForCustomBinaries(final List>> patches, final List appliedPatches) throws IOException { - for (Pair patch : patches) { - if (appliedPatches.contains(patch.getSecond())) { + for (Pair> patch : patches) { + if (appliedPatches.contains(patch.getSecond().getPatch())) { myVerifier.doMoveIfNeeded(patch.getFirst()); } } } - private ApplyPatchStatus applyList(final List> patches, final ApplyPatchContext context, + private > ApplyPatchStatus applyList(final List> patches, final ApplyPatchContext context, ApplyPatchStatus status) throws IOException { - for (Pair patch : patches) { + for (Pair patch : patches) { ApplyPatchStatus patchStatus = ApplyPatchAction.applyOnly(myProject, patch.getSecond(), context, patch.getFirst()); myVerifier.doMoveIfNeeded(patch.getFirst()); status = ApplyPatchStatus.and(status, patchStatus); if (patchStatus != ApplyPatchStatus.FAILURE) { - myRemainingPatches.remove(patch.getSecond()); + myRemainingPatches.remove(patch.getSecond().getPatch()); } else { // interrupt if failure return status; @@ -259,8 +261,8 @@ public class PatchApplier { return (! readonlyStatus.hasReadonlyFiles()); } - private boolean fileTypesAreOk(final List> textPatches) { - for (Pair textPatch : textPatches) { + private boolean fileTypesAreOk(final List> textPatches) { + for (Pair textPatch : textPatches) { final VirtualFile file = textPatch.getFirst(); if (! file.isDirectory()) { FileType fileType = file.getFileType(); diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/formove/PathsVerifier.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/formove/PathsVerifier.java index c5bf4a99724c..d2d57c6ac303 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/formove/PathsVerifier.java +++ b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/formove/PathsVerifier.java @@ -15,8 +15,12 @@ */ package com.intellij.openapi.diff.impl.patch.formove; +import com.intellij.openapi.diff.impl.patch.BinaryFilePatch; import com.intellij.openapi.diff.impl.patch.FilePatch; import com.intellij.openapi.diff.impl.patch.TextFilePatch; +import com.intellij.openapi.diff.impl.patch.apply.ApplyFilePatchBase; +import com.intellij.openapi.diff.impl.patch.apply.ApplyFilePatchFactory; +import com.intellij.openapi.diff.impl.patch.apply.ApplyTextFilePatch; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Pair; @@ -25,6 +29,7 @@ import com.intellij.openapi.vcs.FilePathImpl; import com.intellij.openapi.vcs.ProjectLevelVcsManager; import com.intellij.openapi.vcs.VcsBundle; import com.intellij.openapi.vcs.changes.patch.RelativePathCalculator; +import com.intellij.openapi.vcs.changes.shelf.ShelveChangesManager; import com.intellij.openapi.vcs.impl.ExcludedFileIndex; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; @@ -34,7 +39,7 @@ import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.util.*; -public class PathsVerifier { +public class PathsVerifier { // in private final Project myProject; private final VirtualFile myBaseDirectory; @@ -44,8 +49,8 @@ public class PathsVerifier { private final List myBeforePaths; private final List myCreatedDirectories; // out - private final List> myTextPatches; - private final List> myBinaryPatches; + private final List> myTextPatches; + private final List>> myBinaryPatches; private final List myWritableFiles; private final BaseMapper myBaseMapper; @@ -58,8 +63,8 @@ public class PathsVerifier { myMovedFiles = new HashMap(); myBeforePaths = new ArrayList(); myCreatedDirectories = new ArrayList(); - myTextPatches = new ArrayList>(); - myBinaryPatches = new ArrayList>(); + myTextPatches = new ArrayList>(); + myBinaryPatches = new ArrayList>>(); myWritableFiles = new ArrayList(); } @@ -284,11 +289,14 @@ public class PathsVerifier { } private void addPatch(final FilePatch patch, final VirtualFile file) { - final Pair patchPair = new Pair(file, patch); + final Pair patchPair = new Pair(file, ApplyFilePatchFactory.createGeneral(patch)); if (patch instanceof TextFilePatch) { - myTextPatches.add(patchPair); + myTextPatches.add(new Pair(file, ApplyFilePatchFactory.create((TextFilePatch) patch))); } else { - myBinaryPatches.add(patchPair); + final ApplyFilePatchBase applyBinaryPatch = (ApplyFilePatchBase) ((patch instanceof BinaryFilePatch) ? ApplyFilePatchFactory + .create((BinaryFilePatch) patch) : + ApplyFilePatchFactory.create((ShelveChangesManager.ShelvedBinaryFilePatch) patch)); + myBinaryPatches.add(new Pair>(file, applyBinaryPatch)); } myWritableFiles.add(file); } @@ -410,11 +418,11 @@ public class PathsVerifier { return child; } - public List> getTextPatches() { + public List> getTextPatches() { return myTextPatches; } - public List> getBinaryPatches() { + public List>> getBinaryPatches() { return myBinaryPatches; } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/RevertCommittedStuffAbstractAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/RevertCommittedStuffAbstractAction.java index fc17779c4742..d953a1650c71 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/RevertCommittedStuffAbstractAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/RevertCommittedStuffAbstractAction.java @@ -18,6 +18,7 @@ package com.intellij.openapi.vcs.changes.actions; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.PlatformDataKeys; +import com.intellij.openapi.diff.impl.patch.BinaryFilePatch; import com.intellij.openapi.diff.impl.patch.FilePatch; import com.intellij.openapi.diff.impl.patch.TextPatchBuilder; import com.intellij.openapi.diff.impl.patch.formove.PatchApplier; @@ -76,7 +77,7 @@ abstract class RevertCommittedStuffAbstractAction extends AnAction implements Du Messages.showErrorDialog(project, "Failed to revert changes: " + ex.getMessage(), VcsBundle.message("revert.changes.title")); return; } - new PatchApplier(project, baseDir, patches, chooser.getSelectedList(), null).execute(); + new PatchApplier(project, baseDir, patches, chooser.getSelectedList(), null).execute(); } public void update(final AnActionEvent e) { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchAction.java index c96dacad9137..353b5223ce4f 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchAction.java @@ -32,6 +32,7 @@ import com.intellij.openapi.diff.DiffManager; import com.intellij.openapi.diff.DiffRequestFactory; import com.intellij.openapi.diff.MergeRequest; import com.intellij.openapi.diff.impl.patch.*; +import com.intellij.openapi.diff.impl.patch.apply.ApplyFilePatchBase; import com.intellij.openapi.diff.impl.patch.formove.PatchApplier; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileEditor.impl.LoadTextUtil; @@ -72,7 +73,7 @@ public class ApplyPatchAction extends AnAction { final Collection appliers = new LinkedList(); for (VirtualFile base : patchGroups.keySet()) { final PatchApplier patchApplier = - new PatchApplier(project, base, ObjectsConvertor.convert(patchGroups.get(base), new Convertor() { + new PatchApplier(project, base, ObjectsConvertor.convert(patchGroups.get(base), new Convertor() { public FilePatch convert(FilePatchInProgress o) { return o.getPatch(); } @@ -107,20 +108,21 @@ public class ApplyPatchAction extends AnAction { return sb.toString(); } - public static ApplyPatchStatus applyOnly(final Project project, final FilePatch patch, final ApplyPatchContext context, final VirtualFile file) { + public static ApplyPatchStatus applyOnly(final Project project, final ApplyFilePatchBase patch, final ApplyPatchContext context, final VirtualFile file) { + final T patchBase = patch.getPatch(); try { return patch.apply(file, context, project); } catch(ApplyPatchException ex) { - if (!patch.isNewFile() && !patch.isDeletedFile() && patch instanceof TextFilePatch) { + if (!patchBase.isNewFile() && !patchBase.isDeletedFile() && patchBase instanceof TextFilePatch) { //final VirtualFile beforeRename = (pathBeforeRename == null) ? file : pathBeforeRename; - ApplyPatchStatus mergeStatus = mergeAgainstBaseVersion(project, file, new FilePathImpl(file), (TextFilePatch) patch, + ApplyPatchStatus mergeStatus = mergeAgainstBaseVersion(project, file, new FilePathImpl(file), (TextFilePatch) patchBase, ApplyPatchMergeRequestFactory.INSTANCE); if (mergeStatus != null) { return mergeStatus; } } - Messages.showErrorDialog(project, VcsBundle.message("patch.apply.error", patch.getBeforeName(), ex.getMessage()), + Messages.showErrorDialog(project, VcsBundle.message("patch.apply.error", patchBase.getBeforeName(), ex.getMessage()), VcsBundle.message("patch.apply.dialog.title")); } catch (Exception ex) { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDialog.form b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDialog.form deleted file mode 100644 index fc880aeb17e9..000000000000 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDialog.form +++ /dev/null @@ -1,126 +0,0 @@ - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDialog.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDialog.java deleted file mode 100644 index 136892925fe6..000000000000 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDialog.java +++ /dev/null @@ -1,735 +0,0 @@ -/* - * Copyright 2000-2009 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.openapi.vcs.changes.patch; - -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.CommonShortcuts; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.command.CommandProcessor; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.diff.impl.patch.*; -import com.intellij.openapi.diff.impl.patch.formove.PathMerger; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.fileChooser.FileChooserDescriptor; -import com.intellij.openapi.fileEditor.FileDocumentManager; -import com.intellij.openapi.fileTypes.FileType; -import com.intellij.openapi.fileTypes.FileTypeManager; -import com.intellij.openapi.fileTypes.FileTypes; -import com.intellij.openapi.fileTypes.StdFileTypes; -import com.intellij.openapi.help.HelpManager; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.ui.TextFieldWithBrowseButton; -import com.intellij.openapi.util.Comparing; -import com.intellij.openapi.util.Computable; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.vcs.FilePath; -import com.intellij.openapi.vcs.FilePathImpl; -import com.intellij.openapi.vcs.FileStatus; -import com.intellij.openapi.vcs.VcsBundle; -import com.intellij.openapi.vcs.changes.*; -import com.intellij.openapi.vcs.changes.actions.ShowDiffAction; -import com.intellij.openapi.vcs.changes.ui.ChangeListChooserPanel; -import com.intellij.openapi.vfs.LocalFileSystem; -import com.intellij.openapi.vfs.VfsUtil; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.ui.CollectionListModel; -import com.intellij.ui.ColoredListCellRenderer; -import com.intellij.ui.DocumentAdapter; -import com.intellij.ui.SimpleTextAttributes; -import com.intellij.util.Alarm; -import com.intellij.util.ArrayUtil; -import com.intellij.util.Consumer; -import com.intellij.util.ui.UIUtil; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.PropertyKey; - -import javax.swing.*; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import javax.swing.event.DocumentEvent; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.io.File; -import java.io.IOException; -import java.util.*; -import java.util.List; - -/** - * @author yole - */ -public class ApplyPatchDialog extends DialogWrapper { - private final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.changes.patch.ApplyPatchDialog"); - - private JPanel myRootPanel; - private TextFieldWithBrowseButton myFileNameField; - private JLabel myStatusLabel; - private TextFieldWithBrowseButton myBaseDirectoryField; - private JSpinner myStripLeadingDirectoriesSpinner; - private JList myPatchContentsList; - private ChangeListChooserPanel myChangeListChooser; - private JButton myShowDiffButton; - private List myPatches; - private Collection myPatchesFailedToLoad; - private final Alarm myLoadPatchAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD); - private final Alarm myVerifyPatchAlarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD); - private String myLoadPatchError = null; - private String myDetectedBaseDirectory = null; - private int myDetectedStripLeadingDirs = -1; - private final Project myProject; - private boolean myInnerChange; - private LocalChangeList mySelectedChangeList; - - private final Map, String> myMoveRenameInfo; - - public ApplyPatchDialog(Project project) { - super(project, true); - myProject = project; - setTitle(VcsBundle.message("patch.apply.dialog.title")); - final FileChooserDescriptor descriptor = new FileChooserDescriptor(true, false, false, false, false, false) { - @Override - public boolean isFileSelectable(VirtualFile file) { - return file.getFileType() == StdFileTypes.PATCH || file.getFileType() == FileTypes.PLAIN_TEXT; - } - }; - myMoveRenameInfo = new HashMap, String>(); - myFileNameField.addBrowseFolderListener(VcsBundle.message("patch.apply.select.title"), "", project, descriptor); - myFileNameField.getTextField().getDocument().addDocumentListener(new DocumentAdapter() { - protected void textChanged(DocumentEvent e) { - updateOKAction(); - myStatusLabel.setForeground(UIUtil.getLabelForeground()); - myStatusLabel.setText(VcsBundle.message("patch.load.progress")); - myPatches = null; - myMoveRenameInfo.clear(); - myLoadPatchAlarm.cancelAllRequests(); - myLoadPatchAlarm.addRequest(new Runnable() { - public void run() { - checkLoadPatches(true); - } - }, 400); - } - }); - - myBaseDirectoryField.setText(project.getBaseDir().getPresentableUrl()); - myBaseDirectoryField.addBrowseFolderListener(VcsBundle.message("patch.apply.select.base.directory.title"), "", project, - new FileChooserDescriptor(false, true, false, false, false, false)); - myBaseDirectoryField.getTextField().getDocument().addDocumentListener(new DocumentAdapter() { - protected void textChanged(final DocumentEvent e) { - if (!myInnerChange) { - queueVerifyPatchPaths(); - } - } - }); - - myStripLeadingDirectoriesSpinner.setModel(new SpinnerNumberModel(0, 0, 256, 1)); - myStripLeadingDirectoriesSpinner.addChangeListener(new ChangeListener() { - public void stateChanged(final ChangeEvent e) { - if (!myInnerChange) { - queueVerifyPatchPaths(); - } - } - }); - - myPatchContentsList.setCellRenderer(new PatchCellRendererPanel()); - - ChangeListManager changeListManager = ChangeListManager.getInstance(project); - myChangeListChooser.setChangeLists(changeListManager.getChangeListsCopy()); - myChangeListChooser.setDefaultSelection(changeListManager.getDefaultChangeList()); - myChangeListChooser.init(project); - init(); - updateOKAction(); - myShowDiffButton.addActionListener(new ActionListener() { - public void actionPerformed(final ActionEvent e) { - showDiff(); - } - }); - myPatchContentsList.addMouseListener(new MouseAdapter() { - public void mouseClicked(final MouseEvent e) { - if (e.getButton() == 1 && e.getClickCount() == 2) { - showDiff(); - } - } - }); - - new AnAction() { - @Override - public void actionPerformed(AnActionEvent e) { - showDiff(); - } - }.registerCustomShortcutSet(CommonShortcuts.getDiff(), myRootPanel, myDisposable); - } - - private void showDiff() { - List changes = new ArrayList(); - ApplyPatchContext context = getApplyPatchContext().getPrepareContext(); - Object[] selection = myPatchContentsList.getSelectedValues(); - if (selection.length == 0) { - if (myPatches == null) return; - selection = ArrayUtil.toObjectArray(myPatches); - } - for(Object o: selection) { - final TextFilePatch patch = (TextFilePatch) o; - try { - if (patch.isNewFile()) { - final FilePath newFilePath = FilePathImpl.createNonLocal(patch.getAfterName(), false); - final String content = patch.getNewFileText(); - ContentRevision revision = new SimpleContentRevision(content, newFilePath, patch.getAfterVersionId()); - changes.add(new Change(null, revision)); - } else if ((! patch.isDeletedFile()) && (patch.getBeforeName() != null) && (patch.getAfterName() != null) && - (! patch.getBeforeName().equals(patch.getAfterName()))) { - - final VirtualFile baseDirectory = getBaseDirectory(); - final VirtualFile beforeFile = PathMerger.getFile(baseDirectory, patch.getBeforeName()); - - if (beforeFile != null) { - final List tail = new ArrayList(); - final VirtualFile partFile = PathMerger.getFile(baseDirectory, patch.getAfterName(), tail); - final StringBuilder sb = new StringBuilder(partFile.getPath()); - for (String s : tail) { - if (sb.charAt(sb.length() - 1) != '/') { - sb.append('/'); - } - sb.append(s); - } - - final Change change = - changeForPath(beforeFile, patch, FilePathImpl.createNonLocal(FileUtil.toSystemIndependentName(sb.toString()), false)); - if (change != null) { - changes.add(change); - } - } else { - Messages.showErrorDialog(myProject, "Cannot show difference: cannot find file " + patch.getBeforeName(), - VcsBundle.message("patch.apply.dialog.title")); - } - } - else { - final VirtualFile fileToPatch = patch.findFileToPatch(context); - if (fileToPatch != null) { - final FilePathImpl filePath = new FilePathImpl(fileToPatch); - final CurrentContentRevision currentRevision = new CurrentContentRevision(filePath); - if (patch.isDeletedFile()) { - changes.add(new Change(currentRevision, null)); - } - else { - final Change change = changeForPath(fileToPatch, patch, null); - if (change != null) { - changes.add(change); - } - } - } - } - } - catch (Exception e) { - Messages.showErrorDialog(myProject, "Error loading changes for " + patch.getAfterFileName() + ": " + e.getMessage(), - VcsBundle.message("patch.apply.dialog.title")); - return; - } - } - ShowDiffAction.showDiffForChange(changes.toArray(new Change[changes.size()]), 0, myProject, - ShowDiffAction.DiffExtendUIFactory.NONE, false); - } - - @Nullable - private Change changeForPath(final VirtualFile fileToPatch, final TextFilePatch patch, final FilePath newFilePath) { - try { - final FilePathImpl filePath = new FilePathImpl(fileToPatch); - final CurrentContentRevision currentRevision = new CurrentContentRevision(filePath); - final Document doc = FileDocumentManager.getInstance().getDocument(fileToPatch); - String baseContent = doc.getText(); - StringBuilder newText = new StringBuilder(); - patch.applyModifications(baseContent, newText); - ContentRevision revision = new SimpleContentRevision(newText.toString(), (newFilePath == null) ? filePath : newFilePath, patch.getAfterVersionId()); - return new Change(currentRevision, revision); - } catch (ApplyPatchException e) { - ApplyPatchContext context = new ApplyPatchContext(getBaseDirectory(), 0, false, false); - // just show diff here. maybe refactor further.. - ApplyPatchAction.mergeAgainstBaseVersion(myProject, fileToPatch, context, patch, ApplyPatchAction.ApplyPatchMergeRequestFactory.INSTANCE_READ_ONLY); - return null; - } - } - - @Override - @NonNls - protected String getDimensionServiceKey() { - return "vcs.ApplyPatchDialog"; - } - - private void queueVerifyPatchPaths() { - myStatusLabel.setForeground(UIUtil.getLabelForeground()); - myStatusLabel.setText(VcsBundle.message("apply.patch.progress.verifying")); - myVerifyPatchAlarm.cancelAllRequests(); - myVerifyPatchAlarm.addRequest(new Runnable() { - public void run() { - try { - if (myPatches != null) { - verifyPatchPaths(); - } - } - catch(Exception ex) { - LOG.error(ex); - } - } - }, 400); - } - - public void setFileName(String fileName) { - myFileNameField.setText(fileName); - checkLoadPatches(false); - } - - private void checkLoadPatches(final boolean async) { - final String fileName = myFileNameField.getText().replace(File.separatorChar, '/'); - final VirtualFile patchFile = ApplicationManager.getApplication().runWriteAction(new Computable() { - public VirtualFile compute() { - final VirtualFile file = LocalFileSystem.getInstance().refreshAndFindFileByPath(fileName); - if (file != null) { - file.refresh(false, false); - if (file.isDirectory()) { - // we are looking for file not directory - return null; - } - } - return file; - } - }); - if (patchFile == null) { - queueUpdateStatus("Cannot find patch file"); - return; - } - myChangeListChooser.setDefaultName(patchFile.getNameWithoutExtension().replace('_', ' ').trim()); - if (async) { - ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { - public void run() { - loadPatchesFromFile(patchFile); - } - }); - } - else { - loadPatchesFromFile(patchFile); - } - } - - private void loadPatchesFromFile(final VirtualFile patchFile) { - myPatches = new ArrayList(); - myPatchesFailedToLoad = new HashSet(); - ApplicationManager.getApplication().runReadAction(new Runnable() { - public void run() { - if (!patchFile.isValid()) { - queueUpdateStatus("Cannot find patch file"); - return; - } - PatchReader reader; - try { - reader = new PatchReader(patchFile); - } - catch (IOException e) { - queueUpdateStatus(VcsBundle.message("patch.apply.open.error", e.getMessage())); - return; - } - while(true) { - FilePatch patch; - try { - patch = reader.readNextPatch(); - } - catch (PatchSyntaxException e) { - if (e.getLine() >= 0) { - queueUpdateStatus(VcsBundle.message("patch.apply.load.error.line", e.getMessage(), e.getLine())); - } - else { - queueUpdateStatus(VcsBundle.message("patch.apply.load.error", e.getMessage())); - } - return; - } - if (patch == null) { - break; - } - - final String beforeName = patch.getBeforeName(); - final String afterName = patch.getAfterName(); - final String movedMessage = RelativePathCalculator.getMovedString(beforeName, afterName); - if (movedMessage != null) { - myMoveRenameInfo.put(new Pair(beforeName, afterName), movedMessage); - } - myPatches.add(patch); - } - if (myPatches.isEmpty()) { - queueUpdateStatus(VcsBundle.message("patch.apply.no.patches.found")); - return; - } - - autoDetectBaseDirectory(); - queueUpdateStatus(null); - } - }); - } - - private void autoDetectBaseDirectory() { - boolean autodetectFailed = false; - for(FilePatch patch: myPatches) { - VirtualFile baseDir = myDetectedBaseDirectory == null - ? getBaseDirectory() - : LocalFileSystem.getInstance().findFileByPath(myDetectedBaseDirectory.replace(File.separatorChar, '/')); - int skipTopDirs = myDetectedStripLeadingDirs >= 0 ? myDetectedStripLeadingDirs : 0; - VirtualFile fileToPatch; - try { - fileToPatch = patch.findFileToPatch(new ApplyPatchContext(baseDir, skipTopDirs, false, false)); - } - catch (IOException e) { - myPatchesFailedToLoad.add(patch); - continue; - } - if (fileToPatch == null) { - boolean success = false; - if (!autodetectFailed) { - String oldDetectedBaseDirectory = myDetectedBaseDirectory; - int oldDetectedStripLeadingDirs = myDetectedStripLeadingDirs; - success = detectDirectory(patch); - if (success) { - if ((oldDetectedBaseDirectory != null && !Comparing.equal(oldDetectedBaseDirectory, myDetectedBaseDirectory)) || - (oldDetectedStripLeadingDirs >= 0 && oldDetectedStripLeadingDirs != myDetectedStripLeadingDirs)) { - myDetectedBaseDirectory = null; - myDetectedStripLeadingDirs = -1; - autodetectFailed = true; - } - } - } - if (!success) { - myPatchesFailedToLoad.add(patch); - } - } - } - } - - private boolean detectDirectory(final FilePatch patch) { - if (patch.getBeforeName().equals(patch.getAfterName()) && patch.isNewFile()) { - return false; - } else { - boolean success = detectDirectoryByName(patch.getBeforeName()); - if (! success) { - success = detectDirectoryByName(patch.getAfterName()); - } - return success; - } - } - - private Collection verifyPatchPaths() { - final ApplyPatchContext context = getApplyPatchContext(); - myPatchesFailedToLoad.clear(); - for(FilePatch patch: myPatches) { - try { - if (context.getBaseDir() == null || patch.findFileToPatch(context) == null) { - myPatchesFailedToLoad.add(patch); - } - } - catch (IOException e) { - myPatchesFailedToLoad.add(patch); - } - } - SwingUtilities.invokeLater(new Runnable() { - public void run() { - myPatchContentsList.repaint(); - myStatusLabel.setText(""); - } - }); - return context.getMissingDirectories(); - } - - private boolean detectDirectoryByName(final String patchFileName) { - PatchBaseDirectoryDetector detector = PatchBaseDirectoryDetector.getInstance(myProject); - if (detector == null) return false; - final PatchBaseDirectoryDetector.Result result = detector.detectBaseDirectory(patchFileName); - if (result == null) return false; - myDetectedBaseDirectory = result.baseDir; - myDetectedStripLeadingDirs = result.stripDirs; - return true; - } - - private void queueUpdateStatus(final String s) { - if (!SwingUtilities.isEventDispatchThread()) { - SwingUtilities.invokeLater(new Runnable() { - public void run() { - queueUpdateStatus(s); - } - }); - return; - } - updateStatus(s); - } - - private void updateStatus(String s) { - myInnerChange = true; - try { - if (myDetectedBaseDirectory != null) { - myBaseDirectoryField.setText(myDetectedBaseDirectory); - myDetectedBaseDirectory = null; - } - if (myDetectedStripLeadingDirs != -1) { - myStripLeadingDirectoriesSpinner.setValue(myDetectedStripLeadingDirs); - myDetectedStripLeadingDirs = -1; - } - } - finally { - myInnerChange = false; - } - myLoadPatchError = s; - if (s == null) { - myStatusLabel.setForeground(UIUtil.getLabelForeground()); - myStatusLabel.setText(buildPatchSummary()); - } - else { - myStatusLabel.setText(s); - myStatusLabel.setForeground(Color.red); - } - updatePatchTableModel(); - updateOKAction(); - } - - private void updatePatchTableModel() { - if (myPatches != null) { - myPatchContentsList.setModel(new CollectionListModel(myPatches)); - } - else { - myPatchContentsList.setModel(new DefaultListModel()); - } - myShowDiffButton.setEnabled(myPatches != null && myPatches.size() > 0); - } - - private String buildPatchSummary() { - int newFiles = 0; - int changedFiles = 0; - int deletedFiles = 0; - for(FilePatch patch: myPatches) { - if (patch.isNewFile()) { - newFiles++; - } - else if (patch.isDeletedFile()) { - deletedFiles++; - } - else { - changedFiles++; - } - } - StringBuilder summaryBuilder = new StringBuilder("").append(VcsBundle.message("apply.patch.summary.title")).append(" "); - appendSummary(changedFiles, 0, summaryBuilder, "patch.summary.changed.files"); - appendSummary(newFiles, changedFiles, summaryBuilder, "patch.summary.new.files"); - appendSummary(deletedFiles, changedFiles + newFiles, summaryBuilder, "patch.summary.deleted.files"); - summaryBuilder.append(""); - return summaryBuilder.toString(); - } - - private static void appendSummary(final int count, final int prevCount, final StringBuilder summaryBuilder, - @PropertyKey(resourceBundle = "messages.VcsBundle") final String key) { - if (count > 0) { - if (prevCount > 0) { - summaryBuilder.append(", "); - } - summaryBuilder.append(VcsBundle.message(key, count)); - } - } - - @Override - protected void dispose() { - myLoadPatchAlarm.dispose(); - myVerifyPatchAlarm.dispose(); - super.dispose(); - } - - private void updateOKAction() { - setOKActionEnabled(myFileNameField.getText().length() > 0 && myLoadPatchError == null); - } - - @Override - protected void doOKAction() { - if (myPatches == null) { - myLoadPatchAlarm.cancelAllRequests(); - checkLoadPatches(false); - } - if (myLoadPatchError == null) { - mySelectedChangeList = myChangeListChooser.getSelectedList(myProject); - if (mySelectedChangeList == null) return; - final Collection missingDirs = verifyPatchPaths(); - if (missingDirs.size() > 0 && !checkCreateMissingDirs(missingDirs)) return; - if (getBaseDirectory() == null) { - Messages.showErrorDialog(getContentPane(), "Could not find patch base directory " + myBaseDirectoryField.getText()); - return; - } - super.doOKAction(); - } - } - - private boolean checkCreateMissingDirs(final Collection missingDirs) { - StringBuilder messageBuilder = new StringBuilder(VcsBundle.message("apply.patch.create.dirs.prompt.header")); - for(String missingDir: missingDirs) { - messageBuilder.append(missingDir).append("\r\n"); - } - messageBuilder.append(VcsBundle.message("apply.patch.create.dirs.prompt.footer")); - int rc = Messages.showYesNoCancelDialog(myProject, messageBuilder.toString(), VcsBundle.message("patch.apply.dialog.title"), - Messages.getQuestionIcon()); - if (rc == 0) { - CommandProcessor.getInstance().executeCommand(myProject, new Runnable() { - public void run() { - for(String dir: missingDirs) { - try { - VfsUtil.createDirectories(dir); - } - catch (IOException e) { - Messages.showErrorDialog(myProject, "Error creating directories: " + e.getMessage(), - VcsBundle.message("patch.apply.dialog.title")); - } - } - } - }, "Creating directories for new files in patch", null); - } - else if (rc != 1) { - return false; - } - return true; - } - - @Nullable - protected JComponent createCenterPanel() { - return myRootPanel; - } - - public List getPatches() { - return myPatches; - } - - private VirtualFile getBaseDirectory() { - if (ApplicationManager.getApplication().isDispatchThread()) { - return LocalFileSystem.getInstance().refreshAndFindFileByPath(FileUtil.toSystemIndependentName(myBaseDirectoryField.getText())); - } - return LocalFileSystem.getInstance().findFileByPath(FileUtil.toSystemIndependentName(myBaseDirectoryField.getText())); - } - - private int getStripLeadingDirectories() { - return ((Integer) myStripLeadingDirectoriesSpinner.getValue()).intValue(); - } - - public ApplyPatchContext getApplyPatchContext() { - return new ApplyPatchContext(getBaseDirectory(), getStripLeadingDirectories(), false, false); - } - - public LocalChangeList getSelectedChangeList() { - return mySelectedChangeList; - } - - private static String getChangeType(final FilePatch filePatch) { - if (filePatch.isNewFile()) return VcsBundle.message("change.type.new"); - if (filePatch.isDeletedFile()) return VcsBundle.message("change.type.deleted"); - return VcsBundle.message("change.type.modified"); - } - - protected void doHelpAction() { - HelpManager.getInstance().invokeHelp("reference.dialogs.vcs.patch.apply"); - } - - protected Action[] createActions() { - return new Action[]{ getOKAction(), getCancelAction(), getHelpAction() }; - } - - private void createUIComponents() { - myChangeListChooser = new ChangeListChooserPanel(null, new Consumer() { - public void consume(final String errorMessage) { - setOKActionEnabled(errorMessage == null); - setErrorText(errorMessage); - } - }); - } - - private class PatchCellRendererPanel extends JPanel implements ListCellRenderer { - private final PatchCellRenderer myRenderer; - private final JLabel myFileTypeLabel; - - public PatchCellRendererPanel() { - super(new BorderLayout()); - setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 2)); - myRenderer = new PatchCellRenderer(); - add(myRenderer, BorderLayout.CENTER); - myFileTypeLabel = new JLabel(); - myFileTypeLabel.setHorizontalAlignment(JLabel.RIGHT); - add(myFileTypeLabel, BorderLayout.EAST); - } - - public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { - FilePatch patch = (FilePatch) value; - myRenderer.getListCellRendererComponent(list, value, index, isSelected, false); - myFileTypeLabel.setText("(" + getChangeType(patch) + ")"); - if (isSelected) { - setBackground(UIUtil.getListSelectionBackground()); - setForeground(UIUtil.getListSelectionForeground()); - myFileTypeLabel.setForeground(UIUtil.getListSelectionForeground()); - } - else { - setBackground(UIUtil.getListBackground()); - setForeground(UIUtil.getListForeground()); - myFileTypeLabel.setForeground(Color.gray); - } - return this; - } - } - - private class PatchCellRenderer extends ColoredListCellRenderer { - private final SimpleTextAttributes myNewAttributes = new SimpleTextAttributes(0, FileStatus.ADDED.getColor()); - private final SimpleTextAttributes myDeletedAttributes = new SimpleTextAttributes(0, FileStatus.DELETED.getColor()); - private final SimpleTextAttributes myModifiedAttributes = new SimpleTextAttributes(0, FileStatus.MODIFIED.getColor()); - - private boolean assumeProblemWillBeFixed(final FilePatch filePatch) { - // if some of the files are valid, assume that "red" new files will be fixed by creating directories - if (myPatches == null || myPatchesFailedToLoad == null) return false; - return (filePatch.isNewFile() && myPatchesFailedToLoad.size() != myPatches.size()); - } - - protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) { - FilePatch filePatch = (FilePatch) value; - String name = filePatch.getAfterNameRelative(getStripLeadingDirectories()); - - final FileType fileType = FileTypeManager.getInstance().getFileTypeByFileName(name); - setIcon(fileType.getIcon()); - - if (myPatchesFailedToLoad.contains(filePatch) && !assumeProblemWillBeFixed(filePatch)) { - append(name, SimpleTextAttributes.ERROR_ATTRIBUTES); - } - else if (filePatch.isNewFile()) { - append(name, myNewAttributes); - } - else if (filePatch.isDeletedFile()) { - append(name, myDeletedAttributes); - } - else { - append(name, myModifiedAttributes); - } - - final String afterPath = filePatch.getAfterName(); - final String beforePath = filePatch.getBeforeName(); - - if ((beforePath != null) && (afterPath != null) && (! beforePath.equals(afterPath))) { - final String message = myMoveRenameInfo.get(new Pair(beforePath, afterPath)); - if (message != null) { - append(message, SimpleTextAttributes.REGULAR_ATTRIBUTES); - } - } - } - } -} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchForBaseRevisionTexts.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchForBaseRevisionTexts.java index 9e7722e1135a..d1c5902efc6d 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchForBaseRevisionTexts.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchForBaseRevisionTexts.java @@ -18,13 +18,13 @@ package com.intellij.openapi.vcs.changes.patch; import com.intellij.openapi.diff.impl.patch.ApplyPatchException; import com.intellij.openapi.diff.impl.patch.ApplyPatchStatus; import com.intellij.openapi.diff.impl.patch.TextFilePatch; +import com.intellij.openapi.diff.impl.patch.apply.ApplyFilePatchBase; import com.intellij.openapi.fileEditor.impl.LoadTextUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.Processor; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class ApplyPatchForBaseRevisionTexts { @@ -58,7 +58,7 @@ public class ApplyPatchForBaseRevisionTexts { public boolean process(final CharSequence text) { newText.setLength(0); try { - myStatus = patch.applyModifications(text, newText); + myStatus = ApplyFilePatchBase.applyModifications(patch, text, newText); } catch(ApplyPatchException ex) { return true; // continue to older versions diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/LazyPatchContentRevision.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/LazyPatchContentRevision.java index 4d027bfde718..4c1bbe049c16 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/LazyPatchContentRevision.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/LazyPatchContentRevision.java @@ -17,6 +17,7 @@ package com.intellij.openapi.vcs.changes.patch; import com.intellij.openapi.diff.impl.patch.ApplyPatchException; import com.intellij.openapi.diff.impl.patch.TextFilePatch; +import com.intellij.openapi.diff.impl.patch.apply.ApplyFilePatchBase; import com.intellij.openapi.editor.Document; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.vcs.FilePath; @@ -50,7 +51,7 @@ public class LazyPatchContentRevision implements ContentRevision { } final String baseContent = doc.getText(); final StringBuilder newText = new StringBuilder(); - myPatch.applyModifications(baseContent, newText); + ApplyFilePatchBase.applyModifications(myPatch, baseContent, newText); myContent = newText.toString(); } catch (ApplyPatchException e) { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesAction.java index 850be8768999..cfbb0c06b09d 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesAction.java @@ -19,6 +19,7 @@ import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.diff.DiffRequestFactory; import com.intellij.openapi.diff.MergeRequest; import com.intellij.openapi.diff.impl.patch.*; +import com.intellij.openapi.diff.impl.patch.apply.ApplyTextFilePatch; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.MessageType; @@ -77,7 +78,7 @@ public class DiffShelvedChangesAction extends AnAction implements DumbAware { final Change change = shelvedChange.getChange(project); final String beforePath = shelvedChange.getBeforePath(); try { - final VirtualFile f = FilePatch.findPatchTarget(context, beforePath, shelvedChange.getAfterPath(), beforePath == null); + final VirtualFile f = ApplyTextFilePatch.findPatchTarget(context, beforePath, shelvedChange.getAfterPath(), beforePath == null); if ((f == null) || (! f.exists())) { if (beforePath != null) { missing.add(beforePath); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesManager.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesManager.java index d7d0066f6d3f..d7c2ac6570b3 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesManager.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesManager.java @@ -27,6 +27,7 @@ import com.intellij.openapi.application.PathManager; import com.intellij.openapi.components.ProjectComponent; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diff.impl.patch.*; +import com.intellij.openapi.diff.impl.patch.apply.ApplyFilePatchBase; import com.intellij.openapi.diff.impl.patch.formove.CustomBinaryPatchApplier; import com.intellij.openapi.diff.impl.patch.formove.PatchApplier; import com.intellij.openapi.options.StreamProvider; @@ -269,7 +270,7 @@ public class ShelveChangesManager implements ProjectComponent, JDOMExternalizabl } final BinaryPatchApplier binaryPatchApplier = new BinaryPatchApplier(binaryFilesToUnshelve.size()); - final PatchApplier patchApplier = new PatchApplier(myProject, myProject.getBaseDir(), patches, targetChangeList, binaryPatchApplier); + final PatchApplier patchApplier = new PatchApplier(myProject, myProject.getBaseDir(), patches, targetChangeList, binaryPatchApplier); patchApplier.execute(); remainingPatches.addAll(patchApplier.getRemainingPatches()); @@ -299,7 +300,7 @@ public class ShelveChangesManager implements ProjectComponent, JDOMExternalizabl return textFilePatches; } - private class BinaryPatchApplier implements CustomBinaryPatchApplier { + private class BinaryPatchApplier implements CustomBinaryPatchApplier { private final List myAppliedPatches; private BinaryPatchApplier(final int binaryCount) { @@ -307,9 +308,9 @@ public class ShelveChangesManager implements ProjectComponent, JDOMExternalizabl } @NotNull - public ApplyPatchStatus apply(final List> patches) throws IOException { - for (Pair patch : patches) { - final ShelvedBinaryFilePatch shelvedPatch = (ShelvedBinaryFilePatch) patch.getSecond(); + public ApplyPatchStatus apply(final List>> patches) throws IOException { + for (Pair> patch : patches) { + final ShelvedBinaryFilePatch shelvedPatch = patch.getSecond().getPatch(); unshelveBinaryFile(shelvedPatch.getShelvedBinaryFile(), patch.getFirst()); myAppliedPatches.add(shelvedPatch); } @@ -522,7 +523,7 @@ public class ShelveChangesManager implements ProjectComponent, JDOMExternalizabl return reader.readAllPatches(); } - private static class ShelvedBinaryFilePatch extends FilePatch { + public static class ShelvedBinaryFilePatch extends FilePatch { private final ShelvedBinaryFile myShelvedBinaryFile; public ShelvedBinaryFilePatch(final ShelvedBinaryFile shelvedBinaryFile) { @@ -543,11 +544,6 @@ public class ShelveChangesManager implements ProjectComponent, JDOMExternalizabl return pathNameComponents [pathNameComponents.length-1]; } - protected void applyCreate(final VirtualFile newFile) throws IOException, ApplyPatchException { - } - protected ApplyPatchStatus applyChange(final VirtualFile fileToPatch) throws IOException, ApplyPatchException { - return null; - } public boolean isNewFile() { return myShelvedBinaryFile.BEFORE_PATH == null; } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedChange.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedChange.java index 62210f3339a3..94b705d598ee 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedChange.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedChange.java @@ -26,6 +26,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diff.impl.patch.ApplyPatchException; import com.intellij.openapi.diff.impl.patch.PatchSyntaxException; import com.intellij.openapi.diff.impl.patch.TextFilePatch; +import com.intellij.openapi.diff.impl.patch.apply.ApplyFilePatchBase; import com.intellij.openapi.editor.Document; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.project.Project; @@ -203,7 +204,7 @@ public class ShelvedChange { return null; } StringBuilder newText = new StringBuilder(); - patch.applyModifications(getBaseContent(), newText); + ApplyFilePatchBase.applyModifications(patch, getBaseContent(), newText); return newText.toString(); } From f86bc8b98c78ad6355e94e45b5dcd668da240076 Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Mon, 15 Mar 2010 18:51:30 +0300 Subject: [PATCH 71/93] refactoring --- .../src/META-INF/XmlPlugin.xml | 2 +- .../template/XmlZenCodingTemplate.java | 801 ------------------ .../template/zencoding/MarkerToken.java | 22 + .../template/zencoding/NumberToken.java | 27 + .../template/zencoding/OperationToken.java | 27 + .../codeInsight/template/zencoding/State.java | 23 + .../template/zencoding/TemplateToken.java | 35 + .../codeInsight/template/zencoding/Token.java | 22 + .../zencoding/XmlZenCodingInterpreter.java | 439 ++++++++++ .../zencoding/XmlZenCodingTemplate.java | 349 ++++++++ 10 files changed, 945 insertions(+), 802 deletions(-) delete mode 100644 xml/impl/src/com/intellij/codeInsight/template/XmlZenCodingTemplate.java create mode 100644 xml/impl/src/com/intellij/codeInsight/template/zencoding/MarkerToken.java create mode 100644 xml/impl/src/com/intellij/codeInsight/template/zencoding/NumberToken.java create mode 100644 xml/impl/src/com/intellij/codeInsight/template/zencoding/OperationToken.java create mode 100644 xml/impl/src/com/intellij/codeInsight/template/zencoding/State.java create mode 100644 xml/impl/src/com/intellij/codeInsight/template/zencoding/TemplateToken.java create mode 100644 xml/impl/src/com/intellij/codeInsight/template/zencoding/Token.java create mode 100644 xml/impl/src/com/intellij/codeInsight/template/zencoding/XmlZenCodingInterpreter.java create mode 100644 xml/impl/src/com/intellij/codeInsight/template/zencoding/XmlZenCodingTemplate.java diff --git a/platform/platform-resources/src/META-INF/XmlPlugin.xml b/platform/platform-resources/src/META-INF/XmlPlugin.xml index 98b39a2de6cf..0b04b7fe2ad7 100644 --- a/platform/platform-resources/src/META-INF/XmlPlugin.xml +++ b/platform/platform-resources/src/META-INF/XmlPlugin.xml @@ -289,7 +289,7 @@ implementationClass="com.intellij.codeInsight.hint.XmlImplementationTextSelectioner"/> - + diff --git a/xml/impl/src/com/intellij/codeInsight/template/XmlZenCodingTemplate.java b/xml/impl/src/com/intellij/codeInsight/template/XmlZenCodingTemplate.java deleted file mode 100644 index 5e168aa6adb3..000000000000 --- a/xml/impl/src/com/intellij/codeInsight/template/XmlZenCodingTemplate.java +++ /dev/null @@ -1,801 +0,0 @@ -/* - * Copyright 2000-2010 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.codeInsight.template; - -import com.intellij.application.options.editor.WebEditorOptions; -import com.intellij.codeInsight.template.impl.TemplateImpl; -import com.intellij.lang.xml.XMLLanguage; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.TextRange; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiFileFactory; -import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.psi.xml.*; -import com.intellij.util.containers.HashMap; -import com.intellij.util.containers.HashSet; -import com.intellij.util.containers.IntArrayList; -import org.apache.xerces.util.XML11Char; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.*; - -/** - * @author Eugene.Kudelevsky - */ -public class XmlZenCodingTemplate implements CustomLiveTemplate { - private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.template.XmlZenCodingTemplate"); - - private static final String ATTRS = "ATTRS"; - - private static final String OPERATIONS = ">+*"; - private static final String SELECTORS = ".#["; - private static final char MARKER = '$'; - private static final String ID = "id"; - private static final String CLASS = "class"; - private static final String NUMBER_IN_ITERATION_PLACE_HOLDER = "$"; - - private static enum MyState { - OPERATION, WORD, AFTER_NUMBER, NUMBER - } - - private static class MyToken { - } - - private static class MyMarkerToken extends MyToken { - } - - private static class MyTemplateToken extends MyToken { - final String myKey; - final List> myAttribute2Value; - TemplateImpl myTemplate; - - MyTemplateToken(String key, List> attribute2value) { - myKey = key; - myAttribute2Value = attribute2value; - } - } - - private static class MyNumberToken extends MyToken { - final int myNumber; - - MyNumberToken(int number) { - myNumber = number; - } - } - - private static class MyOperationToken extends MyToken { - final char mySign; - - MyOperationToken(char sign) { - mySign = sign; - } - } - - private static int parseNonNegativeInt(@NotNull String s) { - try { - return Integer.parseInt(s); - } - catch (Throwable ignored) { - } - return -1; - } - - private static String getPrefix(@NotNull String templateKey) { - for (int i = 0, n = templateKey.length(); i < n; i++) { - char c = templateKey.charAt(i); - if (SELECTORS.indexOf(c) >= 0) { - return templateKey.substring(0, i); - } - } - return templateKey; - } - - @Nullable - private static Pair parseAttrNameAndValue(@NotNull String text) { - int eqIndex = text.indexOf('='); - if (eqIndex > 0) { - return new Pair(text.substring(0, eqIndex), text.substring(eqIndex + 1)); - } - return null; - } - - @Nullable - private static MyTemplateToken parseSelectors(@NotNull String text) { - String templateKey = null; - List> attributes = new ArrayList>(); - Set definedAttrs = new HashSet(); - final List classes = new ArrayList(); - StringBuilder builder = new StringBuilder(); - char lastDelim = 0; - text += MARKER; - int classAttrPosition = -1; - for (int i = 0, n = text.length(); i < n; i++) { - char c = text.charAt(i); - if (c == '#' || c == '.' || c == '[' || c == ']' || i == n - 1) { - if (c != ']') { - switch (lastDelim) { - case 0: - templateKey = builder.toString(); - break; - case '#': - if (!definedAttrs.add(ID)) { - return null; - } - attributes.add(new Pair(ID, builder.toString())); - break; - case '.': - if (builder.length() <= 0) { - return null; - } - if (classAttrPosition < 0) { - classAttrPosition = attributes.size(); - } - classes.add(builder.toString()); - break; - case ']': - if (builder.length() > 0) { - return null; - } - break; - default: - return null; - } - } - else if (lastDelim != '[') { - return null; - } - else { - Pair pair = parseAttrNameAndValue(builder.toString()); - if (pair == null || !definedAttrs.add(pair.first)) { - return null; - } - attributes.add(pair); - } - lastDelim = c; - builder = new StringBuilder(); - } - else { - builder.append(c); - } - } - if (classes.size() > 0) { - if (definedAttrs.contains(CLASS)) { - return null; - } - StringBuilder classesAttrValue = new StringBuilder(); - for (int i = 0; i < classes.size(); i++) { - classesAttrValue.append(classes.get(i)); - if (i < classes.size() - 1) { - classesAttrValue.append(' '); - } - } - assert classAttrPosition >= 0; - attributes.add(classAttrPosition, new Pair(CLASS, classesAttrValue.toString())); - } - return new MyTemplateToken(templateKey, attributes); - } - - private static boolean isXML11ValidQName(String str) { - final int colon = str.indexOf(':'); - if (colon == 0 || colon == str.length() - 1) { - return false; - } - if (colon > 0) { - final String prefix = str.substring(0, colon); - final String localPart = str.substring(colon + 1); - return XML11Char.isXML11ValidNCName(prefix) && XML11Char.isXML11ValidNCName(localPart); - } - return XML11Char.isXML11ValidNCName(str); - } - - private static boolean containsAttrsVar(TemplateImpl template) { - for (int i = 0; i < template.getVariableCount(); i++) { - String varName = template.getVariableNameAt(i); - if (ATTRS.equals(varName)) { - return true; - } - } - return false; - } - - private static void removeVariablesWhichHasNoSegment(TemplateImpl template) { - Set segments = new HashSet(); - for (int i = 0; i < template.getSegmentsCount(); i++) { - segments.add(template.getSegmentName(i)); - } - IntArrayList varsToRemove = new IntArrayList(); - for (int i = 0; i < template.getVariableCount(); i++) { - String varName = template.getVariableNameAt(i); - if (!segments.contains(varName)) { - varsToRemove.add(i); - } - } - for (int i = 0; i < varsToRemove.size(); i++) { - template.removeVariable(varsToRemove.get(i)); - } - } - - @Nullable - private static XmlTag parseXmlTagInTemplate(String templateString, Project project) { - XmlFile xmlFile = (XmlFile)PsiFileFactory.getInstance(project).createFileFromText("dummy.xml", templateString); - XmlDocument document = xmlFile.getDocument(); - return document == null ? null : document.getRootTag(); - } - - private static boolean generateTemplateAndAddToToken(MyTemplateToken token, CustomTemplateCallback callback) { - TemplateImpl template = callback.findApplicableTemplate(token.myKey); - assert template != null; - XmlTag tag = parseXmlTagInTemplate(template.getString(), callback.getProject()); - if (tag == null) { - return false; - } - token.myTemplate = template; - return true; - } - - @Nullable - private static List parse(@NotNull String text, @NotNull CustomTemplateCallback callback) { - text += MARKER; - StringBuilder templateKeyBuilder = new StringBuilder(); - List result = new ArrayList(); - for (int i = 0, n = text.length(); i < n; i++) { - char c = text.charAt(i); - if (i == n - 1 || (i < n - 2 && OPERATIONS.indexOf(c) >= 0)) { - String key = templateKeyBuilder.toString(); - templateKeyBuilder = new StringBuilder(); - int num = parseNonNegativeInt(key); - if (num > 0) { - result.add(new MyNumberToken(num)); - } - else { - if (key.length() == 0) { - return null; - } - String prefix = getPrefix(key); - boolean applicable = callback.isLiveTemplateApplicable(prefix); - if (!applicable && !isXML11ValidQName(prefix)) { - return null; - } - MyTemplateToken token = parseSelectors(key); - if (token == null) { - return null; - } - if (applicable && token.myAttribute2Value.size() > 0) { - assert prefix.equals(token.myKey); - if (!generateTemplateAndAddToToken(token, callback)) { - return null; - } - } - result.add(token); - } - result.add(i < n - 1 ? new MyOperationToken(c) : new MyMarkerToken()); - } - else if (!Character.isWhitespace(c)) { - templateKeyBuilder.append(c); - } - else { - return null; - } - } - return result; - } - - private static boolean check(@NotNull Collection tokens) { - MyState state = MyState.WORD; - for (MyToken token : tokens) { - if (token instanceof MyMarkerToken) { - break; - } - switch (state) { - case OPERATION: - if (token instanceof MyOperationToken) { - state = ((MyOperationToken)token).mySign == '*' ? MyState.NUMBER : MyState.WORD; - } - else { - return false; - } - break; - case WORD: - if (token instanceof MyTemplateToken) { - state = MyState.OPERATION; - } - else { - return false; - } - break; - case NUMBER: - if (token instanceof MyNumberToken) { - state = MyState.AFTER_NUMBER; - } - else { - return false; - } - break; - case AFTER_NUMBER: - if (token instanceof MyOperationToken && ((MyOperationToken)token).mySign != '*') { - state = MyState.WORD; - } - else { - return false; - } - break; - } - } - return state == MyState.OPERATION || state == MyState.AFTER_NUMBER; - } - - private static String computeKey(Editor editor, int startOffset) { - int offset = editor.getCaretModel().getOffset(); - String s = editor.getDocument().getCharsSequence().subSequence(startOffset, offset).toString(); - int index = 0; - while (index < s.length() && Character.isWhitespace(s.charAt(index))) { - index++; - } - String key = s.substring(index); - int lastWhitespaceIndex = -1; - for (int i = 0; i < key.length(); i++) { - if (Character.isWhitespace(key.charAt(i))) { - lastWhitespaceIndex = i; - } - } - if (lastWhitespaceIndex >= 0 && lastWhitespaceIndex < key.length() - 1) { - return key.substring(lastWhitespaceIndex + 1); - } - return key; - } - - public String computeTemplateKey(@NotNull CustomTemplateCallback callback) { - Editor editor = callback.getEditor(); - int offset = callback.getOffset(); - PsiElement element = callback.getFile().findElementAt(offset > 0 ? offset - 1 : offset); - int line = editor.getCaretModel().getLogicalPosition().line; - int lineStart = editor.getDocument().getLineStartOffset(line); - int parentStart; - do { - parentStart = element != null ? element.getTextRange().getStartOffset() : 0; - int startOffset = parentStart > lineStart ? parentStart : lineStart; - String key = computeKey(editor, startOffset); - List tokens = parse(key, callback); - if (tokens != null && check(tokens)) { - if (tokens.size() == 2) { - MyToken token = tokens.get(0); - if (token instanceof MyTemplateToken) { - if (key.equals(((MyTemplateToken)token).myKey) && callback.isLiveTemplateApplicable(key)) { - // do not activate only live template - return null; - } - } - } - return key; - } - if (element != null) { - element = element.getParent(); - } - } - while (element != null && parentStart > lineStart); - return null; - } - - public boolean isApplicable(PsiFile file, int offset, boolean selection) { - WebEditorOptions webEditorOptions = WebEditorOptions.getInstance(); - if (!webEditorOptions.isZenCodingEnabled()) { - return false; - } - if (file.getLanguage() instanceof XMLLanguage) { - PsiElement element = file.findElementAt(offset > 0 ? offset - 1 : offset); - if (element == null || element.getLanguage() instanceof XMLLanguage) { - return true; - } - } - return false; - } - - public void expand(String key, @NotNull CustomTemplateCallback callback, @Nullable TemplateInvokationListener listener) { - List tokens = parse(key, callback); - assert tokens != null; - MyInterpreter interpreter = new MyInterpreter(tokens, callback, MyState.WORD, listener); - interpreter.invoke(0); - } - - public void wrap(String selection, @NotNull CustomTemplateCallback callback, @Nullable TemplateInvokationListener listener) { - } - - private static void fail() { - LOG.error("Input string was checked incorrectly during isApplicable() invokation"); - } - - @Nullable - private static Map buildPredefinedValues(List> attribute2value, int numberInIteration) { - StringBuilder result = new StringBuilder(); - for (Iterator> it = attribute2value.iterator(); it.hasNext();) { - Pair pair = it.next(); - String name = pair.first; - String value = getValue(pair, numberInIteration); - result.append(name).append("=\"").append(value).append('"'); - if (it.hasNext()) { - result.append(' '); - } - } - String attributes = result.toString(); - attributes = attributes.length() > 0 ? ' ' + attributes : null; - Map predefinedValues = null; - if (attributes != null) { - predefinedValues = new HashMap(); - predefinedValues.put(ATTRS, attributes); - } - return predefinedValues; - } - - private static String getValue(Pair pair, int numberInIteration) { - return pair.second.replace(NUMBER_IN_ITERATION_PLACE_HOLDER, Integer.toString(numberInIteration + 1)); - } - - @Nullable - private static String addAttrsVar(TemplateImpl modifiedTemplate, XmlTag tag) { - String text = tag.getContainingFile().getText(); - PsiElement[] children = tag.getChildren(); - if (children.length >= 1 && - children[0] instanceof XmlToken && - ((XmlToken)children[0]).getTokenType() == XmlTokenType.XML_START_TAG_START) { - PsiElement beforeAttrs = children[0]; - if (children.length >= 2 && children[1] instanceof XmlToken && ((XmlToken)children[1]).getTokenType() == XmlTokenType.XML_NAME) { - beforeAttrs = children[1]; - } - TextRange range = beforeAttrs.getTextRange(); - if (range == null) { - return null; - } - int offset = range.getEndOffset(); - text = text.substring(0, offset) + " $ATTRS$" + text.substring(offset); - modifiedTemplate.addVariable(ATTRS, "", "", false); - return text; - } - return null; - } - - private static boolean invokeTemplate(MyTemplateToken token, - final CustomTemplateCallback callback, - final TemplateInvokationListener listener, - int numberInIteration) { - List> attr2value = new ArrayList>(token.myAttribute2Value); - if (callback.isLiveTemplateApplicable(token.myKey)) { - if (token.myTemplate != null) { - TemplateImpl modifiedTemplate = token.myTemplate.copy(); - XmlTag tag = parseXmlTagInTemplate(token.myTemplate.getString(), callback.getProject()); - assert tag != null; - for (Iterator> iterator = attr2value.iterator(); iterator.hasNext();) { - Pair pair = iterator.next(); - if (tag.getAttribute(pair.first) != null) { - tag.setAttribute(pair.first, getValue(pair, numberInIteration)); - iterator.remove(); - } - } - String text = null; - if (!containsAttrsVar(modifiedTemplate) && attr2value.size() > 0) { - String textWithAttrs = addAttrsVar(modifiedTemplate, tag); - if (textWithAttrs != null) { - text = textWithAttrs; - } - else { - for (Iterator> iterator = attr2value.iterator(); iterator.hasNext();) { - Pair pair = iterator.next(); - tag.setAttribute(pair.first, getValue(pair, numberInIteration)); - iterator.remove(); - } - } - } - if (text == null) { - text = tag.getContainingFile().getText(); - } - modifiedTemplate.setString(text); - removeVariablesWhichHasNoSegment(modifiedTemplate); - Map predefinedValues = buildPredefinedValues(attr2value, numberInIteration); - return callback.startTemplate(modifiedTemplate, predefinedValues, listener); - } - else { - Map predefinedValues = buildPredefinedValues(attr2value, numberInIteration); - return callback.startTemplate(token.myKey, predefinedValues, listener); - } - } - else { - TemplateImpl template = new TemplateImpl("", ""); - template.addTextSegment('<' + token.myKey); - if (attr2value.size() > 0) { - template.addVariable(ATTRS, "", "", false); - template.addVariableSegment(ATTRS); - } - template.addTextSegment(">"); - template.addVariableSegment(TemplateImpl.END); - template.addTextSegment(""); - template.setToReformat(true); - Map predefinedValues = buildPredefinedValues(attr2value, numberInIteration); - return callback.startTemplate(template, predefinedValues, listener); - } - } - - /*private static boolean hasClosingTag(CharSequence text, CharSequence tagName, int offset, int rightBound) { - if (offset + 1 < text.length() && text.charAt(offset) == '<' && text.charAt(offset + 1) == '/') { - CharSequence closingTagName = parseTagName(text, offset + 2, rightBound); - if (tagName.equals(closingTagName)) { - return true; - } - } - return false; - }*/ - - private class MyInterpreter { - private final List myTokens; - private final CustomTemplateCallback myCallback; - private final TemplateInvokationListener myListener; - private MyState myState; - - private MyInterpreter(List tokens, - CustomTemplateCallback callback, - MyState initialState, - TemplateInvokationListener listener) { - myTokens = tokens; - myCallback = callback; - myListener = listener; - myState = initialState; - } - - private void finish(boolean inSeparateEvent) { - myCallback.gotoEndOffset(); - if (myListener != null) { - myListener.finished(inSeparateEvent); - } - } - - private void gotoChild(Object templateBoundsKey) { - int startOfTemplate = myCallback.getStartOfTemplate(templateBoundsKey); - int endOfTemplate = myCallback.getEndOfTemplate(templateBoundsKey); - Editor editor = myCallback.getEditor(); - int offset = myCallback.getOffset(); - - PsiFile file = myCallback.getFile(); - - PsiElement element = file.findElementAt(offset); - if (element instanceof XmlToken && ((XmlToken)element).getTokenType() == XmlTokenType.XML_END_TAG_START) { - return; - } - - int newOffset = -1; - XmlTag tag = PsiTreeUtil.findElementOfClassAtRange(file, startOfTemplate, endOfTemplate, XmlTag.class); - if (tag != null) { - for (PsiElement child : tag.getChildren()) { - if (child instanceof XmlToken && ((XmlToken)child).getTokenType() == XmlTokenType.XML_END_TAG_START) { - newOffset = child.getTextOffset(); - } - } - } - - if (newOffset >= 0) { - myCallback.fixEndOffset(); - editor.getCaretModel().moveToOffset(newOffset); - } - - /*CharSequence tagName = getPrecedingTagName(text, offset, startOfTemplate); - if (tagName != null) { - *//*if (!hasClosingTag(text, tagName, offset, endOfTemplate)) { - document.insertString(offset, "'); - }*//* - } - else if (offset != endOfTemplate) { - tagName = getPrecedingTagName(text, endOfTemplate, startOfTemplate); - if (tagName != null) { - *//*fixEndOffset(); - document.insertString(endOfTemplate, "');*//* - editor.getCaretModel().moveToOffset(endOfTemplate); - } - }*/ - } - - public boolean invoke(int startIndex) { - final int n = myTokens.size(); - MyTemplateToken templateToken = null; - int number = -1; - for (int i = startIndex; i < n; i++) { - final int finalI = i; - MyToken token = myTokens.get(i); - switch (myState) { - case OPERATION: - if (templateToken != null) { - if (token instanceof MyMarkerToken || token instanceof MyOperationToken) { - final char sign = token instanceof MyOperationToken ? ((MyOperationToken)token).mySign : MARKER; - if (sign == MARKER || sign == '+') { - final Object key = new Object(); - myCallback.fixStartOfTemplate(key); - TemplateInvokationListener listener = new TemplateInvokationListener() { - public void finished(boolean inSeparateEvent) { - myState = MyState.WORD; - if (myCallback.getOffset() != myCallback.getEndOfTemplate(key)) { - myCallback.fixEndOffset(); - } - if (sign == '+') { - myCallback.gotoEndOfTemplate(key); - } - if (inSeparateEvent) { - invoke(finalI + 1); - } - } - }; - if (!invokeTemplate(templateToken, myCallback, listener, 0)) { - return false; - } - templateToken = null; - } - else if (sign == '>') { - if (!startTemplateAndGotoChild(templateToken, finalI)) { - return false; - } - templateToken = null; - } - else if (sign == '*') { - myState = MyState.NUMBER; - } - } - else { - fail(); - } - } - break; - case WORD: - if (token instanceof MyTemplateToken) { - templateToken = ((MyTemplateToken)token); - myState = MyState.OPERATION; - } - else { - fail(); - } - break; - case NUMBER: - if (token instanceof MyNumberToken) { - number = ((MyNumberToken)token).myNumber; - myState = MyState.AFTER_NUMBER; - } - else { - fail(); - } - break; - case AFTER_NUMBER: - if (token instanceof MyMarkerToken || token instanceof MyOperationToken) { - char sign = token instanceof MyOperationToken ? ((MyOperationToken)token).mySign : MARKER; - if (sign == MARKER || sign == '+') { - if (!invokeTemplateSeveralTimes(templateToken, 0, number, finalI)) { - return false; - } - templateToken = null; - } - else if (number > 1) { - return invokeTemplateAndProcessTail(templateToken, 0, number, i + 1); - } - else { - assert number == 1; - if (!startTemplateAndGotoChild(templateToken, finalI)) { - return false; - } - templateToken = null; - } - myState = MyState.WORD; - } - else { - fail(); - } - break; - } - } - finish(startIndex == n); - return true; - } - - private boolean startTemplateAndGotoChild(MyTemplateToken templateToken, final int index) { - final Object key = new Object(); - myCallback.fixStartOfTemplate(key); - TemplateInvokationListener listener = new TemplateInvokationListener() { - public void finished(boolean inSeparateEvent) { - myState = MyState.WORD; - gotoChild(key); - if (inSeparateEvent) { - invoke(index + 1); - } - } - }; - if (!invokeTemplate(templateToken, myCallback, listener, 0)) { - return false; - } - return true; - } - - private boolean invokeTemplateSeveralTimes(final MyTemplateToken templateToken, - final int startIndex, - final int count, - final int globalIndex) { - final Object key = new Object(); - myCallback.fixStartOfTemplate(key); - for (int i = startIndex; i < count; i++) { - final int finalI = i; - TemplateInvokationListener listener = new TemplateInvokationListener() { - public void finished(boolean inSeparateEvent) { - myState = MyState.WORD; - if (myCallback.getOffset() != myCallback.getEndOfTemplate(key)) { - myCallback.fixEndOffset(); - } - myCallback.gotoEndOfTemplate(key); - if (inSeparateEvent) { - if (finalI + 1 < count) { - invokeTemplateSeveralTimes(templateToken, finalI + 1, count, globalIndex); - } - else { - invoke(globalIndex + 1); - } - } - } - }; - if (!invokeTemplate(templateToken, myCallback, listener, i)) { - return false; - } - } - return true; - } - - private boolean invokeTemplateAndProcessTail(final MyTemplateToken templateToken, - final int startIndex, - final int count, - final int tailStart) { - final Object key = new Object(); - myCallback.fixStartOfTemplate(key); - for (int i = startIndex; i < count; i++) { - final int finalI = i; - final boolean[] flag = new boolean[]{false}; - TemplateInvokationListener listener = new TemplateInvokationListener() { - public void finished(boolean inSeparateEvent) { - gotoChild(key); - MyInterpreter interpreter = new MyInterpreter(myTokens, myCallback, MyState.WORD, new TemplateInvokationListener() { - public void finished(boolean inSeparateEvent) { - if (myCallback.getOffset() != myCallback.getEndOfTemplate(key)) { - myCallback.fixEndOffset(); - } - myCallback.gotoEndOfTemplate(key); - if (inSeparateEvent) { - invokeTemplateAndProcessTail(templateToken, finalI + 1, count, tailStart); - } - } - }); - if (interpreter.invoke(tailStart)) { - if (inSeparateEvent) { - invokeTemplateAndProcessTail(templateToken, finalI + 1, count, tailStart); - } - } - else { - flag[0] = true; - } - } - }; - if (!invokeTemplate(templateToken, myCallback, listener, i) || flag[0]) { - return false; - } - } - finish(count == 0); - return true; - } - } - -} \ No newline at end of file diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/MarkerToken.java b/xml/impl/src/com/intellij/codeInsight/template/zencoding/MarkerToken.java new file mode 100644 index 000000000000..ba9c9af1b9c8 --- /dev/null +++ b/xml/impl/src/com/intellij/codeInsight/template/zencoding/MarkerToken.java @@ -0,0 +1,22 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.template.zencoding; + +/** + * @author Eugene.Kudelevsky + */ +class MarkerToken extends Token { +} diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/NumberToken.java b/xml/impl/src/com/intellij/codeInsight/template/zencoding/NumberToken.java new file mode 100644 index 000000000000..4adf4f763c59 --- /dev/null +++ b/xml/impl/src/com/intellij/codeInsight/template/zencoding/NumberToken.java @@ -0,0 +1,27 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.template.zencoding; + +/** + * @author Eugene.Kudelevsky + */ +class NumberToken extends Token { + final int myNumber; + + NumberToken(int number) { + myNumber = number; + } +} diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/OperationToken.java b/xml/impl/src/com/intellij/codeInsight/template/zencoding/OperationToken.java new file mode 100644 index 000000000000..63bb2d343e6c --- /dev/null +++ b/xml/impl/src/com/intellij/codeInsight/template/zencoding/OperationToken.java @@ -0,0 +1,27 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.template.zencoding; + +/** + * @author Eugene.Kudelevsky + */ +class OperationToken extends Token { + final char mySign; + + OperationToken(char sign) { + mySign = sign; + } +} diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/State.java b/xml/impl/src/com/intellij/codeInsight/template/zencoding/State.java new file mode 100644 index 000000000000..23ff80b9cc43 --- /dev/null +++ b/xml/impl/src/com/intellij/codeInsight/template/zencoding/State.java @@ -0,0 +1,23 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.template.zencoding; + +/** + * @author Eugene.Kudelevsky + */ +enum State { + OPERATION, WORD, AFTER_NUMBER, NUMBER +} diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/TemplateToken.java b/xml/impl/src/com/intellij/codeInsight/template/zencoding/TemplateToken.java new file mode 100644 index 000000000000..bb9b2f96b0f6 --- /dev/null +++ b/xml/impl/src/com/intellij/codeInsight/template/zencoding/TemplateToken.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.template.zencoding; + +import com.intellij.codeInsight.template.impl.TemplateImpl; +import com.intellij.openapi.util.Pair; + +import java.util.List; + +/** + * @author Eugene.Kudelevsky + */ +class TemplateToken extends Token { + final String myKey; + final List> myAttribute2Value; + TemplateImpl myTemplate; + + TemplateToken(String key, List> attribute2value) { + myKey = key; + myAttribute2Value = attribute2value; + } +} diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/Token.java b/xml/impl/src/com/intellij/codeInsight/template/zencoding/Token.java new file mode 100644 index 000000000000..082b83bf2f16 --- /dev/null +++ b/xml/impl/src/com/intellij/codeInsight/template/zencoding/Token.java @@ -0,0 +1,22 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.template.zencoding; + +/** + * @author Eugene.Kudelevsky + */ +class Token { +} diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/XmlZenCodingInterpreter.java b/xml/impl/src/com/intellij/codeInsight/template/zencoding/XmlZenCodingInterpreter.java new file mode 100644 index 000000000000..eab488d1498d --- /dev/null +++ b/xml/impl/src/com/intellij/codeInsight/template/zencoding/XmlZenCodingInterpreter.java @@ -0,0 +1,439 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.template.zencoding; + +import com.intellij.codeInsight.template.CustomTemplateCallback; +import com.intellij.codeInsight.template.TemplateInvokationListener; +import com.intellij.codeInsight.template.impl.TemplateImpl; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.xml.*; +import com.intellij.util.containers.HashMap; +import com.intellij.util.containers.HashSet; +import com.intellij.util.containers.IntArrayList; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +/** + * @author Eugene.Kudelevsky + */ +class XmlZenCodingInterpreter { + private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.template.zencoding.XmlZenCodingInterpreter"); + private static final String ATTRS = "ATTRS"; + + private final List myTokens; + private final CustomTemplateCallback myCallback; + private final TemplateInvokationListener myListener; + private static final String NUMBER_IN_ITERATION_PLACE_HOLDER = "$"; + private State myState; + + XmlZenCodingInterpreter(List tokens, + CustomTemplateCallback callback, + State initialState, + TemplateInvokationListener listener) { + myTokens = tokens; + myCallback = callback; + myListener = listener; + myState = initialState; + } + + private void finish(boolean inSeparateEvent) { + myCallback.gotoEndOffset(); + if (myListener != null) { + myListener.finished(inSeparateEvent); + } + } + + private void gotoChild(Object templateBoundsKey) { + int startOfTemplate = myCallback.getStartOfTemplate(templateBoundsKey); + int endOfTemplate = myCallback.getEndOfTemplate(templateBoundsKey); + Editor editor = myCallback.getEditor(); + int offset = myCallback.getOffset(); + + PsiFile file = myCallback.getFile(); + + PsiElement element = file.findElementAt(offset); + if (element instanceof XmlToken && ((XmlToken)element).getTokenType() == XmlTokenType.XML_END_TAG_START) { + return; + } + + int newOffset = -1; + XmlTag tag = PsiTreeUtil.findElementOfClassAtRange(file, startOfTemplate, endOfTemplate, XmlTag.class); + if (tag != null) { + for (PsiElement child : tag.getChildren()) { + if (child instanceof XmlToken && ((XmlToken)child).getTokenType() == XmlTokenType.XML_END_TAG_START) { + newOffset = child.getTextOffset(); + } + } + } + + if (newOffset >= 0) { + myCallback.fixEndOffset(); + editor.getCaretModel().moveToOffset(newOffset); + } + + /*CharSequence tagName = getPrecedingTagName(text, offset, startOfTemplate); + if (tagName != null) { + *//*if (!hasClosingTag(text, tagName, offset, endOfTemplate)) { + document.insertString(offset, "'); + }*//* + } + else if (offset != endOfTemplate) { + tagName = getPrecedingTagName(text, endOfTemplate, startOfTemplate); + if (tagName != null) { + *//*fixEndOffset(); + document.insertString(endOfTemplate, "');*//* + editor.getCaretModel().moveToOffset(endOfTemplate); + } + }*/ + } + + public boolean invoke(int startIndex) { + final int n = myTokens.size(); + TemplateToken templateToken = null; + int number = -1; + for (int i = startIndex; i < n; i++) { + final int finalI = i; + Token token = myTokens.get(i); + switch (myState) { + case OPERATION: + if (templateToken != null) { + if (token instanceof MarkerToken || token instanceof OperationToken) { + final char sign = token instanceof OperationToken ? ((OperationToken)token).mySign : XmlZenCodingTemplate.MARKER; + if (sign == XmlZenCodingTemplate.MARKER || sign == '+') { + final Object key = new Object(); + myCallback.fixStartOfTemplate(key); + TemplateInvokationListener listener = new TemplateInvokationListener() { + public void finished(boolean inSeparateEvent) { + myState = State.WORD; + if (myCallback.getOffset() != myCallback.getEndOfTemplate(key)) { + myCallback.fixEndOffset(); + } + if (sign == '+') { + myCallback.gotoEndOfTemplate(key); + } + if (inSeparateEvent) { + invoke(finalI + 1); + } + } + }; + if (!invokeTemplate(templateToken, myCallback, listener, 0)) { + return false; + } + templateToken = null; + } + else if (sign == '>') { + if (!startTemplateAndGotoChild(templateToken, finalI)) { + return false; + } + templateToken = null; + } + else if (sign == '*') { + myState = State.NUMBER; + } + } + else { + fail(); + } + } + break; + case WORD: + if (token instanceof TemplateToken) { + templateToken = ((TemplateToken)token); + myState = State.OPERATION; + } + else { + fail(); + } + break; + case NUMBER: + if (token instanceof NumberToken) { + number = ((NumberToken)token).myNumber; + myState = State.AFTER_NUMBER; + } + else { + fail(); + } + break; + case AFTER_NUMBER: + if (token instanceof MarkerToken || token instanceof OperationToken) { + char sign = token instanceof OperationToken ? ((OperationToken)token).mySign : XmlZenCodingTemplate.MARKER; + if (sign == XmlZenCodingTemplate.MARKER || sign == '+') { + if (!invokeTemplateSeveralTimes(templateToken, 0, number, finalI)) { + return false; + } + templateToken = null; + } + else if (number > 1) { + return invokeTemplateAndProcessTail(templateToken, 0, number, i + 1); + } + else { + assert number == 1; + if (!startTemplateAndGotoChild(templateToken, finalI)) { + return false; + } + templateToken = null; + } + myState = State.WORD; + } + else { + fail(); + } + break; + } + } + finish(startIndex == n); + return true; + } + + private boolean startTemplateAndGotoChild(TemplateToken templateToken, final int index) { + final Object key = new Object(); + myCallback.fixStartOfTemplate(key); + TemplateInvokationListener listener = new TemplateInvokationListener() { + public void finished(boolean inSeparateEvent) { + myState = State.WORD; + gotoChild(key); + if (inSeparateEvent) { + invoke(index + 1); + } + } + }; + if (!invokeTemplate(templateToken, myCallback, listener, 0)) { + return false; + } + return true; + } + + private boolean invokeTemplateSeveralTimes(final TemplateToken templateToken, + final int startIndex, + final int count, + final int globalIndex) { + final Object key = new Object(); + myCallback.fixStartOfTemplate(key); + for (int i = startIndex; i < count; i++) { + final int finalI = i; + TemplateInvokationListener listener = new TemplateInvokationListener() { + public void finished(boolean inSeparateEvent) { + myState = State.WORD; + if (myCallback.getOffset() != myCallback.getEndOfTemplate(key)) { + myCallback.fixEndOffset(); + } + myCallback.gotoEndOfTemplate(key); + if (inSeparateEvent) { + if (finalI + 1 < count) { + invokeTemplateSeveralTimes(templateToken, finalI + 1, count, globalIndex); + } + else { + invoke(globalIndex + 1); + } + } + } + }; + if (!invokeTemplate(templateToken, myCallback, listener, i)) { + return false; + } + } + return true; + } + + private boolean invokeTemplateAndProcessTail(final TemplateToken templateToken, + final int startIndex, + final int count, + final int tailStart) { + final Object key = new Object(); + myCallback.fixStartOfTemplate(key); + for (int i = startIndex; i < count; i++) { + final int finalI = i; + final boolean[] flag = new boolean[]{false}; + TemplateInvokationListener listener = new TemplateInvokationListener() { + public void finished(boolean inSeparateEvent) { + gotoChild(key); + XmlZenCodingInterpreter interpreter = + new XmlZenCodingInterpreter(myTokens, myCallback, State.WORD, new TemplateInvokationListener() { + public void finished(boolean inSeparateEvent) { + if (myCallback.getOffset() != myCallback.getEndOfTemplate(key)) { + myCallback.fixEndOffset(); + } + myCallback.gotoEndOfTemplate(key); + if (inSeparateEvent) { + invokeTemplateAndProcessTail(templateToken, finalI + 1, count, tailStart); + } + } + }); + if (interpreter.invoke(tailStart)) { + if (inSeparateEvent) { + invokeTemplateAndProcessTail(templateToken, finalI + 1, count, tailStart); + } + } + else { + flag[0] = true; + } + } + }; + if (!invokeTemplate(templateToken, myCallback, listener, i) || flag[0]) { + return false; + } + } + finish(count == 0); + return true; + } + + private static boolean containsAttrsVar(TemplateImpl template) { + for (int i = 0; i < template.getVariableCount(); i++) { + String varName = template.getVariableNameAt(i); + if (ATTRS.equals(varName)) { + return true; + } + } + return false; + } + + private static void removeVariablesWhichHasNoSegment(TemplateImpl template) { + Set segments = new HashSet(); + for (int i = 0; i < template.getSegmentsCount(); i++) { + segments.add(template.getSegmentName(i)); + } + IntArrayList varsToRemove = new IntArrayList(); + for (int i = 0; i < template.getVariableCount(); i++) { + String varName = template.getVariableNameAt(i); + if (!segments.contains(varName)) { + varsToRemove.add(i); + } + } + for (int i = 0; i < varsToRemove.size(); i++) { + template.removeVariable(varsToRemove.get(i)); + } + } + + @Nullable + private static Map buildPredefinedValues(List> attribute2value, int numberInIteration) { + StringBuilder result = new StringBuilder(); + for (Iterator> it = attribute2value.iterator(); it.hasNext();) { + Pair pair = it.next(); + String name = pair.first; + String value = getValue(pair, numberInIteration); + result.append(name).append("=\"").append(value).append('"'); + if (it.hasNext()) { + result.append(' '); + } + } + String attributes = result.toString(); + attributes = attributes.length() > 0 ? ' ' + attributes : null; + Map predefinedValues = null; + if (attributes != null) { + predefinedValues = new HashMap(); + predefinedValues.put(ATTRS, attributes); + } + return predefinedValues; + } + + private static String getValue(Pair pair, int numberInIteration) { + return pair.second.replace(NUMBER_IN_ITERATION_PLACE_HOLDER, Integer.toString(numberInIteration + 1)); + } + + @Nullable + private static String addAttrsVar(TemplateImpl modifiedTemplate, XmlTag tag) { + String text = tag.getContainingFile().getText(); + PsiElement[] children = tag.getChildren(); + if (children.length >= 1 && + children[0] instanceof XmlToken && + ((XmlToken)children[0]).getTokenType() == XmlTokenType.XML_START_TAG_START) { + PsiElement beforeAttrs = children[0]; + if (children.length >= 2 && children[1] instanceof XmlToken && ((XmlToken)children[1]).getTokenType() == XmlTokenType.XML_NAME) { + beforeAttrs = children[1]; + } + TextRange range = beforeAttrs.getTextRange(); + if (range == null) { + return null; + } + int offset = range.getEndOffset(); + text = text.substring(0, offset) + " $ATTRS$" + text.substring(offset); + modifiedTemplate.addVariable(ATTRS, "", "", false); + return text; + } + return null; + } + + private static boolean invokeTemplate(TemplateToken token, + final CustomTemplateCallback callback, + final TemplateInvokationListener listener, + int numberInIteration) { + List> attr2value = new ArrayList>(token.myAttribute2Value); + if (callback.isLiveTemplateApplicable(token.myKey)) { + if (token.myTemplate != null) { + TemplateImpl modifiedTemplate = token.myTemplate.copy(); + XmlTag tag = XmlZenCodingTemplate.parseXmlTagInTemplate(token.myTemplate.getString(), callback.getProject()); + assert tag != null; + for (Iterator> iterator = attr2value.iterator(); iterator.hasNext();) { + Pair pair = iterator.next(); + if (tag.getAttribute(pair.first) != null) { + tag.setAttribute(pair.first, getValue(pair, numberInIteration)); + iterator.remove(); + } + } + String text = null; + if (!containsAttrsVar(modifiedTemplate) && attr2value.size() > 0) { + String textWithAttrs = addAttrsVar(modifiedTemplate, tag); + if (textWithAttrs != null) { + text = textWithAttrs; + } + else { + for (Iterator> iterator = attr2value.iterator(); iterator.hasNext();) { + Pair pair = iterator.next(); + tag.setAttribute(pair.first, getValue(pair, numberInIteration)); + iterator.remove(); + } + } + } + if (text == null) { + text = tag.getContainingFile().getText(); + } + modifiedTemplate.setString(text); + removeVariablesWhichHasNoSegment(modifiedTemplate); + Map predefinedValues = buildPredefinedValues(attr2value, numberInIteration); + return callback.startTemplate(modifiedTemplate, predefinedValues, listener); + } + else { + Map predefinedValues = buildPredefinedValues(attr2value, numberInIteration); + return callback.startTemplate(token.myKey, predefinedValues, listener); + } + } + else { + TemplateImpl template = new TemplateImpl("", ""); + template.addTextSegment('<' + token.myKey); + if (attr2value.size() > 0) { + template.addVariable(ATTRS, "", "", false); + template.addVariableSegment(ATTRS); + } + template.addTextSegment(">"); + template.addVariableSegment(TemplateImpl.END); + template.addTextSegment(""); + template.setToReformat(true); + Map predefinedValues = buildPredefinedValues(attr2value, numberInIteration); + return callback.startTemplate(template, predefinedValues, listener); + } + } + + private static void fail() { + LOG.error("Input string was checked incorrectly during isApplicable() invokation"); + } +} diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/XmlZenCodingTemplate.java b/xml/impl/src/com/intellij/codeInsight/template/zencoding/XmlZenCodingTemplate.java new file mode 100644 index 000000000000..29fdbb908743 --- /dev/null +++ b/xml/impl/src/com/intellij/codeInsight/template/zencoding/XmlZenCodingTemplate.java @@ -0,0 +1,349 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.template.zencoding; + +import com.intellij.application.options.editor.WebEditorOptions; +import com.intellij.codeInsight.template.CustomLiveTemplate; +import com.intellij.codeInsight.template.CustomTemplateCallback; +import com.intellij.codeInsight.template.TemplateInvokationListener; +import com.intellij.codeInsight.template.impl.TemplateImpl; +import com.intellij.lang.xml.XMLLanguage; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiFileFactory; +import com.intellij.psi.xml.*; +import com.intellij.util.containers.HashSet; +import org.apache.xerces.util.XML11Char; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +/** + * @author Eugene.Kudelevsky + */ +public class XmlZenCodingTemplate implements CustomLiveTemplate { + static final char MARKER = '$'; + private static final String OPERATIONS = ">+*"; + private static final String SELECTORS = ".#["; + private static final String ID = "id"; + private static final String CLASS = "class"; + + private static int parseNonNegativeInt(@NotNull String s) { + try { + return Integer.parseInt(s); + } + catch (Throwable ignored) { + } + return -1; + } + + private static String getPrefix(@NotNull String templateKey) { + for (int i = 0, n = templateKey.length(); i < n; i++) { + char c = templateKey.charAt(i); + if (SELECTORS.indexOf(c) >= 0) { + return templateKey.substring(0, i); + } + } + return templateKey; + } + + @Nullable + private static Pair parseAttrNameAndValue(@NotNull String text) { + int eqIndex = text.indexOf('='); + if (eqIndex > 0) { + return new Pair(text.substring(0, eqIndex), text.substring(eqIndex + 1)); + } + return null; + } + + @Nullable + private static TemplateToken parseSelectors(@NotNull String text) { + String templateKey = null; + List> attributes = new ArrayList>(); + Set definedAttrs = new HashSet(); + final List classes = new ArrayList(); + StringBuilder builder = new StringBuilder(); + char lastDelim = 0; + text += MARKER; + int classAttrPosition = -1; + for (int i = 0, n = text.length(); i < n; i++) { + char c = text.charAt(i); + if (c == '#' || c == '.' || c == '[' || c == ']' || i == n - 1) { + if (c != ']') { + switch (lastDelim) { + case 0: + templateKey = builder.toString(); + break; + case '#': + if (!definedAttrs.add(ID)) { + return null; + } + attributes.add(new Pair(ID, builder.toString())); + break; + case '.': + if (builder.length() <= 0) { + return null; + } + if (classAttrPosition < 0) { + classAttrPosition = attributes.size(); + } + classes.add(builder.toString()); + break; + case ']': + if (builder.length() > 0) { + return null; + } + break; + default: + return null; + } + } + else if (lastDelim != '[') { + return null; + } + else { + Pair pair = parseAttrNameAndValue(builder.toString()); + if (pair == null || !definedAttrs.add(pair.first)) { + return null; + } + attributes.add(pair); + } + lastDelim = c; + builder = new StringBuilder(); + } + else { + builder.append(c); + } + } + if (classes.size() > 0) { + if (definedAttrs.contains(CLASS)) { + return null; + } + StringBuilder classesAttrValue = new StringBuilder(); + for (int i = 0; i < classes.size(); i++) { + classesAttrValue.append(classes.get(i)); + if (i < classes.size() - 1) { + classesAttrValue.append(' '); + } + } + assert classAttrPosition >= 0; + attributes.add(classAttrPosition, new Pair(CLASS, classesAttrValue.toString())); + } + return new TemplateToken(templateKey, attributes); + } + + private static boolean isXML11ValidQName(String str) { + final int colon = str.indexOf(':'); + if (colon == 0 || colon == str.length() - 1) { + return false; + } + if (colon > 0) { + final String prefix = str.substring(0, colon); + final String localPart = str.substring(colon + 1); + return XML11Char.isXML11ValidNCName(prefix) && XML11Char.isXML11ValidNCName(localPart); + } + return XML11Char.isXML11ValidNCName(str); + } + + private static boolean generateTemplateAndAddToToken(TemplateToken token, CustomTemplateCallback callback) { + TemplateImpl template = callback.findApplicableTemplate(token.myKey); + assert template != null; + XmlTag tag = parseXmlTagInTemplate(template.getString(), callback.getProject()); + if (tag == null) { + return false; + } + token.myTemplate = template; + return true; + } + + @Nullable + private static List parse(@NotNull String text, @NotNull CustomTemplateCallback callback) { + text += MARKER; + StringBuilder templateKeyBuilder = new StringBuilder(); + List result = new ArrayList(); + for (int i = 0, n = text.length(); i < n; i++) { + char c = text.charAt(i); + if (i == n - 1 || (i < n - 2 && OPERATIONS.indexOf(c) >= 0)) { + String key = templateKeyBuilder.toString(); + templateKeyBuilder = new StringBuilder(); + int num = parseNonNegativeInt(key); + if (num > 0) { + result.add(new NumberToken(num)); + } + else { + if (key.length() == 0) { + return null; + } + String prefix = getPrefix(key); + boolean applicable = callback.isLiveTemplateApplicable(prefix); + if (!applicable && !isXML11ValidQName(prefix)) { + return null; + } + TemplateToken token = parseSelectors(key); + if (token == null) { + return null; + } + if (applicable && token.myAttribute2Value.size() > 0) { + assert prefix.equals(token.myKey); + if (!generateTemplateAndAddToToken(token, callback)) { + return null; + } + } + result.add(token); + } + result.add(i < n - 1 ? new OperationToken(c) : new MarkerToken()); + } + else if (!Character.isWhitespace(c)) { + templateKeyBuilder.append(c); + } + else { + return null; + } + } + return result; + } + + private static boolean check(@NotNull Collection tokens) { + State state = State.WORD; + for (Token token : tokens) { + if (token instanceof MarkerToken) { + break; + } + switch (state) { + case OPERATION: + if (token instanceof OperationToken) { + state = ((OperationToken)token).mySign == '*' ? State.NUMBER : State.WORD; + } + else { + return false; + } + break; + case WORD: + if (token instanceof TemplateToken) { + state = State.OPERATION; + } + else { + return false; + } + break; + case NUMBER: + if (token instanceof NumberToken) { + state = State.AFTER_NUMBER; + } + else { + return false; + } + break; + case AFTER_NUMBER: + if (token instanceof OperationToken && ((OperationToken)token).mySign != '*') { + state = State.WORD; + } + else { + return false; + } + break; + } + } + return state == State.OPERATION || state == State.AFTER_NUMBER; + } + + private static String computeKey(Editor editor, int startOffset) { + int offset = editor.getCaretModel().getOffset(); + String s = editor.getDocument().getCharsSequence().subSequence(startOffset, offset).toString(); + int index = 0; + while (index < s.length() && Character.isWhitespace(s.charAt(index))) { + index++; + } + String key = s.substring(index); + int lastWhitespaceIndex = -1; + for (int i = 0; i < key.length(); i++) { + if (Character.isWhitespace(key.charAt(i))) { + lastWhitespaceIndex = i; + } + } + if (lastWhitespaceIndex >= 0 && lastWhitespaceIndex < key.length() - 1) { + return key.substring(lastWhitespaceIndex + 1); + } + return key; + } + + public String computeTemplateKey(@NotNull CustomTemplateCallback callback) { + Editor editor = callback.getEditor(); + int offset = callback.getOffset(); + PsiElement element = callback.getFile().findElementAt(offset > 0 ? offset - 1 : offset); + int line = editor.getCaretModel().getLogicalPosition().line; + int lineStart = editor.getDocument().getLineStartOffset(line); + int parentStart; + do { + parentStart = element != null ? element.getTextRange().getStartOffset() : 0; + int startOffset = parentStart > lineStart ? parentStart : lineStart; + String key = computeKey(editor, startOffset); + List tokens = parse(key, callback); + if (tokens != null && check(tokens)) { + if (tokens.size() == 2) { + Token token = tokens.get(0); + if (token instanceof TemplateToken) { + if (key.equals(((TemplateToken)token).myKey) && callback.isLiveTemplateApplicable(key)) { + // do not activate only live template + return null; + } + } + } + return key; + } + if (element != null) { + element = element.getParent(); + } + } + while (element != null && parentStart > lineStart); + return null; + } + + public boolean isApplicable(PsiFile file, int offset, boolean selection) { + WebEditorOptions webEditorOptions = WebEditorOptions.getInstance(); + if (!webEditorOptions.isZenCodingEnabled()) { + return false; + } + if (file.getLanguage() instanceof XMLLanguage) { + PsiElement element = file.findElementAt(offset > 0 ? offset - 1 : offset); + if (element == null || element.getLanguage() instanceof XMLLanguage) { + return true; + } + } + return false; + } + + public void expand(String key, @NotNull CustomTemplateCallback callback, @Nullable TemplateInvokationListener listener) { + List tokens = parse(key, callback); + assert tokens != null; + XmlZenCodingInterpreter interpreter = new XmlZenCodingInterpreter(tokens, callback, State.WORD, listener); + interpreter.invoke(0); + } + + public void wrap(String selection, @NotNull CustomTemplateCallback callback, @Nullable TemplateInvokationListener listener) { + } + + @Nullable + static XmlTag parseXmlTagInTemplate(String templateString, Project project) { + XmlFile xmlFile = (XmlFile)PsiFileFactory.getInstance(project).createFileFromText("dummy.xml", templateString); + XmlDocument document = xmlFile.getDocument(); + return document == null ? null : document.getRootTag(); + } +} \ No newline at end of file From 2d3f9423a50214d0a8b7cb1aac94825966b8b460 Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Mon, 15 Mar 2010 18:58:32 +0300 Subject: [PATCH 72/93] fix initial editor state in CustomTemplateCallback's constructor --- .../codeInsight/template/CustomTemplateCallback.java | 10 ---------- .../codeInsight/template/impl/TemplateManagerImpl.java | 1 - 2 files changed, 11 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/CustomTemplateCallback.java b/platform/lang-impl/src/com/intellij/codeInsight/template/CustomTemplateCallback.java index 854c6d05290d..a62ab1038153 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/CustomTemplateCallback.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/CustomTemplateCallback.java @@ -43,23 +43,13 @@ public class CustomTemplateCallback { private Project myProject; private RangeMarker myGlobalMarker; private RangeMarker myEndOffsetMarker; - - //private final Map myCheckpoints = new HashMap(); private final Map myCheckpoints = new HashMap(); - /*private static class MyCheckpoint { - int myFixedLength = null; - RangeMarker myFixedOffset; - }*/ - public CustomTemplateCallback(Editor editor, PsiFile file) { myEditor = editor; myFile = file; myProject = file.getProject(); myTemplateManager = TemplateManagerImpl.getInstance(myProject); - } - - public void fixInitialEditorState() { myStartOffset = myEditor.getCaretModel().getOffset(); myGlobalMarker = myEditor.getDocument().createRangeMarker(myStartOffset, myStartOffset); myGlobalMarker.setGreedyToLeft(true); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateManagerImpl.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateManagerImpl.java index d8baf07f3855..0a331403e0df 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateManagerImpl.java @@ -246,7 +246,6 @@ public class TemplateManagerImpl extends TemplateManager implements ProjectCompo int caretOffset = editor.getCaretModel().getOffset(); if (customLiveTemplate.isApplicable(file, caretOffset, false)) { final CustomTemplateCallback callback = new CustomTemplateCallback(editor, file); - callback.fixInitialEditorState(); String key = customLiveTemplate.computeTemplateKey(callback); if (key != null) { int offsetBeforeKey = caretOffset - key.length(); From dea1fd141246e8a06360f305f831edb6cbca6740 Mon Sep 17 00:00:00 2001 From: irengrig Date: Mon, 15 Mar 2010 19:08:47 +0300 Subject: [PATCH 73/93] VCS: patch creation separated from patch application: 2 more steps --- .../historyIntegrTests/PatchingTestCase.java | 3 +- .../openapi/diff/impl/patch/PatchHunk.java | 115 -------------- .../openapi/diff/impl/patch/PatchReader.java | 13 +- .../impl/patch/PatchVirtualFileReader.java | 32 ++++ .../impl/patch/apply/ApplyFilePatchBase.java | 2 +- .../diff/impl/patch/apply/ApplyPatchHunk.java | 149 ++++++++++++++++++ .../impl/patch/apply/ApplyTextFilePatch.java | 2 +- .../patch/ApplyPatchDifferentiatedDialog.java | 7 +- 8 files changed, 189 insertions(+), 134 deletions(-) create mode 100644 platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchVirtualFileReader.java create mode 100644 platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyPatchHunk.java diff --git a/platform/lvcs-impl/testSrc/com/intellij/historyIntegrTests/PatchingTestCase.java b/platform/lvcs-impl/testSrc/com/intellij/historyIntegrTests/PatchingTestCase.java index 4b7be8efdcfd..a36384218eb1 100644 --- a/platform/lvcs-impl/testSrc/com/intellij/historyIntegrTests/PatchingTestCase.java +++ b/platform/lvcs-impl/testSrc/com/intellij/historyIntegrTests/PatchingTestCase.java @@ -19,6 +19,7 @@ package com.intellij.historyIntegrTests; import com.intellij.openapi.diff.impl.patch.BinaryFilePatch; import com.intellij.openapi.diff.impl.patch.FilePatch; import com.intellij.openapi.diff.impl.patch.PatchReader; +import com.intellij.openapi.diff.impl.patch.PatchVirtualFileReader; import com.intellij.openapi.diff.impl.patch.formove.PatchApplier; import com.intellij.openapi.vfs.VirtualFile; @@ -45,7 +46,7 @@ public abstract class PatchingTestCase extends IntegrationTestCase { protected void applyPatch() throws Exception { List patches = new ArrayList(); - PatchReader reader = new PatchReader(getFS().refreshAndFindFileByPath(patchFilePath)); + PatchReader reader = PatchVirtualFileReader.create(getFS().refreshAndFindFileByPath(patchFilePath)); while (true) { FilePatch p = reader.readNextPatch(); diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchHunk.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchHunk.java index a899bee960bd..3ca09d8d68f3 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchHunk.java +++ b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchHunk.java @@ -64,121 +64,6 @@ public class PatchHunk { return Collections.unmodifiableList(myLines); } - public ApplyPatchStatus apply(final List lines) throws ApplyPatchException { - List originalLines = new ArrayList(lines); - try { - return tryApply(lines, false); - } - catch(ApplyPatchException ex) { - lines.clear(); - lines.addAll(originalLines); - return tryApply(lines, true); - } - } - - private ApplyPatchStatus tryApply(final List lines, boolean acceptPartial) throws ApplyPatchException { - ApplyPatchStatus result = null; - int curLine = findStartLine(lines); - for(PatchLine line: myLines) { - final String patchLineText = line.getText(); - switch (line.getType()) { - case CONTEXT: - checkContextMismatch(lines, curLine, patchLineText); - curLine++; - break; - - case ADD: - if (curLine < lines.size() && lines.get(curLine).equals(patchLineText) && acceptPartial) { - result = ApplyPatchStatus.and(result, ApplyPatchStatus.ALREADY_APPLIED); - } - else { - lines.add(curLine, patchLineText); - result = ApplyPatchStatus.and(result, ApplyPatchStatus.SUCCESS); - } - curLine++; - break; - - case REMOVE: - if (curLine >= lines.size() || !patchLineText.equals(lines.get(curLine))) { - if (acceptPartial) { - // we'll get a context mismatch exception later if it's actually a conflict and not an already applied line - result = ApplyPatchStatus.and(result, ApplyPatchStatus.ALREADY_APPLIED); - } - else { - checkContextMismatch(lines, curLine, patchLineText); - } - } - else { - lines.remove(curLine); - result = ApplyPatchStatus.and(result, ApplyPatchStatus.SUCCESS); - } - break; - } - } - if (result != null) { - return result; - } - return ApplyPatchStatus.SUCCESS; - } - - private static void checkContextMismatch(final List lines, final int curLine, final String patchLineText) throws ApplyPatchException { - if (curLine >= lines.size()) { - throw new ApplyPatchException("Unexpected end of document. Expected line:\n" + patchLineText); - } - if (!patchLineText.equals(lines.get(curLine))) { - throw new ApplyPatchException("Context mismatch. Expected line:\n" + patchLineText + "\nFound line:\n" + lines.get(curLine)); - } - } - - private int findStartLine(final List lines) throws ApplyPatchException { - int totalContextLines = countContextLines(); - if (getLinesProcessingContext(lines, myStartLineBefore) == totalContextLines) { - return myStartLineBefore; - } - int maxContextStartLine = -1; - int maxContextLines = 0; - for(int i=0;i< lines.size(); i++) { - int contextLines = getLinesProcessingContext(lines, i); - if (contextLines == totalContextLines) { - return i; - } - if (contextLines > maxContextLines) { - maxContextLines = contextLines; - maxContextStartLine = i; - } - } - if (maxContextLines < 2) { - throw new ApplyPatchException("couldn't find context"); - } - return maxContextStartLine; - } - - private int countContextLines() { - int count = 0; - for(PatchLine line: myLines) { - if (line.getType() == PatchLine.Type.CONTEXT || line.getType() == PatchLine.Type.REMOVE) { - count++; - } - } - return count; - } - - private int getLinesProcessingContext(final List lines, int startLine) { - int count = 0; - for(PatchLine line: myLines) { - PatchLine.Type type = line.getType(); - if (type == PatchLine.Type.REMOVE || type == PatchLine.Type.CONTEXT) { - // TODO: smarter algorithm (search outward from non-context lines) - if (startLine >= lines.size() || !line.getText().equals(lines.get(startLine))) { - return count; - } - count++; - startLine++; - } - } - return count; - } - public boolean isNewContent() { return myStartLineBefore == -1 && myEndLineBefore == -1; } diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchReader.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchReader.java index 7baf2fe36210..288b39586d53 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchReader.java +++ b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchReader.java @@ -22,17 +22,14 @@ */ package com.intellij.openapi.diff.impl.patch; -import com.intellij.openapi.fileEditor.impl.LoadTextUtil; import com.intellij.openapi.util.text.LineTokenizer; -import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.Nullable; -import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.List; -import java.util.ArrayList; public class PatchReader { @NonNls public static final String NO_NEWLINE_SIGNATURE = "\\ No newline at end of file"; @@ -48,12 +45,6 @@ public class PatchReader { @NonNls private static final Pattern ourContextBeforeHunkStartPattern = Pattern.compile("\\*\\*\\* (\\d+),(\\d+) \\*\\*\\*\\*"); @NonNls private static final Pattern ourContextAfterHunkStartPattern = Pattern.compile("--- (\\d+),(\\d+) ----"); - public PatchReader(VirtualFile virtualFile) throws IOException { - byte[] patchContents = virtualFile.contentsToByteArray(); - CharSequence patchText = LoadTextUtil.getTextByBinaryPresentation(patchContents, virtualFile); - myLines = LineTokenizer.tokenize(patchText, false); - } - public PatchReader(CharSequence patchContent) { myLines = LineTokenizer.tokenize(patchContent, false); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchVirtualFileReader.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchVirtualFileReader.java new file mode 100644 index 000000000000..d416c384cb44 --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchVirtualFileReader.java @@ -0,0 +1,32 @@ +/* + * Copyright 2000-2010 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.openapi.diff.impl.patch; + +import com.intellij.openapi.fileEditor.impl.LoadTextUtil; +import com.intellij.openapi.vfs.VirtualFile; + +import java.io.IOException; + +public class PatchVirtualFileReader { + private PatchVirtualFileReader() { + } + + public static PatchReader create(final VirtualFile virtualFile) throws IOException { + final byte[] patchContents = virtualFile.contentsToByteArray(); + final CharSequence patchText = LoadTextUtil.getTextByBinaryPresentation(patchContents, virtualFile); + return new PatchReader(patchText); + } +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyFilePatchBase.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyFilePatchBase.java index 0b368a9e09ba..b6fda11b4e0b 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyFilePatchBase.java +++ b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyFilePatchBase.java @@ -186,7 +186,7 @@ public abstract class ApplyFilePatchBase implements ApplyFi Collections.addAll(lines, LineTokenizer.tokenize(text, false)); ApplyPatchStatus result = null; for(PatchHunk hunk: hunks) { - result = ApplyPatchStatus.and(result, hunk.apply(lines)); + result = ApplyPatchStatus.and(result, new ApplyPatchHunk(hunk).apply(lines)); } for(int i=0; i lines) throws ApplyPatchException { + List originalLines = new ArrayList(lines); + try { + return tryApply(lines, false); + } + catch(ApplyPatchException ex) { + lines.clear(); + lines.addAll(originalLines); + return tryApply(lines, true); + } + } + + private ApplyPatchStatus tryApply(final List lines, boolean acceptPartial) throws ApplyPatchException { + final List hunkLines = myHunk.getLines(); + ApplyPatchStatus result = null; + int curLine = findStartLine(hunkLines, lines); + for(PatchLine line: hunkLines) { + final String patchLineText = line.getText(); + switch (line.getType()) { + case CONTEXT: + checkContextMismatch(lines, curLine, patchLineText); + curLine++; + break; + + case ADD: + if (curLine < lines.size() && lines.get(curLine).equals(patchLineText) && acceptPartial) { + result = ApplyPatchStatus.and(result, ApplyPatchStatus.ALREADY_APPLIED); + } + else { + lines.add(curLine, patchLineText); + result = ApplyPatchStatus.and(result, ApplyPatchStatus.SUCCESS); + } + curLine++; + break; + + case REMOVE: + if (curLine >= lines.size() || !patchLineText.equals(lines.get(curLine))) { + if (acceptPartial) { + // we'll get a context mismatch exception later if it's actually a conflict and not an already applied line + result = ApplyPatchStatus.and(result, ApplyPatchStatus.ALREADY_APPLIED); + } + else { + checkContextMismatch(lines, curLine, patchLineText); + } + } + else { + lines.remove(curLine); + result = ApplyPatchStatus.and(result, ApplyPatchStatus.SUCCESS); + } + break; + } + } + if (result != null) { + return result; + } + return ApplyPatchStatus.SUCCESS; + } + + private static void checkContextMismatch(final List lines, final int curLine, final String patchLineText) throws ApplyPatchException { + if (curLine >= lines.size()) { + throw new ApplyPatchException("Unexpected end of document. Expected line:\n" + patchLineText); + } + if (!patchLineText.equals(lines.get(curLine))) { + throw new ApplyPatchException("Context mismatch. Expected line:\n" + patchLineText + "\nFound line:\n" + lines.get(curLine)); + } + } + + private int findStartLine(final List hunkLines, final List lines) throws ApplyPatchException { + int totalContextLines = countContextLines(hunkLines); + final int startLineBefore = myHunk.getStartLineBefore(); + if (getLinesProcessingContext(hunkLines, lines, startLineBefore) == totalContextLines) { + return startLineBefore; + } + int maxContextStartLine = -1; + int maxContextLines = 0; + for(int i=0;i< lines.size(); i++) { + int contextLines = getLinesProcessingContext(hunkLines, lines, i); + if (contextLines == totalContextLines) { + return i; + } + if (contextLines > maxContextLines) { + maxContextLines = contextLines; + maxContextStartLine = i; + } + } + if (maxContextLines < 2) { + throw new ApplyPatchException("couldn't find context"); + } + return maxContextStartLine; + } + + private int countContextLines(final List hunkLines) { + int count = 0; + for(PatchLine line: hunkLines) { + if (line.getType() == PatchLine.Type.CONTEXT || line.getType() == PatchLine.Type.REMOVE) { + count++; + } + } + return count; + } + + private int getLinesProcessingContext(final List hunkLines, final List lines, int startLine) { + int count = 0; + for(PatchLine line: hunkLines) { + PatchLine.Type type = line.getType(); + if (type == PatchLine.Type.REMOVE || type == PatchLine.Type.CONTEXT) { + // TODO: smarter algorithm (search outward from non-context lines) + if (startLine >= lines.size() || !line.getText().equals(lines.get(startLine))) { + return count; + } + count++; + startLine++; + } + } + return count; + } +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyTextFilePatch.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyTextFilePatch.java index 0fb9298d9b8f..d59542a67df5 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyTextFilePatch.java +++ b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyTextFilePatch.java @@ -64,7 +64,7 @@ public class ApplyTextFilePatch extends ApplyFilePatchBase { Collections.addAll(lines, LineTokenizer.tokenize(text, false)); ApplyPatchStatus result = null; for(PatchHunk hunk: hunks) { - result = ApplyPatchStatus.and(result, hunk.apply(lines)); + result = ApplyPatchStatus.and(result, new ApplyPatchHunk(hunk).apply(lines)); } for(int i=0; i Date: Mon, 15 Mar 2010 19:11:39 +0300 Subject: [PATCH 74/93] VCS: patch creation separated from patch application, small cleanup --- .../impl/patch/apply/ApplyFilePatchBase.java | 1 - .../impl/patch/apply/ApplyTextFilePatch.java | 29 +------------------ 2 files changed, 1 insertion(+), 29 deletions(-) diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyFilePatchBase.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyFilePatchBase.java index b6fda11b4e0b..502dc684693f 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyFilePatchBase.java +++ b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyFilePatchBase.java @@ -174,7 +174,6 @@ public abstract class ApplyFilePatchBase implements ApplyFi return patchedDir; } - // todo move somewhere? @Nullable public static ApplyPatchStatus applyModifications(final TextFilePatch patch, final CharSequence text, final StringBuilder newText) throws ApplyPatchException { diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyTextFilePatch.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyTextFilePatch.java index d59542a67df5..307cfda70cbd 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyTextFilePatch.java +++ b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/apply/ApplyTextFilePatch.java @@ -17,19 +17,14 @@ package com.intellij.openapi.diff.impl.patch.apply; import com.intellij.openapi.diff.impl.patch.ApplyPatchException; import com.intellij.openapi.diff.impl.patch.ApplyPatchStatus; -import com.intellij.openapi.diff.impl.patch.PatchHunk; import com.intellij.openapi.diff.impl.patch.TextFilePatch; import com.intellij.openapi.editor.Document; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileEditor.impl.LoadTextUtil; -import com.intellij.openapi.util.text.LineTokenizer; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.Nullable; import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; public class ApplyTextFilePatch extends ApplyFilePatchBase { public ApplyTextFilePatch(final TextFilePatch patch) { @@ -41,7 +36,7 @@ public class ApplyTextFilePatch extends ApplyFilePatchBase { byte[] fileContents = fileToPatch.contentsToByteArray(); CharSequence text = LoadTextUtil.getTextByBinaryPresentation(fileContents, fileToPatch); StringBuilder newText = new StringBuilder(); - ApplyPatchStatus status = applyModifications(text, newText); + ApplyPatchStatus status = ApplyFilePatchBase.applyModifications(myPatch, text, newText); if (status != ApplyPatchStatus.ALREADY_APPLIED) { final Document document = FileDocumentManager.getInstance().getDocument(fileToPatch); if (document == null) { @@ -53,28 +48,6 @@ public class ApplyTextFilePatch extends ApplyFilePatchBase { return status; } - // todo taken to another place also, cheeeeeck - @Nullable - public ApplyPatchStatus applyModifications(final CharSequence text, final StringBuilder newText) throws ApplyPatchException { - final List hunks = myPatch.getHunks(); - if (hunks.size() == 0) { - return ApplyPatchStatus.SUCCESS; - } - List lines = new ArrayList(); - Collections.addAll(lines, LineTokenizer.tokenize(text, false)); - ApplyPatchStatus result = null; - for(PatchHunk hunk: hunks) { - result = ApplyPatchStatus.and(result, new ApplyPatchHunk(hunk).apply(lines)); - } - for(int i=0; i Date: Mon, 15 Mar 2010 19:28:41 +0300 Subject: [PATCH 75/93] VCS: patch creation to util --- .../src/com/intellij/openapi/diff/impl/patch/PatchHunk.java | 0 .../openapi/diff/impl/processing/DiffFragmentsProcessor.java | 0 .../openapi/diff/impl/processing/LineFragmentsCollector.java | 0 .../com/intellij/openapi/diff/impl/patch/BinaryFilePatch.java | 0 .../src/com/intellij/openapi/diff/impl/patch/FilePatch.java | 0 .../src/com/intellij/openapi/diff/impl/patch/PatchReader.java | 0 .../intellij/openapi/diff/impl/patch/PatchSyntaxException.java | 0 .../src/com/intellij/openapi/diff/impl/patch/TextFilePatch.java | 0 .../com/intellij/openapi/diff/impl/patch/TextPatchBuilder.java | 0 .../com/intellij/openapi/diff/impl/patch/UnifiedDiffWriter.java | 2 +- 10 files changed, 1 insertion(+), 1 deletion(-) rename platform/{vcs-impl => util}/src/com/intellij/openapi/diff/impl/patch/PatchHunk.java (100%) rename platform/{platform-impl => util}/src/com/intellij/openapi/diff/impl/processing/DiffFragmentsProcessor.java (100%) rename platform/{platform-impl => util}/src/com/intellij/openapi/diff/impl/processing/LineFragmentsCollector.java (100%) rename platform/{vcs-impl => vcs-api}/src/com/intellij/openapi/diff/impl/patch/BinaryFilePatch.java (100%) rename platform/{vcs-impl => vcs-api}/src/com/intellij/openapi/diff/impl/patch/FilePatch.java (100%) rename platform/{vcs-impl => vcs-api}/src/com/intellij/openapi/diff/impl/patch/PatchReader.java (100%) rename platform/{vcs-impl => vcs-api}/src/com/intellij/openapi/diff/impl/patch/PatchSyntaxException.java (100%) rename platform/{vcs-impl => vcs-api}/src/com/intellij/openapi/diff/impl/patch/TextFilePatch.java (100%) rename platform/{vcs-impl => vcs-api}/src/com/intellij/openapi/diff/impl/patch/TextPatchBuilder.java (100%) rename platform/{vcs-impl => vcs-api}/src/com/intellij/openapi/diff/impl/patch/UnifiedDiffWriter.java (100%) diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchHunk.java b/platform/util/src/com/intellij/openapi/diff/impl/patch/PatchHunk.java similarity index 100% rename from platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchHunk.java rename to platform/util/src/com/intellij/openapi/diff/impl/patch/PatchHunk.java diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/processing/DiffFragmentsProcessor.java b/platform/util/src/com/intellij/openapi/diff/impl/processing/DiffFragmentsProcessor.java similarity index 100% rename from platform/platform-impl/src/com/intellij/openapi/diff/impl/processing/DiffFragmentsProcessor.java rename to platform/util/src/com/intellij/openapi/diff/impl/processing/DiffFragmentsProcessor.java diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/processing/LineFragmentsCollector.java b/platform/util/src/com/intellij/openapi/diff/impl/processing/LineFragmentsCollector.java similarity index 100% rename from platform/platform-impl/src/com/intellij/openapi/diff/impl/processing/LineFragmentsCollector.java rename to platform/util/src/com/intellij/openapi/diff/impl/processing/LineFragmentsCollector.java diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/BinaryFilePatch.java b/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/BinaryFilePatch.java similarity index 100% rename from platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/BinaryFilePatch.java rename to platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/BinaryFilePatch.java diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/FilePatch.java b/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/FilePatch.java similarity index 100% rename from platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/FilePatch.java rename to platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/FilePatch.java diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchReader.java b/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/PatchReader.java similarity index 100% rename from platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchReader.java rename to platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/PatchReader.java diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchSyntaxException.java b/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/PatchSyntaxException.java similarity index 100% rename from platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/PatchSyntaxException.java rename to platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/PatchSyntaxException.java diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/TextFilePatch.java b/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/TextFilePatch.java similarity index 100% rename from platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/TextFilePatch.java rename to platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/TextFilePatch.java diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/TextPatchBuilder.java b/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/TextPatchBuilder.java similarity index 100% rename from platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/TextPatchBuilder.java rename to platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/TextPatchBuilder.java diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/UnifiedDiffWriter.java b/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/UnifiedDiffWriter.java similarity index 100% rename from platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/UnifiedDiffWriter.java rename to platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/UnifiedDiffWriter.java index 44d3a8475061..037cbb0c925e 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/UnifiedDiffWriter.java +++ b/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/UnifiedDiffWriter.java @@ -26,8 +26,8 @@ import org.jetbrains.annotations.NonNls; import java.io.IOException; import java.io.Writer; -import java.util.Collection; import java.text.MessageFormat; +import java.util.Collection; public class UnifiedDiffWriter { @NonNls private static final String INDEX_SIGNATURE = "Index: {0}{1}"; From 45ddf6b88d64dee94f79751914b1d1d0beb84a8f Mon Sep 17 00:00:00 2001 From: Maxim Shafirov Date: Mon, 15 Mar 2010 16:25:55 +0300 Subject: [PATCH 76/93] Don't pretend there's indent guide at column 0. This conflicts with common "select word" patterns and not very useful. --- .../openapi/editor/impl/IndentsModelImpl.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/IndentsModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/IndentsModelImpl.java index ddfa1218e4c3..59527d4d3734 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/IndentsModelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/IndentsModelImpl.java @@ -19,8 +19,9 @@ */ package com.intellij.openapi.editor.impl; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.editor.*; +import com.intellij.openapi.editor.IndentGuideDescriptor; +import com.intellij.openapi.editor.IndentsModel; +import com.intellij.openapi.editor.LogicalPosition; import java.util.ArrayList; import java.util.List; @@ -37,9 +38,12 @@ public class IndentsModelImpl implements IndentsModel { final LogicalPosition pos = myEditor.getCaretModel().getLogicalPosition(); final int column = pos.column; final int line = pos.line; - for (IndentGuideDescriptor indent : myIndents) { - if (column == indent.indentLevel && line >= indent.startLine && line < indent.endLine) { - return indent; + + if (column > 0) { + for (IndentGuideDescriptor indent : myIndents) { + if (column == indent.indentLevel && line >= indent.startLine && line < indent.endLine) { + return indent; + } } } return null; From a9a0f599d95ea5337e9dd8c4d7ffb18bec6bfaa5 Mon Sep 17 00:00:00 2001 From: Maxim Shafirov Date: Mon, 15 Mar 2010 19:33:02 +0300 Subject: [PATCH 77/93] More sophisticated logic to decide if closing brace shall be included into "indent guide based" word selection. --- .../actions/SelectWordAtCaretAction.java | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/openapi/editor/actions/SelectWordAtCaretAction.java b/platform/lang-impl/src/com/intellij/openapi/editor/actions/SelectWordAtCaretAction.java index c7117d616122..1b1cf675cf1d 100644 --- a/platform/lang-impl/src/com/intellij/openapi/editor/actions/SelectWordAtCaretAction.java +++ b/platform/lang-impl/src/com/intellij/openapi/editor/actions/SelectWordAtCaretAction.java @@ -25,11 +25,16 @@ package com.intellij.openapi.editor.actions; import com.intellij.codeInsight.editorActions.SelectWordUtil; +import com.intellij.codeInsight.highlighting.BraceMatchingUtil; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.actionSystem.EditorActionHandler; -import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.editor.ex.EditorEx; +import com.intellij.openapi.editor.highlighter.HighlighterIterator; import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.text.CharArrayUtil; import java.util.ArrayList; import java.util.List; @@ -111,7 +116,23 @@ public class SelectWordAtCaretAction extends TextComponentEditorAction implement private static void selectWithGuide(Editor editor, IndentGuideDescriptor guide) { final Document doc = editor.getDocument(); int startOffset = editor.logicalPositionToOffset(new LogicalPosition(guide.startLine, 0)); - int endOffset = Math.min(editor.logicalPositionToOffset(new LogicalPosition(guide.endLine + 1, 0)), doc.getTextLength()); + int endOffset = Math.min(doc.getLineStartOffset(guide.endLine), doc.getTextLength()); + + final VirtualFile file = ((EditorEx)editor).getVirtualFile(); + if (file != null) { + // Make sure selection contains closing matching brace. + + final CharSequence chars = doc.getCharsSequence(); + int nonWhitespaceOffset = CharArrayUtil.shiftForward(chars, endOffset, " \t\n"); + HighlighterIterator iterator = ((EditorEx)editor).getHighlighter().createIterator(nonWhitespaceOffset); + if (BraceMatchingUtil.isRBraceToken(iterator, chars, file.getFileType())) { + if (((EditorEx)editor).calcColumnNumber(iterator.getStart(), doc.getLineNumber(iterator.getStart())) == guide.indentLevel) { + endOffset = iterator.getEnd(); + endOffset = CharArrayUtil.shiftForward(chars, endOffset, " \t"); + if (endOffset < chars.length() && chars.charAt(endOffset) == '\n') endOffset++; + } + } + } editor.getSelectionModel().setSelection(startOffset, endOffset); } From d37299d90849fea34da696aeb035ec2e8593c6d2 Mon Sep 17 00:00:00 2001 From: anna Date: Mon, 15 Mar 2010 16:45:14 +0300 Subject: [PATCH 78/93] catch not found error (IDEA-52890) --- .../intellij/junit4/JUnit4TestRunnerUtil.java | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/plugins/junit_rt/src/com/intellij/junit4/JUnit4TestRunnerUtil.java b/plugins/junit_rt/src/com/intellij/junit4/JUnit4TestRunnerUtil.java index b75212516c5f..cb94f048ac6f 100644 --- a/plugins/junit_rt/src/com/intellij/junit4/JUnit4TestRunnerUtil.java +++ b/plugins/junit_rt/src/com/intellij/junit4/JUnit4TestRunnerUtil.java @@ -25,7 +25,6 @@ import org.junit.runner.manipulation.Filter; import org.junit.runner.notification.RunNotifier; import org.junit.runners.BlockJUnit4ClassRunner; import org.junit.runners.model.FrameworkMethod; -import org.junit.runners.model.InitializationError; import java.io.BufferedReader; import java.io.FileReader; @@ -114,18 +113,24 @@ public class JUnit4TestRunnerUtil { else { int index = suiteClassName.indexOf(','); if (index != -1) { - final Class clazz = loadTestClass(suiteClassName.substring(0, index)); - final Request classRequest = new ClassRequest(clazz){ - public Runner getRunner() { - try { - return new IgnoreIgnoredTestJUnit4ClassRunner(clazz); + try { + Class.forName("org.junit.runners.model.InitializationError"); + final Class clazz = loadTestClass(suiteClassName.substring(0, index)); + final Request classRequest = new ClassRequest(clazz) { + public Runner getRunner() { + try { + return new IgnoreIgnoredTestJUnit4ClassRunner(clazz); + } + catch (Exception ignored) { + } + return super.getRunner(); } - catch (InitializationError ignored) { - } - return super.getRunner(); - } - }; - return classRequest.filterWith(Description.createTestDescription(clazz, suiteClassName.substring(index + 1))); + }; + return classRequest.filterWith(Description.createTestDescription(clazz, suiteClassName.substring(index + 1))); + } + catch (ClassNotFoundException e) { + return Request.method(loadTestClass(suiteClassName.substring(0, index)), suiteClassName.substring(index + 1)); + } } appendTestClass(result, suiteClassName); } @@ -174,7 +179,7 @@ public class JUnit4TestRunnerUtil { private static class IgnoreIgnoredTestJUnit4ClassRunner extends BlockJUnit4ClassRunner { - public IgnoreIgnoredTestJUnit4ClassRunner(Class clazz) throws InitializationError { + public IgnoreIgnoredTestJUnit4ClassRunner(Class clazz) throws Exception { super(clazz); } From 79b397464388c4fe00d264fa95afffcf9d2305af Mon Sep 17 00:00:00 2001 From: anna Date: Mon, 15 Mar 2010 17:25:56 +0300 Subject: [PATCH 79/93] favorites view: multiple directories per package (IDEA-19975) --- .../favoritesTreeView/FavoritesTreeViewPanel.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/ide/favoritesTreeView/FavoritesTreeViewPanel.java b/platform/lang-impl/src/com/intellij/ide/favoritesTreeView/FavoritesTreeViewPanel.java index a0575affbbff..526f7921f678 100644 --- a/platform/lang-impl/src/com/intellij/ide/favoritesTreeView/FavoritesTreeViewPanel.java +++ b/platform/lang-impl/src/com/intellij/ide/favoritesTreeView/FavoritesTreeViewPanel.java @@ -59,6 +59,7 @@ import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.tree.TreeUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; @@ -457,14 +458,15 @@ public class FavoritesTreeViewPanel extends JPanel implements DataProvider { } } - private PsiDirectory getDirectory() { + @Nullable + private PsiDirectory[] getSelectedDirectories() { if (myBuilder == null) return null; final Object[] selectedNodeElements = getSelectedNodeElements(); if (selectedNodeElements.length != 1) return null; for (FavoriteNodeProvider nodeProvider : Extensions.getExtensions(FavoriteNodeProvider.EP_NAME, myProject)) { final PsiElement psiElement = nodeProvider.getPsiElement(selectedNodeElements[0]); if (psiElement instanceof PsiDirectory) { - return (PsiDirectory)psiElement; + return new PsiDirectory[]{(PsiDirectory)psiElement}; } else if (psiElement instanceof PsiDirectoryContainer) { final String moduleName = nodeProvider.getElementModuleName(selectedNodeElements[0]); GlobalSearchScope searchScope = GlobalSearchScope.projectScope(myProject); @@ -474,16 +476,15 @@ public class FavoritesTreeViewPanel extends JPanel implements DataProvider { searchScope = GlobalSearchScope.moduleScope(module); } } - final PsiDirectory[] directories = ((PsiDirectoryContainer)psiElement).getDirectories(searchScope); - if (directories.length == 1) return directories[0]; + return ((PsiDirectoryContainer)psiElement).getDirectories(searchScope); } } return null; } public PsiDirectory[] getDirectories() { - PsiDirectory directory = getDirectory(); - return directory == null ? PsiDirectory.EMPTY_ARRAY : new PsiDirectory[]{directory}; + final PsiDirectory[] directories = getSelectedDirectories(); + return directories == null ? PsiDirectory.EMPTY_ARRAY : directories; } public PsiDirectory getOrChooseDirectory() { From 0dbd16d7efa9d45d44f38b772319bb2728beea29 Mon Sep 17 00:00:00 2001 From: anna Date: Mon, 15 Mar 2010 19:39:04 +0300 Subject: [PATCH 80/93] field can be local: treat non-primitive fields as read/write (IDEA-52901) --- .../FieldCanBeLocalInspection.java | 14 +++++++++++--- .../localStateVar2InnerClass/expected.xml | 2 ++ .../localStateVar2InnerClass/src/Test.java | 11 +++++++++++ .../fieldCanBeLocal/stateField/expected.xml | 3 +++ .../fieldCanBeLocal/stateField/src/Test.java | 8 ++++++++ .../codeInspection/FieldCanBeLocalTest.java | 2 ++ 6 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 java/java-tests/testData/inspection/fieldCanBeLocal/localStateVar2InnerClass/expected.xml create mode 100644 java/java-tests/testData/inspection/fieldCanBeLocal/localStateVar2InnerClass/src/Test.java create mode 100644 java/java-tests/testData/inspection/fieldCanBeLocal/stateField/expected.xml create mode 100644 java/java-tests/testData/inspection/fieldCanBeLocal/stateField/src/Test.java diff --git a/java/java-impl/src/com/intellij/codeInspection/varScopeCanBeNarrowed/FieldCanBeLocalInspection.java b/java/java-impl/src/com/intellij/codeInspection/varScopeCanBeNarrowed/FieldCanBeLocalInspection.java index d6c709e8fa87..c78e0a180756 100644 --- a/java/java-impl/src/com/intellij/codeInspection/varScopeCanBeNarrowed/FieldCanBeLocalInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/varScopeCanBeNarrowed/FieldCanBeLocalInspection.java @@ -15,9 +15,9 @@ */ package com.intellij.codeInspection.varScopeCanBeNarrowed; +import com.intellij.codeInsight.CodeInsightUtil; import com.intellij.codeInsight.daemon.GroupNames; import com.intellij.codeInsight.daemon.ImplicitUsageProvider; -import com.intellij.codeInsight.CodeInsightUtil; import com.intellij.codeInspection.InspectionsBundle; import com.intellij.codeInspection.LocalQuickFix; import com.intellij.codeInspection.ProblemDescriptor; @@ -29,6 +29,7 @@ import com.intellij.openapi.editor.ScrollType; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Ref; import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.codeStyle.VariableKind; @@ -153,13 +154,13 @@ public class FieldCanBeLocalInspection extends BaseLocalInspectionTool { } } } - final Collection writtenVariables = ControlFlowUtil.getWrittenVariables(controlFlow, 0, controlFlow.getSize(), false); + final Ref> writtenVariables = new Ref>(); final List readBeforeWrites = ControlFlowUtil.getReadBeforeWrite(controlFlow); for (final PsiReferenceExpression readBeforeWrite : readBeforeWrites) { final PsiElement resolved = readBeforeWrite.resolve(); if (resolved instanceof PsiField) { final PsiField field = (PsiField)resolved; - if (writtenVariables.contains(field)){ + if (!(field.getType() instanceof PsiPrimitiveType) || getWrittenVariables(controlFlow, writtenVariables).contains(field)){ PsiElement parent = body.getParent(); if (!(parent instanceof PsiMethod) || !((PsiMethod)parent).isConstructor() || @@ -177,6 +178,13 @@ public class FieldCanBeLocalInspection extends BaseLocalInspectionTool { } } + private static Collection getWrittenVariables(ControlFlow controlFlow, Ref> writtenVariables) { + if (writtenVariables.get() == null) { + writtenVariables.set(ControlFlowUtil.getWrittenVariables(controlFlow, 0, controlFlow.getSize(), false)); + } + return writtenVariables.get(); + } + private static void removeFieldsReferencedFromInitializers(final PsiClass aClass, final Set candidates) { aClass.accept(new JavaRecursiveElementWalkingVisitor() { @Override public void visitMethod(PsiMethod method) { diff --git a/java/java-tests/testData/inspection/fieldCanBeLocal/localStateVar2InnerClass/expected.xml b/java/java-tests/testData/inspection/fieldCanBeLocal/localStateVar2InnerClass/expected.xml new file mode 100644 index 000000000000..4704d91e891d --- /dev/null +++ b/java/java-tests/testData/inspection/fieldCanBeLocal/localStateVar2InnerClass/expected.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/java/java-tests/testData/inspection/fieldCanBeLocal/localStateVar2InnerClass/src/Test.java b/java/java-tests/testData/inspection/fieldCanBeLocal/localStateVar2InnerClass/src/Test.java new file mode 100644 index 000000000000..155a4d1dfd6f --- /dev/null +++ b/java/java-tests/testData/inspection/fieldCanBeLocal/localStateVar2InnerClass/src/Test.java @@ -0,0 +1,11 @@ +import java.util.*; +public class Outer { + private Map value = new HashMap(); + + public class Inner { + + public Inner(String s) { + if (!value.containsKey(s)) value.put(s, ""); + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/fieldCanBeLocal/stateField/expected.xml b/java/java-tests/testData/inspection/fieldCanBeLocal/stateField/expected.xml new file mode 100644 index 000000000000..580a889dbfd6 --- /dev/null +++ b/java/java-tests/testData/inspection/fieldCanBeLocal/stateField/expected.xml @@ -0,0 +1,3 @@ + + + diff --git a/java/java-tests/testData/inspection/fieldCanBeLocal/stateField/src/Test.java b/java/java-tests/testData/inspection/fieldCanBeLocal/stateField/src/Test.java new file mode 100644 index 000000000000..79774c0c5ee9 --- /dev/null +++ b/java/java-tests/testData/inspection/fieldCanBeLocal/stateField/src/Test.java @@ -0,0 +1,8 @@ +import java.util.*; +public class Test { + private final Map myModuleToOutput = new HashMap(); + + void foo() { + if (myModuleToOutput.containsKey(null)) myModuleToOutput.put("", ""); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/FieldCanBeLocalTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/FieldCanBeLocalTest.java index 10630e3c49c4..24a8d482ba1b 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/FieldCanBeLocalTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/FieldCanBeLocalTest.java @@ -26,4 +26,6 @@ public class FieldCanBeLocalTest extends InspectionTestCase { public void testStaticAccess() throws Exception { doTest(); } public void testInnerClassConstructor() throws Exception { doTest(); } public void testLocalVar2InnerClass() throws Exception { doTest(); } + public void testStateField() throws Exception { doTest(); } + public void testLocalStateVar2InnerClass() throws Exception { doTest(); } } From 8022d766d489b12429be0eb7efa692c280337eb6 Mon Sep 17 00:00:00 2001 From: Anton Makeev Date: Mon, 15 Mar 2010 20:09:02 +0300 Subject: [PATCH 81/93] Maven: new builtin properties support (baseUri, maven.build.timestamp) (IDEA-52813) --- .../references/MavenPropertyPsiReference.java | 33 ++++++++++++------- ...enPropertyCompletionAndResolutionTest.java | 21 +++++++++++- 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/references/MavenPropertyPsiReference.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/references/MavenPropertyPsiReference.java index 37ebc09c5559..9921ceb0cc14 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/references/MavenPropertyPsiReference.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/references/MavenPropertyPsiReference.java @@ -50,10 +50,16 @@ import org.jetbrains.idea.maven.vfs.MavenPropertiesVirtualFileSystem; import javax.swing.*; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Set; public class MavenPropertyPsiReference extends MavenPsiReference { + private static final Set BASEDIR_PROPS = + new THashSet(Arrays.asList("basedir", "project.basedir", "pom.basedir", "baseUri", "project.baseUri", "pom.baseUri")); + + private static final String TIMESTAMP_PROP = "maven.build.timestamp"; + protected final MavenDomProjectModel myProjectDom; protected final MavenProject myMavenProject; private final boolean mySoft; @@ -95,10 +101,14 @@ public class MavenPropertyPsiReference extends MavenPsiReference { return resolveEnvPropety(); } - if (myText.equals("basedir") || myText.equals("project.basedir") || myText.equals("pom.basedir")) { + if (BASEDIR_PROPS.contains(myText)) { return resolveBasedir(); } + if (myText.equals(TIMESTAMP_PROP)) { + return myElement; + } + PsiElement result = resolveSystemPropety(); if (result != null) return result; @@ -204,7 +214,7 @@ public class MavenPropertyPsiReference extends MavenPsiReference { } protected void collectVariants(List result) { - collectBasedirVariants(result); + collectStandardVariants(result); collectProjectSchemaVariants(result); collectSettingsXmlSchemaVariants(result); collectPropertiesVariants(result); @@ -212,11 +222,12 @@ public class MavenPropertyPsiReference extends MavenPsiReference { collectSystemEnvProperties(MavenPropertiesVirtualFileSystem.ENV_PROPERTIES_FILE, "env.", result); } - private void collectBasedirVariants(List result) { + private void collectStandardVariants(List result) { PsiDirectory basedir = getBaseDir(); - result.add(createLookupElement(basedir, "basedir", MavenIcons.MAVEN_ICON)); - result.add(createLookupElement(basedir, "pom.basedir", MavenIcons.MAVEN_ICON)); - result.add(createLookupElement(basedir, "project.basedir", MavenIcons.MAVEN_ICON)); + for (String each : BASEDIR_PROPS) { + result.add(createLookupElement(basedir, each, MavenIcons.MAVEN_ICON)); + } + result.add(createLookupElement(myElement, TIMESTAMP_PROP, MavenIcons.MAVEN_ICON)); } private void collectProjectSchemaVariants(final List result) { @@ -245,7 +256,7 @@ public class MavenPropertyPsiReference extends MavenPsiReference { public LookupElement fun(XmlTag xmlTag) { return createLookupElement(xmlTag, xmlTag.getName()); } - })) ; + })); } private void collectSystemEnvProperties(String propertiesFileName, String prefix, List result) { @@ -276,7 +287,7 @@ public class MavenPropertyPsiReference extends MavenPsiReference { VirtualFile file = MavenSchemaProvider.getSchemaFile(schema); PsiFile psiFile = PsiManager.getInstance(myProject).findFile(file); if (!(psiFile instanceof XmlFile)) return null; - + XmlFile xmlFile = (XmlFile)psiFile; XmlDocument document = xmlFile.getDocument(); XmlNSDescriptor desc = (XmlNSDescriptor)document.getMetaData(); @@ -285,9 +296,9 @@ public class MavenPropertyPsiReference extends MavenPsiReference { } private static T doProcessSchema(XmlElementDescriptor[] descriptors, - String prefix, - SchemaProcessor processor, - Set recursionGuard) { + String prefix, + SchemaProcessor processor, + Set recursionGuard) { for (XmlElementDescriptor each : descriptors) { if (isCollection(each)) continue; diff --git a/plugins/maven/src/test/java/org/jetbrains/idea/maven/dom/MavenPropertyCompletionAndResolutionTest.java b/plugins/maven/src/test/java/org/jetbrains/idea/maven/dom/MavenPropertyCompletionAndResolutionTest.java index edaed390f0c9..fdb8a71f9153 100644 --- a/plugins/maven/src/test/java/org/jetbrains/idea/maven/dom/MavenPropertyCompletionAndResolutionTest.java +++ b/plugins/maven/src/test/java/org/jetbrains/idea/maven/dom/MavenPropertyCompletionAndResolutionTest.java @@ -91,6 +91,24 @@ public class MavenPropertyCompletionAndResolutionTest extends MavenDomTestCase { "${pom.basedir}"); assertResolved(myProjectPom, baseDir); + + createProjectPom("testproject" + + "1" + + + "${baseUri}"); + + assertResolved(myProjectPom, baseDir); + } + + public void testBuiltInTimestampProperty() throws Exception { + createProjectPom("testproject" + + "1" + + + "${maven.build.timestamp}"); + + assertResolved(myProjectPom, findTag("project.name")); } public void testResolutionWithSeveralProperties() throws Exception { @@ -748,7 +766,8 @@ public class MavenPropertyCompletionAndResolutionTest extends MavenDomTestCase { "parentPomProfilesProp", "parentProfilesXmlProp"); assertContain(variants, "artifactId", "project.artifactId", "pom.artifactId"); - assertContain(variants, "basedir", "project.basedir", "pom.basedir"); + assertContain(variants, "basedir", "project.basedir", "pom.basedir", "baseUri", "project.baseUri", "pom.basedir"); + assertContain(variants, "maven.build.timestamp"); assertContain(variants, "settingsXmlProp"); assertContain(variants, "settings.localRepository"); assertContain(variants, "user.home", "env." + getEnvVar()); From 9d3d9620305506582a3129343501afd3dceea62b Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Mon, 15 Mar 2010 21:32:11 +0300 Subject: [PATCH 82/93] IDEA-52910 Autoinsert pairing brace when typing ${ in tml file --- .../testFramework/EditorTestUtil.java | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 platform/testFramework/src/com/intellij/testFramework/EditorTestUtil.java diff --git a/platform/testFramework/src/com/intellij/testFramework/EditorTestUtil.java b/platform/testFramework/src/com/intellij/testFramework/EditorTestUtil.java new file mode 100644 index 000000000000..64d6170f3ec8 --- /dev/null +++ b/platform/testFramework/src/com/intellij/testFramework/EditorTestUtil.java @@ -0,0 +1,52 @@ +/* + * Copyright 2000-2010 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.testFramework; + +import com.intellij.ide.DataManager; +import com.intellij.openapi.actionSystem.IdeActions; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.actionSystem.EditorActionHandler; +import com.intellij.openapi.editor.actionSystem.EditorActionManager; +import com.intellij.openapi.editor.actionSystem.TypedAction; + +/** + * User: Maxim.Mossienko + * Date: 15.03.2010 + * Time: 21:00:49 + */ +public class EditorTestUtil { + public static final char BACKSPACE_FAKE_CHAR = '\uFFFF'; + public static final char SMART_ENTER_FAKE_CHAR = '\uFFFE'; + + public static void performTypingAction(Editor editor, char c) { + EditorActionManager actionManager = EditorActionManager.getInstance(); + if (c == BACKSPACE_FAKE_CHAR) { + EditorActionHandler actionHandler = actionManager.getActionHandler(IdeActions.ACTION_EDITOR_BACKSPACE); + actionHandler.execute(editor, DataManager.getInstance().getDataContext()); + } else if (c == SMART_ENTER_FAKE_CHAR) { + EditorActionHandler actionHandler = actionManager.getActionHandler(IdeActions.ACTION_EDITOR_COMPLETE_STATEMENT); + actionHandler.execute(editor, DataManager.getInstance().getDataContext()); + } + else if (c == '\n') { + EditorActionHandler actionHandler = actionManager.getActionHandler(IdeActions.ACTION_EDITOR_ENTER); + actionHandler.execute(editor, DataManager.getInstance().getDataContext()); + } + else { + TypedAction action = actionManager.getTypedAction(); + action.actionPerformed(editor, c, DataManager.getInstance().getDataContext()); + } + } +} From caeee64cdf61c839eaff981fce03cedb99fbf22b Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Mon, 15 Mar 2010 22:02:21 +0300 Subject: [PATCH 83/93] fixing test --- .../impl/analysis/HighlightVisitorImpl.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java index 1ab725bc535b..6a021df38048 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java @@ -69,6 +69,17 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh private final Map> mySingleImportedClasses = new THashMap>(); private final Map> mySingleImportedFields = new THashMap>(); private PsiFile myFile; + private final PsiElementVisitor REGISTER_REFERENCES_VISITOR = new PsiRecursiveElementWalkingVisitor() { + @Override public void visitElement(PsiElement element) { + super.visitElement(element); + for (PsiReference reference : element.getReferences()) { + PsiElement resolved = reference.resolve(); + if (resolved instanceof PsiNamedElement) { + myRefCountHolder.registerLocallyReferenced((PsiNamedElement)resolved); + } + } + } + }; @SuppressWarnings({"UnusedDeclaration"}) //in plugin.xml public HighlightVisitorImpl(Project project) { @@ -111,6 +122,14 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh } }, false); } + + private void registerReferencesFromInjectedFragments(final PsiElement element) { + InjectedLanguageUtil.enumerate(element, myFile, new PsiLanguageInjectionHost.InjectedPsiVisitor() { + public void visit(@NotNull final PsiFile injectedPsi, @NotNull final List places) { + injectedPsi.accept(REGISTER_REFERENCES_VISITOR); + } + }, false); + } public boolean analyze(final Runnable action, final boolean updateWholeFile, final PsiFile file) { myFile = file; @@ -269,6 +288,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh @Override public void visitComment(PsiComment comment) { super.visitComment(comment); if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkUnclosedComment(comment)); + if (!myHolder.hasErrorResults()) registerReferencesFromInjectedFragments(comment); } @Override public void visitContinueStatement(PsiContinueStatement statement) { @@ -499,6 +519,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh super.visitLiteralExpression(expression); if (myHolder.hasErrorResults()) return; myHolder.add(HighlightUtil.checkLiteralExpressionParsingError(expression)); + if (!myHolder.hasErrorResults()) registerReferencesFromInjectedFragments(expression); } @Override public void visitMethod(PsiMethod method) { From 87ff325a5a1864de8132abb54893794253e2aeef Mon Sep 17 00:00:00 2001 From: Maxim Shafirov Date: Mon, 15 Mar 2010 22:14:41 +0300 Subject: [PATCH 84/93] WI-1382: Comments, that visually decrease indent level shall not confuse indent guides, i.e. comments shall be ignored. --- .../codeInsight/daemon/impl/IndentsPass.java | 38 +++++++++++++++---- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/IndentsPass.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/IndentsPass.java index 221f2dd4e26b..97d77efcabd6 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/IndentsPass.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/IndentsPass.java @@ -21,10 +21,12 @@ package com.intellij.codeInsight.daemon.impl; import com.intellij.codeHighlighting.TextEditorHighlightingPass; import com.intellij.codeInsight.highlighting.BraceMatchingUtil; +import com.intellij.lang.LanguageParserDefinitions; import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.colors.EditorColors; import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.editor.ex.EditorEx; +import com.intellij.openapi.editor.highlighter.EditorHighlighter; import com.intellij.openapi.editor.highlighter.HighlighterIterator; import com.intellij.openapi.editor.markup.CustomHighlighterRenderer; import com.intellij.openapi.editor.markup.HighlighterTargetArea; @@ -36,6 +38,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiFile; +import com.intellij.psi.tree.TokenSet; import com.intellij.util.containers.IntStack; import com.intellij.util.text.CharArrayUtil; import org.jetbrains.annotations.NotNull; @@ -182,7 +185,7 @@ public class IndentsPass extends TextEditorHighlightingPass implements DumbAware private List buildDescriptors() { if (!myEditor.getSettings().isIndentGuidesShown()) return Collections.emptyList(); - int[] lineIndents = calcIndents(myDocument); + int[] lineIndents = calcIndents(); List descriptors = new ArrayList(); @@ -219,17 +222,31 @@ public class IndentsPass extends TextEditorHighlightingPass implements DumbAware return descriptors; } - private int[] calcIndents(Document doc) { + private int[] calcIndents() { + final Document doc = myDocument; CharSequence chars = doc.getCharsSequence(); int[] lineIndents = new int[doc.getLineCount()]; + TokenSet comments = LanguageParserDefinitions.INSTANCE.forLanguage(myFile.getLanguage()).getCommentTokens(); + int prevColumn = -1; + final EditorHighlighter highlighter = myEditor.getHighlighter(); for (int line = 0; line < lineIndents.length; line++) { - int lineStart = myDocument.getLineStartOffset(line); - int lineEnd = myDocument.getLineEndOffset(line); + int lineStart = doc.getLineStartOffset(line); + int lineEnd = doc.getLineEndOffset(line); int nonWhitespaceOffset = CharArrayUtil.shiftForward(chars, lineStart, " \t"); if (nonWhitespaceOffset < lineEnd) { - lineIndents[line] = myEditor.calcColumnNumber(nonWhitespaceOffset, line); + final int column = myEditor.calcColumnNumber(nonWhitespaceOffset, line); + if (column < prevColumn) { + final HighlighterIterator it = highlighter.createIterator(nonWhitespaceOffset); + if (comments.contains(it.getTokenType())) { + lineIndents[line] = -1; + continue; + } + } + + lineIndents[line] = column; + prevColumn = column; } else { lineIndents[line] = -1; @@ -243,13 +260,17 @@ public class IndentsPass extends TextEditorHighlightingPass implements DumbAware } else { int startLine = line; - for (; line < lineIndents.length && lineIndents[line] == -1; line++); + while (line < lineIndents.length && lineIndents[line] == -1) { + //noinspection AssignmentToForLoopParameter + line++; + } + int bottomIndent = line < lineIndents.length ? lineIndents[line] : topIndent; int indent = Math.min(topIndent, bottomIndent); if (bottomIndent < topIndent) { - int nonWhitespaceOffset = CharArrayUtil.shiftForward(chars, myDocument.getLineStartOffset(line), " \t"); - HighlighterIterator iterator = myEditor.getHighlighter().createIterator(nonWhitespaceOffset); + int nonWhitespaceOffset = CharArrayUtil.shiftForward(chars, doc.getLineStartOffset(line), " \t"); + HighlighterIterator iterator = highlighter.createIterator(nonWhitespaceOffset); if (BraceMatchingUtil.isRBraceToken(iterator, chars, myFile.getFileType())) { indent = topIndent; } @@ -260,6 +281,7 @@ public class IndentsPass extends TextEditorHighlightingPass implements DumbAware lineIndents[blankLine] = Math.min(topIndent, indent); } + //noinspection AssignmentToForLoopParameter line--; // will be incremented back at the end of the loop; } } From b871a7813d44aa48fc35d4394208ae90c31e83a8 Mon Sep 17 00:00:00 2001 From: Maxim Shafirov Date: Mon, 15 Mar 2010 22:27:18 +0300 Subject: [PATCH 85/93] Indent markers shall not start with the blank line. --- .../codeInsight/daemon/impl/IndentsPass.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/IndentsPass.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/IndentsPass.java index 97d77efcabd6..872bebc23d45 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/IndentsPass.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/IndentsPass.java @@ -194,13 +194,14 @@ public class IndentsPass extends TextEditorHighlightingPass implements DumbAware lines.push(0); indents.push(0); + final CharSequence chars = myDocument.getCharsSequence(); for (int line = 1; line < lineIndents.length; line++) { int curIndent = lineIndents[line]; while (!indents.empty() && curIndent <= indents.peek()) { final int level = indents.pop(); int startLine = lines.pop(); - descriptors.add(new IndentGuideDescriptor(level, startLine, line)); + descriptors.add(createDescriptor(level, startLine, line, chars)); } int prevLine = line - 1; @@ -216,12 +217,22 @@ public class IndentsPass extends TextEditorHighlightingPass implements DumbAware final int level = indents.pop(); if (level > 0) { int startLine = lines.pop(); - descriptors.add(new IndentGuideDescriptor(level, startLine, myDocument.getLineCount())); + descriptors.add(createDescriptor(level, startLine, myDocument.getLineCount(), chars)); } } return descriptors; } + private IndentGuideDescriptor createDescriptor(int level, int startLine, int endLine, CharSequence chars) { + while (startLine > 0 && isBlankLine(startLine, chars)) startLine--; + return new IndentGuideDescriptor(level, startLine, endLine); + } + + private boolean isBlankLine(int line, CharSequence chars) { + int startOffset = myDocument.getLineStartOffset(line); + return CharArrayUtil.shiftForward(chars, startOffset, " \t") >= myDocument.getLineEndOffset(line); + } + private int[] calcIndents() { final Document doc = myDocument; CharSequence chars = doc.getCharsSequence(); From 7e0a2805320146cef66130ccdf92e33eed20ee14 Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Mon, 15 Mar 2010 22:56:17 +0300 Subject: [PATCH 86/93] proper semaphore usage: fixed race condition described in IDEA-52853 --- .../execution/ui/RunContentManagerImpl.java | 67 ++++++++++--------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/execution/ui/RunContentManagerImpl.java b/platform/lang-impl/src/com/intellij/execution/ui/RunContentManagerImpl.java index 232c64880345..90b65bfd1e26 100644 --- a/platform/lang-impl/src/com/intellij/execution/ui/RunContentManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/ui/RunContentManagerImpl.java @@ -578,50 +578,51 @@ public class RunContentManagerImpl implements RunContentManager, Disposable { } private void waitForProcess(final RunContentDescriptor descriptor) { - String progressTitle = ExecutionBundle.message("terminating.process.progress.title", descriptor.getDisplayName()); - ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() { - private ProgressIndicator myProgressIndicator; - private final Semaphore mySemaphore = new Semaphore(); + public void run() { + final Semaphore semaphore = new Semaphore(); + semaphore.down(); - private final Runnable myWaitThread = new Runnable() { - public void run() { - descriptor.getProcessHandler().waitFor(); - mySemaphore.up(); - } - }; - - private final Runnable myCancelListener = new Runnable() { - public void run() { - while(true) { - if(myProgressIndicator != null && (myProgressIndicator.isCanceled() || !myProgressIndicator.isRunning())) { - mySemaphore.up(); - break; - } + ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { + public void run() { + final ProcessHandler processHandler = descriptor.getProcessHandler(); try { - synchronized (this) { - wait(2000); + if (processHandler != null) { + processHandler.waitFor(); } } - catch (InterruptedException ignore) { + finally { + semaphore.up(); } } - } - }; + }); + + final ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator(); - public void run() { - myProgressIndicator = ProgressManager.getInstance().getProgressIndicator(); - if (myProgressIndicator != null) { - myProgressIndicator.setText(ExecutionBundle.message("waiting.for.vm.detach.progress.text")); + if (progressIndicator != null) { + progressIndicator.setText(ExecutionBundle.message("waiting.for.vm.detach.progress.text")); + ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { + public void run() { + while(true) { + if(progressIndicator.isCanceled() || !progressIndicator.isRunning()) { + semaphore.up(); + break; + } + try { + synchronized (this) { + wait(2000L); + } + } + catch (InterruptedException ignore) { + } + } + } + }); } - ApplicationManager.getApplication().executeOnPooledThread(myWaitThread); - ApplicationManager.getApplication().executeOnPooledThread(myCancelListener); - - mySemaphore.down(); - mySemaphore.waitFor(); + semaphore.waitFor(); } - }, progressTitle, true, myProject); + }, ExecutionBundle.message("terminating.process.progress.title", descriptor.getDisplayName()), true, myProject); } public interface MyRunContentListener extends EventListener { From 4da7d7d94d990e15955ba14398d908997434816e Mon Sep 17 00:00:00 2001 From: Ekaterina Shliakhovetskaja Date: Tue, 16 Mar 2010 04:13:45 +0300 Subject: [PATCH 87/93] Spellchecker plugin updates: module settings: remove outdated libraries and dependecies, add test-level dependency to testFranwork-java; dictionaries: add compressed dictionary support settings: avoid useless dictionary reloading after settings modification test splitter: minor improvements --- .../spellchecker/lib-src/jazzy-0.5.2.src.zip | Bin 235936 -> 0 bytes plugins/spellchecker/lib/jazzy-core.jar | Bin 63040 -> 0 bytes .../spellchecker/lib/patricia-trie-0.1.jar | Bin 43800 -> 0 bytes plugins/spellchecker/spellchecker.iml | 1 + .../spellchecker/BaseSuggestionProvider.java | 4 +- .../BundledDictionaryProvider.java | 3 - .../DefaultBundledDictionariesProvider.java | 4 +- .../com/intellij/spellchecker/FileLoader.java | 13 +- .../spellchecker/SpellCheckerManager.java | 85 ++++-- .../intellij/spellchecker/StreamLoader.java | 12 +- .../spellchecker/compress/Alphabet.java | 72 ++++++ .../compress/CompressedDictionary.java | 242 ++++++++++++++++++ .../spellchecker/compress/Compressor.java | 128 +++++++++ .../spellchecker/compress/Encoder.java | 61 +++++ .../compress/EncodingException.java | 10 + .../spellchecker/compress/UnitBitSet.java | 120 +++++++++ .../dictionary/AggregatedDictionary.java | 31 +-- .../spellchecker/dictionary/Dictionary.java | 23 +- .../dictionary/EditableDictionary.java | 39 +++ .../EditableDictionaryLoader.java} | 9 +- .../spellchecker/dictionary/Loader.java | 2 + .../dictionary/ProjectDictionary.java | 52 ++-- .../dictionary/UserDictionary.java | 43 ++-- .../spellchecker/engine/BaseSpellChecker.java | 192 +++++++++++--- .../engine/SpellCheckerEngine.java | 8 +- .../spellchecker/engine/Transformation.java | 5 +- .../inspections/SpellCheckingInspection.java | 77 +++--- .../inspections/TextSplitter.java | 33 ++- .../spellchecker/quickfixes/ChangeTo.java | 4 +- .../spellchecker/quickfixes/RenameTo.java | 4 +- .../settings/SpellCheckerSettingsPane.java | 16 +- .../state/CachedDictionaryState.java | 10 +- .../spellchecker/state/DictionaryState.java | 10 +- .../state/ProjectDictionaryState.java | 8 +- .../spellchecker/state/StateLoader.java | 52 ++-- .../tokenizer/CommentTokenizer.java | 5 - .../tokenizer/HtmlSpellcheckingStrategy.java | 5 - .../spellchecker/tokenizer/TextTokenizer.java | 6 - .../spellchecker/tokenizer/Token.java | 6 +- .../spellchecker/tokenizer/Tokenizer.java | 5 - .../tokenizer/XmlAttributeTokenizer.java | 5 - .../tokenizer/XmlTextTokenizer.java | 5 - .../intellij/spellchecker/util/Strings.java | 3 - .../spellchecker/compress/CompressorTest.java | 68 +++++ .../spellchecker/compress/DictionaryTest.java | 150 +++++++++++ .../compress/EncodeAndCompressTest.java | 23 ++ .../spellchecker/compress/EncoderTest.java | 114 +++++++++ .../compress/UnitBitSetTests.java | 62 +++++ .../SpellcheckerInspectionTestCase.java | 1 + .../spellchecker/inspector/SplitterTest.java | 210 ++++++++++----- .../spellchecker/inspector/contents.txt | Bin 0 -> 1002537 bytes .../intellij/spellchecker/inspector/crt.txt | Bin 0 -> 7655 bytes 52 files changed, 1670 insertions(+), 371 deletions(-) delete mode 100644 plugins/spellchecker/lib-src/jazzy-0.5.2.src.zip delete mode 100644 plugins/spellchecker/lib/jazzy-core.jar delete mode 100644 plugins/spellchecker/lib/patricia-trie-0.1.jar create mode 100644 plugins/spellchecker/src/com/intellij/spellchecker/compress/Alphabet.java create mode 100644 plugins/spellchecker/src/com/intellij/spellchecker/compress/CompressedDictionary.java create mode 100644 plugins/spellchecker/src/com/intellij/spellchecker/compress/Compressor.java create mode 100644 plugins/spellchecker/src/com/intellij/spellchecker/compress/Encoder.java create mode 100644 plugins/spellchecker/src/com/intellij/spellchecker/compress/EncodingException.java create mode 100644 plugins/spellchecker/src/com/intellij/spellchecker/compress/UnitBitSet.java create mode 100644 plugins/spellchecker/src/com/intellij/spellchecker/dictionary/EditableDictionary.java rename plugins/spellchecker/src/com/intellij/spellchecker/{trie/Action.java => dictionary/EditableDictionaryLoader.java} (75%) create mode 100644 plugins/spellchecker/testSrc/com/intellij/spellchecker/compress/CompressorTest.java create mode 100644 plugins/spellchecker/testSrc/com/intellij/spellchecker/compress/DictionaryTest.java create mode 100644 plugins/spellchecker/testSrc/com/intellij/spellchecker/compress/EncodeAndCompressTest.java create mode 100644 plugins/spellchecker/testSrc/com/intellij/spellchecker/compress/EncoderTest.java create mode 100644 plugins/spellchecker/testSrc/com/intellij/spellchecker/compress/UnitBitSetTests.java create mode 100644 plugins/spellchecker/testSrc/com/intellij/spellchecker/inspector/contents.txt create mode 100644 plugins/spellchecker/testSrc/com/intellij/spellchecker/inspector/crt.txt diff --git a/plugins/spellchecker/lib-src/jazzy-0.5.2.src.zip b/plugins/spellchecker/lib-src/jazzy-0.5.2.src.zip deleted file mode 100644 index c41b5557959646a2103d3f5a39ce7e57777a77d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 235936 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�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"r&$m$ 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≫ldHS%}pZ`eknkP=1UFz5JbLQxK(hSo#ZwRQP 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 diff --git a/plugins/spellchecker/lib/jazzy-core.jar b/plugins/spellchecker/lib/jazzy-core.jar deleted file mode 100644 index 50d313541ea96e52344f51576f2a5fa1bebf405c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 63040 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
    & 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

    7(g=qDCfisppI#$m6(qis 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 diff --git a/plugins/spellchecker/spellchecker.iml b/plugins/spellchecker/spellchecker.iml index d81c1984675f..edcc0cf37fa4 100644 --- a/plugins/spellchecker/spellchecker.iml +++ b/plugins/spellchecker/spellchecker.iml @@ -14,6 +14,7 @@ + diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/BaseSuggestionProvider.java b/plugins/spellchecker/src/com/intellij/spellchecker/BaseSuggestionProvider.java index 20657aa6be71..5e8a549406b8 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/BaseSuggestionProvider.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/BaseSuggestionProvider.java @@ -33,7 +33,7 @@ public class BaseSuggestionProvider implements SuggestionProvider { @NotNull public List getSuggestions(@NotNull String text) { - + String[] words = NameUtil.nameToWords(text); int index = 0; @@ -54,7 +54,7 @@ public class BaseSuggestionProvider implements SuggestionProvider { index = end; } - String[] all = null; + String[] all; int[] counter = new int[i]; int size = 1; for (int j = 0; j < i; j++) { diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/BundledDictionaryProvider.java b/plugins/spellchecker/src/com/intellij/spellchecker/BundledDictionaryProvider.java index 236ae4a39c9b..3c6ed60b35e2 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/BundledDictionaryProvider.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/BundledDictionaryProvider.java @@ -17,9 +17,6 @@ package com.intellij.spellchecker; import com.intellij.openapi.extensions.ExtensionPointName; -/** - * @author oleg - */ public interface BundledDictionaryProvider { ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.spellchecker.bundledDictionaryProvider"); diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/DefaultBundledDictionariesProvider.java b/plugins/spellchecker/src/com/intellij/spellchecker/DefaultBundledDictionariesProvider.java index 482f3db7bd1d..fc8e77f90ce6 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/DefaultBundledDictionariesProvider.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/DefaultBundledDictionariesProvider.java @@ -15,9 +15,7 @@ */ package com.intellij.spellchecker; -/** - * @author oleg - */ + public class DefaultBundledDictionariesProvider implements BundledDictionaryProvider { public String[] getBundledDictionaries() { return new String[]{"english.dic", "jetbrains.dic"}; diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/FileLoader.java b/plugins/spellchecker/src/com/intellij/spellchecker/FileLoader.java index 2659bb5843d5..cd57fb467dab 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/FileLoader.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/FileLoader.java @@ -25,14 +25,21 @@ import java.io.FileInputStream; import java.io.IOException; +@SuppressWarnings({"IOResourceOpenedButNotSafelyClosed"}) public class FileLoader implements Loader { private static final Logger LOG = Logger.getInstance("#com.intellij.spellchecker.FileLoader"); - private String url; + private final String url; + private final String name; - public FileLoader(String url) { + public FileLoader(String url, String name) { this.url = url; + this.name = name; + } + + public String getName() { + return name; } public void load(@NotNull Consumer consumer) { @@ -40,7 +47,7 @@ public class FileLoader implements Loader { FileInputStream stream = null; try { stream = new FileInputStream(file); - StreamLoader loader = new StreamLoader(stream); + StreamLoader loader = new StreamLoader(stream, file.getName()); loader.load(consumer); } catch (Exception e) { diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/SpellCheckerManager.java b/plugins/spellchecker/src/com/intellij/spellchecker/SpellCheckerManager.java index 630135483e52..762863df385d 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/SpellCheckerManager.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/SpellCheckerManager.java @@ -23,7 +23,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; -import com.intellij.spellchecker.dictionary.Dictionary; +import com.intellij.spellchecker.dictionary.EditableDictionary; import com.intellij.spellchecker.dictionary.Loader; import com.intellij.spellchecker.engine.SpellCheckerEngine; import com.intellij.spellchecker.engine.SpellCheckerFactory; @@ -33,7 +33,6 @@ import com.intellij.spellchecker.state.StateLoader; import com.intellij.spellchecker.util.SPFileUtil; import com.intellij.spellchecker.util.Strings; import com.intellij.util.Consumer; -import com.sun.net.ssl.internal.ssl.SSLEngineImpl; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -51,8 +50,7 @@ public class SpellCheckerManager { private SpellCheckerEngine spellChecker; - private Dictionary userDictionary; - + private EditableDictionary userDictionary; @NotNull @@ -67,26 +65,73 @@ public class SpellCheckerManager { public SpellCheckerManager(Project project, SpellCheckerSettings settings) { this.project = project; this.settings = settings; - reloadConfiguration(); + fullConfigurationReload(); + } + public void fullConfigurationReload() { + spellChecker = SpellCheckerFactory.create(); + fillEngineDictionary(); + } + + + public void updateBundledDictionaries() { + for (BundledDictionaryProvider provider : Extensions.getExtensions(BundledDictionaryProvider.EP_NAME)) { + for (String dictionary : provider.getBundledDictionaries()) { + boolean dictionaryShouldBeLoad = this.settings == null || !this.settings.getBundledDisabledDictionariesPaths().contains(dictionary); + boolean dictionaryIsLoad = spellChecker.isDictionaryLoad(dictionary); + if (dictionaryIsLoad && !dictionaryShouldBeLoad) { + spellChecker.removeDictionary(dictionary); + } + else if (!dictionaryIsLoad && dictionaryShouldBeLoad) { + final Class loaderClass = provider.getClass(); + final InputStream stream = loaderClass.getResourceAsStream(dictionary); + if (stream != null) { + spellChecker.loadDictionary(new StreamLoader(stream, dictionary)); + } + else { + LOG.warn("Couldn't load dictionary '" + dictionary + "' with loader '" + loaderClass + "'"); + } + } + } + } + if (this.settings != null && this.settings.getDictionaryFoldersPaths() != null) { + final Set disabledDictionaries = settings.getDisabledDictionariesPaths(); + for (String folder : this.settings.getDictionaryFoldersPaths()) { + SPFileUtil.processFilesRecursively(folder, new Consumer() { + public void consume(final String s) { + boolean dictionaryShouldBeLoad =!disabledDictionaries.contains(s); + boolean dictionaryIsLoad = spellChecker.isDictionaryLoad(s); + if (dictionaryIsLoad && !dictionaryShouldBeLoad) { + spellChecker.removeDictionary(s); + } + else if (!dictionaryIsLoad && dictionaryShouldBeLoad) { + spellChecker.loadDictionary(new FileLoader(s, s)); + } + + } + }); + + } + } + restartInspections(); } public Project getProject() { return project; } - public Dictionary getUserDictionary() { + public EditableDictionary getUserDictionary() { return userDictionary; } - public void reloadConfiguration() { - spellChecker = SpellCheckerFactory.create(); - fillEngineDictionary(); - } - private void fillEngineDictionary() { spellChecker.reset(); final StateLoader stateLoader = new StateLoader(project); + stateLoader.load(new Consumer() { + public void consume(String s) { + //do nothing - in this loader we don't worry about word list itself - the whole dictionary will be restored + } + }); final List loaders = new ArrayList(); // Load bundled dictionaries from corresponding jars for (BundledDictionaryProvider provider : Extensions.getExtensions(BundledDictionaryProvider.EP_NAME)) { @@ -94,10 +139,11 @@ public class SpellCheckerManager { if (this.settings == null || !this.settings.getBundledDisabledDictionariesPaths().contains(dictionary)) { final Class loaderClass = provider.getClass(); final InputStream stream = loaderClass.getResourceAsStream(dictionary); - if (stream != null){ - loaders.add(new StreamLoader(stream)); - } else { - LOG.warn("Couldn't load dictionary '" + dictionary + "' for loader '" + loaderClass + "'"); + if (stream != null) { + loaders.add(new StreamLoader(stream, dictionary)); + } + else { + LOG.warn("Couldn't load dictionary '" + dictionary + "' with loader '" + loaderClass + "'"); } } } @@ -108,7 +154,7 @@ public class SpellCheckerManager { SPFileUtil.processFilesRecursively(folder, new Consumer() { public void consume(final String s) { if (!disabledDictionaries.contains(s)) { - loaders.add(new FileLoader(s)); + loaders.add(new FileLoader(s, s)); } } }); @@ -120,6 +166,7 @@ public class SpellCheckerManager { spellChecker.loadDictionary(loader); } userDictionary = stateLoader.getDictionary(); + } @@ -131,17 +178,16 @@ public class SpellCheckerManager { final String transformed = spellChecker.getTransformation().transform(word); if (transformed != null) { userDictionary.addToDictionary(transformed); - spellChecker.addToDictionary(transformed); } } - public void update(@Nullable Collection words, SpellCheckerSettings allDictionaries) { + public void updateUserDictionary(@Nullable Collection words) { userDictionary.replaceAll(words); - reloadConfiguration(); restartInspections(); } + @NotNull public List getBundledDictionaries() { final ArrayList dictionaries = new ArrayList(); @@ -162,7 +208,6 @@ public class SpellCheckerManager { } - @NotNull protected List getRawSuggestions(@NotNull String word) { if (!spellChecker.isCorrect(word)) { diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/StreamLoader.java b/plugins/spellchecker/src/com/intellij/spellchecker/StreamLoader.java index 570967259cbb..72e760aa4ff9 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/StreamLoader.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/StreamLoader.java @@ -25,18 +25,26 @@ import java.io.*; public class StreamLoader implements Loader { private static final Logger LOG = Logger.getInstance("#com.intellij.spellchecker.StreamLoader"); + private static final String ENCODING = "UTF-8"; private InputStream stream; + private String name; - public StreamLoader(InputStream stream) { + public StreamLoader(InputStream stream, String name) { this.stream = stream; + this.name=name; } + public String getName() { + return name; + } public void load(@NotNull Consumer consumer) { DataInputStream in = new DataInputStream(stream); - BufferedReader br = new BufferedReader(new InputStreamReader(in)); + BufferedReader br = null; + try { + br = new BufferedReader(new InputStreamReader(in, ENCODING)); String strLine; while ((strLine = br.readLine()) != null) { consumer.consume(strLine); diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/compress/Alphabet.java b/plugins/spellchecker/src/com/intellij/spellchecker/compress/Alphabet.java new file mode 100644 index 000000000000..65c9aaf4e43a --- /dev/null +++ b/plugins/spellchecker/src/com/intellij/spellchecker/compress/Alphabet.java @@ -0,0 +1,72 @@ +package com.intellij.spellchecker.compress; + +import org.jetbrains.annotations.NotNull; + +public final class Alphabet { + + private final Character[] letters; + private int lastIndexUsed = 0; + private int maxIndex = 1023; + + public Character getLetter(int position) { + assert position < maxIndex; + return letters[position]; + } + + /* + @param forceAdd - if set to true - letter will be added to the alphabet if not present yet + @return index of the letter or -1 if letter was not found and could not be added (due to forceAdd property value) + */ + + public int getIndex(@NotNull Character letter, boolean forceAdd) { + return getNextIndex(0, letter, forceAdd); + } + + /* + @param forceAdd - if set to true - letter will be added to the alphabet if not present yet + @return index of the letter or -1 if letter was not found and could not be added (due to forceAdd property value) + */ + public int getNextIndex(int startFrom, @NotNull Character letter, boolean forceAdd) { + for (int i = startFrom; i <= lastIndexUsed; i++) { + if (letters[i] != null && letters[i].equals(letter)) { + return i; + } + } + if (!forceAdd) { + return -1; + } + return add(letter); + } + + public int getLastIndexUsed() { + return lastIndexUsed; + } + + public int getMaxIndex() { + return maxIndex; + } + + public int add(@NotNull Character c) { + lastIndexUsed++; + letters[lastIndexUsed] = c; + return lastIndexUsed; + } + + + Alphabet() { + letters = new Character[maxIndex]; + } + + Alphabet(int maxIndex) { + this.maxIndex = maxIndex; + letters = new Character[maxIndex]; + } + + public static Alphabet create(@NotNull CharSequence alphabet) { + Alphabet result = new Alphabet(alphabet.length() + 1); + for (int i = 0; i < alphabet.length(); i++) { + result.add(alphabet.charAt(i)); + } + return result; + } +} diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/compress/CompressedDictionary.java b/plugins/spellchecker/src/com/intellij/spellchecker/compress/CompressedDictionary.java new file mode 100644 index 000000000000..65fe3c1c9b22 --- /dev/null +++ b/plugins/spellchecker/src/com/intellij/spellchecker/compress/CompressedDictionary.java @@ -0,0 +1,242 @@ +/* + * Copyright 2000-2010 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.spellchecker.compress; + +import com.intellij.spellchecker.dictionary.Dictionary; +import com.intellij.spellchecker.dictionary.Loader; +import com.intellij.spellchecker.engine.Transformation; +import com.intellij.util.Consumer; +import gnu.trove.THashMap; +import org.jetbrains.annotations.NotNull; + +import java.util.*; + +public final class CompressedDictionary implements Dictionary { + + private final Alphabet alphabet; + private int wordsCount; + private byte[][] words; + private int[] lengths; + + private Encoder encoder; + private String name; + private Compressor compressor; + + private final Map> rawData = new THashMap>(); + private static final Comparator COMPARATOR = new Comparator() { + public int compare(byte[] o1, byte[] o2) { + return compareArrays(o1, o2); + } + }; + + CompressedDictionary(@NotNull Alphabet alphabet, @NotNull Compressor compressor, @NotNull Encoder encoder, @NotNull String name) { + this.alphabet = alphabet; + this.compressor = compressor; + this.encoder = encoder; + this.name = name; + } + + void addToDictionary(byte[] word) { + SortedSet set = rawData.get(word.length); + if (set == null) { + set = createSet(); + rawData.put(word.length, set); + } + set.add(word); + wordsCount++; + } + + void pack() { + if (rawData == null) { + return; + } + lengths = new int[rawData.size()]; + this.words = new byte[rawData.size()][]; + int row = 0; + for (Map.Entry> entry : rawData.entrySet()) { + final Integer l = entry.getKey(); + lengths[row] = l; + this.words[row] = new byte[entry.getValue().size() * l]; + int k = 0; + for (byte[] bytes : entry.getValue()) { + for (byte aByte : bytes) { + this.words[row][k++] = aByte; + } + } + row++; + } + rawData.clear(); + } + + private static SortedSet createSet() { + return new TreeSet(COMPARATOR); + + } + + public List getWords(Character first, int minLength, int maxLength) { + int index = alphabet.getIndex(first, false); + List result = new ArrayList(); + if (index == -1) { + return result; + } + int i = 0; + for (byte[] data : words) { + int length = lengths[i]; + for (int x = 0; x < data.length; x += length) { + byte[] toTest = new byte[length]; + System.arraycopy(data, x, toTest, 0, length); + if (toTest[1] != index || toTest[0] > maxLength || toTest[0] < minLength) { + continue; + } + UnitBitSet set = compressor.decompress(toTest); + result.add(encoder.decode(set)); + } + i++; + } + return result; + } + + public List getWords(Character first) { + return getWords(first, 0, Integer.MAX_VALUE); + } + + public String getName() { + return name; + } + + public boolean contains(String word) { + if (word == null) { + return false; + } + try { + UnitBitSet bs = encoder.encode(word, false); + byte[] compressed = compressor.compress(bs); + int index = -1; + for (int i = 0; i < lengths.length; i++) { + if (lengths[i] == compressed.length) { + index = i; + break; + } + } + if (index == -1) { + return false; + } + return contains(compressed, words[index]); + } + catch (EncodingException ignored) { + return false; + } + + } + + public boolean isEmpty() { + return wordsCount <= 0; + } + + public void traverse(Consumer action) { + throw new UnsupportedOperationException(); + } + + public Set getWords() { + throw new UnsupportedOperationException(); + } + + public int getAlphabetLength() { + return alphabet.getLastIndexUsed(); + } + + public int size() { + return wordsCount; + } + + + public String toString() { + final StringBuffer sb = new StringBuffer(); + sb.append("CompressedDictionary"); + sb.append("{wordsCount=").append(wordsCount); + sb.append(", name='").append(name).append('\''); + sb.append('}'); + return sb.toString(); + } + + public static CompressedDictionary create(@NotNull Loader loader, @NotNull final Transformation transform) { + Alphabet alphabet = new Alphabet(); + final Encoder encoder = new Encoder(alphabet); + final Compressor compressor = new Compressor(2); + final CompressedDictionary dictionary = new CompressedDictionary(alphabet, compressor, encoder, loader.getName()); + loader.load(new Consumer() { + public void consume(String s) { + String transformed = transform.transform(s); + if (transformed != null) { + UnitBitSet bs = encoder.encode(transformed, true); + byte[] compressed = compressor.compress(bs); + dictionary.addToDictionary(compressed); + } + } + }); + dictionary.pack(); + return dictionary; + } + + public static int compareArrays(byte[] array1, byte[] array2) { + if (array1.length != array2.length) { + return (array1.length < array2.length ? -1 : 1); + } + //array1.length==array2.length + if (Arrays.equals(array1, array2)) { + return 0; + } + //compare elements values + for (int i = 0; i < array1.length; i++) { + if (array1[i] < array2[i]) { + return -1; + } + else if (array1[i] > array2[i]) { + return 1; + } + } + return 0; + } + + + public static boolean contains(byte[] goal, byte[] data) { + return binarySearchNew(goal, 0, data.length / goal.length, data) >= 0; + } + + public static int binarySearchNew(byte[] goal, int fromIndex, int toIndex, byte[] data) { + int unitLength = goal.length; + int low = fromIndex; + int high = toIndex - 1; + while (low <= high) { + int mid = (low + high) >>> 1; + byte[] toTest = new byte[unitLength]; + System.arraycopy(data, mid * unitLength, toTest, 0, unitLength); + int check = compareArrays(toTest, goal); + if (check == -1) { + low = mid + 1; + } + else if (check == 1) { + high = mid - 1; + } + else { + return mid; + } + } + return -(low + 1); // key not found. + } + + +} diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/compress/Compressor.java b/plugins/spellchecker/src/com/intellij/spellchecker/compress/Compressor.java new file mode 100644 index 000000000000..e8e791121134 --- /dev/null +++ b/plugins/spellchecker/src/com/intellij/spellchecker/compress/Compressor.java @@ -0,0 +1,128 @@ +/* + * Copyright 2000-2010 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.spellchecker.compress; + +import com.intellij.util.Consumer; + +import java.util.ArrayList; +import java.util.List; + +public final class Compressor { + + public static int WORD_LENGTH = 8; + public static byte TRANSITION_WORD = new Double(-Math.pow(2, WORD_LENGTH - 1)).byteValue(); + public final int skipFirst; + + + public Compressor(int skipFirst) { + this.skipFirst = skipFirst; + } + + + public UnitBitSet decompress(byte[] value) { + UnitBitSet bs = new UnitBitSet(WORD_LENGTH - 1, true); + int index = 0; + int bitsPerUnitInOrigin = value[skipFirst]; + + for (int i = skipFirst + 1; i < value.length; i++) { + byte b = value[i]; + if (b > 0) { + //decompress verbose word + bs.setUnitValue(index++, b); + } + else { + int count = (-TRANSITION_WORD + b); + index = index + count + 1; + } + + } + UnitBitSet result = UnitBitSet.create(bs, bitsPerUnitInOrigin); + result.moveRight(skipFirst); + for (int i = 0; i < skipFirst; i++) { + result.setUnitValue(i, value[i]); + } + + return result; + } + + public byte[] compress(UnitBitSet origin) { + if (origin == null) { + return new byte[0]; + } + final List words = new ArrayList(); + final List compressed = new ArrayList(); + + final UnitBitSet copyOfOrigin = UnitBitSet.create(origin, origin.bitsPerUnit); + + for (int i = 0; i < skipFirst; i++) { + compressed.add(Integer.valueOf(copyOfOrigin.getUnitValue(i)).byteValue()); + } + copyOfOrigin.moveLeft(skipFirst); + + compressed.add(Integer.valueOf(copyOfOrigin.bitsPerUnit).byteValue()); + + UnitBitSet bs = UnitBitSet.create(copyOfOrigin, WORD_LENGTH - 1); + bs.iterateParUnits(new Consumer() { + + public void consume(Integer integer) { + if (integer != 0) { + //create verbatim word + words.add(integer.byteValue()); + } + else { + //create transition word + words.add(TRANSITION_WORD); + } + + } + }, 0, true); + + + int count = -1; + for (Byte word : words) { + if (word == TRANSITION_WORD && count == -1) { + count++; + } + else if (word == TRANSITION_WORD && count > -1) { + count++; + } + else if (word != TRANSITION_WORD && count > -1) { + //compress all transition words + while (count > 120) { + compressed.add((byte)(TRANSITION_WORD + 120)); + count -= 121; + } + if (count > -1) { + compressed.add((byte)(TRANSITION_WORD + count)); + } + compressed.add(word); + count = -1; + } + else if (word != TRANSITION_WORD && count == -1) { + compressed.add(word); + count = -1; + } + } + byte[] result = new byte[compressed.size()]; + int i = 0; + for (Byte word : compressed) { + result[i++] = word; + } + return result; + } + + +} diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/compress/Encoder.java b/plugins/spellchecker/src/com/intellij/spellchecker/compress/Encoder.java new file mode 100644 index 000000000000..c2031ff794de --- /dev/null +++ b/plugins/spellchecker/src/com/intellij/spellchecker/compress/Encoder.java @@ -0,0 +1,61 @@ +package com.intellij.spellchecker.compress; + +import com.intellij.spellchecker.compress.Alphabet; +import com.intellij.spellchecker.compress.EncodingException; +import com.intellij.spellchecker.compress.UnitBitSet; +import com.intellij.util.Consumer; +import org.jetbrains.annotations.NotNull; + +import java.text.MessageFormat; + +public final class Encoder { + + private final Alphabet alphabet; + private final int offset = 2; + + public Encoder() { + alphabet = new Alphabet(); + } + + public Encoder(Alphabet alphabet) { + this.alphabet = alphabet; + } + + public Alphabet getAlphabet() { + return alphabet; + } + + public UnitBitSet encode(@NotNull CharSequence letters, boolean force) throws EncodingException { + UnitBitSet bs = new UnitBitSet(alphabet.getMaxIndex(), false); + bs.setUnitValue(0, letters.length()); + bs.setUnitValue(1, alphabet.getIndex(letters.charAt(0), force)); + for (int i = 0; i < letters.length(); i++) { + Character letter = letters.charAt(i); + int index = alphabet.getIndex(letter, force); + bs.setUnitValue(i + offset, index); + } + return bs; + } + + public String decode(@NotNull UnitBitSet bitSet) throws EncodingException { + int wordLength = bitSet.getUnitValue(0); + Character firstLetter = alphabet.getLetter(bitSet.getUnitValue(1)); + final StringBuffer result = new StringBuffer(); + bitSet.iterateParUnits(new Consumer() { + + public void consume(Integer value) { + if (value > 0 && value <= alphabet.getLastIndexUsed()) { + result.append(alphabet.getLetter(value)); + } + } + }, offset, true); + + final String word = result.toString(); + if (word.length() != wordLength || !word.startsWith(String.valueOf(firstLetter))) { + throw new EncodingException( + new MessageFormat("Error during encoding: required length - {0}, starts with {1}, but decoded: {2}") + .format(new Object[]{wordLength, firstLetter, word})); + } + return word; + } +} \ No newline at end of file diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/compress/EncodingException.java b/plugins/spellchecker/src/com/intellij/spellchecker/compress/EncodingException.java new file mode 100644 index 000000000000..a7cd3ddaaaa4 --- /dev/null +++ b/plugins/spellchecker/src/com/intellij/spellchecker/compress/EncodingException.java @@ -0,0 +1,10 @@ +package com.intellij.spellchecker.compress; + +public class EncodingException extends RuntimeException { + + public EncodingException(String message) { + super(message); + } + + +} diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/compress/UnitBitSet.java b/plugins/spellchecker/src/com/intellij/spellchecker/compress/UnitBitSet.java new file mode 100644 index 000000000000..020a5711ca73 --- /dev/null +++ b/plugins/spellchecker/src/com/intellij/spellchecker/compress/UnitBitSet.java @@ -0,0 +1,120 @@ +package com.intellij.spellchecker.compress; + +import com.intellij.util.Consumer; +import org.jetbrains.annotations.NotNull; + +import java.util.BitSet; + +public class UnitBitSet extends BitSet { + + public int bitsPerUnit = 10; + + public UnitBitSet() { + } + + public UnitBitSet(int value, boolean exact) { + if (exact) { + this.bitsPerUnit = value; + } + else { + this.bitsPerUnit = new Double(Math.log(value) / Math.log(2)).intValue() + 1; + } + } + + public void inc() { + boolean t = true; + for (int i = 0; i <= size(); i++) { + if (!get(i) && t) { + set(i); + t = false; + } + else if (get(i) && t) { + set(i, false); + } + } + } + + public int getUnitValue(int number) { + int startIndex = number * bitsPerUnit; + int result = 0; + for (int i = 0; i < bitsPerUnit; i++) { + if (get(startIndex + i)) { + result += Math.pow(2, i); + } + } + return result; + } + + public void setUnitValue(int number, int value) { + if (value > Math.pow(2, bitsPerUnit) - 1) { + throw new IllegalArgumentException(); + } + int startIndex = number * bitsPerUnit; + for (int i = 0; i < bitsPerUnit; i++) { + set(startIndex + i, value % 2 == 1); + value /= 2; + } + } + + public void setBits(int... bits) { + if (bits == null) { + return; + } + for (int bit : bits) { + set(bit); + } + } + + public void moveLeft(int n) { + if (n <= 0) { + return; + } + for (int i = 0; i < size() - n * bitsPerUnit; i++) { + set(i, get(n * bitsPerUnit + i)); + } + for (int i = size() - n * bitsPerUnit; i < size(); i++) { + set(i, false); + } + } + + public void moveRight(int n) { + if (n <= 0) { + return; + } + for (int i = size() - 1; i >= 0; i--) { + set(i + n * bitsPerUnit, get(i)); + } + for (int i = 0; i < n * bitsPerUnit; i++) { + set(i, false); + } + } + + + public void iterateParUnits(@NotNull Consumer consumer, int offset, boolean skipLastZero) { + int units = size() / bitsPerUnit; + for (int i = offset; i <= units; i++) { + consumer.consume(getUnitValue(i)); + if (skipLastZero && nextSetBit(i) == -1) { + break; + } + } + } + + public static UnitBitSet create(int... bits) { + UnitBitSet bs = new UnitBitSet(); + bs.setBits(bits); + return bs; + } + + public static UnitBitSet create(int bitsPerUnit, boolean exact, int... bits) { + UnitBitSet bs = new UnitBitSet(bitsPerUnit, exact); + bs.setBits(bits); + return bs; + } + + public static UnitBitSet create(@NotNull BitSet bitSet, int bitsPerUnit) { + UnitBitSet result = new UnitBitSet(bitsPerUnit, true); + result.or(bitSet); + return result; + } +} diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/AggregatedDictionary.java b/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/AggregatedDictionary.java index ba886c936716..8ae6017fe808 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/AggregatedDictionary.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/AggregatedDictionary.java @@ -15,7 +15,7 @@ */ package com.intellij.spellchecker.dictionary; -import com.intellij.spellchecker.trie.Action; +import com.intellij.util.Consumer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -23,17 +23,17 @@ import java.util.Collection; import java.util.HashSet; import java.util.Set; -public class AggregatedDictionary implements Dictionary { +public class AggregatedDictionary implements EditableDictionary { private static final String DICTIONARY_NAME = "common"; - private Dictionary cachedDictionary; + private EditableDictionary cachedDictionary; private ProjectDictionary projectDictionary; public String getName() { return DICTIONARY_NAME; } - public AggregatedDictionary(@NotNull ProjectDictionary projectDictionary, @NotNull Dictionary cachedDictionary) { + public AggregatedDictionary(@NotNull ProjectDictionary projectDictionary, @NotNull EditableDictionary cachedDictionary) { this.projectDictionary = projectDictionary; this.cachedDictionary = cachedDictionary; this.cachedDictionary.addToDictionary(projectDictionary.getWords()); @@ -77,15 +77,17 @@ public class AggregatedDictionary implements Dictionary { getProjectDictionary().clear(); } - public void traverse(final Action action) { - cachedDictionary.traverse(action); + public void traverse(@NotNull final Consumer consumer) { + cachedDictionary.traverse(consumer); } public Set getWords() { - Set words = new HashSet(); - words.addAll(cachedDictionary.getWords()); - return words; + return cachedDictionary!=null?cachedDictionary.getWords():null; + } + + public int size() { + return (cachedDictionary!=null?cachedDictionary.size():0); } @Nullable @@ -93,22 +95,13 @@ public class AggregatedDictionary implements Dictionary { return getProjectDictionary().getEditableWords(); } - @Nullable - public Set getNotEditableWords() { - Set words = getWords(); - Set editable = getEditableWords(); - if (words != null && editable != null) { - words.removeAll(editable); - } - return words; - } public void addToDictionary(@Nullable Collection words) { getProjectDictionary().addToDictionary(words); getCachedDictionary().addToDictionary(words); } - public Dictionary getCachedDictionary() { + public EditableDictionary getCachedDictionary() { return cachedDictionary; } diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/Dictionary.java b/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/Dictionary.java index 48b17c285a42..5c689b1714c6 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/Dictionary.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/Dictionary.java @@ -15,10 +15,9 @@ */ package com.intellij.spellchecker.dictionary; -import com.intellij.spellchecker.trie.Action; +import com.intellij.util.Consumer; import org.jetbrains.annotations.Nullable; -import java.util.Collection; import java.util.Set; public interface Dictionary { @@ -29,26 +28,10 @@ public interface Dictionary { boolean isEmpty(); - void addToDictionary(String word); - - void removeFromDictionary(String word); - - void addToDictionary(@Nullable Collection words); - - void replaceAll(@Nullable Collection words); - - void clear(); - - void traverse(final Action action); + void traverse(final Consumer action); @Nullable Set getWords(); - @Nullable - Set getEditableWords(); - - @Nullable - Set getNotEditableWords(); - - + int size(); } diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/EditableDictionary.java b/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/EditableDictionary.java new file mode 100644 index 000000000000..d2abc06519bc --- /dev/null +++ b/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/EditableDictionary.java @@ -0,0 +1,39 @@ +/* + * Copyright 2000-2010 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.spellchecker.dictionary; + +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.Set; + +public interface EditableDictionary extends Dictionary { + + void addToDictionary(@Nullable String word); + + void removeFromDictionary(@Nullable String word); + + void addToDictionary(@Nullable Collection words); + + void replaceAll(@Nullable Collection words); + + void clear(); + + @Nullable + Set getEditableWords(); + + +} diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/trie/Action.java b/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/EditableDictionaryLoader.java similarity index 75% rename from plugins/spellchecker/src/com/intellij/spellchecker/trie/Action.java rename to plugins/spellchecker/src/com/intellij/spellchecker/dictionary/EditableDictionaryLoader.java index a5b401680802..2c0fe63b2f7d 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/trie/Action.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/EditableDictionaryLoader.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2010 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. @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.intellij.spellchecker.trie; +package com.intellij.spellchecker.dictionary; -public interface Action { - void run(String entry); +public interface EditableDictionaryLoader extends Loader{ + + EditableDictionary getDictionary(); } diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/Loader.java b/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/Loader.java index e66a13933749..a15cdd5d6e8a 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/Loader.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/Loader.java @@ -22,4 +22,6 @@ import org.jetbrains.annotations.NotNull; public interface Loader { void load(@NotNull Consumer consumer); + + String getName(); } diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/ProjectDictionary.java b/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/ProjectDictionary.java index e31e10a74313..b3ac0e314d41 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/ProjectDictionary.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/ProjectDictionary.java @@ -15,24 +15,27 @@ */ package com.intellij.spellchecker.dictionary; -import com.intellij.spellchecker.trie.Action; +import com.intellij.util.Consumer; +import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; -public class ProjectDictionary implements Dictionary { +public class ProjectDictionary implements EditableDictionary { private static final String DEFAULT_CURRENT_USER_NAME = "default.user"; private static final String DEFAULT_PROJECT_DICTIONARY_NAME = "project"; private String activeName; - private Set dictionaries; + private Set dictionaries; public ProjectDictionary() { } - public ProjectDictionary(Set dictionaries) { + public ProjectDictionary(Set dictionaries) { this.dictionaries = dictionaries; } @@ -73,20 +76,20 @@ public class ProjectDictionary implements Dictionary { } @NotNull - private Dictionary getActiveDictionary() { + private EditableDictionary getActiveDictionary() { return ensureCurrentUserDictionary(); } @NotNull - private Dictionary ensureCurrentUserDictionary() { + private EditableDictionary ensureCurrentUserDictionary() { if (activeName == null) { activeName = DEFAULT_CURRENT_USER_NAME; } - Dictionary result = getDictionaryByName(activeName); + EditableDictionary result = getDictionaryByName(activeName); if (result == null) { result = new UserDictionary(this.activeName); if (dictionaries == null) { - dictionaries = new HashSet(); + dictionaries = new THashSet(); } dictionaries.add(result); } @@ -94,12 +97,12 @@ public class ProjectDictionary implements Dictionary { } @Nullable - private Dictionary getDictionaryByName(@NotNull String name) { + private EditableDictionary getDictionaryByName(@NotNull String name) { if (dictionaries == null) { return null; } - Dictionary result = null; - for (Dictionary dictionary : dictionaries) { + EditableDictionary result = null; + for (EditableDictionary dictionary : dictionaries) { if (dictionary.getName().equals(name)) { result = dictionary; break; @@ -130,13 +133,21 @@ public class ProjectDictionary implements Dictionary { return words; } - public void traverse(Action action) { + public int size(){ + int result = 0; + for (Dictionary dictionary : dictionaries) { + result+=dictionary.size(); + } + return result; + } + + public void traverse(final Consumer consumer) { if (dictionaries == null) { return; } - for (Dictionary dictionary : dictionaries) { - dictionary.traverse(action); + for (EditableDictionary dictionary : dictionaries) { + dictionary.traverse(consumer); } } @@ -146,21 +157,12 @@ public class ProjectDictionary implements Dictionary { return getActiveDictionary().getWords(); } - @Nullable - public Set getNotEditableWords() { - Set words = getWords(); - Set editable = getEditableWords(); - if (words != null && editable != null) { - words.removeAll(editable); - } - return words; - } public void addToDictionary(@Nullable Collection words) { getActiveDictionary().addToDictionary(words); } - public Set getDictionaries() { + public Set getDictionaries() { return dictionaries; } diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/UserDictionary.java b/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/UserDictionary.java index 3a8bd704072b..87975558a9a8 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/UserDictionary.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/dictionary/UserDictionary.java @@ -15,17 +15,20 @@ */ package com.intellij.spellchecker.dictionary; -import com.intellij.spellchecker.trie.Action; +import com.intellij.util.Consumer; import gnu.trove.THashSet; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.Set; -public class UserDictionary implements Dictionary { +public class UserDictionary implements EditableDictionary { private String name; - private THashSet dictionary = new THashSet(); + + @NotNull + private final THashSet words = new THashSet(); public UserDictionary(String name) { this.name = name; @@ -36,26 +39,25 @@ public class UserDictionary implements Dictionary { } public boolean contains(String word) { - return word != null && dictionary.contains(word); + return words.contains(word); + } + + public int size() { + return words == null ? 0 : words.size(); } @Nullable public Set getWords() { - return dictionary; + return words; } @Nullable public Set getEditableWords() { - return dictionary; - } - - @Nullable - public Set getNotEditableWords() { - return null; + return words; } public void clear() { - dictionary.clear(); + words.clear(); } @@ -63,14 +65,14 @@ public class UserDictionary implements Dictionary { if (word == null) { return; } - dictionary.add(word); + words.add(word); } public void removeFromDictionary(String word) { if (word == null) { return; } - dictionary.remove(word); + words.remove(word); } public void replaceAll(@Nullable Collection words) { @@ -88,7 +90,7 @@ public class UserDictionary implements Dictionary { } public boolean isEmpty() { - return (dictionary == null || dictionary.size() == 0); + return words.size() == 0; } @Override @@ -102,12 +104,9 @@ public class UserDictionary implements Dictionary { } - public void traverse(final Action action){ - if (dictionary==null){ - return; - } - for (String s : dictionary) { - action.run(s); + public void traverse(final Consumer consumer) { + for (String word : words) { + consumer.consume(word); } } @@ -118,6 +117,6 @@ public class UserDictionary implements Dictionary { @Override public String toString() { - return "UserDictionary{" + "name='" + name + '\'' + ", words.count=" + dictionary.size() + '}'; + return "UserDictionary{" + "name='" + name + '\'' + ", words.count=" + words.size() + '}'; } } diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/engine/BaseSpellChecker.java b/plugins/spellchecker/src/com/intellij/spellchecker/engine/BaseSpellChecker.java index 29b9844f488c..fcb9729df678 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/engine/BaseSpellChecker.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/engine/BaseSpellChecker.java @@ -15,68 +15,157 @@ */ package com.intellij.spellchecker.engine; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.spellchecker.compress.CompressedDictionary; import com.intellij.spellchecker.dictionary.Dictionary; +import com.intellij.spellchecker.dictionary.EditableDictionary; +import com.intellij.spellchecker.dictionary.EditableDictionaryLoader; import com.intellij.spellchecker.dictionary.Loader; -import com.intellij.spellchecker.dictionary.UserDictionary; -import com.intellij.spellchecker.trie.Action; import com.intellij.util.Consumer; +import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; +import java.util.*; public class BaseSpellChecker implements SpellCheckerEngine { - private Dictionary engineDictionary; private Transformation transform = new Transformation(); + private final Set dictionaries = new THashSet(); + private final Set bundledDictionaries = new THashSet(); private Metrics metrics = new LevenshteinDistance(); - private Consumer consumer = new Consumer() { - public void consume(@Nullable String word) { - final String transformed = transform.transform(word); - if (transformed != null) { - engineDictionary.addToDictionary(transformed); - } - } - }; public BaseSpellChecker() { - ensureEngineDictionary(); } + public void loadDictionary(@NotNull Loader loader) { - loader.load(consumer); + if (loader instanceof EditableDictionaryLoader) { + final EditableDictionary dictionary = ((EditableDictionaryLoader)loader).getDictionary(); + if (dictionary != null) { + addModifiableDictionary(dictionary); + } + } + else { + loadFixedDictionary(loader); + } + + } + + private void loadFixedDictionary(final @NotNull Loader loader) { + /*if (ApplicationManager.getApplication().isUnitTestMode()) { + loadCompressedDictionary(loader); + } + else { + ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { + public void run() { + loadCompressedDictionary(loader); + } + }); + }*/ + loadCompressedDictionary(loader); + } + + private void loadCompressedDictionary(@NotNull Loader loader) { + final CompressedDictionary dictionary = CompressedDictionary.create(loader, transform); + if (dictionary != null) { + addCompressedFixedDictionary(dictionary); + } + } + + private void addModifiableDictionary(@NotNull EditableDictionary dictionary) { + dictionaries.add(dictionary); + } + + private void addCompressedFixedDictionary(@NotNull Dictionary dictionary) { + bundledDictionaries.add(dictionary); } public Transformation getTransformation() { return transform; } - private void ensureEngineDictionary() { - if (engineDictionary == null) { - engineDictionary = new UserDictionary("engine"); + @NotNull + private static List restore(@NotNull Character startFrom, int i, int j, @Nullable Collection dictionaries) { + if (dictionaries == null) { + return Collections.emptyList(); } + List results = new ArrayList(); + + for (Object o : dictionaries) { + if (o instanceof Dictionary) { + results.addAll(restore(startFrom, i, j, (Dictionary)o)); + } + + } + return results; } - - public void addToDictionary(String word) { - final String transformed = transform.transform(word); - if (transformed != null) { - engineDictionary.addToDictionary(transformed); + @NotNull + private static List restore(@NotNull final Character first, final int i, final int j, @Nullable Dictionary dictionary) { + if (dictionary == null) { + return Collections.emptyList(); } + final List result = new ArrayList(); + if (dictionary instanceof CompressedDictionary) { + result.addAll(((CompressedDictionary)dictionary).getWords(first)); + } + else { + dictionary.traverse(new Consumer() { + public void consume(String s) { + if (StringUtil.isEmpty(s)) { + return; + } + if (s.charAt(0) == first && s.length() >= i && s.length() <= j) { + result.add(s); + } + } + }); + } + + return result; + } + + private static boolean isCorrect(@NotNull String transformed, @Nullable Collection dictionaries) { + if (dictionaries == null) { + return true; + } + + for (Object o : dictionaries) { + if (o instanceof Dictionary) { + boolean result = isCorrect(transformed, (Dictionary)o); + if (result) { + return true; + } + } + + } + return false; + } + + private static boolean isCorrect(@NotNull String transformed, @Nullable Dictionary dictionary) { + if (dictionary == null) { + return true; + } + + return dictionary.contains(transformed); } public boolean isCorrect(@NotNull String word) { final String transformed = transform.transform(word); - return transformed != null && engineDictionary.contains(transformed); + if (transformed == null) { + return true; + } + return isCorrect(transformed, bundledDictionaries) || isCorrect(transformed, dictionaries); + + } + @NotNull public List getSuggestions(final @NotNull String word, int threshold, int quality) { final String transformed = transform.transform(word); @@ -84,14 +173,12 @@ public class BaseSpellChecker implements SpellCheckerEngine { return Collections.emptyList(); } final List suggestions = new ArrayList(); - engineDictionary.traverse(new Action() { - public void run(String entry) { - if (transformed.charAt(0) == entry.charAt(0)/* && Math.abs(mpw.length() - entry.getKey().length()) <= 1*/) { - final int distance = metrics.calculateMetrics(transformed, entry); - suggestions.add(new Suggestion(entry, distance)); - } - } - }); + List rawSuggestions = restore(transformed.charAt(0), 0, Integer.MAX_VALUE, bundledDictionaries); + rawSuggestions.addAll(restore(word.charAt(0), 0, Integer.MAX_VALUE, dictionaries)); + for (String rawSuggestion : rawSuggestions) { + final int distance = metrics.calculateMetrics(transformed, rawSuggestion); + suggestions.add(new Suggestion(rawSuggestion, distance)); + } List result = new ArrayList(); if (suggestions.isEmpty()) { return result; @@ -108,13 +195,44 @@ public class BaseSpellChecker implements SpellCheckerEngine { return result; } + @NotNull public List getVariants(@NotNull String prefix) { - return null; + //if (StringUtil.isEmpty(prefix)) { + return Collections.emptyList(); + //} + } public void reset() { - engineDictionary = null; - ensureEngineDictionary(); + bundledDictionaries.clear(); + dictionaries.clear(); + } + + public boolean isDictionaryLoad(@NotNull String name) { + return getBundledDictionaryByName(name) != null; + } + + public void removeDictionary(@NotNull String name) { + for (Iterator iterator = bundledDictionaries.iterator(); iterator.hasNext();) { + Dictionary dictionary = iterator.next(); + if (name.equals(dictionary.getName())) { + iterator.remove(); + break; + } + } + } + + @Nullable + public Dictionary getBundledDictionaryByName(@NotNull String name) { + if (bundledDictionaries == null) { + return null; + } + for (Dictionary dictionary : bundledDictionaries) { + if (name.equals(dictionary.getName())) { + return dictionary; + } + } + return null; } } diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/engine/SpellCheckerEngine.java b/plugins/spellchecker/src/com/intellij/spellchecker/engine/SpellCheckerEngine.java index 05b6e0249788..80bb8dd87232 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/engine/SpellCheckerEngine.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/engine/SpellCheckerEngine.java @@ -30,7 +30,6 @@ public interface SpellCheckerEngine { boolean isCorrect(@NotNull String word); - void addToDictionary(String word); @NotNull List getSuggestions(@NotNull String word, int threshold, int quality); @@ -38,9 +37,10 @@ public interface SpellCheckerEngine { @NotNull List getVariants(@NotNull String prefix); - /** - * This method must clean up user dictionary words and ignored words. - */ + void reset(); + boolean isDictionaryLoad(@NotNull String name); + + void removeDictionary(@NotNull String name); } diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/engine/Transformation.java b/plugins/spellchecker/src/com/intellij/spellchecker/engine/Transformation.java index 6381a025b351..79a5fb88fde1 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/engine/Transformation.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/engine/Transformation.java @@ -21,10 +21,11 @@ import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.Set; +@SuppressWarnings({"ALL"}) public class Transformation { @Nullable - public String transform(@Nullable String word) { + public String transform(@Nullable String word) { if (word == null || word.trim().length() < 3) { return null; } @@ -32,7 +33,7 @@ public class Transformation { } @Nullable - public Set transform(@Nullable Collection words) { + public Set transform(@Nullable Collection words) { if (words == null || words.isEmpty()) { return null; } diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/inspections/SpellCheckingInspection.java b/plugins/spellchecker/src/com/intellij/spellchecker/inspections/SpellCheckingInspection.java index 359f1f9783a8..2cc1e161567a 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/inspections/SpellCheckingInspection.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/inspections/SpellCheckingInspection.java @@ -36,6 +36,7 @@ import com.intellij.spellchecker.tokenizer.Token; import com.intellij.spellchecker.tokenizer.Tokenizer; import com.intellij.spellchecker.util.SpellCheckerBundle; import com.intellij.util.containers.hash.HashMap; +import gnu.trove.THashSet; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -43,14 +44,12 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; -import java.util.ArrayList; -import java.util.Collection; +import java.util.*; import java.util.List; -import java.util.Map; public class SpellCheckingInspection extends LocalInspectionTool { - + public static final String SPELL_CHECKING_INSPECTION_TOOL_NAME = "SpellCheckingInspection"; private static final AcceptWordAsCorrect BATCH_ACCEPT_FIX = new AcceptWordAsCorrect(); @@ -146,56 +145,68 @@ public class SpellCheckingInspection extends LocalInspectionTool { if (tokens == null) { return; } + SpellCheckerManager manager = SpellCheckerManager.getInstance(element.getProject()); + Set alreadyChecked = new THashSet(); for (Token token : tokens) { - inspect(token, holder, isOnTheFly, NAMES_VALIDATORS); + inspect(token, holder, isOnTheFly, alreadyChecked, manager,NAMES_VALIDATORS); } } }; } - private static void inspect(Token token, ProblemsHolder holder, boolean isOnTheFly, NamesValidator... validators) { + + private static void inspect(Token token, ProblemsHolder holder, boolean isOnTheFly,@NotNull Set alreadyChecked, @NotNull SpellCheckerManager manager, NamesValidator... validators) { List areaList = TextSplitter.splitText(token.getText()); if (areaList == null) { return; } for (CheckArea area : areaList) { boolean ignored = area.isIgnored(); - boolean keyword = isKeyword(validators, token.getElement(), area.getWord()); - if (!ignored && !keyword) { - inspect(area, token, holder, isOnTheFly); + final TextRange textRange = area.getTextRange(); + + if (ignored || textRange ==null){ + continue; } + + final String word = area.getWord(); + if (word==null || (!isOnTheFly && alreadyChecked.contains(word))){ + continue; + } + + boolean keyword = isKeyword(validators, token.getElement(), word); + if (keyword){ + continue; + } + + boolean hasProblems = manager.hasProblem(word); + if (hasProblems){ + if (!isOnTheFly){ + alreadyChecked.add(word); + addBatchDescriptor(textRange,token,holder); + } else{ + addRegularDescriptor(textRange,token,holder); + } + } + } } - private static void inspect(@NotNull CheckArea area, @NotNull Token token, @NotNull ProblemsHolder holder, boolean isOnTheFly) { - SpellCheckerManager manager = SpellCheckerManager.getInstance(token.getElement().getProject()); - - final TextRange textRange = area.getTextRange(); - final String word = area.getWord(); - - if (textRange == null || word == null) { - return; - } - - if (manager.hasProblem(word)) { + private static void addBatchDescriptor(@NotNull TextRange textRange, @NotNull Token token, @NotNull ProblemsHolder holder) { List fixes = new ArrayList(); - if (isOnTheFly) { - if (!token.isUseRename()) { - fixes.add(new ChangeTo()); - } - else { - fixes.add(new RenameTo()); - } - } + fixes.add(BATCH_ACCEPT_FIX); - final AcceptWordAsCorrect acceptWordAsCorrect = isOnTheFly ? new AcceptWordAsCorrect() : BATCH_ACCEPT_FIX; - fixes.add(acceptWordAsCorrect); - - final ProblemDescriptor problemDescriptor = createProblemDescriptor(token, holder, textRange, fixes, isOnTheFly); + final ProblemDescriptor problemDescriptor = createProblemDescriptor(token, holder, textRange, fixes, false); holder.registerProblem(problemDescriptor); - } + } + private static void addRegularDescriptor(@NotNull TextRange textRange, @NotNull Token token, @NotNull ProblemsHolder holder) { + List fixes = new ArrayList(); + fixes.add((token.isUseRename()?new RenameTo():new ChangeTo())); + fixes.add(new AcceptWordAsCorrect()); + + final ProblemDescriptor problemDescriptor = createProblemDescriptor(token, holder, textRange, fixes, true); + holder.registerProblem(problemDescriptor); } private static ProblemDescriptor createProblemDescriptor(Token token, diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/inspections/TextSplitter.java b/plugins/spellchecker/src/com/intellij/spellchecker/inspections/TextSplitter.java index 39286a1407b8..9615f15995d6 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/inspections/TextSplitter.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/inspections/TextSplitter.java @@ -15,7 +15,6 @@ */ package com.intellij.spellchecker.inspections; -import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.codeStyle.NameUtil; @@ -29,19 +28,10 @@ import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; -/** - * @author shkate@jetbrains.com - */ public class TextSplitter { - @NonNls - private static final Pattern NON_SPACE = Pattern.compile("\\S+"); @NonNls - private static final Pattern HTML = Pattern.compile("<(0)>"); - - @NonNls - private static final Pattern WORD = Pattern.compile("\\b\\p{L}*'?\\p{L}*"); private static final Pattern EXTENDED_WORD = Pattern.compile("\\b\\p{L}*'?\\p{L}(_*\\p{L})*"); @@ -51,12 +41,15 @@ public class TextSplitter { @NonNls private static final Pattern URL = Pattern.compile("(https?|ftp|mailto)\\:\\/\\/"); + @NonNls private static final Pattern COMPLEX = Pattern.compile("(\\.[^\\.]+)|([@]+)"); @NonNls private static final Pattern SPECIAL = Pattern.compile("^&\\p{Alnum}{4};"); + private static final String delimiters = ".,;:!?*/&"; + private TextSplitter() { } @@ -73,8 +66,8 @@ public class TextSplitter { List results = new ArrayList(); String[] pieces = text.substring(i).split(WORD_SPLITTER); for (String s : pieces) { - ProgressManager.checkCanceled(); - if (s.length() > 0) { + //ProgressManager.checkCanceled(); + if (s.length() > 0 && startWithLetterOrDelimiter(s)) { int p1 = text.indexOf(s, i); TextRange range = TextRange.from(p1, s.length()); List areaList = splitNonSpace(text, range); @@ -87,6 +80,13 @@ public class TextSplitter { return (results.size() == 0) ? null : results; } + private static boolean startWithLetterOrDelimiter(@NotNull String s) { + // Matcher matcher = PRINTABLE.matcher(s); + // return matcher.find(); + return Character.isLetter(s.charAt(0)) || delimiters.contains(s.substring(0,1)); + // return true; + } + @Nullable private static List splitNonSpace(String text, TextRange range) { String nonSpaceArea = text.substring(range.getStartOffset(), range.getEndOffset()); @@ -100,12 +100,12 @@ public class TextSplitter { @NotNull private static List splitSimpleWord(String text, TextRange range) { List results = new ArrayList(); - if (text==null || range==null || range.getLength()<1){ + if (text == null || range == null || range.getLength() < 1) { return results; } String word = text.substring(range.getStartOffset(), range.getEndOffset()); String[] words = NameUtil.splitNameIntoWords(word); - if (words == null || words.length==0) { + if (words == null || words.length == 0) { return results; } @@ -132,7 +132,7 @@ public class TextSplitter { int start = word.indexOf(s, index); int end = start + s.length(); boolean isUpperCase = Strings.isUpperCase(s); - boolean flag = (isUpperCase && !isAllWordsAreUpperCased) || isKeyword(s); + boolean flag = (isUpperCase && !isAllWordsAreUpperCased); Matcher matcher = WORD.matcher(s); if (matcher.find()) { TextRange found = matcherRange(subRange(range, start, end), matcher); @@ -175,9 +175,6 @@ public class TextSplitter { results.add(new CheckArea(text, found, flag || tooShort)); } - private static boolean isKeyword(String s) { - return false; - } private static boolean isAllWordsAreUpperCased(String[] words) { if (words == null) return false; diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/quickfixes/ChangeTo.java b/plugins/spellchecker/src/com/intellij/spellchecker/quickfixes/ChangeTo.java index 3c6188800565..8dac38ae1487 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/quickfixes/ChangeTo.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/quickfixes/ChangeTo.java @@ -45,7 +45,7 @@ public class ChangeTo extends ShowSuggestions implements SpellCheckerQuickFix { @NotNull public String getName() { - return SpellCheckerBundle.message("change.to"); + return SpellCheckerBundle.message("change.to"); } @NotNull @@ -91,4 +91,4 @@ public class ChangeTo extends ShowSuggestions implements SpellCheckerQuickFix { } -} +} \ No newline at end of file diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/quickfixes/RenameTo.java b/plugins/spellchecker/src/com/intellij/spellchecker/quickfixes/RenameTo.java index bf4b4b5846ea..0602561dc62a 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/quickfixes/RenameTo.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/quickfixes/RenameTo.java @@ -20,10 +20,8 @@ import com.intellij.ide.DataManager; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.TextRange; import com.intellij.refactoring.actions.RenameElementAction; import com.intellij.refactoring.rename.NameSuggestionProvider; -import com.intellij.spellchecker.quickfixes.DictionarySuggestionProvider; import com.intellij.spellchecker.util.SpellCheckerBundle; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -52,7 +50,6 @@ public class RenameTo extends ShowSuggestions implements SpellCheckerQuickFix { private static DictionarySuggestionProvider findProvider() { Object[] extensions = Extensions.getExtensions(NameSuggestionProvider.EP_NAME); - DictionarySuggestionProvider provider = null; if (extensions != null) { for (Object extension : extensions) { if (extension instanceof DictionarySuggestionProvider) { @@ -69,6 +66,7 @@ public class RenameTo extends ShowSuggestions implements SpellCheckerQuickFix { return Anchor.FIRST; } + @SuppressWarnings({"SSBasedInspection"}) public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { SwingUtilities.invokeLater(new Runnable() { public void run() { diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/settings/SpellCheckerSettingsPane.java b/plugins/spellchecker/src/com/intellij/spellchecker/settings/SpellCheckerSettingsPane.java index fcbbbbafb331..8cd686d3caab 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/settings/SpellCheckerSettingsPane.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/settings/SpellCheckerSettingsPane.java @@ -25,7 +25,7 @@ import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; import com.intellij.profile.codeInspection.ui.ErrorsConfigurable; import com.intellij.spellchecker.SpellCheckerManager; -import com.intellij.spellchecker.dictionary.Dictionary; +import com.intellij.spellchecker.dictionary.EditableDictionary; import com.intellij.spellchecker.util.SPFileUtil; import com.intellij.spellchecker.util.SpellCheckerBundle; import com.intellij.spellchecker.util.Strings; @@ -161,6 +161,13 @@ public class SpellCheckerSettingsPane implements Disposable { } public void apply() throws ConfigurationException { + if (wordsPanel.isModified()){ + manager.updateUserDictionary(wordsPanel.getWords()); + } + if (!optionalChooserComponent.isModified() && !pathsChooserComponent.isModified()){ + return; + } + optionalChooserComponent.apply(); pathsChooserComponent.apply(); settings.setDictionaryFoldersPaths(pathsChooserComponent.getValues()); @@ -182,7 +189,8 @@ public class SpellCheckerSettingsPane implements Disposable { settings.setDisabledDictionariesPaths(disabledDictionaries); settings.setBundledDisabledDictionariesPaths(bundledDisabledDictionaries); - manager.update(wordsPanel.getWords(), settings); + manager.updateBundledDictionaries(); + } private boolean isUserDictionary(final String dictionary) { @@ -232,9 +240,9 @@ public class SpellCheckerSettingsPane implements Disposable { } public static final class WordDescriber { - private Dictionary dictionary; + private EditableDictionary dictionary; - public WordDescriber(Dictionary dictionary) { + public WordDescriber(EditableDictionary dictionary) { this.dictionary = dictionary; } diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/state/CachedDictionaryState.java b/plugins/spellchecker/src/com/intellij/spellchecker/state/CachedDictionaryState.java index 6877e61ec37e..461cf66ce2fe 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/state/CachedDictionaryState.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/state/CachedDictionaryState.java @@ -18,29 +18,29 @@ package com.intellij.spellchecker.state; import com.intellij.openapi.components.PersistentStateComponent; import com.intellij.openapi.components.State; import com.intellij.openapi.components.Storage; -import com.intellij.spellchecker.dictionary.Dictionary; +import com.intellij.spellchecker.dictionary.EditableDictionary; @State( name = "CachedDictionaryState", storages = {@Storage( id = "spellchecker", file = "$APP_CONFIG$/cachedDictionary.xml")}) -public class CachedDictionaryState extends DictionaryState implements PersistentStateComponent{ +public class CachedDictionaryState extends DictionaryState implements PersistentStateComponent { public static final String DEFAULT_NAME = "cached"; public CachedDictionaryState() { name = DEFAULT_NAME; } - public CachedDictionaryState(Dictionary dictionary) { + public CachedDictionaryState(EditableDictionary dictionary) { super(dictionary); name = DEFAULT_NAME; } @Override public void loadState(DictionaryState state) { - if (state.name==null){ - state.name= DEFAULT_NAME; + if (state.name == null) { + state.name = DEFAULT_NAME; } super.loadState(state); } diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/state/DictionaryState.java b/plugins/spellchecker/src/com/intellij/spellchecker/state/DictionaryState.java index 1339cd523c3f..d392b69d152d 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/state/DictionaryState.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/state/DictionaryState.java @@ -16,7 +16,7 @@ package com.intellij.spellchecker.state; import com.intellij.openapi.components.PersistentStateComponent; -import com.intellij.spellchecker.dictionary.Dictionary; +import com.intellij.spellchecker.dictionary.EditableDictionary; import com.intellij.spellchecker.dictionary.UserDictionary; import com.intellij.util.xmlb.annotations.AbstractCollection; import com.intellij.util.xmlb.annotations.Attribute; @@ -39,24 +39,24 @@ public class DictionaryState implements PersistentStateComponent projectDictionaries = projectDictionary.getDictionaries(); + Set projectDictionaries = projectDictionary.getDictionaries(); if (projectDictionaries != null) { - for (Dictionary dic : projectDictionary.getDictionaries()) { + for (EditableDictionary dic : projectDictionary.getDictionaries()) { dictionaryStates.add(new DictionaryState(dic)); } } @@ -101,7 +103,7 @@ public class ProjectDictionaryState implements PersistentStateComponent dictionaries = new HashSet(); + Set dictionaries = new THashSet(); if (dictionaryStates != null) { for (DictionaryState dictionaryState : dictionaryStates) { dictionaryState.loadState(dictionaryState); diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/state/StateLoader.java b/plugins/spellchecker/src/com/intellij/spellchecker/state/StateLoader.java index 86d163adc407..b31aaf0c5d44 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/state/StateLoader.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/state/StateLoader.java @@ -17,40 +17,48 @@ package com.intellij.spellchecker.state; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.project.Project; -import com.intellij.spellchecker.dictionary.Dictionary; -import com.intellij.spellchecker.dictionary.Loader; +import com.intellij.spellchecker.dictionary.EditableDictionary; +import com.intellij.spellchecker.dictionary.EditableDictionaryLoader; import com.intellij.util.Consumer; import org.jetbrains.annotations.NotNull; import java.util.Set; -public class StateLoader implements Loader { +public class StateLoader implements EditableDictionaryLoader { - private Project project; - private Dictionary dictionary; + private Project project; + private EditableDictionary dictionary; - public StateLoader(Project project) { - this.project = project; + public StateLoader(Project project) { + this.project = project; + } + + + public void load(@NotNull Consumer consumer) { + AggregatedDictionaryState state = ServiceManager.getService(project, AggregatedDictionaryState.class); + state.setProject(project); + state.loadState(); + dictionary = state.getDictionary(); + if (dictionary == null) { + return; + } + final Set storedWords = dictionary.getWords(); + if (storedWords != null) { + for (String word : storedWords) { + consumer.consume(word); + } } + } - public void load(@NotNull Consumer consumer) { - AggregatedDictionaryState state = ServiceManager.getService(project, AggregatedDictionaryState.class); - state.setProject(project); - state.loadState(); - dictionary = state.getDictionary(); - final Set storedWords = dictionary.getWords(); - if (storedWords!=null){ - for (String word : storedWords) { - consumer.consume(word); - } - } - } + public EditableDictionary getDictionary() { + return dictionary; + } - public Dictionary getDictionary() { - return dictionary; - } + public String getName() { + return (dictionary != null ? dictionary.getName() : ""); + } } diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/CommentTokenizer.java b/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/CommentTokenizer.java index a9b47f0964bf..7b1b4360b065 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/CommentTokenizer.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/CommentTokenizer.java @@ -19,11 +19,6 @@ import com.intellij.psi.PsiComment; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -/** - * Created by IntelliJ IDEA. - * - * @author shkate@jetbrains.com - */ public class CommentTokenizer extends Tokenizer { @Nullable diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/HtmlSpellcheckingStrategy.java b/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/HtmlSpellcheckingStrategy.java index d57820623fce..150e46c7d103 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/HtmlSpellcheckingStrategy.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/HtmlSpellcheckingStrategy.java @@ -22,11 +22,6 @@ import com.intellij.psi.xml.XmlAttributeValue; import com.intellij.psi.xml.XmlText; import org.jetbrains.annotations.NotNull; -/** - * Created by IntelliJ IDEA. - * - * @author shkate@jetbrains.com - */ public class HtmlSpellcheckingStrategy extends SpellcheckingStrategy { @NotNull @Override diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/TextTokenizer.java b/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/TextTokenizer.java index dd47ae9e4932..bec720598155 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/TextTokenizer.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/TextTokenizer.java @@ -19,14 +19,8 @@ import com.intellij.psi.PsiPlainText; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -/** - * Created by IntelliJ IDEA. - * - * @author shkate@jetbrains.com - */ public class TextTokenizer extends Tokenizer { - @Nullable @Override public Token[] tokenize(@NotNull PsiPlainText element) { diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/Token.java b/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/Token.java index 903cabaecd07..774c67514043 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/Token.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/Token.java @@ -17,11 +17,7 @@ package com.intellij.spellchecker.tokenizer; import com.intellij.psi.PsiElement; -/** - * Created by IntelliJ IDEA. - * - * @author shkate@jetbrains.com - */ + public class Token { private String text; diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/Tokenizer.java b/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/Tokenizer.java index bb3583f27a78..483c2ff7d39f 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/Tokenizer.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/Tokenizer.java @@ -19,11 +19,6 @@ import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -/** - * Created by IntelliJ IDEA. - * - * @author shkate@jetbrains.com - */ public class Tokenizer { @Nullable diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/XmlAttributeTokenizer.java b/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/XmlAttributeTokenizer.java index 4e538139dfc4..5925f4555c50 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/XmlAttributeTokenizer.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/XmlAttributeTokenizer.java @@ -19,11 +19,6 @@ import com.intellij.psi.xml.XmlAttributeValue; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -/** - * Created by IntelliJ IDEA. - * - * @author shkate@jetbrains.com - */ public class XmlAttributeTokenizer extends Tokenizer{ diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/XmlTextTokenizer.java b/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/XmlTextTokenizer.java index 8a89637d9b39..6b2de76f2d00 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/XmlTextTokenizer.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/tokenizer/XmlTextTokenizer.java @@ -19,11 +19,6 @@ import com.intellij.psi.xml.XmlText; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -/** - * Created by IntelliJ IDEA. - * - * @author shkate@jetbrains.com - */ public class XmlTextTokenizer extends Tokenizer { diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/util/Strings.java b/plugins/spellchecker/src/com/intellij/spellchecker/util/Strings.java index 306ff3d17b5f..008d5ba10755 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/util/Strings.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/util/Strings.java @@ -17,9 +17,6 @@ package com.intellij.spellchecker.util; import java.util.List; -/** - * Text utility. - */ public final class Strings { private Strings() { } diff --git a/plugins/spellchecker/testSrc/com/intellij/spellchecker/compress/CompressorTest.java b/plugins/spellchecker/testSrc/com/intellij/spellchecker/compress/CompressorTest.java new file mode 100644 index 000000000000..59da085f0f5e --- /dev/null +++ b/plugins/spellchecker/testSrc/com/intellij/spellchecker/compress/CompressorTest.java @@ -0,0 +1,68 @@ +/* + * Copyright 2000-2010 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.spellchecker.compress; + +import com.intellij.spellchecker.compress.Compressor; +import com.intellij.spellchecker.compress.UnitBitSet; +import junit.framework.TestCase; + +import java.util.BitSet; +import java.util.Random; + +public class CompressorTest extends TestCase { + + public void testCompressorWithDefinedData() { + Compressor compressor = new Compressor(0); + UnitBitSet bs = UnitBitSet.create(1, 3, 5, 10); + final byte[] compressed = compressor.compress(bs); + assertEquals(3, compressed.length); + assertEquals(42, compressed[1]); + assertEquals(8, compressed[2]); + final UnitBitSet restored = compressor.decompress(compressed); + assertEquals(bs, restored); + } + + public void testCompressorWithRandomData() { + Compressor compressor = new Compressor(0); + int max = 10; + for (int i = 0; i < 1000; i++) { + int[] values = getRandoms(1000, max); + final UnitBitSet bitSet = new UnitBitSet(); + for (int value : values) { + bitSet.set(value); + } + final byte[] compressed = compressor.compress(bitSet); + final BitSet decompressed = compressor.decompress(compressed); + boolean check = (bitSet.equals(decompressed)); + if (!check) { + System.out.println("bitSet: " + bitSet); + System.out.println("decompressed: " + decompressed); + compressor.compress(bitSet); + compressor.decompress(compressed); + } + assertEquals(bitSet, decompressed); + } + } + + private static int[] getRandoms(int max, int count) { + int[] result = new int[count]; + for (int i = 0; i < count; i++) { + result[i] = new Random().nextInt(max); + } + return result; + } + +} diff --git a/plugins/spellchecker/testSrc/com/intellij/spellchecker/compress/DictionaryTest.java b/plugins/spellchecker/testSrc/com/intellij/spellchecker/compress/DictionaryTest.java new file mode 100644 index 000000000000..47120df03721 --- /dev/null +++ b/plugins/spellchecker/testSrc/com/intellij/spellchecker/compress/DictionaryTest.java @@ -0,0 +1,150 @@ +/* + * Copyright 2000-2010 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.spellchecker.compress; + +import com.intellij.openapi.util.Pair; +import com.intellij.spellchecker.DefaultBundledDictionariesProvider; +import com.intellij.spellchecker.StreamLoader; +import com.intellij.spellchecker.dictionary.Dictionary; +import com.intellij.spellchecker.dictionary.Loader; +import com.intellij.spellchecker.engine.Transformation; +import com.intellij.testFramework.IdeaTestUtil; +import com.intellij.util.Consumer; +import gnu.trove.THashSet; +import junit.framework.TestCase; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +@SuppressWarnings({"JUnitTestCaseWithNonTrivialConstructors"}) +public class DictionaryTest extends TestCase { + + private Dictionary dictionary; + + private Map sizes = new HashMap(); + private Map times = new HashMap(); + + { + sizes.put("jetbrains.dic", 1000); + sizes.put("english.dic", 140000); + sizes.put("russian-utf8.dic", 1200000); + } + + { + times.put("jetbrains.dic", 100); + times.put("english.dic", 2700); + times.put("russian-utf8.dic", 32000); + } + + public void testDictionary() throws IOException { + + DefaultBundledDictionariesProvider dp = new DefaultBundledDictionariesProvider(); + final String[] names = dp.getBundledDictionaries(); + for (String name : names) { + StreamLoader loader = createLoader(name); + loadDictionaryTest(loader, sizes.get(name), name); + loadHalfDictionaryTest(50000, name); + } + + } + + + public void loadDictionaryTest(final Loader loader, int wordCount, final String name) throws IOException { + final Transformation transform = new Transformation(); + + IdeaTestUtil + .assertTiming("Dictionary load depends on words count. Approximate word count: " + wordCount + ".", times.get(name), new Runnable() { + public void run() { + dictionary = CompressedDictionary.create(loader, transform); + } + }); + + final Set wordsToStoreAndCheck = createWordSets(name, 50000, 1).getFirst(); + IdeaTestUtil.assertTiming("Invoke 'contains' " + wordsToStoreAndCheck.size() + " times", 1000, new Runnable() { + public void run() { + for (String s : wordsToStoreAndCheck) { + assertTrue(dictionary.contains(s)); + } + } + }); + + } + + private static StreamLoader createLoader(String name) { + return new StreamLoader(DefaultBundledDictionariesProvider.class.getResourceAsStream(name), name); + } + + private static Loader createLoader(final Set words) { + return new Loader() { + public void load(@NotNull Consumer consumer) { + for (String word : words) { + consumer.consume(word); + } + } + + public String getName() { + return "test"; + } + }; + } + + @SuppressWarnings({"unchecked"}) + private static Pair, Set> createWordSets(String name, final int maxCount, final int mod) { + Loader loader = createLoader(name); + final Set wordsToStore = new THashSet(); + final Set wordsToCheck = new THashSet(); + final Transformation transform = new Transformation(); + loader.load(new Consumer() { + private int counter = 0; + + public void consume(String s) { + if (counter > maxCount) { + return; + } + String transformed = transform.transform(s); + if (transformed != null) { + + if (counter % mod == 0) { + wordsToStore.add(transformed); + } + else { + wordsToCheck.add(transformed); + } + counter++; + } + } + }); + + return new Pair(wordsToStore, wordsToCheck); + } + + + public void loadHalfDictionaryTest(final int maxCount, final String name) throws IOException { + + final Pair, Set> sets = createWordSets(name, maxCount, 2); + CompressedDictionary dictionary = CompressedDictionary.create(createLoader(sets.getFirst()), new Transformation()); + for (String s : sets.getSecond()) { + if (!sets.getFirst().contains(s)) { + assertFalse(dictionary.contains(s)); + } + } + } + + +} diff --git a/plugins/spellchecker/testSrc/com/intellij/spellchecker/compress/EncodeAndCompressTest.java b/plugins/spellchecker/testSrc/com/intellij/spellchecker/compress/EncodeAndCompressTest.java new file mode 100644 index 000000000000..37082acc133b --- /dev/null +++ b/plugins/spellchecker/testSrc/com/intellij/spellchecker/compress/EncodeAndCompressTest.java @@ -0,0 +1,23 @@ +package com.intellij.spellchecker.compress; + +import com.intellij.spellchecker.compress.Compressor; +import com.intellij.spellchecker.compress.Encoder; +import com.intellij.spellchecker.compress.UnitBitSet; +import junit.framework.TestCase; + + +public class EncodeAndCompressTest extends TestCase { + + public void testEncodeAndCompress() { + Encoder encoder = new Encoder(); + Compressor compressor = new Compressor(2); + String word = "example"; + UnitBitSet bs = encoder.encode(word, true); + byte[] compressed = compressor.compress(bs); + final UnitBitSet decompressed = compressor.decompress(compressed); + assertEquals(bs,decompressed); + String restored = encoder.decode(decompressed); + assertEquals(word,restored); + } + +} diff --git a/plugins/spellchecker/testSrc/com/intellij/spellchecker/compress/EncoderTest.java b/plugins/spellchecker/testSrc/com/intellij/spellchecker/compress/EncoderTest.java new file mode 100644 index 000000000000..23bff92b5494 --- /dev/null +++ b/plugins/spellchecker/testSrc/com/intellij/spellchecker/compress/EncoderTest.java @@ -0,0 +1,114 @@ +/* + * Copyright 2000-2010 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.spellchecker.compress; + +import com.intellij.spellchecker.compress.Alphabet; +import com.intellij.spellchecker.compress.Encoder; +import com.intellij.spellchecker.compress.UnitBitSet; +import junit.framework.TestCase; + +public class EncoderTest extends TestCase { + + + public void testSimple() { + Encoder encoder = new Encoder(); + final String wordToTest = "abc"; + final UnitBitSet bitSet = encoder.encode(wordToTest, true); + assertEquals(3, encoder.getAlphabet().getLastIndexUsed()); + assertEquals(3, bitSet.getUnitValue(0)); + assertEquals(1, bitSet.getUnitValue(1)); + assertEquals(1, bitSet.getUnitValue(2)); + assertEquals(2, bitSet.getUnitValue(3)); + assertEquals(3, bitSet.getUnitValue(4)); + + assertEquals(wordToTest, encoder.decode(bitSet)); + } + + + public void testDouble() { + Encoder encoder = new Encoder(); + final String wordToTest = "aaa"; + final UnitBitSet bitSet = encoder.encode(wordToTest, true); + assertEquals(1, encoder.getAlphabet().getLastIndexUsed()); + assertEquals(3, bitSet.getUnitValue(0)); + assertEquals(1, bitSet.getUnitValue(1)); + assertEquals(1, bitSet.getUnitValue(2)); + assertEquals(1, bitSet.getUnitValue(3)); + assertEquals(1, bitSet.getUnitValue(4)); + + assertEquals(wordToTest, encoder.decode(bitSet)); + } + + public void testLetterRepetition() { + Encoder encoder = new Encoder(); + final String wordToTest = "aba"; + final UnitBitSet bitSet = encoder.encode(wordToTest, true); + assertEquals(2, encoder.getAlphabet().getLastIndexUsed()); + assertEquals(3, bitSet.getUnitValue(0)); + assertEquals(1, bitSet.getUnitValue(1)); + assertEquals(1, bitSet.getUnitValue(2)); + assertEquals(2, bitSet.getUnitValue(3)); + assertEquals(1, bitSet.getUnitValue(4)); + + assertEquals(wordToTest, encoder.decode(bitSet)); + } + + public void testReverse() { + Encoder encoder = new Encoder(); + final String wordToTest1 = "abc"; + final UnitBitSet bitSet = encoder.encode(wordToTest1, true); + assertEquals(3, encoder.getAlphabet().getLastIndexUsed()); + assertEquals(3, bitSet.getUnitValue(0)); + assertEquals(1, bitSet.getUnitValue(1)); + assertEquals(1, bitSet.getUnitValue(2)); + assertEquals(2, bitSet.getUnitValue(3)); + assertEquals(3, bitSet.getUnitValue(4)); + + assertEquals(wordToTest1, encoder.decode(bitSet)); + + final String wordToTest2 = "cba"; + final UnitBitSet bitSet2 = encoder.encode(wordToTest2, true); + assertEquals(3, encoder.getAlphabet().getLastIndexUsed()); + assertEquals(3, bitSet2.getUnitValue(0)); + assertEquals(3, bitSet2.getUnitValue(1)); + assertEquals(3, bitSet2.getUnitValue(2)); + assertEquals(2, bitSet2.getUnitValue(3)); + assertEquals(1, bitSet2.getUnitValue(4)); + + assertEquals(wordToTest2, encoder.decode(bitSet2)); + } + + + public void testWithPredefinedAlphabet() { + Encoder encoder = new Encoder(Alphabet.create("abcdefghijklmnopqrst")); + final String wordToTest1 = "asia"; + //letter 'a' will be added at the end + final UnitBitSet bitSet = encoder.encode(wordToTest1, true); + assertEquals(20, encoder.getAlphabet().getLastIndexUsed()); + assertEquals(4, bitSet.getUnitValue(0)); + assertEquals(1, bitSet.getUnitValue(1)); + assertEquals(1, bitSet.getUnitValue(2)); + assertEquals(19, bitSet.getUnitValue(3)); + assertEquals(9, bitSet.getUnitValue(4)); + assertEquals(1, bitSet.getUnitValue(5)); + + assertEquals(wordToTest1, encoder.decode(bitSet)); + + + } + + +} diff --git a/plugins/spellchecker/testSrc/com/intellij/spellchecker/compress/UnitBitSetTests.java b/plugins/spellchecker/testSrc/com/intellij/spellchecker/compress/UnitBitSetTests.java new file mode 100644 index 000000000000..90234b66d753 --- /dev/null +++ b/plugins/spellchecker/testSrc/com/intellij/spellchecker/compress/UnitBitSetTests.java @@ -0,0 +1,62 @@ +package com.intellij.spellchecker.compress; + +import com.intellij.spellchecker.compress.UnitBitSet; +import junit.framework.TestCase; + +public class UnitBitSetTests extends TestCase { + + public void testInc0() { + UnitBitSet bs = UnitBitSet.create(); + bs.inc(); + assertEquals(UnitBitSet.create(0), bs); + } + + public void testInc1() { + UnitBitSet bs = UnitBitSet.create(0); + bs.inc(); + assertEquals(UnitBitSet.create(1), bs); + } + + public void testInc2() { + UnitBitSet bs = UnitBitSet.create(1); + bs.inc(); + assertEquals(UnitBitSet.create(0, 1), bs); + } + + public void testUnitValue() { + int bitsPerUnit = 256; + for (int i = 0; i < bitsPerUnit-1; i++) { + UnitBitSet bs = UnitBitSet.create(bitsPerUnit); + bs.setUnitValue(0, i); + assertEquals(i, bs.getUnitValue(0)); + assertEquals(0, bs.getUnitValue(1)); + } + } + + public void testCreateFromBitSet() { + UnitBitSet bs1 = UnitBitSet.create(1,5,10); + UnitBitSet bs2 = UnitBitSet.create(bs1,7); + assertEquals(bs2,bs1); + } + + public void testMoveLeft() { + UnitBitSet bs1 = UnitBitSet.create(3,true, 1,3,5,10); + bs1.moveLeft(2); + assertEquals(UnitBitSet.create(3,true,4),bs1); + } + + public void testMoveLeft2() { + UnitBitSet bs1 = UnitBitSet.create(10,true, 1,3,5,10); + bs1.moveLeft(2); + assertEquals(UnitBitSet.create(10,true),bs1); + } + + public void testMoveRight() { + UnitBitSet bs1 = UnitBitSet.create(3,true, 1,3,5,10); + bs1.moveRight(2); + assertEquals(UnitBitSet.create(3,true,7,9,11,16),bs1); + } + + + +} diff --git a/plugins/spellchecker/testSrc/com/intellij/spellchecker/inspection/SpellcheckerInspectionTestCase.java b/plugins/spellchecker/testSrc/com/intellij/spellchecker/inspection/SpellcheckerInspectionTestCase.java index a1086228b155..4851a41d26de 100644 --- a/plugins/spellchecker/testSrc/com/intellij/spellchecker/inspection/SpellcheckerInspectionTestCase.java +++ b/plugins/spellchecker/testSrc/com/intellij/spellchecker/inspection/SpellcheckerInspectionTestCase.java @@ -19,6 +19,7 @@ import com.intellij.codeInspection.LocalInspectionTool; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.application.PluginPathManager; import com.intellij.openapi.util.io.FileUtil; + import com.intellij.testFramework.fixtures.CodeInsightFixtureTestCase; import org.jetbrains.annotations.NonNls; diff --git a/plugins/spellchecker/testSrc/com/intellij/spellchecker/inspector/SplitterTest.java b/plugins/spellchecker/testSrc/com/intellij/spellchecker/inspector/SplitterTest.java index b76cb2fcedbc..f7e45e32f626 100644 --- a/plugins/spellchecker/testSrc/com/intellij/spellchecker/inspector/SplitterTest.java +++ b/plugins/spellchecker/testSrc/com/intellij/spellchecker/inspector/SplitterTest.java @@ -22,6 +22,10 @@ import junit.framework.TestCase; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -81,7 +85,7 @@ public class SplitterTest extends TestCase { } - public void testWordWithApostrophe4() { + public void testWordWithApostrophe4() { String text = "we'll"; List checkAreas = TextSplitter.splitText(text); correctListToCheck(checkAreas, text, new String[]{"we'll"}); @@ -91,64 +95,64 @@ public class SplitterTest extends TestCase { public void testWordWithApostrophe5() { String text = "I'm you're we'll"; List checkAreas = TextSplitter.splitText(text); - correctListToCheck(checkAreas, text, new String[]{"you're","we'll"}); + correctListToCheck(checkAreas, text, new String[]{"you're", "we'll"}); correctIgnored(checkAreas, text, new String[]{"I'm"}); } public void testConstantName() { String text = "TEST_CONSTANT"; List checkAreas = TextSplitter.splitText(text); - correctListToCheck(checkAreas, text, new String[]{"TEST","CONSTANT"}); + correctListToCheck(checkAreas, text, new String[]{"TEST", "CONSTANT"}); correctIgnored(checkAreas, text, new String[]{}); } public void testLongConstantName() { String text = "TEST_VERY_VERY_LONG_AND_COMPLEX_CONSTANT"; List checkAreas = TextSplitter.splitText(text); - correctListToCheck(checkAreas, text, new String[]{"TEST","VERY","VERY","LONG","COMPLEX","CONSTANT"}); + correctListToCheck(checkAreas, text, new String[]{"TEST", "VERY", "VERY", "LONG", "COMPLEX", "CONSTANT"}); correctIgnored(checkAreas, text, new String[]{"AND"}); } - public void testJavaComments() { + public void testJavaComments() { String text = "/*special symbols*/"; List checkAreas = TextSplitter.splitText(text); - correctListToCheck(checkAreas, text, new String[]{"special","symbols"}); + correctListToCheck(checkAreas, text, new String[]{"special", "symbols"}); correctIgnored(checkAreas, text, new String[]{}); } public void testXmlComments() { - String text = ""; - List checkAreas = TextSplitter.splitText(text); - correctListToCheck(checkAreas, text, new String[]{"special","symbols"}); - correctIgnored(checkAreas, text, new String[]{}); - } + String text = ""; + List checkAreas = TextSplitter.splitText(text); + correctListToCheck(checkAreas, text, new String[]{"special", "symbols"}); + correctIgnored(checkAreas, text, new String[]{}); + } public void testCamelCaseInXmlComments() { - String text = ""; - List checkAreas = TextSplitter.splitText(text); - correctListToCheck(checkAreas, text, new String[]{"special","Case","symbols"}); - correctIgnored(checkAreas, text, new String[]{}); - } + String text = ""; + List checkAreas = TextSplitter.splitText(text); + correctListToCheck(checkAreas, text, new String[]{"special", "Case", "symbols"}); + correctIgnored(checkAreas, text, new String[]{}); + } public void testWordsWithNumbers() { - String text = "testCamelCase123"; - List checkAreas = TextSplitter.splitText(text); - correctListToCheck(checkAreas, text, new String[]{"test","Camel","Case"}); - correctIgnored(checkAreas, text, new String[]{}); - } + String text = "testCamelCase123"; + List checkAreas = TextSplitter.splitText(text); + correctListToCheck(checkAreas, text, new String[]{"test", "Camel", "Case"}); + correctIgnored(checkAreas, text, new String[]{}); + } public void testCommentsWithWordsWithNumbers() { String text = ""; List checkAreas = TextSplitter.splitText(text); - correctListToCheck(checkAreas, text, new String[]{"special","Case","symbols"}); + correctListToCheck(checkAreas, text, new String[]{"special", "Case", "symbols"}); correctIgnored(checkAreas, text, new String[]{}); } public void testCommentsWithAbr() { String text = ""; List checkAreas = TextSplitter.splitText(text); - correctListToCheck(checkAreas, text, new String[]{"Test","Class"}); + correctListToCheck(checkAreas, text, new String[]{"Test", "Class"}); correctIgnored(checkAreas, text, new String[]{"JSP"}); } @@ -162,7 +166,7 @@ public class SplitterTest extends TestCase { public void testCommentWithHtml() { String text = ""; List checkAreas = TextSplitter.splitText(text); - correctListToCheck(checkAreas, text, new String[]{"something","here","foooo","barrrr","text", "text"}); + correctListToCheck(checkAreas, text, new String[]{"something", "here", "foooo", "barrrr", "text", "text"}); correctIgnored(checkAreas, text, new String[]{"go"}); } @@ -177,68 +181,103 @@ public class SplitterTest extends TestCase { String text = "bgColor carLight"; List checkAreas = TextSplitter.splitText(text); correctListToCheck(checkAreas, text, new String[]{"Color", "Light"}); - correctIgnored(checkAreas, text, new String[]{"bg","car"}); + correctIgnored(checkAreas, text, new String[]{"bg", "car"}); } public void testComplex() { - String text = "shkate@gmail.com"; - List checkAreas = TextSplitter.splitText(text); - correctListToCheck(checkAreas, text, new String[]{}); - correctIgnored(checkAreas, text, new String[]{}); - } + String text = "shkate@gmail.com"; + List checkAreas = TextSplitter.splitText(text); + correctListToCheck(checkAreas, text, new String[]{}); + correctIgnored(checkAreas, text, new String[]{}); + } - public void testWordBeforeDelimeter() { + public void testWordBeforeDelimiter() { String text = "badd,"; List checkAreas = TextSplitter.splitText(text); correctListToCheck(checkAreas, text, new String[]{"badd"}); correctIgnored(checkAreas, text, new String[]{}); } - public void testWordAfterDelimeter() { + + public void testWordAfterDelimiter() { String text = ",badd"; List checkAreas = TextSplitter.splitText(text); correctListToCheck(checkAreas, text, new String[]{"badd"}); correctIgnored(checkAreas, text, new String[]{}); } - public void testWordInCapsBeforeDelimeter() { - String text = "BADD,"; - List checkAreas = TextSplitter.splitText(text); - correctListToCheck(checkAreas, text, new String[]{"BADD"}); - correctIgnored(checkAreas, text, new String[]{}); - } - public void testWordInCapsAfterDelimeter() { - String text = ",BADD"; - List checkAreas = TextSplitter.splitText(text); - correctListToCheck(checkAreas, text, new String[]{"BADD"}); - correctIgnored(checkAreas, text, new String[]{}); - } - public void testWordInCapsAfterDelimeter2() { - String text = "BADD;"; - List checkAreas = TextSplitter.splitText(text); - correctListToCheck(checkAreas, text, new String[]{"BADD"}); - correctIgnored(checkAreas, text, new String[]{}); - } - public void testWordInCapsAfterDelimeter3() { - String text = ";BADD;"; - List checkAreas = TextSplitter.splitText(text); - correctListToCheck(checkAreas, text, new String[]{"BADD"}); - correctIgnored(checkAreas, text, new String[]{}); - } + public void testWordInCapsBeforeDelimiter() { + String text = "BADD,"; + List checkAreas = TextSplitter.splitText(text); + correctListToCheck(checkAreas, text, new String[]{"BADD"}); + correctIgnored(checkAreas, text, new String[]{}); + } + + public void testWordInCapsAfterDelimiter() { + String text = ",BADD"; + List checkAreas = TextSplitter.splitText(text); + correctListToCheck(checkAreas, text, new String[]{"BADD"}); + correctIgnored(checkAreas, text, new String[]{}); + } + + public void testWordInCapsAfterDelimiter2() { + String text = "BADD;"; + List checkAreas = TextSplitter.splitText(text); + correctListToCheck(checkAreas, text, new String[]{"BADD"}); + correctIgnored(checkAreas, text, new String[]{}); + } + + public void testWordInCapsAfterDelimiter3() { + String text = ";BADD;"; + List checkAreas = TextSplitter.splitText(text); + correctListToCheck(checkAreas, text, new String[]{"BADD"}); + correctIgnored(checkAreas, text, new String[]{}); + } + + public void testWordWithUmlauts() { + String text = "rechtsb\u00FCndig"; + List checkAreas = TextSplitter.splitText(text); + correctListToCheck(checkAreas, text, new String[]{text}); + correctIgnored(checkAreas, text, new String[]{}); + } - public void testWordWithUmlauts() { - String text = "rechtsb\u00FCndig"; - List checkAreas = TextSplitter.splitText(text); - correctListToCheck(checkAreas, text, new String[]{text}); - correctIgnored(checkAreas, text, new String[]{}); - } - public void testWordUpperCasedWithUmlauts() { - String text = "RECHTSB\u00DCNDIG"; - List checkAreas = TextSplitter.splitText(text); - correctListToCheck(checkAreas, text, new String[]{text}); - correctIgnored(checkAreas, text, new String[]{}); - } + String text = "RECHTSB\u00DCNDIG"; + List checkAreas = TextSplitter.splitText(text); + correctListToCheck(checkAreas, text, new String[]{text}); + correctIgnored(checkAreas, text, new String[]{}); + } + + /* public void testWordsWithDots() { + String text = "properties.test.properties"; + List checkAreas = TextSplitter.splitText(text); + correctListToCheck(checkAreas, text, new String[]{"properties", "test", "properties"}); + correctIgnored(checkAreas, text, new String[]{}); + }*/ + + public void testWordUpperCasedWithUmlautsInTheBeginning() { + String text = "\u00DCNDIG"; + List checkAreas = TextSplitter.splitText(text); + correctListToCheck(checkAreas, text, new String[]{text}); + correctIgnored(checkAreas, text, new String[]{}); + } + + public void testNoWordsInHugeBinaryFile() { + final InputStream stream = SplitterTest.class.getResourceAsStream("crt.txt"); + + String text = convertStreamToString(stream); + List checkAreas = TextSplitter.splitText(text); + correctListToCheck(checkAreas, text, new String[]{}); + } + + public void testNoWordsInHugeBinaryWithXmlFile() { + final InputStream stream = SplitterTest.class.getResourceAsStream("contents.txt"); + String text = convertStreamToString(stream); + List checkAreas = TextSplitter.splitText(text); + List words = wordsToCheck(checkAreas, text); + assertNotNull(words); + assertEquals(45284, words.size()); + } @Nullable @@ -254,7 +293,7 @@ public class SplitterTest extends TestCase { } - @Nullable + @Nullable private static List wordsToIgnore(List toCheck, String text) { if (text == null || toCheck == null) return null; List words = new ArrayList(); @@ -275,7 +314,7 @@ public class SplitterTest extends TestCase { Assert.assertNotNull(words); Assert.assertEquals(expected.length, words.size()); List expectedWords = Arrays.asList(expected); - Assert.assertEquals( expectedWords,words); + Assert.assertEquals(expectedWords, words); } } @@ -288,7 +327,36 @@ public class SplitterTest extends TestCase { Assert.assertNotNull(words); Assert.assertEquals(expected.length, words.size()); List expectedWords = Arrays.asList(expected); - Assert.assertEquals( expectedWords,words); + Assert.assertEquals(expectedWords, words); + } + } + + private String convertStreamToString(InputStream is) { + if (is != null) { + StringBuilder sb = new StringBuilder(); + String line; + + try { + BufferedReader reader = new BufferedReader(new InputStreamReader(is, "UTF-8")); + while ((line = reader.readLine()) != null) { + sb.append(line).append("\n"); + } + } + catch (Exception e) { + throw new RuntimeException(e); + } + finally { + try { + is.close(); + } + catch (IOException ignore) { + + } + } + return sb.toString(); + } + else { + return ""; } } } diff --git a/plugins/spellchecker/testSrc/com/intellij/spellchecker/inspector/contents.txt b/plugins/spellchecker/testSrc/com/intellij/spellchecker/inspector/contents.txt new file mode 100644 index 0000000000000000000000000000000000000000..21733e0fda154583d357facd9054b7631d9a8c84 GIT binary patch literal 1002537 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

    A36 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 O6C3uhuMsOmRDS^9{PD*C literal 0 HcmV?d00001 diff --git a/plugins/spellchecker/testSrc/com/intellij/spellchecker/inspector/crt.txt b/plugins/spellchecker/testSrc/com/intellij/spellchecker/inspector/crt.txt new file mode 100644 index 0000000000000000000000000000000000000000..03c3883d613fd02d5b99ff2cc5eb6e0fd4a967be GIT binary patch literal 7655 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 Date: Mon, 15 Mar 2010 19:40:24 +0300 Subject: [PATCH 88/93] IDEA-12595 --- .../impl/analysis/HighlightMethodUtil.java | 83 +++++++++++++- .../xml/util/CheckEmptyTagInspection.java | 107 +++++++++--------- 2 files changed, 133 insertions(+), 57 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java index c34393b8415a..7087c9eb2c17 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java @@ -32,11 +32,13 @@ import com.intellij.codeInsight.intention.QuickFixFactory; import com.intellij.openapi.project.IndexNotReadyException; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.infos.CandidateInfo; import com.intellij.psi.infos.MethodCandidateInfo; import com.intellij.psi.util.*; import com.intellij.xml.util.XmlStringUtil; +import org.intellij.lang.annotations.Language; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -558,7 +560,7 @@ public class HighlightMethodUtil { return createMismatchedArgumentsHtmlTooltip(list, parameters, methodName, substitutor, aClass); } - private static String createMismatchedArgumentsHtmlTooltip(PsiExpressionList list, + private static String createShortMismatchedArgumentsHtmlTooltip(PsiExpressionList list, PsiParameter[] parameters, String methodName, PsiSubstitutor substitutor, @@ -572,11 +574,86 @@ public class HighlightMethodUtil { Integer.valueOf(cols - parameters.length + 1), parensizedName, HighlightUtil.formatClass(aClass, false), createMismatchedArgsHtmlTooltipParamsRow(parameters, substitutor, expressions), - createMismatchedArgsHtmlTooltipExpressionsRow(expressions, parameters, substitutor, cols) + createMismatchedArgsHtmlTooltipArgumentsRow(expressions, parameters, substitutor, cols) ); } - private static String createMismatchedArgsHtmlTooltipExpressionsRow(final PsiExpression[] expressions, final PsiParameter[] parameters, + private static String esctrim(@NotNull String s) { + return StringUtil.first(XmlStringUtil.escapeString(s), 40, true); + } + + private static String createMismatchedArgumentsHtmlTooltip(PsiExpressionList list, + PsiParameter[] parameters, + String methodName, + PsiSubstitutor substitutor, + PsiClass aClass) { + if (Math.max(parameters.length, list.getExpressions().length) <= 2) { + return createShortMismatchedArgumentsHtmlTooltip(list, parameters, methodName, substitutor, aClass); + } + else { + return createLongMismatchedArgumentsHtmlTooltip(list, parameters, methodName, substitutor, aClass); + } + } + + private static String createLongMismatchedArgumentsHtmlTooltip(PsiExpressionList list, + PsiParameter[] parameters, + String methodName, + PsiSubstitutor substitutor, + PsiClass aClass) { + PsiExpression[] expressions = list.getExpressions(); + + @NonNls @Language("HTML") + String s = "" + + " "+ + " " + +" " + ; + + + for (int i = 0; i < Math.max(parameters.length,expressions.length); i++) { + PsiParameter parameter = i < parameters.length ? parameters[i] : null; + PsiExpression expression = i < expressions.length ? expressions[i] : null; + @NonNls String mismatchColor = showShortType(i, parameters, expressions, substitutor) ? null : "red"; + + s += ""; + s += ""; + + s += ""; + + s += ""; + + s += ""; + } + + s+= "
    " + methodName + "() in " + HighlightUtil.formatClass(aClass, false) +" cannot be applied to:
    Expected
    Parameters:
    Actual
    Arguments:

    "; + if (parameter != null) { + s += esctrim(parameter.getName()) +":"; + } + s += ""; + if (parameter != null) { + PsiType type = substitutor.substitute(parameter.getType()); + s += "" + + esctrim(showShortType(i, parameters, expressions, substitutor) + ? type.getPresentableText() + : HighlightUtil.formatType(type)) + + "" + ; + } + s += ""; + if (expression != null) { + PsiType type = expression.getType(); + s += "" + + esctrim(expression.getText()) + "  "+ + (mismatchColor == null || type == null || type == PsiType.NULL ? "" : "("+esctrim(HighlightUtil.formatType(type))+")") + + "" + ; + + } + s += "
    "; + return s; + } + + private static String createMismatchedArgsHtmlTooltipArgumentsRow(final PsiExpression[] expressions, final PsiParameter[] parameters, final PsiSubstitutor substitutor, final int cols) { @NonNls String ms = ""; for (int i = 0; i < expressions.length; i++) { diff --git a/xml/impl/src/com/intellij/xml/util/CheckEmptyTagInspection.java b/xml/impl/src/com/intellij/xml/util/CheckEmptyTagInspection.java index 76e1624c89cd..728e91eab011 100644 --- a/xml/impl/src/com/intellij/xml/util/CheckEmptyTagInspection.java +++ b/xml/impl/src/com/intellij/xml/util/CheckEmptyTagInspection.java @@ -49,7 +49,7 @@ import java.util.Set; public class CheckEmptyTagInspection extends XmlSuppressableInspectionTool { private static final Logger LOG = Logger.getInstance("#com.intellij.xml.util.CheckEmptyTagInspection"); @NonNls private static final String SCRIPT_TAG_NAME = "script"; - private static Set ourTagsWithEmptyEndsNotAllowed = new THashSet(Arrays.asList(SCRIPT_TAG_NAME, "div", "iframe")); + private static final Set ourTagsWithEmptyEndsNotAllowed = new THashSet(Arrays.asList(SCRIPT_TAG_NAME, "div", "iframe")); public boolean isEnabledByDefault() { return true; @@ -59,55 +59,57 @@ public class CheckEmptyTagInspection extends XmlSuppressableInspectionTool { public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) { return new XmlElementVisitor() { @Override public void visitXmlTag(final XmlTag tag) { - if (isTagWithEmptyEndNotAllowed(tag)) { - final ASTNode child = XmlChildRole.EMPTY_TAG_END_FINDER.findChild(tag.getNode()); - - if (child != null) { - - final LocalQuickFix fix = new LocalQuickFix() { - @NotNull - public String getName() { - return XmlBundle.message("html.inspections.check.empty.script.tag.fix.message"); - } - - public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { - final XmlTag tag = (XmlTag)descriptor.getPsiElement(); - if (tag == null) return; - final PsiFile psiFile = tag.getContainingFile(); - - if (psiFile == null) return; - ReadonlyStatusHandler.getInstance(project).ensureFilesWritable(psiFile.getVirtualFile()); - - final StringBuilder builder = new StringBuilder(tag.getText()); - builder.replace(builder.length() - 2, builder.length(), ">"); - - try { - final FileType fileType = psiFile.getFileType(); - PsiFile file = PsiFileFactory.getInstance(tag.getProject()).createFileFromText( - "dummy." + (fileType == StdFileTypes.JSP || tag.getContainingFile().getLanguage() == HTMLLanguage.INSTANCE ? "html" : "xml"), builder.toString()); - - tag.replace(((XmlFile)file).getDocument().getRootTag()); - } - catch (IncorrectOperationException e) { - LOG.error(e); - } - } - - //to appear in "Apply Fix" statement when multiple Quick Fixes exist - @NotNull - public String getFamilyName() { - return getName(); - } - }; - - holder.registerProblem(tag, - XmlBundle.message("html.inspections.check.empty.script.message"), - tag.getContainingFile().getContext() != null ? - ProblemHighlightType.INFORMATION: - ProblemHighlightType.GENERIC_ERROR_OR_WARNING, - fix); - } + if (!isTagWithEmptyEndNotAllowed(tag)) { + return; } + final ASTNode child = XmlChildRole.EMPTY_TAG_END_FINDER.findChild(tag.getNode()); + + if (child == null) { + return; + } + + final LocalQuickFix fix = new LocalQuickFix() { + @NotNull + public String getName() { + return XmlBundle.message("html.inspections.check.empty.script.tag.fix.message"); + } + + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + final XmlTag tag = (XmlTag)descriptor.getPsiElement(); + if (tag == null) return; + final PsiFile psiFile = tag.getContainingFile(); + + if (psiFile == null) return; + ReadonlyStatusHandler.getInstance(project).ensureFilesWritable(psiFile.getVirtualFile()); + + final StringBuilder builder = new StringBuilder(tag.getText()); + builder.replace(builder.length() - 2, builder.length(), ">"); + + try { + final FileType fileType = psiFile.getFileType(); + PsiFile file = PsiFileFactory.getInstance(tag.getProject()).createFileFromText( + "dummy." + (fileType == StdFileTypes.JSP || tag.getContainingFile().getLanguage() == HTMLLanguage.INSTANCE ? "html" : "xml"), builder.toString()); + + tag.replace(((XmlFile)file).getDocument().getRootTag()); + } + catch (IncorrectOperationException e) { + LOG.error(e); + } + } + + //to appear in "Apply Fix" statement when multiple Quick Fixes exist + @NotNull + public String getFamilyName() { + return getName(); + } + }; + + holder.registerProblem(tag, + XmlBundle.message("html.inspections.check.empty.script.message"), + tag.getContainingFile().getContext() != null ? + ProblemHighlightType.INFORMATION: + ProblemHighlightType.GENERIC_ERROR_OR_WARNING, + fix); } }; } @@ -117,11 +119,8 @@ public class CheckEmptyTagInspection extends XmlSuppressableInspectionTool { if (tag instanceof HtmlTag) tagName = tagName.toLowerCase(); Language language = tag.getLanguage(); - return (ourTagsWithEmptyEndsNotAllowed.contains(tagName) && - language != XMLLanguage.INSTANCE) || - (language == HTMLLanguage.INSTANCE && - ( !HtmlUtil.isSingleHtmlTagL(tagName) && tagName.indexOf(':') == -1)) - ; + return ourTagsWithEmptyEndsNotAllowed.contains(tagName) && language != XMLLanguage.INSTANCE || + language == HTMLLanguage.INSTANCE && !HtmlUtil.isSingleHtmlTagL(tagName) && tagName.indexOf(':') == -1; } @NotNull From a25bfc758762f60ef58e3f9d04cf77b4fa87aeb7 Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Tue, 16 Mar 2010 10:23:31 +0300 Subject: [PATCH 89/93] -proper semaphore usage: down() always precedes up(); -execute up() in finally block --- .../ui/impl/watch/ValueDescriptorImpl.java | 8 +++- .../execution/process/OSProcessHandler.java | 11 +++--- .../util/process/ProcessCloseUtil.java | 39 +++++++++++-------- .../progress/util/SmoothProgressAdapter.java | 8 +++- .../openapi/project/DumbServiceImpl.java | 8 ++-- .../vcs/update/RefreshVFsSynchronously.java | 10 +++-- .../idea/svn/SvnCopiesRefreshManager.java | 2 +- 7 files changed, 53 insertions(+), 33 deletions(-) diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ValueDescriptorImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ValueDescriptorImpl.java index 64f8259bb958..21b0febf54e9 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ValueDescriptorImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ValueDescriptorImpl.java @@ -110,8 +110,12 @@ public abstract class ValueDescriptorImpl extends NodeDescriptorImpl implements myStoredEvaluationContext.getDebugProcess().getManagerThread().invoke(new SuspendContextCommandImpl(myStoredEvaluationContext.getSuspendContext()) { public void contextAction() throws Exception { // re-setting the context will cause value recalculation - setContext(myStoredEvaluationContext); - semaphore.up(); + try { + setContext(myStoredEvaluationContext); + } + finally { + semaphore.up(); + } } }); semaphore.waitFor(); diff --git a/platform/platform-api/src/com/intellij/execution/process/OSProcessHandler.java b/platform/platform-api/src/com/intellij/execution/process/OSProcessHandler.java index bdc7a7dfd412..8668e4d292a7 100644 --- a/platform/platform-api/src/com/intellij/execution/process/OSProcessHandler.java +++ b/platform/platform-api/src/com/intellij/execution/process/OSProcessHandler.java @@ -91,18 +91,19 @@ public class OSProcessHandler extends ProcessHandler { public ProcessWaitFor(final Process process) { myWaitSemaphore.down(); - final Runnable action = new Runnable() { + + myWaitForThreadFuture = executeOnPooledThread(new Runnable() { public void run() { try { myExitCode = process.waitFor(); } catch (InterruptedException ignored) { } - myWaitSemaphore.up(); + finally { + myWaitSemaphore.up(); + } } - }; - - myWaitForThreadFuture = executeOnPooledThread(action); + }); } public int waitFor() { diff --git a/platform/platform-api/src/com/intellij/openapi/util/process/ProcessCloseUtil.java b/platform/platform-api/src/com/intellij/openapi/util/process/ProcessCloseUtil.java index 7081c5fa1beb..6f6db5543ffd 100644 --- a/platform/platform-api/src/com/intellij/openapi/util/process/ProcessCloseUtil.java +++ b/platform/platform-api/src/com/intellij/openapi/util/process/ProcessCloseUtil.java @@ -19,8 +19,8 @@ import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.util.concurrency.Semaphore; -import java.util.concurrent.Future; import java.io.IOException; +import java.util.concurrent.Future; public class ProcessCloseUtil { private static final long ourSynchronousWaitTimeout = 1000; @@ -34,25 +34,32 @@ public class ProcessCloseUtil { outerSemaphore.down(); final Application application = ApplicationManager.getApplication(); - final Future future = application.executeOnPooledThread(new Runnable() { + application.executeOnPooledThread(new Runnable() { public void run() { - final Semaphore semaphore = new Semaphore(); - semaphore.down(); + try { + final Semaphore semaphore = new Semaphore(); + semaphore.down(); - final Runnable closeRunnable = new Runnable() { - public void run() { - closeProcessImpl(process); - semaphore.up(); + final Runnable closeRunnable = new Runnable() { + public void run() { + try { + closeProcessImpl(process); + } + finally { + semaphore.up(); + } + } + }; + + final Future innerFuture = application.executeOnPooledThread(closeRunnable); + semaphore.waitFor(ourAsynchronousWaitTimeout); + if ( ! (innerFuture.isDone() || innerFuture.isCancelled())) { + innerFuture.cancel(true); // will call interrupt() } - }; - - final Future innerFuture = application.executeOnPooledThread(closeRunnable); - semaphore.waitFor(ourAsynchronousWaitTimeout); - if ( ! (innerFuture.isDone() || innerFuture.isCancelled())) { - innerFuture.cancel(true); // will call interrupt() } - - outerSemaphore.up(); + finally { + outerSemaphore.up(); + } } }); diff --git a/platform/platform-impl/src/com/intellij/openapi/progress/util/SmoothProgressAdapter.java b/platform/platform-impl/src/com/intellij/openapi/progress/util/SmoothProgressAdapter.java index 7b22b3b1e736..950d6ea57978 100644 --- a/platform/platform-impl/src/com/intellij/openapi/progress/util/SmoothProgressAdapter.java +++ b/platform/platform-impl/src/com/intellij/openapi/progress/util/SmoothProgressAdapter.java @@ -142,8 +142,12 @@ public class SmoothProgressAdapter extends BlockingProgressIndicator { } ); - super.stop(); // should be last to not leaveModal before closing the dialog - semaphore.up(); + try { + super.stop(); // should be last to not leaveModal before closing the dialog + } + finally { + semaphore.up(); + } } public synchronized void setText(String text) { diff --git a/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.java b/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.java index 4e4a68361ce1..24005c0169ec 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.java @@ -227,10 +227,10 @@ public class DumbServiceImpl extends DumbService { final Semaphore semaphore = new Semaphore(); semaphore.down(); runWhenSmart(new Runnable() { - public void run() { - semaphore.up(); - } - }); + public void run() { + semaphore.up(); + } + }); semaphore.waitFor(); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/update/RefreshVFsSynchronously.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/update/RefreshVFsSynchronously.java index 8852e9acf99f..74465ff77ed1 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/update/RefreshVFsSynchronously.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/update/RefreshVFsSynchronously.java @@ -203,9 +203,13 @@ public class RefreshVFsSynchronously { ApplicationManager.getApplication().invokeLater(new Runnable() { public void run() { - // common lock for all refreshes inside - runnable.run(); - semaphore.up(); + try { + // common lock for all refreshes inside + runnable.run(); + } + finally { + semaphore.up(); + } } }); semaphore.waitFor(); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnCopiesRefreshManager.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnCopiesRefreshManager.java index 7d05232726f5..8610983764e6 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnCopiesRefreshManager.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnCopiesRefreshManager.java @@ -80,12 +80,12 @@ public class SvnCopiesRefreshManager { semaphore.up(); } }; + semaphore.down(); if (isOnlyInit) { myRequestMerger.ensureInitialization(waiter); } else { myRequestMerger.waitRefresh(waiter); } - semaphore.down(); while (true) { if (semaphore.waitFor(ourQueryInterval)) break; if (pi != null) { From 1df0743bbb1bc93ea3c0248be26e10a447e0e6ed Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Tue, 16 Mar 2010 11:18:15 +0300 Subject: [PATCH 90/93] optimization: check for parameters number first --- .../JavaMethodsConflictResolver.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/java/java-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java b/java/java-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java index 1ca3b4b5de97..24d624ff2920 100644 --- a/java/java-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java +++ b/java/java-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java @@ -56,20 +56,23 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{ public CandidateInfo resolveConflict(List conflicts){ if (conflicts.isEmpty()) return null; if (conflicts.size() == 1) return conflicts.get(0); + + checkParametersNumber(conflicts, myActualParameterTypes.length, true); + if (conflicts.size() == 1) return conflicts.get(0); + checkSameSignatures(conflicts); - if (conflicts.size() == 1) return conflicts.get(0); + checkAccessLevels(conflicts); - if (conflicts.size() == 1) return conflicts.get(0); - checkParametersNumber(conflicts, myActualParameterTypes.length); + checkParametersNumber(conflicts, myActualParameterTypes.length, false); if (conflicts.size() == 1) return conflicts.get(0); final int applicabilityLevel = checkApplicability(conflicts); if (conflicts.size() == 1) return conflicts.get(0); - checkSpecifics(conflicts, applicabilityLevel); + checkSpecifics(conflicts, applicabilityLevel); if (conflicts.size() == 1) return conflicts.get(0); THashSet uniques = new THashSet(conflicts); @@ -196,9 +199,12 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{ return ((MethodCandidateInfo)info).isApplicable(); } - private static void checkParametersNumber(final List conflicts, final int argumentsCount) { + private static void checkParametersNumber(final List conflicts, + final int argumentsCount, + boolean checkForStaticAccessProblem) { boolean parametersNumberMatch = false; for (CandidateInfo info : conflicts) { + if (checkForStaticAccessProblem && !info.isStaticsScopeCorrect()) return; if (info instanceof MethodCandidateInfo) { final PsiMethod method = ((MethodCandidateInfo)info).getElement(); if (method.isVarArgs()) return; From 789e80c52368c05a78f591f3c4a87167bc332a3d Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Tue, 16 Mar 2010 11:18:58 +0300 Subject: [PATCH 91/93] IDEA-36812 --- .../find/actions/ShowUsagesAction.java | 4 +- .../intellij/usages/UsageViewSettings.java | 1 + .../impl/UsageGroupingRuleProviderImpl.java | 22 +++- .../impl/rules/UsageScopeGroupingRule.java | 101 ++++++++++++++++++ .../intellij/usages/impl/rules/UsageType.java | 2 +- .../impl/rules/UsageTypeGroupingRule.java | 3 - 6 files changed, 126 insertions(+), 7 deletions(-) create mode 100644 platform/usageView/src/com/intellij/usages/impl/rules/UsageScopeGroupingRule.java diff --git a/platform/lang-impl/src/com/intellij/find/actions/ShowUsagesAction.java b/platform/lang-impl/src/com/intellij/find/actions/ShowUsagesAction.java index 14be608e3edb..64110e91224d 100644 --- a/platform/lang-impl/src/com/intellij/find/actions/ShowUsagesAction.java +++ b/platform/lang-impl/src/com/intellij/find/actions/ShowUsagesAction.java @@ -184,6 +184,7 @@ public class ShowUsagesAction extends AnAction { usageViewSettings.GROUP_BY_MODULE = false; usageViewSettings.GROUP_BY_PACKAGE = false; usageViewSettings.GROUP_BY_USAGE_TYPE = false; + usageViewSettings.GROUP_BY_SCOPE = false; UsageViewManager manager = UsageViewManager.getInstance(handler.getProject()); final UsageViewImpl usageView = (UsageViewImpl)manager.createUsageView(UsageTarget.EMPTY_ARRAY, Usage.EMPTY_ARRAY, presentation, null); @@ -194,6 +195,7 @@ public class ShowUsagesAction extends AnAction { usageViewSettings.GROUP_BY_MODULE = save.GROUP_BY_MODULE; usageViewSettings.GROUP_BY_PACKAGE = save.GROUP_BY_PACKAGE; usageViewSettings.GROUP_BY_USAGE_TYPE = save.GROUP_BY_USAGE_TYPE; + usageViewSettings.GROUP_BY_SCOPE = save.GROUP_BY_SCOPE; } }); @@ -694,4 +696,4 @@ public class ShowUsagesAction extends AnAction { return null; } } -} \ No newline at end of file +} diff --git a/platform/usageView/src/com/intellij/usages/UsageViewSettings.java b/platform/usageView/src/com/intellij/usages/UsageViewSettings.java index cdaf24566a3e..a5de4380aa78 100644 --- a/platform/usageView/src/com/intellij/usages/UsageViewSettings.java +++ b/platform/usageView/src/com/intellij/usages/UsageViewSettings.java @@ -49,6 +49,7 @@ public class UsageViewSettings implements PersistentStateComponent rules = new ArrayList(); rules.add(new NonCodeUsageGroupingRule()); + if (UsageViewSettings.getInstance().GROUP_BY_SCOPE) { + rules.add(new UsageScopeGroupingRule()); + } if (UsageViewSettings.getInstance().GROUP_BY_USAGE_TYPE) { rules.add(new UsageTypeGroupingRule()); } @@ -88,6 +91,7 @@ public class UsageGroupingRuleProviderImpl implements UsageGroupingRuleProvider } }); + final GroupByScopeAction groupByScopeAction = new GroupByScopeAction(impl); if(view.getPresentation().isCodeUsages()) { final GroupByUsageTypeAction groupByUsageTypeAction = new GroupByUsageTypeAction(impl); groupByUsageTypeAction.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_T, InputEvent.CTRL_DOWN_MASK)), component); @@ -104,15 +108,17 @@ public class UsageGroupingRuleProviderImpl implements UsageGroupingRuleProvider return new AnAction[] { groupByUsageTypeAction, + groupByScopeAction, groupByModuleTypeAction, groupByPackageAction, - groupByFileStructureAction + groupByFileStructureAction, }; } else { return new AnAction[] { + groupByScopeAction, groupByModuleTypeAction, - groupByFileStructureAction + groupByFileStructureAction, }; } } @@ -129,6 +135,18 @@ public class UsageGroupingRuleProviderImpl implements UsageGroupingRuleProvider } } + private static class GroupByScopeAction extends RuleAction { + private GroupByScopeAction(UsageViewImpl view) { + super(view, "Group by test/production", Icons.TEST_SOURCE_FOLDER); + } + protected boolean getOptionValue() { + return UsageViewSettings.getInstance().GROUP_BY_SCOPE; + } + protected void setOptionValue(boolean value) { + UsageViewSettings.getInstance().GROUP_BY_SCOPE = value; + } + } + private static class GroupByModuleTypeAction extends RuleAction { private GroupByModuleTypeAction(UsageViewImpl view) { super(view, UsageViewBundle.message("action.group.by.module"), IconLoader.getIcon("/objectBrowser/showModules.png")); diff --git a/platform/usageView/src/com/intellij/usages/impl/rules/UsageScopeGroupingRule.java b/platform/usageView/src/com/intellij/usages/impl/rules/UsageScopeGroupingRule.java new file mode 100644 index 000000000000..77e667f01aef --- /dev/null +++ b/platform/usageView/src/com/intellij/usages/impl/rules/UsageScopeGroupingRule.java @@ -0,0 +1,101 @@ +/* + * Copyright 2000-2010 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.usages.impl.rules; + +import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.vcs.FileStatus; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiElement; +import com.intellij.psi.util.PsiUtilBase; +import com.intellij.usages.Usage; +import com.intellij.usages.UsageGroup; +import com.intellij.usages.UsageView; +import com.intellij.usages.rules.PsiElementUsage; +import com.intellij.usages.rules.UsageGroupingRule; +import com.intellij.util.Icons; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +/** + * @author max + */ +public class UsageScopeGroupingRule implements UsageGroupingRule { + public UsageGroup groupUsage(Usage usage) { + if (!(usage instanceof PsiElementUsage)) { + return null; + } + PsiElementUsage elementUsage = (PsiElementUsage)usage; + + PsiElement element = elementUsage.getElement(); + VirtualFile virtualFile = PsiUtilBase.getVirtualFile(element); + + if (virtualFile == null) { + return null; + } + boolean isInTest = ProjectRootManager.getInstance(element.getProject()).getFileIndex().isInTestSourceContent(virtualFile); + return isInTest ? TEST : PRODUCTION; + } + + private static final UsageScopeGroup TEST = new UsageScopeGroup(true); + private static final UsageScopeGroup PRODUCTION = new UsageScopeGroup(false); + private static class UsageScopeGroup implements UsageGroup { + private final boolean isTest; + + private UsageScopeGroup(boolean isTest) { + this.isTest = isTest; + } + + public void update() { + } + + public Icon getIcon(boolean isOpen) { + return isTest ? Icons.TEST_SOURCE_FOLDER : Icons.SOURCE_FOLDERS_ICON; + } + + @NotNull + public String getText(UsageView view) { + return isTest ? "Test" : "Production"; + } + + public FileStatus getFileStatus() { + return null; + } + + public boolean isValid() { return true; } + public void navigate(boolean focus) { } + public boolean canNavigate() { return false; } + + public boolean canNavigateToSource() { + return false; + } + + public int compareTo(UsageGroup usageGroup) { + return getText(null).compareTo(usageGroup.getText(null)); + } + + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof UsageScopeGroup)) return false; + final UsageScopeGroup usageTypeGroup = (UsageScopeGroup)o; + return isTest == usageTypeGroup.isTest; + } + + public int hashCode() { + return isTest ? 0 : 1; + } + } +} diff --git a/platform/usageView/src/com/intellij/usages/impl/rules/UsageType.java b/platform/usageView/src/com/intellij/usages/impl/rules/UsageType.java index 09b96d088862..3a720fd16a3e 100644 --- a/platform/usageView/src/com/intellij/usages/impl/rules/UsageType.java +++ b/platform/usageView/src/com/intellij/usages/impl/rules/UsageType.java @@ -17,7 +17,7 @@ package com.intellij.usages.impl.rules; import com.intellij.usageView.UsageViewBundle; -public class UsageType { +public final class UsageType { public static final UsageType CLASS_INSTANCE_OF = new UsageType(UsageViewBundle.message("usage.type.instanceof")); public static final UsageType CLASS_IMPORT = new UsageType(UsageViewBundle.message("usage.type.import")); public static final UsageType CLASS_CAST_TO = new UsageType(UsageViewBundle.message("usage.type.cast.target")); diff --git a/platform/usageView/src/com/intellij/usages/impl/rules/UsageTypeGroupingRule.java b/platform/usageView/src/com/intellij/usages/impl/rules/UsageTypeGroupingRule.java index fbd45e4e071f..fba4bdf0b878 100644 --- a/platform/usageView/src/com/intellij/usages/impl/rules/UsageTypeGroupingRule.java +++ b/platform/usageView/src/com/intellij/usages/impl/rules/UsageTypeGroupingRule.java @@ -72,9 +72,6 @@ public class UsageTypeGroupingRule implements UsageGroupingRule { return null; } - - - private class UsageTypeGroup implements UsageGroup { private final UsageType myUsageType; From 9dfcd7540403a4ddc898b428d57c0c1945bbd50b Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Tue, 16 Mar 2010 11:19:22 +0300 Subject: [PATCH 92/93] IDEA-34452 "No declaration found" feedback added --- .../navigation/actions/GotoDeclarationAction.java | 11 +++++++---- .../ide/util/DefaultPsiElementCellRenderer.java | 4 ++-- .../intellij/ide/util/PsiElementListCellRenderer.java | 3 +-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/navigation/actions/GotoDeclarationAction.java b/platform/lang-impl/src/com/intellij/codeInsight/navigation/actions/GotoDeclarationAction.java index 4978f3180d57..9d62e35ad33b 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/navigation/actions/GotoDeclarationAction.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/navigation/actions/GotoDeclarationAction.java @@ -20,6 +20,7 @@ import com.intellij.codeInsight.CodeInsightActionHandler; import com.intellij.codeInsight.CodeInsightBundle; import com.intellij.codeInsight.TargetElementUtilBase; import com.intellij.codeInsight.actions.BaseCodeInsightAction; +import com.intellij.codeInsight.hint.HintManager; import com.intellij.codeInsight.navigation.NavigationUtil; import com.intellij.featureStatistics.FeatureUsageTracker; import com.intellij.ide.util.DefaultPsiElementCellRenderer; @@ -65,10 +66,9 @@ public class GotoDeclarationAction extends BaseCodeInsightAction implements Code public void invoke(@NotNull final Project project, @NotNull Editor editor, @NotNull PsiFile file) { PsiDocumentManager.getInstance(project).commitAllDocuments(); - PsiElement element; try { int offset = editor.getCaretModel().getOffset(); - element = findTargetElement(project, editor, offset); + PsiElement element = findTargetElement(project, editor, offset); if (element == null) { FeatureUsageTracker.getInstance().triggerFeatureUsed("navigation.goto.declaration"); chooseAmbiguousTarget(editor, offset); @@ -109,7 +109,10 @@ public class GotoDeclarationAction extends BaseCodeInsightAction implements Code return true; } }; - chooseAmbiguousTarget(editor, offset,navigateProcessor, CodeInsightBundle.message("declaration.navigation.title")); + boolean found = chooseAmbiguousTarget(editor, offset, navigateProcessor, CodeInsightBundle.message("declaration.navigation.title")); + if (!found) { + HintManager.getInstance().showErrorHint(editor, "Cannot find declaration to go to"); + } } // returns true if processor is run or is going to be run after showing popup @@ -127,7 +130,7 @@ public class GotoDeclarationAction extends BaseCodeInsightAction implements Code processor.execute(element); return true; } - else if (candidates.size() > 1) { + if (candidates.size() > 1) { PsiElement[] elements = candidates.toArray(new PsiElement[candidates.size()]); final TextRange range = reference.getRangeInElement(); final String refText = range.substring(reference.getElement().getText()); diff --git a/platform/lang-impl/src/com/intellij/ide/util/DefaultPsiElementCellRenderer.java b/platform/lang-impl/src/com/intellij/ide/util/DefaultPsiElementCellRenderer.java index 4581b24eb125..ea90cb413355 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/DefaultPsiElementCellRenderer.java +++ b/platform/lang-impl/src/com/intellij/ide/util/DefaultPsiElementCellRenderer.java @@ -20,7 +20,7 @@ import com.intellij.openapi.util.Iconable; import com.intellij.psi.PsiElement; import com.intellij.psi.presentation.java.SymbolPresentationUtil; -public class DefaultPsiElementCellRenderer extends PsiElementListCellRenderer { +public class DefaultPsiElementCellRenderer extends PsiElementListCellRenderer { protected int getIconFlags() { return Iconable.ICON_FLAG_VISIBILITY; } @@ -33,4 +33,4 @@ public class DefaultPsiElementCellRenderer extends PsiElementListCellRenderer { return SymbolPresentationUtil.getSymbolContainerText(element); } -} \ No newline at end of file +} diff --git a/platform/lang-impl/src/com/intellij/ide/util/PsiElementListCellRenderer.java b/platform/lang-impl/src/com/intellij/ide/util/PsiElementListCellRenderer.java index 278cd1f1e4e2..893b28f93dac 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/PsiElementListCellRenderer.java +++ b/platform/lang-impl/src/com/intellij/ide/util/PsiElementListCellRenderer.java @@ -99,12 +99,11 @@ public abstract class PsiElementListCellRenderer extends J } } - SimpleTextAttributes nameAttributes; if (isProblemFile) { attributes = TextAttributes.merge(new TextAttributes(color, null, Color.red, EffectType.WAVE_UNDERSCORE, Font.PLAIN),attributes); } - nameAttributes = attributes != null ? SimpleTextAttributes.fromTextAttributes(attributes):null; + SimpleTextAttributes nameAttributes = attributes != null ? SimpleTextAttributes.fromTextAttributes(attributes) : null; if (nameAttributes == null) nameAttributes = new SimpleTextAttributes(Font.PLAIN, color); From 3db053005908ce4dfc2e92bedf5c90ca52c09a32 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Tue, 16 Mar 2010 13:13:01 +0300 Subject: [PATCH 93/93] PsiClass augmentation point moved; AspectJ module intergration restored --- .../src/com/intellij/psi/impl/PsiClassImplUtil.java | 9 +++++++++ .../src/com/intellij/psi/impl/source/PsiClassImpl.java | 10 ++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/java/java-impl/src/com/intellij/psi/impl/PsiClassImplUtil.java b/java/java-impl/src/com/intellij/psi/impl/PsiClassImplUtil.java index c9115d619abe..900283e9661f 100644 --- a/java/java-impl/src/com/intellij/psi/impl/PsiClassImplUtil.java +++ b/java/java-impl/src/com/intellij/psi/impl/PsiClassImplUtil.java @@ -21,6 +21,7 @@ import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.util.*; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; +import com.intellij.psi.augment.PsiAugmentProvider; import com.intellij.psi.filters.OrFilter; import com.intellij.psi.impl.compiled.ClsElementImpl; import com.intellij.psi.impl.source.PsiImmediateClassType; @@ -554,6 +555,10 @@ public class PsiClassImplUtil { if (!processor.execute(field, state)) return false; } } + + for (PsiField field : PsiAugmentProvider.collectAugments(aClass, PsiField.class)) { + if (!processor.execute(field, state)) return false; + } } if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclaractionKind.METHOD)) { @@ -566,6 +571,10 @@ public class PsiClassImplUtil { } if (!processor.execute(method, state)) return false; } + + for (PsiMethod method : PsiAugmentProvider.collectAugments(aClass, PsiMethod.class)) { + if (!processor.execute(method, state)) return false; + } } if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclaractionKind.CLASS)) { diff --git a/java/java-impl/src/com/intellij/psi/impl/source/PsiClassImpl.java b/java/java-impl/src/com/intellij/psi/impl/source/PsiClassImpl.java index f71306d568fc..9eb3954df378 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/PsiClassImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/PsiClassImpl.java @@ -26,7 +26,6 @@ import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; -import com.intellij.psi.augment.PsiAugmentProvider; import com.intellij.psi.impl.InheritanceImplUtil; import com.intellij.psi.impl.PsiClassImplUtil; import com.intellij.psi.impl.PsiImplUtil; @@ -51,7 +50,6 @@ import com.intellij.psi.stubs.IStubElementType; import com.intellij.psi.stubs.PsiFileStub; import com.intellij.psi.stubs.StubElement; import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -302,16 +300,12 @@ public class PsiClassImpl extends JavaStubPsiElement> implements @NotNull public PsiField[] getFields() { - final PsiField[] owns = getStubOrPsiChildren(Constants.FIELD_BIT_SET, PsiField.ARRAY_FACTORY); - final List augments = PsiAugmentProvider.collectAugments(this, PsiField.class); - return ArrayUtil.mergeArrayAndCollection(owns, augments, PsiField.ARRAY_FACTORY); + return getStubOrPsiChildren(Constants.FIELD_BIT_SET, PsiField.ARRAY_FACTORY); } @NotNull public PsiMethod[] getMethods() { - final PsiMethod[] owns = getStubOrPsiChildren(Constants.METHOD_BIT_SET, PsiMethod.ARRAY_FACTORY); - final List augments = PsiAugmentProvider.collectAugments(this, PsiMethod.class); - return ArrayUtil.mergeArrayAndCollection(owns, augments, PsiMethod.ARRAY_FACTORY); + return getStubOrPsiChildren(Constants.METHOD_BIT_SET, PsiMethod.ARRAY_FACTORY); } @NotNull