




已阅读5页,还剩29页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
公式解析器使用指南第一章 新版公式使用手册21.1 公式主要功能21.2 公式的基本使用方法31.3 数值型计算结果小数位的控制61.4 自定义变量的使用71.5 如何从公式中提取变量91.6 空值”,NULL值及Zero值的处理101.7 如何进行列操作111.8 利用定义函数扩展公式功能121.9 外接函数的使用131.10 (5.02 new) 配置装载自定义函数131.11 运算符重载141.12 公式简单调试151.13公式使用最佳实践17附录:公式解析器内置公式列表213.1 数学运算函数213.2 条件判断函数243.3 数据库查询函数253.4 字符串相关函数273.5 日期函数283.6 类型转换函数283.7 货币金额函数293.8 多语言翻译函数313.9 其他函数313.10 5.02新增函数313.11V5.5新增函数32第一章 新版公式使用手册1.1 公式主要功能1. 支持一般的算术运算+,-,*,/,%例如: sin(1.35)*a/b + cos(3.4)/c其中a,b,c均为变量2. 支持对数值型计算结果小数位的控制3. 支持逻辑运算符&(兼容老版&),|(兼容老版|),!例如: iif(a&b)|(c&d),right,wrong)其中a,b,c,d均为变量4. 支持比较运算符,=,b & a != null)|(cvar1+var2其中var1,var2均为自定义变量,可以为String型,也可以为任意类型6. 公式除了支持String,Number型数据运算,还支持自定义类型例如: combine(vo1,vo2)其中vo1,vo2可为自定义的数据类型,具体用法参考后面的说明7. 支持操作符(+,-,*,/,=,car2)|(factory1col1+col2;b-a+col1*col3;c-a+b;14. 支持一个线程内多个公式执行器实例交替运行的情况,但不支持多个线程内同一公式执行器实例交替运行。所以如果程序中起多线程的话,建议每个线程单独创建自己的公式解析器示例。例如下面的代码是可行的:FormulaParseFather f= new FormulaParse();f.setExpress(formula);f.setNullAsZero(true);FormulaParseFather f1=new FormulaParse(); f1.setNullAsZero(false);f.setDataSArray(map);String res = f.getValueS();15.支持单表查询类公式的自动SQL合并,极大提高查询效率(V56版本)。1.2 公式的基本使用方法使用公式解析器的基本步骤如下:1. 创建公式执行器如果在客户端使用公式解析:FormulaParseFather f = new nc.ui.pub.formulaparse.FormulaParse();如果在服务端使用公式解析:FormulaParseFather f = new nc.bs.pub.formulaparse.FormulaParse();如果不知道当前的代码会在哪一端运行,可以用下面的方法进行判断:if (RuntimeEnv.getInstance().isRunningInServer() parse = new nc.bs.pub.formulaparse.FormulaParse(); else parse = new nc.ui.pub.formulaparse.FormulaParse();如果在客户端执行公式,但是又不希望使用前台缓存(注:在前台通过公式查询跨公司大数据档案时会严重影响LFU算法效果,导致前台缓存数据量急剧增加,影响缓存的稳定性,此种场景下不建议在前台执行公式,所以NC报表类执行公式要求不走前台缓存),可以使用:FormulaParseFather f = new nc.ui.pub.formulaparse.FormulaParseRunInServer();2. 设置公式执行器环境(自定义变量及自定义函数可参考后续章节描述)这里可以给公式执行器添加自定义变量,例如:UFDouble var1 = new UFDouble(5.368);f.addVariable(var1);或者添加自定义函数,例如:类YourFunction是一个定义的函数类(关于如何自定义函数,请参考自定义函数一节),在公式中函数取名为yourfun,则可以这样添加你的自定义函数:f.addFunction(yourfun,new YourFunction);3. 设置公式的值设置公式执行器环境非必须步骤对于单行公式:String fomula = sin(30)*2-56/78;f.setExpress(fomula);对于多行公式:String formulas = new Stringviewmny1-viewnum*viewprice-rate*0.23 ,viewmny2- viewnum*viewprice-rate*0.24;f.setExpressArray(formulas);4. 对公式进行语法检查在设置完公式之后,直接调用执行器的check()方法便可以进行公式检查:bool isok = f.check();如果返回结果为false,那么说明公式存在语法错误,调用getError()可以获得具体的错误信息:String errmsg = f.getErrorMsg();下面是一段具体应用的代码:FormulaParseFather f= new nc.ui.pub.formulaparse.FormulaParse();String formula = a-getChineseCurrency(cchmny,34);boolean isok= f.check(); if(!isok)System.out.println(f.getError();return;另外,如果还没有设置公式,仅仅是想校验公式的正确性,则可以直接通过checkExpress(String formula)或者:checkExpressArray(formulas)来检查公式,例如:bool isok = f. checkExpress (formulas); /单行公式bool isok = f. checkExpressArray(formulas); /多行公式注意:如果是多行公式,那么只要有一个公式写法是错误的,那么检查结果就会是false。5. 提取公式变量在单据模板和打印模板的应用中,公式中的变量并不是已知的,需要从公式中分析得到,取得公式中的变量之后,再把相应的值赋给变量。新版公式解析器中提取公式中变量的接口和老版是一致的:VarryVO varrys = f.getVarryArray();下面是单据模板里取公式变量的典型代码:/设置表达式formulas = filterUsedFormulas(bfc, formulas);if (formulas = null)return null;f.setExpressArray(formulas);/获得变量名final VarryVO varrys = f.getVarryArray();6. 给公式变量赋值下面是一段给公式变量赋值的代码:/下列代码假设varrys不会为nullVarryVO varrys = f.getVarryArray();for (int i = 0; i varrys.length; i+)String varries = varrysi.getVarry();/提取公式变量非必须步骤if(varries != null)for (int j = 0; j cchmny*cchmny*cchmny,b-a*cchmny,c-a*b*cchmny;f.setExpressArray(formulas);Map map = new HashMap();List v2 = new ArrayList();v2.add(new UFDouble(1.99999); /row valuev2.add(new UFDouble(2.9999); /row valuev2.add(new UFDouble(3.99999);v2.add(new UFDouble(1.999994); /row valuemap.put(cchmny,v2);f.setDataSArray(map);f.setScale(a,5);f.setScale(b,6);Object res = f.getValueOArray();assertEquals(应该相等!, 7.99988, res00.toString();10assertEquals(应该相等!, 15.999680, res10.toString();/默认精度为8assertEquals(应该相等!, 255.98976018, res20.toString();1.4 自定义变量的使用公式支持自定义变量,只要相关的操作允许的话,自定义变量可以是任何类型的,下面的代码说明了如何加入一个名为var1,UFDouble型的变量:FormulaParseFather f= new nc.ui.pub.formulaparse.FormulaParse();String formula = a-round(var1,3);f.addVariable(var1,new UFDouble(3.56893);除了可以加入简单类型的变量,还可以加入ArrayList3, 用户自定义对象4等等,下面的代码演示了如何加入一个ArrayList型的变量:FormulaParseFather f= new nc.ui.pub.formulaparse.FormulaParse();String formula = a-round(var1,3);List list1 = new ArrayList();list1.add(new UFDouble(56.2354);list1.add(new UFDouble(23.2343);f.addVariable(var1,list1);另外还可以批量的加入多个变量,公式解析器提供两个接口如下:setDataSArray(Hashtable); /老版接口要求,不推荐使用setDataSArray(Map);需要注意的是,由于老版接口setDataSArray(Hashtable)所有的参数均通过字符串传入,所以在以此方式传入参数时,存在真假字符串之分,真字符串形如:v10 = SHVO0000000000000005;v11 = SHVO0000000000000005;v12 = SHVO0000000000000005;即在字符串的两端加上双引号”,表示传入的参数为String类型,如果两端不加这个符号,则表示传入的为数值型, 公式解析器会将其转换为数值型处理,例如:v10 = 623.23;v11 = 5263.12;v12 = 5242.01;而如果通过addVariable(name,Value)或者以setDataSArray(Map)方式传入参数时,参数的类型取决于实际传入的类型,公式解析器不会做任何转换:-),请注意下面两段代码的差别。老版公式接口,判断真假字符串:FormulaParseFather f = new nc.ui.pub.formulaparse.FormulaParse();f.setExpress(a-var1+var2);List v2 = new ArrayList();v2.add(100); /row valuev2.add(200); /row valueHashtable map = new Hashtable();map.put(var1,v2);map.put(var2,v2);Hashtable maps = new Hashtable1;maps0 = map;f.setDataSArray(maps); /将会转为数值f.setScale(2);String res = f.getValueS();assertEquals(Should equal:,200.00,res0.toString();新版增加的接口,传什么就是什么,完全按Object方式传递参数:FormulaParseFather f = new nc.ui.pub.formulaparse.FormulaParse();f.setExpress(a-var1+var2);List v2 = new ArrayList();v2.add(100); /row valuev2.add(200); /row valueMap map = new HashMap();map.put(var1,v2);map.put(var2,v2);f.setDataSArray(map); /当作字符串f.setScale(2);String res = f.getValueS();assertEquals(Should equal:,100100,res0.toString();注意:公式中的变量取名不可与内置自定义变量名(可参考第25页第2章内置变量列表)相同,也不得与内置的函数名(可参考第26页第3章内置变量列表)相同,如果和内置变量相同,公式解析可能会得到不正确的结果;如果变量和内置函数名相同,则会报公式解析错误。1.5 如何从公式中提取变量单据模板和打印模板的公式解析要求可以解析识别公式中的列变量,以便从模板中取得相应的值赋给这些列变量。比如对下面的公式:String formulas = new Stringviewcode-getColValue(hyca_viewobj,viewcode,pk_viewobj, pk_viewobj) ,viewchinaname-getColValue(hyca_viewobj,viewchinaname,pk_viewobj1,pk_viewobj1),summny-cchmny*25/5+cchmnyFormulaParseFather f = new nc.ui.pub.formulaparse.FormulaParse();f.setExpressArray(formulas);得到的varrys的信息如下:varrys0: formulaName:viewcode;varry1:pk_viewobjvarrys1: formulaName:viewchinaname; varry1: pk_viewobj1varrys2: formulaName:summny; varry1:cchmny其中VarryVO的定义如下:public class VarryVOString formulaName=null;/公式名:等号左边String varry=null;/变量,等号右边的变量利用varrys的信息就可以从模板中取得相应列变量的值,并将公式返回的值赋给每行公式左边列名所对应的列。下面是提取变量的另一个例子,演示了从复杂的函数中提取列变量:String formulas = new Stringviewcode-hyca_viewobj*cvn(hyca_viewobj,viewcode,pk_viewobj,pk_viewobj)+ viewcode*cvs(hyca_viewobj,viewcode,pk_viewobj,pk_viewobj1),viewchinaname-getColNmV(hyca_viewobj,viewchinaname,pk_viewobj,pk_viewobj2);FormulaParseFather f = new nc.ui.pub.formulaparse.FormulaParse();f.setExpressArray(formulas);VarryVO varrys = f.getVarryArray();assertEquals(应该相等!,2, varrys.length);assertEquals(应该相等!,4, (varrys0.getVarry().length);1.6 空值”,NULL值及Zero值的处理在公式解析中,请注意区分以下三个概念:空值:指长度为0的字符串,NULL值:指没有分配任何空间的Object,类似JAVA语言里的NULLZero值:指Double(0)公式执行器有一个共有函数setNullAsZero(boolean value),用于设置在运算过程中是否需要将NULL值作为Zero值来进行运算。默认状态下,公式执行器设置setNullAsZero(false)。请看下面的例子:String formula = new Stringa-val1/val2,b-val1*val2;FormulaParseFather f= new FormulaParse();f.addVariable(val1,null);f.addVariable(val2,new Double(56);f.setExpressArray(formula);f.setNullAsZero(true); /设置为trueString res = f.getValueSArray();/val1当做Double(0)计算,得出a=0assertEquals(应该相等!, 0.00000000, res00);/val1当做Double(0)计算,得出b=0assertEquals(应该相等!, 0.00000000, res10);如果上例中setNullAsZero(false),因为null值无法参与运算,那么得到的结果为空。对于setNullAsZero函数,需要注意的是,NULL值当作Zero值来处理仅仅是指在运算过程中,不适用于返回值为NULL时的处理,例如:String formula = a-val1;FormulaParseFather f= new FormulaParse();f.addVariable(val1,null);f.setExpress(formula);f.setNullAsZero(true);String res = f.getValue();/val1当做Double(0)计算,但结果res为空值.如果上例中,想要返回0,则可以这么改写公式:String formula = a-toNumber(val1);FormulaParseFather f= new FormulaParse();f.addVariable(val1,null);f.setExpress(formula);f.setNullAsZero(true);String res = f.getValue();/结果res为0.00000000.当NULL值参与字符串连接时,NULL值自动被当作空值处理,setNullAsZero此时对运算结果无任何影响,例如:null+dddd+null;null+null+dddd;上述两个公式均应该返回dddd!关于NULL值,空值之间区别另一个需要注意的地方是在写IIF函数时,比较条件到底是NULL还是空值,需要根据实际场景决定,一般getColValue()等数据库查询函数如果无法查到值返回的是null值。例如:fkdw-iif(val2=null,val2 is zero,val2);fkdw-iif(val2=,val2 is zero,val2);iif(getColValue(hyca_viewobj, viewchinaname,pk_viewobj,pk_viewobj)=null,pk_viewobj,getColValue(hyca_viewobj, viewchinaname,pk_viewobj,pk_viewobj);1.7 如何进行列操作公式支持列操作,即列之间的+,-,*,/,%,=, currentMny + initMny下面是完整的代码示例:FormulaParseFather f = new nc.ui.pub.formulaparse.FormulaParse();f.setExpress(a-sum(cchmny,cchmny,2*cchmny);String v2 = new String3;v20 = 1; /row valuev21 = 2; /row valuev22 = 3;Map map = new HashMap();map.put(cchmny,v2);f.setDataSArray(map);String res = f.getValueS();assertEquals(应该相等!, 4.0, res0);assertEquals(应该相等!, 8.0, res1);assertEquals(应该相等!, 12.0, res2);1.8 利用定义函数扩展公式功能你可以在公式中加入自定义函数,为了这个目的,需要做两件事情:1. 从nc.vo.pub.expression.function.NcInnerFunction继承自己的函数处理类下面的例子说明了如何编写自己的函数处理类:public class MyFunction extends NcInnerFunctionpublic MyFunction()numberOfParameters = 0; /函数参数的个数/函数具体的运算,param中是具体的参数public Object function(List param) throws ParseExceptionif (param = null | param.size() != 0)throw new ParseException(错误:参数个数不对,mon()不需要参数!);UFDate date = new UFDate();return String.valueOf(date.getMonth();可以看出,主要在于编写function()函数,这个函数实现了具体的功能,即如何对传入的参数进行处理。2. 在公式执行器中加入自定义函数直接调用addFunction()函数加入你的函数类即可,funname即为你定义函数在公式中的名称。f.addFunction(funname, new yourFunctionClass();完成了上述两步工作,你就可以直接在你的公式中使用自定义的函数了!1.9 外接函数的使用本公式解析器支持外接函数6,即可以调用任何一个类中的函数,只要给出公式中函数名,类名,类中函数名,返回参数类型,函数参数类型等,就可以实现在公式中调用外接函数,比如下面的公式就调用了nc.vo.pub.expression.test.Customfunction1类的combineString函数。FormulaParseFather f = new nc.ui.pub.formulaparse.FormulaParse();f.setSelfMethod( combine, /公式中函数名nc.vo.pub.expression.test.Customfunction1, /类名combineString, /类中函数名ArrayList.class, /返回参数类型new ClassString.class,String.class); /函数参数类型f.setExpress(a-combine(cchmny, down);List v2 = new ArrayList();v2.add(cut); /row valuev2.add(go); /row valuev2.add(step);Map map = new HashMap();map.put(cchmny,v2);f.setDataSArray(map);String res = f.getValueS();assertEquals(Should equal:,cut down,res0);1.10 (5.02 new) 配置装载自定义函数UAP公式解析模块已经内置了许多常用的函数(参见附录),但是具体在开发应用产品时,这些内置的公式或许并不能完全满足需求,公式解析模块预留了扩展机制,二次开发人员可以根据实际产品或者项目的需要自行添加业务函数。1.8节已经介绍了如何开发自定义函数并通过代码方式注册函数,本小节主要介绍如何通过配置文件的方式注册自定义函数,以获得更好的灵活性。如果希望自定义的公式在所有的公式解析器中都可以使用,则在 NCHOME/resources/formulaconfig/custfunction 目录下添加任意文件名的xml注册文件(注意:默认发版盘中此目录下有一个default.xml文件,请不要删除也不要改名),文件格式形如: 0 hzg nc.vo.function.ivallen 0 hzg2 nc.vo.function.ivallen2 如果是特定模块的自定义公式,且公式解析器是区分模块构造的(V502后FormulaParseFather里增加了待模块号的构造函数),比如:FormulaParseFather f = new nc.ui.pub.formulaparse.FormulaParse(“modulesid”);则可以在上述目录下建立名为模块号的目录,然后将xml文件放到此明细目录中。例如注册仅test模块可以使用的公式,则注册xml文件要放到/resources/formulaconfig/custfunction/modulesid 目录下,并且要注意,该目录下一定要保证有一个名为default.xml的文件,所以如果只有一个配置文件,则该文件要命名为default.xml。1.11 运算符重载目前公式解析器支持下列运算符重载:运算符符号相应接口加+ IAddOperator: public Object add(Object obj)减- ISubOperator: Object sub(Object obj)乘* IMulOperator: Object multiply(Object obj)除/ IDivOperator: Object div(Object obj)另外如果用户对象要在公式中进行比较运算,请直接实现java.lang.Comparable接口,下面是一个操作符重载的示例:public class TestVO implements IAddOperator public String name;public int age;public TestVO(String name, int age) = name;this.age = age;/运算符重载public Object add(Object obj) throws ExceptionTestVO param2 = (TestVO)obj;String sname = name+;int sage = age+param2.age;TestVO res = new TestVO(sname,sage);return res;这样便可以在公式里对TestVO进行加法的运算:FormulaParseFather f =new nc.ui.pub.formulaparse.FormulaParse(); f.addVariable(v3, new UFDouble(3.0); f.setExpress(a-vo1+vo2); Map map =initVarMap(); f.setDataSArray(map); Object res = f.getValueO();assertEquals(Should equal:, cch1cch2, (TestVO) res0).name);assertEquals(Should equal:, 48, (TestVO) res0).age);其中nitVarMap()函数对vo1和vo2做了初始化:private Map initVarMap()List v1 = new ArrayList();v1.add(new TestVO(cch1, 24); /row valuev1.add(new TestVO(hey1, 25); /row valuev1.add(new TestVO(xuc1, 25);List v2 = new ArrayList();v2.add(new TestVO(cch2, 24); /row valuev2.add(new TestVO(hey2, 25); /row valuev2.add(new TestVO(xuc2, 23);Map map = new HashMap();map.put(vo1, v1);map.put(vo2, v2);return map;1.12 公式简单调试公式解析器通过debug函数,可以实现简单的调试功能,主要为了帮助分析公式每一步运行的情况,确定错误位置。请看下面debug函数用法示例。比如需要调试的公式如下:A-cvn(table,selectfield,wherefield,pkvalue);B-ZeroIfNull(A)*C/10;现在发现B字段就是没有得到值,那么首先就要确认A是否正确的从数据库查询到了值。添加调试语句如下:A-cvn(table,selectfield,wherefield,pkvalue);tempvar-debug(Variable A=+ A);B-ZeroIfNull(A)*C/10;也可以顺便输出B的值:A-cvn(table,selectfield,wherefield,pkvalue);tempvar-debug(Variable A=+ A);B-ZeroIfNull(A)*C/10;tempvar-debug(Variable B=+ B);注意上面的tempvar是一个临时变量,请务必加上且不要和公式变量及模板KEY重复,多个调试语句可以使用同一个临时变量,主要为了防止模板混淆。运行程序执行公式,便会输出调试记录形如:公式调试Variable A=45.23公式调试Variable B=这样就知道错误大概在什么地方了。1.13公式使用最佳实践公式技术红皮书1.2节提到过公式的一些基本用法,这里针对NC使用公式各场景常见的效率问题,总结一些如何更高效使用公式的原则,如果发现目前代码不符合以下提到的原则,请务必修改以达到更好的效率。1. 尽可能重用公式解析器实例重新构造一个新的公式解析器实例多少会耗用一些时间,一般来讲大约需要200ms的时间(根据具体机器配置及自定义函数数量不等),所以请避免出现下面这类代码:for(.)/在循环里构造公式解析器实例FormulaParseFather f = new nc.ui.pub.formulaparse.FormulaParse(); function()/频繁调用的方法里反复构造公式解析器实例FormulaParseFather f = new nc.ui.pub.formulaparse.FormulaParse();有些代码没有上述列举的这么明显,但是也是殊途同归,最终犯的错误跟上面提到的一样,编码的时候要多多注意,在保证线程安全的情况下,尽量重复使用同一个公式解析器实例。如果在前台,尽量通过成员变量重复使用同一个公式解析器实例,如果没有异步调用,甚至可以考虑使用静态变量保存一个公式解析器实例。如果在后台,则可以考虑使用ThreadLocal来保存一个公式解析器实例,做到线程级共享。可以参考NCFormulaHelper (Since V56),根据需要构造相应的公式解析器实例。/* * 取得一个后台线程安全,但是前台不保证线程安全的公式解析器实例,前台如果需要线程* 全,请调用: * getThreadSafeFormulaParser()方法. * return Returns the formulaParser. */public static FormulaParseFather getFormulaParser() 2. 尽可能减少公式查询要充分认识到公式查询是一个辅助手段,辅助带出(计算出)一些附加数据(称之为计算属性),不要过多的依靠公式解析,甚至想通过公式解析取代一部分业务代码,这将会导致比较严重的效率问题。有些后台代码,如果可以通过后台关联查询得到相关数据,则不推荐在后台代码里通过公式去查询,感觉走了很多弯路,效率也不会太好。当然类似报表模板这种通用框架,则必须借助模板公式才能达到配置的灵活性,另当别论。推荐对于一些数据量大的业务,数据库可以适当的考虑冗余,比如对于基本档案,管理档案这种相对固定的关联数据,可以考虑在业务单据数据表里同时存储管理档案主键和基本档案主键,避免大量的通过管理档案查询基本档案。供应链库存模块通过此类优化使得V56某些报表在走后台查询的情况下比V55走前台缓存还要快一些。今天库存开发人员又把库存的报表效率进行了优化,刚刚又几个主要慢报表的效率重新查询了一遍,结果如下:现存量 8
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 工程造价资料员培训课件
- 工程课件软件
- 二零二五年度城市综合体车位买卖及增值服务合同
- 二零二五版水电站施工三通一平合同范本
- 喜庆的元宵节作文(15篇)
- 工程维修流程培训课件
- 工业设备采购及安装调试服务合同
- 疟疾的诊断与防治
- 体育行业运动员伤病保险责任免除合同
- 渔业养殖及水产销售责任合同书
- 2025年公务员公共基础知识常识考试题库(500题)
- 仓库管理办法及存放标准
- 屋面施工安全措施方案
- 道路交通事故安全警示教育培训
- 关爱老人健康知识讲座
- 宣传用品发放管理办法
- 护士岗位准入管理办法
- 2025年上海市科学学研究所招聘考试笔试试题(含答案)
- 2025至2030中国少儿英语学习App行业调研及市场前景预测评估报告
- 音乐节现场灯光效果设计方案
- 心肌梗死护理疑难病例讨论
评论
0/150
提交评论