/*
* Copyright 2010 The Rabbit Eclipse Plug-in Project
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* 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 rabbit.ui.internal.viewers;
import rabbit.ui.IProvider;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Sets.newLinkedHashSet;
import com.google.common.collect.ImmutableList;
import org.eclipse.jface.viewers.ITreePathContentProvider;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.Viewer;
import static java.util.Collections.emptyList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Observable;
import java.util.Set;
import javax.annotation.Nullable;
/**
* This type of content provider takes a collection of leaf {@link TreePath}s
* from a {@link ITreePathBuilder} each time the input changes and returns
* elements/children/parents based on the leaves.
* <ul>
* <li>
* The root elements of the input will be the distinct first elements of all the
* leaves.</li>
* <li>
* The children of a tree path {@code p} will be the distinct elements at index
* {@code p.getSegmentCount()} of all the leaves that has A as their parent
* path, where {@code leaf.getSegmentCount() > p.getSegmentCount()}</li>
* <li>
* The possible parents of a child will be sub paths of all the leaves that
* contain the child as one of their segments. The range of a sub path will be
* from segment 0 up to but exclude the child segment.</li>
* </ul>
*/
public final class TreePathContentProvider
extends Observable implements ITreePathContentProvider, IProvider<TreePath> {
private static final TreePath[] EMPTY = {};
private final ITreePathBuilder builder;
/**
* Immutable list of leaves, the list may be empty but never null.
*/
private List<TreePath> leaves;
/**
* Constructs a content provider.
*
* @param builder the builder to builder data from input element.
* @throws NullPointerException if {@code builder == null}.
*/
public TreePathContentProvider(ITreePathBuilder builder) {
this.builder = checkNotNull(builder, "builder");
this.leaves = emptyList();
}
@Override
public void dispose() {
leaves = emptyList();
}
/**
* Gets the current tree leaves in use by this content provider.
* @return the leaves.
*/
@Override
public Collection<TreePath> get() {
return leaves;
}
@Override
public Object[] getChildren(@Nullable TreePath branch) {
return childrenOf(branch).toArray();
}
/**
* A {@link TreePathContentProvider} will always return an array of elements
* from the latest input, regardless of what the parameter of this method is.
*
* @param inputElement the input element, this parameter is ignored by this
* content provider.
*/
@Override
public Object[] getElements(@Nullable Object inputElement) {
Set<Object> elements = newLinkedHashSet();
for (TreePath leaf : leaves) {
if (leaf.getFirstSegment() != null) {
elements.add(leaf.getFirstSegment());
}
}
return elements.toArray();
}
@Override
public TreePath[] getParents(@Nullable Object element) {
if (element == null) {
return EMPTY;
}
Set<TreePath> parents = newLinkedHashSet();
for (TreePath leaf : leaves) {
int index = TreePaths.indexOf(leaf, element);
if (index < 0) {
continue;
}
parents.add(TreePaths.headPath(leaf, index));
}
return parents.toArray(new TreePath[parents.size()]);
}
@Override
public boolean hasChildren(@Nullable TreePath branch) {
if (branch == null) {
return false;
}
for (TreePath leaf : leaves) {
if (leaf.getSegmentCount() > branch.getSegmentCount()
&& leaf.startsWith(branch, null)) {
return true;
}
}
return false;
}
/**
* When input changes, the internal data of this content provider will be
* updated.
*/
@Override
public void inputChanged(
Viewer viewer,
@Nullable Object oldInput,
@Nullable Object newInput) {
leaves = ImmutableList.copyOf(builder.build(newInput));
setChanged();
notifyObservers();
}
/**
* Gets the children of the given parent.
* @param branch the parent.
* @return a collection of children, or an empty collection if no children.
*/
private Set<Object> childrenOf(@Nullable TreePath branch) {
if (branch == null) {
return Collections.emptySet();
}
Set<Object> children = newLinkedHashSet();
for (TreePath leaf : leaves) {
if (leaf.getSegmentCount() > branch.getSegmentCount()
&& leaf.startsWith(branch, null)) {
children.add(leaf.getSegment(branch.getSegmentCount()));
}
}
return children;
}
}