io.atlasmap.actions.NumberFieldActions Maven / Gradle / Ivy
/**
* Copyright (C) 2017 Red Hat, Inc.
*
* Licensed 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.
*/
package io.atlasmap.actions;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import io.atlasmap.api.AtlasFieldAction;
import io.atlasmap.spi.AtlasFieldActionInfo;
import io.atlasmap.v2.Action;
import io.atlasmap.v2.AreaUnitType;
import io.atlasmap.v2.CollectionType;
import io.atlasmap.v2.ConvertAreaUnit;
import io.atlasmap.v2.ConvertDistanceUnit;
import io.atlasmap.v2.ConvertMassUnit;
import io.atlasmap.v2.ConvertVolumeUnit;
import io.atlasmap.v2.DistanceUnitType;
import io.atlasmap.v2.FieldType;
import io.atlasmap.v2.MassUnitType;
import io.atlasmap.v2.NumberType;
import io.atlasmap.v2.SumUp;
import io.atlasmap.v2.VolumeUnitType;
public class NumberFieldActions implements AtlasFieldAction {
// 1D
private static final double KILO_GRAMS_IN_A_POUND = 0.45359237;
private static final double YARDS_IN_A_MILE = 1760.0;
private static final double FEET_IN_A_YARD = 3.0;
private static final double INCHES_IN_A_FOOT = 12.0;
private static final double METERS_IN_A_INCH = 0.0254;
// 2D
private static final double SQUARE_FEET_IN_A_SQUARE_METER = Math.pow(1.0 / METERS_IN_A_INCH / INCHES_IN_A_FOOT,
2.0);
private static final double SQUARE_METERS_IN_A_SQUARE_MILE = Math
.pow(YARDS_IN_A_MILE * FEET_IN_A_YARD * INCHES_IN_A_FOOT * METERS_IN_A_INCH, 2.0);
private static final double SQUARE_FEET_IN_A_SQUARE_MILE = Math.pow(YARDS_IN_A_MILE * FEET_IN_A_YARD, 2.0);
// 3D
private static final double LITTERS_IN_A_CUBIC_METER = 1000.0;
private static final double CUBIC_FEET_IN_A_CUBIC_METER = Math.pow(1.0 / METERS_IN_A_INCH / INCHES_IN_A_FOOT, 3.0);
private static final double GALLONS_US_FLUID_IN_A_CUBIC_METER = 264.17205236;
private static Map> massConvertionTable;
static {
Map> rootTable = new HashMap<>();
Map kgRates = new HashMap<>();
kgRates.put(MassUnitType.KILO_GRAM, 1.0);
kgRates.put(MassUnitType.POUND, 1.0 / KILO_GRAMS_IN_A_POUND);
rootTable.put(MassUnitType.KILO_GRAM, Collections.unmodifiableMap(kgRates));
Map lbsRates = new HashMap<>();
lbsRates.put(MassUnitType.KILO_GRAM, KILO_GRAMS_IN_A_POUND);
lbsRates.put(MassUnitType.POUND, 1.0);
rootTable.put(MassUnitType.POUND, Collections.unmodifiableMap(lbsRates));
massConvertionTable = Collections.unmodifiableMap(rootTable);
}
private static Map> distanceConvertionTable;
static {
Map> rootTable = new HashMap<>();
Map mRates = new HashMap<>();
mRates.put(DistanceUnitType.METER, 1.0);
mRates.put(DistanceUnitType.FOOT, 1.0 / METERS_IN_A_INCH / INCHES_IN_A_FOOT);
mRates.put(DistanceUnitType.YARD, 1.0 / METERS_IN_A_INCH / INCHES_IN_A_FOOT / FEET_IN_A_YARD);
mRates.put(DistanceUnitType.MILE, 1.0 / METERS_IN_A_INCH / INCHES_IN_A_FOOT / FEET_IN_A_YARD / YARDS_IN_A_MILE);
mRates.put(DistanceUnitType.INCH, 1.0 / METERS_IN_A_INCH);
rootTable.put(DistanceUnitType.METER, Collections.unmodifiableMap(mRates));
Map ftRates = new HashMap<>();
ftRates.put(DistanceUnitType.METER, INCHES_IN_A_FOOT * METERS_IN_A_INCH);
ftRates.put(DistanceUnitType.FOOT, 1.0);
ftRates.put(DistanceUnitType.YARD, 1.0 / FEET_IN_A_YARD);
ftRates.put(DistanceUnitType.MILE, 1.0 / FEET_IN_A_YARD / YARDS_IN_A_MILE);
ftRates.put(DistanceUnitType.INCH, INCHES_IN_A_FOOT);
rootTable.put(DistanceUnitType.FOOT, Collections.unmodifiableMap(ftRates));
Map ydRates = new HashMap<>();
ydRates.put(DistanceUnitType.METER, FEET_IN_A_YARD * INCHES_IN_A_FOOT * METERS_IN_A_INCH);
ydRates.put(DistanceUnitType.FOOT, FEET_IN_A_YARD);
ydRates.put(DistanceUnitType.YARD, 1.0);
ydRates.put(DistanceUnitType.MILE, 1.0 / YARDS_IN_A_MILE);
ydRates.put(DistanceUnitType.INCH, FEET_IN_A_YARD * INCHES_IN_A_FOOT);
rootTable.put(DistanceUnitType.YARD, Collections.unmodifiableMap(ydRates));
Map miRates = new HashMap<>();
miRates.put(DistanceUnitType.METER, YARDS_IN_A_MILE * FEET_IN_A_YARD * INCHES_IN_A_FOOT * METERS_IN_A_INCH);
miRates.put(DistanceUnitType.FOOT, YARDS_IN_A_MILE * FEET_IN_A_YARD);
miRates.put(DistanceUnitType.YARD, YARDS_IN_A_MILE);
miRates.put(DistanceUnitType.MILE, 1.0);
miRates.put(DistanceUnitType.INCH, YARDS_IN_A_MILE * FEET_IN_A_YARD * INCHES_IN_A_FOOT);
rootTable.put(DistanceUnitType.MILE, Collections.unmodifiableMap(miRates));
Map inRates = new HashMap<>();
inRates.put(DistanceUnitType.METER, METERS_IN_A_INCH);
inRates.put(DistanceUnitType.FOOT, 1.0 / INCHES_IN_A_FOOT);
inRates.put(DistanceUnitType.YARD, 1.0 / INCHES_IN_A_FOOT / FEET_IN_A_YARD);
inRates.put(DistanceUnitType.MILE, 1.0 / INCHES_IN_A_FOOT / FEET_IN_A_YARD / YARDS_IN_A_MILE);
inRates.put(DistanceUnitType.INCH, 1.0);
rootTable.put(DistanceUnitType.INCH, Collections.unmodifiableMap(inRates));
distanceConvertionTable = Collections.unmodifiableMap(rootTable);
}
private static Map> areaConvertionTable;
static {
Map> rootTable = new HashMap<>();
Map m2Rates = new HashMap<>();
m2Rates.put(AreaUnitType.SQUARE_METER, 1.0);
m2Rates.put(AreaUnitType.SQUARE_FOOT, SQUARE_FEET_IN_A_SQUARE_METER);
m2Rates.put(AreaUnitType.SQUARE_MILE, 1.0 / SQUARE_METERS_IN_A_SQUARE_MILE);
rootTable.put(AreaUnitType.SQUARE_METER, Collections.unmodifiableMap(m2Rates));
Map ft2Rates = new HashMap<>();
ft2Rates.put(AreaUnitType.SQUARE_METER, 1.0 / SQUARE_FEET_IN_A_SQUARE_METER);
ft2Rates.put(AreaUnitType.SQUARE_FOOT, 1.0);
ft2Rates.put(AreaUnitType.SQUARE_MILE, 1.0 / SQUARE_FEET_IN_A_SQUARE_MILE);
rootTable.put(AreaUnitType.SQUARE_FOOT, Collections.unmodifiableMap(ft2Rates));
Map mi2Rates = new HashMap<>();
mi2Rates.put(AreaUnitType.SQUARE_METER, SQUARE_METERS_IN_A_SQUARE_MILE);
mi2Rates.put(AreaUnitType.SQUARE_FOOT, SQUARE_FEET_IN_A_SQUARE_MILE);
mi2Rates.put(AreaUnitType.SQUARE_MILE, 1.0);
rootTable.put(AreaUnitType.SQUARE_MILE, Collections.unmodifiableMap(mi2Rates));
areaConvertionTable = Collections.unmodifiableMap(rootTable);
}
private static Map> volumeConvertionTable;
static {
Map> rootTable = new HashMap<>();
Map m3Rates = new HashMap<>();
m3Rates.put(VolumeUnitType.CUBIC_METER, 1.0);
m3Rates.put(VolumeUnitType.LITTER, LITTERS_IN_A_CUBIC_METER);
m3Rates.put(VolumeUnitType.CUBIC_FOOT, CUBIC_FEET_IN_A_CUBIC_METER);
m3Rates.put(VolumeUnitType.GALLON_US_FLUID, GALLONS_US_FLUID_IN_A_CUBIC_METER);
rootTable.put(VolumeUnitType.CUBIC_METER, Collections.unmodifiableMap(m3Rates));
Map litterRates = new HashMap<>();
litterRates.put(VolumeUnitType.CUBIC_METER, 1.0 / LITTERS_IN_A_CUBIC_METER);
litterRates.put(VolumeUnitType.LITTER, 1.0);
litterRates.put(VolumeUnitType.CUBIC_FOOT, 1.0 / LITTERS_IN_A_CUBIC_METER * CUBIC_FEET_IN_A_CUBIC_METER);
litterRates.put(VolumeUnitType.GALLON_US_FLUID,
1.0 / LITTERS_IN_A_CUBIC_METER * GALLONS_US_FLUID_IN_A_CUBIC_METER);
rootTable.put(VolumeUnitType.LITTER, Collections.unmodifiableMap(litterRates));
Map cftRates = new HashMap<>();
cftRates.put(VolumeUnitType.CUBIC_METER, 1.0 / CUBIC_FEET_IN_A_CUBIC_METER);
cftRates.put(VolumeUnitType.LITTER, 1.0 / CUBIC_FEET_IN_A_CUBIC_METER * LITTERS_IN_A_CUBIC_METER);
cftRates.put(VolumeUnitType.CUBIC_FOOT, 1.0);
cftRates.put(VolumeUnitType.GALLON_US_FLUID,
1.0 / CUBIC_FEET_IN_A_CUBIC_METER * GALLONS_US_FLUID_IN_A_CUBIC_METER);
rootTable.put(VolumeUnitType.CUBIC_FOOT, Collections.unmodifiableMap(cftRates));
Map galUsFluidRates = new HashMap<>();
galUsFluidRates.put(VolumeUnitType.CUBIC_METER, 1.0 / GALLONS_US_FLUID_IN_A_CUBIC_METER);
galUsFluidRates.put(VolumeUnitType.LITTER, 1.0 / GALLONS_US_FLUID_IN_A_CUBIC_METER * LITTERS_IN_A_CUBIC_METER);
galUsFluidRates.put(VolumeUnitType.CUBIC_FOOT,
1.0 / GALLONS_US_FLUID_IN_A_CUBIC_METER * CUBIC_FEET_IN_A_CUBIC_METER);
galUsFluidRates.put(VolumeUnitType.GALLON_US_FLUID, 1.0);
rootTable.put(VolumeUnitType.GALLON_US_FLUID, Collections.unmodifiableMap(galUsFluidRates));
volumeConvertionTable = Collections.unmodifiableMap(rootTable);
}
@AtlasFieldActionInfo(name = "ConvertMassUnit", sourceType = FieldType.NUMBER, targetType = FieldType.NUMBER, sourceCollectionType = CollectionType.NONE, targetCollectionType = CollectionType.NONE)
public static Number convertMassUnit(Action action, Number input) {
if (input == null) {
return 0;
}
if (action == null || !(action instanceof ConvertMassUnit) || ((ConvertMassUnit) action).getFromUnit() == null
|| ((ConvertMassUnit) action).getToUnit() == null) {
throw new IllegalArgumentException("ConvertMassUnit must be specfied with fromUnit and toUnit");
}
MassUnitType fromUnit = ((ConvertMassUnit) action).getFromUnit();
MassUnitType toUnit = ((ConvertMassUnit) action).getToUnit();
double rate = massConvertionTable.get(fromUnit).get(toUnit);
return doMultiply(input, rate);
}
@AtlasFieldActionInfo(name = "ConvertDistanceUnit", sourceType = FieldType.NUMBER, targetType = FieldType.NUMBER, sourceCollectionType = CollectionType.NONE, targetCollectionType = CollectionType.NONE)
public static Number convertDistanceUnit(Action action, Number input) {
if (input == null) {
return 0;
}
if (action == null || !(action instanceof ConvertDistanceUnit)
|| ((ConvertDistanceUnit) action).getFromUnit() == null
|| ((ConvertDistanceUnit) action).getToUnit() == null) {
throw new IllegalArgumentException("ConvertDistanceUnit must be specfied with fromUnit and toUnit");
}
DistanceUnitType fromUnit = ((ConvertDistanceUnit) action).getFromUnit();
DistanceUnitType toUnit = ((ConvertDistanceUnit) action).getToUnit();
double rate = distanceConvertionTable.get(fromUnit).get(toUnit);
return doMultiply(input, rate);
}
@AtlasFieldActionInfo(name = "ConvertAreaUnit", sourceType = FieldType.NUMBER, targetType = FieldType.NUMBER, sourceCollectionType = CollectionType.NONE, targetCollectionType = CollectionType.NONE)
public static Number convertAreaUnit(Action action, Number input) {
if (input == null) {
return 0;
}
if (action == null || !(action instanceof ConvertAreaUnit) || ((ConvertAreaUnit) action).getFromUnit() == null
|| ((ConvertAreaUnit) action).getToUnit() == null) {
throw new IllegalArgumentException("ConvertAreaUnit must be specfied with fromUnit and toUnit");
}
AreaUnitType fromUnit = ((ConvertAreaUnit) action).getFromUnit();
AreaUnitType toUnit = ((ConvertAreaUnit) action).getToUnit();
double rate = areaConvertionTable.get(fromUnit).get(toUnit);
return doMultiply(input, rate);
}
@AtlasFieldActionInfo(name = "ConvertVolumeUnit", sourceType = FieldType.NUMBER, targetType = FieldType.NUMBER, sourceCollectionType = CollectionType.NONE, targetCollectionType = CollectionType.NONE)
public static Number convertVolumeUnit(Action action, Number input) {
if (input == null) {
return 0;
}
if (action == null || !(action instanceof ConvertVolumeUnit)
|| ((ConvertVolumeUnit) action).getFromUnit() == null
|| ((ConvertVolumeUnit) action).getToUnit() == null) {
throw new IllegalArgumentException("ConvertVolumeUnit must be specfied with fromUnit and toUnit");
}
VolumeUnitType fromUnit = ((ConvertVolumeUnit) action).getFromUnit();
VolumeUnitType toUnit = ((ConvertVolumeUnit) action).getToUnit();
double rate = volumeConvertionTable.get(fromUnit).get(toUnit);
return doMultiply(input, rate);
}
private static Number doMultiply(Number input, double rate) {
if (input instanceof BigDecimal) {
return ((BigDecimal) input).multiply(new BigDecimal(rate));
} else if (input instanceof Double) {
return (double) ((Double) input * rate);
} else if (input instanceof Float) {
return (float) ((Float) input * rate);
} else if (input instanceof Long) {
return (long) ((Long) input * rate);
} else if (input instanceof AtomicLong) {
return (long) (((AtomicLong) input).get() * rate);
} else if (input instanceof Integer) {
return (int) ((Integer) input * rate);
} else if (input instanceof AtomicInteger) {
return (int) (((AtomicInteger) input).get() * rate);
} else if (input instanceof Byte) {
return (byte) ((Byte) input * rate);
} else {
return Double.parseDouble(input.toString()) * rate;
}
}
@AtlasFieldActionInfo(name = "SumUp", sourceType = FieldType.COMPLEX, targetType = FieldType.NUMBER, sourceCollectionType = CollectionType.ALL, targetCollectionType = CollectionType.NONE)
public static Number sumUp(Action action, Object input) {
if (input == null) {
return 0;
}
if (action == null || !(action instanceof SumUp)) {
throw new IllegalArgumentException("SumUp must be specfied as an action");
}
SumUp sumup = (SumUp) action;
NumberType numberType = sumup.getNumberType();
Iterable> inputs;
if (input instanceof Iterable) {
inputs = (Iterable>) input;
} else if (input instanceof Map) {
inputs = ((Map, ?>) input).values();
} else if (input instanceof Number[]) {
inputs = Arrays.asList((Number[]) input);
} else if (input instanceof double[]) {
double[] din = (double[]) input;
List dinList = new ArrayList<>(din.length);
for (double e : din) {
dinList.add(e);
}
if (numberType == null) {
numberType = NumberType.DOUBLE;
}
inputs = dinList;
} else if (input instanceof float[]) {
float[] fin = (float[]) input;
List finList = new ArrayList<>(fin.length);
for (float e : fin) {
finList.add(e);
}
if (numberType == null) {
numberType = NumberType.FLOAT;
}
inputs = finList;
} else if (input instanceof long[]) {
long[] lin = (long[]) input;
List linList = new ArrayList<>(lin.length);
for (long e : lin) {
linList.add(e);
}
if (numberType == null) {
numberType = NumberType.LONG;
}
inputs = linList;
} else if (input instanceof int[]) {
int[] iin = (int[]) input;
List iinList = new ArrayList<>(iin.length);
for (int e : iin) {
iinList.add(e);
}
if (numberType == null) {
numberType = NumberType.INTEGER;
}
inputs = iinList;
} else if (input instanceof byte[]) {
byte[] bin = (byte[]) input;
List binList = new ArrayList<>(bin.length);
for (byte e : bin) {
binList.add(e);
}
if (numberType == null) {
numberType = NumberType.BYTE;
}
inputs = binList;
} else {
throw new IllegalArgumentException(
"Illegal input[" + input + "] it must be a Collection, Map or array of numbers");
}
// if number type is not yet detected, check the first element
if (numberType == null) {
Object e = inputs.iterator().next();
if (e instanceof BigDecimal) {
numberType = NumberType.DECIMAL;
} else if (e instanceof Float) {
numberType = NumberType.FLOAT;
} else if (e instanceof Long) {
numberType = NumberType.LONG;
} else if (e instanceof Integer) {
numberType = NumberType.INTEGER;
} else if (e instanceof Byte) {
numberType = NumberType.BYTE;
} else {
numberType = NumberType.DOUBLE; // Double by default
}
}
switch (numberType) {
case BYTE:
byte b = 0x0;
for (Object entry : inputs) {
if (entry instanceof Number) {
b += ((Number) entry).byteValue();
} else {
b += Byte.parseByte(entry.toString());
}
}
return b;
case DECIMAL:
BigDecimal bd = new BigDecimal(0);
for (Object entry : inputs) {
if (entry instanceof BigDecimal) {
bd.add((BigDecimal) entry);
} else if (entry instanceof Number) {
double dentry = ((Number) entry).doubleValue();
bd.add(new BigDecimal(dentry));
} else {
bd.add(new BigDecimal(entry.toString()));
}
}
return bd;
case DOUBLE:
double answer = 0.0d;
for (Object entry : inputs) {
if (entry instanceof Number) {
answer += ((Number) entry).doubleValue();
} else {
answer += Double.parseDouble(entry.toString());
}
}
return answer;
case FLOAT:
float f = 0.0f;
for (Object entry : inputs) {
if (entry instanceof Number) {
f += ((Number) entry).floatValue();
} else {
f += Float.parseFloat(entry.toString());
}
}
return f;
case INTEGER:
int i = 0;
for (Object entry : inputs) {
if (entry instanceof Number) {
i += ((Number) entry).intValue();
} else {
i += Integer.parseInt(entry.toString());
}
}
return i;
case LONG:
long l = 0L;
for (Object entry : inputs) {
if (entry instanceof Number) {
l += ((Number) entry).longValue();
} else {
l += Long.parseLong(entry.toString());
}
}
return l;
case SHORT:
short s = 0;
for (Object entry : inputs) {
if (entry instanceof Number) {
s += ((Number) entry).shortValue();
} else {
s += Short.parseShort(entry.toString());
}
}
return s;
default:
throw new IllegalArgumentException("Unsupported number type: " + numberType);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy