/*
* This file is part of LanternServer, licensed under the MIT License (MIT).
*
* Copyright (c) LanternPowered <https://www.lanternpowered.org>
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the Software), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.lanternpowered.server.statistic;
import com.google.common.base.Throwables;
import org.apache.commons.lang3.StringUtils;
import org.lanternpowered.server.util.functions.Long2ObjectFunction;
import org.lanternpowered.server.util.functions.Object2LongThrowableFunction;
import java.text.DecimalFormat;
import java.text.FieldPosition;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.Locale;
/**
* An enumeration of all the statistic formats in vanilla minecraft.
*/
public final class StatisticNumberFormats {
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("########0.00");
/**
* A statistic without a format.
*/
public static NumberFormat COUNT = NumberFormat.getIntegerInstance();
/**
* A statistic measured in centimeters, meters, or kilometers depending on
* the magnitude. The input is taken as centimeters with a scale of 1 block
* equaling 1 meter.
*/
public static NumberFormat DISTANCE = of(
value -> {
if (value <= 50) {
return value + " cm";
}
final double meters = (double) value / 100.0;
if (meters <= 500) {
return DECIMAL_FORMAT.format(meters) + " m";
}
return DECIMAL_FORMAT.format(meters / 1000.0) + " km";
},
object -> {
object = StringUtils.normalizeSpace(object);
final int index = object.lastIndexOf(' ');
final String type = object.substring(index + 1).toLowerCase(Locale.ENGLISH);
final Number value = DECIMAL_FORMAT.parse(object.substring(0, index));
switch (type) {
case "cm": return value.longValue();
case "km": return (long) (value.doubleValue() * 100000.0);
case "m" : return (long) (value.doubleValue() * 100.0);
default:
throw new IllegalArgumentException("Invalid distance formatted string: " + object);
}
});
/**
* A statistic measured in 0.1 steps.
*/
public static NumberFormat FRACTIONAL = of(
value -> DECIMAL_FORMAT.format((double) value * 0.1),
object -> (long) (DECIMAL_FORMAT.parse(object).doubleValue() / 0.1));
/**
* A statistic measured in seconds, minutes, hours, or days depending on the
* magnitude. The input is taken as ticks with 20 ticks equaling one second.
*/
public static NumberFormat TIME = of(
value -> {
final double seconds = (double) value / 20.0;
if (seconds <= 30) {
return DECIMAL_FORMAT.format(seconds) + " s";
}
final double minutes = seconds / 60.0;
if (minutes <= 30) {
return DECIMAL_FORMAT.format(minutes) + " m";
}
final double hours = seconds / 60.0;
if (hours <= 12) {
return DECIMAL_FORMAT.format(hours) + " h";
}
final double days = seconds / 24.0;
if (days <= 183) {
return DECIMAL_FORMAT.format(days) + " d";
}
return DECIMAL_FORMAT.format(days / 365.0) + " y";
},
object -> {
object = StringUtils.normalizeSpace(object);
final int index = object.lastIndexOf(' ');
final String type = object.substring(index + 1).toLowerCase(Locale.ENGLISH);
final double value = DECIMAL_FORMAT.parse(object.substring(0, index)).doubleValue();
switch (type) {
case "s": return (long) (value * 20.0);
case "m": return (long) (value * 1200.0);
case "h": return (long) (value * 72000.0);
case "d": return (long) (value * 1728000.0);
case "y": return (long) (value * 630720000.0);
default:
throw new IllegalArgumentException("Invalid distance formatted string: " + object);
}
});
/**
* Creates a new {@link NumberFormat} with the specified formatter function.
*
* @param formatter The formatter
* @return The number format
*/
public static NumberFormat of(Long2ObjectFunction<String> formatter, Object2LongThrowableFunction<String, ParseException> parser) {
return new NumberFormat() {
@Override
public StringBuffer format(double number, StringBuffer toAppendTo, FieldPosition pos) {
return format((long) number, toAppendTo, pos);
}
@Override
public StringBuffer format(long number, StringBuffer toAppendTo, FieldPosition pos) {
return toAppendTo.append(formatter.apply(number));
}
@Override
public Number parse(String source, ParsePosition parsePosition) {
try {
return parser.apply(source);
} catch (ParseException e) {
throw Throwables.propagate(e);
}
}
};
}
private StatisticNumberFormats() {
}
}