| 
What this is
 Other links
 The source code
/*****************************************************************************
 * Sun Public License Notice
 *
 * The contents of this file are subject to the Sun Public License Version
 * 1.0 (the "License"). You may not use this file except in compliance with
 * the License. A copy of the License is available at http://www.sun.com/
 *
 * The Original Code is the CVS Client Library.
 * The Initial Developer of the Original Code is Robert Greig.
 * Portions created by Robert Greig are Copyright (C) 2000.
 * All Rights Reserved.
 *
 * Contributor(s): Robert Greig.
 *****************************************************************************/
package org.netbeans.lib.cvsclient.file;
import java.io.*;
import java.util.*;
import org.netbeans.lib.cvsclient.command.*;
import org.netbeans.lib.cvsclient.request.*;
import org.netbeans.lib.cvsclient.util.*;
/**
 * Provides a basic implementation of FileHandler, and does much of the
 * handling of reading and writing files and performing CRLF conversions.
 * @author  Robert Greig
 */
public class DefaultFileHandler
        implements FileHandler {
    /**
     * Whether to emit debug information.
     */
    private static final boolean DEBUG = false;
    /**
     * The size of chunks read from disk.
     */
    private static final int CHUNK_SIZE = 32768;
    /**
     * The date the next file written should be marked as being modified on.
     */
    private Date modifiedDate;
    private TransmitTextFilePreprocessor transmitTextFilePreprocessor;
    private WriteTextFilePreprocessor writeTextFilePreprocessor;
    private WriteTextFilePreprocessor writeRcsDiffFilePreprocessor;
    private GlobalOptions globalOptions;
    /**
     * Creates a DefaultFileHandler.
     */
    public DefaultFileHandler() {
        setTransmitTextFilePreprocessor(new DefaultTransmitTextFilePreprocessor());
        setWriteTextFilePreprocessor(new DefaultWriteTextFilePreprocessor());
        setWriteRcsDiffFilePreprocessor(new WriteRcsDiffFilePreprocessor());
    }
    /**
     * Returns the preprocessor for transmitting text files.
     */
    public TransmitTextFilePreprocessor getTransmitTextFilePreprocessor() {
        return transmitTextFilePreprocessor;
    }
    /**
     * Sets the preprocessor for transmitting text files.
     * The default one changes all line endings to Unix-lineendings (cvs default).
     */
    public void setTransmitTextFilePreprocessor(TransmitTextFilePreprocessor transmitTextFilePreprocessor) {
        this.transmitTextFilePreprocessor = transmitTextFilePreprocessor;
    }
    /**
     * Gets the preprocessor for writing text files after getting (and un-gzipping) from server.
     */
    public WriteTextFilePreprocessor getWriteTextFilePreprocessor() {
        return writeTextFilePreprocessor;
    }
    /**
     * Sets the preprocessor for writing text files after getting (and un-gzipping) from server.
     */
    public void setWriteTextFilePreprocessor(WriteTextFilePreprocessor writeTextFilePreprocessor) {
        this.writeTextFilePreprocessor = writeTextFilePreprocessor;
    }
    /**
     * Gets the preprocessor for merging text files after getting
     *  (and un-gzipping) the diff received from server.
     */
    public WriteTextFilePreprocessor getWriteRcsDiffFilePreprocessor() {
        return writeRcsDiffFilePreprocessor;
    }
    /**
     * Sets the preprocessor for merging text files after getting
     *  (and un-gzipping) the diff received from server.
     */
    public void setWriteRcsDiffFilePreprocessor(WriteTextFilePreprocessor writeRcsDiffFilePreprocessor) {
        this.writeRcsDiffFilePreprocessor = writeRcsDiffFilePreprocessor;
    }
    /**
     * Get the string to transmit containing the file transmission length.
     *
     * @return a String to transmit to the server (including carriage return)
     * @param length the amount of data that will be sent
     */
    protected String getLengthString(long length) {
        return String.valueOf(length) + "\n";  //NOI18N
    }
    protected Reader getProcessedReader(File f)
            throws IOException {
        return new FileReader(f);
    }
    protected InputStream getProcessedInputStream(File file) throws IOException {
        return new FileInputStream(file);
    }
    /**
     * Get any requests that must be sent before commands are sent, to init
     * this file handler.
     *
     * @return an array of Requests that must be sent
     */
    public Request[] getInitialisationRequests() {
        return null;
    }
    /**
     * Transmit a text file to the server, using the standard CVS protocol
     * conventions. CR/LFs are converted to the Unix format.
     *
     * @param file the file to transmit
     * @param dos the data outputstream on which to transmit the file
     */
    public void transmitTextFile(File file, LoggedDataOutputStream dos)
            throws IOException {
        if (file == null || !file.exists()) {
            throw new IllegalArgumentException("File is either null or " +
                                               "does not exist. Cannot transmit.");
        }
        File fileToSend = file;
        final TransmitTextFilePreprocessor transmitTextFilePreprocessor =
                                              getTransmitTextFilePreprocessor();
        if (transmitTextFilePreprocessor != null) {
            fileToSend = transmitTextFilePreprocessor.getPreprocessedTextFile(file);
        }
        BufferedInputStream bis = null;
        try {
            // first write the length of the file
            long length = fileToSend.length();
            dos.writeBytes(getLengthString(length));
            bis = new BufferedInputStream(new FileInputStream(fileToSend));
            // now transmit the file itself
            byte[] chunk = new byte[CHUNK_SIZE];
            while (length > 0) {
                int bytesToRead = (length >= CHUNK_SIZE) ? CHUNK_SIZE
                                                         : (int)length;
                int count = bis.read(chunk, 0, bytesToRead);
                length -= count;
                dos.write(chunk, 0, count);
            }
            dos.flush();
        }
        finally {
            if (bis != null) {
                try {
                    bis.close();
                }
                catch (IOException ex) {
                    // ignore
                }
            }
            if (transmitTextFilePreprocessor != null) {
                transmitTextFilePreprocessor.cleanup(fileToSend);
            }
        }
    }
    /**
     * Transmit a binary file to the server, using the standard CVS protocol
     * conventions.
     * @param file the file to transmit
     * @param dos the data outputstream on which to transmit the file
     */
    public void transmitBinaryFile(File file, LoggedDataOutputStream dos)
            throws IOException {
        if (file == null || !file.exists()) {
            throw new IllegalArgumentException("File is either null or " +
                                               "does not exist. Cannot transmit.");
        }
        BufferedInputStream bis = null;
        try {
            bis = new BufferedInputStream(new FileInputStream(file));
            // first write the length of the file
            long length = file.length();
            dos.writeBytes(getLengthString(length));
            // now transmit the file itself
            byte[] chunk = new byte[CHUNK_SIZE];
            while (length > 0) {
                int bytesToRead = (length >= CHUNK_SIZE) ? CHUNK_SIZE
                                                         : (int)length;
                int count = bis.read(chunk, 0, bytesToRead);
                length -= count;
                dos.write(chunk, 0, count);
            }
            dos.flush();
        }
        finally {
            if (bis != null) {
                try {
                    bis.close();
                }
                catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        }
    }
    /**
     * Write (either create or replace) a file on the local machine with
     * one read from the server.
     * @param path the absolute path of the file, (including the file name).
     * @param mode the mode of the file
     * @param dis the stream to read the file from, as bytes
     * @param length the number of bytes to read
     */
    public void writeTextFile(String path, String mode,
                              LoggedDataInputStream dis, int length)
            throws IOException {
        writeAndPostProcessTextFile(path, mode, dis, length,
                                    getWriteTextFilePreprocessor());
    }
    /**
     * Merge a text file on the local machine with
     * the diff from the server. (it uses the RcsDiff response format
     *  - see cvsclient.ps for details)
     * @param path the absolute path of the file, (including the file name).
     * @param mode the mode of the file
     * @param dis the stream to read the file from, as bytes
     * @param length the number of bytes to read
     */
    public void writeRcsDiffFile(String path, String mode, LoggedDataInputStream dis,
                                 int length) throws IOException {
        writeAndPostProcessTextFile(path, mode, dis, length,
                                    getWriteRcsDiffFilePreprocessor());
    }
    /**
     * Common code for writeTextFile() and writeRcsDiffFile() methods.
     * Differs only in the passed file processor.
     */
    private void writeAndPostProcessTextFile(String path, String mode, LoggedDataInputStream dis,
                                             int length, WriteTextFilePreprocessor processor) throws IOException {
        if (DEBUG) {
            System.err.println("[writeTextFile] writing: " + path); //NOI18N
            System.err.println("[writeTextFile] length: " + length); //NOI18N
            System.err.println("Reader object is: " + dis.hashCode()); //NOI18N
        }
        File file = new File(path);
        boolean readOnly = resetReadOnly(file);
        file.getParentFile().mkdirs();
        file.createNewFile();
        // For CRLF conversion, we have to read the file
        // into a temp file, then do the conversion. This is because we cannot
        // perform a sequence of readLines() until we've read the file from
        // the server - the file transmission is not followed by a newline.
        // Bah.
        File cvsDir = new File(file.getParentFile(), "CVS");
        cvsDir.mkdir();
        File tempFile = File.createTempFile("cvsCRLF", "tmp", cvsDir); //NOI18N
        try {
            OutputStream os = null;
            try {
                os = new BufferedOutputStream(new FileOutputStream(tempFile));
                byte[] chunk = new byte[CHUNK_SIZE];
                while (length > 0) {
                    int count = (length >= CHUNK_SIZE) ? CHUNK_SIZE
                                                       :length;
                    count = dis.read(chunk, 0, count);
                    length -= count;
                    if (DEBUG) {
                        System.err.println("Still got: " + length + " to read"); //NOI18N
                    }
                    os.write(chunk, 0, count);
                }
            }
            finally {
                if (os != null) {
                    try {
                        os.close();
                    }
                    catch (IOException ex) {
                        // ignore
                    }
                }
            }
            // Here we read the temp file in again, doing any processing required
            // (for example, unzipping). We must not convert bytes to characters
            // because it would break characters that are not in the current encoding
            InputStream tempInput = getProcessedInputStream(tempFile);
            try {
                //BUGLOG - assert the processor is not null..
                processor.copyTextFileToLocation(tempInput, file);
            } finally {
                tempInput.close();
            }
            if (modifiedDate != null) {
                file.setLastModified(modifiedDate.getTime());
                modifiedDate = null;
            }
        }
        finally {
            tempFile.delete();
        }
        if (readOnly) {
            FileUtils.setFileReadOnly(file, true);
        }
    }
    /**
     * Write (either create or replace) a binary file on the local machine with
     * one read from the server.
     * @param path the absolute path of the file, (including the file name).
     * @param mode the mode of the file
     * @param dis the stream to read the file from, as bytes
     * @param length the number of bytes to read
     */
    public void writeBinaryFile(String path, String mode,
                                LoggedDataInputStream dis, int length)
            throws IOException {
        if (DEBUG) {
            System.err.println("[writeBinaryFile] writing: " + path); //NOI18N
            System.err.println("[writeBinaryFile] length: " + length); //NOI18N
            System.err.println("Reader object is: " + dis.hashCode()); //NOI18N
        }
        File file = new File(path);
        boolean readOnly = resetReadOnly(file);
        file.getParentFile().mkdirs();
        file.createNewFile();
        // FUTURE: optimisation possible - no need to use a temp file if there
        // is no post processing required (e.g. unzipping). So perhaps enhance
        // the interface to allow this stage to be optional
        File cvsDir = new File(file.getParentFile(), "CVS");
        cvsDir.mkdir();
        File tempFile = File.createTempFile("cvsPostConversion", "tmp", cvsDir); //NOI18N
        try {
            BufferedOutputStream bos =
            new BufferedOutputStream(new FileOutputStream(tempFile));
            byte[] chunk = new byte[CHUNK_SIZE];
            try {
                while (length > 0) {
                    int bytesToRead = (length >= CHUNK_SIZE) ? CHUNK_SIZE
                            : (int)length;
                    int count = dis.read(chunk, 0, bytesToRead);
                    if (count < 0) {
                        break;
                    }
                    length -= count;
                    if (DEBUG) {
                        System.err.println("Still got: " + length + " to read"); //NOI18N
                    }
                    bos.write(chunk, 0, count);
                }
            } finally {
                bos.close();
            }
            // Here we read the temp file in, taking the opportunity to process
            // the file, e.g. unzip the data
            BufferedInputStream tempIS =
                     new BufferedInputStream(getProcessedInputStream(tempFile));
            bos = new BufferedOutputStream(new FileOutputStream(file));
            try {
                for (int count = tempIS.read(chunk, 0, CHUNK_SIZE);
                     count > 0;
                     count = tempIS.read(chunk, 0, CHUNK_SIZE)) {
                    bos.write(chunk, 0, count);
                }
            } finally {
                bos.close();
                tempIS.close();
            }
            // now we need to modifiy the timestamp on the file, if specified
            if (modifiedDate != null) {
                file.setLastModified(modifiedDate.getTime());
                modifiedDate = null;
            }
        }
        finally {
            tempFile.delete();
        }
        if (readOnly) {
            FileUtils.setFileReadOnly(file, true);
        }
    }
    private boolean resetReadOnly(File file) throws java.io.IOException {
        boolean readOnly = globalOptions != null && globalOptions.isCheckedOutFilesReadOnly();
        if (file.exists() && readOnly) {
            readOnly = !file.canWrite();
            if (readOnly) {
                FileUtils.setFileReadOnly(file, false);
            }
        }
 
        return readOnly;
    }
    /**
     * Remove the specified file from the local disk.
     *
     * @param pathname the full path to the file to remove
     * @throws IOException if an IO error occurs while removing the file
     */
    public void removeLocalFile(String pathname)
            throws IOException {
        File fileToDelete = new File(pathname);
        if (!fileToDelete.delete()) {
            System.err.println("Could not delete file " +
                               fileToDelete.getAbsolutePath());
        }
    }
    /**
     * Rename the local file.
     *
     * @param pathname the full path to the file to rename
     * @param newName the new name of the file (not the full path)
     * @throws IOException if an IO error occurs while renaming the file
     */
    public void renameLocalFile(String pathname, String newName)
            throws IOException {
        File sourceFile = new File(pathname);
        File destinationFile = new File(sourceFile.getParentFile(), newName);
        sourceFile.renameTo(destinationFile);
    }
    /**
     * Set the modified date of the next file to be written.
     * The next call to writeFile will use this date.
     *
     * @param modifiedDate the date the file should be marked as modified
     */
    public void setNextFileDate(Date modifiedDate) {
        this.modifiedDate = modifiedDate;
    }
    /**
     * Sets the global options.
     * This can be useful to detect, whether local files should be made read-only.
     */
    public void setGlobalOptions(GlobalOptions globalOptions) {
        BugLog.getInstance().assertNotNull(globalOptions);
        this.globalOptions = globalOptions;
        transmitTextFilePreprocessor.setTempDir(globalOptions.getTempDir());
    }
}
 | 
| ... this post is sponsored by my books ... | |
         
           #1 New Release!  | 
      
         
           FP Best Seller  | 
  
Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse 
URI on this website is
paid back to open source projects.