| /*=============================================================================# |
| # 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); |
| } |
| |
| } |