/*
This file belongs to the Servoy development and deployment environment, Copyright (C) 1997-2011 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.solutionmodel;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.mozilla.javascript.annotations.JSFunction;
import com.servoy.j2db.FlattenedSolution;
import com.servoy.j2db.FormManager;
import com.servoy.j2db.IApplication;
import com.servoy.j2db.dataprocessing.FoundSetManager;
import com.servoy.j2db.documentation.ServoyDocumented;
import com.servoy.j2db.persistence.AbstractBase;
import com.servoy.j2db.persistence.ICloneable;
import com.servoy.j2db.persistence.IColumnTypes;
import com.servoy.j2db.persistence.IScriptProvider;
import com.servoy.j2db.persistence.RepositoryException;
import com.servoy.j2db.persistence.ScriptCalculation;
import com.servoy.j2db.persistence.ScriptMethod;
import com.servoy.j2db.persistence.ScriptNameValidator;
import com.servoy.j2db.persistence.TableNode;
import com.servoy.j2db.persistence.TypeIterator;
import com.servoy.j2db.scripting.IConstantsObject;
import com.servoy.j2db.scripting.TableScope;
import com.servoy.j2db.solutionmodel.ISMDataSourceNode;
/**
* Solution model holder for calculations and foundset methods.
*
* @author rgansevles
*/
@SuppressWarnings("nls")
@ServoyDocumented(category = ServoyDocumented.RUNTIME)
public class JSDataSourceNode implements IJSScriptParent<TableNode>, IConstantsObject, ISMDataSourceNode
{
private final IApplication application;
private final String dataSource;
public JSDataSourceNode(IApplication application, String dataSource)
{
this.application = application;
this.dataSource = dataSource;
}
public final void checkModification()
{
// no need to create a copy here, a table node copy will be created when needed.
}
@SuppressWarnings("unchecked")
public <T extends IScriptProvider> T getScriptCopy(T script) throws RepositoryException
{
TableNode tableNode = application.getFlattenedSolution().getSolutionCopyTableNode(dataSource);
T sc = AbstractBase.selectByName(new TypeIterator<T>(tableNode.getAllObjects(), script.getTypeID()), script.getName());
if (sc == null)
{
sc = (T)((ICloneable)script).clonePersist();
tableNode.addChild(sc);
}
return sc;
}
/**
* @see com.servoy.j2db.scripting.solutionmodel.IJSParent#getJSParent()
*/
public IJSParent< ? > getJSParent()
{
return null;
}
/**
* Get the data source for this node.
*
* @sample
* var nodeDataSource = solutionModel.getDataSourceNode("db:/example_data/customers").getDataSource();
*
* @return the dataSource
*/
@JSFunction
public String getDataSource()
{
return dataSource;
}
/**
* @see com.servoy.j2db.scripting.solutionmodel.IJSParent#getBaseComponent()
*/
public TableNode getSupportChild()
{
try
{
return application.getFlattenedSolution().getSolutionCopyTableNode(dataSource);
}
catch (RepositoryException e)
{
throw new RuntimeException(e);
}
}
/**
* Get an existing calculation for the datasource node.
*
* @param name The name of the calculation
*
* @sampleas newCalculation(String, int)
*
*/
@JSFunction
public JSCalculation getCalculation(String name)
{
ScriptCalculation scriptCalculation = application.getFlattenedSolution().getScriptCalculation(name, dataSource);
if (scriptCalculation != null)
{
return new JSCalculation(this, scriptCalculation, application, false);
}
return null;
}
/**
* Gets all the calculations for the datasource node.
*
* @sampleas newCalculation(String, int)
*/
@JSFunction
public JSCalculation[] getCalculations()
{
List<JSCalculation> calculations = new ArrayList<JSCalculation>();
Iterator<ScriptCalculation> scriptCalculations = application.getFlattenedSolution().getScriptCalculations(dataSource, true);
while (scriptCalculations.hasNext())
{
calculations.add(new JSCalculation(this, scriptCalculations.next(), application, false));
}
return calculations.toArray(new JSCalculation[calculations.size()]);
}
/**
* Creates a new calculation for the given code, the type will be the column where it could be build on (if name is a column name),
* else it will default to JSVariable.TEXT;
*
* @param code The code of the calculation, this must be a full function declaration.
*
* @sampleas newCalculation(String, int)
*
*/
@JSFunction
public JSCalculation newCalculation(String code)
{
return newCalculation(code, IColumnTypes.TEXT);
}
/**
* Creates a new calculation for the given code and the type, if it builds on a column (name is a column name) then type will be ignored.
*
* @param code The code of the calculation, this must be a full function declaration.
* @param type The type of the calculation, one of the JSVariable types.
*
* @sample
* var calc = solutionModel.getDataSourceNode("db:/example_data/customers").newCalculation("function myCalculation() { return 123; }", JSVariable.INTEGER);
* var calc2 = solutionModel.getDataSourceNode("db:/example_data/customers").newCalculation("function myCalculation2() { return '20'; }");
* var calc3 = solutionModel.getDataSourceNode("db:/example_data/employees").newCalculation("function myCalculation3() { return 'Hello World!'; }", JSVariable.TEXT);
*
* var c = solutionModel.getDataSourceNode("db:/example_data/customers").getCalculation("myCalculation");
* application.output("Name: " + c.getName() + ", Stored: " + c.isStored());
*
* var allCalcs = solutionModel.getDataSourceNode("db:/example_data/customers").getCalculations();
* for (var i = 0; i < allCalcs.length; i++) {
* application.output(allCalcs[i]);
* }
*
*/
@JSFunction
public JSCalculation newCalculation(String code, int type)
{
try
{
FlattenedSolution fs = application.getFlattenedSolution();
TableNode tablenode = fs.getSolutionCopyTableNode(dataSource);
String name = JSMethod.parseName(code);
ScriptCalculation scriptCalculation = tablenode.createNewScriptCalculation(new ScriptNameValidator(fs), name, null);
scriptCalculation.setDeclaration(code);
scriptCalculation.setTypeAndCheck(type, application);
TableScope tableScope = (TableScope)application.getScriptEngine().getTableScope(scriptCalculation.getTable());
if (tableScope != null)
{
tableScope.put(scriptCalculation, scriptCalculation);
((FoundSetManager)application.getFoundSetManager()).flushSQLSheet(dataSource);
}
return new JSCalculation(this, scriptCalculation, application, true);
}
catch (RepositoryException e)
{
throw new RuntimeException(e);
}
}
/**
* Removes the calculation specified by name.
*
* @sample
* var calc1 = solutionModel.getDataSourceNode("db:/example_data/customers").newCalculation("function myCalculation1() { return 123; }", JSVariable.INTEGER);
* var calc2 = solutionModel.getDataSourceNode("db:/example_data/customers").newCalculation("function myCalculation2() { return '20'; }");
*
* var c = solutionModel.getDataSourceNode("db:/example_data/customers").getCalculation("myCalculation1");
* application.output("Name: " + c.getName() + ", Stored: " + c.isStored());
*
* solutionModel.getDataSourceNode("db:/example_data/customers").removeCalculation("myCalculation1");
* c = solutionModel.getDataSourceNode("db:/example_data/customers").getCalculation("myCalculation1");
* if (c != null) {
* application.output("myCalculation could not be removed.");
* }
*
* var allCalcs = solutionModel.getDataSourceNode("db:/example_data/customers").getCalculations();
* for (var i = 0; i < allCalcs.length; i++) {
* application.output(allCalcs[i]);
* }
*
* @param name the name of the calculation to be removed
*
* @return true if the removal was successful, false otherwise
*/
@JSFunction
public boolean removeCalculation(String name)
{
try
{
FlattenedSolution fs = application.getFlattenedSolution();
TableNode tablenode = fs.getSolutionCopyTableNode(dataSource);
ScriptCalculation sc = tablenode.getScriptCalculation(name);
if (sc != null)
{
tablenode.removeChild(sc);
return true;
}
//it is a design time calculation, therefore we "hide" it
sc = fs.getScriptCalculation(name, dataSource);
if (sc != null)
{
fs.addToRemovedPersists(sc);
return true;
}
// not found
return false;
}
catch (RepositoryException e)
{
throw new RuntimeException(e);
}
}
/**
* Creates a new foundset method with the specified code.
*
* @sample
* var method = solutionModel.getDataSourceNode("db:/example_data/orders").newMethod("function doubleSize() { return 2*getSize(); }");
*
* application.output('Doubled orders for this customer: '+customers_to_orders.doubleSize())
*
* @param code the specified code for the foundset method
*
* @return a JSMethod object
*/
@JSFunction
public JSMethod newMethod(String code)
{
try
{
FlattenedSolution fs = application.getFlattenedSolution();
TableNode tablenode = fs.getSolutionCopyTableNode(dataSource);
if (tablenode == null) throw new RuntimeException("Couldnt create method for datasource: " + dataSource);
String name = JSMethod.parseName(code);
ScriptMethod method = tablenode.createNewFoundsetMethod(new ScriptNameValidator(fs), name, null);
method.setDeclaration(code);
((FoundSetManager)application.getFoundSetManager()).reloadFoundsetMethod(dataSource, method);
return new JSMethod(this, method, application, true);
}
catch (RepositoryException e)
{
throw new RuntimeException(e);
}
}
/**
* Get an existing foundset method for the datasource node.
*
* @param name The name of the method
*
* @sampleas newMethod(String)
*
*/
@JSFunction
public JSMethod getMethod(String name)
{
ScriptMethod method = application.getFlattenedSolution().getFoundsetMethod(name, dataSource);
if (method != null)
{
return new JSMethod(this, method, application, false);
}
return null;
}
/**
* Gets all the foundset methods for the datasource node.
*
* @sampleas newMethod(String)
*/
@JSFunction
public JSMethod[] getMethods()
{
List<JSMethod> methods = new ArrayList<JSMethod>();
Iterator<ScriptMethod> fsMethods = application.getFlattenedSolution().getFoundsetMethods(dataSource, true);
while (fsMethods.hasNext())
{
methods.add(new JSMethod(this, fsMethods.next(), application, false));
}
return methods.toArray(new JSMethod[methods.size()]);
}
/**
* Removes the foundset method specified by name.
*
* @sample
* var method1 = solutionModel.getDataSourceNode("db:/example_data/customers").newMethod("function myFoundsetMethod1() { return 123; }");
* var method2 = solutionModel.getDataSourceNode("db:/example_data/customers").newCalculation("function myFoundsetMethod2() { return '20'; }");
*
* var m = solutionModel.getDataSourceNode("db:/example_data/customers").getMethod("myFoundsetMethod1");
* application.output("Name: " + m.getName());
*
* solutionModel.getDataSourceNode("db:/example_data/customers").removeMethod("myFoundsetMethod1");
* m = solutionModel.getDataSourceNode("db:/example_data/customers").getCalculation("myFoundsetMethod1");
* if (m != null) { application.output("myFoundsetMethod1 could not be removed."); }
*
* var allMethods = solutionModel.getDataSourceNode("db:/example_data/customers").getMethod();
* for (var i = 0; i < allMethods; i++)
* {
* application.output(allMethods[i]);
* }
*
* @param name the name of the method to be removed
*
* @return true if the removal was successful, false otherwise
*/
@JSFunction
public boolean removeMethod(String name)
{
try
{
FlattenedSolution fs = application.getFlattenedSolution();
TableNode tablenode = fs.getSolutionCopyTableNode(dataSource);
ScriptMethod sc = tablenode.getFoundsetMethod(name);
if (sc != null)
{
tablenode.removeChild(sc);
return true;
}
//it is a design time method, therefore we "hide" it
sc = fs.getFoundsetMethod(name, dataSource);
if (sc != null)
{
fs.addToRemovedPersists(sc);
if (application.getFormManager() instanceof FormManager) ((FormManager)application.getFormManager()).fillScriptMenu();
return true;
}
// not found
return false;
}
catch (RepositoryException e)
{
throw new RuntimeException(e);
}
}
@Override
public String toString()
{
return "JSDataSourceNode[" + dataSource + ']';
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((dataSource == null) ? 0 : dataSource.hashCode());
return result;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj)
{
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
JSDataSourceNode other = (JSDataSourceNode)obj;
if (dataSource == null)
{
if (other.dataSource != null) return false;
}
else if (!dataSource.equals(other.dataSource)) return false;
return true;
}
}