/* * Copyright (c) 2013, the Dart project authors. * * Licensed under the Eclipse Public License v1.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.eclipse.org/legal/epl-v10.html * * 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 com.google.dart.tools.ui.internal.typehierarchy; import com.google.common.base.Predicate; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.dart.engine.element.ClassElement; import com.google.dart.engine.element.ConstructorElement; import com.google.dart.engine.element.Element; import com.google.dart.engine.element.ExecutableElement; import com.google.dart.engine.element.PropertyInducingElement; import com.google.dart.engine.element.visitor.GeneralizingElementVisitor; import com.google.dart.engine.search.SearchEngine; import com.google.dart.engine.services.util.HierarchyUtils; import com.google.dart.engine.type.InterfaceType; import com.google.dart.tools.core.DartCore; import com.google.dart.tools.ui.DartToolsPlugin; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.StyledString; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; public class TypeHierarchyContentProvider_OLD implements ITreeContentProvider { public static class SuperItem extends TypeItem { private final InterfaceType[] withTypes; private final InterfaceType[] implementsTypes; public SuperItem(InterfaceType type, InterfaceType[] withTypes) { super(type); this.withTypes = withTypes; this.implementsTypes = type.getInterfaces(); } @Override public StyledString toStyledString() { StyledString styledString = super.toStyledString(); if (withTypes.length != 0) { styledString.append(" with ", StyledString.QUALIFIER_STYLER); styledString.append(StringUtils.join(withTypes, ", ")); } if (implementsTypes.length != 0) { styledString.append(" implements ", StyledString.QUALIFIER_STYLER); styledString.append(StringUtils.join(implementsTypes, ", ")); } return styledString; } } public static class TypeItem { public final InterfaceType type; public final Element element; public TypeItem(InterfaceType type) { this.type = type; this.element = type.getElement(); } @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj instanceof ClassElement) { return obj.equals(element); } if (obj instanceof TypeItem) { return ((TypeItem) obj).element.equals(element); } return false; } @Override public int hashCode() { return element.hashCode(); } public StyledString toStyledString() { return new StyledString(type.toString()); } } private static final SearchEngine searchEngine = DartCore.getProjectManager().newSearchEngine(); private static SuperItem createSuperItem(Set<InterfaceType> addedTypes, InterfaceType type) { InterfaceType superType = type.getSuperclass(); if (superType != null && superType.isObject()) { InterfaceType[] interfaces = type.getInterfaces(); if (interfaces.length != 0) { superType = interfaces[0]; } } if (superType == null) { return null; } if (!addedTypes.add(superType)) { return null; } return new SuperItem(superType, type.getMixins()); } /** * @return any local member of the given {@link ClassElement} with given name. */ private static Element findLocalMember(final ClassElement type, final String name) { final Element result[] = {null}; type.accept(new GeneralizingElementVisitor<Void>() { @Override public Void visitElement(Element element) { if (element instanceof ConstructorElement) { return null; } if (element.isSynthetic()) { return null; } if (element == type) { super.visitElement(element); } else if (name == null || element.getDisplayName().equals(name)) { result[0] = element; } return null; } }); return result[0]; } /** * @return <code>true</code> if given {@link ClassElement} has local member with given name. */ private static boolean hasLocalMember(ClassElement type, String name) { return findLocalMember(type, name) != null; } private String memberName; private final List<SuperItem> superList = Lists.newArrayList(); private final Map<ClassElement, List<ClassElement>> superToSubsMap = Maps.newHashMap(); private final Map<ClassElement, ClassElement> subToSuperMap = Maps.newHashMap(); @Override public void dispose() { } @Override public Object[] getChildren(Object parentElement) { // super { int superIndex = superList.indexOf(parentElement); if (superIndex >= 0 && superIndex < superList.size() - 1) { return new Object[] {superList.get(superIndex + 1)}; } } // TypeItem -> ClassElement if (parentElement instanceof TypeItem) { parentElement = ((TypeItem) parentElement).element; } // subs { List<ClassElement> subs = superToSubsMap.get(parentElement); if (subs != null) { return subs.toArray(new ClassElement[subs.size()]); } } // no children return ArrayUtils.EMPTY_OBJECT_ARRAY; } @Override public Object[] getElements(Object inputElement) { if (!superList.isEmpty()) { return new Object[] {superList.get(0)}; } return ArrayUtils.EMPTY_OBJECT_ARRAY; } @Override public Object getParent(Object element) { int superIndex = superList.indexOf(element); if (superIndex >= 0 && superIndex < superList.size()) { if (superIndex == 0) { return null; } return superList.get(superIndex - 1); } return subToSuperMap.get(element); } @Override public boolean hasChildren(Object element) { return getChildren(element).length != 0; } @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { memberName = null; superList.clear(); superToSubsMap.clear(); subToSuperMap.clear(); if (newInput instanceof Object[] && ((Object[]) newInput).length == 1) { Object inputObject = ((Object[]) newInput)[0]; if (!(inputObject instanceof Element)) { return; } Element inputElement = (Element) inputObject; if (inputElement.getEnclosingElement() instanceof ClassElement) { if (inputElement instanceof ExecutableElement || inputElement instanceof PropertyInducingElement) { memberName = inputElement.getDisplayName(); inputObject = inputElement.getEnclosingElement(); } } if (inputObject instanceof ClassElement) { ClassElement inputClass = (ClassElement) inputObject; InterfaceType inputType = inputClass.getType(); InterfaceType type = inputType; Set<InterfaceType> addedTypes = Sets.<InterfaceType> newHashSet(); while (true) { SuperItem item = createSuperItem(addedTypes, type); if (item == null) { break; } superList.add(0, item); type = item.type; } superList.add(new SuperItem(inputType, InterfaceType.EMPTY_ARRAY)); // sub types scheduleSubTypesSearch(viewer, inputClass); } } } /** * @return the {@link ClassElement} or {@link ExecutableElement} for given {@link ClassElement} * selection. */ Object convertSelectedElement(Object o) { try { if (o instanceof TypeItem) { o = ((TypeItem) o).element; } if (memberName != null && o instanceof ClassElement) { ClassElement type = (ClassElement) o; return findLocalMember(type, memberName); } } catch (Throwable e) { DartToolsPlugin.log(e); } return o; } /** * @return the {@link Predicate} to check if given {@link ClassElement} is not interesting part of * hierarchy, so should be displayed using light color. */ Predicate<Object> getLightPredicate() { return new Predicate<Object>() { @Override public boolean apply(Object input) { if (input instanceof TypeItem) { input = ((TypeItem) input).element; } if (memberName != null && input instanceof ClassElement) { ClassElement type = (ClassElement) input; return !hasLocalMember(type, memberName); } return false; } }; } /** * Builds complete sub-types hierarchy in {@link #superToSubsMap}. */ private void fillSubTypes(ClassElement type) { List<ClassElement> subTypes = HierarchyUtils.getDirectSubClasses(searchEngine, type); for (ClassElement subType : subTypes) { subToSuperMap.put(subType, type); fillSubTypes(subType); } superToSubsMap.put(type, subTypes); } /** * @return <code>true</code> if branch of given {@link ClassElement} has override for member. */ private boolean keepBranchesWithMemberOverride(ClassElement type) { List<ClassElement> subTypes = superToSubsMap.get(type); if (subTypes != null) { for (Iterator<ClassElement> I = subTypes.iterator(); I.hasNext();) { ClassElement subType = I.next(); if (!keepBranchesWithMemberOverride(subType)) { I.remove(); superToSubsMap.remove(subType); } } if (!subTypes.isEmpty()) { return true; } } return hasLocalMember(type, memberName); } /** * Schedules possibly long-running sub-types search operation. */ private void scheduleSubTypesSearch(final Viewer viewer, final ClassElement inputClass) { Thread thread = new Thread() { @Override public void run() { // prepare sub types fillSubTypes(inputClass); if (memberName != null) { keepBranchesWithMemberOverride(inputClass); } // refresh viewer Display.getDefault().asyncExec(new Runnable() { @Override public void run() { Control control = viewer.getControl(); if (control != null && !control.isDisposed()) { viewer.refresh(); if (viewer instanceof TreeViewer) { ((TreeViewer) viewer).expandAll(); } } } }); } }; thread.setDaemon(true); thread.start(); } }