| /******************************************************************************* |
| * Copyright (c) 2012, 2019 Willink Transformations and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v2.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v20.html |
| * |
| * Contributors: |
| * E.D.Willink - initial API and implementation (inspired by Horacio Hoyos' prototype) |
| ******************************************************************************/ |
| package org.eclipse.qvtd.pivot.qvtimperative.evaluation; |
| |
| import java.util.List; |
| |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.jdt.annotation.NonNull; |
| import org.eclipse.jdt.annotation.Nullable; |
| import org.eclipse.ocl.pivot.Import; |
| import org.eclipse.ocl.pivot.OCLExpression; |
| import org.eclipse.ocl.pivot.Property; |
| import org.eclipse.ocl.pivot.Type; |
| import org.eclipse.ocl.pivot.VariableDeclaration; |
| import org.eclipse.ocl.pivot.ids.CollectionTypeId; |
| import org.eclipse.ocl.pivot.internal.evaluation.BasicEvaluationVisitor; |
| import org.eclipse.ocl.pivot.utilities.ClassUtil; |
| import org.eclipse.ocl.pivot.utilities.NameUtil; |
| import org.eclipse.ocl.pivot.utilities.PivotUtil; |
| import org.eclipse.ocl.pivot.utilities.ValueUtil; |
| import org.eclipse.ocl.pivot.values.CollectionValue; |
| import org.eclipse.ocl.pivot.values.InvalidValueException; |
| import org.eclipse.qvtd.pivot.qvtbase.BaseModel; |
| import org.eclipse.qvtd.pivot.qvtbase.Domain; |
| import org.eclipse.qvtd.pivot.qvtbase.Function; |
| import org.eclipse.qvtd.pivot.qvtbase.FunctionParameter; |
| import org.eclipse.qvtd.pivot.qvtbase.Pattern; |
| import org.eclipse.qvtd.pivot.qvtbase.Predicate; |
| import org.eclipse.qvtd.pivot.qvtbase.Rule; |
| import org.eclipse.qvtd.pivot.qvtbase.Transformation; |
| import org.eclipse.qvtd.pivot.qvtbase.TypedModel; |
| import org.eclipse.qvtd.pivot.qvtimperative.AddStatement; |
| import org.eclipse.qvtd.pivot.qvtimperative.AppendParameter; |
| import org.eclipse.qvtd.pivot.qvtimperative.AppendParameterBinding; |
| import org.eclipse.qvtd.pivot.qvtimperative.BufferStatement; |
| import org.eclipse.qvtd.pivot.qvtimperative.CheckStatement; |
| import org.eclipse.qvtd.pivot.qvtimperative.ConnectionVariable; |
| import org.eclipse.qvtd.pivot.qvtimperative.DeclareStatement; |
| import org.eclipse.qvtd.pivot.qvtimperative.EntryPoint; |
| import org.eclipse.qvtd.pivot.qvtimperative.GuardParameter; |
| import org.eclipse.qvtd.pivot.qvtimperative.GuardParameterBinding; |
| import org.eclipse.qvtd.pivot.qvtimperative.ImperativeModel; |
| import org.eclipse.qvtd.pivot.qvtimperative.ImperativeTransformation; |
| import org.eclipse.qvtd.pivot.qvtimperative.ImperativeTypedModel; |
| import org.eclipse.qvtd.pivot.qvtimperative.LoopParameterBinding; |
| import org.eclipse.qvtd.pivot.qvtimperative.LoopVariable; |
| import org.eclipse.qvtd.pivot.qvtimperative.Mapping; |
| import org.eclipse.qvtd.pivot.qvtimperative.MappingCall; |
| import org.eclipse.qvtd.pivot.qvtimperative.MappingLoop; |
| import org.eclipse.qvtd.pivot.qvtimperative.MappingParameter; |
| import org.eclipse.qvtd.pivot.qvtimperative.MappingParameterBinding; |
| import org.eclipse.qvtd.pivot.qvtimperative.MappingStatement; |
| import org.eclipse.qvtd.pivot.qvtimperative.NewStatement; |
| import org.eclipse.qvtd.pivot.qvtimperative.ObservableStatement; |
| import org.eclipse.qvtd.pivot.qvtimperative.SetStatement; |
| import org.eclipse.qvtd.pivot.qvtimperative.SimpleParameter; |
| import org.eclipse.qvtd.pivot.qvtimperative.SimpleParameterBinding; |
| import org.eclipse.qvtd.pivot.qvtimperative.Statement; |
| import org.eclipse.qvtd.pivot.qvtimperative.VariableStatement; |
| import org.eclipse.qvtd.pivot.qvtimperative.utilities.QVTimperativeUtil; |
| import org.eclipse.qvtd.runtime.evaluation.Connection; |
| import org.eclipse.qvtd.runtime.evaluation.Interval; |
| import org.eclipse.qvtd.runtime.evaluation.InvocationConstructor; |
| import org.eclipse.qvtd.runtime.evaluation.InvocationFailedException; |
| import org.eclipse.qvtd.runtime.evaluation.ModeFactory; |
| |
| import com.google.common.collect.Iterables; |
| |
| /** |
| * QVTimperativeEvaluationVisitor is the class for ... |
| */ |
| public class QVTiEvaluationVisitor extends BasicEvaluationVisitor implements IQVTiEvaluationVisitor |
| { |
| protected final @NonNull QVTiExecutor executor; // FIXME fold into templated context |
| |
| /** |
| * Instantiates a new qV tcore evaluation visitor impl. |
| */ |
| public QVTiEvaluationVisitor(@NonNull QVTiExecutor executor) { |
| super(executor); |
| this.executor = executor; |
| } |
| |
| private @Nullable Object doConnectionAccumulation(@NonNull ConnectionVariable targetVariable, @NonNull OCLExpression valueExpression) { |
| try { |
| Object targetValue = ClassUtil.nonNullState(executor.getValueOf(targetVariable)); |
| Connection connectionCollection = (Connection) targetValue; |
| Object values = valueExpression.accept(undecoratedVisitor); |
| assert values != null; |
| // if (values instanceof Iterable<?>) { |
| // CollectionValue valuesCollection = ValueUtil.asCollectionValue(values); |
| // for (Object value : valuesCollection) { |
| // connectionCollection.append(value); |
| // } |
| // } |
| // else { |
| connectionCollection.appendElement(values); |
| // } |
| return connectionCollection; |
| } |
| catch (RuntimeException e) { |
| executor.replace(targetVariable, e, false); |
| throw e; |
| } |
| } |
| |
| private @Nullable Object doMappingCall(@NonNull MappingCall mappingCall) { |
| Mapping referredMapping = mappingCall.getReferredMapping(); |
| if (referredMapping == null) { |
| return null; |
| } |
| Iterable<@NonNull MappingParameterBinding> mappingParameterBindings = QVTimperativeUtil.getOwnedMappingParameterBindings(mappingCall); |
| @NonNull Object @NonNull [] boundValues = new @NonNull Object[Iterables.size(mappingParameterBindings)]; |
| int index = 0; |
| for (@NonNull MappingParameterBinding binding : mappingParameterBindings) { |
| VariableDeclaration boundVariable = binding.getBoundVariable(); |
| if (boundVariable == null) { |
| return null; |
| } |
| if (binding instanceof AppendParameterBinding) { // FIXME visit the bindings |
| AppendParameterBinding appendParameterBinding = (AppendParameterBinding)binding; |
| ConnectionVariable value = appendParameterBinding.getValue(); |
| if (value == null) { |
| return null; |
| } |
| Object valueOrValues = executor.getValueOf(value); |
| if (valueOrValues == null) { |
| return null; |
| } |
| boundValues[index++] = valueOrValues; |
| } |
| else if (binding instanceof GuardParameterBinding) { |
| Type varType = boundVariable.getType(); |
| if (varType == null) { |
| return null; |
| } |
| GuardParameterBinding guardParameterBinding = (GuardParameterBinding)binding; |
| ConnectionVariable value = guardParameterBinding.getValue(); |
| if (value == null) { |
| return null; |
| } |
| Object valueOrValues = executor.getValueOf(value); |
| if (valueOrValues == null) { |
| return null; |
| } |
| Type valueType = idResolver.getDynamicTypeOf(valueOrValues); |
| if (!guardParameterBinding.isIsCheck() || valueType.conformsTo(environmentFactory.getStandardLibrary(), varType)) { |
| boundValues[index++] = valueOrValues; |
| } |
| else { |
| return null; |
| } |
| } |
| else if (binding instanceof LoopParameterBinding) { |
| Type varType = boundVariable.getType(); |
| if (varType == null) { |
| return null; |
| } |
| LoopParameterBinding guardParameterBinding = (LoopParameterBinding)binding; |
| LoopVariable value = guardParameterBinding.getValue(); |
| if (value == null) { |
| return null; |
| } |
| Object valueOrValues = executor.getValueOf(value); |
| if (valueOrValues == null) { |
| return null; |
| } |
| Type valueType = idResolver.getDynamicTypeOf(valueOrValues); |
| if (!guardParameterBinding.isIsCheck() || valueType.conformsTo(environmentFactory.getStandardLibrary(), varType)) { |
| boundValues[index++] = valueOrValues; |
| } |
| else { |
| return null; |
| } |
| } |
| else if (binding instanceof SimpleParameterBinding) { |
| Type varType = boundVariable.getType(); |
| if (varType == null) { |
| return null; |
| } |
| SimpleParameterBinding simpleParameterBinding = (SimpleParameterBinding)binding; |
| OCLExpression value = simpleParameterBinding.getValue(); |
| if (value == null) { |
| return null; |
| } |
| Object valueOrValues = value.accept(undecoratedVisitor); |
| if (valueOrValues == null) { |
| return null; |
| } |
| assert !(boundVariable instanceof ConnectionVariable); |
| // boundValues[index++] = valueOrValues; |
| |
| // } |
| // else { |
| Type valueType = idResolver.getDynamicTypeOf(valueOrValues); |
| if (!simpleParameterBinding.isIsCheck() || valueType.conformsTo(environmentFactory.getStandardLibrary(), varType)) { |
| boundValues[index++] = valueOrValues; |
| } |
| else { |
| return null; |
| } |
| // } |
| } |
| } |
| return executor.internalExecuteMappingCall(mappingCall, boundValues, undecoratedVisitor); |
| } |
| |
| private @Nullable Object doMappingInstall(@NonNull MappingCall mappingCall) { |
| Mapping referredMapping = mappingCall.getReferredMapping(); |
| if (referredMapping == null) { |
| return null; |
| } |
| InvocationConstructor invocationConstructor = executor.getInvocationConstructor(mappingCall, undecoratedVisitor); |
| Iterable<@NonNull MappingParameterBinding> mappingBindings = QVTimperativeUtil.getOwnedMappingParameterBindings(mappingCall); |
| assert mappingBindings != null; |
| for (MappingParameterBinding asMappingCallBinding : mappingBindings) { |
| if (asMappingCallBinding instanceof GuardParameterBinding) { |
| ConnectionVariable connectionVariable = ((GuardParameterBinding)asMappingCallBinding).getValue(); |
| assert connectionVariable != null; |
| Object value = executor.getValueOf(connectionVariable); |
| assert value != null; |
| invocationConstructor.addConsumedConnection((Connection) value); |
| } |
| else if (asMappingCallBinding instanceof AppendParameterBinding) { |
| ConnectionVariable connectionVariable = ((AppendParameterBinding)asMappingCallBinding).getValue(); |
| assert connectionVariable != null; |
| Object value = executor.getValueOf(connectionVariable); |
| assert value != null; |
| invocationConstructor.addAppendedConnection((Connection) value); |
| } |
| } |
| return Boolean.TRUE; |
| } |
| |
| @Override |
| public @Nullable Object visitAddStatement(@NonNull AddStatement connectionStatement) { |
| ConnectionVariable targetVariable = connectionStatement.getTargetVariable() ; |
| if (targetVariable != null) { |
| OCLExpression valueExpression = connectionStatement.getOwnedExpression(); |
| if (valueExpression != null) { |
| doConnectionAccumulation(targetVariable, valueExpression); |
| return true; |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public Object visitAppendParameter(@NonNull AppendParameter object) { |
| CollectionValue.Accumulator accumulator = ValueUtil.createCollectionAccumulatorValue((CollectionTypeId) object.getTypeId()); |
| return executor.replace(object, accumulator, false); |
| } |
| |
| @Override |
| public Object visitAppendParameterBinding(@NonNull AppendParameterBinding object) { |
| return visiting(object); // FIXME |
| } |
| |
| @Override |
| public @Nullable Object visitBaseModel(@NonNull BaseModel object) { |
| return visiting(object); |
| } |
| |
| /* @Override |
| public Object visitOppositePropertyCallExp(@NonNull OppositePropertyCallExp oppositePropertyCallExp) { |
| QVTiModelManager modelManager = (QVTiModelManager) executor.getModelManager(); |
| Integer cacheIndex = modelManager.getTransformationAnalysis().getCacheIndex(oppositePropertyCallExp); |
| if (cacheIndex == null) { |
| return super.visitOppositePropertyCallExp(oppositePropertyCallExp); |
| } |
| Object sourceValue = null; |
| OCLExpression source = oppositePropertyCallExp.getOwnedSource(); |
| if (source != null) { |
| sourceValue = source.accept(undecoratedVisitor); |
| if (sourceValue != null) { |
| Object middleOpposite = modelManager.getUnnavigableOpposite(cacheIndex, sourceValue); |
| if (middleOpposite == null) { |
| throw new NotReadyValueException("Missing opposite value"); |
| } |
| return ClassUtil.nonNullState(middleOpposite); |
| } |
| } |
| throw new InvalidValueException("Failed to evaluate '" + oppositePropertyCallExp.getReferredProperty() + "'", sourceValue, oppositePropertyCallExp); |
| } */ |
| |
| @Override |
| public Object visitBufferStatement(@NonNull BufferStatement object) { |
| Mapping asMapping = QVTimperativeUtil.getContainingMapping(object); |
| Interval interval = executor.getInterval(asMapping); |
| Connection connection = null; |
| OCLExpression ownedExpression = object.getOwnedExpression(); |
| String name = object.getName(); |
| assert name != null; |
| ModeFactory incrementalMode = ModeFactory.NON_INCREMENTAL; // ?? FIXME get from AbstractTransformationInternal |
| if (ownedExpression != null) { |
| Object initValue = ownedExpression.accept(undecoratedVisitor); |
| connection = interval.createConnection(name, ownedExpression.getTypeId(), object.isIsStrict(), incrementalMode); |
| if (initValue != null) { |
| for (Object value : (Iterable<?>)initValue) { |
| assert value != null; |
| connection.appendElement(value); |
| } |
| } |
| } |
| else { |
| connection = interval.createConnection(name, object.getTypeId(), object.isIsStrict(), incrementalMode); |
| } |
| return executor.replace(object, connection, false); |
| } |
| |
| @Override |
| public @Nullable Object visitCheckStatement(@NonNull CheckStatement predicate) { |
| // Each predicate has a conditionExpression that is an OCLExpression |
| OCLExpression exp = predicate.getOwnedExpression(); |
| // The predicated is visited with a nested environment |
| Object expResult = exp.accept(undecoratedVisitor); |
| return expResult; |
| } |
| |
| @Override |
| public Object visitConnectionVariable(@NonNull ConnectionVariable object) { |
| return visiting(object); |
| } |
| |
| @Override |
| public @Nullable Object visitDeclareStatement(@NonNull DeclareStatement asStatement) { |
| Object initValue; |
| OCLExpression ownedExpression = asStatement.getOwnedExpression(); |
| if (ownedExpression == null) { |
| initValue = null; |
| } |
| else { |
| initValue = ownedExpression.accept(undecoratedVisitor); |
| if (asStatement.isIsCheck()) { |
| Type guardType = asStatement.getType(); |
| Type valueType = idResolver.getDynamicTypeOf(initValue); |
| if ((guardType == null) || !valueType.conformsTo(standardLibrary, guardType)) { |
| // The initialization fails, the guard is not met |
| return false; |
| } |
| } |
| } |
| return executor.replace(asStatement, initValue, false); |
| } |
| |
| @Override |
| public @Nullable Object visitDomain(@NonNull Domain object) { |
| return visiting(object); |
| } |
| |
| @Override |
| public @Nullable Object visitEntryPoint(@NonNull EntryPoint entryPoint) { |
| return visitMapping(entryPoint); |
| } |
| |
| @Override |
| public @Nullable Object visitFunction(@NonNull Function object) { |
| return visiting(object); |
| } |
| |
| @Override |
| public @Nullable Object visitFunctionParameter(@NonNull FunctionParameter object) { |
| return visiting(object); |
| } |
| |
| @Override |
| public @Nullable Object visitGuardParameter(@NonNull GuardParameter object) { |
| return visiting(object); |
| } |
| |
| @Override |
| public Object visitGuardParameterBinding(@NonNull GuardParameterBinding object) { |
| return visiting(object); // FIXME |
| } |
| |
| @Override |
| public @Nullable Object visitImperativeModel(@NonNull ImperativeModel imperativeModel) { |
| for (org.eclipse.ocl.pivot.Package pkge : imperativeModel.getOwnedPackages()) { |
| pkge.accept(undecoratedVisitor); |
| } |
| return true; |
| } |
| |
| @Override |
| public @Nullable Object visitImperativeTransformation(@NonNull ImperativeTransformation transformation) { |
| return executor.internalExecuteTransformation(transformation, undecoratedVisitor); |
| } |
| |
| @Override |
| public @Nullable Object visitImperativeTypedModel(@NonNull ImperativeTypedModel object) { |
| return visiting(object); |
| } |
| |
| @Override |
| public @Nullable Object visitImport(@NonNull Import object) { |
| return visiting(object); |
| } |
| |
| @Override |
| public Object visitLoopParameterBinding(@NonNull LoopParameterBinding object) { |
| return visiting(object); // FIXME |
| } |
| |
| @Override |
| public @Nullable Object visitLoopVariable(@NonNull LoopVariable object) { |
| return visiting(object); |
| } |
| |
| @Override |
| public @Nullable Object visitMapping(@NonNull Mapping mapping) { |
| return executor.internalExecuteMapping(mapping, undecoratedVisitor); |
| } |
| |
| @Override |
| public @Nullable Object visitMappingCall(@NonNull MappingCall mappingCall) { |
| if (mappingCall.isIsInstall()) { |
| return doMappingInstall(mappingCall); |
| } |
| else { |
| return doMappingCall(mappingCall); |
| } |
| } |
| |
| @Override |
| public @Nullable Object visitMappingLoop(@NonNull MappingLoop mappingLoop) { |
| Object inValues = mappingLoop.getOwnedExpression().accept(undecoratedVisitor); |
| if (inValues instanceof Connection) { |
| inValues = ((Connection)inValues).typedIterable(Object.class); |
| } |
| if (inValues instanceof Iterable<?>) { |
| List<LoopVariable> iterators = mappingLoop.getOwnedIterators(); |
| if (iterators.size() > 0) { |
| LoopVariable iterator = ClassUtil.nonNullState(iterators.get(0)); |
| for (Object object : (Iterable<?>)inValues) { |
| if (!executor.replace(iterator, object, false)) { |
| return false; |
| } |
| for (@NonNull MappingStatement mappingStatement : ClassUtil.nullFree(mappingLoop.getOwnedMappingStatements())) { |
| context.pushEvaluationEnvironment(mappingStatement, mappingLoop); |
| try { |
| mappingStatement.accept(undecoratedVisitor); |
| } |
| finally { |
| context.popEvaluationEnvironment(); |
| } |
| } |
| } |
| executor.getInvocationManager().flush(); |
| } |
| } |
| return true; |
| } |
| |
| @Override |
| public @Nullable Object visitMappingParameter(@NonNull MappingParameter object) { |
| return visiting(object); // MappingParameter is abstract |
| } |
| |
| @Override |
| public @Nullable Object visitMappingParameterBinding(@NonNull MappingParameterBinding object) { |
| return visiting(object); // MappingCallBinding is serviced by the parent MappingCall |
| } |
| |
| @Override |
| public @Nullable Object visitMappingStatement(@NonNull MappingStatement object) { |
| return visitStatement(object); // MappingStatement is abstract |
| } |
| |
| @Override |
| public @Nullable Object visitNewStatement(@NonNull NewStatement newStatement) { |
| return executor.internalExecuteNewStatement(newStatement, undecoratedVisitor) != null; |
| } |
| |
| @Override |
| public @Nullable Object visitObservableStatement(@NonNull ObservableStatement object) { |
| return visiting(object); |
| } |
| |
| /* @Override |
| public Object visitOppositePropertyCallExp(@NonNull OppositePropertyCallExp oppositePropertyCallExp) { |
| QVTiModelManager modelManager = (QVTiModelManager) executor.getModelManager(); |
| Integer cacheIndex = modelManager.getTransformationAnalysis().getCacheIndex(oppositePropertyCallExp); |
| if (cacheIndex == null) { |
| return super.visitOppositePropertyCallExp(oppositePropertyCallExp); |
| } |
| Object sourceValue = null; |
| OCLExpression source = oppositePropertyCallExp.getOwnedSource(); |
| if (source != null) { |
| sourceValue = source.accept(undecoratedVisitor); |
| if (sourceValue != null) { |
| Object middleOpposite = modelManager.getUnnavigableOpposite(cacheIndex, sourceValue); |
| if (middleOpposite == null) { |
| throw new NotReadyValueException("Missing opposite value"); |
| } |
| return ClassUtil.nonNullState(middleOpposite); |
| } |
| } |
| throw new InvalidValueException("Failed to evaluate '" + oppositePropertyCallExp.getReferredProperty() + "'", sourceValue, oppositePropertyCallExp); |
| } */ |
| |
| @Override |
| public @Nullable Object visitPackage(org.eclipse.ocl.pivot.@NonNull Package pkge) { |
| return true; |
| } |
| |
| @Override |
| public @Nullable Object visitPattern(@NonNull Pattern object) { |
| return visiting(object); |
| } |
| |
| @Override |
| public @Nullable Object visitPredicate(@NonNull Predicate object) { |
| return visiting(object); |
| } |
| |
| @Override |
| public @Nullable Object visitRule(@NonNull Rule object) { |
| return visiting(object); |
| } |
| |
| @Override |
| public @Nullable Object visitSetStatement(@NonNull SetStatement setStatement) { |
| VariableDeclaration targetVariable = setStatement.getTargetVariable(); |
| if (targetVariable == null) { |
| throw new InvalidValueException("Undefined variable", null, null, setStatement); |
| } |
| Object slotObject = executor.getValueOf(targetVariable); |
| if (slotObject instanceof InvalidValueException) { |
| throw (InvalidValueException)slotObject; |
| } |
| if (slotObject instanceof EObject) { |
| try { |
| Object boxedValue = setStatement.getOwnedExpression().accept(undecoratedVisitor); |
| Property targetProperty = QVTimperativeUtil.getTargetProperty(setStatement); |
| Class<?> instanceClass = PivotUtil.getEcoreInstanceClass(targetProperty); |
| Object ecoreValue = idResolver.ecoreValueOf(instanceClass, boxedValue); |
| executor.internalExecuteSetStatement(setStatement, slotObject, ecoreValue); |
| return true; |
| } |
| catch (InvocationFailedException e) { |
| executor.internalExecuteSetStatement(setStatement, slotObject, e); // FIXME This leads to an AssertionError in PropertyImpl.initValue |
| // throw e; |
| } |
| // } else if (slotObject == null){ |
| // throw new InvalidValueException("Null source for '" + navigationAssignment.toString() + "'"); |
| } else { |
| throw new IllegalArgumentException("Unsupported " + setStatement.eClass().getName() |
| + " specification. The assignment slot expression '" + setStatement.toString() + "'evaluates to non-ecore value: " + NameUtil.debugFullName(slotObject)); |
| } |
| return true; |
| } |
| |
| @Override |
| public @Nullable Object visitSimpleParameter(@NonNull SimpleParameter object) { |
| return visiting(object); |
| } |
| |
| @Override |
| public Object visitSimpleParameterBinding(@NonNull SimpleParameterBinding object) { |
| return visiting(object); // FIXME |
| } |
| |
| @Override |
| public @Nullable Object visitStatement(@NonNull Statement object) { |
| return visiting(object); |
| } |
| |
| @Override |
| public @Nullable Object visitTransformation(@NonNull Transformation object) { |
| return visiting(object); |
| } |
| |
| @Override |
| public @Nullable Object visitTypedModel(@NonNull TypedModel object) { |
| return visiting(object); |
| } |
| |
| @Override |
| public @Nullable Object visitVariableStatement(@NonNull VariableStatement object) { |
| return visiting(object); |
| } |
| } |