Browse Source

Pull request #5382: REPORT-54887 公式编辑器优化一期

Merge in DESIGN/design from ~HOKY.HE/design-hoky:feature/x to feature/x

* commit 'b24ab29cd62d587fe00af701b358340424a71dff':
  REPORT-54887 公式编辑器优化一期 1.国际化; 2.修改一些提示逻辑。
  REPORT-54887 公式编辑器优化一期 1.修改一些规范; 2.补充condition逻辑
  REPORT-54887 公式编辑器优化一期 1.添加了公式的参数个数和参数类型的校验; 2.添加了实时计算的功能。
research/11.0
Hoky.He 3 years ago
parent
commit
175063ba89
  1. 221
      designer-base/src/main/java/com/fr/design/formula/FormulaPane.java

221
designer-base/src/main/java/com/fr/design/formula/FormulaPane.java

@ -2,32 +2,68 @@ package com.fr.design.formula;
import com.fr.base.BaseFormula; import com.fr.base.BaseFormula;
import com.fr.base.BaseUtils; 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.actions.UpdateAction;
import com.fr.design.border.UIRoundedBorder; import com.fr.design.border.UIRoundedBorder;
import com.fr.design.constants.UIConstants; import com.fr.design.constants.UIConstants;
import com.fr.design.dialog.BasicDialog; import com.fr.design.dialog.BasicDialog;
import com.fr.design.dialog.BasicPane; import com.fr.design.dialog.BasicPane;
import com.fr.design.dialog.DialogActionAdapter;
import com.fr.design.dialog.FineJOptionPane; import com.fr.design.dialog.FineJOptionPane;
import com.fr.design.file.HistoryTemplateListCache;
import com.fr.design.gui.ibutton.UIButton; import com.fr.design.gui.ibutton.UIButton;
import com.fr.design.gui.icontainer.UIScrollPane; import com.fr.design.gui.icontainer.UIScrollPane;
import com.fr.design.gui.ilable.UILabel; import com.fr.design.gui.ilable.UILabel;
import com.fr.design.gui.ilist.QuickList; 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.itextarea.UITextArea;
import com.fr.design.gui.itextfield.UITextField; import com.fr.design.gui.itextfield.UITextField;
import com.fr.design.gui.syntax.ui.rsyntaxtextarea.RSyntaxTextArea; import com.fr.design.gui.syntax.ui.rsyntaxtextarea.RSyntaxTextArea;
import com.fr.design.gui.syntax.ui.rsyntaxtextarea.SyntaxConstants; 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.layout.FRGUIPaneFactory;
import com.fr.design.mainframe.DesignerContext; import com.fr.design.mainframe.DesignerContext;
import com.fr.design.parameter.ParameterInputPane;
import com.fr.design.utils.gui.GUICoreUtils; import com.fr.design.utils.gui.GUICoreUtils;
import com.fr.general.ComparatorUtils; import com.fr.general.ComparatorUtils;
import com.fr.log.FineLoggerFactory; import com.fr.log.FineLoggerFactory;
import com.fr.main.impl.WorkBook;
import com.fr.parser.FRLexer; import com.fr.parser.FRLexer;
import com.fr.parser.FRParser; 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.ConditionCheckWrongException;
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.EncodeConstants;
import com.fr.stable.ParameterProvider;
import com.fr.stable.StringUtils; import com.fr.stable.StringUtils;
import com.fr.stable.UtilEvalError;
import com.fr.stable.script.ColumnRowRange;
import com.fr.stable.script.Expression; import com.fr.stable.script.Expression;
import com.fr.stable.script.Node;
import javax.swing.*; 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.ListSelectionEvent;
import javax.swing.event.ListSelectionListener; import javax.swing.event.ListSelectionListener;
import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionEvent;
@ -37,7 +73,10 @@ import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.MutableTreeNode; import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreePath; 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.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter; import java.awt.event.KeyAdapter;
@ -55,7 +94,11 @@ import java.io.StringReader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set;
/** /**
* 公式编辑面板 * 公式编辑面板
@ -65,6 +108,8 @@ import java.util.List;
*/ */
public class FormulaPane extends BasicPane implements KeyListener, UIFormula { 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 VariableTreeAndDescriptionArea variableTreeAndDescriptionArea;
private RSyntaxTextArea formulaTextArea; private RSyntaxTextArea formulaTextArea;
private UITextField keyWordTextField = new UITextField(18); private UITextField keyWordTextField = new UITextField(18);
@ -78,6 +123,7 @@ public class FormulaPane extends BasicPane implements KeyListener, UIFormula {
private QuickList functionTypeList; private QuickList functionTypeList;
private DefaultListModel functionNameModel; private DefaultListModel functionNameModel;
private JList functionNameList; private JList functionNameList;
private UITableEditorPane<ParameterProvider> editor4CalPane;
public FormulaPane() { public FormulaPane() {
initComponents(); initComponents();
@ -201,6 +247,7 @@ public class FormulaPane extends BasicPane implements KeyListener, UIFormula {
private void initTextPane() { private void initTextPane() {
// text // text
JPanel textPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); JPanel textPane = FRGUIPaneFactory.createBorderLayout_S_Pane();
this.add(textPane, BorderLayout.CENTER); this.add(textPane, BorderLayout.CENTER);
JPanel checkBoxandbuttonPane = FRGUIPaneFactory.createNormalFlowInnerContainer_S_Pane(); JPanel checkBoxandbuttonPane = FRGUIPaneFactory.createNormalFlowInnerContainer_S_Pane();
@ -217,13 +264,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 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); checkValidButton.addActionListener(checkValidActionListener);
calButton.addActionListener(calculateActionListener);
JPanel checkBoxPane = FRGUIPaneFactory.createNormalFlowInnerContainer_S_Pane(); JPanel checkBoxPane = FRGUIPaneFactory.createNormalFlowInnerContainer_S_Pane();
checkBoxPane.setPreferredSize(new Dimension(450, 30)); checkBoxPane.setPreferredSize(new Dimension(450, 30));
checkBoxandbuttonPane.add(checkBoxPane, BorderLayout.WEST); checkBoxandbuttonPane.add(checkBoxPane, BorderLayout.WEST);
checkBoxandbuttonPane.add(checkValidButton, BorderLayout.EAST); checkBoxandbuttonPane.add(checkValidButton, BorderLayout.EAST);
checkBoxandbuttonPane.add(calButton, BorderLayout.EAST);
extendCheckBoxPane(checkBoxPane); extendCheckBoxPane(checkBoxPane);
ParameterTableModel model = new ParameterTableModel(0);
editor4CalPane = new UITableEditorPane<>(model);
} }
@ -585,40 +638,163 @@ public class FormulaPane extends BasicPane implements KeyListener, UIFormula {
} }
// check valid // check valid
private ActionListener checkValidActionListener = new ActionListener() { private final ActionListener checkValidActionListener = new ActionListener() {
public void actionPerformed(ActionEvent evt) { public void actionPerformed(ActionEvent evt) {
// Execute Formula default cell element. // Execute Formula default cell element.
String formulaText = formulaTextArea.getText().trim(); 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) { private static String getFormulaValidMessage(String formulaText) {
StringReader in = new StringReader(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 (ConditionCheckWrongException cce) {
String functionName = cce.getFunctionName();
StringBuilder errorMsg = new StringBuilder(functionName + com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Formula_Check_Condition_Tips") + ":");
return errorMsg.toString();
} catch (FunctionCheckWrongException ce) {
List<FunctionRule> 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时告知不合法公式
}
}
FRLexer lexer = new FRLexer(in); private static String getTypeString(FunctionParameterType type) {
FRParser parser = new FRParser(lexer); 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 "";
}
Expression expression = null; private final ActionListener calculateActionListener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String formulaText = formulaTextArea.getText().trim();
String formulaValidMessage = getFormulaValidMessage(formulaText);
if (formulaValidMessage.equals(INVALID_FORMULA)) {
FineJOptionPane.showMessageDialog(
FormulaPane.this,
formulaValidMessage,
com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Tool_Tips"),
JOptionPane.INFORMATION_MESSAGE);
} else {
String messageTips = formulaValidMessage.equals(VALID_FORMULA) ? "" : formulaValidMessage + "\n";
Map<String, Object> 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 { try {
expression = parser.parse(); Object value = calculator.evalValue(baseFormula);
} catch (Exception e) { messageTips = messageTips + com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Formula_Cal_Result") + ":" + value;
FineLoggerFactory.getLogger().error(e.getMessage(), e); FineLoggerFactory.getLogger().info("value:{}", value);
// alex:继续往下面走,expression为null时告知不合法公式 } catch (UtilEvalError utilEvalError) {
FineLoggerFactory.getLogger().error("", utilEvalError);
} }
FineJOptionPane.showMessageDialog( FineJOptionPane.showMessageDialog(
FormulaPane.this, FormulaPane.this,
/* messageTips,
* 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"), com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Tool_Tips"),
JOptionPane.INFORMATION_MESSAGE); JOptionPane.INFORMATION_MESSAGE);
} }
} }
}; };
private Map<String, Object> setParamsIfExist(String formulaText) {
Map<String, Object> 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<Parameter> parameterList = new HashSet<>();
Parameter[] getParameterBooty() {
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 { public class VariableTreeAndDescriptionArea extends JPanel {
private JTree variablesTree; private JTree variablesTree;
@ -805,13 +981,13 @@ public class FormulaPane extends BasicPane implements KeyListener, UIFormula {
public void valueChanged(TreeSelectionEvent e) { public void valueChanged(TreeSelectionEvent e) {
DefaultMutableTreeNode selectedTreeNode = (DefaultMutableTreeNode) variablesTree.getLastSelectedPathComponent(); DefaultMutableTreeNode selectedTreeNode = (DefaultMutableTreeNode) variablesTree.getLastSelectedPathComponent();
Object selectedValue = selectedTreeNode.getUserObject(); Object selectedValue = selectedTreeNode.getUserObject();
Object selectedParentValue = ((DefaultMutableTreeNode)selectedTreeNode.getParent()).getUserObject(); Object selectedParentValue = ((DefaultMutableTreeNode) selectedTreeNode.getParent()).getUserObject();
if (selectedValue == null) { if (selectedValue == null) {
return; return;
} }
if (selectedValue instanceof TextUserObject) { if (selectedValue instanceof TextUserObject) {
//有公式说明的条件:1.属于TextUserObject 2.parent是系统参数 //有公式说明的条件:1.属于TextUserObject 2.parent是系统参数
if (ComparatorUtils.equals(((TextFolderUserObject) selectedParentValue).getText(), if (ComparatorUtils.equals(((TextFolderUserObject) selectedParentValue).getText(),
com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_FormulaPane_Variables"))) { com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_FormulaPane_Variables"))) {
@ -1010,6 +1186,7 @@ public class FormulaPane extends BasicPane implements KeyListener, UIFormula {
/** /**
* 把以关键词开头的和不以关键词开头的分别按照字母表顺序排序 * 把以关键词开头的和不以关键词开头的分别按照字母表顺序排序
*
* @param o1 待比较对象1 * @param o1 待比较对象1
* @param o2 待比较对象2 * @param o2 待比较对象2
* @return 比较结果1表示 o1 > o2, -1表示 o1 < o2, 0表示 o1 = o2 * @return 比较结果1表示 o1 > o2, -1表示 o1 < o2, 0表示 o1 = o2

Loading…
Cancel
Save