package org.signalml.plugin.newstager.io; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import org.apache.log4j.Logger; import org.signalml.domain.book.StandardBookAtom; import org.signalml.plugin.newstager.data.NewStagerBookAtom; import org.signalml.plugin.newstager.data.NewStagerBookData; import org.signalml.plugin.newstager.data.NewStagerBookInfo; import org.signalml.plugin.newstager.data.NewStagerDecomposingInfo; import org.signalml.plugin.newstager.data.NewStagerSignalInfo; import org.signalml.plugin.newstager.exception.NewStagerBookReaderException; import pl.edu.fuw.MP.Core.FormatComponentV5; public class NewStagerFastBookV5AtomReader implements INewStagerAtomReader { protected static final Logger logger = Logger .getLogger(NewStagerFastBookV5AtomReader.class); private File bookFile; private MappedByteBuffer fileBuffer; private byte lastCode; private NewStagerSignalInfo signalInfo; private NewStagerDecomposingInfo decomposingInfo; private int offsetDimension; private int maxOffsetNumber; private ArrayList<ArrayList<NewStagerBookAtom[]>> books; private static int FLOAT_SIZE = Float.SIZE >> 3; private static int BYTE_SIZE = Byte.SIZE >> 3; private static int SHORT_SIZE = Short.SIZE >> 3; public NewStagerFastBookV5AtomReader(File bookFile) { this.fileBuffer = null; this.signalInfo = null; this.decomposingInfo = null; this.lastCode = 0; this.bookFile = bookFile; } @Override public NewStagerBookData read() throws NewStagerBookReaderException { RandomAccessFile file = null; try { file = new RandomAccessFile(this.bookFile, "r"); } catch (FileNotFoundException e) { throw new NewStagerBookReaderException(e); } try { FileChannel channel = file.getChannel(); try { this.fileBuffer = channel.map(MapMode.READ_ONLY, 0, channel.size()); this.skipMagicAndComment(); this.readFileHeader(); this.readSegments(); } catch (IOException e) { throw new NewStagerBookReaderException(e); } } finally { try { file.close(); } catch (IOException e) { throw new NewStagerBookReaderException(e); } } return this.createResult(); } private NewStagerBookData createResult() { NewStagerBookAtom atoms[][]; if (this.books.size() == 1) { atoms = this.books.get(0).toArray(new NewStagerBookAtom[0][]); } else { // TODO throw new RuntimeException("Not implemented"); } return new NewStagerBookData(new NewStagerBookInfo( atoms.length, this.offsetDimension, this.signalInfo.samplingFrequency, this.signalInfo.pointsPerMicrovolt), atoms); } private void readSegments() throws NewStagerBookReaderException { this.books = new ArrayList<ArrayList<NewStagerBookAtom[]>>( this.signalInfo.numberOfChannels); while (this.fileBuffer.hasRemaining()) { this.readSegmentCode(); int size = this.fileBuffer.getInt(); switch (this.lastCode) { case FormatComponentV5.OFFSET_SEGMENT_IDENTITY: this.readAtoms(size); break; case FormatComponentV5.COMMENT_SEGMENT_IDENTITY: // skip //$FALL-THROUGH$ default: this.fileBuffer.position(this.fileBuffer.position() + size); } } } private void readAtoms(int segmentSize) throws NewStagerBookReaderException { int endPosition = this.fileBuffer.position() + segmentSize; short offsetNumber = this.fileBuffer.getShort(); this.offsetDimension = this.fileBuffer.getInt(); this.maxOffsetNumber = Math.max(this.maxOffsetNumber, offsetNumber); while (this.fileBuffer.position() < endPosition) { this.readSegmentCode(); segmentSize = this.fileBuffer.getInt(); switch (this.lastCode) { case FormatComponentV5.SIGNAL_SEGMENT_IDENTITY: this.fileBuffer.position(this.fileBuffer.position() + segmentSize); break; case FormatComponentV5.ATOMS_SEGMENT_IDENTITY: this.readSingleAtomSegment(offsetNumber, segmentSize); break; default: logger.warn("Unknown segment code: " + this.lastCode); this.fileBuffer.position(this.fileBuffer.position() + segmentSize); } } } private void readSingleAtomSegment(int offsetNumber, int segmentSize) throws NewStagerBookReaderException { int endPosition = this.fileBuffer.position() + segmentSize; List<NewStagerBookAtom> atoms = new LinkedList<NewStagerBookAtom>(); short channelNumber = this.fileBuffer.getShort(); while (this.fileBuffer.position() < endPosition) { NewStagerBookAtom atom = this.readSingleAtom(); if (atom != null) { atoms.add(atom); } } ArrayList<NewStagerBookAtom[]> channelAtoms; if (channelNumber > this.books.size() || this.books.get(channelNumber - 1) == null) { channelAtoms = new ArrayList<NewStagerBookAtom[]>( this.maxOffsetNumber); while (this.books.size() < channelNumber - 1) { this.books.add(null); } this.books.add(channelNumber - 1, channelAtoms); } else { channelAtoms = this.books.get(channelNumber - 1); } while (channelAtoms.size() < offsetNumber) { channelAtoms.add(null); } channelAtoms.set(offsetNumber - 1, atoms.toArray(new NewStagerBookAtom[atoms.size()])); } private NewStagerBookAtom readSingleAtom() throws NewStagerBookReaderException { this.readSegmentCode(); int atomSize = this.fileBuffer.get(); switch (this.lastCode) { case StandardBookAtom.GABORWAVE_IDENTITY: AssertAtomSizeWorkaround(atomSize, 6 * FLOAT_SIZE); return NewStagerBookAtom.CreateGaborWave( this.fileBuffer.getFloat(), this.fileBuffer.getFloat(), this.fileBuffer.getFloat(), this.fileBuffer.getFloat(), this.fileBuffer.getFloat(), this.fileBuffer.getFloat()); case StandardBookAtom.DIRACDELTA_IDENTITY: AssertAtomSizeWorkaround(atomSize, 3 * FLOAT_SIZE); return NewStagerBookAtom.CreateDiracDelta( this.fileBuffer.getFloat(), this.fileBuffer.getFloat(), this.fileBuffer.getFloat()); case StandardBookAtom.GAUSSFUNCTION_IDENTITY: AssertAtomSizeWorkaround(atomSize, 4 * FLOAT_SIZE); return NewStagerBookAtom.CreateGaussFunction( this.fileBuffer.getFloat(), this.fileBuffer.getFloat(), this.fileBuffer.getFloat(), this.fileBuffer.getFloat()); case StandardBookAtom.SINCOSWAVE_IDENTITY: AssertAtomSizeWorkaround(atomSize, 4 * FLOAT_SIZE); return NewStagerBookAtom.CreateSinCosWave( this.fileBuffer.getFloat(), this.fileBuffer.getFloat(), this.fileBuffer.getFloat(), this.fileBuffer.getFloat()); default: throw new NewStagerBookReaderException("Unknown atom code: " + this.lastCode); } } private void readFileHeader() throws NewStagerBookReaderException { if (this.lastCode != FormatComponentV5.FILE_HEADER) { return; } int size = this.fileBuffer.getInt(); int pos = 0; while (pos < size) { this.readSegmentCode(); byte sizeOfField = this.fileBuffer.get(); pos += sizeOfField + 2; switch (this.lastCode) { case FormatComponentV5.SIGNAL_INFO: AssertSize(sizeOfField, 2 * FLOAT_SIZE + SHORT_SIZE); this.signalInfo = new NewStagerSignalInfo( this.fileBuffer.getFloat(), this.fileBuffer.getFloat(), this.fileBuffer.getShort()); break; case FormatComponentV5.DECOMP_INFO: AssertSize(sizeOfField, 3 * FLOAT_SIZE + BYTE_SIZE); this.decomposingInfo = new NewStagerDecomposingInfo( this.fileBuffer.getFloat(), this.fileBuffer.getInt(), this.fileBuffer.getInt(), (char) this.fileBuffer.get()); break; case FormatComponentV5.WEB_SITE_INFO: // skip; //$FALL-THROUGH$ case FormatComponentV5.TEXT_INFO: // skip; //$FALL-THROUGH$ case FormatComponentV5.DATE_INFO: // skip; //$FALL-THROUGH$ default: this.fileBuffer.position(this.fileBuffer.position() + sizeOfField); } } } private void skipMagicAndComment() { this.fileBuffer.position(this.fileBuffer.position() + 6); this.readSegmentCode(); if (this.lastCode == FormatComponentV5.COMMENT_SEGMENT_IDENTITY) { this.fileBuffer.position(this.fileBuffer.getInt() + this.fileBuffer.position()); this.readSegmentCode(); } } private void readSegmentCode() { this.lastCode = this.fileBuffer.get(); } private static void AssertSize(int size, int... desiredSizes) throws NewStagerBookReaderException { for (int desiredSize : desiredSizes) { if (size == desiredSize) { return; } } throw new NewStagerBookReaderException("Bad field size: " + size + " expected one of: " + Arrays.toString(desiredSizes)); } private static void AssertAtomSizeWorkaround(int atomSize, int desiredSize) throws NewStagerBookReaderException { AssertSize(atomSize, desiredSize + 2 * BYTE_SIZE, desiredSize); } }