/*******************************************************************************
* Copyright 2013 EMBL-EBI
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package net.sf.cram.index;
import htsjdk.samtools.BAMIndexer;
import htsjdk.samtools.FileFormat;
import htsjdk.samtools.IndexFormat;
import htsjdk.samtools.SAMFileHeader;
import htsjdk.samtools.SAMRecord;
import htsjdk.samtools.SamInputResource;
import htsjdk.samtools.SamReader;
import htsjdk.samtools.SamReaderFactory;
import htsjdk.samtools.util.Log;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.EnumSet;
import net.sf.cram.CramTools.LevelConverter;
import com.beust.jcommander.IStringConverter;
import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import com.beust.jcommander.converters.FileConverter;
public class CramIndexer {
private static Log log = Log.getInstance(CramIndexer.class);
public static final String COMMAND = "index";
private static int BUFFER_SIZE = 100 * 1024;
private static void printUsage(JCommander jc) {
StringBuilder sb = new StringBuilder();
sb.append("\n");
jc.usage(sb);
System.out.println("Version " + CramIndexer.class.getPackage().getImplementationVersion());
System.out.println(sb.toString());
}
public static void main(String[] args) throws IOException, IllegalArgumentException, IllegalAccessException {
Params params = new Params();
JCommander jc = new JCommander(params);
try {
jc.parse(args);
} catch (Exception e) {
System.out.println("Failed to parse parameteres, detailed message below: ");
System.out.println(e.getMessage());
System.out.println();
System.out.println("See usage: -h");
System.exit(1);
}
if (args.length == 0 || params.help) {
printUsage(jc);
System.exit(1);
}
Log.setGlobalLogLevel(params.logLevel);
if (!EnumSet.of(IndexFormat.BAI, IndexFormat.CRAI).contains(params.indexFormat)) {
failWithError(String.format("Unexpected index format (%s).", params.indexFormat.name()));
}
BufferedInputStream is = null;
String source = null;
if (params.inputFile == null)
is = new BufferedInputStream(System.in);
else {
is = new BufferedInputStream(new FileInputStream(params.inputFile), BUFFER_SIZE);
source = params.inputFile.getName();
}
FileFormat format = FileFormat.detect(is, source);
if (!format.testHeader(is)) {
failWithError("On a second thought, this is not the format it seems: " + format.name());
}
if (!isValidInputCombination(format, params.indexFormat)) {
failWithError(String.format("Unexpected combination of file format (%s) and index format (%s).",
format.name(), params.indexFormat.name()));
}
// open raw {@link: OutputStream} for index file:
OutputStream indexOutputStream = null;
if (params.indexFile != null) {
indexOutputStream = new FileOutputStream(params.indexFile);
} else {
if (params.inputFile == null)
indexOutputStream = System.out;
else
indexOutputStream = new FileOutputStream(
params.indexFormat.getDefaultIndexFileNameForDataFile(params.inputFile));
}
switch (format) {
case CRAM:
switch (params.indexFormat) {
case BAI:
new BaiIndexer(is, indexOutputStream).run();
break;
case CRAI:
new CraiIndexer(is, indexOutputStream).run();
break;
default:
failWithError("Expecting CRAI or BAI for CRAM input.");
}
break;
case BAM:
if (params.indexFormat != IndexFormat.BAI)
failWithError("CRAM index is not compatible with BAM files.");
SamReader reader = SamReaderFactory.make()
.setOption(SamReaderFactory.Option.INCLUDE_SOURCE_IN_RECORDS, true).open(SamInputResource.of(is));
if (!reader.getFileHeader().getSortOrder().equals(SAMFileHeader.SortOrder.coordinate)) {
reader.close();
failWithError("Input bam file must be sorted by coordinates");
}
BAMIndexer indexer = new BAMIndexer(indexOutputStream, reader.getFileHeader());
for (SAMRecord record : reader) {
indexer.processAlignment(record);
}
indexer.finish();
reader.close();
break;
default:
failWithError("Failed to recognize input file format.");
}
}
private static void failWithError(String message) {
log.error(message);
System.exit(1);
}
private static boolean isValidInputCombination(FileFormat inputFileFormat, IndexFormat indexFileFormat) {
switch (inputFileFormat) {
case CRAM:
return EnumSet.of(IndexFormat.BAI, IndexFormat.CRAI).contains(indexFileFormat);
case BAM:
return IndexFormat.BAI == indexFileFormat;
default:
return false;
}
}
public static class IndexTypeConverter implements IStringConverter<IndexFormat> {
@Override
public IndexFormat convert(String indexNameString) {
return IndexFormat.fromString(indexNameString);
}
};
@Parameters(commandDescription = "BAM/CRAM indexer. ")
public static class Params {
@Parameter(names = { "-l", "--log-level" }, description = "Change log level: DEBUG, INFO, WARNING, ERROR.", converter = LevelConverter.class)
Log.LogLevel logLevel = Log.LogLevel.ERROR;
@Parameter(names = { "--input-file", "-I" }, converter = FileConverter.class, description = "Path to a BAM or CRAM file to be indexed. Omit if standard input (pipe).")
File inputFile;
@Parameter(names = { "--index-file", "-O" }, converter = FileConverter.class, description = "Write index to this file.")
File indexFile;
@Parameter(names = { "--index-format" }, converter = IndexTypeConverter.class, description = "Choose between BAM index (bai) and CRAM index (crai). ")
IndexFormat indexFormat = IndexFormat.CRAI;
@Parameter(names = { "--help", "-h" }, description = "Print help and exit.")
boolean help = false;
}
}