blob: 036926a9a0785b16d378e68c8d3b6f99a7e8d080 [file] [log] [blame]
/********************************************************************************
* Copyright (c) 2015-2018 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
********************************************************************************/
package org.eclipse.mdm.openatfx.mdf.mdf4;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.SeekableByteChannel;
/**
* <p>
* THE FILE HISTORY BLOCK <code>FHBLOCK<code>
* </p>
* The FHBLOCK describes who/which tool generated or changed the MDF file. Each
* FHBLOCK contains a change log entry for the MDF file. The first FHBLOCK
* referenced by the HDBLOCK must contain information about the tool which
* created the MDF file. Starting from this FHBLOCK then a linked list of
* FHBLOCKs can be maintained with a chronological change history, i.e. any
* other application that changes the file must append a new FHBLOCK to the
* list. A zero-based index value is used to reference blocks within this linked
* list, thus the ordering of the blocks must not be changed.
*
* @author Christian Rechner
*/
class FHBLOCK extends BLOCK {
public static String BLOCK_ID = "##FH";
/** Link section */
// Link to next FHBLOCK (can be NIL if list finished)
// LINK
private long lnkFhNext;
// Link to MDBLOCK containing comment about the creation or modification of
// the MDF file.
// LINK
private long lnkMdComment;
/** Data section */
// Time stamp at which the file has been changed/created (first entry) in
// nanoseconds elapsed since 00:00:00
// 01.01.1970 (UTC time or local time, depending on "local time" flag).
// UINT64
private long startTimeNs;
// Time zone offset in minutes.
// The value is not necessarily a multiple of 60 and can be negative! For
// the current time zone definitions, it is
// expected to be in the range [-840,840] min.
// For example a value of 60 (min) means UTC+1 time zone = Central European
// Time
// Only valid if "time offsets valid" flag is set in time flags.
// INT16
private short tzOffsetMin;
// Daylight saving time (DST) offset in minutes for start time stamp. During
// the summer months, most regions observe
// a DST offset of 60 min (1 hour).
// Only valid if "time offsets valid" flag is set in time flags.
// INT16
private short dstOffsetMin;
// Time flags
// The value contains the following bit flags (Bit 0 = LSB):
// Bit 0: Local time flag
// If set, the start time stamp in nanoseconds represents the local time
// instead of the UTC time, In this case, time
// zone and DST offset must not be considered (time offsets flag must not be
// set). Should only be used if UTC time
// is unknown.
// If the bit is not set (default), the start time stamp represents the UTC
// time.
// Bit 1: Time offsets valid flag
// If set, the time zone and DST offsets are valid. Must not be set together
// with "local time" flag (mutually
// exclusive).
// If the offsets are valid, the locally displayed time at start of
// recording can be determined
// (after conversion of offsets to ns) by
// Local time = UTC time + time zone offset + DST offset.
// UINT8
private byte timeFlags;
/**
* Constructor.
*
* @param sbc
* The byte channel pointing to the MDF file.
* @param pos
* The position of the block within the MDF file.
*/
private FHBLOCK(SeekableByteChannel sbc, long pos) {
super(sbc, pos);
}
private void setLnkFhNext(long lnkFhNext) {
this.lnkFhNext = lnkFhNext;
}
private void setLnkMdComment(long lnkMdComment) {
this.lnkMdComment = lnkMdComment;
}
private void setStartTimeNs(long startTimeNs) {
this.startTimeNs = startTimeNs;
}
private void setTzOffsetMin(short tzOffsetMin) {
this.tzOffsetMin = tzOffsetMin;
}
private void setDstOffsetMin(short dstOffsetMin) {
this.dstOffsetMin = dstOffsetMin;
}
private void setTimeFlags(byte timeFlags) {
this.timeFlags = timeFlags;
}
public long getLnkFhNext() {
return lnkFhNext;
}
public long getLnkMdComment() {
return lnkMdComment;
}
public long getStartTimeNs() {
return startTimeNs;
}
public short getTzOffsetMin() {
return tzOffsetMin;
}
public short getDstOffsetMin() {
return dstOffsetMin;
}
public byte getTimeFlags() {
return timeFlags;
}
public boolean isLocalTime() {
return BigInteger.valueOf(timeFlags).testBit(0);
}
public boolean isTimeFlagsValid() {
return BigInteger.valueOf(timeFlags).testBit(1);
}
public FHBLOCK getFhNextBlock() throws IOException {
if (lnkFhNext > 0) {
return FHBLOCK.read(sbc, lnkFhNext);
}
return null;
}
public MDBLOCK getMdCommentBlock() throws IOException {
if (lnkMdComment > 0) {
return MDBLOCK.read(sbc, lnkMdComment);
}
return null;
}
@Override
public String toString() {
return "FHBLOCK [lnkFhNext=" + lnkFhNext + ", lnkMdComment=" + lnkMdComment + ", startTimeNs=" + startTimeNs
+ ", tzOffsetMin=" + tzOffsetMin + ", dstOffsetMin=" + dstOffsetMin + ", timeFlags=" + timeFlags + "]";
}
/**
* Reads a FHBLOCK from the channel starting at current channel position.
*
* @param channel
* The channel to read from.
* @param pos
* The position within the channel.
* @return The block data.
* @throws IOException
* The exception.
*/
public static FHBLOCK read(SeekableByteChannel channel, long pos) throws IOException {
FHBLOCK block = new FHBLOCK(channel, pos);
// read block header
ByteBuffer bb = ByteBuffer.allocate(56);
bb.order(ByteOrder.LITTLE_ENDIAN);
channel.position(pos);
channel.read(bb);
bb.rewind();
// CHAR 4: Block type identifier
block.setId(MDF4Util.readCharsISO8859(bb, 4));
if (!block.getId().equals(BLOCK_ID)) {
throw new IOException("Wrong block type - expected '" + BLOCK_ID + "', found '" + block.getId() + "'");
}
// BYTE 4: Reserved used for 8-Byte alignment
bb.get(new byte[4]);
// UINT64: Length of block
block.setLength(MDF4Util.readUInt64(bb));
// UINT64: Number of links
block.setLinkCount(MDF4Util.readUInt64(bb));
// LINK: Link to next FHBLOCK (can be NIL if list finished)
block.setLnkFhNext(MDF4Util.readLink(bb));
// LINK: Link to MDBLOCK containing comment about the creation or
// modification of the MDF file.
block.setLnkMdComment(MDF4Util.readLink(bb));
// UINT64: Time stamp at which the file has been changed/created (first
// entry)
block.setStartTimeNs(MDF4Util.readUInt64(bb));
// INT16: Time zone offset in minutes.
block.setTzOffsetMin(MDF4Util.readInt16(bb));
// INT16: Daylight saving time (DST) offset in minutes for start time
// stamp.
block.setDstOffsetMin(MDF4Util.readInt16(bb));
// UINT8: Time flags
block.setTimeFlags(MDF4Util.readUInt8(bb));
return block;
}
}