/* * (c) Copyright 2010-2011 AgileBirds * * This file is part of OpenFlexo. * * OpenFlexo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenFlexo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>. * */ package org.openflexo.fib.utils; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.border.TitledBorder; import org.openflexo.antar.binding.AbstractBinding; import org.openflexo.antar.binding.Bindable; import org.openflexo.antar.binding.BindingDefinition; import org.openflexo.antar.binding.BindingDefinition.BindingDefinitionType; import org.openflexo.antar.binding.BindingExpression; import org.openflexo.antar.binding.BindingExpression.BindingValueConstant; import org.openflexo.antar.binding.BindingExpression.BindingValueVariable; import org.openflexo.antar.binding.BindingExpressionFactory; import org.openflexo.antar.binding.BindingValue; import org.openflexo.antar.binding.StaticBinding; import org.openflexo.antar.binding.TypeUtils; import org.openflexo.antar.expr.ArithmeticBinaryOperator; import org.openflexo.antar.expr.ArithmeticUnaryOperator; import org.openflexo.antar.expr.BinaryOperator; import org.openflexo.antar.expr.BinaryOperatorExpression; import org.openflexo.antar.expr.BooleanBinaryOperator; import org.openflexo.antar.expr.BooleanUnaryOperator; import org.openflexo.antar.expr.ConditionalExpression; import org.openflexo.antar.expr.DefaultExpressionPrettyPrinter; import org.openflexo.antar.expr.EvaluationType; import org.openflexo.antar.expr.Expression; import org.openflexo.antar.expr.NullReferenceException; import org.openflexo.antar.expr.Operator; import org.openflexo.antar.expr.OperatorNotSupportedException; import org.openflexo.antar.expr.SymbolicConstant; import org.openflexo.antar.expr.TypeMismatchException; import org.openflexo.antar.expr.UnaryOperator; import org.openflexo.antar.expr.UnaryOperatorExpression; import org.openflexo.antar.pp.ExpressionPrettyPrinter; import org.openflexo.fib.model.FIBModelObject; import org.openflexo.localization.FlexoLocalization; import org.openflexo.swing.MouseOverButton; public class BindingExpressionPanel extends JPanel implements FocusListener { static final Logger logger = Logger.getLogger(BindingExpressionPanel.class.getPackage().getName()); BindingExpression _bindingExpression; protected static ImageIcon iconForOperator(Operator op) { if (op == ArithmeticBinaryOperator.ADDITION) { return FIBIconLibrary.ADDITION_ICON; } else if (op == ArithmeticBinaryOperator.SUBSTRACTION) { return FIBIconLibrary.SUBSTRACTION_ICON; } else if (op == ArithmeticBinaryOperator.MULTIPLICATION) { return FIBIconLibrary.MULTIPLICATION_ICON; } else if (op == ArithmeticBinaryOperator.DIVISION) { return FIBIconLibrary.DIVISION_ICON; } else if (op == ArithmeticBinaryOperator.POWER) { return FIBIconLibrary.POWER_ICON; } else if (op == BooleanBinaryOperator.EQUALS) { return FIBIconLibrary.EQUALS_ICON; } else if (op == BooleanBinaryOperator.NOT_EQUALS) { return FIBIconLibrary.NOT_EQUALS_ICON; } else if (op == BooleanBinaryOperator.LESS_THAN) { return FIBIconLibrary.LESS_THAN_ICON; } else if (op == BooleanBinaryOperator.LESS_THAN_OR_EQUALS) { return FIBIconLibrary.LESS_THAN_OR_EQUALS_ICON; } else if (op == BooleanBinaryOperator.GREATER_THAN) { return FIBIconLibrary.GREATER_THAN_ICON; } else if (op == BooleanBinaryOperator.GREATER_THAN_OR_EQUALS) { return FIBIconLibrary.GREATER_THAN_OR_EQUALS_ICON; } else if (op == BooleanBinaryOperator.AND) { return FIBIconLibrary.AND_ICON; } else if (op == BooleanBinaryOperator.OR) { return FIBIconLibrary.OR_ICON; } else if (op == BooleanUnaryOperator.NOT) { return FIBIconLibrary.NOT_ICON; } else if (op == ArithmeticUnaryOperator.UNARY_MINUS) { return FIBIconLibrary.SUBSTRACTION_ICON; } else if (op == ArithmeticUnaryOperator.SIN) { return FIBIconLibrary.SIN_ICON; } else if (op == ArithmeticUnaryOperator.ASIN) { return FIBIconLibrary.ASIN_ICON; } else if (op == ArithmeticUnaryOperator.COS) { return FIBIconLibrary.COS_ICON; } else if (op == ArithmeticUnaryOperator.ACOS) { return FIBIconLibrary.ACOS_ICON; } else if (op == ArithmeticUnaryOperator.TAN) { return FIBIconLibrary.TAN_ICON; } else if (op == ArithmeticUnaryOperator.ATAN) { return FIBIconLibrary.ATAN_ICON; } else if (op == ArithmeticUnaryOperator.EXP) { return FIBIconLibrary.EXP_ICON; } else if (op == ArithmeticUnaryOperator.LOG) { return FIBIconLibrary.LOG_ICON; } else if (op == ArithmeticUnaryOperator.SQRT) { return FIBIconLibrary.SQRT_ICON; } return null; } public BindingExpressionPanel(BindingExpression bindingExpression) { super(); converter = bindingExpression != null ? bindingExpression.getConverter() : null; // converter = AbstractBinding.bindingExpressionConverter; setLayout(new BorderLayout()); _bindingExpression = bindingExpression; init(); } public void delete() { if (rootExpressionPanel != null) { rootExpressionPanel.delete(); } rootExpressionPanel = null; converter = null; _bindingExpression = null; } private JTextArea expressionTA; private JPanel controls; private ExpressionInnerPanel rootExpressionPanel; private enum ExpressionParsingStatus { UNDEFINED, VALID, SYNTAXICALLY_VALID, INVALID } private ExpressionParsingStatus status = ExpressionParsingStatus.UNDEFINED; private String message = UNDEFINED_EXPRESSION_MESSAGE; private static final String UNDEFINED_EXPRESSION_MESSAGE = "please_define_expression"; private static final String UNDEFINED_OPERAND_FOR_OPERATOR = "undefined_operand_for_operator"; private static final String VALID_EXPRESSION = "expression_is_valid_and_conform_to_required_type"; private static final String VALID_EXPRESSION_BUT_MISMATCH_TYPE = "expression_is_valid_but_not_conform_to_requested_type_(found:($0)_expected:($1))"; private JLabel statusIcon; private JLabel messageLabel; private JTextArea evaluationTA; protected JPanel evaluationPanel; protected ExpressionPrettyPrinter pp = new DefaultExpressionPrettyPrinter(); protected BindingExpressionFactory converter; private ExpressionInnerPanel focusReceiver = null; public void setEditedExpression(BindingExpression bindingExpression) { converter = bindingExpression != null ? bindingExpression.getConverter() : null; _bindingExpression = bindingExpression; if (bindingExpression != null) { _setEditedExpression(bindingExpression.getExpression()); if (rootExpressionPanel.getRepresentedExpression() == null || !rootExpressionPanel.getRepresentedExpression().equals(bindingExpression.getExpression())) { rootExpressionPanel.setRepresentedExpression(bindingExpression.getExpression()); } } update(); } protected void setEditedExpression(Expression expression) { _setEditedExpression(expression); update(); fireEditedExpressionChanged(_bindingExpression); } private void _setEditedExpression(Expression expression) { if (_bindingExpression != null) { _bindingExpression.setExpression(expression); } _checkEditedExpression(); } protected void _checkEditedExpression() { Operator undefinedOperator = null; if (_bindingExpression == null) { return; } if (evaluationPanel != null && evaluationTA != null && evaluationPanel.isVisible()) { evaluationTA.setText(FlexoLocalization.localizedForKey(FIBModelObject.LOCALIZATION, "cannot_evaluate")); } if (_bindingExpression.getExpression() != null) { if (expressionIsUndefined(_bindingExpression.getExpression())) { status = ExpressionParsingStatus.UNDEFINED; message = FlexoLocalization.localizedForKey(FIBModelObject.LOCALIZATION, UNDEFINED_EXPRESSION_MESSAGE); return; } else { for (Expression e : _bindingExpression.getExpression().getAllAtomicExpressions()) { if (e instanceof BindingValueConstant) { BindingValueConstant c = (BindingValueConstant) e; if (c.getStaticBinding() == null || !c.getStaticBinding().isBindingValid()) { message = FlexoLocalization.localizedForKey(FIBModelObject.LOCALIZATION, "invalid_value") + " " + c.getConstant(); status = ExpressionParsingStatus.INVALID; return; } } if (e instanceof BindingValueVariable) { BindingValueVariable v = (BindingValueVariable) e; if (v.getBindingValue() == null || !v.getBindingValue().isBindingValid()) { message = FlexoLocalization.localizedForKey(FIBModelObject.LOCALIZATION, "invalid_binding") + " " + v.getVariable(); status = ExpressionParsingStatus.INVALID; return; } } } } } if (_bindingExpression.getExpression() == null) { message = FlexoLocalization.localizedForKey(FIBModelObject.LOCALIZATION, "cannot_parse") + " " + _bindingExpression.getUnparsableValue(); status = ExpressionParsingStatus.INVALID; } else if ((undefinedOperator = firstOperatorWithUndefinedOperand(_bindingExpression.getExpression())) != null) { status = ExpressionParsingStatus.INVALID; try { message = FlexoLocalization.localizedForKey(FIBModelObject.LOCALIZATION, UNDEFINED_OPERAND_FOR_OPERATOR) + " " + undefinedOperator.getLocalizedName() + " [" + pp.getSymbol(undefinedOperator) + "]"; } catch (OperatorNotSupportedException e) { message = FlexoLocalization.localizedForKey(FIBModelObject.LOCALIZATION, UNDEFINED_OPERAND_FOR_OPERATOR) + " " + undefinedOperator.getLocalizedName() + " [?]"; } } else { try { EvaluationType evaluationType = _bindingExpression.getEvaluationType(); if (_bindingExpression != null && _bindingExpression.getBindingDefinition() != null && _bindingExpression.getBindingDefinition().getType() != null) { EvaluationType wantedEvaluationType = TypeUtils.kindOfType(_bindingExpression.getBindingDefinition().getType()); if (wantedEvaluationType == EvaluationType.LITERAL || evaluationType == wantedEvaluationType || wantedEvaluationType == EvaluationType.ARITHMETIC_FLOAT && evaluationType == EvaluationType.ARITHMETIC_INTEGER) { status = ExpressionParsingStatus.VALID; message = FlexoLocalization.localizedForKey(FIBModelObject.LOCALIZATION, VALID_EXPRESSION) + " : " + evaluationType.getLocalizedName(); } else { status = ExpressionParsingStatus.SYNTAXICALLY_VALID; message = FlexoLocalization.localizedForKeyWithParams(FIBModelObject.LOCALIZATION, VALID_EXPRESSION_BUT_MISMATCH_TYPE, evaluationType.getLocalizedName(), wantedEvaluationType.getLocalizedName()); } } else { status = ExpressionParsingStatus.VALID; message = FlexoLocalization.localizedForKey(FIBModelObject.LOCALIZATION, VALID_EXPRESSION) + " : " + evaluationType.getLocalizedName(); } if (evaluationPanel != null && evaluationTA != null && evaluationPanel.isVisible() && _bindingExpression != null) { BindingExpression evaluatedExpression = _bindingExpression.evaluate(); if (evaluatedExpression.getExpression() != null) { evaluationTA.setText(evaluatedExpression.getExpression().toString()); } } } catch (TypeMismatchException e) { status = ExpressionParsingStatus.INVALID; message = e.getHTMLLocalizedMessage(); } catch (NullReferenceException e) { status = ExpressionParsingStatus.INVALID; message = e.getHTMLLocalizedMessage(); } } } private boolean expressionIsUndefined(Expression expression) { return expression instanceof BindingValueVariable && ((BindingValueVariable) expression).getName().trim().equals(""); } private Operator firstOperatorWithUndefinedOperand(Expression expression) { if (expression instanceof BindingValueVariable) { return null; } else if (expression instanceof BindingValueConstant) { return null; } else if (expression instanceof BinaryOperatorExpression) { Expression leftOperand = ((BinaryOperatorExpression) expression).getLeftArgument(); if (expressionIsUndefined(leftOperand)) { return ((BinaryOperatorExpression) expression).getOperator(); } Operator returned = firstOperatorWithUndefinedOperand(leftOperand); if (returned == null) { Expression rightOperand = ((BinaryOperatorExpression) expression).getRightArgument(); if (expressionIsUndefined(rightOperand)) { return ((BinaryOperatorExpression) expression).getOperator(); } return firstOperatorWithUndefinedOperand(rightOperand); } } else if (expression instanceof UnaryOperatorExpression) { Expression operand = ((UnaryOperatorExpression) expression).getArgument(); if (expressionIsUndefined(operand)) { return ((UnaryOperatorExpression) expression).getOperator(); } return firstOperatorWithUndefinedOperand(operand); } return null; } public BindingExpression getEditedExpression() { return _bindingExpression; } protected void expressionMayHaveBeenEdited() { if (_bindingExpression == null) { return; } try { Expression newExpression; if (expressionTA.getText().trim().equals("") && _bindingExpression.getOwner() instanceof Bindable) { newExpression = new BindingExpression.BindingValueVariable("", _bindingExpression.getOwner()); } else { if (converter == null) { logger.warning("Could not access to BindingExpressionConverter"); return; } Bindable bindable = null; if (_bindingExpression != null) { bindable = _bindingExpression.getOwner(); } newExpression = converter.parseExpressionFromString(expressionTA.getText(), bindable); } if (!newExpression.equals(_bindingExpression.getExpression()) || status == ExpressionParsingStatus.INVALID) { _setEditedExpression(newExpression); rootExpressionPanel.setRepresentedExpression(_bindingExpression.getExpression()); update(); fireEditedExpressionChanged(_bindingExpression); } } catch (org.openflexo.antar.expr.parser.ParseException e) { message = "ERROR: cannot parse " + expressionTA.getText(); status = ExpressionParsingStatus.INVALID; updateAdditionalInformations(); } } private void init() { expressionTA = new JTextArea(3, 50); expressionTA.setLineWrap(true); // expressionTA.setWrapStyleWord(true); expressionTA.addKeyListener(new KeyAdapter() { @Override public void keyTyped(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER) { expressionMayHaveBeenEdited(); } if (e.getKeyChar() == KeyEvent.VK_ENTER) { expressionMayHaveBeenEdited(); } } }); expressionTA.addFocusListener(new FocusAdapter() { @Override public void focusLost(FocusEvent e) { expressionMayHaveBeenEdited(); } }); /*expressionTA.addMouseListener(new MouseAdapter(){ @Override public void mouseClicked(MouseEvent e) { expressionMayHaveBeenEdited(); } });*/ statusIcon = new JLabel(); messageLabel = new JLabel("", SwingConstants.LEFT); messageLabel.setFont(new Font("SansSerif", Font.ITALIC, 9)); evaluationPanel = new JPanel(new BorderLayout()); evaluationTA = new JTextArea(1, 20); evaluationTA.setEditable(false); evaluationTA.setLineWrap(true); evaluationPanel.add(new JLabel(FlexoLocalization.localizedForKey(FIBModelObject.LOCALIZATION, "evaluation") + " "), BorderLayout.WEST); evaluationPanel.add(evaluationTA, BorderLayout.CENTER); final MouseOverButton showEvaluationButton = new MouseOverButton(); showEvaluationButton.setBorder(BorderFactory.createEmptyBorder()); showEvaluationButton.setNormalIcon(FIBIconLibrary.TOGGLE_ARROW_BOTTOM_ICON); showEvaluationButton.setMouseOverIcon(FIBIconLibrary.TOGGLE_ARROW_BOTTOM_SELECTED_ICON); showEvaluationButton.setToolTipText(FlexoLocalization.localizedForKey(FIBModelObject.LOCALIZATION, "show_evaluation")); showEvaluationButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (!evaluationPanel.isVisible()) { showEvaluationButton.setNormalIcon(FIBIconLibrary.TOGGLE_ARROW_TOP_ICON); showEvaluationButton.setMouseOverIcon(FIBIconLibrary.TOGGLE_ARROW_TOP_SELECTED_ICON); showEvaluationButton.setToolTipText(FlexoLocalization.localizedForKey(FIBModelObject.LOCALIZATION, "hide_evaluation")); evaluationPanel.setVisible(true); _checkEditedExpression(); } else { showEvaluationButton.setNormalIcon(FIBIconLibrary.TOGGLE_ARROW_BOTTOM_ICON); showEvaluationButton.setMouseOverIcon(FIBIconLibrary.TOGGLE_ARROW_BOTTOM_SELECTED_ICON); showEvaluationButton.setToolTipText(FlexoLocalization.localizedForKey(FIBModelObject.LOCALIZATION, "show_evaluation")); evaluationPanel.setVisible(false); } } }); JPanel statusAndMessageLabel = new JPanel(new BorderLayout()); statusAndMessageLabel.add(statusIcon, BorderLayout.WEST); statusAndMessageLabel.add(messageLabel, BorderLayout.CENTER); statusAndMessageLabel.add(showEvaluationButton, BorderLayout.EAST); JPanel topPanel = new JPanel(new BorderLayout()); topPanel.add(expressionTA, BorderLayout.NORTH); topPanel.add(statusAndMessageLabel, BorderLayout.CENTER); topPanel.add(evaluationPanel, BorderLayout.SOUTH); evaluationPanel.setVisible(false); add(topPanel, BorderLayout.NORTH); rootExpressionPanel = new ExpressionInnerPanel(_bindingExpression.getExpression()) { @Override public void representedExpressionChanged(Expression newExpression) { setEditedExpression(newExpression); } }; focusReceiver = rootExpressionPanel; add(new JScrollPane(rootExpressionPanel), BorderLayout.CENTER); controls = new JPanel(new BorderLayout()); final JPanel commonControls = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 0)); final JPanel mathControls = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 0)); commonControls.add(createOperatorGroupPanel("logical", BooleanBinaryOperator.AND, BooleanBinaryOperator.OR, BooleanUnaryOperator.NOT)); commonControls.add(createOperatorGroupPanel("comparison", BooleanBinaryOperator.EQUALS, BooleanBinaryOperator.NOT_EQUALS, BooleanBinaryOperator.LESS_THAN, BooleanBinaryOperator.LESS_THAN_OR_EQUALS, BooleanBinaryOperator.GREATER_THAN, BooleanBinaryOperator.GREATER_THAN_OR_EQUALS)); commonControls.add(createOperatorGroupPanel("arithmetic", ArithmeticBinaryOperator.ADDITION, ArithmeticBinaryOperator.SUBSTRACTION, ArithmeticBinaryOperator.MULTIPLICATION, ArithmeticBinaryOperator.DIVISION, ArithmeticUnaryOperator.UNARY_MINUS)); final MouseOverButton moreButton = new MouseOverButton(); moreButton.setBorder(BorderFactory.createEmptyBorder()); moreButton.setNormalIcon(FIBIconLibrary.TOGGLE_ARROW_BOTTOM_ICON); moreButton.setMouseOverIcon(FIBIconLibrary.TOGGLE_ARROW_BOTTOM_SELECTED_ICON); moreButton.setToolTipText(FlexoLocalization.localizedForKey(FIBModelObject.LOCALIZATION, "show_more_operators")); moreButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (!mathControls.isVisible()) { moreButton.setNormalIcon(FIBIconLibrary.TOGGLE_ARROW_TOP_ICON); moreButton.setMouseOverIcon(FIBIconLibrary.TOGGLE_ARROW_TOP_SELECTED_ICON); moreButton.setToolTipText(FlexoLocalization.localizedForKey(FIBModelObject.LOCALIZATION, "hide_extra_operators")); commonControls.remove(moreButton); mathControls.add(moreButton); mathControls.setVisible(true); } else { moreButton.setNormalIcon(FIBIconLibrary.TOGGLE_ARROW_BOTTOM_ICON); moreButton.setMouseOverIcon(FIBIconLibrary.TOGGLE_ARROW_BOTTOM_SELECTED_ICON); moreButton.setToolTipText(FlexoLocalization.localizedForKey(FIBModelObject.LOCALIZATION, "show_more_operators")); mathControls.remove(moreButton); commonControls.add(moreButton); mathControls.setVisible(false); } } }); commonControls.add(moreButton); mathControls.add(createOperatorGroupPanel("scientific", ArithmeticBinaryOperator.POWER, ArithmeticUnaryOperator.SQRT, ArithmeticUnaryOperator.EXP, ArithmeticUnaryOperator.LOG)); mathControls.add(createOperatorGroupPanel("trigonometric", ArithmeticUnaryOperator.SIN, ArithmeticUnaryOperator.ASIN, ArithmeticUnaryOperator.COS, ArithmeticUnaryOperator.ACOS, ArithmeticUnaryOperator.TAN, ArithmeticUnaryOperator.ATAN)); controls.add(commonControls, BorderLayout.CENTER); controls.add(mathControls, BorderLayout.SOUTH); mathControls.setVisible(false); add(controls, BorderLayout.SOUTH); update(); } private JPanel createOperatorGroupPanel(String title, Operator... operators) { JPanel returned = new JPanel(); returned.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 0)); for (final Operator o : operators) { JButton b = new JButton(iconForOperator(o)); b.setBorder(BorderFactory.createEmptyBorder()); b.setToolTipText(o.getLocalizedName()); returned.add(b); b.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (o instanceof UnaryOperator) { appendUnaryOperator((UnaryOperator) o); } else if (o instanceof BinaryOperator) { appendBinaryOperator((BinaryOperator) o); } } }); } if (title.equals("logical")) { JButton b = new JButton(FIBIconLibrary.IF_ICON); b.setBorder(BorderFactory.createEmptyBorder()); b.setToolTipText(FlexoLocalization.localizedForKey("conditional")); returned.add(b); b.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { appendConditional(); } }); } returned.setBorder(BorderFactory.createTitledBorder(null, FlexoLocalization.localizedForKey(FIBModelObject.LOCALIZATION, title), TitledBorder.CENTER, TitledBorder.TOP, new Font("SansSerif", Font.ITALIC, 8))); return returned; } protected void update() { _checkEditedExpression(); updateExpressionTextArea(); updateAdditionalInformations(); revalidate(); repaint(); } protected void fireEditedExpressionChanged(BindingExpression expression) { // Override if required } protected void updateExpressionTextArea() { if (_bindingExpression == null) { return; } expressionTA.setText(pp.getStringRepresentation(_bindingExpression.getExpression())); if (status == ExpressionParsingStatus.UNDEFINED) { statusIcon.setIcon(FIBIconLibrary.WARNING_ICON); } else if (status == ExpressionParsingStatus.INVALID) { statusIcon.setIcon(FIBIconLibrary.ERROR_ICON); } else if (status == ExpressionParsingStatus.SYNTAXICALLY_VALID) { statusIcon.setIcon(FIBIconLibrary.WARNING_ICON); } else if (status == ExpressionParsingStatus.VALID) { statusIcon.setIcon(FIBIconLibrary.OK_ICON); } messageLabel.setText(message); } protected void updateAdditionalInformations() { if (status == ExpressionParsingStatus.UNDEFINED) { statusIcon.setIcon(FIBIconLibrary.WARNING_ICON); } else if (status == ExpressionParsingStatus.INVALID) { statusIcon.setIcon(FIBIconLibrary.ERROR_ICON); } else if (status == ExpressionParsingStatus.SYNTAXICALLY_VALID) { statusIcon.setIcon(FIBIconLibrary.WARNING_ICON); } else if (status == ExpressionParsingStatus.VALID) { statusIcon.setIcon(FIBIconLibrary.OK_ICON); } messageLabel.setText(message); } protected abstract class ExpressionInnerPanel extends JPanel { protected Expression _representedExpression; private BindingSelector _bindingSelector; // private JTextField variableOrConstantTextField; protected ExpressionInnerPanel(Expression expression) { super(); if (logger.isLoggable(Level.FINE)) { logger.fine("Build new ExpressionInnerPanel with " + (expression != null ? expression.toString() : "null")); } _representedExpression = expression; update(); // addFocusListeners(); } public void delete() { if (_bindingSelector != null) { _bindingSelector.delete(); } for (Component c : getComponents()) { if (c instanceof ExpressionInnerPanel) { ((ExpressionInnerPanel) c).delete(); } } removeAll(); _representedExpression = null; _bindingSelector = null; } private void addFocusListeners() { addFocusListenersToAllComponentsOf(this); } private void addFocusListenersToAllComponentsOf(Component c) { c.addFocusListener(BindingExpressionPanel.this); if (c instanceof Container) { Container container = (Container) c; for (Component c2 : container.getComponents()) { addFocusListenersToAllComponentsOf(c2); } } } private void removeFocusListeners() { removeFocusListenersToAllComponentsOf(this); } private void removeFocusListenersToAllComponentsOf(Component c) { c.removeFocusListener(BindingExpressionPanel.this); if (c instanceof Container) { Container container = (Container) c; for (Component c2 : container.getComponents()) { removeFocusListenersToAllComponentsOf(c2); } } } /*protected void textChanged() { try { Expression newExpression; if (variableOrConstantTextField.getText().trim().equals("")) { newExpression = new Variable(""); } else { newExpression = parser.parse(variableOrConstantTextField.getText()); } setRepresentedExpression(newExpression); //System.out.println("Text has changed for "+variableOrConstantTextField.getText()+" parsed as "+newExpression); } catch (ParseException e) { System.out.println("ERROR: cannot parse "+variableOrConstantTextField.getText()); } }*/ protected class OperatorPanel extends JPanel { private final JButton currentOperatorIcon; protected OperatorPanel(Operator operator) { super(); setLayout(new BorderLayout()); currentOperatorIcon = new JButton(iconForOperator(operator)); currentOperatorIcon.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5)); currentOperatorIcon.setToolTipText(operator.getLocalizedName()); add(currentOperatorIcon, BorderLayout.CENTER); } } protected class IfOperatorPanel extends JPanel { private final JButton currentOperatorIcon; protected IfOperatorPanel() { super(); setLayout(new BorderLayout()); currentOperatorIcon = new JButton(FIBIconLibrary.IF_ICON); currentOperatorIcon.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5)); currentOperatorIcon.setToolTipText(FlexoLocalization.localizedForKey("conditional")); add(currentOperatorIcon, BorderLayout.CENTER); } } private void addBinaryExpressionVerticalLayout() { GridBagLayout gridbag2 = new GridBagLayout(); GridBagConstraints c2 = new GridBagConstraints(); setLayout(gridbag2); final BinaryOperatorExpression exp = (BinaryOperatorExpression) _representedExpression; final ExpressionInnerPanel me = this; OperatorPanel operatorPanel = new OperatorPanel(exp.getOperator()); c2.weightx = 0.0; c2.weighty = 0.0; c2.anchor = GridBagConstraints.CENTER; c2.fill = GridBagConstraints.VERTICAL; gridbag2.setConstraints(operatorPanel, c2); add(operatorPanel); operatorPanel.setBorder(BorderFactory.createEtchedBorder()); JPanel argsPanel = new JPanel(); GridBagLayout gridbag = new GridBagLayout(); GridBagConstraints c = new GridBagConstraints(); argsPanel.setLayout(gridbag); ExpressionInnerPanel leftArg = new ExpressionInnerPanel(exp.getLeftArgument()) { @Override public void representedExpressionChanged(Expression newExpression) { exp.setLeftArgument(newExpression); // Take care that we have here a recursion with inner classes // (I known this is not recommanded) // We should here access embedding instance ! me.representedExpressionChanged(exp); } }; c.weightx = 1.0; c.weighty = 1.0; c.anchor = GridBagConstraints.NORTH; c.fill = GridBagConstraints.HORIZONTAL; c.gridwidth = GridBagConstraints.REMAINDER; gridbag.setConstraints(leftArg, c); argsPanel.add(leftArg); ExpressionInnerPanel rightArg = new ExpressionInnerPanel(exp.getRightArgument()/*,depth+1*/) { @Override public void representedExpressionChanged(Expression newExpression) { exp.setRightArgument(newExpression); // Take care that we have here a recursion with inner classes // (I known this is not recommanded) // We should here access embedding instance ! me.representedExpressionChanged(exp); } }; c.weightx = 1.0; c.weighty = 1.0; c.anchor = GridBagConstraints.NORTH; c.fill = GridBagConstraints.HORIZONTAL; c.gridwidth = GridBagConstraints.REMAINDER; gridbag.setConstraints(rightArg, c); argsPanel.add(rightArg); c2.weightx = 1.0; c2.weighty = 0.0; c2.anchor = GridBagConstraints.NORTH; c2.fill = GridBagConstraints.BOTH; c2.gridwidth = GridBagConstraints.REMAINDER; gridbag2.setConstraints(argsPanel, c2); add(argsPanel); Box box = Box.createHorizontalBox(); c2.weightx = 1.0; c2.weighty = 1.0; c2.anchor = GridBagConstraints.SOUTH; c2.fill = GridBagConstraints.BOTH; c2.gridwidth = GridBagConstraints.REMAINDER; gridbag2.setConstraints(box, c2); add(box); isHorizontallyLayouted = false; } private void addConditionalExpressionVerticalLayout() { GridBagLayout gridbag2 = new GridBagLayout(); GridBagConstraints c2 = new GridBagConstraints(); setLayout(gridbag2); final ConditionalExpression exp = (ConditionalExpression) _representedExpression; final ExpressionInnerPanel me = this; IfOperatorPanel operatorPanel = new IfOperatorPanel(); c2.weightx = 0.0; c2.weighty = 0.0; c2.anchor = GridBagConstraints.EAST; c2.fill = GridBagConstraints.NONE; c2.gridwidth = GridBagConstraints.RELATIVE; c2.insets = new Insets(0, 0, 0, 0); gridbag2.setConstraints(operatorPanel, c2); add(operatorPanel); operatorPanel.setBorder(BorderFactory.createEmptyBorder()); ExpressionInnerPanel conditionArg = new ExpressionInnerPanel(exp.getCondition()) { @Override public void representedExpressionChanged(Expression newExpression) { exp.setCondition(newExpression); // Take care that we have here a recursion with inner classes // (I known this is not recommanded) // We should here access embedding instance ! me.representedExpressionChanged(exp); } }; c2.weightx = 1.0; c2.weighty = 0.0; c2.anchor = GridBagConstraints.CENTER; c2.fill = GridBagConstraints.HORIZONTAL; c2.gridwidth = GridBagConstraints.REMAINDER; c2.insets = new Insets(0, 0, 0, 0); gridbag2.setConstraints(conditionArg, c2); add(conditionArg); JLabel thenLabel = new JLabel("then"); thenLabel.setFont(thenLabel.getFont().deriveFont(9.0f)); c2.weightx = 0.0; c2.weighty = 0.0; c2.anchor = GridBagConstraints.EAST; c2.fill = GridBagConstraints.NONE; c2.gridwidth = GridBagConstraints.RELATIVE; c2.insets = new Insets(0, 0, 0, 3); gridbag2.setConstraints(thenLabel, c2); add(thenLabel); ExpressionInnerPanel thenExp = new ExpressionInnerPanel(exp.getThenExpression()) { @Override public void representedExpressionChanged(Expression newExpression) { exp.setThenExpression(newExpression); // Take care that we have here a recursion with inner classes // (I known this is not recommanded) // We should here access embedding instance ! me.representedExpressionChanged(exp); } }; c2.weightx = 1.0; c2.weighty = 0.0; c2.anchor = GridBagConstraints.CENTER; c2.fill = GridBagConstraints.HORIZONTAL; c2.gridwidth = GridBagConstraints.REMAINDER; c2.insets = new Insets(0, 0, 0, 0); gridbag2.setConstraints(thenExp, c2); add(thenExp); JLabel elseLabel = new JLabel("else"); elseLabel.setFont(elseLabel.getFont().deriveFont(9.0f)); c2.weightx = 0.0; c2.weighty = 0.0; c2.anchor = GridBagConstraints.EAST; c2.fill = GridBagConstraints.NONE; c2.gridwidth = GridBagConstraints.RELATIVE; c2.insets = new Insets(0, 0, 0, 3); gridbag2.setConstraints(elseLabel, c2); add(elseLabel); ExpressionInnerPanel elseExp = new ExpressionInnerPanel(exp.getElseExpression()/*,depth+1*/) { @Override public void representedExpressionChanged(Expression newExpression) { exp.setElseExpression(newExpression); // Take care that we have here a recursion with inner classes // (I known this is not recommanded) // We should here access embedding instance ! me.representedExpressionChanged(exp); } }; c2.weightx = 1.0; c2.weighty = 0.0; c2.anchor = GridBagConstraints.CENTER; c2.fill = GridBagConstraints.HORIZONTAL; c2.gridwidth = GridBagConstraints.REMAINDER; c2.insets = new Insets(0, 0, 0, 0); gridbag2.setConstraints(elseExp, c2); add(elseExp); Box box = Box.createHorizontalBox(); c2.weightx = 1.0; c2.weighty = 1.0; c2.anchor = GridBagConstraints.SOUTH; c2.fill = GridBagConstraints.BOTH; c2.gridwidth = GridBagConstraints.REMAINDER; gridbag2.setConstraints(box, c2); add(box); isHorizontallyLayouted = false; } private boolean isHorizontallyLayouted = true; private void addBinaryExpressionHorizontalLayout() { GridBagLayout gridbag = new GridBagLayout(); GridBagConstraints c = new GridBagConstraints(); setLayout(gridbag); final BinaryOperatorExpression exp = (BinaryOperatorExpression) _representedExpression; final ExpressionInnerPanel me = this; OperatorPanel operatorPanel = new OperatorPanel(exp.getOperator()); ExpressionInnerPanel leftArg = new ExpressionInnerPanel(exp.getLeftArgument()) { @Override public void representedExpressionChanged(Expression newExpression) { exp.setLeftArgument(newExpression); // Take care that we have here a recursion with inner classes // (I known this is not recommanded) // We should here access embedding instance ! me.representedExpressionChanged(exp); } }; c.weightx = 1.0; c.weighty = 1.0; c.anchor = GridBagConstraints.NORTH; c.fill = GridBagConstraints.HORIZONTAL; gridbag.setConstraints(leftArg, c); add(leftArg); c.weightx = 0.0; c.weighty = 1.0; c.anchor = GridBagConstraints.NORTH; c.fill = GridBagConstraints.NONE; gridbag.setConstraints(operatorPanel, c); add(operatorPanel); ExpressionInnerPanel rightArg = new ExpressionInnerPanel(exp.getRightArgument()) { @Override public void representedExpressionChanged(Expression newExpression) { exp.setRightArgument(newExpression); // Take care that we have here a recursion with inner classes // (I known this is not recommanded) // We should here access embedding instance ! me.representedExpressionChanged(exp); } }; c.weightx = 1.0; c.weighty = 1.0; c.anchor = GridBagConstraints.NORTH; c.fill = GridBagConstraints.HORIZONTAL; c.gridwidth = GridBagConstraints.REMAINDER; gridbag.setConstraints(rightArg, c); add(rightArg); isHorizontallyLayouted = true; } private void addUnaryExpressionHorizontalLayout() { GridBagLayout gridbag = new GridBagLayout(); GridBagConstraints c = new GridBagConstraints(); setLayout(gridbag); final UnaryOperatorExpression exp = (UnaryOperatorExpression) _representedExpression; final ExpressionInnerPanel me = this; OperatorPanel operatorPanel = new OperatorPanel(exp.getOperator()); c.weightx = 0.0; c.weighty = 1.0; c.anchor = GridBagConstraints.NORTH; c.fill = GridBagConstraints.NONE; gridbag.setConstraints(operatorPanel, c); add(operatorPanel); ExpressionInnerPanel arg = new ExpressionInnerPanel(exp.getArgument()) { @Override public void representedExpressionChanged(Expression newExpression) { exp.setArgument(newExpression); // Take care that we have here a recursion with inner classes // (I known this is not recommanded) // We should here access embedding instance ! me.representedExpressionChanged(exp); } }; c.weightx = 1.0; c.weighty = 1.0; c.anchor = GridBagConstraints.NORTH; c.fill = GridBagConstraints.HORIZONTAL; c.gridwidth = GridBagConstraints.REMAINDER; gridbag.setConstraints(arg, c); add(arg); isHorizontallyLayouted = true; } private void update() { ExpressionInnerPanel parent = (ExpressionInnerPanel) SwingUtilities.getAncestorOfClass(ExpressionInnerPanel.class, this); if (parent != null && parent.isHorizontallyLayouted && parent._representedExpression.getDepth() > 1) { parent.update(); return; } removeFocusListeners(); removeAll(); // System.out.println("Update in ExpressionInnerPanel with " + _representedExpression + " of " + // _representedExpression.getClass()); if (_representedExpression instanceof SymbolicConstant) { GridBagLayout gridbag = new GridBagLayout(); GridBagConstraints c = new GridBagConstraints(); setLayout(gridbag); JLabel symbolicConstantLabel = new JLabel(((SymbolicConstant) _representedExpression).getSymbol()); c.weightx = 0.0; c.weighty = 1.0; c.anchor = GridBagConstraints.NORTH; c.fill = GridBagConstraints.NONE; c.gridwidth = GridBagConstraints.REMAINDER; gridbag.setConstraints(symbolicConstantLabel, c); add(symbolicConstantLabel); } /*else if (_representedExpression instanceof Variable || _representedExpression instanceof Constant) { GridBagLayout gridbag = new GridBagLayout(); GridBagConstraints c = new GridBagConstraints(); setLayout(gridbag); variableOrConstantTextField = new JTextField(); variableOrConstantTextField.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e) { textChanged(); } }); variableOrConstantTextField.addFocusListener(new FocusAdapter(){ public void focusLost(FocusEvent e) { textChanged(); } }); c.weightx = 1.0; c.weighty = 1.0; c.anchor = GridBagConstraints.NORTH; c.fill = GridBagConstraints.HORIZONTAL; c.gridwidth = GridBagConstraints.REMAINDER; gridbag.setConstraints(variableOrConstantTextField, c); add(variableOrConstantTextField); if (_representedExpression instanceof Variable) { variableOrConstantTextField.setText(((Variable)_representedExpression).getName()); } else if (_representedExpression instanceof Constant) { variableOrConstantTextField.setText(((Constant)_representedExpression).toString()); } }*/ else if (_representedExpression instanceof BindingExpression.BindingValueVariable || _representedExpression instanceof BindingExpression.BindingValueFunction || _representedExpression instanceof BindingExpression.BindingValueConstant) { GridBagLayout gridbag = new GridBagLayout(); GridBagConstraints c = new GridBagConstraints(); setLayout(gridbag); AbstractBinding binding = null; if (_representedExpression instanceof BindingExpression.BindingValueVariable) { binding = ((BindingExpression.BindingValueVariable) _representedExpression).getBindingValue(); } else if (_representedExpression instanceof BindingExpression.BindingValueFunction) { binding = ((BindingExpression.BindingValueFunction) _representedExpression).getBindingValue(); } else if (_representedExpression instanceof BindingExpression.BindingValueConstant) { binding = ((BindingExpression.BindingValueConstant) _representedExpression).getStaticBinding(); } if (logger.isLoggable(Level.FINE)) { logger.fine("Building BindingSelector with " + binding); } _bindingSelector = new BindingSelector(binding) { @Override public void apply() { super.apply(); AbstractBinding newEditedBinding = getEditedObject(); if (newEditedBinding instanceof StaticBinding) { setRepresentedExpression(new BindingValueConstant((StaticBinding) newEditedBinding)); } else if (newEditedBinding instanceof BindingValue) { setRepresentedExpression(new BindingValueVariable((BindingValue) newEditedBinding)); } else if (newEditedBinding instanceof BindingExpression) { setRepresentedExpression(((BindingExpression) newEditedBinding).getExpression()); } } @Override public void cancel() { super.cancel(); } @Override public Dimension getPreferredSize() { Dimension parentDim = super.getPreferredSize(); return new Dimension(100, parentDim.height); } }; /*if (_representedExpression instanceof BindingExpression.BindingValueVariable && (binding==null || !binding.isBindingValid())) { // This binding could not be resolved, just set text _bindingSelector.getTextField().setText(((BindingExpression.BindingValueVariable)_representedExpression).getVariable().getName()); System.out.println("setting textfield to be "+((BindingExpression.BindingValueVariable)_representedExpression).getVariable().getName()); }*/ if (binding != null) { _bindingSelector.setBindingDefinition(binding.getBindingDefinition()); } else { _bindingSelector.setBindingDefinition(new BindingDefinition("common", Object.class, BindingDefinitionType.GET, true)); } _bindingSelector.setBindable(_bindingExpression); // _bindingSelector.setEditedObject(binding); if (binding != null) { _bindingSelector.setRevertValue(binding.clone()); } /*variableOrConstantTextField = new JTextField(); variableOrConstantTextField.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e) { textChanged(); } }); variableOrConstantTextField.addFocusListener(new FocusAdapter(){ public void focusLost(FocusEvent e) { textChanged(); } });*/ c.weightx = 1.0; c.weighty = 1.0; c.anchor = GridBagConstraints.NORTH; c.fill = GridBagConstraints.HORIZONTAL; c.gridwidth = GridBagConstraints.REMAINDER; gridbag.setConstraints(_bindingSelector, c); add(_bindingSelector); /*if (_representedExpression instanceof BindingValueVariable) { variableOrConstantTextField.setText(((BindingValueVariable)_representedExpression).getName()); } else if (_representedExpression instanceof BindingValueConstant) { variableOrConstantTextField.setText(((BindingValueConstant)_representedExpression).toString()); }*/ } else if (_representedExpression instanceof BinaryOperatorExpression) { if (_representedExpression.getDepth() > 1) { addBinaryExpressionVerticalLayout(); } else { addBinaryExpressionHorizontalLayout(); } } else if (_representedExpression instanceof UnaryOperatorExpression) { addUnaryExpressionHorizontalLayout(); } else if (_representedExpression instanceof ConditionalExpression) { addConditionalExpressionVerticalLayout(); } addFocusListeners(); revalidate(); repaint(); } public Expression getRepresentedExpression() { return _representedExpression; } public void setRepresentedExpression(Expression representedExpression) { _representedExpression = representedExpression; representedExpressionChanged(representedExpression); update(); updateInfos(); } private void updateInfos() { _checkEditedExpression(); updateExpressionTextArea(); updateAdditionalInformations(); } public abstract void representedExpressionChanged(Expression newExpression); } protected void appendBinaryOperator(BinaryOperator operator) { // System.out.println("appendBinaryOperator " + operator); if (focusReceiver != null) { BindingValueVariable variable = new BindingValueVariable("", _bindingExpression); Expression newExpression = new BinaryOperatorExpression(operator, focusReceiver.getRepresentedExpression(), variable); /*logger.info("variable="+variable.getBindingValue()); logger.info("owner="+variable.getBindingValue().getOwner()); logger.info("bd="+variable.getBindingValue().getBindingDefinition());*/ focusReceiver.setRepresentedExpression(newExpression); } } protected void appendUnaryOperator(UnaryOperator operator) { // System.out.println("appendUnaryOperator " + operator); if (focusReceiver != null) { Expression newExpression = new UnaryOperatorExpression(operator, focusReceiver.getRepresentedExpression()); focusReceiver.setRepresentedExpression(newExpression); } } protected void appendConditional() { // System.out.println("appendConditional"); if (focusReceiver != null) { BindingValueVariable condition = new BindingValueVariable("true", _bindingExpression, new BindingDefinition("condition", Boolean.class, BindingDefinitionType.GET, true)); BindingValueVariable elseExpression = new BindingValueVariable("", _bindingExpression); Expression newExpression = new ConditionalExpression(condition, focusReceiver.getRepresentedExpression(), elseExpression); focusReceiver.setRepresentedExpression(newExpression); } } @Override public void focusGained(FocusEvent e) { focusReceiver = (ExpressionInnerPanel) SwingUtilities.getAncestorOfClass(ExpressionInnerPanel.class, (Component) e.getSource()); if (focusReceiver != null) { if (logger.isLoggable(Level.FINE)) { logger.fine("Focus gained by expression " + focusReceiver.getRepresentedExpression() + " receiver=" + focusReceiver); } } } @Override public void focusLost(FocusEvent e) { // Dont care ExpressionInnerPanel whoLoseFocus = (ExpressionInnerPanel) SwingUtilities.getAncestorOfClass(ExpressionInnerPanel.class, (Component) e.getSource()); if (whoLoseFocus != null) { if (logger.isLoggable(Level.FINE)) { logger.fine("Focus lost by expression " + whoLoseFocus.getRepresentedExpression() + " looser=" + whoLoseFocus); } } } }