org.intellij.lang.xpath.xslt.refactoring.extractTemplate.XsltExtractTemplateAction.java Source code

Java tutorial

Introduction

Here is the source code for org.intellij.lang.xpath.xslt.refactoring.extractTemplate.XsltExtractTemplateAction.java

Source

/*
 * Copyright 2002-2007 Sascha Weinreuter
 *
 * 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.intellij.lang.xpath.xslt.refactoring.extractTemplate;

import com.intellij.psi.codeStyle.CodeStyleManager;
import org.intellij.lang.xpath.psi.XPathVariable;
import org.intellij.lang.xpath.psi.XPathVariableReference;
import org.intellij.lang.xpath.xslt.XsltSupport;
import org.intellij.lang.xpath.xslt.psi.XsltElementFactory;
import org.intellij.lang.xpath.xslt.psi.XsltVariable;
import org.intellij.lang.xpath.xslt.refactoring.RefactoringUtil;
import org.intellij.lang.xpath.xslt.refactoring.XsltRefactoringActionBase;
import org.intellij.lang.xpath.xslt.util.XsltCodeInsightUtil;

import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.SelectionModel;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.*;
import com.intellij.util.Query;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

/**
 * Created by IntelliJ IDEA.
 * User: sweinreuter
 * Date: 10.06.2007
 */
@SuppressWarnings({ "ComponentNotRegistered" })
public class XsltExtractTemplateAction extends XsltRefactoringActionBase {
    public String getRefactoringName() {
        return "Extract Template";
    }

    @Override
    public void invoke(@NotNull Project project, Editor editor, PsiFile file, DataContext dataContext) {
        if (!invokeImpl(editor, file)) {
            super.invoke(project, editor, file, dataContext);
        }
    }

    private boolean invokeImpl(Editor editor, PsiFile file) {
        return invokeImpl(editor, file, null);
    }

    public boolean invokeImpl(Editor editor, PsiFile file, String newName) {
        final SelectionModel selectionModel = editor.getSelectionModel();
        if (!selectionModel.hasSelection()) {
            return false;
        }

        int startOffset = selectionModel.getSelectionStart();
        PsiElement start = file.findElementAt(startOffset);
        if (start instanceof PsiWhiteSpace) {
            if ((start = PsiTreeUtil.nextLeaf(start)) != null) {
                startOffset = start.getTextOffset();
            }
        }

        int endOffset = selectionModel.getSelectionEnd();
        PsiElement end = file.findElementAt(endOffset - 1);
        if (end instanceof PsiWhiteSpace) {
            if ((end = PsiTreeUtil.prevLeaf(end)) != null) {
                endOffset = end.getTextRange().getEndOffset();
            }
        }

        if (start == null || end == null) {
            return false;
        }

        final PsiElement parent = start.getParent();
        if (!(parent instanceof XmlTag || parent instanceof XmlComment)) {
            return false;
        }

        if (start == end) {
            if (start.getTextRange().getStartOffset() != startOffset) {
                return false;
            }
            if (end.getTextRange().getEndOffset() != endOffset) {
                return false;
            }

            if (extractFrom(start, end, newName)) {
                // tests dislike empty selection
                selectionModel.removeSelection();
                return true;
            }
        } else {
            final XmlElement startTag = PsiTreeUtil.getParentOfType(start, XmlTag.class, XmlComment.class);
            if (startTag == null) {
                return false;
            }
            if (startTag.getTextRange().getStartOffset() != startOffset) {
                return false;
            }

            final XmlElement endTag = PsiTreeUtil.getParentOfType(end, XmlTag.class, XmlComment.class);
            if (endTag == null) {
                return false;
            }
            if (endTag.getTextRange().getEndOffset() != endOffset) {
                return false;
            }

            if (startTag != endTag) {
                if (startTag.getParent() != endTag.getParent()) {
                    return false;
                }
            }
            if (extractFrom(startTag, endTag, newName)) {
                // tests dislike empty selection
                selectionModel.removeSelection();
                return true;
            }
        }
        return false;
    }

    private boolean extractFrom(final @NotNull PsiElement start, final PsiElement end, String newName) {
        final XmlTag outerTemplate = XsltCodeInsightUtil.getTemplateTag(start, false);
        if (outerTemplate == null) {
            return false;
        }
        final XmlTag parentScope = PsiTreeUtil.getParentOfType(start, XmlTag.class, true);
        if (parentScope == null) {
            return false;
        }

        final StringBuilder sb = new StringBuilder("\n");
        final Set<String> vars = new LinkedHashSet<String>();

        final int startOffset = start.getTextRange().getStartOffset();
        final int endOffset = end.getTextRange().getEndOffset();

        PsiElement e = start;
        while (e != null) {
            if (e instanceof XmlTag) {
                final XmlTag tag = (XmlTag) e;
                if (XsltSupport.isVariable(tag)) {
                    final XsltVariable variable = XsltElementFactory.getInstance().wrapElement(tag,
                            XsltVariable.class);
                    final LocalSearchScope searchScope = new LocalSearchScope(parentScope);
                    final Query<PsiReference> query = ReferencesSearch.search(variable, searchScope);
                    for (PsiReference reference : query) {
                        final XmlElement context = PsiTreeUtil.getContextOfType(reference.getElement(),
                                XmlElement.class, true);
                        if (context == null || context.getTextRange().getStartOffset() > endOffset) {
                            return false;
                        }
                    }
                }
            }
            sb.append(e.getText());

            final List<XPathVariableReference> references = RefactoringUtil.collectVariableReferences(e);
            for (XPathVariableReference reference : references) {
                final XPathVariable variable = reference.resolve();
                if (variable instanceof XsltVariable) {
                    final XmlTag var = ((XsltVariable) variable).getTag();
                    if (var.getTextRange().getStartOffset() < startOffset) {
                        // don't pass through global parameters and variables
                        if (XsltCodeInsightUtil.getTemplateTag(variable, false) != null) {
                            vars.add(variable.getName());
                        }
                    }
                } else if (variable == null) {
                    // TODO just copy unresolved refs or fail?
                    vars.add(reference.getReferencedName());
                }
            }

            if (e == end) {
                break;
            }
            e = e.getNextSibling();
        }
        sb.append("\n");

        final String s = newName == null ? Messages.showInputDialog(start.getProject(), "Template Name: ",
                getRefactoringName(), Messages.getQuestionIcon()) : newName;

        if (s != null) {
            new WriteCommandAction(start.getProject()) {
                protected void run(Result result) throws Throwable {
                    final PsiFile containingFile = start.getContainingFile();

                    XmlTag templateTag = parentScope.createChildTag("template", XsltSupport.XSLT_NS, sb.toString(),
                            false);
                    templateTag.setAttribute("name", s);

                    final PsiElement dummy = XmlElementFactory.getInstance(start.getProject())
                            .createDisplayText(" ");
                    final PsiElement outerParent = outerTemplate.getParent();
                    final PsiElement element = outerParent.addAfter(dummy, outerTemplate);
                    templateTag = (XmlTag) outerParent.addAfter(templateTag, element);

                    final TextRange adjust = templateTag.getTextRange();

                    final PsiDocumentManager psiDocumentManager = PsiDocumentManager
                            .getInstance(start.getProject());
                    final Document doc = psiDocumentManager.getDocument(containingFile);
                    assert doc != null;
                    psiDocumentManager.doPostponedOperationsAndUnblockDocument(doc);
                    CodeStyleManager.getInstance(start.getManager().getProject()).adjustLineIndent(containingFile,
                            adjust);

                    final PsiElement parent = start.getParent();
                    XmlTag callTag = parentScope.createChildTag("call-template", XsltSupport.XSLT_NS, null, false);
                    callTag.setAttribute("name", s);

                    if (start instanceof XmlToken
                            && ((XmlToken) start).getTokenType() == XmlTokenType.XML_DATA_CHARACTERS) {
                        assert start == end;
                        callTag = (XmlTag) start.replace(callTag);
                    } else {
                        callTag = (XmlTag) parent.addBefore(callTag, start);
                        parent.deleteChildRange(start, end);
                    }

                    for (String var : vars) {
                        final XmlTag param = templateTag.createChildTag("param", XsltSupport.XSLT_NS, null, false);
                        param.setAttribute("name", var);
                        RefactoringUtil.addParameter(templateTag, param);

                        final XmlTag arg = RefactoringUtil.addWithParam(callTag);
                        arg.setAttribute("name", var);
                        arg.setAttribute("select", "$" + var);
                    }
                }
            }.execute().logException(Logger.getInstance(getClass().getName()));
        }
        return true;
    }

    protected boolean actionPerformedImpl(PsiFile file, Editor editor, XmlAttribute context, int offset) {
        return false;
    }

    @Override
    @Nullable
    public String getErrorMessage(Editor editor, PsiFile file, XmlAttribute context) {
        if (!editor.getSelectionModel().hasSelection()) {
            return "Please select the code that should be extracted.";
        }
        return null;
    }
}