//******************************************************************************
//
// File: ProteinDatabase.java
// Package: edu.rit.compbio.seq
// Unit: Class edu.rit.compbio.seq.ProteinDatabase
//
// This Java source file is copyright (C) 2008 by Alan Kaminsky. All rights
// reserved. For further information, contact the author, Alan Kaminsky, at
// ark@cs.rit.edu.
//
// This Java source file is part of the Parallel Java Library ("PJ"). PJ is free
// software; you can redistribute it and/or modify it under the terms of the GNU
// General Public License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// PJ is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU General Public License for more details.
//
// A copy of the GNU General Public License is provided in the file gpl.txt. You
// may also obtain a copy of the GNU General Public License on the World Wide
// Web at http://www.gnu.org/licenses/gpl.html.
//
//******************************************************************************
package edu.rit.compbio.seq;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
/**
* Class ProteinDatabase encapsulates a protein sequence database stored in a
* file. Class ProteinDatabase is used to read {@linkplain ProteinSequence}
* objects from the database.
*
* A protein sequence database file consists of one or more protein sequences
* stored in FASTA format. A FASTA format protein sequence consists of one
* description line and one or more sequence lines. The description line
* consists of an initial '>' character followed by zero or more
* characters (the protein's description). A sequence line consists of one or
* more characters 'A' through 'Z', 'a' through
* 'z', '*', or '-'. For further information, see
* class {@linkplain ProteinSequence}.
*
* Along with the protein sequence database file, there is a protein sequence
* index file. The index file tells where each protein is located in the
* database file. Class ProteinDatabase's constructor requires both the database
* file and the index file to be supplied as arguments.
*
* Class ProteinDatabase includes a main program that reads a protein sequence
* database file and creates the requisite protein sequence index file. If the
* n argument is specified, the program creates an index file for just
* the first n sequences in the database. If the n argument is
* omitted, the program creates an index file for all the sequences in the
* database.
*
* Usage: java edu.rit.compbio.seq.ProteinDatabase databasefile
* indexfile [ n ]
*
databasefile = Input protein sequence database file
*
indexfile = Output protein sequence index file
*
n = Number of sequences in the index (default: all)
*
* @author Alan Kaminsky
* @version 01-Jul-2008
*/
public class ProteinDatabase
{
// Hidden data members.
private File myDatabaseFile;
private File myIndexFile;
private RandomAccessFile myIndex;
private long myProteinCount;
private long myDatabaseLength;
private long myFileLength;
// Exported constructors.
/**
* Construct a new protein sequence database.
*
* @param theDatabaseFile Protein sequence database file.
* @param theIndexFile Protein sequence index file.
*
* @exception NullPointerException
* (unchecked exception) Thrown if theDatabaseFile is null.
* Thrown if theIndexFile is null.
* @exception IOException
* Thrown if an I/O error occurred.
*/
public ProteinDatabase
(File theDatabaseFile,
File theIndexFile)
throws IOException
{
// Verify preconditions.
if (theDatabaseFile == null)
{
throw new NullPointerException
("ProteinDatabase(): theDatabaseFile is null");
}
if (theIndexFile == null)
{
throw new NullPointerException
("ProteinDatabase(): theIndexFile is null");
}
// Record files.
myDatabaseFile = theDatabaseFile;
myIndexFile = theIndexFile;
// Open index file.
myIndex = new RandomAccessFile (theIndexFile, "r");
// Determine number of entries in index file.
long n = myIndex.length();
if ((n & 0x7L) != 0L)
{
throw new IOException
("ProteinDatabase(): Index file \""+theIndexFile+
"\" has invalid length (= "+n+")");
}
myProteinCount = (n >> 3) - 1;
// Determine total length of all sequences in database.
myDatabaseLength = myIndex.readLong();
// Determine length of database file.
myFileLength = theDatabaseFile.length();
}
// Exported operations.
/**
* Get the sum of the lengths of the protein sequences in this protein
* sequence database.
*
* @return Total protein sequence length.
*/
public long getDatabaseLength()
{
return myDatabaseLength;
}
/**
* Get the number of protein sequences in this protein sequence database.
*
* @return Number of protein sequences, N.
*/
public long getProteinCount()
{
return myProteinCount;
}
/**
* Get the protein sequence at the given index in this protein sequence
* database.
*
* @param i Index in the range 0 ≤ i ≤ N−1.
*
* @return Protein sequence.
*
* @exception IndexOutOfBoundsException
* (unchecked exception) Thrown if i is out of bounds.
* @exception IOException
* Thrown if an I/O error occurred.
*/
public ProteinSequence getProteinSequence
(long i)
throws IOException
{
// Verify preconditions.
if (0 > i || i >= myProteinCount)
{
throw new IndexOutOfBoundsException
("ProteinDatabase.getProteinSequence(): i (= "+i+
") out of bounds");
}
// Get offset to protein sequence from index file, in a critical section
// for multiple thread safety.
long offset;
synchronized (this)
{
myIndex.seek ((i + 1) << 3);
offset = myIndex.readLong();
}
if (0 > offset || offset >= myFileLength)
{
throw new IOException
("ProteinDatabase.getProteinSequence("+i+
"): Invalid offset (= "+offset+")");
}
// Get protein sequence from database file.
InputStream fis = null;
try
{
// Open database file.
fis =
(new BufferedInputStream
(new FileInputStream (myDatabaseFile)));
// Seek to offset.
long remaining = offset;
long skipped = 0;
while (remaining > 0)
{
skipped = fis.skip (remaining);
if (skipped == 0)
{
throw new IOException
("ProteinDatabase.getProteinSequence("+i+
"): Unexpected end of file");
}
remaining -= skipped;
}
// Read protein sequence.
return new ProteinSequence (fis);
}
finally
{
// Close database file.
if (fis != null)
{
try { fis.close(); } catch (IOException exc) {}
}
}
}
/**
* Close this protein sequence database.
*
* @exception IOException
* Thrown if an I/O error occurred.
*/
public void close()
throws IOException
{
myIndex.close();
}
/**
* Finalize this protein sequence database.
*/
protected void finalize()
{
try { close(); } catch (IOException exc) {}
}
/**
* Main program that reads a protein sequence database file and creates the
* requisite protein sequence index file. If the n argument is
* specified, the program creates an index file for just the first n
* sequences in the database. If the n argument is omitted, the
* program creates an index file for all the sequences in the database.
*
* Usage: java edu.rit.compbio.seq.ProteinDatabase databasefile
* indexfile [ n ]
*
databasefile = Input protein sequence database file
*
indexfile = Output protein sequence index file
*
n = Number of sequences in the index (default: all)
*/
public static void main
(String[] args)
throws Exception
{
// Parse command line arguments.
if (args.length < 2 || args.length > 3) usage();
File databasefile = new File (args[0]);
File indexfile = new File (args[1]);
long n = Long.MAX_VALUE;
if (args.length == 3) n = Long.parseLong (args[2]);
// Open database file for reading.
InputStream databasein =
new BufferedInputStream
(new FileInputStream (databasefile));
// Open index file for writing. Set aside space for database length.
RandomAccessFile indexout = new RandomAccessFile (indexfile, "rw");
indexout.writeLong (0L);
// Scan the database file. Whenever a '>' is encountered at the
// beginning of a line, store its offset in the index file.
long offset = 0L;
long dblength = 0L;
int state = 0;
int b;
readloop: while ((b = databasein.read()) != -1)
{
switch (state)
{
case 0: // Beginning of line
if (b == '>')
{
if (n == 0) break readloop;
indexout.writeLong (offset);
-- n;
state = 1;
}
else if (b == '\r' || b == '\n')
{
state = 0;
}
else
{
++ dblength;
state = 2;
}
break;
case 1: // In a description line
if (b == '\r' || b == '\n')
{
state = 0;
}
else
{
state = 1;
}
break;
case 2: // In a sequence line
if (b == '\r' || b == '\n')
{
state = 0;
}
else
{
++ dblength;
state = 2;
}
break;
}
++ offset;
}
// Write database length.
indexout.seek (0L);
indexout.writeLong (dblength);
// All done.
databasein.close();
indexout.close();
}
// Hidden operations.
/**
* Print a usage message and exit.
*/
private static void usage()
{
System.err.println ("Usage: java edu.rit.compbio.seq.ProteinDatabase []");
System.err.println (" = Input protein sequence database file");
System.err.println (" = Output protein sequence index file");
System.err.println (" = Number of sequences in the index (default: all)");
System.exit (1);
}
}
Ads help maintain this website.