| /*=============================================================================# |
| # Copyright (c) 2013, 2021 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.element.LtkModelElement; |
| 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 LtkModelElement modelElement= ((IAdaptable) object).getAdapter(LtkModelElement.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; |
| } |
| |
| } |