Java tutorial
/* ==================================================================== * * The ObjectStyle Group Software License, version 1.1 * ObjectStyle Group - http://objectstyle.org/ * * Copyright (c) 2002-2004, Andrei (Andrus) Adamchik and individual authors * of the software. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if any, * must include the following acknowlegement: * "This product includes software developed by independent contributors * and hosted on ObjectStyle Group web site (http://objectstyle.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "ObjectStyle Group" and "Cayenne" must not be used to endorse * or promote products derived from this software without prior written * permission. For written permission, email * "andrus at objectstyle dot org". * * 5. Products derived from this software may not be called "ObjectStyle" * or "Cayenne", nor may "ObjectStyle" or "Cayenne" appear in their * names without prior written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals and hosted on ObjectStyle Group web site. For more * information on the ObjectStyle Group, please see * <http://objectstyle.org/>. */ package org.objectstyle.cayenne.util; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.apache.commons.lang.builder.HashCodeBuilder; import org.apache.log4j.Logger; import org.apache.oro.text.perl.Perl5Util; import org.objectstyle.cayenne.CayenneException; import org.objectstyle.cayenne.CayenneRuntimeException; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; /** * Contains various unorganized static utility methods used across Cayenne. * * @author Andrei Adamchik */ public class Util { private static Logger logObj = Logger.getLogger(Util.class); private static final Perl5Util regexUtil = new Perl5Util(); /** * Reads file contents, returning it as a String, using System default * line separator. */ public static String stringFromFile(File file) throws IOException { return stringFromFile(file, System.getProperty("line.separator")); } /** * Reads file contents, returning it as a String, joining lines with provided separator. */ public static String stringFromFile(File file, String joinWith) throws IOException { StringBuffer buf = new StringBuffer(); BufferedReader in = new BufferedReader(new FileReader(file)); try { String line = null; while ((line = in.readLine()) != null) { buf.append(line).append(joinWith); } } finally { in.close(); } return buf.toString(); } /** * Copies file contents from source to destination. * Makes up for the lack of file copying utilities in Java */ public static boolean copy(File source, File destination) { BufferedInputStream fin = null; BufferedOutputStream fout = null; try { int bufSize = 8 * 1024; fin = new BufferedInputStream(new FileInputStream(source), bufSize); fout = new BufferedOutputStream(new FileOutputStream(destination), bufSize); copyPipe(fin, fout, bufSize); } catch (IOException ioex) { return false; } catch (SecurityException sx) { return false; } finally { if (fin != null) { try { fin.close(); } catch (IOException cioex) { } } if (fout != null) { try { fout.close(); } catch (IOException cioex) { } } } return true; } /** * Save URL contents to a file. */ public static boolean copy(URL from, File to) { BufferedInputStream urlin = null; BufferedOutputStream fout = null; try { int bufSize = 8 * 1024; urlin = new BufferedInputStream(from.openConnection().getInputStream(), bufSize); fout = new BufferedOutputStream(new FileOutputStream(to), bufSize); copyPipe(urlin, fout, bufSize); } catch (IOException ioex) { return false; } catch (SecurityException sx) { return false; } finally { if (urlin != null) { try { urlin.close(); } catch (IOException cioex) { } } if (fout != null) { try { fout.close(); } catch (IOException cioex) { } } } return true; } /** * Reads data from the input and writes it to the output, * until the end of the input stream. * * @param in * @param out * @param bufSizeHint * @throws IOException */ public static void copyPipe(InputStream in, OutputStream out, int bufSizeHint) throws IOException { int read = -1; byte[] buf = new byte[bufSizeHint]; while ((read = in.read(buf, 0, bufSizeHint)) >= 0) { out.write(buf, 0, read); } out.flush(); } /** * Deletes a file or directory, allowing recursive directory * deletion. This is an improved version of File.delete() method. */ public static boolean delete(String filePath, boolean recursive) { File file = new File(filePath); if (!file.exists()) { return true; } if (!recursive || !file.isDirectory()) return file.delete(); String[] list = file.list(); for (int i = 0; i < list.length; i++) { if (!delete(filePath + File.separator + list[i], true)) return false; } return file.delete(); } /** * Replaces all backslashes "\" with forward slashes "/". Convenience * method to convert path Strings to URI format. */ public static String substBackslashes(String string) { if (string == null) { return null; } return regexUtil.match("/\\\\/", string) ? regexUtil.substitute("s/\\\\/\\//g", string) : string; } /** * Looks up and returns the root cause of an exception. If none is found, * returns supplied Throwable object unchanged. If root is found, * recursively "unwraps" it, and returns the result to the user. */ public static Throwable unwindException(Throwable th) { if (th instanceof CayenneException) { CayenneException e = (CayenneException) th; if (e.getCause() != null) { return unwindException(e.getCause()); } } else if (th instanceof CayenneRuntimeException) { CayenneRuntimeException e = (CayenneRuntimeException) th; if (e.getCause() != null) { return unwindException(e.getCause()); } } else if (th instanceof InvocationTargetException) { InvocationTargetException e = (InvocationTargetException) th; if (e.getTargetException() != null) { return unwindException(e.getTargetException()); } } else if (th instanceof SAXException) { SAXException sax = (SAXException) th; if (sax.getException() != null) { return unwindException(sax.getException()); } } else if (th instanceof SQLException) { SQLException sql = (SQLException) th; if (sql.getNextException() != null) { return unwindException(sql.getNextException()); } } return th; } /** * Compares two objects similar to "Object.equals(Object)". * Unlike Object.equals(..), this method doesn't throw an exception * if any of the two objects is null. */ public static boolean nullSafeEquals(Object obj1, Object obj2) { if (obj1 == null && obj2 == null) return true; else if (obj1 != null) return obj1.equals(obj2); else return false; } /** * Compares two objects similar to "Comparable.compareTo(Object)". * Unlike Comparable.compareTo(..), this method doesn't throw an exception * if any of the two objects is null. * * @since 1.1 */ public static int nullSafeCompare(boolean nullsFirst, Comparable o1, Object o2) { if (o1 == null && o2 == null) { return 0; } else if (o1 == null) { return nullsFirst ? -1 : 1; } else if (o2 == null) { return nullsFirst ? 1 : -1; } else { return o1.compareTo(o2); } } /** * Returns true, if the String is null or an empty string. */ public static boolean isEmptyString(String string) { return string == null || string.length() == 0; } /** * Creates Serializable object copy using serialization/deserialization. */ public static Object cloneViaSerialization(Serializable obj) throws Exception { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(bytes); out.writeObject(obj); out.close(); byte[] data = bytes.toByteArray(); if (logObj.isDebugEnabled()) { logObj.debug("Serialized size (bytes) of " + obj.getClass().getName() + ": " + data.length); } ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(data)); Object objCopy = in.readObject(); in.close(); return objCopy; } /** * Creates an XMLReader with default feature set. Note that all Cayenne * internal XML parsers should probably use XMLReader obtained via this * method for consistency sake, and can customize feature sets as needed. */ public static XMLReader createXmlReader() throws SAXException, ParserConfigurationException { SAXParserFactory spf = SAXParserFactory.newInstance(); // Create a JAXP SAXParser SAXParser saxParser = spf.newSAXParser(); // Get the encapsulated SAX XMLReader XMLReader reader = saxParser.getXMLReader(); // set default features reader.setFeature("http://xml.org/sax/features/namespaces", true); return reader; } /** * Returns package name for the Java class as a path separated with forward slash ("/"). * Method is used to lookup resources that are located in package subdirectories. * For example, a String "a/b/c" will be returned for class name "a.b.c.ClassName". */ public static String getPackagePath(String className) { if (regexUtil.match("/\\./", className)) { String path = regexUtil.substitute("s/\\./\\//g", className); return path.substring(0, path.lastIndexOf("/")); } else { return ""; } } /** * Extracts extension from the file name. */ public static String extractFileExtension(String fileName) { int dotInd = fileName.lastIndexOf('.'); // if dot is in the first position, // we are dealing with a hidden file rather than an extension return (dotInd > 0 && dotInd < fileName.length()) ? fileName.substring(dotInd + 1) : null; } /** * Strips extension from the file name. */ public static String stripFileExtension(String fileName) { int dotInd = fileName.lastIndexOf('.'); // if dot is in the first position, // we are dealing with a hidden file rather than an extension return (dotInd > 0) ? fileName.substring(0, dotInd) : fileName; } /** * Encodes a string so that it can be used as an attribute value in an XML document. * Will do conversion of the greater/less signs, quotes and ampersands. */ public static String encodeXmlAttribute(String str) { if (str == null) return null; int len = str.length(); if (len == 0) return str; StringBuffer encoded = new StringBuffer(); for (int i = 0; i < len; i++) { char c = str.charAt(i); if (c == '<') encoded.append("<"); else if (c == '\"') encoded.append("""); else if (c == '>') encoded.append(">"); else if (c == '\'') encoded.append("'"); else if (c == '&') encoded.append("&"); else encoded.append(c); } return encoded.toString(); } /** * Trims long strings substituting middle part with "...". * * @param str String to trim. * @param maxLength maximum allowable length. Must be at least 5, * or an IllegalArgumentException is thrown. * * @return String */ public static String prettyTrim(String str, int maxLength) { if (maxLength < 5) { throw new IllegalArgumentException( "Algorithm for 'prettyTrim' works only with length >= 5. " + "Supplied length is " + maxLength); } if (str == null || str.length() <= maxLength) { return str; } // find a section to cut off int len = maxLength - 3; int startLen = len / 2; int endLen = len - startLen; return str.substring(0, startLen) + "..." + str.substring(str.length() - endLen); } /** * Returns a sorted iterator from an unsorted one. * Use this method as a last resort, since it is * much less efficient then just sorting a collection * that backs the original iterator. */ public static Iterator sortedIterator(Iterator it, Comparator comparator) { List list = new ArrayList(); while (it.hasNext()) { list.add(it.next()); } Collections.sort(list, comparator); return list.iterator(); } /** * Builds a hashCode of Collection. */ public static int hashCode(Collection c) { HashCodeBuilder builder = new HashCodeBuilder(); for (Iterator i = c.iterator(); i.hasNext();) builder.append(i.next()); return builder.toHashCode(); } /** * Converts a SQL-style pattern to a valid Perl regular expression. E.g.: * * <p> * <code>"billing_%"</code> will become <code>/^billing_.*$/</code> * <p> * <code>"user?"</code> will become <code>/^user.?$/</code> * * @since 1.0.6 */ public static String sqlPatternToRegex(String pattern, boolean ignoreCase) { if (pattern == null) { throw new NullPointerException("Null pattern."); } if (pattern.length() == 0) { throw new IllegalArgumentException("Empty pattern."); } StringBuffer buffer = new StringBuffer(); // convert * into regex syntax // e.g. abc*x becomes /^abc.*x$/ // or abc?x becomes /^abc.?x$/ buffer.append("/^"); for (int j = 0; j < pattern.length(); j++) { char nextChar = pattern.charAt(j); if (nextChar == '%') { nextChar = '*'; } if (nextChar == '*' || nextChar == '?') { buffer.append('.'); } // escape special chars else if (nextChar == '.' || nextChar == '/' || nextChar == '$' || nextChar == '^') { buffer.append('\\'); } buffer.append(nextChar); } buffer.append("$/"); if (ignoreCase) { buffer.append('i'); } String finalPattern = buffer.toString(); // test the pattern try { regexUtil.match(finalPattern, "abc_123"); } catch (Exception e) { throw new IllegalArgumentException("Error converting pattern: " + e.getLocalizedMessage()); } return finalPattern; } }