Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package org.nuiton.jaxx.widgets.number;
/*
* #%L
* JAXX :: Widgets Number
* %%
* Copyright (C) 2008 - 2020 Code Lutin, Ultreia.io
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Lesser Public License for more details.
*
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* .
* #L%
*/
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import io.ultreia.java4all.bean.JavaBean;
import io.ultreia.java4all.jaxx.widgets.BeanUIHandlerSupport;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.nuiton.jaxx.runtime.spi.UIHandler;
import javax.swing.JComponent;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.text.BadLocationException;
import java.awt.Dimension;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Created on 11/23/14.
*
* @author Tony Chemit - [email protected]
* @since 2.17
*/
public class NumberEditorHandler extends BeanUIHandlerSupport implements UIHandler {
protected final static ImmutableSet> INT_CLASSES = ImmutableSet.of(
byte.class,
Byte.class,
short.class,
Short.class,
int.class,
Integer.class,
BigInteger.class
);
protected static final ImmutableSet NULL_LIMIT_DECIMAL = ImmutableSet.copyOf(new String[]{"-", ".", "-."});
protected static final ImmutableSet NULL_LIMIT_INTEGER = ImmutableSet.copyOf(new String[]{"-"});
private static final Logger log = LogManager.getLogger(NumberEditorHandler.class);
private static final String VALIDATE_PROPERTY = "validate";
private static Map, NumberParserFormatter> numberFactories;
protected Pattern numberPattern;
protected NumberParserFormatter numberParserFormatter;
protected static NumberParserFormatter getNumberFactory(final Class> numberType) {
if (numberFactories == null) {
numberFactories = new HashMap<>();
NumberParserFormatter byteSupport = new NumberParserFormatter() {
@Override
public String format(Number numberValue) {
return numberValue == null ? "" : String.valueOf(numberValue);
}
@Override
public Byte parse(String textValue) {
Byte v;
if (NULL_LIMIT_INTEGER.contains(textValue)) {
v = null;
} else {
v = Byte.parseByte(textValue);
}
return v;
}
};
numberFactories.put(byte.class, byteSupport);
numberFactories.put(Byte.class, byteSupport);
NumberParserFormatter shortSupport = new NumberParserFormatter() {
@Override
public String format(Number numberValue) {
return numberValue == null ? "" : String.valueOf(numberValue);
}
@Override
public Short parse(String textValue) {
Short v;
if (NULL_LIMIT_INTEGER.contains(textValue)) {
v = null;
} else {
v = Short.parseShort(textValue);
}
return v;
}
};
numberFactories.put(short.class, shortSupport);
numberFactories.put(Short.class, shortSupport);
NumberParserFormatter integerSupport = new NumberParserFormatter() {
@Override
public String format(Number numberValue) {
return numberValue == null ? "" : String.valueOf(numberValue);
}
@Override
public Integer parse(String textValue) {
Integer v;
if (NULL_LIMIT_INTEGER.contains(textValue)) {
v = null;
} else {
v = Integer.parseInt(textValue);
}
return v;
}
};
numberFactories.put(int.class, integerSupport);
numberFactories.put(Integer.class, integerSupport);
NumberParserFormatter longSupport = new NumberParserFormatter() {
@Override
public String format(Number numberValue) {
return numberValue == null ? "" : String.valueOf(numberValue);
}
@Override
public Long parse(String textValue) {
Long v;
if (NULL_LIMIT_INTEGER.contains(textValue)) {
v = null;
} else {
v = Long.parseLong(textValue);
}
return v;
}
};
numberFactories.put(long.class, longSupport);
numberFactories.put(Long.class, longSupport);
NumberParserFormatter floatSupport = new NumberParserFormatter() {
final DecimalFormat df = new DecimalFormat("#.##########");
@Override
public String format(Number numberValue) {
String format;
if (numberValue == null) {
format = "";
} else {
format = String.valueOf(numberValue);
if (format.contains("E")) {
format = df.format(numberValue);
if (format.contains(",")) {
format = format.replace(",", ".");
}
}
}
return format;
}
@Override
public Float parse(String textValue) {
Float v;
if (NULL_LIMIT_DECIMAL.contains(textValue)) {
v = null;
} else {
boolean addSign = false;
if (textValue.startsWith("-")) {
addSign = true;
textValue = textValue.substring(1);
if (textValue.startsWith(".")) {
textValue = "0" + textValue;
}
}
v = Float.parseFloat(textValue);
if (addSign) {
v = -v;
}
}
return v;
}
};
numberFactories.put(float.class, floatSupport);
numberFactories.put(Float.class, floatSupport);
NumberParserFormatter doubleSupport = new NumberParserFormatter() {
final DecimalFormat df = new DecimalFormat("#.##########");
@Override
public String format(Number numberValue) {
String format;
if (numberValue == null) {
format = "";
} else {
format = String.valueOf(numberValue);
if (format.contains("E")) {
format = df.format(numberValue);
if (format.contains(",")) {
format = format.replace(",", ".");
}
}
}
return format;
}
@Override
public Double parse(String textValue) {
Double v;
if (NULL_LIMIT_DECIMAL.contains(textValue)) {
v = null;
} else {
boolean addSign = false;
if (textValue.startsWith("-")) {
addSign = true;
textValue = textValue.substring(1);
if (textValue.startsWith(".")) {
textValue = "0" + textValue;
}
}
v = Double.parseDouble(textValue);
if (addSign) {
v = -v;
}
}
return v;
}
};
numberFactories.put(double.class, doubleSupport);
numberFactories.put(Double.class, doubleSupport);
NumberParserFormatter bigIntegerSupport = new NumberParserFormatter() {
@Override
public String format(Number numberValue) {
return numberValue == null ? "" : String.valueOf(numberValue);
}
@Override
public BigInteger parse(String textValue) {
return new BigInteger(textValue);
}
};
numberFactories.put(BigInteger.class, bigIntegerSupport);
NumberParserFormatter bigDecimalSupport = new NumberParserFormatter() {
@Override
public String format(Number numberValue) {
return numberValue == null ? "" : String.valueOf(numberValue);
}
@Override
public BigDecimal parse(String textValue) {
return new BigDecimal(textValue);
}
};
numberFactories.put(BigDecimal.class, bigDecimalSupport);
}
for (Map.Entry, NumberParserFormatter> entry : numberFactories.entrySet()) {
if (entry.getKey().equals(numberType)) {
return entry.getValue();
}
}
throw new IllegalArgumentException("Could not find a NumberFactory for type " + numberType);
}
@Override
public void beforeInit(NumberEditor ui) {
super.beforeInit(ui);
NumberEditorModel model = new NumberEditorModel(new NumberEditorConfig());
ui.setContextValue(model);
}
@Override
public void afterInit(NumberEditor ui) {
// nothing to do here, everything is done in init method
}
@Override
public void init(NumberEditor ui) {
super.init(ui);
NumberEditorModel model = ui.getModel();
{
// Add some listeners on ui
ui.addPropertyChangeListener(NumberEditor.PROPERTY_SHOW_POPUP_BUTTON, evt -> {
if (ui.getPopup().isVisible()) {
setPopupVisible(false);
}
});
ui.addPropertyChangeListener(NumberEditor.PROPERTY_AUTO_POPUP, evt -> {
if (ui.getPopup().isVisible()) {
setPopupVisible(false);
}
});
ui.addPropertyChangeListener(NumberEditor.PROPERTY_POPUP_VISIBLE, evt -> setPopupVisible((Boolean) evt.getNewValue()));
ui.getTextField().addMouseListener(new PopupListener());
}
// apply incoming number value
Number numberValue = model.getNumberValue();
setTextValueFromNumberValue(numberValue);
}
@Override
protected String getProperty(NumberEditor ui) {
return ui.getModel().getConfig().getProperty();
}
@Override
protected void prepareInit(String property) {
log.debug(String.format("init NumberEditor %s", ui.getName()));
if (property == null) {
ui.setProperty(ui.getName());
}
NumberEditorModel model = ui.getModel();
NumberEditorConfig config = model.getConfig();
Class> numberType = config.getNumberType();
{
// init numberType
Preconditions.checkState(numberType != null, "Required a number type on " + ui);
numberParserFormatter = getNumberFactory(numberType);
log.info("init numberType: " + numberType + " on " + ui);
}
{
// init canUseDecimal
Boolean useDecimal = config.getUseDecimal();
boolean canBeDecimal = !INT_CLASSES.contains(numberType);
if (useDecimal == null) {
// guess from type
useDecimal = canBeDecimal;
config.setUseDecimal(useDecimal);
} else {
// Check this is possible
Preconditions.checkState(!useDecimal || canBeDecimal, "Can't use decimal with the following number type " + numberType + " on " + ui);
}
}
{
// init numberPattern
String numberPatternDef = model.getNumberPattern();
if (StringUtils.isNotEmpty(numberPatternDef)) {
setNumberPattern(numberPatternDef);
}
}
{
// list when number pattern changed to recompute it
model.addPropertyChangeListener(NumberEditorModel.PROPERTY_NUMBER_PATTERN, evt -> {
String newPattern = (String) evt.getNewValue();
setNumberPattern(newPattern);
log.info(String.format("set new numberPattern%s", newPattern));
if (StringUtils.isEmpty(newPattern)) {
numberPattern = null;
} else {
numberPattern = Pattern.compile(newPattern);
}
});
// listen when numberValue changed (should be from outside) to convert to textValue
model.addPropertyChangeListener(NumberEditorModel.PROPERTY_NUMBER_VALUE, evt -> {
Number newValue = (Number) evt.getNewValue();
setTextValueFromNumberValue(newValue);
});
// listen when textValue changed to convert to number value
model.addPropertyChangeListener(NumberEditorModel.PROPERTY_TEXT_VALUE, evt -> {
String newValue = (String) evt.getNewValue();
setNumberValueFromTextValue(newValue);
});
}
}
@Override
protected void prepareBindFromBean(String property, JavaBean bean) {
NumberEditorModel model = ui.getModel();
bean.addPropertyChangeListener(property, e -> {
Number oldValue = model.getNumberValue();
Number newValue = (Number) e.getNewValue();
if (!Objects.equals(oldValue, newValue)) {
ui.setNumberValue(newValue);
}
});
}
@Override
protected void prepareBindToBean(String property, JavaBean bean) {
NumberEditorModel model = ui.getModel();
model.addPropertyChangeListener(
NumberEditorModel.PROPERTY_NUMBER_VALUE, e -> {
if (!model.canUpdateBeanNumberValuePredicate().test(model)) {
return;
}
bean.set(property, e.getNewValue());
});
}
/**
* Ajoute le caractère donné à l'endroit où est le curseur dans la zone de
* saisie et met à jour le modèle.
*
* @param c le caractère à ajouter.
*/
public void addChar(char c) {
try {
ui.getTextField().getDocument().insertString(ui.getTextField().getCaretPosition(), c + "", null);
} catch (BadLocationException e) {
log.warn(e);
}
setTextValue(ui.getTextField().getText());
}
/**
* Supprime le caractère juste avant le curseur du modèle (textuel) et
* met à jour la zone de saisie.
*/
public void removeChar() {
JTextField textField = ui.getTextField();
int position = textField.getCaretPosition();
if (position < 1) {
if (log.isDebugEnabled()) {
log.debug("cannot remove when caret on first position or text empty");
}
// on est au debut du doc, donc rien a faire
return;
}
try {
textField.getDocument().remove(position - 1, 1);
} catch (BadLocationException ex) {
// ne devrait jamais arrive vu qu'on a fait le controle...
log.debug(ex);
return;
}
String newText = textField.getText();
if (log.isDebugEnabled()) {
log.debug("text updated : " + newText);
}
position--;
textField.setCaretPosition(position);
setTextValue(newText);
}
public void reset() {
// ui.getModel().setNumberValue(null);
setTextValue("");
}
/**
* Permute le signe dans la zone de saisie et
* dans le modèle.
*/
public void toggleSign() {
String newValue = ui.getModel().getTextValue();
if (newValue.startsWith("-")) {
newValue = newValue.substring(1);
} else {
newValue = "-" + newValue;
}
setTextValue(newValue);
}
public void setTextValue(KeyEvent event, String newText) {
if (event.getKeyCode() == KeyEvent.VK_D && event.isControlDown()) {
log.debug("Reset code");
newText = "";
}
setTextValue(newText);
}
public void setTextValue(String newText) {
NumberEditorModel model = ui.getModel();
boolean textValid;
if (StringUtils.isEmpty(newText)) {
// empty text is always valid
textValid = true;
} else {
if (numberPattern != null) {
// use given number pattern to check text
Matcher matcher = numberPattern.matcher(newText);
textValid = matcher.matches();
if (!textValid && model.isCanUseSign() && "-".equals(newText)) {
// limit case when we have only "-"
textValid = true;
}
} else {
// check text validity "by hand"
//TODO
textValid = true;
}
}
if (textValid) {
log.info(String.format("Text [%s] is valid, will set it to model", newText));
int caretPosition = ui.getTextField().getCaretPosition();
model.setTextValue(newText);
if (caretPosition >= 0 && caretPosition < newText.length()) {
ui.getTextField().setCaretPosition(caretPosition);
}
} else {
String oldText = model.getTextValue();
if (oldText == null) {
oldText = "";
}
log.info(String.format("Text [%s] is not valid, will rollback to previous valid text: %s", newText, oldText));
int caretPosition = ui.getTextField().getCaretPosition() - 1;
ui.getTextField().setText(oldText);
if (caretPosition >= 0 && caretPosition < oldText.length()) {
ui.getTextField().setCaretPosition(caretPosition);
}
}
}
/**
* Affiche ou cache la popup.
*
* @param newValue la nouvelle valeur de visibilité de la popup.
*/
public void setPopupVisible(Boolean newValue) {
if (log.isTraceEnabled()) {
log.trace(newValue);
}
if (newValue == null || !newValue) {
ui.getPopup().setVisible(false);
return;
}
SwingUtilities.invokeLater(() -> {
JComponent invoker =
ui.isShowPopupButton() ?
ui.getShowPopUpButton() :
ui;
Dimension dim = ui.getPopup().getPreferredSize();
int x = (int) (invoker.getPreferredSize().getWidth() - dim.getWidth());
ui.getPopup().show(invoker, x, invoker.getHeight());
ui.getTextField().requestFocusInWindow();
});
}
protected void setNumberPattern(String newPattern) {
try {
this.numberPattern = Pattern.compile(newPattern);
} catch (Exception e) {
throw new IllegalStateException("Could not compute numberPattern " + newPattern + " on " + ui, e);
}
if (log.isInfoEnabled()) {
log.info("init numberPattern: " + numberPattern + " on " + ui);
}
}
protected void setNumberValueFromTextValue(String textValue) {
if (ui.getModel().isNumberValueIsAdjusting()) {
// do nothing if number value is already adjusting
return;
}
Number numberValue;
if (StringUtils.isBlank(textValue)) {
numberValue = null;
} else {
numberValue = numberParserFormatter.parse(textValue);
}
if (log.isInfoEnabled()) {
log.info("Set numberValue " + numberValue + " from textValue " + textValue);
}
ui.getModel().setNumberValue(numberValue);
}
protected void setTextValueFromNumberValue(Number numberValue) {
if (ui.getModel().isTextValueIsAdjusting()) {
// do nothing if text value is already adjusting
return;
}
String textValue = numberParserFormatter.format(numberValue);
if (log.isInfoEnabled()) {
log.info("Set textValue " + textValue + " from numberValue " + numberValue);
}
ui.getModel().setTextValue(textValue);
}
protected void validate() {
setPopupVisible(false);
// fire validate property (to be able to notify listeners)
ui.firePropertyChange(VALIDATE_PROPERTY, null, true);
}
interface NumberParserFormatter {
Number parse(String textValue);
String format(Number numberValue);
}
protected class PopupListener extends MouseAdapter {
@Override
public void mousePressed(MouseEvent e) {
maybeShowPopup(e);
}
@Override
public void mouseReleased(MouseEvent e) {
maybeShowPopup(e);
}
protected void maybeShowPopup(MouseEvent e) {
if (!e.isPopupTrigger()) {
return;
}
if (ui.isAutoPopup()) {
if (ui.isPopupVisible()) {
if (!ui.getPopup().isVisible()) {
setPopupVisible(true);
}
// popup already visible
} else {
// set popup auto
ui.setPopupVisible(true);
}
} else {
if (ui.isPopupVisible()) {
setPopupVisible(true);
}
}
}
}
}