/* This file belongs to the Servoy development and deployment environment, Copyright (C) 1997-2010 Servoy BV This program is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program; if not, see http://www.gnu.org/licenses or write to the Free Software Foundation,Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 */ package com.servoy.j2db.scripting; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.eclipse.dltk.rhino.dbgp.ContextualScope; import org.mozilla.javascript.Function; import org.mozilla.javascript.NativeJavaArray; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.Wrapper; import com.servoy.j2db.FormController.RuntimeSupportScriptProviders; import com.servoy.j2db.IFormController; import com.servoy.j2db.persistence.AggregateVariable; import com.servoy.j2db.persistence.Column; import com.servoy.j2db.persistence.FlattenedForm; import com.servoy.j2db.persistence.Form; import com.servoy.j2db.persistence.IScriptProvider; import com.servoy.j2db.persistence.ISupportScriptProviders; import com.servoy.j2db.persistence.Relation; import com.servoy.j2db.persistence.RepositoryException; import com.servoy.j2db.persistence.ScriptCalculation; import com.servoy.j2db.persistence.ScriptMethod; import com.servoy.j2db.persistence.ScriptVariable; import com.servoy.j2db.persistence.Table; import com.servoy.j2db.util.Debug; import com.servoy.j2db.util.Utils; /** * @author jcompagner, jblok */ public class FormScope extends ScriptVariableScope implements Wrapper, ContextualScope { private volatile IFormController _fp; private volatile LazyCompilationScope[] extendScopes; public FormScope(IFormController fp, ISupportScriptProviders[] extendsHierarchy) { super(fp.getApplication().getScriptEngine().getSolutionScope(), fp.getApplication().getScriptEngine(), extendsHierarchy[0]); _fp = fp; putWithoutFireChange("_formname_", fp.getName()); //$NON-NLS-1$ this.extendScopes = new LazyCompilationScope[extendsHierarchy.length - 1]; LazyCompilationScope previous = null; for (int i = extendScopes.length; --i >= 0;) { extendScopes[i] = new ExtendsScope(previous, scriptEngine, extendsHierarchy[i + 1]); extendScopes[i].setPrototype(previous); extendScopes[i].setFunctionParentScriptable(this); previous = extendScopes[i]; } } @Override public String getScopeName() { return _fp.getName(); } public void createVars() { //put all vars in scope if getForm is flattenform, it will return overridden scriptvars in correct order, sub wins Iterator<ScriptVariable> it = _fp.getForm().getScriptVariables(false); while (it.hasNext()) { ScriptVariable var = it.next(); put(var); } } /** * @see com.servoy.j2db.scripting.LazyCompilationScope#getFunctionSuper(com.servoy.j2db.persistence.IScriptProvider) */ @Override protected Scriptable getFunctionSuper(IScriptProvider sp) { if (extendScopes.length > 0) { for (int i = 0; i < extendScopes.length; i++) { if (extendScopes[i].getScriptLookup().getScriptMethod(sp.getID()) != null) { if (i + 1 < extendScopes.length) { // the super scope is the next extend scope return extendScopes[i + 1]; } else { // if this was the last extendScope then the script doesn't have a super return null; } } } // if not found then the script method resides in the sup (FlattenedForm) itself // return the first extend scope. return extendScopes[0]; } return super.getFunctionSuper(sp); } /** * @see com.servoy.j2db.scripting.LazyCompilationScope#toString() */ @Override public String toString() { return "FormScope: " + _fp.getName();// + " extendsHierarchy: " + Arrays.toString(extendScopes); //$NON-NLS-1$ } @Override public String getFunctionName(Integer id) { String name = super.getFunctionName(id); if (name == null) { for (LazyCompilationScope element : extendScopes) { name = element.getFunctionName(id); if (name != null) break; } } return name; } public IFormController getFormController() { return _fp; } /** * @see com.servoy.j2db.scripting.LazyCompilationScope#reload() */ @Override public void reload() { super.reload(); for (LazyCompilationScope extendScope : extendScopes) { extendScope.reload(); } } @Override public void destroy() { _fp = null; extendScopes = null; setPrototype(null); super.destroy(); } @Override public Object get(String name, Scriptable start) { if (_fp == null) return NOT_FOUND; _fp.touch(); if ("alldataproviders".equals(name)) //$NON-NLS-1$ { List<String> al = new ArrayList<String>(); Table table = (Table)_fp.getTable(); if (table != null) { al = getDataproviderIdList(table); } return new NativeJavaArray(this, al.toArray(new String[al.size()])); } if ("allmethods".equals(name)) //$NON-NLS-1$ { List<String> al = new ArrayList<String>(); Iterator<ScriptMethod> it = _fp.getForm().getScriptMethods(true); while (it.hasNext()) { ScriptMethod sm = it.next(); al.add(sm.getName()); } return new NativeJavaArray(this, al.toArray(new String[al.size()])); } if ("allrelations".equals(name)) //$NON-NLS-1$ { List<String> al = getFormRelationsIdList(_fp.getForm()); return new NativeJavaArray(this, al.toArray(new String[al.size()])); } if ("allvariables".equals(name)) //$NON-NLS-1$ { List<String> al = getAllVariablesIdList(_fp.getForm()); return new NativeJavaArray(this, al.toArray(new String[al.size()])); } Object object = super.get(name, start); if ((object == null || object == Scriptable.NOT_FOUND) && ("foundset".equals(name) || "elements".equals(name))) { Debug.error(Thread.currentThread().getName() + ": For form " + _fp + " the foundset/elements were asked for but that was not (or was no longer) set. " + (this == _fp.getFormScope()), new RuntimeException()); if (name.equals("foundset")) return _fp.getFormModel(); } return object; } private List<String> getDataproviderIdList(Table table) { List<String> al = new ArrayList<String>(); if (table == null) return al; Iterator<Column> columns = table.getColumnsSortedByName(); try { while (columns.hasNext()) { al.add(columns.next().getDataProviderID()); } Iterator<AggregateVariable> aggs; aggs = _fp.getApplication().getFlattenedSolution().getAggregateVariables(table, true); while (aggs.hasNext()) { al.add(aggs.next().getDataProviderID()); } Iterator<ScriptCalculation> calcs = _fp.getApplication().getFlattenedSolution().getScriptCalculations(table, true); while (calcs.hasNext()) { al.add(calcs.next().getDataProviderID()); } } catch (RepositoryException e) { Debug.error(e); } return al; } List<String> getFormRelationsIdList(Form form) { List<String> al = new ArrayList<String>(); try { Iterator<Relation> it = _fp.getApplication().getFlattenedSolution().getRelations( _fp.getApplication().getFoundSetManager().getTable(form.getDataSource()), true, true); while (it.hasNext()) { Relation r = it.next(); if (!r.isGlobal()) { al.add(r.getName()); } } } catch (RepositoryException e) { Debug.error(e); } return al; } List<String> getAllVariablesIdList(Form form) { List<String> al = new ArrayList<String>(); try { Iterator<ScriptVariable> itScriptVariable = form.getScriptVariables(false); while (itScriptVariable.hasNext()) { al.add(itScriptVariable.next().getName()); } } catch (Exception ex) { Debug.error(ex); } return al; } /* * (non-Javadoc) * * @see com.servoy.j2db.scripting.DefaultScope#hasInstance(org.mozilla.javascript.Scriptable) */ @Override public boolean hasInstance(Scriptable instance) { if (instance instanceof FormScope) { Form instanceForm = ((FormScope)instance).getFormController().getForm(); Form thisForm = getFormController().getForm(); if (thisForm.equals(instanceForm)) return true; if (instanceForm instanceof FlattenedForm) { if (thisForm instanceof FlattenedForm) { thisForm = ((FlattenedForm)thisForm).getForm(); } return ((FlattenedForm)instanceForm).getAllForms().contains(thisForm); } } return false; } /** * @see org.mozilla.javascript.Scriptable#has(String, Scriptable) */ @Override public boolean has(String name, Scriptable start) { if ("allnames".equals(name) || "alldataproviders".equals(name) || "allrelations".equals(name) || "allmethods".equals(name) || "allvariables".equals(name)) return true; //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ return super.has(name, start); } public Object unwrap() { return _fp; } private class ExtendsScope extends LazyCompilationScope implements Wrapper { public ExtendsScope(LazyCompilationScope parent, IExecutingEnviroment scriptEngine, ISupportScriptProviders methodLookup) { super(parent, scriptEngine, methodLookup); } /** * @see com.servoy.j2db.scripting.LazyCompilationScope#get(java.lang.String, org.mozilla.javascript.Scriptable) */ @Override public Object get(String name, Scriptable start) { Object object = super.get(name, start); if (object == Scriptable.NOT_FOUND && getFunctionParentScriptable() != null) { Object obj = getFunctionParentScriptable().get(name, start); // only return form variables not functions, they should be resolved by the (compile)scope. if (obj instanceof Function) return Scriptable.NOT_FOUND; return obj; } return object; } /** * @see com.servoy.j2db.scripting.LazyCompilationScope#getFunctionSuper(com.servoy.j2db.persistence.IScriptProvider) */ @Override protected Scriptable getFunctionSuper(IScriptProvider sp) { return FormScope.this.getFunctionSuper(sp); } /* * (non-Javadoc) * * @see org.mozilla.javascript.Wrapper#unwrap() */ public Object unwrap() { return _fp; } /* * (non-Javadoc) * * @see com.servoy.j2db.scripting.LazyCompilationScope#getScopeName() */ @Override public String getScopeName() { return _fp.getFormScope().getScopeName(); } } @Override public void remove(ScriptVariable var) { ScriptVariable newVar = null; for (ScriptVariable loopVar : Utils.iterate(getScriptLookup().getScriptVariables(false))) { if (loopVar.getName().equals(var.getName())) { newVar = loopVar; break; } } if (newVar == null) { super.remove(var); } else { Form form = getFormController().getForm(); Form deletedVarForm = (Form)var.getParent(); Form newVarForm = (Form)newVar.getParent(); while (form != null) { // when the deleted form var is found first in the hiearchy if (form.getName().equals(deletedVarForm.getName())) { // then remove this var, and put the new one in. super.remove(var); put(newVar); break; } else if (form.getName().equals(newVarForm.getName())) { // else if the newVarForm is still found first (then it is already the current one) return; } form = form.getExtendsForm(); } } } /* * (non-Javadoc) * * @see com.servoy.j2db.scripting.ScriptVariableScope#put(java.lang.String, org.mozilla.javascript.Scriptable, java.lang.Object) */ @Override public void put(String name, Scriptable arg1, Object value) { if (name.equals("foundset")) //$NON-NLS-1$ { throw new RuntimeException("Setting of foundset object is not possible on form: " + _fp.getName()); //$NON-NLS-1$ } super.put(name, arg1, value); } /** * Returns only relevant servoy scriptables from form scope */ @Override public Scriptable getContextScriptable() { Scriptable ret = new DefaultScope(this) { @Override public String getClassName() { return "LocalFormContext"; //$NON-NLS-1$ } @Override public Object[] getIds() { Object[] array = new Object[allVars.size() + allIndex.size()]; int counter = 0; for (String string : allVars.keySet()) { array[counter++] = string; } for (Integer integer : allIndex.keySet()) { array[counter++] = integer; } return array; } }; if (this.get("controller", this) != null) ret.put("controller", ret, this.get("controller", this)); if (this.get("foundset", this) != null) ret.put("foundset", ret, this.get("foundset", this)); //put dataproviders, relations, and form variables List<String> localIDs = getDataproviderIdList((Table)_fp.getTable()); localIDs.addAll(getFormRelationsIdList(_fp.getForm())); localIDs.addAll(getAllVariablesIdList(_fp.getForm())); for (String string : localIDs) { if (this.get(string, this) != null) ret.put(string, ret, this.get(string, this)); } return ret; } public void updateProviderswithCopy(Form originalForm, Form copyForm) { RuntimeSupportScriptProviders thisScope = (RuntimeSupportScriptProviders)getScriptLookup(); thisScope.updateProviderwithCopy(originalForm, copyForm); for (LazyCompilationScope scope : extendScopes) { RuntimeSupportScriptProviders runtimeScriptProvider = (RuntimeSupportScriptProviders)scope.getScriptLookup(); runtimeScriptProvider.updateProviderwithCopy(originalForm, copyForm); } } }