package com.jbidwatcher.util; import com.jbidwatcher.util.config.JConfig; import java.io.*; import java.net.MalformedURLException; import java.net.URL; import java.text.SimpleDateFormat; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; public class StringTools { private static final int YEAR_BASE = 1990; private static GregorianCalendar sMidpointDate = new GregorianCalendar(YEAR_BASE, Calendar.JANUARY, 1); private static final int HIGHBIT_ASCII = 0x80; public static String decode(String original, String charset) { if(charset == null || charset.equalsIgnoreCase("UTF-8")) return original; try { return new String(original.getBytes(), charset); } catch (UnsupportedEncodingException ignore) { return original; } } public static String stripHigh(String inString, String fmtString) { char[] stripOut = new char[inString.length()]; inString.getChars(0, inString.length(), stripOut, 0); char[] format = new char[fmtString.length()]; fmtString.getChars(0, fmtString.length(), format, 0); String legalString = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-:,"; for(int i=0; i<stripOut.length; i++) { if(stripOut[i] > HIGHBIT_ASCII) stripOut[i] = ' '; if(i < format.length) { if( (format[i] == ' ') && (legalString.indexOf(stripOut[i]) == -1)) { stripOut[i] = ' '; } } } return new String(stripOut); } /** * @brief Convert an auction item description URL in String format into a java.net.URL. * This is a brutally simple utility function, so it's static, and should be referred * to via the AuctionServer class directly. * * @param siteAddress - The string URL to convert into a 'real' URL on the given site. * * @return - A java.net.URL that points to what we consider the 'official' item URL on the site. */ public static URL getURLFromString(String siteAddress) { URL auctionURL=null; try { auctionURL = JConfig.getURL(siteAddress); } catch(MalformedURLException e) { JConfig.log().handleException("getURLFromString failed on " + siteAddress, e); } return auctionURL; } /** * @brief Determine if the provided string is all digits, a commonly * needed check for auction ids. * * @param checkVal - The string to check for digits. * * @return - true if all characters in checkVal are digits, false * otherwise or if the string is empty. */ public static boolean isNumberOnly(String checkVal) { int strLength = checkVal.length(); if(strLength == 0) return(false); for(int i = 0; i<strLength; i++) { if(!Character.isDigit(checkVal.charAt(i))) return(false); } return(true); } /** * @param sb - The stringbuffer to delete from. * @param desc_start - The start point to delete from. * @param desc_end - The endpoint to delete to. * @return - true if a deletion occurred, false if the parameters * were invalid in any way. * @brief Delete characters from a range within a stringbuffer, safely. */ private static boolean deleteRange(StringBuffer sb, int desc_start, int desc_end) { if (desc_start < desc_end && desc_start != -1 && desc_end != -1) { sb.delete(desc_start, desc_end); return true; } return false; } /** * Simple utility to delete from a stringbuffer starting * from a string, until the next following string. * * @param sb - The buffer to delete from. * @param startStr - The string to delete starting at. * @param endStr - The string to delete up until. * * @return - true if the delete happened, false otherwise. */ public static boolean deleteRegexPairs(StringBuffer sb, String startStr, String endStr) { String expr = startStr + ".*?" + endStr; Matcher start = Pattern.compile(expr, Pattern.CASE_INSENSITIVE).matcher(sb); List<Pair<Integer, Integer>> startEndPairs = new LinkedList<Pair<Integer,Integer>>(); while(start.find()) { startEndPairs.add(new Pair<Integer, Integer>(start.start(), start.end())); } Collections.reverse(startEndPairs); for(Pair<Integer, Integer> matchedPair : startEndPairs) { deleteRange(sb, matchedPair.getFirst(), matchedPair.getLast()); } return !startEndPairs.isEmpty(); } public static ZoneDate figureDate(String rawTime, String siteDateFormat) { return figureDate(rawTime, siteDateFormat, true, true); } /** * @param endTime - The string containing the human-readable time to be parsed. * @param siteDateFormat - The format describing the human-readable time. * @param strip_high - Whether or not to strip high characters. * @param ignore_badformat - Return null if the date is in a bad format. * * @return - The date/time in Date format that was represented by * the human readable date string. * @brief Use the date parsing code to figure out the time an * auction ends (also used to parse the 'official' time) from the * web page. */ public static ZoneDate figureDate(String endTime, String siteDateFormat, boolean strip_high, boolean ignore_badformat) { if(endTime != null) endTime = endTime.replace("MEZ", "MET"); String endTimeFmt = endTime; SimpleDateFormat sdf = new SimpleDateFormat(siteDateFormat, Locale.US); sdf.set2DigitYearStart(sMidpointDate.getTime()); if (endTime == null) return sNullZoneDate; if (strip_high) { endTimeFmt = StringTools.stripHigh(endTime, siteDateFormat); } return parseDateZone(sdf, endTimeFmt, ignore_badformat); } private static final ZoneDate sNullZoneDate = new ZoneDate(null, null); private static ZoneDate parseDateZone(SimpleDateFormat sdf, String endTimeFmt, boolean ignore_badformat) { Date endingDate; TimeZone tz; try { endingDate = sdf.parse(endTimeFmt); tz = sdf.getCalendar().getTimeZone(); } catch (java.text.ParseException e) { if(!ignore_badformat) { JConfig.log().handleException("Error parsing date (" + endTimeFmt + "), setting to completed.", e); endingDate = new Date(); } else { endingDate = null; } tz = null; } return (new ZoneDate(tz, endingDate)); } public static String cat(URL loadFrom) { if(loadFrom == null) return null; try { InputStream is = loadFrom.openStream(); return cat(is); } catch (IOException e) { JConfig.log().handleException("Failed to load " + loadFrom.toString(), e); } return null; } public static String cat(InputStream is) throws IOException { byte[] buf = new byte[65536]; int offset = 0; int bytes_read; while( (bytes_read = is.read(buf, offset, 8192)) != -1) { offset += bytes_read; } if(bytes_read == buf.length) { JConfig.log().logDebug("File to load is exactly 64K or larger than 64K. This method does not support that."); return null; } return new String(buf, 0, offset); } public static String cat(String filename) { File fp = new File(filename); byte[][] buffer = new byte[1][]; cat(fp, buffer); return new String(buffer[0]); } public static void cat(File fp, byte[][] buf) { try { buf[0] = new byte[(int)fp.length()]; FileInputStream fis = new FileInputStream(fp); int read = fis.read(buf[0], 0, (int)fp.length()); if(read != fp.length()) JConfig.log().logDebug("Couldn't read any data from " + fp.getName()); fis.close(); } catch(IOException e) { JConfig.log().handleException("Can't read file " + fp.getName(), e); } } public static String nullSafe(String s) { return (s == null) ? "" : s; } public static String comma(List<?> list) { String rval = ""; if (list == null || list.isEmpty()) return rval; boolean first = true; for (Object o : list) { if (!first) rval += ", "; else first = false; rval += o.toString(); } return rval; } public static boolean startsWithIgnoreCase(String base, String match) { return base.regionMatches(true, 0, match, 0, match.length()); } }