blob: 3c21cec6ec8853c76be6699df6013dbd1a6fd647 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2013, 2020 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.r.ui.util;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.statet.jcommons.lang.NonNull;
import org.eclipse.statet.jcommons.lang.NonNullByDefault;
import org.eclipse.statet.jcommons.lang.Nullable;
import org.eclipse.statet.ecommons.models.core.util.BasicElementProxy;
import org.eclipse.statet.ecommons.models.core.util.ElementPartition;
import org.eclipse.statet.ecommons.models.core.util.ElementPartitionFactory;
import org.eclipse.statet.ltk.model.core.elements.IModelElement;
import org.eclipse.statet.r.console.core.RProcessREnvironment;
import org.eclipse.statet.r.core.data.CombinedRElement;
import org.eclipse.statet.r.core.data.CombinedRList;
import org.eclipse.statet.rj.data.RObject;
import org.eclipse.statet.rj.data.RReference;
@NonNullByDefault
public class RElementInputContentProvider<TInput extends RElementInput<?>> implements ITreeContentProvider {
public static @Nullable CombinedRElement getCombinedRElement(final Object object) {
if (object instanceof CombinedRElement) {
return (CombinedRElement) object;
}
if (object instanceof IAdaptable) {
final IModelElement modelElement= ((IAdaptable) object).getAdapter(IModelElement.class);
if (modelElement instanceof CombinedRElement) {
return (CombinedRElement) modelElement;
}
}
return null;
}
private static final Object[] NO_CHILDREN= new Object[0];
static class RElementPartition extends BasicElementProxy implements ElementPartition {
private final RElementInputContentProvider<?>.PartitionFactory.PartitionHandle partition;
public RElementPartition(final CombinedRElement value,
final RElementInputContentProvider<?>.PartitionFactory.PartitionHandle partition) {
super(value);
this.partition= partition;
}
@Override
public long getPartitionStart() {
return this.partition.getStart();
}
@Override
public long getPartitionLength() {
return this.partition.getLength();
}
@Override
public int hashCode() {
return super.hashCode() ^ this.partition.hashCode();
}
@Override
public boolean equals(final @Nullable Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof RElementPartition) {
return (super.equals(obj)
&& this.partition.equals(((RElementPartition) obj).partition) );
}
return false;
}
}
private class PartitionFactory extends ElementPartitionFactory<Object, CombinedRList> {
public PartitionFactory() {
super(Object.class, DEFAULT_PART_SIZE);
}
@Override
protected RElementPartition createPartition(final CombinedRList value, final PartitionHandle partition) {
return new RElementPartition(value, partition);
}
@Override
protected Object[] getChildren(final CombinedRList value, final long start, final int length) {
if (RElementInputContentProvider.this.activeInput.hasEnvFilter()
&& value instanceof RProcessREnvironment) {
final Object[] all= RElementInputContentProvider.this.activeInput.getEnvChildren(value);
if (start == 0 && length == all.length) {
return all;
}
final Object[] children= new @NonNull Object[length];
System.arraycopy(all, (int) start, children, 0, length);
return children;
}
if (start == 0 && length == value.getLength()) {
return value.getModelChildren(null).toArray();
}
final Object[] children= new @NonNull Object[length];
for (int i= 0; i < length; i++) {
children[i]= value.get(start + i);
}
return children;
}
}
private final PartitionFactory partitionFactory= new PartitionFactory();
private @Nullable TInput activeInput;
/** References used by the viewer. Use only in UI thread */
private Set<RReference> usedReferences= new HashSet<>();
public RElementInputContentProvider() {
}
@Override
public void dispose() {
}
public void setInput(final @Nullable TInput input) {
this.activeInput= input;
}
public @Nullable TInput getInput() {
return this.activeInput;
}
@Override
public void inputChanged(final Viewer viewer, final Object oldInput, final Object newInput) {
}
@Override
public Object[] getElements(final Object inputElement) {
final Object[] elements;
if (this.activeInput != null && (elements= this.activeInput.getRootElements()) != null) {
return elements;
}
return NO_CHILDREN;
}
@Override
public boolean hasChildren(final Object element) {
if (element instanceof RElementPartition) {
return true;
}
// if (element instanceof TreeElement) {
// final TreeElement treeElement= (TreeElement) element;
// if (treeElement.children != null) {
// return (treeElement.children.length > 0);
// }
// return hasChildren(treeElement, getCombinedRElement(element));
// }
return hasChildren((CombinedRElement) element);
}
@Override
public Object[] getChildren(final Object element) {
if (element instanceof RElementPartition) {
return ((RElementPartition) element).partition.getElements(
(CombinedRList) getCombinedRElement(element) );
}
return getChildren((CombinedRElement) element);
}
private boolean hasChildren(final CombinedRElement rElement) {
switch (rElement.getRObjectType()) {
case RObject.TYPE_DATAFRAME:
case RObject.TYPE_LIST:
return (rElement.getLength() > 0);
case RObject.TYPE_ENVIRONMENT:
if (this.activeInput.hasEnvFilter()) {
return (this.activeInput.getEnvChildren(rElement).length > 0);
}
return (rElement.getLength() > 0);
case RObject.TYPE_REFERENCE: {
final RObject realObject= ((RReference) rElement).getResolvedRObject();
if (realObject != null) {
this.usedReferences.add((RReference) rElement);
return hasChildren((CombinedRElement) realObject);
}
return false; }
case RObject.TYPE_S4OBJECT:
return rElement.hasModelChildren(this.activeInput.getOtherFilter());
default:
return false;
}
}
private Object[] getChildren(final CombinedRElement rElement) {
switch (rElement.getRObjectType()) {
case RObject.TYPE_DATAFRAME:
return rElement.getModelChildren(null).toArray();
case RObject.TYPE_LIST:
if (rElement.hasModelChildren(null)) {
return this.partitionFactory.getElements((CombinedRList) rElement, rElement.getLength());
}
return NO_CHILDREN;
case RObject.TYPE_ENVIRONMENT:
if (this.activeInput.hasEnvFilter()) {
final Object[] children= this.activeInput.getEnvChildren(rElement);
if (children.length > 5000) {
return this.partitionFactory.getElements((CombinedRList) rElement, children.length);
}
return children;
}
if (rElement.getLength() > 5000 && rElement.hasModelChildren(null)) {
return this.partitionFactory.getElements((CombinedRList) rElement, rElement.getLength());
}
return rElement.getModelChildren(null).toArray();
case RObject.TYPE_REFERENCE: {
final RObject realObject= ((RReference) rElement).getResolvedRObject();
if (realObject != null) {
return getChildren((CombinedRElement) realObject);
}
return NO_CHILDREN; }
case RObject.TYPE_S4OBJECT:
return rElement.getModelChildren(this.activeInput.getOtherFilter()).toArray();
default:
return NO_CHILDREN;
}
}
@Override
public @Nullable Object getParent(final Object element) {
return null;
}
public Set<RReference> resetUsedReferences() {
if (this.usedReferences.isEmpty()) {
return Collections.emptySet();
}
final Set<RReference> previousReferences= this.usedReferences;
this.usedReferences= new HashSet<>();
return previousReferences;
}
public Set<RReference> getUsedReferences() {
return this.usedReferences;
}
}