/* * CoreUtility.java * Copyright 2002 (C) Bryan McRoberts <merton_monk@yahoo.com> * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Created on Feb 18, 2002, 5:20:42 PM * * $Id$ */ package pcgen.core.utils; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.regex.Pattern; import pcgen.cdom.base.Constants; import pcgen.core.Equipment; import pcgen.core.SettingsHandler; import pcgen.system.PCGenPropBundle; import pcgen.util.Logging; /** * <code>CoreUtility</code>. * * Assorted generic-ish functionality moved from Globals and PlayerCharacter * (the two biggest classes in the project.) Some of this code seems awfully * similar, and should probably be further refactored. * * @author Jonas Karlsson <pjak@yahoo.com> */ public final class CoreUtility { static final private double epsilon = 0.0001d; public static final Comparator<Equipment> equipmentComparator = new Comparator<Equipment>() { private int compareInts(final int obj1Index, final int obj2Index) { if (obj1Index > obj2Index) { return 1; } else if (obj1Index < obj2Index) { return -1; } else { return 0; } } @Override public int compare(final Equipment obj1, final Equipment obj2) { int o1i = obj1.getOutputIndex(); int o2i = obj2.getOutputIndex(); // Force unset items (index of 0) to appear at the end o1i = (o1i == 0) ? 999 : o1i; o2i = (o2i == 0) ? 999 : o2i; final int result1 = compareInts(o1i, o2i); if (result1 != 0) { return result1; } final int result2 = compareInts(obj1.getOutputSubindex(), obj2 .getOutputSubindex()); if (result2 != 0) { return result2; } final int result3 = obj1.getName().compareToIgnoreCase( obj2.getName()); if (result3 != 0) { return result3; } final int result4 = obj1.getAppliedName().compareToIgnoreCase( obj2.getAppliedName()); if (result4 != 0) { return result4; } return obj1.getParentName().compareToIgnoreCase( obj2.getParentName()); } @Override public boolean equals(final Object obj) { return false; } @Override public int hashCode() { return 0; } }; private CoreUtility() { super(); } /** * return true if the protocol of the URL represented is FTP or HTTP * * @param url * the URL object to test for a network protocol * @return true if the protocol of this URL is FTP or HTTP */ public static boolean isNetURL(final URL url) { return "http".equalsIgnoreCase(url.getProtocol()) || "ftp".equalsIgnoreCase(url.getProtocol()); } /** * Capitalize the first letter of every word in a string * * @param aString * the string to convert to Title case * @return a new string with the first letter of every word capitalised */ public static String capitalizeFirstLetter(final String aString) { boolean toUpper = true; final char[] a = aString.toLowerCase().toCharArray(); for (int i = 0; i < a.length; ++i) { if (Character.isWhitespace(a[i])) { toUpper = true; } else { if (toUpper && Character.isLowerCase(a[i])) { a[i] = Character.toUpperCase(a[i]); } toUpper = false; } } return new String(a); } // this method is unused at the release of 5.13.3 alpha // // /** // * Stick a comma between every character of a string. // * @param oldString // * @return String // */ // public static String commaDelimit(final String oldString) // { // final int oldStringLength = oldString.length(); // final StringBuilder newString = new StringBuilder(oldStringLength); // // for (int i = 0; i < oldStringLength; ++i) // { // if (i != 0) // { // newString.append(','); // } // // newString.append(oldString.charAt(i)); // } // // return newString.toString(); // } // // /** // * Simple passthrough, calls join(stringArray, ',') to do the work. // * @param stringArray // * @return String // */ // public static String commaDelimit(final Collection<String> stringArray) // { // return join(stringArray, ", "); // } /** * Compare two doubles within a given epsilon. * * @param a * first operand * @param b * second operand * @param eps * the epsilon (or deadband) * @return TRUE {@literal if abs(a - b) < eps}, else FALSE */ public static boolean compareDouble(final double a, final double b, final double eps) { // If the difference is less than epsilon, treat as equal. return Math.abs(a - b) < eps; } /** * Compare two doubles within an epsilon of 0.0001. * * @param a * first operand * @param b * second operand * @return TRUE if equal, else FALSE */ public static boolean doublesEqual(final double a, final double b) { // If the difference is less than epsilon, treat as equal. return compareDouble(a, b, epsilon); } /** * protect the floor function from the vagaries of floating point precision. * @param d the double that we would like the floor value for * @return the floor after adding epsilon */ public static double epsilonFloor (double d) { return Math.floor(d + epsilon); } /** * Changes a path to make sure all instances of \ or / are replaced with * File.separatorChar. * * @param argFileName * The path to be fixed * @return String */ public static String fixFilenamePath(final String argFileName) { return argFileName.replace('/', File.separatorChar).replace('\\', File.separatorChar); } /** * Get the inner most String end * * @param aString * The string to be searched for the innermost ( * @return inner most String end */ public static int innerMostStringEnd(final String aString) { int index = 0; int hi = 0; int current = 0; for (int i = 0; i < aString.length(); ++i) { if (aString.charAt(i) == '(') { ++current; if (current > hi) { hi = current; } } else if (aString.charAt(i) == ')') { if (current == hi) { index = i; } --current; } } return index; } /** * Get the innermost String start * * @param aString * the string sto be searched for the ) that closes the innermost * parenthesised expression * * @return innermost String start */ public static int innerMostStringStart(final String aString) { int index = 0; int hi = 0; int current = 0; for (int i = 0; i < aString.length(); ++i) { if (aString.charAt(i) == '(') { ++current; if (current >= hi) { hi = current; index = i; } } else if (aString.charAt(i) == ')') { --current; } } return index; } // /** // * Concatenates the List into a String using the separator // * as the delimitor. // * // * Note the actual delimitor is the separator + " " // * // * @param strings An ArrayList of strings // * @param separator The separating character // * @return A 'separator' separated String // */ // public static String join(final Collection<?> strings, final char // separator) // { // return join(strings, separator + " "); // } /** * Return the english suffix for a given ordinal value * * @param iValue * the ordinal value * @return ordinal suffix (st, nd, etc.) */ public static String ordinal(final int iValue) { String suffix = "th"; if ((iValue < 4) || (iValue > 20)) { switch (iValue % 10) { case 1: suffix = "st"; break; case 2: suffix = "nd"; break; case 3: suffix = "rd"; break; default: break; } } return Integer.toString(iValue) + suffix; } /** * Turn a 'separator' separated string into a ArrayList of strings, each * corresponding to one trimmed 'separator'-separated portion of the * original string. * * @param aString * The string to be split * @param separator * The separator that separates the string. * @return a List of Strings */ public static List<String> split(final String aString, final char separator) { final List<String> temp = new ArrayList<>(); final String sepStr = Pattern.quote(String.valueOf(separator)); if (aString.trim().isEmpty()) { return temp; } for (final String s : Arrays.asList(aString.split(sepStr))) { temp.add(s.trim()); } return temp; } /** * Unescape the : character * * @param in * the string to operate on * @return the modified string */ public static String unEscapeColons2(final String in) { return in.replaceAll(Pattern.quote(";"), ":"); } /** * Merge the equipment list * * @param equip * the collection of Equipment * @param merge * The type of merge to perform * * @return merged list */ public static List<Equipment> mergeEquipmentList( final Collection<Equipment> equip, final int merge) { List<Equipment> workingList = new ArrayList<>(); for (Equipment e : equip) { workingList.add(e.clone()); } workingList.sort(equipmentComparator); // no merging, just sorting (calling this is really stupid, // just use the sort above) if (merge == Constants.MERGE_NONE) { return workingList; } int endIndex = workingList.size(); for (int i = 0; i < endIndex; i++) { final Equipment eq1 = workingList.get(i); double eQty = eq1.qty(); for (int j = i + 1; j < endIndex; j++) { final Equipment eq2 = workingList.get(j); // no container merge or Temporary Bonus generated equipment // must not merge if (eq1.isContainer() || eq1.isType("TEMPORARY") || eq2.isType("TEMPORARY")) { continue; } if (eq1.getName().equals(eq2.getName())) { // merge all like equipment together if (merge == Constants.MERGE_ALL // merge like equipment within same container || (merge == Constants.MERGE_LOCATION && (eq1.getLocation() == eq2.getLocation()) && eq1.getParentName().equals(eq2.getParentName()))) { workingList.remove(eq2); eQty += eq2.qty(); endIndex--; } } } workingList.get(i).setQty(eQty); } return workingList; } /** * Compare the two PCGen versions. * * @param ver * The first version * @param compVer * The second version * @return the value 0 if the PCG versions are equal; a value less than 0 if * the first version is less than the second version; and a value * greater than 0 if the first version is greater than the second * version. */ public static int compareVersions(int[] ver, int[] compVer) { if (ver[0] != compVer[0]) { return Integer.valueOf(ver[0]).compareTo(compVer[0]); } if (ver[1] != compVer[1]) { return Integer.valueOf(ver[1]).compareTo(compVer[1]); } return Integer.valueOf(ver[2]).compareTo(compVer[2]); } /** * Compare the two PCGen versions. * * @param ver * The first version * @param compVer * The second version * @return the value 0 if the PCG versions are equal; a value less than 0 if * the first version is less than the second version; and a value * greater than 0 if the first version is greater than the second * version. */ public static int compareVersions(String ver, String compVer) { if (!ver.equals(compVer)) { return compareVersions(convertVersionToNumber(ver), convertVersionToNumber(compVer)); } return 0; } /** * Check if a version is earlier or equal to the current pcgen version. * * @param version * PCGen version to be checked. * @return True if the version is before or equal to the current pcgen * version. */ public static boolean isPriorToCurrent(String version) { return CoreUtility.compareVersions(version, PCGenPropBundle .getVersionNumber()) <= 0; } /** * Convert a PCGen version to its numerical format. * * @param version * the String version * @return the version as an array of 3 ints */ public static int[] convertVersionToNumber(String version) { int[] intVer = { 0, 0, 0 }; // extract the tokens from the version line String[] tokens = version.split(" |\\.|\\-", 4); //$NON-NLS-1$ for (int idx = 0; idx < 3 && idx < tokens.length; idx++) { try { intVer[idx] = Integer.parseInt(tokens[idx]); } catch (NumberFormatException e) { if (idx == 2 && (tokens[idx].startsWith("RC"))) { // Ignore we are not concerned about Release candidates } else { // Something in the first 3 digits was not an integer Logging.errorPrint("Invalid PCGen version: " + version); } } } return intVer; } /** * Checks if the supplied version shares the same major and minor versions * as the currently running version of PCGen. * * @param ver * the version to check * @return true, if it is the current minor version */ public static boolean isCurrMinorVer(String ver) { if (ver.equals(PCGenPropBundle.getVersionNumber())) { return true; } int[] inVer = convertVersionToNumber(ver); int[] currVer = convertVersionToNumber(PCGenPropBundle.getVersionNumber()); return (inVer[0] == currVer[0] && inVer[1] == currVer[1]); } /** * Check if the two versions are different only in release number. i.e. * they have the same major and minor versions. * * @param ver1 A PCGen version number to be compared. * @param ver2 A PCGen version number to be compared. * @return true if they have the same major and minor versions. */ public static boolean sameMajorMinorVer(int[] ver1, int[] ver2) { return (ver1[0] == ver2[0] && ver1[1] == ver2[1]); } public static URL processFileToURL(String value) throws MalformedURLException { StringBuilder inputPath = new StringBuilder(100); inputPath .append(SettingsHandler.getPcgenSponsorDir().getAbsolutePath()); inputPath.append(File.separator).append(value); // Not a URL; make sure to fix the path syntax String convertedPath = fixFilenamePath(inputPath.toString()); // Make sure the path starts with a separator if (!convertedPath.startsWith(File.separator)) { convertedPath = File.separator + convertedPath; } return new URL("file:" + inputPath); } }