/******************************************************************************* * Copyright (c) 2008 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.commenthandler; import java.io.InputStream; import java.util.Vector; import org.eclipse.cdt.core.dom.ast.ASTVisitor; import org.eclipse.cdt.core.dom.ast.IASTComment; import org.eclipse.cdt.core.dom.ast.IASTDeclaration; import org.eclipse.cdt.core.dom.ast.IASTNode; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; import org.eclipse.cdt.core.dom.ast.cpp.CPPASTVisitor; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateDeclaration; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier.ICPPASTBaseSpecifier; import org.eclipse.cdt.internal.core.dom.parser.ASTNode; import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTDeclarationStatement; import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTExplicitTemplateInstantiation; import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTForStatement; import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTFunctionDefinition; import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTIfStatement; import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTLabelStatement; import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTLinkageSpecification; import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTSwitchStatement; import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTTemplateDeclaration; import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTWhileStatement; import org.eclipse.cdt.internal.core.dom.rewrite.util.OffsetHelper; import org.eclipse.cdt.internal.core.resources.ResourceLookup; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; /** * The NodeCommenter contains all the logic that is needed for the ASTCommentVisitor to assign the comments * to the suitable node. Together with the ASTCommenterVisitor it fills all the comments with the correspondent * node into the NodeCommentMap. * * Following, a little explanation of the assignment logic. It is only a loose illustration a detailed description * would include a combined explanation of ASTCommenterVisitor and NodeCommenter.
* To understand the logic we define the three types of comments:
* leading comments - Comments before a statement, declaration, or definition.
* trailing comments - Comments right after the AST node on the same line.
* freestanding comments - Comments before a closing brace such as they occur in * namespace-, class- and method-definitions or at the end of a file.
* * The first comment is fetched and the position of it is compared to the position of the actual node. If * the position of the comment is smaller than the comment is added to the node as leading. If it is behind the node * but on the same line it is added as trailing. If one of these possibilities match the next comment is fetched for * the same check. If it doesn't match the same procedure is done for all the child nodes. After checking the sub nodes * the actual node is checked again if the comment is trailing. Then there is also the possibility that this comment is * freestanding. This is the case when the comment is not added to any child node but the position is smaller den * the end position of the node. * * @author Guido Zgraggen IFS */ public class NodeCommenter { protected CPPASTVisitor visitor; protected CommentHandler commHandler; protected NodeCommentMap commentMap; protected Vector children; public NodeCommenter(CPPASTVisitor visitor, CommentHandler commHandler, NodeCommentMap commentMap) { this.visitor = visitor; this.commHandler = commHandler; this.commentMap = commentMap; this.children = new Vector(); } protected void writeNodeList(IASTNode[] nodes) { for(int i = 0; i < nodes.length; ++i) { nodes[i].accept(visitor); } } protected void visitNodeIfNotNull(IASTNode node){ if(node != null){ node.accept(visitor); } } protected boolean appendComment(ASTNode node, IASTComment comment) { ASTNode com = (ASTNode) comment; if(node.getFileLocation() == null) { //MacroExpansions have no Filelocation return false; } int nodeLineNumber = OffsetHelper.getEndingLineNumber(node); int commentLineNumber= OffsetHelper.getStartingLineNumber(comment); if(OffsetHelper.getNodeEndPoint(com) <= OffsetHelper.getNodeOffset(node)) { addLeadingCommentToMap(node, comment); return true; } else if(isTrailing(node, com, nodeLineNumber, commentLineNumber)) { addTrailingCommentToMap(node, comment); return true; } return false; } protected boolean appendFreestandingComment(ASTNode node, IASTComment comment) { ASTNode com = (ASTNode) comment; if(node.getFileLocation() == null) { //MacroExpansions have no Filelocation return false; } if(OffsetHelper.getNodeEndPoint(com) <= OffsetHelper.getNodeEndPoint(node)) { addFreestandingCommentToMap(node, comment); return true; } return false; } private void addLeadingCommentToMap(ASTNode node, IASTComment comment) { commentMap.addLeadingCommentToNode(node, comment); commHandler.allreadyAdded(comment); } private void addTrailingCommentToMap(ASTNode node, IASTComment comment) { commentMap.addTrailingCommentToNode(node, comment); commHandler.allreadyAdded(comment); } private void addFreestandingCommentToMap(ASTNode node, IASTComment comment) { commentMap.addFreestandingCommentToNode(node, comment); commHandler.allreadyAdded(comment); } private boolean isTrailing(ASTNode node, ASTNode com, int nodeLineNumber, int commentLineNumber) { if(nodeLineNumber == commentLineNumber && OffsetHelper.getNodeOffset(com) >= OffsetHelper.getNodeEndPoint(node) && canNotBeAddedToParent(node,com) && !mustBeAddToSubnodes(node)) { if(OffsetHelper.getNodeOffset(com) < OffsetHelper.getNodeEndPoint(node) + 2) { return true; } IPath path = new Path(node.getContainingFilename()); IFile file = ResourceLookup.selectFileForLocation(path, null); // NPE thrown below, like original behavior //XXX HSR Guido: Possible Performance Issue (File access) try { InputStream is = file.getContents(); int length = OffsetHelper.getNodeOffset(com)-OffsetHelper.getNodeEndPoint(node); byte[] b = new byte[length]; long count = is.skip(OffsetHelper.getEndOffsetWithoutComments(node)); if(count < OffsetHelper.getEndOffsetWithoutComments(node)) { return false; } if(is.read(b, 0, length) == -1) { return false; } for(byte bb : b) { if(!Character.isWhitespace(bb)) { is.close(); return false; } } is.close(); return true; } catch (Exception e) { return false; } } return false; } private boolean canNotBeAddedToParent(ASTNode node, ASTNode com) { ASTNode parent = (ASTNode) node.getParent(); if(hasNodeSameEndingAsSubnode(parent)) { return true; }else if(parent instanceof IASTTranslationUnit) { return true; }else if(parent instanceof ICPPASTTemplateDeclaration) { return true; }else if(parent instanceof CPPASTIfStatement) { return true; }else if(parent instanceof ICPPASTBaseSpecifier) { parent = (ASTNode) parent.getParent(); } return !(OffsetHelper.getNodeOffset(com) >= OffsetHelper.getNodeEndPoint(parent)); } private boolean mustBeAddToSubnodes(ASTNode node) { return hasNodeSameEndingAsSubnode(node); } private boolean hasNodeSameEndingAsSubnode(ASTNode node) { if(node instanceof CPPASTFunctionDefinition) { return true; }else if(node instanceof CPPASTDeclarationStatement) { return true; }else if(node instanceof CPPASTForStatement) { return true; }else if(node instanceof CPPASTLabelStatement) { return true; }else if(node instanceof CPPASTIfStatement) { return true; }else if(node instanceof CPPASTSwitchStatement) { return true; }else if(node instanceof CPPASTWhileStatement) { return true; }else if(node instanceof CPPASTTemplateDeclaration) { return true; }else if(node instanceof CPPASTLinkageSpecification) { return true; }else if(node instanceof CPPASTExplicitTemplateInstantiation) { return true; } return false; } protected int appendComments(ASTNode node) { while(commHandler.hasMore()) { IASTComment comment = commHandler.getFirst(); if(isNotSameFile(node, comment)) { return ASTVisitor.PROCESS_SKIP; } if(!appendComment(node, comment)) { return ASTVisitor.PROCESS_CONTINUE; } } return ASTVisitor.PROCESS_ABORT; } protected int appendFreestandingComments(ASTNode node) { while(commHandler.hasMore()) { IASTComment comment = commHandler.getFirst(); if(isNotSameFile(node, comment)) { return ASTVisitor.PROCESS_SKIP; } if(appendComment(node, comment)) { return ASTVisitor.PROCESS_CONTINUE; } if(!appendFreestandingComment(node, comment)) { return ASTVisitor.PROCESS_CONTINUE; } } return ASTVisitor.PROCESS_ABORT; } public void appendRemainingComments(IASTDeclaration declaration) { while(commHandler.hasMore()) { IASTComment comment = commHandler.getFirst(); if(appendComment((ASTNode)declaration, comment)) { continue; } addFreestandingCommentToMap((ASTNode) declaration, comment); } } private boolean isNotSameFile(IASTNode node, IASTComment comment) { if(node.getFileLocation()==null) { return true; } return !node.getFileLocation().getFileName().equals(comment.getFileLocation().getFileName()); } }