package com.redhat.ceylon.eclipse.code.outline;
import static com.redhat.ceylon.eclipse.code.outline.CeylonOutlineNode.DEFAULT_CATEGORY;
import static com.redhat.ceylon.eclipse.code.outline.CeylonOutlineNode.IMPORT_LIST_CATEGORY;
import static com.redhat.ceylon.eclipse.code.outline.CeylonOutlineNode.PACKAGE_CATEGORY;
import static com.redhat.ceylon.eclipse.code.outline.CeylonOutlineNode.ROOT_CATEGORY;
import static com.redhat.ceylon.eclipse.code.outline.CeylonOutlineNode.UNIT_CATEGORY;
import java.util.Stack;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IPath;
import com.redhat.ceylon.compiler.typechecker.context.PhasedUnit;
import com.redhat.ceylon.compiler.typechecker.tree.Node;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.compiler.typechecker.tree.Visitor;
import com.redhat.ceylon.eclipse.code.parse.CeylonParseController;
import com.redhat.ceylon.ide.common.model.CeylonUnit;
import com.redhat.ceylon.model.typechecker.model.Module;
import com.redhat.ceylon.model.typechecker.model.TypedDeclaration;
import com.redhat.ceylon.model.typechecker.model.Unit;
public class CeylonOutlineBuilder extends Visitor {
@Override
public void visit(Tree.Parameter that) {
//don't make a node
}
@Override
public void visit(Tree.Declaration that) {
if (!(that instanceof Tree.TypeParameterDeclaration) &&
!(that instanceof Tree.TypeConstraint) &&
!(that instanceof Tree.Variable/* &&
((Tree.Variable) that).getType() instanceof SyntheticVariable*/)) {
if (that instanceof Tree.AnyAttribute) {
Tree.AnyAttribute a =
(Tree.AnyAttribute) that;
TypedDeclaration att =
a.getDeclarationModel();
if (att==null ||
!att.isShared() &&
!att.isToplevel() &&
!att.isClassOrInterfaceMember()) {
return;
}
}
pushSubItem(that);
super.visitAny(that);
popSubItem();
}
}
@Override
public void visit(Tree.SpecifierStatement that) {
Tree.Term bme = that.getBaseMemberExpression();
if (that.getRefinement() &&
(bme instanceof Tree.BaseMemberExpression ||
bme instanceof Tree.ParameterizedExpression &&
((Tree.ParameterizedExpression) bme).getPrimary()
instanceof Tree.BaseMemberExpression)) {
pushSubItem(that);
super.visitAny(that);
popSubItem();
}
}
@Override
public void visit(Tree.PackageDescriptor that) {
pushSubItem(that);
super.visitAny(that);
popSubItem();
}
@Override
public void visit(Tree.ModuleDescriptor that) {
pushSubItem(that);
super.visitAny(that);
popSubItem();
}
@Override
public void visit(Tree.Import that) {
pushSubItem(that);
super.visitAny(that);
popSubItem();
}
@Override
public void visit(Tree.ImportModule that) {
pushSubItem(that);
super.visitAny(that);
popSubItem();
}
@Override
public void visit(Tree.ImportList that) {
Tree.ImportList il = (Tree.ImportList) that;
if (!il.getImports().isEmpty()) {
pushSubItem(that, IMPORT_LIST_CATEGORY);
super.visitAny(that);
popSubItem();
}
}
private Stack<CeylonOutlineNode> itemStack =
new Stack<CeylonOutlineNode>();
public final CeylonOutlineNode buildTree(CeylonParseController cpc) {
if (cpc==null) return null;
Tree.CompilationUnit rootNode = cpc.getLastCompilationUnit();
if (rootNode==null) return null;
Unit u = rootNode.getUnit();
if (u==null) return null;
if (rootNode.getStartIndex()==null) return null;
IProject project = cpc.getProject();
IPath path = cpc.getPath();
IFile file =
project==null || path==null ? null :
project.getFile(path);
if (u instanceof CeylonUnit) {
CeylonUnit unit = (CeylonUnit) u;
PhasedUnit phasedUnit = unit.getPhasedUnit();
if (phasedUnit == null ||
!phasedUnit.isFullyTyped()) {
return null;
}
}
CeylonOutlineNode modelRoot =
createTopItem(rootNode, file);
itemStack.push(modelRoot);
try {
Unit unit = rootNode.getUnit();
if (unit!=null &&
!unit.getFilename()
.equals("module.ceylon")) { //it looks a bit funny to have two nodes representing the module
ModuleNode moduleNode = new ModuleNode();
Module module = unit.getPackage().getModule();
String mname = module.getNameAsString();
moduleNode.setModuleName(mname);
moduleNode.setVersion(module.getVersion());
createSubItem(moduleNode, PACKAGE_CATEGORY,
file==null ? null : file.getParent());
}
if (unit!=null &&
!unit.getFilename()
.equals("module.ceylon") &&
!unit.getFilename()
.equals("package.ceylon")) { //it looks a bit funny to have two nodes representing the package
PackageNode packageNode = new PackageNode();
String pname =
unit.getPackage()
.getQualifiedNameString();
packageNode.setPackageName(pname);
createSubItem(packageNode, PACKAGE_CATEGORY,
file==null ? null : file.getParent());
}
createSubItem(rootNode, UNIT_CATEGORY, file);
rootNode.visit(this);
}
catch (Exception e) {
e.printStackTrace();
}
itemStack.pop();
return modelRoot;
}
private CeylonOutlineNode createTopItem(Node node, IFile file) {
return new CeylonOutlineNode(node, ROOT_CATEGORY, file);
}
/*private CeylonOutlineNode createSubItem(Node n) {
return createSubItem(n, DEFAULT_CATEGORY);
}*/
private CeylonOutlineNode createSubItem(Node n, int category) {
return createSubItem(n, category, null);
}
private CeylonOutlineNode createSubItem(Node n, int category,
IResource file) {
CeylonOutlineNode parent = itemStack.peek();
CeylonOutlineNode treeNode =
new CeylonOutlineNode(n, parent, category, file);
parent.addChild(treeNode);
return treeNode;
}
private CeylonOutlineNode pushSubItem(Node n) {
return pushSubItem(n, DEFAULT_CATEGORY);
}
private CeylonOutlineNode pushSubItem(Node n, int category) {
return itemStack.push(createSubItem(n, category));
}
private void popSubItem() {
itemStack.pop();
}
}