/*******************************************************************************
* Copyright (c) 2008, 2011 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
*******************************************************************************/
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.HashMap;
import java.util.List;
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:
newNode = parentModification.getNewNode();
if (newNode instanceof ContainerNode) {
ContainerNode contNode = (ContainerNode) newNode;
for (IASTNode node : contNode.getNodes()) {
insertNode(clazz, modifiedChildren, parentModification, node);
}
} else {
insertNode(clazz, modifiedChildren, parentModification, newNode);
}
break;
case REPLACE:
}
}
for (T currentChild : unmodifiedChildren) {
for (ASTModification childModification : modificationsForNode(currentChild)) {
try {
final T newNode = cast(childModification.getNewNode(), clazz);
switch (childModification.getKind()) {
case REPLACE:
if (newNode != null) {
copyComments(newNode, currentChild, commentMap);
modifiedChildren.add(
modifiedChildren.indexOf(childModification.getTargetNode()),
newNode);
}
modifiedChildren.remove(childModification.getTargetNode());
break;
case INSERT_BEFORE:
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
ArrayList<IASTComment> leadingComments = commentMap.getLeadingCommentsForNode(oldNode);
for (IASTComment comment : leadingComments) {
commentMap.addLeadingCommentToNode(newNode, comment);
}
ArrayList<IASTComment> trailingComments = commentMap.getTrailingCommentsForNode(oldNode);
for (IASTComment comment : trailingComments) {
commentMap.addTrailingCommentToNode(newNode, comment);
}
ArrayList<IASTComment> freestandingComments = commentMap.getFreestandingCommentsForNode(oldNode);
for (IASTComment comment : freestandingComments) {
commentMap.addFreestandingCommentToNode(newNode, comment);
}
// Detach comments from oldNode (to avoid memory leak)
HashMap<IASTNode, ArrayList<IASTComment>> leadingMap = commentMap.getLeadingMap();
leadingMap.remove(oldNode);
HashMap<IASTNode, ArrayList<IASTComment>> trailingMap = commentMap.getTrailingMap();
trailingMap.remove(oldNode);
HashMap<IASTNode, ArrayList<IASTComment>> freestandingMap = commentMap.getFreestandingMap();
freestandingMap.remove(oldNode);
}
private <T> void insertNode(Class<T> clazz, ArrayList<T> modifiedChildren,
ASTModification parentModification, IASTNode newNode) {
T insertedTNode = cast(newNode, clazz);
int targetNodeIndex = modifiedChildren.indexOf(parentModification.getTargetNode());
if (targetNodeIndex >= 0) {
modifiedChildren.add(targetNodeIndex, insertedTNode);
}
}
@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;
}
}