blob: 1afcdb72d26e5a4d7290f326831c322df9df58bf [file] [log] [blame]
/*
* Copyright 2000-2014 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.pom.xml.impl;
import com.intellij.lang.ASTNode;
import com.intellij.pom.PomModel;
import com.intellij.pom.PomModelAspect;
import com.intellij.pom.event.PomModelEvent;
import com.intellij.pom.tree.TreeAspect;
import com.intellij.pom.tree.events.ChangeInfo;
import com.intellij.pom.tree.events.ReplaceChangeInfo;
import com.intellij.pom.tree.events.TreeChange;
import com.intellij.pom.tree.events.TreeChangeEvent;
import com.intellij.pom.tree.events.impl.ChangeInfoImpl;
import com.intellij.pom.tree.events.impl.TreeChangeImpl;
import com.intellij.pom.xml.XmlAspect;
import com.intellij.pom.xml.impl.events.*;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.TokenType;
import com.intellij.psi.XmlElementVisitor;
import com.intellij.psi.impl.source.tree.FileElement;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.xml.*;
import com.intellij.util.CharTable;
import java.util.Collections;
public class XmlAspectImpl implements XmlAspect {
private final PomModel myModel;
private final TreeAspect myTreeAspect;
public XmlAspectImpl(PomModel model, TreeAspect aspect) {
myModel = model;
myTreeAspect = aspect;
myModel.registerAspect(XmlAspect.class, this, Collections.singleton((PomModelAspect)myTreeAspect));
}
@Override
public void update(PomModelEvent event) {
if (!event.getChangedAspects().contains(myTreeAspect)) return;
final TreeChangeEvent changeSet = (TreeChangeEvent)event.getChangeSet(myTreeAspect);
if (changeSet == null) return;
final ASTNode rootElement = changeSet.getRootElement();
final PsiFile file = (PsiFile)rootElement.getPsi();
if (!(file instanceof XmlFile)) return;
final XmlAspectChangeSetImpl xmlChangeSet = event.registerChangeSetIfAbsent(this, new XmlAspectChangeSetImpl(myModel));
xmlChangeSet.addChangedFile((XmlFile)file);
final ASTNode[] changedElements = changeSet.getChangedElements();
final CharTable table = ((FileElement)changeSet.getRootElement()).getCharTable();
for (ASTNode changedElement : changedElements) {
TreeChange changesByElement = changeSet.getChangesByElement(changedElement);
PsiElement psiElement = null;
while (changedElement != null && (psiElement = changedElement.getPsi()) == null) {
final ASTNode parent = changedElement.getTreeParent();
final ChangeInfoImpl changeInfo = ChangeInfoImpl.create(ChangeInfo.CONTENTS_CHANGED, changedElement);
changeInfo.compactChange(changesByElement);
changesByElement = new TreeChangeImpl(parent);
changesByElement.addChange(changedElement, changeInfo);
changedElement = parent;
}
if (changedElement == null) continue;
final TreeChange finalChangedElement = changesByElement;
psiElement.accept(new XmlElementVisitor() {
TreeChange myChange = finalChangedElement;
@Override
public void visitElement(PsiElement element) {
final ASTNode child = element.getNode();
final ASTNode treeParent = child.getTreeParent();
if (treeParent == null) return;
final PsiElement parent = treeParent.getPsi();
final ChangeInfoImpl changeInfo = ChangeInfoImpl.create(ChangeInfo.CONTENTS_CHANGED, child);
changeInfo.compactChange(myChange);
myChange = new TreeChangeImpl(treeParent);
myChange.addChange(child, changeInfo);
parent.accept(this);
}
@Override
public void visitXmlAttribute(XmlAttribute attribute) {
final ASTNode[] affectedChildren = myChange.getAffectedChildren();
String oldName = null;
String oldValue = null;
for (final ASTNode treeElement : affectedChildren) {
final ChangeInfo changeByChild = myChange.getChangeByChild(treeElement);
final int changeType = changeByChild.getChangeType();
if (treeElement.getElementType() == XmlTokenType.XML_NAME) {
if (changeType == ChangeInfo.REMOVED) {
oldName = treeElement.getText();
}
else if (changeType == ChangeInfo.REPLACE) {
oldName = ((ReplaceChangeInfo)changeByChild).getReplaced().getText();
}
}
if (treeElement.getElementType() == XmlElementType.XML_ATTRIBUTE_VALUE) {
if (changeType == ChangeInfo.REMOVED) {
oldValue = treeElement.getText();
}
else if (changeType == ChangeInfo.REPLACE) {
oldValue = ((ReplaceChangeInfo)changeByChild).getReplaced().getText();
}
}
}
if (oldName != null && !oldName.equals(attribute.getName())) {
xmlChangeSet.add(new XmlAttributeSetImpl(attribute.getParent(), oldName, null));
xmlChangeSet.add(new XmlAttributeSetImpl(attribute.getParent(), attribute.getName(), attribute.getValue()));
}
else if (oldValue != null) {
xmlChangeSet.add(new XmlAttributeSetImpl(attribute.getParent(), attribute.getName(), attribute.getValue()));
}
else {
xmlChangeSet.add(new XmlElementChangedImpl(attribute));
}
}
@Override
public void visitXmlTag(XmlTag tag) {
ASTNode[] affectedChildren = shortenChange(myChange.getAffectedChildren(), changeSet);
for (final ASTNode treeElement : affectedChildren) {
/*final IElementType type = treeElement.getElementType();
if (type == ElementType.WHITE_SPACE) continue;
if (type == ElementType.XML_NAME) {
if (myChange.getChangeByChild(treeElement).getChangeType() == ChangeInfo.REPLACE) {
continue;
}
}*/
if (!(treeElement.getPsi() instanceof XmlTagChild)) {
visitElement(tag);
return;
}
}
for (final ASTNode treeElement : affectedChildren) {
final ChangeInfo changeByChild = myChange.getChangeByChild(treeElement);
final int changeType = changeByChild.getChangeType();
final IElementType type = treeElement.getElementType();
if (type == TokenType.WHITE_SPACE) continue;
/*
if (type == ElementType.XML_NAME) {
final XmlToken xmlToken = (XmlToken)((ReplaceChangeInfo)changeByChild).getReplaced();
xmlChangeSet.add(new XmlTagNameChangedImpl(tag, xmlToken.getText()));
continue;
}
*/
final PsiElement element = treeElement.getPsi();
switch (changeType) {
case ChangeInfo.ADD:
xmlChangeSet.add(new XmlTagChildAddImpl(tag, (XmlTagChild)element));
break;
case ChangeInfo.REMOVED:
treeElement.putUserData(CharTable.CHAR_TABLE_KEY, table);
xmlChangeSet.add(new XmlTagChildRemovedImpl(tag, (XmlTagChild)element));
break;
case ChangeInfo.CONTENTS_CHANGED:
xmlChangeSet.add(new XmlTagChildChangedImpl(tag, (XmlTagChild)element));
break;
case ChangeInfo.REPLACE:
final PsiElement psi = ((ReplaceChangeInfo)changeByChild).getReplaced().getPsi();
if (psi instanceof XmlTagChild) {
final XmlTagChild replaced = (XmlTagChild)psi;
replaced.putUserData(CharTable.CHAR_TABLE_KEY, table);
xmlChangeSet.add(new XmlTagChildRemovedImpl(tag, replaced));
xmlChangeSet.add(new XmlTagChildAddImpl(tag, (XmlTagChild)element));
}
break;
}
}
}
@Override
public void visitXmlDocument(XmlDocument document) {
xmlChangeSet.clear();
xmlChangeSet.add(new XmlDocumentChangedImpl(document));
}
@Override
public void visitFile(PsiFile file) {
final XmlDocument document = ((XmlFile)file).getDocument();
if (document != null) {
xmlChangeSet.clear();
xmlChangeSet.add(new XmlDocumentChangedImpl(document));
}
}
});
}
}
private ASTNode[] shortenChange(ASTNode[] affectedChildren, TreeChangeEvent event) {
// TODO
return affectedChildren;
}
}