001    package calhoun.seq;
002    
003    import java.io.BufferedWriter;
004    import java.io.File;
005    import java.io.FileNotFoundException;
006    import java.io.FileWriter;
007    import java.io.IOException;
008    import java.io.Writer;
009    
010    import org.apache.commons.logging.Log;
011    import org.apache.commons.logging.LogFactory;
012    
013    import calhoun.util.ConfigException;
014    import calhoun.util.FileUtil;
015    
016    /** Used for writing fasta files.  Formats sequences to a specific line length.
017     * Allows multiple sequences to be to the same file.
018     */
019    public class FastaWriter {
020            private static final Log log = LogFactory.getLog(FastaWriter.class);
021    
022            public static final int DEFAULT_LINE_LENGTH = 60;
023    
024            BufferedWriter writer;
025            String filename = "<unknown>";
026            int lineLength;
027            
028            // If this instance was instantiated with a writer rather than
029            // a file, this is false and this object is not responsible for
030            // closing the writer
031            boolean ownsWriter = true;
032            
033            /** Opens a fasta file for writing.  Uses the default line length.
034             * @param file Filename of the file to open
035             * @param append true if sequences should be appended to this file, false if it should be replaced. 
036             * */
037            public FastaWriter(String file, boolean append) {
038                    this(new File(file), append, DEFAULT_LINE_LENGTH);
039            }
040    
041            /** Opens a fasta file for writing.
042             * @param file Filename of the file to open
043             * @param append true if sequences should be appended to this file, false if it should be replaced. 
044             * @param lineLength Allows the length of a sequence line to be set. 
045             * */
046            public FastaWriter(String file, boolean append, int lineLength) {
047                    this(new File(file), append, lineLength);
048            }
049    
050            /** Opens a fasta file for writing.  Uses the default line length
051             * @param file Filename of the file to open
052             * @param append true if sequences should be appended to this file, false if it should be replaced. 
053             * */
054            public FastaWriter(File file, boolean append) {
055                    this(file, append, DEFAULT_LINE_LENGTH);
056            }
057    
058            /** Opens a fasta file for writing.
059             * @param file Filename of the file to open
060             * @param append true if sequences should be appended to this file, false if it should be replaced. 
061             * @param lineLength Allows the length of a sequence line to be set. 
062             * */
063            public FastaWriter(File file, boolean append, int lineLength) {
064                    try {
065                            setupWriter(new FileWriter(file, append), lineLength);
066                            // fasta files don't require a terminal newline (are terminal newlines allowed?)
067                            // but initial newlines are not allowed
068                            if (append && file.length() > 0) {
069                                    writer.newLine();
070                            }
071                    } catch (FileNotFoundException ex) {
072                            throw new ConfigException("Not able to write fasta file: " + file, ex);
073                    } catch (IOException ex) {
074                            throw new ConfigException("Error writing fasta file: " + file, ex);
075                    }
076                    filename = file.getAbsolutePath();
077            }
078    
079            public FastaWriter(Writer w, int lineLength) {
080                    setupWriter(w, lineLength);
081                    ownsWriter = false;
082            }
083    
084            protected void setupWriter(Writer w, int lineLength) {
085                    this.lineLength = lineLength;
086                    writer = new BufferedWriter(w);
087            }
088            
089            /** Writes a sequence to the file. 
090             * @param header The fasta header for the sequence.  Should not include '>'.
091             * @param content The actual sequence.  It will be broken up into lines based on linelength as it is written out.
092             * */
093            public void writeSeq(String header, String content) {
094                    try {
095                            writer.write(">");
096                            writer.write(header);
097                            writer.newLine();
098                            int i = 0;
099                            log.debug("Writing sequence of length: " + content.length() + " to " + filename);
100                            while (i < content.length()) {
101                                    CharSequence line = content.subSequence(i, Math.min(lineLength+i, content.length()) );
102                                    writer.write(line.toString());
103                                    writer.newLine();
104                                    i = i + lineLength;
105                            }
106                            writer.flush();
107                    }
108                    catch(IOException ex) {
109                            throw new ConfigException("Not able to write fasta file: " + filename, ex);
110                    }
111            }
112    
113            /** Writes a portion of the sequence to the file. 
114             * @param header The fasta header for the sequence.  Should not include '>'.
115             * @param content The actual sequence.  It will be broken up into lines based on linelength as it is written out.
116             * @param start 1-based index of the first base to write.  If null, writing will start from the beginning of the sequence. 
117             * @param stop 1-based index of the last base to write.  If null, writing will continue until the end of the sequence.
118             * */
119            public void writeSeq(String header, String string, Integer start, Integer stop) {
120                    CharSequence sequence = string;
121                    // trim the sequence to the specified coordinates
122                    // note that we don't subtract 1 from the stop coordinate,
123                    // because of the way String.substring works
124                    if (start != null && stop != null)
125                            sequence = sequence.subSequence(start.intValue() - 1, stop.intValue());
126                    else if (start != null)
127                            sequence = sequence.subSequence(start.intValue() - 1, sequence.length());
128                    else if (stop != null)
129                            sequence = sequence.subSequence(0, stop.intValue());
130                    writeSeq(header, sequence.toString());
131            }
132    
133            /** Writes a sequence to the file, which may have been previously
134             * read from a different file.
135             */
136            public void writeSeq(FastaSequence seq) {
137                    writeSeq(seq.getHeader(), seq.getSequence());
138            }
139            
140            /** Closes the file.  Once it is closed, no more sequences may be written.
141             * Open a new FastaWriter to append more sequences into the file.
142             */
143            public void close() {
144                    FileUtil.safeClose(writer);
145            }
146            
147            public String getFilename() {
148                    return filename;
149            }
150    
151            @Override
152            protected void finalize() {
153                    if (ownsWriter) {
154                            close();
155                    }
156            }
157    }