blob: 356c4b847a22acb54943f127e54ac7bdee4c0aea [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2010, 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.internal.r.debug.core.model;
import static org.eclipse.statet.internal.r.debug.core.model.RElementVariable.DEFAULT_FRAGMENT_COUNT;
import java.util.Objects;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.model.IVariable;
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.jcommons.status.ProgressMonitor;
import org.eclipse.statet.jcommons.status.StatusException;
import org.eclipse.statet.ecommons.debug.core.model.VariablePartitionFactory;
import org.eclipse.statet.r.core.data.CombinedRElement;
import org.eclipse.statet.r.core.data.RValueFormatter;
import org.eclipse.statet.r.core.data.RValueValidator;
import org.eclipse.statet.r.debug.core.IRVariable;
import org.eclipse.statet.rj.data.RStore;
import org.eclipse.statet.rj.data.RVector;
import org.eclipse.statet.rj.data.UnexpectedRDataException;
import org.eclipse.statet.rj.services.FQRObjectRef;
import org.eclipse.statet.rj.services.util.dataaccess.LazyRStore;
import org.eclipse.statet.rj.services.util.dataaccess.LazyRStore.Fragment;
import org.eclipse.statet.rj.services.util.dataaccess.RDataAssignment;
import org.eclipse.statet.rj.services.util.dataaccess.RVectorDataAdapter;
import org.eclipse.statet.rj.ts.core.RToolService;
@NonNullByDefault
public class RVectorValue extends RElementVariableValue<CombinedRElement> implements IRIndexValueInternal {
private static final RVectorDataAdapter ADAPTER= new RVectorDataAdapter();
public static final int LOAD_SIZE= 1000;
final long length;
private @Nullable LazyRStore<RVector<?>> namesStore;
private @Nullable LazyRStore<RVector<?>> dataStore;
public RVectorValue(final RElementVariable variable) {
super(variable);
final RVector<?> element= getRObject();
this.length= element.getLength();
}
public final RVector<?> getRObject() {
return (RVector<?>) this.element;
}
public boolean hasValueChanged(final long index) {
final RVectorValue previousValue;
synchronized (this.variable) {
if (this != this.variable.getCurrentValue()) {
return false;
}
previousValue= (RVectorValue) this.variable.getPreviousValue();
}
if (previousValue != null) {
if (index >= previousValue.length) {
return true;
}
final LazyRStore.Fragment<RVector<?>> previousFragment;
final LazyRStore.Fragment<RVector<?>> currentFragment;
synchronized (previousValue) {
previousFragment= previousValue.getLoadedDataFragment(index);
if (previousFragment == null || previousFragment.getRObject() == null) {
return false;
}
}
synchronized (this) {
currentFragment= getDataFragment(index);
if (currentFragment == null || currentFragment.getRObject() == null) {
return false;
}
}
return (!Objects.equals(
currentFragment.getRObject().getData().get(
currentFragment.toLocalRowIdx(index) ),
previousFragment.getRObject().getData().get(
previousFragment.toLocalRowIdx(index) )));
}
return false;
}
@Override
public String getValueString() throws DebugException {
if (this.length == 0) {
return ""; //$NON-NLS-1$
}
else if (this.length == 1) {
final String data= getDataExpr(0);
if (data == null) {
throw newRequestLoadDataFailed();
}
return data;
}
else {
return "[" + this.length + ']'; //$NON-NLS-1$
}
}
@Override
public String getDetailString() {
if (this.length == 0) {
return ""; //$NON-NLS-1$
}
else if (this.length == 1) {
final String data= getDataExpr(0);
if (data == null) {
return "<error>"; //$NON-NLS-1$
}
return data;
}
return ""; //$NON-NLS-1$
}
@Override
public boolean hasVariables() throws DebugException {
return (this.length > 1);
}
@Override
public IVariable[] getVariables() throws DebugException {
if (this.length <= 1) {
return NO_VARIABLES;
}
return getPartitionFactory().getVariables(this);
}
@Override
public VariablePartitionFactory<IRIndexElementValue> getPartitionFactory() {
return RElementVariableValue.PARTITION_FACTORY;
}
@Override
public long getSize() throws DebugException {
return this.length;
}
@Override
public IRVariable[] getVariables(final long offset, final int length) {
return getVariables(offset, length, this.variable);
}
@Override
public IRVariable[] getVariables(final long offset, final int length, final IRVariable parent) {
if (this.length <= 1) {
throw new UnsupportedOperationException();
}
if (offset < 0 || length < 0 || offset > this.length - length) {
throw new IllegalArgumentException();
}
final @NonNull IRVariable[] variables= new @NonNull IRVariable[length];
for (int i= 0; i < length; i++) {
variables[i]= new RVectorIndexVariable(this, offset + i, parent);
}
return variables;
}
protected @Nullable String getName(final long idx) {
synchronized (this.variable) {
if (this != this.variable.getCurrentValue()) {
return null;
}
}
final LazyRStore.Fragment<RVector<?>> fragment;
synchronized (this) {
fragment= getNamesFragment(idx);
if (fragment == null || fragment.getRObject() == null) {
return null;
}
}
final RValueFormatter formatter= getDebugTarget().getValueFormatter();
synchronized (formatter) {
return formatter.formatName(
fragment.getRObject().getNames(), (int) fragment.toLocalRowIdx(idx) );
}
}
private @Nullable Fragment<RVector<?>> getNamesFragment(final long idx) {
if (this.namesStore == null) {
this.namesStore= new LazyRStore<>(this.length, 1,
DEFAULT_FRAGMENT_COUNT,
new RDataLoader<RVector<?>>() {
@Override
protected RVector<?> doLoad(final FQRObjectRef ref,
final Fragment<RVector<?>> fragment,
final RToolService r, final ProgressMonitor m) throws StatusException, UnexpectedRDataException {
return ADAPTER.loadRowNames(ref, getRObject(), fragment, null,
r, m );
}
});
}
return this.namesStore.getFragment(idx, 0, 0, null);
}
protected final @Nullable RStore<?> getValueType() {
RStore<?> data= this.element.getData();
if (data.getStoreType() == RStore.FACTOR) {
synchronized (this) {
final Fragment<RVector<?>> fragment= getDataFragmentAny();
if (fragment == null || fragment.getRObject() == null) {
return null;
}
data= fragment.getRObject().getData();
}
}
return data;
}
protected @Nullable String getDataExpr(final long idx) {
synchronized (this.variable) {
if (this != this.variable.getCurrentValue()) {
return null;
}
}
final LazyRStore.Fragment<RVector<?>> fragment;
synchronized (this) {
fragment= getDataFragment(idx);
if (fragment == null || fragment.getRObject() == null) {
return null;
}
}
final RValueFormatter formatter= getDebugTarget().getValueFormatter();
synchronized (formatter) {
return formatter.format(
fragment.getRObject().getData(), (int) fragment.toLocalRowIdx(idx) );
}
}
protected boolean validateDataExpr(final String expression) {
synchronized (this.variable) {
if (this != this.variable.getCurrentValue()) {
return false;
}
}
final RStore<?> type= getValueType();
final RValueValidator validator= getDebugTarget().getValueValidator();
synchronized (validator) {
return validator.isValid(type, expression);
}
}
protected void setDataExpr(final long idx, final String expression) throws DebugException {
synchronized (this.variable) {
if (this != this.variable.getCurrentValue()) {
throw newRequestSetDataFailed();
}
}
final RStore<?> type= getValueType();
final RStore<?> data;
final RValueValidator validator= getDebugTarget().getValueValidator();
synchronized (validator) {
data= validator.toRData(type, expression);
}
if (data == null) {
throw newNotSupported();
}
final RDataAssignment assignment= new RDataAssignment(idx, 0, data);
synchronized (this) {
setData(assignment);
}
}
private LazyRStore<RVector<?>> ensureDataStore() {
if (this.dataStore == null) {
this.dataStore= new LazyRStore<>(this.length, 1,
DEFAULT_FRAGMENT_COUNT,
new RDataLoader<RVector<?>>() {
@Override
protected void doSet(final FQRObjectRef ref,
final RDataAssignment assignment,
final RToolService r, final ProgressMonitor m) throws StatusException, UnexpectedRDataException {
ADAPTER.setData(ref, getRObject(), assignment, null,
r, m );
}
@Override
protected RVector<?> doLoad(final FQRObjectRef ref,
final Fragment<RVector<?>> fragment,
final RToolService r, final ProgressMonitor m) throws StatusException, UnexpectedRDataException {
return ADAPTER.loadData(ref, getRObject(), fragment, null,
r, m );
}
});
}
return this.dataStore;
}
private @Nullable Fragment<RVector<?>> getDataFragment(final long idx) {
return ensureDataStore().getFragment(idx, 0, 0, null);
}
private @Nullable Fragment<RVector<?>> getDataFragmentAny() {
final LazyRStore<RVector<?>> dataStore= ensureDataStore();
Fragment<RVector<?>> fragment= dataStore.getLoadedFragmentAny();
if (fragment == null) {
fragment= dataStore.getLoadedFragment(0, 0);
}
return fragment;
}
private @Nullable Fragment<RVector<?>> getLoadedDataFragment(final long idx) {
return (this.dataStore != null) ?
this.dataStore.getLoadedFragment(idx, 0) :
null;
}
public void setData(final RDataAssignment assignment) {
ensureDataStore().set(assignment, 0, null);
this.variable.fireChangeEvent(DebugEvent.CONTENT);
}
}