| /******************************************************************************* |
| * Copyright (c) 2018 Agence spatiale canadienne / Canadian Space Agency |
| * 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: |
| * Mathieu Larose (Savoir-faire Linux) - Initial API and implementation |
| * |
| * SPDX-License-Identifier: EPL-1.0 |
| * |
| *******************************************************************************/ |
| package org.eclipse.apogy.core.programs.javascript; |
| |
| import java.lang.reflect.Method; |
| |
| import org.eclipse.apogy.common.emf.AbstractFeatureListNode; |
| import org.eclipse.apogy.common.emf.ApogyCommonEMFFacade; |
| import org.eclipse.apogy.common.emf.ApogyCommonEMFFactory; |
| import org.eclipse.apogy.common.emf.ListFeatureNode; |
| import org.eclipse.apogy.common.emf.ListRootNode; |
| import org.eclipse.apogy.core.invocator.ApogyCoreInvocatorFactory; |
| import org.eclipse.apogy.core.invocator.TypeMember; |
| import org.eclipse.apogy.core.invocator.Variable; |
| import org.eclipse.apogy.core.invocator.VariableFeatureReference; |
| import org.eclipse.emf.ecore.EAttribute; |
| import org.eclipse.emf.ecore.EOperation; |
| import org.eclipse.emf.ecore.EReference; |
| import org.eclipse.emf.ecore.EStructuralFeature; |
| import org.eclipse.emf.ecore.util.EcoreUtil; |
| import org.mozilla.javascript.Scriptable; |
| import org.mozilla.javascript.ScriptableObject; |
| |
| /** |
| * Proxy for an Apogy variable. |
| * |
| */ |
| public class VariableProxy extends ScriptableObject { |
| private static final long serialVersionUID = 1L; |
| private VariableFeatureReference variableFeatureReference; |
| private Variable variable; |
| private JavaScriptProgram program; |
| |
| public VariableProxy() { |
| // Rhino requires an empty constructor |
| } |
| |
| /** |
| * |
| * @param variable Apogy variable |
| * @param program JavaScriptProgram being executed |
| * @throws NoSuchMethodException |
| * @throws SecurityException |
| */ |
| public VariableProxy(Variable variable, JavaScriptProgram program) throws NoSuchMethodException, SecurityException { |
| this.variable = variable; |
| this.program = program; |
| init(); |
| } |
| |
| private void init() throws NoSuchMethodException, SecurityException { |
| this.variableFeatureReference = ApogyCoreInvocatorFactory.eINSTANCE.createVariableFeatureReference(); |
| this.variableFeatureReference.setVariable(this.variable); |
| |
| for (EOperation op : ApogyCommonEMFFacade.INSTANCE |
| .getAllAvailableEOperations(this.variable.getVariableType().getInterfaceClass())) { |
| defineProperty(op.getName(), new OperationCallInvoker(this.program, this.variableFeatureReference, op), 0); |
| } |
| |
| for (EStructuralFeature structuralFeature : this.variable.getVariableType().getInterfaceClass() |
| .getEAllStructuralFeatures()) { |
| if (structuralFeature instanceof EReference) { |
| ReferenceGetter referenceGetter = new ReferenceGetter(structuralFeature); |
| Method getter = ReferenceGetter.class.getDeclaredMethod("get", new Class[] { Scriptable.class }); |
| defineProperty(structuralFeature.getName(), referenceGetter, getter, null, 0); |
| } else if (structuralFeature instanceof EAttribute) { |
| AttributeGetter attributeGetter = new AttributeGetter(this.variableFeatureReference, structuralFeature); |
| Method getter = AttributeGetter.class.getDeclaredMethod("get", new Class[] { Scriptable.class }); |
| defineProperty(structuralFeature.getName(), attributeGetter, getter, null, 0); |
| } |
| } |
| |
| for (TypeMember typeMember : this.variable.getVariableType().getMembers()) { |
| recursiveInit(typeMember); |
| } |
| } |
| |
| private void recursiveInit(TypeMember typeMember) throws NoSuchMethodException, SecurityException { |
| // If the property already exists and is read-only, defineProperty |
| // is ignored. |
| // |
| // Delete the property in case it already exists and is |
| // read-only (which is likely the case since we defined read-only |
| // properties for all EReferences in the loop above). |
| delete(typeMember.getName()); |
| |
| TypeMemberGetter typeMemberGetter = new TypeMemberGetter(typeMember); |
| Method getter = TypeMemberGetter.class.getDeclaredMethod("get", new Class[] { Scriptable.class }); |
| defineProperty(typeMember.getName(), typeMemberGetter, getter, null, 0); |
| |
| for (TypeMember subMember : typeMember.getMemberType().getMembers()) { |
| recursiveInit(subMember); |
| } |
| } |
| |
| @Override |
| public String getClassName() { |
| return "Proxy"; |
| } |
| |
| public String getName() { |
| return this.variable.getName(); |
| } |
| |
| /** |
| * Gets a reference of the EObject wrapped by {@link VariableProxy} |
| * |
| */ |
| private class ReferenceGetter { |
| private final EStructuralFeature feature; |
| |
| public ReferenceGetter(EStructuralFeature feature) { |
| this.feature = feature; |
| } |
| |
| /** |
| * Returns the reference (represented by this class) of the EObject (represented |
| * by {@VariableProxy}) |
| * |
| * @param self The JavaScript object |
| * @return the attribute |
| */ |
| @SuppressWarnings("unused") |
| public Object get(Scriptable self) throws NoSuchMethodException, SecurityException { |
| VariableFeatureReference subVariableFeatureReference = VariableFeatureReferenceUtil |
| .clone(VariableProxy.this.variableFeatureReference); |
| |
| ListRootNode listRootNode = ApogyCommonEMFFactory.eINSTANCE.createListRootNode(); |
| listRootNode.setSourceClass(VariableProxy.this.variable.getVariableType().getInterfaceClass()); |
| subVariableFeatureReference.setFeatureRoot(listRootNode); |
| |
| AbstractFeatureListNode leaf = ApogyCommonEMFFacade.INSTANCE |
| .getLeaf(subVariableFeatureReference.getFeatureRoot()); |
| ListFeatureNode node = ApogyCommonEMFFactory.eINSTANCE.createListFeatureNode(); |
| node.setStructuralFeature(this.feature); |
| leaf.setChild(node); |
| |
| Scriptable object = new ReferenceProxy(subVariableFeatureReference, VariableProxy.this.program); |
| object.setParentScope(VariableProxy.this); |
| return object; |
| } |
| } |
| |
| /** |
| * Gets a type member of the EObject wrapped by {@link @VariableProxy} |
| * |
| */ |
| private class TypeMemberGetter { |
| private final TypeMember typeMember; |
| |
| public TypeMemberGetter(TypeMember typeMember) { |
| this.typeMember = typeMember; |
| } |
| |
| /** |
| * Returns the type member (represented by this class) of the EObject |
| * (represented by {@VariableProxy}) |
| * |
| * @param self The JavaScript object |
| * @return the attribute |
| */ |
| @SuppressWarnings("unused") |
| public Object get(Scriptable self) throws NoSuchMethodException, SecurityException { |
| VariableFeatureReference subVariableFeatureReference = EcoreUtil |
| .copy(VariableProxy.this.variableFeatureReference); |
| |
| VariableFeatureReferenceUtil util = new VariableFeatureReferenceUtil(); |
| util.createTypeMemberHierarchy(subVariableFeatureReference, this.typeMember); |
| |
| TypeMemberProxy object = new TypeMemberProxy(subVariableFeatureReference, VariableProxy.this.program); |
| |
| object.setParentScope(VariableProxy.this); |
| return object; |
| } |
| |
| } |
| } |