0001: /* *****************************************************************************
0002: * FileUtils.java
0003: * ****************************************************************************/
0004:
0005: /* J_LZ_COPYRIGHT_BEGIN *******************************************************
0006: * Copyright 2001-2007 Laszlo Systems, Inc. All Rights Reserved. *
0007: * Use is subject to license terms. *
0008: * J_LZ_COPYRIGHT_END *********************************************************/
0009:
0010: package org.openlaszlo.utils;
0011:
0012: import org.openlaszlo.server.LPS;
0013:
0014: import java.io.File;
0015: import java.io.RandomAccessFile;
0016: import java.io.IOException;
0017: import java.io.FileNotFoundException;
0018: import java.io.FilenameFilter;
0019: import java.io.FileInputStream;
0020: import java.io.InputStream;
0021: import java.io.OutputStream;
0022: import java.io.FileOutputStream;
0023: import java.io.FilterOutputStream;
0024: import java.io.FilterInputStream;
0025: import java.io.ByteArrayOutputStream;
0026: import java.io.PushbackInputStream;
0027:
0028: import java.io.ByteArrayInputStream;
0029:
0030: import java.io.Writer;
0031: import java.io.Writer;
0032: import java.io.Reader;
0033: import java.io.StringWriter;
0034: import java.util.*;
0035: import java.util.zip.*;
0036:
0037: import org.apache.log4j.*;
0038: import org.apache.oro.text.regex.*;
0039: import java.io.InputStreamReader;
0040: import java.io.BufferedInputStream;
0041:
0042: // A dir is absolute if it begins with "" (the empty string to
0043: // the left of the initial '/'), or a drive letter.
0044: class AbsolutePathnameTester {
0045: static boolean test(List dirs) {
0046: if (!dirs.isEmpty()) {
0047: String dir = (String) dirs.get(0);
0048: // Add '/' since c:/ will be split to c:, which
0049: // isn't absolute. Also turns "" into "/" which
0050: // is absolute, so the UNIX case can share this
0051: // test.
0052: return new File(dir + "/").isAbsolute();
0053: }
0054: return false;
0055: }
0056: }
0057:
0058: /**
0059: * A utility class containing file utility functions.
0060: *
0061: * @author Oliver Steele
0062: * @author Eric Bloch
0063: */
0064: public abstract class FileUtils {
0065:
0066: private static Logger mLogger = Logger.getLogger(FileUtils.class);
0067:
0068: // TODO: [2003-09-23 bloch] could build out the encoder a bit more
0069: public static final String GZIP = "gzip";
0070: public static final String DEFLATE = "deflate";
0071: public static final int GZIP_HEADER_LENGTH = 10;
0072: public static final int MIN_GZIP_PEEK = 2;
0073: public static final int MIN_SWF_PEEK = 9;
0074:
0075: public static int BUFFER_SIZE = 10240;
0076: public static int THROTTLE = 0;
0077:
0078: static {
0079: try {
0080: String throttle = LPS.getProperty("lps.throttle");
0081: BUFFER_SIZE = Integer.parseInt(LPS.getProperty(
0082: "buffer.size", "10240"));
0083: if (throttle != null) {
0084: THROTTLE = Integer.parseInt(throttle);
0085: BUFFER_SIZE = 1024;
0086: mLogger.info("throttle " + throttle);
0087: }
0088:
0089: } catch (RuntimeException e) {
0090: mLogger.error("Exception initializing FileUtils", e);
0091: } catch (Exception e) {
0092: mLogger.error("Exception initializing FileUtils", e);
0093: }
0094: }
0095:
0096: /** Returns the contents of <var>file</var> as a byte[].
0097: * @param file a File
0098: * @return a byte[]
0099: * @throws IOException if an error occurs
0100: */
0101: public static byte[] readFileBytes(File file) throws IOException {
0102: java.io.InputStream istr = new java.io.FileInputStream(file);
0103: byte bytes[] = new byte[istr.available()];
0104: istr.read(bytes);
0105: istr.close();
0106: return bytes;
0107: }
0108:
0109: /** Returns the contents of <var>file</var> as a String, using UTF-8 character encoding <var>encoding</var>.
0110: * @param file a File
0111: * @return a String
0112: * @throws IOException if an error occurs
0113: */
0114:
0115: public static String readFileString(File file) throws IOException {
0116: byte data[] = readFileBytes(file);
0117: return new String(data, "UTF-8");
0118: }
0119:
0120: /** Returns the contents of <var>file</var> as a String, using character encoding <var>encoding</var>.
0121: * @param file a File
0122: * @param defaultEncoding a character set encoding name
0123: * @return a String
0124: * @throws IOException if an error occurs
0125: */
0126:
0127: public static String readFileString(File file,
0128: String defaultEncoding) throws IOException {
0129: Reader reader = makeXMLReaderForFile(file.getAbsolutePath(),
0130: defaultEncoding);
0131: StringWriter s = new StringWriter();
0132: send(reader, s);
0133: reader.close();
0134: return s.toString();
0135: }
0136:
0137: private static final Pattern pattern;
0138: static {
0139: Perl5Compiler compiler = new Perl5Compiler();
0140: Pattern tmp = null;
0141: try {
0142: tmp = compiler
0143: .compile(
0144: "[^<]*\\s*<[?]xml\\s+[^>]*encoding=[\"'](.*)['\"][^>]*?>",
0145: Perl5Compiler.READ_ONLY_MASK);
0146: } catch (MalformedPatternException failed) {
0147: System.err.println(failed);
0148: System.exit(0);
0149: }
0150: pattern = tmp;
0151: }
0152:
0153: /** Attempt to deduce the encoding of an XML file, by looking for the "encoding" attribute in the
0154: * XML declaration.
0155: * Default is to return "UTF-8"
0156: * @param pathname a filename
0157: * @return the encoding name
0158: * @throws IOException if an error occurs
0159: */
0160: public static Reader getXMLEncodingFromFile(InputStream input,
0161: String defaultEncoding) throws IOException {
0162: ByteArrayOutputStream bout = new ByteArrayOutputStream();
0163: send(input, bout);
0164: Perl5Matcher matcher = new Perl5Matcher();
0165:
0166: byte[] array = bout.toByteArray();
0167: // We will ignore the byte order mark encoding for now,
0168: // hopefully no one is going to be using UTF16. I don't want
0169: // to deal right now with the case where the XML encoding
0170: // directive conflicts with the byte order mark.
0171: int skip = stripByteOrderMark(array);
0172: ByteArrayInputStream bais = new ByteArrayInputStream(array,
0173: skip, array.length);
0174:
0175: if (matcher.contains(new String(array, 0, Math.min(1024,
0176: array.length)), pattern)) {
0177: MatchResult result = matcher.getMatch();
0178: String encoding = result.group(1);
0179: return new InputStreamReader(bais, encoding);
0180: } else {
0181: return new InputStreamReader(bais, defaultEncoding);
0182: }
0183: }
0184:
0185: /**
0186: * Set up a reader for an XML file with the correct charset encoding, and strip
0187: * out any Unicode Byte Order Mark if there is one present. We need to scan the file
0188: * once to try to parse the charset encoding in the XML declaration.
0189: * @param pathname the name of a file to open
0190: * @return reader stream for the file
0191: */
0192: public static Reader makeXMLReaderForFile(String pathname,
0193: String defaultEncoding) throws IOException {
0194: InputStream ifs = new BufferedInputStream(
0195: new java.io.FileInputStream(pathname));
0196: if (pathname.endsWith(".lzo")) {
0197: ifs = new java.util.zip.GZIPInputStream(ifs);
0198: }
0199: return getXMLEncodingFromFile(ifs, defaultEncoding);
0200: }
0201:
0202: /** Read a (pushback-able) byte input stream looking for some form of
0203: Unicode Byte Order Mark Defaults. If found, strip it out.
0204: * @param pbis a byte input stream
0205: * @return encoding name, defaults to null if we have no byte order mark
0206: */
0207: public static String stripByteOrderMark(PushbackInputStream pbis)
0208: throws IOException {
0209: // We need to peek at the stream and if the first three chars
0210: // are a UTF-8 or UTF-16 encoded BOM (byte order mark) we will
0211: // discard them.
0212: int c1 = pbis.read();
0213: int c2 = pbis.read();
0214: int c3 = pbis.read();
0215: if (c1 == 0xFF & c2 == 0xFE) {
0216: // UTF16 Big Endian BOM
0217: // discard the first two chars
0218: pbis.unread(c3);
0219: return "UTF-16BE";
0220: } else if (c1 == 0xFE & c2 == 0xFF) {
0221: // UTF16 Little Endian BOM
0222: // discard the first two chars
0223: pbis.unread(c3);
0224: return "UTF-16LE";
0225: } else if (c1 == 0xEF && c2 == 0xBB && c3 == 0xBF) {
0226: // UTF-8 BOM
0227: // discard all three chars
0228: return "UTF-8";
0229: } else {
0230: // Otherwise put back the chars we just read and proceed
0231: pbis.unread(c3);
0232: pbis.unread(c2);
0233: pbis.unread(c1);
0234: return null;
0235: }
0236: }
0237:
0238: /** Read a (pushback-able) byte input stream looking for some form of
0239: Unicode Byte Order Mark Defaults. If found, strip it out.
0240: * @param raw bytes
0241: * @return the count of characters to skip
0242: */
0243: public static int stripByteOrderMark(byte[] raw) {
0244: try {
0245: // We need to peek at the stream and if the first three chars
0246: // are a UTF-8 or UTF-16 encoded BOM (byte order mark) we will
0247: // discard them.
0248: int c1 = ((int) raw[0]) & 0xff;
0249: int c2 = ((int) raw[1]) & 0xff;
0250: int c3 = ((int) raw[2]) & 0xff;
0251: int count = 0;
0252: if (c1 == 0xFF & c2 == 0xFE) {
0253: // UTF16 Big Endian BOM
0254: // discard the first two chars
0255: // pbis.unread(c3);
0256: // return "UTF-16BE";
0257: return 2;
0258: } else if (c1 == 0xFE & c2 == 0xFF) {
0259: // UTF16 Little Endian BOM
0260: // discard the first two chars
0261: // pbis.unread(c3);
0262: // return "UTF-16LE";
0263: return 2;
0264: } else if (c1 == 0xEF && c2 == 0xBB && c3 == 0xBF) {
0265: // // UTF-8 BOM
0266: // // discard all three chars
0267: // return "UTF-8";
0268: return 3;
0269: } else {
0270: // Otherwise put back the chars we just read and proceed
0271: // pbis.unread(c3);
0272: // pbis.unread(c2);
0273: // pbis.unread(c1);
0274: // return null;
0275: return 0;
0276: }
0277: } catch (Exception e) {
0278: return 0;
0279: }
0280: }
0281:
0282: /**
0283: * @param file file to read
0284: * @return size of file
0285: * @throws FileNotFoundException
0286: * @throws IOException
0287: */
0288: static public long fileSize(File file) throws IOException,
0289: FileNotFoundException {
0290:
0291: RandomAccessFile raf = null;
0292: try {
0293: raf = new RandomAccessFile(file, "r");
0294: return raf.length();
0295: } finally {
0296: if (raf != null) {
0297: try {
0298: raf.close();
0299: } catch (Exception e) {
0300: mLogger
0301: .warn(
0302: /* (non-Javadoc)
0303: * @i18n.test
0304: * @org-mes="ignoring exception while closing random access file: "
0305: */
0306: org.openlaszlo.i18n.LaszloMessages
0307: .getMessage(FileUtils.class
0308: .getName(), "051018-246"),
0309: e);
0310: }
0311: }
0312: }
0313: }
0314:
0315: /**
0316: * @param file file to read
0317: * @return size of file or -1 if it can't be determined.
0318: */
0319: static public long getSize(File file) {
0320: try {
0321: return fileSize(file);
0322: } catch (Exception e) {
0323: return -1;
0324: }
0325: }
0326:
0327: /** Returns the base name (without the extension) of
0328: * <var>pathname</var>. For example,
0329: * <code>FileUtils.getBase("a/b.c") == "a/b"</code>.
0330: * @param pathname a String
0331: * @return a String
0332: */
0333: public static String getBase(String pathname) {
0334: int pos = pathname.lastIndexOf('.');
0335: if (pos >= 0) {
0336: return pathname.substring(0, pos);
0337: } else {
0338: return pathname;
0339: }
0340: }
0341:
0342: /**
0343: * @return the ending extension
0344: * @param pathname a string
0345: */
0346: public static String getExtension(String pathname) {
0347: int pos = pathname.lastIndexOf('.');
0348: if (pos >= 0 && pos != pathname.length() - 1) {
0349: return pathname.substring(pos + 1);
0350: }
0351: return "";
0352: }
0353:
0354: /** Inserts a subdir between the file and existing location.
0355: * Probably deals with cases that will never happen.
0356: * @return path a string specifying file in subdir
0357: * @param pathname a string
0358: * @param subdir a string
0359: */
0360: public static String insertSubdir(String pathname, String subdir) {
0361: boolean hasDrive = false;
0362: if (pathname.length() > 1) {
0363: hasDrive = (pathname.substring(1, 2) == ":");
0364: }
0365: String driveSpecifier = "";
0366: if (hasDrive) {
0367: driveSpecifier = pathname.substring(0, 2);
0368: pathname = pathname.substring(2);
0369: }
0370: char fSep = File.separatorChar;
0371: int slashIndex = pathname.lastIndexOf(fSep);
0372: if (slashIndex == -1) {
0373: return (driveSpecifier + subdir + fSep + pathname);
0374: } else {
0375: if (hasDrive | (slashIndex == 0)) {
0376: return (driveSpecifier + fSep + subdir + pathname);
0377: } else {
0378: String dir = pathname.substring(0, slashIndex);
0379: String name = pathname.substring(slashIndex, pathname
0380: .length());
0381: return (driveSpecifier + dir + fSep + subdir + name);
0382: }
0383: }
0384: }
0385:
0386: public static String insertSuffix(String pathname, String suffix) {
0387: String base = getBase(pathname);
0388: String type = getExtension(pathname);
0389: return (base + suffix + '.' + type);
0390: }
0391:
0392: public static File[] matchPlusSuffix(File fFullPath) {
0393: File mDir = fFullPath.getParentFile();
0394: String sName = fFullPath.getName();
0395: String sBase = getBase(sName);
0396: String sType = getExtension(sName);
0397:
0398: class StartsWithNameFilter implements FilenameFilter {
0399: final String _name;
0400: final String _type;
0401:
0402: StartsWithNameFilter(String sBase, String sType) {
0403: _name = sBase;
0404: _type = sType;
0405: }
0406:
0407: public boolean accept(File fDir, String sName) {
0408: final boolean lenTest = (_name.length()
0409: + _type.length() + 1) < sName.length();
0410: return ((lenTest && sName.startsWith(_name) && sName
0411: .endsWith(_type)));
0412: }
0413: }
0414: File[] fileNames = mDir.listFiles(new StartsWithNameFilter(
0415: sBase, sType));
0416: if ((fileNames == null) || (fileNames.length == 0)) {
0417: return null;
0418: } else {
0419: return fileNames;
0420: }
0421: }
0422:
0423: /** Remove the : from Windows Drive specifier found
0424: * in Absolute path names.
0425: *
0426: * @param src
0427: * @return a fixed string
0428: */
0429: public static String fixAbsWindowsPaths(String src) {
0430:
0431: // See if we're on Windows
0432: if (File.separatorChar == '/')
0433: return src;
0434:
0435: // One char strings
0436: if (src.length() < 2)
0437: return src;
0438:
0439: // Check to make sure we have a Windows absolute path
0440: if (src.charAt(1) != ':')
0441: return src;
0442:
0443: // Sanity check to make sure we really have a drive specifier
0444: char c = src.charAt(0);
0445: int t = Character.getType(c);
0446: boolean isAscii = (t == Character.UPPERCASE_LETTER || t == Character.LOWERCASE_LETTER);
0447: if (!isAscii) {
0448: return src;
0449: }
0450:
0451: // Remove the : that was the 2nd character.
0452: return src.substring(0, 1) + src.substring(2);
0453: }
0454:
0455: /**
0456: * Insure a file and all parent directories exist.
0457: * @param file the file
0458: * @throws IOException
0459: */
0460: public static void makeFileAndParentDirs(File file)
0461: throws IOException {
0462: File dir = file.getParentFile();
0463: if (dir != null)
0464: dir.mkdirs();
0465: file.createNewFile();
0466: }
0467:
0468: /** Delete an entire directory tree. Named after the Python
0469: * function.
0470: * @return true if full removal of cache was successful, otherwise false.
0471: */
0472: public static boolean rmTree(File file) {
0473: boolean delTreeOk = true;
0474: boolean delFileOk = true;
0475: File[] files = file.listFiles(); // null if not a directory
0476: if (files != null) {
0477: for (Iterator iter = Arrays.asList(files).iterator(); iter
0478: .hasNext();) {
0479: if (!rmTree((File) iter.next()))
0480: delTreeOk = false;
0481: }
0482: }
0483: delFileOk = file.delete();
0484:
0485: return (delFileOk && delTreeOk);
0486: }
0487:
0488: /**
0489: * Read the input stream and write contents to output stream.
0490: * @param input stream to read
0491: * @param output stream to write
0492: * @return number of bytes written to output
0493: * @throws IOException if there's an error reading or writing
0494: * the steams
0495: */
0496: public static int send(InputStream input, OutputStream output)
0497: throws IOException {
0498:
0499: int available = input.available();
0500: int bsize;
0501: if (available == 0) {
0502: bsize = BUFFER_SIZE;
0503: } else {
0504: bsize = Math.min(input.available(), BUFFER_SIZE);
0505: }
0506: return send(input, output, bsize);
0507: }
0508:
0509: /**
0510: * Exception when there's an error writing a stream
0511: */
0512: public static class StreamWritingException extends IOException {
0513: public StreamWritingException(String s) {
0514: super (s);
0515: }
0516: }
0517:
0518: /**
0519: * Exception when there's an error reading a stream
0520: */
0521: public static class StreamReadingException extends IOException {
0522: public StreamReadingException(String s) {
0523: super (s);
0524: }
0525: }
0526:
0527: /**
0528: * Read the input character stream and write contents to output stream.
0529: * @param input stream to read
0530: * @param output stream to write
0531: * @return number of bytes written to output
0532: * @throws IOException if there's an error reading or writing
0533: * the steams
0534: */
0535: public static int send(Reader input, Writer output)
0536: throws IOException {
0537: int bsize = BUFFER_SIZE;
0538: return send(input, output, bsize);
0539: }
0540:
0541: /**
0542: * Read the input stream and write contents to output
0543: * stream using a buffer of the requested size.
0544: * @param input stream to read
0545: * @param output stream to write
0546: * @param size size of buffer to use
0547: * @return number of bytes written to output
0548: * @throws IOException if there's an error reading or writing
0549: * the steams
0550: */
0551: public static int send(Reader input, Writer output, int size)
0552: throws IOException {
0553: int c = 0;
0554: char[] buffer = new char[size];
0555: int b = 0;
0556: while ((b = input.read(buffer)) > 0) {
0557: c += b;
0558: output.write(buffer, 0, b);
0559: }
0560: return c;
0561: }
0562:
0563: /**
0564: * Read the input stream and write contents to output
0565: * stream using a buffer of the requested size.
0566: * @param input stream to read
0567: * @param output stream to write
0568: * @param size size of buffer to use
0569: * @return number of bytes written to output
0570: * @throws IOException if there's an error reading or writing
0571: * the steams
0572: */
0573: public static int send(InputStream input, OutputStream output,
0574: int size) throws IOException {
0575: int c = 0;
0576: byte[] buffer = new byte[size];
0577: int b = 0;
0578: while ((b = input.read(buffer)) > 0) {
0579: c += b;
0580: output.write(buffer, 0, b);
0581: }
0582: return c;
0583: }
0584:
0585: /**
0586: * Read the input stream and write contents to output stream.
0587: * @param input stream to read
0588: * @param output stream to write
0589: * @return number of bytes written to output
0590: * @throws StreamWritingException if there's an error writing output
0591: * @throws StreamReadingException if there's an error reading input
0592: */
0593: public static int sendToStream(InputStream input,
0594: OutputStream output) throws IOException {
0595:
0596: int available = input.available();
0597: int bsize;
0598: if (available == 0) {
0599: bsize = BUFFER_SIZE;
0600: } else {
0601: bsize = Math.min(input.available(), BUFFER_SIZE);
0602: }
0603: return sendToStream(input, output, bsize);
0604: }
0605:
0606: /**
0607: * Read the input stream and write contents to output
0608: * stream using a buffer of the requested size.
0609: * @param input stream to read
0610: * @param output stream to write
0611: * @param size size of buffer to use
0612: * @return number of bytes written to output
0613: * @throws StreamWritingException if there's an error writing output
0614: * @throws StreamReadingException if there's an error reading input
0615: */
0616: public static int sendToStream(InputStream input,
0617: OutputStream output, int size) throws IOException {
0618: int c = 0;
0619: byte[] buffer = new byte[size];
0620: int b = 0;
0621: while (true) {
0622: try {
0623: // Until end of stream
0624: if ((b = input.read(buffer)) <= 0) {
0625: return c;
0626: }
0627: } catch (IOException e) {
0628: throw new StreamReadingException(e.getMessage());
0629: }
0630: c += b;
0631: try {
0632: output.write(buffer, 0, b);
0633: } catch (IOException e) {
0634: throw new StreamWritingException(e.getMessage());
0635: }
0636: if (THROTTLE != 0) {
0637: try {
0638: mLogger.info(
0639: /* (non-Javadoc)
0640: * @i18n.test
0641: * @org-mes="Sleeping " + p[0] + " msecs "
0642: */
0643: org.openlaszlo.i18n.LaszloMessages.getMessage(
0644: FileUtils.class.getName(), "051018-519",
0645: new Object[] { new Integer(THROTTLE) }));
0646: Thread.currentThread().sleep(THROTTLE);
0647: } catch (InterruptedException e) {
0648: mLogger
0649: .warn(
0650: /* (non-Javadoc)
0651: * @i18n.test
0652: * @org-mes="interrupted during sleep"
0653: */
0654: org.openlaszlo.i18n.LaszloMessages
0655: .getMessage(FileUtils.class
0656: .getName(), "051018-529"),
0657: e);
0658: }
0659: }
0660: }
0661: }
0662:
0663: /**
0664: * Read the input stream and write contents to output stream.
0665: * @param input stream to read
0666: * @param output stream to write
0667: * @return number of bytes written to output
0668: * @throws IOException
0669: */
0670: public static int escapeHTMLAndSend(InputStream input,
0671: OutputStream output) throws IOException {
0672:
0673: int available = input.available();
0674: int bsize;
0675: if (available == 0) {
0676: bsize = BUFFER_SIZE;
0677: } else {
0678: bsize = Math.min(input.available(), BUFFER_SIZE);
0679: }
0680: return escapeHTMLAndSend(input, output, bsize);
0681: }
0682:
0683: /**
0684: * Read the input stream and write contents to output
0685: * stream using a buffer of the requested size.
0686: * @param input stream to read
0687: * @param output stream to write
0688: * @param size size of buffer to use
0689: * @return number of bytes written to output
0690: * @throws IOException
0691: */
0692: public static int escapeHTMLAndSend(InputStream input,
0693: OutputStream output, int size) throws IOException {
0694: int c = 0;
0695: byte[] buffer = new byte[size];
0696: byte[] buffer2 = new byte[size * 4]; // big enuff to escape each char
0697: int b = 0;
0698: int b2 = 0;
0699: while ((b = input.read(buffer)) > 0) {
0700: b2 = escapeHTML(buffer, b, buffer2);
0701: c += b2;
0702: output.write(buffer2, 0, b2);
0703: }
0704: return c;
0705: }
0706:
0707: /**
0708: * copy input into output escaping HTML chars
0709: * escaped. Output buffer must be 4 x the input buffer size.
0710: *
0711: * @param ib input buffer
0712: * @param buflen input buffer size;
0713: * @param ob output buffer size;
0714: * @return number of bytes written to ob
0715: */
0716: public static int escapeHTML(byte[] ib, int buflen, byte[] ob) {
0717: int bc = 0;
0718: for (int i = 0; i < buflen; i++) {
0719: if (ib[i] == '<') {
0720: ob[bc++] = '&';
0721: ob[bc++] = 'l';
0722: ob[bc++] = 't';
0723: ob[bc++] = ';';
0724: } else if (ib[i] == '>') {
0725: ob[bc++] = '&';
0726: ob[bc++] = 'g';
0727: ob[bc++] = 't';
0728: ob[bc++] = ';';
0729: } else {
0730: ob[bc++] = ib[i];
0731: }
0732: }
0733:
0734: return bc;
0735: }
0736:
0737: public static class RelativizationError extends Error {
0738: RelativizationError(String message) {
0739: super (message);
0740: }
0741: }
0742:
0743: /** Return a path that resolves to the same file relative to
0744: * dest as path does relative to source. Paths use '/' as
0745: * separators. source and dest are assumed to be directories. */
0746: public static String adjustRelativePath(String path, String source,
0747: String dest) throws RelativizationError {
0748: final String separator = "/";
0749: if (path.endsWith(separator)) {
0750: return path;
0751: }
0752: if (source.endsWith(separator)) {
0753: source = source.substring(0, source.length() - 1);
0754: }
0755: if (dest.endsWith(separator)) {
0756: dest = dest.substring(0, dest.length() - 1);
0757: }
0758: List sourcedirs = new Vector(Arrays.asList(StringUtils.split(
0759: source, separator)));
0760: List destdirs = new Vector(Arrays.asList(StringUtils.split(
0761: dest, separator)));
0762: normalizePath(sourcedirs);
0763: normalizePath(destdirs);
0764: // Remove the common prefix
0765: while (!sourcedirs.isEmpty()
0766: && !destdirs.isEmpty()
0767: && ((String) sourcedirs.get(0))
0768: .equals((String) destdirs.get(0))) {
0769: sourcedirs.remove(0);
0770: destdirs.remove(0);
0771: }
0772: // If either dir is absolute, we can't relativize a file in
0773: // one to the other. Do this after removing the common
0774: // prefix, since if both files are absolute and are not on
0775: // different drives (Windows only) then a pathname that's
0776: // relative to one can be rewritten relative to the other.
0777: //
0778: //System.err.println("" + sourcedirs + " -> " + AbsolutePathnameTester.test(sourcedirs));
0779: if (AbsolutePathnameTester.test(sourcedirs)
0780: || AbsolutePathnameTester.test(destdirs)) {
0781: throw new RelativizationError(
0782: /* (non-Javadoc)
0783: * @i18n.test
0784: * @org-mes="can't create a pathname relative to " + p[0] + " that refers to a file relative to " + p[1]
0785: */
0786: org.openlaszlo.i18n.LaszloMessages.getMessage(
0787: FileUtils.class.getName(), "051018-657",
0788: new Object[] { dest, source }));
0789: }
0790: List components = new Vector();
0791: // '..'s to get out of the source directory...
0792: for (Iterator iter = sourcedirs.iterator(); iter.hasNext();) {
0793: iter.next();
0794: components.add("..");
0795: }
0796: // ...and directory names to get into the dest directory
0797: for (Iterator iter = destdirs.iterator(); iter.hasNext();) {
0798: components.add(iter.next());
0799: }
0800: components.add(path);
0801: return StringUtils.join(components, separator);
0802: }
0803:
0804: /**
0805: * @return the path to the given file, relative to the
0806: * given directory name. If the file isn't inside the directory,
0807: * just return the entire file name. Path separators are
0808: * modified to always be '/'.
0809: */
0810: public static String relativePath(File file, String directoryName) {
0811: try {
0812: String fileName = file.getCanonicalPath()
0813: .replace('\\', '/');
0814: String dirName = new File(directoryName).getCanonicalPath()
0815: .replace('\\', '/');
0816: if (!fileName.startsWith(dirName)) {
0817: return fileName;
0818: }
0819: return fileName.substring(dirName.length());
0820: } catch (IOException ioe) {
0821: throw new ChainedException(ioe);
0822: }
0823: }
0824:
0825: /**
0826: * @return the path to the given file, relative to the
0827: * given directory name. If the file isn't inside the directory,
0828: * just return the entire file name. Path separators are
0829: * modified to always be '/'.
0830: */
0831: public static String relativePath(String filename,
0832: String directoryName) {
0833: return relativePath(new File(filename), directoryName);
0834: }
0835:
0836: public static String relativePath(File fFile, File directoryName) {
0837: return relativePath(fFile, directoryName.toString());
0838: }
0839:
0840: public static String toURLPath(File file) {
0841: return toURLPath(file.getPath());
0842: }
0843:
0844: public static String toURLPath(String path) {
0845: // Can't call File.toURL, since it looks at the disk
0846: return StringUtils.join(
0847: StringUtils.split(path, File.separator), "/");
0848: }
0849:
0850: public static String fromURLPath(String path) {
0851: return StringUtils.join(StringUtils.split(path, "/"),
0852: File.separator);
0853: }
0854:
0855: /** Remove "." and non-top "..". Destructive. */
0856: public static void normalizePath(List path) {
0857: for (int i = 0; i < path.size(); i++) {
0858: String component = (String) path.get(i);
0859: if (component.equals(".") || component.equals("")) {
0860: path.remove(i--);
0861: } else if (component.equals("..") && i > 0) {
0862: path.remove(i--);
0863: path.remove(i--);
0864: }
0865: }
0866: }
0867:
0868: /**
0869: * Encode the given file as requested to an outputFile.
0870: * @param inputFile file to be encoded
0871: * @param outputFile encoded file
0872: * @param encoding type of encoding to use ("gzip" or "deflate" supported).
0873: * @throws ChainedException if the requested encoding is not supported.
0874: */
0875: static public void encode(File inputFile, File outputFile,
0876: String encoding) throws IOException {
0877:
0878: FileInputStream in = null;
0879:
0880: try {
0881: in = new FileInputStream(inputFile);
0882: encode(in, outputFile, encoding);
0883: } finally {
0884: if (in != null)
0885: in.close();
0886: }
0887: }
0888:
0889: /**
0890: * Decode the given file as requested to an outputFile.
0891: * @param inputFile file to be decoded
0892: * @param outputFile decoded file
0893: * @param encoding type of encoding to use ("gzip" or "deflate" supported).
0894: * @throws ChainedException if the requested encoding is not supported.
0895: */
0896: static public void decode(File inputFile, File outputFile,
0897: String encoding) throws IOException {
0898:
0899: mLogger.debug("Starting decoding from " + encoding);
0900: FilterInputStream in = null;
0901: OutputStream out = new FileOutputStream(outputFile);
0902: InputStream ins = new FileInputStream(inputFile);
0903:
0904: try {
0905: if (encoding.equals(GZIP)) {
0906: in = new GZIPInputStream(ins);
0907: } else if (encoding.equals(DEFLATE)) {
0908: in = new InflaterInputStream(ins);
0909: } else {
0910: String message =
0911: /* (non-Javadoc)
0912: * @i18n.test
0913: * @org-mes="Unsuppored encoding: " + p[0]
0914: */
0915: org.openlaszlo.i18n.LaszloMessages.getMessage(
0916: FileUtils.class.getName(), "051018-771",
0917: new Object[] { encoding });
0918:
0919: mLogger.error(message);
0920: throw new ChainedException(message);
0921: }
0922:
0923: // Note: use fixed size for buffer. The send(in, out) method
0924: // will use in.available() to guess a good buffersize but
0925: // that won't work out well for these input streams.
0926: int b = FileUtils.send(in, out, BUFFER_SIZE);
0927: mLogger.debug(
0928: /* (non-Javadoc)
0929: * @i18n.test
0930: * @org-mes="decoded into " + p[0] + " bytes"
0931: */
0932: org.openlaszlo.i18n.LaszloMessages.getMessage(
0933: FileUtils.class.getName(), "051018-787",
0934: new Object[] { new Integer(b) }));
0935: } finally {
0936: close(in);
0937: close(ins);
0938: close(out);
0939: }
0940: mLogger.debug(
0941: /* (non-Javadoc)
0942: * @i18n.test
0943: * @org-mes="Done decoding from " + p[0]
0944: */
0945: org.openlaszlo.i18n.LaszloMessages.getMessage(FileUtils.class
0946: .getName(), "051018-800", new Object[] { encoding }));
0947: }
0948:
0949: /**
0950: * Encode the given file as requested to an outputFile.
0951: * @param input file to be encodeded
0952: * @param outputFile encoded file
0953: * @param encoding type of encoding to use ("gzip" or "deflate" supported).
0954: * @throws IOException if the requested encoding is not supported.
0955: */
0956: static public void encode(InputStream input, File outputFile,
0957: String encoding) throws IOException {
0958:
0959: FileOutputStream out = null;
0960: FilterOutputStream filter = null;
0961:
0962: mLogger.debug(
0963: /* (non-Javadoc)
0964: * @i18n.test
0965: * @org-mes="Encoding into " + p[0]
0966: */
0967: org.openlaszlo.i18n.LaszloMessages.getMessage(FileUtils.class
0968: .getName(), "051018-823", new Object[] { encoding }));
0969:
0970: try {
0971: FileUtils.makeFileAndParentDirs(outputFile);
0972:
0973: out = new FileOutputStream(outputFile);
0974:
0975: if (encoding.equals(GZIP)) {
0976: filter = new GZIPOutputStream(out);
0977: } else if (encoding.equals(DEFLATE)) {
0978: filter = new DeflaterOutputStream(out);
0979: } else {
0980: String message =
0981: /* (non-Javadoc)
0982: * @i18n.test
0983: * @org-mes="Unsupported encoding: " + p[0]
0984: */
0985: org.openlaszlo.i18n.LaszloMessages.getMessage(
0986: FileUtils.class.getName(), "051018-842",
0987: new Object[] { encoding });
0988:
0989: mLogger.error(message);
0990: throw new ChainedException(message);
0991: }
0992:
0993: FileUtils.send(input, filter, BUFFER_SIZE);
0994:
0995: } finally {
0996: if (filter != null)
0997: close(filter);
0998: if (out != null)
0999: close(out);
1000: }
1001:
1002: mLogger.debug(
1003: /* (non-Javadoc)
1004: * @i18n.test
1005: * @org-mes="Done encoding into " + p[0]
1006: */
1007: org.openlaszlo.i18n.LaszloMessages.getMessage(FileUtils.class
1008: .getName(), "051018-863", new Object[] { encoding }));
1009: }
1010:
1011: /**
1012: * Make sure you really want to ignore the exception
1013: * before using this.
1014: * @param in stream to close w/out throwing an exception
1015: */
1016: static public void close(InputStream in) {
1017: try {
1018: if (in != null) {
1019: in.close();
1020: }
1021: } catch (Exception e) {
1022: }
1023: }
1024:
1025: /**
1026: * Make sure you really want to ignore the exception
1027: * before using this.
1028: * @param out stream to close w/out throwing an exception
1029: */
1030: static public void close(OutputStream out) {
1031: try {
1032: if (out != null) {
1033: out.close();
1034: }
1035: } catch (Exception e) {
1036: }
1037: }
1038:
1039: /**
1040: * Make sure you really want to ignore the exception
1041: * before using this.
1042: * @param w write to close w/out throwing an exception
1043: */
1044: static public void close(Writer w) {
1045: try {
1046: if (w != null) {
1047: w.close();
1048: }
1049: } catch (Exception e) {
1050: }
1051: }
1052:
1053: /**
1054: * Remove all files in the given list that are immediate children
1055: * of this directory. This does <b>not</b> recursively remove
1056: * files in subdirecetories.
1057: *
1058: * @return true if all files to be removed, could be removed
1059: */
1060: static public boolean deleteFiles(File dir, String[] fileNames) {
1061: boolean ok = true;
1062:
1063: for (int i = 0; i < fileNames.length; i++) {
1064: File file = new File(dir + File.separator + fileNames[i]);
1065: try {
1066: if (!file.isDirectory()) {
1067: file.delete();
1068: }
1069: } catch (Throwable e) {
1070: mLogger.error(
1071: /* (non-Javadoc)
1072: * @i18n.test
1073: * @org-mes="can't delete file: " + p[0]
1074: */
1075: org.openlaszlo.i18n.LaszloMessages.getMessage(
1076: FileUtils.class.getName(), "051018-932",
1077: new Object[] { fileNames[i] }));
1078: ok = false;
1079: }
1080: }
1081: return ok;
1082: }
1083:
1084: /**
1085: * Remove all files that are immediate children
1086: * of this directory. This does <b>not</b> recursively remove
1087: * files in subdirecetories.
1088: *
1089: * @return true if all files to be removed, could be removed
1090: */
1091: static public boolean deleteFiles(File dir) {
1092:
1093: if (!dir.isDirectory()) {
1094: return false;
1095: }
1096: String[] fileNames = dir.list();
1097:
1098: return deleteFiles(dir, fileNames);
1099: }
1100:
1101: /**
1102: * Remove all files that start with the following name
1103: * in the given directory. This does <b>not</b> recursively remove
1104: * files in subdirecetories.
1105: * @return true if all files to be removed, could be removed
1106: */
1107: static public boolean deleteFilesStartingWith(File dir, String name) {
1108:
1109: boolean ok = true;
1110:
1111: /**
1112: * Filter that selects all files that begin with a
1113: * given file name. See java.io.FilenameFilter.
1114: */
1115: class StartsWithNameFilter implements FilenameFilter {
1116: final String _name;
1117:
1118: StartsWithNameFilter(String n) {
1119: _name = n;
1120: }
1121:
1122: public boolean accept(File d, String n) {
1123: return (n.startsWith(_name));
1124: }
1125: }
1126:
1127: String[] fileNames = dir.list(new StartsWithNameFilter(name));
1128:
1129: return deleteFiles(dir, fileNames);
1130: }
1131: }
|