Java tutorial
/* * Copyright (c) 2013 Villu Ruusmann * * This file is part of JPMML-Evaluator * * JPMML-Evaluator is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * JPMML-Evaluator 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with JPMML-Evaluator. If not, see <http://www.gnu.org/licenses/>. */ package org.jpmml.evaluator; import com.google.common.math.DoubleMath; import org.dmg.pmml.DataType; import org.dmg.pmml.OpType; import org.joda.time.Days; import org.joda.time.LocalDate; import org.joda.time.LocalDateTime; import org.joda.time.LocalTime; import org.joda.time.Seconds; import org.joda.time.format.DateTimeFormatter; import org.joda.time.format.DateTimeParser; import org.joda.time.format.DateTimeParserBucket; import org.jpmml.manager.UnsupportedFeatureException; public class TypeUtil { private TypeUtil() { } static public boolean equals(DataType dataType, Object left, Object right) { return (cast(dataType, left)).equals(cast(dataType, right)); } @SuppressWarnings(value = { "rawtypes", "unchecked" }) static public int compare(DataType dataType, Object left, Object right) { return ((Comparable) cast(dataType, left)).compareTo(cast(dataType, right)); } static public Object parseOrCast(DataType dataType, Object value) { if (value instanceof String) { String string = (String) value; return parse(dataType, string); } return cast(dataType, value); } /** * @throws IllegalArgumentException If the String representation of the value cannot be parsed to the requested representation. */ static public Object parse(DataType dataType, String value) { switch (dataType) { case STRING: return value; case INTEGER: return parseInteger(value); case FLOAT: return parseFloat(value); case DOUBLE: return parseDouble(value); case BOOLEAN: return parseBoolean(value); case DATE: return parseDate(value); case TIME: return parseTime(value); case DATE_TIME: return parseDateTime(value); case DATE_DAYS_SINCE_1960: return new DaysSinceDate(YEAR_1960, parseDate(value)); case DATE_DAYS_SINCE_1970: return new DaysSinceDate(YEAR_1970, parseDate(value)); case DATE_DAYS_SINCE_1980: return new DaysSinceDate(YEAR_1980, parseDate(value)); case TIME_SECONDS: return new SecondsSinceMidnight(parseSeconds(value)); case DATE_TIME_SECONDS_SINCE_1960: return new SecondsSinceDate(YEAR_1960, parseDateTime(value)); case DATE_TIME_SECONDS_SINCE_1970: return new SecondsSinceDate(YEAR_1970, parseDateTime(value)); case DATE_TIME_SECONDS_SINCE_1980: return new SecondsSinceDate(YEAR_1980, parseDateTime(value)); default: throw new UnsupportedFeatureException(); } } static private Integer parseInteger(String value) { try { long result = Long.parseLong(value); return toInt(result); } catch (NumberFormatException nfeInteger) { try { double result = Double.parseDouble(value); if (DoubleMath.isMathematicalInteger(result)) { return toInt((long) result); } } catch (NumberFormatException nfeDouble) { // Ignored } throw nfeInteger; } } static private Float parseFloat(String value) { return Float.valueOf(value); } static private Double parseDouble(String value) { return Double.valueOf(value); } static private Boolean parseBoolean(String value) { if ("true".equalsIgnoreCase(value)) { return Boolean.TRUE; } else if ("false".equalsIgnoreCase(value)) { return Boolean.FALSE; } try { return (Boolean) cast(DataType.BOOLEAN, parseDouble(value)); } catch (NumberFormatException nfe) { // Ignored } catch (TypeCheckException tce) { // Ignored } throw new IllegalArgumentException(value); } static private LocalDate parseDate(String value) { return LocalDate.parse(value); } static private LocalTime parseTime(String value) { return LocalTime.parse(value); } static private LocalDateTime parseDateTime(String value) { return LocalDateTime.parse(value); } @SuppressWarnings(value = { "deprecation" }) static private Seconds parseSeconds(String value) { DateTimeFormatter format = SecondsSinceMidnight.getFormat(); DateTimeParser parser = format.getParser(); DateTimeParserBucket bucket = new DateTimeParserBucket(0, null, null); bucket.setZone(null); int result = parser.parseInto(bucket, value, 0); if (result >= 0 && result >= value.length()) { long millis = bucket.computeMillis(true); return Seconds.seconds((int) (millis / 1000L)); } throw new IllegalArgumentException(value); } static public String format(Object value) { if (value instanceof String) { return (String) value; } // End if if (value != null) { return String.valueOf(value); } throw new EvaluationException(); } /** * @return The data type of the value. */ static public DataType getDataType(Object value) { if (value instanceof String) { return DataType.STRING; } else if (value instanceof Integer) { return DataType.INTEGER; } else if (value instanceof Float) { return DataType.FLOAT; } else if (value instanceof Double) { return DataType.DOUBLE; } else if (value instanceof Boolean) { return DataType.BOOLEAN; } else if (value instanceof LocalDate) { return DataType.DATE; } else if (value instanceof LocalTime) { return DataType.TIME; } else if (value instanceof LocalDateTime) { return DataType.DATE_TIME; } else if (value instanceof DaysSinceDate) { DaysSinceDate period = (DaysSinceDate) value; return getDaysDataType(period.getEpoch()); } else if (value instanceof SecondsSinceMidnight) { return DataType.TIME_SECONDS; } else if (value instanceof SecondsSinceDate) { SecondsSinceDate period = (SecondsSinceDate) value; return getSecondsDataType(period.getEpoch()); } throw new EvaluationException(); } /** * @return The least restrictive data type of the data types of two values */ static public DataType getResultDataType(DataType left, DataType right) { if ((left).equals(right)) { return left; } // "When the input parameters have multiple dataTypes, the least restrictive dataType will be inherited by default" for (int i = 0; i < inheritanceSequence.length; i++) { DataType dataType = inheritanceSequence[i]; if ((dataType).equals(left) || (dataType).equals(right)) { return dataType; } } throw new EvaluationException(); } static public OpType getOpType(DataType dataType) { switch (dataType) { case STRING: return OpType.CATEGORICAL; case INTEGER: case FLOAT: case DOUBLE: return OpType.CONTINUOUS; case BOOLEAN: return OpType.CATEGORICAL; case DATE: case TIME: case DATE_TIME: case DATE_DAYS_SINCE_0: case DATE_DAYS_SINCE_1960: case DATE_DAYS_SINCE_1970: case DATE_DAYS_SINCE_1980: case TIME_SECONDS: case DATE_TIME_SECONDS_SINCE_0: case DATE_TIME_SECONDS_SINCE_1960: case DATE_TIME_SECONDS_SINCE_1970: case DATE_TIME_SECONDS_SINCE_1980: return OpType.ORDINAL; default: throw new UnsupportedFeatureException(); } } static public Object cast(DataType dataType, Object value) { switch (dataType) { case STRING: return toString(value); case INTEGER: return toInteger(value); case FLOAT: return toFloat(value); case DOUBLE: return toDouble(value); case BOOLEAN: return toBoolean(value); case DATE: return toDate(value); case TIME: return toTime(value); case DATE_TIME: return toDateTime(value); case DATE_DAYS_SINCE_1960: return toDaysSinceDate(value, YEAR_1960); case DATE_DAYS_SINCE_1970: return toDaysSinceDate(value, YEAR_1970); case DATE_DAYS_SINCE_1980: return toDaysSinceDate(value, YEAR_1980); case TIME_SECONDS: return toSecondsSinceMidnight(value); case DATE_TIME_SECONDS_SINCE_1960: return toSecondsSinceDate(value, YEAR_1960); case DATE_TIME_SECONDS_SINCE_1970: return toSecondsSinceDate(value, YEAR_1970); case DATE_TIME_SECONDS_SINCE_1980: return toSecondsSinceDate(value, YEAR_1980); default: throw new UnsupportedFeatureException(); } } /** * Casts the specified value to String data type. * * @see DataType#STRING */ static private String toString(Object value) { if (value instanceof String) { return (String) value; } else if ((value instanceof Double) || (value instanceof Float) || (value instanceof Long) || (value instanceof Integer) || (value instanceof Short) || (value instanceof Byte)) { Number number = (Number) value; return number.toString(); } throw new TypeCheckException(DataType.STRING, value); } /** * Casts the specified value to Integer data type. * * @see DataType#INTEGER */ static private Integer toInteger(Object value) { if (value instanceof Integer) { return (Integer) value; } else if ((value instanceof Double) || (value instanceof Float)) { Number number = (Number) value; if (DoubleMath.isMathematicalInteger(number.doubleValue())) { return toInt(number.longValue()); } } else if (value instanceof Long) { Long number = (Long) value; return toInt(number.longValue()); } else if ((value instanceof Short) || (value instanceof Byte)) { Number number = (Number) value; return Integer.valueOf(number.intValue()); } else if ((value instanceof DaysSinceDate) || (value instanceof SecondsSinceDate) || (value instanceof SecondsSinceMidnight)) { Number number = (Number) value; return Integer.valueOf(number.intValue()); } throw new TypeCheckException(DataType.INTEGER, value); } static private int toInt(long value) { if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) { throw new EvaluationException(); } return (int) value; } /** * Casts the specified value to Float data type. * * @see DataType#FLOAT */ static private Float toFloat(Object value) { if (value instanceof Float) { return (Float) value; } else if ((value instanceof Long) || (value instanceof Integer) || (value instanceof Short) || (value instanceof Byte)) { Number number = (Number) value; return Float.valueOf(number.floatValue()); } else if ((value instanceof DaysSinceDate) || (value instanceof SecondsSinceDate) || (value instanceof SecondsSinceMidnight)) { Number number = (Number) value; return Float.valueOf(number.floatValue()); } throw new TypeCheckException(DataType.FLOAT, value); } /** * Casts the specified value to Double data type. * * @see DataType#DOUBLE */ static private Double toDouble(Object value) { if (value instanceof Double) { return (Double) value; } else if ((value instanceof Float) || (value instanceof Long) || (value instanceof Integer) || (value instanceof Short) || (value instanceof Byte)) { Number number = (Number) value; return Double.valueOf(number.doubleValue()); } else if ((value instanceof DaysSinceDate) || (value instanceof SecondsSinceDate) || (value instanceof SecondsSinceMidnight)) { Number number = (Number) value; return Double.valueOf(number.doubleValue()); } throw new TypeCheckException(DataType.DOUBLE, value); } /** * @see DataType#BOOLEAN */ static private Boolean toBoolean(Object value) { if (value instanceof Boolean) { return (Boolean) value; } else if ((value instanceof Double) || (value instanceof Float) || (value instanceof Long) || (value instanceof Integer) || (value instanceof Short) || (value instanceof Byte)) { Number number = (Number) value; if (Double.compare(1d, number.doubleValue()) == 0) { return Boolean.TRUE; } else if (Double.compare(0d, number.doubleValue()) == 0) { return Boolean.FALSE; } } throw new TypeCheckException(DataType.BOOLEAN, value); } /** * @see DataType#DATE */ static private LocalDate toDate(Object value) { if (value instanceof LocalDate) { return (LocalDate) value; } else if (value instanceof LocalDateTime) { LocalDateTime instant = (LocalDateTime) value; return instant.toLocalDate(); } throw new TypeCheckException(DataType.DATE, value); } /** * @see DataType#TIME */ static private LocalTime toTime(Object value) { if (value instanceof LocalTime) { return (LocalTime) value; } else if (value instanceof LocalDateTime) { LocalDateTime instant = (LocalDateTime) value; return instant.toLocalTime(); } throw new TypeCheckException(DataType.TIME, value); } /** * @see DataType#DATE_TIME */ static private LocalDateTime toDateTime(Object value) { if (value instanceof LocalDateTime) { return (LocalDateTime) value; } throw new TypeCheckException(DataType.DATE_TIME, value); } /** * @see DataType#DATE_DAYS_SINCE_1960 * @see DataType#DATE_DAYS_SINCE_1970 * @see DataType#DATE_DAYS_SINCE_1980 */ static private DaysSinceDate toDaysSinceDate(Object value, LocalDate epoch) { if (value instanceof DaysSinceDate) { DaysSinceDate period = (DaysSinceDate) value; if ((period.getEpoch()).equals(epoch)) { return period; } Days difference = Days.daysBetween(epoch, period.getEpoch()).plus(period.getDays()); return new DaysSinceDate(epoch, difference); } throw new TypeCheckException(getDaysDataType(epoch), value); } /** * @see DataType#TIME_SECONDS */ static private SecondsSinceMidnight toSecondsSinceMidnight(Object value) { if (value instanceof SecondsSinceMidnight) { return (SecondsSinceMidnight) value; } throw new TypeCheckException(DataType.TIME_SECONDS, value); } /** * @see DataType#DATE_TIME_SECONDS_SINCE_1960 * @see DataType#DATE_TIME_SECONDS_SINCE_1970 * @see DataType#DATE_TIME_SECONDS_SINCE_1980 */ static private SecondsSinceDate toSecondsSinceDate(Object value, LocalDate epoch) { if (value instanceof SecondsSinceDate) { SecondsSinceDate period = (SecondsSinceDate) value; if ((period.getEpoch()).equals(epoch)) { return period; } Seconds difference = Seconds.secondsBetween(toMidnight(epoch), toMidnight(period.getEpoch())) .plus(period.getSeconds()); return new SecondsSinceDate(epoch, difference); } throw new TypeCheckException(getSecondsDataType(epoch), value); } static public DataType getConstantDataType(String string) { try { if (string.indexOf('.') > -1) { Float.parseFloat(string); return DataType.FLOAT; } else { Integer.parseInt(string); return DataType.INTEGER; } } catch (NumberFormatException nfe) { return DataType.STRING; } } static private DataType getDaysDataType(LocalDate epoch) { if ((YEAR_1960).equals(epoch)) { return DataType.DATE_DAYS_SINCE_1960; } else if ((YEAR_1970).equals(epoch)) { return DataType.DATE_DAYS_SINCE_1970; } else if ((YEAR_1980).equals(epoch)) { return DataType.DATE_DAYS_SINCE_1980; } throw new EvaluationException(); } static private DataType getSecondsDataType(LocalDate epoch) { if ((YEAR_1960).equals(epoch)) { return DataType.DATE_TIME_SECONDS_SINCE_1960; } else if ((YEAR_1970).equals(epoch)) { return DataType.DATE_TIME_SECONDS_SINCE_1970; } else if ((YEAR_1980).equals(epoch)) { return DataType.DATE_TIME_SECONDS_SINCE_1980; } throw new EvaluationException(); } static LocalDateTime toMidnight(LocalDate date) { return new LocalDateTime(date.getYear(), date.getMonthOfYear(), date.getDayOfMonth(), 0, 0, 0); } private static final DataType[] inheritanceSequence = { DataType.STRING, DataType.DOUBLE, DataType.FLOAT, DataType.INTEGER }; private static final LocalDate YEAR_1960 = new LocalDate(1960, 1, 1); private static final LocalDate YEAR_1970 = new LocalDate(1970, 1, 1); private static final LocalDate YEAR_1980 = new LocalDate(1980, 1, 1); }