/**
* Copyright (c) 2005-2012 by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the Eclipse Public License (EPL).
* Please see the license.txt included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
package org.python.pydev.parser.visitors.scope;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.python.pydev.parser.jython.SimpleNode;
import org.python.pydev.parser.jython.ast.BoolOp;
import org.python.pydev.parser.jython.ast.For;
import org.python.pydev.parser.jython.ast.If;
import org.python.pydev.parser.jython.ast.Import;
import org.python.pydev.parser.jython.ast.ImportFrom;
import org.python.pydev.parser.jython.ast.Str;
import org.python.pydev.parser.jython.ast.Suite;
import org.python.pydev.parser.jython.ast.TryExcept;
import org.python.pydev.parser.jython.ast.TryFinally;
import org.python.pydev.parser.jython.ast.While;
import org.python.pydev.parser.jython.ast.With;
import org.python.pydev.parser.jython.ast.exprType;
import org.python.pydev.parser.jython.ast.stmtType;
public class CodeFoldingVisitor extends EasyASTIteratorWithChildrenVisitor {
/**
* Creates the iterator and traverses the passed root so that the results can be gotten.
*/
public static CodeFoldingVisitor create(SimpleNode root) {
CodeFoldingVisitor visitor = new CodeFoldingVisitor();
try {
root.accept(visitor);
} catch (Exception e) {
throw new RuntimeException(e);
}
return visitor;
}
@Override
public Object visitIf(If node) throws Exception {
ASTEntry entry = before(node);
parents.push(entry);
traverse(node);
after(entry);
parents.pop();
return null;
}
@Override
protected void doAddNode(ASTEntry entry) {
ASTEntry parent = entry.parent;
if (entry.node instanceof If) {
If entryIf = (If) entry.node;
//treat elifs
if (parent != null && parent.node instanceof If) {
If parentIf = (If) parent.node;
if (parentIf.orelse != null && parentIf.orelse.body != null && parentIf.orelse.body.length > 0
&& parentIf.orelse.body[0] == entryIf) {
parent.endLine = entry.node.beginLine - 1;
if (entry.parent != null) {
entry.parent = entry.parent.parent;
}
super.doAddNode(entry);
return;
}
}
}
super.doAddNode(entry);
}
@Override
protected void after(ASTEntry entry) {
super.after(entry);
//if we just added a node, we have to check if it's an If that has an ending else...
if (entry.node instanceof If) {
If entryIf = (If) entry.node;
checkElse(entryIf, entry);
}
}
/**
* Check if the passed if has an else... If it has, generate a 'fake' If entry for it (so that it's gotten later)
*/
private void checkElse(If entryIf, ASTEntry parentIf) {
//treat elses
if (entryIf.orelse != null && entryIf.orelse.body != null && entryIf.orelse.body.length > 0) {
stmtType firstOrElseStmt = entryIf.orelse.body[0];
if (!(firstOrElseStmt instanceof If) && firstOrElseStmt != null) {
If generatedIf = new If(new BoolOp(BoolOp.And, new exprType[0]), new stmtType[0], new Suite(
new stmtType[0]));
generatedIf.beginLine = firstOrElseStmt.beginLine - 1;
generatedIf.beginColumn = 1;
ASTEntry generatedEntry = createEntry();
generatedEntry.endLine = parentIf.endLine;
parentIf.endLine = generatedIf.beginLine - 1;
generatedEntry.node = generatedIf;
if (generatedEntry.parent != null) {
generatedEntry.parent = generatedEntry.parent.parent;
}
//actually go on and add the entry...
super.doAddNode(generatedEntry);
}
}
}
@Override
public Object visitFor(For node) throws Exception {
return defaultVisit(node);
}
@Override
public Object visitWhile(While node) throws Exception {
return defaultVisit(node);
}
@Override
public Object visitTryExcept(TryExcept node) throws Exception {
return defaultVisit(node);
}
@Override
public Object visitTryFinally(TryFinally node) throws Exception {
return defaultVisit(node);
}
@Override
public Object visitWith(With node) throws Exception {
return defaultVisit(node);
}
//not all methods have bodies... (some have 'atomic' adds)
@Override
public Object visitImport(Import node) throws Exception {
atomic(node);
return null;
}
@Override
public Object visitImportFrom(ImportFrom node) throws Exception {
atomic(node);
return null;
}
@Override
public Object visitStr(Str node) throws Exception {
atomic(node);
return null;
}
@Override
protected ASTEntry atomic(SimpleNode node) {
try {
unhandled_node(node);
} catch (Exception e) {
throw new RuntimeException(e);
}
return super.atomic(node);
}
private Object defaultVisit(SimpleNode node) throws Exception {
unhandled_node(node);
ASTEntry entry = before(node);
parents.push(entry);
traverse(node);
after(entry);
parents.pop();
return null;
}
/**
* Overriden so that we consider the children when iterating (and don't get only the roots)
* because we're interested in having a flat list in this case, and not actually the hierachical info.
*/
@Override
public List<ASTEntry> getAsList(Class... classes) {
List<ASTEntry> newList = new ArrayList<ASTEntry>();
for (Iterator<ASTEntry> iter = nodes.iterator(); iter.hasNext();) {
ASTEntryWithChildren entry = (ASTEntryWithChildren) iter.next();
checkEntry(newList, entry, classes);
}
return newList;
}
private void checkEntry(List<ASTEntry> newList, ASTEntryWithChildren entry, Class... classes) {
if (isFromClass(entry.node, classes)) {
newList.add(entry);
}
if (entry.children != null) {
for (ASTEntry child : entry.children) {
checkEntry(newList, (ASTEntryWithChildren) child, classes);
}
}
}
}