Frames | No Frames |
1: /* ======================================================================== 2: * JCommon : a free general purpose class library for the Java(tm) platform 3: * ======================================================================== 4: * 5: * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors. 6: * 7: * Project Info: http://www.jfree.org/jcommon/index.html 8: * 9: * This library is free software; you can redistribute it and/or modify it 10: * under the terms of the GNU Lesser General Public License as published by 11: * the Free Software Foundation; either version 2.1 of the License, or 12: * (at your option) any later version. 13: * 14: * This library is distributed in the hope that it will be useful, but 15: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 16: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 17: * License for more details. 18: * 19: * You should have received a copy of the GNU Lesser General Public 20: * License along with this library; if not, write to the Free Software 21: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 22: * USA. 23: * 24: * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 25: * in the United States and other countries.] 26: * 27: * ------------ 28: * IOUtils.java 29: * ------------ 30: * (C)opyright 2002-2004, by Thomas Morgner and Contributors. 31: * 32: * Original Author: Thomas Morgner; 33: * Contributor(s): David Gilbert (for Object Refinery Limited); 34: * 35: * $Id: IOUtils.java,v 1.6 2005/11/03 09:55:27 mungady Exp $ 36: * 37: * Changes 38: * ------- 39: * 26-Jan-2003 : Initial version 40: * 23-Feb-2003 : Documentation 41: * 25-Feb-2003 : Fixed Checkstyle issues (DG); 42: * 29-Apr-2003 : Moved to jcommon 43: * 04-Jan-2004 : Fixed JDK 1.2.2 issues with createRelativeURL; 44: * added support for query strings within these urls (TM); 45: */ 46: 47: package org.jfree.io; 48: 49: import java.io.File; 50: import java.io.IOException; 51: import java.io.InputStream; 52: import java.io.OutputStream; 53: import java.io.Reader; 54: import java.io.Writer; 55: import java.net.URL; 56: import java.util.ArrayList; 57: import java.util.Iterator; 58: import java.util.List; 59: import java.util.StringTokenizer; 60: 61: /** 62: * The IOUtils provide some IO related helper methods. 63: * 64: * @author Thomas Morgner. 65: */ 66: public class IOUtils { 67: 68: /** the singleton instance of the utility package. */ 69: private static IOUtils instance; 70: 71: /** 72: * DefaultConstructor. 73: */ 74: private IOUtils() { 75: } 76: 77: /** 78: * Gets the singleton instance of the utility package. 79: * 80: * @return the singleton instance. 81: */ 82: public static IOUtils getInstance() { 83: if (instance == null) { 84: instance = new IOUtils(); 85: } 86: return instance; 87: } 88: 89: /** 90: * Checks, whether the URL uses a file based protocol. 91: * 92: * @param url the url. 93: * @return true, if the url is file based. 94: */ 95: private boolean isFileStyleProtocol(final URL url) { 96: if (url.getProtocol().equals("http")) { 97: return true; 98: } 99: if (url.getProtocol().equals("https")) { 100: return true; 101: } 102: if (url.getProtocol().equals("ftp")) { 103: return true; 104: } 105: if (url.getProtocol().equals("file")) { 106: return true; 107: } 108: if (url.getProtocol().equals("jar")) { 109: return true; 110: } 111: return false; 112: } 113: 114: /** 115: * Parses the given name and returns the name elements as List of Strings. 116: * 117: * @param name the name, that should be parsed. 118: * @return the parsed name. 119: */ 120: private List parseName(final String name) { 121: final ArrayList list = new ArrayList(); 122: final StringTokenizer strTok = new StringTokenizer(name, "/"); 123: while (strTok.hasMoreElements()) { 124: final String s = (String) strTok.nextElement(); 125: if (s.length() != 0) { 126: list.add(s); 127: } 128: } 129: return list; 130: } 131: 132: /** 133: * Transforms the name list back into a single string, separated with "/". 134: * 135: * @param name the name list. 136: * @param query the (optional) query for the URL. 137: * @return the constructed name. 138: */ 139: private String formatName(final List name, final String query) { 140: final StringBuffer b = new StringBuffer(); 141: final Iterator it = name.iterator(); 142: while (it.hasNext()) { 143: b.append(it.next()); 144: if (it.hasNext()) { 145: b.append("/"); 146: } 147: } 148: if (query != null) { 149: b.append('?'); 150: b.append(query); 151: } 152: return b.toString(); 153: } 154: 155: /** 156: * Compares both name lists, and returns the last common index shared 157: * between the two lists. 158: * 159: * @param baseName the name created using the base url. 160: * @param urlName the target url name. 161: * @return the number of shared elements. 162: */ 163: private int startsWithUntil(final List baseName, final List urlName) { 164: final int minIdx = Math.min(urlName.size(), baseName.size()); 165: for (int i = 0; i < minIdx; i++) { 166: final String baseToken = (String) baseName.get(i); 167: final String urlToken = (String) urlName.get(i); 168: if (!baseToken.equals(urlToken)) { 169: return i; 170: } 171: } 172: return minIdx; 173: } 174: 175: /** 176: * Checks, whether the URL points to the same service. A service is equal 177: * if the protocol, host and port are equal. 178: * 179: * @param url a url 180: * @param baseUrl an other url, that should be compared. 181: * @return true, if the urls point to the same host and port and use the 182: * same protocol, false otherwise. 183: */ 184: private boolean isSameService(final URL url, final URL baseUrl) { 185: if (!url.getProtocol().equals(baseUrl.getProtocol())) { 186: return false; 187: } 188: if (!url.getHost().equals(baseUrl.getHost())) { 189: return false; 190: } 191: if (url.getPort() != baseUrl.getPort()) { 192: return false; 193: } 194: return true; 195: } 196: 197: /** 198: * Creates a relative url by stripping the common parts of the the url. 199: * 200: * @param url the to be stripped url 201: * @param baseURL the base url, to which the <code>url</code> is relative 202: * to. 203: * @return the relative url, or the url unchanged, if there is no relation 204: * beween both URLs. 205: */ 206: public String createRelativeURL(final URL url, final URL baseURL) { 207: if (url == null) { 208: throw new NullPointerException("content url must not be null."); 209: } 210: if (baseURL == null) { 211: throw new NullPointerException("baseURL must not be null."); 212: } 213: if (isFileStyleProtocol(url) && isSameService(url, baseURL)) { 214: 215: // If the URL contains a query, ignore that URL; do not 216: // attemp to modify it... 217: final List urlName = parseName(getPath(url)); 218: final List baseName = parseName(getPath(baseURL)); 219: final String query = getQuery(url); 220: 221: if (!isPath(baseURL)) { 222: baseName.remove(baseName.size() - 1); 223: } 224: 225: // if both urls are identical, then return the plain file name... 226: if (url.equals(baseURL)) { 227: return (String) urlName.get(urlName.size() - 1); 228: } 229: 230: int commonIndex = startsWithUntil(urlName, baseName); 231: if (commonIndex == 0) { 232: return url.toExternalForm(); 233: } 234: 235: if (commonIndex == urlName.size()) { 236: // correct the base index if there is some weird mapping 237: // detected, 238: // fi. the file url is fully included in the base url: 239: // 240: // base: /file/test/funnybase 241: // file: /file/test 242: // 243: // this could be a valid configuration whereever virtual 244: // mappings are allowed. 245: commonIndex -= 1; 246: } 247: 248: final ArrayList retval = new ArrayList(); 249: if (baseName.size() >= urlName.size()) { 250: final int levels = baseName.size() - commonIndex; 251: for (int i = 0; i < levels; i++) { 252: retval.add(".."); 253: } 254: } 255: 256: retval.addAll(urlName.subList(commonIndex, urlName.size())); 257: return formatName(retval, query); 258: } 259: return url.toExternalForm(); 260: } 261: 262: /** 263: * Returns <code>true</code> if the URL represents a path, and 264: * <code>false</code> otherwise. 265: * 266: * @param baseURL the URL. 267: * 268: * @return A boolean. 269: */ 270: private boolean isPath(final URL baseURL) { 271: if (getPath(baseURL).endsWith("/")) { 272: return true; 273: } 274: else if (baseURL.getProtocol().equals("file")) { 275: final File f = new File(getPath(baseURL)); 276: try { 277: if (f.isDirectory()) { 278: return true; 279: } 280: } 281: catch (SecurityException se) { 282: // ignored ... 283: } 284: } 285: return false; 286: } 287: 288: /** 289: * Implements the JDK 1.3 method URL.getPath(). The path is defined 290: * as URL.getFile() minus the (optional) query. 291: * 292: * @param url the URL 293: * @return the path 294: */ 295: private String getQuery (final URL url) { 296: final String file = url.getFile(); 297: final int queryIndex = file.indexOf('?'); 298: if (queryIndex == -1) { 299: return null; 300: } 301: return file.substring(queryIndex + 1); 302: } 303: 304: /** 305: * Implements the JDK 1.3 method URL.getPath(). The path is defined 306: * as URL.getFile() minus the (optional) query. 307: * 308: * @param url the URL 309: * @return the path 310: */ 311: private String getPath (final URL url) { 312: final String file = url.getFile(); 313: final int queryIndex = file.indexOf('?'); 314: if (queryIndex == -1) { 315: return file; 316: } 317: return file.substring(0, queryIndex); 318: } 319: 320: /** 321: * Copies the InputStream into the OutputStream, until the end of the stream 322: * has been reached. This method uses a buffer of 4096 kbyte. 323: * 324: * @param in the inputstream from which to read. 325: * @param out the outputstream where the data is written to. 326: * @throws IOException if a IOError occurs. 327: */ 328: public void copyStreams(final InputStream in, final OutputStream out) 329: throws IOException { 330: copyStreams(in, out, 4096); 331: } 332: 333: /** 334: * Copies the InputStream into the OutputStream, until the end of the stream 335: * has been reached. 336: * 337: * @param in the inputstream from which to read. 338: * @param out the outputstream where the data is written to. 339: * @param buffersize the buffer size. 340: * @throws IOException if a IOError occurs. 341: */ 342: public void copyStreams(final InputStream in, final OutputStream out, 343: final int buffersize) throws IOException { 344: // create a 4kbyte buffer to read the file 345: final byte[] bytes = new byte[buffersize]; 346: 347: // the input stream does not supply accurate available() data 348: // the zip entry does not know the size of the data 349: int bytesRead = in.read(bytes); 350: while (bytesRead > -1) { 351: out.write(bytes, 0, bytesRead); 352: bytesRead = in.read(bytes); 353: } 354: } 355: 356: /** 357: * Copies the contents of the Reader into the Writer, until the end of the 358: * stream has been reached. This method uses a buffer of 4096 kbyte. 359: * 360: * @param in the reader from which to read. 361: * @param out the writer where the data is written to. 362: * @throws IOException if a IOError occurs. 363: */ 364: public void copyWriter(final Reader in, final Writer out) 365: throws IOException { 366: copyWriter(in, out, 4096); 367: } 368: 369: /** 370: * Copies the contents of the Reader into the Writer, until the end of the 371: * stream has been reached. 372: * 373: * @param in the reader from which to read. 374: * @param out the writer where the data is written to. 375: * @param buffersize the buffer size. 376: * 377: * @throws IOException if a IOError occurs. 378: */ 379: public void copyWriter(final Reader in, final Writer out, 380: final int buffersize) 381: throws IOException { 382: // create a 4kbyte buffer to read the file 383: final char[] bytes = new char[buffersize]; 384: 385: // the input stream does not supply accurate available() data 386: // the zip entry does not know the size of the data 387: int bytesRead = in.read(bytes); 388: while (bytesRead > -1) { 389: out.write(bytes, 0, bytesRead); 390: bytesRead = in.read(bytes); 391: } 392: } 393: 394: /** 395: * Extracts the file name from the URL. 396: * 397: * @param url the url. 398: * @return the extracted filename. 399: */ 400: public String getFileName(final URL url) { 401: final String file = url.getFile(); 402: final int last = file.lastIndexOf("/"); 403: if (last < 0) { 404: return file; 405: } 406: return file.substring(last); 407: } 408: 409: /** 410: * Removes the file extension from the given file name. 411: * 412: * @param file the file name. 413: * @return the file name without the file extension. 414: */ 415: public String stripFileExtension(final String file) { 416: final int idx = file.lastIndexOf("."); 417: // handles unix hidden files and files without an extension. 418: if (idx < 1) { 419: return file; 420: } 421: return file.substring(0, idx); 422: } 423: 424: /** 425: * Returns the file extension of the given file name. 426: * The returned value will contain the dot. 427: * 428: * @param file the file name. 429: * @return the file extension. 430: */ 431: public String getFileExtension(final String file) { 432: final int idx = file.lastIndexOf("."); 433: // handles unix hidden files and files without an extension. 434: if (idx < 1) { 435: return ""; 436: } 437: return file.substring(idx); 438: } 439: 440: /** 441: * Checks, whether the child directory is a subdirectory of the base 442: * directory. 443: * 444: * @param base the base directory. 445: * @param child the suspected child directory. 446: * @return true, if the child is a subdirectory of the base directory. 447: * @throws IOException if an IOError occured during the test. 448: */ 449: public boolean isSubDirectory(File base, File child) 450: throws IOException { 451: base = base.getCanonicalFile(); 452: child = child.getCanonicalFile(); 453: 454: File parentFile = child; 455: while (parentFile != null) { 456: if (base.equals(parentFile)) { 457: return true; 458: } 459: parentFile = parentFile.getParentFile(); 460: } 461: return false; 462: } 463: }