org.springframework.ide.eclipse.quickfix.jdt.RequestMappingRenameParticipant.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.ide.eclipse.quickfix.jdt.RequestMappingRenameParticipant.java

Source

/*******************************************************************************
 *  Copyright (c) 2012 VMware, Inc.
 *  All rights reserved. This program and the accompanying materials
 *  are made available under the terms of the Eclipse Public License v1.0
 *  which accompanies this distribution, and is available at
 *  http://www.eclipse.org/legal/epl-v10.html
 *
 *  Contributors:
 *      VMware, Inc. - initial API and implementation
 *******************************************************************************/
package org.springframework.ide.eclipse.quickfix.jdt;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.ILocalVariable;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.BodyDeclaration;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.MemberValuePair;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.internal.ui.text.correction.ASTResolving;
import org.eclipse.jdt.internal.ui.text.correction.AssistContext;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.ltk.core.refactoring.TextChange;
import org.eclipse.ltk.core.refactoring.TextFileChange;
import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext;
import org.eclipse.ltk.core.refactoring.participants.ProcessorBasedRefactoring;
import org.eclipse.ltk.core.refactoring.participants.RenameArguments;
import org.eclipse.ltk.core.refactoring.participants.RenameParticipant;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.ReplaceEdit;
import org.eclipse.text.edits.TextEdit;
import org.springframework.ide.eclipse.quickfix.jdt.util.ProposalCalculatorUtil;
import org.springsource.ide.eclipse.commons.core.StatusHandler;

/**
 * Rename participant for renaming template URI variable and path variable when
 * a method parameter is renamed
 * 
 * @author Terry Denney
 */
public class RequestMappingRenameParticipant extends RenameParticipant {

    private IFile file;

    private String oldName;

    private StringLiteral pathVariableLiteral;

    private PathVariableSourceRange methodPathVariableSourceRange;

    private PathVariableSourceRange typePathVariableSourceRange;

    private class PathVariableSourceRange {

        private final List<Integer> offsets;

        public PathVariableSourceRange() {
            offsets = new ArrayList<Integer>();
        }

        public void addOffset(int offset) {
            offsets.add(offset);
        }

        public List<Integer> getOffsets() {
            return offsets;
        }
    }

    @Override
    public RefactoringStatus checkConditions(IProgressMonitor pm, CheckConditionsContext context)
            throws OperationCanceledException {
        return new RefactoringStatus();
    }

    private void getPositions(TextEdit edit, List<Integer> positions) {
        if (edit instanceof MultiTextEdit) {
            for (TextEdit child : ((MultiTextEdit) edit).getChildren()) {
                getPositions(child, positions);
            }
        } else {
            positions.add(edit.getOffset());
        }
    }

    @Override
    public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException {
        RenameArguments arguments = getArguments();
        String newName = arguments.getNewName();
        MultiTextEdit multiEdit = new MultiTextEdit();

        List<Integer> offsets = new ArrayList<Integer>();
        offsets.addAll(typePathVariableSourceRange.getOffsets());
        offsets.addAll(methodPathVariableSourceRange.getOffsets());
        if (pathVariableLiteral != null) {
            offsets.add(pathVariableLiteral.getStartPosition() + 1);
        }

        // need to look at already refactored positions to make sure offsets are
        // correct
        List<Integer> refactoredPositions = new ArrayList<Integer>();
        ProcessorBasedRefactoring refactoring = getProcessor().getRefactoring();
        if (refactoring != null) {
            TextChange textChange = refactoring.getTextChange(file);
            if (textChange != null) {
                TextEdit edit = textChange.getEdit();
                getPositions(edit, refactoredPositions);
            }
        }

        int changeCount = 0;
        int newLength = newName.length();
        int oldLength = oldName.length();
        int diff = newLength - oldLength;

        for (Integer offset : offsets) {
            List<Integer> toBeRemoved = new ArrayList<Integer>();
            for (int position : refactoredPositions) {
                if (position < offset) {
                    changeCount++;

                    // once a refactoredPosition is accounted for, remove from
                    // list so that it won't be double counted
                    toBeRemoved.add(position);
                }
            }
            refactoredPositions.removeAll(toBeRemoved);

            multiEdit.addChild(new ReplaceEdit(offset + changeCount * diff, oldLength, newName));
        }

        if (multiEdit.getChildrenSize() > 0) {
            TextFileChange change = new TextFileChange("", file);
            change.setEdit(multiEdit);
            return change;
        } else {
            return null;
        }
    }

    private ICompilationUnit getCompilationUnit(IJavaElement element) {
        if (element == null || element instanceof ICompilationUnit) {
            return (ICompilationUnit) element;
        }
        return getCompilationUnit(element.getParent());
    }

    @Override
    public String getName() {
        return "Rename path variable referenced in @Controller class";
    }

    @SuppressWarnings("unchecked")
    private void addPathVariableSourceRanges(BodyDeclaration decl, String pathVariable,
            PathVariableSourceRange sourceRange) throws JavaModelException {
        if (decl == null) {
            return;
        }

        Set<Annotation> annotations = ProposalCalculatorUtil.findAnnotations("RequestMapping", decl);
        for (Annotation annotation : annotations) {
            Expression value = null;
            if (annotation instanceof SingleMemberAnnotation) {
                value = ((SingleMemberAnnotation) annotation).getValue();
            } else if (annotation instanceof NormalAnnotation) {
                List<MemberValuePair> pairs = ((NormalAnnotation) annotation).values();
                for (MemberValuePair pair : pairs) {
                    if ("value".equals(pair.getName().getFullyQualifiedName())) {
                        value = pair.getValue();
                        break;
                    }
                }
            }

            if (value instanceof StringLiteral) {
                String uri = ((StringLiteral) value).getLiteralValue();

                int offset = value.getStartPosition() + 1; // skip opening quote
                while (uri != null && uri.length() > 0) {
                    int index = uri.indexOf("{" + pathVariable + "}");
                    if (index > 0) {
                        int pathVariableLength = pathVariable.length();
                        sourceRange.addOffset(offset + index + 1);
                        offset += index + pathVariableLength + 2;
                        uri = uri.substring(index + pathVariableLength + 2);
                    } else {
                        uri = null;
                    }
                }
            }
        }
    }

    @Override
    protected boolean initialize(Object element) {
        boolean hasPathVariable = false;

        if (element instanceof ILocalVariable) {
            ILocalVariable variable = (ILocalVariable) element;
            ICompilationUnit compilationUnit = getCompilationUnit(variable);
            file = (IFile) compilationUnit.getResource();

            try {
                ISourceRange sourceRange = variable.getSourceRange();
                AssistContext assistContext = new AssistContext(compilationUnit, null, sourceRange.getOffset(),
                        sourceRange.getLength());
                ASTNode node = assistContext.getCoveringNode();

                if (node instanceof SingleVariableDeclaration) {
                    SingleVariableDeclaration varDecl = (SingleVariableDeclaration) node;
                    Set<Annotation> annotations = ProposalCalculatorUtil.findAnnotations("PathVariable", varDecl);
                    SimpleName varDeclName = varDecl.getName();
                    String varName = varDeclName.getFullyQualifiedName();

                    String pathVariableName = varName;
                    StringLiteral pathVariableLiteral = null;

                    for (Annotation annotation : annotations) {
                        if (annotation instanceof SingleMemberAnnotation) {
                            Expression value = ((SingleMemberAnnotation) annotation).getValue();
                            if (value instanceof StringLiteral) {
                                pathVariableLiteral = ((StringLiteral) value);
                                pathVariableName = pathVariableLiteral.getLiteralValue();
                                break;
                            }
                        }
                    }

                    if (varName.equals(pathVariableName)) {
                        hasPathVariable = true;
                        this.oldName = varName;
                        this.pathVariableLiteral = pathVariableLiteral;

                        // find reference in method annotation
                        MethodDeclaration methodDecl = (MethodDeclaration) ASTResolving.findAncestor(varDecl,
                                ASTNode.METHOD_DECLARATION);
                        methodPathVariableSourceRange = new PathVariableSourceRange();
                        addPathVariableSourceRanges(methodDecl, pathVariableName, methodPathVariableSourceRange);

                        // find reference in type annotation
                        TypeDeclaration typeDecl = (TypeDeclaration) ASTResolving.findAncestor(varDecl,
                                ASTNode.TYPE_DECLARATION);
                        typePathVariableSourceRange = new PathVariableSourceRange();
                        addPathVariableSourceRanges(typeDecl, pathVariableName, typePathVariableSourceRange);
                    }
                }

            } catch (JavaModelException e) {
                StatusHandler.log(e.getStatus());
            }

        }

        return hasPathVariable;
    }

}