summaryrefslogtreecommitdiffstats
path: root/Essentials/src/net/ess3/utils
diff options
context:
space:
mode:
Diffstat (limited to 'Essentials/src/net/ess3/utils')
-rw-r--r--Essentials/src/net/ess3/utils/DateUtil.java186
-rw-r--r--Essentials/src/net/ess3/utils/DescParseTickFormat.java295
-rw-r--r--Essentials/src/net/ess3/utils/ExecuteTimer.java89
-rw-r--r--Essentials/src/net/ess3/utils/LocationUtil.java142
-rw-r--r--Essentials/src/net/ess3/utils/Util.java442
-rw-r--r--Essentials/src/net/ess3/utils/gnu/inet/encoding/Punycode.java321
-rw-r--r--Essentials/src/net/ess3/utils/gnu/inet/encoding/PunycodeException.java45
-rw-r--r--Essentials/src/net/ess3/utils/textreader/ArrayListInput.java31
-rw-r--r--Essentials/src/net/ess3/utils/textreader/HelpInput.java179
-rw-r--r--Essentials/src/net/ess3/utils/textreader/IText.java14
-rw-r--r--Essentials/src/net/ess3/utils/textreader/KeywordReplacer.java157
-rw-r--r--Essentials/src/net/ess3/utils/textreader/SimpleTextInput.java35
-rw-r--r--Essentials/src/net/ess3/utils/textreader/SimpleTextPager.java31
-rw-r--r--Essentials/src/net/ess3/utils/textreader/TextInput.java149
-rw-r--r--Essentials/src/net/ess3/utils/textreader/TextPager.java200
15 files changed, 2316 insertions, 0 deletions
diff --git a/Essentials/src/net/ess3/utils/DateUtil.java b/Essentials/src/net/ess3/utils/DateUtil.java
new file mode 100644
index 000000000..02916139c
--- /dev/null
+++ b/Essentials/src/net/ess3/utils/DateUtil.java
@@ -0,0 +1,186 @@
+package net.ess3.utils;
+
+import static net.ess3.I18n._;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class DateUtil {
+ public static String formatDateDiff(long date)
+ {
+ Calendar c = new GregorianCalendar();
+ c.setTimeInMillis(date);
+ Calendar now = new GregorianCalendar();
+ return formatDateDiff(now, c);
+ }
+
+ public static String formatDateDiff(Calendar fromDate, Calendar toDate)
+ {
+ boolean future = false;
+ if (toDate.equals(fromDate))
+ {
+ return _("now");
+ }
+ if (toDate.after(fromDate))
+ {
+ future = true;
+ }
+
+ StringBuilder sb = new StringBuilder();
+ int[] types = new int[]
+ {
+ Calendar.YEAR,
+ Calendar.MONTH,
+ Calendar.DAY_OF_MONTH,
+ Calendar.HOUR_OF_DAY,
+ Calendar.MINUTE,
+ Calendar.SECOND
+ };
+ String[] names = new String[]
+ {
+ _("year"),
+ _("years"),
+ _("month"),
+ _("months"),
+ _("day"),
+ _("days"),
+ _("hour"),
+ _("hours"),
+ _("minute"),
+ _("minutes"),
+ _("second"),
+ _("seconds")
+ };
+ for (int i = 0; i < types.length; i++)
+ {
+ int diff = dateDiff(types[i], fromDate, toDate, future);
+ if (diff > 0)
+ {
+ sb.append(" ").append(diff).append(" ").append(names[i * 2 + (diff > 1 ? 1 : 0)]);
+ }
+ }
+ if (sb.length() == 0)
+ {
+ return "now";
+ }
+ return sb.toString();
+ }
+
+ private static int dateDiff(int type, Calendar fromDate, Calendar toDate, boolean future)
+ {
+ int diff = 0;
+ long savedDate = fromDate.getTimeInMillis();
+ while ((future && !fromDate.after(toDate)) || (!future && !fromDate.before(toDate)))
+ {
+ savedDate = fromDate.getTimeInMillis();
+ fromDate.add(type, future ? 1 : -1);
+ diff++;
+ }
+ diff--;
+ fromDate.setTimeInMillis(savedDate);
+ return diff;
+ }
+
+ public static long parseDateDiff(String time, boolean future) throws Exception
+ {
+ Pattern timePattern = Pattern.compile(
+ "(?:([0-9]+)\\s*y[a-z]*[,\\s]*)?"
+ + "(?:([0-9]+)\\s*mo[a-z]*[,\\s]*)?"
+ + "(?:([0-9]+)\\s*w[a-z]*[,\\s]*)?"
+ + "(?:([0-9]+)\\s*d[a-z]*[,\\s]*)?"
+ + "(?:([0-9]+)\\s*h[a-z]*[,\\s]*)?"
+ + "(?:([0-9]+)\\s*m[a-z]*[,\\s]*)?"
+ + "(?:([0-9]+)\\s*(?:s[a-z]*)?)?", Pattern.CASE_INSENSITIVE);
+ Matcher m = timePattern.matcher(time);
+ int years = 0;
+ int months = 0;
+ int weeks = 0;
+ int days = 0;
+ int hours = 0;
+ int minutes = 0;
+ int seconds = 0;
+ boolean found = false;
+ while (m.find())
+ {
+ if (m.group() == null || m.group().isEmpty())
+ {
+ continue;
+ }
+ for (int i = 0; i < m.groupCount(); i++)
+ {
+ if (m.group(i) != null && !m.group(i).isEmpty())
+ {
+ found = true;
+ break;
+ }
+ }
+ if (found)
+ {
+ if (m.group(1) != null && !m.group(1).isEmpty())
+ {
+ years = Integer.parseInt(m.group(1));
+ }
+ if (m.group(2) != null && !m.group(2).isEmpty())
+ {
+ months = Integer.parseInt(m.group(2));
+ }
+ if (m.group(3) != null && !m.group(3).isEmpty())
+ {
+ weeks = Integer.parseInt(m.group(3));
+ }
+ if (m.group(4) != null && !m.group(4).isEmpty())
+ {
+ days = Integer.parseInt(m.group(4));
+ }
+ if (m.group(5) != null && !m.group(5).isEmpty())
+ {
+ hours = Integer.parseInt(m.group(5));
+ }
+ if (m.group(6) != null && !m.group(6).isEmpty())
+ {
+ minutes = Integer.parseInt(m.group(6));
+ }
+ if (m.group(7) != null && !m.group(7).isEmpty())
+ {
+ seconds = Integer.parseInt(m.group(7));
+ }
+ break;
+ }
+ }
+ if (!found)
+ {
+ throw new Exception(_("illegalDate"));
+ }
+ Calendar c = new GregorianCalendar();
+ if (years > 0)
+ {
+ c.add(Calendar.YEAR, years * (future ? 1 : -1));
+ }
+ if (months > 0)
+ {
+ c.add(Calendar.MONTH, months * (future ? 1 : -1));
+ }
+ if (weeks > 0)
+ {
+ c.add(Calendar.WEEK_OF_YEAR, weeks * (future ? 1 : -1));
+ }
+ if (days > 0)
+ {
+ c.add(Calendar.DAY_OF_MONTH, days * (future ? 1 : -1));
+ }
+ if (hours > 0)
+ {
+ c.add(Calendar.HOUR_OF_DAY, hours * (future ? 1 : -1));
+ }
+ if (minutes > 0)
+ {
+ c.add(Calendar.MINUTE, minutes * (future ? 1 : -1));
+ }
+ if (seconds > 0)
+ {
+ c.add(Calendar.SECOND, seconds * (future ? 1 : -1));
+ }
+ return c.getTimeInMillis();
+ }
+}
diff --git a/Essentials/src/net/ess3/utils/DescParseTickFormat.java b/Essentials/src/net/ess3/utils/DescParseTickFormat.java
new file mode 100644
index 000000000..bf7d95c23
--- /dev/null
+++ b/Essentials/src/net/ess3/utils/DescParseTickFormat.java
@@ -0,0 +1,295 @@
+package net.ess3.utils;
+
+import static net.ess3.I18n._;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+
+/**
+ * This utility class is used for converting between the ingame time in ticks to ingame time as a friendly string. Note
+ * that the time is INGAME.
+ *
+ * http://www.minecraftwiki.net/wiki/Day/night_cycle
+ *
+ * @author Olof Larsson
+ */
+public final class DescParseTickFormat
+{
+ public static final Map<String, Integer> nameToTicks = new LinkedHashMap<String, Integer>();
+ public static final Set<String> resetAliases = new HashSet<String>();
+ public static final int ticksAtMidnight = 18000;
+ public static final int ticksPerDay = 24000;
+ public static final int ticksPerHour = 1000;
+ public static final double ticksPerMinute = 1000d / 60d;
+ public static final double ticksPerSecond = 1000d / 60d / 60d;
+ private static final SimpleDateFormat SDFTwentyFour = new SimpleDateFormat("HH:mm", Locale.ENGLISH);
+ private static final SimpleDateFormat SDFTwelve = new SimpleDateFormat("h:mmaa", Locale.ENGLISH);
+
+ static
+ {
+ SDFTwentyFour.setTimeZone(TimeZone.getTimeZone("GMT"));
+ SDFTwelve.setTimeZone(TimeZone.getTimeZone("GMT"));
+
+ nameToTicks.put("sunrise", 23000);
+ nameToTicks.put("dawn", 23000);
+
+ nameToTicks.put("daystart", 0);
+ nameToTicks.put("day", 0);
+
+ nameToTicks.put("morning", 1000);
+
+ nameToTicks.put("midday", 6000);
+ nameToTicks.put("noon", 6000);
+
+ nameToTicks.put("afternoon", 9000);
+
+ nameToTicks.put("sunset", 12000);
+ nameToTicks.put("dusk", 12000);
+ nameToTicks.put("sundown", 12000);
+ nameToTicks.put("nightfall", 12000);
+
+ nameToTicks.put("nightstart", 14000);
+ nameToTicks.put("night", 14000);
+
+ nameToTicks.put("midnight", 18000);
+
+ resetAliases.add("reset");
+ resetAliases.add("normal");
+ resetAliases.add("default");
+ }
+
+ private DescParseTickFormat()
+ {
+ }
+
+ // ============================================
+ // PARSE. From describing String to int
+ // --------------------------------------------
+ public static long parse(String desc) throws NumberFormatException
+ {
+ // Only look at alphanumeric and lowercase and : for 24:00
+ desc = desc.toLowerCase(Locale.ENGLISH).replaceAll("[^A-Za-z0-9:]", "");
+
+ // Detect ticks format
+ try
+ {
+ return parseTicks(desc);
+ }
+ catch (Exception e)
+ {
+ }
+
+ // Detect 24-hour format
+ try
+ {
+ return parse24(desc);
+ }
+ catch (Exception e)
+ {
+ }
+
+ // Detect 12-hour format
+ try
+ {
+ return parse12(desc);
+ }
+ catch (Exception e)
+ {
+ }
+
+ // Detect aliases
+ try
+ {
+ return parseAlias(desc);
+ }
+ catch (Exception e)
+ {
+ }
+
+ // Well we failed to understand...
+ throw new NumberFormatException();
+ }
+
+ public static long parseTicks(String desc) throws NumberFormatException
+ {
+ if (!desc.matches("^[0-9]+ti?c?k?s?$"))
+ {
+ throw new NumberFormatException();
+ }
+
+ desc = desc.replaceAll("[^0-9]", "");
+
+ return Long.parseLong(desc) % 24000;
+ }
+
+ public static long parse24(String desc) throws NumberFormatException
+ {
+ if (!desc.matches("^[0-9]{2}[^0-9]?[0-9]{2}$"))
+ {
+ throw new NumberFormatException();
+ }
+
+ desc = desc.toLowerCase(Locale.ENGLISH).replaceAll("[^0-9]", "");
+
+ if (desc.length() != 4)
+ {
+ throw new NumberFormatException();
+ }
+
+ final int hours = Integer.parseInt(desc.substring(0, 2));
+ final int minutes = Integer.parseInt(desc.substring(2, 4));
+
+ return hoursMinutesToTicks(hours, minutes);
+ }
+
+ public static long parse12(String desc) throws NumberFormatException
+ {
+ if (!desc.matches("^[0-9]{1,2}([^0-9]?[0-9]{2})?(pm|am)$"))
+ {
+ throw new NumberFormatException();
+ }
+
+ int hours = 0;
+ int minutes = 0;
+
+ desc = desc.toLowerCase(Locale.ENGLISH);
+ String parsetime = desc.replaceAll("[^0-9]", "");
+
+ if (parsetime.length() > 4)
+ {
+ throw new NumberFormatException();
+ }
+
+ if (parsetime.length() == 4)
+ {
+ hours += Integer.parseInt(parsetime.substring(0, 2));
+ minutes += Integer.parseInt(parsetime.substring(2, 4));
+ }
+ else if (parsetime.length() == 3)
+ {
+ hours += Integer.parseInt(parsetime.substring(0, 1));
+ minutes += Integer.parseInt(parsetime.substring(1, 3));
+ }
+ else if (parsetime.length() == 2)
+ {
+ hours += Integer.parseInt(parsetime.substring(0, 2));
+ }
+ else if (parsetime.length() == 1)
+ {
+ hours += Integer.parseInt(parsetime.substring(0, 1));
+ }
+ else
+ {
+ throw new NumberFormatException();
+ }
+
+ if (desc.endsWith("pm") && hours != 12)
+ {
+ hours += 12;
+ }
+
+ if (desc.endsWith("am") && hours == 12)
+ {
+ hours -= 12;
+ }
+
+ return hoursMinutesToTicks(hours, minutes);
+ }
+
+ public static long hoursMinutesToTicks(final int hours, final int minutes)
+ {
+ long ret = ticksAtMidnight;
+ ret += (hours) * ticksPerHour;
+
+ ret += (minutes / 60.0) * ticksPerHour;
+
+ ret %= ticksPerDay;
+ return ret;
+ }
+
+ public static long parseAlias(final String desc) throws NumberFormatException
+ {
+ final Integer ret = nameToTicks.get(desc);
+ if (ret == null)
+ {
+ throw new NumberFormatException();
+ }
+
+ return ret;
+ }
+
+ public static boolean meansReset(final String desc)
+ {
+ return resetAliases.contains(desc);
+ }
+
+ // ============================================
+ // FORMAT. From int to describing String
+ // --------------------------------------------
+ public static String format(final long ticks)
+ {
+ return _("timeFormat", format24(ticks), format12(ticks), formatTicks(ticks));
+ }
+
+ public static String formatTicks(final long ticks)
+ {
+ return (ticks % ticksPerDay) + "ticks";
+ }
+
+ public static String format24(final long ticks)
+ {
+ synchronized (SDFTwentyFour)
+ {
+ return formatDateFormat(ticks, SDFTwentyFour);
+ }
+ }
+
+ public static String format12(final long ticks)
+ {
+ synchronized (SDFTwelve)
+ {
+ return formatDateFormat(ticks, SDFTwelve);
+ }
+ }
+
+ public static String formatDateFormat(final long ticks, final SimpleDateFormat format)
+ {
+ final Date date = ticksToDate(ticks);
+ return format.format(date);
+ }
+
+ public static Date ticksToDate(long ticks)
+ {
+ // Assume the server time starts at 0. It would start on a day.
+ // But we will simulate that the server started with 0 at midnight.
+ ticks = ticks - ticksAtMidnight + ticksPerDay;
+
+ // How many ingame days have passed since the server start?
+ final long days = ticks / ticksPerDay;
+ ticks = ticks - days * ticksPerDay;
+
+ // How many hours on the last day?
+ final long hours = ticks / ticksPerHour;
+ ticks = ticks - hours * ticksPerHour;
+
+ // How many minutes on the last day?
+ final long minutes = (long)Math.floor(ticks / ticksPerMinute);
+ final double dticks = ticks - minutes * ticksPerMinute;
+
+ // How many seconds on the last day?
+ final long seconds = (long)Math.floor(dticks / ticksPerSecond);
+
+ // Now we create an english GMT calendar (We wan't no daylight savings)
+ final Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"), Locale.ENGLISH);
+ cal.setLenient(true);
+
+ // And we set the time to 0! And append the time that passed!
+ cal.set(0, Calendar.JANUARY, 1, 0, 0, 0);
+ cal.add(Calendar.DAY_OF_YEAR, (int)days);
+ cal.add(Calendar.HOUR_OF_DAY, (int)hours);
+ cal.add(Calendar.MINUTE, (int)minutes);
+ cal.add(Calendar.SECOND, (int)seconds + 1); // To solve rounding errors.
+
+ return cal.getTime();
+ }
+}
diff --git a/Essentials/src/net/ess3/utils/ExecuteTimer.java b/Essentials/src/net/ess3/utils/ExecuteTimer.java
new file mode 100644
index 000000000..2f0c7a5f6
--- /dev/null
+++ b/Essentials/src/net/ess3/utils/ExecuteTimer.java
@@ -0,0 +1,89 @@
+package net.ess3.utils;
+
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+
+public class ExecuteTimer
+{
+ private final transient List<ExecuteRecord> times;
+ private final transient DecimalFormat decimalFormat = new DecimalFormat("#0.000", DecimalFormatSymbols.getInstance(Locale.US));
+
+
+ public ExecuteTimer()
+ {
+ times = new ArrayList<ExecuteRecord>();
+ }
+
+ public void start()
+ {
+ times.clear();
+ mark("start");
+
+ }
+
+ public void mark(final String label)
+ {
+ if (!times.isEmpty() || "start".equals(label))
+ {
+ times.add(new ExecuteRecord(label, System.nanoTime()));
+ }
+ }
+
+ public String end()
+ {
+ final StringBuilder output = new StringBuilder();
+ output.append("execution time: ");
+ String mark;
+ long time0 = 0;
+ long time1 = 0;
+ long time2 = 0;
+ double duration;
+
+ for (ExecuteRecord pair : times)
+ {
+ mark = (String)pair.getMark();
+ time2 = (Long)pair.getTime();
+ if (time1 > 0)
+ {
+ duration = (time2 - time1)/1000000.0;
+ output.append(mark).append(": ").append(decimalFormat.format(duration)).append("ms - ");
+ }
+ else
+ {
+ time0 = time2;
+ }
+ time1 = time2;
+ }
+ duration = (time1 - time0)/1000000.0;
+ output.append("Total: ").append(decimalFormat.format(duration)).append("ms");
+ times.clear();
+ return output.toString();
+ }
+
+
+ static private class ExecuteRecord
+ {
+ private final String mark;
+ private final long time;
+
+ public ExecuteRecord(final String mark, final long time)
+ {
+ this.mark = mark;
+ this.time = time;
+ }
+
+ public String getMark()
+ {
+ return mark;
+ }
+
+ public long getTime()
+ {
+ return time;
+ }
+ }
+} \ No newline at end of file
diff --git a/Essentials/src/net/ess3/utils/LocationUtil.java b/Essentials/src/net/ess3/utils/LocationUtil.java
new file mode 100644
index 000000000..61cdcec6e
--- /dev/null
+++ b/Essentials/src/net/ess3/utils/LocationUtil.java
@@ -0,0 +1,142 @@
+package net.ess3.utils;
+
+import static net.ess3.I18n._;
+import java.util.HashSet;
+import java.util.Set;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.World;
+import org.bukkit.block.Block;
+import org.bukkit.entity.LivingEntity;
+
+public class LocationUtil {
+ // The player can stand inside these materials
+ private static final Set<Integer> AIR_MATERIALS = new HashSet<Integer>();
+ private static final HashSet<Byte> AIR_MATERIALS_TARGET = new HashSet<Byte>();
+
+ static
+ {
+ AIR_MATERIALS.add(Material.AIR.getId());
+ AIR_MATERIALS.add(Material.SAPLING.getId());
+ AIR_MATERIALS.add(Material.POWERED_RAIL.getId());
+ AIR_MATERIALS.add(Material.DETECTOR_RAIL.getId());
+ AIR_MATERIALS.add(Material.LONG_GRASS.getId());
+ AIR_MATERIALS.add(Material.DEAD_BUSH.getId());
+ AIR_MATERIALS.add(Material.YELLOW_FLOWER.getId());
+ AIR_MATERIALS.add(Material.RED_ROSE.getId());
+ AIR_MATERIALS.add(Material.BROWN_MUSHROOM.getId());
+ AIR_MATERIALS.add(Material.RED_MUSHROOM.getId());
+ AIR_MATERIALS.add(Material.TORCH.getId());
+ AIR_MATERIALS.add(Material.REDSTONE_WIRE.getId());
+ AIR_MATERIALS.add(Material.SEEDS.getId());
+ AIR_MATERIALS.add(Material.SIGN_POST.getId());
+ AIR_MATERIALS.add(Material.WOODEN_DOOR.getId());
+ AIR_MATERIALS.add(Material.LADDER.getId());
+ AIR_MATERIALS.add(Material.RAILS.getId());
+ AIR_MATERIALS.add(Material.WALL_SIGN.getId());
+ AIR_MATERIALS.add(Material.LEVER.getId());
+ AIR_MATERIALS.add(Material.STONE_PLATE.getId());
+ AIR_MATERIALS.add(Material.IRON_DOOR_BLOCK.getId());
+ AIR_MATERIALS.add(Material.WOOD_PLATE.getId());
+ AIR_MATERIALS.add(Material.REDSTONE_TORCH_OFF.getId());
+ AIR_MATERIALS.add(Material.REDSTONE_TORCH_ON.getId());
+ AIR_MATERIALS.add(Material.STONE_BUTTON.getId());
+ AIR_MATERIALS.add(Material.SUGAR_CANE_BLOCK.getId());
+ AIR_MATERIALS.add(Material.DIODE_BLOCK_OFF.getId());
+ AIR_MATERIALS.add(Material.DIODE_BLOCK_ON.getId());
+ AIR_MATERIALS.add(Material.TRAP_DOOR.getId());
+ AIR_MATERIALS.add(Material.PUMPKIN_STEM.getId());
+ AIR_MATERIALS.add(Material.MELON_STEM.getId());
+ AIR_MATERIALS.add(Material.VINE.getId());
+ AIR_MATERIALS.add(Material.NETHER_WARTS.getId());
+ AIR_MATERIALS.add(Material.WATER_LILY.getId());
+
+ for (Integer integer : AIR_MATERIALS)
+ {
+ AIR_MATERIALS_TARGET.add(integer.byteValue());
+ }
+ AIR_MATERIALS_TARGET.add((byte)Material.WATER.getId());
+ AIR_MATERIALS_TARGET.add((byte)Material.STATIONARY_WATER.getId());
+ }
+
+ public static Location getTarget(final LivingEntity entity) throws Exception
+ {
+ final Block block = entity.getTargetBlock(AIR_MATERIALS_TARGET, 300);
+ if (block == null)
+ {
+ throw new Exception("Not targeting a block");
+ }
+ return block.getLocation();
+ }
+
+ public static Location getSafeDestination(final Location loc) throws Exception
+ {
+ if (loc == null || loc.getWorld() == null)
+ {
+ throw new Exception(_("destinationNotSet"));
+ }
+ final World world = loc.getWorld();
+ int x = loc.getBlockX();
+ int y = (int)Math.round(loc.getY());
+ int z = loc.getBlockZ();
+
+ while (isBlockAboveAir(world, x, y, z))
+ {
+ y -= 1;
+ if (y < 0)
+ {
+ break;
+ }
+ }
+
+ while (isBlockUnsafe(world, x, y, z))
+ {
+ y += 1;
+ if (y >= world.getHighestBlockYAt(x, z))
+ {
+ x += 1;
+ break;
+ }
+ }
+ while (isBlockUnsafe(world, x, y, z))
+ {
+ y -= 1;
+ if (y <= 1)
+ {
+ x += 1;
+ y = world.getHighestBlockYAt(x, z);
+ if (x - 32 > loc.getBlockX())
+ {
+ throw new Exception(_("holeInFloor"));
+ }
+ }
+ }
+ return new Location(world, x + 0.5D, y, z + 0.5D, loc.getYaw(), loc.getPitch());
+ }
+
+ private static boolean isBlockAboveAir(final World world, final int x, final int y, final int z)
+ {
+ return AIR_MATERIALS.contains(world.getBlockAt(x, y - 1, z).getType().getId());
+ }
+
+ public static boolean isBlockUnsafe(final World world, final int x, final int y, final int z)
+ {
+ final Block below = world.getBlockAt(x, y - 1, z);
+ if (below.getType() == Material.LAVA || below.getType() == Material.STATIONARY_LAVA)
+ {
+ return true;
+ }
+
+ if (below.getType() == Material.FIRE)
+ {
+ return true;
+ }
+
+ if ((!AIR_MATERIALS.contains(world.getBlockAt(x, y, z).getType().getId()))
+ || (!AIR_MATERIALS.contains(world.getBlockAt(x, y + 1, z).getType().getId())))
+ {
+ return true;
+ }
+ return isBlockAboveAir(world, x, y, z);
+ }
+}
diff --git a/Essentials/src/net/ess3/utils/Util.java b/Essentials/src/net/ess3/utils/Util.java
new file mode 100644
index 000000000..8121f8ff8
--- /dev/null
+++ b/Essentials/src/net/ess3/utils/Util.java
@@ -0,0 +1,442 @@
+package net.ess3.utils;
+
+import static net.ess3.I18n._;
+import net.ess3.api.IEssentials;
+import net.ess3.api.ISettings;
+import net.ess3.api.IUser;
+import net.ess3.api.InvalidNameException;
+import net.ess3.utils.gnu.inet.encoding.Punycode;
+import net.ess3.utils.gnu.inet.encoding.PunycodeException;
+import de.bananaco.bpermissions.imp.Permissions;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.util.Collection;
+import java.util.Locale;
+import java.util.regex.Pattern;
+import lombok.Cleanup;
+import org.bukkit.Bukkit;
+import org.bukkit.Material;
+import org.bukkit.block.Block;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.permissions.Permission;
+import org.bukkit.permissions.PermissionDefault;
+import org.bukkit.plugin.PluginManager;
+
+
+public final class Util
+{
+ private Util()
+ {
+ }
+ private final static Pattern INVALIDFILECHARS = Pattern.compile("[^\u0020-\u007E\u0085\u00A0-\uD7FF\uE000-\uFFFC]");
+ private final static Pattern INVALIDCHARS = Pattern.compile("[^\t\n\r\u0020-\u007E\u0085\u00A0-\uD7FF\uE000-\uFFFC]");
+
+ public static String sanitizeFileName(String name) throws InvalidNameException
+ {
+ try
+ {
+ String r = name.toLowerCase(Locale.ENGLISH);
+ r = r.replace('.', (char)('\ue200' + '.'));
+ r = r.replace('\\', (char)('\ue200' + '\\'));
+ r = r.replace('/', (char)('\ue200' + '/'));
+ r = r.replace('"', (char)('\ue200' + '"'));
+ r = r.replace('<', (char)('\ue200' + '<'));
+ r = r.replace('>', (char)('\ue200' + '>'));
+ r = r.replace('|', (char)('\ue200' + '|'));
+ r = r.replace('?', (char)('\ue200' + '?'));
+ r = r.replace('*', (char)('\ue200' + '*'));
+ r = r.replace(':', (char)('\ue200' + ':'));
+ r = r.replace('-', (char)('\ue200' + '-'));
+ r = INVALIDFILECHARS.matcher(r).replaceAll("");
+ return Punycode.encode(r);
+ }
+ catch (PunycodeException ex)
+ {
+ throw new InvalidNameException(ex);
+ }
+ }
+
+ public static String decodeFileName(String name) throws InvalidNameException
+ {
+ try
+ {
+ String r = Punycode.decode(name);
+ r = r.replace((char)('\ue200' + '.'), '.');
+ r = r.replace((char)('\ue200' + '\\'), '\\');
+ r = r.replace((char)('\ue200' + '/'), '/');
+ r = r.replace((char)('\ue200' + '"'), '"');
+ r = r.replace((char)('\ue200' + '<'), '<');
+ r = r.replace((char)('\ue200' + '>'), '>');
+ r = r.replace((char)('\ue200' + '|'), '|');
+ r = r.replace((char)('\ue200' + '?'), '?');
+ r = r.replace((char)('\ue200' + '*'), '*');
+ r = r.replace((char)('\ue200' + ':'), ':');
+ r = r.replace((char)('\ue200' + '-'), '-');
+ return r;
+ }
+ catch (PunycodeException ex)
+ {
+ throw new InvalidNameException(ex);
+ }
+ }
+
+ public static String sanitizeKey(String name)
+ {
+ return INVALIDCHARS.matcher(name.toLowerCase(Locale.ENGLISH)).replaceAll("_");
+ }
+
+ public static String sanitizeString(final String string)
+ {
+ return INVALIDCHARS.matcher(string).replaceAll("");
+ }
+
+ public static ItemStack convertBlockToItem(final Block block)
+ {
+ final ItemStack is = new ItemStack(block.getType(), 1, (short)0, block.getData());
+ switch (is.getType())
+ {
+ case WOODEN_DOOR:
+ is.setType(Material.WOOD_DOOR);
+ is.setDurability((short)0);
+ break;
+ case IRON_DOOR_BLOCK:
+ is.setType(Material.IRON_DOOR);
+ is.setDurability((short)0);
+ break;
+ case SIGN_POST:
+ case WALL_SIGN:
+ is.setType(Material.SIGN);
+ is.setDurability((short)0);
+ break;
+ case CROPS:
+ is.setType(Material.SEEDS);
+ is.setDurability((short)0);
+ break;
+ case CAKE_BLOCK:
+ is.setType(Material.CAKE);
+ is.setDurability((short)0);
+ break;
+ case BED_BLOCK:
+ is.setType(Material.BED);
+ is.setDurability((short)0);
+ break;
+ case REDSTONE_WIRE:
+ is.setType(Material.REDSTONE);
+ is.setDurability((short)0);
+ break;
+ case REDSTONE_TORCH_OFF:
+ case REDSTONE_TORCH_ON:
+ is.setType(Material.REDSTONE_TORCH_ON);
+ is.setDurability((short)0);
+ break;
+ case DIODE_BLOCK_OFF:
+ case DIODE_BLOCK_ON:
+ is.setType(Material.DIODE);
+ is.setDurability((short)0);
+ break;
+ case DOUBLE_STEP:
+ is.setType(Material.STEP);
+ break;
+ case TORCH:
+ case RAILS:
+ case LADDER:
+ case WOOD_STAIRS:
+ case COBBLESTONE_STAIRS:
+ case LEVER:
+ case STONE_BUTTON:
+ case FURNACE:
+ case DISPENSER:
+ case PUMPKIN:
+ case JACK_O_LANTERN:
+ case WOOD_PLATE:
+ case STONE_PLATE:
+ case PISTON_STICKY_BASE:
+ case PISTON_BASE:
+ case IRON_FENCE:
+ case THIN_GLASS:
+ case TRAP_DOOR:
+ case FENCE:
+ case FENCE_GATE:
+ case NETHER_FENCE:
+ is.setDurability((short)0);
+ break;
+ case FIRE:
+ return null;
+ case PUMPKIN_STEM:
+ is.setType(Material.PUMPKIN_SEEDS);
+ break;
+ case MELON_STEM:
+ is.setType(Material.MELON_SEEDS);
+ break;
+ }
+ return is;
+ }
+ private static DecimalFormat dFormat = new DecimalFormat("#0.00", DecimalFormatSymbols.getInstance(Locale.US));
+
+ public static String formatAsCurrency(final double value)
+ {
+
+ String str = dFormat.format(value);
+ if (str.endsWith(".00"))
+ {
+ str = str.substring(0, str.length() - 3);
+ }
+ return str;
+ }
+
+ public static String displayCurrency(final double value, final IEssentials ess)
+ {
+ @Cleanup
+ final ISettings settings = ess.getSettings();
+ settings.acquireReadLock();
+ return _("currency", settings.getData().getEconomy().getCurrencySymbol(), formatAsCurrency(value));
+ }
+
+ public static String shortCurrency(final double value, final IEssentials ess)
+ {
+ @Cleanup
+ final ISettings settings = ess.getSettings();
+ settings.acquireReadLock();
+ return settings.getData().getEconomy().getCurrencySymbol() + formatAsCurrency(value);
+ }
+
+ public static double roundDouble(final double d)
+ {
+ return Math.round(d * 100.0) / 100.0;
+ }
+
+ public static boolean isInt(final String sInt)
+ {
+ try
+ {
+ Integer.parseInt(sInt);
+ }
+ catch (NumberFormatException e)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ public static String joinList(Object... list)
+ {
+ return joinList(", ", list);
+ }
+
+ public static String joinList(String seperator, Object... list)
+ {
+ StringBuilder buf = new StringBuilder();
+ for (Object each : list)
+ {
+ if (buf.length() > 0)
+ {
+ buf.append(seperator);
+ }
+
+ if (each instanceof Collection)
+ {
+ buf.append(joinList(seperator, ((Collection)each).toArray()));
+ }
+ else
+ {
+ try
+ {
+ buf.append(each.toString());
+ }
+ catch (Exception e)
+ {
+ buf.append(each.toString());
+ }
+ }
+ }
+ return buf.toString();
+ }
+
+ public static void registerPermissions(String path, Collection<String> nodes, boolean hasDefault, IEssentials ess)
+ {
+ if (nodes == null || nodes.isEmpty())
+ {
+ return;
+ }
+ final PluginManager pluginManager = ess.getServer().getPluginManager();
+ Permission basePerm = pluginManager.getPermission(path + ".*");
+ if (basePerm != null && !basePerm.getChildren().isEmpty())
+ {
+ basePerm.getChildren().clear();
+ }
+ if (basePerm == null)
+ {
+ basePerm = new Permission(path + ".*", PermissionDefault.OP);
+ pluginManager.addPermission(basePerm);
+ Permission mainPerm = pluginManager.getPermission("essentials.*");
+ if (mainPerm == null)
+ {
+ mainPerm = new Permission("essentials.*", PermissionDefault.OP);
+ pluginManager.addPermission(mainPerm);
+ }
+ mainPerm.getChildren().put(basePerm.getName(), Boolean.TRUE);
+ }
+
+ for (String nodeName : nodes)
+ {
+ final String permissionName = path + "." + nodeName;
+ Permission perm = pluginManager.getPermission(permissionName);
+ if (perm == null)
+ {
+ final PermissionDefault defaultPerm = hasDefault && nodeName.equalsIgnoreCase("default") ? PermissionDefault.TRUE : PermissionDefault.OP;
+ perm = new Permission(permissionName, defaultPerm);
+ pluginManager.addPermission(perm);
+ }
+ basePerm.getChildren().put(permissionName, Boolean.TRUE);
+ }
+ basePerm.recalculatePermissibles();
+ }
+ private static transient final Pattern DOT_PATTERN = Pattern.compile("\\.");
+
+ public static Permission registerPermission(String permission, PermissionDefault defaultPerm)
+ {
+ final PluginManager pluginManager = Bukkit.getServer().getPluginManager();
+ final String[] parts = DOT_PATTERN.split(permission);
+ final StringBuilder builder = new StringBuilder(permission.length());
+ Permission parent = null;
+ for (int i = 0; i < parts.length - 1; i++)
+ {
+ builder.append(parts[i]).append(".*");
+ String permString = builder.toString();
+ Permission perm = pluginManager.getPermission(permString);
+ if (perm == null)
+ {
+ perm = new Permission(permString, PermissionDefault.FALSE);
+ pluginManager.addPermission(perm);
+ if (parent != null)
+ {
+ parent.getChildren().put(perm.getName(), Boolean.TRUE);
+ }
+ parent = perm;
+ }
+ builder.deleteCharAt(builder.length() - 1);
+ }
+ Permission perm = pluginManager.getPermission(permission);
+ if (perm == null)
+ {
+ perm = new Permission(permission, defaultPerm);
+ pluginManager.addPermission(perm);
+ if (parent != null)
+ {
+ parent.getChildren().put(perm.getName(), Boolean.TRUE);
+ }
+ parent = perm;
+ }
+ perm.recalculatePermissibles();
+ return perm;
+ }
+ private static transient final Pattern URL_PATTERN = Pattern.compile("((?:(?:https?)://)?[\\w-_\\.]{2,})\\.([a-z]{2,3}(?:/\\S+)?)");
+ private static transient final Pattern VANILLA_PATTERN = Pattern.compile("\u00A7+[0-9A-FK-ORa-fk-or]");
+ private static transient final Pattern REPLACE_PATTERN = Pattern.compile("&([0-9a-fk-or])");
+ private static transient final Pattern VANILLA_COLOR_PATTERN = Pattern.compile("\u00A7+[0-9A-Fa-f]");
+ private static transient final Pattern VANILLA_MAGIC_PATTERN = Pattern.compile("\u00A7+[Kk]");
+ private static transient final Pattern VANILLA_FORMAT_PATTERN = Pattern.compile("\u00A7+[L-ORl-or]");
+ private static transient final Pattern REPLACE_COLOR_PATTERN = Pattern.compile("&([0-9a-f])");
+ private static transient final Pattern REPLACE_MAGIC_PATTERN = Pattern.compile("&(k)");
+ private static transient final Pattern REPLACE_FORMAT_PATTERN = Pattern.compile("&([l-or])");
+
+ public static String stripFormat(final String input)
+ {
+ if (input == null)
+ {
+ return null;
+ }
+ return VANILLA_PATTERN.matcher(input).replaceAll("");
+ }
+
+ public static String replaceFormat(final String input)
+ {
+ if (input == null)
+ {
+ return null;
+ }
+ return REPLACE_PATTERN.matcher(input).replaceAll("\u00a7$1");
+ }
+
+ public static String blockURL(final String input)
+ {
+ if (input == null)
+ {
+ return null;
+ }
+ String text = URL_PATTERN.matcher(input).replaceAll("$1 $2");
+ while (URL_PATTERN.matcher(text).find())
+ {
+ text = URL_PATTERN.matcher(text).replaceAll("$1 $2");
+ }
+ return text;
+ }
+
+ public static String formatString(final IUser user, final String permBase, final String input)
+ {
+ if (input == null)
+ {
+ return null;
+ }
+ String message;
+ if (Permissions.hasPermission(user.getBase(), permBase + ".color"))
+ {
+ message = Util.replaceColor(input, REPLACE_COLOR_PATTERN);
+ }
+ else
+ {
+ message = Util.stripColor(input, VANILLA_COLOR_PATTERN);
+ }
+ if (Permissions.hasPermission(user.getBase(), permBase + ".magic"))
+ {
+ message = Util.replaceColor(message, REPLACE_MAGIC_PATTERN);
+ }
+ else
+ {
+ message = Util.stripColor(message, VANILLA_MAGIC_PATTERN);
+ }
+ if (Permissions.hasPermission(user.getBase(), permBase + ".format"))
+ {
+ message = Util.replaceColor(message, REPLACE_FORMAT_PATTERN);
+ }
+ else
+ {
+ message = Util.stripColor(message, VANILLA_FORMAT_PATTERN);
+ }
+ return message;
+ }
+
+ public static String formatMessage(final IUser user, final String permBase, final String input)
+ {
+ if (input == null)
+ {
+ return null;
+ }
+ String message = formatString(user, permBase, input);
+ if (!Permissions.hasPermission(user.getBase(), permBase + ".url"))
+ {
+ message = Util.blockURL(message);
+ }
+ return message;
+ }
+
+ public static String stripColor(final String input)
+ {
+ if (input == null)
+ {
+ return null;
+ }
+
+ return VANILLA_COLOR_PATTERN.matcher(input).replaceAll("");
+ }
+
+ private static String stripColor(final String input, final Pattern pattern)
+ {
+ return pattern.matcher(input).replaceAll("");
+ }
+
+ private static String replaceColor(final String input, final Pattern pattern)
+ {
+ return pattern.matcher(input).replaceAll("\u00a7$1");
+ }
+}
diff --git a/Essentials/src/net/ess3/utils/gnu/inet/encoding/Punycode.java b/Essentials/src/net/ess3/utils/gnu/inet/encoding/Punycode.java
new file mode 100644
index 000000000..8d8aa3750
--- /dev/null
+++ b/Essentials/src/net/ess3/utils/gnu/inet/encoding/Punycode.java
@@ -0,0 +1,321 @@
+package net.ess3.utils.gnu.inet.encoding;
+
+
+/**
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software
+ * Foundation, Inc.
+ *
+ * Author: Oliver Hitz
+ *
+ * This file is part of GNU Libidn.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
+ * USA
+ */
+/**
+ * This class offers static methods for encoding/decoding strings
+ * using the Punycode algorithm.
+ * <ul>
+ * <li>RFC3492 Punycode
+ * </ul>
+ * Note that this implementation only supports 16-bit Unicode code
+ * points.
+ */
+/*
+ * Changes by snowleo:
+ * - Correctly catch wrong characters after the delimiter
+ * - If the string starts with the delimiter, it's an encoded string
+ * - If there is no delimiter, it's an ascii string.
+ * - Note: the string should never contain the delimiter.
+ */
+public class Punycode
+{
+ /*
+ * Punycode parameters
+ */
+ final static int TMIN = 1;
+ final static int TMAX = 26;
+ final static int BASE = 36;
+ final static int INITIAL_N = 128;
+ final static int INITIAL_BIAS = 72;
+ final static int DAMP = 700;
+ final static int SKEW = 38;
+ final static char DELIMITER = '-';
+
+ /**
+ * Punycodes a unicode string.
+ *
+ * @param input Unicode string.
+ * @return Punycoded string.
+ */
+ public static String encode(String input)
+ throws PunycodeException
+ {
+ int n = INITIAL_N;
+ int delta = 0;
+ int bias = INITIAL_BIAS;
+ StringBuffer output = new StringBuffer();
+
+ // Copy all basic code points to the output
+ int b = 0;
+ for (int i = 0; i < input.length(); i++)
+ {
+ char c = input.charAt(i);
+ if (isBasic(c))
+ {
+ output.append(c);
+ b++;
+ }
+ }
+
+ // Append delimiter
+ if (b < input.length()) // Changed by snowleo
+ {
+ output.append(DELIMITER);
+ }
+
+ int h = b;
+ while (h < input.length())
+ {
+ int m = Integer.MAX_VALUE;
+
+ // Find the minimum code point >= n
+ for (int i = 0; i < input.length(); i++)
+ {
+ int c = input.charAt(i);
+ if (c >= n && c < m)
+ {
+ m = c;
+ }
+ }
+
+ if (m - n > (Integer.MAX_VALUE - delta) / (h + 1))
+ {
+ throw new PunycodeException(PunycodeException.OVERFLOW);
+ }
+ delta = delta + (m - n) * (h + 1);
+ n = m;
+
+ for (int j = 0; j < input.length(); j++)
+ {
+ int c = input.charAt(j);
+ if (c < n)
+ {
+ delta++;
+ if (0 == delta)
+ {
+ throw new PunycodeException(PunycodeException.OVERFLOW);
+ }
+ }
+ if (c == n)
+ {
+ int q = delta;
+
+ for (int k = BASE;; k += BASE)
+ {
+ int t;
+ if (k <= bias)
+ {
+ t = TMIN;
+ }
+ else if (k >= bias + TMAX)
+ {
+ t = TMAX;
+ }
+ else
+ {
+ t = k - bias;
+ }
+ if (q < t)
+ {
+ break;
+ }
+ output.append((char)digit2codepoint(t + (q - t) % (BASE - t)));
+ q = (q - t) / (BASE - t);
+ }
+
+ output.append((char)digit2codepoint(q));
+ bias = adapt(delta, h + 1, h == b);
+ delta = 0;
+ h++;
+ }
+ }
+
+ delta++;
+ n++;
+ }
+
+ return output.toString();
+ }
+
+ /**
+ * Decode a punycoded string.
+ *
+ * @param input Punycode string
+ * @return Unicode string.
+ */
+ public static String decode(String input)
+ throws PunycodeException
+ {
+ int n = INITIAL_N;
+ int i = 0;
+ int bias = INITIAL_BIAS;
+ StringBuffer output = new StringBuffer();
+
+ int d = input.lastIndexOf(DELIMITER);
+ // Change start by snowleo
+ if (d < 0) {
+ return input;
+ }
+ else if (d > 0) // Change end by snowleo
+ {
+ for (int j = 0; j < d; j++)
+ {
+ char c = input.charAt(j);
+ if (!isBasic(c))
+ {
+ throw new PunycodeException(PunycodeException.BAD_INPUT);
+ }
+ output.append(c);
+ }
+ d++;
+ }
+ else
+ {
+ d = 1; // Changed by snowleo
+ }
+
+ while (d < input.length())
+ {
+ int oldi = i;
+ int w = 1;
+
+ for (int k = BASE;; k += BASE)
+ {
+ if (d == input.length())
+ {
+ throw new PunycodeException(PunycodeException.BAD_INPUT);
+ }
+ int c = input.charAt(d++);
+ int digit = codepoint2digit(c);
+ if (digit > (Integer.MAX_VALUE - i) / w)
+ {
+ throw new PunycodeException(PunycodeException.OVERFLOW);
+ }
+
+ i = i + digit * w;
+
+ int t;
+ if (k <= bias)
+ {
+ t = TMIN;
+ }
+ else if (k >= bias + TMAX)
+ {
+ t = TMAX;
+ }
+ else
+ {
+ t = k - bias;
+ }
+ if (digit < t)
+ {
+ break;
+ }
+ w = w * (BASE - t);
+ }
+
+ bias = adapt(i - oldi, output.length() + 1, oldi == 0);
+
+ if (i / (output.length() + 1) > Integer.MAX_VALUE - n)
+ {
+ throw new PunycodeException(PunycodeException.OVERFLOW);
+ }
+
+ n = n + i / (output.length() + 1);
+ i = i % (output.length() + 1);
+ output.insert(i, (char)n);
+ i++;
+ }
+
+ return output.toString();
+ }
+
+ public final static int adapt(int delta, int numpoints, boolean first)
+ {
+ if (first)
+ {
+ delta = delta / DAMP;
+ }
+ else
+ {
+ delta = delta / 2;
+ }
+
+ delta = delta + (delta / numpoints);
+
+ int k = 0;
+ while (delta > ((BASE - TMIN) * TMAX) / 2)
+ {
+ delta = delta / (BASE - TMIN);
+ k = k + BASE;
+ }
+
+ return k + ((BASE - TMIN + 1) * delta) / (delta + SKEW);
+ }
+
+ public final static boolean isBasic(char c)
+ {
+ return c < 0x80;
+ }
+
+ public final static int digit2codepoint(int d)
+ throws PunycodeException
+ {
+ if (d < 26)
+ {
+ // 0..25 : 'a'..'z'
+ return d + 'a';
+ }
+ else if (d < 36)
+ {
+ // 26..35 : '0'..'9';
+ return d - 26 + '0';
+ }
+ else
+ {
+ throw new PunycodeException(PunycodeException.BAD_INPUT);
+ }
+ }
+
+ public final static int codepoint2digit(int c)
+ throws PunycodeException
+ {
+ if (c - '0' < 10 && c >= '0') // Changed by snowleo
+ {
+ // '0'..'9' : 26..35
+ return c - '0' + 26;
+ }
+ else if (c - 'a' < 26 && c >= 'a') // Changed by snowleo
+ {
+ // 'a'..'z' : 0..25
+ return c - 'a';
+ }
+ else
+ {
+ throw new PunycodeException(PunycodeException.BAD_INPUT);
+ }
+ }
+}
diff --git a/Essentials/src/net/ess3/utils/gnu/inet/encoding/PunycodeException.java b/Essentials/src/net/ess3/utils/gnu/inet/encoding/PunycodeException.java
new file mode 100644
index 000000000..1f9649813
--- /dev/null
+++ b/Essentials/src/net/ess3/utils/gnu/inet/encoding/PunycodeException.java
@@ -0,0 +1,45 @@
+package net.ess3.utils.gnu.inet.encoding;
+
+
+/**
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software
+ * Foundation, Inc.
+ *
+ * Author: Oliver Hitz
+ *
+ * This file is part of GNU Libidn.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
+ * USA
+ */
+/**
+ * Exception handling for Punycode class.
+ */
+public class PunycodeException
+ extends Exception
+{
+ public static String OVERFLOW = "Overflow.";
+ public static String BAD_INPUT = "Bad input.";
+
+ /**
+ * Creates a new PunycodeException.
+ *
+ * @param m message.
+ */
+ public PunycodeException(String m)
+ {
+ super(m);
+ }
+}
diff --git a/Essentials/src/net/ess3/utils/textreader/ArrayListInput.java b/Essentials/src/net/ess3/utils/textreader/ArrayListInput.java
new file mode 100644
index 000000000..4f3a915f2
--- /dev/null
+++ b/Essentials/src/net/ess3/utils/textreader/ArrayListInput.java
@@ -0,0 +1,31 @@
+package net.ess3.utils.textreader;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+
+public class ArrayListInput implements IText
+{
+ private final transient List<String> lines = new ArrayList<String>();
+
+ @Override
+ public List<String> getLines()
+ {
+ return lines;
+ }
+
+ @Override
+ public List<String> getChapters()
+ {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public Map<String, Integer> getBookmarks()
+ {
+ return Collections.emptyMap();
+ }
+
+}
diff --git a/Essentials/src/net/ess3/utils/textreader/HelpInput.java b/Essentials/src/net/ess3/utils/textreader/HelpInput.java
new file mode 100644
index 000000000..109cc3702
--- /dev/null
+++ b/Essentials/src/net/ess3/utils/textreader/HelpInput.java
@@ -0,0 +1,179 @@
+package net.ess3.utils.textreader;
+
+import static net.ess3.I18n._;
+import net.ess3.api.IEssentials;
+import net.ess3.api.ISettings;
+import net.ess3.api.IUser;
+import net.ess3.permissions.HelpPermissions;
+import java.io.IOException;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import lombok.Cleanup;
+import org.bukkit.plugin.Plugin;
+import org.bukkit.plugin.PluginDescriptionFile;
+
+
+public class HelpInput implements IText
+{
+ private static final String DESCRIPTION = "description";
+ private static final String PERMISSION = "permission";
+ private static final String PERMISSIONS = "permissions";
+ private final transient List<String> lines = new ArrayList<String>();
+ private final transient List<String> chapters = new ArrayList<String>();
+ private final transient Map<String, Integer> bookmarks = new HashMap<String, Integer>();
+ private final static Logger logger = Logger.getLogger("Minecraft");
+
+ public HelpInput(final IUser user, final String match, final IEssentials ess) throws IOException
+ {
+ @Cleanup
+ final ISettings settings = ess.getSettings();
+ settings.acquireReadLock();
+ boolean reported = false;
+ final List<String> newLines = new ArrayList<String>();
+ String pluginName = "";
+ String pluginNameLow = "";
+ if (!match.equalsIgnoreCase(""))
+ {
+ lines.add(_("helpMatching", match));
+ }
+
+ for (Plugin p : ess.getServer().getPluginManager().getPlugins())
+ {
+ try
+ {
+ final List<String> pluginLines = new ArrayList<String>();
+ final PluginDescriptionFile desc = p.getDescription();
+ final Map<String, Map<String, Object>> cmds = desc.getCommands();
+ pluginName = p.getDescription().getName();
+ pluginNameLow = pluginName.toLowerCase(Locale.ENGLISH);
+ if (pluginNameLow.equals(match))
+ {
+ lines.clear();
+ newLines.clear();
+ lines.add(_("helpFrom", p.getDescription().getName()));
+ }
+
+ for (Map.Entry<String, Map<String, Object>> k : cmds.entrySet())
+ {
+ try
+ {
+ if (!match.equalsIgnoreCase("") && (!pluginNameLow.contains(match)) && (!k.getKey().toLowerCase(Locale.ENGLISH).contains(match))
+ && (!(k.getValue().get(DESCRIPTION) instanceof String
+ && ((String)k.getValue().get(DESCRIPTION)).toLowerCase(Locale.ENGLISH).contains(match))))
+ {
+ continue;
+ }
+
+ if (pluginNameLow.contains("essentials"))
+ {
+ final String node = "essentials." + k.getKey();
+ if (!settings.getData().getCommands().isDisabled(k.getKey()) && user.hasPermission(node))
+ {
+ pluginLines.add(_("helpLine", k.getKey(), k.getValue().get(DESCRIPTION)));
+ }
+ }
+ else
+ {
+ if (settings.getData().getCommands().getHelp().isShowNonEssCommandsInHelp())
+ {
+ final Map<String, Object> value = k.getValue();
+ Object permissions = null;
+ if (value.containsKey(PERMISSION))
+ {
+ permissions = value.get(PERMISSION);
+ }
+ else if (value.containsKey(PERMISSIONS))
+ {
+ permissions = value.get(PERMISSIONS);
+ }
+ if (HelpPermissions.getPermission(pluginNameLow).isAuthorized(user))
+ {
+ pluginLines.add(_("helpLine", k.getKey(), value.get(DESCRIPTION)));
+ }
+ else if (permissions instanceof List && !((List<Object>)permissions).isEmpty())
+ {
+ boolean enabled = false;
+ for (Object o : (List<Object>)permissions)
+ {
+ if (o instanceof String && user.hasPermission(o.toString()))
+ {
+ enabled = true;
+ break;
+ }
+ }
+ if (enabled)
+ {
+ pluginLines.add(_("helpLine", k.getKey(), value.get(DESCRIPTION)));
+ }
+ }
+ else if (permissions instanceof String && !"".equals(permissions))
+ {
+ if (user.hasPermission(permissions.toString()))
+ {
+ pluginLines.add(_("helpLine", k.getKey(), value.get(DESCRIPTION)));
+ }
+ }
+ else
+ {
+ if (!settings.getData().getCommands().getHelp().isHidePermissionlessCommands())
+ {
+ pluginLines.add(_("helpLine", k.getKey(), value.get(DESCRIPTION)));
+ }
+ }
+ }
+ }
+ }
+ catch (NullPointerException ex)
+ {
+ continue;
+ }
+ }
+ if (!pluginLines.isEmpty())
+ {
+ newLines.addAll(pluginLines);
+ if (pluginNameLow.equals(match))
+ {
+ break;
+ }
+ if (match.equalsIgnoreCase(""))
+ {
+ lines.add(_("helpPlugin", pluginName, pluginNameLow));
+ }
+ }
+ }
+ catch (NullPointerException ex)
+ {
+ continue;
+ }
+ catch (Exception ex)
+ {
+ if (!reported)
+ {
+ logger.log(Level.WARNING, _("commandHelpFailedForPlugin", pluginNameLow), ex);
+ }
+ reported = true;
+ continue;
+ }
+ }
+ lines.addAll(newLines);
+ }
+
+ @Override
+ public List<String> getLines()
+ {
+ return lines;
+ }
+
+ @Override
+ public List<String> getChapters()
+ {
+ return chapters;
+ }
+
+ @Override
+ public Map<String, Integer> getBookmarks()
+ {
+ return bookmarks;
+ }
+}
diff --git a/Essentials/src/net/ess3/utils/textreader/IText.java b/Essentials/src/net/ess3/utils/textreader/IText.java
new file mode 100644
index 000000000..89e5de19f
--- /dev/null
+++ b/Essentials/src/net/ess3/utils/textreader/IText.java
@@ -0,0 +1,14 @@
+package net.ess3.utils.textreader;
+
+import java.util.List;
+import java.util.Map;
+
+
+public interface IText
+{
+ List<String> getLines();
+
+ List<String> getChapters();
+
+ Map<String, Integer> getBookmarks();
+}
diff --git a/Essentials/src/net/ess3/utils/textreader/KeywordReplacer.java b/Essentials/src/net/ess3/utils/textreader/KeywordReplacer.java
new file mode 100644
index 000000000..3ec331d7a
--- /dev/null
+++ b/Essentials/src/net/ess3/utils/textreader/KeywordReplacer.java
@@ -0,0 +1,157 @@
+package net.ess3.utils.textreader;
+
+import net.ess3.utils.DescParseTickFormat;
+import net.ess3.api.IEssentials;
+import net.ess3.api.IUser;
+import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import lombok.Cleanup;
+import org.bukkit.World;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+import org.bukkit.plugin.Plugin;
+
+
+public class KeywordReplacer implements IText
+{
+ private final transient IText input;
+ private final transient List<String> replaced;
+ private final transient IEssentials ess;
+
+ public KeywordReplacer(final IText input, final CommandSender sender, final IEssentials ess)
+ {
+ this.input = input;
+ this.replaced = new ArrayList<String>(this.input.getLines().size());
+ this.ess = ess;
+ replaceKeywords(sender);
+ }
+
+ private void replaceKeywords(final CommandSender sender)
+ {
+ String displayName, ipAddress, balance, mails, world;
+ String worlds, online, unique, playerlist, date, time;
+ String worldTime12, worldTime24, worldDate, plugins;
+ String userName, address, version;
+ if (sender instanceof Player)
+ {
+ @Cleanup
+ final IUser user = ess.getUser((Player)sender);
+ user.acquireReadLock();
+ user.setDisplayNick();
+ displayName = user.getDisplayName();
+ userName = user.getName();
+ ipAddress = user.getAddress().getAddress().toString();
+ address = user.getAddress().toString();
+ balance = Double.toString(user.getMoney());
+ mails = Integer.toString(user.getData().getMails() == null ? 0 : user.getData().getMails().size());
+ world = user.getLocation() == null || user.getLocation().getWorld() == null ? "" : user.getLocation().getWorld().getName();
+ worldTime12 = DescParseTickFormat.format12(user.getWorld() == null ? 0 : user.getWorld().getTime());
+ worldTime24 = DescParseTickFormat.format24(user.getWorld() == null ? 0 : user.getWorld().getTime());
+ worldDate = DateFormat.getDateInstance(DateFormat.MEDIUM, ess.getI18n().getCurrentLocale()).format(DescParseTickFormat.ticksToDate(user.getWorld() == null ? 0 : user.getWorld().getFullTime()));
+ }
+ else
+ {
+ displayName = ipAddress = balance = mails = world = worldTime12 = worldTime24 = worldDate = "";
+ }
+
+ int playerHidden = 0;
+ for (Player p : ess.getServer().getOnlinePlayers())
+ {
+ if (ess.getUser(p).isHidden())
+ {
+ playerHidden++;
+ }
+ }
+ online = Integer.toString(ess.getServer().getOnlinePlayers().length - playerHidden);
+ unique = Integer.toString(ess.getUserMap().getUniqueUsers());
+
+ final StringBuilder worldsBuilder = new StringBuilder();
+ for (World w : ess.getServer().getWorlds())
+ {
+ if (worldsBuilder.length() > 0)
+ {
+ worldsBuilder.append(", ");
+ }
+ worldsBuilder.append(w.getName());
+ }
+ worlds = worldsBuilder.toString();
+
+ final StringBuilder playerlistBuilder = new StringBuilder();
+ for (Player p : ess.getServer().getOnlinePlayers())
+ {
+ if (ess.getUser(p).isHidden())
+ {
+ continue;
+ }
+ if (playerlistBuilder.length() > 0)
+ {
+ playerlistBuilder.append(", ");
+ }
+ playerlistBuilder.append(p.getDisplayName());
+ }
+ playerlist = playerlistBuilder.toString();
+
+ final StringBuilder pluginlistBuilder = new StringBuilder();
+ for (Plugin p : ess.getServer().getPluginManager().getPlugins())
+ {
+ if (pluginlistBuilder.length() > 0)
+ {
+ pluginlistBuilder.append(", ");
+ }
+ pluginlistBuilder.append(p.getDescription().getName());
+ }
+ plugins = pluginlistBuilder.toString();
+
+ date = DateFormat.getDateInstance(DateFormat.MEDIUM, ess.getI18n().getCurrentLocale()).format(new Date());
+ time = DateFormat.getTimeInstance(DateFormat.MEDIUM, ess.getI18n().getCurrentLocale()).format(new Date());
+
+ version = ess.getServer().getVersion();
+
+ for (int i = 0; i < input.getLines().size(); i++)
+ {
+ String line = input.getLines().get(i);
+
+ line = line.replace("{PLAYER}", displayName);
+ line = line.replace("{DISPLAYNAME}", displayName);
+ line = line.replace("{USERNAME}", displayName);
+ line = line.replace("{IP}", ipAddress);
+ line = line.replace("{ADDRESS}", ipAddress);
+ line = line.replace("{BALANCE}", balance);
+ line = line.replace("{MAILS}", mails);
+ line = line.replace("{WORLD}", world);
+ line = line.replace("{ONLINE}", online);
+ line = line.replace("{UNIQUE}", unique);
+ line = line.replace("{WORLDS}", worlds);
+ line = line.replace("{PLAYERLIST}", playerlist);
+ line = line.replace("{TIME}", time);
+ line = line.replace("{DATE}", date);
+ line = line.replace("{WORLDTIME12}", worldTime12);
+ line = line.replace("{WORLDTIME24}", worldTime24);
+ line = line.replace("{WORLDDATE}", worldDate);
+ line = line.replace("{PLUGINS}", plugins);
+ line = line.replace("{VERSION}", version);
+ replaced.add(line);
+ }
+ }
+
+ @Override
+ public List<String> getLines()
+ {
+ return replaced;
+ }
+
+ @Override
+ public List<String> getChapters()
+ {
+ return input.getChapters();
+ }
+
+ @Override
+ public Map<String, Integer> getBookmarks()
+ {
+ return input.getBookmarks();
+ }
+}
diff --git a/Essentials/src/net/ess3/utils/textreader/SimpleTextInput.java b/Essentials/src/net/ess3/utils/textreader/SimpleTextInput.java
new file mode 100644
index 000000000..8d392448e
--- /dev/null
+++ b/Essentials/src/net/ess3/utils/textreader/SimpleTextInput.java
@@ -0,0 +1,35 @@
+package net.ess3.utils.textreader;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+
+public class SimpleTextInput implements IText
+{
+ private final transient List<String> lines = new ArrayList<String>();
+
+ public SimpleTextInput (final String input) {
+ lines.add(input);
+ }
+
+ @Override
+ public List<String> getLines()
+ {
+ return lines;
+ }
+
+ @Override
+ public List<String> getChapters()
+ {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public Map<String, Integer> getBookmarks()
+ {
+ return Collections.emptyMap();
+ }
+
+}
diff --git a/Essentials/src/net/ess3/utils/textreader/SimpleTextPager.java b/Essentials/src/net/ess3/utils/textreader/SimpleTextPager.java
new file mode 100644
index 000000000..ea70010b6
--- /dev/null
+++ b/Essentials/src/net/ess3/utils/textreader/SimpleTextPager.java
@@ -0,0 +1,31 @@
+package net.ess3.utils.textreader;
+
+import org.bukkit.command.CommandSender;
+
+
+public class SimpleTextPager
+{
+ private final transient IText text;
+
+ public SimpleTextPager(final IText text)
+ {
+ this.text = text;
+ }
+
+ public void showPage(final CommandSender sender)
+ {
+ for (String line : text.getLines())
+ {
+ sender.sendMessage(line);
+ }
+ }
+
+ public String getString(int line)
+ {
+ if (text.getLines().size() < line)
+ {
+ return null;
+ }
+ return text.getLines().get(line);
+ }
+}
diff --git a/Essentials/src/net/ess3/utils/textreader/TextInput.java b/Essentials/src/net/ess3/utils/textreader/TextInput.java
new file mode 100644
index 000000000..20fecc255
--- /dev/null
+++ b/Essentials/src/net/ess3/utils/textreader/TextInput.java
@@ -0,0 +1,149 @@
+package net.ess3.utils.textreader;
+
+import net.ess3.utils.Util;
+import net.ess3.api.IEssentials;
+import net.ess3.api.IUser;
+import net.ess3.api.InvalidNameException;
+import java.io.*;
+import java.lang.ref.SoftReference;
+import java.util.*;
+import java.util.logging.Level;
+import org.bukkit.Bukkit;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+
+public class TextInput implements IText
+{
+ private final transient List<String> lines;
+ private final transient List<String> chapters;
+ private final transient Map<String, Integer> bookmarks;
+ private final transient long lastChange;
+ private final static HashMap<String, SoftReference<TextInput>> cache = new HashMap<String, SoftReference<TextInput>>();
+
+ public TextInput(final CommandSender sender, final String filename, final boolean createFile, final IEssentials ess) throws IOException
+ {
+
+ File file = null;
+ if (sender instanceof Player)
+ {
+ try
+ {
+ final IUser user = ess.getUser((Player)sender);
+ file = new File(ess.getDataFolder(), filename + "_" + Util.sanitizeFileName(user.getName()) + ".txt");
+ if (!file.exists())
+ {
+ file = new File(ess.getDataFolder(), filename + "_" + Util.sanitizeFileName(ess.getRanks().getMainGroup(user)) + ".txt");
+ }
+ }
+ catch (InvalidNameException ex)
+ {
+ Bukkit.getLogger().log(Level.WARNING, ex.getMessage(), ex);
+ }
+ }
+ if (file == null || !file.exists())
+ {
+ file = new File(ess.getDataFolder(), filename + ".txt");
+ }
+ if (file.exists())
+ {
+ lastChange = file.lastModified();
+ boolean readFromfile;
+ synchronized (cache)
+ {
+ final SoftReference<TextInput> inputRef = cache.get(file.getName());
+ TextInput input;
+ if (inputRef == null || (input = inputRef.get()) == null || input.lastChange < lastChange)
+ {
+ lines = new ArrayList<String>();
+ chapters = new ArrayList<String>();
+ bookmarks = new HashMap<String, Integer>();
+ cache.put(file.getName(), new SoftReference<TextInput>(this));
+ readFromfile = true;
+ }
+ else
+ {
+ lines = Collections.unmodifiableList(input.getLines());
+ chapters = Collections.unmodifiableList(input.getChapters());
+ bookmarks = Collections.unmodifiableMap(input.getBookmarks());
+ readFromfile = false;
+ }
+ }
+ if (readFromfile)
+ {
+ final BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
+ try
+ {
+ int lineNumber = 0;
+ while (bufferedReader.ready())
+ {
+ final String line = bufferedReader.readLine();
+ if (line == null)
+ {
+ break;
+ }
+ if (line.length() > 0 && line.charAt(0) == '#')
+ {
+ bookmarks.put(line.substring(1).toLowerCase(Locale.ENGLISH).replaceAll("&[0-9a-fk]", ""), lineNumber);
+ chapters.add(line.substring(1).replace('&', '§').replace("§§", "&"));
+ }
+ lines.add(line.replace('&', '§').replace("§§", "&"));
+ lineNumber++;
+ }
+ }
+ finally
+ {
+ bufferedReader.close();
+ }
+ }
+ }
+ else
+ {
+ lastChange = 0;
+ lines = Collections.emptyList();
+ chapters = Collections.emptyList();
+ bookmarks = Collections.emptyMap();
+ if (createFile)
+ {
+ final InputStream input = ess.getResource(filename + ".txt");
+ final OutputStream output = new FileOutputStream(file);
+ try
+ {
+ final byte[] buffer = new byte[1024];
+ int length = input.read(buffer);
+ while (length > 0)
+ {
+ output.write(buffer, 0, length);
+ length = input.read(buffer);
+ }
+ }
+ finally
+ {
+ if(output != null)
+ output.close();
+ if(input != null)
+ input.close();
+ }
+ throw new FileNotFoundException("File " + filename + ".txt does not exist. Creating one for you.");
+ }
+ }
+ }
+
+ @Override
+ public List<String> getLines()
+ {
+ return lines;
+ }
+
+ @Override
+ public List<String> getChapters()
+ {
+ return chapters;
+ }
+
+ @Override
+ public Map<String, Integer> getBookmarks()
+ {
+ return bookmarks;
+ }
+}
diff --git a/Essentials/src/net/ess3/utils/textreader/TextPager.java b/Essentials/src/net/ess3/utils/textreader/TextPager.java
new file mode 100644
index 000000000..18b662be5
--- /dev/null
+++ b/Essentials/src/net/ess3/utils/textreader/TextPager.java
@@ -0,0 +1,200 @@
+package net.ess3.utils.textreader;
+
+import net.ess3.I18n;
+import static net.ess3.I18n._;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import org.bukkit.command.CommandSender;
+
+
+public class TextPager
+{
+ private final transient IText text;
+ private final transient boolean onePage;
+
+ public TextPager(final IText text)
+ {
+ this(text, false);
+ }
+
+ public TextPager(final IText text, final boolean onePage)
+ {
+ this.text = text;
+ this.onePage = onePage;
+ }
+
+ public void showPage(final String pageStr, final String chapterPageStr, final String commandName, final CommandSender sender)
+ {
+ List<String> lines = text.getLines();
+ List<String> chapters = text.getChapters();
+ Map<String, Integer> bookmarks = text.getBookmarks();
+
+ if (bookmarks.isEmpty())
+ {
+ int page = 1;
+ try
+ {
+ page = Integer.parseInt(pageStr);
+ }
+ catch (Exception ex)
+ {
+ page = 1;
+ }
+ if (page < 1)
+ {
+ page = 1;
+ }
+
+ final int start = onePage ? 0 : (page - 1) * 9;
+ final int pages = lines.size() / 9 + (lines.size() % 9 > 0 ? 1 : 0);
+ if (!onePage)
+ {
+ StringBuilder content = new StringBuilder();
+ final String[] title = commandName.split(" ", 2);
+ if (title.length > 1)
+ {
+ content.append(I18n.capitalCase(title[0])).append(": ");
+ content.append(title[1]);
+ }
+ else if (chapterPageStr != null)
+ {
+ content.append(I18n.capitalCase(commandName)).append(": ");
+ content.append(chapterPageStr);
+ }
+ else
+ {
+ content.append(I18n.capitalCase(commandName));
+ }
+ sender.sendMessage(_("infoPages", page, pages, content));
+ }
+ for (int i = start; i < lines.size() && i < start + (onePage ? 20 : 9); i++)
+ {
+ sender.sendMessage(lines.get(i));
+ }
+ if (!onePage && page < pages)
+ {
+ sender.sendMessage(_("readNextPage", commandName, page + 1));
+ }
+ return;
+ }
+
+ if (pageStr == null || pageStr.isEmpty() || pageStr.matches("[0-9]+"))
+ {
+ if (lines.get(0).startsWith("#"))
+ {
+ if (onePage)
+ {
+ return;
+ }
+ sender.sendMessage(_("infoChapter"));
+ final StringBuilder sb = new StringBuilder();
+ boolean first = true;
+ for (String string : chapters)
+ {
+ if (!first)
+ {
+ sb.append(", ");
+ }
+ first = false;
+ sb.append(string);
+ }
+ sender.sendMessage(sb.toString());
+ return;
+ }
+ else
+ {
+ int page = 1;
+ try
+ {
+ page = Integer.parseInt(pageStr);
+ }
+ catch (Exception ex)
+ {
+ page = 1;
+ }
+ if (page < 1)
+ {
+ page = 1;
+ }
+
+ int start = onePage ? 0 : (page - 1) * 9;
+ int end;
+ for (end = 0; end < lines.size(); end++)
+ {
+ String line = lines.get(end);
+ if (line.startsWith("#"))
+ {
+ break;
+ }
+ }
+
+ int pages = end / 9 + (end % 9 > 0 ? 1 : 0);
+ if (!onePage)
+ {
+
+ sender.sendMessage(_("infoPages", page, pages, I18n.capitalCase(commandName)));
+ }
+ for (int i = start; i < end && i < start + (onePage ? 20 : 9); i++)
+ {
+ sender.sendMessage(lines.get(i));
+ }
+ if (!onePage && page < pages)
+ {
+ sender.sendMessage(_("readNextPage", commandName, page + 1));
+ }
+ return;
+ }
+ }
+
+ int chapterpage = 0;
+ if (chapterPageStr != null)
+ {
+ try
+ {
+ chapterpage = Integer.parseInt(chapterPageStr) - 1;
+ }
+ catch (Exception ex)
+ {
+ chapterpage = 0;
+ }
+ if (chapterpage < 0)
+ {
+ chapterpage = 0;
+ }
+ }
+
+ if (!bookmarks.containsKey(pageStr.toLowerCase(Locale.ENGLISH)))
+ {
+ sender.sendMessage(_("infoUnknownChapter"));
+ return;
+ }
+ final int chapterstart = bookmarks.get(pageStr.toLowerCase(Locale.ENGLISH)) + 1;
+ int chapterend;
+ for (chapterend = chapterstart; chapterend < lines.size(); chapterend++)
+ {
+ final String line = lines.get(chapterend);
+ if (line.length() > 0 && line.charAt(0) == '#')
+ {
+ break;
+ }
+ }
+ final int start = chapterstart + (onePage ? 0 : chapterpage * 9);
+
+ final int page = chapterpage + 1;
+ final int pages = (chapterend - chapterstart) / 9 + ((chapterend - chapterstart) % 9 > 0 ? 1 : 0);
+ if (!onePage)
+ {
+ sender.sendMessage(_("infoChapterPages", pageStr, page, pages));
+ }
+ for (int i = start; i < chapterend && i < start + (onePage ? 20 : 9); i++)
+ {
+ sender.sendMessage(lines.get(i));
+ }
+ if (!onePage && page < pages)
+ {
+ sender.sendMessage(_("readNextPage", commandName, pageStr + " " + (page + 1)));
+ }
+ }
+}