added support for array composition and several improvements

- implemented CABLOCK
- only array composition at CNBLOCKs is supported yet, no
structure-based composition
- when a CABLOCK is linked via the composition link from a CNBLOCK, the
resulting AoMeasurementQuantity in ODS will have specified the rank and
dimension attributes accordingly, as specified by the ODS standard
- an additional attribute ('columnOriented' of type boolean at
AoMeasurementQuantity) is also set, containing the information, whether
the channel's value tensor has to be interpreted in a row- or
column-oriented manner, when calculating the actual values from it
- support for half-precision floating point data type
- fixed local column creation: independent and axistype are now set
according to SyncType and not ChannelType
- added BlockReferenceTriple for handling of according references in mdf
files
- fixed misleading names of fields in CABLOCK
- support default values at scale conversions with CCBLOCKS

Signed-off-by: Markus Renner <m.renner@peak-solution.de>
diff --git a/README.md b/README.md
index de19ee0..fc3a110 100644
--- a/README.md
+++ b/README.md
@@ -49,7 +49,6 @@
   - hd_ch_first must be 0 (channel hierarchy not yet supported)
   - hd_flags must be 0 [bits 00] (start angle value below is invalid, start distance value below is invalid)
 * CHBLOCK (MDF3): not yet supported, will be ignored with warning
-* CABLOCK (MDF4): not yet supported, will be ignored with warning
 * ATBLOCK: not yet supported, will be ignored with warning
 * EVBLOCK:
   - ev_ev_parent will be ignored with warning
@@ -62,7 +61,7 @@
   - cg_flags must be null (VLSD and bus events not supported), otherwise an exception is thrown
   - cg_inval_bytes must be null (invalidation bits not supported), otherwise an exception is thrown
 * CNBLOCK
-  - cn_composition must be null (composition not supported), otherwise an exception is thrown
+  - cn_composition only array composition supported, otherwise an exception is thrown
   - cn_at_reference: not yet supported, will be ignored with warning
   - cn_limit_min: not yet supported, will be ignored with warning
   - cn_limit_max: not yet supported, will be ignored with warning
diff --git a/build.gradle b/build.gradle
index 176aa07..be1125f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -19,7 +19,8 @@
 

 description = 'OpenATFX MDF driver'

 group = 'org.eclipse.mdm'

-version = '1.2.0'

+version = '1.3.0'

+def atfxVersion  = '0.8.8'

 

 apply plugin: 'java'

 apply plugin: 'eclipse'

@@ -29,7 +30,7 @@
 

 dependencies {

 	// openATFX

-	compile fileTree(dir: 'build/openatfx-0.6.4/lib', include: '*.jar')

+	compile fileTree(dir: "${buildDir}/openatfx-${atfxVersion}/lib", include: '*.jar')

 

 	// testing

 	testCompile 'junit:junit:4.12'

@@ -40,20 +41,18 @@
 task downloadOpenATFX(type: Download) {

 	acceptAnyCertificate true

 	overwrite false

-	// use mirror subdomain of 'https://sourceforge.net/projects/openatfx/files/openatfx-0.6.4-jars.zip/download'

-	// because gradle has problems establishing a ssl connection with sourceforge directly

-	src 'https://10gbps-io.dl.sourceforge.net/project/openatfx/openatfx-0.6.4-jars.zip'

-	dest new File(buildDir, 'openatfx-0.6.4-jars.zip')

+	src "https://sourceforge.net/projects/openatfx/files/openatfx-${atfxVersion}-jars.zip/download"

+	dest file("${buildDir}/openatfx-${atfxVersion}-jars.zip")

 	outputs.file dest

 }

 

 task unzipOpenATFX(dependsOn: downloadOpenATFX, type: Copy) {

 	from zipTree(downloadOpenATFX.dest)

 	into buildDir

-	outputs.dir new File(buildDir, 'openatfx-0.6.4')

+	outputs.dir file("${buildDir}/openatfx-${atfxVersion}")

 }

 

-//compileJava.dependsOn unzipOpenATFX

+compileJava.dependsOn unzipOpenATFX

 

 tasks.withType(JavaCompile) {

 	sourceCompatibility = '1.8'

diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 2cfaf45..ff6b478 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
-distributionBase=GRADLE_USER_HOME
-distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-bin.zip
-zipStoreBase=GRADLE_USER_HOME
-zipStorePath=wrapper/dists
+distributionBase=GRADLE_USER_HOME

+distributionPath=wrapper/dists

+distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip

+zipStoreBase=GRADLE_USER_HOME

+zipStorePath=wrapper/dists

diff --git a/src/main/java/org/eclipse/mdm/openatfx/mdf/mdf4/AoSessionWriter.java b/src/main/java/org/eclipse/mdm/openatfx/mdf/mdf4/AoSessionWriter.java
index e8d8347..810ef31 100644
--- a/src/main/java/org/eclipse/mdm/openatfx/mdf/mdf4/AoSessionWriter.java
+++ b/src/main/java/org/eclipse/mdm/openatfx/mdf/mdf4/AoSessionWriter.java
@@ -112,6 +112,8 @@
 	private boolean attachLinks2Tsts = false;

 	private boolean copyToSub = false;

 	private boolean ignoreATBLOCKs = false;

+	

+	private Map<Long, Long> arraySubMatrixIDsByNrOfValues = new HashMap<>();

 

 	/**

 	 * Constructor.

@@ -283,11 +285,14 @@
 

 		ODSInsertStatement ins = new ODSInsertStatement(modelCache, "mea");

 

+		String mrName = null;

 		if (useFileNameAsResultName) {

-			ins.setStringVal("iname", FileUtil.getResultName(fileName.toString(), resultSuffix));

+		  mrName = FileUtil.getResultName(fileName.toString(), resultSuffix);

 		} else {

-			ins.setStringVal("iname", resultName + resultSuffix);

+		  mrName = resultName + resultSuffix;

 		}

+		ins.setStringVal("iname", mrName);

+		LOG.debug("MeaResult name: " + mrName);

 		ins.setStringVal("mt", resultMimeType);

 

 		if (addMDF3FileAsResultAttachment) {

@@ -530,8 +535,11 @@
 

 				// create SubMatrix instance

 				ODSInsertStatement ins = new ODSInsertStatement(modelCache, "sm");

-				ins.setStringVal("iname", "sm_" + countFormat.format(grpNo));

+				String smName = "sm_" + countFormat.format(grpNo);

+				ins.setStringVal("iname", smName);

 

+				LOG.debug("SubMatrix name: " + smName);

+				

 				// write CGComment

 				BLOCK mdblock = cgBlock.getMdCommentBlock();

 				if (mdblock != null) {

@@ -619,7 +627,8 @@
 		// iterate over channel blocks

 		CNBLOCK cnBlock = cgBlock.getCnFirstBlock();

 		while (cnBlock != null) {

-

+      LOG.debug("CNBLOCK=" + cnBlock);

+		  

 			if ((cnBlock.getFlags() & 0x02) != 0 && cnBlock.getInvalBitPos() > 0) {

 				if (writeFlagsFile) {

 					// NOTE: flags are exported within writeEc()!

@@ -630,14 +639,8 @@
 					continue;

 				}

 			}

-			// check invalidation bits (not yet supported)

-			if (cnBlock.getLnkComposition() != 0) {

-				LOG.warn("Composition of channels not supported! [CNBLOCK=" + cnBlock + "]");

-				// throw new IOException("Composition of channels not supported!

-				// [CNBLOCK=" + cnBlock + "]");

-			}

 

-			// cn_at_reference: attachments TODO

+			// TODO cn_at_reference: attachments

 			if (cnBlock.getLnkAtReference().length > 0) {

 				LOG.warn("Found channel 'cn_at_reference'>0, not yet supported ");

 			}

@@ -650,6 +653,8 @@
 				meqName = meqName.replaceAll("\\[", "{");

 				meqName = meqName.replaceAll("\\]", "}");

 			}

+			

+			LOG.debug("Channel name: " + meqName);

 

 			// check for duplicate signal names and add suffix (except time

 			// channel)

@@ -700,15 +705,21 @@
 				}

 			}

 

+			BLOCK compositionBlock = null;

+			if (cnBlock.getLnkComposition() != 0) {

+	      LOG.debug("Composition found!");

+	      compositionBlock = cnBlock.getCompositionBlock();

+			}

+			

 			// create instance of 'AoMeasurementQuantity' (if not yet existing)

 			Long iidMeq = meqInstances.get(meqName);

 			if (iidMeq == null) {

-				iidMeq = createMeasurementQuantity(modelCache, cnBlock, ccBlock, meqName, iidMea, null, untInstances);

+				iidMeq = createMeasurementQuantity(modelCache, cnBlock, ccBlock, compositionBlock, meqName, iidMea, null, untInstances);

 				meqInstances.put(meqName, iidMeq);

 			}

 

 			// create 'AoLocalColumn' instance

-			long iidLc = createLocalColumn(modelCache, dgBlock, cgBlock, cnBlock, ccBlock, meqName, iidSm, iidMeq,

+			long iidLc = createLocalColumn(modelCache, dgBlock, cgBlock, cnBlock, ccBlock, compositionBlock, meqName, iidMea, iidSm, iidMeq,

 					iidPrevSm, null);

 			// relation to previews

 			if (iidPrevSm != null) {

@@ -722,7 +733,7 @@
 			// (6-9) or VLSD-Channel (Type ==1) or virtual master channel (Type == 3))

 			if (!(cnBlock.getDataType() >= 6 && cnBlock.getDataType() <= 9

 					|| cnBlock.getChannelType() == 1 || cnBlock.getChannelType() == 3)) {

-				writeEc(modelCache, iidLc, idBlock, dgBlock, cgBlock, cnBlock, ccBlock, dgBlock.getLnkData(), 0);

+				writeEc(modelCache, iidLc, idBlock, dgBlock, cgBlock, cnBlock, ccBlock, compositionBlock, dgBlock.getLnkData(), 0);

 			}

 

 			// create Table for Lookup conversion, if conversion type is 4 to 10

@@ -744,7 +755,7 @@
 			cnBlock = cnBlock.getCnNextBlock();

 		}

 	}

-

+	

 	/**

 	 * Create and insert the actual MeasurementQuantity This function also

 	 * creates the corresponding unit if it doesn't already exist.

@@ -755,6 +766,8 @@
 	 *            The Channel Block.

 	 * @param ccBlock

 	 *            The Conversion Block.

+	 * @param compositionBlock

+	 *            The composition Block, may be null.

 	 * @param meqName

 	 *            The Name the Measurement will have.

 	 * @param iidMea

@@ -770,7 +783,7 @@
 	 *             If an I/O-error occurs.

 	 * @throws AoException

 	 */

-	long createMeasurementQuantity(ODSModelCache modelCache, CNBLOCK cnBlock, CCBLOCK ccBlock, String meqName,

+	long createMeasurementQuantity(ODSModelCache modelCache, CNBLOCK cnBlock, CCBLOCK ccBlock, BLOCK compositionBlock, String meqName,

 			long iidMea, String mimeType, Map<String, Long> untInstances) throws IOException, AoException {

 		int seqRep = getSeqRep(cnBlock, ccBlock);

 		double[] genParams = getGenerationParameters(ccBlock);

@@ -825,9 +838,98 @@
 		if (uiid != -1) {

 			ins.setLongLongVal("unt", uiid);

 		}

-		long iidMeq = ins.execute();

-

-		return iidMeq;

+		

+		// handle composition block and transform into according MeaQuantity

+		handleCompositionInfo(compositionBlock, ins);

+		

+		return ins.execute();

+	}

+	

+	/**

+   * Handles composition of blocks. Only array composition is supported so far.

+   * 

+   * @param compositionBlock

+   *            The composition Block, may be null.

+   * @param ins the insert statement for a MeaQuantity

+   * @throws IOException

+   */

+	private void handleCompositionInfo(BLOCK compositionBlock, ODSInsertStatement ins) throws IOException

+	{

+	  if (compositionBlock == null)

+	  {

+	    return;

+	  }

+	  

+    CABLOCK arrayCompositionBlock = null;

+    if (compositionBlock instanceof CNBLOCK) {

+      throw new IOException("Channel Composition based on compact structures not yet supported!");

+    } else if (compositionBlock instanceof CABLOCK) {

+      LOG.debug("Array block for composition: [" + compositionBlock + "]");

+      arrayCompositionBlock = (CABLOCK)compositionBlock;

+    }

+    

+    if (arrayCompositionBlock != null)

+    {

+      handleArrayComposition(arrayCompositionBlock, ins);

+    }

+	}

+	

+	/**

+   * Handles array compositions. The ods information on rank and dimension is

+   * read from the CABLOCK. Additionally the inverse layout flag has to be

+   * transferred as well, otherwise no client may correctly calculate the values

+   * from the tensor in this channel. This additional flag is not yet specified

+   * by ODS, but will be written as an application attribute 'columnOriented' to

+   * the MeaQuantity.

+   * 

+   * @param arrayCompositionBlock

+   * @param ins the insert statement for a MeaQuantity

+   * @throws IOException

+   */

+	private void handleArrayComposition(CABLOCK arrayCompositionBlock, ODSInsertStatement ins) throws IOException

+	{

+	  if (arrayCompositionBlock == null || ins == null)

+	  {

+	    return;

+	  }

+	  

+	  // set the rank attribute of the MeaQuantity

+    int odsRank = arrayCompositionBlock.getNrOfDimensions();

+    ins.setLongVal(ODSModelCache.ATTR_NAME_MEAQU_RANK, odsRank);

+    

+    // set the dimension attribute of the MeaQuantity

+    long[] longDimSizes = arrayCompositionBlock.getDimSizes();

+    int[] odsDimension = new int[longDimSizes.length];

+    for (int i = 0; i < longDimSizes.length; i++)

+    {

+      long currentDimSize = longDimSizes[i];

+      if (currentDimSize > Integer.MAX_VALUE)

+      {

+        throw new IOException("Found dimension (index=" + i + ") in MDF with more values (" + currentDimSize

+            + ") than can be handled by ODS!");

+      }

+      odsDimension[i] = (int) currentDimSize;

+    }

+    ins.setLongSeq(ODSModelCache.ATTR_NAME_MEAQU_DIMENSION, odsDimension);

+    

+    // set the ColumnOriented attribute of the MeaQuantity

+    boolean odsInverseLayout = arrayCompositionBlock.isFlagSet(CABLOCK.FLAG_INVERSE_LAYOUT);

+    ins.setBooleanVal(ODSModelCache.ATTR_NAME_MEAQU_COL_ORIENTED_TENSOR, odsInverseLayout);

+    

+    // TODO handle working points

+    

+    // handle scaling axes

+    if (arrayCompositionBlock.isFlagSet(CABLOCK.FLAG_SCALING_AXIS_DEFINED))

+    {

+      if (arrayCompositionBlock.isFlagSet(CABLOCK.FLAG_FIXED_AXES))

+      {

+//        throw new IOException("Handling of fixed scaling axes for arrays is not yet implemented!");

+      }

+      else

+      {

+//        throw new IOException("Handling of variable scaling axes for arrays is not yet implemented!");

+      }

+    }

 	}

 

 	/**

@@ -843,8 +945,12 @@
 	 *            The Channel Block.

 	 * @param ccBlock

 	 *            The Conversion Block.

+	 * @param compositionBlock

+	 *            The composition Block.

 	 * @param lcName

 	 *            The Name the Local Column will have.

+	 * @param iidMea

+	 *            The ID of the Measurement this LocalColumn's SubMatrix will be related to in case of array composition.

 	 * @param iidMeq

 	 *            The ID of the SubMatrix this LocalColumn is related to.

 	 * @param iidMeq

@@ -858,13 +964,13 @@
 	 *             If an I/O-error occurs.

 	 * @throws AoException

 	 */

-	long createLocalColumn(ODSModelCache modelCache, DGBLOCK dgBlock, CGBLOCK cgBlock, CNBLOCK cnBlock, CCBLOCK ccBlock,

-			String lcName, long iidSm, long iidMeq, long[] iidPrevSm, String mimeType) throws IOException, AoException {

+  long createLocalColumn(ODSModelCache modelCache, DGBLOCK dgBlock, CGBLOCK cgBlock, CNBLOCK cnBlock, CCBLOCK ccBlock,

+      BLOCK compositionBlock, String lcName, long iidMea, long iidSm, long iidMeq, long[] iidPrevSm, String mimeType)

+      throws IOException, AoException {

 		int seqRep = getSeqRep(cnBlock, ccBlock);

 		double[] genParams = getGenerationParameters(ccBlock);

 		ODSInsertStatement ins = new ODSInsertStatement(modelCache, "lc");

 		ins.setStringVal("iname", lcName);

-		ins.setLongLongVal("sm", iidSm);

 

 		// MIME-Type

 		if (mimeType != null) {

@@ -875,9 +981,7 @@
 

 		// sequence_representation: string channels cannot be referenced in ASAM

 		// ODS: read and write external

-		if (cnBlock.getDataType() >= 6 && cnBlock.getDataType() <= 9 && cnBlock.getChannelType() != 1) { // 6

-																											// To

-																											// 9:

+		if (cnBlock.getDataType() >= 6 && cnBlock.getDataType() <= 9 && cnBlock.getChannelType() != 1) { // 6 To 9:

 			// Datatype

 			// String

 			String[] stringDataValues = readStringDataValues(dgBlock, cgBlock, cnBlock);

@@ -894,7 +998,7 @@
 		}

 

 		// independent flag

-		short idp = cnBlock.getChannelType() > 0 ? (short) 1 : (short) 0;

+		short idp = cnBlock.getSyncType() > 0 ? (short) 1 : (short) 0;

 		ins.setShortVal("idp", idp);

 

 		// generation parameters

@@ -906,7 +1010,7 @@
 		ins.setEnumVal("rdt", rawDataType);

 

 		// axistype

-		int axistype = cnBlock.getChannelType() == 0 ? 1 : 0;

+		int axistype = cnBlock.getSyncType() == 0 ? 1 : 0;

 		ins.setEnumVal("axistype", axistype);

 		// minimum/maximum

 		if (cnBlock.isValueRangeValid()) {

@@ -914,13 +1018,74 @@
 			ins.setDoubleVal("max", cnBlock.getValRangeMax());

 		}

 

+		// identify the actual SubMatrix to connect to

+		long actualSubMatrixId = identifySubMatrixIdToConnect(modelCache, cgBlock, compositionBlock, iidSm, iidMea);

+		

 		// relation to submatrix

-		ins.setLongLongVal("sm", iidSm);

+		ins.setLongLongVal("sm", actualSubMatrixId);

 		ins.setLongLongVal("meq", iidMeq);

 

 		long iidLc = ins.execute();

 		return iidLc;

 	}

+  

+  /**

+   * For array composition, the nrOfValues for the SubMatrix has to be

+   * corrected. An according array channel will have the following length:

+   * cycleCount * dimSize1 * ... * dimSizeN

+   * Therefore a new SubMatrix with this nrOfRows may have to be created.

+   * If one with this length was already created, its ID is returned. If no

+   * composition was passed, the original SubMatrix ID is returned.

+   * 

+   * @param modelCache

+   *              Cache with model Objects.

+   * @param cgBlock

+   *              The CGBLOCK.

+   * @param compositionBlock

+   *              The composition Block.

+   * @param originalSmId

+   *              If compositionBlock is null, this ID will be returned.

+   * @param iidMea

+   *              The measurement ID to connect a newly created SubMatrix to.

+   * @return SubMatrix ID to be used for the LocalColumn, for which this method was called.

+   * @throws AoException

+   */

+  private long identifySubMatrixIdToConnect(ODSModelCache modelCache, CGBLOCK cgBlock, BLOCK compositionBlock, long originalSmId,

+      long iidMea) throws AoException

+  {

+    long actualSubMatrixId = originalSmId;

+    if (compositionBlock instanceof CABLOCK && iidMea > 0) {

+      long smNrOfValues = cgBlock.getCycleCount();

+      long[] dimSizes = ((CABLOCK)compositionBlock).getDimSizes();

+      for (long dimSize : dimSizes)

+      {

+        smNrOfValues *= dimSize;

+      }

+      

+      Long existingArraySubmatrixId = arraySubMatrixIDsByNrOfValues.get(smNrOfValues);

+      

+      if (existingArraySubmatrixId == null)

+      {

+        // create array SubMatrix instance

+        ODSInsertStatement smIns = new ODSInsertStatement(modelCache, "sm");

+        String smName = "sm_arrays_" + smNrOfValues;

+        smIns.setStringVal("iname", smName);

+  

+        LOG.debug("SubMatrix name: " + smName);

+  

+        smIns.setLongVal("rows", (int) smNrOfValues);

+        // Relation to measurement

+        smIns.setLongLongVal("mea", iidMea);

+        actualSubMatrixId = smIns.execute();

+        arraySubMatrixIDsByNrOfValues.put(smNrOfValues, actualSubMatrixId);

+      }

+      else

+      {

+        actualSubMatrixId = existingArraySubmatrixId;

+      }

+    }

+    return actualSubMatrixId;

+  }

 

 	/**

 	 * Read values from an VLSD-Channel and write them directly to file.

@@ -1150,6 +1315,8 @@
 	 *            The CNBLOCK.

 	 * @param ccBlock

 	 *            The CCBLOCK.

+	 * @param compositionBlock

+	 *            The composition Block, may be null.

 	 * @param sectionstart

 	 *            The link to data, can be a DL, RD or DT Block or HL, DZ, but

 	 *            these are not supported.

@@ -1165,18 +1332,16 @@
 	 *             Error reading from MDF file.

 	 */

 	void writeEc(ODSModelCache modelCache, long iidLc, IDBLOCK idBlock, DGBLOCK dgBlock, CGBLOCK cgBlock,

-			CNBLOCK cnBlock, CCBLOCK ccBlock, long sectionstart, int parity) throws AoException, IOException {

+			CNBLOCK cnBlock, CCBLOCK ccBlock, BLOCK compositionBlock, long sectionstart, int parity) throws AoException, IOException {

 

 		if (isRatConv2ExtComp(ccBlock)) {

 			// NOTE: once CCBLOCK is no longer required, it should be removed

 			// from this method's signature!

 			createCustomRatConvEC(modelCache, iidLc, idBlock, dgBlock, cgBlock, cnBlock, ccBlock, sectionstart, parity);

 		} else {

-			ODSInsertStatement ins = new ODSInsertStatement(modelCache, "ec");

-

 			DLBLOCK currdl = null; // current list block

 			int dlindex = 0; // index in data list block.

-			int totalindex = 0; // nuber of blocks read;

+			int totalindex = 0; // number of blocks read;

 			long currblock = -1;

 

 			switch (BLOCK.getBlockType(idBlock.sbc, sectionstart)) {

@@ -1197,6 +1362,7 @@
 				throw new IOException("Zipped blocks cannot be parsed into the ODS-Format.");

 			}

 

+			ODSInsertStatement ins = new ODSInsertStatement(modelCache, "ec");

 			while (currblock != -1) {

 				ins.setStringVal("iname", "ec_" + countFormat.format(++totalindex));

 				Path mdfFilePath = idBlock.getMdfFilePath().getFileName();

@@ -1212,8 +1378,20 @@
 				ins.setEnumVal("vt", vt);

 				ins.setLongLongVal("so", startOffset);

 

+				int valuesPerBlock = 1; // default is one value per block

+				if (compositionBlock != null)

+				{

+				  if (compositionBlock instanceof CABLOCK) {

+  				  // for array composition set valuesPerBlock to the product of all dim sizes

+				    long[] dimSizes = ((CABLOCK)compositionBlock).getDimSizes();

+				    for (long dimSize : dimSizes)

+				    {

+				      valuesPerBlock *= dimSize;

+				    }

+				  }

+				}

 				// TODO Strings, write number of Bytes? Spec4/61

-				ins.setLongVal("vb", 1); // one value per block

+				ins.setLongVal("vb", valuesPerBlock); 

 

 				// Calculate ByteSize and Offset

 				long bytesize;

@@ -1236,13 +1414,25 @@
 				int cycleCount = -1;

 				if (BLOCK.getBlockType(idBlock.sbc, currblock).equals(DTBLOCK.BLOCK_ID)) {

 					cycleCount = (int) ((DTBLOCK.read(idBlock.sbc, currblock).getLength() - 24) / bytesize);

-					ins.setLongVal("cl", cycleCount);

 				} else if (BLOCK.getBlockType(idBlock.sbc, currblock).equals(RDBLOCK.BLOCK_ID)) {

 					cycleCount = (int) ((RDBLOCK.read(idBlock.sbc, currblock).getLength() - 24) / bytesize);

-					ins.setLongVal("cl", cycleCount);

 				} else {

 

 				}

+				

+				// for array composition the component length must be adjusted like for the SubMatrix nrOfRows

+				if (compositionBlock != null)

+        {

+          if (compositionBlock instanceof CABLOCK) {

+            long[] dimSizes = ((CABLOCK)compositionBlock).getDimSizes();

+            for (long dimSize : dimSizes)

+            {

+              cycleCount *= dimSize;

+            }

+          }

+        }

+				

+				ins.setLongVal("cl", cycleCount);

 

 				// export flags

 				exportFlags(ins, idBlock, dgBlock, cgBlock, cnBlock, startOffset, cycleCount);

@@ -1598,7 +1788,7 @@
 

 		// 4 IEEE 754 floating-point format LEO

 		else if (dt == 4) {

-			if (nb == 32 && bitOffset == 0) { // 32 bit: ieeefloat4

+		  if (nb == 32 && bitOffset == 0) { // 32 bit: ieeefloat4

 				return 5;

 			} else if (nb == 64 && bitOffset == 0) { // 64 bit: ieeefloat8

 				return 6;

@@ -1738,7 +1928,7 @@
 		else if (typeSpec == 31 || typeSpec == 32) {

 			int dt = cnBlock.getDataType();

 			int nb = (int) cnBlock.getBitCount();

-			if ((dt == 2 || dt == 3 || dt == 11 || dt == 12 || dt == 15 || dt == 16) && nb == 32) { // ieeefloat4,

+			if ((dt == 2 || dt == 3 || dt == 11 || dt == 12 || dt == 15 || dt == 16) && (nb == 16 || nb == 32)) { // half precision floating point, ieeefloat4,

 				// ieeefloat4_beo

 				ret = 3; // DT_FLOAT

 			} else if ((dt == 2 || dt == 3 || dt == 11 || dt == 12 || dt == 15 || dt == 16) && nb == 64) { // ieeefloat8,

@@ -1841,7 +2031,9 @@
 			} else if ((dt == 2 || dt == 3) && nb >= 33 && nb <= 64) { // dt_longlong,

 																		// dt_longlong_beo

 				return expandDataType ? 7 : 8; // [DT_DOUBLE] DT_LONGLONG

-			} else if ((dt == 4 || dt == 5) && nb == 32) { // ieeefloat4,

+			} else if ((dt == 4 || dt == 5) && nb == 16) { // half precision floating point

+        return 3; // DT_FLOAT

+      } else if ((dt == 4 || dt == 5) && nb == 32) { // ieeefloat4,

 				// ieeefloat4_beo

 				return 3; // DT_FLOAT

 			} else if ((dt == 4 || dt == 5) && nb == 64) { // ieeefloat8,

diff --git a/src/main/java/org/eclipse/mdm/openatfx/mdf/mdf4/BlockReferenceTriple.java b/src/main/java/org/eclipse/mdm/openatfx/mdf/mdf4/BlockReferenceTriple.java
new file mode 100644
index 0000000..87ec805
--- /dev/null
+++ b/src/main/java/org/eclipse/mdm/openatfx/mdf/mdf4/BlockReferenceTriple.java
@@ -0,0 +1,54 @@
+package org.eclipse.mdm.openatfx.mdf.mdf4;

+

+/**

+ * <p> Title: {@link BlockReferenceTriple} </p>

+ * 

+ * <b>Description:</b> 

+ * <p> Storage class for a reference triple used at some points in an mdf file. </p>

+ *

+ * @author Markus Renner

+ *

+ *  <p>Company: Peak Solution GmbH</p>

+ *

+ * $Rev: $:     Revision of last commit<br/>

+ * $Author: $:  Author of last commit<br/>

+ * $Date: $:    Date of last commit

+ * 

+ */

+public class BlockReferenceTriple

+{

+  private final long dgBlockOffset;

+  private final long cgBlockOffset;

+  private final long cnBlockOffset;

+  

+  public BlockReferenceTriple(long dgBlockOffset, long cgBlockOffset, long cnBlockOffset)

+  {

+    this.dgBlockOffset = dgBlockOffset;

+    this.cgBlockOffset = cgBlockOffset;

+    this.cnBlockOffset = cnBlockOffset;

+  }

+

+  /**

+   * @return the dgBlockOffset

+   */

+  public long getDgBlockOffset()

+  {

+    return dgBlockOffset;

+  }

+

+  /**

+   * @return the cgBlockOffset

+   */

+  public long getCgBlockOffset()

+  {

+    return cgBlockOffset;

+  }

+

+  /**

+   * @return the cnBlockOffset

+   */

+  public long getCnBlockOffset()

+  {

+    return cnBlockOffset;

+  }

+}

diff --git a/src/main/java/org/eclipse/mdm/openatfx/mdf/mdf4/CABLOCK.java b/src/main/java/org/eclipse/mdm/openatfx/mdf/mdf4/CABLOCK.java
new file mode 100644
index 0000000..a65bdbf
--- /dev/null
+++ b/src/main/java/org/eclipse/mdm/openatfx/mdf/mdf4/CABLOCK.java
@@ -0,0 +1,892 @@
+package org.eclipse.mdm.openatfx.mdf.mdf4;

+

+import java.io.IOException;

+import java.nio.ByteBuffer;

+import java.nio.ByteOrder;

+import java.nio.channels.SeekableByteChannel;

+import java.util.ArrayList;

+import java.util.Arrays;

+import java.util.List;

+

+public class CABLOCK extends BLOCK

+{

+  public static final String BLOCK_ID = "##CA";

+  

+  /**

+   * Array of composed elements: Pointer to a CNBLOCK for array of structures, or to a CABLOCK for array of arrays (can be NIL).

+   * If a CABLOCK is referenced, it must use the "CN template" storage type (ca_storage = 0).

+   */

+  private long lnkComposition;

+  

+  /**

+   * List of links to either a data block (DTBLOCK or DZBLOCK for this block type) or a data list block (DLBLOCK of data blocks or its HLBLOCK) for each element.

+   * <p>

+   * Only present for storage type "DG template" (ca_storage = 2)!

+   * </p>

+   * <p>

+   * A link in this list may only be NIL if the cycle count of the respective element is 0: ca_data[k] = NIL => ca_cycle_count[k] = 0<br>

+   * The links are stored line-oriented, i.e. element k uses ca_data[k]. The size of the list must be equal to Π N(d),

+   * i.e. to the product of the number of elements per dimension N(d) over all dimensions D.<br>

+   * </p>

+   * <p>

+   * Note: link ca_data[0] must be equal to dg_data link of the parent DGBLOCK.

+   * </p>

+   */

+  private long[] lnksData;

+  

+  /**

+   * References to channels for size signal of each dimension (can be NIL).

+   * <p>

+   * Only present if "dynamic size" flag (bit 0) is set.

+   * </p>

+   * <p>

+   * Each reference is a link triple with pointer to parent DGBLOCK, parent CGBLOCK and CNBLOCK for the channel

+   * (either all three links are assigned or NIL). Thus the links have the following order:<br>

+   * DGBLOCK for size signal of dimension 1<br>

+   * CGBLOCK for size signal of dimension 1<br>

+   * CNBLOCK for size signal of dimension 1<br>

+   * …<br>

+   * DGBLOCK for size signal of dimension D<br>

+   * CGBLOCK for size signal of dimension D<br>

+   * CNBLOCK for size signal of dimension D<br>

+   * </p>

+   * <p>

+   * The size signal can be used to model arrays whose number of elements per dimension can vary over time.

+   * If a size signal is specified for a dimension, the number of elements for this dimension at some point in time

+   * is equal to the value of the size signal at this time (i.e. for time-synchronized signals,

+   * the size signal value with highest time stamp less or equal to current time stamp).

+   * If the size signal has no recorded signal value for this time (yet), assume 0 as size.

+   * </p>

+   * <p>

+   * Since each referenced channel defines the current size of a dimension, its physical values must be Integer values ≥ 0 and ≤ N(d),

+   * i.e. the values must not exceed the maximum number of elements for the respective dimension.

+   * Usually, a size signal should have some Integer data type (cn_data_type ≤ 3) without conversion rule and without unit.

+   * </p>

+   * <p>

+   * Since the size signal and the array signal must be synchronized, their channel groups must contain at least one common master channel type.

+   * </p>

+   * <p>

+   * Note: the "positions" of the array elements are fixed as defined for the maximum allowed array size.

+   * They will not change when the array is reduced by a size signal value less than N(d).

+   * However, the "removed" array elements must not be considered.

+   * For row-oriented CN template storage, the elements would be stored in the record in following order:<br>

+   * a11, a12, a13, a21, a22, a23<br>

+   * Assume that the size of the second dimension is reduced from 3 to 2. Then the values of elements a13 and a23

+   * will be undefined and should not be used. However, the other elements stay at the same position (Byte offset).<br>

+   * a11, a12, [a13], a21, a22, [a23]

+   * </p>

+   */

+  private long[] lnksDynamicSize;

+  

+  /**

+   * Reference to channels for input quantity signal for each dimension (can be NIL).

+   * <p>

+   * Only present if "input quantity" flag (bit 1) is set.

+   * </p>

+   * <p>

+   * Reference to channels for input quantity signal for each dimension (can be NIL).

+   * Each reference is a link triple with pointer to parent DGBLOCK, parent CGBLOCK and

+   * CNBLOCK for the channel (either all three links are assigned or NIL). Thus the links have the following order:<br>

+   * DGBLOCK for input quantity of dimension 1<br>

+   * CGBLOCK for input quantity of dimension 1<br>

+   * CNBLOCK for input quantity of dimension 1<br>

+   * …<br>

+   * DGBLOCK for input quantity of dimension D<br>

+   * CGBLOCK for input quantity of dimension D<br>

+   * CNBLOCK for input quantity of dimension D<br>

+   * </p>

+   * <p>

+   * Since the input quantity signal and the array signal must be synchronized,

+   * their channel groups must contain at least one common master channel type.

+   * </p>

+   * <p>

+   * The input quantity often is denoted as "working point" because it is used for look-up of a value in the array.<br>

+   * For array type "look-up", the value of the input quantity will be used for looking up a value in the

+   * scaling axis of the respective dimension (either axis channel or fixed axis values listed in ca_axis_value list).<br>

+   * For array type "scaling axis", the value of the input quantity will be used for looking up a value in the axis itself.

+   * The input quantity should have the same physical unit as the axis it is applied to.

+   * </p>

+   */

+  private long[] lnksInputQuantity;

+  

+  /**

+   * Reference to channel for output quantity (can be NIL).

+   * <p>

+   * Only present if "output quantity" flag (bit 2) is set.

+   * </p>

+   * <p>

+   * The reference is a link triple with pointer to parent DGBLOCK, parent CGBLOCK and CNBLOCK for the channel

+   * (either all three links are assigned or NIL). Since the output quantity signal and the array signal must be synchronized,

+   * their channel groups must contain at least one common master channel type.

+   * For array type "look-up", the output quantity is the result of the complete look-up.

+   * The output quantity should have the same physical unit as the array elements of the array that references it.

+   * </p>

+   */

+  private long[] lnksOutputQuantity;

+  

+  /**

+   * Reference to channel for comparison quantity (can be NIL).

+   * <p>

+   * Only present if "comparison quantity" flag (bit 3) is set.

+   * </p>

+   * <p>

+   * The reference is a link triple with pointer to parent DGBLOCK, parent CGBLOCK and CNBLOCK for the channel

+   * (either all three links are assigned or NIL). Since the comparison quantity signal and the array signal must be synchronized,

+   * their channel groups must contain at least one common master channel type.

+   * The comparison quantity should have the same physical unit as the array elements.

+   * </p>

+   */

+  private long[] lnksComparisonQuantity;

+

+  /**

+   * Pointer to a conversion rule (CCBLOCK) for the axis of each dimension. If a link NIL a 1:1 conversion must be used for this axis.

+   * <p>

+   * Only present if "axis" flag (bit 4) is set.

+   * </p>

+   * <p>

+   * If the "fixed axis" flag (Bit 5) is set, the conversion must be applied to the fixed axis values of the respective axis/dimension

+   * (ca_axis_value list stores the raw values as REAL). If the link to the CCBLOCK is NIL already the physical values are stored

+   * in the ca_axis_value list. If the "fixed axes" flag (Bit 5) is not set, the conversion must be applied to the raw values

+   * of the respective axis channel, i.e. it overrules the conversion specified for the axis channel, even if the ca_axis_conversion link is NIL!<br>

+   * </p>

+   * <p>

+   * Note: ca_axis_conversion may reference the same CCBLOCK as referenced by the respective axis channel ("sharing" of CCBLOCK).

+   * </p>

+   */

+  private long[] lnksConversionRule;

+  

+  /**

+   * References to channels for axis of respective dimension (can be NIL).

+   * <p>

+   * Only present if "axis" flag (bit 4) is set and "fixed axes flag" (bit 5) is not set.

+   * </p>

+   * <p>

+   * Each reference is a link triple with pointer to parent DGBLOCK, parent CGBLOCK and CNBLOCK for the channel

+   * (either all three links are assigned or NIL). Thus the links have the following order:<br>

+   * DGBLOCK for axis of dimension 1<br>

+   * CGBLOCK for axis of dimension 1<br>

+   * CNBLOCK for axis of dimension 1<br>

+   * …<br>

+   * DGBLOCK for axis of dimension D<br>

+   * CGBLOCK for axis of dimension D<br>

+   * CNBLOCK for axis of dimension D<br>

+   * </p>

+   * <p>

+   * Each referenced channel must be an array of type "scaling axis" or "interval axis".<br>

+   * For a "scaling axis" the maximum number of elements (ca_dim_size[0] in axis) must not be less than the maximum number of elements

+   * of the respective dimension d in the array which references it (ca_dim_size[d-1]).<br>

+   * For an "interval axis" the number of elements must be exactly one more than respective dimension d

+   * in the "classification result" array (ca_dim_size[d-1]+1).

+   * </p>

+   * <p>

+   * If an axis signal must be synchronized with the array signal (e.g. for dynamic size axis),

+   * the channel groups of the axis and the array signal must contain at least one common master channel type.

+   * If a fixed axis is stored by an axis channel, its channel group should only contain one cycle.

+   * </p>

+   * <p>

+   * If a reference to an axis is NIL, there is no axis and a zero-based index should be used for axis values.

+   * </p>

+   */

+  private long[] lnksScalingAxis;

+  

+  /**

+   * <b>Array type</b> (defines semantic of the array)

+   * <p>Attention: for some array types certain features are not allowed (see ca_flags)</p>

+   * <p><ul>

+   * <li>0 = Array<br> 

+   * The array is a simple D-dimensional value array (value block) without axes and without input/output/comparison quantities.

+   * 

+   * <li>1 = Scaling axis<br>

+   * The array is a scaling axis (1-dimensional vector), possibly referenced by one or more arrays.

+   * If referenced by an array of type "look-up", the axis itself may have a scaling axis (e.g. CURVE_AXIS) and an own input quantity.

+   * 

+   * <li>2 = Look-up<br>

+   * The array is a D-dimensional array with axes. It can have input/output/comparison quantities.

+   * 

+   * <li>3 = Interval axis<br>

+   * The array is an axis (1-dimensional vector) defining interval ranges as "axis points".

+   * It can be referenced by one or more arrays of type "classification result" (ca_type = 4).

+   * The interval axis must have at least two elements (ca_dim_size[0] = N(0) > 1)

+   * and the physical values of the elements must be strictly monotonous increasing over the element index,

+   * i.e. ai < ai+1 for 1 ≤ i < N(0)<br>

+   * In contrast to a scaling axis (ca_type = 1), an interval axis always has one element more than the number of elements

+   * for the respective dimension of the classification result array which references it.

+   * The elements of the class interval axis define the borders of interval ranges that are seen as axis points.

+   * Depending on the "left-open interval" flag (bit 7 in ca_flags), the intervals are defined as<br>

+   * ]ao,a1], ]a1,a2], …, ]aN(0)-1,aN(0)] (flag set)<br>

+   * [ao,a1[, [a1,a2[, …, [aN(0)-1,aN(0)[ (flag not set)<br>

+   * valid since MDF 4.1.0, should not occur for earlier versions

+   * 

+   * <li>4 = Classification result<br>

+   * The array is a D-dimensional array containing classification results.

+   * It can have scaling axes (ca_type = 1) or interval axes (ca_type = 3), even mixed.

+   * Details about the classification method and parameters can be given as XML tags in cn_md_comment

+   * of the parent CN block and the CN blocks of the axes.<br>

+   * valid since MDF 4.1.0, should not occur for earlier versions

+   * </ul><p>

+   */

+  private byte arrayType;

+  public static final byte ARRAYTYPE_ARRAY = 1;

+  public static final byte ARRAYTYPE_SCALING_AXIS = 1 << 1;

+  public static final byte ARRAYTYPE_LOOKUP = 1 << 2;

+  public static final byte ARRAYTYPE_INTERVAL_AXIS = 1 << 3;

+  public static final byte ARRAYTYPE_CLASSIFICATION_RESULT = 1 << 4;

+  

+  /**

+   * <b>Storage type</b (defines how the element values are stored)

+   * <p><ul>

+   * <li>0 = CN template<br>

+   * Values of all elements of the array are stored in the same record (i.e. all elements are measured together).

+   * The parent CNBLOCK defines the first element in the record (k = 0).

+   * All other elements are defined by the same CNBLOCK except that the values for cn_byte_offset

+   * and cn_inval_bit_pos change for each component.

+   * 

+   * <li>1 = CG template<br>

+   * Value for each element of the array is stored in a separate record (i.e. elements are stored independently of each other).

+   * All records are stored in the same data block referenced by the parent DGBLOCK.

+   * As a consequence the data group (and thus the MDF file) is unsorted.

+   * All elements have exactly the same record layout specified by the parent CGBLOCK.

+   * However, each element uses a different cycle count (given by ca_cycle_count[k])

+   * and a different record ID which must be calculated by "auto-increment" of the record ID of the parent CGBLOCK:

+   * cg_record_id + k.<br> Since ca_cycle_count[0] must be equal to cg_cycle_count of the parent CGBLOCK,

+   * the parent CNBLOCK of the CABLOCK automatically describes the first array element (k = 0).

+   * When sorting a data group, a CABLOCK with "CG template" storage will be converted to a CABLOCK with "DG template" storage.

+   * 

+   * <li>2 = DG template<br>

+   * Similar to CG template, the value of each element of the array is stored in a separate record (i.e. elements are stored independently of each other).

+   * However, the records for each element are stored in separate data blocks referenced by the list of links in ca_data.

+   * Similar to "CG template" storage, all elements have exactly the same record layout (defined by the parent CGBLOCK)

+   * but a different cycle count (specified by ca_cycle_count[k]).<br>

+   * Since ca_cycle_count[0] must be equal to cg_cycle_count of the parent CGBLOCK, and ca_data[0] must be equal to

+   * dg_data of the parent DGBLOCK, the parent CNBLOCK of the CABLOCK automatically describes the first array element (k = 0).

+   * </ul></p>

+   */

+  private byte storageType;

+  public static final byte STORAGETYPE_CN_TEMPLATE = 1;

+  public static final byte STORAGETYPE_CG_TEMPLATE = 1 << 1;

+  public static final byte STORAGETYPE_DG_TEMPLATE = 1 << 2;

+  

+  /**

+   * <b>Flags (0-based!)</b>

+   * <p> The value contains the following bit flags (Bit 0 = LSB):

+   * <ul>

+   * <li>Bit 0: dynamic size flag<br>

+   * If set, the number of scaling points for the array is not fixed but can vary over time.

+   * Can only be set for array types "look-up" and "scaling axis".

+   * 

+   * <li>Bit 1: input quantity flag<br>

+   * If set, a channel for the input quantity is specified for each dimension by ca_input_quantity.

+   * Can only be set for array types "look-up" and "scaling axis".

+   * 

+   * <li>Bit 2: output quantity flag<br>

+   * If set, a channel for the output quantity is specified by ca_output_quantity. Can only be set for array type "look-up".

+   * 

+   * <li>Bit 3: comparison quantity flag<br>

+   * If set, a channel for the comparison quantity is specified by ca_comparison_quantity. Can only be set for array type "look-up".

+   * 

+   * <li>Bit 4: axis flag<br>

+   * If set, a scaling axis is given for each dimension of the array, either as fixed or as dynamic axis,

+   * depending on "fixed axis" flag (bit 5). Can only be set for array types "look-up", "scaling axis" and "classification result".

+   * 

+   * <li>Bit 5: fixed axis flag<br>

+   * If set, the scaling axis is fixed and the axis points are stored as raw values in ca_axis_value list.

+   * If not set, the scaling axis may vary over time and the axis points are stored as channel referenced in ca_axis for each dimension.

+   * Only relevant if "axis" flag (bit 4) is set. Can only not be set for array types "look-up" and "scaling axis".

+   * 

+   * <li>Bit 6: inverse layout flag<br>

+   * If set, the record layout is "column oriented" instead of "row oriented".

+   * Only relevant for "CN template" storage type and for number of dimensions > 1.

+   * 

+   * <li>Bit 7: left-open interval flag<br>

+   * If set, the interval ranges for the class interval axes are left-open and right-closed, i.e. ]a,b] = {x | a < x ≤ b}.<br>

+   * If not set, the interval ranges for the class interval axes are left-closed and right-open, i.e. [a,b[ = {x | a ≤ x < b}.<br>

+   * Note that opening or closing is irrelevant for infinite endpoints (there can only be -INF for the left border of the

+   * very first interval, or +INF for right border of the very last interval).

+   * Only relevant for array type "interval axes".<br>

+   * valid since MDF 4.1.0, should not be set for earlier versions

+   * </ul></p>

+   */

+  private long flags;

+  public static final byte FLAG_DYNAMIC_SIZE = 1;

+  public static final byte FLAG_INPUT_QUANTITY_DEFINED = 1 << 1;

+  public static final byte FLAG_OUTPUT_QUANTITY_DEFINED = 1 << 2;

+  public static final byte FLAG_COMPARISON_QUANTITY_DEFINED = 1 << 3;

+  public static final byte FLAG_SCALING_AXIS_DEFINED = 1 << 4;

+  public static final byte FLAG_FIXED_AXES = 1 << 5;

+  public static final byte FLAG_INVERSE_LAYOUT = 1 << 6;

+  public static final byte FLAG_LEFT_OPEN_INTERVALS = -128;

+  

+  /**

+   * Base factor for calculation of Byte offsets for "CN template" storage type.

+   * <p>The absolute value of ca_byte_offset_base should be larger than or equal to the size of Bytes required

+   * to store a component channel value in the record (all must have the same size).

+   * If the values are equal, then the component values are stored contiguously, i.e.next to each other without gaps.

+   * If ca_byte_offset_base is negative, the component values are stored with decreasing index.</p>

+   */

+  private long byteOffsetBase;

+  

+  /**

+   * Base factor for calculation of invalidation bit positions for CN template storage type.

+   * <p>For a simple array which is not part of a nested composition,

+   * ca_inval_bit_pos_base = 1 means that the invalidation bits for the component channels are stored without gaps and

+   * ca_inval_bit_pos_base = 0 means that all component channels use the same invalidation bit in the record.

+   */

+  private long invalBitPosBase;

+  

+  /**

+   * Maximum number of elements for each dimension N(d).

+   * <p>N(d) > 0 for all d. <br>For array type "interval axis", there must be at least two elements, i.e. N(0) > 1.</p>

+   */

+  private long[] dimSizes;

+  

+  /**

+   * Number of dimensions D > 0<br>

+   * For array type "scaling axis" or "interval axis", D must be 1.

+   */

+  private int nrOfDimensions;

+  

+  /**

+   * List of raw values for (fixed) axis points of each axis.

+   * <p>Only present if "fixed axes" flag (bit 5) is set!</p>

+   * <p>

+   * List of raw values for (fixed) axis points of each axis. The axis points are stored in a linear sequence for each dimension:<br>

+   * A1(1), A1(2), ..., A1(N(1)),<br>

+   * A2(1), A2(2), ..., A2(N(2)),<br>

+   * ...<br>

+   * AD(1), AD(2), ..., AD(N(D))<br>

+   * where Ad(j) is axis point j of dimension d and N(d) is the number of elements for dimension d.<br>

+   * Example for D = 2 dimensions (as explained later Y axis for A1 and X axis for A2):<br>

+   * Y(1), Y(2), ..., Y(N(1)),<br>

+   * X(1), X(2), ..., X(N(2))<br>

+   * The size of the list must be equal to Σ N(d) = the sum of the number of elements per dimension N(d) over all dimensions D.

+   * To convert the raw axis values to physical values, the conversion rule of the respective dimension must be applied

+   * (CCBLOCK referenced by ca_cc_axis_conversion[i]). If the CCBLOCK link of a dimension is NIL,

+   * the raw values are equal to the physical values (1:1 conversion).

+   * </p>

+   */

+  private double[] axisValues;

+  

+  /**

+   * List of cycle counts for each element in case of "CG/DG template" storage.

+   * <p>Only present for storage types "CG/DG template"!</p>

+   * <p>

+   * List of cycle counts for each element in case of "CG/DG template" storage. The offsets are stored line-oriented,

+   * i.e. element k uses ca_cycle_count[k]<br>

+   * The size of the list must be equal to Π N(d) = the product of the number of elements per dimension N(d) over all dimensions D.<br>

+   * Note: ca_cycle_count[0] must be equal to cg_cycle_count of parent CGBLOCK!

+   * </p>

+   */

+  private long[] cycleCounts;

+  

+  private CABLOCK(SeekableByteChannel sbc, long pos)

+  {

+    super(sbc, pos);

+  }

+

+  /**

+   * Reads a CABLOCK from the channel starting at current channel position.

+   *

+   * @param channel

+   *            The channel to read from.

+   * @param pos

+   *            The position

+   * @return The block data.

+   * @throws IOException

+   *             The exception.

+   */

+  public static CABLOCK read(SeekableByteChannel channel, long pos) throws IOException {

+    CABLOCK block = new CABLOCK(channel, pos);

+

+    // read block header

+    ByteBuffer bb = ByteBuffer.allocate(24);

+    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));

+

+    // read block data

+    bb = ByteBuffer.allocate((int) block.getLength() - 24);

+    bb.order(ByteOrder.LITTLE_ENDIAN);

+    channel.position(pos + 24);

+    channel.read(bb);

+    bb.rewind();

+

+    // read links

+    long[] lnks = new long[(int) block.getLinkCount()];

+    for (int i = 0; i < lnks.length; i++) {

+      lnks[i] = MDF4Util.readLink(bb);

+    }

+

+    // read data

+

+    // UINT8: Array type (ca_type)

+    byte arrayType = MDF4Util.readUInt8(bb);

+    block.setArrayType(arrayType);

+

+    // UINT8: Storage type (ca_storage)

+    byte storageTyp = MDF4Util.readUInt8(bb);

+    block.setStorageType(storageTyp);

+

+    // UINT16: Number of dimensions (ca_ndim)

+    int nrOfDims = MDF4Util.readUInt16(bb);

+    block.setNrOfDimensions(nrOfDims);

+

+    // UINT32: Flags (ca_flags)

+    long readFlags = MDF4Util.readUInt32(bb);

+    block.setFlags(readFlags);

+

+    // INT32: Byte offset base (ca_byte_offset_base)

+    block.setByteOffsetBase(MDF4Util.readInt32(bb));

+

+    // UINT32: Invalidation bit position base (ca_inval_bit_pos_base)

+    block.setInvalidationBitPositionBase(MDF4Util.readUInt32(bb));

+

+    // UINT64: Maximum size of each dimension (ca_dim_size)

+    List<Long> dimSizesList = new ArrayList<>();

+    for (int i = 0; i < nrOfDims; i++) {

+      dimSizesList.add(MDF4Util.readUInt64(bb));

+    }

+    block.setDimSizes(dimSizesList);

+

+    long sumNumberOfValuesPerDimension = dimSizesList.stream().mapToLong(Long::longValue).sum();

+    long productNumberOfValuesPerDimension = dimSizesList.stream().mapToLong(Long::longValue).reduce(1, (a, b) -> a * b);

+    

+    // REAL: Axis value for each dimension's number of values (ca_axis_value)

+    if (isBitSet(readFlags, FLAG_FIXED_AXES)) {

+      List<Double> axisValuesList = new ArrayList<>();

+      for (long i = 0; i < sumNumberOfValuesPerDimension; i++) {

+        axisValuesList.add(MDF4Util.readReal(bb));

+      }

+      block.setAxisValues(axisValuesList);

+    }

+    

+    // UINT64: Cycle counts (ca_cycle_count)

+    if (isBitSet(storageTyp, STORAGETYPE_CG_TEMPLATE) || isBitSet(storageTyp, STORAGETYPE_DG_TEMPLATE)) {

+      List<Long> cycleCountsList = new ArrayList<>();

+      for (long i = 0; i < productNumberOfValuesPerDimension; i++) {

+        cycleCountsList.add(MDF4Util.readUInt64(bb));

+      }

+      block.setCycleCounts(cycleCountsList);

+    }

+    

+    // extract links after reading data

+    

+    int currentLinkIndex = 0;

+    // ca_composition

+    long lnkNestedComposition = lnks[currentLinkIndex++];

+    block.setLnkComposition(lnkNestedComposition);

+    // ca_data

+    if (isBitSet(storageTyp, STORAGETYPE_DG_TEMPLATE)) {

+      List<Long> lnkDataList = new ArrayList<>();

+      for (long i = 0; i < productNumberOfValuesPerDimension; i++) {

+        lnkDataList.add(lnks[currentLinkIndex++]);

+      }

+      block.setLnksData(lnkDataList);

+    }

+    // ca_dynamic_size

+    if (isBitSet(readFlags, FLAG_DYNAMIC_SIZE)) {

+      List<Long> lnkDynamicSizeList = new ArrayList<>();

+      for (long i = 0; i < nrOfDims * 3; i++) {

+        lnkDynamicSizeList.add(lnks[currentLinkIndex++]);

+      }

+      block.setLnksDynamicSize(lnkDynamicSizeList);

+    }

+    // ca_input_quantity

+    if (isBitSet(readFlags, FLAG_INPUT_QUANTITY_DEFINED)) {

+      List<Long> lnkInputQuantityList = new ArrayList<>();

+      for (long i = 0; i < nrOfDims * 3; i++) {

+        lnkInputQuantityList.add(lnks[currentLinkIndex++]);

+      }

+      block.setLnksInputQuantities(lnkInputQuantityList);

+    }

+    // ca_output_quantity

+    if (isBitSet(readFlags, FLAG_OUTPUT_QUANTITY_DEFINED)) {

+      List<Long> lnkOutputQuantityList = new ArrayList<>();

+      for (long i = 0; i < 3; i++) {

+        lnkOutputQuantityList.add(lnks[currentLinkIndex++]);

+      }

+      block.setLnksOutputQuantity(lnkOutputQuantityList);

+    }

+    // ca_comparison_quantity

+    if (isBitSet(readFlags, FLAG_COMPARISON_QUANTITY_DEFINED)) {

+      List<Long> lnkComparisonQuantityList = new ArrayList<>();

+      for (long i = 0; i < 3; i++) {

+        lnkComparisonQuantityList.add(lnks[currentLinkIndex++]);

+      }

+      block.setLnksComparisonQuantity(lnkComparisonQuantityList);

+    }

+    // ca_cc_axis_conversion

+    if (isBitSet(readFlags, FLAG_SCALING_AXIS_DEFINED)) {

+      List<Long> lnkConversionRuleList = new ArrayList<>();

+      for (long i = 0; i < nrOfDims; i++) {

+        lnkConversionRuleList.add(lnks[currentLinkIndex++]);

+      }

+      block.setLnksConversionRule(lnkConversionRuleList);

+    }

+    // ca_axis

+    if (isBitSet(readFlags, FLAG_SCALING_AXIS_DEFINED) && !isBitSet(readFlags, FLAG_FIXED_AXES)) {

+      List<Long> lnkScalingAxisList = new ArrayList<>();

+      for (long i = 0; i < nrOfDims * 3; i++) {

+        lnkScalingAxisList.add(lnks[currentLinkIndex++]);

+      }

+      block.setLnksScalingAxis(lnkScalingAxisList);

+    }

+    

+    // throw exception if found unsupported features

+    if (lnkNestedComposition > 0) {

+      throw new IOException("Nested composition is not yet supported! Found at CABlock: [" + block + "]");

+    }

+    if (isBitSet(arrayType, ARRAYTYPE_INTERVAL_AXIS)) {

+      throw new IOException("Array type 'Interval Axis' is not yet supported! Found at CABlock: [" + block + "]");

+    }

+    else if (isBitSet(arrayType, ARRAYTYPE_CLASSIFICATION_RESULT)) {

+      throw new IOException("Array type 'Classification Result' is not yet supported! Found at CABlock: [" + block + "]");

+    }

+    if (isBitSet(storageTyp, STORAGETYPE_CG_TEMPLATE)) {

+      throw new IOException("Storage type 'CG Template' is not yet supported! Found at CABlock: [" + block + "]");

+    }

+    else if (isBitSet(storageTyp, STORAGETYPE_DG_TEMPLATE)) {

+      throw new IOException("Storage type 'DG Template' is not yet supported! Found at CABlock: [" + block + "]");

+    }

+

+    return block;

+  }

+  

+  private void setArrayType(byte arrayType) {

+    this.arrayType = arrayType;

+  }

+  

+  private void setStorageType(byte storageType) {

+    this.storageType = storageType;

+  }

+  

+  private void setNrOfDimensions(int nrOfDimensions) {

+    this.nrOfDimensions = nrOfDimensions;

+  }

+  

+  private void setFlags(long flags) {

+    this.flags = flags;

+  }

+  

+  private void setByteOffsetBase(long byteOffsetBase) {

+    this.byteOffsetBase = byteOffsetBase;

+  }

+  

+  private void setInvalidationBitPositionBase(long invalBitPosBase) {

+    this.invalBitPosBase = invalBitPosBase;

+  }

+  

+  private void setDimSizes(List<Long> dimSizesList) {

+    this.dimSizes = dimSizesList.stream().mapToLong(i -> i).toArray();

+  }

+  

+  private void setAxisValues(List<Double> axisValues) {

+    this.axisValues = axisValues.stream().mapToDouble(i -> i).toArray();

+  }

+  

+  private void setCycleCounts(List<Long> cycleCounts) {

+    this.cycleCounts = cycleCounts.stream().mapToLong(i -> i).toArray();

+  }

+  

+  private void setLnkComposition(long lnkComposition) {

+    this.lnkComposition = lnkComposition;

+  }

+  

+  private void setLnksData(List<Long> lnksData) {

+    this.lnksData = lnksData.stream().mapToLong(i -> i).toArray();

+  }

+  

+  private void setLnksDynamicSize(List<Long> lnksDynamicSize) {

+    this.lnksDynamicSize = lnksDynamicSize.stream().mapToLong(i -> i).toArray();

+  }

+  

+  private void setLnksInputQuantities(List<Long> lnksInputQuantities) {

+    this.lnksInputQuantity = lnksInputQuantities.stream().mapToLong(i -> i).toArray();

+  }

+  

+  private void setLnksOutputQuantity(List<Long> lnksOutputQuantity) {

+    this.lnksOutputQuantity = lnksOutputQuantity.stream().mapToLong(i -> i).toArray();

+  }

+  

+  private void setLnksComparisonQuantity(List<Long> lnksComparisonQuantity) {

+    this.lnksComparisonQuantity = lnksComparisonQuantity.stream().mapToLong(i -> i).toArray();

+  }

+  

+  private void setLnksConversionRule(List<Long> lnksConversionRule) {

+    this.lnksConversionRule = lnksConversionRule.stream().mapToLong(i -> i).toArray();

+  }

+  

+  private void setLnksScalingAxis(List<Long> lnksScalingAxis) {

+    this.lnksScalingAxis = lnksScalingAxis.stream().mapToLong(i -> i).toArray();

+  }

+  

+  /**

+   * @return the arrayType

+   */

+  public byte getArrayType()

+  {

+    return arrayType;

+  }

+

+  /**

+   * @return the storageType

+   */

+  public byte getStorageType()

+  {

+    return storageType;

+  }

+

+  /**

+   * @return the flags

+   */

+  public long getFlags()

+  {

+    return flags;

+  }

+

+  /**

+   * @return the byteOffsetBase

+   */

+  public long getByteOffsetBase()

+  {

+    return byteOffsetBase;

+  }

+

+  /**

+   * @return the invalBitPosBase

+   */

+  public long getInvalBitPosBase()

+  {

+    return invalBitPosBase;

+  }

+

+  /**

+   * @return the dimSize for given dimension index

+   */

+  public long getDimSize(int dimIndex)

+  {

+    return dimSizes[dimIndex];

+  }

+  

+  /**

+   * @return all value sizes of dimensions

+   */

+  public long[] getDimSizes()

+  {

+    return dimSizes;

+  }

+

+  /**

+   * @return the nrOfDimensions

+   */

+  public int getNrOfDimensions()

+  {

+    return nrOfDimensions;

+  }

+

+  /**

+   * @return the axisValues

+   */

+  public double[] getAxisValues()

+  {

+    return axisValues;

+  }

+

+  /**

+   * @return the cycleCounts

+   */

+  public long[] getCycleCounts()

+  {

+    return cycleCounts;

+  }

+  

+  public BLOCK getCompositionBlock() throws IOException

+  {

+    if (lnkComposition > 0) {

+      String blockType = getBlockType(sbc, lnkComposition);

+      BLOCK compositionBlock = null;

+      // link points to a CNBLOCK

+      if (blockType.equals(CNBLOCK.BLOCK_ID)) {

+        compositionBlock = CNBLOCK.read(sbc, lnkComposition);

+      }

+      // link points to CABLOCK

+      else if (blockType.equals(CABLOCK.BLOCK_ID)) {

+        compositionBlock = CABLOCK.read(sbc, lnkComposition);

+      }

+      // unknown

+      else {

+        throw new IOException("Unsupported block type for Composition: " + blockType);

+      }

+      

+      throw new IOException("Composition of CABLOCKs is not yet supported! Found the following block: " + compositionBlock);

+    }

+    return null;

+  }

+

+  /**

+   * Checks if the given bitConstant is set in the given bitfield.

+   * 

+   * @param bits

+   * @param bitConstant

+   * @return

+   */

+  private static boolean isBitSet(long bits, byte bitConstant) {

+    return (bits & bitConstant) != 0;

+  }

+  

+  /**

+   * Returns the requested flag value.

+   * 

+   * @param flagConstant one of the flag constants given in this class

+   * @return

+   */

+  public boolean isFlagSet(byte flagConstant) {

+    return (flags & flagConstant) != 0;

+  }

+  

+  /**

+   * This method should only be called if the flag FLAG_SCALING_AXIS_DEFINED is

+   * set. If this is true and null is returned here, it 

+   * 

+   * @param dimIndex

+   * @return

+   */

+  public long getConversionRuleOffsetForDimension(int dimIndex)

+  {

+    if (lnksConversionRule != null && dimIndex < nrOfDimensions && lnksConversionRule.length == 3 * nrOfDimensions)

+    {

+      return lnksConversionRule[nrOfDimensions * 3 - 1];

+    }

+    return -1;

+  }

+  

+  /**

+   * This method should only be called if the flag FLAG_SCALING_AXIS_DEFINED is

+   * set and FLAG_FIXED_AXIS is not set.

+   * 

+   * @param dimIndex

+   *        the index of the dimension to get the scaling axis offsets for

+   * @return a {@link BlockReferenceTriple} containing the offsets to read the

+   *         CNBLOCK defining the scaling axis for the dimension with the

+   *         given index or null if none defined

+   */

+  public BlockReferenceTriple getScalingAxisOffsetsForDimension(int dimIndex)

+  {

+    return createReferenceTriple(dimIndex, lnksScalingAxis);

+  }

+  

+  /**

+   * This method should only be called if the flag FLAG_INPUT_QUANTITY_DEFINED

+   * is set.

+   * 

+   * @param dimIndex

+   *        the index of the dimension to get the input quantity offsets for

+   * @return a {@link BlockReferenceTriple} containing the offsets to read the

+   *         CNBLOCK defining the input quantity for the dimension with the

+   *         given index or null if none defined

+   */

+  public BlockReferenceTriple getInputQuantityOffsetsForDimension(int dimIndex)

+  {

+    return createReferenceTriple(dimIndex, lnksInputQuantity);

+  }

+  

+  /**

+   * This method should only be called if the flag FLAG_OUTPUR_QUANTITY_DEFINED

+   * is set.

+   * 

+   * @param dimIndex

+   *        the index of the dimension to get the output quantity offsets for

+   * @return a {@link BlockReferenceTriple} containing the offsets to read the

+   *         CNBLOCK defining the output quantity for the dimension with the

+   *         given index or null if none defined

+   */

+  public BlockReferenceTriple getOutputQuantityOffsetsForDimension(int dimIndex)

+  {

+    return createReferenceTriple(dimIndex, lnksOutputQuantity);

+  }

+  

+  /**

+   * This method should only be called if the flag

+   * FLAG_COMPARISON_QUANTITY_DEFINED is set.

+   * 

+   * @param dimIndex

+   *        the index of the dimension to get the comparison quantity offsets

+   *        for

+   * @return a {@link BlockReferenceTriple} containing the offsets to read the

+   *         CNBLOCK defining the comparison quantity for the dimension with the

+   *         given index or null if none defined

+   */

+  public BlockReferenceTriple getComparisonQuantityOffsetsForDimension(int dimIndex)

+  {

+    return createReferenceTriple(dimIndex, lnksComparisonQuantity);

+  }

+  

+  /**

+   * This method should only be called if the flag FLAG_DYNAMIC_SIZE is set.

+   * 

+   * @param dimIndex

+   *        the index of the dimension to get the dynamic size signal offsets

+   *        for

+   * @return a {@link BlockReferenceTriple} containing the offsets to read the

+   *         CNBLOCK defining the dynamic size signal for the dimension with the

+   *         given index or null if none defined

+   */

+  public BlockReferenceTriple getSizeSignalOffsetsForDimension(int dimIndex)

+  {

+    return createReferenceTriple(dimIndex, lnksDynamicSize);

+  }

+  

+  /**

+   * Creates a {@link BlockReferenceTriple} from the given offsets for the given

+   * dimension index.

+   * 

+   * @param dimIndex

+   *        the index of the dimension to get the offset triple for

+   * @param offsets

+   *        the offset structure to read from

+   * @return a {@link BlockReferenceTriple} containing the requested offsets or

+   *         null if none defined

+   */

+  private BlockReferenceTriple createReferenceTriple(int dimIndex, long[] offsets)

+  {

+    if (offsets != null && dimIndex < nrOfDimensions && offsets.length == 3 * nrOfDimensions)

+    {

+      return new BlockReferenceTriple(offsets[dimIndex * 3 + 0], offsets[dimIndex * 3 + 1], offsets[dimIndex * 3 + 2]);

+    }

+    return null;

+  }

+  

+  /**

+   * @see java.lang.Object#toString()

+   *

+   * @return

+   */

+  @Override

+  public String toString()

+  {

+    return "CABLOCK [lnkComposition=" + lnkComposition + ", lnksData=" + Arrays.toString(lnksData)

+        + ", lnksDynamicSize=" + Arrays.toString(lnksDynamicSize) + ", lnksInputQuantities="

+        + Arrays.toString(lnksInputQuantity) + ", lnksOutputQuantity=" + Arrays.toString(lnksOutputQuantity)

+        + ", lnksComparisonQuantity=" + Arrays.toString(lnksComparisonQuantity) + ", lnksScalingAxis="

+        + Arrays.toString(lnksConversionRule) + ", lnksAxis=" + Arrays.toString(lnksScalingAxis) + ", arrayType=" + arrayType

+        + ", storageType=" + storageType + ", flags=" + flags + ", byteOffsetBase=" + byteOffsetBase

+        + ", invalBitPosBase=" + invalBitPosBase + ", dimSizes=" + Arrays.toString(dimSizes) + ", nrOfDimensions="

+        + nrOfDimensions + ", axisValues=" + Arrays.toString(axisValues) + ", cycleCounts="

+        + Arrays.toString(cycleCounts) + "]";

+  }

+}

diff --git a/src/main/java/org/eclipse/mdm/openatfx/mdf/mdf4/CCBLOCK.java b/src/main/java/org/eclipse/mdm/openatfx/mdf/mdf4/CCBLOCK.java
index 4401cae..3099d0f 100644
--- a/src/main/java/org/eclipse/mdm/openatfx/mdf/mdf4/CCBLOCK.java
+++ b/src/main/java/org/eclipse/mdm/openatfx/mdf/mdf4/CCBLOCK.java
@@ -121,7 +121,9 @@
 	// List of additional conversion parameters.
 	// Length of list is given by cc_val_count. The list can be empty.
 	// REAL N
-	private double[] val;
+	private double[] val;

+	

+	private CCBLOCK defaultValueBlock;
 
 	/**
 	 * Constructor.
@@ -306,17 +308,15 @@
 			TXBLOCK[] ccRef = new TXBLOCK[lnkCcRef.length];
 			for (int i = 0; i < ccRef.length; i++) {
 				if (lnkCcRef[i] > 0) {
-					// There might be a CC Block, but this is not supported.
-
 					String blockType = getBlockType(sbc, lnkCcRef[i]);
 					// link points to a TXBLOCK
 					if (blockType.equals(TXBLOCK.BLOCK_ID)) {
 						ccRef[i] = TXBLOCK.read(sbc, lnkCcRef[i]);
 					}
-					// links points to CCBLOCK
-					else if (blockType.equals(CCBLOCK.BLOCK_ID)) {
-						throw new IOException("Scale conversions with CCBlocks are not supported.");
-					}
+          // link points to CCBLOCK containing formula for default value

+          else if (blockType.equals(CCBLOCK.BLOCK_ID) && defaultValueBlock == null) {

+            defaultValueBlock = CCBLOCK.read(sbc, lnkCcRef[i]);

+          }
 					// unknown
 					else {
 						throw new IOException("Unsupported block type for Conversion Reference: " + blockType);
@@ -514,12 +514,14 @@
 		}
 	}
 
-	/**
-	 * Returns the default value for this conversion.
-	 * 
-	 * @return The default value.
-	 * @throws IOException
-	 */
+	/**

+   * Returns the default value for this conversion. For types 7, 8 and 10 the

+   * default value is specified at last cc_ref value and it may be not defined,

+   * if no default value is available.

+   * 

+   * @return The default value.

+   * @throws IOException

+   */
 	public String getDefaultValue() throws IOException {
 		if (type == 7 || type == 8 || type == 10) {
 			long lnkLastRef = lnkCcRef[getRefCount() - 1];
@@ -529,14 +531,17 @@
 				// link points to a TXBLOCK, return content.
 				if (blockType.equals(TXBLOCK.BLOCK_ID)) {
 					return TXBLOCK.read(sbc, lnkLastRef).getTxData();
-				}
-				// links points to CCBLOCK
-				else if (blockType.equals(CCBLOCK.BLOCK_ID)) {
-					throw new IOException("Scale conversions with CCBlocks are not supported.");
-				}
-				// unknown
+        }

+        // link points to CCBLOCK

+        else if (blockType.equals(CCBLOCK.BLOCK_ID)) {

+          if (defaultValueBlock == null) {

+            defaultValueBlock = CCBLOCK.read(sbc, lnkLastRef);

+          }

+//          throw new IOException("Scale conversions with CCBlocks are not supported by ASAM ODS.");

+        }

+        // unknown
 				else {
-					throw new IOException("Unsupported block type for Conversion Reference: " + blockType);
+					throw new IOException("Unsupported block type for Default Value Conversion Reference: " + blockType);
 				}
 			}
 		}
diff --git a/src/main/java/org/eclipse/mdm/openatfx/mdf/mdf4/CNBLOCK.java b/src/main/java/org/eclipse/mdm/openatfx/mdf/mdf4/CNBLOCK.java
index 8ae8940..6d7b3e1 100644
--- a/src/main/java/org/eclipse/mdm/openatfx/mdf/mdf4/CNBLOCK.java
+++ b/src/main/java/org/eclipse/mdm/openatfx/mdf/mdf4/CNBLOCK.java
@@ -553,6 +553,31 @@
 		}
 		return null;
 	}
+

+  /**

+   * Returns the CA- or CN-block, linked by the composition link.

+   * 

+   * @return

+   * @throws IOException

+   */

+  public BLOCK getCompositionBlock() throws IOException {

+    if (lnkComposition > 0) {

+      String blockType = getBlockType(sbc, lnkComposition);

+      // link points to a CNBLOCK

+      if (blockType.equals(CNBLOCK.BLOCK_ID)) {

+        return CNBLOCK.read(sbc, lnkComposition);

+      }

+      // link points to CABLOCK

+      else if (blockType.equals(CABLOCK.BLOCK_ID)) {

+        return CABLOCK.read(sbc, lnkComposition);

+      }

+      // unknown

+      else {

+        throw new IOException("Unsupported block type for Composition: " + blockType);

+      }

+    }

+    return null;

+  }

 
 	@Override
 	public String toString() {
diff --git a/src/main/java/org/eclipse/mdm/openatfx/mdf/mdf4/ChannelReader.java b/src/main/java/org/eclipse/mdm/openatfx/mdf/mdf4/ChannelReader.java
new file mode 100644
index 0000000..56c66e2
--- /dev/null
+++ b/src/main/java/org/eclipse/mdm/openatfx/mdf/mdf4/ChannelReader.java
@@ -0,0 +1,241 @@
+package org.eclipse.mdm.openatfx.mdf.mdf4;

+

+import java.io.IOException;

+import java.nio.Buffer;

+import java.nio.ByteBuffer;

+import java.nio.ByteOrder;

+import java.nio.channels.SeekableByteChannel;

+import java.nio.file.Files;

+import java.nio.file.Paths;

+import java.nio.file.StandardOpenOption;

+import java.util.ArrayList;

+import java.util.List;

+import java.util.stream.Collectors;

+

+import org.apache.commons.logging.Log;

+import org.apache.commons.logging.LogFactory;

+

+public class ChannelReader

+{

+  private static final Log LOG = LogFactory.getLog(ChannelReader.class);

+  private static final int MAX_BUFFER_SIZE = 2048;

+  private final DGBLOCK dgBlock;

+  private final CGBLOCK cgBlock;

+  private final CNBLOCK cnBlock;

+  

+  public ChannelReader(String mdfPath, long dgOffset, long cgOffset, long cnOffset)

+  {

+    SeekableByteChannel sbc;

+    try

+    {

+      sbc = Files.newByteChannel(Paths.get(mdfPath), StandardOpenOption.READ);

+      dgBlock = DGBLOCK.read(sbc, dgOffset);

+      cgBlock = CGBLOCK.read(sbc, cgOffset);

+      cnBlock = CNBLOCK.read(sbc, cnOffset);

+    }

+    catch (IOException e)

+    {

+      throw new RuntimeException("Error reading from mdf file: " + e.getLocalizedMessage());

+    }

+  }

+  

+  public static void main(String[] args)

+  {

+    if (args.length != 4)

+    {

+      LOG.error("Wrong arguments: Must be called with the mdf filepath and the offsets for DGBLOCK, CGBLOCK and CNBLOCK!");

+    }

+    else

+    {

+      String mdfPath = null;

+      long dgOffset = -1;

+      long cgOffset = -1;

+      long cnOffset = -1;

+      for (int i = 0; i < 4; i++)

+      {

+        String arg = args[i];

+        switch (i)

+        {

+        case 0:

+          mdfPath = arg.trim();

+          break;

+

+        case 1:

+          dgOffset = Long.valueOf(arg);

+          break;

+          

+        case 2:

+          cgOffset = Long.valueOf(arg);

+          break;

+          

+        case 3:

+          cnOffset = Long.valueOf(arg);

+          break;

+          

+        default:

+          break;

+        }

+      }

+      

+      ChannelReader reader = new ChannelReader(mdfPath, dgOffset, cgOffset, cnOffset);

+      try

+      {

+        LOG.info(reader.readValues());

+      }

+      catch (IOException e)

+      {

+        LOG.error("Error reading channel values: " + e.getLocalizedMessage());

+      }

+    }

+  }

+

+  public String readValues() throws IOException

+  {

+    DTBLOCK dataBlock = DTBLOCK.read(dgBlock.sbc, dgBlock.getLnkData());

+    long startDataOffset = dgBlock.getLnkData() + 24; // header of DTBLOCK is 24 bytes long

+    long dataBlockLength = dataBlock.getLength();

+    

+    long position = dgBlock.getLnkData() + 24 + cnBlock.getByteOffset();

+    switch (cnBlock.getDataType())

+    {

+    case 0:

+      List<Long> values0 = readIntsFromByteChannel(true, true, (int)cnBlock.getBitCount(), cnBlock.sbc, position, (int)cgBlock.getCycleCount());

+      return values0.stream().map(String::valueOf).collect(Collectors.joining(", "));

+    case 1:

+      List<Long> values1 = readIntsFromByteChannel(true, false, (int)cnBlock.getBitCount(), cnBlock.sbc, position, (int)cgBlock.getCycleCount());

+      return values1.stream().map(String::valueOf).collect(Collectors.joining(", "));

+    case 2:

+      List<Long> values2 = readIntsFromByteChannel(false, true, (int)cnBlock.getBitCount(), cnBlock.sbc, position, (int)cgBlock.getCycleCount());

+      return values2.stream().map(String::valueOf).collect(Collectors.joining(", "));

+    case 3:

+      List<Long> values3 = readIntsFromByteChannel(false, false, (int)cnBlock.getBitCount(), cnBlock.sbc, position, (int)cgBlock.getCycleCount());

+      return values3.stream().map(String::valueOf).collect(Collectors.joining(", "));

+    case 4:

+      List<Double> values4 = readRealsFromByteChannel(cnBlock.sbc, true, position, (int)cgBlock.getCycleCount());

+      return values4.stream().map(String::valueOf).collect(Collectors.joining(", "));

+    case 5:

+      List<Double> values5 = readRealsFromByteChannel(cnBlock.sbc, false, position, (int)cgBlock.getCycleCount());

+      return values5.stream().map(String::valueOf).collect(Collectors.joining(", "));

+    default:

+      throw new RuntimeException("readValues() not supported yet for String or complex datatypes!");

+    }

+  }

+  

+  public List<Long> readIntsFromByteChannel(boolean unsigned, boolean littleEndian, int bits, SeekableByteChannel channel, long pos, int readCount) throws IOException

+  {

+    ByteBuffer bb = ByteBuffer.allocate(bits / 8 * readCount);

+    if (littleEndian)

+    {

+      bb.order(ByteOrder.LITTLE_ENDIAN);

+    }

+    else

+    {

+      bb.order(ByteOrder.BIG_ENDIAN);

+    }

+    channel.position(pos);

+    channel.read(bb);

+    bb.rewind();

+    

+    List<Long> readValues = new ArrayList<>();

+    for (int i = 0; i < readCount; i++)

+    {

+      switch (bits)

+      {

+      case 8:

+        readValues.add((long) MDF4Util.readUInt8(bb));

+        break;

+      case 16:

+        if (unsigned)

+        {

+          readValues.add((long) MDF4Util.readUInt16(bb));

+        }

+        else

+        {

+          readValues.add((long) MDF4Util.readInt16(bb));

+        }

+        break;

+      case 32:

+        if (unsigned)

+        {

+          readValues.add(MDF4Util.readUInt32(bb));

+        }

+        else

+        {

+          readValues.add((long) MDF4Util.readInt32(bb));

+        }

+        break;

+      case 64:

+        if (unsigned)

+        {

+          readValues.add(MDF4Util.readUInt64(bb));

+        }

+        else

+        {

+          readValues.add(MDF4Util.readInt64(bb));

+        }

+        break;

+      default:

+        break;

+      }

+    }

+    

+    return readValues;

+  }

+  

+  public List<Double> readRealsFromByteChannel(SeekableByteChannel channel, boolean littleEndian, long pos, int readCount) throws IOException

+  {

+    ByteBuffer bb = ByteBuffer.allocate(8 * readCount);

+    if (littleEndian)

+    {

+      bb.order(ByteOrder.LITTLE_ENDIAN);

+    }

+    else

+    {

+      bb.order(ByteOrder.BIG_ENDIAN);

+    }

+    channel.position(pos);

+    channel.read(bb);

+    bb.rewind();

+    

+    List<Double> readValues = new ArrayList<>();

+    for (int i = 0; i < readCount; i++)

+    {

+      readValues.add(MDF4Util.readReal(bb));

+    }

+    

+    return readValues;

+  }

+  

+  public void temp(short bitCount, short bitOffset, ByteBuffer sourceMbb)

+  {

+    // read that number of bytes from the byte position within the file

+    int bytesToRead = ((bitCount + bitOffset - 1) / 8) + 1;

+    byte[] tmp = new byte[bytesToRead];

+    sourceMbb.get(tmp);

+

+    // put the byte into an integer to enable bit shifting

+    if (tmp.length <= 4) {

+        ByteBuffer bb = ByteBuffer.allocate(4);

+        bb.order(sourceMbb.order());

+        bb.put(tmp);

+        Buffer.class.cast(bb).rewind(); // workaround: make buildable with both java8 and java9

+        int intValue = bb.getInt();

+        intValue = intValue >> bitOffset; // shift right bit offset

+        int mask = 0xFFFFFFFF >>> (32 - bitCount); // mask out unnecessary bits

+        intValue = intValue & mask;

+//        list.add(intValue);

+    }

+    // put the byte into a long to enable bit shifting

+    else {

+        ByteBuffer bb = ByteBuffer.allocate(8);

+        bb.order(sourceMbb.order());

+        bb.put(tmp);

+        Buffer.class.cast(bb).rewind(); // workaround: make buildable with both java8 and java9

+        long longValue = bb.getLong();

+        longValue = longValue >> bitOffset; // shift right bit offset

+        long mask = 0xFFFFFFFFFFFFFFFFl >>> (64 - bitCount); // mask out unnecessary bits

+        longValue = longValue & mask;

+//        list.add(longValue);

+    }

+  }

+}

diff --git a/src/main/java/org/eclipse/mdm/openatfx/mdf/mdf4/MDF4Util.java b/src/main/java/org/eclipse/mdm/openatfx/mdf/mdf4/MDF4Util.java
index 10f8be1..d181986 100644
--- a/src/main/java/org/eclipse/mdm/openatfx/mdf/mdf4/MDF4Util.java
+++ b/src/main/java/org/eclipse/mdm/openatfx/mdf/mdf4/MDF4Util.java
@@ -15,15 +15,15 @@
 
 package org.eclipse.mdm.openatfx.mdf.mdf4;
 
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.Date;
-import java.util.Iterator;
-import java.util.Map.Entry;
-import java.util.Properties;
-
-import org.eclipse.mdm.openatfx.mdf.util.ODSHelper;
+import java.io.IOException;

+import java.nio.ByteBuffer;

+import java.nio.ByteOrder;

+import java.util.Date;

+import java.util.Iterator;

+import java.util.Map.Entry;

+import java.util.Properties;

+

+import org.eclipse.mdm.openatfx.mdf.util.ODSHelper;

 import org.eclipse.mdm.openatfx.mdf.util.ODSInsertStatement;
 
 /**
diff --git a/src/main/java/org/eclipse/mdm/openatfx/mdf/mdf4/PreviewHelper.java b/src/main/java/org/eclipse/mdm/openatfx/mdf/mdf4/PreviewHelper.java
index 1348eb8..cf18fba 100644
--- a/src/main/java/org/eclipse/mdm/openatfx/mdf/mdf4/PreviewHelper.java
+++ b/src/main/java/org/eclipse/mdm/openatfx/mdf/mdf4/PreviewHelper.java
@@ -11,211 +11,211 @@
  * SPDX-License-Identifier: EPL-2.0

  *

  ********************************************************************************/

-
-
-package org.eclipse.mdm.openatfx.mdf.mdf4;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.Map;
-
-import org.asam.ods.AoException;
-import org.asam.ods.InstanceElement;
-import org.asam.ods.InstanceElementIterator;
-import org.asam.ods.Relationship;
-import org.eclipse.mdm.openatfx.mdf.util.ODSHelper;
-import org.eclipse.mdm.openatfx.mdf.util.ODSInsertStatement;
-import org.eclipse.mdm.openatfx.mdf.util.ODSModelCache;
-
-/**
- * Helper class to convert Sample Reduction blocks to a ASAM ODS
- * 'AoMeasurement'.
- *
- * @author Tobias Leemann
- */
-class PreviewHelper {
-
-	// the cached lookup instance element
-	private long previewMeaiid = Long.MIN_VALUE;
-	private ODSModelCache cache;
-	private AoSessionWriter writer;
-
-	// The SRBLOCKs used.
-	private SRBLOCK[] srBlocks;
-
-	// The smIids in use.
-	private Long[] smIids;
-
-	private Map<String, Long> meqInstances = new HashMap<String, Long>();
-
-	private int generationCount = 0;
-
-	public synchronized void setCache(ODSModelCache cache) {
-		this.cache = cache;
-	}
-
-	public synchronized void setWriter(AoSessionWriter writer) {
-		this.writer = writer;
-	}
-
-	/**
-	 * Creates a preview Measurment if none exists.
-	 * 
-	 * @param ieMea
-	 *            The InstanceElement of the Main Measurement
-	 * @throws AoException
-	 *             If an ASAM ODS error occurs.
-	 */
-	private synchronized void createMeasurementIfNeeded(InstanceElement ieMea) throws AoException {
-		// create 'AoMeasurement' instance (if not yet existing)
-		if (previewMeaiid == Long.MIN_VALUE) {
-			// lookup parent 'AoTest' instance
-			InstanceElementIterator iter = ieMea.getRelatedInstancesByRelationship(Relationship.FATHER, "*");
-			InstanceElement ieTst = iter.nextOne();
-			iter.destroy();
-			String meaName = ieMea.getName() + "_previews";
-			ODSInsertStatement ins = new ODSInsertStatement(cache, "mea");
-			ins.setStringVal("iname", meaName);
-			ins.setStringVal("mt", "application/x-asam.aomeasurement.mdf_preview");
-			ins.setNameValueUnit(ieMea.getValue("date_created"));
-			ins.setNameValueUnit(ieMea.getValue("mea_begin"));
-			ins.setNameValueUnit(ieMea.getValue("mea_end"));
-			ins.setLongLongVal("tst", ODSHelper.asJLong(ieTst.getId()));
-			previewMeaiid = ins.execute();
-		}
-	}
-
-	/**
-	 * Creates Previews for a Channel in all SubMatrix elements created by
-	 * createPreviewSubMatrices. This method must be invoked after
-	 * createPreviewSubMatrices.
-	 * 
-	 * @param channelName
-	 *            The name of the Channel the preview will be created for.
-	 * @param idBlock
-	 *            The IDBLOCK.
-	 * @param cgBlock
-	 *            The CGBLOCK of the Channel.
-	 * @param dgBlock
-	 *            The DGBLOCK of the Channel.
-	 * @param cnBlock
-	 *            The CNBLOCK of this Channel.
-	 * @param ccBlock
-	 *            The CCBLOCK of the Channel.
-	 * @param untInstances
-	 *            MapContaining all existing Units.
-	 * @throws AoException
-	 * @throws IOException
-	 */
-	public synchronized void createPreviewChannels(String channelName, IDBLOCK idBlock, CGBLOCK cgBlock,
-			DGBLOCK dgBlock, CNBLOCK cnBlock, CCBLOCK ccBlock, Map<String, Long> untInstances)
-			throws AoException, IOException {
-		if (smIids == null) {
-			throw new IOException("Preview IIDS not set.");
-		}
-		if (srBlocks == null) {
-			throw new IOException("Preview srBlocks not set.");
-		}
-		if (writer == null) {
-			throw new IOException("Preview writer not set.");
-		}
-		String[] nameExtensions = { "average", "maximum", "minimum" };
-		for (int reductionNo = 0; reductionNo < srBlocks.length; reductionNo++) {
-			for (int i = 0; i < 3; i++) {
-				String extendedName = channelName + "_" + nameExtensions[i];
-				// create MeasurmentQuantity if needed
-				Long iidMeq = meqInstances.get(extendedName);
-				if (iidMeq == null) {
-					iidMeq = writer.createMeasurementQuantity(cache, cnBlock, ccBlock, extendedName, previewMeaiid,
-							"application/x-asam.aomeasurementquantity.mdf_preview." + nameExtensions[i], untInstances);
-					meqInstances.put(extendedName, iidMeq);
-				}
-				// create AoLocalColumns
-				long iidLc = writer.createLocalColumn(cache, dgBlock, cgBlock, cnBlock, ccBlock, extendedName,
-						smIids[reductionNo], iidMeq, null,
-						"application/x-asam.aolocalcolumn.mdf_preview." + nameExtensions[i]);
-
-				// create AoExternalComponents
-				writer.writeEc(cache, iidLc, idBlock, dgBlock, cgBlock, cnBlock, ccBlock,
-						srBlocks[reductionNo].getLnkRdData(), i + 1);
-			}
-		}
-
-	}
-
-	/**
-	 * Create SubMatices for all SRBLOCKs linked from <code>srBlock</code> This
-	 * method must be called before <code>createPreviewChannels()</code>.
-	 * 
-	 * @param ieMea
-	 *            The instance element of the main Measurement.
-	 * @param srBlock
-	 *            The first SRBLOCK in the list.
-	 * @return The IDs of the created SubMatrix elements.
-	 * @throws AoException
-	 * @throws IOException
-	 */
-	public synchronized long[] createPreviewSubMatrices(InstanceElement ieMea, SRBLOCK srBlock)
-			throws AoException, IOException {
-		if (srBlock == null) {
-			return null;
-			// nothing to do
-		}
-
-		createMeasurementIfNeeded(ieMea);
-
-		// create AoSubMatrix instance
-		LinkedList<SRBLOCK> srBlocks = new LinkedList<SRBLOCK>();
-		LinkedList<Long> iids = new LinkedList<Long>();
-		while (srBlock != null) {
-			ODSInsertStatement ins = new ODSInsertStatement(cache, "sm");
-			ins.setStringVal("iname", getPrevName(srBlock));
-			ins.setStringVal("mt", "application/x-asam.aosubmatrix.mdf_preview");
-			ins.setLongVal("rows", (int) srBlock.getCycleCount());
-			ins.setLongLongVal("mea", previewMeaiid);
-			long smIid = ins.execute();
-			srBlocks.add(srBlock);
-			iids.add(smIid);
-			srBlock = srBlock.getSrNextBlock();
-		}
-		this.srBlocks = srBlocks.toArray(new SRBLOCK[0]);
-		smIids = iids.toArray(new Long[0]);
-		generationCount++;
-		long[] ret = new long[smIids.length];
-		for (int i = 0; i < smIids.length; i++) {
-			ret[i] = smIids[i];
-		}
-		return ret;
-	}
-
-	/**
-	 * Create a unique Channel name from an SRBLOCK.
-	 * 
-	 * @param srBlock
-	 *            The SRBLOCK
-	 * @return The name.
-	 */
-	private String getPrevName(SRBLOCK srBlock) {
-		String ret = "reduction_group" + generationCount + "_";
-		ret += String.valueOf(srBlock.getInterval());
-		switch (srBlock.getSyncType()) {
-		case 1:
-			ret += "s";
-			break;
-		case 2:
-			ret += "rad";
-			break;
-		case 3:
-			ret += "m";
-			break;
-		case 4:
-			ret += "records";
-			break;
-		default:
-			break;
-		}
-		return ret;
-	}
-
-}
+

+

+package org.eclipse.mdm.openatfx.mdf.mdf4;

+

+import java.io.IOException;

+import java.util.HashMap;

+import java.util.LinkedList;

+import java.util.Map;

+

+import org.asam.ods.AoException;

+import org.asam.ods.InstanceElement;

+import org.asam.ods.InstanceElementIterator;

+import org.asam.ods.Relationship;

+import org.eclipse.mdm.openatfx.mdf.util.ODSHelper;

+import org.eclipse.mdm.openatfx.mdf.util.ODSInsertStatement;

+import org.eclipse.mdm.openatfx.mdf.util.ODSModelCache;

+

+/**

+ * Helper class to convert Sample Reduction blocks to a ASAM ODS

+ * 'AoMeasurement'.

+ *

+ * @author Tobias Leemann

+ */

+class PreviewHelper {

+

+	// the cached lookup instance element

+	private long previewMeaiid = Long.MIN_VALUE;

+	private ODSModelCache cache;

+	private AoSessionWriter writer;

+

+	// The SRBLOCKs used.

+	private SRBLOCK[] srBlocks;

+

+	// The smIids in use.

+	private Long[] smIids;

+

+	private Map<String, Long> meqInstances = new HashMap<String, Long>();

+

+	private int generationCount = 0;

+

+	public synchronized void setCache(ODSModelCache cache) {

+		this.cache = cache;

+	}

+

+	public synchronized void setWriter(AoSessionWriter writer) {

+		this.writer = writer;

+	}

+

+	/**

+	 * Creates a preview Measurment if none exists.

+	 * 

+	 * @param ieMea

+	 *            The InstanceElement of the Main Measurement

+	 * @throws AoException

+	 *             If an ASAM ODS error occurs.

+	 */

+	private synchronized void createMeasurementIfNeeded(InstanceElement ieMea) throws AoException {

+		// create 'AoMeasurement' instance (if not yet existing)

+		if (previewMeaiid == Long.MIN_VALUE) {

+			// lookup parent 'AoTest' instance

+			InstanceElementIterator iter = ieMea.getRelatedInstancesByRelationship(Relationship.FATHER, "*");

+			InstanceElement ieTst = iter.nextOne();

+			iter.destroy();

+			String meaName = ieMea.getName() + "_previews";

+			ODSInsertStatement ins = new ODSInsertStatement(cache, "mea");

+			ins.setStringVal("iname", meaName);

+			ins.setStringVal("mt", "application/x-asam.aomeasurement.mdf_preview");

+			ins.setNameValueUnit(ieMea.getValue("date_created"));

+			ins.setNameValueUnit(ieMea.getValue("mea_begin"));

+			ins.setNameValueUnit(ieMea.getValue("mea_end"));

+			ins.setLongLongVal("tst", ODSHelper.asJLong(ieTst.getId()));

+			previewMeaiid = ins.execute();

+		}

+	}

+

+	/**

+	 * Creates Previews for a Channel in all SubMatrix elements created by

+	 * createPreviewSubMatrices. This method must be invoked after

+	 * createPreviewSubMatrices.

+	 * 

+	 * @param channelName

+	 *            The name of the Channel the preview will be created for.

+	 * @param idBlock

+	 *            The IDBLOCK.

+	 * @param cgBlock

+	 *            The CGBLOCK of the Channel.

+	 * @param dgBlock

+	 *            The DGBLOCK of the Channel.

+	 * @param cnBlock

+	 *            The CNBLOCK of this Channel.

+	 * @param ccBlock

+	 *            The CCBLOCK of the Channel.

+	 * @param untInstances

+	 *            MapContaining all existing Units.

+	 * @throws AoException

+	 * @throws IOException

+	 */

+	public synchronized void createPreviewChannels(String channelName, IDBLOCK idBlock, CGBLOCK cgBlock,

+			DGBLOCK dgBlock, CNBLOCK cnBlock, CCBLOCK ccBlock, Map<String, Long> untInstances)

+			throws AoException, IOException {

+		if (smIids == null) {

+			throw new IOException("Preview IIDS not set.");

+		}

+		if (srBlocks == null) {

+			throw new IOException("Preview srBlocks not set.");

+		}

+		if (writer == null) {

+			throw new IOException("Preview writer not set.");

+		}

+		String[] nameExtensions = { "average", "maximum", "minimum" };

+		for (int reductionNo = 0; reductionNo < srBlocks.length; reductionNo++) {

+			for (int i = 0; i < 3; i++) {

+				String extendedName = channelName + "_" + nameExtensions[i];

+				// create MeasurmentQuantity if needed

+				Long iidMeq = meqInstances.get(extendedName);

+				if (iidMeq == null) {

+					iidMeq = writer.createMeasurementQuantity(cache, cnBlock, ccBlock, null, extendedName, previewMeaiid,

+							"application/x-asam.aomeasurementquantity.mdf_preview." + nameExtensions[i], untInstances);

+					meqInstances.put(extendedName, iidMeq);

+				}

+				// create AoLocalColumns

+				long iidLc = writer.createLocalColumn(cache, dgBlock, cgBlock, cnBlock, ccBlock, null, extendedName,

+						smIids[reductionNo], -1, iidMeq, null,

+						"application/x-asam.aolocalcolumn.mdf_preview." + nameExtensions[i]);

+

+				// create AoExternalComponents

+				writer.writeEc(cache, iidLc, idBlock, dgBlock, cgBlock, cnBlock, ccBlock, null,

+						srBlocks[reductionNo].getLnkRdData(), i + 1);

+			}

+		}

+

+	}

+

+	/**

+	 * Create SubMatices for all SRBLOCKs linked from <code>srBlock</code> This

+	 * method must be called before <code>createPreviewChannels()</code>.

+	 * 

+	 * @param ieMea

+	 *            The instance element of the main Measurement.

+	 * @param srBlock

+	 *            The first SRBLOCK in the list.

+	 * @return The IDs of the created SubMatrix elements.

+	 * @throws AoException

+	 * @throws IOException

+	 */

+	public synchronized long[] createPreviewSubMatrices(InstanceElement ieMea, SRBLOCK srBlock)

+			throws AoException, IOException {

+		if (srBlock == null) {

+			return null;

+			// nothing to do

+		}

+

+		createMeasurementIfNeeded(ieMea);

+

+		// create AoSubMatrix instance

+		LinkedList<SRBLOCK> srBlocks = new LinkedList<SRBLOCK>();

+		LinkedList<Long> iids = new LinkedList<Long>();

+		while (srBlock != null) {

+			ODSInsertStatement ins = new ODSInsertStatement(cache, "sm");

+			ins.setStringVal("iname", getPrevName(srBlock));

+			ins.setStringVal("mt", "application/x-asam.aosubmatrix.mdf_preview");

+			ins.setLongVal("rows", (int) srBlock.getCycleCount());

+			ins.setLongLongVal("mea", previewMeaiid);

+			long smIid = ins.execute();

+			srBlocks.add(srBlock);

+			iids.add(smIid);

+			srBlock = srBlock.getSrNextBlock();

+		}

+		this.srBlocks = srBlocks.toArray(new SRBLOCK[0]);

+		smIids = iids.toArray(new Long[0]);

+		generationCount++;

+		long[] ret = new long[smIids.length];

+		for (int i = 0; i < smIids.length; i++) {

+			ret[i] = smIids[i];

+		}

+		return ret;

+	}

+

+	/**

+	 * Create a unique Channel name from an SRBLOCK.

+	 * 

+	 * @param srBlock

+	 *            The SRBLOCK

+	 * @return The name.

+	 */

+	private String getPrevName(SRBLOCK srBlock) {

+		String ret = "reduction_group" + generationCount + "_";

+		ret += String.valueOf(srBlock.getInterval());

+		switch (srBlock.getSyncType()) {

+		case 1:

+			ret += "s";

+			break;

+		case 2:

+			ret += "rad";

+			break;

+		case 3:

+			ret += "m";

+			break;

+		case 4:

+			ret += "records";

+			break;

+		default:

+			break;

+		}

+		return ret;

+	}

+

+}

diff --git a/src/main/java/org/eclipse/mdm/openatfx/mdf/util/ODSHelper.java b/src/main/java/org/eclipse/mdm/openatfx/mdf/util/ODSHelper.java
index 097b5d6..d50568a 100644
--- a/src/main/java/org/eclipse/mdm/openatfx/mdf/util/ODSHelper.java
+++ b/src/main/java/org/eclipse/mdm/openatfx/mdf/util/ODSHelper.java
@@ -11,772 +11,772 @@
  * SPDX-License-Identifier: EPL-2.0

  *

  ********************************************************************************/

-
-
-package org.eclipse.mdm.openatfx.mdf.util;
-
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.asam.ods.NameValue;
-import org.asam.ods.NameValueUnit;
-import org.asam.ods.TS_Union;
-import org.asam.ods.TS_Value;
-import org.asam.ods.T_DCOMPLEX;
-import org.asam.ods.T_ExternalReference;
-import org.asam.ods.T_LONGLONG;
-
-/**
- * Helper class with ODS specific functions.
- *
- * @author Christian Rechner
- */
-public abstract class ODSHelper {
-
-	// prepare dateformats to avoid instantiation a single object every time
-	// parsing a date.
-	private static Map<Integer, DateFormat> ODS_DATEFORMATS = new HashMap<Integer, DateFormat>();
-	static {
-		ODS_DATEFORMATS.put(4, new SimpleDateFormat("yyyy"));
-		ODS_DATEFORMATS.put(6, new SimpleDateFormat("yyyyMM"));
-		ODS_DATEFORMATS.put(8, new SimpleDateFormat("yyyyMMdd"));
-		ODS_DATEFORMATS.put(10, new SimpleDateFormat("yyyyMMddHH"));
-		ODS_DATEFORMATS.put(12, new SimpleDateFormat("yyyyMMddHHmm"));
-		ODS_DATEFORMATS.put(14, new SimpleDateFormat("yyyyMMddHHmmss"));
-		ODS_DATEFORMATS.put(17, new SimpleDateFormat("yyyyMMddHHmmssSSS"));
-	}
-
-	/**
-	 * Return an ODS date from a <code>java.util.Date</code>.
-	 *
-	 * @param date
-	 *            the <code>java.util.Date</code> to convert
-	 * @return the date in ODS date-format (YYYYMMDDhhmmss)
-	 */
-	public static synchronized String asODSDate(Date date) {
-		if (date == null) {
-			return "";
-		}
-		return ODS_DATEFORMATS.get(14).format(date);
-	}
-
-	/**
-	 * Returns the java date from an ODS date.
-	 *
-	 * @param odsDate
-	 *            the ODS date string
-	 * @return the java <code>java.util.Date</code> object, null if empty date
-	 * @throws IllegalArgumentException
-	 *             unable to parse
-	 */
-	public static synchronized Date asJDate(String odsDate) {
-		try {
-			if (odsDate == null || odsDate.length() < 1) {
-				return null;
-			}
-			DateFormat format = ODS_DATEFORMATS.get(odsDate.length());
-			if (format == null) {
-				throw new IllegalArgumentException("Invalid ODS date: " + odsDate);
-			}
-			return format.parse(odsDate);
-		} catch (ParseException e) {
-			throw new IllegalArgumentException("Invalid ODS date: " + odsDate);
-		}
-	}
-
-	/**
-	 * Returns a Java long from ODS T_LONGLONG.
-	 *
-	 * @param ll
-	 *            ODS T_LONGLONG value
-	 * @return Java long with the same value as ll
-	 */
-	public static long asJLong(T_LONGLONG ll) {
-		long tmp;
-		if (ll.low >= 0) {
-			tmp = ll.high * 0x100000000L + ll.low;
-		} else {
-			tmp = (ll.high + 1) * 0x100000000L + ll.low;
-		}
-		return tmp;
-	}
-
-	/**
-	 * Returns an array of Java long from ODS T_LONGLONG.
-	 *
-	 * @param ll
-	 *            array of ODS T_LONGLONG values
-	 * @return array of Java long values
-	 */
-	public static long[] asJLong(T_LONGLONG[] ll) {
-		long[] ar = new long[ll.length];
-		for (int i = 0; i < ll.length; i++) {
-			ar[i] = asJLong(ll[i]);
-		}
-		return ar;
-	}
-
-	/**
-	 * Return ODS T_LONGLONG from Java long.
-	 *
-	 * @param v
-	 *            Java long value
-	 * @return ODS T_LONGLONG with the same value as v
-	 */
-	public static T_LONGLONG asODSLongLong(long v) {
-		return new T_LONGLONG((int) (v >> 32 & 0xffffffffL), (int) (v & 0xffffffffL));
-	}
-
-	/**
-	 * Returns an array of ODS T_LONGLONG from Java longs.
-	 *
-	 * @param v
-	 *            array of Java long values
-	 * @return array of ODS T_LONGLONG values
-	 */
-	public static T_LONGLONG[] asODSLongLong(long[] v) {
-		T_LONGLONG[] ar = new T_LONGLONG[v.length];
-		for (int i = 0; i < v.length; i++) {
-			ar[i] = asODSLongLong(v[i]);
-		}
-		return ar;
-	}
-
-	public static String getCurrentODSDate() {
-		return asODSDate(new Date());
-	}
-
-	private static NameValue createNV(String valName, TS_Union union) {
-		NameValue nv = new NameValue();
-		nv.valName = valName;
-		nv.value = new TS_Value();
-		nv.value.flag = 15;
-		nv.value.u = union;
-		return nv;
-	}
-
-	private static NameValueUnit createNVU(String attrName, TS_Union union) {
-		NameValueUnit nvu = new NameValueUnit();
-		nvu.valName = attrName;
-		nvu.value = new TS_Value();
-		nvu.unit = "";
-		nvu.value.flag = 15;
-		nvu.value.u = union;
-		return nvu;
-	}
-
-	private static NameValueUnit createNVU(String attrName, TS_Union union, String unit) {
-		NameValueUnit nvu = new NameValueUnit();
-		nvu.valName = attrName;
-		nvu.value = new TS_Value();
-		nvu.value.u = new TS_Union();
-		nvu.unit = unit;
-		nvu.value.flag = 15;
-		nvu.value.u = union;
-		return nvu;
-	}
-
-	public static NameValueUnit createCurrentDateNVU(String attrName) {
-		TS_Union union = new TS_Union();
-		union.dateVal(getCurrentODSDate());
-		return createNVU(attrName, union);
-	}
-
-	public static NameValue createStringNV(String valName, String value) {
-		NameValue nv = new NameValue();
-		nv.valName = valName;
-		nv.value = new TS_Value();
-		nv.value.u = new TS_Union();
-		if (value == null) {
-			nv.value.flag = 0;
-			nv.value.u.stringVal("");
-		} else {
-			nv.value.flag = 15;
-			nv.value.u.stringVal(value);
-		}
-		return nv;
-	}
-
-	public static NameValueUnit createStringNVU(String valName, String value) {
-		NameValueUnit nvu = new NameValueUnit();
-		nvu.valName = valName;
-		nvu.value = new TS_Value();
-		nvu.unit = "";
-		nvu.value.u = new TS_Union();
-		if (value == null || value.length() < 1) {
-			nvu.value.flag = 0;
-			nvu.value.u.stringVal("");
-		} else {
-			nvu.value.flag = 15;
-			nvu.value.u.stringVal(value);
-		}
-		return nvu;
-	}
-
-	public static NameValue createShortNV(String valName, short value) {
-		TS_Union union = new TS_Union();
-		union.shortVal(value);
-		return createNV(valName, union);
-	}
-
-	public static NameValueUnit createShortNVU(String valName, short value) {
-		TS_Union union = new TS_Union();
-		union.shortVal(value);
-		return createNVU(valName, union);
-	}
-
-	public static NameValueUnit createShortNVU(String valName, short value, String unit) {
-		TS_Union union = new TS_Union();
-		union.shortVal(value);
-		return createNVU(valName, union, unit);
-	}
-
-	public static NameValue createFloatNV(String valName, float value) {
-		TS_Union union = new TS_Union();
-		union.floatVal(value);
-		return createNV(valName, union);
-	}
-
-	public static NameValueUnit createFloatNVU(String valName, float value) {
-		TS_Union union = new TS_Union();
-		union.floatVal(value);
-		return createNVU(valName, union);
-	}
-
-	public static NameValueUnit createFloatNVU(String valName, float value, String unit) {
-		TS_Union union = new TS_Union();
-		union.floatVal(value);
-		return createNVU(valName, union, unit);
-	}
-
-	public static NameValue createBooleanNV(String valName, boolean value) {
-		TS_Union union = new TS_Union();
-		union.booleanVal(value);
-		return createNV(valName, union);
-	}
-
-	public static NameValueUnit createBooleanNVU(String valName, boolean value) {
-		TS_Union union = new TS_Union();
-		union.booleanVal(value);
-		return createNVU(valName, union);
-	}
-
-	public static NameValue createByteNV(String valName, byte value) {
-		TS_Union union = new TS_Union();
-		union.byteVal(value);
-		return createNV(valName, union);
-	}
-
-	public static NameValueUnit createByteNVU(String valName, byte value) {
-		TS_Union union = new TS_Union();
-		union.byteVal(value);
-		return createNVU(valName, union);
-	}
-
-	public static NameValue createBytestrNV(String valName, byte value[]) {
-		TS_Union union = new TS_Union();
-		union.bytestrVal(value);
-		return createNV(valName, union);
-	}
-
-	public static NameValueUnit createBytestrNVU(String valName, byte value[]) {
-		TS_Union union = new TS_Union();
-		union.bytestrVal(value);
-		return createNVU(valName, union);
-	}
-
-	public static NameValue createDoubleNV(String valName, double value) {
-		TS_Union union = new TS_Union();
-		union.doubleVal(value);
-		return createNV(valName, union);
-	}
-
-	public static NameValueUnit createDoubleNVU(String valName, Double value) {
-		NameValueUnit nvu = new NameValueUnit();
-		nvu.valName = valName;
-		nvu.value = new TS_Value();
-		nvu.unit = "";
-		nvu.value.u = new TS_Union();
-		if (value == null) {
-			nvu.value.flag = 0;
-			nvu.value.u.doubleVal(0);
-		} else {
-			nvu.value.flag = 15;
-			nvu.value.u.doubleVal(value);
-		}
-		return nvu;
-	}
-
-	public static NameValue createDComplexNV(String valName, T_DCOMPLEX value) {
-		TS_Union union = new TS_Union();
-		union.dcomplexVal(value);
-		return createNV(valName, union);
-	}
-
-	public static NameValueUnit createDComplexNVU(String valName, T_DCOMPLEX value) {
-		TS_Union union = new TS_Union();
-		union.dcomplexVal(value);
-		return createNVU(valName, union);
-	}
-
-	public static NameValueUnit createDoubleNVU(String valName, double value, String unit) {
-		TS_Union union = new TS_Union();
-		union.doubleVal(value);
-		return createNVU(valName, union, unit);
-	}
-
-	public static NameValue createLongNV(String valName, int value) {
-		TS_Union union = new TS_Union();
-		union.longVal(value);
-		return createNV(valName, union);
-	}
-
-	public static NameValueUnit createLongNVU(String valName, int value) {
-		TS_Union union = new TS_Union();
-		union.longVal(value);
-		return createNVU(valName, union);
-	}
-
-	public static NameValueUnit createLongNVU(String valName, int value, String unit) {
-		TS_Union union = new TS_Union();
-		union.longVal(value);
-		return createNVU(valName, union, unit);
-	}
-
-	public static NameValue createLongLongNV(String valName, T_LONGLONG value) {
-		TS_Union union = new TS_Union();
-		union.longlongVal(value);
-		return createNV(valName, union);
-	}
-
-	public static NameValue createLongLongNV(String valName, long value) {
-		return createLongLongNV(valName, asODSLongLong(value));
-	}
-
-	public static NameValueUnit createLongLongNVU(String valName, long value) {
-		TS_Union union = new TS_Union();
-		union.longlongVal(asODSLongLong(value));
-		return createNVU(valName, union);
-	}
-
-	public static NameValueUnit createLongLongNVU(String valName, long value, String unit) {
-		TS_Union union = new TS_Union();
-		union.longlongVal(asODSLongLong(value));
-		return createNVU(valName, union, unit);
-	}
-
-	public static NameValue createDateNV(String valName, String value) {
-		NameValue nv = new NameValue();
-		nv.valName = valName;
-		nv.value = new TS_Value();
-		nv.value.u = new TS_Union();
-		if (value == null || value.length() < 1) {
-			nv.value.flag = 0;
-			nv.value.u.dateVal("");
-		} else {
-			nv.value.flag = 15;
-			nv.value.u.dateVal(value);
-		}
-		return nv;
-	}
-
-	public static NameValue createDateNV(String valName, Date value) {
-		return createDateNV(valName, asODSDate(value));
-	}
-
-	public static NameValueUnit createDateNVU(String valName, String value) {
-		NameValueUnit nvu = new NameValueUnit();
-		nvu.valName = valName;
-		nvu.value = new TS_Value();
-		nvu.unit = "";
-		nvu.value.u = new TS_Union();
-		if (value == null || value.length() < 1) {
-			nvu.value.flag = 0;
-			nvu.value.u.dateVal("");
-		} else {
-			nvu.value.flag = 15;
-			nvu.value.u.dateVal(value);
-		}
-		return nvu;
-	}
-
-	public static NameValueUnit createDateNVU(String valName, Date value) {
-		return createDateNVU(valName, asODSDate(value));
-	}
-
-	public static NameValue createEnumNV(String valName, int value) {
-		TS_Union union = new TS_Union();
-		union.enumVal(value);
-		return createNV(valName, union);
-	}
-
-	public static NameValueUnit createEnumNVU(String valName, int value) {
-		TS_Union union = new TS_Union();
-		union.enumVal(value);
-		return createNVU(valName, union);
-	}
-
-	public static NameValue createExtRefNV(String valName, T_ExternalReference value) {
-		TS_Union union = new TS_Union();
-		union.extRefVal(value);
-		return createNV(valName, union);
-	}
-
-	public static NameValueUnit createExtRefNVU(String valName, T_ExternalReference value) {
-		TS_Union union = new TS_Union();
-		union.extRefVal(value);
-		return createNVU(valName, union);
-	}
-
-	public static NameValue createStringSeqNV(String valName, String values[]) {
-		TS_Union union = new TS_Union();
-		union.stringSeq(values);
-		return createNV(valName, union);
-	}
-
-	public static NameValueUnit createStringSeqNVU(String valName, String values[]) {
-		TS_Union union = new TS_Union();
-		union.stringSeq(values);
-		return createNVU(valName, union);
-	}
-
-	public static NameValue createShortSeqNV(String valName, short values[]) {
-		TS_Union union = new TS_Union();
-		union.shortSeq(values);
-		return createNV(valName, union);
-	}
-
-	public static NameValueUnit createShortSeqNVU(String attrName, short values[]) {
-		TS_Union union = new TS_Union();
-		union.shortSeq(values);
-		return createNVU(attrName, union);
-	}
-
-	public static NameValue createFloatSeqNV(String attrName, float values[]) {
-		TS_Union union = new TS_Union();
-		union.floatSeq(values);
-		return createNV(attrName, union);
-	}
-
-	public static NameValueUnit createFloatSeqNVU(String attrName, float values[]) {
-		TS_Union union = new TS_Union();
-		union.floatSeq(values);
-		return createNVU(attrName, union);
-	}
-
-	public static NameValue createBooleanSeqNV(String attrName, boolean values[]) {
-		TS_Union union = new TS_Union();
-		union.booleanSeq(values);
-		return createNV(attrName, union);
-	}
-
-	public static NameValueUnit createBooleanSeqNVU(String attrName, boolean values[]) {
-		TS_Union union = new TS_Union();
-		union.booleanSeq(values);
-		return createNVU(attrName, union);
-	}
-
-	public static NameValue createByteSeqNV(String attrName, byte values[]) {
-		TS_Union union = new TS_Union();
-		union.byteSeq(values);
-		return createNV(attrName, union);
-	}
-
-	public static NameValueUnit createByteSeqNVU(String attrName, byte values[]) {
-		TS_Union union = new TS_Union();
-		union.byteSeq(values);
-		return createNVU(attrName, union);
-	}
-
-	public static NameValue createBytestrSeqNV(String valName, byte value[][]) {
-		TS_Union union = new TS_Union();
-		union.bytestrSeq(value);
-		return createNV(valName, union);
-	}
-
-	public static NameValueUnit createBytestrSeqNVU(String valName, byte value[][]) {
-		TS_Union union = new TS_Union();
-		union.bytestrSeq(value);
-		return createNVU(valName, union);
-	}
-
-	public static NameValue createDComplexSeqNV(String valName, T_DCOMPLEX value[]) {
-		TS_Union union = new TS_Union();
-		union.dcomplexSeq(value);
-		return createNV(valName, union);
-	}
-
-	public static NameValueUnit createDComplexSeqNVU(String valName, T_DCOMPLEX value[]) {
-		TS_Union union = new TS_Union();
-		union.dcomplexSeq(value);
-		return createNVU(valName, union);
-	}
-
-	public static NameValue createDoubleSeqNV(String attrName, double values[]) {
-		TS_Union union = new TS_Union();
-		union.doubleSeq(values);
-		return createNV(attrName, union);
-	}
-
-	public static NameValueUnit createDoubleSeqNVU(String attrName, double values[]) {
-		TS_Union union = new TS_Union();
-		union.doubleSeq(values);
-		return createNVU(attrName, union);
-	}
-
-	public static NameValue createEnumSeqNV(String valName, int value[]) {
-		TS_Union union = new TS_Union();
-		union.enumSeq(value);
-		return createNV(valName, union);
-	}
-
-	public static NameValueUnit createEnumSeqNVU(String valName, int value[]) {
-		TS_Union union = new TS_Union();
-		union.enumSeq(value);
-		return createNVU(valName, union);
-	}
-
-	public static NameValue createLongSeqNV(String attrName, int values[]) {
-		TS_Union union = new TS_Union();
-		union.longSeq(values);
-		return createNV(attrName, union);
-	}
-
-	public static NameValueUnit createLongSeqNVU(String attrName, int values[]) {
-		TS_Union union = new TS_Union();
-		union.longSeq(values);
-		return createNVU(attrName, union);
-	}
-
-	public static NameValue createLongLongSeqNV(String attrName, T_LONGLONG values[]) {
-		TS_Union union = new TS_Union();
-		union.longlongSeq(values);
-		return createNV(attrName, union);
-	}
-
-	public static NameValue createLongLongSeqNV(String attrName, long values[]) {
-		TS_Union union = new TS_Union();
-		union.longlongSeq(asODSLongLong(values));
-		return createNV(attrName, union);
-	}
-
-	public static NameValueUnit createLongLongSeqNVU(String attrName, long values[]) {
-		TS_Union union = new TS_Union();
-		union.longlongSeq(asODSLongLong(values));
-		return createNVU(attrName, union);
-	}
-
-	public static NameValue createDateSeqNV(String attrName, String values[]) {
-		TS_Union union = new TS_Union();
-		union.dateSeq(values);
-		return createNV(attrName, union);
-	}
-
-	public static NameValue createDateSeqNV(String attrName, Date values[]) {
-		String[] valuesstr = new String[values.length];
-		for (int i = 0; i < values.length; i++) {
-			valuesstr[i] = ODSHelper.asODSDate(values[i]);
-		}
-		return createDateSeqNV(attrName, valuesstr);
-	}
-
-	public static NameValueUnit createDateSeqNVU(String attrName, String values[]) {
-		TS_Union union = new TS_Union();
-		union.dateSeq(values);
-		return createNVU(attrName, union);
-	}
-
-	public static NameValue createExtRefSeqNV(String attrName, T_ExternalReference values[]) {
-		TS_Union union = new TS_Union();
-		union.extRefSeq(values);
-		return createNV(attrName, union);
-	}
-
-	public static NameValueUnit createExtRefSeqNVU(String attrName, T_ExternalReference values[]) {
-		TS_Union union = new TS_Union();
-		union.extRefSeq(values);
-		return createNVU(attrName, union);
-	}
-
-	public static boolean isNullVal(TS_Value value) {
-		if (value.flag != 15) {
-			return true;
-		}
-		return false;
-	}
-
-	public static boolean isNullVal(NameValueUnit nvu) {
-		if (nvu.value.flag != 15) {
-			return true;
-		}
-		return false;
-	}
-
-	public static long getLongLongVal(NameValueUnit nvu) {
-		if (isNullVal(nvu)) {
-			return 0;
-		} else {
-			return asJLong(nvu.value.u.longlongVal());
-		}
-	}
-
-	public static int getLongVal(NameValueUnit nvu) {
-		if (isNullVal(nvu)) {
-			return 0;
-		} else {
-			return nvu.value.u.longVal();
-		}
-	}
-
-	public static double getDoubleVal(NameValueUnit nvu) {
-		if (isNullVal(nvu)) {
-			return 0d;
-		} else {
-			return nvu.value.u.doubleVal();
-		}
-	}
-
-	public static short getShortVal(NameValueUnit nvu) {
-		if (isNullVal(nvu)) {
-			return (short) 0;
-		} else {
-			return nvu.value.u.shortVal();
-		}
-	}
-
-	public static byte getByteVal(NameValueUnit nvu) {
-		if (isNullVal(nvu)) {
-			return (byte) 0;
-		} else {
-			return nvu.value.u.byteVal();
-		}
-	}
-
-	public static float getFloatVal(NameValueUnit nvu) {
-		if (isNullVal(nvu)) {
-			return 0f;
-		} else {
-			return nvu.value.u.floatVal();
-		}
-	}
-
-	public static String getStringVal(NameValueUnit nvu) {
-		if (isNullVal(nvu)) {
-			return "";
-		} else {
-			return nvu.value.u.stringVal();
-		}
-	}
-
-	public static int getEnumVal(NameValueUnit nvu) {
-		if (isNullVal(nvu)) {
-			return 0;
-		} else {
-			return nvu.value.u.enumVal();
-		}
-	}
-
-	public static String getDateVal(NameValueUnit nvu) {
-		if (isNullVal(nvu)) {
-			return "";
-		} else {
-			return nvu.value.u.dateVal();
-		}
-	}
-
-	public static boolean getBooleanVal(NameValueUnit nvu) {
-		if (isNullVal(nvu)) {
-			return false;
-		} else {
-			return nvu.value.u.booleanVal();
-		}
-	}
-
-	public static String[] getStringSeq(NameValueUnit nvu) {
-		if (isNullVal(nvu)) {
-			return new String[0];
-		} else {
-			return nvu.value.u.stringSeq();
-		}
-	}
-
-	public static short[] getShortSeq(NameValueUnit nvu) {
-		if (isNullVal(nvu)) {
-			return new short[0];
-		} else {
-			return nvu.value.u.shortSeq();
-		}
-	}
-
-	public static float[] getFloatSeq(NameValueUnit nvu) {
-		if (isNullVal(nvu)) {
-			return new float[0];
-		} else {
-			return nvu.value.u.floatSeq();
-		}
-	}
-
-	public static boolean[] getBooleanSeq(NameValueUnit nvu) {
-		if (isNullVal(nvu)) {
-			return new boolean[0];
-		} else {
-			return nvu.value.u.booleanSeq();
-		}
-	}
-
-	public static byte[] getByteSeq(NameValueUnit nvu) {
-		if (isNullVal(nvu)) {
-			return new byte[0];
-		} else {
-			return nvu.value.u.byteSeq();
-		}
-	}
-
-	public static int[] getLongSeq(NameValueUnit nvu) {
-		if (isNullVal(nvu)) {
-			return new int[0];
-		} else {
-			return nvu.value.u.longSeq();
-		}
-	}
-
-	public static double[] getDoubleSeq(NameValueUnit nvu) {
-		if (isNullVal(nvu)) {
-			return new double[0];
-		} else {
-			return nvu.value.u.doubleSeq();
-		}
-	}
-
-	public static long[] getLongLongSeq(NameValueUnit nvu) {
-		if (isNullVal(nvu)) {
-			return new long[0];
-		} else {
-			return asJLong(nvu.value.u.longlongSeq());
-		}
-	}
-
-	public static String[] getDateSeq(NameValueUnit nvu) {
-		if (isNullVal(nvu)) {
-			return new String[0];
-		} else {
-			return nvu.value.u.stringSeq();
-		}
-	}
-
-	public static void setBit(byte[] data, int pos, boolean val) {
-		int posByte = pos / 8;
-		int posBit = pos % 8;
-		byte oldByte = data[posByte];
-		if (val) {
-			data[posByte] = (byte) (oldByte | 1 << 7 - posBit);
-		} else {
-			data[posByte] = (byte) (oldByte | 0 << 7 - posBit);
-		}
-	}
-
-}
+

+

+package org.eclipse.mdm.openatfx.mdf.util;

+

+import java.text.DateFormat;

+import java.text.ParseException;

+import java.text.SimpleDateFormat;

+import java.util.Date;

+import java.util.HashMap;

+import java.util.Map;

+

+import org.asam.ods.NameValue;

+import org.asam.ods.NameValueUnit;

+import org.asam.ods.TS_Union;

+import org.asam.ods.TS_Value;

+import org.asam.ods.T_DCOMPLEX;

+import org.asam.ods.T_ExternalReference;

+import org.asam.ods.T_LONGLONG;

+

+/**

+ * Helper class with ODS specific functions.

+ *

+ * @author Christian Rechner

+ */

+public abstract class ODSHelper {

+

+	// prepare dateformats to avoid instantiation a single object every time

+	// parsing a date.

+	private static Map<Integer, DateFormat> ODS_DATEFORMATS = new HashMap<Integer, DateFormat>();

+	static {

+		ODS_DATEFORMATS.put(4, new SimpleDateFormat("yyyy"));

+		ODS_DATEFORMATS.put(6, new SimpleDateFormat("yyyyMM"));

+		ODS_DATEFORMATS.put(8, new SimpleDateFormat("yyyyMMdd"));

+		ODS_DATEFORMATS.put(10, new SimpleDateFormat("yyyyMMddHH"));

+		ODS_DATEFORMATS.put(12, new SimpleDateFormat("yyyyMMddHHmm"));

+		ODS_DATEFORMATS.put(14, new SimpleDateFormat("yyyyMMddHHmmss"));

+		ODS_DATEFORMATS.put(17, new SimpleDateFormat("yyyyMMddHHmmssSSS"));

+	}

+	

+	/**

+	 * Return an ODS date from a <code>java.util.Date</code>.

+	 *

+	 * @param date

+	 *            the <code>java.util.Date</code> to convert

+	 * @return the date in ODS date-format (YYYYMMDDhhmmss)

+	 */

+	public static synchronized String asODSDate(Date date) {

+		if (date == null) {

+			return "";

+		}

+		return ODS_DATEFORMATS.get(14).format(date);

+	}

+

+	/**

+	 * Returns the java date from an ODS date.

+	 *

+	 * @param odsDate

+	 *            the ODS date string

+	 * @return the java <code>java.util.Date</code> object, null if empty date

+	 * @throws IllegalArgumentException

+	 *             unable to parse

+	 */

+	public static synchronized Date asJDate(String odsDate) {

+		try {

+			if (odsDate == null || odsDate.length() < 1) {

+				return null;

+			}

+			DateFormat format = ODS_DATEFORMATS.get(odsDate.length());

+			if (format == null) {

+				throw new IllegalArgumentException("Invalid ODS date: " + odsDate);

+			}

+			return format.parse(odsDate);

+		} catch (ParseException e) {

+			throw new IllegalArgumentException("Invalid ODS date: " + odsDate);

+		}

+	}

+

+	/**

+	 * Returns a Java long from ODS T_LONGLONG.

+	 *

+	 * @param ll

+	 *            ODS T_LONGLONG value

+	 * @return Java long with the same value as ll

+	 */

+	public static long asJLong(T_LONGLONG ll) {

+		long tmp;

+		if (ll.low >= 0) {

+			tmp = ll.high * 0x100000000L + ll.low;

+		} else {

+			tmp = (ll.high + 1) * 0x100000000L + ll.low;

+		}

+		return tmp;

+	}

+

+	/**

+	 * Returns an array of Java long from ODS T_LONGLONG.

+	 *

+	 * @param ll

+	 *            array of ODS T_LONGLONG values

+	 * @return array of Java long values

+	 */

+	public static long[] asJLong(T_LONGLONG[] ll) {

+		long[] ar = new long[ll.length];

+		for (int i = 0; i < ll.length; i++) {

+			ar[i] = asJLong(ll[i]);

+		}

+		return ar;

+	}

+

+	/**

+	 * Return ODS T_LONGLONG from Java long.

+	 *

+	 * @param v

+	 *            Java long value

+	 * @return ODS T_LONGLONG with the same value as v

+	 */

+	public static T_LONGLONG asODSLongLong(long v) {

+		return new T_LONGLONG((int) (v >> 32 & 0xffffffffL), (int) (v & 0xffffffffL));

+	}

+

+	/**

+	 * Returns an array of ODS T_LONGLONG from Java longs.

+	 *

+	 * @param v

+	 *            array of Java long values

+	 * @return array of ODS T_LONGLONG values

+	 */

+	public static T_LONGLONG[] asODSLongLong(long[] v) {

+		T_LONGLONG[] ar = new T_LONGLONG[v.length];

+		for (int i = 0; i < v.length; i++) {

+			ar[i] = asODSLongLong(v[i]);

+		}

+		return ar;

+	}

+

+	public static String getCurrentODSDate() {

+		return asODSDate(new Date());

+	}

+

+	private static NameValue createNV(String valName, TS_Union union) {

+		NameValue nv = new NameValue();

+		nv.valName = valName;

+		nv.value = new TS_Value();

+		nv.value.flag = 15;

+		nv.value.u = union;

+		return nv;

+	}

+

+	private static NameValueUnit createNVU(String attrName, TS_Union union) {

+		NameValueUnit nvu = new NameValueUnit();

+		nvu.valName = attrName;

+		nvu.value = new TS_Value();

+		nvu.unit = "";

+		nvu.value.flag = 15;

+		nvu.value.u = union;

+		return nvu;

+	}

+

+	private static NameValueUnit createNVU(String attrName, TS_Union union, String unit) {

+		NameValueUnit nvu = new NameValueUnit();

+		nvu.valName = attrName;

+		nvu.value = new TS_Value();

+		nvu.value.u = new TS_Union();

+		nvu.unit = unit;

+		nvu.value.flag = 15;

+		nvu.value.u = union;

+		return nvu;

+	}

+

+	public static NameValueUnit createCurrentDateNVU(String attrName) {

+		TS_Union union = new TS_Union();

+		union.dateVal(getCurrentODSDate());

+		return createNVU(attrName, union);

+	}

+

+	public static NameValue createStringNV(String valName, String value) {

+		NameValue nv = new NameValue();

+		nv.valName = valName;

+		nv.value = new TS_Value();

+		nv.value.u = new TS_Union();

+		if (value == null) {

+			nv.value.flag = 0;

+			nv.value.u.stringVal("");

+		} else {

+			nv.value.flag = 15;

+			nv.value.u.stringVal(value);

+		}

+		return nv;

+	}

+

+	public static NameValueUnit createStringNVU(String valName, String value) {

+		NameValueUnit nvu = new NameValueUnit();

+		nvu.valName = valName;

+		nvu.value = new TS_Value();

+		nvu.unit = "";

+		nvu.value.u = new TS_Union();

+		if (value == null || value.length() < 1) {

+			nvu.value.flag = 0;

+			nvu.value.u.stringVal("");

+		} else {

+			nvu.value.flag = 15;

+			nvu.value.u.stringVal(value);

+		}

+		return nvu;

+	}

+

+	public static NameValue createShortNV(String valName, short value) {

+		TS_Union union = new TS_Union();

+		union.shortVal(value);

+		return createNV(valName, union);

+	}

+

+	public static NameValueUnit createShortNVU(String valName, short value) {

+		TS_Union union = new TS_Union();

+		union.shortVal(value);

+		return createNVU(valName, union);

+	}

+

+	public static NameValueUnit createShortNVU(String valName, short value, String unit) {

+		TS_Union union = new TS_Union();

+		union.shortVal(value);

+		return createNVU(valName, union, unit);

+	}

+

+	public static NameValue createFloatNV(String valName, float value) {

+		TS_Union union = new TS_Union();

+		union.floatVal(value);

+		return createNV(valName, union);

+	}

+

+	public static NameValueUnit createFloatNVU(String valName, float value) {

+		TS_Union union = new TS_Union();

+		union.floatVal(value);

+		return createNVU(valName, union);

+	}

+

+	public static NameValueUnit createFloatNVU(String valName, float value, String unit) {

+		TS_Union union = new TS_Union();

+		union.floatVal(value);

+		return createNVU(valName, union, unit);

+	}

+

+	public static NameValue createBooleanNV(String valName, boolean value) {

+		TS_Union union = new TS_Union();

+		union.booleanVal(value);

+		return createNV(valName, union);

+	}

+

+	public static NameValueUnit createBooleanNVU(String valName, boolean value) {

+		TS_Union union = new TS_Union();

+		union.booleanVal(value);

+		return createNVU(valName, union);

+	}

+

+	public static NameValue createByteNV(String valName, byte value) {

+		TS_Union union = new TS_Union();

+		union.byteVal(value);

+		return createNV(valName, union);

+	}

+

+	public static NameValueUnit createByteNVU(String valName, byte value) {

+		TS_Union union = new TS_Union();

+		union.byteVal(value);

+		return createNVU(valName, union);

+	}

+

+	public static NameValue createBytestrNV(String valName, byte value[]) {

+		TS_Union union = new TS_Union();

+		union.bytestrVal(value);

+		return createNV(valName, union);

+	}

+

+	public static NameValueUnit createBytestrNVU(String valName, byte value[]) {

+		TS_Union union = new TS_Union();

+		union.bytestrVal(value);

+		return createNVU(valName, union);

+	}

+

+	public static NameValue createDoubleNV(String valName, double value) {

+		TS_Union union = new TS_Union();

+		union.doubleVal(value);

+		return createNV(valName, union);

+	}

+

+	public static NameValueUnit createDoubleNVU(String valName, Double value) {

+		NameValueUnit nvu = new NameValueUnit();

+		nvu.valName = valName;

+		nvu.value = new TS_Value();

+		nvu.unit = "";

+		nvu.value.u = new TS_Union();

+		if (value == null) {

+			nvu.value.flag = 0;

+			nvu.value.u.doubleVal(0);

+		} else {

+			nvu.value.flag = 15;

+			nvu.value.u.doubleVal(value);

+		}

+		return nvu;

+	}

+

+	public static NameValue createDComplexNV(String valName, T_DCOMPLEX value) {

+		TS_Union union = new TS_Union();

+		union.dcomplexVal(value);

+		return createNV(valName, union);

+	}

+

+	public static NameValueUnit createDComplexNVU(String valName, T_DCOMPLEX value) {

+		TS_Union union = new TS_Union();

+		union.dcomplexVal(value);

+		return createNVU(valName, union);

+	}

+

+	public static NameValueUnit createDoubleNVU(String valName, double value, String unit) {

+		TS_Union union = new TS_Union();

+		union.doubleVal(value);

+		return createNVU(valName, union, unit);

+	}

+

+	public static NameValue createLongNV(String valName, int value) {

+		TS_Union union = new TS_Union();

+		union.longVal(value);

+		return createNV(valName, union);

+	}

+

+	public static NameValueUnit createLongNVU(String valName, int value) {

+		TS_Union union = new TS_Union();

+		union.longVal(value);

+		return createNVU(valName, union);

+	}

+

+	public static NameValueUnit createLongNVU(String valName, int value, String unit) {

+		TS_Union union = new TS_Union();

+		union.longVal(value);

+		return createNVU(valName, union, unit);

+	}

+

+	public static NameValue createLongLongNV(String valName, T_LONGLONG value) {

+		TS_Union union = new TS_Union();

+		union.longlongVal(value);

+		return createNV(valName, union);

+	}

+

+	public static NameValue createLongLongNV(String valName, long value) {

+		return createLongLongNV(valName, asODSLongLong(value));

+	}

+

+	public static NameValueUnit createLongLongNVU(String valName, long value) {

+		TS_Union union = new TS_Union();

+		union.longlongVal(asODSLongLong(value));

+		return createNVU(valName, union);

+	}

+

+	public static NameValueUnit createLongLongNVU(String valName, long value, String unit) {

+		TS_Union union = new TS_Union();

+		union.longlongVal(asODSLongLong(value));

+		return createNVU(valName, union, unit);

+	}

+

+	public static NameValue createDateNV(String valName, String value) {

+		NameValue nv = new NameValue();

+		nv.valName = valName;

+		nv.value = new TS_Value();

+		nv.value.u = new TS_Union();

+		if (value == null || value.length() < 1) {

+			nv.value.flag = 0;

+			nv.value.u.dateVal("");

+		} else {

+			nv.value.flag = 15;

+			nv.value.u.dateVal(value);

+		}

+		return nv;

+	}

+

+	public static NameValue createDateNV(String valName, Date value) {

+		return createDateNV(valName, asODSDate(value));

+	}

+

+	public static NameValueUnit createDateNVU(String valName, String value) {

+		NameValueUnit nvu = new NameValueUnit();

+		nvu.valName = valName;

+		nvu.value = new TS_Value();

+		nvu.unit = "";

+		nvu.value.u = new TS_Union();

+		if (value == null || value.length() < 1) {

+			nvu.value.flag = 0;

+			nvu.value.u.dateVal("");

+		} else {

+			nvu.value.flag = 15;

+			nvu.value.u.dateVal(value);

+		}

+		return nvu;

+	}

+

+	public static NameValueUnit createDateNVU(String valName, Date value) {

+		return createDateNVU(valName, asODSDate(value));

+	}

+

+	public static NameValue createEnumNV(String valName, int value) {

+		TS_Union union = new TS_Union();

+		union.enumVal(value);

+		return createNV(valName, union);

+	}

+

+	public static NameValueUnit createEnumNVU(String valName, int value) {

+		TS_Union union = new TS_Union();

+		union.enumVal(value);

+		return createNVU(valName, union);

+	}

+

+	public static NameValue createExtRefNV(String valName, T_ExternalReference value) {

+		TS_Union union = new TS_Union();

+		union.extRefVal(value);

+		return createNV(valName, union);

+	}

+

+	public static NameValueUnit createExtRefNVU(String valName, T_ExternalReference value) {

+		TS_Union union = new TS_Union();

+		union.extRefVal(value);

+		return createNVU(valName, union);

+	}

+

+	public static NameValue createStringSeqNV(String valName, String values[]) {

+		TS_Union union = new TS_Union();

+		union.stringSeq(values);

+		return createNV(valName, union);

+	}

+

+	public static NameValueUnit createStringSeqNVU(String valName, String values[]) {

+		TS_Union union = new TS_Union();

+		union.stringSeq(values);

+		return createNVU(valName, union);

+	}

+

+	public static NameValue createShortSeqNV(String valName, short values[]) {

+		TS_Union union = new TS_Union();

+		union.shortSeq(values);

+		return createNV(valName, union);

+	}

+

+	public static NameValueUnit createShortSeqNVU(String attrName, short values[]) {

+		TS_Union union = new TS_Union();

+		union.shortSeq(values);

+		return createNVU(attrName, union);

+	}

+

+	public static NameValue createFloatSeqNV(String attrName, float values[]) {

+		TS_Union union = new TS_Union();

+		union.floatSeq(values);

+		return createNV(attrName, union);

+	}

+

+	public static NameValueUnit createFloatSeqNVU(String attrName, float values[]) {

+		TS_Union union = new TS_Union();

+		union.floatSeq(values);

+		return createNVU(attrName, union);

+	}

+

+	public static NameValue createBooleanSeqNV(String attrName, boolean values[]) {

+		TS_Union union = new TS_Union();

+		union.booleanSeq(values);

+		return createNV(attrName, union);

+	}

+

+	public static NameValueUnit createBooleanSeqNVU(String attrName, boolean values[]) {

+		TS_Union union = new TS_Union();

+		union.booleanSeq(values);

+		return createNVU(attrName, union);

+	}

+

+	public static NameValue createByteSeqNV(String attrName, byte values[]) {

+		TS_Union union = new TS_Union();

+		union.byteSeq(values);

+		return createNV(attrName, union);

+	}

+

+	public static NameValueUnit createByteSeqNVU(String attrName, byte values[]) {

+		TS_Union union = new TS_Union();

+		union.byteSeq(values);

+		return createNVU(attrName, union);

+	}

+

+	public static NameValue createBytestrSeqNV(String valName, byte value[][]) {

+		TS_Union union = new TS_Union();

+		union.bytestrSeq(value);

+		return createNV(valName, union);

+	}

+

+	public static NameValueUnit createBytestrSeqNVU(String valName, byte value[][]) {

+		TS_Union union = new TS_Union();

+		union.bytestrSeq(value);

+		return createNVU(valName, union);

+	}

+

+	public static NameValue createDComplexSeqNV(String valName, T_DCOMPLEX value[]) {

+		TS_Union union = new TS_Union();

+		union.dcomplexSeq(value);

+		return createNV(valName, union);

+	}

+

+	public static NameValueUnit createDComplexSeqNVU(String valName, T_DCOMPLEX value[]) {

+		TS_Union union = new TS_Union();

+		union.dcomplexSeq(value);

+		return createNVU(valName, union);

+	}

+

+	public static NameValue createDoubleSeqNV(String attrName, double values[]) {

+		TS_Union union = new TS_Union();

+		union.doubleSeq(values);

+		return createNV(attrName, union);

+	}

+

+	public static NameValueUnit createDoubleSeqNVU(String attrName, double values[]) {

+		TS_Union union = new TS_Union();

+		union.doubleSeq(values);

+		return createNVU(attrName, union);

+	}

+

+	public static NameValue createEnumSeqNV(String valName, int value[]) {

+		TS_Union union = new TS_Union();

+		union.enumSeq(value);

+		return createNV(valName, union);

+	}

+

+	public static NameValueUnit createEnumSeqNVU(String valName, int value[]) {

+		TS_Union union = new TS_Union();

+		union.enumSeq(value);

+		return createNVU(valName, union);

+	}

+

+	public static NameValue createLongSeqNV(String attrName, int values[]) {

+		TS_Union union = new TS_Union();

+		union.longSeq(values);

+		return createNV(attrName, union);

+	}

+

+	public static NameValueUnit createLongSeqNVU(String attrName, int values[]) {

+		TS_Union union = new TS_Union();

+		union.longSeq(values);

+		return createNVU(attrName, union);

+	}

+

+	public static NameValue createLongLongSeqNV(String attrName, T_LONGLONG values[]) {

+		TS_Union union = new TS_Union();

+		union.longlongSeq(values);

+		return createNV(attrName, union);

+	}

+

+	public static NameValue createLongLongSeqNV(String attrName, long values[]) {

+		TS_Union union = new TS_Union();

+		union.longlongSeq(asODSLongLong(values));

+		return createNV(attrName, union);

+	}

+

+	public static NameValueUnit createLongLongSeqNVU(String attrName, long values[]) {

+		TS_Union union = new TS_Union();

+		union.longlongSeq(asODSLongLong(values));

+		return createNVU(attrName, union);

+	}

+

+	public static NameValue createDateSeqNV(String attrName, String values[]) {

+		TS_Union union = new TS_Union();

+		union.dateSeq(values);

+		return createNV(attrName, union);

+	}

+

+	public static NameValue createDateSeqNV(String attrName, Date values[]) {

+		String[] valuesstr = new String[values.length];

+		for (int i = 0; i < values.length; i++) {

+			valuesstr[i] = ODSHelper.asODSDate(values[i]);

+		}

+		return createDateSeqNV(attrName, valuesstr);

+	}

+

+	public static NameValueUnit createDateSeqNVU(String attrName, String values[]) {

+		TS_Union union = new TS_Union();

+		union.dateSeq(values);

+		return createNVU(attrName, union);

+	}

+

+	public static NameValue createExtRefSeqNV(String attrName, T_ExternalReference values[]) {

+		TS_Union union = new TS_Union();

+		union.extRefSeq(values);

+		return createNV(attrName, union);

+	}

+

+	public static NameValueUnit createExtRefSeqNVU(String attrName, T_ExternalReference values[]) {

+		TS_Union union = new TS_Union();

+		union.extRefSeq(values);

+		return createNVU(attrName, union);

+	}

+

+	public static boolean isNullVal(TS_Value value) {

+		if (value.flag != 15) {

+			return true;

+		}

+		return false;

+	}

+

+	public static boolean isNullVal(NameValueUnit nvu) {

+		if (nvu.value.flag != 15) {

+			return true;

+		}

+		return false;

+	}

+

+	public static long getLongLongVal(NameValueUnit nvu) {

+		if (isNullVal(nvu)) {

+			return 0;

+		} else {

+			return asJLong(nvu.value.u.longlongVal());

+		}

+	}

+

+	public static int getLongVal(NameValueUnit nvu) {

+		if (isNullVal(nvu)) {

+			return 0;

+		} else {

+			return nvu.value.u.longVal();

+		}

+	}

+

+	public static double getDoubleVal(NameValueUnit nvu) {

+		if (isNullVal(nvu)) {

+			return 0d;

+		} else {

+			return nvu.value.u.doubleVal();

+		}

+	}

+

+	public static short getShortVal(NameValueUnit nvu) {

+		if (isNullVal(nvu)) {

+			return (short) 0;

+		} else {

+			return nvu.value.u.shortVal();

+		}

+	}

+

+	public static byte getByteVal(NameValueUnit nvu) {

+		if (isNullVal(nvu)) {

+			return (byte) 0;

+		} else {

+			return nvu.value.u.byteVal();

+		}

+	}

+

+	public static float getFloatVal(NameValueUnit nvu) {

+		if (isNullVal(nvu)) {

+			return 0f;

+		} else {

+			return nvu.value.u.floatVal();

+		}

+	}

+

+	public static String getStringVal(NameValueUnit nvu) {

+		if (isNullVal(nvu)) {

+			return "";

+		} else {

+			return nvu.value.u.stringVal();

+		}

+	}

+

+	public static int getEnumVal(NameValueUnit nvu) {

+		if (isNullVal(nvu)) {

+			return 0;

+		} else {

+			return nvu.value.u.enumVal();

+		}

+	}

+

+	public static String getDateVal(NameValueUnit nvu) {

+		if (isNullVal(nvu)) {

+			return "";

+		} else {

+			return nvu.value.u.dateVal();

+		}

+	}

+

+	public static boolean getBooleanVal(NameValueUnit nvu) {

+		if (isNullVal(nvu)) {

+			return false;

+		} else {

+			return nvu.value.u.booleanVal();

+		}

+	}

+

+	public static String[] getStringSeq(NameValueUnit nvu) {

+		if (isNullVal(nvu)) {

+			return new String[0];

+		} else {

+			return nvu.value.u.stringSeq();

+		}

+	}

+

+	public static short[] getShortSeq(NameValueUnit nvu) {

+		if (isNullVal(nvu)) {

+			return new short[0];

+		} else {

+			return nvu.value.u.shortSeq();

+		}

+	}

+

+	public static float[] getFloatSeq(NameValueUnit nvu) {

+		if (isNullVal(nvu)) {

+			return new float[0];

+		} else {

+			return nvu.value.u.floatSeq();

+		}

+	}

+

+	public static boolean[] getBooleanSeq(NameValueUnit nvu) {

+		if (isNullVal(nvu)) {

+			return new boolean[0];

+		} else {

+			return nvu.value.u.booleanSeq();

+		}

+	}

+

+	public static byte[] getByteSeq(NameValueUnit nvu) {

+		if (isNullVal(nvu)) {

+			return new byte[0];

+		} else {

+			return nvu.value.u.byteSeq();

+		}

+	}

+

+	public static int[] getLongSeq(NameValueUnit nvu) {

+		if (isNullVal(nvu)) {

+			return new int[0];

+		} else {

+			return nvu.value.u.longSeq();

+		}

+	}

+

+	public static double[] getDoubleSeq(NameValueUnit nvu) {

+		if (isNullVal(nvu)) {

+			return new double[0];

+		} else {

+			return nvu.value.u.doubleSeq();

+		}

+	}

+

+	public static long[] getLongLongSeq(NameValueUnit nvu) {

+		if (isNullVal(nvu)) {

+			return new long[0];

+		} else {

+			return asJLong(nvu.value.u.longlongSeq());

+		}

+	}

+

+	public static String[] getDateSeq(NameValueUnit nvu) {

+		if (isNullVal(nvu)) {

+			return new String[0];

+		} else {

+			return nvu.value.u.stringSeq();

+		}

+	}

+

+	public static void setBit(byte[] data, int pos, boolean val) {

+		int posByte = pos / 8;

+		int posBit = pos % 8;

+		byte oldByte = data[posByte];

+		if (val) {

+			data[posByte] = (byte) (oldByte | 1 << 7 - posBit);

+		} else {

+			data[posByte] = (byte) (oldByte | 0 << 7 - posBit);

+		}

+	}

+

+}

diff --git a/src/main/java/org/eclipse/mdm/openatfx/mdf/util/ODSInsertStatement.java b/src/main/java/org/eclipse/mdm/openatfx/mdf/util/ODSInsertStatement.java
index 1316eb2..c097f86 100644
--- a/src/main/java/org/eclipse/mdm/openatfx/mdf/util/ODSInsertStatement.java
+++ b/src/main/java/org/eclipse/mdm/openatfx/mdf/util/ODSInsertStatement.java
@@ -598,7 +598,8 @@
 			}
 
 			// do not insert if attribute is "id"
-			if (cache.applAttrExists(aeName, attr) && cache.getApplAttr(aeName, attr).baName.equals("id")) {
+      if (cache.applAttrExists(aeName, attr) && cache.getApplAttr(aeName, attr).baName != null

+          && cache.getApplAttr(aeName, attr).baName.equals("id")) {
 				continue;
 			}
 
diff --git a/src/main/java/org/eclipse/mdm/openatfx/mdf/util/ODSModelCache.java b/src/main/java/org/eclipse/mdm/openatfx/mdf/util/ODSModelCache.java
index 5924fb8..2757db8 100644
--- a/src/main/java/org/eclipse/mdm/openatfx/mdf/util/ODSModelCache.java
+++ b/src/main/java/org/eclipse/mdm/openatfx/mdf/util/ODSModelCache.java
@@ -32,10 +32,12 @@
 import org.asam.ods.ApplicationElement;
 import org.asam.ods.ApplicationRelation;
 import org.asam.ods.ApplicationStructure;
-import org.asam.ods.ApplicationStructureValue;
+import org.asam.ods.ApplicationStructureValue;

+import org.asam.ods.BaseAttribute;
 import org.asam.ods.BaseElement;
 import org.asam.ods.BaseRelation;
-import org.asam.ods.BaseStructure;
+import org.asam.ods.BaseStructure;

+import org.asam.ods.DataType;
 import org.asam.ods.ElemId;
 import org.asam.ods.EnumerationAttributeStructure;
 import org.asam.ods.EnumerationDefinition;
@@ -53,6 +55,10 @@
  * @author Christian Rechner
  */
 public class ODSModelCache {
+

+  public static final String ATTR_NAME_MEAQU_RANK = "rank";

+  public static final String ATTR_NAME_MEAQU_DIMENSION = "dimension";

+  public static final String ATTR_NAME_MEAQU_COL_ORIENTED_TENSOR = "columnOriented";

 
 	private static final Log LOG = LogFactory.getLog(ODSModelCache.class);
 
@@ -122,9 +128,58 @@
 		applicationElemCache = new HashMap<String, ApplicationElement>();
 		applicationAttrCache = new HashMap<String, ApplicationAttribute[]>();
 		applicationRelCache = new HashMap<ApplicationRelKey, ApplicationRelation>();
-		enumDefCache = new HashMap<String, EnumerationDefinition>();
+		enumDefCache = new HashMap<String, EnumerationDefinition>();

+

+    try

+    {

+      extendSourceApplicationStructure(aoSession);

+    }

+    catch (AoException e)

+    {

+      throw new RuntimeException("Error occurred trying to extend mdf datamodel with required additions: " + e.reason);

+    }

+
 		this.aoSession = aoSession;
-	}
+	}

+	

+  /**

+   * <p>

+   * Adds attributes to model needed additionally to transport information on

+   * specific mdf features to the ODS target. So far these are:

+   * </p>

+   * <p>

+   * For array composition:

+   * <ul>

+   * <li>MeaQuantity - rank attribute: for the number of value dimensions in the

+   * values tensor for this channel</li>

+   * <li>MeaQuantity - dimension attribute: for the number of values in each

+   * dimension of the values tensor for this channel</li>

+   * <li>MeaQuantity - columnOriented attribute: to specify in which way the

+   * tensor has to be interpreted when calculating the actual values</li>

+   * </ul>

+   * </p>

+   * @throws AoException

+   */

+  private final void extendSourceApplicationStructure(AoSession aoSession) throws AoException

+  {

+    ApplicationStructure as = aoSession.getApplicationStructure();

+

+    // Additional MeasurementQuantity attributes

+    ApplicationElement meaQuantityAe = as.getElementsByBaseType("aomeasurementquantity")[0];

+

+    // to handle array composition for channels

+    ApplicationAttribute rankAttr = meaQuantityAe.createAttribute();

+    BaseAttribute rankBaseAttr = meaQuantityAe.getBaseElement().getAttributes("rank")[0];

+    rankAttr.setBaseAttribute(rankBaseAttr);

+    rankAttr.setName(ATTR_NAME_MEAQU_RANK);

+    ApplicationAttribute dimensionAttr = meaQuantityAe.createAttribute();

+    BaseAttribute dimensionBaseAttr = meaQuantityAe.getBaseElement().getAttributes("dimension")[0];

+    dimensionAttr.setBaseAttribute(dimensionBaseAttr);

+    dimensionAttr.setName(ATTR_NAME_MEAQU_DIMENSION);

+    ApplicationAttribute columnOrientedAttr = meaQuantityAe.createAttribute();

+    columnOrientedAttr.setName(ATTR_NAME_MEAQU_COL_ORIENTED_TENSOR);

+    columnOrientedAttr.setDataType(DataType.DT_BOOLEAN);

+  }
 
 	/**
 	 * Returns a Java long from ODS T_LONGLONG.
@@ -537,7 +592,7 @@
 	public final ApplElem[] getApplElemsByBaseName(String beName) throws AoException {
 		List<ApplElem> list = new LinkedList<ApplElem>();
 		for (ApplElem applElem : getApplElems()) {
-			if (applElem.beName.equals(beName)) {
+			if (applElem.beName.equalsIgnoreCase(beName)) {
 				list.add(applElem);
 			}
 		}