/*
* Copyright (c) 2012, 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.internal.search.ui.text;
import com.google.dart.tools.search.internal.ui.text.FileMatch;
import com.google.dart.tools.search.internal.ui.text.FileResource;
import com.google.dart.tools.search.internal.ui.text.FileResourceMatch;
import com.google.dart.tools.search.internal.ui.text.FileSearchQuery;
import com.google.dart.tools.search.internal.ui.text.FileSearchResult;
import com.google.dart.tools.search.internal.ui.text.IFileSearchContentProvider;
import com.google.dart.tools.search.internal.ui.text.LineElement;
import com.google.dart.tools.search.ui.ISearchQuery;
import com.google.dart.tools.search.ui.text.AbstractTextSearchResult;
import com.google.dart.tools.search.ui.text.FileTextSearchScope;
import com.google.dart.tools.search.ui.text.Match;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.jface.viewers.AbstractTreeViewer;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.Viewer;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class FileTreeContentProvider implements ITreeContentProvider, IFileSearchContentProvider {
private final Object[] EMPTY_ARR = new Object[0];
private AbstractTextSearchResult fResult;
private FileSearchPage fPage;
private AbstractTreeViewer fTreeViewer;
private Map<Object, Set<Object>> fChildrenMap;
private ArrayList<File> externalRoots;
public FileTreeContentProvider(FileSearchPage page, AbstractTreeViewer viewer) {
fPage = page;
fTreeViewer = viewer;
}
@Override
public void clear() {
initialize(fResult);
fTreeViewer.refresh();
}
@Override
public void dispose() {
// nothing to do
}
@Override
public synchronized void elementsChanged(Object[] updatedElements) {
for (int i = 0; i < updatedElements.length; i++) {
if (!(updatedElements[i] instanceof LineElement)) {
// change events to elements are reported in file search
if (fResult.getMatchCount(updatedElements[i]) > 0) {
insert(updatedElements[i], true);
} else {
remove(updatedElements[i], true);
}
} else {
// change events to line elements are reported in text search
LineElement lineElement = (LineElement) updatedElements[i];
int nMatches = lineElement.getNumberOfMatches(fResult);
if (nMatches > 0) {
if (hasChild(lineElement.getParent(), lineElement)) {
fTreeViewer.update(new Object[] {lineElement, lineElement.getParent()}, null);
} else {
insert(lineElement, true);
}
} else {
remove(lineElement, true);
}
}
}
}
@Override
public Object[] getChildren(Object parentElement) {
Set<Object> children = fChildrenMap.get(parentElement);
if (children == null) {
return EMPTY_ARR;
}
return children.toArray();
}
@Override
public Object[] getElements(Object inputElement) {
Object[] children = getChildren(inputElement);
int elementLimit = getElementLimit();
if (elementLimit != -1 && elementLimit < children.length) {
Object[] limitedChildren = new Object[elementLimit];
System.arraycopy(children, 0, limitedChildren, 0, elementLimit);
return limitedChildren;
}
return children;
}
@Override
public Object getParent(Object element) {
if (element instanceof IProject) {
return null;
}
if (element instanceof File) {
File file = (File) element;
if (file.isDirectory()) {
return null;
}
return getExternalRoot(file);
}
if (element instanceof IResource) {
IResource resource = (IResource) element;
return resource.getParent();
}
if (element instanceof LineElement) {
return ((LineElement) element).getParent();
}
if (element instanceof FileMatch) {
FileMatch match = (FileMatch) element;
return match.getLineElement();
}
if (element instanceof FileResource<?>) {
return ((FileResource<?>) element).getResource();
}
return null;
}
@Override
public boolean hasChildren(Object element) {
return getChildren(element).length > 0;
}
@Override
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
if (newInput instanceof FileSearchResult) {
initialize((FileSearchResult) newInput);
}
}
private int getElementLimit() {
return fPage.getElementLimit().intValue();
}
private Object getExternalRoot(File file) {
while (file != null) {
file = file.getParentFile();
if (externalRoots.contains(file)) {
break;
}
}
return file;
}
private boolean hasChild(Object parent, Object child) {
Set<Object> children = fChildrenMap.get(parent);
return children != null && children.contains(child);
}
private boolean hasMatches(Object element) {
if (element instanceof LineElement) {
LineElement lineElement = (LineElement) element;
return lineElement.getNumberOfMatches(fResult) > 0;
}
return fResult.getMatchCount(element) > 0;
}
private synchronized void initialize(AbstractTextSearchResult result) {
fResult = result;
fChildrenMap = new HashMap<Object, Set<Object>>();
initializeExternalRoots(result);
boolean showLineMatches = showLineMatches();
if (result != null) {
Object[] elements = result.getElements();
for (int i = 0; i < elements.length; i++) {
if (showLineMatches) {
Match[] matches = result.getMatches(elements[i]);
for (int j = 0; j < matches.length; j++) {
insert(((FileResourceMatch) matches[j]).getLineElement(), false);
}
} else {
insert(elements[i], false);
}
}
}
}
private void initializeExternalRoots(AbstractTextSearchResult result) {
externalRoots = new ArrayList<File>();
ISearchQuery query = result.getQuery();
if (query instanceof FileSearchQuery) {
FileSearchQuery fileQuery = (FileSearchQuery) query;
FileTextSearchScope searchScope = fileQuery.getSearchScope();
for (File root : searchScope.getExternalRoots()) {
externalRoots.add(root);
}
}
}
private void insert(Object child, boolean refreshViewer) {
Object parent = getParent(child);
while (parent != null) {
if (insertChild(parent, child)) {
if (refreshViewer) {
fTreeViewer.add(parent, child);
}
} else {
if (refreshViewer) {
fTreeViewer.refresh(parent);
}
return;
}
child = parent;
parent = getParent(child);
}
if (insertChild(fResult, child)) {
if (refreshViewer) {
fTreeViewer.add(fResult, child);
}
}
}
/**
* Adds the child to the parent.
*
* @param parent the parent
* @param child the child
* @return <code>true</code> if this set did not already contain the specified element
*/
private boolean insertChild(Object parent, Object child) {
Set<Object> children = fChildrenMap.get(parent);
if (children == null) {
children = new HashSet<Object>();
fChildrenMap.put(parent, children);
}
return children.add(child);
}
private void remove(Object element, boolean refreshViewer) {
// precondition here: fResult.getMatchCount(child) <= 0
if (hasChildren(element)) {
if (refreshViewer) {
fTreeViewer.refresh(element);
}
} else {
if (!hasMatches(element)) {
fChildrenMap.remove(element);
Object parent = getParent(element);
if (parent != null) {
removeFromSiblings(element, parent);
remove(parent, refreshViewer);
} else {
removeFromSiblings(element, fResult);
if (refreshViewer) {
fTreeViewer.refresh();
}
}
} else {
if (refreshViewer) {
fTreeViewer.refresh(element);
}
}
}
}
private void removeFromSiblings(Object element, Object parent) {
Set<Object> siblings = fChildrenMap.get(parent);
if (siblings != null) {
siblings.remove(element);
}
}
private boolean showLineMatches() {
//TODO(pquitslund): line matches are not shown (by design)
return false;
// return !((FileSearchQuery) fResult.getQuery()).isFileNameSearch();
}
}