* //read in the file
* MatFileReader mfr = new MatFileReader( "mat_file.mat" );
*
* //get array of a name "my_array" from file
* MLArray mlArrayRetrived = mfr.getMLArray( "my_array" );
*
* //or get the collection of all arrays that were stored in the file
* Map content = mfr.getContent();
*
*
* @see com.jmatio.io.MatFileFilter
* @author Wojciech Gradkowski (wgradkowski@gmail.com)
*/
/**
* @author Wojciech Gradkowski (wgradkowski@gmail.com)
*
*/
public class MatFileReader
{
public static final int MEMORY_MAPPED_FILE = 1;
public static final int DIRECT_BYTE_BUFFER = 2;
public static final int HEAP_BYTE_BUFFER = 4;
/**
* MAT-file header
*/
private MatFileHeader matFileHeader;
/**
* Container for red MLArrays
*/
private Map data;
/**
* Tells how bytes are organized in the buffer.
*/
private ByteOrder byteOrder;
/**
* Array name filter
*/
private MatFileFilter filter;
/**
* Creates instance of MatFileReader and reads MAT-file
* from location given as fileName.
*
* This method reads MAT-file without filtering.
*
* @param fileName the MAT-file path String
* @throws IOException when error occurred while processing the file.
*/
public MatFileReader(String fileName) throws FileNotFoundException, IOException
{
this ( new File(fileName), new MatFileFilter() );
}
/**
* Creates instance of MatFileReader and reads MAT-file
* from location given as fileName.
*
* Results are filtered by MatFileFilter. Arrays that do not meet
* filter match condition will not be available in results.
*
* @param fileName the MAT-file path String
* @param MatFileFilter array name filter.
* @throws IOException when error occurred while processing the file.
*/
public MatFileReader(String fileName, MatFileFilter filter ) throws IOException
{
this( new File(fileName), filter );
}
/**
* Creates instance of MatFileReader and reads MAT-file
* from file.
*
* This method reads MAT-file without filtering.
*
* @param file the MAT-file
* @throws IOException when error occurred while processing the file.
*/
public MatFileReader(File file) throws IOException
{
this ( file, new MatFileFilter() );
}
/**
* Creates instance of MatFileReader and reads MAT-file from
* file.
*
* Results are filtered by MatFileFilter. Arrays that do not
* meet filter match condition will not be available in results.
*
* Note: this method reads file using the memory mapped file policy, see
* notes to {@link #read(File, MatFileFilter, com.jmatio.io.MatFileReader.MallocPolicy)}
*
* @param file
* the MAT-file
* @param MatFileFilter
* array name filter.
* @throws IOException
* when error occurred while processing the file.
*/
public MatFileReader(File file, MatFileFilter filter) throws IOException
{
this();
read(file, filter, MEMORY_MAPPED_FILE);
}
public MatFileReader()
{
filter = new MatFileFilter();
data = new LinkedHashMap();
}
/**
* Reads the content of a MAT-file and returns the mapped content.
*
* This method calls
* read(file, new MatFileFilter(), MallocPolicy.MEMORY_MAPPED_FILE).
*
* @param file
* a valid MAT-file file to be read
* @return the same as {@link #getContent()}
* @throws IOException
* if error occurs during file processing
*/
public synchronized Map read(File file) throws IOException
{
return read(file, new MatFileFilter(), MEMORY_MAPPED_FILE);
}
/**
* Reads the content of a MAT-file and returns the mapped content.
*
* This method calls
* read(file, new MatFileFilter(), policy).
*
* @param file
* a valid MAT-file file to be read
* @param policy
* the file memory allocation policy
* @return the same as {@link #getContent()}
* @throws IOException
* if error occurs during file processing
*/
public synchronized Map read(File file, int policy) throws IOException
{
return read(file, new MatFileFilter(), policy);
}
/**
* Reads the content of a MAT-file and returns the mapped content.
*
* Because of java bug #4724038
* which disables releasing the memory mapped resource, additional different
* allocation modes are available.
*
*
{@link #MEMORY_MAPPED_FILE} - a memory mapped file
*
{@link #DIRECT_BYTE_BUFFER} - a uses
* {@link ByteBuffer#allocateDirect(int)} method to read in
* the file contents
*
{@link #HEAP_BYTE_BUFFER} - a uses
* {@link ByteBuffer#allocate(int)} method to read in the
* file contents
*
* Note: memory mapped file will try to invoke a nasty code to relase
* it's resources
*
* @param file
* a valid MAT-file file to be read
* @param filter
* the array filter applied during reading
* @param policy
* the file memory allocation policy
* @return the same as {@link #getContent()}
* @see MatFileFilter
* @throws IOException
* if error occurs during file processing
*/
private static final int DIRECT_BUFFER_LIMIT = 1 << 25;
public synchronized Map read(File file, MatFileFilter filter,
int policy) throws IOException
{
this.filter = filter;
//clear the results
for ( String key : data.keySet() )
{
data.remove(key);
}
FileChannel roChannel = null;
RandomAccessFile raFile = null;
ByteBuffer buf = null;
WeakReference bufferWeakRef = null;
try
{
//Create a read-only memory-mapped file
raFile = new RandomAccessFile(file, "r");
roChannel = raFile.getChannel();
// until java bug #4715154 is fixed I am not using memory mapped files
// The bug disables re-opening the memory mapped files for writing
// or deleting until the VM stops working. In real life I need to open
// and update files
switch ( policy )
{
case DIRECT_BYTE_BUFFER:
buf = ByteBuffer.allocateDirect( (int)roChannel.size() );
roChannel.read(buf, 0);
buf.rewind();
break;
case HEAP_BYTE_BUFFER:
int filesize = (int)roChannel.size();
System.gc();
buf = ByteBuffer.allocate( filesize );
// The following two methods couldn't be used (at least under MS Windows)
// since they are implemented in a suboptimal way. Each of them
// allocates its own _direct_ buffer of exactly the same size,
// the buffer passed as parameter has, reads data into it and
// only afterwards moves data into the buffer passed as parameter.
// roChannel.read(buf, 0); // ends up in outOfMemory
// raFile.readFully(buf.array()); // ends up in outOfMemory
int numberOfBlocks = filesize / DIRECT_BUFFER_LIMIT + ((filesize % DIRECT_BUFFER_LIMIT) > 0 ? 1 : 0);
if (numberOfBlocks > 1) {
ByteBuffer tempByteBuffer = ByteBuffer.allocateDirect(DIRECT_BUFFER_LIMIT);
for (int block=0; block((MappedByteBuffer)buf);
break;
default:
throw new IllegalArgumentException("Unknown file allocation policy");
}
//read in file header
readHeader(buf);
while ( buf.remaining() > 0 )
{
readData( buf );
}
return getContent();
}
catch ( IOException e )
{
throw e;
}
finally
{
if ( roChannel != null )
{
roChannel.close();
}
if ( raFile != null )
{
raFile.close();
}
if ( buf != null && bufferWeakRef != null && policy == MEMORY_MAPPED_FILE )
{
try
{
clean(buf);
}
catch ( Exception e )
{
int GC_TIMEOUT_MS = 1000;
buf = null;
long start = System.currentTimeMillis();
while (bufferWeakRef.get() != null)
{
if (System.currentTimeMillis() - start > GC_TIMEOUT_MS)
{
break; //a hell cannot be unmapped - hopefully GC will
//do it's job later
}
System.gc();
Thread.yield();
}
}
}
}
}
/**
* Workaround taken from bug #4724038
* to release the memory mapped byte buffer.
*
* Little quote from SUN: This is highly inadvisable, to put it mildly.
* It is exceedingly dangerous to forcibly unmap a mapped byte buffer that's
* visible to Java code. Doing so risks both the security and stability of
* the system
*
* Since the memory byte buffer used to map the file is not exposed to the
* outside world, maybe it's save to use it without being cursed by the SUN.
* Since there is no other solution this will do (don't trust voodoo GC
* invocation)
*
* @param buffer
* the buffer to be unmapped
* @throws Exception
* all kind of evil stuff
*/
private void clean(final Object buffer) throws Exception
{
AccessController.doPrivileged(new PrivilegedAction