/*
* Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Business Objects nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* NumberValueEditor.java
* Creation date: (1/12/2001 11:38:20 PM)
* By: Luke Evans
*/
package org.openquark.gems.client.valueentry;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.FocusTraversalPolicy;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Stack;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import org.openquark.cal.compiler.PreludeTypeConstants;
import org.openquark.cal.compiler.TypeExpr;
import org.openquark.cal.module.Cal.Core.CAL_Prelude;
import org.openquark.cal.valuenode.LiteralValueNode;
import org.openquark.cal.valuenode.ValueNode;
/**
* Allows the user to manipulate a number value thru a calculator.
* @author Luke Evans
*/
class NumberValueEditor extends ValueEditor {
private static final long serialVersionUID = -789074961805648387L;
/**
* A custom value editor provider for the NumberValueEditor.
*/
public static class NumberValueEditorProvider extends ValueEditorProvider<NumberValueEditor> {
public NumberValueEditorProvider(ValueEditorManager valueEditorManager) {
super(valueEditorManager);
}
/**
* {@inheritDoc}
*/
@Override
public boolean canHandleValue(ValueNode valueNode, SupportInfo providerSupportInfo) {
TypeExpr typeExpr = valueNode.getTypeExpr();
PreludeTypeConstants typeConstants = getValueEditorManager().getValueNodeBuilderHelper().getPreludeTypeConstants();
// Integer and Decimal are not built-in types, so we cannot compare them to static
// constants the way we can for the other types.
return valueNode instanceof LiteralValueNode &&
(typeExpr.sameType(typeConstants.getByteType()) ||
typeExpr.sameType(typeConstants.getShortType()) ||
typeExpr.sameType(typeConstants.getIntType()) ||
typeExpr.sameType(typeConstants.getIntegerType()) ||
typeExpr.sameType(typeConstants.getDecimalType()) ||
typeExpr.sameType(typeConstants.getLongType()) ||
typeExpr.sameType(typeConstants.getFloatType()) ||
typeExpr.sameType(typeConstants.getDoubleType()));
}
/**
* @see org.openquark.gems.client.valueentry.ValueEditorProvider#getEditorInstance(ValueEditorHierarchyManager, ValueNode)
*/
@Override
public NumberValueEditor getEditorInstance(ValueEditorHierarchyManager valueEditorHierarchyManager,
ValueNode valueNode) {
NumberValueEditor editor = new NumberValueEditor(valueEditorHierarchyManager);
editor.setOwnerValueNode(valueNode);
return editor;
}
}
private static final String UNDEFINED_STRING = "Undefined.";
private JButton clearButton = null;
private JButton decimalButton = null;
private JButton divideButton = null;
private JButton minusButton = null;
private JButton multiplyButton = null;
private JButton number0Button = null;
private JButton number1Button = null;
private JButton number2Button = null;
private JButton number3Button = null;
private JButton number4Button = null;
private JButton number5Button = null;
private JButton number6Button = null;
private JButton number7Button = null;
private JButton number8Button = null;
private JButton number9Button = null;
private JButton plusButton = null;
private JButton signButton = null;
private JButton sqrtButton = null;
private JButton equalButton = null;
private JButton oneOverXButton = null;
private JPanel buttonPanel = null;
private JTextField displayField = null;
private Stack <String> operatorStack = null;
private Stack <String> operandStack = null;
/**
* The precedence of the divide operator.
* Note: A higher number means a higher precedence and should be evaluated before lower precedence operators.
*/
private static final int PRECEDENCE_DIVIDE = 2;
/**
* The precedence of the multiply operator.
*/
private static final int PRECEDENCE_MULTIPLY = 2;
/**
* The precedence of the minus operator.
*/
private static final int PRECEDENCE_MINUS = 1;
/**
* The precedence of the plus operator.
*/
private static final int PRECEDENCE_PLUS = 1;
/**
* A flag to denote whether or not the next key press should be an operand key press,
* or if it could be either operand or operator key press.
* True if it should be an operand key press,
* False if it could be either.
* Note: If operandNext is true, and user presses an operator, then the previously pressed
* operator will be replaced by the currently pressed operator.
*/
private boolean operandNext;
/**
* A flag to denote that if the next key press is an 'operand' key ("#0-#9" or "."),
* then replace the value in the parent textfield with the operand key pressed.
*/
private boolean replaceOnOperandKeyPress;
/**
* Flag to signal whether or not we are in the handling undefined results stage.
* Set to true the moment we enter handling mode.
* Set to false when we exit the handling mode (user presses "C", or this NumberValueEditor is closed).
*/
private boolean isHandlingResultUndefined;
/**
* The event handler for button presses.
*/
private class ButtonEventHandler implements ActionListener {
public void actionPerformed(ActionEvent e) {
if (e.getSource() == NumberValueEditor.this.getNumber0Button()) {
appendToDisplay("0");
}
if (e.getSource() == NumberValueEditor.this.getNumber1Button()) {
appendToDisplay("1");
}
if (e.getSource() == NumberValueEditor.this.getNumber2Button()) {
appendToDisplay("2");
}
if (e.getSource() == NumberValueEditor.this.getNumber3Button()) {
appendToDisplay("3");
}
if (e.getSource() == NumberValueEditor.this.getNumber4Button()) {
appendToDisplay("4");
}
if (e.getSource() == NumberValueEditor.this.getNumber5Button()) {
appendToDisplay("5");
}
if (e.getSource() == NumberValueEditor.this.getNumber6Button()) {
appendToDisplay("6");
}
if (e.getSource() == NumberValueEditor.this.getNumber7Button()) {
appendToDisplay("7");
}
if (e.getSource() == NumberValueEditor.this.getNumber8Button()) {
appendToDisplay("8");
}
if (e.getSource() == NumberValueEditor.this.getNumber9Button()) {
appendToDisplay("9");
}
if (e.getSource() == NumberValueEditor.this.getSignButton()) {
sign_ActionEvents();
}
if (e.getSource() == NumberValueEditor.this.getDecimalButton()) {
decimal_ActionEvents();
}
if (e.getSource() == NumberValueEditor.this.getDivideButton()) {
handleOpKeyPress("/");
}
if (e.getSource() == NumberValueEditor.this.getMultiplyButton()) {
handleOpKeyPress("*");
}
if (e.getSource() == NumberValueEditor.this.getMinusButton()) {
handleOpKeyPress("-");
}
if (e.getSource() == NumberValueEditor.this.getPlusButton()) {
handleOpKeyPress("+");
}
if (e.getSource() == NumberValueEditor.this.getEqualButton()) {
equal_ActionEvents();
}
if (e.getSource() == NumberValueEditor.this.getSqrtButton()) {
sqrt_ActionEvents();
}
if (e.getSource() == NumberValueEditor.this.getClearButton()) {
clear_ActionEvents();
}
if (e.getSource() == NumberValueEditor.this.getOneOverXButton()) {
oneOverX_ActionEvents();
}
}
}
/**
* KeyListener used to listen for user's input.
* Currently, the following KeyEvents are supported:
* "0" -> "9", "/", "*", "-", "+", ".", "delete"(for clear), "enter"(for equal), and
* "esc"(for closing NumberValueEditor and going back to the parent).
*/
private class CalculatorKeyListener extends KeyAdapter {
@Override
public void keyPressed(KeyEvent evt) {
if ((evt.getKeyCode() == KeyEvent.VK_1 && !evt.isShiftDown()) || evt.getKeyCode() == KeyEvent.VK_NUMPAD1) {
handleButtonKeyPress(getNumber1Button());
} else if ((evt.getKeyCode() == KeyEvent.VK_2 && !evt.isShiftDown()) || evt.getKeyCode() == KeyEvent.VK_NUMPAD2) {
handleButtonKeyPress(getNumber2Button());
} else if ((evt.getKeyCode() == KeyEvent.VK_3 && !evt.isShiftDown()) || evt.getKeyCode() == KeyEvent.VK_NUMPAD3) {
handleButtonKeyPress(getNumber3Button());
} else if ((evt.getKeyCode() == KeyEvent.VK_4 && !evt.isShiftDown()) || evt.getKeyCode() == KeyEvent.VK_NUMPAD4) {
handleButtonKeyPress(getNumber4Button());
} else if ((evt.getKeyCode() == KeyEvent.VK_5 && !evt.isShiftDown()) || evt.getKeyCode() == KeyEvent.VK_NUMPAD5) {
handleButtonKeyPress(getNumber5Button());
} else if ((evt.getKeyCode() == KeyEvent.VK_6 && !evt.isShiftDown()) || evt.getKeyCode() == KeyEvent.VK_NUMPAD6) {
handleButtonKeyPress(getNumber6Button());
} else if ((evt.getKeyCode() == KeyEvent.VK_7 && !evt.isShiftDown()) || evt.getKeyCode() == KeyEvent.VK_NUMPAD7) {
handleButtonKeyPress(getNumber7Button());
} else if ((evt.getKeyCode() == KeyEvent.VK_8 && !evt.isShiftDown()) || evt.getKeyCode() == KeyEvent.VK_NUMPAD8) {
handleButtonKeyPress(getNumber8Button());
} else if ((evt.getKeyCode() == KeyEvent.VK_9 && !evt.isShiftDown()) || evt.getKeyCode() == KeyEvent.VK_NUMPAD9) {
handleButtonKeyPress(getNumber9Button());
} else if ((evt.getKeyCode() == KeyEvent.VK_0 && !evt.isShiftDown()) || evt.getKeyCode() == KeyEvent.VK_NUMPAD0) {
handleButtonKeyPress(getNumber0Button());
} else if (evt.getKeyCode() == KeyEvent.VK_DECIMAL || (evt.getKeyCode() == KeyEvent.VK_PERIOD && !evt.isShiftDown())) {
handleButtonKeyPress(getDecimalButton());
} else if (evt.getKeyCode() == KeyEvent.VK_ENTER) {
handleCommitGesture();
evt.consume(); // Don't want the button with the focus to perform its action.
} else if (evt.getKeyCode() == KeyEvent.VK_ADD || evt.getKeyCode() == KeyEvent.VK_EQUALS && evt.isShiftDown()) {
handleButtonKeyPress(getPlusButton());
} else if (evt.getKeyCode() == KeyEvent.VK_SUBTRACT || (evt.getKeyCode() == KeyEvent.VK_MINUS && !evt.isShiftDown())) {
handleButtonKeyPress(getMinusButton());
} else if ((evt.getKeyCode() == KeyEvent.VK_MULTIPLY) || (evt.getKeyCode() == KeyEvent.VK_8 && evt.isShiftDown())) {
handleButtonKeyPress(getMultiplyButton());
} else if (evt.getKeyCode() == KeyEvent.VK_DIVIDE || (evt.getKeyCode() == KeyEvent.VK_SLASH && !evt.isShiftDown())) {
handleButtonKeyPress(getDivideButton());
} else if ((evt.getKeyCode() == KeyEvent.VK_EQUALS && !evt.isShiftDown())) {
handleButtonKeyPress(getEqualButton());
} else if (evt.getKeyCode() == KeyEvent.VK_DELETE && !evt.isAltDown()) {
handleButtonKeyPress(getClearButton());
} else if (evt.getKeyCode() == KeyEvent.VK_ESCAPE) {
handleCancelGesture();
evt.consume();
}
}
/**
* Handle a key press as if a button were clicked.
* @param button the button corresponding to the key which was typed.
*/
private void handleButtonKeyPress(JButton button) {
if (button.isEnabled()) {
button.doClick();
button.requestFocus();
}
}
}
/**
* Creates a new NumberValueEditor.
* @param valueEditorHierarchyManager
*/
protected NumberValueEditor(ValueEditorHierarchyManager valueEditorHierarchyManager) {
super(valueEditorHierarchyManager);
initialize();
}
/**
* Initialize the class.
*/
private void initialize() {
setName("NumberValueEditor");
setSize(150, 100);
// The panel containing all the buttons
buttonPanel = new JPanel();
GridLayout buttonPanelGridLayout = new GridLayout();
buttonPanelGridLayout.setRows(4);
buttonPanel.setLayout(buttonPanelGridLayout);
buttonPanel.add(getNumber7Button());
buttonPanel.add(getNumber8Button());
buttonPanel.add(getNumber9Button());
buttonPanel.add(getDivideButton());
buttonPanel.add(getClearButton());
buttonPanel.add(getNumber4Button());
buttonPanel.add(getNumber5Button());
buttonPanel.add(getNumber6Button());
buttonPanel.add(getMultiplyButton());
buttonPanel.add(getSqrtButton());
buttonPanel.add(getNumber1Button());
buttonPanel.add(getNumber2Button());
buttonPanel.add(getNumber3Button());
buttonPanel.add(getMinusButton());
buttonPanel.add(getOneOverXButton());
buttonPanel.add(getNumber0Button());
buttonPanel.add(getSignButton());
buttonPanel.add(getDecimalButton());
buttonPanel.add(getPlusButton());
buttonPanel.add(getEqualButton());
setLayout(new BorderLayout());
add(buttonPanel, BorderLayout.CENTER);
add(getDisplayField(), BorderLayout.NORTH);
initConnections();
// Give this ValueEditor its preferred size.
this.setSize(this.getPreferredSize());
// Set the flags.
operandNext = false;
replaceOnOperandKeyPress = true;
isHandlingResultUndefined = false;
// Init the stacks.
operatorStack = new Stack<String>();
operandStack = new Stack<String>();
FocusTraversalPolicy focusTraversalPolicy = new FocusTraversalPolicy() {
@Override
public Component getComponentAfter(Container focusCycleRoot, Component aComponent) {
Component[] components = getComponents();
int index = Arrays.asList(components).indexOf(aComponent);
return components[(index + 1) % components.length];
}
@Override
public Component getComponentBefore(Container focusCycleRoot, Component aComponent) {
Component[] components = getComponents();
int index = Arrays.asList(components).indexOf(aComponent);
return components[(index - 1) % components.length];
}
@Override
public Component getDefaultComponent(Container focusCycleRoot) {
return getComponent(0);
}
@Override
public Component getFirstComponent(Container focusCycleRoot) {
return getComponent(0);
}
@Override
public Component getLastComponent(Container focusCycleRoot) {
return getComponent(getComponents().length - 1);
}
};
setFocusTraversalPolicy(focusTraversalPolicy);
getDisplayField().setFocusTraversalPolicy(focusTraversalPolicy);
// Ensure that keypad presses are correctly handled.
CalculatorKeyListener calcKeyListener = new CalculatorKeyListener();
getDisplayField().addKeyListener(calcKeyListener);
addKeyListener(calcKeyListener);
Component[] componentList = this.getComponents();
for (final Component component : componentList) {
component.addKeyListener(calcKeyListener);
}
Component[] buttonPanelComponentList = buttonPanel.getComponents();
for (final Component buttonPanelComponent : buttonPanelComponentList) {
buttonPanelComponent.addKeyListener(calcKeyListener);
}
}
/**
* Initializes connections
*/
private void initConnections() {
ButtonEventHandler buttonEventHandler = new ButtonEventHandler();
getNumber0Button().addActionListener(buttonEventHandler);
getNumber1Button().addActionListener(buttonEventHandler);
getNumber2Button().addActionListener(buttonEventHandler);
getNumber3Button().addActionListener(buttonEventHandler);
getNumber4Button().addActionListener(buttonEventHandler);
getNumber5Button().addActionListener(buttonEventHandler);
getNumber6Button().addActionListener(buttonEventHandler);
getNumber7Button().addActionListener(buttonEventHandler);
getNumber8Button().addActionListener(buttonEventHandler);
getNumber9Button().addActionListener(buttonEventHandler);
getSignButton().addActionListener(buttonEventHandler);
getDecimalButton().addActionListener(buttonEventHandler);
getDivideButton().addActionListener(buttonEventHandler);
getMultiplyButton().addActionListener(buttonEventHandler);
getMinusButton().addActionListener(buttonEventHandler);
getPlusButton().addActionListener(buttonEventHandler);
getEqualButton().addActionListener(buttonEventHandler);
getSqrtButton().addActionListener(buttonEventHandler);
getClearButton().addActionListener(buttonEventHandler);
getOneOverXButton().addActionListener(buttonEventHandler);
}
/**
* Make a button configured for use in the calculator.
* @param buttonText the text displayed by the button.
* @return a new button
*/
private JButton makeCalculatorButton(String buttonText) {
JButton button = new JButton();
button.setText(buttonText);
button.setVerticalAlignment(SwingConstants.CENTER);
button.setMargin(new Insets(0, 0, 0, 0));
return button;
}
/**
* Return the Clear Button
* @return JButton
*/
private JButton getClearButton() {
if (clearButton == null) {
clearButton = makeCalculatorButton("C");
}
return clearButton;
}
/**
* Return the Decimal Button
* @return JButton
*/
private JButton getDecimalButton() {
if (decimalButton == null) {
decimalButton = makeCalculatorButton(".");
}
return decimalButton;
}
/**
* Return the Divide Button
* @return JButton
*/
private JButton getDivideButton() {
if (divideButton == null) {
divideButton = makeCalculatorButton("/");
}
return divideButton;
}
/**
* Return the Equal Button
* @return JButton
*/
private JButton getEqualButton() {
if (equalButton == null) {
equalButton = makeCalculatorButton("=");
equalButton.setFont(new Font("sansserif", Font.BOLD, 12));
}
return equalButton;
}
/**
* Return the Minus Button
* @return JButton
*/
private JButton getMinusButton() {
if (minusButton == null) {
minusButton = makeCalculatorButton("-");
}
return minusButton;
}
/**
* Return the Multiply Button
* @return JButton
*/
private JButton getMultiplyButton() {
if (multiplyButton == null) {
multiplyButton = makeCalculatorButton("*");
}
return multiplyButton;
}
/**
* Return the 0 button
* @return JButton
*/
private JButton getNumber0Button() {
if (number0Button == null) {
number0Button = makeCalculatorButton("0");
}
return number0Button;
}
/**
* Return the 1 Button
* @return JButton
*/
private JButton getNumber1Button() {
if (number1Button == null) {
number1Button = makeCalculatorButton("1");
}
return number1Button;
}
/**
* Return the 2 Button
* @return JButton
*/
private JButton getNumber2Button() {
if (number2Button == null) {
number2Button = makeCalculatorButton("2");
}
return number2Button;
}
/**
* Return the 3 Button
* @return JButton
*/
private JButton getNumber3Button() {
if (number3Button == null) {
number3Button = makeCalculatorButton("3");
}
return number3Button;
}
/**
* Return the 4 Button
* @return JButton
*/
private JButton getNumber4Button() {
if (number4Button == null) {
number4Button = makeCalculatorButton("4");
}
return number4Button;
}
/**
* Return the 5 Button
* @return JButton
*/
private JButton getNumber5Button() {
if (number5Button == null) {
number5Button = makeCalculatorButton("5");
}
return number5Button;
}
/**
* Return the 6 Button.
* @return JButton
*/
private JButton getNumber6Button() {
if (number6Button == null) {
number6Button = makeCalculatorButton("6");
}
return number6Button;
}
/**
* Return the 7 Button
* @return JButton
*/
private JButton getNumber7Button() {
if (number7Button == null) {
number7Button = makeCalculatorButton("7");
}
return number7Button;
}
/**
* Return the 8 Button
* @return JButton
*/
private JButton getNumber8Button() {
if (number8Button == null) {
number8Button = makeCalculatorButton("8");
}
return number8Button;
}
/**
* Return the 9 Button
* @return JButton
*/
private JButton getNumber9Button() {
if (number9Button == null) {
number9Button = makeCalculatorButton("9");
}
return number9Button;
}
/**
* Return the OneOverX Button
* @return JButton
*/
private JButton getOneOverXButton() {
if (oneOverXButton == null) {
oneOverXButton = makeCalculatorButton("1/x");
}
return oneOverXButton;
}
/**
* Return the Plus Button
* @return JButton
*/
private JButton getPlusButton() {
if (plusButton == null) {
plusButton = makeCalculatorButton("+");
}
return plusButton;
}
/**
* Return the Sign Button
* @return JButton
*/
private JButton getSignButton() {
if (signButton == null) {
signButton = makeCalculatorButton("+/-");
}
return signButton;
}
/**
* Return the Sqrt Button
* @return JButton
*/
private JButton getSqrtButton() {
if (sqrtButton == null) {
sqrtButton = makeCalculatorButton("sqrt");
}
return sqrtButton;
}
/**
* Returns the calculator display field.
* @return JTextField the text field used for calculator display
*/
private JTextField getDisplayField() {
if (displayField == null) {
displayField = new JTextField();
}
return displayField;
}
/**
* Returns the precedence level of the param op if op is one of "/", "*", "-", or "+".
* Returns -1 otherwise.
* @return int
* @param op String
*/
private int getOpPrecedence(String op) {
int precedence = -1;
if (op.equals("/")) {
precedence = PRECEDENCE_DIVIDE;
} else if (op.equals("*")) {
precedence = PRECEDENCE_MULTIPLY;
} else if (op.equals("-")) {
precedence = PRECEDENCE_MINUS;
} else if (op.equals("+")) {
precedence = PRECEDENCE_PLUS;
}
return precedence;
}
/**
* Takes care of an op key being pressed.
* Call this whenever an op key is pressed.
* Param op should be one of "/", "*", "-", or "+".
* @param op String
*/
private void handleOpKeyPress(String op) {
replaceOnOperandKeyPress = false;
// If it's supposed to be an operand next, then replace the top
// operator in the stack.
if (operandNext) {
// Discard the top operator.
operatorStack.pop();
} else {
operandNext = true;
operandStack.push(getDisplayField().getText());
}
// If the next top operator has higher precedence, then
// eval it (and keep checking down stack if necessary).
while (!operatorStack.empty() && getOpPrecedence(operatorStack.peek()) >= getOpPrecedence(op)) {
evalTopOp();
// Possible that an illegal eval occured.
if (isHandlingResultUndefined) {
return;
}
}
operatorStack.push(op);
}
/**
* Takes the top operator in the operatorStack, and the top two operands in the operandStack, and evaluate.
* Then takes the result and pushes it on the operandStack.
* Note: You should always check the flag isHandlingResultUndefined, just in case an illegal eval occurred.
* Creation date: (24/01/01 10:37:16 AM)
*/
private void evalTopOp() {
double result = 0;
double operandB = Double.parseDouble(operandStack.pop());
double operandA = Double.parseDouble(operandStack.pop());
String op = operatorStack.pop();
if (op.equals("/")) {
// Check for invalid divider.
if (operandB == 0) {
handleResultUndefined(UNDEFINED_STRING);
return;
}
result = operandA / operandB;
} else if (op.equals("*")) {
result = operandA * operandB;
} else if (op.equals("-")) {
result = operandA - operandB;
} else if (op.equals("+")) {
result = operandA + operandB;
}
operandStack.push(String.valueOf(result));
}
/**
* Appends the param (appendStr) to the end of the text in the display.
* Note: If the display text is "" or "0", then we replace the display text with appendStr.
* Note: If we are trying to append after an operator (binary) key press (operandNext is true),
* then we replace the display text with appendStr, and set operandNext to false.
* @param appendStr
*/
private void appendToDisplay(String appendStr) {
if (operandNext) {
getDisplayField().setText(appendStr);
operandNext = false;
return;
}
if (replaceOnOperandKeyPress) {
getDisplayField().setText(appendStr);
replaceOnOperandKeyPress = false;
return;
}
String textValue = getDisplayField().getText();
if (!textValue.equals("") && !textValue.equals("0")) {
getDisplayField().setText(textValue + appendStr);
} else {
// No append, just replace.
getDisplayField().setText(appendStr);
}
}
/**
* Determines whether or not the text field will be follow its document mode.
* (only proper values matching type allowed, or anything goes).
* @param set whether or not to set document checking.
*/
private void setDocumentChecking(boolean set) {
((ValueFormat)getDisplayField().getDocument()).setChecking(set);
}
/**
* Handles the situation where the results of an expression are undefined.
* Disables all buttons except the clear button "C", and displays an error message.
* @param displayText the error message to display.
*/
private void handleResultUndefined(String displayText) {
isHandlingResultUndefined = true;
// Note: There are two cases:
// A) Focus is on calculator, in which case we want to display the error message,
// and disable buttons.
// B) Focus is on something else (not necessarily the parent), then no need to
// display the error message, or disable buttons.
if (this.hasOverallFocus()) {
// Give error msg.
setDocumentChecking(false);
getDisplayField().setText(displayText);
Component[] compList = buttonPanel.getComponents();
for (int i = 0; i < compList.length; i++) {
if (compList[i] instanceof JButton) {
// Do not allow the Clear button to be disabled.
// (It will mess up the focus).
if (!compList[i].equals(getClearButton())) {
compList[i].setEnabled(false);
}
}
}
// Disable all buttons except for the clear button:
getClearButton().requestFocus();
}
}
/**
* Reverts the overall state of this NumberValueEditor (and its parent) to normal.
*/
private void exitHandlingResultUndefined() {
// Correct the parent's textfield.
setDocumentChecking(true);
// Enable all buttons.
Component[] compList = buttonPanel.getComponents();
for (final Component component : compList) {
if (component instanceof JButton) {
component.setEnabled(true);
}
}
getDisplayField().setText("0");
isHandlingResultUndefined = false;
}
/**
* Clears everything.
* (Display value is 0, operand and operator stack is emptied, etc...)
*/
private void clear_ActionEvents() {
// First, check to see if we need to exit handling the undefined result.
if (isHandlingResultUndefined) {
exitHandlingResultUndefined();
}
getDisplayField().setText("0");
operandNext = false;
replaceOnOperandKeyPress = true;
operatorStack.clear();
operandStack.clear();
}
/**
* Reflect the '.' key press in the display.
* Note: Nothing will happen if the text in the display is empty, or it already contans a "."
* Note: An exception to the above note is if the "." appears as the last character, in which case we drop the ".".
*/
private void decimal_ActionEvents() {
// First, check to see if it's the special case that
// an operator (binary) key was just pressed previously.
if (operandNext) {
getDisplayField().setText("0.");
operandNext = false;
return;
}
// Next, check to see if it's the other special case that
// we need to replace on an operand key press.
if (replaceOnOperandKeyPress) {
getDisplayField().setText("0.");
replaceOnOperandKeyPress = false;
return;
}
String textValue = getDisplayField().getText();
if ((!textValue.equals("")) && (-1 == textValue.indexOf("."))) {
// Add a "." to the end of the value.
getDisplayField().setText(textValue + ".");
} else if (textValue.indexOf(".") == (textValue.length() - 1)) {
// Drop the "." at the end of the value.
getDisplayField().setText(textValue.substring(0, textValue.indexOf(".")));
}
}
/**
* Evaluates the entire expression, and displays it on the parent textfield.
* Note: If there are no operators to be eval, then nothing happens.
*/
private void equal_ActionEvents() {
if (!operatorStack.empty()) {
operandNext = false;
operandStack.push(getDisplayField().getText());
while (!operatorStack.empty()) {
evalTopOp();
// Possible that an illegal eval occured.
if (isHandlingResultUndefined) {
return;
}
}
// In theory, the final result should be the one value in the operandStack.
String result = operandStack.pop();
try {
Double.parseDouble(result);
// the result parses..
getDisplayField().setText(result);
} catch (NumberFormatException e) {
// the double does not parse (eg. it's "Infinity" or "NaN")
handleResultUndefined(result);
}
}
replaceOnOperandKeyPress = true;
}
/**
* Reflect the 1/x key press in the display.
*/
private void oneOverX_ActionEvents() {
operandNext = false;
replaceOnOperandKeyPress = true;
double value = Double.parseDouble(getDisplayField().getText());
// Check for invalid value.
if (value == 0) {
handleResultUndefined(UNDEFINED_STRING);
return;
}
double result = 1 / value;
getDisplayField().setText(String.valueOf(result));
}
/**
* Reflect the sign key press in the display.
*/
private void sign_ActionEvents() {
String textValue = getDisplayField().getText();
// Can only apply +/- to non-zero, non-empty strings.
if (!textValue.equals("") && !textValue.equals("0")) {
StringBuilder sbTextValue = new StringBuilder(textValue);
if (sbTextValue.charAt(0) == '-') {
// Turn value positive.
sbTextValue.deleteCharAt(0);
} else {
// Turn value negative.
sbTextValue.insert(0, '-');
}
getDisplayField().setText(sbTextValue.toString());
}
}
/**
* Perform square root on the Value in the display.
*/
private void sqrt_ActionEvents() {
operandNext = false;
replaceOnOperandKeyPress = true;
double value = Double.parseDouble(getDisplayField().getText());
// Check for invalid value.
if (value < 0) {
handleResultUndefined(UNDEFINED_STRING);
return;
}
double result = Math.sqrt(value);
getDisplayField().setText(String.valueOf(result));
}
/**
* {@inheritDoc}
*/
@Override
protected void commitValue() {
// Simulate a '=' key press.
equal_ActionEvents();
// Check if we need to evaluate the user input - maybe it's an undefined result.
if (isHandlingResultUndefined) {
exitHandlingResultUndefined();
}
ValueNode returnVN = null;
TypeExpr typeExpr = getValueNode().getTypeExpr();
PreludeTypeConstants typeConstants = valueEditorManager.getValueNodeBuilderHelper().getPreludeTypeConstants();
if (typeExpr.sameType(typeConstants.getByteType())) {
Double unRoundedVal = new Double(getDisplayField().getText());
Byte byteVal = new Byte((byte) Math.round(unRoundedVal.doubleValue()));
returnVN = new LiteralValueNode(byteVal, getValueNode().getTypeExpr());
} else if (typeExpr.sameType(typeConstants.getShortType())) {
Double unRoundedVal = new Double(getDisplayField().getText());
Short shortVal = new Short((short) Math.round(unRoundedVal.doubleValue()));
returnVN = new LiteralValueNode(shortVal, getValueNode().getTypeExpr());
} else if (typeExpr.sameType(typeConstants.getIntType())) {
Double unRoundedVal = new Double(getDisplayField().getText());
Integer integerVal = new Integer((int) Math.round(unRoundedVal.doubleValue()));
returnVN = new LiteralValueNode(integerVal, getValueNode().getTypeExpr());
} else if (typeExpr.sameType(typeConstants.getIntegerType())) {
BigDecimal unRoundedVal = new BigDecimal(getDisplayField().getText());
// Ridiculously, BigDecimal has 8 rounding modes, not one of which is equivalent
// to the mode used by Math.round! ROUND_HALF_CEILING is what such a mode would
// be called if it existed; we can fake it out by using a different rounding mode
// depending upon the sign of the unrounded value.
BigInteger bigIntegerVal;
if(unRoundedVal.signum() >= 0) {
bigIntegerVal = unRoundedVal.setScale(0, BigDecimal.ROUND_HALF_UP).toBigInteger();
} else {
bigIntegerVal = unRoundedVal.setScale(0, BigDecimal.ROUND_HALF_DOWN).toBigInteger();
}
returnVN = new LiteralValueNode(bigIntegerVal, getValueNode().getTypeExpr());
} else if (typeExpr.sameType(typeConstants.getDecimalType())) {
BigDecimal decimalVal = new BigDecimal(getDisplayField().getText());
returnVN = new LiteralValueNode(decimalVal, getValueNode().getTypeExpr());
} else if (typeExpr.sameType(typeConstants.getLongType())) {
Double unRoundedVal = new Double(getDisplayField().getText());
Long longVal = new Long(Math.round(unRoundedVal.doubleValue()));
returnVN = new LiteralValueNode(longVal, getValueNode().getTypeExpr());
} else if (typeExpr.sameType(typeConstants.getFloatType())) {
Float floatVal = new Float(getDisplayField().getText());
returnVN = new LiteralValueNode(floatVal, getValueNode().getTypeExpr());
} else if (typeExpr.sameType(typeConstants.getDoubleType())) {
Double doubleVal = new Double(getDisplayField().getText());
returnVN = new LiteralValueNode(doubleVal, getValueNode().getTypeExpr());
} else {
throw new IllegalStateException("Error in NumberValueEditor close:\nCurrently cannot handle this type.");
}
replaceValueNode(returnVN, false);
notifyValueCommitted();
}
/**
* {@inheritDoc}
*/
@Override
protected void cancelValue() {
// Simulate a '=' key press.
equal_ActionEvents();
// Check if we need to evaluate the user input - maybe it's an undefined result.
if (isHandlingResultUndefined) {
exitHandlingResultUndefined();
}
}
/**
* {@inheritDoc}
*/
@Override
public Component getDefaultFocusComponent() {
return getNumber0Button();
}
/**
* {@inheritDoc}
*/
@Override
public void setInitialValue() {
// Figure out what the value is that we're editing, as a double.
Double doubleVal;
try {
doubleVal = new Double(getValueNode().getTextValue());
} catch (NumberFormatException e) {
// What to do?
doubleVal = new Double("0.0");
}
// Configure the value format - the document for the display area.
TypeExpr doubleType = TypeExpr.makeNonParametricType(valueEditorManager.getPerspective().getTypeConstructor(CAL_Prelude.TypeConstructors.Double));
ValueNode doubleValueNode = new LiteralValueNode(doubleVal, doubleType);
ValueFormat valueFormat = new ValueFormat(new ValueEntryPanel(valueEditorHierarchyManager, doubleValueNode));
valueFormat.setChecking(true);
// Configure the display area itself.
getDisplayField().setDocument(valueFormat);
getDisplayField().setText(getValueNode().getTextValue());
getDisplayField().setEditable(false);
getDisplayField().setBackground(Color.WHITE);
getDisplayField().setHorizontalAlignment(SwingConstants.RIGHT);
}
}