git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@1190 f203690c-595d-4dc9-a70b-905162fa7fd2tags/jackcess-2.2.0
import java.util.Collection; | import java.util.Collection; | ||||
/** | /** | ||||
* FIXME, doc me and my friend | |||||
* | |||||
* An Expression is an executable handle to an Access expression. While the | |||||
* expression framework is implemented as separate from the core database | |||||
* functionality, most usage of Expressions will happen indirectly within the | |||||
* context of normal database operations. Thus, most users will not ever | |||||
* directly interact with an Expression instance. That said, Expressions may | |||||
* be executed independently of a Database instance if desired. | |||||
* | * | ||||
* @author James Ahlborn | * @author James Ahlborn | ||||
*/ | */ |
/* | |||||
Copyright (c) 2018 James Ahlborn | |||||
Licensed under the Apache License, Version 2.0 (the "License"); | |||||
you may not use this file except in compliance with the License. | |||||
You may obtain a copy of the License at | |||||
http://www.apache.org/licenses/LICENSE-2.0 | |||||
Unless required by applicable law or agreed to in writing, software | |||||
distributed under the License is distributed on an "AS IS" BASIS, | |||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
See the License for the specific language governing permissions and | |||||
limitations under the License. | |||||
*/ | |||||
/** | |||||
* Jackcess has support for evaluating Access expressions (beta support as of | |||||
* the 2.2.0 release). This functionality is currently disabled by default | |||||
* but can be globally enabled via the system property | |||||
* "com.healthmarketscience.jackcess.enableExpressionEvaluation" or | |||||
* selectively enabled on a per database basis using {@link com.healthmarketscience.jackcess.Database#setEvaluateExpressions(Boolean)}. Expressions can be used in a | |||||
* number of different places within an Access database. | |||||
*<p/> | |||||
* When enabled, Jackcess supports the following usage: | |||||
* <ul> | |||||
* <li><b>Default Values:</b> When a row is added which has a | |||||
* {@code null} value for a field which has a default value | |||||
* expression defined, that expression will be evaluated and the | |||||
* result will be inserted for that field. Like an auto-generated | |||||
* id, the generated default value will be returned in the input | |||||
* row array.</li> | |||||
* <li><b>Calculated Fields:</b> In databases which support calculated | |||||
* fields, any input value for a calculated field will be ignored. | |||||
* Instead, the result of evaluating the calculated field | |||||
* expression will be inserted. Like an auto-generated id, the | |||||
* calculated value will be returned in the input row array.</li> | |||||
* <li><b>Field Validation:</b> Field validation rules will be | |||||
* evaluated whenever a field value is updated. If the rule fails, | |||||
* the update operation will fail. The failure message will | |||||
* specify which field's validation rule failed and include the | |||||
* custom validation rule message if defined.</li> | |||||
* <li><b>Record Validation:</b> Similar to field validation rules, | |||||
* record validation rules will be run for the entire record before | |||||
* update. Failures are handled in a similar manner.</li> | |||||
* </ul> | |||||
* <p/> | |||||
* <h2>Supporting Classes</h2> | |||||
* <p/> | |||||
* The classes in this package make up the public api for expression handling | |||||
* in Jackcess. They geneerally fall into two categoreies: | |||||
* <p/> | |||||
* <h3>General Use Classes</h3> | |||||
* <p/> | |||||
* <ul> | |||||
* </ul> | |||||
* <p/> | |||||
* <h3>Advanced Use Classes</h3> | |||||
* <p/> | |||||
* <ul> | |||||
* </ul> | |||||
* <p/> | |||||
* <p/> | |||||
* <h2>Function Support</h2> | |||||
* <p/> | |||||
* Jackcess supports many of the standard Access functions. The following | |||||
* tables list the (hopefully) current status of support built into Jackcess. | |||||
* | |||||
* <h3>Conversion</h3> | |||||
* | |||||
* <table border="1" width="25%" cellpadding="3" cellspacing="0"> | |||||
* <tr class="TableHeadingColor" align="left"><th>Function</th><th>Supported</th></tr> | |||||
* <tr class="TableRowColor"><td>Asc</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>AscW</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>Chr</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>ChrW</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>EuroConvert</td><td></td></tr> | |||||
* <tr class="TableRowColor"><td>FormatCurrency</td><td></td></tr> | |||||
* <tr class="TableRowColor"><td>FormatDateTime</td><td></td></tr> | |||||
* <tr class="TableRowColor"><td>FormatNumber</td><td></td></tr> | |||||
* <tr class="TableRowColor"><td>FormatPercent</td><td></td></tr> | |||||
* <tr class="TableRowColor"><td>GUIDFromString</td><td></td></tr> | |||||
* <tr class="TableRowColor"><td>Hex($)</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>Nz</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>Oct($)</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>Str($)</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>StringFromGUID</td><td></td></tr> | |||||
* <tr class="TableRowColor"><td>Val</td><td></td></tr> | |||||
* <tr class="TableRowColor"><td>CBool</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>CByte</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>CCur</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>CDate</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>CVDate</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>CDbl</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>CDec</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>CInt</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>CLng</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>CSng</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>CStr</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>CVar</td><td>Y</td></tr> | |||||
* </table> | |||||
* | |||||
* <h3>Date/Time</h3> | |||||
* | |||||
* <table border="1" width="25%" cellpadding="3" cellspacing="0"> | |||||
* <tr class="TableHeadingColor" align="left"><th>Function</th><th>Supported</th></tr> | |||||
* <tr class="TableRowColor"><td>Day</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>Date </td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>DateAdd</td><td></td></tr> | |||||
* <tr class="TableRowColor"><td>DateDiff</td><td></td></tr> | |||||
* <tr class="TableRowColor"><td>DatePart</td><td></td></tr> | |||||
* <tr class="TableRowColor"><td>DateSerial</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>DateValue</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>Hour</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>Minute</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>Month</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>MonthName</td><td></td></tr> | |||||
* <tr class="TableRowColor"><td>Now</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>Second</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>Time</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>Timer</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>TimeSerial</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>TimeValue</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>Weekday</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>WeekdayName</td><td></td></tr> | |||||
* <tr class="TableRowColor"><td>Year</td><td>Y</td></tr> | |||||
* </table> | |||||
* | |||||
* <h3>Financial</h3> | |||||
* | |||||
* <table border="1" width="25%" cellpadding="3" cellspacing="0"> | |||||
* <tr class="TableHeadingColor" align="left"><th>Function</th><th>Supported</th></tr> | |||||
* <tr class="TableRowColor"><td>DDB</td><td></td></tr> | |||||
* <tr class="TableRowColor"><td>FV</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>IPmt</td><td></td></tr> | |||||
* <tr class="TableRowColor"><td>IRR</td><td></td></tr> | |||||
* <tr class="TableRowColor"><td>MIRR</td><td></td></tr> | |||||
* <tr class="TableRowColor"><td>NPer</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>NPV</td><td></td></tr> | |||||
* <tr class="TableRowColor"><td>Pmt</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>PPmt</td><td></td></tr> | |||||
* <tr class="TableRowColor"><td>PV</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>Rate</td><td></td></tr> | |||||
* <tr class="TableRowColor"><td>SLN</td><td></td></tr> | |||||
* <tr class="TableRowColor"><td>SYD</td><td></td></tr> | |||||
* </table> | |||||
* | |||||
* <h3>Inspection</h3> | |||||
* | |||||
* <table border="1" width="25%" cellpadding="3" cellspacing="0"> | |||||
* <tr class="TableHeadingColor" align="left"><th>Function</th><th>Supported</th></tr> | |||||
* <tr class="TableRowColor"><td>Environ</td><td></td></tr> | |||||
* <tr class="TableRowColor"><td>GetAllSettings</td><td></td></tr> | |||||
* <tr class="TableRowColor"><td>GetSetting</td><td></td></tr> | |||||
* <tr class="TableRowColor"><td>IsArray</td><td></td></tr> | |||||
* <tr class="TableRowColor"><td>IsDate</td><td>Partial</td></tr> | |||||
* <tr class="TableRowColor"><td>IsEmpty</td><td></td></tr> | |||||
* <tr class="TableRowColor"><td>IsError</td><td></td></tr> | |||||
* <tr class="TableRowColor"><td>IsMissing</td><td></td></tr> | |||||
* <tr class="TableRowColor"><td>IsNull</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>IsNumeric</td><td></td></tr> | |||||
* <tr class="TableRowColor"><td>IsObject</td><td></td></tr> | |||||
* <tr class="TableRowColor"><td>TypeName</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>VarType</td><td>Y</td></tr> | |||||
* </table> | |||||
* | |||||
* <h3>Math</h3> | |||||
* | |||||
* <table border="1" width="25%" cellpadding="3" cellspacing="0"> | |||||
* <tr class="TableHeadingColor" align="left"><th>Function</th><th>Supported</th></tr> | |||||
* <tr class="TableRowColor"><td>Abs</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>Atn</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>Cos</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>Exp</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>Int</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>Fix</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>Log</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>Rnd</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>Round</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>Sgn</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>Sin</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>Sqr</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>Tan</td><td>Y</td></tr> | |||||
* </table> | |||||
* | |||||
* <h3>Program Flow</h3> | |||||
* | |||||
* <table border="1" width="25%" cellpadding="3" cellspacing="0"> | |||||
* <tr class="TableHeadingColor" align="left"><th>Function</th><th>Supported</th></tr> | |||||
* <tr class="TableRowColor"><td>Choose</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>DoEvents</td><td></td></tr> | |||||
* <tr class="TableRowColor"><td>IIf</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>Switch</td><td>Y</td></tr> | |||||
* </table> | |||||
* | |||||
* <h3>Text</h3> | |||||
* | |||||
* <table border="1" width="25%" cellpadding="3" cellspacing="0"> | |||||
* <tr class="TableHeadingColor" align="left"><th>Function</th><th>Supported</th></tr> | |||||
* <tr class="TableRowColor"><td>Format</td><td></td></tr> | |||||
* <tr class="TableRowColor"><td>InStr</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>InStrRev</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>LCase($)</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>Left($)</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>Len</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>LTrim($)</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>RTrim($)</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>Trim($)</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>Mid($)</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>Replace</td><td></td></tr> | |||||
* <tr class="TableRowColor"><td>Right($)</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>Space($)</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>StrComp</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>StrConv</td><td></td></tr> | |||||
* <tr class="TableRowColor"><td>String($)</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>StrReverse</td><td>Y</td></tr> | |||||
* <tr class="TableRowColor"><td>UCase($)</td><td>Y</td></tr> | |||||
* </table> | |||||
* | |||||
* | |||||
* | |||||
*/ | |||||
package com.healthmarketscience.jackcess.expr; |
import com.healthmarketscience.jackcess.expr.Value; | import com.healthmarketscience.jackcess.expr.Value; | ||||
import com.healthmarketscience.jackcess.impl.ColumnImpl; | import com.healthmarketscience.jackcess.impl.ColumnImpl; | ||||
import static com.healthmarketscience.jackcess.impl.expr.DefaultFunctions.*; | import static com.healthmarketscience.jackcess.impl.expr.DefaultFunctions.*; | ||||
import static com.healthmarketscience.jackcess.impl.expr.FunctionSupport.*; | |||||
/** | /** | ||||
* | * |
import com.healthmarketscience.jackcess.expr.Function; | import com.healthmarketscience.jackcess.expr.Function; | ||||
import com.healthmarketscience.jackcess.expr.Value; | import com.healthmarketscience.jackcess.expr.Value; | ||||
import static com.healthmarketscience.jackcess.impl.expr.DefaultFunctions.*; | import static com.healthmarketscience.jackcess.impl.expr.DefaultFunctions.*; | ||||
import static com.healthmarketscience.jackcess.impl.expr.FunctionSupport.*; | |||||
/** | /** | ||||
* | * | ||||
* @author James Ahlborn | * @author James Ahlborn | ||||
*/ | */ | ||||
public class DefaultFinancialFunctions | |||||
public class DefaultFinancialFunctions | |||||
{ | { | ||||
/** 0 - payment end of month (default) */ | /** 0 - payment end of month (default) */ | ||||
private static final int PMT_END_MNTH = 0; | private static final int PMT_END_MNTH = 0; | ||||
static void init() { | static void init() { | ||||
// dummy method to ensure this class is loaded | // dummy method to ensure this class is loaded | ||||
} | } | ||||
public static final Function NPER = registerFunc(new FuncVar("NPer", 3, 5) { | public static final Function NPER = registerFunc(new FuncVar("NPer", 3, 5) { | ||||
@Override | @Override | ||||
protected Value evalVar(EvalContext ctx, Value[] params) { | protected Value evalVar(EvalContext ctx, Value[] params) { | ||||
double rate = params[0].getAsDouble(); | |||||
double pmt = params[1].getAsDouble(); | |||||
double pv = params[2].getAsDouble(); | |||||
double rate = params[0].getAsDouble(); | |||||
double pmt = params[1].getAsDouble(); | |||||
double pv = params[2].getAsDouble(); | |||||
double fv = 0d; | double fv = 0d; | ||||
if(params.length > 3) { | if(params.length > 3) { | ||||
public static final Function FV = registerFunc(new FuncVar("FV", 3, 5) { | public static final Function FV = registerFunc(new FuncVar("FV", 3, 5) { | ||||
@Override | @Override | ||||
protected Value evalVar(EvalContext ctx, Value[] params) { | protected Value evalVar(EvalContext ctx, Value[] params) { | ||||
double rate = params[0].getAsDouble(); | |||||
double nper = params[1].getAsDouble(); | |||||
double pmt = params[2].getAsDouble(); | |||||
double rate = params[0].getAsDouble(); | |||||
double nper = params[1].getAsDouble(); | |||||
double pmt = params[2].getAsDouble(); | |||||
double pv = 0d; | double pv = 0d; | ||||
if(params.length > 3) { | if(params.length > 3) { | ||||
public static final Function PV = registerFunc(new FuncVar("PV", 3, 5) { | public static final Function PV = registerFunc(new FuncVar("PV", 3, 5) { | ||||
@Override | @Override | ||||
protected Value evalVar(EvalContext ctx, Value[] params) { | protected Value evalVar(EvalContext ctx, Value[] params) { | ||||
double rate = params[0].getAsDouble(); | |||||
double nper = params[1].getAsDouble(); | |||||
double pmt = params[2].getAsDouble(); | |||||
double rate = params[0].getAsDouble(); | |||||
double nper = params[1].getAsDouble(); | |||||
double pmt = params[2].getAsDouble(); | |||||
double fv = 0d; | double fv = 0d; | ||||
if(params.length > 3) { | if(params.length > 3) { | ||||
public static final Function PMT = registerFunc(new FuncVar("Pmt", 3, 5) { | public static final Function PMT = registerFunc(new FuncVar("Pmt", 3, 5) { | ||||
@Override | @Override | ||||
protected Value evalVar(EvalContext ctx, Value[] params) { | protected Value evalVar(EvalContext ctx, Value[] params) { | ||||
double rate = params[0].getAsDouble(); | |||||
double nper = params[1].getAsDouble(); | |||||
double pv = params[2].getAsDouble(); | |||||
double rate = params[0].getAsDouble(); | |||||
double nper = params[1].getAsDouble(); | |||||
double pv = params[2].getAsDouble(); | |||||
double fv = 0d; | double fv = 0d; | ||||
if(params.length > 3) { | if(params.length > 3) { | ||||
// public static final Function IPMT = registerFunc(new FuncVar("IPmt", 4, 6) { | // public static final Function IPMT = registerFunc(new FuncVar("IPmt", 4, 6) { | ||||
// @Override | // @Override | ||||
// protected Value evalVar(EvalContext ctx, Value[] params) { | // protected Value evalVar(EvalContext ctx, Value[] params) { | ||||
// double rate = params[0].getAsDouble(); | |||||
// double per = params[1].getAsDouble(); | |||||
// double nper = params[2].getAsDouble(); | |||||
// double pv = params[3].getAsDouble(); | |||||
// double rate = params[0].getAsDouble(); | |||||
// double per = params[1].getAsDouble(); | |||||
// double nper = params[2].getAsDouble(); | |||||
// double pv = params[3].getAsDouble(); | |||||
// double fv = 0d; | // double fv = 0d; | ||||
// if(params.length > 4) { | // if(params.length > 4) { | ||||
// public static final Function PPMT = registerFunc(new FuncVar("PPmt", 4, 6) { | // public static final Function PPMT = registerFunc(new FuncVar("PPmt", 4, 6) { | ||||
// @Override | // @Override | ||||
// protected Value evalVar(EvalContext ctx, Value[] params) { | // protected Value evalVar(EvalContext ctx, Value[] params) { | ||||
// double rate = params[0].getAsDouble(); | |||||
// double per = params[1].getAsDouble(); | |||||
// double nper = params[2].getAsDouble(); | |||||
// double pv = params[3].getAsDouble(); | |||||
// double rate = params[0].getAsDouble(); | |||||
// double per = params[1].getAsDouble(); | |||||
// double nper = params[2].getAsDouble(); | |||||
// double pv = params[3].getAsDouble(); | |||||
// double fv = 0d; | // double fv = 0d; | ||||
// if(params.length > 4) { | // if(params.length > 4) { | ||||
// public static final Function DDB = registerFunc(new FuncVar("DDB", 4, 5) { | // public static final Function DDB = registerFunc(new FuncVar("DDB", 4, 5) { | ||||
// @Override | // @Override | ||||
// protected Value evalVar(EvalContext ctx, Value[] params) { | // protected Value evalVar(EvalContext ctx, Value[] params) { | ||||
// double cost = params[0].getAsDouble(); | |||||
// double salvage = params[1].getAsDouble(); | |||||
// double life = params[2].getAsDouble(); | |||||
// double period = params[3].getAsDouble(); | |||||
// double cost = params[0].getAsDouble(); | |||||
// double salvage = params[1].getAsDouble(); | |||||
// double life = params[2].getAsDouble(); | |||||
// double period = params[3].getAsDouble(); | |||||
// double factor = 2d; | // double factor = 2d; | ||||
// if(params.length > 4) { | // if(params.length > 4) { | ||||
// }); | // }); | ||||
// FIXME, untested | // FIXME, untested | ||||
public static final Function SLN = registerFunc(new FuncVar("SLN", 3, 3) { | |||||
@Override | |||||
protected Value evalVar(EvalContext ctx, Value[] params) { | |||||
double cost = params[0].getAsDouble(); | |||||
double salvage = params[1].getAsDouble(); | |||||
double life = params[2].getAsDouble(); | |||||
// public static final Function SLN = registerFunc(new FuncVar("SLN", 3, 3) { | |||||
// @Override | |||||
// protected Value evalVar(EvalContext ctx, Value[] params) { | |||||
// double cost = params[0].getAsDouble(); | |||||
// double salvage = params[1].getAsDouble(); | |||||
// double life = params[2].getAsDouble(); | |||||
double result = calculateStraightLineDepreciation(cost, salvage, life); | |||||
// double result = calculateStraightLineDepreciation(cost, salvage, life); | |||||
return BuiltinOperators.toValue(result); | |||||
} | |||||
}); | |||||
// return BuiltinOperators.toValue(result); | |||||
// } | |||||
// }); | |||||
// FIXME, untested | // FIXME, untested | ||||
public static final Function SYD = registerFunc(new FuncVar("SYD", 4, 4) { | |||||
@Override | |||||
protected Value evalVar(EvalContext ctx, Value[] params) { | |||||
double cost = params[0].getAsDouble(); | |||||
double salvage = params[1].getAsDouble(); | |||||
double life = params[2].getAsDouble(); | |||||
double period = params[3].getAsDouble(); | |||||
// public static final Function SYD = registerFunc(new FuncVar("SYD", 4, 4) { | |||||
// @Override | |||||
// protected Value evalVar(EvalContext ctx, Value[] params) { | |||||
// double cost = params[0].getAsDouble(); | |||||
// double salvage = params[1].getAsDouble(); | |||||
// double life = params[2].getAsDouble(); | |||||
// double period = params[3].getAsDouble(); | |||||
double result = calculateSumOfYearsDepreciation( | |||||
cost, salvage, life, period); | |||||
// double result = calculateSumOfYearsDepreciation( | |||||
// cost, salvage, life, period); | |||||
return BuiltinOperators.toValue(result); | |||||
} | |||||
}); | |||||
// return BuiltinOperators.toValue(result); | |||||
// } | |||||
// }); | |||||
private static double calculateLoanPaymentPeriods( | private static double calculateLoanPaymentPeriods( | ||||
} | } | ||||
double v1 = Math.log(1d + (rate * pv / pmt)); | double v1 = Math.log(1d + (rate * pv / pmt)); | ||||
double v2 = Math.log(1d + rate); | double v2 = Math.log(1d + rate); | ||||
double result = -v1 / v2; | double result = -v1 / v2; | ||||
if(pmtType == PMT_BEG_MNTH) { | if(pmtType == PMT_BEG_MNTH) { | ||||
double v1 = Math.log(1d - (rate * fv / pmt)); | double v1 = Math.log(1d - (rate * fv / pmt)); | ||||
double v2 = Math.log(1d + rate); | double v2 = Math.log(1d + rate); | ||||
double result = v1 / v2; | double result = v1 / v2; | ||||
if(pmtType == PMT_BEG_MNTH) { | if(pmtType == PMT_BEG_MNTH) { | ||||
} | } | ||||
return result; | return result; | ||||
} | |||||
} | |||||
private static double calculateAnnuityPayment( | private static double calculateAnnuityPayment( | ||||
double rate, double nper, double fv, int pmtType) { | double rate, double nper, double fv, int pmtType) { | ||||
} | } | ||||
return result; | return result; | ||||
} | |||||
} | |||||
private static double calculateInterestPayment( | private static double calculateInterestPayment( | ||||
double pmt, double rate, double per, double pv, int pmtType) { | double pmt, double rate, double per, double pv, int pmtType) { | ||||
fvPer -= 1d; | fvPer -= 1d; | ||||
} | } | ||||
double remBalance = (pv * Math.pow((1d + rate), pvPer)) - | |||||
double remBalance = (pv * Math.pow((1d + rate), pvPer)) - | |||||
// FIXME, always use pmtType of 0? | // FIXME, always use pmtType of 0? | ||||
calculateFutureValue(rate, fvPer, pmt, PMT_END_MNTH); | calculateFutureValue(rate, fvPer, pmt, PMT_END_MNTH); | ||||
} | } | ||||
} | } | ||||
package com.healthmarketscience.jackcess.impl.expr; | package com.healthmarketscience.jackcess.impl.expr; | ||||
import java.math.BigDecimal; | import java.math.BigDecimal; | ||||
import java.util.Arrays; | |||||
import java.util.HashMap; | import java.util.HashMap; | ||||
import java.util.Map; | import java.util.Map; | ||||
import com.healthmarketscience.jackcess.expr.Value; | import com.healthmarketscience.jackcess.expr.Value; | ||||
import com.healthmarketscience.jackcess.impl.DatabaseImpl; | import com.healthmarketscience.jackcess.impl.DatabaseImpl; | ||||
import com.healthmarketscience.jackcess.impl.NumberFormatter; | import com.healthmarketscience.jackcess.impl.NumberFormatter; | ||||
import static com.healthmarketscience.jackcess.impl.expr.FunctionSupport.*; | |||||
/** | /** | ||||
* | * | ||||
private static final Map<String,Function> FUNCS = | private static final Map<String,Function> FUNCS = | ||||
new HashMap<String,Function>(); | new HashMap<String,Function>(); | ||||
private static final char NON_VAR_SUFFIX = '$'; | |||||
static { | static { | ||||
// load all default functions | // load all default functions | ||||
DefaultTextFunctions.init(); | DefaultTextFunctions.init(); | ||||
private DefaultFunctions() {} | private DefaultFunctions() {} | ||||
public static abstract class BaseFunction implements Function | |||||
{ | |||||
private final String _name; | |||||
private final int _minParams; | |||||
private final int _maxParams; | |||||
protected BaseFunction(String name, int minParams, int maxParams) | |||||
{ | |||||
_name = name; | |||||
_minParams = minParams; | |||||
_maxParams = maxParams; | |||||
} | |||||
public String getName() { | |||||
return _name; | |||||
} | |||||
public boolean isPure() { | |||||
// most functions are probably pure, so make this the default | |||||
return true; | |||||
} | |||||
protected void validateNumParams(Value[] params) { | |||||
int num = params.length; | |||||
if((num < _minParams) || (num > _maxParams)) { | |||||
String range = ((_minParams == _maxParams) ? "" + _minParams : | |||||
_minParams + " to " + _maxParams); | |||||
throw new EvalException( | |||||
"Invalid number of parameters " + | |||||
num + " passed, expected " + range); | |||||
} | |||||
} | |||||
protected EvalException invalidFunctionCall( | |||||
Throwable t, Value[] params) | |||||
{ | |||||
String paramStr = Arrays.toString(params); | |||||
String msg = "Invalid function call {" + _name + "(" + | |||||
paramStr.substring(1, paramStr.length() - 1) + ")}"; | |||||
return new EvalException(msg, t); | |||||
} | |||||
@Override | |||||
public String toString() { | |||||
return getName() + "()"; | |||||
} | |||||
} | |||||
public static abstract class Func0 extends BaseFunction | |||||
{ | |||||
protected Func0(String name) { | |||||
super(name, 0, 0); | |||||
} | |||||
@Override | |||||
public boolean isPure() { | |||||
// 0-arg functions are usually not pure | |||||
return false; | |||||
} | |||||
public final Value eval(EvalContext ctx, Value... params) { | |||||
try { | |||||
validateNumParams(params); | |||||
return eval0(ctx); | |||||
} catch(Exception e) { | |||||
throw invalidFunctionCall(e, params); | |||||
} | |||||
} | |||||
protected abstract Value eval0(EvalContext ctx); | |||||
} | |||||
public static abstract class Func1 extends BaseFunction | |||||
{ | |||||
protected Func1(String name) { | |||||
super(name, 1, 1); | |||||
} | |||||
public final Value eval(EvalContext ctx, Value... params) { | |||||
try { | |||||
validateNumParams(params); | |||||
return eval1(ctx, params[0]); | |||||
} catch(Exception e) { | |||||
throw invalidFunctionCall(e, params); | |||||
} | |||||
} | |||||
protected abstract Value eval1(EvalContext ctx, Value param); | |||||
} | |||||
public static abstract class Func1NullIsNull extends BaseFunction | |||||
{ | |||||
protected Func1NullIsNull(String name) { | |||||
super(name, 1, 1); | |||||
} | |||||
public final Value eval(EvalContext ctx, Value... params) { | |||||
try { | |||||
validateNumParams(params); | |||||
Value param1 = params[0]; | |||||
if(param1.isNull()) { | |||||
return param1; | |||||
} | |||||
return eval1(ctx, param1); | |||||
} catch(Exception e) { | |||||
throw invalidFunctionCall(e, params); | |||||
} | |||||
} | |||||
protected abstract Value eval1(EvalContext ctx, Value param); | |||||
} | |||||
public static abstract class Func2 extends BaseFunction | |||||
{ | |||||
protected Func2(String name) { | |||||
super(name, 2, 2); | |||||
} | |||||
public final Value eval(EvalContext ctx, Value... params) { | |||||
try { | |||||
validateNumParams(params); | |||||
return eval2(ctx, params[0], params[1]); | |||||
} catch(Exception e) { | |||||
throw invalidFunctionCall(e, params); | |||||
} | |||||
} | |||||
protected abstract Value eval2(EvalContext ctx, Value param1, Value param2); | |||||
} | |||||
public static abstract class Func3 extends BaseFunction | |||||
{ | |||||
protected Func3(String name) { | |||||
super(name, 3, 3); | |||||
} | |||||
public final Value eval(EvalContext ctx, Value... params) { | |||||
try { | |||||
validateNumParams(params); | |||||
return eval3(ctx, params[0], params[1], params[2]); | |||||
} catch(Exception e) { | |||||
throw invalidFunctionCall(e, params); | |||||
} | |||||
} | |||||
protected abstract Value eval3(EvalContext ctx, | |||||
Value param1, Value param2, Value param3); | |||||
} | |||||
public static abstract class FuncVar extends BaseFunction | |||||
{ | |||||
protected FuncVar(String name) { | |||||
super(name, 0, Integer.MAX_VALUE); | |||||
} | |||||
protected FuncVar(String name, int minParams, int maxParams) { | |||||
super(name, minParams, maxParams); | |||||
} | |||||
public final Value eval(EvalContext ctx, Value... params) { | |||||
try { | |||||
validateNumParams(params); | |||||
return evalVar(ctx, params); | |||||
} catch(Exception e) { | |||||
throw invalidFunctionCall(e, params); | |||||
} | |||||
} | |||||
protected abstract Value evalVar(EvalContext ctx, Value[] params); | |||||
} | |||||
public static class StringFuncWrapper implements Function | |||||
{ | |||||
private final String _name; | |||||
private final Function _delegate; | |||||
public StringFuncWrapper(Function delegate) { | |||||
_delegate = delegate; | |||||
_name = _delegate.getName() + NON_VAR_SUFFIX; | |||||
} | |||||
public String getName() { | |||||
return _name; | |||||
} | |||||
public boolean isPure() { | |||||
return _delegate.isPure(); | |||||
} | |||||
public Value eval(EvalContext ctx, Value... params) { | |||||
Value result = _delegate.eval(ctx, params); | |||||
if(result.isNull()) { | |||||
// non-variant version does not do null-propagation, so force | |||||
// exception to be thrown here | |||||
result.getAsString(); | |||||
} | |||||
return result; | |||||
} | |||||
@Override | |||||
public String toString() { | |||||
return getName() + "()"; | |||||
} | |||||
} | |||||
public static final Function IIF = registerFunc(new Func3("IIf") { | public static final Function IIF = registerFunc(new Func3("IIf") { | ||||
@Override | @Override | ||||
protected Value eval3(EvalContext ctx, | protected Value eval3(EvalContext ctx, |
import com.healthmarketscience.jackcess.expr.Value; | import com.healthmarketscience.jackcess.expr.Value; | ||||
import com.healthmarketscience.jackcess.impl.NumberFormatter; | import com.healthmarketscience.jackcess.impl.NumberFormatter; | ||||
import static com.healthmarketscience.jackcess.impl.expr.DefaultFunctions.*; | import static com.healthmarketscience.jackcess.impl.expr.DefaultFunctions.*; | ||||
import static com.healthmarketscience.jackcess.impl.expr.FunctionSupport.*; | |||||
/** | /** | ||||
* | * | ||||
}); | }); | ||||
// public static final Function Val = registerFunc(new Func1("Val") { | |||||
// public static final Function VAL = registerFunc(new Func1("Val") { | |||||
// @Override | // @Override | ||||
// protected Value eval1(EvalContext ctx, Value param1) { | // protected Value eval1(EvalContext ctx, Value param1) { | ||||
// // FIXME, maybe leverage ExpressionTokenizer.maybeParseNumberLiteral (note, leading - or + is valid, exponent form is valid) | // // FIXME, maybe leverage ExpressionTokenizer.maybeParseNumberLiteral (note, leading - or + is valid, exponent form is valid) |
import com.healthmarketscience.jackcess.expr.Function; | import com.healthmarketscience.jackcess.expr.Function; | ||||
import com.healthmarketscience.jackcess.expr.Value; | import com.healthmarketscience.jackcess.expr.Value; | ||||
import static com.healthmarketscience.jackcess.impl.expr.DefaultFunctions.*; | import static com.healthmarketscience.jackcess.impl.expr.DefaultFunctions.*; | ||||
import static com.healthmarketscience.jackcess.impl.expr.FunctionSupport.*; | |||||
/** | /** | ||||
* | * |
/* | |||||
Copyright (c) 2018 James Ahlborn | |||||
Licensed under the Apache License, Version 2.0 (the "License"); | |||||
you may not use this file except in compliance with the License. | |||||
You may obtain a copy of the License at | |||||
http://www.apache.org/licenses/LICENSE-2.0 | |||||
Unless required by applicable law or agreed to in writing, software | |||||
distributed under the License is distributed on an "AS IS" BASIS, | |||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
See the License for the specific language governing permissions and | |||||
limitations under the License. | |||||
*/ | |||||
package com.healthmarketscience.jackcess.impl.expr; | |||||
import java.util.Arrays; | |||||
import com.healthmarketscience.jackcess.expr.EvalContext; | |||||
import com.healthmarketscience.jackcess.expr.EvalException; | |||||
import com.healthmarketscience.jackcess.expr.Function; | |||||
import com.healthmarketscience.jackcess.expr.Value; | |||||
/** | |||||
* | |||||
* @author James Ahlborn | |||||
*/ | |||||
public class FunctionSupport | |||||
{ | |||||
private static final char NON_VAR_SUFFIX = '$'; | |||||
private FunctionSupport() {} | |||||
public static abstract class BaseFunction implements Function | |||||
{ | |||||
private final String _name; | |||||
private final int _minParams; | |||||
private final int _maxParams; | |||||
protected BaseFunction(String name, int minParams, int maxParams) | |||||
{ | |||||
_name = name; | |||||
_minParams = minParams; | |||||
_maxParams = maxParams; | |||||
} | |||||
public String getName() { | |||||
return _name; | |||||
} | |||||
public boolean isPure() { | |||||
// most functions are probably pure, so make this the default | |||||
return true; | |||||
} | |||||
protected void validateNumParams(Value[] params) { | |||||
int num = params.length; | |||||
if((num < _minParams) || (num > _maxParams)) { | |||||
String range = ((_minParams == _maxParams) ? "" + _minParams : | |||||
_minParams + " to " + _maxParams); | |||||
throw new EvalException( | |||||
"Invalid number of parameters " + | |||||
num + " passed, expected " + range); | |||||
} | |||||
} | |||||
protected EvalException invalidFunctionCall( | |||||
Throwable t, Value[] params) | |||||
{ | |||||
String paramStr = Arrays.toString(params); | |||||
String msg = "Invalid function call {" + _name + "(" + | |||||
paramStr.substring(1, paramStr.length() - 1) + ")}"; | |||||
return new EvalException(msg, t); | |||||
} | |||||
@Override | |||||
public String toString() { | |||||
return getName() + "()"; | |||||
} | |||||
} | |||||
public static abstract class Func0 extends BaseFunction | |||||
{ | |||||
protected Func0(String name) { | |||||
super(name, 0, 0); | |||||
} | |||||
@Override | |||||
public boolean isPure() { | |||||
// 0-arg functions are usually not pure | |||||
return false; | |||||
} | |||||
public final Value eval(EvalContext ctx, Value... params) { | |||||
try { | |||||
validateNumParams(params); | |||||
return eval0(ctx); | |||||
} catch(Exception e) { | |||||
throw invalidFunctionCall(e, params); | |||||
} | |||||
} | |||||
protected abstract Value eval0(EvalContext ctx); | |||||
} | |||||
public static abstract class Func1 extends BaseFunction | |||||
{ | |||||
protected Func1(String name) { | |||||
super(name, 1, 1); | |||||
} | |||||
public final Value eval(EvalContext ctx, Value... params) { | |||||
try { | |||||
validateNumParams(params); | |||||
return eval1(ctx, params[0]); | |||||
} catch(Exception e) { | |||||
throw invalidFunctionCall(e, params); | |||||
} | |||||
} | |||||
protected abstract Value eval1(EvalContext ctx, Value param); | |||||
} | |||||
public static abstract class Func1NullIsNull extends BaseFunction | |||||
{ | |||||
protected Func1NullIsNull(String name) { | |||||
super(name, 1, 1); | |||||
} | |||||
public final Value eval(EvalContext ctx, Value... params) { | |||||
try { | |||||
validateNumParams(params); | |||||
Value param1 = params[0]; | |||||
if(param1.isNull()) { | |||||
return param1; | |||||
} | |||||
return eval1(ctx, param1); | |||||
} catch(Exception e) { | |||||
throw invalidFunctionCall(e, params); | |||||
} | |||||
} | |||||
protected abstract Value eval1(EvalContext ctx, Value param); | |||||
} | |||||
public static abstract class Func2 extends BaseFunction | |||||
{ | |||||
protected Func2(String name) { | |||||
super(name, 2, 2); | |||||
} | |||||
public final Value eval(EvalContext ctx, Value... params) { | |||||
try { | |||||
validateNumParams(params); | |||||
return eval2(ctx, params[0], params[1]); | |||||
} catch(Exception e) { | |||||
throw invalidFunctionCall(e, params); | |||||
} | |||||
} | |||||
protected abstract Value eval2(EvalContext ctx, Value param1, Value param2); | |||||
} | |||||
public static abstract class Func3 extends BaseFunction | |||||
{ | |||||
protected Func3(String name) { | |||||
super(name, 3, 3); | |||||
} | |||||
public final Value eval(EvalContext ctx, Value... params) { | |||||
try { | |||||
validateNumParams(params); | |||||
return eval3(ctx, params[0], params[1], params[2]); | |||||
} catch(Exception e) { | |||||
throw invalidFunctionCall(e, params); | |||||
} | |||||
} | |||||
protected abstract Value eval3(EvalContext ctx, | |||||
Value param1, Value param2, Value param3); | |||||
} | |||||
public static abstract class FuncVar extends BaseFunction | |||||
{ | |||||
protected FuncVar(String name) { | |||||
super(name, 0, Integer.MAX_VALUE); | |||||
} | |||||
protected FuncVar(String name, int minParams, int maxParams) { | |||||
super(name, minParams, maxParams); | |||||
} | |||||
public final Value eval(EvalContext ctx, Value... params) { | |||||
try { | |||||
validateNumParams(params); | |||||
return evalVar(ctx, params); | |||||
} catch(Exception e) { | |||||
throw invalidFunctionCall(e, params); | |||||
} | |||||
} | |||||
protected abstract Value evalVar(EvalContext ctx, Value[] params); | |||||
} | |||||
public static class StringFuncWrapper implements Function | |||||
{ | |||||
private final String _name; | |||||
private final Function _delegate; | |||||
public StringFuncWrapper(Function delegate) { | |||||
_delegate = delegate; | |||||
_name = _delegate.getName() + NON_VAR_SUFFIX; | |||||
} | |||||
public String getName() { | |||||
return _name; | |||||
} | |||||
public boolean isPure() { | |||||
return _delegate.isPure(); | |||||
} | |||||
public Value eval(EvalContext ctx, Value... params) { | |||||
Value result = _delegate.eval(ctx, params); | |||||
if(result.isNull()) { | |||||
// non-variant version does not do null-propagation, so force | |||||
// exception to be thrown here | |||||
result.getAsString(); | |||||
} | |||||
return result; | |||||
} | |||||
@Override | |||||
public String toString() { | |||||
return getName() + "()"; | |||||
} | |||||
} | |||||
} |
for more info. | for more info. | ||||
</p> | </p> | ||||
<subsection name="Expression Evaluation"> | |||||
<p> | |||||
Have you ever wished that Jackcess could handle field "default | |||||
values" (or other expressions)? Wish no longer! The 2.2.0 version | |||||
of Jackcess has finally landed a beta version of expression | |||||
evaluation. See the <a href="apidocs/com/healthmarketscience/jackcess/expr/package-summary.html#package_description">expr package</a> javadocs for more details. | |||||
</p> | |||||
</subsection> | |||||
<subsection name="Brand New License!"> | <subsection name="Brand New License!"> | ||||
<p> | <p> | ||||
Due to the generosity of Health Market Science and the efforts of | Due to the generosity of Health Market Science and the efforts of |
import com.healthmarketscience.jackcess.expr.Value; | import com.healthmarketscience.jackcess.expr.Value; | ||||
import com.healthmarketscience.jackcess.impl.expr.BuiltinOperators; | import com.healthmarketscience.jackcess.impl.expr.BuiltinOperators; | ||||
import com.healthmarketscience.jackcess.impl.expr.DefaultFunctions; | import com.healthmarketscience.jackcess.impl.expr.DefaultFunctions; | ||||
import com.healthmarketscience.jackcess.impl.expr.FunctionSupport; | |||||
import junit.framework.TestCase; | import junit.framework.TestCase; | ||||
import static com.healthmarketscience.jackcess.Database.*; | import static com.healthmarketscience.jackcess.Database.*; | ||||
props.save(); | props.save(); | ||||
} | } | ||||
private static final Function FOO = new DefaultFunctions.Func0("FooFunc") { | |||||
private static final Function FOO = new FunctionSupport.Func0("FooFunc") { | |||||
@Override | @Override | ||||
public boolean isPure() { return false; } | public boolean isPure() { return false; } | ||||
@Override | @Override |