diff --git a/designer-base/src/main/java/com/fr/design/formula/FormulaPane.java b/designer-base/src/main/java/com/fr/design/formula/FormulaPane.java index 8b68dcdbe..d2756c28f 100644 --- a/designer-base/src/main/java/com/fr/design/formula/FormulaPane.java +++ b/designer-base/src/main/java/com/fr/design/formula/FormulaPane.java @@ -2,32 +2,67 @@ package com.fr.design.formula; import com.fr.base.BaseFormula; import com.fr.base.BaseUtils; +import com.fr.base.Parameter; +import com.fr.base.ParameterMapNameSpace; +import com.fr.base.TableDataNameSpace; +import com.fr.data.TableDataSource; import com.fr.design.actions.UpdateAction; import com.fr.design.border.UIRoundedBorder; import com.fr.design.constants.UIConstants; import com.fr.design.dialog.BasicDialog; import com.fr.design.dialog.BasicPane; +import com.fr.design.dialog.DialogActionAdapter; import com.fr.design.dialog.FineJOptionPane; +import com.fr.design.file.HistoryTemplateListCache; import com.fr.design.gui.ibutton.UIButton; import com.fr.design.gui.icontainer.UIScrollPane; import com.fr.design.gui.ilable.UILabel; import com.fr.design.gui.ilist.QuickList; +import com.fr.design.gui.itableeditorpane.ParameterTableModel; +import com.fr.design.gui.itableeditorpane.UITableEditorPane; import com.fr.design.gui.itextarea.UITextArea; import com.fr.design.gui.itextfield.UITextField; import com.fr.design.gui.syntax.ui.rsyntaxtextarea.RSyntaxTextArea; import com.fr.design.gui.syntax.ui.rsyntaxtextarea.SyntaxConstants; +import com.fr.design.i18n.Toolkit; import com.fr.design.layout.FRGUIPaneFactory; import com.fr.design.mainframe.DesignerContext; +import com.fr.design.parameter.ParameterInputPane; import com.fr.design.utils.gui.GUICoreUtils; import com.fr.general.ComparatorUtils; import com.fr.log.FineLoggerFactory; +import com.fr.main.impl.WorkBook; import com.fr.parser.FRLexer; import com.fr.parser.FRParser; +import com.fr.report.core.namespace.SimpleCellValueNameSpace; +import com.fr.script.Calculator; +import com.fr.script.ScriptConstants; +import com.fr.script.checker.FunctionCheckerDispatcher; +import com.fr.script.checker.exception.FunctionCheckWrongException; +import com.fr.script.rules.FunctionParameterType; +import com.fr.script.rules.FunctionRule; import com.fr.stable.EncodeConstants; +import com.fr.stable.ParameterProvider; import com.fr.stable.StringUtils; +import com.fr.stable.UtilEvalError; +import com.fr.stable.script.ColumnRowRange; import com.fr.stable.script.Expression; - -import javax.swing.*; +import com.fr.stable.script.Node; +import com.fr.stable.script.Tiny; +import com.fr.stable.script.TinyHunter; + +import javax.swing.BorderFactory; +import javax.swing.DefaultListCellRenderer; +import javax.swing.DefaultListModel; +import javax.swing.Icon; +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JList; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JTree; +import javax.swing.SwingUtilities; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.event.TreeSelectionEvent; @@ -37,7 +72,10 @@ import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.MutableTreeNode; import javax.swing.tree.TreePath; -import java.awt.*; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; @@ -55,7 +93,11 @@ import java.io.StringReader; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Set; /** * 公式编辑面板 @@ -65,6 +107,8 @@ import java.util.List; */ public class FormulaPane extends BasicPane implements KeyListener, UIFormula { + public static final String VALID_FORMULA = Toolkit.i18nText("Fine-Design_Basic_FormulaD_Valid_Formula"); + public static final String INVALID_FORMULA = Toolkit.i18nText("Fine-Design_Basic_FormulaD_Invalid_Formula"); private VariableTreeAndDescriptionArea variableTreeAndDescriptionArea; private RSyntaxTextArea formulaTextArea; private UITextField keyWordTextField = new UITextField(18); @@ -78,6 +122,7 @@ public class FormulaPane extends BasicPane implements KeyListener, UIFormula { private QuickList functionTypeList; private DefaultListModel functionNameModel; private JList functionNameList; + private UITableEditorPane editor4CalPane; public FormulaPane() { initComponents(); @@ -201,6 +246,7 @@ public class FormulaPane extends BasicPane implements KeyListener, UIFormula { private void initTextPane() { // text + JPanel textPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); this.add(textPane, BorderLayout.CENTER); JPanel checkBoxandbuttonPane = FRGUIPaneFactory.createNormalFlowInnerContainer_S_Pane(); @@ -217,13 +263,19 @@ public class FormulaPane extends BasicPane implements KeyListener, UIFormula { UIButton checkValidButton = new UIButton(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_FormulaD_Check_Valid")); + UIButton calButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Formula_Cal_Button")); checkValidButton.addActionListener(checkValidActionListener); + calButton.addActionListener(calculateActionListener); JPanel checkBoxPane = FRGUIPaneFactory.createNormalFlowInnerContainer_S_Pane(); checkBoxPane.setPreferredSize(new Dimension(450, 30)); checkBoxandbuttonPane.add(checkBoxPane, BorderLayout.WEST); checkBoxandbuttonPane.add(checkValidButton, BorderLayout.EAST); + checkBoxandbuttonPane.add(calButton, BorderLayout.EAST); extendCheckBoxPane(checkBoxPane); + + ParameterTableModel model = new ParameterTableModel(0); + editor4CalPane = new UITableEditorPane<>(model); } @@ -585,39 +637,150 @@ public class FormulaPane extends BasicPane implements KeyListener, UIFormula { } // check valid - private ActionListener checkValidActionListener = new ActionListener() { + private final ActionListener checkValidActionListener = new ActionListener() { public void actionPerformed(ActionEvent evt) { // Execute Formula default cell element. String formulaText = formulaTextArea.getText().trim(); + String formulaValidMessage = getFormulaValidMessage(formulaText); + FineJOptionPane.showMessageDialog( + FormulaPane.this, + formulaValidMessage + ".", + com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Tool_Tips"), + JOptionPane.INFORMATION_MESSAGE); + } + }; - if (formulaText != null && formulaText.length() > 0) { - StringReader in = new StringReader(formulaText); + private static String getFormulaValidMessage(String formulaText) { + StringReader in = new StringReader(formulaText); + + FRLexer lexer = new FRLexer(in); + FRParser parser = new FRParser(lexer); + + try { + Expression expression = parser.parse(); + Node node = expression.getConditionalExpression(); + return FunctionCheckerDispatcher.getInstance() + .getFunctionChecker(node) + .checkFunction(node) ? VALID_FORMULA : INVALID_FORMULA; + } catch (FunctionCheckWrongException ce) { + List rules = ce.getRules(); + String functionName = ce.getFunctionName(); + StringBuilder errorMsg = new StringBuilder(functionName + com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Formula_Check_Error_Tips") + ":"); + for (int i = 0; i < rules.size(); i++) { + errorMsg.append("("); + for (FunctionParameterType functionParameterType : rules.get(i).getParameterList()) { + errorMsg.append(getTypeString(functionParameterType)).append(","); + } + if (",".equals(errorMsg.charAt(errorMsg.length() - 1) + "")) { + errorMsg.deleteCharAt(errorMsg.length() - 1); + } + errorMsg.append(")"); + if (i != rules.size() - 1) { + errorMsg.append(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Formula_Check_Or")); + } + } + return errorMsg.toString(); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + return INVALID_FORMULA; + // alex:继续往下面走,expression为null时告知不合法公式 + } + } + + private static String getTypeString(FunctionParameterType type) { + switch (type) { + case NUMBER: + return com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Formula_Check_ParamType_Number"); + case STRING: + return com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Formula_Check_ParamType_String"); + case ANY: + return com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Formula_Check_ParamType_Any"); + case DATE: + return com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Formula_Check_ParamType_Date"); + case BOOLEAN: + return com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Formula_Check_ParamType_Boolean"); + case ARRAY: + return com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Formula_Check_ParamType_Array"); + } + return ""; + } - FRLexer lexer = new FRLexer(in); - FRParser parser = new FRParser(lexer); + private final ActionListener calculateActionListener = new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + String formulaText = formulaTextArea.getText().trim(); + String messageTips = getFormulaValidMessage(formulaText).equals(VALID_FORMULA) ? " " : getFormulaValidMessage(formulaText) + "\n"; + Map paramsMap = setParamsIfExist(formulaText); + Calculator calculator = Calculator.createCalculator(); + ParameterMapNameSpace parameterMapNameSpace = ParameterMapNameSpace.create(paramsMap); + calculator.pushNameSpace(parameterMapNameSpace); + + WorkBook workBook = (WorkBook) HistoryTemplateListCache.getInstance().getCurrentEditingTemplate().getTarget(); + calculator.pushNameSpace(SimpleCellValueNameSpace.getInstance()); + TableDataSource tableDataSource = workBook.getReport(0).getTableDataSource(); + calculator.setAttribute(TableDataSource.KEY, tableDataSource); + calculator.pushNameSpace(TableDataNameSpace.getInstance()); + BaseFormula baseFormula = BaseFormula.createFormulaBuilder().build(formulaText); + try { + Object value = calculator.evalValue(baseFormula); + messageTips = messageTips + com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Formula_Cal_Result") + ":" + value; + FineLoggerFactory.getLogger().info("value:{}", value); + } catch (UtilEvalError utilEvalError) { + FineLoggerFactory.getLogger().error("", utilEvalError); + } + FineJOptionPane.showMessageDialog( + FormulaPane.this, + messageTips, + com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Tool_Tips"), + JOptionPane.INFORMATION_MESSAGE); - Expression expression = null; - try { - expression = parser.parse(); - } catch (Exception e) { - FineLoggerFactory.getLogger().error(e.getMessage(), e); - // alex:继续往下面走,expression为null时告知不合法公式 + } + }; + + private Map setParamsIfExist(String formulaText) { + Map parameterMap = new HashMap<>(); + try { + Expression expression = Calculator.createCalculator().parse(formulaText); + ParameterCellHunter parameterCellHunter = new ParameterCellHunter(); + expression.traversal4Tiny(parameterCellHunter); + Parameter[] parameters = parameterCellHunter.getParameterBooty(); + + if (parameters.length < 1 && editor4CalPane.update().size() < 1) { + return parameterMap; + } + ParameterInputPane pPane = new ParameterInputPane(parameters); + pPane.showSmallWindow(new JFrame(), new DialogActionAdapter() { + @Override + public void doOk() { + parameterMap.putAll(pPane.update()); } + }).setVisible(true); + } catch (Exception e) { + FineLoggerFactory.getLogger().error("", e); + } + + return parameterMap; + } + + private static class ParameterCellHunter extends TinyHunter { + + private final Set parameterList = new HashSet<>(); + + Parameter[] getParameterBooty() { - FineJOptionPane.showMessageDialog( - FormulaPane.this, - /* - * alex:仅仅只需要根据expression是否为null作合法性判断 - * 不需要eval - * TODO 但有个问题,有些函数的参数个数是有规定的,何以判别之 - */ - (expression != null ? com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_FormulaD_Valid_Formula") : com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_FormulaD_Invalid_Formula")) + ".", - com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Tool_Tips"), - JOptionPane.INFORMATION_MESSAGE); + return parameterList.toArray(new Parameter[0]); + } + + public void hunter4Tiny(Tiny tiny) { + String statement = tiny.getStatement(); + if (StringUtils.isNotBlank(statement) && statement.startsWith(ScriptConstants.DETAIL_TAG)) { + parameterList.add(new Parameter(statement.substring(1))); + } else if (tiny.getClass().equals(ColumnRowRange.class)) { + parameterList.add(new Parameter(tiny.toString())); } } - }; + } public class VariableTreeAndDescriptionArea extends JPanel { @@ -805,13 +968,13 @@ public class FormulaPane extends BasicPane implements KeyListener, UIFormula { public void valueChanged(TreeSelectionEvent e) { DefaultMutableTreeNode selectedTreeNode = (DefaultMutableTreeNode) variablesTree.getLastSelectedPathComponent(); Object selectedValue = selectedTreeNode.getUserObject(); - Object selectedParentValue = ((DefaultMutableTreeNode)selectedTreeNode.getParent()).getUserObject(); + Object selectedParentValue = ((DefaultMutableTreeNode) selectedTreeNode.getParent()).getUserObject(); if (selectedValue == null) { return; } - if (selectedValue instanceof TextUserObject) { + if (selectedValue instanceof TextUserObject) { //有公式说明的条件:1.属于TextUserObject 2.parent是系统参数 if (ComparatorUtils.equals(((TextFolderUserObject) selectedParentValue).getText(), com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_FormulaPane_Variables"))) { @@ -1010,6 +1173,7 @@ public class FormulaPane extends BasicPane implements KeyListener, UIFormula { /** * 把以关键词开头的和不以关键词开头的分别按照字母表顺序排序 + * * @param o1 待比较对象1 * @param o2 待比较对象2 * @return 比较结果,1表示 o1 > o2, -1表示 o1 < o2, 0表示 o1 = o2