| /* |
| * 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 com.intellij.psi.impl.source.tree; |
| |
| import com.intellij.lang.ASTFactory; |
| import com.intellij.lang.ASTNode; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.extensions.Extensions; |
| import com.intellij.pom.PomManager; |
| import com.intellij.pom.PomModel; |
| import com.intellij.pom.event.PomModelEvent; |
| import com.intellij.pom.impl.PomTransactionBase; |
| import com.intellij.pom.tree.TreeAspect; |
| import com.intellij.pom.tree.events.TreeChangeEvent; |
| import com.intellij.pom.tree.events.impl.TreeChangeEventImpl; |
| import com.intellij.psi.PsiElement; |
| import com.intellij.psi.PsiManager; |
| import com.intellij.psi.impl.DebugUtil; |
| import com.intellij.psi.impl.source.DummyHolder; |
| import com.intellij.psi.impl.source.DummyHolderFactory; |
| import com.intellij.psi.impl.source.SourceTreeToPsiMap; |
| import com.intellij.psi.impl.source.codeStyle.CodeEditUtil; |
| import com.intellij.psi.util.PsiUtilCore; |
| import com.intellij.util.CharTable; |
| import com.intellij.util.containers.HashMap; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.Map; |
| |
| public class ChangeUtil { |
| private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.ChangeUtil"); |
| |
| private ChangeUtil() { } |
| |
| public static void encodeInformation(TreeElement element) { |
| encodeInformation(element, element); |
| } |
| |
| private static void encodeInformation(TreeElement element, ASTNode original) { |
| DebugUtil.startPsiModification("encodeInformation"); |
| try { |
| encodeInformation(element, original, new HashMap<Object, Object>()); |
| } |
| finally { |
| DebugUtil.finishPsiModification(); |
| } |
| } |
| |
| private static void encodeInformation(TreeElement element, ASTNode original, Map<Object, Object> state) { |
| for (TreeCopyHandler handler : Extensions.getExtensions(TreeCopyHandler.EP_NAME)) { |
| handler.encodeInformation(element, original, state); |
| } |
| |
| if (original instanceof CompositeElement) { |
| TreeElement child = element.getFirstChildNode(); |
| ASTNode child1 = original.getFirstChildNode(); |
| while (child != null) { |
| encodeInformation(child, child1, state); |
| child = child.getTreeNext(); |
| child1 = child1.getTreeNext(); |
| } |
| } |
| } |
| |
| public static TreeElement decodeInformation(TreeElement element) { |
| DebugUtil.startPsiModification("decodeInformation"); |
| try { |
| return decodeInformation(element, new HashMap<Object, Object>()); |
| } |
| finally { |
| DebugUtil.finishPsiModification(); |
| } |
| } |
| |
| private static TreeElement decodeInformation(TreeElement element, Map<Object, Object> state) { |
| TreeElement child = element.getFirstChildNode(); |
| while (child != null) { |
| child = decodeInformation(child, state); |
| child = child.getTreeNext(); |
| } |
| |
| for (TreeCopyHandler handler : Extensions.getExtensions(TreeCopyHandler.EP_NAME)) { |
| final TreeElement handled = handler.decodeInformation(element, state); |
| if (handled != null) return handled; |
| } |
| |
| return element; |
| } |
| |
| public static LeafElement copyLeafWithText(LeafElement original, String text) { |
| LeafElement element = ASTFactory.leaf(original.getElementType(), text); |
| original.copyCopyableDataTo(element); |
| encodeInformation(element, original); |
| TreeUtil.clearCaches(element); |
| saveIndentationToCopy(original, element); |
| return element; |
| } |
| |
| public static TreeElement copyElement(@NotNull TreeElement original, CharTable table) { |
| CompositeElement treeParent = original.getTreeParent(); |
| return copyElement(original, treeParent == null ? null : treeParent.getPsi(), table); |
| } |
| |
| public static TreeElement copyElement(TreeElement original, final PsiElement context, CharTable table) { |
| final TreeElement element = (TreeElement)original.clone(); |
| final PsiManager manager = original.getManager(); |
| DummyHolderFactory.createHolder(manager, element, context, table).getTreeElement(); |
| encodeInformation(element, original); |
| TreeUtil.clearCaches(element); |
| saveIndentationToCopy(original, element); |
| return element; |
| } |
| |
| private static void saveIndentationToCopy(final TreeElement original, final TreeElement element) { |
| if(original == null || element == null || CodeEditUtil.isNodeGenerated(original)) return; |
| final int indentation = CodeEditUtil.getOldIndentation(original); |
| if(indentation < 0) CodeEditUtil.saveWhitespacesInfo(original); |
| CodeEditUtil.setOldIndentation(element, CodeEditUtil.getOldIndentation(original)); |
| if(indentation < 0) CodeEditUtil.setOldIndentation(original, -1); |
| } |
| |
| public static TreeElement copyToElement(PsiElement original) { |
| final DummyHolder holder = DummyHolderFactory.createHolder(original.getManager(), null, original.getLanguage()); |
| final FileElement holderElement = holder.getTreeElement(); |
| final TreeElement treeElement = generateTreeElement(original, holderElement.getCharTable(), original.getManager()); |
| // TreeElement treePrev = treeElement.getTreePrev(); // This is hack to support bug used in formater |
| holderElement.rawAddChildren(treeElement); |
| TreeUtil.clearCaches(holderElement); |
| // treeElement.setTreePrev(treePrev); |
| saveIndentationToCopy((TreeElement)original.getNode(), treeElement); |
| return treeElement; |
| } |
| |
| @Nullable |
| public static TreeElement generateTreeElement(PsiElement original, CharTable table, final PsiManager manager) { |
| PsiUtilCore.ensureValid(original); |
| if (SourceTreeToPsiMap.hasTreeElement(original)) { |
| return copyElement((TreeElement)SourceTreeToPsiMap.psiElementToTree(original), table); |
| } |
| else { |
| for (TreeGenerator generator : Extensions.getExtensions(TreeGenerator.EP_NAME)) { |
| final TreeElement element = generator.generateTreeFor(original, table, manager); |
| if (element != null) return element; |
| } |
| return null; |
| } |
| } |
| |
| public static void prepareAndRunChangeAction(final ChangeAction action, final TreeElement changedElement){ |
| final FileElement changedFile = TreeUtil.getFileElement(changedElement); |
| final PsiManager manager = changedFile.getManager(); |
| final PomModel model = PomManager.getModel(manager.getProject()); |
| final TreeAspect treeAspect = model.getModelAspect(TreeAspect.class); |
| model.runTransaction(new PomTransactionBase(changedElement.getPsi(), treeAspect) { |
| @Override |
| public PomModelEvent runInner() { |
| final PomModelEvent event = new PomModelEvent(model); |
| final TreeChangeEvent destinationTreeChange = new TreeChangeEventImpl(treeAspect, changedFile); |
| event.registerChangeSet(treeAspect, destinationTreeChange); |
| action.makeChange(destinationTreeChange); |
| |
| TreeUtil.clearCaches(changedElement); |
| if (changedElement instanceof CompositeElement) { |
| ((CompositeElement) changedElement).subtreeChanged(); |
| } |
| return event; |
| } |
| }); |
| } |
| |
| public interface ChangeAction{ |
| void makeChange(TreeChangeEvent destinationTreeChange); |
| } |
| } |