diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/JavaDebuggerSupport.java b/java/debugger/impl/src/com/intellij/debugger/ui/JavaDebuggerSupport.java index 2dda3d70feb4..3b4ff49e6eff 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/JavaDebuggerSupport.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/JavaDebuggerSupport.java @@ -33,6 +33,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.Key; +import com.intellij.util.containers.ContainerUtil; import com.intellij.xdebugger.AbstractDebuggerSession; import com.intellij.xdebugger.breakpoints.ui.BreakpointItem; import com.intellij.xdebugger.breakpoints.ui.XBreakpointGroupingRule; @@ -50,7 +51,6 @@ import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; /** * @author nik @@ -187,7 +187,7 @@ public class JavaDebuggerSupport extends DebuggerSupport { } private static class JavaBreakpointPanelProvider extends BreakpointPanelProvider { - private List myListeners = new CopyOnWriteArrayList(); + private List myListeners = ContainerUtil.createEmptyCOWList(); @NotNull public Collection> getBreakpointPanels(@NotNull final Project project, @NotNull final DialogWrapper parentDialog) { diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java index 555cbfef7581..14e7352028ee 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java @@ -1278,6 +1278,7 @@ public class HighlightMethodUtil { if (classReference != null) { ConstructorParametersFixer.registerFixActions(classReference, constructorCall, info, getFixRange(infoElement)); ChangeMethodSignatureFromUsageFix.registerIntentions(results, list, info, null); + ChangeTypeArgumentsFix.registerIntentions(results, list, info, aClass); ConvertDoubleToFloatFix.registerIntentions(results, list, info, null); PermuteArgumentsFix.registerFix(info, constructorCall, toMethodCandidates(results), getFixRange(list)); ChangeParameterClassFix.registerQuickFixActions(constructorCall, list, info); diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ChangeParameterClassFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ChangeParameterClassFix.java index c4dab56f1f17..9c6d5f19bfbc 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ChangeParameterClassFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ChangeParameterClassFix.java @@ -122,6 +122,7 @@ public class ChangeParameterClassFix extends ExtendsListFix { if (rClass instanceof PsiAnonymousClass) return; if (rClass.isInheritor(lClass, true)) return; if (lClass.isInheritor(rClass, true)) return; + if (lClass == rClass) return; QuickFixAction.registerQuickFixAction(info, new ChangeParameterClassFix(rClass, (PsiClassType)lType)); } diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ChangeTypeArgumentsFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ChangeTypeArgumentsFix.java new file mode 100644 index 000000000000..3945e632a75a --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ChangeTypeArgumentsFix.java @@ -0,0 +1,165 @@ +/* + * 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. + */ + +/** + * Created by IntelliJ IDEA. + * User: cdr + * Date: Nov 13, 2002 + * Time: 3:26:50 PM + * To change this template use Options | File Templates. + */ +package com.intellij.codeInsight.daemon.impl.quickfix; + +import com.intellij.codeInsight.CodeInsightUtilBase; +import com.intellij.codeInsight.daemon.impl.HighlightInfo; +import com.intellij.codeInsight.intention.HighPriorityAction; +import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.*; +import com.intellij.psi.impl.source.resolve.DefaultParameterTypeInferencePolicy; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.TypeConversionUtil; +import com.intellij.util.Function; +import org.jetbrains.annotations.NotNull; + +public class ChangeTypeArgumentsFix implements IntentionAction, HighPriorityAction { + private final PsiMethod myTargetMethod; + private final PsiClass myPsiClass; + private final PsiExpression[] myExpressions; + private static final Logger LOG = Logger.getInstance("#" + ChangeTypeArgumentsFix.class.getName()); + private final PsiNewExpression myNewExpression; + + ChangeTypeArgumentsFix(@NotNull PsiMethod targetMethod, + PsiClass psiClass, + @NotNull PsiExpression[] expressions, + @NotNull PsiElement context) { + myTargetMethod = targetMethod; + myPsiClass = psiClass; + myExpressions = expressions; + myNewExpression = PsiTreeUtil.getParentOfType(context, PsiNewExpression.class); + } + + @Override + @NotNull + public String getText() { + final PsiSubstitutor substitutor = inferTypeArguments(); + return "Change type arguments to <" + StringUtil.join(myPsiClass.getTypeParameters(), new Function() { + @Override + public String fun(PsiTypeParameter typeParameter) { + return substitutor.substitute(typeParameter).getPresentableText(); + } + }, ", ") + ">"; + } + + + @Override + @NotNull + public String getFamilyName() { + return "Change type arguments"; + } + + @Override + public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { + final PsiTypeParameter[] typeParameters = myPsiClass.getTypeParameters(); + if (typeParameters.length > 0) { + if (myNewExpression != null && myNewExpression.isValid() && myNewExpression.getArgumentList() != null) { + final PsiJavaCodeReferenceElement reference = myNewExpression.getClassOrAnonymousClassReference(); + if (reference != null) { + final PsiReferenceParameterList parameterList = reference.getParameterList(); + if (parameterList != null) { + final PsiSubstitutor substitutor = inferTypeArguments(); + final PsiParameter[] parameters = myTargetMethod.getParameterList().getParameters(); + if (parameters.length != myExpressions.length) return false; + for (int i = 0, length = parameters.length; i < length; i++) { + PsiParameter parameter = parameters[i]; + final PsiType expectedType = substitutor.substitute(parameter.getType()); + if (!myExpressions[i].isValid()) return false; + final PsiType actualType = myExpressions[i].getType(); + if (expectedType == null || actualType == null || !TypeConversionUtil.isAssignable(expectedType, actualType)) return false; + } + return true; + } + } + } + } + return false; + } + + @Override + public void invoke(@NotNull final Project project, Editor editor, final PsiFile file) { + if (!CodeInsightUtilBase.prepareFileForWrite(file)) return; + + final PsiTypeParameter[] typeParameters = myPsiClass.getTypeParameters(); + final PsiSubstitutor psiSubstitutor = inferTypeArguments(); + final PsiJavaCodeReferenceElement reference = myNewExpression.getClassOrAnonymousClassReference(); + LOG.assertTrue(reference != null, myNewExpression); + final PsiReferenceParameterList parameterList = reference.getParameterList(); + LOG.assertTrue(parameterList != null, myNewExpression); + PsiTypeElement[] elements = parameterList.getTypeParameterElements(); + for (int i = elements.length - 1; i >= 0; i--) { + PsiTypeElement typeElement = elements[i]; + final PsiType typeArg = psiSubstitutor.substitute(typeParameters[i]); + typeElement.replace(JavaPsiFacade.getElementFactory(project).createTypeElement(typeArg)); + } + } + + private PsiSubstitutor inferTypeArguments() { + final JavaPsiFacade facade = JavaPsiFacade.getInstance(myNewExpression.getProject()); + final PsiResolveHelper resolveHelper = facade.getResolveHelper(); + final PsiParameter[] parameters = myTargetMethod.getParameterList().getParameters(); + final PsiExpressionList argumentList = myNewExpression.getArgumentList(); + LOG.assertTrue(argumentList != null); + final PsiExpression[] expressions = argumentList.getExpressions(); + return resolveHelper.inferTypeArguments(myPsiClass.getTypeParameters(), parameters, expressions, + PsiSubstitutor.EMPTY, + myNewExpression.getParent(), + DefaultParameterTypeInferencePolicy.INSTANCE); + } + + + public static void registerIntentions(@NotNull JavaResolveResult[] candidates, + @NotNull PsiExpressionList list, + @NotNull HighlightInfo highlightInfo, + PsiClass psiClass) { + if (candidates.length == 0) return; + PsiExpression[] expressions = list.getExpressions(); + for (JavaResolveResult candidate : candidates) { + registerIntention(expressions, highlightInfo, psiClass, candidate, list); + } + } + + private static void registerIntention(@NotNull PsiExpression[] expressions, + @NotNull HighlightInfo highlightInfo, + PsiClass psiClass, + @NotNull JavaResolveResult candidate, + @NotNull PsiElement context) { + if (!candidate.isStaticsScopeCorrect()) return; + PsiMethod method = (PsiMethod)candidate.getElement(); + PsiSubstitutor substitutor = candidate.getSubstitutor(); + if (method != null && context.getManager().isInProject(method)) { + final ChangeTypeArgumentsFix fix = new ChangeTypeArgumentsFix(method, psiClass, expressions, context); + QuickFixAction.registerQuickFixAction(highlightInfo, null, fix); + } + } + + @Override + public boolean startInWriteAction() { + return true; + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeParameterClass/beforeDifferentTypeArgs.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeParameterClass/beforeDifferentTypeArgs.java new file mode 100644 index 000000000000..599d08760860 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeParameterClass/beforeDifferentTypeArgs.java @@ -0,0 +1,10 @@ +// "Make 'Generic' extend 'Generic'" "false" +class Generic { + Generic(E arg) { } +} + +class Tester { + void method() { + Generic aIntegerGeneric = new Generic(""); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/after1.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/after1.java new file mode 100644 index 000000000000..430b68756e80 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/after1.java @@ -0,0 +1,11 @@ +// "Change type arguments to " "true" +class Generic { + Generic(E arg) { + } +} + +class Tester { + void method() { + new Generic("hi"); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/afterMultipleParams.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/afterMultipleParams.java new file mode 100644 index 000000000000..55ff7b66dbb4 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/afterMultipleParams.java @@ -0,0 +1,11 @@ +// "Change type arguments to " "true" +class Generic { + Generic(E arg, K arg1) { + } +} + +class Tester { + void method() { + new Generic("hi", 1); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/afterSuperType.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/afterSuperType.java new file mode 100644 index 000000000000..337708e8fb14 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/afterSuperType.java @@ -0,0 +1,13 @@ +import java.io.Serializable; + +// "Change type arguments to " "true" +class Generic { + Generic(E arg, E arg1) { + } +} + +class Tester { + void method() { + new Generic("hi", 1); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/before1.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/before1.java new file mode 100644 index 000000000000..dd660b853e5c --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/before1.java @@ -0,0 +1,11 @@ +// "Change type arguments to " "true" +class Generic { + Generic(E arg) { + } +} + +class Tester { + void method() { + new Generic("hi"); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeCorrectTypeParam.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeCorrectTypeParam.java new file mode 100644 index 000000000000..3d3c223e7260 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeCorrectTypeParam.java @@ -0,0 +1,11 @@ +// "Change type arguments to " "false" +class Generic { + Generic(E arg, int i) { + } +} + +class Tester { + void method() { + new Generic("hi", ""); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeDisabled.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeDisabled.java new file mode 100644 index 000000000000..2869486b752e --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeDisabled.java @@ -0,0 +1,13 @@ +// "Change type arguments to " "false" +import java.util.List; + +class Generic { + Generic(E arg, List arg1) { + } +} + +class Tester { + void method() { + new Generic("hi", "hi"); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeIncomplete.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeIncomplete.java new file mode 100644 index 000000000000..b6b5b04cbf2a --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeIncomplete.java @@ -0,0 +1,11 @@ +// "Change type arguments to " "false" +class Generic { + Generic(E arg) { + } +} + +class Tester { + void method() { + new Generic("hi" + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeIncomplete2.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeIncomplete2.java new file mode 100644 index 000000000000..9b1def3056fc --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeIncomplete2.java @@ -0,0 +1,11 @@ +// "Change type arguments to " "false" +class Generic { + Generic(E arg, int i) { + } +} + +class Tester { + void method() { + new Generic("hi"); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeMultipleParams.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeMultipleParams.java new file mode 100644 index 000000000000..896686b641fc --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeMultipleParams.java @@ -0,0 +1,11 @@ +// "Change type arguments to " "true" +class Generic { + Generic(E arg, K arg1) { + } +} + +class Tester { + void method() { + new Generic("hi", 1); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeSuperType.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeSuperType.java new file mode 100644 index 000000000000..1b6580752258 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeSuperType.java @@ -0,0 +1,11 @@ +// "Change type arguments to " "true" +class Generic { + Generic(E arg, E arg1) { + } +} + +class Tester { + void method() { + new Generic("hi", 1); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/ChangeTypeArgumentsFixTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/ChangeTypeArgumentsFixTest.java new file mode 100644 index 000000000000..7aa289c49cbf --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/ChangeTypeArgumentsFixTest.java @@ -0,0 +1,26 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.daemon.quickFix; + +public class ChangeTypeArgumentsFixTest extends LightQuickFix15TestCase { + + public void test() throws Exception { doAllTests(); } + + @Override + protected String getBasePath() { + return "/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs"; + } +} diff --git a/java/openapi/src/com/intellij/patterns/PsiMethodPattern.java b/java/openapi/src/com/intellij/patterns/PsiMethodPattern.java index 87247577c30f..d4f60967aff4 100644 --- a/java/openapi/src/com/intellij/patterns/PsiMethodPattern.java +++ b/java/openapi/src/com/intellij/patterns/PsiMethodPattern.java @@ -22,6 +22,7 @@ import com.intellij.psi.*; import com.intellij.psi.search.searches.SuperMethodsSearch; import com.intellij.psi.util.MethodSignatureBackedByPsiMethod; import com.intellij.psi.util.TypeConversionUtil; +import com.intellij.util.ArrayUtil; import com.intellij.util.PairProcessor; import com.intellij.util.ProcessingContext; import com.intellij.util.Processor; @@ -38,6 +39,7 @@ public class PsiMethodPattern extends PsiMemberPattern("withParameterCount") { + @Override public boolean accepts(@NotNull final PsiMethod method, final ProcessingContext context) { return method.getParameterList().getParametersCount() == paramCount; } @@ -46,12 +48,14 @@ public class PsiMethodPattern extends PsiMemberPattern
  • "?" - means any type
  • ".." - instructs pattern to accept the rest of the arguments
  • * @return */ - public PsiMethodPattern withParameters(@NonNls final String... types) { + public PsiMethodPattern withParameters(@NonNls final String... inputTypes) { + final String[] types = inputTypes.length == 0 ? ArrayUtil.EMPTY_STRING_ARRAY : inputTypes; return with(new PatternCondition("withParameters") { + @Override public boolean accepts(@NotNull final PsiMethod psiMethod, final ProcessingContext context) { final PsiParameterList parameterList = psiMethod.getParameterList(); int dotsIndex = -1; @@ -91,7 +95,7 @@ public class PsiMethodPattern extends PsiMemberPattern result = Ref.create(Boolean.TRUE); SuperMethodsSearch.search(t, null, true, false).forEach(new Processor() { + @Override public boolean process(final MethodSignatureBackedByPsiMethod signature) { if (!processor.process(signature.getMethod().getContainingClass(), context)) { result.set(Boolean.FALSE); @@ -118,6 +123,7 @@ public class PsiMethodPattern extends PsiMemberPattern("constructor") { + @Override public boolean accepts(@NotNull final PsiMethod method, final ProcessingContext context) { return method.isConstructor() == isConstructor; } @@ -127,6 +133,7 @@ public class PsiMethodPattern extends PsiMemberPattern pattern) { return with(new PatternCondition("withThrowsList") { + @Override public boolean accepts(@NotNull final PsiMethod method, final ProcessingContext context) { return pattern.accepts(method.getThrowsList()); } diff --git a/platform/lang-api/src/com/intellij/find/FindModel.java b/platform/lang-api/src/com/intellij/find/FindModel.java index 07ad7422fd96..0af15cb80990 100644 --- a/platform/lang-api/src/com/intellij/find/FindModel.java +++ b/platform/lang-api/src/com/intellij/find/FindModel.java @@ -19,11 +19,12 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.UserDataHolderBase; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.search.SearchScope; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.concurrent.CopyOnWriteArrayList; +import java.util.List; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; @@ -49,7 +50,7 @@ public class FindModel extends UserDataHolderBase implements Cloneable { void findModelChanged(FindModel findModel); } - private final CopyOnWriteArrayList myObservers = new CopyOnWriteArrayList(); + private final List myObservers = ContainerUtil.createEmptyCOWList(); public void addObserver(FindModelObserver observer) { myObservers.add(observer); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/Advertiser.java b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/Advertiser.java index 43b01646d15a..760f07ac614f 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/Advertiser.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/Advertiser.java @@ -18,6 +18,7 @@ package com.intellij.codeInsight.lookup.impl; import com.google.common.collect.ImmutableMap; import com.intellij.openapi.application.ApplicationManager; import com.intellij.ui.ClickListener; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.GridBag; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; @@ -34,7 +35,7 @@ import java.util.concurrent.CopyOnWriteArrayList; * @author peter */ public class Advertiser { - private final List myTexts = new CopyOnWriteArrayList(); + private final List myTexts = ContainerUtil.createEmptyCOWList(); private volatile Dimension myCachedPrefSize; private final JPanel myComponent = new JPanel(new GridBagLayout()) { private JLabel mySample = createLabel(); diff --git a/platform/lang-impl/src/com/intellij/execution/impl/RunManagerImpl.java b/platform/lang-impl/src/com/intellij/execution/impl/RunManagerImpl.java index 58ba805b5f96..f3b498d36e61 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/RunManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/RunManagerImpl.java @@ -87,6 +87,7 @@ public class RunManagerImpl extends RunManagerEx implements JDOMExternalizable, // separate method needed for tests public final void initializeConfigurationTypes(@NotNull final ConfigurationType[] factories) { Arrays.sort(factories, new Comparator() { + @Override public int compare(final ConfigurationType o1, final ConfigurationType o2) { return o1.getDisplayName().compareTo(o2.getDisplayName()); } @@ -109,15 +110,19 @@ public class RunManagerImpl extends RunManagerEx implements JDOMExternalizable, initializeConfigurationTypes(configurationTypes); } + @Override public void disposeComponent() { } + @Override public void initComponent() { } + @Override public void projectOpened() { } + @Override @NotNull public RunnerAndConfigurationSettings createConfiguration(final String name, final ConfigurationFactory factory) { return createConfiguration(doCreateConfiguration(name, factory, true), factory); @@ -134,6 +139,7 @@ public class RunManagerImpl extends RunManagerEx implements JDOMExternalizable, } } + @Override @NotNull public RunnerAndConfigurationSettings createConfiguration(final RunConfiguration runConfiguration, final ConfigurationFactory factory) { @@ -145,14 +151,17 @@ public class RunManagerImpl extends RunManagerEx implements JDOMExternalizable, return settings; } + @Override public void projectClosed() { myTemplateConfigurationsMap.clear(); } + @Override public RunManagerConfig getConfig() { return myConfig; } + @Override @NotNull public ConfigurationType[] getConfigurationFactories() { return myTypes.clone(); @@ -177,6 +186,7 @@ public class RunManagerImpl extends RunManagerEx implements JDOMExternalizable, /** * Template configuration is not included */ + @Override @NotNull public RunConfiguration[] getConfigurations(@NotNull final ConfigurationType type) { @@ -191,6 +201,7 @@ public class RunManagerImpl extends RunManagerEx implements JDOMExternalizable, return array.toArray(new RunConfiguration[array.size()]); } + @Override @NotNull public RunConfiguration[] getAllConfigurations() { RunConfiguration[] result = new RunConfiguration[myConfigurations.size()]; @@ -215,6 +226,7 @@ public class RunManagerImpl extends RunManagerEx implements JDOMExternalizable, /** * Template configuration is not included */ + @Override @NotNull public RunnerAndConfigurationSettings[] getConfigurationSettings(@NotNull final ConfigurationType type) { @@ -240,6 +252,7 @@ public class RunManagerImpl extends RunManagerEx implements JDOMExternalizable, return template; } + @Override public void addConfiguration(RunnerAndConfigurationSettings settings, boolean shared, List tasks, boolean addEnabledTemplateTasksIfAbsent) { @@ -336,6 +349,7 @@ public class RunManagerImpl extends RunManagerEx implements JDOMExternalizable, } } + @Override @Nullable public RunnerAndConfigurationSettings getSelectedConfiguration() { if (mySelectedConfigurationId == null && myLoadedSelectedConfigurationUniqueName != null) { @@ -344,6 +358,7 @@ public class RunManagerImpl extends RunManagerEx implements JDOMExternalizable, return mySelectedConfigurationId == null ? null : myConfigurations.get(mySelectedConfigurationId); } + @Override public void setSelectedConfiguration(@Nullable RunnerAndConfigurationSettings settings) { setSelectedConfigurationId(settings == null ? null : settings.getConfiguration().getUniqueID()); if (settings != null) { @@ -420,6 +435,7 @@ public class RunManagerImpl extends RunManagerEx implements JDOMExternalizable, return true; } + @Override public void writeExternal(@NotNull final Element parentNode) throws WriteExternalException { writeContext(parentNode); for (final RunnerAndConfigurationSettings runnerAndConfigurationSettings : myTemplateConfigurationsMap.values()) { @@ -529,10 +545,12 @@ public class RunManagerImpl extends RunManagerEx implements JDOMExternalizable, } + @Override public void readExternal(final Element parentNode) throws InvalidDataException { clear(); final Comparator comparator = new Comparator() { + @Override public int compare(Element a, Element b) { final boolean aDefault = Boolean.valueOf(a.getAttributeValue("default", "false")); final boolean bDefault = Boolean.valueOf(b.getAttributeValue("default", "false")); @@ -716,11 +734,13 @@ public class RunManagerImpl extends RunManagerEx implements JDOMExternalizable, return null; } + @Override @NotNull public String getComponentName() { return "RunManager"; } + @Override public void setTemporaryConfiguration(@Nullable final RunnerAndConfigurationSettings tempConfiguration) { if (tempConfiguration == null) return; @@ -748,18 +768,22 @@ public class RunManagerImpl extends RunManagerEx implements JDOMExternalizable, return result.values(); } + @Override public boolean isTemporary(@NotNull final RunConfiguration configuration) { return Arrays.asList(getTempConfigurations()).contains(configuration); } + @Override public boolean isTemporary(@NotNull RunnerAndConfigurationSettings settings) { return settings.isTemporary(); } + @Override @NotNull public RunConfiguration[] getTempConfigurations() { List configurations = ContainerUtil.mapNotNull(myConfigurations.values(), new NullableFunction() { + @Override public RunConfiguration fun(RunnerAndConfigurationSettings settings) { return settings.isTemporary() ? settings.getConfiguration() : null; } @@ -767,6 +791,7 @@ public class RunManagerImpl extends RunManagerEx implements JDOMExternalizable, return configurations.toArray(new RunConfiguration[configurations.size()]); } + @Override public void makeStable(@NotNull RunConfiguration configuration) { RunnerAndConfigurationSettings settings = getSettings(configuration); if (settings != null) { @@ -780,11 +805,13 @@ public class RunManagerImpl extends RunManagerEx implements JDOMExternalizable, } } + @Override @NotNull public RunnerAndConfigurationSettings createRunConfiguration(String name, ConfigurationFactory type) { return createConfiguration(name, type); } + @Override public boolean isConfigurationShared(final RunnerAndConfigurationSettings settings) { Boolean shared = mySharedConfigurations.get(settings.getConfiguration().getUniqueID()); if (shared == null) { @@ -794,6 +821,7 @@ public class RunManagerImpl extends RunManagerEx implements JDOMExternalizable, return shared != null && shared.booleanValue(); } + @Override @NotNull public List getBeforeRunTasks(Key taskProviderID) { final List tasks = new ArrayList(); @@ -822,10 +850,12 @@ public class RunManagerImpl extends RunManagerEx implements JDOMExternalizable, return tasks; } + @Override public void invalidateConfigurationIcon(@NotNull final RunnerAndConfigurationSettings settings) { myIdToIcon.remove(settings.getConfiguration().getUniqueID()); } + @Override public Icon getConfigurationIcon(@NotNull final RunnerAndConfigurationSettings settings) { final int uniqueID = settings.getConfiguration().getUniqueID(); Icon icon = myIdToIcon.get(uniqueID); @@ -881,6 +911,7 @@ public class RunManagerImpl extends RunManagerEx implements JDOMExternalizable, return result; } + @Override @NotNull public List getBeforeRunTasks(final RunConfiguration settings) { final List tasks = myConfigurationToBeforeTasksMap.get(settings); @@ -920,7 +951,7 @@ public class RunManagerImpl extends RunManagerEx implements JDOMExternalizable, result.add(task.clone()); } } - return result; + return result.isEmpty() ? Collections.emptyList() : result; } public void shareConfiguration(final RunConfiguration runConfiguration, final boolean shareConfiguration) { @@ -933,6 +964,7 @@ public class RunManagerImpl extends RunManagerEx implements JDOMExternalizable, if (shouldFire) fireRunConfigurationChanged(settings); } + @Override public final void setBeforeRunTasks(final RunConfiguration runConfiguration, @NotNull List tasks, boolean addEnabledTemplateTasksIfAbsent) { List result = new ArrayList(tasks); if (addEnabledTemplateTasksIfAbsent) { @@ -949,7 +981,7 @@ public class RunManagerImpl extends RunManagerEx implements JDOMExternalizable, } } } - myConfigurationToBeforeTasksMap.put(runConfiguration, result); + myConfigurationToBeforeTasksMap.put(runConfiguration, result.isEmpty() ? Collections.emptyList() : result); fireBeforeRunTasksUpdated(); } @@ -958,6 +990,7 @@ public class RunManagerImpl extends RunManagerEx implements JDOMExternalizable, fireBeforeRunTasksUpdated(); } + @Override public void addConfiguration(final RunnerAndConfigurationSettings settings, final boolean isShared) { addConfiguration(settings, isShared, getTemplateBeforeRunTasks(settings.getConfiguration()), false); } diff --git a/platform/lang-impl/src/com/intellij/ide/actions/ReloadFromDiskAction.java b/platform/lang-impl/src/com/intellij/ide/actions/ReloadFromDiskAction.java index 94969d53c9d0..702f3c79d679 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/ReloadFromDiskAction.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/ReloadFromDiskAction.java @@ -22,8 +22,8 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.project.Project; import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; @@ -53,7 +53,6 @@ public class ReloadFromDiskAction extends AnAction implements DumbAware { new Runnable() { public void run() { PsiManager.getInstance(project).reloadFromDisk(psiFile); - CommandProcessor.getInstance().markCurrentCommandAsGlobal(project); } } ); diff --git a/platform/lvcs-impl/src/com/intellij/history/core/changes/ChangeSet.java b/platform/lvcs-impl/src/com/intellij/history/core/changes/ChangeSet.java index 527a48760745..210205d50029 100644 --- a/platform/lvcs-impl/src/com/intellij/history/core/changes/ChangeSet.java +++ b/platform/lvcs-impl/src/com/intellij/history/core/changes/ChangeSet.java @@ -31,7 +31,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; public class ChangeSet { private final long myId; @@ -44,7 +43,7 @@ public class ChangeSet { public ChangeSet(long id, long timestamp) { myId = id; myTimestamp = timestamp; - myChanges = new CopyOnWriteArrayList(); + myChanges = ContainerUtil.createEmptyCOWList(); } public ChangeSet(DataInput in) throws IOException { diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java index 20c3a1753343..04ac15d65fd4 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java @@ -38,6 +38,7 @@ import com.intellij.ui.tabs.impl.singleRow.SingleRowLayout; import com.intellij.ui.tabs.impl.singleRow.SingleRowPassInfo; import com.intellij.ui.tabs.impl.table.TableLayout; import com.intellij.ui.tabs.impl.table.TablePassInfo; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.Animator; import com.intellij.util.ui.JBInsets; import com.intellij.util.ui.TimedDeadzone; @@ -80,8 +81,8 @@ public class JBTabsImpl extends JComponent private Insets myInnerInsets = JBInsets.NONE; - private final List myTabMouseListeners = new CopyOnWriteArrayList(); - private final List myTabListeners = new CopyOnWriteArrayList(); + private final List myTabMouseListeners = ContainerUtil.createEmptyCOWList(); + private final List myTabListeners = ContainerUtil.createEmptyCOWList(); private boolean myFocused; private Getter myPopupGroup; diff --git a/platform/platform-impl/src/com/intellij/notification/EventLog.java b/platform/platform-impl/src/com/intellij/notification/EventLog.java index a0233b7afbee..e03f6db7de40 100644 --- a/platform/platform-impl/src/com/intellij/notification/EventLog.java +++ b/platform/platform-impl/src/com/intellij/notification/EventLog.java @@ -47,6 +47,7 @@ import com.intellij.ui.awt.RelativePoint; import com.intellij.ui.content.Content; import com.intellij.ui.content.ContentFactory; import com.intellij.util.containers.CollectionFactory; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.hash.LinkedHashMap; import com.intellij.util.text.CharArrayUtil; import org.jetbrains.annotations.NonNls; @@ -344,7 +345,7 @@ public class EventLog implements Notifications { public static class ProjectTracker extends AbstractProjectComponent { private volatile EventLogConsole myConsole; - private final List myInitial = new CopyOnWriteArrayList(); + private final List myInitial = ContainerUtil.createEmptyCOWList(); private final LogModel myProjectModel; public ProjectTracker(@NotNull final Project project) { diff --git a/platform/platform-impl/src/com/intellij/openapi/command/impl/CommandMerger.java b/platform/platform-impl/src/com/intellij/openapi/command/impl/CommandMerger.java index 20a9f76f8891..7aaaacd6b6c7 100644 --- a/platform/platform-impl/src/com/intellij/openapi/command/impl/CommandMerger.java +++ b/platform/platform-impl/src/com/intellij/openapi/command/impl/CommandMerger.java @@ -180,7 +180,7 @@ public class CommandMerger { myForcedGlobal = true; } - private boolean isTransparent() { + public boolean isTransparent() { return myTransparent; } diff --git a/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoManagerImpl.java index 82741248cd9a..f753b1fc3996 100644 --- a/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoManagerImpl.java @@ -16,8 +16,10 @@ package com.intellij.openapi.command.impl; import com.intellij.CommonBundle; +import com.intellij.ide.DataManager; import com.intellij.idea.ActionsBundle; import com.intellij.openapi.Disposable; +import com.intellij.openapi.actionSystem.LangDataKeys; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.*; @@ -43,6 +45,7 @@ import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiFile; import com.intellij.util.containers.HashSet; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; @@ -253,7 +256,7 @@ public class UndoManagerImpl extends UndoManager implements ProjectComponent, Ap myCommandLevel--; if (myCommandLevel > 0) return; - if (myProject != null && myCurrentMerger.hasActions() && !myCurrentMerger.isGlobal()) { + if (myProject != null && !myCurrentMerger.isGlobal() && myCurrentMerger.hasActions() && !myCurrentMerger.isTransparent()) { addFocusedDocumentAsAffected(); } @@ -264,14 +267,14 @@ public class UndoManagerImpl extends UndoManager implements ProjectComponent, Ap } private void addFocusedDocumentAsAffected() { - VirtualFile[] selected = FileEditorManager.getInstance(myProject).getSelectedFiles(); - if (selected.length == 0) return; + PsiFile psiFile = LangDataKeys.PSI_FILE.getData(DataManager.getInstance().getDataContext()); + if (psiFile == null) return; - final DocumentReference[] refs = new DocumentReference[selected.length]; - for (int i = 0; i < refs.length; i++) { - refs[i] = DocumentReferenceManager.getInstance().create(selected[i]); - } + VirtualFile file = psiFile.getVirtualFile(); + if (file == null) return; + + final DocumentReference[] refs = new DocumentReference[]{DocumentReferenceManager.getInstance().create(file)}; myCurrentMerger.addAction(new BasicUndoableAction() { @Override public void undo() throws UnexpectedUndoException { diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/actions/DuplicateAction.java b/platform/platform-impl/src/com/intellij/openapi/editor/actions/DuplicateAction.java index 551d8efcc8d9..9bd2213b8559 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/actions/DuplicateAction.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/actions/DuplicateAction.java @@ -85,8 +85,10 @@ public class DuplicateAction extends EditorAction { final int lineToCheck = nextLineStart.line - 1; int newOffset = end + offset - start; - if(lineToCheck == document.getLineCount () /*empty document*/ || - document.getLineSeparatorLength(lineToCheck) == 0) { + if(lineToCheck == document.getLineCount () /* empty document */ + || nextLineStart.line == document.getLineCount() - 1 /* last line*/ + || document.getLineSeparatorLength(lineToCheck) == 0) + { s = "\n"+s; newOffset++; } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileDocumentManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileDocumentManagerImpl.java index fbfeb4b0b30a..28efc4d225f6 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileDocumentManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileDocumentManagerImpl.java @@ -21,6 +21,7 @@ import com.intellij.codeStyle.CodeStyleFacade; import com.intellij.openapi.application.AccessToken; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; +import com.intellij.openapi.command.UndoConfirmationPolicy; import com.intellij.openapi.components.ApplicationComponent; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diff.DiffManager; @@ -578,7 +579,7 @@ public class FileDocumentManagerImpl extends FileDocumentManager implements Appl } ); } - }, UIBundle.message("file.cache.conflict.action"), null); + }, UIBundle.message("file.cache.conflict.action"), null, UndoConfirmationPolicy.REQUEST_CONFIRMATION); myUnsavedDocuments.remove(document); diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/ex/KeymapManagerEx.java b/platform/platform-impl/src/com/intellij/openapi/keymap/ex/KeymapManagerEx.java index c38222825553..0dc7c02223a2 100644 --- a/platform/platform-impl/src/com/intellij/openapi/keymap/ex/KeymapManagerEx.java +++ b/platform/platform-impl/src/com/intellij/openapi/keymap/ex/KeymapManagerEx.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2012 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,7 @@ public abstract class KeymapManagerEx extends KeymapManager { } /** - * @return all available keymaps. The method return an aempty array if no + * @return all available keymaps. The method return an empty array if no * keymaps are available. */ public abstract Keymap[] getAllKeymaps(); diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/AltStateManager.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/AltStateManager.java index 3f11e60200d5..34a4025c51cf 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/AltStateManager.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/AltStateManager.java @@ -16,12 +16,12 @@ package com.intellij.openapi.wm.impl; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.util.containers.ContainerUtil; import java.awt.*; import java.awt.event.AWTEventListener; import java.awt.event.KeyEvent; import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; /** * @author pegov @@ -37,7 +37,7 @@ public class AltStateManager implements AWTEventListener { } } - private final List myListeners = new CopyOnWriteArrayList(); + private final List myListeners = ContainerUtil.createEmptyCOWList(); public interface AltListener { void altPressed(); diff --git a/platform/platform-impl/src/com/intellij/ui/ColorPicker.java b/platform/platform-impl/src/com/intellij/ui/ColorPicker.java index d51711bac17a..5a4a57c1868c 100644 --- a/platform/platform-impl/src/com/intellij/ui/ColorPicker.java +++ b/platform/platform-impl/src/com/intellij/ui/ColorPicker.java @@ -26,6 +26,7 @@ import com.intellij.openapi.util.text.StringUtilRt; import com.intellij.openapi.wm.WindowManager; import com.intellij.util.Alarm; import com.intellij.util.Consumer; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -440,7 +441,7 @@ public class ColorPicker extends JPanel implements ColorListener, DocumentListen private Color myColor; - private CopyOnWriteArrayList myListeners = new CopyOnWriteArrayList(); + private CopyOnWriteArrayList myListeners = ContainerUtil.createEmptyCOWList(); private int myOpacity; private ColorWheel() { diff --git a/platform/platform-impl/src/com/intellij/ui/SlideComponent.java b/platform/platform-impl/src/com/intellij/ui/SlideComponent.java index 3be566722165..b331220ffd54 100644 --- a/platform/platform-impl/src/com/intellij/ui/SlideComponent.java +++ b/platform/platform-impl/src/com/intellij/ui/SlideComponent.java @@ -19,12 +19,13 @@ import com.intellij.codeInsight.hint.HintUtil; import com.intellij.openapi.ui.popup.Balloon; import com.intellij.ui.awt.RelativePoint; import com.intellij.util.Consumer; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.UIUtil; import javax.swing.*; import java.awt.*; import java.awt.event.*; -import java.util.concurrent.CopyOnWriteArrayList; +import java.util.List; /** * @author Alexey Pegov @@ -37,9 +38,9 @@ class SlideComponent extends JComponent { private final boolean myVertical; private final String myTitle; - private CopyOnWriteArrayList> myListeners = new CopyOnWriteArrayList>(); + private final List> myListeners = ContainerUtil.createEmptyCOWList(); private LightweightHint myTooltipHint; - private JLabel myLabel = new JLabel(); + private final JLabel myLabel = new JLabel(); SlideComponent(String title, boolean vertical) { myTitle = title; diff --git a/platform/util/src/com/intellij/openapi/util/registry/RegistryValue.java b/platform/util/src/com/intellij/openapi/util/registry/RegistryValue.java index 076e298aa218..6f93e6e5cf0b 100644 --- a/platform/util/src/com/intellij/openapi/util/registry/RegistryValue.java +++ b/platform/util/src/com/intellij/openapi/util/registry/RegistryValue.java @@ -17,10 +17,11 @@ package com.intellij.openapi.util.registry; import com.intellij.openapi.Disposable; import com.intellij.openapi.util.Disposer; +import com.intellij.util.containers.ContainerUtil; import java.awt.*; +import java.util.List; import java.util.MissingResourceException; -import java.util.concurrent.CopyOnWriteArraySet; /** * @author Kirill Kalishev @@ -31,7 +32,7 @@ public class RegistryValue { private final Registry myRegistry; private final String myKey; - private final CopyOnWriteArraySet myListeners = new CopyOnWriteArraySet(); + private final List myListeners = ContainerUtil.createEmptyCOWList(); private boolean myChangedSinceStart; diff --git a/platform/util/src/com/intellij/util/ReflectionCache.java b/platform/util/src/com/intellij/util/ReflectionCache.java index 4c65b45c29ed..9e310236e196 100644 --- a/platform/util/src/com/intellij/util/ReflectionCache.java +++ b/platform/util/src/com/intellij/util/ReflectionCache.java @@ -29,42 +29,52 @@ import java.lang.reflect.TypeVariable; @SuppressWarnings({"MismatchedQueryAndUpdateOfCollection"}) public class ReflectionCache { private static final ConcurrentFactoryMap ourSuperClasses = new ConcurrentFactoryMap() { + @Override protected Class create(final Class key) { return key.getSuperclass(); } }; private static final ConcurrentFactoryMap ourInterfaces = new ConcurrentFactoryMap() { + @Override @NotNull protected Class[] create(final Class key) { - return key.getInterfaces(); + Class[] classes = key.getInterfaces(); + return classes.length == 0 ? ArrayUtil.EMPTY_CLASS_ARRAY : classes; } }; + private static final Method[] EMPTY_METHODS = new Method[0]; private static final ConcurrentFactoryMap ourMethods = new ConcurrentFactoryMap() { + @Override @NotNull protected Method[] create(final Class key) { - return key.getMethods(); + Method[] methods = key.getMethods(); + return methods.length == 0 ? EMPTY_METHODS : methods; } }; private static final ConcurrentFactoryMap ourIsInterfaces = new ConcurrentFactoryMap() { + @Override @NotNull protected Boolean create(final Class key) { return key.isInterface(); } }; private static final ConcurrentFactoryMap ourTypeParameters = new ConcurrentFactoryMap() { + @Override @NotNull protected TypeVariable[] create(final Class key) { return key.getTypeParameters(); } }; private static final ConcurrentFactoryMap ourGenericInterfaces = new ConcurrentFactoryMap() { + @Override @NotNull protected Type[] create(final Class key) { return key.getGenericInterfaces(); } }; private static final ConcurrentFactoryMap ourActualTypeArguments = new ConcurrentFactoryMap() { + @Override @NotNull protected Type[] create(final ParameterizedType key) { return key.getActualTypeArguments(); diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XBreakpointPanelProvider.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XBreakpointPanelProvider.java index 02ca25d275f0..877871da0451 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XBreakpointPanelProvider.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XBreakpointPanelProvider.java @@ -24,6 +24,7 @@ import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.containers.ContainerUtil; import com.intellij.xdebugger.XDebuggerManager; import com.intellij.xdebugger.XDebuggerUtil; import com.intellij.xdebugger.breakpoints.*; @@ -40,14 +41,13 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; /** * @author nik */ public class XBreakpointPanelProvider extends BreakpointPanelProvider { - private List myListeners = new CopyOnWriteArrayList(); + private final List myListeners = ContainerUtil.createEmptyCOWList(); @Override public void createBreakpointsGroupingRules(Collection rules) { @@ -173,7 +173,7 @@ public class XBreakpointPanelProvider extends BreakpointPanelProvider myType; + private final XBreakpointType myType; public AddXBreakpointAction(XBreakpointType type) { myType = type; diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/ui/ExternalizableStringSet.java b/plugins/InspectionGadgets/src/com/siyeh/ig/ui/ExternalizableStringSet.java index 828311aae432..63d69e367aac 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/ui/ExternalizableStringSet.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/ui/ExternalizableStringSet.java @@ -19,6 +19,7 @@ import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.JDOMExternalizable; import com.intellij.openapi.util.WriteExternalException; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.ArrayUtil; import com.intellij.util.containers.OrderedSet; import org.jdom.Element; import org.jetbrains.annotations.NonNls; @@ -48,7 +49,7 @@ public class ExternalizableStringSet extends OrderedSet * note: reference to defaultValues is retained by this set! */ public ExternalizableStringSet(@NonNls String... defaultValues) { - this.defaultValues = defaultValues; + this.defaultValues = defaultValues.length == 0 ? ArrayUtil.EMPTY_STRING_ARRAY : defaultValues; for (String defaultValue : defaultValues) { add(defaultValue); } diff --git a/plugins/groovy/resources/standardDsls/extensions.gdsl b/plugins/groovy/resources/standardDsls/extensions.gdsl index 17a71ec16568..534814cd2e97 100644 --- a/plugins/groovy/resources/standardDsls/extensions.gdsl +++ b/plugins/groovy/resources/standardDsls/extensions.gdsl @@ -17,6 +17,9 @@ package standardDsls +import com.intellij.psi.search.GlobalSearchScope +import org.jetbrains.plugins.groovy.dgm.GroovyExtensionProvider + /** * @author Maxim.Medvedev */ @@ -35,4 +38,12 @@ contributor([:]) { category "org.codehaus.groovy.runtime.SwingGroovyMethods" category "org.codehaus.groovy.runtime.XmlGroovyMethods" + def pair = GroovyExtensionProvider.getInstance(project).collectExtensions(GlobalSearchScope.allScope(project)) + for (def inst : pair.first) { + category inst, false + } + + for (def stat : pair.second) { + category stat, true + } } diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index 13f1dd3e41b5..1d302ffb98a1 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -139,6 +139,10 @@ + + + + @@ -163,6 +167,7 @@ + @@ -290,6 +295,8 @@ + + + + + + diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/dgm/DGMClassReference.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/dgm/DGMClassReference.java new file mode 100644 index 000000000000..12d81532794a --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/dgm/DGMClassReference.java @@ -0,0 +1,95 @@ +/* + * 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 org.jetbrains.plugins.groovy.dgm; + +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.*; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; + +/** + * @author Max Medvedev + */ +public class DGMClassReference implements PsiReference { + private final PsiElement myElement; + private TextRange myRange; + + public DGMClassReference(PsiElement element, int start, int end) { + + myElement = element; + myRange = new TextRange(start, end); + } + + + @Override + public PsiElement getElement() { + return myElement; + } + + @Override + public TextRange getRangeInElement() { + return myRange; + } + + @Override + public PsiElement resolve() { + Project project = myElement.getProject(); + return JavaPsiFacade.getInstance(project).findClass(myRange.substring(myElement.getText()), myElement.getResolveScope()); + } + + @NotNull + @Override + public String getCanonicalText() { + return myRange.substring(myElement.getText()); + } + + @Override + public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException { + if (element instanceof PsiClass) { + String qname = ((PsiClass)element).getQualifiedName(); + if (qname == null) return myElement; + PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myElement.getProject()); + Document document = documentManager.getDocument(myElement.getContainingFile()); + TextRange range = myRange.shiftRight(myElement.getTextRange().getStartOffset()); + document.replaceString(range.getStartOffset(), range.getEndOffset(), qname); + documentManager.commitDocument(document); + } + return myElement; + } + + @Override + public boolean isReferenceTo(PsiElement element) { + return myElement.getManager().areElementsEquivalent(element, resolve()); + } + + @NotNull + @Override + public Object[] getVariants() { + return EMPTY_ARRAY; + } + + @Override + public boolean isSoft() { + return true; + } +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/dgm/DGMCompletionContributor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/dgm/DGMCompletionContributor.java new file mode 100644 index 000000000000..16f71c77b922 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/dgm/DGMCompletionContributor.java @@ -0,0 +1,73 @@ +/* + * 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 org.jetbrains.plugins.groovy.dgm; + +import com.intellij.codeInsight.completion.*; +import com.intellij.codeInsight.lookup.LookupElementBuilder; +import com.intellij.lang.properties.parsing.PropertiesTokenTypes; +import com.intellij.lang.properties.psi.PropertiesFile; +import com.intellij.patterns.PlatformPatterns; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; +import com.intellij.util.Consumer; +import com.intellij.util.ProcessingContext; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.groovy.lang.completion.GroovyCompletionUtil; + +import java.util.Map; + +/** + * @author Max Medvedev + */ +public class DGMCompletionContributor extends CompletionContributor { + public DGMCompletionContributor() { + extend(CompletionType.BASIC, PlatformPatterns.psiElement(PropertiesTokenTypes.KEY_CHARACTERS), + new CompletionProvider() { + @Override + protected void addCompletions(@NotNull CompletionParameters parameters, + ProcessingContext context, + @NotNull CompletionResultSet result) { + PsiElement position = parameters.getPosition(); + if (!DGMUtil.isInDGMFile(position)) return; + + Map map = ((PropertiesFile)position.getContainingFile()).getNamesMap(); + for (String key : DGMUtil.KEYS) { + if (!map.containsKey(key)) { + result.addElement(LookupElementBuilder.create(key)); + } + } + } + }); + + extend(CompletionType.BASIC, PlatformPatterns.psiElement(PropertiesTokenTypes.VALUE_CHARACTERS), + new CompletionProvider() { + @Override + protected void addCompletions(@NotNull CompletionParameters parameters, + ProcessingContext context, + @NotNull final CompletionResultSet result) { + PsiElement position = parameters.getPosition(); + if (!DGMUtil.isInDGMFile(position)) return; + + AllClassesGetter.processJavaClasses(parameters, result.getPrefixMatcher(), true, new Consumer() { + @Override + public void consume(PsiClass aClass) { + result.addElement(GroovyCompletionUtil.createClassLookupItem(aClass)); + } + }); + } + }); + } +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/dgm/DGMFileTypeFactory.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/dgm/DGMFileTypeFactory.java new file mode 100644 index 000000000000..e82c560e4571 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/dgm/DGMFileTypeFactory.java @@ -0,0 +1,36 @@ +/* + * 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 org.jetbrains.plugins.groovy.dgm; + +import com.intellij.lang.properties.PropertiesFileType; +import com.intellij.openapi.fileTypes.ExactFileNameMatcher; +import com.intellij.openapi.fileTypes.FileTypeConsumer; +import com.intellij.openapi.fileTypes.FileTypeFactory; +import com.intellij.openapi.util.SystemInfo; +import org.jetbrains.annotations.NotNull; + +/** + * @author Max Medvedev + */ +public class DGMFileTypeFactory extends FileTypeFactory { + + @Override + public void createFileTypes(@NotNull FileTypeConsumer consumer) { + ExactFileNameMatcher matcher = new ExactFileNameMatcher(GroovyExtensionProvider.ORG_CODEHAUS_GROOVY_RUNTIME_EXTENSION_MODULE, + !SystemInfo.isFileSystemCaseSensitive); + consumer.consume(PropertiesFileType.INSTANCE, matcher); + } +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/dgm/DGMImplicitPropertyUsageProvider.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/dgm/DGMImplicitPropertyUsageProvider.java new file mode 100644 index 000000000000..9fc2820b840a --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/dgm/DGMImplicitPropertyUsageProvider.java @@ -0,0 +1,34 @@ +/* + * 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 org.jetbrains.plugins.groovy.dgm; + +import com.intellij.codeInspection.unused.ImplicitPropertyUsageProvider; +import com.intellij.lang.properties.psi.Property; +import com.intellij.util.ArrayUtil; + +/** + * @author Max Medvedev + */ +public class DGMImplicitPropertyUsageProvider extends ImplicitPropertyUsageProvider { + @Override + protected boolean isUsed(Property property) { + if (DGMUtil.isInDGMFile(property)) { + String name = property.getName(); + return ArrayUtil.find(DGMUtil.KEYS, name) >= 0; + } + return false; + } +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/dgm/DGMReferenceContributor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/dgm/DGMReferenceContributor.java new file mode 100644 index 000000000000..442f3e4caed7 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/dgm/DGMReferenceContributor.java @@ -0,0 +1,80 @@ +/* + * 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 org.jetbrains.plugins.groovy.dgm; + +import com.intellij.lang.properties.IProperty; +import com.intellij.lang.properties.parsing.PropertiesTokenTypes; +import com.intellij.patterns.PlatformPatterns; +import com.intellij.psi.*; +import com.intellij.util.ProcessingContext; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; + +/** + * @author Max Medvedev + */ +public class DGMReferenceContributor extends PsiReferenceContributor { + + @Override + public void registerReferenceProviders(PsiReferenceRegistrar registrar) { + registrar.registerReferenceProvider(PlatformPatterns.psiElement(PropertiesTokenTypes.VALUE_CHARACTERS), new PsiReferenceProvider() { + @NotNull + @Override + public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) { + if (!DGMUtil.isInDGMFile(element)) return PsiReference.EMPTY_ARRAY; + + IProperty parent = (IProperty)element.getParent(); + if (!"extensionClasses".equals(parent.getName())) { + return PsiReference.EMPTY_ARRAY; + } + + ArrayList result = new ArrayList(); + + String text = element.getText(); + + int i = 0; + while ((i = skipWhiteSpace(i, text)) < text.length()) { + int end = findWhiteSpaceOrComma(i, text); + if (end <= text.length()) { + result.add(new DGMClassReference(element, i, end)); + } + i = end; + i = skipWhiteSpace(i, text); + if (i == text.length()) break; + if (text.charAt(i) == ',') i++; + i = skipWhiteSpace(i, text); + } + + return result.toArray(new PsiReference[result.size()]); + } + }); + } + + private static int skipWhiteSpace(int i, String text) { + while (i < text.length() && Character.isWhitespace(text.charAt(i))) { + i++; + } + return i; + } + + private static int findWhiteSpaceOrComma(int i, String text) { + while (i < text.length() && !Character.isWhitespace(text.charAt(i)) && text.charAt(i) != ',') { + i++; + } + return i; + } +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/dgm/DGMUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/dgm/DGMUtil.java new file mode 100644 index 000000000000..af695a38be7c --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/dgm/DGMUtil.java @@ -0,0 +1,36 @@ +/* + * 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 org.jetbrains.plugins.groovy.dgm; + +import com.intellij.lang.properties.psi.PropertiesFile; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; + +/** + * @author Max Medvedev + */ +public class DGMUtil { + public static final String[] KEYS = new String[]{"moduleName", "moduleVersion", "extensionClasses", "staticExtensionClasses",}; + + public static boolean isInDGMFile(PsiElement e) { + PsiFile file = e.getContainingFile(); + return file instanceof PropertiesFile && + Comparing.equal(file.getName(), GroovyExtensionProvider.ORG_CODEHAUS_GROOVY_RUNTIME_EXTENSION_MODULE, + SystemInfo.isFileSystemCaseSensitive); + } +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/dgm/GroovyExtensionProvider.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/dgm/GroovyExtensionProvider.java new file mode 100644 index 000000000000..c4ae7fe067af --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/dgm/GroovyExtensionProvider.java @@ -0,0 +1,80 @@ +/* + * 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 org.jetbrains.plugins.groovy.dgm; + +import com.intellij.lang.properties.IProperty; +import com.intellij.lang.properties.psi.PropertiesFile; +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; +import com.intellij.psi.JavaPsiFacade; +import com.intellij.psi.PsiDirectory; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiPackage; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NonNls; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * @author Max Medvedev + */ +public class GroovyExtensionProvider { + @NonNls public static final String ORG_CODEHAUS_GROOVY_RUNTIME_EXTENSION_MODULE = "org.codehaus.groovy.runtime.ExtensionModule"; + private final Project myProject; + + public GroovyExtensionProvider(Project project) { + myProject = project; + } + + public static GroovyExtensionProvider getInstance(Project project) { + return ServiceManager.getService(project, GroovyExtensionProvider.class); + } + + public Pair, List> collectExtensions(GlobalSearchScope resolveScope) { + PsiPackage aPackage = JavaPsiFacade.getInstance(myProject).findPackage("META-INF.services"); + if (aPackage == null) { + return new Pair, List>(Collections.emptyList(), Collections.emptyList()); + } + + + List instanceClasses = new ArrayList(); + List staticClasses = new ArrayList(); + for (PsiDirectory directory : aPackage.getDirectories(resolveScope)) { + PsiFile file = directory.findFile("org.codehaus.groovy.runtime.ExtensionModule"); + if (file instanceof PropertiesFile) { + IProperty inst = ((PropertiesFile)file).findPropertyByKey("extensionClasses"); + IProperty stat = ((PropertiesFile)file).findPropertyByKey("staticExtensionClasses"); + + if (inst != null) collectClasses(inst, instanceClasses); + if (stat != null) collectClasses(stat, staticClasses); + } + } + + return new Pair, List>(instanceClasses, staticClasses); + } + + private static void collectClasses(IProperty pr, List classes) { + String value = pr.getValue(); + if (value == null) return; + value = value.trim(); + String[] qnames = value.split("\\s*,\\s*"); + ContainerUtil.addAll(classes, qnames); + } +} diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveMethodTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveMethodTest.groovy index a576a32b8805..cc9229809b73 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveMethodTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveMethodTest.groovy @@ -1053,4 +1053,21 @@ class Category2 { assertNotNull(ref.resolve()) } + + void testGroovyExtensions() { + def ref = configureByText('pack._a.groovy', '''\ +package pack + +class StringExt { + static sub(String s) {} +} + +"".sub()''') + + myFixture.addFileToProject("META-INF/services/org.codehaus.groovy.runtime.ExtensionModule", """\ +extensionClasses=pack.StringExt +""") + + assertNotNull(ref.resolve()) + } } diff --git a/plugins/properties/src/META-INF/plugin.xml b/plugins/properties/src/META-INF/plugin.xml index b5053ce9440b..8e152052a51b 100644 --- a/plugins/properties/src/META-INF/plugin.xml +++ b/plugins/properties/src/META-INF/plugin.xml @@ -6,6 +6,11 @@ This plugin enables smart editing of properties files. JetBrains + + + + + + implementationClass="com.intellij.codeInspection.unused.UnusedPropertyInspection"/> diff --git a/plugins/properties/src/com/intellij/codeInspection/unused/ImplicitPropertyUsageProvider.java b/plugins/properties/src/com/intellij/codeInspection/unused/ImplicitPropertyUsageProvider.java new file mode 100644 index 000000000000..ee0b9f593638 --- /dev/null +++ b/plugins/properties/src/com/intellij/codeInspection/unused/ImplicitPropertyUsageProvider.java @@ -0,0 +1,36 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInspection.unused; + +import com.intellij.lang.properties.psi.Property; +import com.intellij.openapi.extensions.ExtensionPointName; + +/** + * @author Max Medvedev + */ +public abstract class ImplicitPropertyUsageProvider { + private static final ExtensionPointName EP_NAME = + ExtensionPointName.create("com.intellij.properties.implicitPropertyUsageProvider"); + + public static boolean isImplicitlyUsed(Property property) { + for (ImplicitPropertyUsageProvider provider : EP_NAME.getExtensions()) { + if (provider.isUsed(property)) return true; + } + return false; + } + + protected abstract boolean isUsed(Property property); +} diff --git a/plugins/properties/src/com/intellij/lang/properties/UnusedPropertyInspection.java b/plugins/properties/src/com/intellij/codeInspection/unused/UnusedPropertyInspection.java similarity index 92% rename from plugins/properties/src/com/intellij/lang/properties/UnusedPropertyInspection.java rename to plugins/properties/src/com/intellij/codeInspection/unused/UnusedPropertyInspection.java index 0166a7c989f0..86d58988e5fe 100644 --- a/plugins/properties/src/com/intellij/lang/properties/UnusedPropertyInspection.java +++ b/plugins/properties/src/com/intellij/codeInspection/unused/UnusedPropertyInspection.java @@ -13,12 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.intellij.lang.properties; +package com.intellij.codeInspection.unused; import com.intellij.codeInspection.LocalInspectionToolSession; import com.intellij.codeInspection.ProblemHighlightType; import com.intellij.codeInspection.ProblemsHolder; import com.intellij.lang.ASTNode; +import com.intellij.lang.properties.PropertiesBundle; +import com.intellij.lang.properties.PropertySuppressableInspectionBase; +import com.intellij.lang.properties.RemovePropertyLocalFix; import com.intellij.lang.properties.findUsages.PropertySearcher; import com.intellij.lang.properties.psi.Property; import com.intellij.openapi.extensions.Extensions; @@ -75,6 +78,8 @@ public class UnusedPropertyInspection extends PropertySuppressableInspectionBase original.setText(PropertiesBundle.message("searching.for.property.key.progress.text", property.getUnescapedKey())); } + if (ImplicitPropertyUsageProvider.isImplicitlyUsed(property)) return; + String name = property.getName(); if (name == null) return; if (searcher != null) { @@ -97,7 +102,7 @@ public class UnusedPropertyInspection extends PropertySuppressableInspectionBase PsiElement key = nodes.length == 0 ? property : nodes[0].getPsi(); String description = PropertiesBundle.message("unused.property.problem.descriptor.name"); - holder.registerProblem(key, description, ProblemHighlightType.LIKE_UNUSED_SYMBOL,RemovePropertyLocalFix.INSTANCE); + holder.registerProblem(key, description, ProblemHighlightType.LIKE_UNUSED_SYMBOL, RemovePropertyLocalFix.INSTANCE); } }; } diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/engine/BaseSpellChecker.java b/plugins/spellchecker/src/com/intellij/spellchecker/engine/BaseSpellChecker.java index ac3b703bb513..091237bb2b9a 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/engine/BaseSpellChecker.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/engine/BaseSpellChecker.java @@ -33,6 +33,7 @@ import com.intellij.spellchecker.dictionary.EditableDictionary; import com.intellij.spellchecker.dictionary.EditableDictionaryLoader; import com.intellij.spellchecker.dictionary.Loader; import com.intellij.util.Consumer; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.UIUtil; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; @@ -49,11 +50,11 @@ public class BaseSpellChecker implements SpellCheckerEngine { private final Transformation transform = new Transformation(); private final Set dictionaries = new THashSet(); - private final List bundledDictionaries = new CopyOnWriteArrayList(); + private final List bundledDictionaries = ContainerUtil.createEmptyCOWList(); private final Metrics metrics = new LevenshteinDistance(); private AtomicBoolean myLoadingDictionaries = new AtomicBoolean(false); - private List>> myDictionariesToLoad = new CopyOnWriteArrayList>>(); + private List>> myDictionariesToLoad = ContainerUtil.createEmptyCOWList(); private Project myProject; public BaseSpellChecker(final Project project) {