Skip to content

Commit

Permalink
allow setting a rounding mode when writing a textfile, fixes #4782
Browse files Browse the repository at this point in the history
add documentation, #4782

some cleanup
  • Loading branch information
hansva committed Jan 16, 2025
1 parent 88e4fd9 commit 70121d9
Show file tree
Hide file tree
Showing 16 changed files with 266 additions and 241 deletions.
72 changes: 42 additions & 30 deletions core/src/main/java/org/apache/hop/core/row/IValueMeta.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import lombok.Getter;
import org.apache.hop.core.Const;
import org.apache.hop.core.database.DatabaseMeta;
import org.apache.hop.core.database.IDatabase;
Expand Down Expand Up @@ -280,16 +281,40 @@ enum TrimType implements IEnumHasCodeAndDescription {
LEFT("left", BaseMessages.getString(PKG, "ValueMeta.TrimType.Left"), TRIM_TYPE_LEFT),
RIGHT("right", BaseMessages.getString(PKG, "ValueMeta.TrimType.Right"), TRIM_TYPE_RIGHT),
BOTH("both", BaseMessages.getString(PKG, "ValueMeta.TrimType.Both"), TRIM_TYPE_BOTH);
private final String code;
private final String description;
private final int type;

/**
* -- GETTER -- Gets code
*
* @return value of code
*/
@Getter private final String code;

/**
* -- GETTER -- Gets description
*
* @return value of description
*/
@Getter private final String description;

/**
* -- GETTER -- Gets type
*
* @return value of type
*/
@Getter private final int type;

TrimType(String code, String description, int type) {
this.code = code;
this.description = description;
this.type = type;
}

/** The Rounding type codes */
@Getter
public static final String[] roundingTypeCode = {
"unnecessary", "ceiling", "down", "floor", "half_down", "half_even", "half_up", "up"
};

public static String[] getDescriptions() {
return IEnumHasCodeAndDescription.getDescriptions(TrimType.class);
}
Expand All @@ -310,33 +335,6 @@ public static TrimType lookupType(int type) {
}
return NONE;
}

/**
* Gets code
*
* @return value of code
*/
public String getCode() {
return code;
}

/**
* Gets description
*
* @return value of description
*/
public String getDescription() {
return description;
}

/**
* Gets type
*
* @return value of type
*/
public int getType() {
return type;
}
}

/** Default integer length for hardcoded metadata integers */
Expand Down Expand Up @@ -465,6 +463,20 @@ static String getTypeDescription(int type) {
*/
void setTrimType(int trimType);

/**
* Gets the rounding type.
*
* @return the rounding type
*/
String getRoundingType();

/**
* Sets the rounding type.
*
* @param roundingType the new rounding type
*/
void setRoundingType(String roundingType);

/**
* Gets the index.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public IValueMeta deserialize(
IValueMeta valueMeta = ValueMetaFactory.createValueMeta(name, type, length, precision);
valueMeta.setTrimType(root.get("trimType").asInt());
valueMeta.setStorageType(root.get("storageType").asInt());
valueMeta.setRoundingType(root.get("roundingType").asText());
valueMeta.setConversionMask(asString(root.get("conversionMask")));
JsonNode stringEncoding = root.get("stringEncoding");
if (!stringEncoding.isNull()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.InetAddress;
import java.net.SocketTimeoutException;
import java.nio.charset.Charset;
Expand All @@ -58,6 +59,8 @@
import java.util.Objects;
import java.util.TimeZone;
import javax.annotation.Nullable;
import lombok.Getter;
import lombok.Setter;
import org.apache.hop.core.Const;
import org.apache.hop.core.database.DatabaseMeta;
import org.apache.hop.core.database.IDatabase;
Expand Down Expand Up @@ -175,6 +178,11 @@ public class ValueMetaBase implements IValueMeta {
@HopMetadataProperty(key = "trim_type", intCodeConverter = TrimTypeCodeConverter.class)
protected int trimType;

@Getter
@Setter
@HopMetadataProperty(key = "roundingType")
protected String roundingType;

// The storage-type isn't meant to be serialized as metadata
protected int storageType;

Expand Down Expand Up @@ -257,6 +265,23 @@ public class ValueMetaBase implements IValueMeta {
BaseMessages.getString(PKG, "ValueMeta.TrimType.Both")
};

/** The Rounding type codes */
public static final String[] roundingTypeCode = {
"unnecessary", "ceiling", "down", "floor", "half_down", "half_even", "half_up", "up"
};

/** The Rounding description */
public static final String[] roundingTypeDesc = {
BaseMessages.getString(PKG, "ValueMeta.RoundingType.Unnecessary"),
BaseMessages.getString(PKG, "ValueMeta.RoundingType.Ceiling"),
BaseMessages.getString(PKG, "ValueMeta.RoundingType.Down"),
BaseMessages.getString(PKG, "ValueMeta.RoundingType.Floor"),
BaseMessages.getString(PKG, "ValueMeta.RoundingType.HalfDown"),
BaseMessages.getString(PKG, "ValueMeta.RoundingType.HalfEven"),
BaseMessages.getString(PKG, "ValueMeta.RoundingType.HalfUp"),
BaseMessages.getString(PKG, "ValueMeta.RoundingType.Up"),
};

// endregion

public ValueMetaBase() {
Expand Down Expand Up @@ -439,6 +464,7 @@ public boolean equals(Object o) {
&& getPrecision() == that.getPrecision()
&& type == that.type
&& trimType == that.trimType
&& roundingType == that.roundingType
&& storageType == that.storageType
&& collatorStrength == that.collatorStrength
&& caseInsensitive == that.caseInsensitive
Expand Down Expand Up @@ -469,6 +495,7 @@ public int hashCode() {
precision,
type,
trimType,
roundingType,
storageType,
origin,
comments,
Expand Down Expand Up @@ -1309,6 +1336,40 @@ public synchronized DecimalFormat getDecimalFormat(boolean useBigDecimal) {
decimalFormat.setParseBigDecimal(useBigDecimal);
DecimalFormatSymbols decimalFormatSymbols = decimalFormat.getDecimalFormatSymbols();

// Set the Rounding mode

if (roundingType != null) {
switch (roundingType) {
case "unnecessary":
decimalFormat.setRoundingMode(RoundingMode.UNNECESSARY);
break;
case "ceiling":
decimalFormat.setRoundingMode(RoundingMode.CEILING);
break;
case "down":
decimalFormat.setRoundingMode(RoundingMode.DOWN);
break;
case "floor":
decimalFormat.setRoundingMode(RoundingMode.FLOOR);
break;
case "half_down":
decimalFormat.setRoundingMode(RoundingMode.HALF_DOWN);
break;
case "half_even":
decimalFormat.setRoundingMode(RoundingMode.HALF_EVEN);
break;
case "half_up":
decimalFormat.setRoundingMode(RoundingMode.HALF_UP);
break;
case "up":
decimalFormat.setRoundingMode(RoundingMode.UP);
break;
default:
decimalFormat.setRoundingMode(RoundingMode.HALF_EVEN);
break;
}
}

if (!Utils.isEmpty(currencySymbol)) {
decimalFormatSymbols.setCurrencySymbol(currencySymbol);
}
Expand Down Expand Up @@ -4958,6 +5019,26 @@ public static final String getTrimTypeDesc(int i) {
return trimTypeDesc[i];
}

public static final String getRoundingTypeDesc(String code) {
for (int i = 0; i < roundingTypeCode.length; i++) {
if (roundingTypeCode[i].equalsIgnoreCase(code)) {
return roundingTypeDesc[i];
}
}
// return half_even as default
return roundingTypeDesc[5];
}

public static final String getRoundingTypeCode(String desc) {
for (int i = 0; i < roundingTypeDesc.length; i++) {
if (roundingTypeCode[i].equalsIgnoreCase(desc)) {
return roundingTypeCode[i];
}
}
// return half_even as default
return roundingTypeCode[5];
}

/**
* @return the conversionMetadata
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ public static void cloneInfo(IValueMeta source, IValueMeta target) throws HopPlu
}
target.setStringEncoding(source.getStringEncoding());
target.setTrimType(source.getTrimType());
target.setRoundingType(source.getRoundingType());
target.setDateFormatLenient(source.isDateFormatLenient());
target.setDateFormatLocale(source.getDateFormatLocale());
target.setDateFormatTimeZone(source.getDateFormatTimeZone());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,11 @@ ValueMeta.TrimType.Both=both
ValueMeta.TrimType.Left=left
ValueMeta.TrimType.None=none
ValueMeta.TrimType.Right=right
ValueMeta.RoundingType.Ceiling=Ceiling
ValueMeta.RoundingType.Down=Down
ValueMeta.RoundingType.Floor=Floor
ValueMeta.RoundingType.HalfDown=Half Down
ValueMeta.RoundingType.HalfEven=Half Even
ValueMeta.RoundingType.HalfUp=Half Up
ValueMeta.RoundingType.Unnecessary=Unnecessary
ValueMeta.RoundingType.Up=Up
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,11 @@ See Number Formats for a complete description of format symbols.
|Trim type|The trimming method to apply on the string.
Note: Trimming only works when there is no field length given.
|Null|If the value of the field is null, insert this string into the textfile
|Rounding Type| When writing numbers to a file you can specify the Rounding type being used by default `Half Even` is used, for more information look at the Rounding Types section
|Get|Click to retrieve the list of fields from the input fields stream(s)
|Minimal width|Alter the options in the fields tab in such a way that the resulting width of lines in the text file is minimal.
So instead of save 0000001, we write 1, etc.
String fields will no longer be padded to their specified length.
|===

include::../../snippets/rounding-types.adoc[]
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
////
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
////

=== Rounding Types

Rounding on Number and BigNumber data type fields is based on https://docs.oracle.com/javase/8/docs/api/java/math/RoundingMode.html[Java Rounding Mode]

By default, rounding mode `Half Even` is used this Rounding mode will round towards the "nearest neighbor" unless both neighbors are equidistant, in which case, round towards the even neighbor.

Example: Rounding from 1 to 0 digits +
5.5 -> 6 +
2.5 -> 2 +
-2.5 -> -2 +
-5.5 -> -6

==== Unnecessary
Rounding mode to assert that the requested operation has an exact result, hence no rounding is necessary. This mode will throw an error when you try to reduce the precision of a number

==== Ceiling
Rounding mode to round towards positive infinity.

==== Down
Rounding mode to round towards zero.

==== Floor
Rounding mode to round towards negative infinity.

==== Half Down
Rounding mode to round towards "nearest neighbor" unless both neighbors are equidistant, in which case round down.

==== Half Even
Rounding mode to round towards the "nearest neighbor" unless both neighbors are equidistant, in which case, round towards the even neighbor.

==== Half Up
Rounding mode to round towards "nearest neighbor" unless both neighbors are equidistant, in which case round up.

==== Up
Rounding mode to round away from zero.

==== Examples

[options="header"]
|===
| Input Number | Up |Down |Ceiling |Floor |Half Up |Half Down | Half Even | Unnecessary
|5.5|6|5|6|5|6|5|6|throw ArithmeticException
|2.5|3|2|3|2|3|2|2|throw ArithmeticException
|1.6|2|1|2|1|2|2|2|throw ArithmeticException
|1.1|2|1|2|1|1|1|1|throw ArithmeticException
|1.0|1|1|1|1|1|1|1|1
|-1.0|-1|-1|-1|-1|-1|-1|-1|-1
|-1.1|-2|-1|-1|-2|-1|-1|-1|throw ArithmeticException
|-1.6|-2|-1|-1|-2|-2|-2|-2|throw ArithmeticException
|-2.5|-3|-2|-3|-3|-3|-2|-2|throw ArithmeticException
|-5.5|-6|-5|-6|-6|-6|-5|-6|throw ArithmeticException
|===
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,12 @@ public void createControl(Composite parent) {
new ColumnInfo(
BaseMessages.getString(PKG, "SchemaDefinitionDialog.Fields.Column.FieldComment"),
ColumnInfo.COLUMN_TYPE_TEXT,
false)
false),
new ColumnInfo(
BaseMessages.getString(PKG, "SchemaDefinitionDialog.Fields.Column.RoundingType"),
ColumnInfo.COLUMN_TYPE_CCOMBO,
ValueMetaString.roundingTypeDesc,
true),
};

wFields =
Expand Down Expand Up @@ -257,6 +262,8 @@ public void setWidgetsContent() {
item.setText(10, trimType);

item.setText(11, Const.NVL(field.getComment(), ""));
String roundingType = ValueMetaBase.getRoundingTypeDesc(field.getRoundingType());
item.setText(12, roundingType);
}
}

Expand All @@ -283,6 +290,7 @@ public void getWidgetsContent(SchemaDefinition SchemaDefinition) {
sfd.setIfNullValue(item.getText(9));
sfd.setTrimType(ValueMetaString.getTrimTypeByDesc(item.getText(10)));
sfd.setComment(item.getText(11));
sfd.setRoundingType(ValueMetaBase.getRoundingTypeCode(item.getText(11)));
SchemaDefinition.getFieldDefinitions().add(sfd);
}
}
Expand Down
Loading

0 comments on commit 70121d9

Please sign in to comment.