+package org.simantics.scl.ui.search;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.jface.viewers.AbstractTreeViewer;
+import org.eclipse.jface.viewers.DecoratingStyledCellLabelProvider;
+import org.eclipse.jface.viewers.DecorationContext;
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.IColorProvider;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.ILabelProviderListener;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.OpenEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.StyledString;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerComparator;
+import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider;
+import org.eclipse.search.ui.text.AbstractTextSearchResult;
+import org.eclipse.search.ui.text.AbstractTextSearchViewPage;
+import org.eclipse.search.ui.text.Match;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.simantics.scl.compiler.module.debug.SymbolReference;
+import org.simantics.scl.ui.editor2.OpenDeclaration;
+import org.simantics.scl.ui.editor2.OpenSCLDefinition;
+
+public class SCLSearchResultPage extends AbstractTextSearchViewPage {
+
+ private SCLSearchResultContentProvider contentProvider;
+
+ public SCLSearchResultPage() {
+ setElementLimit(-1);
+ }
+ @Override
+ protected void elementsChanged(Object[] objects) {
+ if (contentProvider != null)
+ contentProvider.elementsChanged(objects);
+ }
+
+ @Override
+ protected void clear() {
+
+ }
+
+ private static final ViewerComparator comparator = new ViewerComparator((o1, o2) -> o1.compareTo(o2));
+
+ @Override
+ protected void configureTreeViewer(TreeViewer viewer) {
+ viewer.setUseHashlookup(true);
+ contentProvider = new SCLSearchResultContentProvider(this);
+ viewer.setContentProvider(contentProvider);
+ viewer.setComparator(comparator);
+ viewer.setLabelProvider(contentProvider);
+ }
+
+ @Override
+ protected void configureTableViewer(TableViewer viewer) {
+ viewer.setUseHashlookup(true);
+ contentProvider = new SCLSearchResultContentProvider(this);
+ viewer.setContentProvider(contentProvider);
+ viewer.setComparator(comparator);
+ viewer.setLabelProvider(contentProvider);
+ }
+
+ @Override
+ protected void handleOpen(OpenEvent event) {
+ Object selection = ((StructuredSelection)event.getSelection()).getFirstElement();
+ if (selection != null) {
+ SymbolReference reference = (SymbolReference) selection;
+ OpenSCLDefinition.openDefinition(reference.referrer.module, reference.referenceLocation);
+ }
+ }
+
+ @Override
+ protected void showMatch(Match match, int currentOffset, int currentLength) throws PartInitException {
+ SymbolReference reference = (SymbolReference) match.getElement();
+ OpenSCLDefinition.openDefinition(reference.referrer.module, reference.referenceLocation);
+ }
+
+ public static class SCLSearchResultContentProvider extends DecoratingStyledCellLabelProvider implements ITreeContentProvider, ILabelProvider {
+
+ private Map<Object, Set<Object>> fChildrenMap;
+ private AbstractTextSearchResult result;
+ private SCLSearchResultPage page;
+
+ public SCLSearchResultContentProvider(SCLSearchResultPage sclSearchResultPage) {
+ super(new SCLSearchResultLabelProvider(), PlatformUI.getWorkbench().getDecoratorManager().getLabelDecorator(), DecorationContext.DEFAULT_CONTEXT);
+ this.page = sclSearchResultPage;
+ }
+
+ @Override
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ initialize((AbstractTextSearchResult) newInput);
+ }
+
+ protected void initialize(AbstractTextSearchResult result) {
+ this.result = result;
+ fChildrenMap= new HashMap<>();
+ if (result != null) {
+ Object[] elements= result.getElements();
+ for (int i= 0; i < elements.length; i++) {
+ if (getPage().getDisplayedMatchCount(elements[i]) > 0) {
+ insert(null, null, elements[i]);
+ }
+ }
+ }
+ }
+
+ private AbstractTextSearchResult getSearchResult() {
+ return result;
+ }
+
+ public SCLSearchResultPage getPage() {
+ return page;
+ }
+
+ public void elementsChanged(Object[] updatedElements) {
+ if (getSearchResult() == null)
+ return;
+
+ AbstractTreeViewer viewer= (AbstractTreeViewer) getPage().getViewer();
+
+ Set<Object> toRemove= new HashSet<>();
+ Set<Object> toUpdate= new HashSet<>();
+ Map<Object, Set<Object>> toAdd= new HashMap<>();
+ for (int i= 0; i < updatedElements.length; i++) {
+ if (getPage().getDisplayedMatchCount(updatedElements[i]) > 0)
+ insert(toAdd, toUpdate, updatedElements[i]);
+ else
+ remove(toRemove, toUpdate, updatedElements[i]);
+ }
+
+ viewer.remove(toRemove.toArray());
+ for (Iterator<Object> iter= toAdd.keySet().iterator(); iter.hasNext();) {
+ Object parent= iter.next();
+ HashSet<Object> children= (HashSet<Object>) toAdd.get(parent);
+ viewer.add(parent, children.toArray());
+ }
+ for (Iterator<Object> elementsToUpdate= toUpdate.iterator(); elementsToUpdate.hasNext();) {
+ viewer.refresh(elementsToUpdate.next());
+ }
+ }
+
+ protected void insert(Map<Object, Set<Object>> toAdd, Set<Object> toUpdate, Object child) {
+ Object parent= getParent(child);
+ while (parent != null) {
+ if (insertChild(parent, child)) {
+ if (toAdd != null)
+ insertInto(parent, child, toAdd);
+ } else {
+ if (toUpdate != null)
+ toUpdate.add(parent);
+ return;
+ }
+ child= parent;
+ parent= getParent(child);
+ }
+ if (insertChild(getSearchResult(), child)) {
+ if (toAdd != null)
+ insertInto(getSearchResult(), child, toAdd);
+ }
+ }
+
+ private boolean insertChild(Object parent, Object child) {
+ return insertInto(parent, child, fChildrenMap);
+ }
+
+ private boolean insertInto(Object parent, Object child, Map<Object, Set<Object>> map) {
+ Set<Object> children= map.get(parent);
+ if (children == null) {
+ children= new HashSet<>();
+ map.put(parent, children);
+ }
+ return children.add(child);
+ }
+
+ protected void remove(Set<Object> toRemove, Set<Object> toUpdate, Object element) {
+ // precondition here: fResult.getMatchCount(child) <= 0
+
+ if (hasChildren(element)) {
+ if (toUpdate != null)
+ toUpdate.add(element);
+ } else {
+ if (getPage().getDisplayedMatchCount(element) == 0) {
+ fChildrenMap.remove(element);
+ Object parent= getParent(element);
+ if (parent != null) {
+ if (removeFromSiblings(element, parent)) {
+ remove(toRemove, toUpdate, parent);
+ }
+ } else {
+ if (removeFromSiblings(element, getSearchResult())) {
+ if (toRemove != null)
+ toRemove.add(element);
+ }
+ }
+ } else {
+ if (toUpdate != null) {
+ toUpdate.add(element);
+ }
+ }
+ }
+ }
+
+ /**
+ * Tries to remove the given element from the list of stored siblings.
+ *
+ * @param element potential child
+ * @param parent potential parent
+ * @return returns true if it really was a remove (i.e. element was a child of parent).
+ */
+ private boolean removeFromSiblings(Object element, Object parent) {
+ Set<Object> siblings= fChildrenMap.get(parent);
+ if (siblings != null) {
+ return siblings.remove(element);
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public Object[] getElements(Object inputElement) {
+ return getChildren(inputElement);
+ }
+
+ @Override
+ public Object getParent(Object element) {
+ return null;
+ }
+
+ protected final Object[] EMPTY_ARR= new Object[0];
+
+ @Override
+ public Object[] getChildren(Object parentElement) {
+ Set<Object> children= fChildrenMap.get(parentElement);
+ if (children == null)
+ return EMPTY_ARR;
+ int limit= getPage().getElementLimit().intValue();
+ if (limit != -1 && limit < children.size()) {
+ Object[] limitedArray= new Object[limit];
+ Iterator<Object> iterator= children.iterator();
+ for (int i= 0; i < limit; i++) {
+ limitedArray[i]= iterator.next();
+ }
+ return limitedArray;
+ }
+
+ return children.toArray();
+ }
+
+ @Override
+ public boolean hasChildren(Object element) {
+ Set<Object> children= fChildrenMap.get(element);
+ return children != null && !children.isEmpty();
+ }
+
+ @Override
+ public String getText(Object element) {
+ SymbolReference ref = (SymbolReference) element;
+ return ref.referrer.toString();
+ }
+
+ }
+
+ public static class SCLSearchResultLabelProvider implements ILabelProvider, IColorProvider, IStyledLabelProvider {
+
+ @Override
+ public void addListener(ILabelProviderListener listener) {
+
+ }
+
+ @Override
+ public void dispose() {
+
+ }
+
+ @Override
+ public boolean isLabelProperty(Object element, String property) {
+ return true;
+ }
+
+ @Override
+ public void removeListener(ILabelProviderListener listener) {
+
+ }
+
+ @Override
+ public StyledString getStyledText(Object element) {
+ SymbolReference ref = (SymbolReference) element;
+ return new StyledString(ref.referrer.toString()); //+ " " + ref.referred + " " + ref.referenceLocation);
+ }
+
+ @Override
+ public Color getForeground(Object element) {
+ return null;
+ }
+
+ @Override
+ public Color getBackground(Object element) {
+ return null;
+ }
+
+ @Override
+ public Image getImage(Object element) {
+ return null;
+ }
+
+ @Override
+ public String getText(Object element) {
+ return null;
+ }
+
+ }
+}