| /*=============================================================================# |
| # Copyright (c) 2010, 2019 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.jcommons.lang.ObjectUtils.nonNullAssert; |
| |
| 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.lang.ObjectUtils; |
| import org.eclipse.statet.jcommons.status.ProgressMonitor; |
| import org.eclipse.statet.jcommons.status.StatusException; |
| |
| import org.eclipse.statet.r.core.data.CombinedRElement; |
| import org.eclipse.statet.r.core.data.RValueFormatter; |
| import org.eclipse.statet.r.core.model.RElementName; |
| import org.eclipse.statet.r.debug.core.IRVariable; |
| import org.eclipse.statet.r.nico.ICombinedRDataAdapter; |
| import org.eclipse.statet.rj.data.RLanguage; |
| import org.eclipse.statet.rj.data.RObject; |
| import org.eclipse.statet.rj.data.UnexpectedRDataException; |
| import org.eclipse.statet.rj.data.impl.RPromiseImpl; |
| import org.eclipse.statet.rj.services.FQRObject; |
| import org.eclipse.statet.rj.services.FQRObjectRef; |
| import org.eclipse.statet.rj.services.RService; |
| import org.eclipse.statet.rj.services.util.dataaccess.LazyRStore; |
| import org.eclipse.statet.rj.services.util.dataaccess.LazyRStore.Fragment; |
| import org.eclipse.statet.rj.ts.core.RToolService; |
| |
| |
| @NonNullByDefault |
| public class RPromiseValue extends RElementVariableValue<CombinedRElement> { |
| |
| |
| private static final RObjectAdapter<RPromiseImpl> ADAPTER= new RObjectAdapter<RPromiseImpl>(RObject.TYPE_PROMISE) { |
| @Override |
| public RPromiseImpl loadObject(final FQRObjectRef ref, final RObject referenceObject, |
| final LazyRStore.Fragment<RPromiseImpl> fragment, |
| final RService r, final ProgressMonitor m) throws StatusException, UnexpectedRDataException { |
| final RObject object; |
| { final FQRObject fqrObject= r.findData(((RLanguage) ref.getName()).getSource(), |
| ref.getEnv(), false, |
| null, getLoadOptions(), RService.DEPTH_INFINITE, |
| m ); |
| object= (fqrObject != null) ? fqrObject.getObject() : null; |
| } |
| |
| return validateObject(object, referenceObject, fragment); |
| } |
| }; |
| |
| |
| private @Nullable LazyRStore<RPromiseImpl> detailObjectStore; |
| private boolean validationFailed; |
| |
| private final RElementVariableCompactStore detailVariables; |
| |
| |
| public RPromiseValue(final RElementVariable variable) { |
| super(variable); |
| |
| this.detailVariables= new RElementVariableCompactStore(2); |
| } |
| |
| |
| public final RObject getRObject() { |
| return this.element; |
| } |
| |
| public final @Nullable RPromiseValue getVariablePreviousValue() { |
| return (RPromiseValue) this.variable.getPreviousValue(); |
| } |
| |
| |
| @Override |
| public String getValueString() throws DebugException { |
| final LazyRStore.Fragment<RPromiseImpl> fragment; |
| synchronized (this) { |
| fragment= getDetailObjectFragment(); |
| if (fragment == null || fragment.getRObject() == null) { |
| return ""; //$NON-NLS-1$ |
| } |
| } |
| |
| final RPromiseImpl rObject= fragment.getRObject(); |
| if (rObject.getExpression() instanceof RLanguage) { |
| final @NonNull RLanguage exprObject= (RLanguage) rObject.getExpression(); |
| if (exprObject.getSource() != null) { |
| final RValueFormatter valueFormatter= getDebugTarget().getValueFormatter(); |
| synchronized (valueFormatter) { |
| valueFormatter.clear(); |
| valueFormatter.append("= "); //$NON-NLS-1$ |
| RLanguageValue.appendValue(valueFormatter, exprObject); |
| return valueFormatter.getString(); |
| } |
| } |
| } |
| return ""; //$NON-NLS-1$ |
| } |
| |
| @Override |
| public String getDetailString() { |
| return (!this.validationFailed) ? "<not yet evaluated>" : ""; |
| } |
| |
| @Override |
| public boolean hasVariables() throws DebugException { |
| return true; |
| } |
| |
| @Override |
| public IVariable[] getVariables() throws DebugException { |
| final RPromiseValue previousValue; |
| synchronized (this.variable) { |
| if (this != this.variable.getCurrentValue()) { |
| return NO_VARIABLES; |
| } |
| previousValue= getVariablePreviousValue(); |
| } |
| |
| final @Nullable IRVariable[] variables= new @Nullable IRVariable[2]; |
| synchronized (this.detailVariables) { |
| this.detailVariables.toArray(variables, 0); |
| if (!ObjectUtils.isAnyNull(variables)) { |
| return (IRVariable[]) variables; |
| } |
| } |
| |
| final LazyRStore.Fragment<RPromiseImpl> fragment; |
| synchronized (this) { |
| fragment= getDetailObjectFragment(); |
| if (fragment == null || fragment.getRObject() == null) { |
| if (this.validationFailed) { |
| return NO_VARIABLES; |
| } |
| throw newRequestLoadDataFailed(); |
| } |
| } |
| synchronized (this.detailVariables) { |
| for (int idx= 0; idx < 2; idx++) { |
| RElementVariable childVariable= this.detailVariables.get(idx); |
| if (childVariable == null) { |
| final RObject dataObject; |
| final String label; |
| switch (idx) { |
| case 0: |
| dataObject= nonNullAssert(fragment.getRObject().getExpression()); |
| label= "expression"; //$NON-NLS-1$ |
| break; |
| case 1: |
| dataObject= nonNullAssert(fragment.getRObject().getEnvironment()); |
| label= "environment"; //$NON-NLS-1$ |
| break; |
| default: |
| throw new IllegalStateException(); |
| } |
| final CombinedRElement childElement= ICombinedRDataAdapter.create(dataObject, |
| RElementName.create(RElementName.MAIN_OTHER, label) ); |
| if (previousValue != null) { |
| childVariable= checkPreviousVariable(previousValue, idx, childElement); |
| } |
| if (childVariable == null) { |
| childVariable= new RElementVariable(childElement, |
| this.variable.getThread(), this.stamp, this.variable ); |
| if (dataObject instanceof RLanguage) { |
| final RLanguageValue childValue= ((RLanguageValue) childVariable.getValue()); |
| childValue.setDataObject((RLanguage) dataObject); |
| } |
| } |
| this.detailVariables.set(idx, childVariable); |
| } |
| variables[idx]= childVariable; |
| } |
| return (IRVariable[]) variables; |
| } |
| } |
| |
| protected @Nullable RElementVariable checkPreviousVariable(final RPromiseValue previousValue, |
| final int idx, final CombinedRElement element) { |
| if (idx >= 0 && idx < previousValue.element.getLength()) { |
| final RElementVariable previousVariable; |
| synchronized (previousValue) { |
| previousVariable= previousValue.detailVariables.clear(idx); |
| } |
| if (previousVariable != null |
| && previousVariable.update(element, this.stamp) ) { |
| return previousVariable; |
| } |
| } |
| return null; |
| } |
| |
| |
| private @Nullable Fragment<RPromiseImpl> getDetailObjectFragment() { |
| if (this.detailObjectStore == null) { |
| this.detailObjectStore= new LazyRStore<>(1, 1, 1, |
| new RDataLoader<RPromiseImpl>() { |
| @Override |
| protected RPromiseImpl doLoad(final FQRObjectRef ref, |
| final Fragment<RPromiseImpl> fragment, |
| final RToolService r, final ProgressMonitor m) throws StatusException, UnexpectedRDataException { |
| try { |
| return ADAPTER.loadObject(ref, getRObject(), fragment, r, m); |
| } |
| catch (final UnexpectedRDataException e) { |
| RPromiseValue.this.validationFailed= true; |
| throw e; |
| } |
| } |
| }); |
| } |
| return this.detailObjectStore.getFragment(0, 0, 0, null); |
| } |
| |
| } |