// =====================================================================
//
// Copyright (C) 2012 - 2016, Philip Graf
//
// 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
//
// =====================================================================
package ch.acanda.eclipse.pmd.java.resolution;
import java.util.List;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor;
/**
* Utilitites for manipulationg ASTs.
*
* @author Philip Graf
*/
public final class ASTUtil {
private ASTUtil() {
// hide constructor of utility class
}
/**
* Returns a deep copy of the subtree of AST nodes rooted at the given node. The resulting nodes are owned by the
* same AST as the given node. Even if the given node has a parent, the result node will be unparented.
* <p>
* Source range information on the original nodes is automatically copied to the new nodes. Client properties (
* <code>properties</code>) are not carried over.
* </p>
* <p>
* The node's <code>AST</code> and the target <code>AST</code> must support the same API level.
* </p>
*
* @param node The node to copy, or <code>null</code> if none.
*
* @return The copied node, or <code>null</code> if <code>node</code> is <code>null</code>
*/
@SuppressWarnings("unchecked")
public static <T extends ASTNode> T copy(final T node) {
return (T) ASTNode.copySubtree(node.getAST(), node);
}
/**
* Returns a deep copy of the subtrees of AST nodes rooted at the given list of nodes. The resulting nodes are owned
* by the given AST, which may be different from the ASTs of the nodes in the list. Even if the nodes in the list
* have parents, the nodes in the result will be unparented.
* <p>
* Source range information on the original nodes is automatically copied to the new nodes. Client properties (
* <code>properties</code>) are not carried over.
* </p>
*
* @param nodes The node to copy, or <code>null</code> if none.
*
* @return The copied nodes, or <code>null</code> if <code>node</code> is <code>null</code>
*/
@SuppressWarnings("unchecked")
public static <T extends ASTNode> List<T> copy(final List<T> nodes) {
if (nodes == null || nodes.isEmpty()) {
return nodes;
}
return ASTNode.copySubtrees(nodes.get(0).getAST(), nodes);
}
/**
* Replaces a node in an AST with another node. If the replacement is successful the original node is deleted.
*
* @param node The node to replace.
* @param replacement The replacement node.
* @return <code>true</code> if the node was successfully replaced.
*/
public static boolean replace(final ASTNode node, final ASTNode replacement) {
final ASTNode parent = node.getParent();
final StructuralPropertyDescriptor descriptor = node.getLocationInParent();
if (descriptor != null) {
if (descriptor.isChildProperty()) {
parent.setStructuralProperty(descriptor, replacement);
node.delete();
return true;
} else if (descriptor.isChildListProperty()) {
@SuppressWarnings("unchecked")
final List<ASTNode> children = (List<ASTNode>) parent.getStructuralProperty(descriptor);
children.set(children.indexOf(node), replacement);
node.delete();
return true;
}
}
return false;
}
/**
* Replaces a node in an AST with other nodes. If the replacement is successful the original node is deleted.
*
* @param node The node to replace.
* @param replacement The replacement nodes.
* @return <code>true</code> if the node was successfully replaced.
*/
public static boolean replace(final ASTNode node, final List<? extends ASTNode> replacement) {
final ASTNode parent = node.getParent();
final StructuralPropertyDescriptor descriptor = node.getLocationInParent();
if (descriptor != null && descriptor.isChildListProperty()) {
@SuppressWarnings("unchecked")
final List<ASTNode> children = (List<ASTNode>) parent.getStructuralProperty(descriptor);
children.addAll(children.indexOf(node), replacement);
node.delete();
return true;
}
return false;
}
/**
* Creates an unparented node of the given node class.
*
* @param ast The AST to which the created node will be attached. Must not be {@code null}.
* @param nodeClass The AST node class. Must not be {@code null}.
* @return The new unparented node owned by the AST.
*/
@SuppressWarnings("unchecked")
public static <T extends ASTNode> T create(final AST ast, final Class<T> nodeClass) {
return (T) ast.createInstance(nodeClass);
}
}