package com.redhat.ceylon.eclipse.code.outline;
import static com.redhat.ceylon.compiler.typechecker.tree.TreeUtil.formatPath;
import static com.redhat.ceylon.compiler.typechecker.tree.TreeUtil.hasAnnotation;
import static com.redhat.ceylon.eclipse.code.preferences.CeylonPreferenceInitializer.PARAMS_IN_OUTLINES;
import static com.redhat.ceylon.eclipse.code.preferences.CeylonPreferenceInitializer.PARAM_TYPES_IN_OUTLINES;
import static com.redhat.ceylon.eclipse.code.preferences.CeylonPreferenceInitializer.RETURN_TYPES_IN_OUTLINES;
import static com.redhat.ceylon.eclipse.code.preferences.CeylonPreferenceInitializer.TYPE_PARAMS_IN_OUTLINES;
import static com.redhat.ceylon.eclipse.core.builder.CeylonBuilder.getModule;
import static com.redhat.ceylon.eclipse.core.builder.CeylonBuilder.getPackage;
import static com.redhat.ceylon.eclipse.util.Highlights.ARROW_STYLER;
import static com.redhat.ceylon.eclipse.util.Highlights.ID_STYLER;
import static com.redhat.ceylon.eclipse.util.Highlights.KW_STYLER;
import static com.redhat.ceylon.eclipse.util.Highlights.PACKAGE_STYLER;
import static com.redhat.ceylon.eclipse.util.Highlights.STRING_STYLER;
import static com.redhat.ceylon.eclipse.util.Highlights.TYPE_ID_STYLER;
import static com.redhat.ceylon.eclipse.util.Highlights.TYPE_STYLER;
import static com.redhat.ceylon.model.typechecker.model.ModelUtil.isConstructor;
import static com.redhat.ceylon.model.typechecker.model.ModelUtil.isTypeUnknown;
import static org.eclipse.core.resources.IMarker.SEVERITY_ERROR;
import static org.eclipse.core.resources.IMarker.SEVERITY_WARNING;
import static org.eclipse.core.resources.IResource.DEPTH_INFINITE;
import static org.eclipse.core.resources.IResource.DEPTH_ONE;
import static org.eclipse.jface.viewers.IDecoration.BOTTOM_LEFT;
import static org.eclipse.jface.viewers.IDecoration.BOTTOM_RIGHT;
import static org.eclipse.jface.viewers.IDecoration.TOP_LEFT;
import static org.eclipse.jface.viewers.IDecoration.TOP_RIGHT;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jdt.core.IImportDeclaration;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider;
import org.eclipse.jface.viewers.IDecoration;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.StyledCellLabelProvider;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.StyledString.Styler;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import com.redhat.ceylon.compiler.typechecker.analyzer.UsageWarning;
import com.redhat.ceylon.compiler.typechecker.tree.Message;
import com.redhat.ceylon.compiler.typechecker.tree.Node;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.eclipse.ui.CeylonPlugin;
import com.redhat.ceylon.eclipse.ui.CeylonResources;
import com.redhat.ceylon.eclipse.util.ErrorCollectionVisitor;
import com.redhat.ceylon.eclipse.util.Highlights;
import com.redhat.ceylon.ide.common.model.BaseIdeModule;
import com.redhat.ceylon.ide.common.util.types_;
import com.redhat.ceylon.model.typechecker.model.Class;
import com.redhat.ceylon.model.typechecker.model.Declaration;
import com.redhat.ceylon.model.typechecker.model.Function;
import com.redhat.ceylon.model.typechecker.model.Interface;
import com.redhat.ceylon.model.typechecker.model.IntersectionType;
import com.redhat.ceylon.model.typechecker.model.ModelUtil;
import com.redhat.ceylon.model.typechecker.model.Module;
import com.redhat.ceylon.model.typechecker.model.NothingType;
import com.redhat.ceylon.model.typechecker.model.Package;
import com.redhat.ceylon.model.typechecker.model.Parameter;
import com.redhat.ceylon.model.typechecker.model.Type;
import com.redhat.ceylon.model.typechecker.model.TypeAlias;
import com.redhat.ceylon.model.typechecker.model.TypeDeclaration;
import com.redhat.ceylon.model.typechecker.model.TypeParameter;
import com.redhat.ceylon.model.typechecker.model.UnionType;
import com.redhat.ceylon.model.typechecker.model.Unit;
import com.redhat.ceylon.model.typechecker.model.Value;
/**
* Styled Label Provider which can be used to provide labels for Ceylon elements.
*
* Extends StyledCellLabelProvider to provide custom styling by doing its own painting
* - here the {@link #update(ViewerCell)} method is the entry point
* Implements DelegatingStyledCellLabelProvider.IStyledLabelProvider too, but this
* probably is not required.
*
* @author max
*
*/
public class CeylonLabelProvider extends StyledCellLabelProvider
implements DelegatingStyledCellLabelProvider.IStyledLabelProvider,
ILabelProvider, CeylonResources {
private Set<ILabelProviderListener> fListeners =
new HashSet<ILabelProviderListener>();
public static final Point BIG_SIZE = new Point(22,16);
public static final Point SMALL_SIZE = new Point(16,16);
private final boolean smallSize;
public final static int WARNING = 1 << 2;
public final static int ERROR = 1 << 3;
private final static int REFINES = 1 << 4;
private final static int IMPLEMENTS = 1 << 5;
private final static int FORMAL = 1 << 6;
private final static int DEFAULT = 1 << 7;
private final static int ABSTRACT = 1 << 8;
private final static int FINAL = 1 << 9;
private final static int SEALED = 1 << 10;
private final static int VARIABLE = 1 << 11;
private final static int ANNOTATION = 1 << 12;
private final static int ENUM = 1 << 13;
private final static int ALIAS = 1 << 14;
private final static int DEPRECATED = 1 << 15;
private final static int NATIVE = 1 << 16;
final static int FOCUS = 1 << 17;
static final DecorationDescriptor[] DECORATIONS =
new DecorationDescriptor[] {
new DecorationDescriptor(WARNING, WARNING_IMAGE, BOTTOM_LEFT),
new DecorationDescriptor(ERROR, ERROR_IMAGE, BOTTOM_LEFT),
new DecorationDescriptor(REFINES, REFINES_IMAGE, BOTTOM_RIGHT),
new DecorationDescriptor(IMPLEMENTS, IMPLEMENTS_IMAGE, BOTTOM_RIGHT),
new DecorationDescriptor(FORMAL, FORMAL_IMAGE, TOP_RIGHT),
new DecorationDescriptor(DEFAULT, DEFAULT_IMAGE, TOP_RIGHT),
new DecorationDescriptor(ABSTRACT, ABSTRACT_IMAGE, TOP_RIGHT),
new DecorationDescriptor(FINAL, FINAL_IMAGE, TOP_RIGHT),
new DecorationDescriptor(SEALED, SEALED_IMAGE, TOP_RIGHT),
new DecorationDescriptor(NATIVE, NATIVE_IMAGE, TOP_RIGHT),
new DecorationDescriptor(VARIABLE, VARIABLE_IMAGE, TOP_LEFT),
new DecorationDescriptor(ANNOTATION, ANNOTATION_IMAGE, TOP_LEFT),
new DecorationDescriptor(ENUM, ENUM_IMAGE, TOP_LEFT),
new DecorationDescriptor(ALIAS, ALIAS_IMAGE, TOP_LEFT),
new DecorationDescriptor(DEPRECATED, DEPRECATED_IMAGE, IDecoration.UNDERLAY),
new DecorationDescriptor(FOCUS, FOCUS_IMAGE, IDecoration.TOP_LEFT)
};
public CeylonLabelProvider() {
this(false);
}
public CeylonLabelProvider(boolean smallSize) {
this.smallSize = smallSize;
}
private static Image getDecoratedImage(Object element,
String key, boolean smallSize, boolean focus) {
if (key==null) return null;
int attributes = getDecorationAttributes(element);
if (focus) attributes |= FOCUS;
return getDecoratedImage(key, attributes, smallSize);
}
public static Image getDecoratedImage(String key,
int decorationAttributes, boolean smallSize) {
ImageDescriptor descriptor =
imageRegistry.getDescriptor(key);
if (descriptor==null) {
return null;
}
String decoratedKey =
key+'#'+decorationAttributes +
(smallSize ? "#small" : "");
Image image = imageRegistry.get(decoratedKey);
if (image==null) {
imageRegistry.put(decoratedKey,
new DecoratedImageDescriptor(descriptor,
decorationAttributes,
smallSize ? SMALL_SIZE : BIG_SIZE));
image = imageRegistry.get(decoratedKey);
}
return image;
}
@Override
public Image getImage(Object element) {
return getDecoratedImage(element,
getImageKey(element),
smallSize, false);
}
protected String getImageKey(Object element) {
if (element instanceof IFile) {
IFile file = (IFile) element;
return getImageKeyForFile(file);
}
else if (element instanceof IPath) {
IPath path = (IPath) element;
String name = path.lastSegment();
if (name.equals("module.ceylon")) {
return CEYLON_MODULE_DESC;
}
else if (name.equals("package.ceylon")) {
return CEYLON_PACKAGE_DESC;
}
return CEYLON_FILE;
}
if (element instanceof IFolder) {
IFolder folder = (IFolder) element;
if (folder.getAdapter(IPackageFragmentRoot.class)!=null) {
return CEYLON_SOURCE_FOLDER;
}
else if (folder.getAdapter(IPackageFragment.class)!=null) {
return CEYLON_PACKAGE;
}
return CEYLON_FOLDER;
}
else if (element instanceof IPackageFragmentRoot) {
IPackageFragmentRoot pfr =
(IPackageFragmentRoot) element;
if (pfr.getPath().getFileExtension()!=null) {
return CEYLON_MODULE;
}
else {
return CEYLON_SOURCE_FOLDER;
}
}
if (element instanceof IProject ||
element instanceof IJavaProject) {
return CEYLON_PROJECT;
}
if (element instanceof IPackageFragment) {
return isModule((IPackageFragment) element) ?
CEYLON_MODULE : CEYLON_PACKAGE;
}
if (element instanceof Package ||
element instanceof IPackageFragment) {
return CEYLON_PACKAGE;
}
if (element instanceof IImportDeclaration) {
return CEYLON_IMPORT;
}
if (element instanceof Module) {
return CEYLON_MODULE;
}
if (element instanceof Unit) {
return CEYLON_FILE;
}
if (element instanceof Node) {
Node treeNode = (Node) element;
return getImageKeyForNode(treeNode);
}
return null;
}
private static boolean isModule(IPackageFragment element) {
IFolder folder = (IFolder) element.getResource();
if (folder!=null &&
folder.getFile("module.ceylon").exists()) {
return true;
}
else {
return false;
}
}
public static String getImageKeyForFile(IFile element) {
String name = element.getName();
if (name.equals("module.ceylon")) {
return CEYLON_MODULE_DESC;
}
else if (name.equals("package.ceylon")) {
return CEYLON_PACKAGE_DESC;
}
else if (element.getFileExtension()
.equalsIgnoreCase("ceylon")) {
return CEYLON_FILE;
}
else if (element.getFileExtension()
.equalsIgnoreCase("java")) {
return JAVA_FILE;
}
else {
return GENERIC_FILE;
}
}
public static String getImageKeyForNode(Node n) {
if (n instanceof PackageNode) {
return CEYLON_PACKAGE;
}
else if (n instanceof ModuleNode) {
return CEYLON_MODULE;
}
else if (n instanceof Tree.PackageDescriptor) {
return CEYLON_PACKAGE;
}
else if (n instanceof Tree.ModuleDescriptor) {
return CEYLON_MODULE;
}
else if (n instanceof Tree.CompilationUnit) {
return CEYLON_FILE;
}
else if (n instanceof Tree.ImportList) {
return CEYLON_IMPORT_LIST;
}
else if (n instanceof Tree.Import ||
n instanceof Tree.ImportModule) {
return CEYLON_IMPORT;
}
else if (n instanceof Tree.Declaration) {
Tree.Declaration dec = (Tree.Declaration) n;
return getImageKeyForDeclarationNode(dec);
}
else if (n instanceof Tree.SpecifierStatement) {
Tree.SpecifierStatement ss =
(Tree.SpecifierStatement) n;
Tree.Term bme = ss.getBaseMemberExpression();
if (bme instanceof Tree.BaseMemberExpression) {
return CEYLON_LOCAL_ATTRIBUTE;
}
else if (bme instanceof Tree.ParameterizedExpression) {
return CEYLON_LOCAL_METHOD;
}
else {
throw new RuntimeException("unexpected node type");
}
}
else {
return null;
}
}
private static String getImageKeyForDeclarationNode(
Tree.Declaration n) {
boolean shared =
hasAnnotation(n.getAnnotationList(),
"shared", n.getUnit());
if (n instanceof Tree.AnyClass) {
if (shared) {
return CEYLON_CLASS;
}
else {
return CEYLON_LOCAL_CLASS;
}
}
else if (n instanceof Tree.AnyInterface) {
if (shared) {
return CEYLON_INTERFACE;
}
else {
return CEYLON_LOCAL_INTERFACE;
}
}
else if (n instanceof Tree.Constructor) {
if (shared) {
return CEYLON_CONSTRUCTOR;
}
else {
return CEYLON_CONSTRUCTOR; //TODO!!
}
}
else if (n instanceof Tree.Enumerated) {
if (shared) {
return CEYLON_CONSTRUCTOR;
}
else {
return CEYLON_CONSTRUCTOR; //TODO!!
}
}
else if (n instanceof Tree.AnyMethod) {
if (shared) {
return CEYLON_METHOD;
}
else {
return CEYLON_LOCAL_METHOD;
}
}
else if (n instanceof Tree.TypeAliasDeclaration) {
return CEYLON_ALIAS;
}
else if (n instanceof Tree.ObjectDefinition) {
if (shared) {
return CEYLON_OBJECT;
}
else {
return CEYLON_LOCAL_OBJECT;
}
}
else {
if (shared) {
return CEYLON_ATTRIBUTE;
}
else {
return CEYLON_LOCAL_ATTRIBUTE;
}
}
}
public static Image getImageForDeclaration(
Declaration element) {
return getDecoratedImage(element,
getImageKeyForDeclaration(element),
false, false);
}
public static Image getImageForDeclaration(
Declaration element, boolean focus) {
return getDecoratedImage(element,
getImageKeyForDeclaration(element),
false, focus);
}
public static Image getImageForFile(IFile file) {
return getDecoratedImage(file,
getImageKeyForFile(file),
false, false);
}
static String getImageKeyForDeclaration(Declaration d) {
if (d==null) return null;
boolean shared = d.isShared();
if (d instanceof Class) {
if (d.isAnonymous()) {
if (shared) {
return CEYLON_OBJECT;
}
else {
return CEYLON_LOCAL_OBJECT;
}
}
if (shared) {
return CEYLON_CLASS;
}
else {
return CEYLON_LOCAL_CLASS;
}
}
else if (d instanceof Interface) {
if (shared) {
return CEYLON_INTERFACE;
}
else {
return CEYLON_LOCAL_INTERFACE;
}
}
if (isConstructor(d)) {
if (shared) {
return CEYLON_CONSTRUCTOR;
}
else {
return CEYLON_CONSTRUCTOR; //TODO!
}
}
else if (d instanceof TypeParameter) {
return CEYLON_TYPE_PARAMETER;
}
else if (d.isParameter()) {
if (d instanceof Function) {
return CEYLON_PARAMETER_METHOD;
}
else {
return CEYLON_PARAMETER;
}
}
else if (d instanceof Function) {
if (shared) {
return CEYLON_METHOD;
}
else {
return CEYLON_LOCAL_METHOD;
}
}
else if (d instanceof TypeAlias ||
d instanceof NothingType) {
return CEYLON_ALIAS;
}
else if (d instanceof UnionType ||
d instanceof IntersectionType) {
return CEYLON_ALIAS; //not quite right!
}
else {
if (d instanceof Value) {
Value value = (Value) d;
if (ModelUtil.isObject(value)) {
if (shared) {
return CEYLON_OBJECT;
}
else {
return CEYLON_LOCAL_OBJECT;
}
}
}
if (shared) {
return CEYLON_ATTRIBUTE;
}
else {
return CEYLON_LOCAL_ATTRIBUTE;
}
}
}
@Override
public StyledString getStyledText(Object element) {
if (element instanceof IFile) {
IFile file = (IFile) element;
return new StyledString(file.getName());
}
else if (element instanceof IPath) {
IPath path = (IPath) element;
return new StyledString(path.lastSegment());
}
else if (element instanceof IFolder) {
IFolder folder = (IFolder) element;
return new StyledString(folder.getName());
}
else if (element instanceof IProject) {
IProject project = (IProject) element;
return new StyledString(project.getName());
}
else if (element instanceof IJavaProject) {
IJavaProject project = (IJavaProject) element;
return new StyledString(project.getElementName());
}
else if (element instanceof IPackageFragment) {
IPackageFragment pf =
(IPackageFragment) element;
String packageName = pf.getElementName();
if (packageName.isEmpty()) {
packageName = "(default package)";
}
return new StyledString(packageName, PACKAGE_STYLER);
}
else if (element instanceof IPackageFragmentRoot) {
IPackageFragmentRoot pfr =
(IPackageFragmentRoot) element;
boolean isCar =
pfr.getPath().getFileExtension()!=null;
IJavaElement je = (IJavaElement) element;
String name = je.getElementName();
int loc = name.lastIndexOf('.');
if (loc>=0) name = name.substring(0, loc);
loc = name.indexOf('-');
if (loc>=0) name = name.substring(0, loc);
if (isCar) {
return new StyledString(name, PACKAGE_STYLER);
}
else {
return new StyledString(name);
}
}
else if (element instanceof IImportDeclaration) {
IImportDeclaration id =
(IImportDeclaration) element;
StyledString label =
new StyledString("import ", KW_STYLER);
label.append(id.getElementName(), PACKAGE_STYLER);
return label;
}
else if (element instanceof Package) {
Package pack = (Package) element;
return new StyledString(getPackageLabel(pack),
PACKAGE_STYLER);
}
else if (element instanceof Module) {
Module mod = (Module) element;
return new StyledString(getModuleLabel(mod),
PACKAGE_STYLER);
}
else if (element instanceof Unit) {
Unit unit = (Unit) element;
return new StyledString(unit.getFilename());
}
else if (element instanceof Node) {
Node treeNode = (Node) element;
return getStyledLabelForNode(treeNode,
getPrefix(), getFont());
}
else {
return new StyledString("");
}
}
public String getPrefix() {
return null;
}
public Font getFont() {
return null;
}
@Override
public String getText(Object element) {
return getStyledText(element).toString();
}
private static void appendPostfixType(
Tree.TypedDeclaration td, StyledString label) {
if (CeylonPlugin.getPreferences()
.getBoolean(RETURN_TYPES_IN_OUTLINES)) {
Tree.Type type = td.getType();
if (type!=null &&
!(type instanceof Tree.DynamicModifier) &&
!(type instanceof Tree.VoidModifier)) {
Type tm = type.getTypeModel();
if (!isTypeUnknown(tm)) {
label.append(" ∊ ");
appendTypeName(label, tm, td.getUnit(),
ARROW_STYLER);
}
}
}
}
public static StyledString getStyledLabelForNode(Node node) {
return getStyledLabelForNode(node, null, null);
}
static void appendName(StyledString result, String name, Styler styler,
String prefix, Font font) {
if (prefix==null) {
result.append(name, styler);
}
else {
Highlights.styleIdentifier(result, prefix, name, styler, font);
}
}
public static StyledString getStyledLabelForNode(Node node,
String prefix, Font font) {
//TODO: it would be much better to render types
// from the tree nodes instead of from the
// model nodes
if (node instanceof Tree.TypeParameterDeclaration) {
Tree.TypeParameterDeclaration ac =
(Tree.TypeParameterDeclaration) node;
String name = name(ac.getIdentifier());
StyledString label = new StyledString();
appendName(label, name, ID_STYLER, prefix, font);
return label;
}
else if (node instanceof Tree.AnyClass) {
Tree.AnyClass ac = (Tree.AnyClass) node;
StyledString label =
new StyledString("class ", KW_STYLER);
String name = name(ac.getIdentifier());
appendName(label, name, TYPE_ID_STYLER, prefix, font);
parameters(ac.getTypeParameterList(), label);
parameters(ac.getParameterList(), label);
return label;
}
else if (node instanceof Tree.AnyInterface) {
Tree.AnyInterface ai = (Tree.AnyInterface) node;
StyledString label =
new StyledString("interface ", KW_STYLER);
String name = name(ai.getIdentifier());
appendName(label, name, TYPE_ID_STYLER, prefix, font);
parameters(ai.getTypeParameterList(), label);
return label;
}
else if (node instanceof Tree.Constructor) {
Tree.Constructor ac = (Tree.Constructor) node;
StyledString label =
new StyledString("new ", KW_STYLER);
if (ac.getIdentifier()!=null) {
String name = name(ac.getIdentifier());
appendName(label, name, ID_STYLER, prefix, font);
}
parameters(ac.getParameterList(), label);
return label;
}
else if (node instanceof Tree.Enumerated) {
Tree.Enumerated ac = (Tree.Enumerated) node;
StyledString label =
new StyledString("new ", KW_STYLER);
String name = name(ac.getIdentifier());
appendName(label, name, ID_STYLER, prefix, font);
return label;
}
else if (node instanceof Tree.TypeAliasDeclaration) {
Tree.TypeAliasDeclaration ac =
(Tree.TypeAliasDeclaration) node;
StyledString label =
new StyledString("alias ", KW_STYLER);
String name = name(ac.getIdentifier());
appendName(label, name, TYPE_ID_STYLER, prefix, font);
parameters(ac.getTypeParameterList(), label);
return label;
}
else if (node instanceof Tree.ObjectDefinition) {
Tree.ObjectDefinition ai =
(Tree.ObjectDefinition) node;
String name = name(ai.getIdentifier());
StyledString label =
new StyledString("object ", KW_STYLER);
appendName(label, name, ID_STYLER, prefix, font);
return label;
}
else if (node instanceof Tree.AttributeSetterDefinition) {
Tree.AttributeSetterDefinition ai =
(Tree.AttributeSetterDefinition) node;
String name = name(ai.getIdentifier());
StyledString label =
new StyledString("assign ", KW_STYLER);
appendName(label, name, ID_STYLER, prefix, font);
return label;
}
else if (node instanceof Tree.AnyMethod) {
Tree.TypedDeclaration td =
(Tree.TypedDeclaration) node;
String kind;
Tree.Type type = td.getType();
if (type instanceof Tree.DynamicModifier) {
kind = "dynamic";
}
else if (type instanceof Tree.VoidModifier) {
kind = "void";
}
else {
kind = "function";
}
String name = name(td.getIdentifier());
StyledString label =
new StyledString(kind, KW_STYLER)
.append(" ");
appendName(label, name, ID_STYLER, prefix, font);
Tree.AnyMethod am = (Tree.AnyMethod) node;
parameters(am.getTypeParameterList(), label);
for (Tree.ParameterList pl: am.getParameterLists()) {
parameters(pl, label);
}
appendPostfixType(td, label);
return label;
}
else if (node instanceof Tree.TypedDeclaration) {
Tree.TypedDeclaration td =
(Tree.TypedDeclaration) node;
String kind;
Tree.Type type = td.getType();
if (type instanceof Tree.DynamicModifier) {
kind = "dynamic";
}
else {
kind = "value";
}
String name = name(td.getIdentifier());
StyledString label =
new StyledString(kind, KW_STYLER)
.append(" ");
appendName(label, name, ID_STYLER, prefix, font);
appendPostfixType(td, label);
return label;
}
// else if (n instanceof Tree.TypedDeclaration) {
// Tree.TypedDeclaration td = (Tree.TypedDeclaration) n;
// Tree.Type tt = td.getType();
// StyledString label = new StyledString();
// label.append(type(tt, td))
// .append(" ")
// .append(name(td.getIdentifier()), ID_STYLER);
// if (n instanceof Tree.AnyMethod) {
// Tree.AnyMethod am = (Tree.AnyMethod) n;
// parameters(am.getTypeParameterList(), label);
// for (Tree.ParameterList pl: am.getParameterLists()) {
// parameters(pl, label);
// }
// }
// return label;
// }
else if (node instanceof Tree.CompilationUnit) {
Tree.CompilationUnit ai =
(Tree.CompilationUnit) node;
if (ai.getUnit()==null) {
return new StyledString("unknown");
}
return new StyledString(ai.getUnit().getFilename());
}
else if (node instanceof Tree.ModuleDescriptor) {
Tree.ModuleDescriptor i =
(Tree.ModuleDescriptor) node;
Tree.ImportPath p = i.getImportPath();
if (isNonempty(p)) {
StyledString styledString =
new StyledString("module ", KW_STYLER)
.append(toPath(p), PACKAGE_STYLER);
Tree.ModuleDescriptor md =
(Tree.ModuleDescriptor) node;
Tree.QuotedLiteral version = md.getVersion();
if (version!=null) {
styledString.append(" ")
.append(version.getText(), STRING_STYLER);
}
return styledString;
}
}
else if (node instanceof Tree.PackageDescriptor) {
Tree.PackageDescriptor i =
(Tree.PackageDescriptor) node;
Tree.ImportPath p = i.getImportPath();
if (isNonempty(p)) {
return new StyledString("package ", KW_STYLER)
.append(toPath(p), PACKAGE_STYLER);
}
}
else if (node instanceof Tree.ImportList) {
return new StyledString("imports");
}
else if (node instanceof Tree.ImportPath) {
Tree.ImportPath p = (Tree.ImportPath) node;
if (isNonempty(p)) {
return new StyledString(toPath(p), PACKAGE_STYLER);
}
}
else if (node instanceof Tree.Import) {
Tree.Import i = (Tree.Import) node;
Tree.ImportPath p = i.getImportPath();
if (isNonempty(p)) {
return new StyledString("import ", KW_STYLER)
.append(toPath(p), PACKAGE_STYLER);
}
}
else if (node instanceof Tree.ImportModule) {
Tree.ImportModule i = (Tree.ImportModule) node;
StyledString styledString =
new StyledString("import ", KW_STYLER);
Tree.ImportPath p = i.getImportPath();
if (isNonempty(p)) {
styledString.append(toPath(p), PACKAGE_STYLER);
}
Tree.QuotedLiteral ql = i.getQuotedLiteral();
if (ql!=null) {
styledString.append(ql.getText(), STRING_STYLER);
}
Tree.QuotedLiteral version = i.getVersion();
if (version!=null) {
styledString.append(" ")
.append(version.getText(), STRING_STYLER);
}
return styledString;
}
else if (node instanceof PackageNode) {
PackageNode packageNode = (PackageNode) node;
String name = packageNode.getPackageName();
if (name.isEmpty()) {
return new StyledString("(default package)");
}
else {
return new StyledString(name/*, PACKAGE_STYLER*/);
}
}
else if (node instanceof ModuleNode) {
ModuleNode moduleNode = (ModuleNode) node;
String name = moduleNode.getModuleName();
if (name.isEmpty() || "default".equals(name)) {
return new StyledString("(default module)");
}
else {
return new StyledString(name/*, PACKAGE_STYLER*/)
.append(" \"", STRING_STYLER)
.append(moduleNode.getVersion(), STRING_STYLER)
.append("\"", STRING_STYLER);
}
}
else if (node instanceof Tree.SpecifierStatement) {
Tree.SpecifierStatement ss =
(Tree.SpecifierStatement) node;
Tree.Term bme = ss.getBaseMemberExpression();
Tree.Identifier id;
String kw;
List<Tree.ParameterList> pls;
if (bme instanceof Tree.BaseMemberExpression) {
Tree.BaseMemberExpression be =
(Tree.BaseMemberExpression) bme;
id = be.getIdentifier();
pls = null;
kw = "value";
}
else if (bme instanceof Tree.ParameterizedExpression) {
Tree.ParameterizedExpression pe =
(Tree.ParameterizedExpression) bme;
Tree.Primary primary = pe.getPrimary();
Tree.BaseMemberExpression bp =
(Tree.BaseMemberExpression) primary;
id = bp.getIdentifier();
kw = "function";
pls = pe.getParameterLists();
}
else {
throw new RuntimeException("unexpected node type");
}
StyledString label =
new StyledString(kw, KW_STYLER)
.append(" ");
String name = name(id);
appendName(label, name, ID_STYLER, prefix, font);
if (pls!=null) {
for (Tree.ParameterList pl: pls) {
parameters(pl, label);
}
}
return label;
}
return new StyledString("");
}
private static boolean isNonempty(Tree.ImportPath p) {
return p!=null && !p.getIdentifiers().isEmpty();
}
private static String toPath(Tree.ImportPath p) {
return formatPath(p.getIdentifiers());
}
private static StyledString type(Tree.Type type,
Tree.TypedDeclaration node) {
StyledString result = new StyledString();
if (type!=null) {
if (type instanceof Tree.VoidModifier) {
return result.append("void", KW_STYLER);
}
if (type instanceof Tree.DynamicModifier) {
return result.append("dynamic", KW_STYLER);
}
Type tm = type.getTypeModel();
if (tm!=null && !isTypeUnknown(tm)) {
Unit unit = node.getUnit();
if (type instanceof Tree.SequencedType) {
Tree.SequencedType st =
(Tree.SequencedType) type;
Type itm =
type.getUnit()
.getIteratedType(tm);
if (itm!=null) {
appendTypeName(result, itm, unit);
result.append(st.getAtLeastOne()?"+":"*");
return result;
}
}
appendTypeName(result, tm, unit);
return result;
}
}
String keyword;
if (node instanceof Tree.AnyMethod) {
keyword = "function";
}
else {
keyword = "value";
}
return result.append(keyword, KW_STYLER);
}
private static String name(Tree.Identifier id) {
if (id==null ||
id.getText().startsWith("<missing")) {
return "<unknown>";
}
else {
return id.getText();
}
}
private static void parameters(Tree.ParameterList pl,
StyledString label) {
IPreferenceStore prefs = CeylonPlugin.getPreferences();
boolean names =
prefs.getBoolean(PARAMS_IN_OUTLINES);
boolean types =
prefs.getBoolean(PARAM_TYPES_IN_OUTLINES);
if (names || types) {
if (pl==null ||
pl.getParameters().isEmpty()) {
label.append("()");
}
else {
label.append("(");
int len = pl.getParameters().size(), i=0;
for (Tree.Parameter p: pl.getParameters()) {
if (p!=null) {
if (p instanceof Tree.ParameterDeclaration) {
Tree.ParameterDeclaration pd =
(Tree.ParameterDeclaration) p;
Tree.TypedDeclaration td =
pd.getTypedDeclaration();
if (types) {
label.append(type(td.getType(), td));
}
if (names && types) label.append(' ');
if (names) {
String name = name(td.getIdentifier());
label.append(name, ID_STYLER);
}
if (p instanceof Tree.FunctionalParameterDeclaration) {
Tree.MethodDeclaration md =
(Tree.MethodDeclaration) td;
List<Tree.ParameterList> pls =
md.getParameterLists();
if (!names) {
pls = new ArrayList<Tree.ParameterList>(pls);
Collections.reverse(pls);
}
for (Tree.ParameterList ipl: pls) {
parameters(ipl, label);
}
}
}
else if (p instanceof Tree.InitializerParameter) {
Tree.InitializerParameter ip =
(Tree.InitializerParameter) p;
Parameter model =
p.getParameterModel();
if (model!=null &&
model.getType()!=null) {
if (types) {
appendTypeName(label,
model.getType(),
pl.getUnit());
}
if (types && names) {
label.append(' ');
}
}
if (names) {
String name = name(ip.getIdentifier());
label.append(name, ID_STYLER);
}
}
}
if (++i<len) label.append(", ");
}
label.append(")");
}
}
}
private static void parameters(Tree.TypeParameterList tpl,
StyledString label) {
if (CeylonPlugin.getPreferences()
.getBoolean(TYPE_PARAMS_IN_OUTLINES)) {
if (tpl!=null &&
!tpl.getTypeParameterDeclarations()
.isEmpty()) {
label.append("<");
int len = tpl.getTypeParameterDeclarations().size();
int i=0;
for (Tree.TypeParameterDeclaration p:
tpl.getTypeParameterDeclarations()) {
Tree.TypeVariance var = p.getTypeVariance();
if (var!=null) {
label.append(var.getText(), KW_STYLER)
.append(" ");
}
String name = name(p.getIdentifier());
label.append(name, TYPE_STYLER);
if (++i<len) label.append(", ");
}
label.append(">");
}
}
}
@Override
public void addListener(ILabelProviderListener listener) {
fListeners.add(listener);
}
@Override
public void dispose() {}
@Override
public boolean isLabelProperty(Object element, String property) {
return false;
}
@Override
public void removeListener(ILabelProviderListener listener) {
fListeners.remove(listener);
}
@Override
public void update(ViewerCell cell) {
Object element = cell.getElement();
StyledString styledText = getStyledText(element);
cell.setText(styledText.toString());
cell.setStyleRanges(styledText.getStyleRanges());
cell.setImage(getImage(element));
super.update(cell);
}
public static void appendTypeName(StyledString result,
Type type) {
appendTypeName(result, type, null, TYPE_STYLER);
}
public static void appendTypeName(StyledString result,
Type type, Unit unit) {
appendTypeName(result, type, unit, TYPE_STYLER);
}
public static void appendTypeName(StyledString result,
Type type, Styler styler) {
appendTypeName(result, type, null, styler);
}
public static void appendTypeName(StyledString result,
Type type, Unit unit, Styler styler) {
try {
String typeName = type.asString(unit);
StringTokenizer tokens =
new StringTokenizer(typeName,
"|&?[]{}*+=-<>(),. ",true);
while (tokens.hasMoreTokens()) {
String token = tokens.nextToken();
if (Character.isLetter(token.charAt(0))) {
result.append(token, styler);
}
else {
result.append(token);
}
}
}
catch (Exception e) {
e.printStackTrace();
}
}
public static int getDecorationAttributes(Object entity) {
try {
if (entity instanceof IProject) {
int decorationAttributes = 0;
IProject project = (IProject) entity;
switch (getMaxProblemMarkerSeverity(
project, DEPTH_INFINITE)) {
case IMarker.SEVERITY_ERROR:
decorationAttributes |= ERROR;
break;
case IMarker.SEVERITY_WARNING:
decorationAttributes |= WARNING;
break;
}
return decorationAttributes;
}
if (entity instanceof IPackageFragment) {
IPackageFragment pf =
(IPackageFragment) entity;
IFolder folder = null;
try {
folder = (IFolder)
pf.getCorrespondingResource();
}
catch (JavaModelException e) {}
if (folder != null) {
final BaseIdeModule moduleOfRootPackage =
getModule(folder);
int sev = getMaxProblemMarkerSeverity(
folder, DEPTH_INFINITE,
new IMarkerFilter() {
@Override
public boolean select(IMarker marker) {
IResource r = marker.getResource();
if (r instanceof IFile) {
IFile f = (IFile) r;
Package currentPackage =
getPackage(f);
if (moduleOfRootPackage != null &&
currentPackage != null) {
return moduleOfRootPackage.equals(
currentPackage.getModule());
}
}
return false;
}
});
switch (sev) {
case SEVERITY_ERROR:
return ERROR;
case SEVERITY_WARNING:
return WARNING;
default:
return 0;
}
}
}
if (entity instanceof IResource) {
IResource resource = (IResource) entity;
int sev = getMaxProblemMarkerSeverity(
resource, DEPTH_ONE);
switch (sev) {
case SEVERITY_ERROR:
return ERROR;
case SEVERITY_WARNING:
return WARNING;
default:
return 0;
}
}
if (entity instanceof Declaration) {
Declaration dec = (Declaration) entity;
return getDecorationAttributes(dec);
}
if (entity instanceof Node) {
Node treeNode = (Node) entity;
return getNodeDecorationAttributes(treeNode);
}
}
catch (Exception e) {
e.printStackTrace();
}
return 0;
}
public static int getNodeDecorationAttributes(Node node) {
int result = 0;
if (node instanceof Tree.Declaration ||
node instanceof Tree.Import) {
ErrorCollectionVisitor ev =
new ErrorCollectionVisitor(node, true);
node.visit(ev);
boolean warnings=false;
boolean errors=false;
for (Message m: ev.getErrors()) {
if (m instanceof UsageWarning) {
warnings = true;
}
else {
errors = true;
}
}
if (errors) {
result |= ERROR;
}
else if (warnings) {
result |= WARNING;
}
if (node instanceof Tree.Declaration) {
Tree.Declaration dec =
(Tree.Declaration) node;
result |= getDecorationAttributes(
dec.getDeclarationModel());
}
}
else if (node instanceof Tree.SpecifierStatement) {
Tree.SpecifierStatement ss =
(Tree.SpecifierStatement) node;
Tree.Term bme = ss.getBaseMemberExpression();
Declaration model;
if (bme instanceof Tree.StaticMemberOrTypeExpression) {
Tree.StaticMemberOrTypeExpression smte =
(Tree.StaticMemberOrTypeExpression) bme;
model = smte.getDeclaration();
}
else if (bme instanceof Tree.ParameterizedExpression) {
Tree.ParameterizedExpression pe =
(Tree.ParameterizedExpression) bme;
Tree.Primary primary = pe.getPrimary();
Tree.BaseMemberExpression pbe =
(Tree.BaseMemberExpression) primary;
model = pbe.getDeclaration();
}
else {
throw new RuntimeException("unexpected node type");
}
if (model!=null) {
Declaration refined =
types_.get_()
.getRefinedDeclaration(model);
if (refined!=null) {
result |= refined.isFormal() ?
IMPLEMENTS : REFINES;
}
}
}
return result;
}
private static int getDecorationAttributes(Declaration model) {
if (model == null) {
return 0;
}
int result = 0;
if (model.isDeprecated()) {
result |= DEPRECATED;
}
if (model.isFormal()) {
result |= FORMAL;
}
if (model.isDefault()) {
result |= DEFAULT;
}
if (model.isNative()) {
result |= NATIVE;
}
if (model instanceof TypeDeclaration &&
((TypeDeclaration) model).isSealed()) {
result |= SEALED;
}
if (model.isAnnotation()) {
result |= ANNOTATION;
}
if ((model instanceof Value) &&
((Value) model).isVariable()) {
result |= VARIABLE;
}
if (model instanceof Class &&
((Class) model).isAbstract()) {
result |= ABSTRACT;
}
if (model instanceof Class &&
((Class) model).isFinal()) {
result |= FINAL;
}
// if (model instanceof Class && ((Class) model).isFinal()) {
// result |= FINAL;
// }
if (model instanceof TypeDeclaration) {
TypeDeclaration td =
(TypeDeclaration) model;
if(td.getCaseTypes()!=null) {
result |= ENUM;
}
if (td.isAlias()) {
result |= ALIAS;
}
}
if (model.isActual()) {
Declaration refined =
types_.get_()
.getRefinedDeclaration(model);
if (refined!=null) {
result |= refined.isFormal() ?
IMPLEMENTS : REFINES;
}
}
return result;
}
static interface IMarkerFilter {
boolean select(IMarker marker);
}
static IMarkerFilter acceptAllMarkers =
new IMarkerFilter() {
@Override
public boolean select(IMarker marker) {
return true;
}
};
/**
* Returns the maximum problem marker severity for the given resource, and, if
* depth is IResource.DEPTH_INFINITE, its children. The return value will be
* one of IMarker.SEVERITY_ERROR, IMarker.SEVERITY_WARNING, IMarker.SEVERITY_INFO
* or 0, indicating that no problem markers exist on the given resource.
* @param depth TODO
*/
static int getMaxProblemMarkerSeverity(IResource res,
int depth, IMarkerFilter markerFilter) {
if (res == null || !res.isAccessible())
return 0;
boolean hasWarnings = false; // if resource has errors, will return error image immediately
IMarker[] markers = null;
try {
markers = res.findMarkers(IMarker.PROBLEM, true, depth);
}
catch (CoreException e) {
e.printStackTrace();
}
if (markers == null)
return 0; // don't know - say no errors/warnings/infos
for (int i= 0; i < markers.length; i++) {
IMarker m = markers[i];
if (markerFilter.select(m)) {
int priority = m.getAttribute(IMarker.SEVERITY, -1);
if (priority == IMarker.SEVERITY_WARNING) {
hasWarnings = true;
}
else if (priority == IMarker.SEVERITY_ERROR) {
return IMarker.SEVERITY_ERROR;
}
}
}
return hasWarnings ? IMarker.SEVERITY_WARNING : 0;
}
/**
* Returns the maximum problem marker severity for the given resource, and, if
* depth is IResource.DEPTH_INFINITE, its children. The return value will be
* one of IMarker.SEVERITY_ERROR, IMarker.SEVERITY_WARNING, IMarker.SEVERITY_INFO
* or 0, indicating that no problem markers exist on the given resource.
* @param depth TODO
*/
static int getMaxProblemMarkerSeverity(IResource res, int depth) {
return getMaxProblemMarkerSeverity(res, depth, acceptAllMarkers);
}
private static String getPackageLabel(Package packageModel) {
String name = packageModel.getQualifiedNameString();
if (name.isEmpty()) {
return "(default package)";
}
return name;
}
private static String getModuleLabel(Module moduleModel) {
String name = moduleModel.getNameAsString();
if (name.isEmpty() ||
name.equals(Module.DEFAULT_MODULE_NAME)) {
return "(default module)";
}
return name;
}
public static String getModuleLabel(Declaration dec) {
Unit unit = dec.getUnit();
return unit==null ? "(unknown module)" :
getModuleLabel(unit.getPackage().getModule());
}
public static String getPackageLabel(Declaration dec) {
Unit unit = dec.getUnit();
return unit==null ? "(unknown package)" :
getPackageLabel(unit.getPackage());
}
private static String getRefinementIconKey(Declaration dec) {
if (dec.isParameter()) {
return CEYLON_ARGUMENT;
}
else {
return dec.isFormal() ?
CEYLON_FORMAL_REFINEMENT :
CEYLON_DEFAULT_REFINEMENT;
}
}
public static Image getRefinementIcon(Declaration dec) {
return getDecoratedImage(null,
getRefinementIconKey(dec),
false, false);
}
}