/*******************************************************************************
* Copyright (c) 2008, 2014 Institute for Software, HSR Hochschule fuer Technik
* Rapperswil, University of applied sciences and others
* 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:
* Institute for Software - initial API and implementation
* Thomas Corbat (IFS)
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.rewrite.changegenerator;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.eclipse.cdt.core.dom.ast.IASTComment;
import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTInitializer;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.internal.core.dom.rewrite.ASTModification;
import org.eclipse.cdt.internal.core.dom.rewrite.ASTModification.ModificationKind;
import org.eclipse.cdt.internal.core.dom.rewrite.astwriter.ContainerNode;
import org.eclipse.cdt.internal.core.dom.rewrite.commenthandler.NodeCommentMap;
public class ASTModificationHelper {
private final ModificationScopeStack modificationStore;
public ASTModificationHelper(ModificationScopeStack stack) {
this.modificationStore = stack;
}
public <T extends IASTNode> T[] createModifiedChildArray(IASTNode parent, T[] unmodifiedChildren, Class<T> clazz, NodeCommentMap commentMap) {
ArrayList<T> modifiedChildren = new ArrayList<T>(Arrays.asList(unmodifiedChildren));
for (ASTModification parentModification : modificationsForNode(parent)) {
switch (parentModification.getKind()) {
case APPEND_CHILD:
IASTNode newNode = parentModification.getNewNode();
T appendedTNode = cast(newNode, clazz);
if (appendedTNode != null) {
modifiedChildren.add(appendedTNode);
} else if (newNode instanceof ContainerNode) {
ContainerNode nodeContainer = (ContainerNode) newNode;
for (IASTNode currentNode : nodeContainer.getNodes()) {
T tnode= cast(currentNode, clazz);
if (tnode != null) {
modifiedChildren.add(tnode);
}
}
}
break;
case INSERT_BEFORE:
case REPLACE:
break;
}
}
for (T currentChild : unmodifiedChildren) {
for (ASTModification childModification : modificationsForNode(currentChild)) {
try {
IASTNode newNode = childModification.getNewNode();
final T castedNewNode = cast(newNode, clazz);
switch (childModification.getKind()) {
case REPLACE:
if (castedNewNode != null) {
copyComments(castedNewNode, currentChild, commentMap);
modifiedChildren.add(modifiedChildren.indexOf(childModification.getTargetNode()), castedNewNode);
}
modifiedChildren.remove(childModification.getTargetNode());
break;
case INSERT_BEFORE:
if (castedNewNode != null) {
modifiedChildren.add(modifiedChildren.indexOf(childModification.getTargetNode()), castedNewNode);
} else if (newNode instanceof ContainerNode) {
ContainerNode nodeContainer = (ContainerNode) newNode;
for (IASTNode currentNode : nodeContainer.getNodes()) {
T tnode = cast(currentNode, clazz);
if (tnode != null) {
modifiedChildren.add(modifiedChildren.indexOf(childModification.getTargetNode()), tnode);
}
}
}
break;
case APPEND_CHILD:
throw new UnhandledASTModificationException(childModification);
}
} catch (ClassCastException e) {
throw new UnhandledASTModificationException(childModification);
}
}
}
return modifiedChildren.toArray(newArrayInstance(clazz, modifiedChildren.size()));
}
private void copyComments(IASTNode newNode, IASTNode oldNode, NodeCommentMap commentMap) {
// Attach all the comments that is attached to oldNode to newNode
List<IASTComment> leadingComments = commentMap.getLeadingCommentsForNode(oldNode);
for (IASTComment comment : leadingComments) {
commentMap.addLeadingCommentToNode(newNode, comment);
}
List<IASTComment> trailingComments = commentMap.getTrailingCommentsForNode(oldNode);
for (IASTComment comment : trailingComments) {
commentMap.addTrailingCommentToNode(newNode, comment);
}
List<IASTComment> freestandingComments = commentMap.getFreestandingCommentsForNode(oldNode);
for (IASTComment comment : freestandingComments) {
commentMap.addFreestandingCommentToNode(newNode, comment);
}
// Detach comments from oldNode (to avoid memory leak)
Map<IASTNode, List<IASTComment>> leadingMap = commentMap.getLeadingMap();
leadingMap.remove(oldNode);
Map<IASTNode, List<IASTComment>> trailingMap = commentMap.getTrailingMap();
trailingMap.remove(oldNode);
Map<IASTNode, List<IASTComment>> freestandingMap = commentMap.getFreestandingMap();
freestandingMap.remove(oldNode);
}
@SuppressWarnings("unchecked")
private <T> T[] newArrayInstance(Class<T> clazz, int size) {
return (T[]) Array.newInstance(clazz, size);
}
@SuppressWarnings("unchecked")
private <T> T cast(IASTNode node, Class<T> clazz) {
if (clazz.isInstance(node)) {
return (T) node;
}
return null;
}
public List<ASTModification> modificationsForNode(IASTNode targetNode) {
List<ASTModification> modificationsForNode;
if (modificationStore.getModifiedNodes().contains(targetNode)) {
modificationsForNode = modificationStore.getModificationsForNode(targetNode);
} else {
modificationsForNode = Collections.emptyList();
}
return modificationsForNode;
}
public IASTInitializer getInitializer(IASTDeclarator decl) {
IASTInitializer initializer = decl.getInitializer();
if (initializer != null) {
for (ASTModification childModification : modificationsForNode(initializer)) {
switch (childModification.getKind()) {
case REPLACE:
if (childModification.getNewNode() instanceof IASTInitializer) {
return (IASTInitializer)childModification.getNewNode();
} else if (childModification.getNewNode() == null) {
return null;
}
throw new UnhandledASTModificationException(childModification);
case INSERT_BEFORE:
throw new UnhandledASTModificationException(childModification);
case APPEND_CHILD:
throw new UnhandledASTModificationException(childModification);
}
}
} else {
for (ASTModification parentModification : modificationsForNode(decl)) {
if (parentModification.getKind() == ModificationKind.APPEND_CHILD) {
IASTNode newNode = parentModification.getNewNode();
if (newNode instanceof IASTInitializer) {
return (IASTInitializer) newNode;
}
}
}
}
return initializer;
}
@SuppressWarnings("unchecked")
public <T extends IASTNode> T getNodeAfterReplacement(T replacedNode) {
List<ASTModification> modifications = modificationsForNode(replacedNode);
for (ASTModification currentModification : modifications) {
try {
if (currentModification.getKind() == ModificationKind.REPLACE) {
return (T) currentModification.getNewNode();
}
} catch (ClassCastException e) {
throw new UnhandledASTModificationException(currentModification);
}
}
return replacedNode;
}
}