<commit_before>/*
 * Copyright 2017, <NAME>, <NAME>, <NAME>, <NAME>
 *                 Iowa State University of Science and Technology
 *                 and Bowling Green State University
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package boa.datagen.util;

import java.util.*;

import org.eclipse.jdt.core.dom.*;

import boa.types.Ast.*;
import boa.types.Ast.Type;

import static boa.datagen.util.JavaASTUtil.getFullyQualifiedName;

/**
 * @author rdyer
 */
public class Java7Visitor extends ASTVisitor {
	public static final String PROPERTY_INDEX = "i";
	
	protected CompilationUnit root = null;
	protected PositionInfo.Builder pos = null;
	protected String src = null;
	protected Map<String, Integer> declarationFile, declarationNode;

	protected Namespace.Builder b = Namespace.newBuilder();
	protected List<boa.types.Ast.Comment> comments = new ArrayList<boa.types.Ast.Comment>();
	protected Stack<List<boa.types.Ast.Declaration>> declarations = new Stack<List<boa.types.Ast.Declaration>>();
	protected Stack<boa.types.Ast.Modifier> modifiers = new Stack<boa.types.Ast.Modifier>();
	protected Stack<boa.types.Ast.Expression> expressions = new Stack<boa.types.Ast.Expression>();
	protected Stack<List<boa.types.Ast.Variable>> fields = new Stack<List<boa.types.Ast.Variable>>();
	protected Stack<List<boa.types.Ast.Method>> methods = new Stack<List<boa.types.Ast.Method>>();
	protected Stack<List<boa.types.Ast.Statement>> statements = new Stack<List<boa.types.Ast.Statement>>();

	public Java7Visitor(String src) {
		super();
		this.src = src;
	}

	public Java7Visitor(String src, Map<String, Integer> declarationFile, Map<String, Integer> declarationNode) {
		this(src);
		this.declarationFile = declarationFile;
		this.declarationNode = declarationNode;
	}

	public Namespace getNamespaces(CompilationUnit node) {
		root = node;
		node.accept(this);
		return b.build();
	}

	public List<boa.types.Ast.Comment> getComments() {
		return comments;
	}

/*
	// builds a Position message for every node and stores in the field pos
	public void preVisit(ASTNode node) {
		buildPosition(node);
	}
*/

	protected void buildPosition(final ASTNode node) {
		pos = PositionInfo.newBuilder();
		int start = node.getStartPosition();
		int length = node.getLength();
		pos.setStartPos(start);
		pos.setLength(length);
		pos.setStartLine(root.getLineNumber(start));
		pos.setStartCol(root.getColumnNumber(start));
		pos.setEndLine(root.getLineNumber(start + length));
		pos.setEndCol(root.getColumnNumber(start + length));
	}

	@Override
	public boolean visit(CompilationUnit node) {
		PackageDeclaration pkg = node.getPackage();
		if (pkg == null) {
			b.setName("");
		} else {
			b.setName(pkg.getName().getFullyQualifiedName());
			for (Object a : pkg.annotations()) {
				((Annotation)a).accept(this);
				b.addModifiers(modifiers.pop());
			}
			Integer index = (Integer) pkg.getProperty(Java7Visitor.PROPERTY_INDEX);
			if (index != null)
				b.setKey(index);
		}
		for (Object i : node.imports()) {
			ImportDeclaration id = (ImportDeclaration)i;
			String imp = "";
			if (id.isStatic())
				imp += "static ";
			imp += id.getName().getFullyQualifiedName();
			if (id.isOnDemand())
				imp += ".*";
			b.addImports(imp);
		}
		for (Object t : node.types()) {
			declarations.push(new ArrayList<boa.types.Ast.Declaration>());
			((AbstractTypeDeclaration)t).accept(this);
			for (boa.types.Ast.Declaration d : declarations.pop())
				b.addDeclarations(d);
		}
		for (Object c : node.getCommentList())
			((org.eclipse.jdt.core.dom.Comment)c).accept(this);
		return false;
	}

	@Override
	public boolean visit(BlockComment node) {
		boa.types.Ast.Comment.Builder b = boa.types.Ast.Comment.newBuilder();
		Integer index = (Integer) node.getProperty(Java7Visitor.PROPERTY_INDEX);
		if (index != null)
			b.setKey(index);
		buildPosition(node);
		b.setPosition(pos.build());
		b.setKind(boa.types.Ast.Comment.CommentKind.BLOCK);
		b.setValue(src.substring(node.getStartPosition(), node.getStartPosition() + node.getLength()));
		comments.add(b.build());
		return false;
	}

	@Override
	public boolean visit(LineComment node) {
		boa.types.Ast.Comment.Builder b = boa.types.Ast.Comment.newBuilder();
		Integer index = (Integer) node.getProperty(Java7Visitor.PROPERTY_INDEX);
		if (index != null)
			b.setKey(index);
		buildPosition(node);
		b.setPosition(pos.build());
		b.setKind(boa.types.Ast.Comment.CommentKind.LINE);
		b.setValue(src.substring(node.getStartPosition(), node.getStartPosition() + node.getLength()));
		comments.add(b.build());
		return false;
	}

	@Override
	public boolean visit(Javadoc node) {
		boa.types.Ast.Comment.Builder b = boa.types.Ast.Comment.newBuilder();
		Integer index = (Integer) node.getProperty(Java7Visitor.PROPERTY_INDEX);
		if (index != null)
			b.setKey(index);
		buildPosition(node);
		b.setPosition(pos.build());
		b.setKind(boa.types.Ast.Comment.CommentKind.DOC);
		b.setValue(src.substring(node.getStartPosition(), node.getStartPosition() + node.getLength()));
		comments.add(b.build());
		return false;
	}

	//////////////////////////////////////////////////////////////
	// Type Declarations

	@Override
	public boolean visit(TypeDeclaration node) {
		boa.types.Ast.Declaration.Builder b = boa.types.Ast.Declaration.newBuilder();
		b.setName(node.getName().getIdentifier());
		b.setFullyQualifiedName(getFullyQualifiedName(node));
		setDeclaringClass(b, node.resolveBinding());
		Integer key = (Integer) node.getProperty(PROPERTY_INDEX);
		if (key != null)
			b.setKey((int) key);
		if (node.isInterface())
			b.setKind(boa.types.Ast.TypeKind.INTERFACE);
		else
			b.setKind(boa.types.Ast.TypeKind.CLASS);
		for (Object m : node.modifiers()) {
			if (((IExtendedModifier)m).isAnnotation())
				((Annotation)m).accept(this);
			else
				((org.eclipse.jdt.core.dom.Modifier)m).accept(this);
			b.addModifiers(modifiers.pop());
		}
		for (Object t : node.typeParameters()) {
			boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
			String name = ((TypeParameter)t).getName().getFullyQualifiedName();
			String bounds = "";
			for (Object o : ((TypeParameter)t).typeBounds()) {
				if (bounds.length() > 0)
					bounds += " & ";
				bounds += typeName((org.eclipse.jdt.core.dom.Type)o);
			}
			if (bounds.length() > 0)
				name = name + " extends " + bounds;
			tb.setName(name);
			tb.setKind(boa.types.Ast.TypeKind.GENERIC);
			setTypeBinding(tb, ((TypeParameter) t).getName());
			Integer index = (Integer) ((ASTNode) t).getProperty(Java7Visitor.PROPERTY_INDEX);
			if (index != null)
				tb.setKey(index);
			b.addGenericParameters(tb.build());
		}
		if (node.getSuperclassType() != null) {
			boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
			tb.setName(typeName(node.getSuperclassType()));
			tb.setKind(boa.types.Ast.TypeKind.CLASS);
			setTypeBinding(tb, node.getSuperclassType());
			Integer index = (Integer) node.getSuperclassType().getProperty(Java7Visitor.PROPERTY_INDEX);
			if (index != null)
				tb.setKey(index);
			b.addParents(tb.build());
		}
		for (Object t : node.superInterfaceTypes()) {
			boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
			tb.setName(typeName((org.eclipse.jdt.core.dom.Type) t));
			tb.setKind(boa.types.Ast.TypeKind.INTERFACE);
			setTypeBinding(tb, (org.eclipse.jdt.core.dom.Type) t);
			Integer index = (Integer) ((ASTNode) t).getProperty(Java7Visitor.PROPERTY_INDEX);
			if (index != null)
				tb.setKey(index);
			b.addParents(tb.build());
		}
		for (Object d : node.bodyDeclarations()) {
			if (d instanceof FieldDeclaration) {
				fields.push(new ArrayList<boa.types.Ast.Variable>());
				((FieldDeclaration)d).accept(this);
				for (boa.types.Ast.Variable v : fields.pop())
					b.addFields(v);
			} else if (d instanceof MethodDeclaration) {
				methods.push(new ArrayList<boa.types.Ast.Method>());
				((MethodDeclaration)d).accept(this);
				for (boa.types.Ast.Method m : methods.pop())
					b.addMethods(m);
			} else if (d instanceof Initializer) {
				methods.push(new ArrayList<boa.types.Ast.Method>());
				((Initializer)d).accept(this);
				for (boa.types.Ast.Method m : methods.pop())
					b.addMethods(m);
			} else {
				declarations.push(new ArrayList<boa.types.Ast.Declaration>());
				((BodyDeclaration)d).accept(this);
				for (boa.types.Ast.Declaration nd : declarations.pop())
					b.addNestedDeclarations(nd);
			}
		}
		declarations.peek().add(b.build());
		return false;
	}

	@Override
	public boolean visit(AnonymousClassDeclaration node) {
		boa.types.Ast.Declaration.Builder b = boa.types.Ast.Declaration.newBuilder();
		b.setName("");
		b.setKind(boa.types.Ast.TypeKind.ANONYMOUS);
//		b.setFullyQualifiedName(getFullyQualifiedName(node));
		Integer index = (Integer) node.getProperty(Java7Visitor.PROPERTY_INDEX);
		if (index != null)
			b.setKey(index);
		for (Object d : node.bodyDeclarations()) {
			if (d instanceof FieldDeclaration) {
				fields.push(new ArrayList<boa.types.Ast.Variable>());
				((FieldDeclaration)d).accept(this);
				for (boa.types.Ast.Variable v : fields.pop())
					b.addFields(v);
			} else if (d instanceof MethodDeclaration) {
				methods.push(new ArrayList<boa.types.Ast.Method>());
				((MethodDeclaration)d).accept(this);
				for (boa.types.Ast.Method m : methods.pop())
					b.addMethods(m);
			} else if (d instanceof Initializer) {
				methods.push(new ArrayList<boa.types.Ast.Method>());
				((Initializer)d).accept(this);
				for (boa.types.Ast.Method m : methods.pop())
					b.addMethods(m);
			} else {
				declarations.push(new ArrayList<boa.types.Ast.Declaration>());
				((BodyDeclaration)d).accept(this);
				for (boa.types.Ast.Declaration nd : declarations.pop())
					b.addNestedDeclarations(nd);
			}
		}
		declarations.peek().add(b.build());
		return false;
	}

	@Override
	public boolean visit(EnumDeclaration node) {
		boa.types.Ast.Declaration.Builder b = boa.types.Ast.Declaration.newBuilder();
		b.setName(node.getName().getIdentifier());
		b.setKind(boa.types.Ast.TypeKind.ENUM);
		b.setFullyQualifiedName(getFullyQualifiedName(node));
		setDeclaringClass(b, node.resolveBinding());
		Integer key = (Integer) node.getProperty(PROPERTY_INDEX);
		if (key != null)
			b.setKey((int) key);
		for (Object c : node.enumConstants()) {
			// TODO EnumConstantDeclaration
		}
		for (Object t : node.superInterfaceTypes()) {
			boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
			tb.setName(typeName((org.eclipse.jdt.core.dom.Type) t));
			tb.setKind(boa.types.Ast.TypeKind.INTERFACE);
			setTypeBinding(tb, (org.eclipse.jdt.core.dom.Type) t);
			Integer index = (Integer) ((ASTNode) t).getProperty(Java7Visitor.PROPERTY_INDEX);
			if (index != null)
				tb.setKey(index);
			b.addParents(tb.build());
		}
		for (Object d : node.bodyDeclarations()) {
			if (d instanceof FieldDeclaration) {
				fields.push(new ArrayList<boa.types.Ast.Variable>());
				((FieldDeclaration)d).accept(this);
				for (boa.types.Ast.Variable v : fields.pop())
					b.addFields(v);
			} else if (d instanceof MethodDeclaration) {
				methods.push(new ArrayList<boa.types.Ast.Method>());
				((MethodDeclaration)d).accept(this);
				for (boa.types.Ast.Method m : methods.pop())
					b.addMethods(m);
			} else if (d instanceof Initializer) {
				methods.push(new ArrayList<boa.types.Ast.Method>());
				((Initializer)d).accept(this);
				for (boa.types.Ast.Method m : methods.pop())
					b.addMethods(m);
			} else {
				declarations.push(new ArrayList<boa.types.Ast.Declaration>());
				((BodyDeclaration)d).accept(this);
				for (boa.types.Ast.Declaration nd : declarations.pop())
					b.addNestedDeclarations(nd);
			}
		}
		declarations.peek().add(b.build());
		return false;
	}

	@Override
	public boolean visit(AnnotationTypeDeclaration node) {
		boa.types.Ast.Declaration.Builder b = boa.types.Ast.Declaration.newBuilder();
		b.setName(node.getName().getFullyQualifiedName());
		b.setKind(boa.types.Ast.TypeKind.ANNOTATION);
		b.setFullyQualifiedName(getFullyQualifiedName(node));
		setDeclaringClass(b, node.resolveBinding());
		Integer key = (Integer) node.getProperty(PROPERTY_INDEX);
		if (key != null)
			b.setKey((int) key);
		for (Object m : node.modifiers()) {
			if (((IExtendedModifier)m).isAnnotation())
				((Annotation)m).accept(this);
			else
				((org.eclipse.jdt.core.dom.Modifier)m).accept(this);
			b.addModifiers(modifiers.pop());
		}
		for (Object d : node.bodyDeclarations()) {
			if (d instanceof FieldDeclaration) {
				fields.push(new ArrayList<boa.types.Ast.Variable>());
				((FieldDeclaration)d).accept(this);
				for (boa.types.Ast.Variable v : fields.pop())
					b.addFields(v);
			} else if (d instanceof Initializer) {
				methods.push(new ArrayList<boa.types.Ast.Method>());
				((Initializer)d).accept(this);
				for (boa.types.Ast.Method m : methods.pop())
					b.addMethods(m);
			} else if (d instanceof AnnotationTypeMemberDeclaration) {
				methods.push(new ArrayList<boa.types.Ast.Method>());
				((AnnotationTypeMemberDeclaration)d).accept(this);
				for (boa.types.Ast.Method m : methods.pop())
					b.addMethods(m);
			} else {
				declarations.push(new ArrayList<boa.types.Ast.Declaration>());
				((BodyDeclaration)d).accept(this);
				for (boa.types.Ast.Declaration nd : declarations.pop())
					b.addNestedDeclarations(nd);
			}
		}
		declarations.peek().add(b.build());
		return false;
	}

	//////////////////////////////////////////////////////////////
	// Utilities

	protected void setDeclaringClass(Declaration.Builder db, ITypeBinding binding) {
		if (declarationNode == null || binding == null)
			return;
		ITypeBinding tb = binding.getDeclaringClass();
		if (tb != null) {
			if (tb.getTypeDeclaration() != null)
				tb = tb.getTypeDeclaration();
			String key = tb.getKey();
			Integer index = declarationNode.get(key);
			if (index != null)
				db.setDeclaringType(index);
		}
	}

	protected void setDeclaringClass(Method.Builder b, IMethodBinding binding) {
		if (declarationNode == null || binding == null)
			return;
		ITypeBinding tb = binding.getDeclaringClass();
		if (tb == null)
			return;
		if (tb.getTypeDeclaration() != null)
			tb = tb.getTypeDeclaration();
		String key = tb.getKey();
		Integer index = declarationNode.get(key);
		if (index != null)
			b.setDeclaringType(index);
	}

	protected void setDeclaringClass(Variable.Builder b, IVariableBinding binding) {
		if (declarationNode == null || binding == null)
			return;
		ITypeBinding tb = binding.getDeclaringClass();
		if (tb == null)
			return;
		if (tb.getTypeDeclaration() != null)
			tb = tb.getTypeDeclaration();
		String key = tb.getKey();
		Integer index = declarationNode.get(key);
		if (index != null)
			b.setDeclaringType(index);
	}

	protected void setTypeBinding(boa.types.Ast.Type.Builder b, org.eclipse.jdt.core.dom.Type type) {
		ITypeBinding tb = type.resolveBinding();
		if (tb != null) {
			if (tb.getTypeDeclaration() != null)
				tb = tb.getTypeDeclaration();
			if (tb.isInterface())
				b.setKind(boa.types.Ast.TypeKind.INTERFACE);
			else if (tb.isEnum())
				b.setKind(boa.types.Ast.TypeKind.ENUM);
			else if (tb.isAnnotation())
				b.setKind(boa.types.Ast.TypeKind.ANNOTATION);
			else if (tb.isAnonymous())
				b.setKind(boa.types.Ast.TypeKind.ANONYMOUS);
			else 
				b.setKind(boa.types.Ast.TypeKind.OTHER);
			if (!tb.isPrimitive()) {
				b.setFullyQualifiedName(tb.getQualifiedName());
				if (declarationFile != null && !tb.isArray()) {
					String key = tb.getKey();
					Integer index = declarationFile.get(key);
					if (index != null) {
						b.setDeclarationFile(index);
						b.setDeclaration(declarationNode.get(key));
					}
				}
			}
		}
	}

	protected void setTypeBinding(boa.types.Ast.Type.Builder b, org.eclipse.jdt.core.dom.Expression e) {
		ITypeBinding tb = e.resolveTypeBinding();
		if (tb != null) {
			if (tb.getTypeDeclaration() != null)
				tb = tb.getTypeDeclaration();
			if (tb.isInterface())
				b.setKind(boa.types.Ast.TypeKind.INTERFACE);
			else if (tb.isEnum())
				b.setKind(boa.types.Ast.TypeKind.ENUM);
			else if (tb.isAnnotation())
				b.setKind(boa.types.Ast.TypeKind.ANNOTATION);
			else if (tb.isAnonymous())
				b.setKind(boa.types.Ast.TypeKind.ANONYMOUS);
			else 
				b.setKind(boa.types.Ast.TypeKind.OTHER);
			if (!tb.isPrimitive()) {
				b.setFullyQualifiedName(tb.getQualifiedName());
				if (declarationFile != null && !tb.isArray()) {
					String key = tb.getKey();
					Integer index = declarationFile.get(key);
					if (index != null) {
						b.setDeclarationFile(index);
						b.setDeclaration(declarationNode.get(key));
					}
				}
			}
		}
	}

	protected Type buildType(ITypeBinding itb) {
		if (itb.getTypeDeclaration() != null)
			itb = itb.getTypeDeclaration();
		boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
		tb.setName(itb.getName());
		if (itb.isInterface())
			tb.setKind(boa.types.Ast.TypeKind.INTERFACE);
		else if (itb.isEnum())
			tb.setKind(boa.types.Ast.TypeKind.ENUM);
		else if (itb.isAnnotation())
			tb.setKind(boa.types.Ast.TypeKind.ANNOTATION);
		else if (itb.isAnonymous())
			tb.setKind(boa.types.Ast.TypeKind.ANONYMOUS);
		else 
			tb.setKind(boa.types.Ast.TypeKind.OTHER);
		if (!itb.isPrimitive()) {
			tb.setFullyQualifiedName(itb.getQualifiedName());
			if (declarationFile != null && !itb.isArray()) {
				String key = itb.getKey();
				Integer index = declarationFile.get(key);
				if (index != null) {
					tb.setDeclarationFile(index);
					tb.setDeclaration(declarationNode.get(key));
				}
			}
		}
		return tb.build();
	}

	//////////////////////////////////////////////////////////////
	// Field/Method Declarations

	@Override
	public boolean visit(MethodDeclaration node) {
		List<boa.types.Ast.Method> list = methods.peek();
		Method.Builder b = Method.newBuilder();
		Integer index = (Integer) node.getProperty(Java7Visitor.PROPERTY_INDEX);
		if (index != null)
			b.setKey(index);
		if (node.isConstructor())
			b.setName("<init>");
		else
			b.setName(node.getName().getFullyQualifiedName());
		for (Object m : node.modifiers()) {
			if (((IExtendedModifier)m).isAnnotation())
				((Annotation)m).accept(this);
			else
				((org.eclipse.jdt.core.dom.Modifier)m).accept(this);
			b.addModifiers(modifiers.pop());
		}
		boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
		if (node.getReturnType2() != null) {
			String name = typeName(node.getReturnType2());
			for (int i = 0; i < node.getExtraDimensions(); i++)
				name += "[]";
			tb.setName(name);
			tb.setKind(boa.types.Ast.TypeKind.OTHER);
			setTypeBinding(tb, node.getReturnType2());
			index = (Integer) node.getReturnType2().getProperty(Java7Visitor.PROPERTY_INDEX);
			if (index != null)
				tb.setKey(index);
			b.setReturnType(tb.build());
		} else {
			tb.setName("void");
			tb.setKind(boa.types.Ast.TypeKind.OTHER);
			b.setReturnType(tb.build());
		}
		for (Object t : node.typeParameters()) {
			boa.types.Ast.Type.Builder tp = boa.types.Ast.Type.newBuilder();
			String name = ((TypeParameter)t).getName().getFullyQualifiedName();
			String bounds = "";
			for (Object o : ((TypeParameter)t).typeBounds()) {
				if (bounds.length() > 0)
					bounds += " & ";
				bounds += typeName((org.eclipse.jdt.core.dom.Type)o);
			}
			if (bounds.length() > 0)
				name = name + " extends " + bounds;
			tp.setName(name);
			tp.setKind(boa.types.Ast.TypeKind.GENERIC);
			setTypeBinding(tp, ((TypeParameter)t).getName());
			index = (Integer) ((ASTNode) t).getProperty(Java7Visitor.PROPERTY_INDEX);
			if (index != null)
				tp.setKey(index);
			b.addGenericParameters(tp.build());
		}
		for (Object o : node.parameters()) {
			SingleVariableDeclaration ex = (SingleVariableDeclaration)o;
			Variable.Builder vb = Variable.newBuilder();
			index = (Integer) ex.getProperty(Java7Visitor.PROPERTY_INDEX);
			if (index != null)
				vb.setKey(index);
			vb.setName(ex.getName().getFullyQualifiedName());
			for (Object m : ex.modifiers()) {
				if (((IExtendedModifier)m).isAnnotation())
					((Annotation)m).accept(this);
				else
					((org.eclipse.jdt.core.dom.Modifier)m).accept(this);
				vb.addModifiers(modifiers.pop());
			}
			boa.types.Ast.Type.Builder tp = boa.types.Ast.Type.newBuilder();
			String name = typeName(ex.getType());
			for (int i = 0; i < ex.getExtraDimensions(); i++)
				name += "[]";
			if (ex.isVarargs())
				name += "...";
			tp.setName(name);
			tp.setKind(boa.types.Ast.TypeKind.OTHER);
			setTypeBinding(tp, ex.getType());
			index = (Integer) ex.getType().getProperty(Java7Visitor.PROPERTY_INDEX);
			if (index != null)
				tp.setKey(index);
			vb.setVariableType(tp.build());
			if (ex.getInitializer() != null) {
				ex.getInitializer().accept(this);
				vb.setInitializer(expressions.pop());
			}
			b.addArguments(vb.build());
		}
		for (Object o : node.thrownExceptionTypes()) {
			boa.types.Ast.Type.Builder tp = boa.types.Ast.Type.newBuilder();
			tp.setName(typeName((org.eclipse.jdt.core.dom.Type) o));
			tp.setKind(boa.types.Ast.TypeKind.CLASS);
			setTypeBinding(tp, (org.eclipse.jdt.core.dom.Type) o);
			index = (Integer) ((ASTNode) o).getProperty(Java7Visitor.PROPERTY_INDEX);
			if (index != null)
				tp.setKey(index);
			b.addExceptionTypes(tp.build());
		}
		if (node.getBody() != null) {
			statements.push(new ArrayList<boa.types.Ast.Statement>());
			node.getBody().accept(this);
			for (boa.types.Ast.Statement s : statements.pop())
				b.addStatements(s);
		}
		setDeclaringClass(b, node.resolveBinding());
		list.add(b.build());
		return false;
	}

	@Override
	public boolean visit(AnnotationTypeMemberDeclaration node) {
		List<boa.types.Ast.Method> list = methods.peek();
		Method.Builder b = Method.newBuilder();
		Integer index = (Integer) node.getProperty(Java7Visitor.PROPERTY_INDEX);
		if (index != null)
			b.setKey(index);
		b.setName(node.getName().getFullyQualifiedName());
		for (Object m : node.modifiers()) {
			if (((IExtendedModifier)m).isAnnotation())
				((Annotation)m).accept(this);
			else
				((org.eclipse.jdt.core.dom.Modifier)m).accept(this);
			b.addModifiers(modifiers.pop());
		}
		boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
		tb.setName(typeName(node.getType()));
		tb.setKind(boa.types.Ast.TypeKind.OTHER);
		setTypeBinding(tb, node.getType());
		index = (Integer) node.getType().getProperty(Java7Visitor.PROPERTY_INDEX);
		if (index != null)
			tb.setKey(index);
		b.setReturnType(tb.build());
		if (node.getDefault() != null) {
			boa.types.Ast.Statement.Builder sb = boa.types.Ast.Statement.newBuilder();
			index = (Integer) node.getDefault().getProperty(Java7Visitor.PROPERTY_INDEX);
			if (index != null)
				sb.setKey(index);
			sb.setKind(boa.types.Ast.Statement.StatementKind.EXPRESSION);
			node.getDefault().accept(this);
			sb.setExpression(expressions.pop());
			b.addStatements(sb.build());
		}
		setDeclaringClass(b, node.resolveBinding());
		list.add(b.build());
		return false;
	}

	@Override
	public boolean visit(FieldDeclaration node) {
		List<boa.types.Ast.Variable> list = fields.peek();
		for (Object o : node.fragments()) {
			VariableDeclarationFragment f = (VariableDeclarationFragment)o;
			Variable.Builder b = Variable.newBuilder();
			Integer index = (Integer) f.getProperty(Java7Visitor.PROPERTY_INDEX);
			if (index != null)
				b.setKey(index);
			b.setName(f.getName().getFullyQualifiedName());
			for (Object m : node.modifiers()) {
				if (((IExtendedModifier)m).isAnnotation())
					((Annotation)m).accept(this);
				else
					((org.eclipse.jdt.core.dom.Modifier)m).accept(this);
				b.addModifiers(modifiers.pop());
			}
			boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
			String name = typeName(node.getType());
			for (int i = 0; i < f.getExtraDimensions(); i++)
				name += "[]";
			tb.setName(name);
			tb.setKind(boa.types.Ast.TypeKind.OTHER);
			setTypeBinding(tb, node.getType());
			index = (Integer) node.getType().getProperty(Java7Visitor.PROPERTY_INDEX);
			if (index != null)
				tb.setKey(index);
			b.setVariableType(tb.build());
			if (f.getInitializer() != null) {
				f.getInitializer().accept(this);
				b.setInitializer(expressions.pop());
			}
			setDeclaringClass(b, f.resolveBinding());
			list.add(b.build());
		}
		return false;
	}

	//////////////////////////////////////////////////////////////
	// Modifiers and Annotations

	protected boa.types.Ast.Modifier.Builder getAnnotationBuilder(Annotation node) {
		boa.types.Ast.Modifier.Builder b = boa.types.Ast.Modifier.newBuilder();
		Integer index = (Integer) node.getProperty(Java7Visitor.PROPERTY_INDEX);
		if (index != null)
			b.setKey(index);
		b.setKind(boa.types.Ast.Modifier.ModifierKind.ANNOTATION);
		b.setAnnotationName(node.getTypeName().getFullyQualifiedName());
		return b;
	}

	@Override
	public boolean visit(MarkerAnnotation node) {
		modifiers.push(getAnnotationBuilder(node).build());
		return false;
	}

	@Override
	public boolean visit(SingleMemberAnnotation node) {
		boa.types.Ast.Modifier.Builder b = getAnnotationBuilder(node);
		node.getValue().accept(this);
		if (expressions.empty()) {
			boa.types.Ast.Expression.Builder eb = boa.types.Ast.Expression.newBuilder();
			eb.setKind(boa.types.Ast.Expression.ExpressionKind.ANNOTATION);
			eb.setAnnotation(modifiers.pop());
			b.addAnnotationMembers("value");
			b.addAnnotationValues(eb.build());
		} else {
			b.addAnnotationMembers("value");
			b.addAnnotationValues(expressions.pop());
		}
		modifiers.push(b.build());
		return false;
	}

	@Override
	public boolean visit(NormalAnnotation node) {
		boa.types.Ast.Modifier.Builder b = getAnnotationBuilder(node);
		for (Object v : node.values()) {
			MemberValuePair pair = (MemberValuePair)v;
			pair.getValue().accept(this);
			if (expressions.empty()) {
				boa.types.Ast.Expression.Builder eb = boa.types.Ast.Expression.newBuilder();
				eb.setKind(boa.types.Ast.Expression.ExpressionKind.ANNOTATION);
				eb.setAnnotation(modifiers.pop());
				b.addAnnotationMembers(pair.getName().getFullyQualifiedName());
				b.addAnnotationValues(eb.build());
			} else {
				b.addAnnotationMembers(pair.getName().getFullyQualifiedName());
				b.addAnnotationValues(expressions.pop());
			}
		}
		modifiers.push(b.build());
		return false;
	}

	@Override
	public boolean visit(org.eclipse.jdt.core.dom.Modifier node) {
		boa.types.Ast.Modifier.Builder b = boa.types.Ast.Modifier.newBuilder();
		Integer index = (Integer) node.getProperty(Java7Visitor.PROPERTY_INDEX);
		if (index != null)
			b.setKey(index);
		if (node.isFinal())
			b.setKind(boa.types.Ast.Modifier.ModifierKind.FINAL);
		else if (node.isAbstract())
			b.setKind(boa.types.Ast.Modifier.ModifierKind.ABSTRACT);
		else if (node.isStatic())
			b.setKind(boa.types.Ast.Modifier.ModifierKind.STATIC);
		else if (node.isSynchronized())
			b.setKind(boa.types.Ast.Modifier.ModifierKind.SYNCHRONIZED);
		else if (node.isPublic()) {
			b.setKind(boa.types.Ast.Modifier.ModifierKind.VISIBILITY);
			b.setVisibility(boa.types.Ast.Modifier.Visibility.PUBLIC);
		} else if (node.isPrivate()) {
			b.setKind(boa.types.Ast.Modifier.ModifierKind.VISIBILITY);
			b.setVisibility(boa.types.Ast.Modifier.Visibility.PRIVATE);
		} else if (node.isProtected()) {
			b.setKind(boa.types.Ast.Modifier.ModifierKind.VISIBILITY);
			b.setVisibility(boa.types.Ast.Modifier.Visibility.PROTECTED);
		} else {
			b.setKind(boa.types.Ast.Modifier.ModifierKind.OTHER);
			b.setOther(node.getKeyword().toString());
		}
		modifiers.push(b.build());
		return false;
	}

	//////////////////////////////////////////////////////////////
	// Statements

	@Override
	public boolean visit(AssertStatement node) {
		boa.types.Ast.Statement.Builder b = boa.types.Ast.Statement.newBuilder();
		Integer index = (Integer) node.getProperty(Java7Visitor.PROPERTY_INDEX);
		if (index != null)
			b.setKey(index);
		List<boa.types.Ast.Statement> list = statements.peek();
		b.setKind(boa.types.Ast.Statement.StatementKind.ASSERT);
		node.getExpression().accept(this);
		b.setCondition(expressions.pop());
		if (node.getMessage() != null) {
			node.getMessage().accept(this);
			b.setExpression(expressions.pop());
		}
		list.add(b.build());
		return false;
	}

	@Override
	public boolean visit(Block node) {
		boa.types.Ast.Statement.Builder b = boa.types.Ast.Statement.newBuilder();
		Integer index = (Integer) node.getProperty(Java7Visitor.PROPERTY_INDEX);
		if (index != null)
			b.setKey(index);
		List<boa.types.Ast.Statement> list = statements.peek();
		b.setKind(boa.types.Ast.Statement.StatementKind.BLOCK);
		for (Object s : node.statements()) {
			statements.push(new ArrayList<boa.types.Ast.Statement>());
			((org.eclipse.jdt.core.dom.Statement)s).accept(this);
			for (boa.types.Ast.Statement st : statements.pop())
				b.addStatements(st);
		}
		list.add(b.build());
		return false;
	}

	@Override
	public boolean visit(BreakStatement node) {
		boa.types.Ast.Statement.Builder b = boa.types.Ast.Statement.newBuilder();
		Integer index = (Integer) node.getProperty(Java7Visitor.PROPERTY_INDEX);
		if (index != null)
			b.setKey(index);
		List<boa.types.Ast.Statement> list = statements.peek();
		b.setKind(boa.types.Ast.Statement.StatementKind.BREAK);
		if (node.getLabel() != null) {
			boa.types.Ast.Expression.Builder eb = boa.types.Ast.Expression.newBuilder();
			index = (Integer) node.getLabel().getProperty(Java7Visitor.PROPERTY_INDEX);
			if (index != null)
				eb.setKey(index);
			eb.setLiteral(node.getLabel().getFullyQualifiedName());
			eb.setKind(boa.types.Ast.Expression.ExpressionKind.LITERAL);
			b.setExpression(eb.build());
		}
		list.add(b.build());
		return false;
	}

	@Override
	public boolean visit(CatchClause node) {
		boa.types.Ast.Statement.Builder b = boa.types.Ast.Statement.newBuilder();
		Integer index = (Integer) node.getProperty(Java7Visitor.PROPERTY_INDEX);
		if (index != null)
			b.setKey(index);
		List<boa.types.Ast.Statement> list = statements.peek();
		b.setKind(boa.types.Ast.Statement.StatementKind.CATCH);
		SingleVariableDeclaration ex = node.getException();
		Variable.Builder vb = Variable.newBuilder();
		index = (Integer) ex.getProperty(Java7Visitor.PROPERTY_INDEX);
		if (index != null)
			vb.setKey(index);
		vb.setName(ex.getName().getFullyQualifiedName());
		for (Object m : ex.modifiers()) {
			if (((IExtendedModifier)m).isAnnotation())
				((Annotation)m).accept(this);
			else
				((org.eclipse.jdt.core.dom.Modifier)m).accept(this);
			vb.addModifiers(modifiers.pop());
		}
		boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
		String name = typeName(ex.getType());
		for (int i = 0; i < ex.getExtraDimensions(); i++)
			name += "[]";
		tb.setName(name);
		tb.setKind(boa.types.Ast.TypeKind.OTHER);
		setTypeBinding(tb, ex.getType());
		index = (Integer) ex.getType().getProperty(Java7Visitor.PROPERTY_INDEX);
		if (index != null)
			tb.setKey(index);
		vb.setVariableType(tb.build());
		if (ex.getInitializer() != null) {
			ex.getInitializer().accept(this);
			vb.setInitializer(expressions.pop());
		}
		b.setVariableDeclaration(vb.build());
		statements.push(new ArrayList<boa.types.Ast.Statement>());
		for (Object s : node.getBody().statements())
			((org.eclipse.jdt.core.dom.Statement)s).accept(this);
		for (boa.types.Ast.Statement s : statements.pop())
			b.addStatements(s);
		list.add(b.build());
		return false;
	}

	@Override
	public boolean visit(ConstructorInvocation node) {
		boa.types.Ast.Statement.Builder b = boa.types.Ast.Statement.newBuilder();
		Integer index = (Integer) node.getProperty(Java7Visitor.PROPERTY_INDEX);
		if (index != null)
			b.setKey(index);
		List<boa.types.Ast.Statement> list = statements.peek();
		b.setKind(boa.types.Ast.Statement.StatementKind.EXPRESSION);
		boa.types.Ast.Expression.Builder eb = boa.types.Ast.Expression.newBuilder();
		index = (Integer) node.getProperty(Java7Visitor.PROPERTY_INDEX);
		if (index != null)
			eb.setKey(index);
		if (node.resolveConstructorBinding() != null) {
			IMethodBinding mb = node.resolveConstructorBinding();
			eb.setReturnType(buildType(mb.getReturnType()));
			if (mb.getDeclaringClass() != null)
				eb.setDeclaringType(buildType(mb.getDeclaringClass()));
		}
		eb.setKind(boa.types.Ast.Expression.ExpressionKind.METHODCALL);
		eb.setMethod("<init>");
		for (Object a : node.arguments()) {
			((org.eclipse.jdt.core.dom.Expression)a).accept(this);
			eb.addMethodArgs(expressions.pop());
		}
		for (Object t : node.typeArguments()) {
			boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
			tb.setName(typeName((org.eclipse.jdt.core.dom.Type) t));
			tb.setKind(boa.types.Ast.TypeKind.GENERIC);
			setTypeBinding(tb, (org.eclipse.jdt.core.dom.Type) t);
			index = (Integer) ((ASTNode) t).getProperty(Java7Visitor.PROPERTY_INDEX);
			if (index != null)
				tb.setKey(index);
			eb.addGenericParameters(tb.build());
		}
		b.setExpression(eb.build());
		list.add(b.build());
		return false;
	}

	@Override
	public boolean visit(ContinueStatement node) {
		boa.types.Ast.Statement.Builder b = boa.types.Ast.Statement.newBuilder();
		Integer index = (Integer) node.getProperty(Java7Visitor.PROPERTY_INDEX);
		if (index != null)
			b.setKey(index);
		List<boa.types.Ast.Statement> list = statements.peek();
		b.setKind(boa.types.Ast.Statement.StatementKind.CONTINUE);
		if (node.getLabel() != null) {
			boa.types.Ast.Expression.Builder eb = boa.types.Ast.Expression.newBuilder();
			index = (Integer) node.getLabel().getProperty(Java7Visitor.PROPERTY_INDEX);
			if (index != null)
				b.setKey(index);
			eb.setLiteral(node.getLabel().getFullyQualifiedName());
			eb.setKind(boa.types.Ast.Expression.ExpressionKind.LITERAL);
			b.setExpression(eb.build());
		}
		list.add(b.build());
		return false;
	}

	@Override
	public boolean visit(DoStatement node) {
		boa.types.Ast.Statement.Builder b = boa.types.Ast.Statement.newBuilder();
		Integer index = (Integer) node.getProperty(Java7Visitor.PROPERTY_INDEX);<commit_msg>fix bug: wrong variable name<commit_after>/*
 * Copyright 2017, <NAME>, <NAME>, <NAME>, <NAME>
 *                 Iowa State University of Science and Technology
 *                 and Bowling Green State University
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package boa.datagen.util;

import java.util.*;

import org.eclipse.jdt.core.dom.*;

import boa.types.Ast.*;
import boa.types.Ast.Type;

import static boa.datagen.util.JavaASTUtil.getFullyQualifiedName;

/**
 * @author rdyer
 */
public class Java7Visitor extends ASTVisitor {
	public static final String PROPERTY_INDEX = "i";
	
	protected CompilationUnit root = null;
	protected PositionInfo.Builder pos = null;
	protected String src = null;
	protected Map<String, Integer> declarationFile, declarationNode;

	protected Namespace.Builder b = Namespace.newBuilder();
	protected List<boa.types.Ast.Comment> comments = new ArrayList<boa.types.Ast.Comment>();
	protected Stack<List<boa.types.Ast.Declaration>> declarations = new Stack<List<boa.types.Ast.Declaration>>();
	protected Stack<boa.types.Ast.Modifier> modifiers = new Stack<boa.types.Ast.Modifier>();
	protected Stack<boa.types.Ast.Expression> expressions = new Stack<boa.types.Ast.Expression>();
	protected Stack<List<boa.types.Ast.Variable>> fields = new Stack<List<boa.types.Ast.Variable>>();
	protected Stack<List<boa.types.Ast.Method>> methods = new Stack<List<boa.types.Ast.Method>>();
	protected Stack<List<boa.types.Ast.Statement>> statements = new Stack<List<boa.types.Ast.Statement>>();

	public Java7Visitor(String src) {
		super();
		this.src = src;
	}

	public Java7Visitor(String src, Map<String, Integer> declarationFile, Map<String, Integer> declarationNode) {
		this(src);
		this.declarationFile = declarationFile;
		this.declarationNode = declarationNode;
	}

	public Namespace getNamespaces(CompilationUnit node) {
		root = node;
		node.accept(this);
		return b.build();
	}

	public List<boa.types.Ast.Comment> getComments() {
		return comments;
	}

/*
	// builds a Position message for every node and stores in the field pos
	public void preVisit(ASTNode node) {
		buildPosition(node);
	}
*/

	protected void buildPosition(final ASTNode node) {
		pos = PositionInfo.newBuilder();
		int start = node.getStartPosition();
		int length = node.getLength();
		pos.setStartPos(start);
		pos.setLength(length);
		pos.setStartLine(root.getLineNumber(start));
		pos.setStartCol(root.getColumnNumber(start));
		pos.setEndLine(root.getLineNumber(start + length));
		pos.setEndCol(root.getColumnNumber(start + length));
	}

	@Override
	public boolean visit(CompilationUnit node) {
		PackageDeclaration pkg = node.getPackage();
		if (pkg == null) {
			b.setName("");
		} else {
			b.setName(pkg.getName().getFullyQualifiedName());
			for (Object a : pkg.annotations()) {
				((Annotation)a).accept(this);
				b.addModifiers(modifiers.pop());
			}
			Integer index = (Integer) pkg.getProperty(Java7Visitor.PROPERTY_INDEX);
			if (index != null)
				b.setKey(index);
		}
		for (Object i : node.imports()) {
			ImportDeclaration id = (ImportDeclaration)i;
			String imp = "";
			if (id.isStatic())
				imp += "static ";
			imp += id.getName().getFullyQualifiedName();
			if (id.isOnDemand())
				imp += ".*";
			b.addImports(imp);
		}
		for (Object t : node.types()) {
			declarations.push(new ArrayList<boa.types.Ast.Declaration>());
			((AbstractTypeDeclaration)t).accept(this);
			for (boa.types.Ast.Declaration d : declarations.pop())
				b.addDeclarations(d);
		}
		for (Object c : node.getCommentList())
			((org.eclipse.jdt.core.dom.Comment)c).accept(this);
		return false;
	}

	@Override
	public boolean visit(BlockComment node) {
		boa.types.Ast.Comment.Builder b = boa.types.Ast.Comment.newBuilder();
		Integer index = (Integer) node.getProperty(Java7Visitor.PROPERTY_INDEX);
		if (index != null)
			b.setKey(index);
		buildPosition(node);
		b.setPosition(pos.build());
		b.setKind(boa.types.Ast.Comment.CommentKind.BLOCK);
		b.setValue(src.substring(node.getStartPosition(), node.getStartPosition() + node.getLength()));
		comments.add(b.build());
		return false;
	}

	@Override
	public boolean visit(LineComment node) {
		boa.types.Ast.Comment.Builder b = boa.types.Ast.Comment.newBuilder();
		Integer index = (Integer) node.getProperty(Java7Visitor.PROPERTY_INDEX);
		if (index != null)
			b.setKey(index);
		buildPosition(node);
		b.setPosition(pos.build());
		b.setKind(boa.types.Ast.Comment.CommentKind.LINE);
		b.setValue(src.substring(node.getStartPosition(), node.getStartPosition() + node.getLength()));
		comments.add(b.build());
		return false;
	}

	@Override
	public boolean visit(Javadoc node) {
		boa.types.Ast.Comment.Builder b = boa.types.Ast.Comment.newBuilder();
		Integer index = (Integer) node.getProperty(Java7Visitor.PROPERTY_INDEX);
		if (index != null)
			b.setKey(index);
		buildPosition(node);
		b.setPosition(pos.build());
		b.setKind(boa.types.Ast.Comment.CommentKind.DOC);
		b.setValue(src.substring(node.getStartPosition(), node.getStartPosition() + node.getLength()));
		comments.add(b.build());
		return false;
	}

	//////////////////////////////////////////////////////////////
	// Type Declarations

	@Override
	public boolean visit(TypeDeclaration node) {
		boa.types.Ast.Declaration.Builder b = boa.types.Ast.Declaration.newBuilder();
		b.setName(node.getName().getIdentifier());
		b.setFullyQualifiedName(getFullyQualifiedName(node));
		setDeclaringClass(b, node.resolveBinding());
		Integer key = (Integer) node.getProperty(PROPERTY_INDEX);
		if (key != null)
			b.setKey((int) key);
		if (node.isInterface())
			b.setKind(boa.types.Ast.TypeKind.INTERFACE);
		else
			b.setKind(boa.types.Ast.TypeKind.CLASS);
		for (Object m : node.modifiers()) {
			if (((IExtendedModifier)m).isAnnotation())
				((Annotation)m).accept(this);
			else
				((org.eclipse.jdt.core.dom.Modifier)m).accept(this);
			b.addModifiers(modifiers.pop());
		}
		for (Object t : node.typeParameters()) {
			boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
			String name = ((TypeParameter)t).getName().getFullyQualifiedName();
			String bounds = "";
			for (Object o : ((TypeParameter)t).typeBounds()) {
				if (bounds.length() > 0)
					bounds += " & ";
				bounds += typeName((org.eclipse.jdt.core.dom.Type)o);
			}
			if (bounds.length() > 0)
				name = name + " extends " + bounds;
			tb.setName(name);
			tb.setKind(boa.types.Ast.TypeKind.GENERIC);
			setTypeBinding(tb, ((TypeParameter) t).getName());
			Integer index = (Integer) ((ASTNode) t).getProperty(Java7Visitor.PROPERTY_INDEX);
			if (index != null)
				tb.setKey(index);
			b.addGenericParameters(tb.build());
		}
		if (node.getSuperclassType() != null) {
			boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
			tb.setName(typeName(node.getSuperclassType()));
			tb.setKind(boa.types.Ast.TypeKind.CLASS);
			setTypeBinding(tb, node.getSuperclassType());
			Integer index = (Integer) node.getSuperclassType().getProperty(Java7Visitor.PROPERTY_INDEX);
			if (index != null)
				tb.setKey(index);
			b.addParents(tb.build());
		}
		for (Object t : node.superInterfaceTypes()) {
			boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
			tb.setName(typeName((org.eclipse.jdt.core.dom.Type) t));
			tb.setKind(boa.types.Ast.TypeKind.INTERFACE);
			setTypeBinding(tb, (org.eclipse.jdt.core.dom.Type) t);
			Integer index = (Integer) ((ASTNode) t).getProperty(Java7Visitor.PROPERTY_INDEX);
			if (index != null)
				tb.setKey(index);
			b.addParents(tb.build());
		}
		for (Object d : node.bodyDeclarations()) {
			if (d instanceof FieldDeclaration) {
				fields.push(new ArrayList<boa.types.Ast.Variable>());
				((FieldDeclaration)d).accept(this);
				for (boa.types.Ast.Variable v : fields.pop())
					b.addFields(v);
			} else if (d instanceof MethodDeclaration) {
				methods.push(new ArrayList<boa.types.Ast.Method>());
				((MethodDeclaration)d).accept(this);
				for (boa.types.Ast.Method m : methods.pop())
					b.addMethods(m);
			} else if (d instanceof Initializer) {
				methods.push(new ArrayList<boa.types.Ast.Method>());
				((Initializer)d).accept(this);
				for (boa.types.Ast.Method m : methods.pop())
					b.addMethods(m);
			} else {
				declarations.push(new ArrayList<boa.types.Ast.Declaration>());
				((BodyDeclaration)d).accept(this);
				for (boa.types.Ast.Declaration nd : declarations.pop())
					b.addNestedDeclarations(nd);
			}
		}
		declarations.peek().add(b.build());
		return false;
	}

	@Override
	public boolean visit(AnonymousClassDeclaration node) {
		boa.types.Ast.Declaration.Builder b = boa.types.Ast.Declaration.newBuilder();
		b.setName("");
		b.setKind(boa.types.Ast.TypeKind.ANONYMOUS);
//		b.setFullyQualifiedName(getFullyQualifiedName(node));
		Integer index = (Integer) node.getProperty(Java7Visitor.PROPERTY_INDEX);
		if (index != null)
			b.setKey(index);
		for (Object d : node.bodyDeclarations()) {
			if (d instanceof FieldDeclaration) {
				fields.push(new ArrayList<boa.types.Ast.Variable>());
				((FieldDeclaration)d).accept(this);
				for (boa.types.Ast.Variable v : fields.pop())
					b.addFields(v);
			} else if (d instanceof MethodDeclaration) {
				methods.push(new ArrayList<boa.types.Ast.Method>());
				((MethodDeclaration)d).accept(this);
				for (boa.types.Ast.Method m : methods.pop())
					b.addMethods(m);
			} else if (d instanceof Initializer) {
				methods.push(new ArrayList<boa.types.Ast.Method>());
				((Initializer)d).accept(this);
				for (boa.types.Ast.Method m : methods.pop())
					b.addMethods(m);
			} else {
				declarations.push(new ArrayList<boa.types.Ast.Declaration>());
				((BodyDeclaration)d).accept(this);
				for (boa.types.Ast.Declaration nd : declarations.pop())
					b.addNestedDeclarations(nd);
			}
		}
		declarations.peek().add(b.build());
		return false;
	}

	@Override
	public boolean visit(EnumDeclaration node) {
		boa.types.Ast.Declaration.Builder b = boa.types.Ast.Declaration.newBuilder();
		b.setName(node.getName().getIdentifier());
		b.setKind(boa.types.Ast.TypeKind.ENUM);
		b.setFullyQualifiedName(getFullyQualifiedName(node));
		setDeclaringClass(b, node.resolveBinding());
		Integer key = (Integer) node.getProperty(PROPERTY_INDEX);
		if (key != null)
			b.setKey((int) key);
		for (Object c : node.enumConstants()) {
			// TODO EnumConstantDeclaration
		}
		for (Object t : node.superInterfaceTypes()) {
			boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
			tb.setName(typeName((org.eclipse.jdt.core.dom.Type) t));
			tb.setKind(boa.types.Ast.TypeKind.INTERFACE);
			setTypeBinding(tb, (org.eclipse.jdt.core.dom.Type) t);
			Integer index = (Integer) ((ASTNode) t).getProperty(Java7Visitor.PROPERTY_INDEX);
			if (index != null)
				tb.setKey(index);
			b.addParents(tb.build());
		}
		for (Object d : node.bodyDeclarations()) {
			if (d instanceof FieldDeclaration) {
				fields.push(new ArrayList<boa.types.Ast.Variable>());
				((FieldDeclaration)d).accept(this);
				for (boa.types.Ast.Variable v : fields.pop())
					b.addFields(v);
			} else if (d instanceof MethodDeclaration) {
				methods.push(new ArrayList<boa.types.Ast.Method>());
				((MethodDeclaration)d).accept(this);
				for (boa.types.Ast.Method m : methods.pop())
					b.addMethods(m);
			} else if (d instanceof Initializer) {
				methods.push(new ArrayList<boa.types.Ast.Method>());
				((Initializer)d).accept(this);
				for (boa.types.Ast.Method m : methods.pop())
					b.addMethods(m);
			} else {
				declarations.push(new ArrayList<boa.types.Ast.Declaration>());
				((BodyDeclaration)d).accept(this);
				for (boa.types.Ast.Declaration nd : declarations.pop())
					b.addNestedDeclarations(nd);
			}
		}
		declarations.peek().add(b.build());
		return false;
	}

	@Override
	public boolean visit(AnnotationTypeDeclaration node) {
		boa.types.Ast.Declaration.Builder b = boa.types.Ast.Declaration.newBuilder();
		b.setName(node.getName().getFullyQualifiedName());
		b.setKind(boa.types.Ast.TypeKind.ANNOTATION);
		b.setFullyQualifiedName(getFullyQualifiedName(node));
		setDeclaringClass(b, node.resolveBinding());
		Integer key = (Integer) node.getProperty(PROPERTY_INDEX);
		if (key != null)
			b.setKey((int) key);
		for (Object m : node.modifiers()) {
			if (((IExtendedModifier)m).isAnnotation())
				((Annotation)m).accept(this);
			else
				((org.eclipse.jdt.core.dom.Modifier)m).accept(this);
			b.addModifiers(modifiers.pop());
		}
		for (Object d : node.bodyDeclarations()) {
			if (d instanceof FieldDeclaration) {
				fields.push(new ArrayList<boa.types.Ast.Variable>());
				((FieldDeclaration)d).accept(this);
				for (boa.types.Ast.Variable v : fields.pop())
					b.addFields(v);
			} else if (d instanceof Initializer) {
				methods.push(new ArrayList<boa.types.Ast.Method>());
				((Initializer)d).accept(this);
				for (boa.types.Ast.Method m : methods.pop())
					b.addMethods(m);
			} else if (d instanceof AnnotationTypeMemberDeclaration) {
				methods.push(new ArrayList<boa.types.Ast.Method>());
				((AnnotationTypeMemberDeclaration)d).accept(this);
				for (boa.types.Ast.Method m : methods.pop())
					b.addMethods(m);
			} else {
				declarations.push(new ArrayList<boa.types.Ast.Declaration>());
				((BodyDeclaration)d).accept(this);
				for (boa.types.Ast.Declaration nd : declarations.pop())
					b.addNestedDeclarations(nd);
			}
		}
		declarations.peek().add(b.build());
		return false;
	}

	//////////////////////////////////////////////////////////////
	// Utilities

	protected void setDeclaringClass(Declaration.Builder db, ITypeBinding binding) {
		if (declarationNode == null || binding == null)
			return;
		ITypeBinding tb = binding.getDeclaringClass();
		if (tb != null) {
			if (tb.getTypeDeclaration() != null)
				tb = tb.getTypeDeclaration();
			String key = tb.getKey();
			Integer index = declarationNode.get(key);
			if (index != null)
				db.setDeclaringType(index);
		}
	}

	protected void setDeclaringClass(Method.Builder b, IMethodBinding binding) {
		if (declarationNode == null || binding == null)
			return;
		ITypeBinding tb = binding.getDeclaringClass();
		if (tb == null)
			return;
		if (tb.getTypeDeclaration() != null)
			tb = tb.getTypeDeclaration();
		String key = <KEY>();
		Integer index = declarationNode.get(key);
		if (index != null)
			b.setDeclaringType(index);
	}

	protected void setDeclaringClass(Variable.Builder b, IVariableBinding binding) {
		if (declarationNode == null || binding == null)
			return;
		ITypeBinding tb = binding.getDeclaringClass();
		if (tb == null)
			return;
		if (tb.getTypeDeclaration() != null)
			tb = tb.getTypeDeclaration();
		String key = tb.getKey();
		Integer index = declarationNode.get(key);
		if (index != null)
			b.setDeclaringType(index);
	}

	protected void setTypeBinding(boa.types.Ast.Type.Builder b, org.eclipse.jdt.core.dom.Type type) {
		ITypeBinding tb = type.resolveBinding();
		if (tb != null) {
			if (tb.getTypeDeclaration() != null)
				tb = tb.getTypeDeclaration();
			if (tb.isInterface())
				b.setKind(boa.types.Ast.TypeKind.INTERFACE);
			else if (tb.isEnum())
				b.setKind(boa.types.Ast.TypeKind.ENUM);
			else if (tb.isAnnotation())
				b.setKind(boa.types.Ast.TypeKind.ANNOTATION);
			else if (tb.isAnonymous())
				b.setKind(boa.types.Ast.TypeKind.ANONYMOUS);
			else 
				b.setKind(boa.types.Ast.TypeKind.OTHER);
			if (!tb.isPrimitive()) {
				b.setFullyQualifiedName(tb.getQualifiedName());
				if (declarationFile != null && !tb.isArray()) {
					String key = tb.getKey();
					Integer index = declarationFile.get(key);
					if (index != null) {
						b.setDeclarationFile(index);
						b.setDeclaration(declarationNode.get(key));
					}
				}
			}
		}
	}

	protected void setTypeBinding(boa.types.Ast.Type.Builder b, org.eclipse.jdt.core.dom.Expression e) {
		ITypeBinding tb = e.resolveTypeBinding();
		if (tb != null) {
			if (tb.getTypeDeclaration() != null)
				tb = tb.getTypeDeclaration();
			if (tb.isInterface())
				b.setKind(boa.types.Ast.TypeKind.INTERFACE);
			else if (tb.isEnum())
				b.setKind(boa.types.Ast.TypeKind.ENUM);
			else if (tb.isAnnotation())
				b.setKind(boa.types.Ast.TypeKind.ANNOTATION);
			else if (tb.isAnonymous())
				b.setKind(boa.types.Ast.TypeKind.ANONYMOUS);
			else 
				b.setKind(boa.types.Ast.TypeKind.OTHER);
			if (!tb.isPrimitive()) {
				b.setFullyQualifiedName(tb.getQualifiedName());
				if (declarationFile != null && !tb.isArray()) {
					String key = tb.getKey();
					Integer index = declarationFile.get(key);
					if (index != null) {
						b.setDeclarationFile(index);
						b.setDeclaration(declarationNode.get(key));
					}
				}
			}
		}
	}

	protected Type buildType(ITypeBinding itb) {
		if (itb.getTypeDeclaration() != null)
			itb = itb.getTypeDeclaration();
		boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
		tb.setName(itb.getName());
		if (itb.isInterface())
			tb.setKind(boa.types.Ast.TypeKind.INTERFACE);
		else if (itb.isEnum())
			tb.setKind(boa.types.Ast.TypeKind.ENUM);
		else if (itb.isAnnotation())
			tb.setKind(boa.types.Ast.TypeKind.ANNOTATION);
		else if (itb.isAnonymous())
			tb.setKind(boa.types.Ast.TypeKind.ANONYMOUS);
		else 
			tb.setKind(boa.types.Ast.TypeKind.OTHER);
		if (!itb.isPrimitive()) {
			tb.setFullyQualifiedName(itb.getQualifiedName());
			if (declarationFile != null && !itb.isArray()) {
				String key = itb.getKey();
				Integer index = declarationFile.get(key);
				if (index != null) {
					tb.setDeclarationFile(index);
					tb.setDeclaration(declarationNode.get(key));
				}
			}
		}
		return tb.build();
	}

	//////////////////////////////////////////////////////////////
	// Field/Method Declarations

	@Override
	public boolean visit(MethodDeclaration node) {
		List<boa.types.Ast.Method> list = methods.peek();
		Method.Builder b = Method.newBuilder();
		Integer index = (Integer) node.getProperty(Java7Visitor.PROPERTY_INDEX);
		if (index != null)
			b.setKey(index);
		if (node.isConstructor())
			b.setName("<init>");
		else
			b.setName(node.getName().getFullyQualifiedName());
		for (Object m : node.modifiers()) {
			if (((IExtendedModifier)m).isAnnotation())
				((Annotation)m).accept(this);
			else
				((org.eclipse.jdt.core.dom.Modifier)m).accept(this);
			b.addModifiers(modifiers.pop());
		}
		boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
		if (node.getReturnType2() != null) {
			String name = typeName(node.getReturnType2());
			for (int i = 0; i < node.getExtraDimensions(); i++)
				name += "[]";
			tb.setName(name);
			tb.setKind(boa.types.Ast.TypeKind.OTHER);
			setTypeBinding(tb, node.getReturnType2());
			index = (Integer) node.getReturnType2().getProperty(Java7Visitor.PROPERTY_INDEX);
			if (index != null)
				tb.setKey(index);
			b.setReturnType(tb.build());
		} else {
			tb.setName("void");
			tb.setKind(boa.types.Ast.TypeKind.OTHER);
			b.setReturnType(tb.build());
		}
		for (Object t : node.typeParameters()) {
			boa.types.Ast.Type.Builder tp = boa.types.Ast.Type.newBuilder();
			String name = ((TypeParameter)t).getName().getFullyQualifiedName();
			String bounds = "";
			for (Object o : ((TypeParameter)t).typeBounds()) {
				if (bounds.length() > 0)
					bounds += " & ";
				bounds += typeName((org.eclipse.jdt.core.dom.Type)o);
			}
			if (bounds.length() > 0)
				name = name + " extends " + bounds;
			tp.setName(name);
			tp.setKind(boa.types.Ast.TypeKind.GENERIC);
			setTypeBinding(tp, ((TypeParameter)t).getName());
			index = (Integer) ((ASTNode) t).getProperty(Java7Visitor.PROPERTY_INDEX);
			if (index != null)
				tp.setKey(index);
			b.addGenericParameters(tp.build());
		}
		for (Object o : node.parameters()) {
			SingleVariableDeclaration ex = (SingleVariableDeclaration)o;
			Variable.Builder vb = Variable.newBuilder();
			index = (Integer) ex.getProperty(Java7Visitor.PROPERTY_INDEX);
			if (index != null)
				vb.setKey(index);
			vb.setName(ex.getName().getFullyQualifiedName());
			for (Object m : ex.modifiers()) {
				if (((IExtendedModifier)m).isAnnotation())
					((Annotation)m).accept(this);
				else
					((org.eclipse.jdt.core.dom.Modifier)m).accept(this);
				vb.addModifiers(modifiers.pop());
			}
			boa.types.Ast.Type.Builder tp = boa.types.Ast.Type.newBuilder();
			String name = typeName(ex.getType());
			for (int i = 0; i < ex.getExtraDimensions(); i++)
				name += "[]";
			if (ex.isVarargs())
				name += "...";
			tp.setName(name);
			tp.setKind(boa.types.Ast.TypeKind.OTHER);
			setTypeBinding(tp, ex.getType());
			index = (Integer) ex.getType().getProperty(Java7Visitor.PROPERTY_INDEX);
			if (index != null)
				tp.setKey(index);
			vb.setVariableType(tp.build());
			if (ex.getInitializer() != null) {
				ex.getInitializer().accept(this);
				vb.setInitializer(expressions.pop());
			}
			b.addArguments(vb.build());
		}
		for (Object o : node.thrownExceptionTypes()) {
			boa.types.Ast.Type.Builder tp = boa.types.Ast.Type.newBuilder();
			tp.setName(typeName((org.eclipse.jdt.core.dom.Type) o));
			tp.setKind(boa.types.Ast.TypeKind.CLASS);
			setTypeBinding(tp, (org.eclipse.jdt.core.dom.Type) o);
			index = (Integer) ((ASTNode) o).getProperty(Java7Visitor.PROPERTY_INDEX);
			if (index != null)
				tp.setKey(index);
			b.addExceptionTypes(tp.build());
		}
		if (node.getBody() != null) {
			statements.push(new ArrayList<boa.types.Ast.Statement>());
			node.getBody().accept(this);
			for (boa.types.Ast.Statement s : statements.pop())
				b.addStatements(s);
		}
		setDeclaringClass(b, node.resolveBinding());
		list.add(b.build());
		return false;
	}

	@Override
	public boolean visit(AnnotationTypeMemberDeclaration node) {
		List<boa.types.Ast.Method> list = methods.peek();
		Method.Builder b = Method.newBuilder();
		Integer index = (Integer) node.getProperty(Java7Visitor.PROPERTY_INDEX);
		if (index != null)
			b.setKey(index);
		b.setName(node.getName().getFullyQualifiedName());
		for (Object m : node.modifiers()) {
			if (((IExtendedModifier)m).isAnnotation())
				((Annotation)m).accept(this);
			else
				((org.eclipse.jdt.core.dom.Modifier)m).accept(this);
			b.addModifiers(modifiers.pop());
		}
		boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
		tb.setName(typeName(node.getType()));
		tb.setKind(boa.types.Ast.TypeKind.OTHER);
		setTypeBinding(tb, node.getType());
		index = (Integer) node.getType().getProperty(Java7Visitor.PROPERTY_INDEX);
		if (index != null)
			tb.setKey(index);
		b.setReturnType(tb.build());
		if (node.getDefault() != null) {
			boa.types.Ast.Statement.Builder sb = boa.types.Ast.Statement.newBuilder();
			index = (Integer) node.getDefault().getProperty(Java7Visitor.PROPERTY_INDEX);
			if (index != null)
				sb.setKey(index);
			sb.setKind(boa.types.Ast.Statement.StatementKind.EXPRESSION);
			node.getDefault().accept(this);
			sb.setExpression(expressions.pop());
			b.addStatements(sb.build());
		}
		setDeclaringClass(b, node.resolveBinding());
		list.add(b.build());
		return false;
	}

	@Override
	public boolean visit(FieldDeclaration node) {
		List<boa.types.Ast.Variable> list = fields.peek();
		for (Object o : node.fragments()) {
			VariableDeclarationFragment f = (VariableDeclarationFragment)o;
			Variable.Builder b = Variable.newBuilder();
			Integer index = (Integer) f.getProperty(Java7Visitor.PROPERTY_INDEX);
			if (index != null)
				b.setKey(index);
			b.setName(f.getName().getFullyQualifiedName());
			for (Object m : node.modifiers()) {
				if (((IExtendedModifier)m).isAnnotation())
					((Annotation)m).accept(this);
				else
					((org.eclipse.jdt.core.dom.Modifier)m).accept(this);
				b.addModifiers(modifiers.pop());
			}
			boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
			String name = typeName(node.getType());
			for (int i = 0; i < f.getExtraDimensions(); i++)
				name += "[]";
			tb.setName(name);
			tb.setKind(boa.types.Ast.TypeKind.OTHER);
			setTypeBinding(tb, node.getType());
			index = (Integer) node.getType().getProperty(Java7Visitor.PROPERTY_INDEX);
			if (index != null)
				tb.setKey(index);
			b.setVariableType(tb.build());
			if (f.getInitializer() != null) {
				f.getInitializer().accept(this);
				b.setInitializer(expressions.pop());
			}
			setDeclaringClass(b, f.resolveBinding());
			list.add(b.build());
		}
		return false;
	}

	//////////////////////////////////////////////////////////////
	// Modifiers and Annotations

	protected boa.types.Ast.Modifier.Builder getAnnotationBuilder(Annotation node) {
		boa.types.Ast.Modifier.Builder b = boa.types.Ast.Modifier.newBuilder();
		Integer index = (Integer) node.getProperty(Java7Visitor.PROPERTY_INDEX);
		if (index != null)
			b.setKey(index);
		b.setKind(boa.types.Ast.Modifier.ModifierKind.ANNOTATION);
		b.setAnnotationName(node.getTypeName().getFullyQualifiedName());
		return b;
	}

	@Override
	public boolean visit(MarkerAnnotation node) {
		modifiers.push(getAnnotationBuilder(node).build());
		return false;
	}

	@Override
	public boolean visit(SingleMemberAnnotation node) {
		boa.types.Ast.Modifier.Builder b = getAnnotationBuilder(node);
		node.getValue().accept(this);
		if (expressions.empty()) {
			boa.types.Ast.Expression.Builder eb = boa.types.Ast.Expression.newBuilder();
			eb.setKind(boa.types.Ast.Expression.ExpressionKind.ANNOTATION);
			eb.setAnnotation(modifiers.pop());
			b.addAnnotationMembers("value");
			b.addAnnotationValues(eb.build());
		} else {
			b.addAnnotationMembers("value");
			b.addAnnotationValues(expressions.pop());
		}
		modifiers.push(b.build());
		return false;
	}

	@Override
	public boolean visit(NormalAnnotation node) {
		boa.types.Ast.Modifier.Builder b = getAnnotationBuilder(node);
		for (Object v : node.values()) {
			MemberValuePair pair = (MemberValuePair)v;
			pair.getValue().accept(this);
			if (expressions.empty()) {
				boa.types.Ast.Expression.Builder eb = boa.types.Ast.Expression.newBuilder();
				eb.setKind(boa.types.Ast.Expression.ExpressionKind.ANNOTATION);
				eb.setAnnotation(modifiers.pop());
				b.addAnnotationMembers(pair.getName().getFullyQualifiedName());
				b.addAnnotationValues(eb.build());
			} else {
				b.addAnnotationMembers(pair.getName().getFullyQualifiedName());
				b.addAnnotationValues(expressions.pop());
			}
		}
		modifiers.push(b.build());
		return false;
	}

	@Override
	public boolean visit(org.eclipse.jdt.core.dom.Modifier node) {
		boa.types.Ast.Modifier.Builder b = boa.types.Ast.Modifier.newBuilder();
		Integer index = (Integer) node.getProperty(Java7Visitor.PROPERTY_INDEX);
		if (index != null)
			b.setKey(index);
		if (node.isFinal())
			b.setKind(boa.types.Ast.Modifier.ModifierKind.FINAL);
		else if (node.isAbstract())
			b.setKind(boa.types.Ast.Modifier.ModifierKind.ABSTRACT);
		else if (node.isStatic())
			b.setKind(boa.types.Ast.Modifier.ModifierKind.STATIC);
		else if (node.isSynchronized())
			b.setKind(boa.types.Ast.Modifier.ModifierKind.SYNCHRONIZED);
		else if (node.isPublic()) {
			b.setKind(boa.types.Ast.Modifier.ModifierKind.VISIBILITY);
			b.setVisibility(boa.types.Ast.Modifier.Visibility.PUBLIC);
		} else if (node.isPrivate()) {
			b.setKind(boa.types.Ast.Modifier.ModifierKind.VISIBILITY);
			b.setVisibility(boa.types.Ast.Modifier.Visibility.PRIVATE);
		} else if (node.isProtected()) {
			b.setKind(boa.types.Ast.Modifier.ModifierKind.VISIBILITY);
			b.setVisibility(boa.types.Ast.Modifier.Visibility.PROTECTED);
		} else {
			b.setKind(boa.types.Ast.Modifier.ModifierKind.OTHER);
			b.setOther(node.getKeyword().toString());
		}
		modifiers.push(b.build());
		return false;
	}

	//////////////////////////////////////////////////////////////
	// Statements

	@Override
	public boolean visit(AssertStatement node) {
		boa.types.Ast.Statement.Builder b = boa.types.Ast.Statement.newBuilder();
		Integer index = (Integer) node.getProperty(Java7Visitor.PROPERTY_INDEX);
		if (index != null)
			b.setKey(index);
		List<boa.types.Ast.Statement> list = statements.peek();
		b.setKind(boa.types.Ast.Statement.StatementKind.ASSERT);
		node.getExpression().accept(this);
		b.setCondition(expressions.pop());
		if (node.getMessage() != null) {
			node.getMessage().accept(this);
			b.setExpression(expressions.pop());
		}
		list.add(b.build());
		return false;
	}

	@Override
	public boolean visit(Block node) {
		boa.types.Ast.Statement.Builder b = boa.types.Ast.Statement.newBuilder();
		Integer index = (Integer) node.getProperty(Java7Visitor.PROPERTY_INDEX);
		if (index != null)
			b.setKey(index);
		List<boa.types.Ast.Statement> list = statements.peek();
		b.setKind(boa.types.Ast.Statement.StatementKind.BLOCK);
		for (Object s : node.statements()) {
			statements.push(new ArrayList<boa.types.Ast.Statement>());
			((org.eclipse.jdt.core.dom.Statement)s).accept(this);
			for (boa.types.Ast.Statement st : statements.pop())
				b.addStatements(st);
		}
		list.add(b.build());
		return false;
	}

	@Override
	public boolean visit(BreakStatement node) {
		boa.types.Ast.Statement.Builder b = boa.types.Ast.Statement.newBuilder();
		Integer index = (Integer) node.getProperty(Java7Visitor.PROPERTY_INDEX);
		if (index != null)
			b.setKey(index);
		List<boa.types.Ast.Statement> list = statements.peek();
		b.setKind(boa.types.Ast.Statement.StatementKind.BREAK);
		if (node.getLabel() != null) {
			boa.types.Ast.Expression.Builder eb = boa.types.Ast.Expression.newBuilder();
			index = (Integer) node.getLabel().getProperty(Java7Visitor.PROPERTY_INDEX);
			if (index != null)
				eb.setKey(index);
			eb.setLiteral(node.getLabel().getFullyQualifiedName());
			eb.setKind(boa.types.Ast.Expression.ExpressionKind.LITERAL);
			b.setExpression(eb.build());
		}
		list.add(b.build());
		return false;
	}

	@Override
	public boolean visit(CatchClause node) {
		boa.types.Ast.Statement.Builder b = boa.types.Ast.Statement.newBuilder();
		Integer index = (Integer) node.getProperty(Java7Visitor.PROPERTY_INDEX);
		if (index != null)
			b.setKey(index);
		List<boa.types.Ast.Statement> list = statements.peek();
		b.setKind(boa.types.Ast.Statement.StatementKind.CATCH);
		SingleVariableDeclaration ex = node.getException();
		Variable.Builder vb = Variable.newBuilder();
		index = (Integer) ex.getProperty(Java7Visitor.PROPERTY_INDEX);
		if (index != null)
			vb.setKey(index);
		vb.setName(ex.getName().getFullyQualifiedName());
		for (Object m : ex.modifiers()) {
			if (((IExtendedModifier)m).isAnnotation())
				((Annotation)m).accept(this);
			else
				((org.eclipse.jdt.core.dom.Modifier)m).accept(this);
			vb.addModifiers(modifiers.pop());
		}
		boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
		String name = typeName(ex.getType());
		for (int i = 0; i < ex.getExtraDimensions(); i++)
			name += "[]";
		tb.setName(name);
		tb.setKind(boa.types.Ast.TypeKind.OTHER);
		setTypeBinding(tb, ex.getType());
		index = (Integer) ex.getType().getProperty(Java7Visitor.PROPERTY_INDEX);
		if (index != null)
			tb.setKey(index);
		vb.setVariableType(tb.build());
		if (ex.getInitializer() != null) {
			ex.getInitializer().accept(this);
			vb.setInitializer(expressions.pop());
		}
		b.setVariableDeclaration(vb.build());
		statements.push(new ArrayList<boa.types.Ast.Statement>());
		for (Object s : node.getBody().statements())
			((org.eclipse.jdt.core.dom.Statement)s).accept(this);
		for (boa.types.Ast.Statement s : statements.pop())
			b.addStatements(s);
		list.add(b.build());
		return false;
	}

	@Override
	public boolean visit(ConstructorInvocation node) {
		boa.types.Ast.Statement.Builder b = boa.types.Ast.Statement.newBuilder();
		Integer index = (Integer) node.getProperty(Java7Visitor.PROPERTY_INDEX);
		if (index != null)
			b.setKey(index);
		List<boa.types.Ast.Statement> list = statements.peek();
		b.setKind(boa.types.Ast.Statement.StatementKind.EXPRESSION);
		boa.types.Ast.Expression.Builder eb = boa.types.Ast.Expression.newBuilder();
		index = (Integer) node.getProperty(Java7Visitor.PROPERTY_INDEX);
		if (index != null)
			eb.setKey(index);
		if (node.resolveConstructorBinding() != null) {
			IMethodBinding mb = node.resolveConstructorBinding();
			eb.setReturnType(buildType(mb.getReturnType()));
			if (mb.getDeclaringClass() != null)
				eb.setDeclaringType(buildType(mb.getDeclaringClass()));
		}
		eb.setKind(boa.types.Ast.Expression.ExpressionKind.METHODCALL);
		eb.setMethod("<init>");
		for (Object a : node.arguments()) {
			((org.eclipse.jdt.core.dom.Expression)a).accept(this);
			eb.addMethodArgs(expressions.pop());
		}
		for (Object t : node.typeArguments()) {
			boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
			tb.setName(typeName((org.eclipse.jdt.core.dom.Type) t));
			tb.setKind(boa.types.Ast.TypeKind.GENERIC);
			setTypeBinding(tb, (org.eclipse.jdt.core.dom.Type) t);
			index = (Integer) ((ASTNode) t).getProperty(Java7Visitor.PROPERTY_INDEX);
			if (index != null)
				tb.setKey(index);
			eb.addGenericParameters(tb.build());
		}
		b.setExpression(eb.build());
		list.add(b.build());
		return false;
	}

	@Override
	public boolean visit(ContinueStatement node) {
		boa.types.Ast.Statement.Builder b = boa.types.Ast.Statement.newBuilder();
		Integer index = (Integer) node.getProperty(Java7Visitor.PROPERTY_INDEX);
		if (index != null)
			b.setKey(index);
		List<boa.types.Ast.Statement> list = statements.peek();
		b.setKind(boa.types.Ast.Statement.StatementKind.CONTINUE);
		if (node.getLabel() != null) {
			boa.types.Ast.Expression.Builder eb = boa.types.Ast.Expression.newBuilder();
			index = (Integer) node.getLabel().getProperty(Java7Visitor.PROPERTY_INDEX);
			if (index != null)
				eb.setKey(index);
			eb.setLiteral(node.getLabel().getFullyQualifiedName());
			eb.setKind(boa.types.Ast.Expression.ExpressionKind.LITERAL);
			b.setExpression(eb.build());
		}
		list.add(b.build());
		return false;
	}

	@Override
	public boolean visit(DoStatement node) {
		boa.types.Ast.Statement.Builder b = boa.types.Ast.Statement.newBuilder();
		Integer index = (Integer) node.getProperty(Java7Visitor.PROPERTY_INDEX);
{'fraction_non_alphanumeric': 0.16749585406301823, 'fraction_numerical': 0.0016874690873119782, 'mean_word_length': 3.9849891225525744, 'pattern_counts': {'":': 0, '<': 134, '<?xml version=': 0, '>': 134, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3822463', 'n_tokens_mistral': 26377, 'n_tokens_neox': 23233, 'n_words': 3807}