This is an automated email from the ASF dual-hosted git repository.
doebele pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/empire-db.git
The following commit(s) were added to refs/heads/master by this push:
new c50e671c EMPIREDB-455: TextInputControl: Converter for numeric
ValueExpressions added
c50e671c is described below
commit c50e671cb0c12c3ab08b6361db121a8e78538496
Author: Rainer Döbele <[email protected]>
AuthorDate: Wed Mar 5 13:17:47 2025 +0100
EMPIREDB-455:
TextInputControl: Converter for numeric ValueExpressions added
---
.../empire/jakarta/controls/InputControl.java | 16 +-
.../empire/jakarta/controls/TextInputControl.java | 196 +++++++++++++++++----
.../apache/empire/jsf2/controls/InputControl.java | 16 +-
.../empire/jsf2/controls/TextInputControl.java | 178 ++++++++++++++++---
4 files changed, 337 insertions(+), 69 deletions(-)
diff --git
a/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/controls/InputControl.java
b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/controls/InputControl.java
index b2f9681f..0a612a21 100644
---
a/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/controls/InputControl.java
+++
b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/controls/InputControl.java
@@ -516,11 +516,10 @@ public abstract class InputControl
boolean evalExpression = !isInputValueExpressionEnabled();
Object value = ii.getValue(evalExpression);
if (value instanceof ValueExpression)
- {
- input.setValue(null);
- input.setLocalValueSet(false);
- input.setValueExpression("value", (ValueExpression) value);
-
+ { // set value expression
+ ValueExpression current = input.getValueExpression("value");
+ if (current!=value)
+ setInputValueExpression(input, (ValueExpression)value, ii);
// Object check =
((ValueExpression)value).getValue(FacesContext.getCurrentInstance().getELContext());
// log.info("Expression value is {}.", check);
}
@@ -532,6 +531,13 @@ public abstract class InputControl
addRemoveValueNullStyle(input, ObjectUtils.isEmpty(value));
}
}
+
+ protected void setInputValueExpression(UIInput input, ValueExpression
value, InputInfo ii)
+ {
+ input.setValue(null);
+ input.setLocalValueSet(false);
+ input.setValueExpression("value", value);
+ }
protected void clearSubmittedValue(UIInput input)
{
diff --git
a/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/controls/TextInputControl.java
b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/controls/TextInputControl.java
index 9a621310..dd1e40bc 100644
---
a/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/controls/TextInputControl.java
+++
b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/controls/TextInputControl.java
@@ -19,8 +19,11 @@
package org.apache.empire.jakarta.controls;
import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
import java.sql.Timestamp;
import java.text.DateFormat;
+import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
@@ -28,26 +31,34 @@ import java.util.Date;
import java.util.List;
import java.util.Locale;
-import jakarta.faces.component.UIComponent;
-import jakarta.faces.component.UIInput;
-import jakarta.faces.component.html.HtmlInputText;
-import jakarta.faces.component.html.HtmlOutputText;
-import jakarta.faces.component.html.HtmlPanelGroup;
-import jakarta.faces.context.FacesContext;
-import jakarta.faces.context.ResponseWriter;
-import jakarta.faces.event.PhaseId;
-
import org.apache.empire.commons.ObjectUtils;
import org.apache.empire.commons.Options;
import org.apache.empire.commons.StringUtils;
import org.apache.empire.data.Column;
import org.apache.empire.data.DataType;
+import org.apache.empire.db.exceptions.FieldIllegalValueException;
import org.apache.empire.exceptions.InvalidArgumentException;
+import org.apache.empire.exceptions.InvalidValueException;
import org.apache.empire.exceptions.UnexpectedReturnValueException;
+import org.apache.empire.jakarta.components.ControlTag;
+import org.apache.empire.jakarta.components.InputTag;
import org.apache.empire.jakarta.utils.TagStyleClass;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import jakarta.el.ValueExpression;
+import jakarta.faces.component.StateHolder;
+import jakarta.faces.component.UIComponent;
+import jakarta.faces.component.UIInput;
+import jakarta.faces.component.html.HtmlForm;
+import jakarta.faces.component.html.HtmlInputText;
+import jakarta.faces.component.html.HtmlOutputText;
+import jakarta.faces.component.html.HtmlPanelGroup;
+import jakarta.faces.context.FacesContext;
+import jakarta.faces.context.ResponseWriter;
+import jakarta.faces.convert.Converter;
+import jakarta.faces.event.PhaseId;
+
public class TextInputControl extends InputControl
{
private static final Logger log =
LoggerFactory.getLogger(TextInputControl.class);
@@ -62,6 +73,97 @@ public class TextInputControl extends InputControl
public static final String FRACTION_DIGITS = "fraction-digits:";
+ /**
+ * NumberInputConverter
+ * Formats a decimal value based on the NumberFormat
+ */
+ public static class NumberInputConverter implements Converter<Object>,
StateHolder
+ {
+ private NumberFormat nf;
+ private boolean trans = false;
+
+ /*
+ * Must have a default constructor!
+ */
+ public NumberInputConverter()
+ {
+ this.nf = null;
+ }
+
+ public NumberInputConverter(NumberFormat nf)
+ {
+ this.nf = nf;
+ }
+
+ @Override
+ public Object saveState(FacesContext context)
+ {
+ return nf;
+ }
+
+ @Override
+ public void restoreState(FacesContext context, Object state)
+ {
+ this.nf = (NumberFormat)state;
+ }
+
+ @Override
+ public boolean isTransient()
+ {
+ return trans;
+ }
+
+ @Override
+ public void setTransient(boolean newTransientValue)
+ {
+ this.trans = newTransientValue;
+ }
+
+ @Override
+ public String getAsString(FacesContext context, UIComponent component,
Object value)
+ {
+ return (nf!=null ? nf.format(value) : StringUtils.valueOf(value));
+ }
+
+ @Override
+ public Object getAsObject(FacesContext context, UIComponent component,
String value)
+ {
+ if (ObjectUtils.isEmpty(value))
+ return null;
+ try
+ { // parse
+ if (nf==null)
+ return new BigDecimal(value);
+ // parse now
+ Number number = nf.parse(value);
+ if (number instanceof BigDecimal)
+ { // Round to scale
+ int scale = nf.getMaximumFractionDigits();
+ number = ((BigDecimal)number).setScale(scale,
RoundingMode.HALF_UP);
+ }
+ return number;
+ }
+ catch (ParseException e)
+ { // find column
+ UIComponent inputComp = component.getParent();
+ while (inputComp!=null) {
+ // set the tag
+ if ((inputComp instanceof InputTag) || (inputComp
instanceof ControlTag))
+ { // Found an InputTag or ControlTag
+ Object column =
inputComp.getAttributes().get("column");
+ if (column instanceof Column)
+ throw new
FieldIllegalValueException((Column)column, value);
+ }
+ inputComp = inputComp.getParent();
+ if (inputComp instanceof HtmlForm)
+ break;
+ }
+ // Just throw an InvalidValueException
+ throw new InvalidValueException(value);
+ }
+ }
+ }
+
private Class<? extends HtmlInputText> inputComponentClass;
public TextInputControl(String name, Class<? extends HtmlInputText>
inputComponentClass)
@@ -184,6 +286,29 @@ public class TextInputControl extends InputControl
super.setInputStyleClass(input, cssStyleClass);
}
+ @Override
+ protected void setInputValueExpression(UIInput input, ValueExpression
value, InputInfo ii)
+ {
+ super.setInputValueExpression(input, value, ii);
+ // establish converter for decimal
+ DataType dataType = ii.getColumn().getDataType();
+ if (dataType.isNumeric())
+ { // get number format
+ NumberFormat nf;
+ if (dataType == DataType.INTEGER || dataType == DataType.AUTOINC)
+ nf = NumberFormat.getIntegerInstance(ii.getLocale()); //
Integer only
+ else {
+ // Decimal or Float
+ nf = getNumberFormat(dataType, ii, ii.getColumn());
+ // ParseBigDecimal
+ if (nf instanceof DecimalFormat)
+
((DecimalFormat)nf).setParseBigDecimal((dataType==DataType.DECIMAL));
+ }
+ // create converter
+ input.setConverter(new NumberInputConverter(nf));
+ }
+ }
+
// ------- parsing -------
@Override
@@ -203,12 +328,17 @@ public class TextInputControl extends InputControl
}
// Check other types
if (type == DataType.INTEGER)
- {
- return parseInteger(value, ii.getLocale());
+ { // get IntegerFormat and parse
+ NumberFormat nf = NumberFormat.getIntegerInstance(ii.getLocale());
+ return parseNumber(value, nf);
}
if (type == DataType.DECIMAL || type == DataType.FLOAT)
- {
- return parseDecimal(value, ii.getLocale());
+ { // get number format
+ NumberFormat nf = getNumberFormat(type, ii, column);
+ if (nf instanceof DecimalFormat)
+
((DecimalFormat)nf).setParseBigDecimal((type==DataType.DECIMAL));
+ // parse
+ return parseNumber(value, nf);
}
if (type == DataType.DATE || type == DataType.DATETIME || type ==
DataType.TIMESTAMP)
{
@@ -509,7 +639,7 @@ public class TextInputControl extends InputControl
String type =
StringUtils.valueOf(column.getAttribute(Column.COLATTR_NUMBER_TYPE));
boolean isInteger = "Integer".equalsIgnoreCase(type);
NumberFormat nf = (isInteger) ? NumberFormat.getIntegerInstance(locale)
- :
NumberFormat.getNumberInstance(locale);
+ : NumberFormat.getNumberInstance(locale);
// Groups Separator?
Object groupSep = column.getAttribute(Column.COLATTR_NUMBER_GROUPSEP);
nf.setGroupingUsed(groupSep != null &&
ObjectUtils.getBoolean(groupSep));
@@ -550,6 +680,12 @@ public class TextInputControl extends InputControl
nf.setMinimumFractionDigits(minFactionDigits);
nf.setMaximumFractionDigits(maxFactionDigits);
}
+ else if (!isInteger && dataType==DataType.DECIMAL)
+ { // Detect from column
+ int length = (int)column.getSize();
+ int maxFactionDigits = (int)(column.getSize()*10)-(length*10);
+ nf.setMaximumFractionDigits(maxFactionDigits);
+ }
// IntegerDigits (left-padding)
Object intDigits = column.getAttribute(Column.COLATTR_INTEGER_DIGITS);
if (intDigits != null) {
@@ -633,36 +769,28 @@ public class TextInputControl extends InputControl
// ------- value parsing -------
- protected Object parseInteger(String value, Locale locale)
- {
- NumberFormat nf = NumberFormat.getIntegerInstance();
- // Parse String
- try
- {
- return nf.parseObject(value);
- } catch (ParseException pe) {
- throw new NumberFormatException("Not a number: " + value + "
Exception: " + pe.toString());
- }
- }
-
- protected Object parseDecimal(String value, Locale locale)
+ protected Object parseNumber(String value, NumberFormat nf)
{
- NumberFormat nf = NumberFormat.getNumberInstance(locale);
- // Parse String
try
+ { // Parse Number
+ Number number = nf.parse(value);
+ if (number instanceof BigDecimal)
+ { // Round to scale
+ int scale = nf.getMaximumFractionDigits();
+ number = ((BigDecimal)number).setScale(scale,
RoundingMode.HALF_UP);
+ }
+ return number;
+ }
+ catch (ParseException pe)
{
- return nf.parseObject(value);
- } catch (ParseException pe) {
throw new NumberFormatException("Not a number: " + value + "
Exception: " + pe.toString());
}
}
protected Object parseDate(String s, DateFormat df)
{
- // Try to convert
try
- {
- // Parse Date
+ { // Parse Date
df.setLenient(true);
return df.parseObject(s);
} catch (ParseException pe) {
diff --git
a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/InputControl.java
b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/InputControl.java
index 95f7ec2a..51c23446 100644
---
a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/InputControl.java
+++
b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/InputControl.java
@@ -516,11 +516,10 @@ public abstract class InputControl
boolean evalExpression = !isInputValueExpressionEnabled();
Object value = ii.getValue(evalExpression);
if (value instanceof ValueExpression)
- {
- input.setValue(null);
- input.setLocalValueSet(false);
- input.setValueExpression("value", (ValueExpression) value);
-
+ { // set value expression
+ ValueExpression current = input.getValueExpression("value");
+ if (current!=value)
+ setInputValueExpression(input, (ValueExpression)value, ii);
// Object check =
((ValueExpression)value).getValue(FacesContext.getCurrentInstance().getELContext());
// log.info("Expression value is {}.", check);
}
@@ -532,6 +531,13 @@ public abstract class InputControl
addRemoveValueNullStyle(input, ObjectUtils.isEmpty(value));
}
}
+
+ protected void setInputValueExpression(UIInput input, ValueExpression
value, InputInfo ii)
+ {
+ input.setValue(null);
+ input.setLocalValueSet(false);
+ input.setValueExpression("value", value);
+ }
protected void clearSubmittedValue(UIInput input)
{
diff --git
a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/TextInputControl.java
b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/TextInputControl.java
index acac4d34..066f9020 100644
---
a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/TextInputControl.java
+++
b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/TextInputControl.java
@@ -19,8 +19,11 @@
package org.apache.empire.jsf2.controls;
import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
import java.sql.Timestamp;
import java.text.DateFormat;
+import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
@@ -28,13 +31,17 @@ import java.util.Date;
import java.util.List;
import java.util.Locale;
+import javax.el.ValueExpression;
+import javax.faces.component.StateHolder;
import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
+import javax.faces.component.html.HtmlForm;
import javax.faces.component.html.HtmlInputText;
import javax.faces.component.html.HtmlOutputText;
import javax.faces.component.html.HtmlPanelGroup;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
+import javax.faces.convert.Converter;
import javax.faces.event.PhaseId;
import org.apache.empire.commons.ObjectUtils;
@@ -42,8 +49,12 @@ import org.apache.empire.commons.Options;
import org.apache.empire.commons.StringUtils;
import org.apache.empire.data.Column;
import org.apache.empire.data.DataType;
+import org.apache.empire.db.exceptions.FieldIllegalValueException;
import org.apache.empire.exceptions.InvalidArgumentException;
+import org.apache.empire.exceptions.InvalidValueException;
import org.apache.empire.exceptions.UnexpectedReturnValueException;
+import org.apache.empire.jsf2.components.ControlTag;
+import org.apache.empire.jsf2.components.InputTag;
import org.apache.empire.jsf2.utils.TagStyleClass;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -62,6 +73,97 @@ public class TextInputControl extends InputControl
public static final String FRACTION_DIGITS = "fraction-digits:";
+ /**
+ * NumberInputConverter
+ * Formats a decimal value based on the NumberFormat
+ */
+ public static class NumberInputConverter implements Converter, StateHolder
+ {
+ private NumberFormat nf;
+ private boolean trans = false;
+
+ /*
+ * Must have a default constructor!
+ */
+ public NumberInputConverter()
+ {
+ this.nf = null;
+ }
+
+ public NumberInputConverter(NumberFormat nf)
+ {
+ this.nf = nf;
+ }
+
+ @Override
+ public Object saveState(FacesContext context)
+ {
+ return nf;
+ }
+
+ @Override
+ public void restoreState(FacesContext context, Object state)
+ {
+ this.nf = (NumberFormat)state;
+ }
+
+ @Override
+ public boolean isTransient()
+ {
+ return trans;
+ }
+
+ @Override
+ public void setTransient(boolean newTransientValue)
+ {
+ this.trans = newTransientValue;
+ }
+
+ @Override
+ public String getAsString(FacesContext context, UIComponent component,
Object value)
+ {
+ return (nf!=null ? nf.format(value) : StringUtils.valueOf(value));
+ }
+
+ @Override
+ public Object getAsObject(FacesContext context, UIComponent component,
String value)
+ {
+ if (ObjectUtils.isEmpty(value))
+ return null;
+ try
+ { // parse
+ if (nf==null)
+ return new BigDecimal(value);
+ // parse now
+ Number number = nf.parse(value);
+ if (number instanceof BigDecimal)
+ { // Round to scale
+ int scale = nf.getMaximumFractionDigits();
+ number = ((BigDecimal)number).setScale(scale,
RoundingMode.HALF_UP);
+ }
+ return number;
+ }
+ catch (ParseException e)
+ { // find column
+ UIComponent inputComp = component.getParent();
+ while (inputComp!=null) {
+ // set the tag
+ if ((inputComp instanceof InputTag) || (inputComp
instanceof ControlTag))
+ { // Found an InputTag or ControlTag
+ Object column =
inputComp.getAttributes().get("column");
+ if (column instanceof Column)
+ throw new
FieldIllegalValueException((Column)column, value);
+ }
+ inputComp = inputComp.getParent();
+ if (inputComp instanceof HtmlForm)
+ break;
+ }
+ // Just throw an InvalidValueException
+ throw new InvalidValueException(value);
+ }
+ }
+ }
+
private Class<? extends HtmlInputText> inputComponentClass;
public TextInputControl(String name, Class<? extends HtmlInputText>
inputComponentClass)
@@ -184,6 +286,29 @@ public class TextInputControl extends InputControl
super.setInputStyleClass(input, cssStyleClass);
}
+ @Override
+ protected void setInputValueExpression(UIInput input, ValueExpression
value, InputInfo ii)
+ {
+ super.setInputValueExpression(input, value, ii);
+ // establish converter for decimal
+ DataType dataType = ii.getColumn().getDataType();
+ if (dataType.isNumeric())
+ { // get number format
+ NumberFormat nf;
+ if (dataType == DataType.INTEGER || dataType == DataType.AUTOINC)
+ nf = NumberFormat.getIntegerInstance(ii.getLocale()); //
Integer only
+ else {
+ // Decimal or Float
+ nf = getNumberFormat(dataType, ii, ii.getColumn());
+ // ParseBigDecimal
+ if (nf instanceof DecimalFormat)
+
((DecimalFormat)nf).setParseBigDecimal((dataType==DataType.DECIMAL));
+ }
+ // create converter
+ input.setConverter(new NumberInputConverter(nf));
+ }
+ }
+
// ------- parsing -------
@Override
@@ -203,12 +328,17 @@ public class TextInputControl extends InputControl
}
// Check other types
if (type == DataType.INTEGER)
- {
- return parseInteger(value, ii.getLocale());
+ { // get IntegerFormat and parse
+ NumberFormat nf = NumberFormat.getIntegerInstance(ii.getLocale());
+ return parseNumber(value, nf);
}
if (type == DataType.DECIMAL || type == DataType.FLOAT)
- {
- return parseDecimal(value, ii.getLocale());
+ { // get number format
+ NumberFormat nf = getNumberFormat(type, ii, column);
+ if (nf instanceof DecimalFormat)
+
((DecimalFormat)nf).setParseBigDecimal((type==DataType.DECIMAL));
+ // parse
+ return parseNumber(value, nf);
}
if (type == DataType.DATE || type == DataType.DATETIME || type ==
DataType.TIMESTAMP)
{
@@ -509,7 +639,7 @@ public class TextInputControl extends InputControl
String type =
StringUtils.valueOf(column.getAttribute(Column.COLATTR_NUMBER_TYPE));
boolean isInteger = "Integer".equalsIgnoreCase(type);
NumberFormat nf = (isInteger) ? NumberFormat.getIntegerInstance(locale)
- :
NumberFormat.getNumberInstance(locale);
+ : NumberFormat.getNumberInstance(locale);
// Groups Separator?
Object groupSep = column.getAttribute(Column.COLATTR_NUMBER_GROUPSEP);
nf.setGroupingUsed(groupSep != null &&
ObjectUtils.getBoolean(groupSep));
@@ -550,6 +680,12 @@ public class TextInputControl extends InputControl
nf.setMinimumFractionDigits(minFactionDigits);
nf.setMaximumFractionDigits(maxFactionDigits);
}
+ else if (!isInteger && dataType==DataType.DECIMAL)
+ { // Detect from column
+ int length = (int)column.getSize();
+ int maxFactionDigits = (int)(column.getSize()*10)-(length*10);
+ nf.setMaximumFractionDigits(maxFactionDigits);
+ }
// IntegerDigits (left-padding)
Object intDigits = column.getAttribute(Column.COLATTR_INTEGER_DIGITS);
if (intDigits != null) {
@@ -633,36 +769,28 @@ public class TextInputControl extends InputControl
// ------- value parsing -------
- protected Object parseInteger(String value, Locale locale)
+ protected Object parseNumber(String value, NumberFormat nf)
{
- NumberFormat nf = NumberFormat.getIntegerInstance();
- // Parse String
- try
- {
- return nf.parseObject(value);
- } catch (ParseException pe) {
- throw new NumberFormatException("Not a number: " + value + "
Exception: " + pe.toString());
- }
- }
-
- protected Object parseDecimal(String value, Locale locale)
- {
- NumberFormat nf = NumberFormat.getNumberInstance(locale);
- // Parse String
try
+ { // Parse Number
+ Number number = nf.parse(value);
+ if (number instanceof BigDecimal)
+ { // Round to scale
+ int scale = nf.getMaximumFractionDigits();
+ number = ((BigDecimal)number).setScale(scale,
RoundingMode.HALF_UP);
+ }
+ return number;
+ }
+ catch (ParseException pe)
{
- return nf.parseObject(value);
- } catch (ParseException pe) {
throw new NumberFormatException("Not a number: " + value + "
Exception: " + pe.toString());
}
}
protected Object parseDate(String s, DateFormat df)
{
- // Try to convert
try
- {
- // Parse Date
+ { // Parse Date
df.setLenient(true);
return df.parseObject(s);
} catch (ParseException pe) {