Merge remote-tracking branch 'origin/master'

This commit is contained in:
Dmitry Trofimov
2012-08-29 22:51:19 +02:00
64 changed files with 2806 additions and 599 deletions
@@ -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
@@ -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<String> paths = fetchFiles(compileContext);
final List<Module> modules = paths.isEmpty() && !isRebuild && !allProjectModulesAffected(compileContext)? Arrays.asList(compileContext.getCompileScope().getAffectedModules()) : Collections.<Module>emptyList();
final Set<Artifact> 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<File> outputs = new HashSet<File>();
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<File> outputs = new HashSet<File>();
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) {
@@ -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 {
@@ -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<Object> 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) {
@@ -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());
@@ -88,6 +88,10 @@ class JavaClassNameInsertHandler implements InsertHandler<JavaPsiClassReferenceE
}
PsiTypeLookupItem.addImportForItem(context, psiClass);
if (context.getOffset(refEnd) < 0) {
return;
}
context.setTailOffset(context.getOffset(refEnd));
if (shouldInsertParentheses(file.findElementAt(context.getTailOffset() - 1))) {
@@ -356,6 +356,7 @@ public class MethodParameterInfoHandler implements ParameterInfoHandlerWithTabAc
if (param.isValid()) {
PsiType paramType = param.getType();
assert paramType.isValid();
if (substitutor != null) {
paramType = substitutor.substitute(paramType);
}
@@ -180,12 +180,9 @@ public class JavaClassReference extends GenericReference implements PsiJavaRefer
@Override
public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
final ElementManipulator<PsiElement> 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<PsiElement> 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
@@ -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);
@@ -233,25 +233,13 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations
final List<PsiFile> 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<PsiFile> onlyValid = new ArrayList<PsiFile>();
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) {
@@ -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) {
@@ -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;
}
@@ -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();
}
}
@@ -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();
/**
@@ -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++) {
@@ -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();
}
@@ -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);
@@ -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<CompletionContributor> 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();
@@ -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<String> 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<LookupElement> 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<LookupElement> items = getMatchingItems();
MultiMap<CompletionSorterImpl, LookupElement> itemsBySorter = groupItemsBySorter(items);
LookupElement relevantSelection = findMostRelevantItem(itemsBySorter);
List<LookupElement> 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<LookupElement> fillModelByRelevance(LookupImpl lookup,
List<LookupElement> items,
MultiMap<CompletionSorterImpl, LookupElement> inputBySorter) {
MultiMap<CompletionSorterImpl, LookupElement> inputBySorter,
@Nullable LookupElement relevantSelection) {
Iterator<LookupElement> byRelevance = sortByRelevance(inputBySorter).iterator();
final LinkedHashSet<LookupElement> model = new LinkedHashSet<LookupElement>();
@@ -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<LookupElement>(model);
@@ -230,16 +239,14 @@ public class CompletionLookupArranger extends LookupArranger {
});
}
private static void ensureCurrentSelectionAdded(LookupImpl lookup,
List<LookupElement> items,
LinkedHashSet<LookupElement> model,
Iterator<LookupElement> byRelevance) {
final LookupElement currentItem = lookup.getCurrentItem();
if (ContainerUtil.indexOfIdentity(items, currentItem) >= 0 && !model.contains(currentItem)) {
private static void ensureItemAdded(List<LookupElement> items,
LinkedHashSet<LookupElement> model,
Iterator<LookupElement> byRelevance, @Nullable final LookupElement item) {
if (item != null && ContainerUtil.indexOfIdentity(items, item) >= 0 && !model.contains(item)) {
addSomeItems(model, byRelevance, new Condition<LookupElement>() {
@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<LookupElement> items,
MultiMap<CompletionSorterImpl, LookupElement> itemsBySorter,
boolean onExplicitAction) {
private static int getItemToSelect(Lookup lookup, List<LookupElement> 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<CompletionSorterImpl, LookupElement> 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<StatisticsInfo> toIncrement = new ArrayList<StatisticsInfo>();
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.<StatisticsInfo>emptyList());
}
StatisticsUpdate update = new StatisticsUpdate(toIncrement);
StatisticsUpdate update = new StatisticsUpdate(StatisticsWeigher.composeStatsWithPrefix(base, lookup.itemPattern(item)));
ourPendingUpdate = update;
Disposer.register(update, new Disposable() {
@Override
@@ -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<StatisticsInfo> 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<StatisticsInfo> 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<StatisticsInfo> 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<StatisticsInfo> composeStatsWithPrefix(StatisticsInfo info, final String fullPrefix) {
ArrayList<StatisticsInfo> infos = new ArrayList<StatisticsInfo>(fullPrefix.length() + 1);
infos.add(info);
for (int i = 1; i <= fullPrefix.length(); i++) {
@@ -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());
}
@@ -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;
@@ -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);
}
@@ -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<String> chooser = new ContentChooser<String>(myConsole.getProject(), myConsole.getTitle(), true) {
final ContentChooser<String> chooser = new ContentChooser<String>(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<String> getContents() {
return myModel.getHistory();
List<String> 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);
@@ -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<RangeHighlighter>() {
final EditorHyperlinkSupport hyperlinks = myHyperlinks;
if (hyperlinks == null) {
return null;
}
return EditorHyperlinkSupport.getNextOccurrence(myEditor, hyperlinks.getHyperlinks().keySet(), delta, new Consumer<RangeHighlighter>() {
@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);
}
@@ -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<LiveOccurrence>(), 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;
@@ -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<T extends ClassMember> 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('(');
@@ -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 {
@@ -96,6 +96,7 @@ public class DefaultChooseByNameItemProvider implements ChooseByNameItemProvider
boolean previousElemSeparator = false;
boolean wasElement = false;
List<Pair<String, MinusculeMatcher>> 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<Pair<String, MinusculeMatcher>> patternsAndMatchers) {
final String name = base.getModel().getFullName(element);
if (name == null) return false;
final List<String> suspects = split(name, base);
final List<Pair<String, MinusculeMatcher>> patternsAndMatchers =
ContainerUtil.map2List(split(qualifierPattern, base), new Function<String, Pair<String, MinusculeMatcher>>() {
@NotNull
@Override
public Pair<String, MinusculeMatcher> 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<Pair<String, MinusculeMatcher>> getPatternsAndMatchers(String qualifierPattern, final ChooseByNameBase base) {
return ContainerUtil.map2List(split(qualifierPattern, base), new Function<String, Pair<String, MinusculeMatcher>>() {
@NotNull
@Override
public Pair<String, MinusculeMatcher> fun(String s) {
return Pair.create(getNamePattern(base, s), buildPatternMatcher(getNamePattern(base, s), NameUtil.MatchingCaseSensitivity.NONE));
}
});
}
@NotNull
@Override
public List<String> filterNames(@NotNull ChooseByNameBase base, @NotNull String[] names, @NotNull String pattern) {
@@ -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
@@ -21,7 +21,6 @@ public class WebModuleType extends WebModuleTypeBase<ModuleBuilder> {
@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"
@@ -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 <T extends PsiElement> ElementManipulator<T> getManipulator(T currentElement){
return ElementManipulators.getManipulator(currentElement);
ElementManipulator<T> 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 {
@@ -399,18 +399,15 @@ public class FileReference implements FileReferenceOwner, PsiPolyVariantReferenc
@Override
public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
final ElementManipulator<PsiElement> 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<PsiElement> manipulator = CachingReference.getManipulator(getElement());
PsiElement element = getElement();
final ElementManipulator<PsiElement> 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
@@ -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);
}
@@ -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
@@ -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();
}
}
@@ -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);
}
@@ -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;
@@ -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<Data> extends DialogWrapper {
private JList myList;
private List<Data> 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<Data> 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<Data> 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<Data> 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<Data> 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<Data> 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<Data> 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<Data> 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<Data> 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<Data> 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<Data> 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<Data> 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);
}
}
}
@@ -41,7 +41,7 @@ public abstract class WebProjectGenerator<T> implements DirectoryProjectGenerato
public abstract String getName();
@Override
@Nullable
@NotNull
public final T showGenerationSettings(VirtualFile baseDir) throws ProcessCanceledException {
GeneratorPeer<T> peer = createPeer();
DialogWrapper dialog = new MyDialogWrapper(peer);
@@ -53,7 +53,7 @@ public abstract class WebProjectGenerator<T> 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}.
@@ -197,7 +197,6 @@
<mouse-shortcut keystroke="button2" />
<keyboard-shortcut first-keystroke="control B" />
</action>
<action id="GotoFile" />
<action id="GotoImplementation">
<mouse-shortcut keystroke="meta alt button1" />
<keyboard-shortcut first-keystroke="control alt B" />
@@ -211,6 +210,12 @@
<action id="GotoPreviousError">
<keyboard-shortcut first-keystroke="shift meta QUOTE" />
</action>
<action id="GotoClass">
<keyboard-shortcut first-keystroke="meta O" />
</action>
<action id="GotoFile">
<keyboard-shortcut first-keystroke="meta alt O" />
</action>
<action id="GotoSymbol">
<keyboard-shortcut first-keystroke="shift meta O" />
<keyboard-shortcut first-keystroke="shift meta alt N" />
@@ -319,7 +324,6 @@
<action id="FileChooser.NewFolder">
<keyboard-shortcut first-keystroke="alt INSERT" />
</action>
<action id="GotoClass" />
<action id="NewElement">
<keyboard-shortcut first-keystroke="control ENTER" />
<keyboard-shortcut first-keystroke="meta N" />
@@ -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() {
@@ -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);
}
}
}
@@ -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.
* <p>
* 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;
}
}
}
}
@@ -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';
}
@@ -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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
*
* <p>
* The C structure for a Tar Entry's header is:
* <pre>
* 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;
* </pre>
*
*/
// 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;
}
}
@@ -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);
}
}
}
@@ -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;
}
}
@@ -1331,6 +1331,10 @@
<localInspection language="JAVA" shortName="IgnoredJUnitTest" bundle="com.siyeh.InspectionGadgetsBundle" key="ignored.junit.test.display.name"
groupBundle="messages.InspectionsBundle" groupKey="group.names.junit.issues" enabledByDefault="false" level="WARNING"
implementationClass="com.siyeh.ig.junit.IgnoredJUnitTestInspection"/>
<localInspection language="JAVA" shortName="JUnit3StyleTestMethodInJUnit4Class" bundle="com.siyeh.InspectionGadgetsBundle"
key="junit3.style.test.method.in.junit4.class.display.name" groupBundle="messages.InspectionsBundle"
groupKey="group.names.junit.issues" enabledByDefault="false" level="WARNING"
implementationClass="com.siyeh.ig.junit.JUnit3StyleTestMethodInJUnit4ClassInspection"/>
<localInspection language="JAVA" shortName="JUnit4AnnotatedMethodInJUnit3TestCase" bundle="com.siyeh.InspectionGadgetsBundle"
key="junit4.test.method.in.class.extending.junit3.testcase.display.name" groupBundle="messages.InspectionsBundle"
groupKey="group.names.junit.issues" enabledByDefault="true" level="WARNING"
@@ -1441,6 +1441,7 @@ suspicious.system.arraycopy.problem.descriptor5=<code>#ref</code> is not of an a
suspicious.system.arraycopy.problem.descriptor6=Source parameter type ''{0}'' is not assignable to destination parameter <code>#ref</code> 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 <code>#ref()</code>#loc is only used from an anonymous class extending ''{0}'' #loc
method.only.used.from.inner.class.problem.descriptor.anonymous.implementing=Method <code>#ref()</code>#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 <code>#ref</code> #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 <code>#ref</code> inside class extending JUnit 3 TestCase #loc
ignore.test.method.in.class.extending.junit3.testcase.problem.descriptor=@Ignore annotated method <code>#ref</code> 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 <code>#ref()</code> annotated with '@Test' inside class extending JUnit 3 TestCase #loc
ignore.test.method.in.class.extending.junit3.testcase.problem.descriptor=JUnit 3 test method <code>#ref()</code> 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=<code>#ref</code> 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 <code>#ref</code> has public default constructor
public.constructor.problem.descriptor=Public constructor <code>#ref()</code>
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 <code>#ref</code> in JUnit 4 class
@@ -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<DataContext> contextFromFocus = DataManager.getInstance().getDataContextFromFocus();
contextFromFocus.doWhenDone(new AsyncResult.Handler<DataContext>() {
@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();
@@ -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);
}
}
}
@@ -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<InspectionGadgetsFix> 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);
}
}
}
@@ -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;
@@ -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;
}
@@ -0,0 +1,10 @@
<html>
<body>
This inspection reports JUnit 3 style test methods which are located inside a class
which does not extend the abstract JUnit 3 class <b>TestCase</b> and contains JUnit 4 <b>@Test</b> annotated methods.
In addition to being confusing such test methods will not be run.
<!-- tooltip end -->
<p>
<small>New in 12, Powered by InspectionGadgets</small>
</body>
</html>
@@ -1,8 +1,9 @@
<html>
<body>
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 <b>@Test</b> annotated methods which are located inside a class
extending the abstract JUnit 3 class <b>TestCase</b>. Mixing JUnit API's like this is
confusing and can lead to problems running the tests, for example a method annotated with <b>@Ignore</b> won't
be actually ignored if its name starts with test.
<!-- tooltip end -->
<p>
<small>Powered by InspectionGadgets</small>
@@ -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;
}
}
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<problems>
</problems>
@@ -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);
}
}
@@ -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);
}
}
@@ -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;
}
}
@@ -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<TextRange> 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() {
@@ -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<TextRange> 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<TextRange> 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;
}
}
}