/* * This file is part of Skript. * * Skript is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Skript 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Skript. If not, see <http://www.gnu.org/licenses/>. * * * Copyright 2011-2014 Peter Güttinger * */ package ch.njol.skript.util; import java.util.HashMap; import org.eclipse.jdt.annotation.Nullable; import ch.njol.skript.Skript; import ch.njol.skript.localization.GeneralWords; import ch.njol.skript.localization.Language; import ch.njol.skript.localization.LanguageChangeListener; import ch.njol.skript.localization.Noun; import ch.njol.util.NonNullPair; import ch.njol.util.coll.CollectionUtils; import ch.njol.yggdrasil.YggdrasilSerializable; /** * @author Peter Güttinger */ public class Timespan implements YggdrasilSerializable, Comparable<Timespan> { // REMIND unit private final static Noun m_tick = new Noun("time.tick"); private final static Noun m_second = new Noun("time.second"); private final static Noun m_minute = new Noun("time.minute"); private final static Noun m_hour = new Noun("time.hour"); private final static Noun m_day = new Noun("time.day"); final static Noun[] names = {m_tick, m_second, m_minute, m_hour, m_day}; final static int[] times = {50, 1000, 1000 * 60, 1000 * 60 * 60, 1000 * 60 * 60 * 24}; final static HashMap<String, Integer> parseValues = new HashMap<String, Integer>(); static { Language.addListener(new LanguageChangeListener() { @Override public void onLanguageChange() { for (int i = 0; i < names.length; i++) { parseValues.put(names[i].getSingular().toLowerCase(), times[i]); parseValues.put(names[i].getPlural().toLowerCase(), times[i]); } } }); } @Nullable public final static Timespan parse(final String s) { if (s.isEmpty()) return null; long t = 0; boolean minecraftTime = false; boolean isMinecraftTimeSet = false; if (s.matches("^\\d+:\\d\\d(:\\d\\d)?(\\.\\d{1,4})?$")) { // MM:SS[.ms] or HH:MM:SS[.ms] final String[] ss = s.split("[:.]"); final int[] times = {1000 * 60 * 60, 1000 * 60, 1000, 1}; // h, m, s, ms final int offset = ss.length == 3 && !s.contains(".") || ss.length == 4 ? 0 : 1; for (int i = 0; i < ss.length; i++) { t += times[offset + i] * Utils.parseInt("" + ss[i]); } } else { final String[] subs = s.toLowerCase().split("\\s+"); for (int i = 0; i < subs.length; i++) { String sub = subs[i]; if (sub.equals(GeneralWords.and.toString())) { if (i == 0 || i == subs.length - 1) return null; continue; } float amount = 1; if (Noun.isIndefiniteArticle(sub)) { if (i == subs.length - 1) return null; amount = 1; sub = subs[++i]; } else if (sub.matches("^\\d+(.\\d+)?$")) { if (i == subs.length - 1) return null; amount = Float.parseFloat(sub); sub = subs[++i]; } if (CollectionUtils.contains(Language.getList("time.real"), sub)) { if (i == subs.length - 1 || isMinecraftTimeSet && minecraftTime) return null; sub = subs[++i]; } else if (CollectionUtils.contains(Language.getList("time.minecraft"), sub)) { if (i == subs.length - 1 || isMinecraftTimeSet && !minecraftTime) return null; minecraftTime = true; sub = subs[++i]; } if (sub.endsWith(",")) sub = sub.substring(0, sub.length() - 1); final Integer d = parseValues.get(sub.toLowerCase()); if (d == null) return null; if (minecraftTime && d != times[0]) // times[0] == tick amount *= 72f; t += Math.round(amount * d); isMinecraftTimeSet = true; } } return new Timespan(t); } private final long millis; public Timespan() { millis = 0; } public Timespan(final long millis) { if (millis < 0) throw new IllegalArgumentException("millis must be >= 0"); this.millis = millis; } public static Timespan fromTicks(final int ticks) { return new Timespan((long) ticks * 50); } public long getMilliSeconds() { return millis; } public int getTicks() { return Math.round(millis / 50f); } @Override public String toString() { return toString(millis); } public String toString(final int flags) { return toString(millis, flags); } @SuppressWarnings("unchecked") final static NonNullPair<Noun, Integer>[] simpleValues = new NonNullPair[] { new NonNullPair<Noun, Integer>(m_day, 1000 * 60 * 60 * 24), new NonNullPair<Noun, Integer>(m_hour, 1000 * 60 * 60), new NonNullPair<Noun, Integer>(m_minute, 1000 * 60), new NonNullPair<Noun, Integer>(m_second, 1000) }; public static String toString(final long millis) { return toString(millis, 0); } @SuppressWarnings("null") public static String toString(final long millis, final int flags) { for (int i = 0; i < simpleValues.length - 1; i++) { if (millis >= simpleValues[i].getSecond()) { final double second = 1. * (millis % simpleValues[i].getSecond()) / simpleValues[i + 1].getSecond(); if (!"0".equals(Skript.toString(second))) { // bad style but who cares... return toString(Math.floor(1. * millis / simpleValues[i].getSecond()), simpleValues[i], flags) + " " + GeneralWords.and + " " + toString(second, simpleValues[i + 1], flags); } else { return toString(1. * millis / simpleValues[i].getSecond(), simpleValues[i], flags); } } } return toString(1. * millis / simpleValues[simpleValues.length - 1].getSecond(), simpleValues[simpleValues.length - 1], flags); } private static String toString(final double amount, final NonNullPair<Noun, Integer> p, final int flags) { return p.getFirst().withAmount(amount, flags); } @Override public int compareTo(final @Nullable Timespan o) { final long d = o == null ? millis : millis - o.millis; return d > 0 ? 1 : d < 0 ? -1 : 0; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (int) (millis ^ (millis >>> 32)); return result; } @Override public boolean equals(final @Nullable Object obj) { if (this == obj) return true; if (obj == null) return false; if (!(obj instanceof Timespan)) return false; final Timespan other = (Timespan) obj; if (millis != other.millis) return false; return true; } }