diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaUtil.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaUtil.java index 1bcbe7ae9380..99588b50c8c9 100644 --- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaUtil.java +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaUtil.java @@ -143,7 +143,6 @@ public class DfaUtil { return codeBlock; } - @Nullable("null means DFA analysis has failed (too complex to analyze)") public static Collection getPossibleInitializationElements(final PsiElement qualifierExpression) { if (qualifierExpression instanceof PsiMethodCallExpression) { return Collections.singletonList(qualifierExpression); @@ -152,8 +151,7 @@ public class DfaUtil { final PsiElement targetElement = ((PsiReferenceExpression)qualifierExpression).resolve(); if (targetElement instanceof PsiVariable) { final Collection variableValues = getCachedVariableValues((PsiVariable)targetElement, qualifierExpression); - if (variableValues == null) return null; - if (variableValues.isEmpty() && targetElement instanceof PsiField) { + if ((variableValues == null || variableValues.isEmpty())) { return getVariableAssignmentsInFile((PsiVariable)targetElement, false); } return variableValues; diff --git a/java/java-impl/src/com/intellij/ide/JavaLanguageCodeStyleSettingsProvider.java b/java/java-impl/src/com/intellij/ide/JavaLanguageCodeStyleSettingsProvider.java index 823e663f284b..858faf1254c2 100644 --- a/java/java-impl/src/com/intellij/ide/JavaLanguageCodeStyleSettingsProvider.java +++ b/java/java-impl/src/com/intellij/ide/JavaLanguageCodeStyleSettingsProvider.java @@ -18,6 +18,7 @@ package com.intellij.ide; import com.intellij.application.options.codeStyle.LanguageCodeStyleSettingsProvider; import com.intellij.lang.Language; import com.intellij.lang.StdLanguages; +import com.intellij.psi.codeStyle.CodeStyleCustomizationsConsumer; import org.jetbrains.annotations.NotNull; /** @@ -44,6 +45,17 @@ public class JavaLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSett return GENERAL_CODE_SAMPLE; } + @Override + public void customizeSpacingOptions(CodeStyleCustomizationsConsumer consumer) { + consumer.showAllStandardOptions(); + } + + @Override + public void customizeBlankLinesOptions(CodeStyleCustomizationsConsumer consumer) { + consumer.showAllStandardOptions(); + } + + private static final String GENERAL_CODE_SAMPLE = "public class Foo {\n" + " public int[] X = new int[]{1, 3, 5 7, 9, 11};\n" + "\n" + diff --git a/java/java-impl/src/com/intellij/psi/impl/source/javadoc/PsiDocParamRef.java b/java/java-impl/src/com/intellij/psi/impl/source/javadoc/PsiDocParamRef.java index d1ed0fbe0b06..d46e566e3b99 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/javadoc/PsiDocParamRef.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/javadoc/PsiDocParamRef.java @@ -24,6 +24,7 @@ import com.intellij.psi.impl.source.tree.Factory; import com.intellij.psi.impl.source.tree.LeafElement; import com.intellij.psi.impl.source.tree.SharedImplUtil; import com.intellij.psi.infos.CandidateInfo; +import com.intellij.psi.javadoc.PsiDocComment; import com.intellij.psi.javadoc.PsiDocTagValue; import com.intellij.psi.javadoc.PsiDocToken; import com.intellij.psi.scope.PsiScopeProcessor; @@ -50,7 +51,9 @@ public class PsiDocParamRef extends CompositePsiElement implements PsiDocTagValu public PsiReference getReference() { PsiReference cachedReference = myCachedReference; if (cachedReference != null) return cachedReference; - final PsiDocCommentOwner owner = PsiTreeUtil.getParentOfType(this, PsiDocCommentOwner.class); + final PsiDocComment comment = PsiTreeUtil.getParentOfType(this, PsiDocComment.class); + if (comment == null) return null; + final PsiDocCommentOwner owner = comment.getOwner(); if (!(owner instanceof PsiMethod) && !(owner instanceof PsiClass)) return null; final ASTNode valueToken = findChildByType(JavaDocTokenType.DOC_TAG_VALUE_TOKEN); diff --git a/platform/lang-api/src/com/intellij/lang/ASTNode.java b/platform/lang-api/src/com/intellij/lang/ASTNode.java index 0734ac0c6b2e..78f70fcc5bed 100644 --- a/platform/lang-api/src/com/intellij/lang/ASTNode.java +++ b/platform/lang-api/src/com/intellij/lang/ASTNode.java @@ -108,16 +108,16 @@ public interface ASTNode extends UserDataHolder { ASTNode getLastChildNode(); /** - * Returns the previous sibling of this node in the tree. + * Returns the next sibling of this node in the tree. * - * @return the previous sibling node. + * @return the next sibling node. */ ASTNode getTreeNext(); /** - * Returns the next sibling of this node in the tree. + * Returns the previous sibling of this node in the tree. * - * @return the next sibling node. + * @return the previous sibling node. */ ASTNode getTreePrev(); diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleCustomizationsConsumer.java b/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleCustomizationsConsumer.java new file mode 100644 index 000000000000..28f74d7581ea --- /dev/null +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleCustomizationsConsumer.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.codeStyle; + +import com.intellij.openapi.application.ApplicationBundle; + +/** + * @author yole + */ +public interface CodeStyleCustomizationsConsumer { + void showAllStandardOptions(); + void showStandardOptions(String... optionNames); + void showCustomOption(Class settingsClass, String fieldName, String optionName, String groupName); + + String AROUND_OPERATORS = ApplicationBundle.message("group.spaces.around.operators"); + String BEFORE_PARENTHESES = ApplicationBundle.message("group.spaces.before.parentheses"); + String BEFORE_LEFT_BRACE = ApplicationBundle.message("group.spaces.before.left.brace"); + String WITHIN_PARENTHESES = ApplicationBundle.message("group.spaces.within.parentheses"); + String TERNARY_OPERATOR = ApplicationBundle.message("group.spaces.in.ternary.operator"); + String TYPE_ARGUMENTS = ApplicationBundle.message("group.spaces.in.type.arguments"); + String OTHER = ApplicationBundle.message("group.spaces.other"); +} diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettingsProvider.java b/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettingsProvider.java index 446067dec7ff..a154a764d6e1 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettingsProvider.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettingsProvider.java @@ -44,4 +44,8 @@ public abstract class CodeStyleSettingsProvider { public String getConfigurableDisplayName() { return null; } + + public boolean hasSettingsPage() { + return true; + } } diff --git a/platform/lang-impl/src/com/intellij/application/options/CodeStyleSchemesConfigurable.java b/platform/lang-impl/src/com/intellij/application/options/CodeStyleSchemesConfigurable.java index def93b6a2a80..b63e5b5d7d6a 100644 --- a/platform/lang-impl/src/com/intellij/application/options/CodeStyleSchemesConfigurable.java +++ b/platform/lang-impl/src/com/intellij/application/options/CodeStyleSchemesConfigurable.java @@ -208,11 +208,13 @@ public class CodeStyleSchemesConfigurable extends SearchableConfigurable.Parent. myPanels = new ArrayList(); for (final CodeStyleSettingsProvider provider : Extensions.getExtensions(CodeStyleSettingsProvider.EXTENSION_POINT_NAME)) { - myPanels.add(new CodeStyleConfigurableWrapper(provider, new CodeStyleSettingsPanelFactory() { - public NewCodeStyleSettingsPanel createPanel(final CodeStyleScheme scheme) { - return new NewCodeStyleSettingsPanel(provider.createSettingsPage(scheme.getCodeStyleSettings(), ensureModel().getCloneSettings(scheme))); - } - })); + if (provider.hasSettingsPage()) { + myPanels.add(new CodeStyleConfigurableWrapper(provider, new CodeStyleSettingsPanelFactory() { + public NewCodeStyleSettingsPanel createPanel(final CodeStyleScheme scheme) { + return new NewCodeStyleSettingsPanel(provider.createSettingsPage(scheme.getCodeStyleSettings(), ensureModel().getCloneSettings(scheme))); + } + })); + } } return myPanels.toArray(new Configurable[myPanels.size()]); diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/CodeStyleBlankLinesPanel.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/CodeStyleBlankLinesPanel.java index 6b5e17dff008..b5c1e73da957 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/CodeStyleBlankLinesPanel.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/CodeStyleBlankLinesPanel.java @@ -16,34 +16,36 @@ package com.intellij.application.options.codeStyle; import com.intellij.openapi.application.ApplicationBundle; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.extensions.Extensions; import com.intellij.psi.PsiFile; +import com.intellij.psi.codeStyle.CodeStyleCustomizationsConsumer; import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.codeStyle.CustomCodeStyleSettings; import com.intellij.ui.OptionGroup; import javax.swing.*; import java.awt.*; +import java.lang.reflect.Field; +import java.util.*; +import java.util.List; -public class CodeStyleBlankLinesPanel extends MultilanguageCodeStyleAbstractPanel { - private JTextField myKeepBlankLinesInDeclarations; - private JTextField myKeepBlankLinesInCode; - private JTextField myBlankLinesBeforePackage; - private JTextField myBlankLinesAfterPackage; - private JTextField myBlankLinesBeforeImports; - private JTextField myBlankLinesAfterImports; - private JTextField myBlankLinesAroundClass; - private JTextField myBlankLinesAroundField; - private JTextField myBlankLinesAroundMethod; - private JTextField myBlankLinesBeforeMethodBody; - private JTextField myBlankLinesAroundFieldI; - private JTextField myBlankLinesAroundMethodI; - private JTextField myBlankLinesAfterClassHeader; - private JTextField myKeepBlankLinesBeforeRBrace; +public class CodeStyleBlankLinesPanel extends MultilanguageCodeStyleAbstractPanel implements CodeStyleCustomizationsConsumer { + private static final Logger LOG = Logger.getInstance("#com.intellij.application.options.codeStyle.CodeStyleBlankLinesPanel"); + + private List myOptions = new ArrayList(); + private Set myAllowedOptions = new HashSet(); + private boolean myAllOptionsAllowed = false; private final JPanel myPanel = new JPanel(new GridBagLayout()); public CodeStyleBlankLinesPanel(CodeStyleSettings settings) { super(settings); + for(LanguageCodeStyleSettingsProvider provider: Extensions.getExtensions(LanguageCodeStyleSettingsProvider.EP_NAME)) { + provider.customizeBlankLinesOptions(this); + } + myPanel .add(createKeepBlankLinesPanel(), new GridBagConstraints(0, 0, 1, 1, 0, 0, GridBagConstraints.NORTH, GridBagConstraints.HORIZONTAL, new Insets(0, 4, 0, 4), 0, 0)); @@ -69,38 +71,17 @@ public class CodeStyleBlankLinesPanel extends MultilanguageCodeStyleAbstractPane private JPanel createBlankLinesPanel() { OptionGroup optionGroup = new OptionGroup(ApplicationBundle.message("title.blank.lines")); - myBlankLinesBeforePackage = createTextField(); - optionGroup.add(new JLabel(ApplicationBundle.message("editbox.blanklines.before.package.statement")), myBlankLinesBeforePackage); - - myBlankLinesAfterPackage = createTextField(); - optionGroup.add(new JLabel(ApplicationBundle.message("editbox.blanklines.after.package.statement")), myBlankLinesAfterPackage); - - myBlankLinesBeforeImports = createTextField(); - optionGroup.add(new JLabel(ApplicationBundle.message("editbox.blanklines.before.imports")), myBlankLinesBeforeImports); - - myBlankLinesAfterImports = createTextField(); - optionGroup.add(new JLabel(ApplicationBundle.message("editbox.blanklines.after.imports")), myBlankLinesAfterImports); - - myBlankLinesAroundClass = createTextField(); - optionGroup.add(new JLabel(ApplicationBundle.message("editbox.blanklines.around.class")), myBlankLinesAroundClass); - - myBlankLinesAroundField = createTextField(); - optionGroup.add(new JLabel(ApplicationBundle.message("editbox.blanklines.around.field")), myBlankLinesAroundField); - - myBlankLinesAroundMethod = createTextField(); - optionGroup.add(new JLabel(ApplicationBundle.message("editbox.blanklines.around.method")), myBlankLinesAroundMethod); - - myBlankLinesBeforeMethodBody = createTextField(); - optionGroup.add(new JLabel(ApplicationBundle.message("editbox.blanklines.before.method.body")), myBlankLinesBeforeMethodBody); - - myBlankLinesAroundFieldI = createTextField(); - optionGroup.add(new JLabel("Around field in interface:"), myBlankLinesAroundFieldI); - - myBlankLinesAroundMethodI = createTextField(); - optionGroup.add(new JLabel("Around method in interface:"), myBlankLinesAroundMethodI); - - myBlankLinesAfterClassHeader = createTextField(); - optionGroup.add(new JLabel(ApplicationBundle.message("editbox.blanklines.after.class.header")), myBlankLinesAfterClassHeader); + createOption(optionGroup, ApplicationBundle.message("editbox.blanklines.before.package.statement"), "BLANK_LINES_BEFORE_PACKAGE"); + createOption(optionGroup, ApplicationBundle.message("editbox.blanklines.after.package.statement"), "BLANK_LINES_AFTER_PACKAGE"); + createOption(optionGroup, ApplicationBundle.message("editbox.blanklines.before.imports"), "BLANK_LINES_BEFORE_IMPORTS"); + createOption(optionGroup, ApplicationBundle.message("editbox.blanklines.after.imports"), "BLANK_LINES_AFTER_IMPORTS"); + createOption(optionGroup, ApplicationBundle.message("editbox.blanklines.around.class"), "BLANK_LINES_AROUND_CLASS"); + createOption(optionGroup, ApplicationBundle.message("editbox.blanklines.around.field"), "BLANK_LINES_AROUND_FIELD"); + createOption(optionGroup, ApplicationBundle.message("editbox.blanklines.around.method"), "BLANK_LINES_AROUND_METHOD"); + createOption(optionGroup, ApplicationBundle.message("editbox.blanklines.before.method.body"), "BLANK_LINES_BEFORE_METHOD_BODY"); + createOption(optionGroup, "Around field in interface:", "BLANK_LINES_AROUND_FIELD_IN_INTERFACE"); + createOption(optionGroup, "Around method in interface:", "BLANK_LINES_AROUND_METHOD_IN_INTERFACE"); + createOption(optionGroup, ApplicationBundle.message("editbox.blanklines.after.class.header"), "BLANK_LINES_AFTER_CLASS_HEADER"); return optionGroup.createPanel(); } @@ -108,97 +89,41 @@ public class CodeStyleBlankLinesPanel extends MultilanguageCodeStyleAbstractPane private JPanel createKeepBlankLinesPanel() { OptionGroup optionGroup = new OptionGroup(ApplicationBundle.message("title.keep.blank.lines")); - myKeepBlankLinesInDeclarations = createTextField(); - optionGroup.add(new JLabel(ApplicationBundle.message("editbox.keep.blanklines.in.declarations")), myKeepBlankLinesInDeclarations); - - myKeepBlankLinesInCode = createTextField(); - optionGroup.add(new JLabel(ApplicationBundle.message("editbox.keep.blanklines.in.code")), myKeepBlankLinesInCode); - - myKeepBlankLinesBeforeRBrace = createTextField(); - optionGroup.add(new JLabel(ApplicationBundle.message("editbox.keep.blanklines.before.rbrace")), myKeepBlankLinesBeforeRBrace); + createOption(optionGroup, ApplicationBundle.message("editbox.keep.blanklines.in.declarations"), "KEEP_BLANK_LINES_IN_DECLARATIONS"); + createOption(optionGroup, ApplicationBundle.message("editbox.keep.blanklines.in.code"), "KEEP_BLANK_LINES_IN_CODE"); + createOption(optionGroup, ApplicationBundle.message("editbox.keep.blanklines.before.rbrace"), "KEEP_BLANK_LINES_BEFORE_RBRACE"); return optionGroup.createPanel(); } - protected void resetImpl(final CodeStyleSettings settings) { - myKeepBlankLinesInDeclarations.setText(String.valueOf(settings.KEEP_BLANK_LINES_IN_DECLARATIONS)); - myKeepBlankLinesInCode.setText(String.valueOf(settings.KEEP_BLANK_LINES_IN_CODE)); - myKeepBlankLinesBeforeRBrace.setText(String.valueOf(settings.KEEP_BLANK_LINES_BEFORE_RBRACE)); - myBlankLinesBeforePackage.setText(String.valueOf(settings.BLANK_LINES_BEFORE_PACKAGE)); - myBlankLinesAfterPackage.setText(String.valueOf(settings.BLANK_LINES_AFTER_PACKAGE)); - myBlankLinesBeforeImports.setText(String.valueOf(settings.BLANK_LINES_BEFORE_IMPORTS)); - myBlankLinesAfterImports.setText(String.valueOf(settings.BLANK_LINES_AFTER_IMPORTS)); - myBlankLinesAroundClass.setText(String.valueOf(settings.BLANK_LINES_AROUND_CLASS)); - myBlankLinesAroundField.setText(String.valueOf(settings.BLANK_LINES_AROUND_FIELD)); - myBlankLinesAroundMethod.setText(String.valueOf(settings.BLANK_LINES_AROUND_METHOD)); - myBlankLinesBeforeMethodBody.setText(String.valueOf(settings.BLANK_LINES_BEFORE_METHOD_BODY)); - myBlankLinesAroundFieldI.setText(String.valueOf(settings.BLANK_LINES_AROUND_FIELD_IN_INTERFACE)); - myBlankLinesAroundMethodI.setText(String.valueOf(settings.BLANK_LINES_AROUND_METHOD_IN_INTERFACE)); - myBlankLinesAfterClassHeader.setText(String.valueOf(settings.BLANK_LINES_AFTER_CLASS_HEADER)); + private void createOption(OptionGroup optionGroup, String label, String fieldName) { + if (myAllOptionsAllowed || myAllowedOptions.contains(fieldName)) { + IntOption option = new IntOption(CodeStyleSettings.class, fieldName); + optionGroup.add(new JLabel(label), option.myTextField); + myOptions.add(option); + } + } + protected void resetImpl(final CodeStyleSettings settings) { + for (IntOption option : myOptions) { + option.setValue(option.getFieldValue(settings)); + } } public void apply(CodeStyleSettings settings) { - settings.KEEP_BLANK_LINES_IN_DECLARATIONS = getValue(myKeepBlankLinesInDeclarations); - settings.KEEP_BLANK_LINES_IN_CODE = getValue(myKeepBlankLinesInCode); - settings.KEEP_BLANK_LINES_BEFORE_RBRACE = getValue(myKeepBlankLinesBeforeRBrace); - settings.BLANK_LINES_BEFORE_PACKAGE = getValue(myBlankLinesBeforePackage); - settings.BLANK_LINES_AFTER_PACKAGE = getValue(myBlankLinesAfterPackage); - settings.BLANK_LINES_BEFORE_IMPORTS = getValue(myBlankLinesBeforeImports); - settings.BLANK_LINES_AFTER_IMPORTS = getValue(myBlankLinesAfterImports); - settings.BLANK_LINES_AROUND_CLASS = getValue(myBlankLinesAroundClass); - settings.BLANK_LINES_AROUND_FIELD = getValue(myBlankLinesAroundField); - settings.BLANK_LINES_AROUND_METHOD = getValue(myBlankLinesAroundMethod); - settings.BLANK_LINES_BEFORE_METHOD_BODY = getValue(myBlankLinesBeforeMethodBody); - - settings.BLANK_LINES_AROUND_FIELD_IN_INTERFACE = getValue(myBlankLinesAroundFieldI); - settings.BLANK_LINES_AROUND_METHOD_IN_INTERFACE = getValue(myBlankLinesAroundMethodI); - - settings.BLANK_LINES_AFTER_CLASS_HEADER = getValue(myBlankLinesAfterClassHeader); - + for (IntOption option : myOptions) { + option.setFieldValue(settings, option.getValue()); + } } public boolean isModified(CodeStyleSettings settings) { - boolean isModified; - isModified = settings.KEEP_BLANK_LINES_IN_DECLARATIONS != getValue(myKeepBlankLinesInDeclarations); - isModified |= settings.KEEP_BLANK_LINES_IN_CODE != getValue(myKeepBlankLinesInCode); - isModified |= settings.KEEP_BLANK_LINES_BEFORE_RBRACE != getValue(myKeepBlankLinesBeforeRBrace); - isModified |= settings.BLANK_LINES_BEFORE_PACKAGE != getValue(myBlankLinesBeforePackage); - isModified |= settings.BLANK_LINES_AFTER_PACKAGE != getValue(myBlankLinesAfterPackage); - isModified |= settings.BLANK_LINES_BEFORE_IMPORTS != getValue(myBlankLinesBeforeImports); - isModified |= settings.BLANK_LINES_AFTER_IMPORTS != getValue(myBlankLinesAfterImports); - isModified |= settings.BLANK_LINES_AROUND_CLASS != getValue(myBlankLinesAroundClass); - isModified |= settings.BLANK_LINES_AROUND_FIELD != getValue(myBlankLinesAroundField); - isModified |= settings.BLANK_LINES_AROUND_METHOD != getValue(myBlankLinesAroundMethod); - isModified |= settings.BLANK_LINES_BEFORE_METHOD_BODY != getValue(myBlankLinesBeforeMethodBody); - isModified |= settings.BLANK_LINES_AROUND_FIELD_IN_INTERFACE != getValue(myBlankLinesAroundFieldI); - isModified |= settings.BLANK_LINES_AROUND_METHOD_IN_INTERFACE != getValue(myBlankLinesAroundMethodI); - isModified |= settings.BLANK_LINES_AFTER_CLASS_HEADER != getValue(myBlankLinesAfterClassHeader); - return isModified; - - } - - private static int getValue(JTextField textField) { - int ret = 0; - try { - ret = Integer.parseInt(textField.getText()); - if (ret < 0) { - ret = 0; - } - if (ret > 10) { - ret = 10; + for (IntOption option : myOptions) { + if (option.getFieldValue(settings) != option.getValue()) { + return true; } } - catch (NumberFormatException e) { - //bad number entered - } - return ret; - } + return false; - private static JTextField createTextField() { - JTextField field = new JTextField(6); - field.setMinimumSize(new Dimension(30, field.getMinimumSize().height)); - return field; } protected int getRightMargin() { @@ -212,4 +137,75 @@ public class CodeStyleBlankLinesPanel extends MultilanguageCodeStyleAbstractPane protected void prepareForReformat(final PsiFile psiFile) { //psiFile.putUserData(PsiUtil.FILE_LANGUAGE_LEVEL_KEY, LanguageLevel.HIGHEST); } + + public void showAllStandardOptions() { + myAllOptionsAllowed = true; + } + + public void showStandardOptions(String... optionNames) { + Collections.addAll(myAllowedOptions, optionNames); + } + + public void showCustomOption(Class settingsClass, + String fieldName, + String optionName, + String groupName) { + throw new UnsupportedOperationException(); + } + + private static class IntOption { + private final JTextField myTextField; + private final Field myTarget; + + private IntOption(Class targetClass, String fieldName) { + try { + myTarget = targetClass.getField(fieldName); + } + catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } + myTextField = new JTextField(6); + myTextField.setMinimumSize(new Dimension(30, myTextField.getMinimumSize().height)); + } + + private int getFieldValue(CodeStyleSettings settings) { + try { + return myTarget.getInt(settings); + } + catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + public void setFieldValue(CodeStyleSettings settings, int value) { + try { + myTarget.setInt(settings, value); + } + catch (IllegalAccessException e) { + LOG.error(e); + } + } + + private int getValue() { + int ret = 0; + try { + ret = Integer.parseInt(myTextField.getText()); + if (ret < 0) { + ret = 0; + } + if (ret > 10) { + ret = 10; + } + } + catch (NumberFormatException e) { + //bad number entered + } + return ret; + } + + public void setValue(int fieldValue) { + myTextField.setText(String.valueOf(fieldValue)); + } + } + } \ No newline at end of file diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/CodeStyleSpacesPanel.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/CodeStyleSpacesPanel.java index a66e94093485..abfd213b5cc4 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/CodeStyleSpacesPanel.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/CodeStyleSpacesPanel.java @@ -16,13 +16,24 @@ package com.intellij.application.options.codeStyle; import com.intellij.openapi.application.ApplicationBundle; -//import com.intellij.openapi.editor.Editor; -//import com.intellij.openapi.editor.EditorSettings; +import com.intellij.openapi.extensions.Extensions; +import com.intellij.openapi.util.Trinity; import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.codeStyle.CustomCodeStyleSettings; +import com.intellij.psi.codeStyle.CodeStyleCustomizationsConsumer; +import com.intellij.util.containers.MultiMap; +import org.jetbrains.annotations.NonNls; import javax.swing.*; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +public class CodeStyleSpacesPanel extends OptionTreeWithPreviewPanel implements CodeStyleCustomizationsConsumer { + private boolean myShowAllStandardOptions = false; + private Set myAllowedOptions; + private MultiMap, String, String>> myCustomOptions; -public class CodeStyleSpacesPanel extends OptionTreeWithPreviewPanel { public CodeStyleSpacesPanel(CodeStyleSettings settings) { super(settings); } @@ -32,15 +43,13 @@ public class CodeStyleSpacesPanel extends OptionTreeWithPreviewPanel { return LanguageCodeStyleSettingsProvider.SettingsType.SPACING_SETTINGS; } - private static final String AROUND_OPERATORS = ApplicationBundle.message("group.spaces.around.operators"); - private static final String BEFORE_PARENTHESES = ApplicationBundle.message("group.spaces.before.parentheses"); - private static final String BEFORE_LEFT_BRACE = ApplicationBundle.message("group.spaces.before.left.brace"); - private static final String WITHIN_PARENTHESES = ApplicationBundle.message("group.spaces.within.parentheses"); - private static final String TERNARY_OPERATOR = ApplicationBundle.message("group.spaces.in.ternary.operator"); - private static final String TYPE_ARGUMENTS = ApplicationBundle.message("group.spaces.in.type.arguments"); - private static final String OTHER = ApplicationBundle.message("group.spaces.other"); - protected void initTables() { + myAllowedOptions = new HashSet(); + myCustomOptions = new MultiMap, String, String>>(); + for(LanguageCodeStyleSettingsProvider provider: Extensions.getExtensions(LanguageCodeStyleSettingsProvider.EP_NAME)) { + provider.customizeSpacingOptions(this); + } + initBooleanField("SPACE_BEFORE_METHOD_CALL_PARENTHESES", ApplicationBundle.message("checkbox.spaces.method.call.parentheses"), BEFORE_PARENTHESES); initBooleanField("SPACE_BEFORE_METHOD_PARENTHESES", ApplicationBundle.message("checkbox.spaces.method.declaration.parentheses"), BEFORE_PARENTHESES); initBooleanField("SPACE_BEFORE_IF_PARENTHESES", ApplicationBundle.message("checkbox.spaces.if.parentheses"), BEFORE_PARENTHESES); @@ -50,6 +59,7 @@ public class CodeStyleSpacesPanel extends OptionTreeWithPreviewPanel { initBooleanField("SPACE_BEFORE_SWITCH_PARENTHESES", ApplicationBundle.message("checkbox.spaces.switch.parentheses"), BEFORE_PARENTHESES); initBooleanField("SPACE_BEFORE_SYNCHRONIZED_PARENTHESES", ApplicationBundle.message("checkbox.spaces.synchronized.parentheses"), BEFORE_PARENTHESES); initBooleanField("SPACE_BEFORE_ANOTATION_PARAMETER_LIST", ApplicationBundle.message("checkbox.spaces.annotation.parameters"), BEFORE_PARENTHESES); + initCustomOptions(BEFORE_PARENTHESES); initBooleanField("SPACE_AROUND_ASSIGNMENT_OPERATORS", ApplicationBundle.message("checkbox.spaces.assignment.operators"), AROUND_OPERATORS); initBooleanField("SPACE_AROUND_LOGICAL_OPERATORS", ApplicationBundle.message("checkbox.spaces.logical.operators"), AROUND_OPERATORS); @@ -59,6 +69,7 @@ public class CodeStyleSpacesPanel extends OptionTreeWithPreviewPanel { initBooleanField("SPACE_AROUND_ADDITIVE_OPERATORS", ApplicationBundle.message("checkbox.spaces.additive.operators"), AROUND_OPERATORS); initBooleanField("SPACE_AROUND_MULTIPLICATIVE_OPERATORS", ApplicationBundle.message("checkbox.spaces.multiplicative.operators"), AROUND_OPERATORS); initBooleanField("SPACE_AROUND_SHIFT_OPERATORS", ApplicationBundle.message("checkbox.spaces.shift.operators"), AROUND_OPERATORS); + initCustomOptions(AROUND_OPERATORS); initBooleanField("SPACE_BEFORE_CLASS_LBRACE", ApplicationBundle.message("checkbox.spaces.class.left.brace"), BEFORE_LEFT_BRACE); initBooleanField("SPACE_BEFORE_METHOD_LBRACE", ApplicationBundle.message("checkbox.spaces.method.left.brace"), BEFORE_LEFT_BRACE); @@ -73,6 +84,7 @@ public class CodeStyleSpacesPanel extends OptionTreeWithPreviewPanel { initBooleanField("SPACE_BEFORE_FINALLY_LBRACE", ApplicationBundle.message("checkbox.spaces.finally.left.brace"), BEFORE_LEFT_BRACE); initBooleanField("SPACE_BEFORE_SYNCHRONIZED_LBRACE", ApplicationBundle.message("checkbox.spaces.synchronized.left.brace"), BEFORE_LEFT_BRACE); initBooleanField("SPACE_BEFORE_ARRAY_INITIALIZER_LBRACE", ApplicationBundle.message("checkbox.spaces.array.initializer.left.brace"), BEFORE_LEFT_BRACE); + initCustomOptions(BEFORE_LEFT_BRACE); initBooleanField("SPACE_WITHIN_PARENTHESES", ApplicationBundle.message("checkbox.spaces.parentheses"), WITHIN_PARENTHESES); initBooleanField("SPACE_WITHIN_METHOD_CALL_PARENTHESES", ApplicationBundle.message("checkbox.spaces.checkbox.spaces.method.call.parentheses"), WITHIN_PARENTHESES); @@ -85,15 +97,18 @@ public class CodeStyleSpacesPanel extends OptionTreeWithPreviewPanel { initBooleanField("SPACE_WITHIN_SYNCHRONIZED_PARENTHESES", ApplicationBundle.message("checkbox.spaces.synchronized.parentheses"), WITHIN_PARENTHESES); initBooleanField("SPACE_WITHIN_CAST_PARENTHESES", ApplicationBundle.message("checkbox.spaces.type.cast.parentheses"), WITHIN_PARENTHESES); initBooleanField("SPACE_WITHIN_ANNOTATION_PARENTHESES", ApplicationBundle.message("checkbox.spaces.annotation.parentheses"), WITHIN_PARENTHESES); + initCustomOptions(WITHIN_PARENTHESES); initBooleanField("SPACE_BEFORE_QUEST", ApplicationBundle.message("checkbox.spaces.before.question"), TERNARY_OPERATOR); initBooleanField("SPACE_AFTER_QUEST", ApplicationBundle.message("checkbox.spaces.after.question"), TERNARY_OPERATOR); initBooleanField("SPACE_BEFORE_COLON", ApplicationBundle.message("checkbox.spaces.before.colon"), TERNARY_OPERATOR); initBooleanField("SPACE_AFTER_COLON", ApplicationBundle.message("checkbox.spaces.after.colon"), TERNARY_OPERATOR); + initCustomOptions(TERNARY_OPERATOR); initBooleanField("SPACE_AFTER_COMMA_IN_TYPE_ARGUMENTS", ApplicationBundle.message("checkbox.spaces.after.comma"), TYPE_ARGUMENTS); + initCustomOptions(TYPE_ARGUMENTS); - initBooleanField("SPACE_AFTER_LABEL", ApplicationBundle.message("checkbox.spaces.after.colon.in.label.declaration"), OTHER); + //TODO looks like this option is never implemented: initBooleanField("SPACE_AFTER_LABEL", ApplicationBundle.message("checkbox.spaces.after.colon.in.label.declaration"), OTHER); initBooleanField("SPACE_WITHIN_BRACKETS", ApplicationBundle.message("checkbox.spaces.within.brackets"), OTHER); initBooleanField("SPACE_WITHIN_ARRAY_INITIALIZER_BRACES", ApplicationBundle.message("checkbox.spaces.within.array.initializer.braces"), OTHER); initBooleanField("SPACE_AFTER_COMMA", ApplicationBundle.message("checkbox.spaces.after.comma"), OTHER); @@ -102,6 +117,13 @@ public class CodeStyleSpacesPanel extends OptionTreeWithPreviewPanel { initBooleanField("SPACE_BEFORE_SEMICOLON", ApplicationBundle.message("checkbox.spaces.before.semicolon"), OTHER); initBooleanField("SPACE_AFTER_TYPE_CAST", ApplicationBundle.message("checkbox.spaces.after.type.cast"), OTHER); initBooleanField("SPACE_AFTER_UNARY_OPERATOR", ApplicationBundle.message("checkbox.spaces.after.unary.operator"), OTHER); + initCustomOptions(OTHER); + } + + private void initCustomOptions(String groupName) { + for(Trinity, String, String> option: myCustomOptions.get(groupName)) { + initCustomBooleanField(option.first, option.second, option.third, groupName); + } } /* @@ -117,7 +139,30 @@ public class CodeStyleSpacesPanel extends OptionTreeWithPreviewPanel { } */ + @Override + protected void initBooleanField(@NonNls String fieldName, String cbName, String groupName) { + if (myShowAllStandardOptions || myAllowedOptions.contains(fieldName)) { + super.initBooleanField(fieldName, cbName, groupName); + } + } + public JComponent getPanel() { return getInternalPanel(); } + + public void showAllStandardOptions() { + myShowAllStandardOptions = true; + } + + public void showStandardOptions(String... optionNames) { + Collections.addAll(myAllowedOptions, optionNames); + } + + public void showCustomOption(Class settingsClass, + String fieldName, + String optionName, + String groupName) { + myCustomOptions.putValue(groupName, + Trinity., String, String>create(settingsClass, fieldName, optionName)); + } } diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/LanguageCodeStyleSettingsProvider.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/LanguageCodeStyleSettingsProvider.java index d3529925cccf..fa500465c80a 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/LanguageCodeStyleSettingsProvider.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/LanguageCodeStyleSettingsProvider.java @@ -18,6 +18,7 @@ package com.intellij.application.options.codeStyle; import com.intellij.lang.Language; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.extensions.Extensions; +import com.intellij.psi.codeStyle.CodeStyleCustomizationsConsumer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -41,6 +42,12 @@ public abstract class LanguageCodeStyleSettingsProvider { public abstract String getCodeSample(@NotNull SettingsType settingsType); + public void customizeSpacingOptions(CodeStyleCustomizationsConsumer consumer) { + } + + public void customizeBlankLinesOptions(CodeStyleCustomizationsConsumer consumer) { + } + public static Language[] getLanguagesWithCodeStyleSettings() { ArrayList langs = new ArrayList(); for (LanguageCodeStyleSettingsProvider provider : Extensions.getExtensions(EP_NAME)) { diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/OptionTreeWithPreviewPanel.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/OptionTreeWithPreviewPanel.java index 46ed4d5f7801..d4a07cfb0f04 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/OptionTreeWithPreviewPanel.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/OptionTreeWithPreviewPanel.java @@ -18,11 +18,12 @@ package com.intellij.application.options.codeStyle; import com.intellij.openapi.diagnostic.Logger; import com.intellij.psi.PsiFile; import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.codeStyle.CustomCodeStyleSettings; import com.intellij.ui.treeStructure.Tree; -import com.intellij.util.containers.HashMap; import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.tree.TreeUtil; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; import javax.swing.*; import javax.swing.tree.*; @@ -40,7 +41,6 @@ import java.util.ArrayList; public abstract class OptionTreeWithPreviewPanel extends MultilanguageCodeStyleAbstractPanel { private static final Logger LOG = Logger.getInstance("#com.intellij.application.options.CodeStyleSpacesPanel"); private final JTree myOptionsTree; - private final HashMap myKeyToFieldMap = new HashMap(); private final ArrayList myKeys = new ArrayList(); private final JPanel myPanel = new JPanel(new GridBagLayout()); @@ -144,7 +144,7 @@ public abstract class OptionTreeWithPreviewPanel extends MultilanguageCodeStyleA resetNode(root, settings); } - private void resetNode(TreeNode node, final CodeStyleSettings settings) { + private static void resetNode(TreeNode node, final CodeStyleSettings settings) { if (node instanceof MyToggleTreeNode) { resetMyTreeNode((MyToggleTreeNode)node, settings); return; @@ -155,11 +155,10 @@ public abstract class OptionTreeWithPreviewPanel extends MultilanguageCodeStyleA } } - private void resetMyTreeNode(MyToggleTreeNode childNode, final CodeStyleSettings settings) { + private static void resetMyTreeNode(MyToggleTreeNode childNode, final CodeStyleSettings settings) { try { BooleanOptionKey key = (BooleanOptionKey)childNode.getKey(); - Field field = myKeyToFieldMap.get(key); - childNode.setSelected(field.getBoolean(settings)); + childNode.setSelected(key.getValue(settings)); } catch (IllegalArgumentException e) { LOG.error(e); @@ -175,7 +174,7 @@ public abstract class OptionTreeWithPreviewPanel extends MultilanguageCodeStyleA applyNode(root, settings); } - private void applyNode(TreeNode node, final CodeStyleSettings settings) { + private static void applyNode(TreeNode node, final CodeStyleSettings settings) { if (node instanceof MyToggleTreeNode) { applyToggleNode((MyToggleTreeNode)node, settings); return; @@ -186,18 +185,9 @@ public abstract class OptionTreeWithPreviewPanel extends MultilanguageCodeStyleA } } - private void applyToggleNode(MyToggleTreeNode childNode, final CodeStyleSettings settings) { - try { - BooleanOptionKey key = (BooleanOptionKey)childNode.getKey(); - Field field = myKeyToFieldMap.get(key); - field.set(settings, childNode.isSelected() ? Boolean.TRUE : Boolean.FALSE); - } - catch (IllegalArgumentException e) { - LOG.error(e); - } - catch (IllegalAccessException e) { - LOG.error(e); - } + private static void applyToggleNode(MyToggleTreeNode childNode, final CodeStyleSettings settings) { + BooleanOptionKey key = (BooleanOptionKey)childNode.getKey(); + key.setValue(settings, childNode.isSelected() ? Boolean.TRUE : Boolean.FALSE); } public boolean isModified(CodeStyleSettings settings) { @@ -210,7 +200,7 @@ public abstract class OptionTreeWithPreviewPanel extends MultilanguageCodeStyleA } - private boolean isModified(TreeNode node, final CodeStyleSettings settings) { + private static boolean isModified(TreeNode node, final CodeStyleSettings settings) { if (node instanceof MyToggleTreeNode) { if (isToggleNodeModified((MyToggleTreeNode)node, settings)) { return true; @@ -225,11 +215,10 @@ public abstract class OptionTreeWithPreviewPanel extends MultilanguageCodeStyleA return false; } - private boolean isToggleNodeModified(MyToggleTreeNode childNode, final CodeStyleSettings settings) { + private static boolean isToggleNodeModified(MyToggleTreeNode childNode, final CodeStyleSettings settings) { try { BooleanOptionKey key = (BooleanOptionKey)childNode.getKey(); - Field field = myKeyToFieldMap.get(key); - return childNode.isSelected() != field.getBoolean(settings); + return childNode.isSelected() != key.getValue(settings); } catch (IllegalArgumentException e) { LOG.error(e); @@ -244,13 +233,27 @@ public abstract class OptionTreeWithPreviewPanel extends MultilanguageCodeStyleA try { Class styleSettingsClass = CodeStyleSettings.class; Field field = styleSettingsClass.getField(fieldName); - BooleanOptionKey key = new BooleanOptionKey(groupName, cbName); - myKeyToFieldMap.put(key, field); + BooleanOptionKey key = new BooleanOptionKey(groupName, cbName, field); myKeys.add(key); } catch (NoSuchFieldException e) { + LOG.error(e); } catch (SecurityException e) { + LOG.error(e); + } + } + + protected void initCustomBooleanField(@NotNull Class customClass, String fieldName, String cbName, String groupName) { + try { + Field field = customClass.getField(fieldName); + myKeys.add(new CustomBooleanOptionKey(groupName, cbName, customClass, field)); + } + catch (NoSuchFieldException e) { + LOG.error(e); + } + catch (SecurityException e) { + LOG.error(e); } } @@ -359,10 +362,12 @@ public abstract class OptionTreeWithPreviewPanel extends MultilanguageCodeStyleA private static class BooleanOptionKey { final String groupName; final String cbName; + final Field field; - public BooleanOptionKey(String groupName, String cbName) { + public BooleanOptionKey(String groupName, String cbName, Field field) { this.groupName = groupName; this.cbName = cbName; + this.field = field; } public boolean equals(Object obj) { @@ -374,6 +379,45 @@ public abstract class OptionTreeWithPreviewPanel extends MultilanguageCodeStyleA public int hashCode() { return cbName.hashCode(); } + + public void setValue(CodeStyleSettings settings, Boolean aBoolean) { + try { + field.set(settings, aBoolean); + } + catch (IllegalAccessException e) { + LOG.error(e); + } + } + + public boolean getValue(CodeStyleSettings settings) throws IllegalAccessException { + return field.getBoolean(settings); + } + } + + private static class CustomBooleanOptionKey extends BooleanOptionKey { + private final Class mySettingsClass; + + public CustomBooleanOptionKey(String groupName, String cbName, Class settingsClass, Field field) { + super(groupName, cbName, field); + mySettingsClass = settingsClass; + } + + @Override + public void setValue(CodeStyleSettings settings, Boolean aBoolean) { + final CustomCodeStyleSettings customSettings = settings.getCustomSettings(mySettingsClass); + try { + field.set(customSettings, aBoolean); + } + catch (IllegalAccessException e) { + LOG.error(e); + } + } + + @Override + public boolean getValue(CodeStyleSettings settings) throws IllegalAccessException { + final CustomCodeStyleSettings customSettings = settings.getCustomSettings(mySettingsClass); + return field.getBoolean(customSettings); + } } private static class MyToggleTreeNode extends DefaultMutableTreeNode { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/CustomTemplateCallback.java b/platform/lang-impl/src/com/intellij/codeInsight/template/CustomTemplateCallback.java index d09e0fbeaba3..3d96a531ea0f 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/CustomTemplateCallback.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/CustomTemplateCallback.java @@ -222,11 +222,6 @@ public class CustomTemplateCallback { myBuilder.insertText(offset, text); } - public void insertNewLine() { - myBuilder.insertText(myOffset, "\n"); - myOffset++; - } - public boolean newLineBefore() { int i = myOffset - 1; CharSequence text = myBuilder.getText(); diff --git a/platform/util/src/com/intellij/util/containers/CollectionFactory.java b/platform/util/src/com/intellij/util/containers/CollectionFactory.java index bcbf4c02e43d..ac6f57f463d8 100644 --- a/platform/util/src/com/intellij/util/containers/CollectionFactory.java +++ b/platform/util/src/com/intellij/util/containers/CollectionFactory.java @@ -47,6 +47,10 @@ public class CollectionFactory { return new ArrayList(); } + public static HashSet hashSet() { + return new HashSet(); + } + public static LinkedHashMap linkedMap() { return new LinkedHashMap(); } diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/CalledInAny.java b/platform/vcs-api/src/com/intellij/openapi/vcs/CalledInAny.java new file mode 100644 index 000000000000..43625f357b96 --- /dev/null +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/CalledInAny.java @@ -0,0 +1,26 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.vcs; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.SOURCE) +@Target({ElementType.METHOD}) +public @interface CalledInAny { +} diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/ConcurrentTasks.java b/platform/vcs-api/src/com/intellij/openapi/vcs/ConcurrentTasks.java new file mode 100644 index 000000000000..2255fd7beb96 --- /dev/null +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/ConcurrentTasks.java @@ -0,0 +1,110 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.vcs; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.progress.EmptyProgressIndicator; +import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.util.Consumer; +import com.intellij.util.concurrency.Semaphore; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.Future; + +// competes to calculate The Result +public class ConcurrentTasks { + private volatile boolean myResultKnown; + private final Semaphore mySemaphore; + private volatile T myResult; + private volatile int myCntAlive; + private final ProgressIndicator myParentIndicator; + private final Consumer>[] myTasks; + + public void compute() { + final EmptyProgressIndicator pi = new EmptyProgressIndicator() { + @Override + public void checkCanceled() { + if (myResultKnown || (myParentIndicator != null) && myParentIndicator.isCanceled()) { + super.cancel(); + } + super.checkCanceled(); + } + }; + myCntAlive = myTasks.length; + mySemaphore.down(); + + final List> futures = new LinkedList>(); + for (final Consumer> task : myTasks) { + if (myResultKnown) break; + final Runnable computableProxy = new Runnable() { + public void run() { + try { + task.consume(new Consumer() { + public void consume(T t) { + myResult = t; + myResultKnown = true; + -- myCntAlive; + mySemaphore.up(); + } + }); + } + catch (ProcessCanceledException e) { + -- myCntAlive; + if (myCntAlive == 0) { + mySemaphore.up(); + } + throw e; + } + } + }; + final Future future = ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { + public void run() { + ProgressManager.getInstance().runProcess(computableProxy, pi); + } + }); + futures.add(future); + } + + while (true) { + mySemaphore.waitFor(1000); + if (myResultKnown) break; + pi.checkCanceled(); + } + // in it possible to even interrupt() threads involved, but at the moment it's better for tasks themselves to check cancel status + for (Future future : futures) { + if ((! future.isCancelled() && (! future.isDone()))) { + future.cancel(true); + } + } + } + + public boolean isResultKnown() { + return myResultKnown; + } + + public T getResult() { + return myResult; + } + + public ConcurrentTasks(final ProgressIndicator parentIndicator, final Consumer>... tasks) { + myParentIndicator = parentIndicator; + myTasks = tasks; + mySemaphore = new Semaphore(); + } +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/RunBackgroundable.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/RunBackgroundable.java new file mode 100644 index 000000000000..22f4a7bfd081 --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/RunBackgroundable.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.vcs.changes.committed; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; +import org.jetbrains.annotations.NotNull; + +public class RunBackgroundable { + private RunBackgroundable() { + } + + public static void run(@NotNull final Task task) { + final ProgressManager pm = ProgressManager.getInstance(); + if (ApplicationManager.getApplication().isDispatchThread()) { + pm.run(task); + } else { + task.run(pm.getProgressIndicator()); + } + } +} diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/AccessToNonThreadSafeStaticFieldFromInstance.html b/plugins/InspectionGadgets/src/inspectionDescriptions/AccessToNonThreadSafeStaticFieldFromInstance.html index 517c929c4c40..295b5eacad00 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/AccessToNonThreadSafeStaticFieldFromInstance.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/AccessToNonThreadSafeStaticFieldFromInstance.html @@ -5,5 +5,5 @@ This inspection reports on any access to a static New in 7, Powered by InspectionGadgets + Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/AssertEqualsBetweenInconvertibleTypes.html b/plugins/InspectionGadgets/src/inspectionDescriptions/AssertEqualsBetweenInconvertibleTypes.html index 6fcbc08da100..e6b775586df4 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/AssertEqualsBetweenInconvertibleTypes.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/AssertEqualsBetweenInconvertibleTypes.html @@ -4,5 +4,5 @@ This inspection reports any calls to JUnit's assertEquals() method where the expected result and actual result arguments are of incompatible types. While such a call might theoretically be useful, most likely it represents a bug. - New in 7, Powered by InspectionGadgets + Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/BooleanFieldAlwaysNegated.html b/plugins/InspectionGadgets/src/inspectionDescriptions/BooleanFieldAlwaysNegated.html index 668cbdf72341..45d80b8e5204 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/BooleanFieldAlwaysNegated.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/BooleanFieldAlwaysNegated.html @@ -2,5 +2,5 @@
This global inspection reports any boolean fields which are always negated when read. -
New in 7, Powered by InspectionGadgets
+ Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/CaughtExceptionImmediatelyRethrown.html b/plugins/InspectionGadgets/src/inspectionDescriptions/CaughtExceptionImmediatelyRethrown.html index dcb8732375bd..446ba28445ef 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/CaughtExceptionImmediatelyRethrown.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/CaughtExceptionImmediatelyRethrown.html @@ -5,5 +5,5 @@ This inspection reports any catch block wher the caught exception is immediately rethrown, without performing any action on it. Such catch blocks are unnecessary or lack error handling. - New in 7, Powered by InspectionGadgets + Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/ClassWithTooManyDependencies.html b/plugins/InspectionGadgets/src/inspectionDescriptions/ClassWithTooManyDependencies.html index 84372de0666e..c98414610031 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/ClassWithTooManyDependencies.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/ClassWithTooManyDependencies.html @@ -8,5 +8,5 @@ classes are reported by this inspection.

Use the field below to specify the maximum number of dependencies a class may have before triggering this inspection - New in 7, Powered by InspectionGadgets + Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/ClassWithTooManyDependents.html b/plugins/InspectionGadgets/src/inspectionDescriptions/ClassWithTooManyDependents.html index c4fbaad3f273..5701bc4b5ec7 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/ClassWithTooManyDependents.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/ClassWithTooManyDependents.html @@ -9,5 +9,5 @@ Only top-level classes are reported by this inspection. Use the field below to specify the maximum number of dependents a class may have before triggering this inspection

- New in 7, Powered by InspectionGadgets + Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/ClassWithTooManyTransitiveDependencies.html b/plugins/InspectionGadgets/src/inspectionDescriptions/ClassWithTooManyTransitiveDependencies.html index b68ac5f0f8a3..6a55cd9493e1 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/ClassWithTooManyTransitiveDependencies.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/ClassWithTooManyTransitiveDependencies.html @@ -9,5 +9,5 @@ Only top-level classes are reported by this inspection. Use the field below to specify the maximum number of direct or indirect dependencies a class may have before triggering this inspection

- New in 7, Powered by InspectionGadgets + Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/ClassWithTooManyTransitiveDependents.html b/plugins/InspectionGadgets/src/inspectionDescriptions/ClassWithTooManyTransitiveDependents.html index 115a666e3767..523a4c28724d 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/ClassWithTooManyTransitiveDependents.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/ClassWithTooManyTransitiveDependents.html @@ -9,5 +9,5 @@ are reported by this inspection. Use the field below to specify the maximum number of direct or indirect dependents a class may have before triggering this inspection

- New in 7, Powered by InspectionGadgets + Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/CollectionContainsUrl.html b/plugins/InspectionGadgets/src/inspectionDescriptions/CollectionContainsUrl.html index 665660e2fed1..e72c5e4f06a9 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/CollectionContainsUrl.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/CollectionContainsUrl.html @@ -10,5 +10,5 @@ methods of java.net.URL. java.net.URL's equals and hashCode method use a DNS lookup, which depending on the availability of the network and the speed of the DNS server can cause significant delays. - New in 7, Powered by InspectionGadgets + Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/ComparatorMethodParameterNotUsed.html b/plugins/InspectionGadgets/src/inspectionDescriptions/ComparatorMethodParameterNotUsed.html index 5b366bfe0a04..36c3d8ef7a25 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/ComparatorMethodParameterNotUsed.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/ComparatorMethodParameterNotUsed.html @@ -4,5 +4,5 @@ This inspection reports any parameters of java.util.Comparator.compare() which are not used. Most likely this is the result of a typing mistake and one parameter is compared with itself or the method is not implemented correctly. - New in 7, Powered by InspectionGadgets + Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/CyclicClassDependency.html b/plugins/InspectionGadgets/src/inspectionDescriptions/CyclicClassDependency.html index 4c162821b517..05f4e2f7ee6c 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/CyclicClassDependency.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/CyclicClassDependency.html @@ -3,5 +3,5 @@ This global inspection reports any classes which are mutually or cyclically dependent on other classes. Such cyclic dependencies make for fragile code and high maintenance costs. - New in 7, Powered by InspectionGadgets + Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/CyclicPackageDependency.html b/plugins/InspectionGadgets/src/inspectionDescriptions/CyclicPackageDependency.html index 0e7aa564d965..904b8f9a6ec2 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/CyclicPackageDependency.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/CyclicPackageDependency.html @@ -3,5 +3,5 @@ This global inspection reports any packages which are mutually or cyclically dependent on other packages. Such cyclic dependencies make for fragile code and high maintenance costs. - New in 7, Powered by InspectionGadgets + Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/EnumerationCanBeIteration.html b/plugins/InspectionGadgets/src/inspectionDescriptions/EnumerationCanBeIteration.html index 9404aa2478e1..9c4cf4ce2c81 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/EnumerationCanBeIteration.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/EnumerationCanBeIteration.html @@ -5,5 +5,5 @@ This inspection reports instances of Enumeration used, which can be replaced equivalent Iterator constructs. Iterators are part of the Java Collection Framework, which has been available since Java 1.2. - New in 7, Powered by InspectionGadgets + Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/EqualsHashCodeCalledOnUrl.html b/plugins/InspectionGadgets/src/inspectionDescriptions/EqualsHashCodeCalledOnUrl.html index 2e896885d69b..1ec178baebc8 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/EqualsHashCodeCalledOnUrl.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/EqualsHashCodeCalledOnUrl.html @@ -8,5 +8,5 @@ problems because those methods uses a DNS lookup to determine the equality of two java.net.URL objects. Depending on the availability of the network and the speed of the DNS server, this can cause significant delays. - New in 7, Powered by InspectionGadgets + Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/FieldCanBeMovedToSubclass.html b/plugins/InspectionGadgets/src/inspectionDescriptions/FieldCanBeMovedToSubclass.html index 70e1fab71013..04bdf491f688 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/FieldCanBeMovedToSubclass.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/FieldCanBeMovedToSubclass.html @@ -2,5 +2,5 @@
This global inspection reports any instance fields which can be moved to a subclass. -
New in 7, Powered by InspectionGadgets
+ Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/ImplicitArrayToString.html b/plugins/InspectionGadgets/src/inspectionDescriptions/ImplicitArrayToString.html index d505d53298bf..7a70465d64f3 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/ImplicitArrayToString.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/ImplicitArrayToString.html @@ -5,5 +5,5 @@ This inspection reports any arrays used in Stringjava.io.PrintStream methods (such as System.out.println()). Usually in such a case, the contents of the array were meant to be used and the not array object itself. - New in 7, Powered by InspectionGadgets + Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/LawOfDemeter.html b/plugins/InspectionGadgets/src/inspectionDescriptions/LawOfDemeter.html index e1ff32973286..8fbc4aaea35a 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/LawOfDemeter.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/LawOfDemeter.html @@ -3,5 +3,5 @@ This inspection reports any Law of Demeter violations. See here http://en.wikipedia.org/wiki/Law_of_Demeter for an explanation what the Law of Demeter is. - New in 7, Powered by InspectionGadgets + Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/ListIndexOfReplaceableByContains.html b/plugins/InspectionGadgets/src/inspectionDescriptions/ListIndexOfReplaceableByContains.html index 520786b74384..55917fda5b11 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/ListIndexOfReplaceableByContains.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/ListIndexOfReplaceableByContains.html @@ -4,5 +4,5 @@ This inspection reports any List.indexOf() expressions which can be replaced with the method List.contains(). - New in 7, Powered by InspectionGadgets + Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/LoopWithImplicitTerminationCondition.html b/plugins/InspectionGadgets/src/inspectionDescriptions/LoopWithImplicitTerminationCondition.html index f86730095511..7751de4e8f04 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/LoopWithImplicitTerminationCondition.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/LoopWithImplicitTerminationCondition.html @@ -11,5 +11,5 @@ in a while or fordo-while loop. Such a loop would be clearer if the if statement was removed and its condition was made an explicit loop condition. - New in 7, Powered by InspectionGadgets + Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/ManualArrayToCollectionCopy.html b/plugins/InspectionGadgets/src/inspectionDescriptions/ManualArrayToCollectionCopy.html index 43d039462be6..ad0fbbc001d4 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/ManualArrayToCollectionCopy.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/ManualArrayToCollectionCopy.html @@ -5,5 +5,5 @@ is added individually using a for loop. Such constructs may be replaced by a cal Collection.addAll(Arrays.asList()).
This inspection provides a quick fix. - New in 7, Powered by InspectionGadgets + Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/MethodMayBeSynchronized.html b/plugins/InspectionGadgets/src/inspectionDescriptions/MethodMayBeSynchronized.html index 7fc5c7db93e1..f8d2fcf0654d 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/MethodMayBeSynchronized.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/MethodMayBeSynchronized.html @@ -8,5 +8,5 @@ equal to this for instance methods or ClassName.class for static methods. In such cases the synchronized statements may be replaced by their contents and the containing method marked synchronized. - New in 7, Powered by InspectionGadgets + Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/MethodReturnAlwaysConstant.html b/plugins/InspectionGadgets/src/inspectionDescriptions/MethodReturnAlwaysConstant.html index 1d8e2aca4eac..0d0c4d4043ee 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/MethodReturnAlwaysConstant.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/MethodReturnAlwaysConstant.html @@ -2,5 +2,5 @@
This global inspection reports methods which only ever return a constant. -
New in 7, Powered by InspectionGadgets
+ Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/ModuleWithTooFewClasses.html b/plugins/InspectionGadgets/src/inspectionDescriptions/ModuleWithTooFewClasses.html index 473bbf6130cb..be253ede4b25 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/ModuleWithTooFewClasses.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/ModuleWithTooFewClasses.html @@ -6,5 +6,5 @@ indicate an overly fragmented design.

Use the field below to specify the minimum number of classes a module may have before triggering this inspection.

- New in 7, Powered by InspectionGadgets + Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/ModuleWithTooManyClasses.html b/plugins/InspectionGadgets/src/inspectionDescriptions/ModuleWithTooManyClasses.html index e144180dbe0a..a286b15fb65f 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/ModuleWithTooManyClasses.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/ModuleWithTooManyClasses.html @@ -6,5 +6,5 @@ indicate a lack of design clarity.

Use the field below to specify the maximum number of classes a module may have before triggering this inspection.

- New in 7, Powered by InspectionGadgets + Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/NewStringBufferWithCharArgument.html b/plugins/InspectionGadgets/src/inspectionDescriptions/NewStringBufferWithCharArgument.html index 48d0e1e74826..f0f2106cbdfe 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/NewStringBufferWithCharArgument.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/NewStringBufferWithCharArgument.html @@ -6,5 +6,5 @@ and new StringBuilder() calls with an argume with type char. Such an argument is silently casted to an interger used to specify the length of the buffer. Usually this is not what was intended. - New in 7, Powered by InspectionGadgets + Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/NumberEquality.html b/plugins/InspectionGadgets/src/inspectionDescriptions/NumberEquality.html index d7b0d227db35..00dba0650c0d 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/NumberEquality.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/NumberEquality.html @@ -5,5 +5,5 @@ This inspection reports any use of == to tes rather than the ".equals()" method. With auto-boxing it is easy to make the mistake of comparing two Integer (or other subclass of java.lang.Number) objects instead of two ints. - New in 7, Powered by InspectionGadgets + Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/PackageInMultipleModules.html b/plugins/InspectionGadgets/src/inspectionDescriptions/PackageInMultipleModules.html index dded731bb4e6..121d0350204e 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/PackageInMultipleModules.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/PackageInMultipleModules.html @@ -2,5 +2,5 @@
This global inspection reports any packages which are present in multiple modules. -
New in 7, Powered by InspectionGadgets
+ Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/PackageWithTooFewClasses.html b/plugins/InspectionGadgets/src/inspectionDescriptions/PackageWithTooFewClasses.html index 3d03f7522799..7b900e8ba2ec 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/PackageWithTooFewClasses.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/PackageWithTooFewClasses.html @@ -6,5 +6,5 @@ indicate an overly fragmented design.

Use the field below to specify the minimum number of classes a package may have before triggering this inspection.

- New in 7, Powered by InspectionGadgets + Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/PackageWithTooManyClasses.html b/plugins/InspectionGadgets/src/inspectionDescriptions/PackageWithTooManyClasses.html index a6846511e69f..a3d8f3d812ac 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/PackageWithTooManyClasses.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/PackageWithTooManyClasses.html @@ -6,5 +6,5 @@ indicate a lack of design clarity.

Use the field below to specify the maximum number of classes a package may have before triggering this inspection.

- New in 7, Powered by InspectionGadgets + Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/ParametersPerConstructor.html b/plugins/InspectionGadgets/src/inspectionDescriptions/ParametersPerConstructor.html index 722bc8641bbf..258b5a7ed54d 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/ParametersPerConstructor.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/ParametersPerConstructor.html @@ -5,5 +5,5 @@ This inspection reports any instances of constructors with too many parameters. with too many parameters can be a good sign that refactoring is necessary.

Use the field provided below to specify the maximum acceptable number of parameters a constructor might have. - New in 7, Powered by InspectionGadgets + Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/StaticFieldCanBeMovedToUse.html b/plugins/InspectionGadgets/src/inspectionDescriptions/StaticFieldCanBeMovedToUse.html index eaa312987c41..62baa7e4d7d9 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/StaticFieldCanBeMovedToUse.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/StaticFieldCanBeMovedToUse.html @@ -3,5 +3,5 @@ This global inspection reports any static fields wich are only used in a differenc class than the one they are defined in. Such fields can be moved. - New in 7, Powered by InspectionGadgets + Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/SuspiciousIndentAfterControlStatement.html b/plugins/InspectionGadgets/src/inspectionDescriptions/SuspiciousIndentAfterControlStatement.html index b982b05de1fd..6049cba22507 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/SuspiciousIndentAfterControlStatement.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/SuspiciousIndentAfterControlStatement.html @@ -4,5 +4,5 @@ This inspection reports any suspicious indentation of statements after a control statement without braces. Such indentation can make it look like the statement is part of the control statement, when in fact it will be executed after the control stratement. - New in 7, Powered by InspectionGadgets + Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/SynchronizedOnLiteralObject.html b/plugins/InspectionGadgets/src/inspectionDescriptions/SynchronizedOnLiteralObject.html index e84618c4bbe0..cf0dfa221f0e 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/SynchronizedOnLiteralObject.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/SynchronizedOnLiteralObject.html @@ -8,5 +8,5 @@ Because of this, it is possible that some other part of the system which uses an object initialized with the same literal, is actually holding a reference to the exact same object. This can create unexpected dead-lock situations, if the string was thought to be private. - New in 7, Powered by InspectionGadgets + Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/ThrowableInstanceNeverThrown.html b/plugins/InspectionGadgets/src/inspectionDescriptions/ThrowableInstanceNeverThrown.html index c5a4f9ec4c6d..feac2068e800 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/ThrowableInstanceNeverThrown.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/ThrowableInstanceNeverThrown.html @@ -4,5 +4,5 @@ This inspection reports any instances of Throwable instantiation, where the created Throwable is never actually thrown. Most often this is the result of a simple mistake. - New in 7, Powered by InspectionGadgets + Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/ToArrayCallWithZeroLengthArrayArgument.html b/plugins/InspectionGadgets/src/inspectionDescriptions/ToArrayCallWithZeroLengthArrayArgument.html index 0ec363ef3b7c..c2420afa9bbc 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/ToArrayCallWithZeroLengthArrayArgument.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/ToArrayCallWithZeroLengthArrayArgument.html @@ -7,5 +7,5 @@ with a zero-length array argument. When passing in an array of too small size, t toArray() method has to construct a new array of the right size using reflection. This has significantly worse performance than passing in an array of at least the size of the collection itself. - New in 7, Powered by InspectionGadgets + Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/TransientFieldNotInitialized.html b/plugins/InspectionGadgets/src/inspectionDescriptions/TransientFieldNotInitialized.html index 1201240568c1..8e2ba0d17a8c 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/TransientFieldNotInitialized.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/TransientFieldNotInitialized.html @@ -9,5 +9,5 @@ to be initialized separately in a readObject during deserialization. Any transient fields which are not initialized during normal object construction are considered to use the default initialization and are not reported by this inspection. - New in 7, Powered by InspectionGadgets + Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/TypeMayBeWeakened.html b/plugins/InspectionGadgets/src/inspectionDescriptions/TypeMayBeWeakened.html index 4eb3848be1bd..308004eeda4a 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/TypeMayBeWeakened.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/TypeMayBeWeakened.html @@ -22,5 +22,5 @@ and containsValue(). Use the third checkbox below to specify if this inspection should warn when a type can be weakened to java.lang.Object. Weakening to java.lang.Object is often not very useful. - New in 7, Powered by InspectionGadgets + Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/UnnecessaryUnaryMinus.html b/plugins/InspectionGadgets/src/inspectionDescriptions/UnnecessaryUnaryMinus.html index d00fd6c77c7b..1c2bb98b4fb7 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/UnnecessaryUnaryMinus.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/UnnecessaryUnaryMinus.html @@ -12,5 +12,5 @@ This inspection reports any unnecessary unary minuses. For example: i = i - 8; -New in 7, Powered by InspectionGadgets +Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/UnpredictableBigDecimalConstructorCall.html b/plugins/InspectionGadgets/src/inspectionDescriptions/UnpredictableBigDecimalConstructorCall.html index ca94fbf362bd..2e48811af608 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/UnpredictableBigDecimalConstructorCall.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/UnpredictableBigDecimalConstructorCall.html @@ -7,5 +7,5 @@ These constructors can have somewhat unpredictable results because many numbers cannot be represented exactly in a double. It is recommend to use the constructors which accept a String instead. -New in 7, Powered by InspectionGadgets +Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/UnqualifiedFieldAccess.html b/plugins/InspectionGadgets/src/inspectionDescriptions/UnqualifiedFieldAccess.html index ceb99539313c..aafd8f950c0f 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/UnqualifiedFieldAccess.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/UnqualifiedFieldAccess.html @@ -5,5 +5,5 @@ This inspection reports on field accesses which are not qualified with this or some other qualifier. Some coding styles mandate that all field accesses are qualified to prevent confusion with local variable or parameter accesses. -New in 7, Powered by InspectionGadgets +Powered by InspectionGadgets \ No newline at end of file diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/control/GroovyConditionalWithIdenticalBranchesInspection.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/control/GroovyConditionalWithIdenticalBranchesInspection.java index 8c031b7625de..27ce86a987db 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/control/GroovyConditionalWithIdenticalBranchesInspection.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/control/GroovyConditionalWithIdenticalBranchesInspection.java @@ -53,9 +53,10 @@ public class GroovyConditionalWithIdenticalBranchesInspection extends BaseInspec return "Collapse conditional expression"; } - public void doFix(Project project, ProblemDescriptor descriptor) - throws IncorrectOperationException { - final GrConditionalExpression expression = (GrConditionalExpression) descriptor.getPsiElement(); + public void doFix(Project project, ProblemDescriptor descriptor) throws IncorrectOperationException { + final PsiElement element = descriptor.getPsiElement(); + if (!(element instanceof GrConditionalExpression)) return; + final GrConditionalExpression expression = (GrConditionalExpression)element; final GrExpression thenBranch = expression.getThenBranch(); replaceExpression(expression, thenBranch.getText()); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/filters/types/BuiltInTypeFilter.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/filters/types/BuiltInTypeFilter.java index d867f977afc1..bcbf212dc661 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/filters/types/BuiltInTypeFilter.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/filters/types/BuiltInTypeFilter.java @@ -37,9 +37,10 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil; */ public class BuiltInTypeFilter implements ElementFilter { public boolean isAcceptable(Object element, PsiElement context) { - PsiElement previous = PsiImplUtil.realPrevious(context.getParent().getPrevSibling()); - if (previous != null && - GroovyTokenTypes.mAT.equals(previous.getNode().getElementType())) { + final PsiElement parent = context.getParent(); + if (parent == null) return false; + PsiElement previous = PsiImplUtil.realPrevious(parent.getPrevSibling()); + if (previous != null && GroovyTokenTypes.mAT.equals(previous.getNode().getElementType())) { return false; } if (GroovyCompletionUtil.asSimpleVariable(context) || @@ -47,29 +48,29 @@ public class BuiltInTypeFilter implements ElementFilter { GroovyCompletionUtil.asVariableInBlock(context)) { return true; } - if ((context.getParent() instanceof GrParameter && - ((GrParameter) context.getParent()).getTypeElementGroovy() == null) || - context.getParent() instanceof GrReferenceElement && - !(context.getParent().getParent() instanceof GrImportStatement) && - !(context.getParent().getParent() instanceof GrPackageDefinition) && - !(context.getParent().getParent() instanceof GrArgumentList)) { + if ((parent instanceof GrParameter && + ((GrParameter)parent).getTypeElementGroovy() == null) || + parent instanceof GrReferenceElement && + !(parent.getParent() instanceof GrImportStatement) && + !(parent.getParent() instanceof GrPackageDefinition) && + !(parent.getParent() instanceof GrArgumentList)) { PsiElement prevSibling = context.getPrevSibling(); - if (context.getParent() instanceof GrReferenceElement && prevSibling != null && prevSibling.getNode() != null) { + if (parent instanceof GrReferenceElement && prevSibling != null && prevSibling.getNode() != null) { ASTNode node = prevSibling.getNode(); return !GroovyTokenTypes.DOTS.contains(node.getElementType()); } else { return true; } } - if (PsiImplUtil.realPrevious(context.getParent().getPrevSibling()) instanceof GrModifierList) { + if (PsiImplUtil.realPrevious(parent.getPrevSibling()) instanceof GrModifierList) { return true; } if (PsiImplUtil.realPrevious(context.getPrevSibling()) instanceof GrModifierList) { return true; } - return context.getParent() instanceof GrExpression && - context.getParent().getParent() instanceof GroovyFile && - GroovyCompletionUtil.isNewStatement(context, false); + return parent instanceof GrExpression && + parent.getParent() instanceof GroovyFile && + GroovyCompletionUtil.isNewStatement(context, false); } public boolean isClassAcceptable(Class hintClass) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyPsiElementFactoryImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyPsiElementFactoryImpl.java index 2b7e14dd29f8..f3d48556d83c 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyPsiElementFactoryImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyPsiElementFactoryImpl.java @@ -346,9 +346,14 @@ public class GroovyPsiElementFactoryImpl extends GroovyPsiElementFactory { public GrMethod createMethodFromText(@NotNull String methodText) { GroovyFileBase file = createDummyFile(methodText); - GrTopLevelDefintion defintion = file.getTopLevelDefinitions()[0]; - assert defintion != null && defintion instanceof GrMethod; - return ((GrMethod) defintion); + try { + GrTopLevelDefintion defintion = file.getTopLevelDefinitions()[0]; + assert defintion != null && defintion instanceof GrMethod; + return ((GrMethod)defintion); + } + catch (Error error) { + throw new IncorrectOperationException("Can't create method from text: '" + file.getText() + "'"); + } } @Override diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/surroundWith/surrounders/surroundersImpl/expressions/GroovyWithTypeCastSurrounder.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/surroundWith/surrounders/surroundersImpl/expressions/GroovyWithTypeCastSurrounder.java index 4f082171c7c7..6724da5e03b6 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/surroundWith/surrounders/surroundersImpl/expressions/GroovyWithTypeCastSurrounder.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/surroundWith/surrounders/surroundersImpl/expressions/GroovyWithTypeCastSurrounder.java @@ -29,7 +29,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement; public class GroovyWithTypeCastSurrounder extends GroovyExpressionSurrounder { protected TextRange surroundExpression(GrExpression expression) { GrParenthesizedExpression parenthesized = (GrParenthesizedExpression) GroovyPsiElementFactory.getInstance(expression.getProject()).createTopElementFromText("((Type)a)"); - parenthesized = (GrParenthesizedExpression) expression.replaceWithExpression(parenthesized, true); + parenthesized = (GrParenthesizedExpression) expression.replaceWithExpression(parenthesized, false); GrTypeCastExpression typeCast = (GrTypeCastExpression) parenthesized.getOperand(); replaceToOldExpression(typeCast.getOperand(), expression); GrTypeElement typeElement = typeCast.getCastTypeElement(); diff --git a/plugins/hg4idea/resources/org/zmlx/hg4idea/HgVcsMessages.properties b/plugins/hg4idea/resources/org/zmlx/hg4idea/HgVcsMessages.properties index 427f9c831216..ac8b6431e4c2 100644 --- a/plugins/hg4idea/resources/org/zmlx/hg4idea/HgVcsMessages.properties +++ b/plugins/hg4idea/resources/org/zmlx/hg4idea/HgVcsMessages.properties @@ -22,8 +22,8 @@ hg4idea.revert=Revert hg4idea.commit=Commit hg4idea.configuration.title=Mercurial Configuration -hg4idea.configuration.description=Path to hg executable -hg4idea.configuration.executable.error=\"{0}\" doesn't appear to be a valid hg executable +hg4idea.configuration.description=Full path to hg executable +hg4idea.configuration.executable.error=\"{0}\" doesn\'t appear to be a valid hg executable hg4idea.command.executable.error=Error executing \"{0}\". Make sure you have setup the right executable in Mercurial configuration @@ -54,4 +54,3 @@ hg4idea.status.currentBranch.text={0} hg4idea.status.currentBranch.description=Current mercurial branch hg4idea.warning.no-default-update-path=Skipped \"{0}\". No default update path. hg4idea.merge.please-commit=Merged heads, please commit repository \"{0}\" -hg4idea.error.invalidExecutable=\"{0}\" is not a valid mercurial executable diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgConfigurationIdePanel.form b/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgConfigurationIdePanel.form index 79ffd1b867bb..4dcb91491827 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgConfigurationIdePanel.form +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgConfigurationIdePanel.form @@ -34,7 +34,7 @@ - + diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgConfigurationIdePanel.java b/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgConfigurationIdePanel.java index 4759010fdcf3..0fdacc8fe5ce 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgConfigurationIdePanel.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgConfigurationIdePanel.java @@ -45,14 +45,6 @@ public class HgConfigurationIdePanel { this.globalSettings = globalSettings; loadSettings(); - String title = HgVcsMessages.message("hg4idea.configuration.title"); - String description = HgVcsMessages.message("hg4idea.configuration.description"); - - pathSelector.addBrowseFolderListener( - title, description, null, - new FileChooserDescriptor(true, false, false, false, false, false) - ); - final ActionListener listener = new ActionListener() { public void actionPerformed(ActionEvent e) { pathSelector.setEnabled(selectRadioButton.isSelected()); @@ -109,54 +101,7 @@ public class HgConfigurationIdePanel { } } - { -// GUI initializer generated by IntelliJ IDEA GUI Designer -// >>> IMPORTANT!! <<< -// DO NOT EDIT OR ADD ANY CODE HERE! - $$$setupUI$$$(); - } - - /** - * Method generated by IntelliJ IDEA GUI Designer - * >>> IMPORTANT!! <<< - * DO NOT edit this method OR call it in your code! - * - * @noinspection ALL - */ - private void $$$setupUI$$$() { - basePanel = new JPanel(); - basePanel.setLayout(new GridLayoutManager(2, 1, new Insets(0, 0, 0, 0), -1, -1)); - final JPanel panel1 = new JPanel(); - panel1.setLayout(new GridLayoutManager(2, 2, new Insets(0, 0, 0, 0), -1, -1)); - basePanel.add(panel1, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); - panel1.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Path to hg executable")); - autoRadioButton = new JRadioButton(); - autoRadioButton.setSelected(true); - autoRadioButton.setText("Autodetect hg in PATH"); - autoRadioButton.setMnemonic('A'); - autoRadioButton.setDisplayedMnemonicIndex(0); - panel1.add(autoRadioButton, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); - selectRadioButton = new JRadioButton(); - selectRadioButton.setEnabled(true); - selectRadioButton.setText("Specify executable path"); - selectRadioButton.setMnemonic('S'); - selectRadioButton.setDisplayedMnemonicIndex(0); - panel1.add(selectRadioButton, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); - pathSelector = new TextFieldWithBrowseButton(); - pathSelector.setEnabled(false); - panel1.add(pathSelector, new GridConstraints(1, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, new Dimension(100, -1), null, null, 0, false)); - final Spacer spacer1 = new Spacer(); - basePanel.add(spacer1, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_VERTICAL, 1, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false)); - ButtonGroup buttonGroup; - buttonGroup = new ButtonGroup(); - buttonGroup.add(autoRadioButton); - buttonGroup.add(selectRadioButton); - } - - /** - * @noinspection ALL - */ - public JComponent $$$getRootComponent$$$() { - return basePanel; + private void createUIComponents() { + pathSelector = new HgSetExecutablePathPanel(); } } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgSetExecutableDialog.form b/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgSetExecutableDialog.form index 2a0505454315..9ba2bbb7a460 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgSetExecutableDialog.form +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgSetExecutableDialog.form @@ -1,6 +1,6 @@

- + @@ -8,7 +8,7 @@ - + @@ -16,27 +16,17 @@ - + - - - - - - - - - - - + - + diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgSetExecutableDialog.java b/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgSetExecutableDialog.java index bf0d3df8cdc5..915ed8000f75 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgSetExecutableDialog.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgSetExecutableDialog.java @@ -14,26 +14,16 @@ package org.zmlx.hg4idea.ui; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.openapi.ui.TextFieldWithBrowseButton; -import com.intellij.uiDesigner.core.GridConstraints; -import com.intellij.uiDesigner.core.GridLayoutManager; -import com.intellij.uiDesigner.core.Spacer; import org.zmlx.hg4idea.HgVcsMessages; -import javax.swing.JComponent; -import javax.swing.JLabel; -import javax.swing.JPanel; -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Font; -import java.awt.Insets; -import java.util.ResourceBundle; +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; public class HgSetExecutableDialog extends DialogWrapper { - private JPanel centerPanel; - private TextFieldWithBrowseButton hgExecutablePath; - private JLabel infoLabel; - private JLabel errorLabel; + private JPanel myCenterPanel; + private HgSetExecutablePathPanel myHgExecutablePath; + private JLabel myInfoLabel; public HgSetExecutableDialog(Project project) { super(project, false); @@ -42,81 +32,24 @@ public class HgSetExecutableDialog extends DialogWrapper { @Override protected JComponent createCenterPanel() { - return centerPanel; + return myCenterPanel; } public void setBadHgPath(String hgPath) { - hgExecutablePath.setText(hgPath); - errorLabel.setText(HgVcsMessages.message("hg4idea.error.invalidExecutable", hgPath)); + myHgExecutablePath.setText(hgPath); + setErrorText(HgVcsMessages.message("hg4idea.configuration.executable.error", hgPath)); } public String getNewHgPath() { - return hgExecutablePath.getText(); + return myHgExecutablePath.getText(); } - - { -// GUI initializer generated by IntelliJ IDEA GUI Designer -// >>> IMPORTANT!! <<< -// DO NOT EDIT OR ADD ANY CODE HERE! - $$$setupUI$$$(); - } - - /** - * Method generated by IntelliJ IDEA GUI Designer - * >>> IMPORTANT!! <<< - * DO NOT edit this method OR call it in your code! - * - * @noinspection ALL - */ - private void $$$setupUI$$$() { - centerPanel = new JPanel(); - centerPanel.setLayout(new GridLayoutManager(4, 1, new Insets(0, 0, 0, 0), -1, -1)); - hgExecutablePath = new TextFieldWithBrowseButton(); - centerPanel.add(hgExecutablePath, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_NORTHWEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, new Dimension(400, -1), null, 0, false)); - infoLabel = new JLabel(); - infoLabel.setText("Full path to hg executable or 'hg' if PATH has it:"); - centerPanel.add(infoLabel, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); - errorLabel = new JLabel(); - errorLabel.setFont(new Font(errorLabel.getFont().getName(), Font.ITALIC, errorLabel.getFont().getSize())); - errorLabel.setForeground(Color.red); - this.$$$loadLabelText$$$(errorLabel, ResourceBundle.getBundle("org/zmlx/hg4idea/HgVcsMessages").getString("hg4idea.error.invalidExecutable")); - centerPanel.add(errorLabel, new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); - final Spacer spacer1 = new Spacer(); - centerPanel.add(spacer1, new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_VERTICAL, 1, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false)); - } - - /** - * @noinspection ALL - */ - private void $$$loadLabelText$$$(JLabel component, String text) { - StringBuffer result = new StringBuffer(); - boolean haveMnemonic = false; - char mnemonic = '\0'; - int mnemonicIndex = -1; - for (int i = 0; i < text.length(); i++) { - if (text.charAt(i) == '&') { - i++; - if (i == text.length()) break; - if (!haveMnemonic && text.charAt(i) != '&') { - haveMnemonic = true; - mnemonic = text.charAt(i); - mnemonicIndex = result.length(); - } + private void createUIComponents() { + myHgExecutablePath = new HgSetExecutablePathPanel(); + myHgExecutablePath.addOKListener(new ActionListener() { + public void actionPerformed(ActionEvent event) { + setErrorText(""); } - result.append(text.charAt(i)); - } - component.setText(result.toString()); - if (haveMnemonic) { - component.setDisplayedMnemonic(mnemonic); - component.setDisplayedMnemonicIndex(mnemonicIndex); - } - } - - /** - * @noinspection ALL - */ - public JComponent $$$getRootComponent$$$() { - return centerPanel; + }); } } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgSetExecutablePathPanel.java b/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgSetExecutablePathPanel.java new file mode 100644 index 000000000000..7594a7c86244 --- /dev/null +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgSetExecutablePathPanel.java @@ -0,0 +1,45 @@ +package org.zmlx.hg4idea.ui; + +import com.intellij.openapi.fileChooser.FileChooserDescriptor; +import com.intellij.openapi.options.ConfigurationException; +import com.intellij.openapi.ui.TextFieldWithBrowseButton; +import com.intellij.openapi.vfs.VirtualFile; +import org.zmlx.hg4idea.HgVcsMessages; +import org.zmlx.hg4idea.command.HgVersionCommand; + +import java.awt.event.ActionListener; +import java.util.HashSet; +import java.util.Set; + +/** + * HgSetExecutablePathPanel is a {@link com.intellij.openapi.ui.TextFieldWithBrowseButton}, which opens a file chooser for hg executable + * and checks validity of the selected file to be an hg executable. + */ +class HgSetExecutablePathPanel extends TextFieldWithBrowseButton { + + private final Set myOkListeners = new HashSet(); + + HgSetExecutablePathPanel() { + FileChooserDescriptor descriptor = new FileChooserDescriptor(true, false, false, false, false, false) { + public void validateSelectedFiles(VirtualFile[] files) throws Exception { + HgVersionCommand command = new HgVersionCommand(); + String path = files[0].getPath(); + if (!command.isValid(path)) { + throw new ConfigurationException(HgVcsMessages.message("hg4idea.configuration.executable.error", path)); + } + for (ActionListener okListener : myOkListeners) { + okListener.actionPerformed(null); + } + } + }; + addBrowseFolderListener(HgVcsMessages.message("hg4idea.configuration.title"), HgVcsMessages.message("hg4idea.configuration.description"), null, descriptor); + } + + /** + * Adds a listener which will be called when file chooser dialog is closed successfully. + */ + void addOKListener(ActionListener listener) { + myOkListeners.add(listener); + } + +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java index 2f17758f35d1..209325d8c928 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java @@ -68,6 +68,7 @@ import org.jetbrains.idea.svn.actions.ShowPropertiesDiffWithLocalAction; import org.jetbrains.idea.svn.actions.SvnMergeProvider; import org.jetbrains.idea.svn.annotate.SvnAnnotationProvider; import org.jetbrains.idea.svn.checkin.SvnCheckinEnvironment; +import org.jetbrains.idea.svn.dialogs.SvnBranchPointsCalculator; import org.jetbrains.idea.svn.dialogs.SvnFormatWorker; import org.jetbrains.idea.svn.dialogs.WCInfo; import org.jetbrains.idea.svn.history.*; @@ -144,6 +145,8 @@ public class SvnVcs extends AbstractVcs { public static final Topic ROOTS_RELOADED = new Topic("ROOTS_RELOADED", Runnable.class); private VcsListener myVcsListener; + private SvnBranchPointsCalculator mySvnBranchPointsCalculator; + private final RootsToWorkingCopies myRootsToWorkingCopies; private final SvnAuthenticationNotifier myAuthNotifier; private static RareLogger.LogFilter[] ourLogFilters; @@ -365,6 +368,7 @@ public class SvnVcs extends AbstractVcs { FrameStateManager.getInstance().addListener(myFrameStateListener); myAuthNotifier.init(); + mySvnBranchPointsCalculator = new SvnBranchPointsCalculator(myProject); // do one time after project loaded StartupManager.getInstance(myProject).runWhenProjectIsInitialized(new DumbAwareRunnable() { @@ -466,6 +470,8 @@ public class SvnVcs extends AbstractVcs { myAuthNotifier.stop(); myAuthNotifier.clear(); + + mySvnBranchPointsCalculator = null; } public VcsShowConfirmationOption getAddConfirmation() { @@ -1038,4 +1044,8 @@ public class SvnVcs extends AbstractVcs { public CommittedChangeList getRevisionChanges(VcsFileRevision revision, VirtualFile file) throws VcsException { return ShowAllSubmittedFilesAction.loadRevisions(getProject(), (SvnFileRevision)revision, file, false); } + + public SvnBranchPointsCalculator getSvnBranchPointsCalculator() { + return mySvnBranchPointsCalculator; + } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/BranchMerger.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/BranchMerger.java index 90d53fca5770..e3ba46c21b03 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/BranchMerger.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/BranchMerger.java @@ -55,7 +55,7 @@ public class BranchMerger implements IMerger { } public String getComment() { - return "Merge from " + myBranchName; + return "Merge all from " + myBranchName; } public boolean hasNext() { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/CopiesPanel.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/CopiesPanel.java index 05363a1327f8..547bba8336b7 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/CopiesPanel.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/CopiesPanel.java @@ -17,56 +17,29 @@ package org.jetbrains.idea.svn.dialogs; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; -import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Comparing; -import com.intellij.openapi.vcs.AbstractVcsHelper; -import com.intellij.openapi.vcs.CommittedChangesProvider; import com.intellij.openapi.vcs.ObjectsConvertor; -import com.intellij.openapi.vcs.VcsException; -import com.intellij.openapi.vcs.changes.BackgroundFromStartOption; -import com.intellij.openapi.vcs.changes.ui.ChangesViewBalloonProblemNotifier; -import com.intellij.openapi.vcs.versionBrowser.ChangeBrowserSettings; -import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.ui.DottedBorder; import com.intellij.ui.components.labels.LinkLabel; import com.intellij.ui.components.labels.LinkListener; -import com.intellij.util.AsynchConsumer; -import com.intellij.util.Consumer; import com.intellij.util.containers.Convertor; import com.intellij.util.io.EqualityPolicy; import com.intellij.util.messages.MessageBusConnection; import com.intellij.util.ui.UIUtil; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.NestedCopyType; import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.WorkingCopyFormat; -import org.jetbrains.idea.svn.actions.ChangeListsMergerFactory; import org.jetbrains.idea.svn.actions.SelectBranchPopup; import org.jetbrains.idea.svn.branchConfig.SvnBranchConfigurationNew; import org.jetbrains.idea.svn.checkout.SvnCheckoutProvider; -import org.jetbrains.idea.svn.history.FirstInBranch; -import org.jetbrains.idea.svn.history.SvnChangeList; -import org.jetbrains.idea.svn.history.SvnRepositoryLocation; -import org.jetbrains.idea.svn.integrate.IMerger; -import org.jetbrains.idea.svn.integrate.MergerFactory; -import org.jetbrains.idea.svn.integrate.SvnIntegrateChangesTask; -import org.jetbrains.idea.svn.integrate.WorkingCopyInfo; -import org.jetbrains.idea.svn.mergeinfo.BranchInfo; -import org.jetbrains.idea.svn.mergeinfo.SvnMergeInfoCache; -import org.jetbrains.idea.svn.update.UpdateEventHandler; import org.tmatesoft.svn.core.SVNDepth; -import org.tmatesoft.svn.core.SVNException; -import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.wc.SVNRevision; import javax.swing.*; @@ -77,8 +50,6 @@ import java.awt.event.FocusEvent; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.io.File; -import java.util.Collections; -import java.util.LinkedList; import java.util.List; public class CopiesPanel { @@ -291,149 +262,11 @@ public class CopiesPanel { private void mergeFrom(final WCInfo wcInfo, final VirtualFile root, final LinkLabel mergeLabel) { SelectBranchPopup.showForBranchRoot(myProject, root, new SelectBranchPopup.BranchSelectedCallback() { public void branchSelected(Project project, SvnBranchConfigurationNew configuration, String url, long revision) { - if (url.equals(wcInfo.getRootUrl())) { - showErrorBalloon("Cannot merge from self"); - return; - } - ProgressManager.getInstance().run(new MergeCalculator(wcInfo, root, url, configuration.getBaseName(url))); + new QuickMerge(project, url, wcInfo, configuration.getBaseName(url), configuration).execute(); } }, "Select branch", mergeLabel); } - private void showErrorBalloon(final String s) { - ChangesViewBalloonProblemNotifier.showMe(myProject, s, MessageType.ERROR); - } - - private class MergeCalculator extends Task.Backgroundable { - private final WCInfo myWcInfo; - private final VirtualFile myRoot; - private final String mySourceUrl; - private final String myBranchName; - private boolean myIsReintegrate; - - private final List myNotMerged; - private String myMergeTitle; - private BranchInfo myBranchInfo; - - private MergeCalculator(WCInfo wcInfo, VirtualFile root, String sourceUrl, String branchName) { - super(CopiesPanel.this.myProject, "Calculating not merged revisions", true, BackgroundFromStartOption.getInstance()); - myWcInfo = wcInfo; - myRoot = root; - mySourceUrl = sourceUrl; - myBranchName = branchName; - myNotMerged = new LinkedList(); - myMergeTitle = "Merge from " + branchName; - } - - - public void run(@NotNull ProgressIndicator indicator) { - // branch is info holder - new FirstInBranch(myVcs, myWcInfo.getRepositoryRoot(), myWcInfo.getRootUrl(), mySourceUrl, - new Consumer() { - public void consume(FirstInBranch.CopyData copyData) { - if (copyData == null) { - showErrorBalloon("Merge start wasn't found"); - return; - } - - myIsReintegrate = ! copyData.isTrunkSupposedCorrect(); - if (! myWcInfo.getFormat().supportsMergeInfo()) return; - final long localLatest = Math.max(copyData.getCopyTargetRevision(), copyData.getCopySourceRevision()); - myBranchInfo = new BranchInfo(myVcs, myWcInfo.getRepositoryRoot(), myWcInfo.getRootUrl(), mySourceUrl, mySourceUrl, myVcs.createWCClient()); - - final CommittedChangesProvider committedChangesProvider = - myVcs.getCommittedChangesProvider(); - final ChangeBrowserSettings settings = new ChangeBrowserSettings(); - settings.CHANGE_AFTER = Long.toString(localLatest); - try { - committedChangesProvider.loadCommittedChanges(settings, new SvnRepositoryLocation(mySourceUrl), - committedChangesProvider.getUnlimitedCountValue(), new AsynchConsumer() { - public void finished() { - } - - public void consume(CommittedChangeList committedChangeList) { - final SvnChangeList svnList = (SvnChangeList)committedChangeList; - if (localLatest >= svnList.getNumber()) return; - - final SvnMergeInfoCache.MergeCheckResult checkResult = - myBranchInfo.checkList(svnList, myWcInfo.getPath()); - if (SvnMergeInfoCache.MergeCheckResult.NOT_MERGED.equals(checkResult)) { - myNotMerged.add(svnList); - } - } - }); - } - catch (VcsException e) { - AbstractVcsHelper.getInstance(myProject).showErrors(Collections.singletonList(e), "Checking revisions for merge fault"); - } - } - }).run(); - } - - @Override - public void onCancel() { - onSuccess(); - } - - @Nullable - private MergerFactory askParameters() { - final int result = Messages.showDialog(myProject, myNotMerged.size() + " not merged revision(s) found.", myMergeTitle, - new String[]{"Merge &all", "&Select revisions to merge", "Cancel"}, 0, Messages.getQuestionIcon()); - if (result == 2) return null; - - final MergerFactory factory; - if (result == 0) { - factory = createMergeAllFactory(); - } else { - final ToBeMergedDialog dialog = new ToBeMergedDialog(myProject, myNotMerged, myMergeTitle, myBranchInfo); - dialog.show(); - if (dialog.getExitCode() == DialogWrapper.CANCEL_EXIT_CODE) { - return null; - } - final List lists = dialog.getSelected(); - if (lists.isEmpty()) return null; - factory = new ChangeListsMergerFactory(lists); - } - return factory; - } - - private MergerFactory createMergeAllFactory() { - return new MergerFactory() { - public IMerger createMerger(SvnVcs vcs, File target, UpdateEventHandler handler, SVNURL currentBranchUrl) { - return new BranchMerger(vcs, currentBranchUrl, myWcInfo.getUrl(), myWcInfo.getPath(), handler, myIsReintegrate, myBranchName); - } - }; - } - - @Override - public void onSuccess() { - if (! myWcInfo.getFormat().supportsMergeInfo()) { - doMerge(createMergeAllFactory()); - return; - } - if (myNotMerged.isEmpty()) { - ChangesViewBalloonProblemNotifier.showMe(myProject, "Everything is up-to-date", MessageType.WARNING); - return; - } - final MergerFactory factory = askParameters(); - if (factory == null) return; - doMerge(factory); - } - - private void doMerge(MergerFactory factory) { - final SVNURL sourceUrlUrl; - try { - sourceUrlUrl = SVNURL.parseURIEncoded(mySourceUrl); - } catch (SVNException e) { - showErrorBalloon("Cannot merge: " + e.getMessage()); - return; - } - final SvnIntegrateChangesTask task = new SvnIntegrateChangesTask(SvnVcs.getInstance(myProject), - new WorkingCopyInfo(myWcInfo.getPath(), true), factory, sourceUrlUrl, myMergeTitle, false); - ProgressManager.getInstance().run(task); - } - } - private void setFocusableForLinks(final LinkLabel label) { final Border border = new DottedBorder(new Insets(1,2,1,1), Color.black); label.setFocusable(true); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/FactsCalculator.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/FactsCalculator.java new file mode 100644 index 000000000000..09748bca5d70 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/FactsCalculator.java @@ -0,0 +1,59 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.idea.svn.dialogs; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vcs.CalledInAwt; +import com.intellij.util.Consumer; +import com.intellij.util.ValueHolder; +import com.intellij.util.containers.Convertor; + +// cache. persistent. by request +public class FactsCalculator { + private final Project myProject; + private final String myTaskTitle; + private final ValueHolder myCache; + private final Convertor myLive; + + public FactsCalculator(Project project, String taskTitle, ValueHolder cache, Convertor live) { + myProject = project; + myTaskTitle = taskTitle; + myCache = cache; + myLive = live; + } + + @CalledInAwt + public void get(final In in, final Consumer resultConsumer) { + new RunOrContinuation(myProject, myTaskTitle) { + @Override + protected Out calculate() { + return myCache.getValue(in); + } + @Override + protected Out calculateLong() { + final Out result = myLive.convert(in); + if (result != null) { + myCache.setValue(result, in); + } + return result; + } + @Override + protected void processResult(Out out) { + resultConsumer.consume(out); + } + }.execute(); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/QuickMerge.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/QuickMerge.java new file mode 100644 index 000000000000..16aa43e886ad --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/QuickMerge.java @@ -0,0 +1,258 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.idea.svn.dialogs; + +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.ui.MessageType; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.vcs.*; +import com.intellij.openapi.vcs.changes.BackgroundFromStartOption; +import com.intellij.openapi.vcs.changes.committed.RunBackgroundable; +import com.intellij.openapi.vcs.changes.ui.ChangesViewBalloonProblemNotifier; +import com.intellij.openapi.vcs.versionBrowser.ChangeBrowserSettings; +import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList; +import com.intellij.util.AsynchConsumer; +import com.intellij.util.Consumer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.SvnVcs; +import org.jetbrains.idea.svn.actions.ChangeListsMergerFactory; +import org.jetbrains.idea.svn.branchConfig.SvnBranchConfigurationNew; +import org.jetbrains.idea.svn.history.CopyData; +import org.jetbrains.idea.svn.history.FirstInBranch; +import org.jetbrains.idea.svn.history.SvnChangeList; +import org.jetbrains.idea.svn.history.SvnRepositoryLocation; +import org.jetbrains.idea.svn.integrate.IMerger; +import org.jetbrains.idea.svn.integrate.MergerFactory; +import org.jetbrains.idea.svn.integrate.SvnIntegrateChangesTask; +import org.jetbrains.idea.svn.integrate.WorkingCopyInfo; +import org.jetbrains.idea.svn.mergeinfo.BranchInfo; +import org.jetbrains.idea.svn.mergeinfo.SvnMergeInfoCache; +import org.jetbrains.idea.svn.update.UpdateEventHandler; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.SVNURL; +import org.tmatesoft.svn.core.internal.util.SVNPathUtil; + +import java.io.File; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +public class QuickMerge { + private final Project myProject; + private final String myBranchName; + private final SvnBranchConfigurationNew myConfiguration; + private final WCInfo myWcInfo; + private String mySourceUrl; + private SvnVcs myVcs; + private final String myTitle; + + public QuickMerge(Project project, String sourceUrl, WCInfo wcInfo, final String branchName, final SvnBranchConfigurationNew configuration) { + myProject = project; + myBranchName = branchName; + myConfiguration = configuration; + myVcs = SvnVcs.getInstance(project); + mySourceUrl = sourceUrl; + myWcInfo = wcInfo; + myTitle = "Merge from " + myBranchName; + } + + private void correctSourceUrl() throws SVNException { + final SVNURL branch = myConfiguration.getWorkingBranch(myWcInfo.getUrl()); + if (branch != null && (! myWcInfo.getUrl().equals(branch))) { + final String branchString = branch.toString(); + if (SVNPathUtil.isAncestor(branchString, myWcInfo.getRootUrl())) { + final String subPath = SVNPathUtil.getRelativePath(branchString, myWcInfo.getRootUrl()); + mySourceUrl = SVNPathUtil.append(mySourceUrl, subPath); + } + } + } + + private boolean prompt(final String question) { + return Messages.showOkCancelDialog(myProject, question, myTitle, Messages.getQuestionIcon()) == 0; + } + + @CalledInAwt + public void execute() { + if (SVNPathUtil.isAncestor(mySourceUrl, myWcInfo.getRootUrl()) || SVNPathUtil.isAncestor(myWcInfo.getRootUrl(), mySourceUrl)) { + showErrorBalloon("Cannot merge from self"); + return; + } + + try { + correctSourceUrl(); + } + catch (SVNException e) { + showErrorBalloon(e.getMessage()); + return; + } + + if (! myWcInfo.getFormat().supportsMergeInfo()) { + mergeAll(); + return; + } + + final int result = Messages.showDialog(myProject, "Merge all?", myTitle, + new String[]{"Merge &all", "&Select revisions to merge", "Cancel"}, 0, Messages.getQuestionIcon()); + if (result == 2) return; + if (result == 0) { + mergeAll(); + return; + } + + ProgressManager.getInstance().run(new MergeCalculator(myProject, myWcInfo, mySourceUrl, myBranchName)); + } + + @CalledInAny + private void showErrorBalloon(final String s) { + ChangesViewBalloonProblemNotifier.showMe(myProject, s, MessageType.ERROR); + } + + // continuation... continuation.. hidden continuation.. + private void mergeAll() { + // suppose we're in branch + myVcs.getSvnBranchPointsCalculator().getFirstCopyPoint(myWcInfo.getRepositoryRoot(), mySourceUrl, myWcInfo.getRootUrl(), + new Consumer>() { + public void consume(SvnBranchPointsCalculator.WrapperInvertor result) { + if (result == null) { + showErrorBalloon("Merge start wasn't found"); + return; + } + final boolean reintegrate = result.isInvertedSense(); + final MergerFactory mergerFactory = new MergerFactory() { + public IMerger createMerger(SvnVcs vcs, File target, UpdateEventHandler handler, SVNURL currentBranchUrl) { + return new BranchMerger(vcs, currentBranchUrl, myWcInfo.getUrl(), myWcInfo.getPath(), handler, reintegrate, myBranchName); + } + }; + + final String title = "Merging all from " + myBranchName + (reintegrate ? " (reintegrate)" : ""); + doMerge(mergerFactory, title); + } + }); + } + + @CalledInAny + private void doMerge(final MergerFactory factory, final String mergeTitle) { + final SVNURL sourceUrlUrl; + try { + sourceUrlUrl = SVNURL.parseURIEncoded(mySourceUrl); + } catch (SVNException e) { + showErrorBalloon("Cannot merge: " + e.getMessage()); + return; + } + final SvnIntegrateChangesTask task = new SvnIntegrateChangesTask(SvnVcs.getInstance(myProject), + new WorkingCopyInfo(myWcInfo.getPath(), true), factory, sourceUrlUrl, mergeTitle, false); + RunBackgroundable.run(task); + } + + private class MergeCalculator extends Task.Backgroundable { + private final WCInfo myWcInfo; + private final String mySourceUrl; + private final String myBranchName; + private boolean myIsReintegrate; + + private final List myNotMerged; + private String myMergeTitle; + private BranchInfo myBranchInfo; + + private MergeCalculator(Project project, WCInfo wcInfo, String sourceUrl, String branchName) { + super(project, "Calculating not merged revisions", true, BackgroundFromStartOption.getInstance()); + myWcInfo = wcInfo; + mySourceUrl = sourceUrl; + myBranchName = branchName; + myNotMerged = new LinkedList(); + myMergeTitle = "Merge from " + branchName; + } + + public void run(@NotNull ProgressIndicator indicator) { + // branch is info holder + new FirstInBranch(myVcs, myWcInfo.getRepositoryRoot(), myWcInfo.getRootUrl(), mySourceUrl, + new Consumer() { + public void consume(CopyData copyData) { + if (copyData == null) { + showErrorBalloon("Merge start wasn't found"); + return; + } + + myIsReintegrate = ! copyData.isTrunkSupposedCorrect(); + if (! myWcInfo.getFormat().supportsMergeInfo()) return; + final long localLatest = Math.max(copyData.getCopyTargetRevision(), copyData.getCopySourceRevision()); + myBranchInfo = new BranchInfo(myVcs, myWcInfo.getRepositoryRoot(), myWcInfo.getRootUrl(), mySourceUrl, mySourceUrl, myVcs.createWCClient()); + + final CommittedChangesProvider committedChangesProvider = + myVcs.getCommittedChangesProvider(); + final ChangeBrowserSettings settings = new ChangeBrowserSettings(); + settings.CHANGE_AFTER = Long.toString(localLatest); + try { + committedChangesProvider.loadCommittedChanges(settings, new SvnRepositoryLocation(mySourceUrl), + committedChangesProvider.getUnlimitedCountValue(), new AsynchConsumer() { + public void finished() { + } + + public void consume(CommittedChangeList committedChangeList) { + final SvnChangeList svnList = (SvnChangeList)committedChangeList; + if (localLatest >= svnList.getNumber()) return; + + final SvnMergeInfoCache.MergeCheckResult checkResult = + myBranchInfo.checkList(svnList, myWcInfo.getPath()); + if (SvnMergeInfoCache.MergeCheckResult.NOT_MERGED.equals(checkResult)) { + myNotMerged.add(svnList); + } + } + }); + } + catch (VcsException e) { + AbstractVcsHelper.getInstance(myProject).showErrors(Collections.singletonList(e), "Checking revisions for merge fault"); + } + } + }).run(); + } + + @Override + public void onCancel() { + onSuccess(); + } + + @Nullable + private MergerFactory askParameters() { + final MergerFactory factory; + final ToBeMergedDialog dialog = new ToBeMergedDialog(myProject, myNotMerged, myMergeTitle, myBranchInfo); + dialog.show(); + if (dialog.getExitCode() == DialogWrapper.CANCEL_EXIT_CODE) { + return null; + } + final List lists = dialog.getSelected(); + if (lists.isEmpty()) return null; + factory = new ChangeListsMergerFactory(lists); + return factory; + } + + @Override + public void onSuccess() { + if (myNotMerged.isEmpty()) { + ChangesViewBalloonProblemNotifier.showMe(myProject, "Everything is up-to-date", MessageType.WARNING); + return; + } + final MergerFactory factory = askParameters(); + if (factory == null) return; + doMerge(factory, myMergeTitle); + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/RunOrContinuation.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/RunOrContinuation.java new file mode 100644 index 000000000000..b5e91c0dc310 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/RunOrContinuation.java @@ -0,0 +1,70 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.idea.svn.dialogs; + +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.vcs.CalledInAwt; +import com.intellij.openapi.vcs.CalledInBackground; +import com.intellij.openapi.vcs.changes.BackgroundFromStartOption; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public abstract class RunOrContinuation { + protected final Project myProject; + private final String myTaskTitle; + private boolean myWasCanceled; + + protected RunOrContinuation(final Project project, final String taskTitle) { + myProject = project; + myTaskTitle = taskTitle; + } + + @Nullable + @CalledInAwt + protected abstract T calculate(); + @Nullable + @CalledInBackground + protected abstract T calculateLong(); + @CalledInAwt + protected abstract void processResult(final T t); + + protected void cancel() { + myWasCanceled = true; + } + + @CalledInAwt + public void execute() { + final Ref refT = new Ref(); + refT.set(calculate()); + if ((! myWasCanceled) && (! refT.isNull())) { + processResult(refT.get()); + return; + } + ProgressManager.getInstance().run(new Task.Backgroundable(myProject, myTaskTitle, true, BackgroundFromStartOption.getInstance()) { + public void run(@NotNull ProgressIndicator indicator) { + refT.set(calculateLong()); + } + @Override + public void onSuccess() { + if ((! myWasCanceled) && (! refT.isNull())) processResult(refT.get()); + } + }); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SvnBranchPointsCalculator.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SvnBranchPointsCalculator.java new file mode 100644 index 000000000000..5080ce1dd61e --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SvnBranchPointsCalculator.java @@ -0,0 +1,337 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.idea.svn.dialogs; + +import com.intellij.openapi.application.PathManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Ref; +import com.intellij.util.Consumer; +import com.intellij.util.Processor; +import com.intellij.util.ValueHolder; +import com.intellij.util.containers.Convertor; +import com.intellij.util.containers.MultiMap; +import com.intellij.util.io.DataExternalizer; +import com.intellij.util.io.EnumeratorStringDescriptor; +import com.intellij.util.io.PersistentHashMap; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.SvnVcs; +import org.jetbrains.idea.svn.history.CopyData; +import org.jetbrains.idea.svn.history.FirstInBranch; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.File; +import java.io.IOException; +import java.util.*; + +public class SvnBranchPointsCalculator { + private final FactsCalculator> myCalculator; + + private PersistentHolder myPersistentHolder; + + public SvnBranchPointsCalculator(final Project project) { + final File vcs = new File(PathManager.getSystemPath(), "vcs"); + File file = new File(vcs, "svn_copy_sources"); + file.mkdirs(); + file = new File(file, project.getLocationHash()); + + // todo when will it die? + ValueHolder, KeyData> cache = null; + + try { + myPersistentHolder = new PersistentHolder(file); + cache = new ValueHolder, KeyData>() { + public WrapperInvertor getValue(KeyData dataHolder) { + try { + return myPersistentHolder.getBestHit(dataHolder.getRepoUrl(), dataHolder.getSourceUrl(), dataHolder.getTargetUrl()); + } catch (IOException e) { + } + return null; + } + // todo carefully check + public void setValue(WrapperInvertor value, KeyData dataHolder) { + try { + myPersistentHolder.put(dataHolder.getRepoUrl(), + value.isInvertedSense() ? dataHolder.getSourceUrl() : dataHolder.getTargetUrl(), value.getWrapped()); + } + catch (IOException e) { + } + } + }; + } + catch (IOException e) { + cache = new ValueHolder, KeyData>() { + public WrapperInvertor getValue(KeyData dataHolder) { + return null; + } + public void setValue(WrapperInvertor value, KeyData dataHolder) { + } + }; + } + + myCalculator = new FactsCalculator>(project, "Looking for branch origin", cache, new Loader(project)); + } + + private static class BranchDataExternalizer implements DataExternalizer> { + public void save(DataOutput out, TreeMap value) throws IOException { + out.writeInt(value.size()); + for (Map.Entry entry : value.entrySet()) { + out.writeUTF(entry.getKey()); + final BranchCopyData entryValue = entry.getValue(); + out.writeUTF(entryValue.getSource()); + out.writeUTF(entryValue.getTarget()); + out.writeLong(entryValue.getSourceRevision()); + out.writeLong(entryValue.getTargetRevision()); + } + } + + public TreeMap read(DataInput in) throws IOException { + final TreeMap result = new TreeMap(); + + final int num = in.readInt(); + for (int i = 0; i < num; i++) { + final String key = in.readUTF(); + final String source = in.readUTF(); + final String target = in.readUTF(); + final long sourceRevision = in.readLong(); + final long targetRevision = in.readLong(); + + result.put(key, new BranchCopyData(source, sourceRevision, target, targetRevision)); + } + return result; + } + } + + public static class WrapperInvertor> { + private final T myWrapped; + private final boolean myInvertedSense; + + public WrapperInvertor(boolean invertedSense, T wrapped) { + myInvertedSense = invertedSense; + myWrapped = wrapped; + } + + public boolean isInvertedSense() { + return myInvertedSense; + } + + public T getWrapped() { + return myWrapped; + } + + public T getTrue() { + return myInvertedSense ? myWrapped.invertSelf() : myWrapped; + } + + public T inverted() { + return myWrapped.invertSelf(); + } + } + + private static class PersistentHolder { + private final PersistentHashMap> myPersistentMap; + private final MultiMap myForSearchMap; + private final Object myLock; + + PersistentHolder(final File file) throws IOException { + myLock = new Object(); + myPersistentMap = new PersistentHashMap>( + file, new EnumeratorStringDescriptor(), new BranchDataExternalizer()); + final Ref excRef = new Ref(); + // list for values by default + myForSearchMap = new MultiMap(); + myPersistentMap.iterateData(new Processor() { + public boolean process(final String s) { + try { + final TreeMap map = myPersistentMap.get(s); + myForSearchMap.put(s, new ArrayList(map.keySet())); + } + catch (IOException e) { + excRef.set(e); + return false; + } + return true; + } + }); + if (! excRef.isNull()) { + throw excRef.get(); + } + + for (String key : myForSearchMap.keySet()) { + Collections.sort((List) myForSearchMap.get(key)); + } + } + + public void put(final String uid, final String target, final BranchCopyData data) throws IOException { + // todo - rewrite of rather big piece; consider rewriting + synchronized (myLock) { + TreeMap map = myPersistentMap.get(uid); + if (map == null) { + map = new TreeMap(); + } + map.put(target, data); + myPersistentMap.put(uid, map); + if (myForSearchMap.containsKey(uid)) { + final List list = (List)myForSearchMap.get(uid); + final int idx = Collections.binarySearch(list, target); + if (idx < 0) { + final int insertionIdx = - idx - 1; + list.add(insertionIdx, target); + } + } else { + myForSearchMap.putValue(uid, target); + } + } + myPersistentMap.force(); + } + + @Nullable + public WrapperInvertor getBestHit(final String repoUrl, final String source, final String target) throws IOException { + final List keys; + synchronized (myLock) { + keys = (List) myForSearchMap.get(repoUrl); + } + // keys are never removed, so we can use 2 synchronized blocks + final String sourceMatching = getMatchingUrl(keys, source); + final String targetMatching = getMatchingUrl(keys, target); + + if (sourceMatching == null && targetMatching == null) return null; + + synchronized (myLock) { + final TreeMap map = myPersistentMap.get(repoUrl); + + final boolean sourceIsOut = sourceMatching == null; + if (sourceIsOut || targetMatching == null) { + // if found by "target" url - we correctly thought that target of copy is target + return sourceIsOut ? new WrapperInvertor(false, map.get(targetMatching)) : + new WrapperInvertor(false, map.get(sourceMatching)); + } + final BranchCopyData sourceData = map.get(sourceMatching); + final BranchCopyData targetData = map.get(targetMatching); + + final boolean inverted = sourceData.getTargetRevision() > targetData.getTargetRevision(); + return new WrapperInvertor(inverted, inverted ? sourceData : targetData); + } + } + + @Nullable + private String getMatchingUrl(List keys, String source) { + final int idx = Collections.binarySearch(keys, source); + if (idx >= 0) return keys.get(idx); + final int beforeInsertionIdx = - idx - 2; + if (beforeInsertionIdx < 0) return null; + final String candidate = keys.get(beforeInsertionIdx); + if (source.startsWith(candidate)) return candidate; + return null; + } + } + + private static class Loader implements Convertor> { + private SvnVcs myVcs; + + private Loader(final Project project) { + myVcs = SvnVcs.getInstance(project); + } + + public WrapperInvertor convert(final KeyData keyData) { + final Ref> result = new Ref>(); + + new FirstInBranch(myVcs, keyData.getRepoUrl(), keyData.getTargetUrl(), keyData.getSourceUrl(), new Consumer() { + public void consume(CopyData copyData) { + if (copyData != null) { + final boolean correct = copyData.isTrunkSupposedCorrect(); + final BranchCopyData branchCopyData; + if (correct) { + branchCopyData = new BranchCopyData(keyData.getSourceUrl(), copyData.getCopySourceRevision(), keyData.getTargetUrl(), + copyData.getCopyTargetRevision()); + } else { + branchCopyData = new BranchCopyData(keyData.getTargetUrl(), copyData.getCopySourceRevision(), keyData.getSourceUrl(), + copyData.getCopyTargetRevision()); + } + result.set(new WrapperInvertor(! correct, branchCopyData)); + } + } + }).run(); + return result.get(); + } + } + + private static class KeyData { + private final String myRepoUrl; + private final String mySourceUrl; + private final String myTargetUrl; + + public KeyData(final String repoUID, final String sourceUrl, final String targetUrl) { + myRepoUrl = repoUID; + mySourceUrl = sourceUrl; + myTargetUrl = targetUrl; + } + + public String getRepoUrl() { + return myRepoUrl; + } + + public String getSourceUrl() { + return mySourceUrl; + } + + public String getTargetUrl() { + return myTargetUrl; + } + } + + public static class BranchCopyData implements Invertor { + private final String mySource; + private final String myTarget; + private final long mySourceRevision; + private final long myTargetRevision; + + public BranchCopyData(String source, long sourceRevision, String target, long targetRevision) { + mySource = source; + mySourceRevision = sourceRevision; + myTarget = target; + myTargetRevision = targetRevision; + } + + public String getSource() { + return mySource; + } + + public long getSourceRevision() { + return mySourceRevision; + } + + public String getTarget() { + return myTarget; + } + + public long getTargetRevision() { + return myTargetRevision; + } + + public BranchCopyData invertSelf() { + return new BranchCopyData(myTarget, myTargetRevision, mySource, mySourceRevision); + } + } + + private interface Invertor { + T invertSelf(); + } + + public void getFirstCopyPoint(final String repoUID, final String sourceUrl, final String targetUrl, Consumer> consumer) { + myCalculator.get(new KeyData(repoUID, sourceUrl, targetUrl), consumer); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/CopyData.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/CopyData.java new file mode 100644 index 000000000000..8839cc8d6864 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/CopyData.java @@ -0,0 +1,40 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.idea.svn.history; + +public class CopyData { + private final long myCopySourceRevision; + private final long myCopyTargetRevision; + private final boolean myTrunkSupposedCorrect; + + public CopyData(long copySourceRevision, long copyTargetRevision, boolean trunkSupposedCorrect) { + myCopySourceRevision = copySourceRevision; + myCopyTargetRevision = copyTargetRevision; + myTrunkSupposedCorrect = trunkSupposedCorrect; + } + + public long getCopySourceRevision() { + return myCopySourceRevision; + } + + public long getCopyTargetRevision() { + return myCopyTargetRevision; + } + + public boolean isTrunkSupposedCorrect() { + return myTrunkSupposedCorrect; + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/FirstInBranch.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/FirstInBranch.java index f17dc92aabd7..7496e97f473d 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/FirstInBranch.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/FirstInBranch.java @@ -15,6 +15,9 @@ */ package org.jetbrains.idea.svn.history; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.vcs.ConcurrentTasks; import com.intellij.util.Consumer; import org.jetbrains.idea.svn.SvnVcs; import org.tmatesoft.svn.core.*; @@ -26,27 +29,16 @@ import java.util.Map; public class FirstInBranch implements Runnable { private final SvnVcs myVcs; - private final String myRepositoryRoot; private final String myFullBranchUrl; private final String myFullTrunkUrl; private final String myBranchUrl; private final String myTrunkUrl; private final Consumer myConsumer; - private CopyData myResult; - private final boolean myPrimary; public FirstInBranch(final SvnVcs vcs, final String repositoryRoot, final String branchUrl, final String trunkUrl, final Consumer consumer) { - this(vcs, repositoryRoot, branchUrl, trunkUrl, consumer, true); - } - - public FirstInBranch(final SvnVcs vcs, final String repositoryRoot, final String branchUrl, final String trunkUrl, final Consumer consumer, final boolean primary) { - myPrimary = primary; myVcs = vcs; - myRepositoryRoot = repositoryRoot; myConsumer = consumer; - myResult = null; - myFullBranchUrl = branchUrl; myFullTrunkUrl = trunkUrl; myBranchUrl = relativePath(repositoryRoot, branchUrl); @@ -59,65 +51,84 @@ public class FirstInBranch implements Runnable { } public void run() { - final SVNLogClient logClient = myVcs.createLogClient(); - + final SVNURL branchURL; + final SVNURL trunkURL; try { - logClient.doLog(SVNURL.parseURIEncoded(myFullBranchUrl), null, SVNRevision.UNDEFINED, SVNRevision.HEAD, SVNRevision.create(0), true, true, 0, - new ISVNLogEntryHandler() { - public void handleLogEntry(final SVNLogEntry logEntry) throws SVNException { - final Map map = logEntry.getChangedPaths(); - for (Object o : map.values()) { - final SVNLogEntryPath path = (SVNLogEntryPath) o; - final String localPath = path.getPath(); - final String copyPath = path.getCopyPath(); - - if ('A' == path.getType() && - (myBranchUrl.equals(localPath) || SVNPathUtil.isAncestor(localPath, myBranchUrl)) && - (myTrunkUrl.equals(copyPath)) || SVNPathUtil.isAncestor(copyPath, myTrunkUrl)) { - myResult = new CopyData(path.getCopyRevision(), logEntry.getRevision(), myPrimary); - throw new MockException(); - } - } - } - }); - } - catch (MockException e) { - myConsumer.consume(myResult); - return; + branchURL = SVNURL.parseURIEncoded(myFullBranchUrl); + trunkURL = SVNURL.parseURIEncoded(myFullTrunkUrl); } catch (SVNException e) { - myConsumer.consume(myResult); + myConsumer.consume(null); + return; } - if (myPrimary) { - new FirstInBranch(myVcs, myRepositoryRoot, myFullTrunkUrl, myFullBranchUrl, myConsumer, false).run(); + + final ConcurrentTasks tasks = + new ConcurrentTasks(ProgressManager.getInstance().getProgressIndicator(), createTask(branchURL), createTask(trunkURL)); + tasks.compute(); + if (tasks.isResultKnown()) { + myConsumer.consume(tasks.getResult()); } else { - myConsumer.consume(myResult); + myConsumer.consume(null); } } - private static class MockException extends RuntimeException {} + private Consumer> createTask(final SVNURL branchURL) { + return new Consumer>() { + public void consume(final Consumer copyDataConsumer) { + final SVNLogClient logClient = myVcs.createLogClient(); + final long start1 = getStart(logClient, branchURL); + if (start1 > 0) { + final SVNRevision start1Rev = SVNRevision.create(start1); + try { + logClient.doLog(branchURL, null, SVNRevision.UNDEFINED, start1Rev, start1Rev, true, true, 1, new ISVNLogEntryHandler() { + public void handleLogEntry(SVNLogEntry logEntry) throws SVNException { + checkForCopy(logEntry, copyDataConsumer); + } + }); + } catch (SVNException e) { + // + } + } + } + }; + } - public static class CopyData { - private final long myCopySourceRevision; - private final long myCopyTargetRevision; - private final boolean myTrunkSupposedCorrect; + private static long getStart(final SVNLogClient logClient, final SVNURL url) { + final Ref myRevisionCandidate = new Ref(0L); + try { + logClient.doLog(url, null, SVNRevision.UNDEFINED, SVNRevision.HEAD, SVNRevision.create(0), + true, false, 0, new ISVNLogEntryHandler() { + public void handleLogEntry(SVNLogEntry logEntry) throws SVNException { + ProgressManager.checkCanceled(); - public CopyData(long copySourceRevision, long copyTargetRevision, boolean trunkSupposedCorrect) { - myCopySourceRevision = copySourceRevision; - myCopyTargetRevision = copyTargetRevision; - myTrunkSupposedCorrect = trunkSupposedCorrect; + myRevisionCandidate.set(logEntry.getRevision()); + } + }); } - - public long getCopySourceRevision() { - return myCopySourceRevision; + catch (SVNException e) { + // } + return myRevisionCandidate.get(); + } - public long getCopyTargetRevision() { - return myCopyTargetRevision; - } + private void checkForCopy(final SVNLogEntry logEntry, final Consumer result) { + final Map map = logEntry.getChangedPaths(); + for (Object o : map.values()) { + final SVNLogEntryPath path = (SVNLogEntryPath) o; + final String localPath = path.getPath(); + final String copyPath = path.getCopyPath(); - public boolean isTrunkSupposedCorrect() { - return myTrunkSupposedCorrect; + if ('A' == path.getType()) { + if ((myBranchUrl.equals(localPath) || SVNPathUtil.isAncestor(localPath, myBranchUrl)) && + (myTrunkUrl.equals(copyPath)) || SVNPathUtil.isAncestor(copyPath, myTrunkUrl)) { + result.consume(new CopyData(path.getCopyRevision(), logEntry.getRevision(), true)); + } else { + if ((myBranchUrl.equals(copyPath) || SVNPathUtil.isAncestor(copyPath, myBranchUrl)) && + (myTrunkUrl.equals(localPath)) || SVNPathUtil.isAncestor(localPath, myTrunkUrl)) { + result.consume(new CopyData(path.getCopyRevision(), logEntry.getRevision(), false)); + } + } + } } } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/SvnIntegrateChangesTask.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/SvnIntegrateChangesTask.java index a0ce583b920b..a2f81a3207e2 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/SvnIntegrateChangesTask.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/SvnIntegrateChangesTask.java @@ -56,16 +56,20 @@ public class SvnIntegrateChangesTask extends Task.Backgroundable { private final List myExceptions; - private final UpdateEventHandler myHandler; - private final IMerger myMerger; - private final ResolveWorker myResolveWorker; + private UpdateEventHandler myHandler; + private IMerger myMerger; + private ResolveWorker myResolveWorker; private FilePathImpl myMergeTarget; private final String myTitle; + private final MergerFactory myMergerFactory; + private final SVNURL myCurrentBranchUrl; private boolean myDryRun; public SvnIntegrateChangesTask(final SvnVcs vcs, final WorkingCopyInfo info, final MergerFactory mergerFactory, final SVNURL currentBranchUrl, final String title, final boolean dryRun) { super(vcs.getProject(), title, true, VcsConfiguration.getInstance(vcs.getProject()).getUpdateOption()); + myMergerFactory = mergerFactory; + myCurrentBranchUrl = currentBranchUrl; myDryRun = dryRun; myTitle = title; @@ -76,10 +80,6 @@ public class SvnIntegrateChangesTask extends Task.Backgroundable { myAccomulatedFiles = new UpdatedFilesReverseSide(UpdatedFiles.create()); myExceptions = new ArrayList(); - - myHandler = new IntegrateEventHandler(myVcs, ProgressManager.getInstance().getProgressIndicator()); - myMerger = mergerFactory.createMerger(myVcs, new File(info.getLocalPath()), myHandler, currentBranchUrl); - myResolveWorker = new ResolveWorker(myInfo.isUnderProjectRoot(), myProject); } private void indicatorOnStart() { @@ -93,6 +93,10 @@ public class SvnIntegrateChangesTask extends Task.Backgroundable { } public void run(@NotNull final ProgressIndicator indicator) { + myHandler = new IntegrateEventHandler(myVcs, ProgressManager.getInstance().getProgressIndicator()); + myMerger = myMergerFactory.createMerger(myVcs, new File(myInfo.getLocalPath()), myHandler, myCurrentBranchUrl); + myResolveWorker = new ResolveWorker(myInfo.isUnderProjectRoot(), myProject); + BlockReloadingUtil.block(); myProjectLevelVcsManager.startBackgroundVcsOperation(); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/BranchInfo.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/BranchInfo.java index 752b813a7aa9..0ffc7580dfa7 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/BranchInfo.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/BranchInfo.java @@ -157,6 +157,7 @@ public class BranchInfo { final String absoluteInTrunkPath = SVNPathUtil.append(myRepositoryRoot, path); if (! absoluteInTrunkPath.startsWith(myTrunkPathCorrespondingToLocalBranchPath)) { result.putValue(SvnMergeInfoCache.MergeCheckResult.NOT_EXISTS, path); + continue; } final String relativeToTrunkPath = absoluteInTrunkPath.substring(myTrunkPathCorrespondingToLocalBranchPath.length()); final String localPathInBranch = new File(branchPath, relativeToTrunkPath).getAbsolutePath(); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/SvnMergeInfoCache.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/SvnMergeInfoCache.java index d0863d83953c..93e69a85aa1f 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/SvnMergeInfoCache.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/SvnMergeInfoCache.java @@ -26,6 +26,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.dialogs.WCInfoWithBranches; import org.jetbrains.idea.svn.dialogs.WCPaths; +import org.jetbrains.idea.svn.history.CopyData; import org.jetbrains.idea.svn.history.FirstInBranch; import org.jetbrains.idea.svn.history.SvnChangeList; import org.tmatesoft.svn.core.internal.wc.DefaultSVNOptions; @@ -167,8 +168,8 @@ public class SvnMergeInfoCache { myRevision = -1; ApplicationManager.getApplication().executeOnPooledThread(new FirstInBranch(vcs, repositoryRoot, branchUrl, trunkUrl, - new Consumer() { - public void consume(FirstInBranch.CopyData copyData) { + new Consumer() { + public void consume(CopyData copyData) { if (copyData == null) return; myRevision = copyData.getCopySourceRevision(); if (myRevision != -1) { diff --git a/resources-en/src/inspectionDescriptions/InconsistentResourceBundle.html b/resources-en/src/inspectionDescriptions/InconsistentResourceBundle.html index 64bd39a4d8fa..d8d5003ec066 100644 --- a/resources-en/src/inspectionDescriptions/InconsistentResourceBundle.html +++ b/resources-en/src/inspectionDescriptions/InconsistentResourceBundle.html @@ -42,6 +42,6 @@ Property abc will be reported as unnecessarily inherited in the file messages_fr.properties.
-

New in 7

+ diff --git a/resources-en/src/inspectionDescriptions/LossyEncoding.html b/resources-en/src/inspectionDescriptions/LossyEncoding.html index f16214b4a4e5..c305effb8b9b 100644 --- a/resources-en/src/inspectionDescriptions/LossyEncoding.html +++ b/resources-en/src/inspectionDescriptions/LossyEncoding.html @@ -8,6 +8,6 @@ or configuring the Settings|General|File Encoding|Default encoding setting, or by setting up the file/directory encoding in the Settings|File/Directory Options|File Encodings. -

New in 7

+ \ No newline at end of file diff --git a/resources-en/src/inspectionDescriptions/MimeType.html b/resources-en/src/inspectionDescriptions/MimeType.html index 3c2dc26c20c5..bde57c9c4d7d 100644 --- a/resources-en/src/inspectionDescriptions/MimeType.html +++ b/resources-en/src/inspectionDescriptions/MimeType.html @@ -4,6 +4,6 @@ This inspection lets you control MIME type name validity. -

New in 7

+ \ No newline at end of file diff --git a/resources-en/src/inspectionDescriptions/TestOnlyProblems.html b/resources-en/src/inspectionDescriptions/TestOnlyProblems.html index 4053a86b76ea..b01ffd2ec11a 100644 --- a/resources-en/src/inspectionDescriptions/TestOnlyProblems.html +++ b/resources-en/src/inspectionDescriptions/TestOnlyProblems.html @@ -9,6 +9,6 @@
  • another @TestOnly-annotated method -

    New in 7

    + diff --git a/resources-en/src/inspectionDescriptions/UnusedLibrary.html b/resources-en/src/inspectionDescriptions/UnusedLibrary.html index df2874e02d16..8ef356706c8c 100644 --- a/resources-en/src/inspectionDescriptions/UnusedLibrary.html +++ b/resources-en/src/inspectionDescriptions/UnusedLibrary.html @@ -3,6 +3,6 @@ This inspection reports libraries attached to the specified inspection scope that are not used directly from code.

    -

    New in 7

    + diff --git a/xml/impl/resources/inspectionDescriptions/CheckDtdRefs.html b/xml/impl/resources/inspectionDescriptions/CheckDtdRefs.html index 6346786d41c3..cb613ce559a2 100644 --- a/xml/impl/resources/inspectionDescriptions/CheckDtdRefs.html +++ b/xml/impl/resources/inspectionDescriptions/CheckDtdRefs.html @@ -3,6 +3,6 @@ Checks consistency of DTD specific references, e.g. references to XML entity or to DTD element declaration. The validation works in DTD or XML file types.
    -

    New in 7

    + \ No newline at end of file diff --git a/xml/impl/resources/inspectionDescriptions/CheckEmptyScriptTag.html b/xml/impl/resources/inspectionDescriptions/CheckEmptyScriptTag.html index c2a0137c06a6..4b748cd8f2fb 100644 --- a/xml/impl/resources/inspectionDescriptions/CheckEmptyScriptTag.html +++ b/xml/impl/resources/inspectionDescriptions/CheckEmptyScriptTag.html @@ -3,7 +3,7 @@ Reports empty tags (like script) that do not work in some browsers. The validation works in html or jsp file types.
    -

    New in 7

    + \ No newline at end of file diff --git a/xml/impl/resources/inspectionDescriptions/CheckTagEmptyBody.html b/xml/impl/resources/inspectionDescriptions/CheckTagEmptyBody.html index 473ed1831830..f4f43054fa65 100644 --- a/xml/impl/resources/inspectionDescriptions/CheckTagEmptyBody.html +++ b/xml/impl/resources/inspectionDescriptions/CheckTagEmptyBody.html @@ -3,6 +3,6 @@ Reports empty tag body. The validation works in XML / JSP / JSPX / HTML/ XHTML file types.
    -

    New in 7

    + \ No newline at end of file diff --git a/xml/impl/resources/inspectionDescriptions/CheckXmlFileWithXercesValidator.html b/xml/impl/resources/inspectionDescriptions/CheckXmlFileWithXercesValidator.html index 673e56f6239c..4d9a080ba349 100644 --- a/xml/impl/resources/inspectionDescriptions/CheckXmlFileWithXercesValidator.html +++ b/xml/impl/resources/inspectionDescriptions/CheckXmlFileWithXercesValidator.html @@ -3,6 +3,6 @@ Thoroughly checks XML files for DTD / schema compliance using Xerces validator. The validation works in XML or XHTML file types.
    -

    New in 7

    + \ No newline at end of file diff --git a/xml/impl/resources/inspectionDescriptions/HtmlExtraClosingTag.html b/xml/impl/resources/inspectionDescriptions/HtmlExtraClosingTag.html index 0b7b48202d10..3a2cc854d19c 100644 --- a/xml/impl/resources/inspectionDescriptions/HtmlExtraClosingTag.html +++ b/xml/impl/resources/inspectionDescriptions/HtmlExtraClosingTag.html @@ -2,6 +2,6 @@ This inspection spots extra closing tag for the empty elements like IMG or BR.
    -

    New in 7

    + \ No newline at end of file diff --git a/xml/impl/resources/inspectionDescriptions/HtmlUnknownAttribute.html b/xml/impl/resources/inspectionDescriptions/HtmlUnknownAttribute.html index 2d0b657b9fbc..0de33643a9dc 100644 --- a/xml/impl/resources/inspectionDescriptions/HtmlUnknownAttribute.html +++ b/xml/impl/resources/inspectionDescriptions/HtmlUnknownAttribute.html @@ -3,6 +3,6 @@ This inspection highlight unknown HTML tag attributes as invalid and provides an ability to mark such attributes as Custom to not highlight them as invalid.
    -

    New in 7

    + \ No newline at end of file diff --git a/xml/impl/resources/inspectionDescriptions/HtmlUnknownTag.html b/xml/impl/resources/inspectionDescriptions/HtmlUnknownTag.html index 66a28ad5062b..d1a8e80f56ee 100644 --- a/xml/impl/resources/inspectionDescriptions/HtmlUnknownTag.html +++ b/xml/impl/resources/inspectionDescriptions/HtmlUnknownTag.html @@ -3,6 +3,6 @@ This inspection highlights unknown HTML tags and provides an ability to mark such tags as Custom to not highlight them as unknown in future.
    -

    New in 7

    + \ No newline at end of file diff --git a/xml/impl/resources/inspectionDescriptions/XmlWrongRootElement.html b/xml/impl/resources/inspectionDescriptions/XmlWrongRootElement.html index 918d93223ce0..81bf01cbb342 100644 --- a/xml/impl/resources/inspectionDescriptions/XmlWrongRootElement.html +++ b/xml/impl/resources/inspectionDescriptions/XmlWrongRootElement.html @@ -2,6 +2,6 @@ This inspection checks if root tag name is the same as in <doctype>.
    -

    New in 7

    + \ No newline at end of file