diff --git a/java/compiler/impl/src/com/intellij/compiler/CompilerConfigurationImpl.java b/java/compiler/impl/src/com/intellij/compiler/CompilerConfigurationImpl.java index 3c0dfa750cec..81f9019d8619 100644 --- a/java/compiler/impl/src/com/intellij/compiler/CompilerConfigurationImpl.java +++ b/java/compiler/impl/src/com/intellij/compiler/CompilerConfigurationImpl.java @@ -177,9 +177,11 @@ public class CompilerConfigurationImpl extends CompilerConfiguration implements else { previous = myModuleBytecodeTarget.put(module.getName(), level); } - if (!Comparing.equal(previous, level)) { - //final Project project = module.getProject(); // todo[jeka] so what? - } + // todo: mark module as dirty in order to rebuild it completely with the new target level + //if (!Comparing.equal(previous, level)) { + // final Project project = module.getProject(); + // + //} } @Override 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 6672627dfba1..a1b1747ae286 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java @@ -453,7 +453,6 @@ public class CompileDriver { return buildManager.scheduleBuild(myProject, compileContext.isRebuild(), compileContext.isMake(), moduleNames, artifactNames, paths, builderParams, new DefaultMessageHandler(myProject) { @Override public void sessionTerminated() { - notifyCompilationCompleted(compileContext, callback, COMPILE_SERVER_BUILD_STATUS.get(compileContext)); } @Override @@ -587,12 +586,16 @@ public class CompileDriver { } return; } - final long start = System.currentTimeMillis(); try { LOG.info("COMPILATION STARTED (BUILD PROCESS)"); if (message != null) { compileContext.addMessage(message); } + if (!executeCompileTasks(compileContext, true)) { + COMPILE_SERVER_BUILD_STATUS.set(compileContext, ExitStatus.CANCELLED); + return; + } + final Collection paths = fetchFiles(compileContext); final List modules = paths.isEmpty() && !isRebuild && !allProjectModulesAffected(compileContext)? Arrays.asList(compileContext.getCompileScope().getAffectedModules()) : Collections.emptyList(); final Set artifacts = ArtifactCompileScope.getArtifactsToBuild(myProject, compileContext.getCompileScope(), true); @@ -603,33 +606,35 @@ public class CompileDriver { future.cancel(false); } } - } - else { - callback.finished(false, compileContext.getMessageCount(CompilerMessageCategory.ERROR), compileContext.getMessageCount(CompilerMessageCategory.WARNING), compileContext); + if (!executeCompileTasks(compileContext, false)) { + COMPILE_SERVER_BUILD_STATUS.set(compileContext, ExitStatus.CANCELLED); + return; + } } } catch (Throwable e) { LOG.error(e); // todo - callback.finished(false, compileContext.getMessageCount(CompilerMessageCategory.ERROR), compileContext.getMessageCount(CompilerMessageCategory.WARNING), compileContext); } finally { - final long finish = System.currentTimeMillis(); + final long duration = notifyCompilationCompleted(compileContext, callback, COMPILE_SERVER_BUILD_STATUS.get(compileContext)); CompilerUtil.logDuration( "\tCOMPILATION FINISHED (BUILD PROCESS); Errors: " + compileContext.getMessageCount(CompilerMessageCategory.ERROR) + "; warnings: " + compileContext.getMessageCount(CompilerMessageCategory.WARNING), - finish - start + duration ); CompilerCacheManager.getInstance(myProject).flushCaches(); - // todo: need this for tests; should be removed later - final Set outputs = new HashSet(); - for (final String path : CompilerPathsEx.getOutputPaths(ModuleManager.getInstance(myProject).getModules())) { - outputs.add(new File(path)); + if (ApplicationManager.getApplication().isUnitTestMode()) { + // need this for tests only; + final Set outputs = new HashSet(); + for (final String path : CompilerPathsEx.getOutputPaths(ModuleManager.getInstance(myProject).getModules())) { + outputs.add(new File(path)); + } + CompilerUtil.refreshIOFiles(outputs); } - CompilerUtil.refreshIOFiles(outputs); } } }; @@ -643,7 +648,6 @@ public class CompileDriver { } return; } - long start = System.currentTimeMillis(); try { if (myProject.isDisposed()) { return; @@ -656,14 +660,6 @@ public class CompileDriver { doCompile(compileContext, isRebuild, forceCompile, callback, checkCachesVersion); } finally { - final long finish = System.currentTimeMillis(); - CompilerUtil.logDuration( - "\tCOMPILATION FINISHED; Errors: " + - compileContext.getMessageCount(CompilerMessageCategory.ERROR) + - "; warnings: " + - compileContext.getMessageCount(CompilerMessageCategory.WARNING), - finish - start - ); CompilerCacheManager.getInstance(myProject).flushCaches(); FileUtil.delete(CompilerPaths.getRebuildMarkerFile(myProject)); } @@ -727,7 +723,7 @@ public class CompileDriver { final boolean checkCachesVersion) { ExitStatus status = ExitStatus.ERRORS; boolean wereExceptions = false; - final long vfsTimestamp = ((PersistentFS)ManagingFS.getInstance()).getCreationTimestamp(); + final long vfsTimestamp = (ManagingFS.getInstance()).getCreationTimestamp(); try { if (checkCachesVersion) { checkCachesVersion(compileContext, vfsTimestamp); @@ -765,7 +761,6 @@ public class CompileDriver { } finally { dropDependencyCache(compileContext); - final ExitStatus _status = status; if (compileContext.isRebuildRequested()) { ApplicationManager.getApplication().invokeLater(new Runnable() { public void run() { @@ -778,13 +773,20 @@ public class CompileDriver { if (!myProject.isDisposed()) { writeStatus(new CompileStatus(CompilerConfigurationImpl.DEPENDENCY_FORMAT_VERSION, wereExceptions, vfsTimestamp), compileContext); } - notifyCompilationCompleted(compileContext, callback, _status); + final long duration = notifyCompilationCompleted(compileContext, callback, status); + CompilerUtil.logDuration( + "\tCOMPILATION FINISHED; Errors: " + + compileContext.getMessageCount(CompilerMessageCategory.ERROR) + + "; warnings: " + + compileContext.getMessageCount(CompilerMessageCategory.WARNING), + duration + ); } } } /** @noinspection SSBasedInspection*/ - private void notifyCompilationCompleted(final CompileContextImpl compileContext, final CompileStatusNotification callback, final ExitStatus _status) { + private long notifyCompilationCompleted(final CompileContextImpl compileContext, final CompileStatusNotification callback, final ExitStatus _status) { final long duration = System.currentTimeMillis() - compileContext.getStartCompilationStamp(); SwingUtilities.invokeLater(new Runnable() { public void run() { @@ -812,6 +814,7 @@ public class CompileDriver { } } }); + return duration; } private void checkCachesVersion(final CompileContextImpl compileContext, final long currentVFSTimestamp) { diff --git a/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java b/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java index beb1c594cbc3..9b4ea0d48831 100644 --- a/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java +++ b/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java @@ -53,7 +53,10 @@ import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vfs.*; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.SavingRequestor; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.openapi.vfs.encoding.EncodingManager; import com.intellij.openapi.vfs.newvfs.BulkFileListener; import com.intellij.openapi.vfs.newvfs.events.VFileEvent; @@ -276,6 +279,11 @@ public class BuildManager implements ApplicationComponent{ } catch (RejectedExecutionException ignored) { // we were shut down + myAutoMakeInProgress.set(false); + } + catch (Throwable e) { + myAutoMakeInProgress.set(false); + throw new RuntimeException(e); } } else { diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointChooser.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointChooser.java index f7823f066780..459cfc06bcf6 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointChooser.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointChooser.java @@ -19,6 +19,7 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.ComboBox; import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.UserDataHolderBase; import com.intellij.ui.CollectionComboBoxModel; import com.intellij.ui.components.JBList; @@ -28,6 +29,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import javax.swing.event.ListSelectionEvent; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.util.List; @@ -76,11 +78,16 @@ public class BreakpointChooser { break; } } + final Ref hackedSelection = Ref.create(); + myDetailController = new DetailController(new MasterController() { JLabel fake = new JLabel(); @Override public ItemWrapper[] getSelectedItems() { - return new ItemWrapper[]{((BreakpointItem)myList.getSelectedValue())}; + if (hackedSelection.get() == null) { + return new ItemWrapper[0]; + } + return new ItemWrapper[]{((BreakpointItem) hackedSelection.get())}; } @Override @@ -92,18 +99,21 @@ public class BreakpointChooser { final ItemWrapperListRenderer listRenderer = new ItemWrapperListRenderer(project, null); ComboBoxModel model = new CollectionComboBoxModel(breakpointItems, breakpointItem); - myComboBox = new ComboBox(model) { + myComboBox = new ComboBox(model); + myComboBox.setRenderer(new ItemWrapperListRenderer(project, null) { @Override - protected JBList createJBList(ComboBoxModel model) { - myList = super.createJBList(model); - myDetailController.setList(myList); - myList.setCellRenderer(listRenderer); - return myList; + protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) { + super.customizeCellRenderer(list, value, index, selected, + hasFocus); + if (selected) { + if (hackedSelection.get() != value) { + hackedSelection.set(value); + myDetailController.selectionChanged(); + } + } } - }; - myComboBox.setRenderer(listRenderer); + }); - myComboBox.setSwingPopup(false); myComboBox.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent event) { diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointPropertiesPanel.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointPropertiesPanel.java index 1d13ac9a49ac..4ecb756a14ca 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointPropertiesPanel.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointPropertiesPanel.java @@ -863,22 +863,19 @@ public abstract class BreakpointPropertiesPanel { } protected void updateCheckboxes() { - JCheckBox [] checkBoxes = {myInstanceFiltersCheckBox, myClassFiltersCheckBox }; - JCheckBox selected = null; - for (JCheckBox checkBoxe : checkBoxes) { - if (checkBoxe.isSelected()) { - selected = checkBoxe; + JCheckBox [] checkBoxes = {myConditionCheckbox, myInstanceFiltersCheckBox, myClassFiltersCheckBox }; + boolean passCountApplicable = true; + for (JCheckBox checkBox : checkBoxes) { + if (checkBox.isSelected()) { + passCountApplicable = false; break; } } - if(selected != null || !myConditionCheckbox.isSelected()){ - myPassCountCheckbox.setEnabled(false); - } else { - myPassCountCheckbox.setEnabled(true); - } + myPassCountCheckbox.setEnabled(passCountApplicable); - for (JCheckBox checkBoxe : checkBoxes) { - checkBoxe.setEnabled(!myPassCountCheckbox.isSelected()); + final boolean passCountSelected = myPassCountCheckbox.isSelected(); + for (JCheckBox checkBox : checkBoxes) { + checkBox.setEnabled(!passCountSelected); } myPassCountField.setEditable(myPassCountCheckbox.isSelected()); diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaClassNameInsertHandler.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaClassNameInsertHandler.java index db375547f0c9..8665e945a59c 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaClassNameInsertHandler.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaClassNameInsertHandler.java @@ -88,6 +88,10 @@ class JavaClassNameInsertHandler implements InsertHandler manipulator = getManipulator(getElement()); - if (manipulator != null) { - final PsiElement element = manipulator.handleContentChange(getElement(), getRangeInElement(), newElementName); - myRange = new TextRange(getRangeInElement().getStartOffset(), getRangeInElement().getStartOffset() + newElementName.length()); - return element; - } - throw new IncorrectOperationException("Manipulator for this element is not defined: " + getElement()); + final PsiElement element = manipulator.handleContentChange(getElement(), getRangeInElement(), newElementName); + myRange = new TextRange(getRangeInElement().getStartOffset(), getRangeInElement().getStartOffset() + newElementName.length()); + return element; } @Override @@ -215,12 +212,9 @@ public class JavaClassReference extends GenericReference implements PsiJavaRefer } TextRange range = new TextRange(myJavaClassReferenceSet.getReference(0).getRangeInElement().getStartOffset(), end); final ElementManipulator manipulator = getManipulator(getElement()); - if (manipulator != null) { - final PsiElement finalElement = manipulator.handleContentChange(getElement(), range, newName); - myJavaClassReferenceSet.reparse(finalElement, TextRange.from(range.getStartOffset(), newName.length())); - return finalElement; - } - return element; + final PsiElement finalElement = manipulator.handleContentChange(getElement(), range, newName); + myJavaClassReferenceSet.reparse(finalElement, TextRange.from(range.getStartOffset(), newName.length())); + return finalElement; } @Override diff --git a/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MoveJavaClassHandler.java b/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MoveJavaClassHandler.java index 5ba0655e2169..103dc784644b 100644 --- a/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MoveJavaClassHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MoveJavaClassHandler.java @@ -87,7 +87,7 @@ public class MoveJavaClassHandler implements MoveClassHandler { newClass.getContainingFile().accept(new JavaRecursiveElementVisitor() { @Override public void visitReferenceElement(PsiJavaCodeReferenceElement reference) { - if (reference.isReferenceTo(oldClass)) { + if (reference.isValid() && reference.isReferenceTo(oldClass)) { final PsiImportStatementBase importStatement = PsiTreeUtil.getParentOfType(reference, PsiImportStatementBase.class); if (importStatement != null) { importsToDelete.add(importStatement); diff --git a/java/java-psi-api/src/com/intellij/codeInsight/BaseExternalAnnotationsManager.java b/java/java-psi-api/src/com/intellij/codeInsight/BaseExternalAnnotationsManager.java index 788fe3eb8c7f..97f853d27ace 100644 --- a/java/java-psi-api/src/com/intellij/codeInsight/BaseExternalAnnotationsManager.java +++ b/java/java-psi-api/src/com/intellij/codeInsight/BaseExternalAnnotationsManager.java @@ -233,25 +233,13 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations final List files = myExternalAnnotations.get(fqn); if (files == NULL) return null; if (files != null) { - boolean hasInvalidFiles = false; + boolean allValid = true; for (PsiFile file : files) { - if (!file.isValid()) { - hasInvalidFiles = true; - break; - } + allValid &= file.isValid(); } - if (hasInvalidFiles) { - ArrayList onlyValid = new ArrayList(); - for (PsiFile file : files) { - if (file.isValid()) { - onlyValid.add(file); - } - } - onlyValid.trimToSize(); - myExternalAnnotations.put(fqn, onlyValid); - return onlyValid; + if (allValid) { + return files; } - return files; } if (virtualFile == null) { diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiJavaCodeReferenceElementImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiJavaCodeReferenceElementImpl.java index 039545a50998..bfe6b5b1ef87 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiJavaCodeReferenceElementImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiJavaCodeReferenceElementImpl.java @@ -85,12 +85,14 @@ public class PsiJavaCodeReferenceElementImpl extends CompositePsiElement impleme } public int getKind() { - IElementType i = getTreeParent().getElementType(); + LOG.assertTrue(isValid()); + CompositeElement treeParent = getTreeParent(); + IElementType i = treeParent.getElementType(); if (isDummy(i)) { return myKindWhenDummy; } if (i == JavaElementType.TYPE) { - return getTreeParent().getTreeParent().getPsi() instanceof PsiTypeCodeFragment ? CLASS_OR_PACKAGE_NAME_KIND : CLASS_NAME_KIND; + return treeParent.getTreeParent().getPsi() instanceof PsiTypeCodeFragment ? CLASS_OR_PACKAGE_NAME_KIND : CLASS_NAME_KIND; } if (i == JavaElementType.EXTENDS_LIST || i == JavaElementType.IMPLEMENTS_LIST || @@ -108,13 +110,13 @@ public class PsiJavaCodeReferenceElementImpl extends CompositePsiElement impleme return CLASS_NAME_KIND; } if (i == JavaElementType.NEW_EXPRESSION) { - final ASTNode qualifier = getTreeParent().findChildByRole(ChildRole.QUALIFIER); + final ASTNode qualifier = treeParent.findChildByRole(ChildRole.QUALIFIER); return qualifier != null ? CLASS_IN_QUALIFIED_NEW_KIND : CLASS_NAME_KIND; } if (i == JavaElementType.ANONYMOUS_CLASS) { - if (getTreeParent().getChildRole(this) == ChildRole.BASE_CLASS_REFERENCE) { - LOG.assertTrue(getTreeParent().getTreeParent().getElementType() == JavaElementType.NEW_EXPRESSION); - final ASTNode qualifier = getTreeParent().getTreeParent().findChildByRole(ChildRole.QUALIFIER); + if (treeParent.getChildRole(this) == ChildRole.BASE_CLASS_REFERENCE) { + LOG.assertTrue(treeParent.getTreeParent().getElementType() == JavaElementType.NEW_EXPRESSION); + final ASTNode qualifier = treeParent.getTreeParent().findChildByRole(ChildRole.QUALIFIER); return qualifier != null ? CLASS_IN_QUALIFIED_NEW_KIND : CLASS_NAME_KIND; } else { @@ -125,14 +127,14 @@ public class PsiJavaCodeReferenceElementImpl extends CompositePsiElement impleme return PACKAGE_NAME_KIND; } if (i == JavaElementType.IMPORT_STATEMENT) { - final boolean isOnDemand = ((PsiImportStatement)SourceTreeToPsiMap.treeElementToPsi(getTreeParent())).isOnDemand(); + final boolean isOnDemand = ((PsiImportStatement)SourceTreeToPsiMap.treeElementToPsi(treeParent)).isOnDemand(); return isOnDemand ? CLASS_FQ_OR_PACKAGE_NAME_KIND : CLASS_FQ_NAME_KIND; } if (i == JavaElementType.IMPORT_STATIC_STATEMENT) { return CLASS_FQ_OR_PACKAGE_NAME_KIND; } if (i == JavaElementType.JAVA_CODE_REFERENCE) { - final int parentKind = ((PsiJavaCodeReferenceElementImpl)getTreeParent()).getKind(); + final int parentKind = ((PsiJavaCodeReferenceElementImpl)treeParent).getKind(); switch (parentKind) { case CLASS_NAME_KIND: return CLASS_OR_PACKAGE_NAME_KIND; @@ -170,11 +172,17 @@ public class PsiJavaCodeReferenceElementImpl extends CompositePsiElement impleme return CLASS_OR_PACKAGE_NAME_KIND; } if (isCodeFragmentType(i)) { - PsiJavaCodeReferenceCodeFragment fragment = (PsiJavaCodeReferenceCodeFragment)getTreeParent().getPsi(); + PsiJavaCodeReferenceCodeFragment fragment = (PsiJavaCodeReferenceCodeFragment)treeParent.getPsi(); return fragment.isClassesAccepted() ? CLASS_FQ_OR_PACKAGE_NAME_KIND : PACKAGE_NAME_KIND; } + diagnoseUnknownParent(); + return CLASS_NAME_KIND; + } + + private void diagnoseUnknownParent() { CompositeElement parent = getTreeParent(); + IElementType i = parent.getElementType(); String message = "Unknown parent for java code reference: '" + parent + "'; Type: " + i + ";\n"; while (parent != null && parent.getPsi() instanceof PsiExpression) { parent = parent.getTreeParent(); @@ -184,7 +192,6 @@ public class PsiJavaCodeReferenceElementImpl extends CompositePsiElement impleme message += DebugUtil.treeToString(parent, false); } LOG.error(message); - return CLASS_NAME_KIND; } private static boolean isCodeFragmentType(IElementType type) { diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiReferenceExpressionImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiReferenceExpressionImpl.java index 7f537205fc30..77bf60684b11 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiReferenceExpressionImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiReferenceExpressionImpl.java @@ -319,6 +319,10 @@ public class PsiReferenceExpressionImpl extends PsiReferenceExpressionBase imple LOG.error("getManager() == null!"); return JavaResolveResult.EMPTY_ARRAY; } + if (!isValid()) { + LOG.error("invalid!"); + return JavaResolveResult.EMPTY_ARRAY; + } ResolveResult[] results = ResolveCache.getInstance(getProject()).resolveWithCaching(this, OurGenericsResolver.INSTANCE, true, incompleteCode); return results.length == 0 ? JavaResolveResult.EMPTY_ARRAY : (JavaResolveResult[])results; } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/CompletionSortingTestCase.java b/java/java-tests/testSrc/com/intellij/codeInsight/completion/CompletionSortingTestCase.java index ed2113bae6b5..fbbf0fae59ed 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/CompletionSortingTestCase.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/CompletionSortingTestCase.java @@ -5,7 +5,6 @@ package com.intellij.codeInsight.completion; import com.intellij.codeInsight.CodeInsightSettings; -import com.intellij.codeInsight.completion.impl.CompletionServiceImpl; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupManager; import com.intellij.codeInsight.lookup.impl.LookupImpl; @@ -77,7 +76,7 @@ public abstract class CompletionSortingTestCase extends LightFixtureCompletionTe protected static void imitateItemSelection(final LookupImpl lookup, final int index) { final LookupElement item = lookup.getItems().get(index); lookup.setCurrentItem(item); - CompletionLookupArranger.collectStatisticChanges(CompletionServiceImpl.getCompletionService().getCurrentCompletion(), item); + CompletionLookupArranger.collectStatisticChanges(item, lookup); CompletionLookupArranger.applyLastCompletionStatisticsUpdate(); } } diff --git a/platform/core-api/src/com/intellij/psi/PsiElement.java b/platform/core-api/src/com/intellij/psi/PsiElement.java index 9f1b0d026376..e6394bb13b1c 100644 --- a/platform/core-api/src/com/intellij/psi/PsiElement.java +++ b/platform/core-api/src/com/intellij/psi/PsiElement.java @@ -96,7 +96,6 @@ public interface PsiElement extends UserDataHolder, Iconable { * * @return the first child, or null if the element has no children. */ - @Nullable PsiElement getFirstChild(); /** @@ -104,7 +103,6 @@ public interface PsiElement extends UserDataHolder, Iconable { * * @return the last child, or null if the element has no children. */ - @Nullable PsiElement getLastChild(); /** diff --git a/platform/core-impl/src/com/intellij/openapi/editor/impl/CharArray.java b/platform/core-impl/src/com/intellij/openapi/editor/impl/CharArray.java index 1329da1d6495..c88c0fa3d071 100644 --- a/platform/core-impl/src/com/intellij/openapi/editor/impl/CharArray.java +++ b/platform/core-impl/src/com/intellij/openapi/editor/impl/CharArray.java @@ -18,6 +18,7 @@ package com.intellij.openapi.editor.impl; import com.intellij.diagnostic.Dumpable; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.event.DocumentEvent; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.ArrayUtil; import com.intellij.util.LocalTimeCounter; import com.intellij.util.text.CharArrayCharSequence; @@ -197,20 +198,20 @@ abstract class CharArray implements CharSequenceBackedByArray, Dumpable { assert count == stringLen || stringLen==-1; if (!myDebug) return; - final String stringFromCharArray; + final CharSequence seqFromCharArray; if (myArray != null) { assert myCount <= myArray.length; - stringFromCharArray = new String(myArray, myStart, myCount); + seqFromCharArray = new CharArrayCharSequence(myArray, myStart, myCount); } else { - stringFromCharArray = null; + seqFromCharArray = null; } - if (stringFromCharArray != null && originalSequence != null) { - assert stringFromCharArray.equals(originalSequence); + if (seqFromCharArray != null && originalSequence != null) { + assert StringUtil.equals(seqFromCharArray, originalSequence); } - if (!isDeferredChangeMode() && stringFromCharArray != null && string != null) { - assert stringFromCharArray.equals(string); + if (!isDeferredChangeMode() && seqFromCharArray != null && string != null) { + assert StringUtil.equals(seqFromCharArray, string); } if (originalSequence != null && string != null) { assert string.equals(originalSequence.toString()); @@ -218,7 +219,7 @@ abstract class CharArray implements CharSequenceBackedByArray, Dumpable { myDebugArray.assertConsistency(); - String str = myStringRef == null ? null : myStringRef.get(); + CharSequence str = myStringRef == null ? null : myStringRef.get(); if (str == null) { if (myHasDeferredChanges) { str = doSubString(0, myCount + myDeferredShift).toString(); @@ -227,7 +228,7 @@ abstract class CharArray implements CharSequenceBackedByArray, Dumpable { str = myOriginalSequence.toString(); } else { - str = stringFromCharArray; + str = seqFromCharArray; } } assert count == str.length(); @@ -675,8 +676,8 @@ abstract class CharArray implements CharSequenceBackedByArray, Dumpable { ", deferred shift: " + myDeferredShift+"); view offsets: [" + myStart+"; "+myCount+"]; deferred changes: "+myDeferredChangesStorage; } - private void checkStrings(@NonNls @NotNull String operation, @NotNull String expected, @NotNull String actual) { - if (expected.equals(actual)) { + private void checkStrings(@NonNls @NotNull String operation, @NotNull String expected, @NotNull CharSequence actual) { + if (StringUtil.equals(expected, actual)) { return; } for (int i = 0, max = Math.min(expected.length(), actual.length()); i < max; i++) { diff --git a/platform/lang-api/src/com/intellij/execution/filters/FileHyperlinkInfo.java b/platform/lang-api/src/com/intellij/execution/filters/FileHyperlinkInfo.java index 33be9b0fc937..59cf8690c29f 100644 --- a/platform/lang-api/src/com/intellij/execution/filters/FileHyperlinkInfo.java +++ b/platform/lang-api/src/com/intellij/execution/filters/FileHyperlinkInfo.java @@ -17,11 +17,13 @@ package com.intellij.execution.filters; import com.intellij.openapi.fileEditor.OpenFileDescriptor; +import org.jetbrains.annotations.Nullable; /** * @author yole * @since 7.0.3 */ public interface FileHyperlinkInfo extends HyperlinkInfo { + @Nullable OpenFileDescriptor getDescriptor(); } diff --git a/platform/lang-api/src/com/intellij/execution/filters/OpenFileHyperlinkInfo.java b/platform/lang-api/src/com/intellij/execution/filters/OpenFileHyperlinkInfo.java index 1aba19c8c268..9b7ef584162a 100644 --- a/platform/lang-api/src/com/intellij/execution/filters/OpenFileHyperlinkInfo.java +++ b/platform/lang-api/src/com/intellij/execution/filters/OpenFileHyperlinkInfo.java @@ -51,6 +51,10 @@ public final class OpenFileHyperlinkInfo implements FileHyperlinkInfo { } public OpenFileDescriptor getDescriptor() { + if (!myFile.isValid()) { + return null; + } + int offset = calculateOffset(myFile, myDocumentLine, myDocumentColumn); if (offset != UNDEFINED_OFFSET) { return new OpenFileDescriptor(myProject, myFile, offset); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java index 43f39b09af1b..fe7b26c87a50 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java @@ -113,15 +113,9 @@ public class CodeCompletionHandlerBase { final PsiFile psiFile = PsiUtilBase.getPsiFileInEditor(editor, project); assert psiFile != null : "no PSI file: " + FileDocumentManager.getInstance().getFile(editor.getDocument()); - if (!ApplicationManager.getApplication().isUnitTestMode()) { - if (ApplicationManager.getApplication().isWriteAccessAllowed()) { - throw new AssertionError("Completion should not be invoked inside write action"); - } - } + checkNoWriteAccess(); - if (editor instanceof EditorWindow && !((EditorWindow)editor).isValid()) { - throw new AssertionError(); - } + checkEditorValid(editor); if (editor.isViewer()) { editor.getDocument().fireReadOnlyModificationAttempt(); @@ -191,9 +185,7 @@ public class CodeCompletionHandlerBase { throw new AssertionError("unsuccessful commit: injected=" + (editor instanceof EditorWindow)); } - if (editor instanceof EditorWindow && !((EditorWindow)editor).isValid()) { - throw new AssertionError(); - } + checkEditorValid(editor); final Ref current = Ref.create(null); initializationContext[0] = new CompletionInitializationContext(editor, psiFile, myCompletionType) { @@ -215,6 +207,7 @@ public class CodeCompletionHandlerBase { current.set(contributor); contributor.beforeCompletion(initializationContext[0]); + checkEditorValid(editor); assert !documentManager.isUncommited(document) : "Contributor " + contributor + " left the document uncommitted"; } } @@ -234,6 +227,20 @@ public class CodeCompletionHandlerBase { insertDummyIdentifier(initializationContext[0], hasModifiers, time); } + private static void checkEditorValid(Editor editor) { + if (editor instanceof EditorWindow && !((EditorWindow)editor).isValid()) { + throw new AssertionError(); + } + } + + private static void checkNoWriteAccess() { + if (!ApplicationManager.getApplication().isUnitTestMode()) { + if (ApplicationManager.getApplication().isWriteAccessAllowed()) { + throw new AssertionError("Completion should not be invoked inside write action"); + } + } + } + private static boolean shouldSkipAutoPopup(Editor editor, PsiFile psiFile) { int offset = editor.getCaretModel().getOffset(); int psiOffset = Math.max(0, offset - 1); @@ -260,6 +267,7 @@ public class CodeCompletionHandlerBase { @NotNull private LookupImpl obtainLookup(Editor editor) { + checkEditorValid(editor); LookupImpl existing = (LookupImpl)LookupManager.getActiveLookup(editor); if (existing != null && existing.isCompletion()) { existing.markReused(); @@ -593,7 +601,8 @@ public class CodeCompletionHandlerBase { FeatureUsageTracker.getInstance().triggerFeatureUsed(CodeCompletionFeatures.EDITING_COMPLETION_BASIC); } - final CompletionLookupArranger.StatisticsUpdate update = CompletionLookupArranger.collectStatisticChanges(indicator, item); + final CompletionLookupArranger.StatisticsUpdate update = + CompletionLookupArranger.collectStatisticChanges(item, indicator.getParameters().getLookup()); final Editor editor = indicator.getEditor(); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionLookupArranger.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionLookupArranger.java index de37b32b9a23..c747145b9f3f 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionLookupArranger.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionLookupArranger.java @@ -26,6 +26,7 @@ import com.intellij.featureStatistics.FeatureUsageTrackerImpl; import com.intellij.ide.ui.UISettings; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.editor.event.DocumentAdapter; @@ -48,6 +49,7 @@ import javax.swing.*; import java.util.*; public class CompletionLookupArranger extends LookupArranger { + private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.CompletionLookupArranger"); @Nullable private static StatisticsUpdate ourPendingUpdate; private static final Alarm ourStatsAlarm = new Alarm(ApplicationManager.getApplication()); private static final Key PRESENTATION_INVARIANT = Key.create("PRESENTATION_INVARIANT"); @@ -136,7 +138,11 @@ public class CompletionLookupArranger extends LookupArranger { @Override public void addElement(Lookup lookup, LookupElement element, LookupElementPresentation presentation) { - super.addElement(lookup, element, presentation); + StatisticsWeigher.clearBaseStatisticsInfo(element); + + final String invariant = presentation.getItemText() + "###" + getTailTextOrSpace(presentation) + "###" + presentation.getTypeText(); + element.putUserData(PRESENTATION_INVARIANT, invariant); + CompletionSorterImpl sorter = obtainSorter(element); Classifier classifier = myClassifiers.get(sorter); if (classifier == null) { @@ -144,8 +150,7 @@ public class CompletionLookupArranger extends LookupArranger { } classifier.addElement(element); - final String invariant = presentation.getItemText() + "###" + getTailTextOrSpace(presentation) + "###" + presentation.getTypeText(); - element.putUserData(PRESENTATION_INVARIANT, invariant); + super.addElement(lookup, element, presentation); } @NotNull @@ -175,11 +180,13 @@ public class CompletionLookupArranger extends LookupArranger { List items = getMatchingItems(); MultiMap itemsBySorter = groupItemsBySorter(items); + LookupElement relevantSelection = findMostRelevantItem(itemsBySorter); List listModel = isAlphaSorted() ? sortByPresentation(items) : - fillModelByRelevance((LookupImpl)lookup, items, itemsBySorter); + fillModelByRelevance((LookupImpl)lookup, items, itemsBySorter, relevantSelection); - int toSelect = getItemToSelect(lookup, listModel, itemsBySorter, onExplicitAction); + int toSelect = getItemToSelect(lookup, listModel, onExplicitAction, relevantSelection); + LOG.assertTrue(toSelect >= 0); addDummyItems(items.size() - listModel.size(), listModel); @@ -195,7 +202,8 @@ public class CompletionLookupArranger extends LookupArranger { private List fillModelByRelevance(LookupImpl lookup, List items, - MultiMap inputBySorter) { + MultiMap inputBySorter, + @Nullable LookupElement relevantSelection) { Iterator byRelevance = sortByRelevance(inputBySorter).iterator(); final LinkedHashSet model = new LinkedHashSet(); @@ -212,7 +220,8 @@ public class CompletionLookupArranger extends LookupArranger { freezeTopItems(lookup, model); - ensureCurrentSelectionAdded(lookup, items, model, byRelevance); + ensureItemAdded(items, model, byRelevance, lookup.getCurrentItem()); + ensureItemAdded(items, model, byRelevance, relevantSelection); ensureEverythingVisibleAdded(lookup, model, byRelevance); return new ArrayList(model); @@ -230,16 +239,14 @@ public class CompletionLookupArranger extends LookupArranger { }); } - private static void ensureCurrentSelectionAdded(LookupImpl lookup, - List items, - LinkedHashSet model, - Iterator byRelevance) { - final LookupElement currentItem = lookup.getCurrentItem(); - if (ContainerUtil.indexOfIdentity(items, currentItem) >= 0 && !model.contains(currentItem)) { + private static void ensureItemAdded(List items, + LinkedHashSet model, + Iterator byRelevance, @Nullable final LookupElement item) { + if (item != null && ContainerUtil.indexOfIdentity(items, item) >= 0 && !model.contains(item)) { addSomeItems(model, byRelevance, new Condition() { @Override public boolean value(LookupElement lastAdded) { - return lastAdded == currentItem; + return lastAdded == item; } }); } @@ -306,9 +313,7 @@ public class CompletionLookupArranger extends LookupArranger { return new CompletionLookupArranger(myParameters, myProcess); } - private int getItemToSelect(Lookup lookup, List items, - MultiMap itemsBySorter, - boolean onExplicitAction) { + private static int getItemToSelect(Lookup lookup, List items, boolean onExplicitAction, @Nullable LookupElement mostRelevant) { if (items.isEmpty() || !lookup.isFocused()) { return 0; } @@ -345,35 +350,38 @@ public class CompletionLookupArranger extends LookupArranger { } } + return Math.max(0, ContainerUtil.indexOfIdentity(items, mostRelevant)); + } + + @Nullable + private LookupElement findMostRelevantItem(MultiMap itemsBySorter) { final CompletionPreselectSkipper[] skippers = CompletionPreselectSkipper.EP_NAME.getExtensions(); for (CompletionSorterImpl sorter : myClassifiers.keySet()) { ProcessingContext context = createContext(true); for (LookupElement element : myClassifiers.get(sorter).classify(itemsBySorter.get(sorter), context)) { if (!shouldSkip(skippers, element)) { - return items.indexOf(element); + return element; } } } - return 0; + return null; } + private static boolean isLiveTemplate(LookupElement element) { return element instanceof LiveTemplateLookupElement && ((LiveTemplateLookupElement)element).sudden; } - public static StatisticsUpdate collectStatisticChanges(CompletionProgressIndicator indicator, LookupElement item) { + public static StatisticsUpdate collectStatisticChanges(LookupElement item, final Lookup lookup) { applyLastCompletionStatisticsUpdate(); - CompletionLocation location = new CompletionLocation(indicator.getParameters()); - final StatisticsInfo main = StatisticsManager.serialize(CompletionService.STATISTICS_KEY, item, location); - - final List toIncrement = new ArrayList(); - if (main != null && main != StatisticsInfo.EMPTY) { - toIncrement.addAll(StatisticsWeigher.composeStatsWithPrefix(main, location, item)); + final StatisticsInfo base = StatisticsWeigher.getBaseStatisticsInfo(item, null); + if (base == StatisticsInfo.EMPTY) { + return new StatisticsUpdate(Collections.emptyList()); } - StatisticsUpdate update = new StatisticsUpdate(toIncrement); + StatisticsUpdate update = new StatisticsUpdate(StatisticsWeigher.composeStatsWithPrefix(base, lookup.itemPattern(item))); ourPendingUpdate = update; Disposer.register(update, new Disposable() { @Override diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/StatisticsWeigher.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/StatisticsWeigher.java index 86092ef5fa5c..baf889f7877f 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/StatisticsWeigher.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/StatisticsWeigher.java @@ -23,6 +23,7 @@ import com.intellij.openapi.util.Key; import com.intellij.psi.statistics.StatisticsInfo; import com.intellij.psi.statistics.StatisticsManager; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; @@ -32,14 +33,14 @@ import java.util.List; */ public class StatisticsWeigher extends CompletionWeigher { private static final StatisticsManager ourStatManager = StatisticsManager.getInstance(); + private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.StatisticsWeigher.LookupStatisticsWeigher"); + private static final Key BASE_STATISTICS_INFO = Key.create("Base statistics info"); public Comparable weigh(@NotNull final LookupElement item, @NotNull final CompletionLocation location) { throw new UnsupportedOperationException(); } public static class LookupStatisticsWeigher extends LookupElementWeigher { - private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.StatisticsWeigher.LookupStatisticsWeigher"); - private static final Key BASE_INFO = Key.create("Base statistics info"); private final CompletionLocation myLocation; public LookupStatisticsWeigher(CompletionLocation location) { @@ -49,38 +50,45 @@ public class StatisticsWeigher extends CompletionWeigher { @Override public Integer weigh(@NotNull LookupElement item) { - final StatisticsInfo info = getBaseStatisticsInfo(item); + final StatisticsInfo info = getBaseStatisticsInfo(item, myLocation); if (info == StatisticsInfo.EMPTY) { return 0; } int max = 0; - for (StatisticsInfo statisticsInfo : composeStatsWithPrefix(info, myLocation, item)) { + for (StatisticsInfo statisticsInfo : composeStatsWithPrefix(info, myLocation.getCompletionParameters().getLookup().itemPattern(item))) { max = Math.max(max, ourStatManager.getUseCount(statisticsInfo)); } return max; } - @NotNull - private StatisticsInfo getBaseStatisticsInfo(LookupElement item) { - StatisticsInfo info = BASE_INFO.get(item); - if (info == null) { - BASE_INFO.set(item, info = calcBaseInfo(item)); - } - return info; - } - - @NotNull - private StatisticsInfo calcBaseInfo(LookupElement item) { - if (!ApplicationManager.getApplication().isUnitTestMode()) { - LOG.assertTrue(!ApplicationManager.getApplication().isDispatchThread()); - } - StatisticsInfo info = StatisticsManager.serialize(CompletionService.STATISTICS_KEY, item, myLocation); - return info == null ? StatisticsInfo.EMPTY : info; - } } - public static List composeStatsWithPrefix(StatisticsInfo info, CompletionLocation location, LookupElement item) { - String fullPrefix = location.getCompletionParameters().getLookup().itemPattern(item); + public static void clearBaseStatisticsInfo(LookupElement item) { + item.putUserData(BASE_STATISTICS_INFO, null); + } + + @NotNull + public static StatisticsInfo getBaseStatisticsInfo(LookupElement item, @Nullable CompletionLocation location) { + StatisticsInfo info = BASE_STATISTICS_INFO.get(item); + if (info == null) { + if (location == null) { + return StatisticsInfo.EMPTY; + } + BASE_STATISTICS_INFO.set(item, info = calcBaseInfo(item, location)); + } + return info; + } + + @NotNull + private static StatisticsInfo calcBaseInfo(LookupElement item, @NotNull CompletionLocation location) { + if (!ApplicationManager.getApplication().isUnitTestMode()) { + LOG.assertTrue(!ApplicationManager.getApplication().isDispatchThread()); + } + StatisticsInfo info = StatisticsManager.serialize(CompletionService.STATISTICS_KEY, item, location); + return info == null ? StatisticsInfo.EMPTY : info; + } + + public static List composeStatsWithPrefix(StatisticsInfo info, final String fullPrefix) { ArrayList infos = new ArrayList(fullPrefix.length() + 1); infos.add(info); for (int i = 1; i <= fullPrefix.length(); i++) { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/PsiChangeHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/PsiChangeHandler.java index 956f39823ba5..b1db442a5162 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/PsiChangeHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/PsiChangeHandler.java @@ -108,8 +108,8 @@ public class PsiChangeHandler extends PsiTreeChangeAdapter implements Disposable @Override public void run() { EditorMarkupModel markupModel = (EditorMarkupModel)editor.getMarkupModel(); - PsiFile file = PsiDocumentManager.getInstance(myProject).getPsiFile(document); - TrafficLightRenderer.setOrRefreshErrorStripeRenderer(markupModel, myProject, document, file); + PsiFile file = PsiDocumentManager.getInstance(myProject).getPsiFile(editor.getDocument()); + TrafficLightRenderer.setOrRefreshErrorStripeRenderer(markupModel, myProject, editor.getDocument(), file); } }, ModalityState.stateForComponent(editor.getComponent()), myProject.getDisposed()); } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/TrafficLightRenderer.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/TrafficLightRenderer.java index fe80e2e3f555..123c60b9c0d9 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/TrafficLightRenderer.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/TrafficLightRenderer.java @@ -39,6 +39,7 @@ import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; import com.intellij.psi.FileViewProvider; +import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; import com.intellij.ui.LayeredIcon; import com.intellij.util.ArrayUtil; diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/editorActions/SpaceHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/editorActions/SpaceHandler.java index 5b81d9acc6ca..3c05ed5f2795 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/editorActions/SpaceHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/editorActions/SpaceHandler.java @@ -44,6 +44,10 @@ public class SpaceHandler extends TypedActionHandlerBase { } TemplateManagerImpl templateManager = (TemplateManagerImpl)TemplateManager.getInstance(project); + if (templateManager == null) { + throw new AssertionError(project + "; " + project.isDisposed()); + } + if (!templateManager.startTemplate(editor, TemplateSettings.SPACE_CHAR)) { if (myOriginalHandler != null) myOriginalHandler.execute(editor, charTyped, dataContext); } diff --git a/platform/lang-impl/src/com/intellij/execution/console/ConsoleHistoryController.java b/platform/lang-impl/src/com/intellij/execution/console/ConsoleHistoryController.java index 0ad9d8945153..bb57b42c59dc 100644 --- a/platform/lang-impl/src/com/intellij/execution/console/ConsoleHistoryController.java +++ b/platform/lang-impl/src/com/intellij/execution/console/ConsoleHistoryController.java @@ -17,6 +17,7 @@ package com.intellij.execution.console; import com.intellij.codeInsight.lookup.LookupManager; import com.intellij.execution.process.ConsoleHistoryModel; +import com.intellij.lang.Language; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; @@ -26,17 +27,26 @@ import com.intellij.openapi.application.PathManager; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.command.undo.UndoConstants; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.editor.CaretModel; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.ScrollType; +import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.actions.ContentChooser; +import com.intellij.openapi.editor.ex.EditorEx; +import com.intellij.openapi.editor.ex.util.LexerEditorHighlighter; +import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.fileTypes.SyntaxHighlighter; +import com.intellij.openapi.fileTypes.SyntaxHighlighterFactory; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ex.ProjectEx; import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.text.CharFilter; import com.intellij.openapi.util.text.StringHash; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.CharsetToolkit; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiFileFactory; +import com.intellij.testFramework.LightVirtualFile; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.io.SafeFileOutputStream; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.xml.XppReader; @@ -218,9 +228,12 @@ public class ConsoleHistoryController { private static void cleanupOldFiles(final File dir) { final long keep2weeks = 2 * 1000L * 60 * 60 * 24 * 7; final long curTime = System.currentTimeMillis(); - for (File file : dir.listFiles()) { - if (file.isFile() && file.getName().endsWith(".hist.xml") && curTime - file.lastModified() > keep2weeks) { - file.delete(); + File[] files = dir.listFiles(); + if (files != null) { + for (File file : files) { + if (file.isFile() && file.getName().endsWith(".hist.xml") && curTime - file.lastModified() > keep2weeks) { + file.delete(); + } } } } @@ -246,19 +259,46 @@ public class ConsoleHistoryController { if (storeUserText) { myUserValue = document.getText(); } + String text = StringUtil.notNullize(command); + int offset; if (regularMode) { - document.setText(StringUtil.notNullize(command)); + if (myMultiline) { + if (text.isEmpty()) return; + int selectionStart = editor.getSelectionModel().getSelectionStart(); + int selectionEnd = editor.getSelectionModel().getSelectionEnd(); + int caretOffset = editor.getCaretModel().getOffset(); + int line = document.getLineNumber(caretOffset); + int lineStartOffset = document.getLineStartOffset(line); + if (selectionStart == lineStartOffset) document.deleteString(selectionStart, selectionEnd); + String trimmedLine = document.getText(new TextRange(lineStartOffset, document.getLineEndOffset(line))).trim(); + if (StringUtil.findFirst(trimmedLine, new CharFilter() { + @Override + public boolean accept(char ch) { + return ch =='\'' || ch == '\"' || ch == '_' || Character.isLetterOrDigit(ch); + } + }) > -1) { + text += "\n"; + } + document.insertString(lineStartOffset, text); + offset = lineStartOffset; + editor.getSelectionModel().setSelection(lineStartOffset, lineStartOffset + text.length()); + } + else { + document.setText(text); + offset = document.getTextLength(); + } } else { + offset = 0; try { document.putUserData(UndoConstants.DONT_RECORD_UNDO, Boolean.TRUE); - document.setText(StringUtil.notNullize(command)); + document.setText(text); } finally { document.putUserData(UndoConstants.DONT_RECORD_UNDO, null); } } - editor.getCaretModel().moveToOffset(regularMode? document.getTextLength() : 0); + editor.getCaretModel().moveToOffset(offset); editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); } }.execute(); @@ -276,15 +316,14 @@ public class ConsoleHistoryController { @Override public void actionPerformed(final AnActionEvent e) { final String command; - command = myNext ? myModel.getHistoryNext() : StringUtil.notNullize(myModel.getHistoryPrev(), StringUtil.notNullize(myUserValue)); + command = myNext ? myModel.getHistoryNext() : StringUtil.notNullize(myModel.getHistoryPrev(), myMultiline? "" : StringUtil.notNullize(myUserValue)); setConsoleText(command, myNext && myModel.getHistoryCursor() == 0, true); } @Override public void update(final AnActionEvent e) { super.update(e); - final boolean hasStuff = myModel.hasHistory(myNext); - e.getPresentation().setEnabled(hasStuff && (myMultiline || canMoveInEditor(myNext))); + e.getPresentation().setEnabled(myMultiline || canMoveInEditor(myNext)); } } @@ -358,23 +397,51 @@ public class ConsoleHistoryController { @Override public void actionPerformed(final AnActionEvent e) { - final ContentChooser chooser = new ContentChooser(myConsole.getProject(), myConsole.getTitle(), true) { + final ContentChooser chooser = new ContentChooser(myConsole.getProject(), myConsole.getTitle() + " History", true) { @Override - protected void removeContentAt(final String content) { + protected void removeContentAt(String content) { myModel.removeFromHistory(content); } @Override - protected String getStringRepresentationFor(final String content) { + protected String getStringRepresentationFor(String content) { return content; } @Override protected List getContents() { - return myModel.getHistory(); + List history = myModel.getHistory(); + return ContainerUtil.reverse(history); + } + + @Override + protected Editor createIdeaEditor(String text) { + PsiFile consoleFile = myConsole.getFile(); + Language language = consoleFile.getLanguage(); + Project project = consoleFile.getProject(); + + PsiFile psiFile = PsiFileFactory.getInstance(project).createFileFromText( + "a."+consoleFile.getFileType().getDefaultExtension(), + language, + StringUtil.convertLineSeparators(new String(text)), false, true); + VirtualFile virtualFile = psiFile.getViewProvider().getVirtualFile(); + if (virtualFile instanceof LightVirtualFile) ((LightVirtualFile)virtualFile).setWritable(false); + Document document = FileDocumentManager.getInstance().getDocument(virtualFile); + EditorFactory editorFactory = EditorFactory.getInstance(); + EditorEx editor = (EditorEx)editorFactory.createViewer(document, project); + editor.getSettings().setFoldingOutlineShown(false); + editor.getSettings().setLineMarkerAreaShown(false); + editor.getSettings().setIndentGuidesShown(false); + + SyntaxHighlighter highlighter = SyntaxHighlighterFactory.getSyntaxHighlighter(language, project, psiFile.getViewProvider().getVirtualFile()); + editor.setHighlighter(new LexerEditorHighlighter(highlighter, editor.getColorsScheme())); + return editor; } }; + chooser.setContentIcon(null); + chooser.setSplitterOrientation(false); + chooser.setSelectedIndex(Math.min(myModel.getHistorySize() - myModel.getHistoryCursor() - 1, myModel.getHistorySize() - 1)); chooser.show(); if (chooser.isOK()) { setConsoleText(myModel.getHistory().get(chooser.getSelectedIndex()), false, true); diff --git a/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java b/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java index 74ae71206c4f..52ffcbf1bec2 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java @@ -1356,11 +1356,16 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo @Nullable protected OccurenceInfo calcNextOccurrence(final int delta) { - return EditorHyperlinkSupport.getNextOccurrence(myEditor, myHyperlinks.getHyperlinks().keySet(), delta, new Consumer() { + final EditorHyperlinkSupport hyperlinks = myHyperlinks; + if (hyperlinks == null) { + return null; + } + + return EditorHyperlinkSupport.getNextOccurrence(myEditor, hyperlinks.getHyperlinks().keySet(), delta, new Consumer() { @Override public void consume(RangeHighlighter next) { scrollTo(next.getStartOffset()); - final HyperlinkInfo hyperlinkInfo = myHyperlinks.getHyperlinks().get(next); + final HyperlinkInfo hyperlinkInfo = hyperlinks.getHyperlinks().get(next); if (hyperlinkInfo != null) { hyperlinkInfo.navigate(myProject); } diff --git a/platform/lang-impl/src/com/intellij/find/impl/livePreview/SearchResults.java b/platform/lang-impl/src/com/intellij/find/impl/livePreview/SearchResults.java index 3f3f3c5271cf..65ed2d47e14a 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/livePreview/SearchResults.java +++ b/platform/lang-impl/src/com/intellij/find/impl/livePreview/SearchResults.java @@ -16,6 +16,7 @@ import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.concurrency.FutureResult; import com.intellij.util.containers.HashSet; @@ -192,45 +193,6 @@ public class SearchResults implements DocumentListener { public void clear() { searchCompleted(new ArrayList(), getEditor(), null, false, null, getStamp()); } - - private static class BombedCharSequence implements CharSequence { - private CharSequence delegate; - private long myTime; - private long i = 0; - - public BombedCharSequence(CharSequence sequence, long time) { - delegate = sequence; - myTime = time; - } - - @Override - public int length() { - check(); - return delegate.length(); - } - - @Override - public char charAt(int i) { - check(); - return delegate.charAt(i); - } - - private void check() { - ++i; - if (i % 1000 == 0) { - long l = System.currentTimeMillis(); - if (l >= myTime) { - throw new ProcessCanceledException(); - } - } - } - - @Override - public CharSequence subSequence(int i, int i1) { - check(); - return delegate.subSequence(i, i1); - } - } public void updateThreadSafe(final FindModel findModel, final boolean toChangeSelection, @Nullable final TextRange next, final int stamp) { if (myDisposed) return; @@ -330,8 +292,8 @@ public class SearchResults implements DocumentListener { FindManager findManager = FindManager.getInstance(getProject()); FindResult result; try { - BombedCharSequence - bombedCharSequence = new BombedCharSequence(editor.getDocument().getCharsSequence(), System.currentTimeMillis() + 3000); + StringUtil.BombedCharSequence + bombedCharSequence = new StringUtil.BombedCharSequence(editor.getDocument().getCharsSequence(), System.currentTimeMillis() + 3000); result = findManager.findString(bombedCharSequence, offset, findModel, virtualFile); } catch(PatternSyntaxException e) { result = null; diff --git a/platform/lang-impl/src/com/intellij/ide/util/MemberChooser.java b/platform/lang-impl/src/com/intellij/ide/util/MemberChooser.java index 30de49da9963..7e0ded9a4836 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/MemberChooser.java +++ b/platform/lang-impl/src/com/intellij/ide/util/MemberChooser.java @@ -32,6 +32,7 @@ import com.intellij.psi.codeStyle.CodeStyleSettingsManager; import com.intellij.ui.*; import com.intellij.ui.treeStructure.Tree; import com.intellij.util.PlatformIcons; +import com.intellij.util.PlatformUtils; import com.intellij.util.containers.Convertor; import com.intellij.util.containers.FactoryMap; import com.intellij.util.containers.HashMap; @@ -327,7 +328,7 @@ public class MemberChooser extends DialogWrapper implemen String text = lastPathComponent.getDelegate().getText(); if (text != null) { int i = text.indexOf(':'); - if (i >= 0) { + if (i >= 0 && !PlatformUtils.isCidr()) { // In AppCode colons can occur in the method selectors and matching should work after them text = text.substring(0, i); } i = text.indexOf('('); diff --git a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNameBase.java b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNameBase.java index f83be44e33cb..4b9a9d836244 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNameBase.java +++ b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNameBase.java @@ -53,10 +53,7 @@ import com.intellij.psi.codeStyle.NameUtil; import com.intellij.psi.statistics.StatisticsInfo; import com.intellij.psi.statistics.StatisticsManager; import com.intellij.psi.util.PsiUtilCore; -import com.intellij.ui.ClickListener; -import com.intellij.ui.DocumentAdapter; -import com.intellij.ui.ListScrollingUtil; -import com.intellij.ui.ScrollPaneFactory; +import com.intellij.ui.*; import com.intellij.ui.components.JBList; import com.intellij.ui.popup.PopupOwner; import com.intellij.ui.popup.PopupPositionManager; @@ -1416,12 +1413,18 @@ public abstract class ChooseByNameBase { public static final String NON_PREFIX_SEPARATOR = "non-prefix matches:"; public static Component renderNonPrefixSeparatorComponent(Color backgroundColor) { + final TitledSeparator separator = new TitledSeparator(); + separator.setBorder(BorderFactory.createEmptyBorder(0, 2, 0, 0)); + separator.setBackground(backgroundColor); + return separator; + /* final JPanel panel = new JPanel(new BorderLayout()); final JSeparator separator = new JSeparator(SwingConstants.HORIZONTAL); panel.setPreferredSize(new Dimension(0, 3)); panel.add(separator, BorderLayout.SOUTH); panel.setBackground(backgroundColor); return panel; +*/ } private class CalcElementsThread implements Runnable { diff --git a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/DefaultChooseByNameItemProvider.java b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/DefaultChooseByNameItemProvider.java index 2091111f8d71..00d806438553 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/DefaultChooseByNameItemProvider.java +++ b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/DefaultChooseByNameItemProvider.java @@ -96,6 +96,7 @@ public class DefaultChooseByNameItemProvider implements ChooseByNameItemProvider boolean previousElemSeparator = false; boolean wasElement = false; + List> patternsAndMatchers = getPatternsAndMatchers(qualifierPattern, base); for (String name : namesList) { indicator.checkCanceled(); if (name == ChooseByNameBase.NON_PREFIX_SEPARATOR) { @@ -109,7 +110,7 @@ public class DefaultChooseByNameItemProvider implements ChooseByNameItemProvider if (elements.length > 1) { sameNameElements.clear(); for (final Object element : elements) { - if (matchesQualifier(element, qualifierPattern, base)) { + if (matchesQualifier(element, base, patternsAndMatchers)) { sameNameElements.add(element); } } @@ -121,7 +122,7 @@ public class DefaultChooseByNameItemProvider implements ChooseByNameItemProvider wasElement = true; } } - else if (elements.length == 1 && matchesQualifier(elements[0], qualifierPattern, base)) { + else if (elements.length == 1 && matchesQualifier(elements[0], base, patternsAndMatchers)) { if (previousElemSeparator && !consumer.process(ChooseByNameBase.NON_PREFIX_SEPARATOR)) return; if (!consumer.process(elements[0])) return; previousElemSeparator = false; @@ -181,20 +182,12 @@ public class DefaultChooseByNameItemProvider implements ChooseByNameItemProvider } private static boolean matchesQualifier(final Object element, - @NotNull final String qualifierPattern, - @NotNull final ChooseByNameBase base) { + @NotNull final ChooseByNameBase base, + final List> patternsAndMatchers) { final String name = base.getModel().getFullName(element); if (name == null) return false; final List suspects = split(name, base); - final List> patternsAndMatchers = - ContainerUtil.map2List(split(qualifierPattern, base), new Function>() { - @NotNull - @Override - public Pair fun(String s) { - return Pair.create(getNamePattern(base, s), buildPatternMatcher(getNamePattern(base, s), NameUtil.MatchingCaseSensitivity.NONE)); - } - }); int matchPosition = 0; @@ -224,6 +217,16 @@ public class DefaultChooseByNameItemProvider implements ChooseByNameItemProvider return true; } + private static List> getPatternsAndMatchers(String qualifierPattern, final ChooseByNameBase base) { + return ContainerUtil.map2List(split(qualifierPattern, base), new Function>() { + @NotNull + @Override + public Pair fun(String s) { + return Pair.create(getNamePattern(base, s), buildPatternMatcher(getNamePattern(base, s), NameUtil.MatchingCaseSensitivity.NONE)); + } + }); + } + @NotNull @Override public List filterNames(@NotNull ChooseByNameBase base, @NotNull String[] names, @NotNull String pattern) { diff --git a/platform/lang-impl/src/com/intellij/ide/util/projectWizard/WebModuleGenerationStep.java b/platform/lang-impl/src/com/intellij/ide/util/projectWizard/WebModuleGenerationStep.java index b04e3f4c8d37..89ae729c865c 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/projectWizard/WebModuleGenerationStep.java +++ b/platform/lang-impl/src/com/intellij/ide/util/projectWizard/WebModuleGenerationStep.java @@ -15,14 +15,14 @@ */ package com.intellij.ide.util.projectWizard; +import com.intellij.ide.startup.StartupManagerEx; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.module.Module; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.ValidationInfo; -import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.platform.DirectoryProjectGenerator; import com.intellij.platform.WebProjectGenerator; @@ -30,6 +30,7 @@ import com.intellij.ui.ListCellRendererWrapper; import com.intellij.ui.components.JBList; import com.intellij.ui.components.JBScrollPane; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -46,7 +47,6 @@ import java.util.Map; */ public class WebModuleGenerationStep extends ModuleWizardStep { - private final Project myProject; private final ModuleBuilder myModuleBuilder; private final Icon myIcon; private final String myHelpId; @@ -55,11 +55,9 @@ public class WebModuleGenerationStep extends ModuleWizardStep { private WebProjectGenerator myCurrentGenerator; private JPanel myRightPanel; - public WebModuleGenerationStep(@Nullable Project project, - @NotNull ModuleBuilder moduleBuilder, + public WebModuleGenerationStep(@NotNull ModuleBuilder moduleBuilder, @NotNull Icon icon, @NotNull String helpId) { - myProject = project; myModuleBuilder = moduleBuilder; myIcon = icon; myHelpId = helpId; @@ -126,11 +124,6 @@ public class WebModuleGenerationStep extends ModuleWizardStep { splitPane.setRightComponent(myRightPanel); generatorList.setSelectedValue(emptyProjectGenerator, true); - ApplicationManager.getApplication().invokeLater(new Runnable() { - public void run() { - generatorList.requestFocusInWindow(); - } - }, ModalityState.any()); return splitPane; } @@ -202,7 +195,7 @@ public class WebModuleGenerationStep extends ModuleWizardStep { @SuppressWarnings("unchecked") @Override public void updateDataModel() { - WebProjectGenerator generator = myCurrentGenerator; + final WebProjectGenerator generator = myCurrentGenerator; if (generator == null) { throw new RuntimeException("Current generator should be not-null"); } @@ -210,14 +203,42 @@ public class WebModuleGenerationStep extends ModuleWizardStep { if (peer == null) { throw new RuntimeException("Peer should be not-null for " + myCurrentGenerator.getName()); } - Object settings = peer.getSettings(); + final Object settings = peer.getSettings(); File dir = new File(myModuleBuilder.getModuleFileDirectory()); - VirtualFile moduleDir = LocalFileSystem.getInstance().findFileByIoFile(dir); - if (moduleDir == null || !moduleDir.isValid()) { - moduleDir = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(dir); - } + final VirtualFile moduleDir = VfsUtil.findFileByIoFile(dir, true); if (moduleDir != null && moduleDir.isValid()) { - generator.generateProject(myProject, moduleDir, settings, null); + myModuleBuilder.addListener(new ModuleBuilderListener() { + @Override + public void moduleCreated(@NotNull final Module module) { + myModuleBuilder.removeListener(this); + + final Project project = module.getProject(); + final Runnable task = new Runnable() { + @Override + public void run() { + generator.generateProject(project, moduleDir, settings, module); + } + }; + final boolean scheduledAsPostStartupActivity; + StartupManagerEx startupManager = StartupManagerEx.getInstanceEx(project); + //noinspection SynchronizationOnLocalVariableOrMethodParameter + synchronized (startupManager) { + scheduledAsPostStartupActivity = !startupManager.postStartupActivityPassed(); + if (scheduledAsPostStartupActivity) { + startupManager.registerPostStartupActivity(task); + } + } + if (!scheduledAsPostStartupActivity) { + UIUtil.invokeLaterIfNeeded(new Runnable() { + public void run() { + if (!project.isDisposed()) { + task.run(); + } + } + }); + } + } + }); } } @@ -238,7 +259,7 @@ public class WebModuleGenerationStep extends ModuleWizardStep { } @Override - public void generateProject(Project project, VirtualFile baseDir, Object settings, Module module) {} + public void generateProject(@NotNull Project project, @NotNull VirtualFile baseDir, @NotNull Object settings, @NotNull Module module) {} @NotNull @Override diff --git a/platform/lang-impl/src/com/intellij/openapi/module/WebModuleType.java b/platform/lang-impl/src/com/intellij/openapi/module/WebModuleType.java index 69917d4b553c..426686827d0e 100644 --- a/platform/lang-impl/src/com/intellij/openapi/module/WebModuleType.java +++ b/platform/lang-impl/src/com/intellij/openapi/module/WebModuleType.java @@ -21,7 +21,6 @@ public class WebModuleType extends WebModuleTypeBase { @Override public ModuleWizardStep[] createWizardSteps(WizardContext wizardContext, ModuleBuilder moduleBuilder, ModulesProvider modulesProvider) { WebModuleGenerationStep generationStep = new WebModuleGenerationStep( - wizardContext.getProject(), moduleBuilder, wizardContext.getStepIcon(), "reference.dialogs.new.project.fromScratch.webModuleGeneration" diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/CachingReference.java b/platform/lang-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/CachingReference.java index fe032d918805..5f59d511a03b 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/CachingReference.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/CachingReference.java @@ -21,6 +21,7 @@ import com.intellij.psi.ElementManipulators; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiReference; import com.intellij.psi.impl.source.resolve.ResolveCache; +import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -46,9 +47,13 @@ public abstract class CachingReference implements PsiReference, EmptyResolveMess return false; } - @Nullable + @NotNull public static ElementManipulator getManipulator(T currentElement){ - return ElementManipulators.getManipulator(currentElement); + ElementManipulator manipulator = ElementManipulators.getManipulator(currentElement); + if (manipulator == null) { + throw new IncorrectOperationException("Manipulator for this element is not defined: " + currentElement); + } + return manipulator; } private static class MyResolver implements ResolveCache.Resolver { diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/FileReference.java b/platform/lang-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/FileReference.java index 56620d1fd2d4..ae652a95a1e0 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/FileReference.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/FileReference.java @@ -399,18 +399,15 @@ public class FileReference implements FileReferenceOwner, PsiPolyVariantReferenc @Override public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException { final ElementManipulator manipulator = CachingReference.getManipulator(getElement()); - if (manipulator != null) { - myFileReferenceSet.setElement(manipulator.handleContentChange(getElement(), getRangeInElement(), newElementName)); - //Correct ranges - int delta = newElementName.length() - myRange.getLength(); - myRange = new TextRange(getRangeInElement().getStartOffset(), getRangeInElement().getStartOffset() + newElementName.length()); - FileReference[] references = myFileReferenceSet.getAllReferences(); - for (int idx = myIndex + 1; idx < references.length; idx++) { - references[idx].myRange = references[idx].myRange.shiftRight(delta); - } - return myFileReferenceSet.getElement(); + myFileReferenceSet.setElement(manipulator.handleContentChange(getElement(), getRangeInElement(), newElementName)); + //Correct ranges + int delta = newElementName.length() - myRange.getLength(); + myRange = new TextRange(getRangeInElement().getStartOffset(), getRangeInElement().getStartOffset() + newElementName.length()); + FileReference[] references = myFileReferenceSet.getAllReferences(); + for (int idx = myIndex + 1; idx < references.length; idx++) { + references[idx].myRange = references[idx].myRange.shiftRight(delta); } - throw new IncorrectOperationException("Manipulator for this element is not defined: " + getElement()); + return myFileReferenceSet.getElement(); } public PsiElement bindToElement(@NotNull final PsiElement element, final boolean absolute) throws IncorrectOperationException { @@ -535,11 +532,12 @@ public class FileReference implements FileReferenceOwner, PsiPolyVariantReferenc protected PsiElement rename(final String newName) throws IncorrectOperationException { final TextRange range = new TextRange(myFileReferenceSet.getStartInElement(), getRangeInElement().getEndOffset()); - final ElementManipulator manipulator = CachingReference.getManipulator(getElement()); + PsiElement element = getElement(); + final ElementManipulator manipulator = CachingReference.getManipulator(element); if (manipulator == null) { - throw new IncorrectOperationException("Manipulator not defined for: " + getElement()); + throw new IncorrectOperationException("Manipulator not defined for: " + element + " of class " + element.getClass()); } - return manipulator.handleContentChange(getElement(), range, newName); + return manipulator.handleContentChange(element, range, newName); } @Override diff --git a/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/InplaceRefactoring.java b/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/InplaceRefactoring.java index 67395edb926f..7f743742985f 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/InplaceRefactoring.java +++ b/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/InplaceRefactoring.java @@ -190,7 +190,7 @@ public abstract class InplaceRefactoring { return buildTemplateAndStart(refs, stringUsages, scope, containingFile); } - protected boolean notSameFile(@Nullable VirtualFile file, PsiFile containingFile) { + protected boolean notSameFile(@Nullable VirtualFile file, @NotNull PsiFile containingFile) { return !Comparing.equal(getTopLevelVirtualFile(containingFile.getViewProvider()), file); } diff --git a/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/MemberInplaceRenamer.java b/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/MemberInplaceRenamer.java index 853cf081c58d..4a86b817e16a 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/MemberInplaceRenamer.java +++ b/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/MemberInplaceRenamer.java @@ -125,9 +125,9 @@ public class MemberInplaceRenamer extends VariableInplaceRenamer { } @Override - protected boolean notSameFile(@Nullable VirtualFile file, PsiFile containingFile) { + protected boolean notSameFile(@Nullable VirtualFile file, @NotNull PsiFile containingFile) { final PsiFile currentFile = PsiDocumentManager.getInstance(myProject).getPsiFile(myEditor.getDocument()); - return currentFile == null || containingFile != currentFile; + return currentFile == null || InjectedLanguageUtil.getTopLevelFile(containingFile) != InjectedLanguageUtil.getTopLevelFile(currentFile); } @Override diff --git a/platform/lang-impl/src/com/intellij/ui/popup/util/DetailController.java b/platform/lang-impl/src/com/intellij/ui/popup/util/DetailController.java index a834cec2e503..c9187a5b8dbf 100644 --- a/platform/lang-impl/src/com/intellij/ui/popup/util/DetailController.java +++ b/platform/lang-impl/src/com/intellij/ui/popup/util/DetailController.java @@ -80,7 +80,7 @@ public class DetailController implements TreeSelectionListener, ListSelectionLis }, 100); } - void updateDetailViewLater() { + public void selectionChanged() { doUpdateDetailView(); } @@ -105,11 +105,11 @@ public class DetailController implements TreeSelectionListener, ListSelectionLis @Override public void valueChanged(TreeSelectionEvent event) { - updateDetailViewLater(); + selectionChanged(); } @Override public void valueChanged(ListSelectionEvent event) { - updateDetailViewLater(); + selectionChanged(); } } \ No newline at end of file diff --git a/platform/platform-api/src/com/intellij/ui/ColoredListCellRenderer.java b/platform/platform-api/src/com/intellij/ui/ColoredListCellRenderer.java index 6ec50ebba74f..92bd61536d3a 100644 --- a/platform/platform-api/src/com/intellij/ui/ColoredListCellRenderer.java +++ b/platform/platform-api/src/com/intellij/ui/ColoredListCellRenderer.java @@ -19,8 +19,6 @@ import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import javax.swing.*; -import javax.swing.border.EmptyBorder; -import javax.swing.border.LineBorder; import java.awt.*; /** @@ -29,25 +27,27 @@ import java.awt.*; * * @author Vladimir Kondratyev */ -public abstract class ColoredListCellRenderer extends SimpleColoredComponent implements ListCellRenderer{ +public abstract class ColoredListCellRenderer extends SimpleColoredComponent implements ListCellRenderer { protected boolean mySelected; + protected Color myForeground; + protected Color mySelectionForeground; - public ColoredListCellRenderer(){ + public ColoredListCellRenderer() { setFocusBorderAroundIcon(true); getIpad().left = UIUtil.getListCellHPadding(); getIpad().right = UIUtil.getListCellHPadding(); } - public Component getListCellRendererComponent( - final JList list, - final Object value, - final int index, - final boolean selected, - final boolean hasFocus - ){ + public Component getListCellRendererComponent(JList list, + Object value, + int index, + boolean selected, + boolean hasFocus) { clear(); - mySelected=selected; + mySelected = selected; + myForeground = list.getForeground(); + mySelectionForeground = list.getSelectionForeground(); if (UIUtil.isWinLafOnVista()) { // the system draws a gradient background on the combobox selected item - don't overdraw it with our solid background if (index == -1) { @@ -60,17 +60,12 @@ public abstract class ColoredListCellRenderer extends SimpleColoredComponent imp } } else { - if(selected){ - setBackground(list.getSelectionBackground()); - }else{ - setBackground(null); - } + setBackground(selected ? list.getSelectionBackground() : null); } setPaintFocusBorder(hasFocus); customizeCellRenderer(list, value, index, selected, hasFocus); - return this; } @@ -78,16 +73,12 @@ public abstract class ColoredListCellRenderer extends SimpleColoredComponent imp * When the item is selected then we use default tree's selection foreground. * It guaranties readability of selected text in any LAF. */ - public final void append(@NotNull final String fragment, @NotNull final SimpleTextAttributes attributes, boolean isMainText) { - if(mySelected) { - super.append( - fragment, - new SimpleTextAttributes( - attributes.getStyle(), UIUtil.getListSelectionForeground() - ), isMainText); + public final void append(@NotNull String fragment, @NotNull SimpleTextAttributes attributes, boolean isMainText) { + if (mySelected) { + super.append(fragment, new SimpleTextAttributes(attributes.getStyle(), mySelectionForeground), isMainText); } else if (attributes.getFgColor() == null) { - super.append(fragment, new SimpleTextAttributes(attributes.getStyle(), UIUtil.getListForeground()), isMainText); + super.append(fragment, new SimpleTextAttributes(attributes.getStyle(), myForeground), isMainText); } else { super.append(fragment, attributes, isMainText); @@ -98,23 +89,17 @@ public abstract class ColoredListCellRenderer extends SimpleColoredComponent imp // There is a bug in BasicComboPopup. It does not add renderer into CellRendererPane, // so font can be null here. - final Font oldFont = getFont(); - if(oldFont == null){ + Font oldFont = getFont(); + if (oldFont == null) { setFont(UIUtil.getListFont()); } - final Dimension result = super.getPreferredSize(); - if(oldFont == null){ + Dimension result = super.getPreferredSize(); + if (oldFont == null) { setFont(null); } return result; } - protected abstract void customizeCellRenderer( - JList list, - Object value, - int index, - boolean selected, - boolean hasFocus - ); + protected abstract void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus); } diff --git a/platform/platform-api/src/com/intellij/ui/components/labels/LinkLabel.java b/platform/platform-api/src/com/intellij/ui/components/labels/LinkLabel.java index b2489b897fc6..dfacf0300d7f 100644 --- a/platform/platform-api/src/com/intellij/ui/components/labels/LinkLabel.java +++ b/platform/platform-api/src/com/intellij/ui/components/labels/LinkLabel.java @@ -120,6 +120,7 @@ public class LinkLabel extends JLabel { } protected void paintComponent(Graphics g) { + super.paintComponent(g); final Border border = getBorder(); int shiftX = 0; int shiftY = 0; diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/actions/ContentChooser.java b/platform/platform-impl/src/com/intellij/openapi/editor/actions/ContentChooser.java index b56fdfedb49a..7d7cb137453b 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/actions/ContentChooser.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/actions/ContentChooser.java @@ -17,18 +17,26 @@ package com.intellij.openapi.editor.actions; import com.intellij.CommonBundle; import com.intellij.icons.AllIcons; +import com.intellij.ide.ui.SplitterProportionsDataImpl; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.EditorFactory; +import com.intellij.openapi.editor.colors.EditorColorsManager; +import com.intellij.openapi.editor.colors.EditorColorsScheme; +import com.intellij.openapi.editor.colors.EditorFontType; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Splitter; +import com.intellij.openapi.ui.SplitterProportionsData; import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.*; import com.intellij.ui.components.JBList; +import com.intellij.util.Alarm; import com.intellij.util.ArrayUtil; +import com.intellij.util.ObjectUtils; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.event.ListSelectionEvent; @@ -38,18 +46,21 @@ import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; public abstract class ContentChooser extends DialogWrapper { - private JList myList; private List myAllContents; - private Editor myViewer; + private final boolean myUseIdeaEditor; - private Splitter mySplitter; + private final JList myList; + private final Splitter mySplitter; private final Project myProject; private final boolean myAllowMultipleSelections; + private final Alarm myUpdateAlarm; + private Icon myListEntryIcon = AllIcons.FileTypes.Text; public ContentChooser(Project project, String title, boolean useIdeaEditor) { this(project, title, useIdeaEditor, false); @@ -60,6 +71,9 @@ public abstract class ContentChooser extends DialogWrapper { myProject = project; myUseIdeaEditor = useIdeaEditor; myAllowMultipleSelections = allowMultipleSelections; + myUpdateAlarm = new Alarm(getDisposable()); + mySplitter = new Splitter(true, 0.3f); + myList = new JBList(); setOKButtonText(CommonBundle.getOkButtonText()); setTitle(title); @@ -67,6 +81,14 @@ public abstract class ContentChooser extends DialogWrapper { init(); } + public void setContentIcon(@Nullable Icon icon) { + myListEntryIcon = icon; + } + + public void setSplitterOrientation(boolean vertical) { + mySplitter.setOrientation(vertical); + } + @Override public JComponent getPreferredFocusedComponent() { return myList; @@ -74,10 +96,17 @@ public abstract class ContentChooser extends DialogWrapper { @Override protected JComponent createCenterPanel() { - myList = new JBList(); - final int selectionMode = myAllowMultipleSelections ? ListSelectionModel.MULTIPLE_INTERVAL_SELECTION + final int selectionMode = myAllowMultipleSelections ? ListSelectionModel.MULTIPLE_INTERVAL_SELECTION : ListSelectionModel.SINGLE_SELECTION; myList.setSelectionMode(selectionMode); + if (myUseIdeaEditor) { + EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme(); + myList.setFont(scheme.getFont(EditorFontType.PLAIN)); + Color fg = ObjectUtils.chooseNotNull(scheme.getDefaultForeground(), UIUtil.getListForeground()); + Color bg = ObjectUtils.chooseNotNull(scheme.getDefaultBackground(), UIUtil.getListBackground()); + myList.setForeground(fg); + myList.setBackground(bg); + } rebuildListContent(); @@ -91,11 +120,6 @@ public abstract class ContentChooser extends DialogWrapper { myList.setCellRenderer(new MyListCellRenderer()); - - if (myAllContents.size() > 0) { - myList.setSelectedIndex(0); - } - myList.addKeyListener(new KeyAdapter() { @Override public void keyReleased(KeyEvent e) { @@ -131,19 +155,31 @@ public abstract class ContentChooser extends DialogWrapper { } }); - mySplitter = new Splitter(true); mySplitter.setFirstComponent(ScrollPaneFactory.createScrollPane(myList)); mySplitter.setSecondComponent(new JPanel()); - updateViewerForSelection(); + ListScrollingUtil.installActions(myList); + ListScrollingUtil.ensureSelectionExists(myList); + updateViewerForSelection(); myList.addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { - updateViewerForSelection(); + myUpdateAlarm.cancelAllRequests(); + myUpdateAlarm.addRequest(new Runnable() { + @Override + public void run() { + updateViewerForSelection(); + } + }, 100); } }); mySplitter.setPreferredSize(new Dimension(500, 500)); + + SplitterProportionsData d = new SplitterProportionsDataImpl(); + d.externalizeToDimensionService(getClass().getName()); + d.restoreSplitterProportions(mySplitter); + new ListSpeedSearch(myList); return mySplitter; @@ -153,7 +189,7 @@ public abstract class ContentChooser extends DialogWrapper { @Override protected String getDimensionServiceKey() { - return "#com.intellij.openapi.editor.actions.MultiplePasteAction.Chooser"; + return getClass().getName(); // store different values for multi-paste, history and commit messages } private void updateViewerForSelection() { @@ -169,14 +205,10 @@ public abstract class ContentChooser extends DialogWrapper { } if (myUseIdeaEditor) { - Document doc = EditorFactory.getInstance().createDocument(fullString); - myViewer = EditorFactory.getInstance().createViewer(doc, myProject); - myViewer.getComponent().setPreferredSize(new Dimension(300, 500)); - myViewer.getSettings().setFoldingOutlineShown(false); - myViewer.getSettings().setLineNumbersShown(false); - myViewer.getSettings().setLineMarkerAreaShown(false); - myViewer.getSettings().setIndentGuidesShown(false); - mySplitter.setSecondComponent(myViewer.getComponent()); + myViewer = createIdeaEditor(fullString); + JComponent component = myViewer.getComponent(); + component.setPreferredSize(new Dimension(300, 500)); + mySplitter.setSecondComponent(component); } else { final JTextArea textArea = new JTextArea(fullString); textArea.setRows(3); @@ -190,9 +222,24 @@ public abstract class ContentChooser extends DialogWrapper { mySplitter.revalidate(); } + protected Editor createIdeaEditor(String text) { + Document doc = EditorFactory.getInstance().createDocument(text); + Editor editor = EditorFactory.getInstance().createViewer(doc, myProject); + editor.getSettings().setFoldingOutlineShown(false); + editor.getSettings().setLineNumbersShown(false); + editor.getSettings().setLineMarkerAreaShown(false); + editor.getSettings().setIndentGuidesShown(false); + return editor; + } + @Override public void dispose() { super.dispose(); + + SplitterProportionsData d = new SplitterProportionsDataImpl(); + d.externalizeToDimensionService(getClass().getName()); + d.saveSplitterProportions(mySplitter); + if (myViewer != null) { EditorFactory.getInstance().releaseEditor(myViewer); myViewer = null; @@ -241,6 +288,12 @@ public abstract class ContentChooser extends DialogWrapper { return myList.getSelectedIndex(); } + public void setSelectedIndex(int index) { + myList.setSelectedIndex(index); + ListScrollingUtil.ensureIndexIsVisible(myList, index, 0); + updateViewerForSelection(); + } + @NotNull public int[] getSelectedIndices() { return myList.getSelectedIndices(); @@ -260,7 +313,7 @@ public abstract class ContentChooser extends DialogWrapper { return result; } - private static class MyListCellRenderer extends ColoredListCellRenderer { + private class MyListCellRenderer extends ColoredListCellRenderer { @Override protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) { // Fix GTK background @@ -268,11 +321,23 @@ public abstract class ContentChooser extends DialogWrapper { final Color background = selected ? UIUtil.getTreeSelectionBackground() : UIUtil.getTreeTextBackground(); UIUtil.changeBackGround(this, background); } - setIcon(AllIcons.FileTypes.Text); - if (index <= 9) { - append(String.valueOf((index + 1) % 10) + " ", SimpleTextAttributes.GRAYED_ATTRIBUTES); + setIcon(myListEntryIcon); + if (myUseIdeaEditor) { + int max = list.getModel().getSize(); + String indexString = String.valueOf(index + 1); + int count = String.valueOf(max).length() - indexString.length(); + char[] spaces = new char[count]; + Arrays.fill(spaces, ' '); + String prefix = indexString + new String(spaces) + " "; + append(prefix, SimpleTextAttributes.GRAYED_ATTRIBUTES); } - append((String) value, SimpleTextAttributes.REGULAR_ATTRIBUTES); + String text = (String)value; + + FontMetrics metrics = list.getFontMetrics(list.getFont()); + int charWidth = metrics.charWidth('m'); + int maxLength = list.getParent().getParent().getWidth() * 3 / charWidth / 2; + text = StringUtil.first(text, maxLength, true); // do not paint long strings + append(text, SimpleTextAttributes.REGULAR_ATTRIBUTES); } } } diff --git a/platform/platform-impl/src/com/intellij/platform/WebProjectGenerator.java b/platform/platform-impl/src/com/intellij/platform/WebProjectGenerator.java index d11569f29274..413463df977d 100644 --- a/platform/platform-impl/src/com/intellij/platform/WebProjectGenerator.java +++ b/platform/platform-impl/src/com/intellij/platform/WebProjectGenerator.java @@ -41,7 +41,7 @@ public abstract class WebProjectGenerator implements DirectoryProjectGenerato public abstract String getName(); @Override - @Nullable + @NotNull public final T showGenerationSettings(VirtualFile baseDir) throws ProcessCanceledException { GeneratorPeer peer = createPeer(); DialogWrapper dialog = new MyDialogWrapper(peer); @@ -53,7 +53,7 @@ public abstract class WebProjectGenerator implements DirectoryProjectGenerato } @Override - public abstract void generateProject(Project project, VirtualFile baseDir, T settings, Module module); + public abstract void generateProject(@NotNull Project project, @NotNull VirtualFile baseDir, @NotNull T settings, @NotNull Module module); /** * Always returns {@link ValidationResult#OK}. diff --git a/platform/platform-resources/src/idea/Keymap_Xcode.xml b/platform/platform-resources/src/idea/Keymap_Xcode.xml index c72b234af602..7353ec503e42 100644 --- a/platform/platform-resources/src/idea/Keymap_Xcode.xml +++ b/platform/platform-resources/src/idea/Keymap_Xcode.xml @@ -197,7 +197,6 @@ - @@ -211,6 +210,12 @@ + + + + + + @@ -319,7 +324,6 @@ - diff --git a/platform/platform-tests/testSrc/com/intellij/psi/util/NameUtilTest.java b/platform/platform-tests/testSrc/com/intellij/psi/util/NameUtilTest.java index 525353b9cdbd..cf5ea8bdb826 100644 --- a/platform/platform-tests/testSrc/com/intellij/psi/util/NameUtilTest.java +++ b/platform/platform-tests/testSrc/com/intellij/psi/util/NameUtilTest.java @@ -399,6 +399,10 @@ public class NameUtilTest extends UsefulTestCase { assertFalse(new MinusculeMatcher("WL", NameUtil.MatchingCaseSensitivity.ALL).matches("weblogic")); assertFalse(new MinusculeMatcher("webLogic", NameUtil.MatchingCaseSensitivity.ALL).matches("WebLogic")); assertFalse(new MinusculeMatcher("Str", NameUtil.MatchingCaseSensitivity.ALL).matches("SomeThingRidiculous")); + assertFalse(new MinusculeMatcher("*list*", NameUtil.MatchingCaseSensitivity.ALL).matches("List")); + assertFalse(new MinusculeMatcher("*list*", NameUtil.MatchingCaseSensitivity.ALL).matches("AbstractList")); + assertFalse(new MinusculeMatcher("java.util.list", NameUtil.MatchingCaseSensitivity.ALL).matches("java.util.List")); + assertFalse(new MinusculeMatcher("java.util.list", NameUtil.MatchingCaseSensitivity.ALL).matches("java.util.AbstractList")); } public void testMatchingFragments() { 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 b8ea6813f351..b5cd790fbda6 100644 --- a/platform/util/src/com/intellij/openapi/util/text/StringUtil.java +++ b/platform/util/src/com/intellij/openapi/util/text/StringUtil.java @@ -17,6 +17,7 @@ package com.intellij.openapi.util.text; import com.intellij.CommonBundle; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.TextRange; import com.intellij.util.*; @@ -2351,4 +2352,43 @@ public class StringUtil extends StringUtilRt { public static String getShortName(@NotNull String fqName, char separator) { return StringUtilRt.getShortName(fqName, separator); } + + public static class BombedCharSequence implements CharSequence { + private CharSequence delegate; + private long myTime; + private long i = 0; + + public BombedCharSequence(CharSequence sequence, long time) { + delegate = sequence; + myTime = time; + } + + @Override + public int length() { + check(); + return delegate.length(); + } + + @Override + public char charAt(int i) { + check(); + return delegate.charAt(i); + } + + private void check() { + ++i; + if (i % 1000 == 0) { + long l = System.currentTimeMillis(); + if (l >= myTime) { + throw new ProcessCanceledException(); + } + } + } + + @Override + public CharSequence subSequence(int i, int i1) { + check(); + return delegate.subSequence(i, i1); + } + } } diff --git a/platform/util/src/com/intellij/util/io/tar/TarBuffer.java b/platform/util/src/com/intellij/util/io/tar/TarBuffer.java new file mode 100644 index 000000000000..311d7dd1c954 --- /dev/null +++ b/platform/util/src/com/intellij/util/io/tar/TarBuffer.java @@ -0,0 +1,440 @@ +package com.intellij.util.io.tar; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Arrays; + +/** + * The TarBuffer class implements the tar archive concept + * of a buffered input stream. This concept goes back to the + * days of blocked tape drives and special io devices. In the + * Java universe, the only real function that this class + * performs is to ensure that files have the correct "block" + * size, or other tars will complain. + *

+ * You should never have a need to access this class directly. + * TarBuffers are created by Tar IO Streams. + * + */ + +// Copy-pasted from org.apache.tools.tar.TarBuffer +public class TarBuffer { + + /** Default record size */ + public static final int DEFAULT_RCDSIZE = (512); + + /** Default block size */ + public static final int DEFAULT_BLKSIZE = (DEFAULT_RCDSIZE * 20); + + private InputStream inStream; + private OutputStream outStream; + private byte[] blockBuffer; + private int currBlkIdx; + private int currRecIdx; + private int blockSize; + private int recordSize; + private int recsPerBlock; + private boolean debug; + + /** + * Constructor for a TarBuffer on an input stream. + * @param inStream the input stream to use + */ + public TarBuffer(InputStream inStream) { + this(inStream, TarBuffer.DEFAULT_BLKSIZE); + } + + /** + * Constructor for a TarBuffer on an input stream. + * @param inStream the input stream to use + * @param blockSize the block size to use + */ + public TarBuffer(InputStream inStream, int blockSize) { + this(inStream, blockSize, TarBuffer.DEFAULT_RCDSIZE); + } + + /** + * Constructor for a TarBuffer on an input stream. + * @param inStream the input stream to use + * @param blockSize the block size to use + * @param recordSize the record size to use + */ + public TarBuffer(InputStream inStream, int blockSize, int recordSize) { + this.inStream = inStream; + this.outStream = null; + + this.initialize(blockSize, recordSize); + } + + /** + * Constructor for a TarBuffer on an output stream. + * @param outStream the output stream to use + */ + public TarBuffer(OutputStream outStream) { + this(outStream, TarBuffer.DEFAULT_BLKSIZE); + } + + /** + * Constructor for a TarBuffer on an output stream. + * @param outStream the output stream to use + * @param blockSize the block size to use + */ + public TarBuffer(OutputStream outStream, int blockSize) { + this(outStream, blockSize, TarBuffer.DEFAULT_RCDSIZE); + } + + /** + * Constructor for a TarBuffer on an output stream. + * @param outStream the output stream to use + * @param blockSize the block size to use + * @param recordSize the record size to use + */ + public TarBuffer(OutputStream outStream, int blockSize, int recordSize) { + this.inStream = null; + this.outStream = outStream; + + this.initialize(blockSize, recordSize); + } + + /** + * Initialization common to all constructors. + */ + private void initialize(int blockSize, int recordSize) { + this.debug = false; + this.blockSize = blockSize; + this.recordSize = recordSize; + this.recsPerBlock = (this.blockSize / this.recordSize); + this.blockBuffer = new byte[this.blockSize]; + + if (this.inStream != null) { + this.currBlkIdx = -1; + this.currRecIdx = this.recsPerBlock; + } else { + this.currBlkIdx = 0; + this.currRecIdx = 0; + } + } + + /** + * Get the TAR Buffer's block size. Blocks consist of multiple records. + * @return the block size + */ + public int getBlockSize() { + return this.blockSize; + } + + /** + * Get the TAR Buffer's record size. + * @return the record size + */ + public int getRecordSize() { + return this.recordSize; + } + + /** + * Set the debugging flag for the buffer. + * + * @param debug If true, print debugging output. + */ + public void setDebug(boolean debug) { + this.debug = debug; + } + + /** + * Determine if an archive record indicate End of Archive. End of + * archive is indicated by a record that consists entirely of null bytes. + * + * @param record The record data to check. + * @return true if the record data is an End of Archive + */ + public boolean isEOFRecord(byte[] record) { + for (int i = 0, sz = getRecordSize(); i < sz; ++i) { + if (record[i] != 0) { + return false; + } + } + + return true; + } + + /** + * Skip over a record on the input stream. + * @throws java.io.IOException on error + */ + public void skipRecord() throws IOException { + if (debug) { + System.err.println("SkipRecord: recIdx = " + currRecIdx + + " blkIdx = " + currBlkIdx); + } + + if (inStream == null) { + throw new IOException("reading (via skip) from an output buffer"); + } + + if (currRecIdx >= recsPerBlock) { + if (!readBlock()) { + return; // UNDONE + } + } + + currRecIdx++; + } + + /** + * Read a record from the input stream and return the data. + * + * @return The record data. + * @throws IOException on error + */ + public byte[] readRecord() throws IOException { + if (debug) { + System.err.println("ReadRecord: recIdx = " + currRecIdx + + " blkIdx = " + currBlkIdx); + } + + if (inStream == null) { + throw new IOException("reading from an output buffer"); + } + + if (currRecIdx >= recsPerBlock) { + if (!readBlock()) { + return null; + } + } + + byte[] result = new byte[recordSize]; + + System.arraycopy(blockBuffer, + (currRecIdx * recordSize), result, 0, + recordSize); + + currRecIdx++; + + return result; + } + + /** + * @return false if End-Of-File, else true + */ + private boolean readBlock() throws IOException { + if (debug) { + System.err.println("ReadBlock: blkIdx = " + currBlkIdx); + } + + if (inStream == null) { + throw new IOException("reading from an output buffer"); + } + + currRecIdx = 0; + + int offset = 0; + int bytesNeeded = blockSize; + + while (bytesNeeded > 0) { + long numBytes = inStream.read(blockBuffer, offset, + bytesNeeded); + + // + // NOTE + // We have fit EOF, and the block is not full! + // + // This is a broken archive. It does not follow the standard + // blocking algorithm. However, because we are generous, and + // it requires little effort, we will simply ignore the error + // and continue as if the entire block were read. This does + // not appear to break anything upstream. We used to return + // false in this case. + // + // Thanks to 'Yohann.Roussel@alcatel.fr' for this fix. + // + if (numBytes == -1) { + if (offset == 0) { + // Ensure that we do not read gigabytes of zeros + // for a corrupt tar file. + // See http://issues.apache.org/bugzilla/show_bug.cgi?id=39924 + return false; + } + // However, just leaving the unread portion of the buffer dirty does + // cause problems in some cases. This problem is described in + // http://issues.apache.org/bugzilla/show_bug.cgi?id=29877 + // + // The solution is to fill the unused portion of the buffer with zeros. + + Arrays.fill(blockBuffer, offset, offset + bytesNeeded, (byte)0); + + break; + } + + offset += numBytes; + bytesNeeded -= numBytes; + + if (numBytes != blockSize) { + if (debug) { + System.err.println("ReadBlock: INCOMPLETE READ " + + numBytes + " of " + blockSize + + " bytes read."); + } + } + } + + currBlkIdx++; + + return true; + } + + /** + * Get the current block number, zero based. + * + * @return The current zero based block number. + */ + public int getCurrentBlockNum() { + return currBlkIdx; + } + + /** + * Get the current record number, within the current block, zero based. + * Thus, current offset = (currentBlockNum * recsPerBlk) + currentRecNum. + * + * @return The current zero based record number. + */ + public int getCurrentRecordNum() { + return currRecIdx - 1; + } + + /** + * Write an archive record to the archive. + * + * @param record The record data to write to the archive. + * @throws IOException on error + */ + public void writeRecord(byte[] record) throws IOException { + if (debug) { + System.err.println("WriteRecord: recIdx = " + currRecIdx + + " blkIdx = " + currBlkIdx); + } + + if (outStream == null) { + throw new IOException("writing to an input buffer"); + } + + if (record.length != recordSize) { + throw new IOException("record to write has length '" + + record.length + + "' which is not the record size of '" + + recordSize + "'"); + } + + if (currRecIdx >= recsPerBlock) { + writeBlock(); + } + + System.arraycopy(record, 0, blockBuffer, + (currRecIdx * recordSize), + recordSize); + + currRecIdx++; + } + + /** + * Write an archive record to the archive, where the record may be + * inside of a larger array buffer. The buffer must be "offset plus + * record size" long. + * + * @param buf The buffer containing the record data to write. + * @param offset The offset of the record data within buf. + * @throws IOException on error + */ + public void writeRecord(byte[] buf, int offset) throws IOException { + if (debug) { + System.err.println("WriteRecord: recIdx = " + currRecIdx + + " blkIdx = " + currBlkIdx); + } + + if (outStream == null) { + throw new IOException("writing to an input buffer"); + } + + if ((offset + recordSize) > buf.length) { + throw new IOException("record has length '" + buf.length + + "' with offset '" + offset + + "' which is less than the record size of '" + + recordSize + "'"); + } + + if (currRecIdx >= recsPerBlock) { + writeBlock(); + } + + System.arraycopy(buf, offset, blockBuffer, + (currRecIdx * recordSize), + recordSize); + + currRecIdx++; + } + + /** + * Write a TarBuffer block to the archive. + */ + private void writeBlock() throws IOException { + if (debug) { + System.err.println("WriteBlock: blkIdx = " + currBlkIdx); + } + + if (outStream == null) { + throw new IOException("writing to an input buffer"); + } + + outStream.write(blockBuffer, 0, blockSize); + outStream.flush(); + + currRecIdx = 0; + currBlkIdx++; + Arrays.fill(blockBuffer, (byte) 0); + } + + /** + * Flush the current data block if it has any data in it. + */ + void flushBlock() throws IOException { + if (debug) { + System.err.println("TarBuffer.flushBlock() called."); + } + + if (outStream == null) { + throw new IOException("writing to an input buffer"); + } + + if (currRecIdx > 0) { + writeBlock(); + } + } + + /** + * Close the TarBuffer. If this is an output buffer, also flush the + * current block before closing. + * @throws IOException on error + */ + public void close() throws IOException { + if (debug) { + System.err.println("TarBuffer.closeBuffer()."); + } + + if (outStream != null) { + flushBlock(); + + if (outStream != System.out + && outStream != System.err) { + outStream.close(); + + outStream = null; + } + } else if (inStream != null) { + if (inStream != System.in) { + inStream.close(); + + inStream = null; + } + } + } +} diff --git a/platform/util/src/com/intellij/util/io/tar/TarConstants.java b/platform/util/src/com/intellij/util/io/tar/TarConstants.java new file mode 100644 index 000000000000..950b4d8ad4b5 --- /dev/null +++ b/platform/util/src/com/intellij/util/io/tar/TarConstants.java @@ -0,0 +1,136 @@ +package com.intellij.util.io.tar; + +/** + * This interface contains all the definitions used in the package. + * + */ +// CheckStyle:InterfaceIsTypeCheck OFF (bc) +// Copy-pasted from org.apache.tools.tar.TarConstants +public interface TarConstants { + + /** + * The length of the name field in a header buffer. + */ + int NAMELEN = 100; + + /** + * The length of the mode field in a header buffer. + */ + int MODELEN = 8; + + /** + * The length of the user id field in a header buffer. + */ + int UIDLEN = 8; + + /** + * The length of the group id field in a header buffer. + */ + int GIDLEN = 8; + + /** + * The length of the checksum field in a header buffer. + */ + int CHKSUMLEN = 8; + + /** + * The length of the size field in a header buffer. + */ + int SIZELEN = 12; + + /** + * The maximum size of a file in a tar archive (That's 11 sevens, octal). + */ + long MAXSIZE = 077777777777L; + + /** + * The length of the magic field in a header buffer. + */ + int MAGICLEN = 8; + + /** + * The length of the modification time field in a header buffer. + */ + int MODTIMELEN = 12; + + /** + * The length of the user name field in a header buffer. + */ + int UNAMELEN = 32; + + /** + * The length of the group name field in a header buffer. + */ + int GNAMELEN = 32; + + /** + * The length of the devices field in a header buffer. + */ + int DEVLEN = 8; + + /** + * LF_ constants represent the "link flag" of an entry, or more commonly, + * the "entry type". This is the "old way" of indicating a normal file. + */ + byte LF_OLDNORM = 0; + + /** + * Normal file type. + */ + byte LF_NORMAL = (byte) '0'; + + /** + * Link file type. + */ + byte LF_LINK = (byte) '1'; + + /** + * Symbolic link file type. + */ + byte LF_SYMLINK = (byte) '2'; + + /** + * Character device file type. + */ + byte LF_CHR = (byte) '3'; + + /** + * Block device file type. + */ + byte LF_BLK = (byte) '4'; + + /** + * Directory file type. + */ + byte LF_DIR = (byte) '5'; + + /** + * FIFO (pipe) file type. + */ + byte LF_FIFO = (byte) '6'; + + /** + * Contiguous file type. + */ + byte LF_CONTIG = (byte) '7'; + + /** + * The magic tag representing a POSIX tar archive. + */ + String TMAGIC = "ustar"; + + /** + * The magic tag representing a GNU tar archive. + */ + String GNU_TMAGIC = "ustar "; + + /** + * The namr of the GNU tar entry which contains a long name. + */ + String GNU_LONGLINK = "././@LongLink"; + + /** + * Identifies the *next* file on the tape as having a long name. + */ + byte LF_GNUTYPE_LONGNAME = (byte) 'L'; +} diff --git a/platform/util/src/com/intellij/util/io/tar/TarEntry.java b/platform/util/src/com/intellij/util/io/tar/TarEntry.java new file mode 100644 index 000000000000..7b590b7574ea --- /dev/null +++ b/platform/util/src/com/intellij/util/io/tar/TarEntry.java @@ -0,0 +1,642 @@ +package com.intellij.util.io.tar; + +import java.io.File; +import java.util.Date; +import java.util.Locale; + +/** + * This class represents an entry in a Tar archive. It consists + * of the entry's header, as well as the entry's File. Entries + * can be instantiated in one of three ways, depending on how + * they are to be used. + *

+ * TarEntries that are created from the header bytes read from + * an archive are instantiated with the TarEntry( byte[] ) + * constructor. These entries will be used when extracting from + * or listing the contents of an archive. These entries have their + * header filled in using the header bytes. They also set the File + * to null, since they reference an archive entry not a file. + *

+ * TarEntries that are created from Files that are to be written + * into an archive are instantiated with the TarEntry( File ) + * constructor. These entries have their header filled in using + * the File's information. They also keep a reference to the File + * for convenience when writing entries. + *

+ * Finally, TarEntries can be constructed from nothing but a name. + * This allows the programmer to construct the entry by hand, for + * instance when only an InputStream is available for writing to + * the archive, and the header information is constructed from + * other information. In this case the header fields are set to + * defaults and the File is set to null. + * + *

+ * The C structure for a Tar Entry's header is: + *

+ * struct header {
+ * char name[NAMSIZ];
+ * char mode[8];
+ * char uid[8];
+ * char gid[8];
+ * char size[12];
+ * char mtime[12];
+ * char chksum[8];
+ * char linkflag;
+ * char linkname[NAMSIZ];
+ * char magic[8];
+ * char uname[TUNMLEN];
+ * char gname[TGNMLEN];
+ * char devmajor[8];
+ * char devminor[8];
+ * } header;
+ * 
+ * + */ +// Copy-pasted from org.apache.tools.tar.TarEntry + +public class TarEntry implements TarConstants { + /** The entry's name. */ + private StringBuffer name; + + /** The entry's permission mode. */ + private int mode; + + /** The entry's user id. */ + private int userId; + + /** The entry's group id. */ + private int groupId; + + /** The entry's size. */ + private long size; + + /** The entry's modification time. */ + private long modTime; + + /** The entry's link flag. */ + private byte linkFlag; + + /** The entry's link name. */ + private StringBuffer linkName; + + /** The entry's magic tag. */ + private StringBuffer magic; + + /** The entry's user name. */ + private StringBuffer userName; + + /** The entry's group name. */ + private StringBuffer groupName; + + /** The entry's major device number. */ + private int devMajor; + + /** The entry's minor device number. */ + private int devMinor; + + /** The entry's file reference */ + private File file; + + /** Maximum length of a user's name in the tar file */ + public static final int MAX_NAMELEN = 31; + + /** Default permissions bits for directories */ + public static final int DEFAULT_DIR_MODE = 040755; + + /** Default permissions bits for files */ + public static final int DEFAULT_FILE_MODE = 0100644; + + /** Convert millis to seconds */ + public static final int MILLIS_PER_SECOND = 1000; + + /** + * Construct an empty entry and prepares the header values. + */ + private TarEntry () { + this.magic = new StringBuffer(TMAGIC); + this.name = new StringBuffer(); + this.linkName = new StringBuffer(); + + String user = System.getProperty("user.name", ""); + + if (user.length() > MAX_NAMELEN) { + user = user.substring(0, MAX_NAMELEN); + } + + this.userId = 0; + this.groupId = 0; + this.userName = new StringBuffer(user); + this.groupName = new StringBuffer(""); + this.file = null; + } + + /** + * Construct an entry with only a name. This allows the programmer + * to construct the entry's header "by hand". File is set to null. + * + * @param name the entry name + */ + public TarEntry(String name) { + this(name, false); + } + + /** + * Construct an entry with only a name. This allows the programmer + * to construct the entry's header "by hand". File is set to null. + * + * @param name the entry name + * @param preserveLeadingSlashes whether to allow leading slashes + * in the name. + */ + public TarEntry(String name, boolean preserveLeadingSlashes) { + this(); + + name = normalizeFileName(name, preserveLeadingSlashes); + boolean isDir = name.endsWith("/"); + + this.devMajor = 0; + this.devMinor = 0; + this.name = new StringBuffer(name); + this.mode = isDir ? DEFAULT_DIR_MODE : DEFAULT_FILE_MODE; + this.linkFlag = isDir ? LF_DIR : LF_NORMAL; + this.userId = 0; + this.groupId = 0; + this.size = 0; + this.modTime = (new Date()).getTime() / MILLIS_PER_SECOND; + this.linkName = new StringBuffer(""); + this.userName = new StringBuffer(""); + this.groupName = new StringBuffer(""); + this.devMajor = 0; + this.devMinor = 0; + + } + + /** + * Construct an entry with a name and a link flag. + * + * @param name the entry name + * @param linkFlag the entry link flag. + */ + public TarEntry(String name, byte linkFlag) { + this(name); + this.linkFlag = linkFlag; + if (linkFlag == LF_GNUTYPE_LONGNAME) { + magic = new StringBuffer(GNU_TMAGIC); + } + } + + /** + * Construct an entry for a file. File is set to file, and the + * header is constructed from information from the file. + * + * @param file The file that the entry represents. + */ + public TarEntry(File file) { + this(); + + this.file = file; + + String fileName = normalizeFileName(file.getPath(), false); + this.linkName = new StringBuffer(""); + this.name = new StringBuffer(fileName); + + if (file.isDirectory()) { + this.mode = DEFAULT_DIR_MODE; + this.linkFlag = LF_DIR; + + int nameLength = name.length(); + if (nameLength == 0 || name.charAt(nameLength - 1) != '/') { + this.name.append("/"); + } + this.size = 0; + } else { + this.mode = DEFAULT_FILE_MODE; + this.linkFlag = LF_NORMAL; + this.size = file.length(); + } + + this.modTime = file.lastModified() / MILLIS_PER_SECOND; + this.devMajor = 0; + this.devMinor = 0; + } + + /** + * Construct an entry from an archive's header bytes. File is set + * to null. + * + * @param headerBuf The header bytes from a tar archive entry. + */ + public TarEntry(byte[] headerBuf) { + this(); + parseTarHeader(headerBuf); + } + + /** + * Determine if the two entries are equal. Equality is determined + * by the header names being equal. + * + * @param it Entry to be checked for equality. + * @return True if the entries are equal. + */ + public boolean equals(TarEntry it) { + return getName().equals(it.getName()); + } + + /** + * Determine if the two entries are equal. Equality is determined + * by the header names being equal. + * + * @param it Entry to be checked for equality. + * @return True if the entries are equal. + */ + public boolean equals(Object it) { + if (it == null || getClass() != it.getClass()) { + return false; + } + return equals((TarEntry) it); + } + + /** + * Hashcodes are based on entry names. + * + * @return the entry hashcode + */ + public int hashCode() { + return getName().hashCode(); + } + + /** + * Determine if the given entry is a descendant of this entry. + * Descendancy is determined by the name of the descendant + * starting with this entry's name. + * + * @param desc Entry to be checked as a descendent of this. + * @return True if entry is a descendant of this. + */ + public boolean isDescendent(TarEntry desc) { + return desc.getName().startsWith(getName()); + } + + /** + * Get this entry's name. + * + * @return This entry's name. + */ + public String getName() { + return name.toString(); + } + + /** + * Set this entry's name. + * + * @param name This entry's new name. + */ + public void setName(String name) { + this.name = new StringBuffer(normalizeFileName(name, false)); + } + + /** + * Set the mode for this entry + * + * @param mode the mode for this entry + */ + public void setMode(int mode) { + this.mode = mode; + } + + /** + * Get this entry's link name. + * + * @return This entry's link name. + */ + public String getLinkName() { + return linkName.toString(); + } + + /** + * Get this entry's user id. + * + * @return This entry's user id. + */ + public int getUserId() { + return userId; + } + + /** + * Set this entry's user id. + * + * @param userId This entry's new user id. + */ + public void setUserId(int userId) { + this.userId = userId; + } + + /** + * Get this entry's group id. + * + * @return This entry's group id. + */ + public int getGroupId() { + return groupId; + } + + /** + * Set this entry's group id. + * + * @param groupId This entry's new group id. + */ + public void setGroupId(int groupId) { + this.groupId = groupId; + } + + /** + * Get this entry's user name. + * + * @return This entry's user name. + */ + public String getUserName() { + return userName.toString(); + } + + /** + * Set this entry's user name. + * + * @param userName This entry's new user name. + */ + public void setUserName(String userName) { + this.userName = new StringBuffer(userName); + } + + /** + * Get this entry's group name. + * + * @return This entry's group name. + */ + public String getGroupName() { + return groupName.toString(); + } + + /** + * Set this entry's group name. + * + * @param groupName This entry's new group name. + */ + public void setGroupName(String groupName) { + this.groupName = new StringBuffer(groupName); + } + + /** + * Convenience method to set this entry's group and user ids. + * + * @param userId This entry's new user id. + * @param groupId This entry's new group id. + */ + public void setIds(int userId, int groupId) { + setUserId(userId); + setGroupId(groupId); + } + + /** + * Convenience method to set this entry's group and user names. + * + * @param userName This entry's new user name. + * @param groupName This entry's new group name. + */ + public void setNames(String userName, String groupName) { + setUserName(userName); + setGroupName(groupName); + } + + /** + * Set this entry's modification time. The parameter passed + * to this method is in "Java time". + * + * @param time This entry's new modification time. + */ + public void setModTime(long time) { + modTime = time / MILLIS_PER_SECOND; + } + + /** + * Set this entry's modification time. + * + * @param time This entry's new modification time. + */ + public void setModTime(Date time) { + modTime = time.getTime() / MILLIS_PER_SECOND; + } + + /** + * Set this entry's modification time. + * + * @return time This entry's new modification time. + */ + public Date getModTime() { + return new Date(modTime * MILLIS_PER_SECOND); + } + + /** + * Get this entry's file. + * + * @return This entry's file. + */ + public File getFile() { + return file; + } + + /** + * Get this entry's mode. + * + * @return This entry's mode. + */ + public int getMode() { + return mode; + } + + /** + * Get this entry's file size. + * + * @return This entry's file size. + */ + public long getSize() { + return size; + } + + /** + * Set this entry's file size. + * + * @param size This entry's new file size. + */ + public void setSize(long size) { + this.size = size; + } + + + /** + * Indicate if this entry is a GNU long name block + * + * @return true if this is a long name extension provided by GNU tar + */ + public boolean isGNULongNameEntry() { + return linkFlag == LF_GNUTYPE_LONGNAME + && name.toString().equals(GNU_LONGLINK); + } + + /** + * Return whether or not this entry represents a directory. + * + * @return True if this entry is a directory. + */ + public boolean isDirectory() { + if (file != null) { + return file.isDirectory(); + } + + if (linkFlag == LF_DIR) { + return true; + } + + if (getName().endsWith("/")) { + return true; + } + + return false; + } + + /** + * If this entry represents a file, and the file is a directory, return + * an array of TarEntries for this entry's children. + * + * @return An array of TarEntry's for this entry's children. + */ + public TarEntry[] getDirectoryEntries() { + if (file == null || !file.isDirectory()) { + return new TarEntry[0]; + } + + String[] list = file.list(); + TarEntry[] result = new TarEntry[list.length]; + + for (int i = 0; i < list.length; ++i) { + result[i] = new TarEntry(new File(file, list[i])); + } + + return result; + } + + /** + * Write an entry's header information to a header buffer. + * + * @param outbuf The tar entry header buffer to fill in. + */ + public void writeEntryHeader(byte[] outbuf) { + int offset = 0; + + offset = TarUtils.getNameBytes(name, outbuf, offset, NAMELEN); + offset = TarUtils.getOctalBytes(mode, outbuf, offset, MODELEN); + offset = TarUtils.getOctalBytes(userId, outbuf, offset, UIDLEN); + offset = TarUtils.getOctalBytes(groupId, outbuf, offset, GIDLEN); + offset = TarUtils.getLongOctalBytes(size, outbuf, offset, SIZELEN); + offset = TarUtils.getLongOctalBytes(modTime, outbuf, offset, MODTIMELEN); + + int csOffset = offset; + + for (int c = 0; c < CHKSUMLEN; ++c) { + outbuf[offset++] = (byte) ' '; + } + + outbuf[offset++] = linkFlag; + offset = TarUtils.getNameBytes(linkName, outbuf, offset, NAMELEN); + offset = TarUtils.getNameBytes(magic, outbuf, offset, MAGICLEN); + offset = TarUtils.getNameBytes(userName, outbuf, offset, UNAMELEN); + offset = TarUtils.getNameBytes(groupName, outbuf, offset, GNAMELEN); + offset = TarUtils.getOctalBytes(devMajor, outbuf, offset, DEVLEN); + offset = TarUtils.getOctalBytes(devMinor, outbuf, offset, DEVLEN); + + while (offset < outbuf.length) { + outbuf[offset++] = 0; + } + + long chk = TarUtils.computeCheckSum(outbuf); + + TarUtils.getCheckSumOctalBytes(chk, outbuf, csOffset, CHKSUMLEN); + } + + /** + * Parse an entry's header information from a header buffer. + * + * @param header The tar entry header buffer to get information from. + */ + public void parseTarHeader(byte[] header) { + int offset = 0; + + name = TarUtils.parseName(header, offset, NAMELEN); + offset += NAMELEN; + mode = (int) TarUtils.parseOctal(header, offset, MODELEN); + offset += MODELEN; + userId = (int) TarUtils.parseOctal(header, offset, UIDLEN); + offset += UIDLEN; + groupId = (int) TarUtils.parseOctal(header, offset, GIDLEN); + offset += GIDLEN; + size = TarUtils.parseOctal(header, offset, SIZELEN); + offset += SIZELEN; + modTime = TarUtils.parseOctal(header, offset, MODTIMELEN); + offset += MODTIMELEN; + offset += CHKSUMLEN; + linkFlag = header[offset++]; + linkName = TarUtils.parseName(header, offset, NAMELEN); + offset += NAMELEN; + magic = TarUtils.parseName(header, offset, MAGICLEN); + offset += MAGICLEN; + userName = TarUtils.parseName(header, offset, UNAMELEN); + offset += UNAMELEN; + groupName = TarUtils.parseName(header, offset, GNAMELEN); + offset += GNAMELEN; + devMajor = (int) TarUtils.parseOctal(header, offset, DEVLEN); + offset += DEVLEN; + devMinor = (int) TarUtils.parseOctal(header, offset, DEVLEN); + } + + /** + * Strips Windows' drive letter as well as any leading slashes, + * turns path separators into forward slahes. + */ + private static String normalizeFileName(String fileName, + boolean preserveLeadingSlashes) { + String osname = System.getProperty("os.name").toLowerCase(Locale.ENGLISH); + + if (osname != null) { + + // Strip off drive letters! + // REVIEW Would a better check be "(File.separator == '\')"? + + if (osname.startsWith("windows")) { + if (fileName.length() > 2) { + char ch1 = fileName.charAt(0); + char ch2 = fileName.charAt(1); + + if (ch2 == ':' + && ((ch1 >= 'a' && ch1 <= 'z') + || (ch1 >= 'A' && ch1 <= 'Z'))) { + fileName = fileName.substring(2); + } + } + } else if (osname.indexOf("netware") > -1) { + int colon = fileName.indexOf(':'); + if (colon != -1) { + fileName = fileName.substring(colon + 1); + } + } + } + + fileName = fileName.replace(File.separatorChar, '/'); + + // No absolute pathnames + // Windows (and Posix?) paths can start with "\\NetworkDrive\", + // so we loop on starting /'s. + while (!preserveLeadingSlashes && fileName.startsWith("/")) { + fileName = fileName.substring(1); + } + return fileName; + } +} diff --git a/platform/util/src/com/intellij/util/io/tar/TarInputStream.java b/platform/util/src/com/intellij/util/io/tar/TarInputStream.java new file mode 100644 index 000000000000..29ca41672b46 --- /dev/null +++ b/platform/util/src/com/intellij/util/io/tar/TarInputStream.java @@ -0,0 +1,380 @@ +package com.intellij.util.io.tar; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * The TarInputStream reads a UNIX tar archive as an InputStream. + * methods are provided to position at each successive entry in + * the archive, and the read each entry as a normal input stream + * using read(). + * + */ +// Copy-pasted from org.apache.tools.tar.TarInputStream +public class TarInputStream extends FilterInputStream { + private static final int SMALL_BUFFER_SIZE = 256; + private static final int BUFFER_SIZE = 8 * 1024; + private static final int LARGE_BUFFER_SIZE = 32 * 1024; + private static final int BYTE_MASK = 0xFF; + + // CheckStyle:VisibilityModifier OFF - bc + protected boolean debug; + protected boolean hasHitEOF; + protected long entrySize; + protected long entryOffset; + protected byte[] readBuf; + protected TarBuffer buffer; + protected TarEntry currEntry; + + /** + * This contents of this array is not used at all in this class, + * it is only here to avoid repreated object creation during calls + * to the no-arg read method. + */ + protected byte[] oneBuf; + + // CheckStyle:VisibilityModifier ON + + /** + * Constructor for TarInputStream. + * @param is the input stream to use + */ + public TarInputStream(InputStream is) { + this(is, TarBuffer.DEFAULT_BLKSIZE, TarBuffer.DEFAULT_RCDSIZE); + } + + /** + * Constructor for TarInputStream. + * @param is the input stream to use + * @param blockSize the block size to use + */ + public TarInputStream(InputStream is, int blockSize) { + this(is, blockSize, TarBuffer.DEFAULT_RCDSIZE); + } + + /** + * Constructor for TarInputStream. + * @param is the input stream to use + * @param blockSize the block size to use + * @param recordSize the record size to use + */ + public TarInputStream(InputStream is, int blockSize, int recordSize) { + super(is); + + this.buffer = new TarBuffer(is, blockSize, recordSize); + this.readBuf = null; + this.oneBuf = new byte[1]; + this.debug = false; + this.hasHitEOF = false; + } + + /** + * Sets the debugging flag. + * + * @param debug True to turn on debugging. + */ + public void setDebug(boolean debug) { + this.debug = debug; + buffer.setDebug(debug); + } + + /** + * Closes this stream. Calls the TarBuffer's close() method. + * @throws java.io.IOException on error + */ + public void close() throws IOException { + buffer.close(); + } + + /** + * Get the record size being used by this stream's TarBuffer. + * + * @return The TarBuffer record size. + */ + public int getRecordSize() { + return buffer.getRecordSize(); + } + + /** + * Get the available data that can be read from the current + * entry in the archive. This does not indicate how much data + * is left in the entire archive, only in the current entry. + * This value is determined from the entry's size header field + * and the amount of data already read from the current entry. + * Integer.MAX_VALUE is returen in case more than Integer.MAX_VALUE + * bytes are left in the current entry in the archive. + * + * @return The number of available bytes for the current entry. + * @throws IOException for signature + */ + public int available() throws IOException { + if (entrySize - entryOffset > Integer.MAX_VALUE) { + return Integer.MAX_VALUE; + } + return (int) (entrySize - entryOffset); + } + + /** + * Skip bytes in the input buffer. This skips bytes in the + * current entry's data, not the entire archive, and will + * stop at the end of the current entry's data if the number + * to skip extends beyond that point. + * + * @param numToSkip The number of bytes to skip. + * @return the number actually skipped + * @throws IOException on error + */ + public long skip(long numToSkip) throws IOException { + // REVIEW + // This is horribly inefficient, but it ensures that we + // properly skip over bytes via the TarBuffer... + // + byte[] skipBuf = new byte[BUFFER_SIZE]; + long skip = numToSkip; + while (skip > 0) { + int realSkip = (int) (skip > skipBuf.length ? skipBuf.length : skip); + int numRead = read(skipBuf, 0, realSkip); + if (numRead == -1) { + break; + } + skip -= numRead; + } + return (numToSkip - skip); + } + + /** + * Since we do not support marking just yet, we return false. + * + * @return False. + */ + public boolean markSupported() { + return false; + } + + /** + * Since we do not support marking just yet, we do nothing. + * + * @param markLimit The limit to mark. + */ + public void mark(int markLimit) { + } + + /** + * Since we do not support marking just yet, we do nothing. + */ + public void reset() { + } + + /** + * Get the next entry in this tar archive. This will skip + * over any remaining data in the current entry, if there + * is one, and place the input stream at the header of the + * next entry, and read the header and instantiate a new + * TarEntry from the header bytes and return that entry. + * If there are no more entries in the archive, null will + * be returned to indicate that the end of the archive has + * been reached. + * + * @return The next TarEntry in the archive, or null. + * @throws IOException on error + */ + public TarEntry getNextEntry() throws IOException { + if (hasHitEOF) { + return null; + } + + if (currEntry != null) { + long numToSkip = entrySize - entryOffset; + + if (debug) { + System.err.println("TarInputStream: SKIP currENTRY '" + + currEntry.getName() + "' SZ " + + entrySize + " OFF " + + entryOffset + " skipping " + + numToSkip + " bytes"); + } + + while (numToSkip > 0) { + long skipped = skip(numToSkip); + if (skipped <= 0) { + throw new RuntimeException("failed to skip current tar" + + " entry"); + } + numToSkip -= skipped; + } + + readBuf = null; + } + + byte[] headerBuf = buffer.readRecord(); + + if (headerBuf == null) { + if (debug) { + System.err.println("READ NULL RECORD"); + } + hasHitEOF = true; + } else if (buffer.isEOFRecord(headerBuf)) { + if (debug) { + System.err.println("READ EOF RECORD"); + } + hasHitEOF = true; + } + + if (hasHitEOF) { + currEntry = null; + } else { + currEntry = new TarEntry(headerBuf); + + if (debug) { + System.err.println("TarInputStream: SET CURRENTRY '" + + currEntry.getName() + + "' size = " + + currEntry.getSize()); + } + + entryOffset = 0; + + entrySize = currEntry.getSize(); + } + + if (currEntry != null && currEntry.isGNULongNameEntry()) { + // read in the name + StringBuffer longName = new StringBuffer(); + byte[] buf = new byte[SMALL_BUFFER_SIZE]; + int length = 0; + while ((length = read(buf)) >= 0) { + longName.append(new String(buf, 0, length)); + } + getNextEntry(); + if (currEntry == null) { + // Bugzilla: 40334 + // Malformed tar file - long entry name not followed by entry + return null; + } + // remove trailing null terminator + if (longName.length() > 0 + && longName.charAt(longName.length() - 1) == 0) { + longName.deleteCharAt(longName.length() - 1); + } + currEntry.setName(longName.toString()); + } + + return currEntry; + } + + /** + * Reads a byte from the current tar archive entry. + * + * This method simply calls read( byte[], int, int ). + * + * @return The byte read, or -1 at EOF. + * @throws IOException on error + */ + public int read() throws IOException { + int num = read(oneBuf, 0, 1); + return num == -1 ? -1 : ((int) oneBuf[0]) & BYTE_MASK; + } + + /** + * Reads bytes from the current tar archive entry. + * + * This method is aware of the boundaries of the current + * entry in the archive and will deal with them as if they + * were this stream's start and EOF. + * + * @param buf The buffer into which to place bytes read. + * @param offset The offset at which to place bytes read. + * @param numToRead The number of bytes to read. + * @return The number of bytes read, or -1 at EOF. + * @throws IOException on error + */ + public int read(byte[] buf, int offset, int numToRead) throws IOException { + int totalRead = 0; + + if (entryOffset >= entrySize) { + return -1; + } + + if ((numToRead + entryOffset) > entrySize) { + numToRead = (int) (entrySize - entryOffset); + } + + if (readBuf != null) { + int sz = (numToRead > readBuf.length) ? readBuf.length + : numToRead; + + System.arraycopy(readBuf, 0, buf, offset, sz); + + if (sz >= readBuf.length) { + readBuf = null; + } else { + int newLen = readBuf.length - sz; + byte[] newBuf = new byte[newLen]; + + System.arraycopy(readBuf, sz, newBuf, 0, newLen); + + readBuf = newBuf; + } + + totalRead += sz; + numToRead -= sz; + offset += sz; + } + + while (numToRead > 0) { + byte[] rec = buffer.readRecord(); + + if (rec == null) { + // Unexpected EOF! + throw new IOException("unexpected EOF with " + numToRead + + " bytes unread"); + } + + int sz = numToRead; + int recLen = rec.length; + + if (recLen > sz) { + System.arraycopy(rec, 0, buf, offset, sz); + + readBuf = new byte[recLen - sz]; + + System.arraycopy(rec, sz, readBuf, 0, recLen - sz); + } else { + sz = recLen; + + System.arraycopy(rec, 0, buf, offset, recLen); + } + + totalRead += sz; + numToRead -= sz; + offset += sz; + } + + entryOffset += totalRead; + + return totalRead; + } + + /** + * Copies the contents of the current tar archive entry directly into + * an output stream. + * + * @param out The OutputStream into which to write the entry's data. + * @throws IOException on error + */ + public void copyEntryContents(OutputStream out) throws IOException { + byte[] buf = new byte[LARGE_BUFFER_SIZE]; + + while (true) { + int numRead = read(buf, 0, buf.length); + + if (numRead == -1) { + break; + } + + out.write(buf, 0, numRead); + } + } +} diff --git a/platform/util/src/com/intellij/util/io/tar/TarUtils.java b/platform/util/src/com/intellij/util/io/tar/TarUtils.java new file mode 100644 index 000000000000..c763e09ddf23 --- /dev/null +++ b/platform/util/src/com/intellij/util/io/tar/TarUtils.java @@ -0,0 +1,184 @@ +package com.intellij.util.io.tar; + +/** + * This class provides static utility methods to work with byte streams. + * + */ +// CheckStyle:HideUtilityClassConstructorCheck OFF (bc) +// Copy-pasted from org.apache.tools.tar.TarUtils +public class TarUtils { + + private static final int BYTE_MASK = 255; + + /** + * Parse an octal string from a header buffer. This is used for the + * file permission mode value. + * + * @param header The header buffer from which to parse. + * @param offset The offset into the buffer from which to parse. + * @param length The number of header bytes to parse. + * @return The long value of the octal string. + */ + public static long parseOctal(byte[] header, int offset, int length) { + long result = 0; + boolean stillPadding = true; + int end = offset + length; + + for (int i = offset; i < end; ++i) { + if (header[i] == 0) { + break; + } + + if (header[i] == (byte) ' ' || header[i] == '0') { + if (stillPadding) { + continue; + } + + if (header[i] == (byte) ' ') { + break; + } + } + + stillPadding = false; + // CheckStyle:MagicNumber OFF + result = (result << 3) + (header[i] - '0'); + // CheckStyle:MagicNumber ON + } + + return result; + } + + /** + * Parse an entry name from a header buffer. + * + * @param header The header buffer from which to parse. + * @param offset The offset into the buffer from which to parse. + * @param length The number of header bytes to parse. + * @return The header's entry name. + */ + public static StringBuffer parseName(byte[] header, int offset, int length) { + StringBuffer result = new StringBuffer(length); + int end = offset + length; + + for (int i = offset; i < end; ++i) { + if (header[i] == 0) { + break; + } + + result.append((char) header[i]); + } + + return result; + } + + /** + * Determine the number of bytes in an entry name. + * + * @param name The header name from which to parse. + * @param buf The buffer from which to parse. + * @param offset The offset into the buffer from which to parse. + * @param length The number of header bytes to parse. + * @return The number of bytes in a header's entry name. + */ + public static int getNameBytes(StringBuffer name, byte[] buf, int offset, int length) { + int i; + + for (i = 0; i < length && i < name.length(); ++i) { + buf[offset + i] = (byte) name.charAt(i); + } + + for (; i < length; ++i) { + buf[offset + i] = 0; + } + + return offset + length; + } + + /** + * Parse an octal integer from a header buffer. + * + * @param value The header value + * @param buf The buffer from which to parse. + * @param offset The offset into the buffer from which to parse. + * @param length The number of header bytes to parse. + * @return The integer value of the octal bytes. + */ + public static int getOctalBytes(long value, byte[] buf, int offset, int length) { + int idx = length - 1; + + buf[offset + idx] = 0; + --idx; + buf[offset + idx] = (byte) ' '; + --idx; + + if (value == 0) { + buf[offset + idx] = (byte) '0'; + --idx; + } else { + for (long val = value; idx >= 0 && val > 0; --idx) { + // CheckStyle:MagicNumber OFF + buf[offset + idx] = (byte) ((byte) '0' + (byte) (val & 7)); + val = val >> 3; + // CheckStyle:MagicNumber ON + } + } + + for (; idx >= 0; --idx) { + buf[offset + idx] = (byte) ' '; + } + + return offset + length; + } + + /** + * Parse an octal long integer from a header buffer. + * + * @param value The header value + * @param buf The buffer from which to parse. + * @param offset The offset into the buffer from which to parse. + * @param length The number of header bytes to parse. + * @return The long value of the octal bytes. + */ + public static int getLongOctalBytes(long value, byte[] buf, int offset, int length) { + byte[] temp = new byte[length + 1]; + + getOctalBytes(value, temp, 0, length + 1); + System.arraycopy(temp, 0, buf, offset, length); + + return offset + length; + } + + /** + * Parse the checksum octal integer from a header buffer. + * + * @param value The header value + * @param buf The buffer from which to parse. + * @param offset The offset into the buffer from which to parse. + * @param length The number of header bytes to parse. + * @return The integer value of the entry's checksum. + */ + public static int getCheckSumOctalBytes(long value, byte[] buf, int offset, int length) { + getOctalBytes(value, buf, offset, length); + + buf[offset + length - 1] = (byte) ' '; + buf[offset + length - 2] = 0; + + return offset + length; + } + + /** + * Compute the checksum of a tar entry header. + * + * @param buf The tar entry's header buffer. + * @return The computed checksum. + */ + public static long computeCheckSum(byte[] buf) { + long sum = 0; + + for (int i = 0; i < buf.length; ++i) { + sum += BYTE_MASK & buf[i]; + } + + return sum; + } +} diff --git a/plugins/InspectionGadgets/src/META-INF/plugin.xml b/plugins/InspectionGadgets/src/META-INF/plugin.xml index da06bb69d070..661418d198fe 100644 --- a/plugins/InspectionGadgets/src/META-INF/plugin.xml +++ b/plugins/InspectionGadgets/src/META-INF/plugin.xml @@ -1331,6 +1331,10 @@ + #ref is not of an a suspicious.system.arraycopy.problem.descriptor6=Source parameter type ''{0}'' is not assignable to destination parameter #ref of type ''{1}'' #loc raw.use.of.parameterized.type.ignore.new.objects.option=Ignore construction of new objects raw.use.of.parameterized.type.ignore.type.casts.option=Ignore type casts +raw.use.of.parameterized.type.ignore.uncompilable.option=Ignore where a type parameter would not compile method.only.used.from.inner.class.display.name=Private method only used from inner class method.only.used.from.inner.class.problem.descriptor.anonymous.extending=Method #ref()#loc is only used from an anonymous class extending ''{0}'' #loc method.only.used.from.inner.class.problem.descriptor.anonymous.implementing=Method #ref()#loc is only used from an anonymous class implementing ''{0}'' #loc @@ -1728,9 +1729,11 @@ redundant.string.format.call.display.name=Redundant call to 'String.format()' redundant.string.format.call.problem.descriptor=Redundant call to #ref #loc redundant.string.format.call.quickfix=Remove redundant call to 'String.format()' junit4.test.method.in.class.extending.junit3.testcase.display.name=JUnit 4 test method in class extending JUnit 3 TestCase -junit4.test.method.in.class.extending.junit3.testcase.problem.descriptor=@Test annotated method #ref inside class extending JUnit 3 TestCase #loc -ignore.test.method.in.class.extending.junit3.testcase.problem.descriptor=@Ignore annotated method #ref won't be ignored #loc -ignore.test.method.in.class.extending.junit3.testcase.problem.fix=Remove @Ignore and rename method to ''{0}'' +junit4.test.method.in.class.extending.junit3.testcase.problem.descriptor=Method #ref() annotated with '@Test' inside class extending JUnit 3 TestCase #loc +ignore.test.method.in.class.extending.junit3.testcase.problem.descriptor=JUnit 3 test method #ref() annotated with '@Ignore' won't be ignored #loc +ignore.test.method.in.class.extending.junit3.testcase.quickfix=Remove ''@Ignore'' and rename method to ''{0}'' +convert.junit3.test.class.quickfix=Convert JUnit 3 class ''{0}'' to JUnit 4 +remove.junit4.test.annotation.quickfix=Remove '@Test' annotation equals.called.on.enum.constant.display.name='equals()' called on Enum value equals.called.on.enum.constant.problem.descriptor=#ref called on Enum value #loc equals.called.on.enum.constant.quickfix=Replace 'equals()' with '==' @@ -1981,3 +1984,5 @@ public.constructor.display.name='public' constructor public.default.constructor.problem.descriptor=Class #ref has public default constructor public.constructor.problem.descriptor=Public constructor #ref() public.constructor.quickfix=Replace constructor with factory method +junit3.style.test.method.in.junit4.class.display.name=Old style JUnit test method in JUnit 4 class +junit3.style.test.method.in.junit4.class.problem.descriptor=Old style JUnit test method #ref in JUnit 4 class diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/fixes/RenameFix.java b/plugins/InspectionGadgets/src/com/siyeh/ig/fixes/RenameFix.java index d1f7e0ec47a5..009dc196afbe 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/fixes/RenameFix.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/fixes/RenameFix.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2009 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2012 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,8 +18,8 @@ package com.siyeh.ig.fixes; import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.ide.DataManager; import com.intellij.openapi.actionSystem.DataContext; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.AsyncResult; import com.intellij.psi.PsiElement; import com.intellij.refactoring.RefactoringFactory; import com.intellij.refactoring.RenameRefactoring; @@ -51,14 +51,14 @@ public class RenameFix extends InspectionGadgetsFix { m_searchInNonJavaFiles = searchInNonJavaFiles; } + @Override @NotNull public String getName() { if (m_targetName == null) { return InspectionGadgetsBundle.message("rename.quickfix"); } else { - return InspectionGadgetsBundle.message("renameto.quickfix", - m_targetName); + return InspectionGadgetsBundle.message("renameto.quickfix", m_targetName); } } @@ -67,30 +67,24 @@ public class RenameFix extends InspectionGadgetsFix { } @Override - public void doFix(final Project project, ProblemDescriptor descriptor) { + public void doFix(final Project project, final ProblemDescriptor descriptor) { final PsiElement nameIdentifier = descriptor.getPsiElement(); final PsiElement elementToRename = nameIdentifier.getParent(); if (m_targetName == null) { - final DataManager dataManager = DataManager.getInstance(); - final DataContext dataContext = dataManager.getDataContext(); - final RenameHandler renameHandler = RenameHandlerRegistry.getInstance().getRenameHandler(dataContext); - if (renameHandler == null) return; - Runnable runnable = new Runnable() { - public void run() { - renameHandler.invoke(project, new PsiElement[]{elementToRename}, - dataContext); + final AsyncResult contextFromFocus = DataManager.getInstance().getDataContextFromFocus(); + contextFromFocus.doWhenDone(new AsyncResult.Handler() { + @Override + public void run(DataContext context) { + final RenameHandler renameHandler = RenameHandlerRegistry.getInstance().getRenameHandler(context); + if (renameHandler == null) { + return; + } + renameHandler.invoke(project, new PsiElement[]{elementToRename}, context); } - }; - if (ApplicationManager.getApplication().isUnitTestMode()) { - runnable.run(); - } - else { - ApplicationManager.getApplication().invokeLater(runnable, project.getDisposed()); - } + }); } else { - final RefactoringFactory factory = - RefactoringFactory.getInstance(project); + final RefactoringFactory factory = RefactoringFactory.getInstance(project); final RenameRefactoring renameRefactoring = factory.createRename(elementToRename, m_targetName, m_searchInStrings, m_searchInNonJavaFiles); renameRefactoring.run(); diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/junit/JUnit3StyleTestMethodInJUnit4ClassInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/junit/JUnit3StyleTestMethodInJUnit4ClassInspection.java new file mode 100644 index 000000000000..b151fbcda8ab --- /dev/null +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/junit/JUnit3StyleTestMethodInJUnit4ClassInspection.java @@ -0,0 +1,93 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.junit; + +import com.intellij.codeInspection.AnnotateMethodFix; +import com.intellij.psi.*; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.DelegatingFix; +import com.siyeh.ig.InspectionGadgetsFix; +import com.siyeh.ig.psiutils.ImportUtils; +import com.siyeh.ig.psiutils.TestUtils; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Bas Leijdekkers + */ +public class JUnit3StyleTestMethodInJUnit4ClassInspection extends BaseInspection { + + @Nls + @NotNull + @Override + public String getDisplayName() { + return InspectionGadgetsBundle.message("junit3.style.test.method.in.junit4.class.display.name"); + } + + @NotNull + @Override + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("junit3.style.test.method.in.junit4.class.problem.descriptor"); + } + + @Nullable + @Override + protected InspectionGadgetsFix buildFix(Object... infos) { + return new DelegatingFix(new AnnotateMethodFix("org.junit.Test")); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new JUnit3StyleTestMethodInJUnit4ClassInspectionVisitor(); + } + + private static class JUnit3StyleTestMethodInJUnit4ClassInspectionVisitor extends BaseInspectionVisitor { + + @Override + public void visitMethod(PsiMethod method) { + super.visitMethod(method); + final String name = method.getName(); + if (!name.startsWith("test")) { + return; + } + if (method.hasModifierProperty(PsiModifier.ABSTRACT) || !method.hasModifierProperty(PsiModifier.PUBLIC)) { + return; + } + if (TestUtils.isJUnit4TestMethod(method)) { + return; + } + final PsiType returnType = method.getReturnType(); + if (returnType == null || !returnType.equals(PsiType.VOID)) { + return; + } + final PsiParameterList parameterList = method.getParameterList(); + if (parameterList.getParametersCount() != 0) { + return; + } + final PsiClass containingClass = method.getContainingClass(); + if (TestUtils.isJUnitTestClass(containingClass)) { + return; + } + if (!ImportUtils.containsReferenceToClass(containingClass, "org.junit.Test")) { + return; + } + registerMethodError(method); + } + } +} diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/junit/JUnit4AnnotatedMethodInJUnit3TestCaseInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/junit/JUnit4AnnotatedMethodInJUnit3TestCaseInspection.java index 29057727aacc..3a206139aac3 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/junit/JUnit4AnnotatedMethodInJUnit3TestCaseInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/junit/JUnit4AnnotatedMethodInJUnit3TestCaseInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2009 Bas Leijdekkers + * Copyright 2008-2012 Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +19,7 @@ import com.intellij.codeInsight.AnnotationUtil; import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.openapi.project.Project; import com.intellij.psi.*; -import com.intellij.util.IncorrectOperationException; +import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; @@ -30,8 +30,10 @@ import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; -public class JUnit4AnnotatedMethodInJUnit3TestCaseInspection extends - BaseInspection { +import java.util.ArrayList; +import java.util.List; + +public class JUnit4AnnotatedMethodInJUnit3TestCaseInspection extends BaseInspection { private static final String IGNORE = "org.junit.Ignore"; @@ -39,20 +41,16 @@ public class JUnit4AnnotatedMethodInJUnit3TestCaseInspection extends @Nls @NotNull public String getDisplayName() { - return InspectionGadgetsBundle.message( - "junit4.test.method.in.class.extending.junit3.testcase.display.name"); + return InspectionGadgetsBundle.message("junit4.test.method.in.class.extending.junit3.testcase.display.name"); } @Override @NotNull protected String buildErrorString(Object... infos) { - - if (infos[0] instanceof PsiMethod && - AnnotationUtil.isAnnotated((PsiModifierListOwner)infos[0], IGNORE, false)) { + if (AnnotationUtil.isAnnotated((PsiMethod)infos[1], IGNORE, false)) { return InspectionGadgetsBundle.message("ignore.test.method.in.class.extending.junit3.testcase.problem.descriptor"); } - return InspectionGadgetsBundle.message( - "junit4.test.method.in.class.extending.junit3.testcase.problem.descriptor"); + return InspectionGadgetsBundle.message("junit4.test.method.in.class.extending.junit3.testcase.problem.descriptor"); } @Override @@ -63,39 +61,18 @@ public class JUnit4AnnotatedMethodInJUnit3TestCaseInspection extends @NotNull @Override protected InspectionGadgetsFix[] buildFixes(Object... infos) { - String className = null; - if (infos[0] instanceof PsiMethod) { - final PsiMethod method = (PsiMethod)infos[0]; - PsiClass containingClass = method.getContainingClass(); - if (containingClass == null) return InspectionGadgetsFix.EMPTY_ARRAY; - className = containingClass.getName(); - if (AnnotationUtil.isAnnotated(method, IGNORE, false)) { - if (!TestUtils.isJUnit4TestMethod(method)) { - return new InspectionGadgetsFix[]{new RemoveIgnoreAndRename(method), - new RemoveExtendsTestCaseFix(className)}; - } - else { - return new InspectionGadgetsFix[]{new RemoveIgnoreAndRename(method), - new RemoveTestAnnotationFix(), - new RemoveExtendsTestCaseFix(className)}; - } - } + final List fixes = new ArrayList(3); + final PsiMethod method = (PsiMethod)infos[1]; + if (AnnotationUtil.isAnnotated(method, IGNORE, false)) { + fixes.add(new RemoveIgnoreAndRename(method)); } - - if (className == null) { - className = (String)infos[0]; - } - if (className != null) { - return new InspectionGadgetsFix[]{ - new RemoveTestAnnotationFix(), - new RemoveExtendsTestCaseFix(className) - }; - } - else { - return new InspectionGadgetsFix[]{ - new RemoveTestAnnotationFix() - }; + if (TestUtils.isJUnit4TestMethod(method)) { + fixes.add(new RemoveTestAnnotationFix()); } + final PsiClass aClass = (PsiClass)infos[0]; + final String className = aClass.getName(); + fixes.add(new ConvertToJUnit4Fix(className)); + return fixes.toArray(new InspectionGadgetsFix[fixes.size()]); } private static void deleteAnnotation(ProblemDescriptor descriptor, final String qualifiedName) { @@ -117,6 +94,7 @@ public class JUnit4AnnotatedMethodInJUnit3TestCaseInspection extends } private static class RemoveIgnoreAndRename extends RenameFix { + public RemoveIgnoreAndRename(@NonNls PsiMethod method) { super("_" + method.getName()); } @@ -124,7 +102,7 @@ public class JUnit4AnnotatedMethodInJUnit3TestCaseInspection extends @NotNull @Override public String getName() { - return InspectionGadgetsBundle.message("ignore.test.method.in.class.extending.junit3.testcase.problem.fix", getTargetName()); + return InspectionGadgetsBundle.message("ignore.test.method.in.class.extending.junit3.testcase.quickfix", getTargetName()); } @Override @@ -134,53 +112,157 @@ public class JUnit4AnnotatedMethodInJUnit3TestCaseInspection extends } } + private static class ConvertToJUnit4Fix extends InspectionGadgetsFix { - private static class RemoveExtendsTestCaseFix extends InspectionGadgetsFix { private final String className; - RemoveExtendsTestCaseFix(String className) { + ConvertToJUnit4Fix(String className) { this.className = className; } + @Override @NotNull public String getName() { - return "remove 'extends TestCase' from class '" + className + '\''; + return InspectionGadgetsBundle.message("convert.junit3.test.class.quickfix", className); } @Override - protected void doFix(Project project, ProblemDescriptor descriptor) - throws IncorrectOperationException { + protected void doFix(Project project, ProblemDescriptor descriptor) { final PsiElement element = descriptor.getPsiElement(); final PsiElement parent = element.getParent(); if (!(parent instanceof PsiMember)) { return; } - final PsiMember method = (PsiMember)parent; - final PsiClass containingClass = method.getContainingClass(); + final PsiMember member = (PsiMember)parent; + final PsiClass containingClass = member.getContainingClass(); if (containingClass == null) { return; } - final PsiReferenceList extendsList = - containingClass.getExtendsList(); + final PsiReferenceList extendsList = containingClass.getExtendsList(); if (extendsList == null) { return; } - extendsList.delete(); + final PsiMethod[] methods = containingClass.getMethods(); + for (PsiMethod method : methods) { + @NonNls final String name = method.getName(); + if (method.hasModifierProperty(PsiModifier.STATIC)) { + continue; + } + final PsiType returnType = method.getReturnType(); + if (!PsiType.VOID.equals(returnType)) { + continue; + } + final PsiModifierList modifierList = method.getModifierList(); + if (name.startsWith("test")) { + addAnnotationIfNotPresent(modifierList, "org.junit.Test"); + } + else if (name.equals("setUp")) { + transformSetUpOrTearDownMethod(method); + addAnnotationIfNotPresent(modifierList, "org.junit.Before"); + } + else if (name.equals("tearDown")) { + transformSetUpOrTearDownMethod(method); + addAnnotationIfNotPresent(modifierList, "org.junit.After"); + } + method.accept(new MethodCallModifier()); + } + final PsiJavaCodeReferenceElement[] referenceElements = extendsList.getReferenceElements(); + for (PsiJavaCodeReferenceElement referenceElement : referenceElements) { + referenceElement.delete(); + } + } + + private static void addAnnotationIfNotPresent(PsiModifierList modifierList, String qualifiedAnnotationName) { + if (modifierList.findAnnotation(qualifiedAnnotationName) != null) { + return; + } + final PsiAnnotation annotation = modifierList.addAnnotation(qualifiedAnnotationName); + final Project project = modifierList.getProject(); + final JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(project); + codeStyleManager.shortenClassReferences(annotation); + } + + private static void transformSetUpOrTearDownMethod(PsiMethod method) { + final PsiModifierList modifierList = method.getModifierList(); + if (modifierList.hasModifierProperty(PsiModifier.PROTECTED)) { + modifierList.setModifierProperty(PsiModifier.PROTECTED, false); + } + if (!modifierList.hasModifierProperty(PsiModifier.PUBLIC)) { + modifierList.setModifierProperty(PsiModifier.PUBLIC, true); + } + final PsiAnnotation overrideAnnotation = modifierList.findAnnotation("java.lang.Override"); + if (overrideAnnotation != null) { + overrideAnnotation.delete(); + } + method.accept(new SuperLifeCycleCallRemover(method.getName())); + } + + private static class SuperLifeCycleCallRemover extends JavaRecursiveElementVisitor { + + @NotNull private final String myLifeCycleMethodName; + + private SuperLifeCycleCallRemover(@NotNull String lifeCycleMethodName) { + myLifeCycleMethodName = lifeCycleMethodName; + } + + @Override + public void visitMethodCallExpression(PsiMethodCallExpression expression) { + super.visitMethodCallExpression(expression); + final PsiReferenceExpression methodExpression = expression.getMethodExpression(); + final String methodName = methodExpression.getReferenceName(); + if (!myLifeCycleMethodName.equals(methodName)) { + return; + } + final PsiExpression target = methodExpression.getQualifierExpression(); + if (!(target instanceof PsiSuperExpression)) { + return; + } + expression.delete(); + } + } + + private static class MethodCallModifier extends JavaRecursiveElementVisitor { + + @Override + public void visitMethodCallExpression(PsiMethodCallExpression expression) { + super.visitMethodCallExpression(expression); + final PsiReferenceExpression methodExpression = expression.getMethodExpression(); + if (methodExpression.getQualifierExpression() != null) { + return; + } + final PsiMethod method = expression.resolveMethod(); + if (method == null) { + return; + } + final PsiClass aClass = method.getContainingClass(); + if (aClass == null) { + return; + } + final String name = aClass.getQualifiedName(); + if (!"junit.framework.Assert".equals(name)) { + return; + } + @NonNls final String newExpressionText = "org.junit.Assert." + expression.getText(); + final Project project = expression.getProject(); + final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); + final PsiExpression newExpression = factory.createExpressionFromText(newExpressionText, expression); + final JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(project); + final PsiElement replacedExpression = expression.replace(newExpression); + codeStyleManager.shortenClassReferences(replacedExpression); + } } } + private static class RemoveTestAnnotationFix extends InspectionGadgetsFix { - private static class RemoveTestAnnotationFix - extends InspectionGadgetsFix { - + @Override @NotNull public String getName() { - return "Remove @Test annotation"; + return InspectionGadgetsBundle.message("remove.junit4.test.annotation.quickfix"); } @Override - protected void doFix(Project project, ProblemDescriptor descriptor) - throws IncorrectOperationException { + protected void doFix(Project project, ProblemDescriptor descriptor) { deleteAnnotation(descriptor, "org.junit.Test"); } } @@ -190,8 +272,7 @@ public class JUnit4AnnotatedMethodInJUnit3TestCaseInspection extends return new Junit4AnnotatedMethodInJunit3TestCaseVisitor(); } - private static class Junit4AnnotatedMethodInJunit3TestCaseVisitor - extends BaseInspectionVisitor { + private static class Junit4AnnotatedMethodInJunit3TestCaseVisitor extends BaseInspectionVisitor { @Override public void visitMethod(PsiMethod method) { @@ -204,14 +285,10 @@ public class JUnit4AnnotatedMethodInJUnit3TestCaseInspection extends return; } if (AnnotationUtil.isAnnotated(method, IGNORE, false) && method.getName().startsWith("test")) { - registerMethodError(method, method); - return; + registerMethodError(method, containingClass, method); + } else if (TestUtils.isJUnit4TestMethod(method)) { + registerMethodError(method, containingClass, method); } - if (!TestUtils.isJUnit4TestMethod(method)) { - return; - } - final String className = containingClass.getName(); - registerMethodError(method, className); } } } diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/ImportUtils.java b/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/ImportUtils.java index 576e71a1aeb1..fc3d59e8bebe 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/ImportUtils.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/ImportUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2011 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2012 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,8 +32,7 @@ import java.util.List; public class ImportUtils { - private ImportUtils() { - } + private ImportUtils() {} public static void addImportIfNeeded(@NotNull PsiClass aClass, @NotNull PsiElement context) { @@ -302,10 +301,10 @@ public class ImportUtils { return true; } final String qualifiedClassName = aClass.getQualifiedName(); - final ClassReferenceVisitor visitor = - new ClassReferenceVisitor(qualifiedClassName); - file.accept(visitor); - return visitor.isReferenceFound(); + if (fqName.equals(qualifiedClassName)) { + continue; + } + return containsReferenceToClass(file, qualifiedClassName); } } return hasJavaLangImportConflict(fqName, file); @@ -411,19 +410,15 @@ public class ImportUtils { return false; } - private static boolean containsConflictingClass(String fqName, - PsiJavaFile file) { + private static boolean containsConflictingClass(String fqName, PsiJavaFile file) { final PsiClass[] classes = file.getClasses(); for (PsiClass aClass : classes) { if (containsConflictingInnerClass(fqName, aClass)) { return true; } } - //return false; - final ClassReferenceVisitor visitor = - new ClassReferenceVisitor(fqName); - file.accept(visitor); - return visitor.isReferenceFound(); + return false; + //return containsReferenceToClass(file, fqName); } /** @@ -646,8 +641,13 @@ public class ImportUtils { } } - private static class ClassReferenceVisitor - extends JavaRecursiveElementVisitor { + public static boolean containsReferenceToClass(PsiElement element, String fullyQualifiedName) { + final ClassReferenceVisitor visitor = new ClassReferenceVisitor(fullyQualifiedName); + element.accept(visitor); + return visitor.isReferenceFound(); + } + + private static class ClassReferenceVisitor extends JavaRecursiveElementVisitor { private final String name; private final String fullyQualifiedName; @@ -659,8 +659,7 @@ public class ImportUtils { } @Override - public void visitReferenceElement( - PsiJavaCodeReferenceElement reference) { + public void visitReferenceElement(PsiJavaCodeReferenceElement reference) { super.visitReferenceElement(reference); if (referenceFound) { return; @@ -670,16 +669,14 @@ public class ImportUtils { return; } final PsiElement element = reference.resolve(); - if (!(element instanceof PsiClass) - || element instanceof PsiTypeParameter) { + if (!(element instanceof PsiClass) || element instanceof PsiTypeParameter) { return; } final PsiClass aClass = (PsiClass)element; final String testClassName = aClass.getName(); final String testClassQualifiedName = aClass.getQualifiedName(); if (testClassQualifiedName == null || testClassName == null - || testClassQualifiedName.equals(fullyQualifiedName) || - !testClassName.equals(name)) { + || !testClassQualifiedName.equals(fullyQualifiedName) || !testClassName.equals(name)) { return; } referenceFound = true; diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/style/UnnecessaryParenthesesInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/style/UnnecessaryParenthesesInspection.java index a7db797d908a..8a6577083a46 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/style/UnnecessaryParenthesesInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/style/UnnecessaryParenthesesInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2008 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2012 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,34 +36,29 @@ public class UnnecessaryParenthesesInspection extends BaseInspection { @SuppressWarnings({"PublicField"}) public boolean ignoreParenthesesOnConditionals = false; + + @SuppressWarnings("PublicField") public boolean ignoreParenthesesOnLambdaParameter = false; @Override @NotNull public String getDisplayName() { - return InspectionGadgetsBundle.message( - "unnecessary.parentheses.display.name"); + return InspectionGadgetsBundle.message("unnecessary.parentheses.display.name"); } @Override @NotNull protected String buildErrorString(Object... infos) { - return InspectionGadgetsBundle.message( - "unnecessary.parentheses.problem.descriptor"); + return InspectionGadgetsBundle.message("unnecessary.parentheses.problem.descriptor"); } @Override public JComponent createOptionsPanel() { - final MultipleCheckboxOptionsPanel optionsPanel = - new MultipleCheckboxOptionsPanel(this); - optionsPanel.addCheckbox(InspectionGadgetsBundle.message( - "unnecessary.parentheses.option"), - "ignoreClarifyingParentheses"); - optionsPanel.addCheckbox(InspectionGadgetsBundle.message( - "unnecessary.parentheses.conditional.option"), + final MultipleCheckboxOptionsPanel optionsPanel = new MultipleCheckboxOptionsPanel(this); + optionsPanel.addCheckbox(InspectionGadgetsBundle.message("unnecessary.parentheses.option"), "ignoreClarifyingParentheses"); + optionsPanel.addCheckbox(InspectionGadgetsBundle.message("unnecessary.parentheses.conditional.option"), "ignoreParenthesesOnConditionals"); - optionsPanel.addCheckbox("Ignore parentheses around single no formal type lambda parameter", - "ignoreParenthesesOnLambdaParameter"); + optionsPanel.addCheckbox("Ignore parentheses around single no formal type lambda parameter", "ignoreParenthesesOnLambdaParameter"); return optionsPanel; } @@ -112,26 +107,28 @@ public class UnnecessaryParenthesesInspection extends BaseInspection { } @Override - public void visitParenthesizedExpression( - PsiParenthesizedExpression expression) { + public void visitParenthesizedExpression(PsiParenthesizedExpression expression) { final PsiElement parent = expression.getParent(); final PsiExpression child = expression.getExpression(); if (child == null) { return; } - if (!(parent instanceof PsiExpression) || - parent instanceof PsiParenthesizedExpression) { + if (!(parent instanceof PsiExpression) || parent instanceof PsiParenthesizedExpression) { registerError(expression); return; } - final int parentPrecedence = - ParenthesesUtils.getPrecedence((PsiExpression)parent); + final int parentPrecedence = ParenthesesUtils.getPrecedence((PsiExpression)parent); final int childPrecedence = ParenthesesUtils.getPrecedence(child); if (parentPrecedence > childPrecedence) { if (ignoreClarifyingParentheses) { - if (parent instanceof PsiPolyadicExpression && - child instanceof PsiPolyadicExpression) { - return; + if (child instanceof PsiPolyadicExpression) { + if (parent instanceof PsiPolyadicExpression) { + return; + } else if (parent instanceof PsiConditionalExpression) { + return; + } else if (parent instanceof PsiInstanceOfExpression) { + return; + } } else if (child instanceof PsiInstanceOfExpression) { return; @@ -139,10 +136,8 @@ public class UnnecessaryParenthesesInspection extends BaseInspection { } if (ignoreParenthesesOnConditionals) { if (parent instanceof PsiConditionalExpression) { - final PsiConditionalExpression conditionalExpression = - (PsiConditionalExpression)parent; - final PsiExpression condition = - conditionalExpression.getCondition(); + final PsiConditionalExpression conditionalExpression = (PsiConditionalExpression)parent; + final PsiExpression condition = conditionalExpression.getCondition(); if (expression == condition) { return; } @@ -152,8 +147,7 @@ public class UnnecessaryParenthesesInspection extends BaseInspection { return; } if (parentPrecedence == childPrecedence) { - if (!ParenthesesUtils.areParenthesesNeeded(expression, - ignoreClarifyingParentheses)) { + if (!ParenthesesUtils.areParenthesesNeeded(expression, ignoreClarifyingParentheses)) { registerError(expression); return; } diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/JUnit3StyleTestMethodInJUnit4Class.html b/plugins/InspectionGadgets/src/inspectionDescriptions/JUnit3StyleTestMethodInJUnit4Class.html new file mode 100644 index 000000000000..6ccb84e18ee1 --- /dev/null +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/JUnit3StyleTestMethodInJUnit4Class.html @@ -0,0 +1,10 @@ + + +This inspection reports JUnit 3 style test methods which are located inside a class +which does not extend the abstract JUnit 3 class TestCase and contains JUnit 4 @Test annotated methods. +In addition to being confusing such test methods will not be run. + +

+New in 12, Powered by InspectionGadgets + + \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/JUnit4AnnotatedMethodInJUnit3TestCase.html b/plugins/InspectionGadgets/src/inspectionDescriptions/JUnit4AnnotatedMethodInJUnit3TestCase.html index 04475ada1283..736370eb6bfa 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/JUnit4AnnotatedMethodInJUnit3TestCase.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/JUnit4AnnotatedMethodInJUnit3TestCase.html @@ -1,8 +1,9 @@ -This inspection reports JUnit 4 @Test annotated methods which are located inside a class -extending the abstract JUnit 3 class TestCase. Mixing JUnit API's like this is -confusing and can lead to problems running the tests, e.g. method annotated with @Ignore won't be actually ignored if its name starts with test +This inspection reports JUnit 4 @Test annotated methods which are located inside a class +extending the abstract JUnit 3 class TestCase. Mixing JUnit API's like this is +confusing and can lead to problems running the tests, for example a method annotated with @Ignore won't +be actually ignored if its name starts with test.

Powered by InspectionGadgets diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/style/clarifying_parentheses/UnnecessaryParentheses.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/style/clarifying_parentheses/UnnecessaryParentheses.java new file mode 100644 index 000000000000..2f7a798fe0bc --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/style/clarifying_parentheses/UnnecessaryParentheses.java @@ -0,0 +1,14 @@ +package com.siyeh.igtest.style.clarifying_parentheses; + +class UnnecessaryParentheses { + + String oldValue; + boolean update(UnnecessaryParentheses that) { + final boolean c = ("" + "asdf") instanceof String; + boolean b = true && (that instanceof Object); + if ((oldValue != null) ? !oldValue.equals(that.oldValue) : (that.oldValue != null)) { + return false; + } + return true; + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/style/clarifying_parentheses/expected.xml b/plugins/InspectionGadgets/test/com/siyeh/igtest/style/clarifying_parentheses/expected.xml new file mode 100644 index 000000000000..e8f00e82629f --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/style/clarifying_parentheses/expected.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/UnnecessaryParenthesesInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/UnnecessaryParenthesesInspectionTest.java index 4fcc38e7e850..97ce053c04da 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/UnnecessaryParenthesesInspectionTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/UnnecessaryParenthesesInspectionTest.java @@ -8,8 +8,7 @@ import com.siyeh.ig.IGInspectionTestCase; public class UnnecessaryParenthesesInspectionTest extends IGInspectionTestCase { public void test() throws Exception { - final UnnecessaryParenthesesInspection inspection = - new UnnecessaryParenthesesInspection(); + final UnnecessaryParenthesesInspection inspection = new UnnecessaryParenthesesInspection(); inspection.ignoreParenthesesOnConditionals = true; final LanguageLevelProjectExtension levelProjectExtension = LanguageLevelProjectExtension.getInstance(getProject()); final LanguageLevel level = levelProjectExtension.getLanguageLevel(); @@ -22,4 +21,10 @@ public class UnnecessaryParenthesesInspectionTest extends IGInspectionTestCase { levelProjectExtension.setLanguageLevel(level); } } + + public void testClarifyingParentheses() { + final UnnecessaryParenthesesInspection inspection = new UnnecessaryParenthesesInspection(); + inspection.ignoreClarifyingParentheses = true; + doTest("com/siyeh/igtest/style/clarifying_parentheses", inspection); + } } \ No newline at end of file diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/junit/ConvertJUnit3TestCaseToJUnit4Intention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/junit/ConvertJUnit3TestCaseToJUnit4Intention.java index bc5cef0480aa..4e0c6167d1ca 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/junit/ConvertJUnit3TestCaseToJUnit4Intention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/junit/ConvertJUnit3TestCaseToJUnit4Intention.java @@ -1,5 +1,5 @@ /* - * Copyright 2009 Bas Leijdekkers + * Copyright 2009-2012 Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.util.IncorrectOperationException; import com.siyeh.ipp.base.Intention; import com.siyeh.ipp.base.PsiElementPredicate; +import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; public class ConvertJUnit3TestCaseToJUnit4Intention extends Intention { @@ -32,8 +33,7 @@ public class ConvertJUnit3TestCaseToJUnit4Intention extends Intention { } @Override - protected void processIntention(@NotNull PsiElement element) - throws IncorrectOperationException { + protected void processIntention(@NotNull PsiElement element) throws IncorrectOperationException { final PsiElement parent = element.getParent(); if (!(parent instanceof PsiClass)) { return; @@ -45,7 +45,7 @@ public class ConvertJUnit3TestCaseToJUnit4Intention extends Intention { } final PsiMethod[] methods = aClass.getMethods(); for (PsiMethod method : methods) { - final String name = method.getName(); + @NonNls final String name = method.getName(); if (method.hasModifierProperty(PsiModifier.STATIC)) { continue; } @@ -67,23 +67,19 @@ public class ConvertJUnit3TestCaseToJUnit4Intention extends Intention { } method.accept(new MethodCallModifier()); } - final PsiJavaCodeReferenceElement[] referenceElements = - extendsList.getReferenceElements(); + final PsiJavaCodeReferenceElement[] referenceElements = extendsList.getReferenceElements(); for (PsiJavaCodeReferenceElement referenceElement : referenceElements) { referenceElement.delete(); } } - private static void addAnnotationIfNotPresent( - PsiModifierList modifierList, String qualifiedAnnotationName) { + private static void addAnnotationIfNotPresent(PsiModifierList modifierList, String qualifiedAnnotationName) { if (modifierList.findAnnotation(qualifiedAnnotationName) != null) { return; } - final PsiAnnotation annotation = - modifierList.addAnnotation(qualifiedAnnotationName); + final PsiAnnotation annotation = modifierList.addAnnotation(qualifiedAnnotationName); final Project project = modifierList.getProject(); - final JavaCodeStyleManager codeStyleManager = - JavaCodeStyleManager.getInstance(project); + final JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(project); codeStyleManager.shortenClassReferences(annotation); } @@ -95,17 +91,14 @@ public class ConvertJUnit3TestCaseToJUnit4Intention extends Intention { if (!modifierList.hasModifierProperty(PsiModifier.PUBLIC)) { modifierList.setModifierProperty(PsiModifier.PUBLIC, true); } - final PsiAnnotation overrideAnnotation = - modifierList.findAnnotation("java.lang.Override"); + final PsiAnnotation overrideAnnotation = modifierList.findAnnotation("java.lang.Override"); if (overrideAnnotation != null) { overrideAnnotation.delete(); } - method.accept(new SuperLifeCycleCallRemover(method.getName())); } - private static class SuperLifeCycleCallRemover - extends JavaRecursiveElementVisitor { + private static class SuperLifeCycleCallRemover extends JavaRecursiveElementVisitor { @NotNull private final String myLifeCycleMethodName; @@ -114,17 +107,14 @@ public class ConvertJUnit3TestCaseToJUnit4Intention extends Intention { } @Override - public void visitMethodCallExpression( - PsiMethodCallExpression expression) { + public void visitMethodCallExpression(PsiMethodCallExpression expression) { super.visitMethodCallExpression(expression); - final PsiReferenceExpression methodExpression = - expression.getMethodExpression(); + final PsiReferenceExpression methodExpression = expression.getMethodExpression(); final String methodName = methodExpression.getReferenceName(); if (!myLifeCycleMethodName.equals(methodName)) { return; } - final PsiExpression target = - methodExpression.getQualifierExpression(); + final PsiExpression target = methodExpression.getQualifierExpression(); if (!(target instanceof PsiSuperExpression)) { return; } @@ -132,15 +122,12 @@ public class ConvertJUnit3TestCaseToJUnit4Intention extends Intention { } } - private static class MethodCallModifier - extends JavaRecursiveElementVisitor { + private static class MethodCallModifier extends JavaRecursiveElementVisitor { @Override - public void visitMethodCallExpression( - PsiMethodCallExpression expression) { + public void visitMethodCallExpression(PsiMethodCallExpression expression) { super.visitMethodCallExpression(expression); - final PsiReferenceExpression methodExpression = - expression.getMethodExpression(); + final PsiReferenceExpression methodExpression = expression.getMethodExpression(); if (methodExpression.getQualifierExpression() != null) { return; } @@ -156,18 +143,12 @@ public class ConvertJUnit3TestCaseToJUnit4Intention extends Intention { if (!"junit.framework.Assert".equals(name)) { return; } - final String newExpressionText = - "org.junit.Assert." + expression.getText(); + @NonNls final String newExpressionText = "org.junit.Assert." + expression.getText(); final Project project = expression.getProject(); - final PsiElementFactory factory = - JavaPsiFacade.getElementFactory(project); - final PsiExpression newExpression = - factory.createExpressionFromText(newExpressionText, - expression); - final JavaCodeStyleManager codeStyleManager = - JavaCodeStyleManager.getInstance(project); - final PsiElement replacedExpression = - expression.replace(newExpression); + final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); + final PsiExpression newExpression = factory.createExpressionFromText(newExpressionText, expression); + final JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(project); + final PsiElement replacedExpression = expression.replace(newExpression); codeStyleManager.shortenClassReferences(replacedExpression); } } diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/junit/ConvertJUnit3TestCaseToJUnit4Predicate.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/junit/ConvertJUnit3TestCaseToJUnit4Predicate.java index 09e1a6482c1b..349e9171498a 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/junit/ConvertJUnit3TestCaseToJUnit4Predicate.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/junit/ConvertJUnit3TestCaseToJUnit4Predicate.java @@ -1,5 +1,5 @@ /* - * Copyright 2009 Bas Leijdekkers + * Copyright 2009-2012 Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,9 +20,9 @@ import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; import com.siyeh.ipp.base.PsiElementPredicate; -public class ConvertJUnit3TestCaseToJUnit4Predicate - implements PsiElementPredicate { +class ConvertJUnit3TestCaseToJUnit4Predicate implements PsiElementPredicate { + @Override public boolean satisfiedBy(PsiElement element) { final PsiElement parent = element.getParent(); if (!(parent instanceof PsiClass)) { @@ -31,21 +31,18 @@ public class ConvertJUnit3TestCaseToJUnit4Predicate final PsiClass aClass = (PsiClass)parent; final PsiElement leftBrace = aClass.getLBrace(); final int offsetInParent = element.getStartOffsetInParent(); - if (leftBrace == null || - offsetInParent >= leftBrace.getStartOffsetInParent()) { + if (leftBrace == null || offsetInParent >= leftBrace.getStartOffsetInParent()) { return false; } final PsiReferenceList extendsList = aClass.getExtendsList(); if (extendsList == null) { return false; } - final PsiJavaCodeReferenceElement[] referenceElements = - extendsList.getReferenceElements(); + final PsiJavaCodeReferenceElement[] referenceElements = extendsList.getReferenceElements(); if (referenceElements.length != 1) { return false; } - final PsiJavaCodeReferenceElement referenceElement = - referenceElements[0]; + final PsiJavaCodeReferenceElement referenceElement = referenceElements[0]; final PsiElement target = referenceElement.resolve(); if (!(target instanceof PsiClass)) { return false; @@ -58,8 +55,7 @@ public class ConvertJUnit3TestCaseToJUnit4Predicate final Project project = element.getProject(); final GlobalSearchScope scope = element.getResolveScope(); final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project); - final PsiClass testAnnotation = - psiFacade.findClass("org.junit.Test", scope); + final PsiClass testAnnotation = psiFacade.findClass("org.junit.Test", scope); return testAnnotation != null; } } diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/inspections/BaseSplitter.java b/plugins/spellchecker/src/com/intellij/spellchecker/inspections/BaseSplitter.java index 72d29cac9454..512fe5e493c0 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/inspections/BaseSplitter.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/inspections/BaseSplitter.java @@ -15,8 +15,11 @@ */ package com.intellij.spellchecker.inspections; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicatorProvider; import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.Consumer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -24,6 +27,7 @@ import org.jetbrains.annotations.Nullable; import java.text.CharacterIterator; import java.text.StringCharacterIterator; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -31,7 +35,10 @@ import java.util.regex.Pattern; public abstract class BaseSplitter implements Splitter { + static final Logger LOG = Logger.getInstance("#com.intellij.spellchecker.inspections.BaseSplitter"); + public static final int MIN_RANGE_LENGTH = 3; + public static final int MAX_RANGE_LENGTH = 50; protected static void addWord(@NotNull Consumer consumer, boolean ignore, @Nullable TextRange found) { @@ -83,8 +90,9 @@ public abstract class BaseSplitter implements Splitter { return TextRange.from(range.getStartOffset() + start, end - start); } - protected static boolean tooSmall(int from, int till) { - return till - from <= MIN_RANGE_LENGTH; + protected static boolean badSize(int from, int till) { + int l = till - from; + return l <= MIN_RANGE_LENGTH || l>= MAX_RANGE_LENGTH; } @NotNull @@ -93,35 +101,40 @@ public abstract class BaseSplitter implements Splitter { int from = range.getStartOffset(); int till; boolean addLast = true; - Matcher matcher = toExclude.matcher(range.substring(text)); - while (matcher.find()) { - - checkCancelled(); - - TextRange found = matcherRange(range, matcher); - till = found.getStartOffset() - 1; - if (range.getEndOffset() - found.getEndOffset() < MIN_RANGE_LENGTH) { - addLast = false; + Matcher matcher = toExclude.matcher(new StringUtil.BombedCharSequence(range.substring(text), 500)); + try { + while (matcher.find()) { + checkCancelled(); + TextRange found = matcherRange(range, matcher); + till = found.getStartOffset(); + if (range.getEndOffset() - found.getEndOffset() < MIN_RANGE_LENGTH) { + addLast = false; + } + if (!badSize(from, till)) { + toCheck.add(new TextRange(from, till)); + } + if (groupToInclude > 0) { + TextRange contentFound = matcherRange(range, matcher, groupToInclude); + if (badSize(contentFound.getEndOffset(), contentFound.getStartOffset())) { + toCheck.add(TextRange.create(contentFound)); + } + } + from = found.getEndOffset(); } - if (!tooSmall(from, till)) { + till = range.getEndOffset(); + if (badSize(from, till)) { + return toCheck; + } + if (addLast) { toCheck.add(new TextRange(from, till)); } - if (groupToInclude > 0) { - TextRange contentFound = matcherRange(range, matcher, groupToInclude); - if (tooSmall(contentFound.getEndOffset(), contentFound.getStartOffset())) { - toCheck.add(TextRange.create(contentFound)); - } - } - from = found.getEndOffset(); - } - till = range.getEndOffset(); - if (tooSmall(from, till)) { return toCheck; } - if (addLast) { - toCheck.add(new TextRange(from, till)); + catch (ProcessCanceledException e) { + //LOG.warn("Matching took too long: >>>" + range.substring(text) + "<<< " + toExclude); + return Collections.singletonList(range); + //return Collections.emptyList(); } - return toCheck; } public static void checkCancelled() { diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/inspections/PlainTextSplitter.java b/plugins/spellchecker/src/com/intellij/spellchecker/inspections/PlainTextSplitter.java index 0ec175a083bd..4d8113361b6c 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/inspections/PlainTextSplitter.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/inspections/PlainTextSplitter.java @@ -25,6 +25,7 @@ import org.jetbrains.annotations.Nullable; import java.util.Collections; import java.util.List; +import java.util.regex.Matcher; import java.util.regex.Pattern; public class PlainTextSplitter extends BaseSplitter { @@ -34,6 +35,10 @@ public class PlainTextSplitter extends BaseSplitter { return INSTANCE; } + @NonNls + private static final + Pattern SPLIT_PATTERN = Pattern.compile("(\\s)"); + @NonNls private static final Pattern MAIL = Pattern.compile("([\\p{L}0-9\\.\\-\\_]+@([\\p{L}0-9\\-\\_]+\\.)+(com|net|[a-z]{2}))"); @@ -64,25 +69,42 @@ public class PlainTextSplitter extends BaseSplitter { // } //} - List toCheck; - if (text.indexOf('@')>0) { - toCheck = excludeByPattern(text, range, MAIL, 0); - } - else - if (text.indexOf(':')>0) { - toCheck = excludeByPattern(text, range, URL, 0); - } - else - { - toCheck = Collections.singletonList(range); - } - final TextSplitter ws = TextSplitter.getInstance(); - for (TextRange r : toCheck) { - + int from = range.getStartOffset(); + int till; + Matcher matcher = SPLIT_PATTERN.matcher(range.substring(text)); + while (true) { checkCancelled(); - - ws.split(text, r, consumer); + List toCheck; + TextRange wRange; + String word; + if(matcher.find()) { + TextRange found = matcherRange(range, matcher); + till = found.getStartOffset(); + if (badSize(from, till)) { + continue; + } + wRange = new TextRange(from, till); + word = wRange.substring(text); + from = found.getEndOffset(); + } else { // end hit or zero matches + wRange = new TextRange(from, range.getEndOffset()); + word = wRange.substring(text); + } + if (word.contains("@")) { + toCheck = excludeByPattern(text, wRange, MAIL, 0); + } + else + if (text.contains("://")) { + toCheck = excludeByPattern(text, wRange, URL, 0); + } + else { + toCheck = Collections.singletonList(wRange); + } + for (TextRange r : toCheck) { + ws.split(text, r, consumer); + } + if(matcher.hitEnd()) break; } } }