Merge remote-tracking branch 'origin/master'

This commit is contained in:
Konstantin Bulenkov
2013-09-30 12:31:46 +02:00
60 changed files with 1459 additions and 260 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
* Copyright 2000-2013 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.
@@ -74,7 +74,7 @@ public class RefactoringConflictsUtil {
public static void checkAccessibilityConflicts(@NotNull PsiMember member,
@PsiModifier.ModifierConstant @Nullable String newVisibility,
@Nullable PsiClass targetClass,
@NotNull Set<PsiMember> membersToMove,
@NotNull Set<? extends PsiMember> membersToMove,
@NotNull MultiMap<PsiElement, String> conflicts) {
PsiModifierList modifierListCopy = member.getModifierList();
if (modifierListCopy != null) {
@@ -97,7 +97,7 @@ public class RefactoringConflictsUtil {
public static void checkAccessibilityConflicts(@NotNull PsiMember member,
@Nullable PsiModifierList modifierListCopy,
@Nullable PsiClass targetClass,
@NotNull Set<PsiMember> membersToMove,
@NotNull Set<? extends PsiMember> membersToMove,
@NotNull MultiMap<PsiElement, String> conflicts) {
for (PsiReference psiReference : ReferencesSearch.search(member)) {
checkAccessibilityConflicts(psiReference, member, modifierListCopy, targetClass, membersToMove, conflicts);
@@ -108,7 +108,7 @@ public class RefactoringConflictsUtil {
@NotNull PsiMember member,
@Nullable PsiModifierList modifierListCopy,
@Nullable PsiClass targetClass,
@NotNull Set<PsiMember> membersToMove,
@NotNull Set<? extends PsiMember> membersToMove,
@NotNull MultiMap<PsiElement, String> conflicts) {
JavaPsiFacade manager = JavaPsiFacade.getInstance(member.getProject());
PsiElement ref = reference.getElement();
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2013 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.
@@ -47,7 +47,7 @@ public class RefactoringHierarchyUtil {
private RefactoringHierarchyUtil() {}
public static boolean willBeInTargetClass(PsiElement place,
@NotNull Set<PsiMember> membersToMove,
@NotNull Set<? extends PsiMember> membersToMove,
@Nullable PsiClass targetClass,
boolean includeSubclasses) {
PsiElement parent = place;
@@ -113,6 +113,7 @@ public abstract class PullUpDialogBase<Storage extends AbstractMemberInfoStorage
}
}
});
updateMemberInfo();
gbConstraints.gridy++;
panel.add(myClassCombo, gbConstraints);
@@ -123,7 +124,7 @@ public abstract class PullUpDialogBase<Storage extends AbstractMemberInfoStorage
protected abstract Class getPreselection();
private void updateMemberInfo() {
protected void updateMemberInfo() {
final Class targetClass = (Class) myClassCombo.getSelectedItem();
myMemberInfos = myMemberInfoStorage.getIntermediateMemberInfosList(targetClass);
}
@@ -53,7 +53,10 @@ public class GroovyLiteralCopyPasteProcessor extends StringLiteralCopyPasteProce
@Override
protected boolean isStringLiteral(@NotNull PsiElement token) {
ASTNode node = token.getNode();
return node != null && (TokenSets.STRING_LITERALS.contains(node.getElementType()) || node.getElementType() == GroovyElementTypes.GSTRING_INJECTION);
return node != null &&
(TokenSets.STRING_LITERALS.contains(node.getElementType()) ||
node.getElementType() == GroovyElementTypes.GSTRING_INJECTION ||
node.getElementType() == GroovyElementTypes.GSTRING_CONTENT);
}
@Nullable
@@ -74,11 +77,7 @@ public class GroovyLiteralCopyPasteProcessor extends StringLiteralCopyPasteProce
elementType = elementAtSelectionStart.getNode().getElementType();
}
if (!isStringLiteral(elementAtSelectionStart) &&
!isCharLiteral(elementAtSelectionStart) &&
!(elementType == GroovyElementTypes.GSTRING_INJECTION)) {
if (!isStringLiteral(elementAtSelectionStart)) {
return null;
}
@@ -103,9 +102,14 @@ public class GroovyLiteralCopyPasteProcessor extends StringLiteralCopyPasteProce
selectionStart++;
selectionEnd--;
}
if (selectionStart <= textRange.getStartOffset() || selectionEnd >= textRange.getEndOffset()) {
if (textRange.getLength() > 0 && (selectionStart <= textRange.getStartOffset() || selectionEnd >= textRange.getEndOffset())) {
return null;
}
if (elementType == GroovyElementTypes.GSTRING_CONTENT) {
elementAtSelectionStart = elementAtSelectionStart.getFirstChild();
}
return elementAtSelectionStart;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2013 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.
@@ -35,30 +35,24 @@ public class RemoveUnnecessaryBracesInGStringIntention extends Intention {
@NotNull
@Override
protected PsiElementPredicate getElementPredicate() {
return new MyPredicate();
return new PsiElementPredicate() {
public boolean satisfiedBy(PsiElement element) {
if (!(element instanceof GrString)) return false;
if (ErrorUtil.containsError(element)) return false;
for (GrStringInjection child : ((GrString)element).getInjections()) {
if (GrStringUtil.checkGStringInjectionForUnnecessaryBraces(child)) return true;
}
return false;
}
};
}
@Override
protected void processIntention(@NotNull PsiElement element, Project project, Editor editor) throws IncorrectOperationException {
GrStringUtil.removeUnnecessaryBracesInGString((GrString)element);
}
public static class MyPredicate implements PsiElementPredicate {
public boolean satisfiedBy(PsiElement element) {
return isIntentionAvailable(element);
}
public static boolean isIntentionAvailable(PsiElement element) {
if (!(element instanceof GrString)) return false;
if (ErrorUtil.containsError(element)) return false;
for (GrStringInjection child : ((GrString)element).getInjections()) {
if (GrStringUtil.checkGStringInjectionForUnnecessaryBraces(child)) return true;
}
return false;
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
* Copyright 2000-2013 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.
@@ -47,15 +47,13 @@ import java.util.List;
* @author Max Medvedev
*/
public abstract class ClosureCompleter {
private static ExtensionPointName<ClosureCompleter> EP_NAME = ExtensionPointName.create("org.intellij.groovy.closureCompleter");
private static final ExtensionPointName<ClosureCompleter> EP_NAME = ExtensionPointName.create("org.intellij.groovy.closureCompleter");
@Nullable
protected abstract List<ClosureParameterInfo> getParameterInfos(InsertionContext context,
PsiMethod method,
PsiSubstitutor substitutor,
Document document,
int offset,
PsiElement parent);
PsiElement place);
public static boolean runClosureCompletion(InsertionContext context,
PsiMethod method,
@@ -64,7 +62,7 @@ public abstract class ClosureCompleter {
int offset,
PsiElement parent) {
for (ClosureCompleter completer : EP_NAME.getExtensions()) {
final List<ClosureParameterInfo> parameterInfos = completer.getParameterInfos(context, method, substitutor, document, offset, parent);
final List<ClosureParameterInfo> parameterInfos = completer.getParameterInfos(context, method, substitutor, parent);
if (parameterInfos != null) {
runClosureTemplate(context, document, offset, substitutor, method, parameterInfos);
return true;
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
* Copyright 2000-2013 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.
@@ -16,7 +16,6 @@
package org.jetbrains.plugins.groovy.lang.completion;
import com.intellij.codeInsight.completion.InsertionContext;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.util.InheritanceUtil;
@@ -42,9 +41,7 @@ public class EachWithIndexClosureCompleter extends ClosureCompleter {
protected List<ClosureParameterInfo> getParameterInfos(InsertionContext context,
PsiMethod method,
PsiSubstitutor substitutor,
Document document,
int offset,
PsiElement parent) {
PsiElement place) {
final String name = method.getName();
if (!"eachWithIndex".equals(name)) return null;
@@ -63,7 +60,7 @@ public class EachWithIndexClosureCompleter extends ClosureCompleter {
final PsiType type = parameters[0].getType();
final PsiType collection = substitutor.substitute(type);
final PsiType iterable = getIteratedType(parent, collection);
final PsiType iterable = getIteratedType(place, collection);
if (iterable != null) {
return Arrays.asList(
new ClosureParameterInfo(iterable.getCanonicalText(), "entry"),
@@ -76,7 +73,7 @@ public class EachWithIndexClosureCompleter extends ClosureCompleter {
final Project project = context.getProject();
final PsiClass entry = JavaPsiFacade.getInstance(project).findClass(CommonClassNames.JAVA_UTIL_MAP_ENTRY, parent.getResolveScope());
final PsiClass entry = JavaPsiFacade.getInstance(project).findClass(CommonClassNames.JAVA_UTIL_MAP_ENTRY, place.getResolveScope());
if (entry == null) return null;
final PsiClassType entryType = JavaPsiFacade.getElementFactory(project).createType(entry, typeParams);
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
* Copyright 2000-2013 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.
@@ -16,7 +16,6 @@
package org.jetbrains.plugins.groovy.lang.completion;
import com.intellij.codeInsight.completion.InsertionContext;
import com.intellij.openapi.editor.Document;
import com.intellij.psi.*;
import com.intellij.psi.scope.BaseScopeProcessor;
import org.jetbrains.annotations.NotNull;
@@ -42,8 +41,6 @@ public class GdslClosureCompleter extends ClosureCompleter {
protected List<ClosureParameterInfo> getParameterInfos(InsertionContext context,
PsiMethod method,
PsiSubstitutor substitutor,
Document document,
int offset,
PsiElement place) {
final ArrayList<ClosureDescriptor> descriptors = new ArrayList<ClosureDescriptor>();
GrReferenceExpression ref = (GrReferenceExpression)place;
@@ -93,9 +93,9 @@ public abstract class GrDocCommentUtil {
PsiElement parent = owner.getParent();
ASTNode node = owner.getNode();
parent.getNode().addLeaf(GroovyTokenTypes.mNLS, "\n", node);
parent.getNode().addLeaf(GroovyTokenTypes.mNLS, "\n ", node);
PsiElement added = parent.addBefore(comment, node.getPsi());
PsiElement added = parent.addBefore(comment, owner);
assert added instanceof GrDocComment;
return (GrDocComment)added;
@@ -19,6 +19,7 @@ package org.jetbrains.plugins.groovy.lang.parser.parsing.statements.expressions.
import com.intellij.lang.PsiBuilder;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.GroovyBundle;
import org.jetbrains.plugins.groovy.lang.lexer.GroovyElementType;
@@ -33,6 +34,128 @@ import org.jetbrains.plugins.groovy.lang.parser.parsing.util.ParserUtils;
*/
public class CompoundStringExpression implements GroovyElementTypes {
private static final Logger LOG = Logger.getInstance(CompoundStringExpression.class);
private final PsiBuilder myBuilder;
private final GroovyParser myParser;
private final boolean myForRefExpr;
private final IElementType myBegin;
private final IElementType myContent;
private final IElementType myEnd;
private final IElementType mySimpleLiteral;
private final GroovyElementType myCompoundLiteral;
private final String myMessage;
private CompoundStringExpression(PsiBuilder builder,
GroovyParser parser,
boolean forRefExpr,
IElementType begin,
IElementType content,
IElementType end,
IElementType literal,
GroovyElementType compoundLiteral,
String message) {
myBuilder = builder;
myParser = parser;
myForRefExpr = forRefExpr;
myBegin = begin;
myContent = content;
myEnd = end;
mySimpleLiteral = literal;
myCompoundLiteral = compoundLiteral;
myMessage = message;
}
private boolean parse() {
PsiBuilder.Marker marker = myBuilder.mark();
final PsiBuilder.Marker marker2 = myBuilder.mark();
LOG.assertTrue(ParserUtils.getToken(myBuilder, myBegin));
if (mySimpleLiteral != null && myBuilder.getTokenType() == myEnd) {
myBuilder.advanceLexer();
finishSimpleLiteral(marker, marker2);
return true;
}
if (myBuilder.getTokenType() == myContent) {
final PsiBuilder.Marker contentMarker = myBuilder.mark();
myBuilder.advanceLexer();
if (myBuilder.getTokenType() == mDOLLAR || mySimpleLiteral == null) {
contentMarker.done(GSTRING_CONTENT);
}
else {
contentMarker.drop();
}
}
else {
processContent();
}
boolean hasInjection = myBuilder.getTokenType() == mDOLLAR;
while (myBuilder.getTokenType() == mDOLLAR) {
parseInjection();
processContent();
}
if (!ParserUtils.getToken(myBuilder, myEnd)) {
myBuilder.error(myMessage);
}
if (hasInjection || mySimpleLiteral == null) {
marker2.drop();
marker.done(myCompoundLiteral);
}
else {
finishSimpleLiteral(marker, marker2);
}
return hasInjection;
}
private void processContent() {
PsiBuilder.Marker marker = myBuilder.mark();
if (myBuilder.getTokenType() == myContent) {
myBuilder.advanceLexer();
}
else {
myBuilder.mark().done(myContent);
}
marker.done(GSTRING_CONTENT);
}
private void finishSimpleLiteral(PsiBuilder.Marker marker, PsiBuilder.Marker marker2) {
marker2.done(mySimpleLiteral);
if (myForRefExpr) {
marker.drop();
}
else {
marker.done(LITERAL);
}
}
/**
* Parses heredoc's content in GString
*
* @return nothing
*/
private boolean parseInjection() {
if (myBuilder.getTokenType() != mDOLLAR) return false;
final PsiBuilder.Marker injection = myBuilder.mark();
ParserUtils.getToken(myBuilder, mDOLLAR);
if (mIDENT.equals(myBuilder.getTokenType())) {
PathExpression.parse(myBuilder, myParser);
}
else if (mLCURLY.equals(myBuilder.getTokenType())) {
OpenOrClosableBlock.parseClosableBlock(myBuilder, myParser);
}
else {
ParserUtils.wrapError(myBuilder, GroovyBundle.message("identifier.or.block.expected"));
}
injection.done(GSTRING_INJECTION);
return true;
}
/**
* Groovy lexer does not smart enough to understand whether a regex contents injections or not. So the parser should do this job.
@@ -48,82 +171,15 @@ public class CompoundStringExpression implements GroovyElementTypes {
*
* @return true if there are any injections
*/
public static boolean parse(PsiBuilder builder,
GroovyParser parser,
public static boolean parse(@NotNull PsiBuilder builder,
@NotNull GroovyParser parser,
boolean forRefExpr,
IElementType begin,
IElementType content,
IElementType end,
@NotNull IElementType begin,
@NotNull IElementType content,
@NotNull IElementType end,
@Nullable IElementType literal,
GroovyElementType compoundLiteral, String message) {
PsiBuilder.Marker marker = builder.mark();
final PsiBuilder.Marker marker2 = builder.mark();
LOG.assertTrue(ParserUtils.getToken(builder, begin));
if (builder.getTokenType() == content) {
final PsiBuilder.Marker contentMarker = builder.mark();
builder.advanceLexer();
if (builder.getTokenType() == mDOLLAR || literal == null) {
contentMarker.done(GSTRING_CONTENT);
}
else {
contentMarker.drop();
}
}
boolean inj = builder.getTokenType() == mDOLLAR;
while (builder.getTokenType() == mDOLLAR || builder.getTokenType() == content) {
if (builder.getTokenType() == mDOLLAR) {
parseInjection(builder, parser);
}
else {
ParserUtils.eatElement(builder, GSTRING_CONTENT);
}
}
if (!ParserUtils.getToken(builder, end)) {
builder.error(message);
}
if (inj || literal == null) {
marker2.drop();
marker.done(compoundLiteral);
}
else {
marker2.done(literal);
if (forRefExpr) {
marker.drop();
}
else {
marker.done(LITERAL);
}
}
return inj;
}
/**
* Parses heredoc's content in GString
*
* @param builder given builder
* @return nothing
*/
private static boolean parseInjection(PsiBuilder builder, GroovyParser parser) {
if (builder.getTokenType() != mDOLLAR) return false;
final PsiBuilder.Marker injection = builder.mark();
ParserUtils.getToken(builder, mDOLLAR);
if (mIDENT.equals(builder.getTokenType())) {
PathExpression.parse(builder, parser);
}
else if (mLCURLY.equals(builder.getTokenType())) {
OpenOrClosableBlock.parseClosableBlock(builder, parser);
}
else {
ParserUtils.wrapError(builder, GroovyBundle.message("identifier.or.block.expected"));
}
injection.done(GSTRING_INJECTION);
return true;
@NotNull GroovyElementType compoundLiteral,
@NotNull String message) {
return new CompoundStringExpression(builder, parser, forRefExpr, begin, content, end, literal, compoundLiteral, message).parse();
}
}
@@ -19,8 +19,7 @@ import com.intellij.lang.ASTNode;
import com.intellij.psi.LiteralTextEscaper;
import com.intellij.psi.PsiLanguageInjectionHost;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrString;
import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrStringContent;
import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyPsiElementImpl;
import org.jetbrains.plugins.groovy.lang.psi.util.GrStringUtil;
@@ -48,10 +47,10 @@ public class GrStringContentImpl extends GroovyPsiElementImpl implements GrStrin
@Override
public GrStringContentImpl updateText(@NotNull String text) {
final GrString fromText = (GrString)GroovyPsiElementFactory.getInstance(getProject()).createExpressionFromText("\"${0}" + text + "\"");
final GrStringContent content = fromText.getContents()[0];
getNode().replaceChild(getFirstChild().getNode(), content.getFirstChild().getNode());
if (getFirstChild() != null) {
getFirstChild().delete();
}
getNode().addLeaf(GroovyTokenTypes.mGSTRING_CONTENT, text, null);
return this;
}
@@ -61,7 +61,10 @@ public abstract class GrReferenceListImpl extends GrStubElementBase<GrReferenceL
keyword.delete();
}
else {
PsiElement comma = PsiUtil.skipWhitespacesAndComments(psi, refs[0] == psi, true);
boolean forward = refs[0] == psi;
PsiElement comma = forward
? PsiUtil.skipWhitespacesAndComments(psi.getNextSibling(), forward, true)
: PsiUtil.skipWhitespacesAndComments(psi.getPrevSibling(), forward, true);
if (comma != null && comma.getNode().getElementType() == GroovyTokenTypes.mCOMMA) {
comma.delete();
}
@@ -468,9 +468,10 @@ public class GrStringUtil {
if (literal instanceof GrString) {
final GrStringInjection[] injections = ((GrString)literal).getInjections();
if (injections.length > 0) {
if (injections[injections.length - 1].getExpression() != null) {
if (!checkBraceIsUnnecessary(injections[injections.length - 1].getExpression(), injection.getNextSibling())) {
wrapInjection(injections[injections.length - 1]);
GrStringInjection last = injections[injections.length - 1];
if (last.getExpression() != null) {
if (!checkBraceIsUnnecessary(last.getExpression(), injection.getNextSibling())) {
wrapInjection(last);
}
}
}
@@ -488,8 +489,9 @@ public class GrStringUtil {
final GrExpression expression = factory.createExpressionFromText("\"\"\"${}" + literalText + "\"\"\"");
expression.getFirstChild().delete();
expression.getFirstChild().delete();
expression.getFirstChild().delete();//quote
expression.getFirstChild().delete();//empty gstring content
expression.getFirstChild().delete();//empty injection
final ASTNode node = grString.getNode();
if (expression.getFirstChild() != null) {
@@ -541,12 +543,12 @@ public class GrStringUtil {
if (!(statements[0] instanceof GrReferenceExpression)) return false;
final PsiElement next = injection.getNextSibling();
return checkBraceIsUnnecessary(statements[0], next);
return checkBraceIsUnnecessary(statements[0], injection.getNextSibling());
}
private static boolean checkBraceIsUnnecessary(GrStatement injected, PsiElement next) {
if (next.getTextLength() == 0) next = next.getNextSibling();
char nextChar = next.getText().charAt(0);
if (nextChar == '"' || nextChar == '$') {
return true;
@@ -561,8 +563,14 @@ public class GrStringUtil {
}
if (!(gString instanceof GrString)) return false;
final PsiElement child = gString.getChildren()[0];
if (!(child instanceof GrStringInjection)) return false;
PsiElement child = gString.getFirstChild();
if (!(child.getNode().getElementType() == mGSTRING_BEGIN)) return false;
child = child.getNextSibling();
if (child == null || !(child instanceof GrStringContent)) return false;
child = child.getNextSibling();
if (child == null || !(child instanceof GrStringInjection)) return false;
final PsiElement refExprCopy = ((GrStringInjection)child).getExpression();
if (!(refExprCopy instanceof GrReferenceExpression)) return false;
@@ -0,0 +1,262 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.groovy.refactoring;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.light.LightElement;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.PsiSearchScopeUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.util.*;
import com.intellij.usageView.UsageInfo;
import com.intellij.util.VisibilityUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.FilteringIterator;
import com.intellij.util.containers.HashSet;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.GrReferenceElement;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
import org.jetbrains.plugins.groovy.lang.psi.GroovyRecursiveElementVisitor;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrNewExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrAnonymousClassDefinition;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMember;
import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.imports.GrImportStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement;
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
import java.util.Collection;
import java.util.List;
import java.util.Set;
/**
* Created by Max Medvedev on 9/28/13
*/
public class GrRefactoringConflictsUtil {
private GrRefactoringConflictsUtil() { }
public static void analyzeAccessibilityConflicts(@NotNull Set<GrMember> membersToMove,
@NotNull PsiClass targetClass,
@NotNull MultiMap<PsiElement, String> conflicts,
@Nullable String newVisibility) {
analyzeAccessibilityConflicts(membersToMove, targetClass, conflicts, newVisibility, targetClass, null);
}
public static void analyzeAccessibilityConflicts(@NotNull Set<GrMember> membersToMove,
@Nullable PsiClass targetClass,
@NotNull MultiMap<PsiElement, String> conflicts,
@Nullable String newVisibility,
@NotNull PsiElement context,
@Nullable Set<PsiMethod> abstractMethods) {
if (VisibilityUtil.ESCALATE_VISIBILITY.equals(newVisibility)) { //Still need to check for access object
newVisibility = PsiModifier.PUBLIC;
}
for (GrMember member : membersToMove) {
checkUsedElements(member, member, membersToMove, abstractMethods, targetClass, context, conflicts);
RefactoringConflictsUtil.checkAccessibilityConflicts(member, newVisibility, targetClass, membersToMove, conflicts);
}
}
public static void checkUsedElements(PsiMember member,
PsiElement scope,
@NotNull Set<GrMember> membersToMove,
@Nullable Set<PsiMethod> abstractMethods,
@Nullable PsiClass targetClass,
@NotNull PsiElement context,
MultiMap<PsiElement, String> conflicts) {
final Set<PsiMember> moving = new HashSet<PsiMember>(membersToMove);
if (abstractMethods != null) {
moving.addAll(abstractMethods);
}
if (scope instanceof GrReferenceExpression) {
GrReferenceExpression refExpr = (GrReferenceExpression)scope;
PsiElement refElement = refExpr.resolve();
if (refElement instanceof PsiMember) {
if (!RefactoringHierarchyUtil.willBeInTargetClass(refElement, moving, targetClass, false)) {
GrExpression qualifier = refExpr.getQualifierExpression();
PsiClass accessClass = (PsiClass)(qualifier != null ? PsiUtil.getAccessObjectClass(
qualifier).getElement() : null);
RefactoringConflictsUtil.checkAccessibility((PsiMember)refElement, context, accessClass, member, conflicts);
}
}
}
else if (scope instanceof GrNewExpression) {
final GrNewExpression newExpression = (GrNewExpression)scope;
final GrAnonymousClassDefinition anonymousClass = newExpression.getAnonymousClassDefinition();
if (anonymousClass != null) {
if (!RefactoringHierarchyUtil.willBeInTargetClass(anonymousClass, moving, targetClass, false)) {
RefactoringConflictsUtil.checkAccessibility(anonymousClass, context, anonymousClass, member, conflicts);
}
}
else {
final PsiMethod refElement = newExpression.resolveMethod();
if (refElement != null) {
if (!RefactoringHierarchyUtil.willBeInTargetClass(refElement, moving, targetClass, false)) {
RefactoringConflictsUtil.checkAccessibility(refElement, context, null, member, conflicts);
}
}
}
}
else if (scope instanceof GrCodeReferenceElement) {
GrCodeReferenceElement refExpr = (GrCodeReferenceElement)scope;
PsiElement refElement = refExpr.resolve();
if (refElement instanceof PsiMember) {
if (!RefactoringHierarchyUtil.willBeInTargetClass(refElement, moving, targetClass, false)) {
RefactoringConflictsUtil.checkAccessibility((PsiMember)refElement, context, null, member, conflicts);
}
}
}
for (PsiElement child : scope.getChildren()) {
if (child instanceof PsiWhiteSpace || child instanceof PsiComment) continue;
checkUsedElements(member, child, membersToMove, abstractMethods, targetClass, context, conflicts);
}
}
public static void analyzeModuleConflicts(final Project project,
final Collection<? extends PsiElement> scopes,
final UsageInfo[] usages,
final PsiElement target,
final MultiMap<PsiElement,String> conflicts) {
if (scopes == null) return;
final VirtualFile vFile = PsiUtilCore.getVirtualFile(target);
if (vFile == null) return;
List<GroovyPsiElement> groovyScopes =
ContainerUtil.collect(scopes.iterator(), new FilteringIterator.InstanceOf<GroovyPsiElement>(GroovyPsiElement.class));
analyzeModuleConflicts(project, groovyScopes, usages, vFile, conflicts);
scopes.removeAll(groovyScopes);
RefactoringConflictsUtil.analyzeModuleConflicts(project, scopes, usages, vFile, conflicts);
}
public static void analyzeModuleConflicts(final Project project,
final Collection<? extends GroovyPsiElement> scopes,
final UsageInfo[] usages,
final VirtualFile vFile,
final MultiMap<PsiElement, String> conflicts) {
if (scopes == null) return;
for (final PsiElement scope : scopes) {
if (scope instanceof PsiPackage) return;
}
final Module targetModule = ModuleUtilCore.findModuleForFile(vFile, project);
if (targetModule == null) return;
final GlobalSearchScope resolveScope = GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(targetModule);
final HashSet<PsiElement> reported = new HashSet<PsiElement>();
for (final GroovyPsiElement scope : scopes) {
scope.accept(new GroovyRecursiveElementVisitor() {
@Override
public void visitCodeReferenceElement(GrCodeReferenceElement refElement) {
super.visitCodeReferenceElement(refElement);
visit(refElement);
}
@Override
public void visitReferenceExpression(GrReferenceExpression reference) {
super.visitReferenceExpression(reference);
visit(reference);
}
private void visit(GrReferenceElement<? extends GroovyPsiElement> reference) {
final PsiElement resolved = reference.resolve();
if (resolved != null &&
!reported.contains(resolved) &&
!CommonRefactoringUtil.isAncestor(resolved, scopes) &&
!PsiSearchScopeUtil.isInScope(resolveScope, resolved) &&
!(resolved instanceof LightElement)) {
final String scopeDescription = RefactoringUIUtil.getDescription(ConflictsUtil.getContainer(reference), true);
final String message = RefactoringBundle.message("0.referenced.in.1.will.not.be.accessible.in.module.2",
RefactoringUIUtil.getDescription(resolved, true),
scopeDescription,
CommonRefactoringUtil.htmlEmphasize(targetModule.getName()));
conflicts.putValue(resolved, CommonRefactoringUtil.capitalize(message));
reported.add(resolved);
}
}
});
}
boolean isInTestSources = ModuleRootManager.getInstance(targetModule).getFileIndex().isInTestSourceContent(vFile);
NextUsage:
for (UsageInfo usage : usages) {
final PsiElement element = usage.getElement();
if (element != null && PsiTreeUtil.getParentOfType(element, GrImportStatement.class, false) == null) {
for (PsiElement scope : scopes) {
if (PsiTreeUtil.isAncestor(scope, element, false)) continue NextUsage;
}
final GlobalSearchScope resolveScope1 = element.getResolveScope();
if (!resolveScope1.isSearchInModuleContent(targetModule, isInTestSources)) {
final PsiFile usageFile = element.getContainingFile();
PsiElement container;
if (usageFile instanceof PsiJavaFile) {
container = ConflictsUtil.getContainer(element);
}
else {
container = usageFile;
}
final String scopeDescription = RefactoringUIUtil.getDescription(container, true);
final VirtualFile usageVFile = usageFile.getVirtualFile();
if (usageVFile != null) {
Module module = ProjectRootManager.getInstance(project).getFileIndex().getModuleForFile(usageVFile);
if (module != null) {
final String message;
final PsiElement referencedElement;
if (usage instanceof MoveRenameUsageInfo) {
referencedElement = ((MoveRenameUsageInfo)usage).getReferencedElement();
}
else {
referencedElement = usage.getElement();
}
assert referencedElement != null : usage;
if (module == targetModule && isInTestSources) {
message = RefactoringBundle.message("0.referenced.in.1.will.not.be.accessible.from.production.of.module.2",
RefactoringUIUtil.getDescription(referencedElement, true),
scopeDescription,
CommonRefactoringUtil.htmlEmphasize(module.getName()));
}
else {
message = RefactoringBundle.message("0.referenced.in.1.will.not.be.accessible.from.module.2",
RefactoringUIUtil.getDescription(referencedElement, true),
scopeDescription,
CommonRefactoringUtil.htmlEmphasize(module.getName()));
}
conflicts.putValue(referencedElement, CommonRefactoringUtil.capitalize(message));
}
}
}
}
}
}
}
@@ -31,14 +31,13 @@ import com.intellij.util.IncorrectOperationException;
import com.intellij.util.VisibilityUtil;
import com.intellij.util.containers.HashSet;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrConstructorInvocation;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
import java.util.Arrays;
import java.util.Iterator;
@@ -92,7 +91,7 @@ class GrChangeSignatureConflictSearcher {
PsiClass accessObjectClass = null;
GrExpression qualifier = ((GrReferenceExpression)element).getQualifierExpression();
if (qualifier != null) {
accessObjectClass = getAccessObjectClass(qualifier);
accessObjectClass = (PsiClass)PsiUtil.getAccessObjectClass(qualifier).getElement();
}
PsiResolveHelper helper = JavaPsiFacade.getInstance(element.getProject()).getResolveHelper();
@@ -112,23 +111,6 @@ class GrChangeSignatureConflictSearcher {
}
}
@Nullable
private static PsiClass getAccessObjectClass(GrExpression expression) {
if (expression instanceof GrConstructorInvocation) return null;
PsiType type = expression.getType();
if (type instanceof PsiClassType) {
return ((PsiClassType)type).resolveGenerics().getElement();
}
if (type == null && expression instanceof PsiReferenceExpression) {
JavaResolveResult resolveResult = ((PsiReferenceExpression)expression).advancedResolve(false);
if (resolveResult.getElement() instanceof PsiClass) {
return (PsiClass)resolveResult.getElement();
}
}
return null;
}
private void addMethodConflicts(MultiMap<PsiElement, String> conflicts) {
try {
GrMethod prototype;
@@ -0,0 +1,376 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.groovy.refactoring.memberPullUp;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.search.searches.ClassInheritorsSearch;
import com.intellij.psi.util.*;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.classMembers.MemberInfoBase;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.refactoring.util.RefactoringHierarchyUtil;
import com.intellij.refactoring.util.RefactoringUIUtil;
import com.intellij.refactoring.util.classMembers.InterfaceContainmentVerifier;
import com.intellij.usageView.UsageInfo;
import com.intellij.util.VisibilityUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.GrReferenceElement;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMember;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
import org.jetbrains.plugins.groovy.refactoring.GrRefactoringConflictsUtil;
import org.jetbrains.plugins.groovy.refactoring.classMembers.GrClassMemberReferenceVisitor;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Created by Max Medvedev on 9/28/13
*/
public class GrPullUpConflictsUtil {
private GrPullUpConflictsUtil() {}
public static MultiMap<PsiElement, String> checkConflicts(MemberInfoBase<? extends GrMember>[] infos,
PsiClass subclass,
@Nullable PsiClass superClass,
@NotNull PsiPackage targetPackage,
@NotNull PsiDirectory targetDirectory,
final InterfaceContainmentVerifier interfaceContainmentVerifier) {
return checkConflicts(infos, subclass, superClass, targetPackage, targetDirectory, interfaceContainmentVerifier, true);
}
public static MultiMap<PsiElement, String> checkConflicts(final MemberInfoBase<? extends GrMember>[] infos,
@NotNull final PsiClass subclass,
@Nullable PsiClass superClass,
@NotNull final PsiPackage targetPackage,
@NotNull PsiDirectory targetDirectory,
final InterfaceContainmentVerifier interfaceContainmentVerifier,
boolean movedMembers2Super) {
final PsiElement targetRepresentativeElement;
final boolean isInterfaceTarget;
if (superClass != null) {
isInterfaceTarget = superClass.isInterface();
targetRepresentativeElement = superClass;
}
else {
isInterfaceTarget = false;
targetRepresentativeElement = targetDirectory;
}
final Set<GrMember> movedMembers = ContainerUtil.newHashSet();
final Set<GrMethod> abstractMethods = ContainerUtil.newHashSet();
for (MemberInfoBase<? extends GrMember> info : infos) {
GrMember member = info.getMember();
if (member instanceof GrMethod) {
if (!info.isToAbstract() && !isInterfaceTarget) {
movedMembers.add(member);
}
else {
abstractMethods.add((GrMethod)member);
}
}
else {
movedMembers.add(member);
}
}
final Set<PsiMethod> allAbstractMethods = new HashSet<PsiMethod>(abstractMethods);
if (superClass != null) {
for (PsiMethod method : subclass.getMethods()) {
if (!movedMembers.contains(method) && !method.hasModifierProperty(PsiModifier.PRIVATE)) {
if (method.findSuperMethods(superClass).length > 0) {
allAbstractMethods.add(method);
}
}
}
}
final MultiMap<PsiElement, String> conflicts = new MultiMap<PsiElement, String>();
GrRefactoringConflictsUtil.analyzeAccessibilityConflicts(movedMembers, superClass, conflicts, VisibilityUtil.ESCALATE_VISIBILITY, targetRepresentativeElement,
allAbstractMethods);
if (superClass != null) {
if (movedMembers2Super) {
checkSuperclassMembers(superClass, infos, conflicts);
if (isInterfaceTarget) {
checkInterfaceTarget(infos, conflicts);
}
} else {
final String qualifiedName = superClass.getQualifiedName();
assert qualifiedName != null;
if (superClass.hasModifierProperty(PsiModifier.PACKAGE_LOCAL)) {
if (!Comparing.strEqual(StringUtil.getPackageName(qualifiedName), targetPackage.getQualifiedName())) {
conflicts.putValue(superClass, RefactoringUIUtil.getDescription(superClass, true) + " won't be accessible from " +RefactoringUIUtil.getDescription(targetPackage, true));
}
}
}
}
// check if moved methods use other members in the classes between Subclass and Superclass
List<PsiElement> checkModuleConflictsList = new ArrayList<PsiElement>();
for (PsiMember member : movedMembers) {
if (member instanceof PsiMethod || member instanceof PsiClass && !(member instanceof PsiCompiledElement)) {
GrClassMemberReferenceVisitor visitor =
movedMembers2Super? new ConflictingUsagesOfSubClassMembers(member, movedMembers, abstractMethods, subclass, superClass,
superClass != null ? null : targetPackage, conflicts,
interfaceContainmentVerifier)
: new ConflictingUsagesOfSuperClassMembers(member, subclass, targetPackage, movedMembers, conflicts);
((GroovyPsiElement)member).accept(visitor);
}
checkModuleConflictsList.add(member);
}
for (final PsiMethod method : abstractMethods) {
ContainerUtil.addIfNotNull(checkModuleConflictsList, method.getParameterList());
ContainerUtil.addIfNotNull(checkModuleConflictsList, method.getReturnTypeElement());
ContainerUtil.addIfNotNull(checkModuleConflictsList, method.getTypeParameterList());
}
GrRefactoringConflictsUtil.analyzeModuleConflicts(subclass.getProject(), checkModuleConflictsList, new UsageInfo[0], targetRepresentativeElement, conflicts);
final String fqName = subclass.getQualifiedName();
final String packageName;
if (fqName != null) {
packageName = StringUtil.getPackageName(fqName);
} else {
final PsiFile psiFile = PsiTreeUtil.getParentOfType(subclass, PsiFile.class);
if (psiFile instanceof PsiClassOwner) {
packageName = ((PsiClassOwner)psiFile).getPackageName();
} else {
packageName = null;
}
}
final boolean toDifferentPackage = !Comparing.strEqual(targetPackage.getQualifiedName(), packageName);
for (final GrMethod abstractMethod : abstractMethods) {
abstractMethod.accept(new GrClassMemberReferenceVisitor(subclass) {
@Override
protected void visitClassMemberReferenceElement(GrMember classMember, GrReferenceElement classMemberReference) {
if (classMember != null && willBeMoved(classMember, movedMembers)) {
boolean isAccessible = false;
if (classMember.hasModifierProperty(PsiModifier.PRIVATE)) {
isAccessible = true;
}
else if (classMember.hasModifierProperty(PsiModifier.PACKAGE_LOCAL) &&
toDifferentPackage) {
isAccessible = true;
}
if (isAccessible) {
String message = RefactoringUIUtil.getDescription(abstractMethod, false) +
" uses " +
RefactoringUIUtil.getDescription(classMember, true) +
" which won't be accessible from the subclass.";
message = CommonRefactoringUtil.capitalize(message);
conflicts.putValue(classMember, message);
}
}
}
});
if (abstractMethod.hasModifierProperty(PsiModifier.PACKAGE_LOCAL) && toDifferentPackage) {
if (!isInterfaceTarget) {
String message = "Can't make " + RefactoringUIUtil.getDescription(abstractMethod, false) +
" abstract as it won't be accessible from the subclass.";
message = CommonRefactoringUtil.capitalize(message);
conflicts.putValue(abstractMethod, message);
}
}
}
return conflicts;
}
private static void checkInterfaceTarget(MemberInfoBase<? extends GrMember>[] infos, MultiMap<PsiElement, String> conflictsList) {
for (MemberInfoBase<? extends GrMember> info : infos) {
GrMember member = info.getMember();
if (member instanceof PsiField || member instanceof PsiClass) {
if (!member.hasModifierProperty(PsiModifier.STATIC) && !(member instanceof PsiClass && ((PsiClass)member).isInterface())) {
String message = RefactoringBundle.message("0.is.not.static.it.cannot.be.moved.to.the.interface", RefactoringUIUtil.getDescription(member, false));
message = CommonRefactoringUtil.capitalize(message);
conflictsList.putValue(member, message);
}
}
if (member instanceof PsiField && ((PsiField)member).getInitializer() == null) {
String message = RefactoringBundle.message("0.is.not.initialized.in.declaration.such.fields.are.not.allowed.in.interfaces",
RefactoringUIUtil.getDescription(member, false));
conflictsList.putValue(member, CommonRefactoringUtil.capitalize(message));
}
}
}
private static void checkSuperclassMembers(PsiClass superClass,
MemberInfoBase<? extends GrMember>[] infos,
MultiMap<PsiElement, String> conflictsList) {
for (MemberInfoBase<? extends GrMember> info : infos) {
GrMember member = info.getMember();
boolean isConflict = false;
if (member instanceof PsiField) {
String name = member.getName();
isConflict = superClass.findFieldByName(name, false) != null;
}
else if (member instanceof PsiMethod) {
PsiSubstitutor superSubstitutor = TypeConversionUtil
.getSuperClassSubstitutor(superClass, member.getContainingClass(), PsiSubstitutor.EMPTY);
MethodSignature signature = ((PsiMethod) member).getSignature(superSubstitutor);
final PsiMethod superClassMethod = MethodSignatureUtil.findMethodBySignature(superClass, signature, false);
isConflict = superClassMethod != null;
}
if (isConflict) {
String message = RefactoringBundle.message("0.already.contains.a.1",
RefactoringUIUtil.getDescription(superClass, false),
RefactoringUIUtil.getDescription(member, false));
message = CommonRefactoringUtil.capitalize(message);
conflictsList.putValue(superClass, message);
}
if (member instanceof PsiMethod) {
final PsiMethod method = (PsiMethod)member;
final PsiModifierList modifierList = method.getModifierList();
if (!modifierList.hasModifierProperty(PsiModifier.PRIVATE)) {
for (PsiClass subClass : ClassInheritorsSearch.search(superClass)) {
if (method.getContainingClass() != subClass) {
MethodSignature signature = ((PsiMethod) member).getSignature(TypeConversionUtil.getSuperClassSubstitutor(superClass, subClass, PsiSubstitutor.EMPTY));
final PsiMethod wouldBeOverriden = MethodSignatureUtil.findMethodBySignature(subClass, signature, false);
if (wouldBeOverriden != null && VisibilityUtil.compare(VisibilityUtil.getVisibilityModifier(wouldBeOverriden.getModifierList()),
VisibilityUtil.getVisibilityModifier(modifierList)) > 0) {
conflictsList.putValue(wouldBeOverriden, CommonRefactoringUtil.capitalize(RefactoringUIUtil.getDescription(method, true) + " in super class would clash with local method from " + RefactoringUIUtil.getDescription(subClass, true)));
}
}
}
}
}
}
}
private static boolean willBeMoved(PsiElement element, Set<GrMember> movedMembers) {
PsiElement parent = element;
while (parent != null) {
if (movedMembers.contains(parent)) return true;
parent = parent.getParent();
}
return false;
}
private static class ConflictingUsagesOfSuperClassMembers extends GrClassMemberReferenceVisitor {
private PsiMember myMember;
private PsiClass mySubClass;
private PsiPackage myTargetPackage;
private Set<GrMember> myMovedMembers;
private MultiMap<PsiElement, String> myConflicts;
public ConflictingUsagesOfSuperClassMembers(PsiMember member, PsiClass aClass,
PsiPackage targetPackage,
Set<GrMember> movedMembers,
MultiMap<PsiElement, String> conflicts) {
super(aClass);
myMember = member;
mySubClass = aClass;
myTargetPackage = targetPackage;
myMovedMembers = movedMembers;
myConflicts = conflicts;
}
@Override
protected void visitClassMemberReferenceElement(GrMember classMember, GrReferenceElement ref) {
if (classMember != null && !willBeMoved(classMember, myMovedMembers)) {
final PsiClass containingClass = classMember.getContainingClass();
if (containingClass != null &&
!PsiUtil.isAccessibleFromPackage(classMember, myTargetPackage) &&
(classMember.hasModifierProperty(PsiModifier.PACKAGE_LOCAL) ||
classMember.hasModifierProperty(PsiModifier.PROTECTED) && !mySubClass.isInheritor(containingClass, true))) {
myConflicts.putValue(myMember, RefactoringUIUtil.getDescription(classMember, true) + " won't be accessible");
}
}
}
}
private static class ConflictingUsagesOfSubClassMembers extends GrClassMemberReferenceVisitor {
private final PsiElement myScope;
private final Set<GrMember> myMovedMembers;
private final Set<GrMethod> myAbstractMethods;
private final PsiClass mySubclass;
private final PsiClass mySuperClass;
private final PsiPackage myTargetPackage;
private final MultiMap<PsiElement, String> myConflictsList;
private final InterfaceContainmentVerifier myInterfaceContainmentVerifier;
ConflictingUsagesOfSubClassMembers(PsiElement scope,
Set<GrMember> movedMembers, Set<GrMethod> abstractMethods,
PsiClass subclass, PsiClass superClass,
PsiPackage targetPackage, MultiMap<PsiElement, String> conflictsList,
InterfaceContainmentVerifier interfaceContainmentVerifier) {
super(subclass);
myScope = scope;
myMovedMembers = movedMembers;
myAbstractMethods = abstractMethods;
mySubclass = subclass;
mySuperClass = superClass;
myTargetPackage = targetPackage;
myConflictsList = conflictsList;
myInterfaceContainmentVerifier = interfaceContainmentVerifier;
}
@Override
protected void visitClassMemberReferenceElement(GrMember classMember, GrReferenceElement ref) {
if (classMember != null && RefactoringHierarchyUtil.isMemberBetween(mySuperClass, mySubclass, classMember)) {
if (classMember.hasModifierProperty(PsiModifier.STATIC) && !willBeMoved(classMember, myMovedMembers)) {
final boolean isAccessible = mySuperClass != null ? PsiUtil.isAccessible(classMember, mySuperClass, null) :
myTargetPackage != null ? PsiUtil.isAccessibleFromPackage(classMember, myTargetPackage):
classMember.hasModifierProperty(PsiModifier.PUBLIC);
if (!isAccessible) {
String message = RefactoringBundle.message("0.uses.1.which.is.not.accessible.from.the.superclass",
RefactoringUIUtil.getDescription(myScope, false),
RefactoringUIUtil.getDescription(classMember, true));
message = CommonRefactoringUtil.capitalize(message);
myConflictsList.putValue(classMember, message);
}
return;
}
if (!myAbstractMethods.contains(classMember) && !willBeMoved(classMember, myMovedMembers)) {
if (!existsInSuperClass(classMember)) {
String message = RefactoringBundle.message("0.uses.1.which.is.not.moved.to.the.superclass",
RefactoringUIUtil.getDescription(myScope, false),
RefactoringUIUtil.getDescription(classMember, true));
message = CommonRefactoringUtil.capitalize(message);
myConflictsList.putValue(classMember, message);
}
}
}
}
private boolean existsInSuperClass(PsiElement classMember) {
if (!(classMember instanceof PsiMethod)) return false;
final PsiMethod method = ((PsiMethod)classMember);
if (myInterfaceContainmentVerifier.checkedInterfacesContain(method)) return true;
if (mySuperClass == null) return false;
final PsiMethod methodBySignature = mySuperClass.findMethodBySignature(method, true);
return methodBySignature != null;
}
}
}
@@ -46,8 +46,6 @@ import org.jetbrains.plugins.groovy.refactoring.classMembers.GrMemberSelectionTa
import javax.swing.*;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.List;
/**
@@ -93,20 +91,19 @@ class GrPullUpDialog extends PullUpDialogBase<GrMemberInfoStorage, GrMemberInfo,
return myInterfaceContainmentVerifier;
}
@Override
protected void updateMemberInfo() {
super.updateMemberInfo();
if (myMemberSelectionPanel != null) {
((MyMemberInfoModel)myMemberInfoModel).setSuperClass(getSuperClass());
myMemberSelectionPanel.getTable().setMemberInfos(myMemberInfos);
myMemberSelectionPanel.getTable().fireExternalDataChange();
}
}
@Override
protected void initClassCombo(JComboBox classCombo) {
classCombo.setRenderer(new ClassCellRenderer(classCombo.getRenderer()));
classCombo.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
if (myMemberSelectionPanel != null) {
// ((MyMemberInfoModel)myMemberInfoModel).setSuperClass(getSuperClass());
myMemberSelectionPanel.getTable().setMemberInfos(myMemberInfos);
myMemberSelectionPanel.getTable().fireExternalDataChange();
}
}
}
});
}
protected PsiClass getPreselection() {
@@ -140,13 +137,11 @@ class GrPullUpDialog extends PullUpDialogBase<GrMemberInfoStorage, GrMemberInfo,
final PsiClass superClass = getSuperClass();
String name = superClass.getQualifiedName();
if (name != null) {
StatisticsManager
.getInstance().incUseCount(new StatisticsInfo(PULL_UP_STATISTICS_KEY + myClass.getQualifiedName(), name));
StatisticsManager.getInstance().incUseCount(new StatisticsInfo(PULL_UP_STATISTICS_KEY + myClass.getQualifiedName(), name));
}
List<GrMemberInfo> infos = getSelectedMemberInfos();
GrPullUpHelper processor =
new GrPullUpHelper(myClass, superClass, infos.toArray(new GrMemberInfo[infos.size()]), new DocCommentPolicy(getJavaDocPolicy()));
GrPullUpHelper processor = new GrPullUpHelper(myClass, superClass, infos.toArray(new GrMemberInfo[infos.size()]), new DocCommentPolicy(getJavaDocPolicy()));
invokeRefactoring(processor);
close(OK_EXIT_CODE);
}
@@ -29,7 +29,6 @@ import com.intellij.refactoring.RefactoringActionHandler;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.classMembers.MemberInfoBase;
import com.intellij.refactoring.lang.ElementsHandler;
import com.intellij.refactoring.memberPullUp.PullUpConflictsUtil;
import com.intellij.refactoring.ui.ConflictsDialog;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.refactoring.util.RefactoringHierarchyUtil;
@@ -168,8 +167,7 @@ public class GrPullUpHandler implements RefactoringActionHandler, GrPullUpDialog
public void run() {
final PsiDirectory targetDirectory = superClass.getContainingFile().getContainingDirectory();
final PsiPackage targetPackage = targetDirectory != null ? JavaDirectoryService.getInstance().getPackage(targetDirectory) : null;
conflicts.putAllValues(PullUpConflictsUtil.checkConflicts(infos, mySubclass, superClass, targetPackage, targetDirectory,
dialog.getContainmentVerifier()));
conflicts.putAllValues(GrPullUpConflictsUtil.checkConflicts(infos, mySubclass, superClass, targetPackage, targetDirectory, dialog.getContainmentVerifier()));
}
}, RefactoringBundle.message("detecting.possible.conflicts"), true, myProject)) {
return false;
@@ -25,7 +25,6 @@ import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.searches.OverridingMethodsSearch;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.MethodSignatureUtil;
import com.intellij.psi.util.PsiTreeUtil;
@@ -45,6 +44,9 @@ import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ContainerUtil;
import org.codehaus.groovy.runtime.DefaultGroovyMethods;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.lang.groovydoc.psi.api.GrDocComment;
import org.jetbrains.plugins.groovy.lang.groovydoc.psi.api.GrDocCommentOwner;
import org.jetbrains.plugins.groovy.lang.groovydoc.psi.impl.GrDocCommentUtil;
import org.jetbrains.plugins.groovy.lang.psi.GrReferenceElement;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
@@ -187,13 +189,13 @@ public class GrPullUpHelper extends BaseRefactoringProcessor {
// do actual move
for (GrMemberInfo info : myMembersToMove) {
if (info.getMember() instanceof PsiMethod) {
doMoveMethod(movedMembers, substitutor, info);
doMoveMethod(substitutor, info);
}
else if (info.getMember() instanceof GrField) {
doMoveField(movedMembers, substitutor, info);
doMoveField(substitutor, info);
}
else if (info.getMember() instanceof PsiClass) {
doMoveClass(movedMembers, substitutor, info);
doMoveClass(substitutor, info);
}
}
@@ -315,7 +317,7 @@ public class GrPullUpHelper extends BaseRefactoringProcessor {
return false;
}
private void doMoveMethod(Set<PsiMember> movedMembers, PsiSubstitutor substitutor, GrMemberInfo info) {
private void doMoveMethod(PsiSubstitutor substitutor, GrMemberInfo info) {
GroovyPsiElementFactory elementFactory = GroovyPsiElementFactory.getInstance(myProject);
GrMethod method = (GrMethod)info.getMember();
PsiMethod sibling = method;
@@ -346,10 +348,8 @@ public class GrPullUpHelper extends BaseRefactoringProcessor {
}
replaceMovedMemberTypeParameters(methodCopy, PsiUtil.typeParametersIterable(mySourceClass), substitutor, elementFactory);
myDocCommentPolicy.processCopiedJavaDoc(methodCopy.getDocComment(), method.getDocComment(), isOriginalMethodAbstract);
final PsiMember movedElement =
anchor != null ? (PsiMember)myTargetSuperClass.addBefore(methodCopy, anchor) : (PsiMember)myTargetSuperClass.add(methodCopy);
final GrMethod movedElement =
anchor != null ? (GrMethod)myTargetSuperClass.addBefore(methodCopy, anchor) : (GrMethod)myTargetSuperClass.add(methodCopy);
CodeStyleSettings styleSettings = CodeStyleSettingsManager.getSettings(method.getProject());
if (styleSettings.INSERT_OVERRIDE_ANNOTATION) {
if (PsiUtil.isLanguageLevel5OrHigher(mySourceClass) && !myTargetSuperClass.isInterface() ||
@@ -359,19 +359,16 @@ public class GrPullUpHelper extends BaseRefactoringProcessor {
}
}
if (!PsiUtil.isLanguageLevel6OrHigher(mySourceClass) && myTargetSuperClass.isInterface()) {
if (isOriginalMethodAbstract) {
for (PsiMethod oMethod : OverridingMethodsSearch.search(method)) {
deleteOverrideAnnotationIfFound(oMethod);
}
}
deleteOverrideAnnotationIfFound(method);
GrDocComment oldDoc = method.getDocComment();
if (oldDoc != null) {
GrDocCommentUtil.setDocComment(movedElement, oldDoc);
}
myDocCommentPolicy.processCopiedJavaDoc(methodCopy.getDocComment(), oldDoc, isOriginalMethodAbstract);
myMembersAfterMove.add(movedElement);
if (isOriginalMethodAbstract) {
method.delete();
deleteMemberWithDocComment(method);
}
}
else {
@@ -382,19 +379,30 @@ public class GrPullUpHelper extends BaseRefactoringProcessor {
//fixReferencesToStatic(methodCopy, movedMembers);
replaceMovedMemberTypeParameters(methodCopy, PsiUtil.typeParametersIterable(mySourceClass), substitutor, elementFactory);
final PsiMethod superClassMethod = myTargetSuperClass.findMethodBySignature(methodCopy, false);
final GrMethod movedElement;
if (superClassMethod != null && superClassMethod.hasModifierProperty(PsiModifier.ABSTRACT)) {
superClassMethod.replace(methodCopy);
movedElement = (GrMethod)superClassMethod.replace(methodCopy);
}
else {
final PsiMember movedElement =
anchor != null ? (PsiMember)myTargetSuperClass.addBefore(methodCopy, anchor) : (PsiMember)myTargetSuperClass.add(methodCopy);
movedElement =
anchor != null ? (GrMethod)myTargetSuperClass.addBefore(methodCopy, anchor) : (GrMethod)myTargetSuperClass.add(methodCopy);
myMembersAfterMove.add(movedElement);
}
method.delete();
GrDocCommentUtil.setDocComment(movedElement, method.getDocComment());
deleteMemberWithDocComment(method);
}
}
private static void deleteMemberWithDocComment(GrDocCommentOwner docCommentOwner) {
GrDocComment oldDoc = docCommentOwner.getDocComment();
if (oldDoc != null) {
oldDoc.delete();
}
docCommentOwner.delete();
}
private static void deleteOverrideAnnotationIfFound(PsiMethod oMethod) {
final PsiAnnotation annotation = AnnotationUtil.findAnnotation(oMethod, CommonClassNames.JAVA_LANG_OVERRIDE);
if (annotation != null) {
@@ -594,7 +602,7 @@ public class GrPullUpHelper extends BaseRefactoringProcessor {
}
}
private void doMoveField(Set<PsiMember> movedMembers, PsiSubstitutor substitutor, GrMemberInfo info) {
private void doMoveField(PsiSubstitutor substitutor, GrMemberInfo info) {
GroovyPsiElementFactory elementFactory = GroovyPsiElementFactory.getInstance(myProject);
GrField field = (GrField)info.getMember();
field.normalizeDeclaration();
@@ -605,10 +613,10 @@ public class GrPullUpHelper extends BaseRefactoringProcessor {
}
final PsiMember movedElement = (PsiMember)myTargetSuperClass.add(field);
myMembersAfterMove.add(movedElement);
field.delete();
deleteMemberWithDocComment(field);
}
private void doMoveClass(Set<PsiMember> movedMembers, PsiSubstitutor substitutor, GrMemberInfo info) {
private void doMoveClass(PsiSubstitutor substitutor, GrMemberInfo info) {
GroovyPsiElementFactory elementFactory = GroovyPsiElementFactory.getInstance(myProject);
GrTypeDefinition aClass = (GrTypeDefinition)info.getMember();
if (Boolean.FALSE.equals(info.getOverrides())) {
@@ -654,7 +662,7 @@ public class GrPullUpHelper extends BaseRefactoringProcessor {
PsiMember movedElement = (PsiMember)myTargetSuperClass.addAfter(aClass, null);
//movedElement = (PsiMember)CodeStyleManager.getInstance(myProject).reformat(movedElement);
myMembersAfterMove.add(movedElement);
aClass.delete();
deleteMemberWithDocComment(aClass);
}
}
@@ -137,12 +137,24 @@ class GrPullUpTest extends LightGroovyTestCase {
doTest(false, new MemberDescriptor("foo", PsiMethod));
}
public void _testExtensionMethod() {
public void testPreserveOverride() {
doTest(false, new MemberDescriptor("foo", PsiMethod));
}
public void testPreserveOverride() {
doTest(false, new MemberDescriptor("foo", PsiMethod));
void testImplementsList1() {
doTest(false, new MemberDescriptor("I1", PsiClass))
}
void testImplementsList2() {
doTest(false, new MemberDescriptor("I2", PsiClass))
}
void testImplementsList3() {
doTest(false, new MemberDescriptor("I1", PsiClass), new MemberDescriptor("I2", PsiClass))
}
void testDocCommentInMethod() {
doTest(false, new MemberDescriptor("foo", PsiMethod))
}
private void doTest(MemberDescriptor... membersToFind) {
@@ -3,6 +3,9 @@
Groovy script
Compound Gstring
PsiElement(Gstring begin)('"')
GrStringContentImpl(GString content element)
ASTWrapperPsiElement(Gstring content)
<empty list>
GString injection
PsiElement($)('$')
Reference expression
@@ -10,6 +10,9 @@ Groovy script
PsiElement($)('$')
Reference expression
PsiElement(identifier)('str')
GrStringContentImpl(GString content element)
ASTWrapperPsiElement(Gstring content)
<empty list>
GString injection
PsiElement($)('$')
Reference expression
@@ -31,6 +34,9 @@ Groovy script
Reference expression
PsiElement(identifier)('str')
PsiElement(})('}')
GrStringContentImpl(GString content element)
ASTWrapperPsiElement(Gstring content)
<empty list>
GString injection
PsiElement($)('$')
Reference expression
@@ -9,5 +9,8 @@ Groovy script
PsiWhiteSpace(' ')
Compound Gstring
PsiElement(Gstring begin)('"')
GrStringContentImpl(GString content element)
ASTWrapperPsiElement(Gstring content)
<empty list>
PsiErrorElement:String end expected
<empty list>
@@ -10,6 +10,9 @@ Groovy script
PsiWhiteSpace(' ')
Compound Gstring
PsiElement(Gstring begin)('"')
GrStringContentImpl(GString content element)
ASTWrapperPsiElement(Gstring content)
<empty list>
PsiErrorElement:String end expected
<empty list>
PsiElement(new line)('\n')
@@ -14,8 +14,14 @@ Groovy script
Reference expression
PsiElement(identifier)('href')
PsiElement(})('}')
GrStringContentImpl(GString content element)
ASTWrapperPsiElement(Gstring content)
<empty list>
GString injection
PsiElement($)('$')
Reference expression
PsiElement(identifier)('x')
GrStringContentImpl(GString content element)
ASTWrapperPsiElement(Gstring content)
<empty list>
PsiElement(Gstring end)('"""')
@@ -23,6 +23,9 @@ Groovy script
PsiElement(.)('.')
Compound regular expression
PsiElement(regex begin)('/')
GrStringContentImpl(GString content element)
ASTWrapperPsiElement(regex content)
<empty list>
GString injection
PsiElement($)('$')
Closable block
@@ -32,6 +35,9 @@ Groovy script
Reference expression
PsiElement(identifier)('a')
PsiElement(})('}')
GrStringContentImpl(GString content element)
ASTWrapperPsiElement(regex content)
<empty list>
PsiElement(regex end)('/')
PsiElement(.)('.')
Compound regular expression
@@ -42,4 +48,7 @@ Groovy script
PsiElement($)('$')
Reference expression
PsiElement(identifier)('g')
GrStringContentImpl(GString content element)
ASTWrapperPsiElement($/ regex content)
<empty list>
PsiElement($/ regex end)('/$')
@@ -13,5 +13,8 @@ Groovy script
Literal
GroovyASTPsiElementImpl($/ regex literal)
PsiElement($/ regex begin)('$/')
GrStringContentImpl(GString content element)
ASTWrapperPsiElement($/ regex content)
<empty list>
PsiErrorElement:Dollar slash ending expected
<empty list>
@@ -38,6 +38,9 @@ Groovy script
Reference expression
PsiElement(identifier)('b')
PsiElement(})('}')
GrStringContentImpl(GString content element)
ASTWrapperPsiElement(Gstring content)
<empty list>
PsiElement(Gstring end)('"')
PsiElement(})('}')
GrStringContentImpl(GString content element)
@@ -3,9 +3,15 @@
Groovy script
Compound regular expression
PsiElement(regex begin)('/')
GrStringContentImpl(GString content element)
ASTWrapperPsiElement(regex content)
<empty list>
GString injection
PsiElement($)('$')
PsiErrorElement:Identifier or code block expected
<empty list>
GrStringContentImpl(GString content element)
ASTWrapperPsiElement(regex content)
<empty list>
PsiErrorElement:Regex ending expected
<empty list>
@@ -20,5 +20,8 @@ Groovy script
Literal
PsiElement(Integer)('2')
PsiElement(})('}')
GrStringContentImpl(GString content element)
ASTWrapperPsiElement(regex content)
<empty list>
PsiElement(regex end)('/')
PsiElement())(')')
@@ -3,6 +3,9 @@
Groovy script
Compound regular expression
PsiElement(regex begin)('/')
GrStringContentImpl(GString content element)
ASTWrapperPsiElement(regex content)
<empty list>
GString injection
PsiElement($)('$')
Closable block
@@ -12,6 +15,9 @@ Groovy script
Literal
PsiElement(Integer)('1')
PsiElement(})('}')
GrStringContentImpl(GString content element)
ASTWrapperPsiElement(regex content)
<empty list>
GString injection
PsiElement($)('$')
Closable block
@@ -21,4 +27,7 @@ Groovy script
Literal
PsiElement(Integer)('2')
PsiElement(})('}')
GrStringContentImpl(GString content element)
ASTWrapperPsiElement(regex content)
<empty list>
PsiElement(regex end)('/')
@@ -3,6 +3,9 @@ $/${1}${2}/$
Groovy script
Compound regular expression
PsiElement($/ regex begin)('$/')
GrStringContentImpl(GString content element)
ASTWrapperPsiElement($/ regex content)
<empty list>
GString injection
PsiElement($)('$')
Closable block
@@ -12,6 +15,9 @@ Groovy script
Literal
PsiElement(Integer)('1')
PsiElement(})('}')
GrStringContentImpl(GString content element)
ASTWrapperPsiElement($/ regex content)
<empty list>
GString injection
PsiElement($)('$')
Closable block
@@ -21,4 +27,7 @@ Groovy script
Literal
PsiElement(Integer)('2')
PsiElement(})('}')
GrStringContentImpl(GString content element)
ASTWrapperPsiElement($/ regex content)
<empty list>
PsiElement($/ regex end)('/$')
@@ -39,6 +39,9 @@ Groovy script
Reference expression
PsiElement(identifier)('b')
PsiElement(})('}')
GrStringContentImpl(GString content element)
ASTWrapperPsiElement(Gstring content)
<empty list>
PsiElement(Gstring end)('"')
PsiElement(})('}')
GrStringContentImpl(GString content element)
@@ -40,6 +40,9 @@ Groovy script
Reference expression
PsiElement(identifier)('b')
PsiElement(})('}')
GrStringContentImpl(GString content element)
ASTWrapperPsiElement(Gstring content)
<empty list>
PsiElement(Gstring end)('"')
PsiElement(})('}')
GrStringContentImpl(GString content element)
@@ -7,6 +7,9 @@ Groovy script
Argument label
Compound Gstring
PsiElement(Gstring begin)('"')
GrStringContentImpl(GString content element)
ASTWrapperPsiElement(Gstring content)
<empty list>
GString injection
PsiElement($)('$')
Reference expression
@@ -21,6 +24,9 @@ Groovy script
PsiWhiteSpace(' ')
Compound Gstring
PsiElement(Gstring begin)('"')
GrStringContentImpl(GString content element)
ASTWrapperPsiElement(Gstring content)
<empty list>
GString injection
PsiElement($)('$')
Reference expression
@@ -39,6 +39,9 @@ Groovy script
Literal
PsiElement(Integer)('3')
PsiElement(})('}')
GrStringContentImpl(GString content element)
ASTWrapperPsiElement(Gstring content)
<empty list>
PsiErrorElement:String end expected
<empty list>
PsiElement(new line)('\n ')
@@ -48,5 +51,8 @@ Groovy script
Command arguments
Compound Gstring
PsiElement(Gstring begin)('"""')
GrStringContentImpl(GString content element)
ASTWrapperPsiElement(Gstring content)
<empty list>
PsiErrorElement:String end expected
<empty list>
@@ -0,0 +1,9 @@
class Super {
}
class Foo extends Super {
/**
* doc
*/
def fo<caret>o() {}
}
@@ -0,0 +1,11 @@
class Super {
/**
* doc
*/
def foo() {}
}
class Foo extends Super {
}
@@ -0,0 +1,6 @@
interface I1 {}
interface I2 {}
class Super {}
class Foo extends Super impleme<caret>nts I1, I2 {}
@@ -0,0 +1,6 @@
interface I1 {}
interface I2 {}
class Super implements I1 {}
class Foo extends Super implements I2 {}
@@ -0,0 +1,6 @@
interface I1 {}
interface I2 {}
class Super {}
class Foo extends Super imp<caret>lements I1, I2 {}
@@ -0,0 +1,6 @@
interface I1 {}
interface I2 {}
class Super implements I2 {}
class Foo extends Super implements I1 {}
@@ -0,0 +1,6 @@
interface I1 {}
interface I2 {}
class Super {}
class Foo extends Super impl<caret>ements I1, I2 {}
@@ -0,0 +1,6 @@
interface I1 {}
interface I2 {}
class Super implements I1, I2 {}
class Foo extends Super {}
@@ -101,6 +101,10 @@ public enum WorkingCopyFormat {
return myFormat;
}
public boolean isOrGreater(@NotNull WorkingCopyFormat format) {
return myVersion.isOrGreaterThan(format.getVersion().major, format.getVersion().minor);
}
@NotNull
public static WorkingCopyFormat from(@NotNull Version version) {
WorkingCopyFormat result = UNKNOWN;
@@ -3,10 +3,17 @@ package org.jetbrains.idea.svn.api;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.VcsException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.SvnVcs;
import org.jetbrains.idea.svn.WorkingCopyFormat;
import org.jetbrains.idea.svn.commandLine.SvnBindException;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.wc.ISVNEventHandler;
import org.tmatesoft.svn.core.wc.SVNEvent;
import org.tmatesoft.svn.core.wc.SVNEventAction;
import org.tmatesoft.svn.core.wc2.SvnTarget;
import java.io.File;
import java.util.Collection;
/**
@@ -56,4 +63,20 @@ public abstract class BaseSvnClient implements SvnClient {
String.format("%s format is not supported. Supported formats are: %s.", format.getName(), StringUtil.join(supported, ",")));
}
}
protected static void callHandler(@Nullable ISVNEventHandler handler, @NotNull SVNEvent event) throws VcsException {
if (handler != null) {
try {
handler.handleEvent(event, 0);
}
catch (SVNException e) {
throw new SvnBindException(e);
}
}
}
@NotNull
protected static SVNEvent createEvent(@NotNull File path, @Nullable SVNEventAction action) {
return new SVNEvent(path, null, null, 0, null, null, null, null, action, null, null, null, null, null, null);
}
}
@@ -22,6 +22,7 @@ import org.jetbrains.idea.svn.portable.SvnWcClientI;
import org.jetbrains.idea.svn.properties.PropertyClient;
import org.jetbrains.idea.svn.revert.RevertClient;
import org.jetbrains.idea.svn.update.RelocateClient;
import org.jetbrains.idea.svn.upgrade.UpgradeClient;
/**
* @author Konstantin Kolosovsky.
@@ -51,6 +52,7 @@ public abstract class ClientFactory {
protected VersionClient myVersionClient;
protected ImportClient myImportClient;
protected ExportClient myExportClient;
protected UpgradeClient myUpgradeClient;
protected ClientFactory(@NotNull SvnVcs vcs) {
myVcs = vcs;
@@ -166,6 +168,11 @@ public abstract class ClientFactory {
return prepare(myExportClient);
}
@NotNull
public UpgradeClient createUpgradeClient() {
return prepare(myUpgradeClient);
}
@NotNull
protected <T extends SvnClient> T prepare(@NotNull T client) {
client.setVcs(myVcs);
@@ -23,6 +23,7 @@ import org.jetbrains.idea.svn.update.UpdateClient;
import org.jetbrains.idea.svn.properties.CmdPropertyClient;
import org.jetbrains.idea.svn.revert.CmdRevertClient;
import org.jetbrains.idea.svn.update.CmdRelocateClient;
import org.jetbrains.idea.svn.upgrade.CmdUpgradeClient;
/**
* @author Konstantin Kolosovsky.
@@ -53,6 +54,7 @@ public class CmdClientFactory extends ClientFactory {
myVersionClient = new CmdVersionClient();
myImportClient = new CmdImportClient();
myExportClient = new CmdExportClient();
myUpgradeClient = new CmdUpgradeClient();
statusClient = new SvnCommandLineStatusClient(myVcs);
infoClient = new SvnCommandLineInfoClient(myVcs);
}
@@ -0,0 +1,21 @@
package org.jetbrains.idea.svn.api;
import org.jetbrains.annotations.NotNull;
import org.tmatesoft.svn.core.SVNURL;
/**
* @author Konstantin Kolosovsky.
*/
public class Repository {
@NotNull private final SVNURL myUrl;
public Repository(@NotNull SVNURL url) {
myUrl = url;
}
@NotNull
public SVNURL getUrl() {
return myUrl;
}
}
@@ -23,6 +23,7 @@ import org.jetbrains.idea.svn.portable.SvnkitSvnWcClient;
import org.jetbrains.idea.svn.properties.SvnKitPropertyClient;
import org.jetbrains.idea.svn.revert.SvnKitRevertClient;
import org.jetbrains.idea.svn.update.SvnKitRelocateClient;
import org.jetbrains.idea.svn.upgrade.SvnKitUpgradeClient;
/**
* @author Konstantin Kolosovsky.
@@ -53,6 +54,7 @@ public class SvnKitClientFactory extends ClientFactory {
myVersionClient = new SvnKitVersionClient();
myImportClient = new SvnKitImportClient();
myExportClient = new SvnKitExportClient();
myUpgradeClient = new SvnKitUpgradeClient();
statusClient = new SvnkitSvnStatusClient(myVcs, null);
infoClient = new SvnkitSvnWcClient(myVcs);
}
@@ -63,6 +63,7 @@ import java.util.concurrent.atomic.AtomicReference;
public class SvnCheckoutProvider implements CheckoutProvider {
public void doCheckout(@NotNull final Project project, Listener listener) {
// TODO: Several dialogs is invoked while dialog.show() - seems code should be rewritten to be more transparent
CheckoutDialog dialog = new CheckoutDialog(project, listener);
dialog.show();
}
@@ -308,8 +309,9 @@ public class SvnCheckoutProvider implements CheckoutProvider {
private WorkingCopyFormat displayUpgradeDialog(@NotNull WorkingCopyFormat defaultSelection) {
final UpgradeFormatDialog dialog = new UpgradeFormatDialog(myProject, myPath, false);
dialog.startLoading();
final ModalityState dialogState = ModalityState.any();
dialog.startLoading();
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
@Override
public void run() {
@@ -331,7 +333,7 @@ public class SvnCheckoutProvider implements CheckoutProvider {
dialog.stopLoading();
}
}
}, ModalityState.stateForComponent(dialog.getWindow()));
}, dialogState);
}
});
@@ -24,7 +24,7 @@ import java.util.List;
*/
public class SvnKitCheckoutClient extends BaseSvnClient implements CheckoutClient {
private static final List<WorkingCopyFormat> SUPPORTED_FORMATS;
public static final List<WorkingCopyFormat> SUPPORTED_FORMATS;
static {
List<WorkingCopyFormat> supportedFormats = new ArrayList<WorkingCopyFormat>();
@@ -10,6 +10,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.RootUrlInfo;
import org.jetbrains.idea.svn.SvnVcs;
import org.jetbrains.idea.svn.api.Repository;
import org.jetbrains.idea.svn.checkin.IdeaSvnkitBasedAuthenticationCallback;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNException;
@@ -215,19 +216,15 @@ public class CommandUtil {
}
private static SVNURL resolveRepositoryUrl(@NotNull SvnVcs vcs, @NotNull SvnCommandName name, @NotNull SvnTarget target) {
RootUrlInfo rootInfo = target.isFile()
? vcs.getSvnFileUrlMapping().getWcRootForFilePath(target.getFile())
: vcs.getSvnFileUrlMapping().getWcRootForUrl(target.getURL().toDecodedString());
SVNURL repositoryUrl = rootInfo != null ? rootInfo.getRepositoryUrlUrl() : null;
UrlMappingRepositoryProvider urlMappingProvider = new UrlMappingRepositoryProvider(vcs, target);
InfoCommandRepositoryProvider infoCommandProvider = new InfoCommandRepositoryProvider(vcs, target);
// resolve repository url using "svn info" command except the case when that command is executing right now.
if (repositoryUrl == null && !SvnCommandName.info.equals(name)) {
SVNInfo info = getInfo(vcs, target);
repositoryUrl = info != null ? info.getRepositoryRootURL() : null;
Repository repository = urlMappingProvider.get();
if (repository == null && !SvnCommandName.info.equals(name)) {
repository = infoCommandProvider.get();
}
return repositoryUrl;
return repository != null ? repository.getUrl() : null;
}
@NotNull
@@ -296,4 +293,67 @@ public class CommandUtil {
}
return contentsStatus;
}
public interface RepositoryProvider {
@Nullable
Repository get();
}
public static abstract class BaseRepositoryProvider implements RepositoryProvider {
@NotNull protected final SvnVcs myVcs;
@NotNull protected final SvnTarget myTarget;
protected BaseRepositoryProvider(@NotNull SvnVcs vcs, @NotNull SvnTarget target) {
myVcs = vcs;
myTarget = target;
}
}
public static class UrlMappingRepositoryProvider extends BaseRepositoryProvider {
public UrlMappingRepositoryProvider(@NotNull SvnVcs vcs, @NotNull SvnTarget target) {
super(vcs, target);
}
@Nullable
@Override
public Repository get() {
RootUrlInfo rootInfo = null;
if (!myVcs.getProject().isDefault()) {
rootInfo = myTarget.isFile()
? myVcs.getSvnFileUrlMapping().getWcRootForFilePath(myTarget.getFile())
: myVcs.getSvnFileUrlMapping().getWcRootForUrl(myTarget.getURL().toDecodedString());
}
return rootInfo != null ? new Repository(rootInfo.getRepositoryUrlUrl()) : null;
}
}
public static class InfoCommandRepositoryProvider extends BaseRepositoryProvider {
public InfoCommandRepositoryProvider(@NotNull SvnVcs vcs, @NotNull SvnTarget target) {
super(vcs, target);
}
@Nullable
@Override
public Repository get() {
Repository result;
if (myTarget.isURL()) {
// TODO: Also could still execute info when target is url - either to use info for authentication or to just get correct repository
// TODO: url in case of "read" operations are allowed anonymously.
result = new Repository(myTarget.getURL());
}
else {
SVNInfo info = getInfo(myVcs, myTarget);
result = info != null ? new Repository(info.getRepositoryRootURL()) : null;
}
return result;
}
}
}
@@ -49,7 +49,8 @@ public enum SvnCommandName {
lock("lock", true),
unlock("unlock", true),
importFolder("import", false),
export("export", false);
export("export", false),
upgrade("upgrade", true);
private final String myName;
private final boolean myWriteable;
@@ -17,12 +17,14 @@ package org.jetbrains.idea.svn.dialogs;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.vcs.ObjectsConvertor;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.IdeFocusManager;
@@ -61,11 +63,13 @@ 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.Comparator;
import java.util.*;
import java.util.List;
public class CopiesPanel {
private static final Logger LOG = Logger.getInstance(CopiesPanel.class);
private final Project myProject;
private MessageBusConnection myConnection;
private SvnVcs myVcs;
@@ -98,6 +102,7 @@ public class CopiesPanel {
@Override
public void run() {
final List<WCInfo> infoList = myVcs.getAllWcInfos();
final List<WorkingCopyFormat> supportedFormats = getSupportedFormats();
Runnable runnable = new Runnable() {
@Override
public void run() {
@@ -117,7 +122,7 @@ public class CopiesPanel {
myCurrentInfoList = newList;
}
Collections.sort(infoList, WCComparator.getInstance());
updateList(infoList);
updateList(infoList, supportedFormats);
myRefreshLabel.setEnabled(true);
SwingUtilities.invokeLater(focus);
}
@@ -178,7 +183,7 @@ public class CopiesPanel {
return myRefreshLabel;
}
private void updateList(final List<WCInfo> infoList) {
private void updateList(@NotNull final List<WCInfo> infoList, @NotNull final List<WorkingCopyFormat> supportedFormats) {
myPanel.removeAll();
final Insets nullIndent = new Insets(1, 3, 1, 0);
final GridBagConstraints gb =
@@ -217,7 +222,7 @@ public class CopiesPanel {
SVNDepth.INFINITY, false, null, wcInfo.getFormat());
}
} else if (CHANGE_FORMAT.equals(e.getDescription())) {
changeFormat(wcInfo);
changeFormat(wcInfo, supportedFormats);
} else if (MERGE_FROM.equals(e.getDescription())) {
if (! checkRoot(root, wcInfo.getPath(), " invoke Merge From")) return;
mergeFrom(wcInfo, root, editorPane);
@@ -237,7 +242,7 @@ public class CopiesPanel {
}
});
editorPane.setBorder(null);
editorPane.setText(formatWc(wcInfo));
editorPane.setText(formatWc(wcInfo, supportedFormats));
final JPanel copyPanel = new JPanel(new GridBagLayout());
@@ -263,14 +268,14 @@ public class CopiesPanel {
}
@SuppressWarnings("MethodMayBeStatic")
private String formatWc(WCInfo info) {
private String formatWc(@NotNull WCInfo info, @NotNull List<WorkingCopyFormat> supportedFormats) {
final StringBuilder sb = new StringBuilder().append("<html><head>").append(UIUtil.getCssFontDeclaration(UIUtil.getLabelFont()))
.append("</head><body><table bgColor=\"").append(ColorUtil.toHex(UIUtil.getPanelBackground())).append("\">");
sb.append("<tr valign=\"top\"><td colspan=\"3\"><b>").append(info.getPath()).append("</b></td></tr>");
sb.append("<tr valign=\"top\"><td>URL:</td><td colspan=\"2\">").append(info.getRootUrl()).append("</td></tr>");
if (! WorkingCopyFormat.ONE_DOT_SEVEN.equals(info.getFormat())) {
// can convert
Collection<WorkingCopyFormat> upgradeFormats = getUpgradeFormats(info, supportedFormats);
if (upgradeFormats.size() > 1) {
sb.append("<tr valign=\"top\"><td>Format:</td><td>").append(info.getFormat().getName()).append("</td><td><a href=\"").
append(CHANGE_FORMAT).append("\">Change</a></td></tr>");
} else {
@@ -302,6 +307,33 @@ public class CopiesPanel {
return sb.toString();
}
@NotNull
private List<WorkingCopyFormat> getSupportedFormats() {
List<WorkingCopyFormat> result = Collections.emptyList();
try {
result = myVcs.getFactory().createUpgradeClient().getSupportedFormats();
}
catch (VcsException e) {
LOG.info(e);
}
return result;
}
public static Set<WorkingCopyFormat> getUpgradeFormats(@NotNull WCInfo info, @NotNull List<WorkingCopyFormat> supportedFormats) {
Set<WorkingCopyFormat> canUpgradeTo = EnumSet.noneOf(WorkingCopyFormat.class);
for (WorkingCopyFormat format : supportedFormats) {
if (format.isOrGreater(info.getFormat())) {
canUpgradeTo.add(format);
}
}
canUpgradeTo.add(info.getFormat());
return canUpgradeTo;
}
private void mergeFrom(@NotNull final WCInfo wcInfo, @NotNull final VirtualFile root, @Nullable final Component mergeLabel) {
SelectBranchPopup.showForBranchRoot(myProject, root, new SelectBranchPopup.BranchSelectedCallback() {
@Override
@@ -338,8 +370,10 @@ public class CopiesPanel {
});
}
private void changeFormat(final WCInfo wcInfo) {
private void changeFormat(@NotNull final WCInfo wcInfo, @NotNull final List<WorkingCopyFormat> supportedFormats) {
ChangeFormatDialog dialog = new ChangeFormatDialog(myProject, new File(wcInfo.getPath()), false, ! wcInfo.isIsWcRoot());
dialog.setSupported(supportedFormats);
dialog.setData(wcInfo.getFormat());
dialog.show();
if (! dialog.isOK()) {
@@ -37,7 +37,6 @@ import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.wc.ISVNEventHandler;
import org.tmatesoft.svn.core.wc.SVNEvent;
import org.tmatesoft.svn.core.wc.SVNEventAction;
import org.tmatesoft.svn.core.wc.SVNWCClient;
import java.io.File;
import java.util.ArrayList;
@@ -122,20 +121,7 @@ public class SvnFormatWorker extends Task.Backgroundable {
if (supportsChangelists) {
myBeforeChangeLists = ChangeListManager.getInstance(myProject).getChangeListsCopy();
}
final SVNWCClient wcClient = myVcs.createWCClient();
wcClient.setEventHandler(new ISVNEventHandler() {
@Override
public void handleEvent(SVNEvent event, double progress) throws SVNException {
if (SVNEventAction.UPGRADED_PATH.equals(event.getAction()) && event.getFile() != null) {
indicator.setText2("Upgraded path " + VcsUtil.getPathForProgressPresentation(event.getFile()));
}
}
@Override
public void checkCancelled() throws SVNCancelException {
indicator.checkCanceled();
}
});
try {
for (WCInfo wcInfo : myWcInfos) {
File path = new File(wcInfo.getPath());
@@ -143,13 +129,13 @@ public class SvnFormatWorker extends Task.Backgroundable {
path = SvnUtil.getWorkingCopyRoot(path);
}
try {
if (WorkingCopyFormat.ONE_DOT_SEVEN.equals(myNewFormat)) {
indicator.setText(SvnBundle.message("action.Subversion.cleanup.progress.text", path.getAbsolutePath()));
wcClient.doCleanup(path);
}
indicator.setText(SvnBundle.message("action.change.wcopy.format.task.progress.text", path.getAbsolutePath(),
SvnUtil.formatRepresentation(wcInfo.getFormat()), SvnUtil.formatRepresentation(myNewFormat)));
wcClient.doSetWCFormat(path, myNewFormat.getFormat());
String cleanupMessage = SvnBundle.message("action.Subversion.cleanup.progress.text", path.getAbsolutePath());
String upgradeMessage = SvnBundle.message("action.change.wcopy.format.task.progress.text", path.getAbsolutePath(),
SvnUtil.formatRepresentation(wcInfo.getFormat()),
SvnUtil.formatRepresentation(myNewFormat));
ISVNEventHandler handler = createUpgradeHandler(indicator, cleanupMessage, upgradeMessage);
myVcs.getFactory(path).createUpgradeClient().upgrade(path, myNewFormat, handler);
} catch (Throwable e) {
myExceptions.add(e);
}
@@ -166,4 +152,32 @@ public class SvnFormatWorker extends Task.Backgroundable {
ApplicationManager.getApplication().getMessageBus().syncPublisher(SvnVcs.WC_CONVERTED).run();
}
}
private static ISVNEventHandler createUpgradeHandler(@NotNull final ProgressIndicator indicator,
@NotNull final String cleanupMessage,
@NotNull final String upgradeMessage) {
return new ISVNEventHandler() {
@Override
public void handleEvent(SVNEvent event, double progress) throws SVNException {
if (event.getFile() != null) {
if (SVNEventAction.UPGRADED_PATH.equals(event.getAction())) {
indicator.setText2("Upgraded path " + VcsUtil.getPathForProgressPresentation(event.getFile()));
}
// fake event indicating cleanup start
if (SVNEventAction.UPDATE_STARTED.equals(event.getAction())) {
indicator.setText(cleanupMessage);
}
// fake event indicating upgrade start
if (SVNEventAction.UPDATE_COMPLETED.equals(event.getAction())) {
indicator.setText(upgradeMessage);
}
}
}
@Override
public void checkCancelled() throws SVNCancelException {
indicator.checkCanceled();
}
};
}
}
@@ -60,8 +60,7 @@ public class CmdRevertClient extends BaseSvnClient implements RevertClient {
String statusMessage = matcher.group(1);
String path = matcher.group(2);
return new SVNEvent(new File(path), null, null, 0, null, null, null, null, createAction(statusMessage), null, null, null, null, null,
null);
return createEvent(new File(path), createAction(statusMessage));
}
@Nullable
@@ -0,0 +1,118 @@
package org.jetbrains.idea.svn.upgrade;
import com.intellij.execution.process.ProcessOutputTypes;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.util.containers.Convertor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.WorkingCopyFormat;
import org.jetbrains.idea.svn.api.BaseSvnClient;
import org.jetbrains.idea.svn.api.FileStatusResultParser;
import org.jetbrains.idea.svn.commandLine.CommandUtil;
import org.jetbrains.idea.svn.commandLine.LineCommandListener;
import org.jetbrains.idea.svn.commandLine.SvnCommandName;
import org.tmatesoft.svn.core.wc.ISVNEventHandler;
import org.tmatesoft.svn.core.wc.SVNEvent;
import org.tmatesoft.svn.core.wc.SVNEventAction;
import org.tmatesoft.svn.core.wc2.SvnTarget;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author Konstantin Kolosovsky.
*/
public class CmdUpgradeClient extends BaseSvnClient implements UpgradeClient {
private static final String STATUS = "\\s*(.+?)\\s*";
private static final String PATH = "\\s*\'(.*?)\'\\s*";
private static final Pattern CHANGED_PATH = Pattern.compile(STATUS + PATH);
@Override
public void upgrade(@NotNull File path, @NotNull WorkingCopyFormat format, @Nullable ISVNEventHandler handler) throws VcsException {
validateFormat(format, getSupportedFormats());
// fake event indicating upgrade start
callHandler(handler, createEvent(path, SVNEventAction.UPDATE_COMPLETED));
List<String> parameters = new ArrayList<String>();
CommandUtil.put(parameters, path);
// TODO: Add general possibility to invoke "handler.checkCancelled" (process should be killed). But currently upgrade process is not
// TODO: cancellable from UI - and this makes sense.
// for 1.8 - no output
// for 1.7 - output in format "Upgraded '<path>'"
FileStatusResultParser parser = new FileStatusResultParser(CHANGED_PATH, handler, new UpgradeStatusConvertor());
UpgradeLineCommandListener listener = new UpgradeLineCommandListener(parser);
CommandUtil.execute(myVcs, SvnTarget.fromFile(path), SvnCommandName.upgrade, parameters, listener);
listener.throwIfException();
}
@Override
public List<WorkingCopyFormat> getSupportedFormats() throws VcsException {
List<WorkingCopyFormat> result = new ArrayList<WorkingCopyFormat>();
result.add(WorkingCopyFormat.from(myFactory.createVersionClient().getVersion()));
return result;
}
private static class UpgradeStatusConvertor implements Convertor<Matcher, SVNEvent> {
public SVNEvent convert(@NotNull Matcher matcher) {
String statusMessage = matcher.group(1);
String path = matcher.group(2);
return createEvent(new File(path), createAction(statusMessage));
}
@Nullable
public static SVNEventAction createAction(@NotNull String code) {
SVNEventAction result = null;
if ("Upgraded".equals(code)) {
result = SVNEventAction.UPGRADED_PATH;
}
return result;
}
}
private static class UpgradeLineCommandListener extends LineCommandListener {
@NotNull private final FileStatusResultParser parser;
@NotNull private final AtomicReference<VcsException> exception;
private UpgradeLineCommandListener(@NotNull FileStatusResultParser parser) {
this.parser = parser;
exception = new AtomicReference<VcsException>();
}
@Override
public void onLineAvailable(String line, Key outputType) {
if (ProcessOutputTypes.STDOUT.equals(outputType)) {
try {
parser.onLine(line);
}
catch (VcsException e) {
exception.set(e);
}
}
}
public void throwIfException() throws VcsException {
VcsException e = exception.get();
if (e != null) {
throw e;
}
}
}
}
@@ -0,0 +1,64 @@
package org.jetbrains.idea.svn.upgrade;
import com.intellij.openapi.vcs.VcsException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.WorkingCopyFormat;
import org.jetbrains.idea.svn.api.BaseSvnClient;
import org.jetbrains.idea.svn.checkout.SvnKitCheckoutClient;
import org.jetbrains.idea.svn.commandLine.SvnBindException;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.wc.ISVNEventHandler;
import org.tmatesoft.svn.core.wc.SVNEventAction;
import org.tmatesoft.svn.core.wc.SVNWCClient;
import java.io.File;
import java.util.List;
/**
* @author Konstantin Kolosovsky.
*/
public class SvnKitUpgradeClient extends BaseSvnClient implements UpgradeClient {
@Override
public void upgrade(@NotNull File path, @NotNull WorkingCopyFormat format, @Nullable ISVNEventHandler handler) throws VcsException {
validateFormat(format, getSupportedFormats());
SVNWCClient client = myVcs.createWCClient();
client.setEventHandler(handler);
try {
cleanupIfNecessary(path, format, client, handler);
upgrade(path, format, client, handler);
}
catch (SVNException e) {
throw new SvnBindException(e);
}
}
@Override
public List<WorkingCopyFormat> getSupportedFormats() throws VcsException {
return SvnKitCheckoutClient.SUPPORTED_FORMATS;
}
private static void cleanupIfNecessary(@NotNull File path,
@NotNull WorkingCopyFormat format,
@NotNull SVNWCClient client,
@Nullable ISVNEventHandler handler) throws SVNException, VcsException {
// cleanup is executed only for SVNKit as it could handle both 1.6 and 1.7 formats
if (WorkingCopyFormat.ONE_DOT_SEVEN.equals(format)) {
// fake event indicating cleanup start
callHandler(handler, createEvent(path, SVNEventAction.UPDATE_STARTED));
client.doCleanup(path);
}
}
private static void upgrade(@NotNull File path,
@NotNull WorkingCopyFormat format,
@NotNull SVNWCClient client,
@Nullable ISVNEventHandler handler) throws SVNException, VcsException {
// fake event indicating upgrade start
callHandler(handler, createEvent(path, SVNEventAction.UPDATE_COMPLETED));
client.doSetWCFormat(path, format.getFormat());
}
}
@@ -0,0 +1,21 @@
package org.jetbrains.idea.svn.upgrade;
import com.intellij.openapi.vcs.VcsException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.WorkingCopyFormat;
import org.jetbrains.idea.svn.api.SvnClient;
import org.tmatesoft.svn.core.wc.ISVNEventHandler;
import java.io.File;
import java.util.List;
/**
* @author Konstantin Kolosovsky.
*/
public interface UpgradeClient extends SvnClient {
void upgrade(@NotNull File path, @NotNull WorkingCopyFormat format, @Nullable ISVNEventHandler handler) throws VcsException;
List<WorkingCopyFormat> getSupportedFormats() throws VcsException;
}