/*******************************************************************************
* 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
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.rewrite.astwriter;
import org.eclipse.cdt.core.dom.ast.IASTASMDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTCompoundStatement;
import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition;
import org.eclipse.cdt.core.dom.ast.IASTIfStatement;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIncludeStatement;
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTStatement;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceDefinition;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTVisibilityLabel;
import org.eclipse.cdt.internal.core.dom.rewrite.ASTModificationStore;
import org.eclipse.cdt.internal.core.dom.rewrite.changegenerator.ChangeGeneratorWriterVisitor;
import org.eclipse.cdt.internal.core.dom.rewrite.commenthandler.ASTCommenter;
import org.eclipse.cdt.internal.core.dom.rewrite.commenthandler.NodeCommentMap;
import java.util.List;
/**
* ASTWriter main class. Generates source code from <code>IASTNode</code>.
* Uses a {@link ChangeGeneratorWriterVisitor} to generate the code for the given nodes.
*
* @author Emanuel Graf
*/
public class ASTWriter {
private final ASTModificationStore modificationStore = new ASTModificationStore();
/**
* Creates a <code>ASTWriter</code>.
*/
public ASTWriter() {
super();
}
/**
* Generates the source code representing this node.
*
* @param rootNode Node to write.
* @return A <code>String</code> representing the source code for the node.
* @throws ProblemRuntimeException if the node or one of it's children is a <code>IASTProblemNode</code>.
*/
public String write(IASTNode rootNode) throws ProblemRuntimeException {
return write(rootNode, new NodeCommentMap());
}
/**
* Generates the source code representing this node including comments.
*
* @param rootNode Node to write.
* @param commentMap comments for the translation unit
* @return A <code>String</code> representing the source code for the node.
* @throws ProblemRuntimeException if the node or one of it's children is
* an <code>IASTProblemNode</code>.
*
* @see ASTCommenter#getCommentedNodeMap(org.eclipse.cdt.core.dom.ast.IASTTranslationUnit)
*/
public String write(IASTNode rootNode, NodeCommentMap commentMap) throws ProblemRuntimeException {
ChangeGeneratorWriterVisitor writer = new ChangeGeneratorWriterVisitor(
modificationStore, null, commentMap);
if (rootNode != null) {
rootNode.accept(writer);
}
return writer.toString();
}
/**
* Returns <code>true</code> if the node should be separated by a blank line from the node
* before it.
*
* @param node The node.
* @return <code>true</code> if the node should be separated by a blank line from the node
* before it.
*/
public static boolean requiresLeadingBlankLine(IASTNode node) {
if (node instanceof ICPPASTTemplateDeclaration) {
node = ((ICPPASTTemplateDeclaration) node).getDeclaration();
}
return node instanceof IASTASMDeclaration ||
node instanceof IASTFunctionDefinition ||
node instanceof ICPPASTVisibilityLabel;
}
/**
* Returns <code>true</code> if the node should be separated by a blank line from the node
* after it.
*
* @param node The node.
* @return <code>true</code> if the node should be separated by a blank line from the node
* after it.
*/
public static boolean requiresTrailingBlankLine(IASTNode node) {
if (node instanceof ICPPASTNamespaceDefinition)
return true;
if (node instanceof IASTFunctionDefinition)
return true;
if (node instanceof IASTIfStatement) {
IASTIfStatement statement = ((IASTIfStatement) node);
IASTStatement lastClause = statement.getElseClause();
if (lastClause == null)
lastClause = statement.getThenClause();
if (!(lastClause instanceof IASTCompoundStatement) &&
!doNodesHaveSameOffset(lastClause, statement)) {
return true;
}
}
return false;
}
/**
* Returns <code>true</code> if there should be no blank line after this node even if a blank
* line is normally required before the subsequent node.
*
* @param node The node.
* @return <code>true</code> if there should be no blank line after this node.
*/
public static boolean suppressesTrailingBlankLine(IASTNode node) {
return node instanceof ICPPASTVisibilityLabel;
}
/**
* Returns <code>true</code> if the two given nodes should be separated by a blank line.
*
* @param node1 The first node.
* @param node2 The second node.
* @return <code>true</code> if the blank line between the nodes is needed.
*/
public static boolean requireBlankLineInBetween(IASTNode node1, IASTNode node2) {
if (node1 instanceof ContainerNode) {
List<IASTNode> nodes = ((ContainerNode) node1).getNodes();
if (!nodes.isEmpty()) {
node1 = nodes.get(nodes.size() - 1);
}
}
if (node2 instanceof ContainerNode) {
List<IASTNode> nodes = ((ContainerNode) node2).getNodes();
if (!nodes.isEmpty()) {
node2 = nodes.get(0);
}
}
while (node1 instanceof ICPPASTTemplateDeclaration) {
node1 = ((ICPPASTTemplateDeclaration) node1).getDeclaration();
}
while (node2 instanceof ICPPASTTemplateDeclaration) {
node2 = ((ICPPASTTemplateDeclaration) node2).getDeclaration();
}
if (node1 instanceof ICPPASTVisibilityLabel && node2 instanceof ICPPASTVisibilityLabel) {
return true;
}
if (suppressesTrailingBlankLine(node1)) {
return false;
}
if (node1 instanceof IASTPreprocessorIncludeStatement !=
node2 instanceof IASTPreprocessorIncludeStatement) {
return true;
}
if (isFunctionDeclaration(node1) != isFunctionDeclaration(node2)) {
return true;
}
if (node2 != null && requiresTrailingBlankLine(node1)) {
return true;
}
return requiresLeadingBlankLine(node2);
}
private static boolean isFunctionDeclaration(IASTNode node) {
if (!(node instanceof IASTSimpleDeclaration)) {
return false;
}
for (IASTDeclarator declarator : ((IASTSimpleDeclaration) node).getDeclarators()) {
if (declarator instanceof IASTFunctionDeclarator)
return true;
}
return false;
}
/**
* Returns true if the two given nodes have the same offset. For nodes that are normally
* separated by other tokens this is an indication that they were produced by the same macro
* expansion.
*/
private static boolean doNodesHaveSameOffset(IASTNode node1, IASTNode node2) {
IASTFileLocation fileLocation1 = node1.getFileLocation();
IASTFileLocation fileLocation2 = node2.getFileLocation();
return fileLocation1 != null && fileLocation2 != null &&
fileLocation1.getNodeOffset() == fileLocation2.getNodeOffset();
}
}