/*=============================================================================# # Copyright (c) 2013-2016 Stephan Wahlbrink (WalWare.de) and others. # All rights reserved. This program and the accompanying materials # are made available under the terms of the Eclipse Public License v1.0 # which accompanies this distribution, and is available at # http://www.eclipse.org/legal/epl-v10.html # # Contributors: # Stephan Wahlbrink - initial API and implementation #=============================================================================*/ package de.walware.statet.r.internal.objectbrowser; 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 de.walware.ecommons.ltk.core.model.IModelElement; import de.walware.ecommons.models.core.util.ElementPartitionFactory; import de.walware.ecommons.models.core.util.ElementProxy; import de.walware.ecommons.models.core.util.IElementPartition; import de.walware.rj.data.RObject; import de.walware.rj.data.RReference; import de.walware.statet.r.console.core.RWorkspace.ICombinedREnvironment; import de.walware.statet.r.console.core.RWorkspace.ICombinedRList; import de.walware.statet.r.core.data.ICombinedRElement; class ContentProvider implements ITreeContentProvider { static ICombinedRElement getCombinedRElement(final Object object) { if (object instanceof ICombinedRElement) { return (ICombinedRElement) object; } if (object instanceof IAdaptable) { final IModelElement modelElement = (IModelElement) ((IAdaptable) object).getAdapter(IModelElement.class); if (modelElement instanceof ICombinedRElement) { return (ICombinedRElement) modelElement; } } return null; } private static final Object[] NO_CHILDREN = new Object[0]; static class ElementPartition extends ElementProxy implements IElementPartition { private final PartitionFactory.PartitionHandle partition; public ElementPartition(final ICombinedRElement value, final 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 Object obj) { if (this == obj) { return true; } if (!(obj instanceof ElementPartition)) { return false; } return (super.equals(obj) && this.partition.equals(((ElementPartition) obj).partition) ); } } private class PartitionFactory extends ElementPartitionFactory<Object, ICombinedRList> { public PartitionFactory() { super(Object.class, DEFAULT_PART_SIZE); } @Override protected ElementPartition createPartition(final ICombinedRList value, final PartitionHandle partition) { return new ElementPartition(value, partition); } @Override protected Object[] getChildren(final ICombinedRList value, final long start, final int length) { if (ContentProvider.this.activeInput.hasEnvFilter() && value instanceof ICombinedREnvironment) { final Object[] all = ContentProvider.this.activeInput.getEnvFilterChildren(value); if (start == 0 && length == all.length) { return all; } final Object[] children = new 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 Object[length]; for (int i = 0; i < length; i++) { children[i] = value.get(start + i); } return children; } } private final PartitionFactory partitionFactory = new PartitionFactory(); private ContentInput activeInput; /** References used by the viewer. Use only in UI thread */ private Set<RReference> usedReferences= new HashSet<>(); public ContentProvider() { } @Override public void dispose() { } public void setInput(final ContentInput input) { this.activeInput = input; } public ContentInput getInput() { return this.activeInput; } @Override public void inputChanged(final Viewer viewer, final Object oldInput, final Object newInput) { } @Override public Object[] getElements(final Object inputElement) { if (this.activeInput != null && this.activeInput.rootElements != null) { return this.activeInput.rootElements; } return NO_CHILDREN; } @Override public boolean hasChildren(final Object element) { if (element instanceof ElementPartition) { 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((ICombinedRElement) element); } @Override public Object[] getChildren(final Object element) { if (element instanceof ElementPartition) { return ((ElementPartition) element).partition.getElements( (ICombinedRList) getCombinedRElement(element) ); } return getChildren((ICombinedRElement) element); } private boolean hasChildren(final ICombinedRElement rElement) { switch (rElement.getRObjectType()) { case RObject.TYPE_DATAFRAME: case RObject.TYPE_LIST: return (rElement.getLength() > 0); case RObject.TYPE_ENV: if (this.activeInput.hasEnvFilter()) { return (this.activeInput.getEnvFilterChildren(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((ICombinedRElement) realObject); } return false; } case RObject.TYPE_S4OBJECT: return rElement.hasModelChildren(this.activeInput.otherFilter); default: return false; } } private Object[] getChildren(final ICombinedRElement 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((ICombinedRList) rElement, rElement.getLength()); } return NO_CHILDREN; case RObject.TYPE_ENV: if (this.activeInput.hasEnvFilter()) { final Object[] children = this.activeInput.getEnvFilterChildren(rElement); if (children.length > 5000) { return this.partitionFactory.getElements((ICombinedRList) rElement, children.length); } return children; } if (rElement.getLength() > 5000 && rElement.hasModelChildren(null)) { return this.partitionFactory.getElements((ICombinedRList) rElement, rElement.getLength()); } return rElement.getModelChildren(null).toArray(); case RObject.TYPE_REFERENCE: { final RObject realObject = ((RReference) rElement).getResolvedRObject(); if (realObject != null) { return getChildren((ICombinedRElement) realObject); } return NO_CHILDREN; } case RObject.TYPE_S4OBJECT: return rElement.getModelChildren(this.activeInput.otherFilter).toArray(); default: return NO_CHILDREN; } } @Override public 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; } }