/* This file is Copyright (c) 2005 Robert Alten Simons (info@cohort.com). * See the MIT/X-like license in LICENSE.txt. * For more information visit www.cohort.com or contact info@cohort.com. */ package ucar.nc2.ogc.erddap.util; /** * A class with static String methods that add to native String methods. * All are static methods. */ public class ErddapString2 { /** * ERROR is a constant so that it will be consistent, so that one can * search for it in output files. * This is NOT final, so EDStatic can change it. * This is the original definition, referenced by many other classes. */ public static String ERROR = "ERROR"; /** * Finds the first instance of c at or after fromIndex (0.. ) in cArray. * * @param cArray * @param c the char you want to find * @param fromIndex the index number of the position to start the search * @return The first instance of c. If not found, it returns -1. */ public static int indexOf(char[] cArray, char c, int fromIndex) { int cArrayLength = cArray.length; for (int index = Math.max(fromIndex, 0); index < cArrayLength; index++) { if (cArray[index] == c) return index; } return -1; } /** * This includes hiASCII/ISO Latin 1/ISO 8859-1, but not extensive unicode characters. * Letters are A..Z, a..z, and #192..#255 (except #215 and #247). * For unicode characters, see Java Lang Spec pg 14. * * @param c a char * @return true if c is a letter */ public static boolean isLetter(int c) { //return (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) //|| ((c >= '\u00c0') && (c <= '\u00FF') && (c != '\u00d7') //&& (c != '\u00f7'))); if (c < 'A') return false; if (c <= 'Z') return true; if (c < 'a') return false; if (c <= 'z') return true; if (c < '\u00c0') return false; if (c == '\u00d7') return false; if (c <= '\u00FF') return true; return false; } /** * 0..9. * Non-Latin numeric characters are not included (see Java Lang Spec pg 14). * * @param c a char * @return true if c is a digit */ public static boolean isDigit(int c) { return ((c >= '0') && (c <= '9')); } /** * Returns a string where all occurences of <TT>oldCh</TT> have * been replaced with <TT>newCh</TT>. * This doesn't throw exceptions if bad values. */ public static String replaceAll(String s, char oldCh, char newCh) { int po = s.indexOf(oldCh); if (po < 0) return s; StringBuilder buffer = new StringBuilder(s); while (po >= 0) { buffer.setCharAt(po, newCh); po = s.indexOf(oldCh, po + 1); } return buffer.toString(); } /** * Convert a string to an int. * Leading or trailing spaces are automatically removed. * This accepts hexadecimal integers starting with "0x". * Leading 0's (e.g., 0012) are ignored; number is treated as decimal (not octal as Java would). * Floating point numbers are rounded. * This won't throw an exception if the number isn't formatted right. * To make a string from an int, use ""+i, Integer.toHexString, or Integer.toString(i,radix). * * @param s is the String representation of a number. * @return the int value from the String * (or Integer.MAX_VALUE if error). */ public static int parseInt(String s) { //*** XML.decodeEntities relies on leading 0's being ignored // and number treated as decimal (not octal) //quickly reject most non-numbers //This is a huge speed improvement when parsing ASCII data files // because Java is very slow at filling in the stack trace when an exception is thrown. if (s == null) return Integer.MAX_VALUE; s = s.trim(); if (s.length() == 0) return Integer.MAX_VALUE; char ch = s.charAt(0); if ((ch < '0' || ch > '9') && ch != '-' && ch != '+' && ch != '.') return Integer.MAX_VALUE; //try to parse hex or regular int try { if (s.startsWith("0x")) return Integer.parseInt(s.substring(2), 16); return Integer.parseInt(s); } catch (Exception e) { //falls through } //round from double? try { //2011-02-09 Bob Simons added to avoid Java hang bug. //But now, latest version of Java is fixed. //if (isDoubleTrouble(s)) return 0; return ErddapMath2.roundToInt(Double.parseDouble(s)); } catch (Exception e) { return Integer.MAX_VALUE; } } /** * Convert a string to a double. * Leading or trailing spaces are automatically removed. * This accepts hexadecimal integers starting with "0x". * Whole number starting with '0' (e.g., 012) is treated as decimal (not octal as Java would). * This won't throw an exception if the number isn't formatted right. * * @param s is the String representation of a number. * @return the double value from the String (a finite value, * Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, * or Double.NaN if error). */ public static double parseDouble(String s) { //quickly reject most non-numbers //This is a huge speed improvement when parsing ASCII data files // because Java is very slow at filling in the stack trace when an exception is thrown. if (s == null) return Double.NaN; s = s.trim(); if (s.length() == 0) return Double.NaN; char ch = s.charAt(0); if ((ch < '0' || ch > '9') && ch != '-' && ch != '+' && ch != '.') return Double.NaN; try { if (s.startsWith("0x")) return Integer.parseInt(s.substring(2), 16); //2011-02-09 Bob Simons added to avoid Java hang bug. //But now, latest version of Java is fixed. //if (isDoubleTrouble(s)) return 0; return Double.parseDouble(s); } catch (Exception e) { return Double.NaN; } } /** * This converts String representation of a long. * Leading or trailing spaces are automatically removed. * This *doesn't* round. So floating point values lead to Long.MAX_VALUE. * * @param s a valid String representation of a long value * @return a long (or Long.MAX_VALUE if trouble). */ public static long parseLong(String s) { //quickly reject most non-numbers //This is a huge speed improvement when parsing ASCII data files // because Java is very slow at filling in the stack trace when an exception is thrown. if (s == null) return Long.MAX_VALUE; s = s.trim(); if (s.length() == 0) return Long.MAX_VALUE; char ch = s.charAt(0); if ((ch < '0' || ch > '9') && ch != '-' && ch != '+') return Long.MAX_VALUE; try { if (s.startsWith("0x")) return Long.parseLong(s.substring(2), 16); return Long.parseLong(s); } catch (Exception e) { return Long.MAX_VALUE; } } /** * This converts a double to a rational number (m * 10^t). * This is similar to Math2.mantissa and Math2.intExponent, but works via string manipulation * to avoid roundoff problems (e.g., with 6.6260755e-24). * * @param d * @return int[2]: [0]=m, [1]=t. * (or {0, 0} if d=0, or {1, Integer.MAX_VALUE} if !finite(d)) */ public static int[] toRational(double d) { if (d == 0) return new int[]{0, 0}; if (!ErddapMath2.isFinite(d)) return new int[]{1, Integer.MAX_VALUE}; String s = "" + d; //-12.0 or 6.6260755E-24 //String2.log("\nd=" + d + "\ns=" + s); int ten = 0; //remove the e int epo = s.indexOf('E'); if (epo > 0) { ten = parseInt(s.substring(epo + 1)); s = s.substring(0, epo); //String2.log("remove E s=" + s + " ten=" + ten); } //remove .0; remove decimal point if (s.endsWith(".0")) s = s.substring(0, s.length() - 2); int dpo = s.indexOf('.'); if (dpo > 0) { ten -= s.length() - dpo - 1; s = s.substring(0, dpo) + s.substring(dpo + 1); //String2.log("remove . s=" + s + " ten=" + ten); } //convert s to long //need to lose some precision? long tl = parseLong(s); //String2.log("tl=" + tl + " s=" + s); while (Math.abs(tl) > 1000000000) { tl = Math.round(tl / 10.0); ten++; //String2.log("tl=" + tl + " ten=" + ten); } //remove trailing 0's while (tl != 0 && tl / 10 == tl / 10.0) { tl /= 10; ten++; //String2.log("remove 0 tl=" + tl + " ten=" + ten); } //add up to 3 0's? if (tl < 100000 && ten >= 1 && ten <= 3) { while (ten > 0) { tl *= 10; ten--; } } return new int[]{(int)tl, ten}; //safe since large values handled above } }