Merge branch 'mkoller/nodeprovider' into mkoller/webclient

Conflicts:
	api/atfxadapter/openatfx/build.gradle

Change-Id: Ieab74c9222711b539e0f6d962208824631f54116
diff --git a/WebContent/META-INF/MANIFEST.MF b/WebContent/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..254272e
--- /dev/null
+++ b/WebContent/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Class-Path: 
+
diff --git a/api/atfxadapter/openatfx/build.gradle b/api/atfxadapter/openatfx/build.gradle
index 904c096..c0a9e5c 100644
--- a/api/atfxadapter/openatfx/build.gradle
+++ b/api/atfxadapter/openatfx/build.gradle
@@ -19,7 +19,7 @@
 
 import de.undercouch.gradle.tasks.download.Download
 
-def atfxVersion  = '0.7.4'
+def atfxVersion  = '0.8.10'
 description = 'Downloads openATFX and publishes it to the local maven repository'
 
 dependencies {
@@ -31,8 +31,9 @@
 	compile project(":api:odsadapter")
 	testCompile 'junit:junit:4.12'
 	testCompile 'org.apache.commons:commons-math:2.2'
+	testCompile 'org.mockito:mockito-core:2.13.0'
 	testCompile 'org.assertj:assertj-core:3.6.2'
-	testCompile 'org.mockito:mockito-core:2.13.0'	
+
 }
 
 configurations.all {
@@ -64,9 +65,15 @@
 	outputs.dir file("${buildDir}/openatfx-${atfxVersion}")
 }
 
+
 task copySource(dependsOn: unzipOpenATFX, type: Copy) {
 	from file("${buildDir}/openatfx-${atfxVersion}/src")
 	into 'src'
+	/*
+	 * openATFX is implemented against the ODS API compiled by JacORB. The JacORB IDL compiler generates SelItem#operator() while SunORB IDL compiler generates SelItem#_operator().
+	 * Since openMDM uses SunORB to generate the ODS API from IDL, openATFX source code is missing SelItem#operator(). Therefore we change it with a simple replace and add the underscore.
+	 */
+	filter { line -> line.replaceAll('SelOperator.AND.equals\\(condition.operator\\(\\)', 'SelOperator.AND.equals\\(condition._operator\\(\\)').replaceAll('return new TS_ValueSeq\\(new TS_UnionSeq\\(\\), new short\\[0\\]\\)', 'return ODSHelper.tsValue2tsValueSeq\\(new TS_Value\\[0\\], dt\\)') }
 }
 
 compileJava.dependsOn copySource
diff --git a/api/base/src/main/java/org/eclipse/mdm/api/base/massdata/AnyTypeValuesBuilder.java b/api/base/src/main/java/org/eclipse/mdm/api/base/massdata/AnyTypeValuesBuilder.java
index 208edf9..0b0f40d 100644
--- a/api/base/src/main/java/org/eclipse/mdm/api/base/massdata/AnyTypeValuesBuilder.java
+++ b/api/base/src/main/java/org/eclipse/mdm/api/base/massdata/AnyTypeValuesBuilder.java
@@ -15,12 +15,9 @@
 package org.eclipse.mdm.api.base.massdata;
 
 import java.time.LocalDateTime;
-import java.util.Arrays;
-import java.util.List;
 
 import org.eclipse.mdm.api.base.model.FileLink;
 import org.eclipse.mdm.api.base.model.ScalarType;
-import org.eclipse.mdm.api.base.model.SequenceRepresentation;
 
 /**
  * This builder adds values to the {@link WriteRequest} of types listed below.
@@ -206,44 +203,4 @@
 		createValues(ScalarType.BLOB, values);
 		throw new UnsupportedOperationException("Not implemented.");
 	}
-	
-	public UnitIndependentBuilder externalComponent(ScalarType scalarType, ExternalComponent externalComponent) {
-		return externalComponents(scalarType, null, externalComponent == null ? null : Arrays.asList(externalComponent));
-	}
-	
-	public UnitIndependentBuilder externalComponents(ScalarType scalarType, Short globalFlag, ExternalComponent externalComponent) {
-		return externalComponents(scalarType, globalFlag, externalComponent == null ? null : Arrays.asList(externalComponent));
-	}
-	
-	public UnitIndependentBuilder externalComponents(ScalarType scalarType, List<ExternalComponent> externalComponents) {
-		return externalComponents(scalarType, null, externalComponents);
-	}
-
-	public UnitIndependentBuilder externalComponents(ScalarType scalarType, Short globalFlag, List<ExternalComponent> externalComponents) {
-		SequenceRepresentation seqRep = getWriteRequest().getSequenceRepresentation();
-		if (SequenceRepresentation.EXPLICIT.equals(seqRep)) {
-			seqRep = SequenceRepresentation.EXPLICIT_EXTERNAL;
-		} else if (SequenceRepresentation.RAW_LINEAR.equals(seqRep)) {
-			seqRep = SequenceRepresentation.RAW_LINEAR_EXTERNAL;
-		} else if (SequenceRepresentation.RAW_LINEAR_CALIBRATED.equals(seqRep)) {
-			seqRep = SequenceRepresentation.RAW_LINEAR_CALIBRATED_EXTERNAL;
-		} else if (SequenceRepresentation.RAW_POLYNOMIAL.equals(seqRep)) {
-			seqRep = SequenceRepresentation.RAW_POLYNOMIAL_EXTERNAL;
-		} else if (null == seqRep || !seqRep.isExternal()) {
-			seqRep = SequenceRepresentation.EXPLICIT_EXTERNAL;
-		}
-		
-		WriteRequest writeRequest = getWriteRequest();
-		writeRequest.setSequenceRepresentation(seqRep);
-		writeRequest.setRawScalarType(scalarType);
-		writeRequest.setGlobalFlag(globalFlag);
-		if (externalComponents != null) {
-			for (ExternalComponent externalComponent : externalComponents) {
-				writeRequest.addExternalComponent(externalComponent);
-			}
-		}
-		
-		return new UnitIndependentBuilder(writeRequest);
-	}
-
 }
diff --git a/api/base/src/main/java/org/eclipse/mdm/api/base/massdata/ExternalComponentBuilder.java b/api/base/src/main/java/org/eclipse/mdm/api/base/massdata/ExternalComponentBuilder.java
new file mode 100644
index 0000000..1a6d00f
--- /dev/null
+++ b/api/base/src/main/java/org/eclipse/mdm/api/base/massdata/ExternalComponentBuilder.java
@@ -0,0 +1,83 @@
+/********************************************************************************

+ * Copyright (c) 2015-2021 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

+ * http://www.eclipse.org/legal/epl-2.0.

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

+ ********************************************************************************/

+

+package org.eclipse.mdm.api.base.massdata;

+

+import java.util.Arrays;

+import java.util.List;

+

+import org.eclipse.mdm.api.base.model.ScalarType;

+import org.eclipse.mdm.api.base.model.SequenceRepresentation;

+

+/**

+ * This builder adds {@link ExternalComponent}s to the {@link WriteRequest} . It

+ * is possible to add a sequence where all of its values are valid.

+ *

+ */

+public class ExternalComponentBuilder extends BaseValuesBuilder {

+

+	/**

+	 * Constructor.

+	 *

+	 * @param writeRequest The {@link WriteRequest} given {@link ExternalComponent}s

+	 *                     will be added to.

+	 */

+	ExternalComponentBuilder(WriteRequest writeRequest) {

+		super(writeRequest);

+	}

+

+	public UnitIndependentBuilder externalComponent(ScalarType scalarType, ExternalComponent externalComponent) {

+		return externalComponents(scalarType, null,

+				externalComponent == null ? null : Arrays.asList(externalComponent));

+	}

+

+	public UnitIndependentBuilder externalComponents(ScalarType scalarType, Short globalFlag,

+			ExternalComponent externalComponent) {

+		return externalComponents(scalarType, globalFlag,

+				externalComponent == null ? null : Arrays.asList(externalComponent));

+	}

+

+	public UnitIndependentBuilder externalComponents(ScalarType scalarType,

+			List<ExternalComponent> externalComponents) {

+		return externalComponents(scalarType, null, externalComponents);

+	}

+

+	public UnitIndependentBuilder externalComponents(ScalarType scalarType, Short globalFlag,

+			List<ExternalComponent> externalComponents) {

+		SequenceRepresentation seqRep = getWriteRequest().getSequenceRepresentation();

+		if (SequenceRepresentation.EXPLICIT.equals(seqRep)) {

+			seqRep = SequenceRepresentation.EXPLICIT_EXTERNAL;

+		} else if (SequenceRepresentation.RAW_LINEAR.equals(seqRep)) {

+			seqRep = SequenceRepresentation.RAW_LINEAR_EXTERNAL;

+		} else if (SequenceRepresentation.RAW_LINEAR_CALIBRATED.equals(seqRep)) {

+			seqRep = SequenceRepresentation.RAW_LINEAR_CALIBRATED_EXTERNAL;

+		} else if (SequenceRepresentation.RAW_POLYNOMIAL.equals(seqRep)) {

+			seqRep = SequenceRepresentation.RAW_POLYNOMIAL_EXTERNAL;

+		} else if (null == seqRep || !seqRep.isExternal()) {

+			seqRep = SequenceRepresentation.EXPLICIT_EXTERNAL;

+		}

+

+		WriteRequest writeRequest = getWriteRequest();

+		writeRequest.setSequenceRepresentation(seqRep);

+		writeRequest.setRawScalarType(scalarType);

+		writeRequest.setGlobalFlag(globalFlag);

+		if (externalComponents != null) {

+			for (ExternalComponent externalComponent : externalComponents) {

+				writeRequest.addExternalComponent(externalComponent);

+			}

+		}

+

+		return new UnitIndependentBuilder(writeRequest);

+	}

+}

diff --git a/api/base/src/main/java/org/eclipse/mdm/api/base/massdata/WriteRequestBuilder.java b/api/base/src/main/java/org/eclipse/mdm/api/base/massdata/WriteRequestBuilder.java
index f810efc..6477ee4 100644
--- a/api/base/src/main/java/org/eclipse/mdm/api/base/massdata/WriteRequestBuilder.java
+++ b/api/base/src/main/java/org/eclipse/mdm/api/base/massdata/WriteRequestBuilder.java
@@ -56,6 +56,18 @@
 	}
 
 	/**
+	 * Configures the {@link WriteRequest} to create an explicit sequence of
+	 * measured values provided as {@link ExternalComponent}s.
+	 *
+	 * @return An {@link ExternalComponentBuilder} is returned.
+	 * @see SequenceRepresentation#EXPLICIT_EXTERNAL
+	 */
+	public ExternalComponentBuilder explicitExternal() {
+		getWriteRequest().setSequenceRepresentation(SequenceRepresentation.EXPLICIT_EXTERNAL);
+		return new ExternalComponentBuilder(getWriteRequest());
+	}
+
+	/**
 	 * Configures the {@link WriteRequest} to create an implicit constant sequence
 	 * of measured values. An implicit sequence allows only numerical
 	 * {@link ScalarType}s as listed below:
@@ -225,6 +237,21 @@
 	}
 
 	/**
+	 * Configures the {@link WriteRequest} to create a raw linear sequence of
+	 * measured values provided as {@link ExternalComponent}s.
+	 *
+	 * @param offset The offset for each value.
+	 * @param factor The factor for each value.
+	 * @return A {@link ExternalComponentBuilder} is returned.
+	 * @see SequenceRepresentation#RAW_LINEAR_EXTERNAL
+	 */
+	public ExternalComponentBuilder rawLinearExternal(double offset, double factor) {
+		getWriteRequest().setSequenceRepresentation(SequenceRepresentation.RAW_LINEAR_EXTERNAL);
+		getWriteRequest().setGenerationParameters(new double[] { offset, factor });
+		return new ExternalComponentBuilder(getWriteRequest());
+	}
+
+	/**
 	 * Configures the {@link WriteRequest} to create a raw polynomial sequence of
 	 * measured values.
 	 *
@@ -254,6 +281,35 @@
 	}
 
 	/**
+	 * Configures the {@link WriteRequest} to create a raw polynomial sequence of
+	 * measured values provided as {@link ExternalComponent}s.
+	 *
+	 * @param coefficients At least 2 coefficients must be provided.
+	 * @return A {@link ExternalComponentBuilder} is returned.
+	 * @throws IllegalArgumentException Thrown if coefficients are missing or their
+	 *                                  length is less than 2.
+	 * @see SequenceRepresentation#RAW_POLYNOMIAL_EXTERNAL
+	 */
+	public ExternalComponentBuilder rawPolynomialExternal(double... coefficients) {
+		if (coefficients == null || coefficients.length < 2) {
+			throw new IllegalArgumentException(
+					"Coefficients either missing or their length is " + "inconsitent with given grade");
+		}
+
+		getWriteRequest().setSequenceRepresentation(SequenceRepresentation.RAW_POLYNOMIAL_EXTERNAL);
+
+		double[] generationParameters = new double[coefficients.length + 1];
+		generationParameters[0] = coefficients.length - 1;
+		System.arraycopy(coefficients, 0, generationParameters, 1, coefficients.length);
+		getWriteRequest().setGenerationParameters(generationParameters);
+
+		// TODO: currently it is possible to define such a channel as
+		// independent
+		// should we prevent this?!
+		return new ExternalComponentBuilder(getWriteRequest());
+	}
+
+	/**
 	 * Configures the {@link WriteRequest} to create a raw linear calibrated
 	 * sequence of measured values.
 	 *
@@ -268,4 +324,20 @@
 		getWriteRequest().setGenerationParameters(new double[] { offset, factor, calibration });
 		return new ComplexNumericalValuesBuilder(getWriteRequest());
 	}
+
+	/**
+	 * Configures the {@link WriteRequest} to create a raw linear calibrated
+	 * sequence of measured values provided as {@link ExternalComponent}s.
+	 *
+	 * @param offset      The offset for each value.
+	 * @param factor      The factor for each value.
+	 * @param calibration The calibration factor.
+	 * @return A {@link ExternalComponentBuilder} is returned.
+	 * @see SequenceRepresentation#RAW_LINEAR_CALIBRATED_EXTERNAL
+	 */
+	public ExternalComponentBuilder rawLinearCalibratedExternal(double offset, double factor, double calibration) {
+		getWriteRequest().setSequenceRepresentation(SequenceRepresentation.RAW_LINEAR_CALIBRATED_EXTERNAL);
+		getWriteRequest().setGenerationParameters(new double[] { offset, factor, calibration });
+		return new ExternalComponentBuilder(getWriteRequest());
+	}
 }
diff --git a/api/base/src/test/java/org/eclipse/mdm/api/base/WriteRequestBuilderTest.java b/api/base/src/test/java/org/eclipse/mdm/api/base/WriteRequestBuilderTest.java
index 3e791b8..f80d7c0 100644
--- a/api/base/src/test/java/org/eclipse/mdm/api/base/WriteRequestBuilderTest.java
+++ b/api/base/src/test/java/org/eclipse/mdm/api/base/WriteRequestBuilderTest.java
@@ -36,7 +36,7 @@
 

 	@Test

 	public void testOneExternalComponent() {

-		WriteRequest r1 = WriteRequest.create(channelGroup, channel, axisType).explicit()

+		WriteRequest r1 = WriteRequest.create(channelGroup, channel, axisType).explicitExternal()

 				.externalComponent(ScalarType.INTEGER, externalComponent1).independent().build();

 

 		assertThat(r1).hasFieldOrPropertyWithValue("channelGroup", channelGroup)

@@ -46,7 +46,7 @@
 

 	@Test

 	public void testMultipleExternalComponents() {

-		WriteRequest r1 = WriteRequest.create(channelGroup, channel, axisType).explicit()

+		WriteRequest r1 = WriteRequest.create(channelGroup, channel, axisType).explicitExternal()

 				.externalComponents(ScalarType.INTEGER, Arrays.asList(externalComponent1, externalComponent2))

 				.independent().build();

 

@@ -58,29 +58,26 @@
 	@Test

 	public void testMultipleExternalComponentsCopyExtComp() {

 

-		WriteRequest r1 = WriteRequest.create(channelGroup, channel, axisType).explicit().externalComponents(

-				ScalarType.INTEGER,

-				Arrays.asList(new ExternalComponent[] { (new ExternalComponent())

-				.setTypeSpecification(externalComponent1.getTypeSpecification())

-				.setStartOffset(externalComponent1.getStartOffset())

-				.setBlocksize(externalComponent1.getBlocksize())

-				.setValuesPerBlock(externalComponent1.getValuesPerBlock())

-				.setLength(externalComponent1.getLength())

-				.setFileLink(externalComponent1.getFileLink())

-				.setFlagsFileLink(externalComponent1.getFlagsFileLink())

-				.setFlagsStartOffset(externalComponent1.getFlagsStartOffset())

-				.setBitCount(externalComponent1.getBitCount())

-				.setBitOffset(externalComponent1.getBitOffset()),

-				(new ExternalComponent()).setTypeSpecification(externalComponent2.getTypeSpecification())

-				.setStartOffset(externalComponent2.getStartOffset())

-				.setBlocksize(externalComponent2.getBlocksize())

-				.setValuesPerBlock(externalComponent2.getValuesPerBlock())

-				.setLength(externalComponent2.getLength())

-				.setFileLink(externalComponent2.getFileLink())

-				.setFlagsFileLink(externalComponent2.getFlagsFileLink())

-				.setFlagsStartOffset(externalComponent2.getFlagsStartOffset())

-				.setBitCount(externalComponent2.getBitCount())

-				.setBitOffset(externalComponent2.getBitOffset())}))

+		WriteRequest r1 = WriteRequest.create(channelGroup, channel, axisType).explicitExternal()

+				.externalComponents(ScalarType.INTEGER, Arrays.asList(new ExternalComponent[] {

+						(new ExternalComponent()).setTypeSpecification(externalComponent1.getTypeSpecification())

+								.setStartOffset(externalComponent1.getStartOffset())

+								.setBlocksize(externalComponent1.getBlocksize())

+								.setValuesPerBlock(externalComponent1.getValuesPerBlock())

+								.setLength(externalComponent1.getLength()).setFileLink(externalComponent1.getFileLink())

+								.setFlagsFileLink(externalComponent1.getFlagsFileLink())

+								.setFlagsStartOffset(externalComponent1.getFlagsStartOffset())

+								.setBitCount(externalComponent1.getBitCount())

+								.setBitOffset(externalComponent1.getBitOffset()),

+						(new ExternalComponent()).setTypeSpecification(externalComponent2.getTypeSpecification())

+								.setStartOffset(externalComponent2.getStartOffset())

+								.setBlocksize(externalComponent2.getBlocksize())

+								.setValuesPerBlock(externalComponent2.getValuesPerBlock())

+								.setLength(externalComponent2.getLength()).setFileLink(externalComponent2.getFileLink())

+								.setFlagsFileLink(externalComponent2.getFlagsFileLink())

+								.setFlagsStartOffset(externalComponent2.getFlagsStartOffset())

+								.setBitCount(externalComponent2.getBitCount())

+								.setBitOffset(externalComponent2.getBitOffset()) }))

 				.independent().build();

 

 		assertThat(r1).hasFieldOrPropertyWithValue("channelGroup", channelGroup)

diff --git a/api/odsadapter/src/main/java/org/eclipse/mdm/api/odsadapter/ReadRequestHandler.java b/api/odsadapter/src/main/java/org/eclipse/mdm/api/odsadapter/ReadRequestHandler.java
index 25e3481..0ba1064 100644
--- a/api/odsadapter/src/main/java/org/eclipse/mdm/api/odsadapter/ReadRequestHandler.java
+++ b/api/odsadapter/src/main/java/org/eclipse/mdm/api/odsadapter/ReadRequestHandler.java
@@ -16,6 +16,8 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -42,6 +44,7 @@
 import org.eclipse.mdm.api.base.model.Channel;
 import org.eclipse.mdm.api.base.model.Entity;
 import org.eclipse.mdm.api.base.model.MeasuredValues;
+import org.eclipse.mdm.api.base.model.ScalarType;
 import org.eclipse.mdm.api.base.model.SequenceRepresentation;
 import org.eclipse.mdm.api.base.model.Unit;
 import org.eclipse.mdm.api.base.model.Value;
@@ -54,6 +57,8 @@
 import org.eclipse.mdm.api.odsadapter.query.ODSEntityType;
 import org.eclipse.mdm.api.odsadapter.query.ODSModelManager;
 import org.eclipse.mdm.api.odsadapter.utils.ODSConverter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.google.common.collect.Lists;
 
@@ -64,6 +69,8 @@
  * @author Viktor Stoehr, Gigatronik Ingolstadt GmbH
  */
 public final class ReadRequestHandler {
+	private static final Logger LOGGER = LoggerFactory.getLogger(ReadRequestHandler.class);
+
 	public static final class ColumnAttributes {
 		private String name;
 		private String id;
@@ -72,9 +79,15 @@
 		private boolean independent;
 		private AxisType axisType;
 		private Short globalFlag;
+		private ScalarType dataType;
+		private ScalarType rawDataType;
+
+		private String unit;
+		private String meaQuantityId;
 
 		public ColumnAttributes(String name, String columnId, SequenceRepresentation sequenceRepresentation,
-				double[] generationParameters, boolean independent, AxisType axisType, Short globalFlag) {
+				double[] generationParameters, boolean independent, AxisType axisType, Short globalFlag,
+				ScalarType rawDataType, String meaQuantityId) {
 			this.name = name;
 			this.id = columnId;
 			this.sequenceRepresentation = sequenceRepresentation;
@@ -82,6 +95,8 @@
 			this.independent = independent;
 			this.axisType = axisType;
 			this.globalFlag = globalFlag;
+			this.rawDataType = rawDataType;
+			this.meaQuantityId = meaQuantityId;
 		}
 
 		public String getName() {
@@ -140,6 +155,34 @@
 			this.globalFlag = globalFlag;
 		}
 
+		public ScalarType getRawDataType() {
+			return rawDataType;
+		}
+
+		public void setRawDataType(ScalarType rawDataType) {
+			this.rawDataType = rawDataType;
+		}
+
+		public ScalarType getDataType() {
+			return dataType;
+		}
+
+		public void setDataType(ScalarType dataType) {
+			this.dataType = dataType;
+		}
+
+		public String getMeaQuantityId() {
+			return meaQuantityId;
+		}
+
+		public String getUnit() {
+			return unit;
+		}
+
+		public void setUnit(String unit) {
+			this.unit = unit;
+		}
+
 	}
 
 	// ======================================================================
@@ -190,87 +233,58 @@
 			}
 		}
 
-		ValueMatrix valueMatrix = null;
-		Column[] arrColumns = null;
+		// Load ColumnAttributes:
+		Map<String, ColumnAttributes> mapColumnAttributes = getColumnAttributes(readRequest);
+
+		if (mapColumnAttributes.isEmpty()) {
+			return Collections.emptyList();
+		}
 
 		List<MeasuredValues> listMeasuredValues = new ArrayList<>();
 
-		try {
-			// Load ColumnAttributes:
-			Map<String, ColumnAttributes> mapColumnAttributes = getColumnAttributes(readRequest);
-
-			final Map<String, List<ExternalComponent>> mapExternalComponents;
-
-			if (ValuesMode.STORAGE_PRESERVE_EXTCOMPS == readRequest.getValuesMode() && externalComponentExists()) {
-
-				EntityType localColumnEntityType = modelManager.getEntityType("LocalColumn");
-				EntityType externalComponentEntityType = modelManager.getEntityType("ExternalComponent");
-
-				Relation relExternalComponentToLocalColumn = externalComponentEntityType
-						.getRelation(localColumnEntityType);
-
-				List<Attribute> listExtCompAttributes = externalComponentEntityType.getAttributes();
-
-				boolean hasExtCompBitCount = (listExtCompAttributes.stream()
-						.filter(a -> ExternalComponentEntity.ATTR_BITCOUNT.equals(a.getName())).count() > 0);
-				boolean hasExtCompBitOffset = (listExtCompAttributes.stream()
-						.filter(a -> ExternalComponentEntity.ATTR_BITOFFSET.equals(a.getName())).count() > 0);
-
-				List<String> ids = new ArrayList<>();
-				for (Map.Entry<String, ColumnAttributes> me : mapColumnAttributes.entrySet()) {
-					if (me.getValue().getSequenceRepresentation().isExternal()) {
-						ids.add(me.getKey());
-					}
-				}
-
-				mapExternalComponents = queryService.createQuery().select(externalComponentEntityType.getAttributes())
-						.select(relExternalComponentToLocalColumn.getAttribute())
-						.order(relExternalComponentToLocalColumn.getAttribute(),
-								externalComponentEntityType.getAttribute(ExternalComponentEntity.ATTR_ORDINALNUMBER))
-						.fetch(Filter.and()
-								.add(ComparisonOperator.IN_SET.create(relExternalComponentToLocalColumn.getAttribute(),
-										ids.toArray(new String[0]))))
-						.stream()
-						.map(r -> new ImmutablePair<String, ExternalComponent>(
-								r.getValue(relExternalComponentToLocalColumn.getAttribute()).extract(),
-								new ExternalComponent(
-										r.getValue(externalComponentEntityType.getAttribute(
-												ExternalComponentEntity.ATTR_TYPESPECIFICATION)).extract(),
-										r.getValue(externalComponentEntityType
-												.getAttribute(ExternalComponentEntity.ATTR_LENGTH)).extract(),
-										r.getValue(externalComponentEntityType
-												.getAttribute(ExternalComponentEntity.ATTR_STARTOFFSET)).extract(),
-										r.getValue(externalComponentEntityType
-												.getAttribute(ExternalComponentEntity.ATTR_BLOCKSIZE)).extract(),
-										r.getValue(externalComponentEntityType
-												.getAttribute(ExternalComponentEntity.ATTR_VALUESPERBLOCK)).extract(),
-										r.getValue(externalComponentEntityType
-												.getAttribute(ExternalComponentEntity.ATTR_VALUEOFFSET)).extract(),
-										r.getValue(externalComponentEntityType
-												.getAttribute(ExternalComponentEntity.ATTR_FILENAMEURL))
-												.extractWithDefault(null),
-										r.getValue(externalComponentEntityType
-												.getAttribute(ExternalComponentEntity.ATTR_FLAGSFILENAMEURL))
-												.extractWithDefault(null),
-										r.getValue(externalComponentEntityType
-												.getAttribute(ExternalComponentEntity.ATTR_FLAGSSTARTOFFSET))
-												.extractWithDefault(null),
-										(hasExtCompBitCount
-												? r.getValue(externalComponentEntityType
-														.getAttribute(ExternalComponentEntity.ATTR_BITCOUNT)).extract()
-												: Short.valueOf((short) 0)),
-										(hasExtCompBitOffset
-												? r.getValue(externalComponentEntityType
-														.getAttribute(ExternalComponentEntity.ATTR_BITOFFSET)).extract()
-												: Short.valueOf((short) 0)))))
-						.collect(Collectors.groupingBy(Pair::getKey,
-								Collectors.mapping(Pair::getValue, Collectors.toList())));
-
-			} else {
-				mapExternalComponents = new HashMap<>();
+		if (ValuesMode.STORAGE_PRESERVE_EXTCOMPS == readRequest.getValuesMode() && externalComponentExists()) {
+			List<String> processedLcIds = new ArrayList<>();
+			Map<String, List<ExternalComponent>> mapExternalComponents = loadExternalComponents(mapColumnAttributes);
+			LOGGER.debug("Loaded external components for {} localcolumns.", mapExternalComponents.size());
+			// Create MeasuredValues for local columns whose external components have been
+			// loaded above:
+			for (Map.Entry<String, List<ExternalComponent>> entry : mapExternalComponents.entrySet()) {
+				String lcId = entry.getKey();
+				List<ExternalComponent> listExtComps = entry.getValue();
+				listMeasuredValues
+						.add(ODSConverter.fromExternalComponents(listExtComps, mapColumnAttributes.get(lcId)));
+				processedLcIds.add(lcId);
 			}
 
-			if (mapColumnAttributes.size() > 0) {
+			// implict
+			for (Map.Entry<String, ColumnAttributes> entry : mapColumnAttributes.entrySet()) {
+				String lcId = entry.getKey();
+				ColumnAttributes ca = entry.getValue();
+				if (entry.getValue().getSequenceRepresentation().isImplicit()) {
+
+					ScalarType scalarType = ca.getRawDataType();
+					if (scalarType == ScalarType.UNKNOWN) {
+						scalarType = ca.getDataType();
+					}
+
+					listMeasuredValues.add(scalarType.createMeasuredValues(ca.getName(), ca.getUnit(),
+							ca.getSequenceRepresentation(), ca.getGenerationParameters(), ca.isIndependentColumn(),
+							ca.getAxisType(), ca.getGlobalFlag(), Collections.emptyList()));
+					processedLcIds.add(lcId);
+				}
+			}
+			// remove processed localcolumns
+			processedLcIds.forEach(lcId -> mapColumnAttributes.remove(lcId));
+		}
+
+		if (!mapColumnAttributes.isEmpty()) {
+			// if there are still localcolumns left we load them be valuematrix
+			LOGGER.debug("Loading {} localcolumns by value matrix.", mapColumnAttributes.size());
+
+			ValueMatrix valueMatrix = null;
+			Column[] arrColumns = null;
+
+			try {
 				valueMatrix = getValueMatrix(readRequest);
 				List<Pair<Column, ColumnAttributes>> listColumnPairs = getODSColumns(readRequest, valueMatrix,
 						mapColumnAttributes);
@@ -278,36 +292,100 @@
 				// Get array of all ODS columns for later release:
 				arrColumns = listColumnPairs.stream().map(Pair::getLeft).toArray(Column[]::new);
 
-				// Create MeasuredValues for local columns whose external components have been
-				// loaded above:
-				for (Pair<Column, ColumnAttributes> pair : listColumnPairs) {
-					Column column = pair.getLeft();
-					ColumnAttributes ca = pair.getRight();
-					List<ExternalComponent> listExtComps = mapExternalComponents.get(ca.getId());
-					if (listExtComps != null) {
-						listMeasuredValues.add(ODSConverter.fromExternalComponents(listExtComps,
-								column.getRawDataType(), column.getUnit(), ca));
-					}
-				}
-
 				// Create MeasuredValues for the remaining local columns:
-				NameValueSeqUnit[] nvsus = valueMatrix.getValue(
-						listColumnPairs.stream()
-								.filter(p -> !contains(mapExternalComponents.keySet(), p.getRight().getId()))
-								.map(p -> p.getLeft()).toArray(Column[]::new),
-						readRequest.getStartIndex(), readRequest.getRequestSize());
+				NameValueSeqUnit[] nvsus = valueMatrix.getValue(arrColumns, readRequest.getStartIndex(),
+						readRequest.getRequestSize());
 				listMeasuredValues.addAll(ODSConverter.fromODSMeasuredValuesSeq(nvsus,
 						listColumnPairs.stream().map(Pair::getRight).toArray(ColumnAttributes[]::new)));
+			} catch (AoException aoe) {
+				throw new DataAccessException(aoe.reason, aoe);
+			} finally {
+				releaseColumns(arrColumns);
+				releaseValueMatrix(valueMatrix);
 			}
-
-			return listMeasuredValues;
-
-		} catch (AoException aoe) {
-			throw new DataAccessException(aoe.reason, aoe);
-		} finally {
-			releaseColumns(arrColumns);
-			releaseValueMatrix(valueMatrix);
 		}
+
+		return listMeasuredValues;
+	}
+
+	/**
+	 * Loads External Components for all ColumnAttributes which satisfy
+	 * {@link SequenceRepresentation#isExternal()}
+	 * 
+	 * @param mapColumnAttributes
+	 * @return a map with the LocalColumn.Id as key and a list of its
+	 *         ExternalComponents as value
+	 */
+	private Map<String, List<ExternalComponent>> loadExternalComponents(
+			Map<String, ColumnAttributes> mapColumnAttributes) {
+		Map<String, List<ExternalComponent>> mapExternalComponents;
+		EntityType localColumnEntityType = modelManager.getEntityType("LocalColumn");
+		EntityType externalComponentEntityType = modelManager.getEntityType("ExternalComponent");
+
+		Relation relExternalComponentToLocalColumn = externalComponentEntityType.getRelation(localColumnEntityType);
+
+		List<Attribute> listExtCompAttributes = externalComponentEntityType.getAttributes();
+
+		boolean hasExtCompBitCount = (listExtCompAttributes.stream()
+				.filter(a -> ExternalComponentEntity.ATTR_BITCOUNT.equals(a.getName())).count() > 0);
+		boolean hasExtCompBitOffset = (listExtCompAttributes.stream()
+				.filter(a -> ExternalComponentEntity.ATTR_BITOFFSET.equals(a.getName())).count() > 0);
+
+		List<String> ids = new ArrayList<>();
+		for (Map.Entry<String, ColumnAttributes> me : mapColumnAttributes.entrySet()) {
+			if (me.getValue().getSequenceRepresentation().isExternal()) {
+				ids.add(me.getKey());
+			}
+		}
+
+		if (ids.isEmpty()) {
+			// no external sequence representations -> no externalcomponents to load
+			return Collections.emptyMap();
+		}
+
+		mapExternalComponents = queryService.createQuery().select(externalComponentEntityType.getAttributes())
+				.select(relExternalComponentToLocalColumn.getAttribute())
+				.order(relExternalComponentToLocalColumn.getAttribute(),
+						externalComponentEntityType.getAttribute(ExternalComponentEntity.ATTR_ORDINALNUMBER))
+				.fetch(Filter.and()
+						.add(ComparisonOperator.IN_SET
+								.create(relExternalComponentToLocalColumn.getAttribute(), ids.toArray(new String[0]))))
+				.stream()
+				.map(r -> new ImmutablePair<String, ExternalComponent>(
+						r.getValue(relExternalComponentToLocalColumn.getAttribute()).extract(),
+						new ExternalComponent(
+								r.getValue(externalComponentEntityType
+										.getAttribute(ExternalComponentEntity.ATTR_TYPESPECIFICATION)).extract(),
+								r.getValue(
+										externalComponentEntityType.getAttribute(ExternalComponentEntity.ATTR_LENGTH))
+										.extract(),
+								r.getValue(externalComponentEntityType
+										.getAttribute(ExternalComponentEntity.ATTR_STARTOFFSET)).extract(),
+								r.getValue(externalComponentEntityType
+										.getAttribute(ExternalComponentEntity.ATTR_BLOCKSIZE)).extract(),
+								r.getValue(externalComponentEntityType
+										.getAttribute(ExternalComponentEntity.ATTR_VALUESPERBLOCK)).extract(),
+								r.getValue(externalComponentEntityType
+										.getAttribute(ExternalComponentEntity.ATTR_VALUEOFFSET)).extract(),
+								r.getValue(externalComponentEntityType
+										.getAttribute(ExternalComponentEntity.ATTR_FILENAMEURL))
+										.extractWithDefault(null),
+								r.getValue(externalComponentEntityType
+										.getAttribute(ExternalComponentEntity.ATTR_FLAGSFILENAMEURL))
+										.extractWithDefault(null),
+								r.getValue(externalComponentEntityType
+										.getAttribute(ExternalComponentEntity.ATTR_FLAGSSTARTOFFSET))
+										.extractWithDefault(null),
+								(hasExtCompBitCount
+										? r.getValue(externalComponentEntityType
+												.getAttribute(ExternalComponentEntity.ATTR_BITCOUNT)).extract()
+										: Short.valueOf((short) 0)),
+								(hasExtCompBitOffset
+										? r.getValue(externalComponentEntityType
+												.getAttribute(ExternalComponentEntity.ATTR_BITOFFSET)).extract()
+										: Short.valueOf((short) 0)))))
+				.collect(Collectors.groupingBy(Pair::getKey, Collectors.mapping(Pair::getValue, Collectors.toList())));
+		return mapExternalComponents;
 	}
 
 	private boolean externalComponentExists() {
@@ -404,8 +482,8 @@
 		Map<String, ColumnAttributes> mapColumnAttributes = new HashMap<>();
 
 		EntityType localColumnEntityType = modelManager.getEntityType("LocalColumn");
-		Attribute idAttr = localColumnEntityType.getAttribute("Id");
-		Attribute nameAttr = localColumnEntityType.getAttribute("Name");
+		Attribute idAttr = localColumnEntityType.getIDAttribute();
+		Attribute nameAttr = localColumnEntityType.getNameAttribute();
 		Attribute submatrixAttr = localColumnEntityType.getAttribute("SubMatrix");
 
 		// Don't query GenerationParameters together with other non-ID attributes as
@@ -413,7 +491,8 @@
 		Query query1 = queryService.createQuery().select(Lists.newArrayList(idAttr, nameAttr,
 				localColumnEntityType.getAttribute("SequenceRepresentation"),
 				localColumnEntityType.getAttribute("IndependentFlag"), localColumnEntityType.getAttribute("GlobalFlag"),
-				localColumnEntityType.getAttribute("axistype")));
+				localColumnEntityType.getAttribute("axistype"), localColumnEntityType.getAttribute("RawDatatype"),
+				localColumnEntityType.getAttribute("MeaQuantity")));
 
 		// OpenATFX can't handle multiple search conditions, so use just one...
 		Filter filter = Filter.and().add(
@@ -430,12 +509,14 @@
 
 			if (null == setColumnNames || setColumnNames.contains(columnName)) {
 				String columnId = result.getRecord(localColumnEntityType).getID();
-				ColumnAttributes ca = new ColumnAttributes(columnName, columnId,
-						(ValuesMode.CALCULATED == readRequest.getValuesMode() ? SequenceRepresentation.EXPLICIT
-								: mapValues.get("SequenceRepresentation").extract()),
-						new double[0], ((short) mapValues.get("IndependentFlag").extract() != 0),
-						mapValues.get("axistype").extract(),
-						mapValues.get("GlobalFlag").extractWithDefault(null));
+				SequenceRepresentation seqRep = ValuesMode.CALCULATED == readRequest.getValuesMode()
+						? SequenceRepresentation.EXPLICIT
+						: mapValues.get("SequenceRepresentation").extract();
+
+				ColumnAttributes ca = new ColumnAttributes(columnName, columnId, seqRep, new double[0],
+						((short) mapValues.get("IndependentFlag").extract() != 0), mapValues.get("axistype").extract(),
+						mapValues.get("GlobalFlag").extractWithDefault(null), mapValues.get("RawDatatype").extract(),
+						mapValues.get("MeaQuantity").extract());
 
 				mapColumnAttributes.put(mapValues.get("Id").extract(), ca);
 			}
@@ -456,10 +537,59 @@
 			}
 		}
 
+		applyUnitNamesAndDataTypesFallback(mapColumnAttributes.values());
+
 		return mapColumnAttributes;
 	}
 
 	/**
+	 * Loads and sets the unit property of the given ColumnAttributes. This method
+	 * does not use Queries with joins as openATFX does not support it. Instead, two
+	 * queries are used. (LocalColumn.Id, LocalColumn.MeaQuantity) ->
+	 * (MeaQuantity.Id, MeaQuantity.Unit) -> (Unit.Id, Unit.Name)
+	 * 
+	 * @param mapColumnAttributes ColumnAttributes to set the unit.
+	 */
+	private void applyUnitNamesAndDataTypesFallback(Collection<ColumnAttributes> columnAttributes) {
+		EntityType meaQuantityEntityType = modelManager.getEntityType(Channel.class);
+
+		Query queryMq = queryService.createQuery().select(meaQuantityEntityType.getIDAttribute(),
+				meaQuantityEntityType.getAttribute("DataType"), meaQuantityEntityType.getAttribute("Unit"));
+		Filter filterMq = Filter.and().add(ComparisonOperator.IN_SET.create(meaQuantityEntityType.getIDAttribute(),
+				columnAttributes.stream().map(ca -> ca.getMeaQuantityId()).toArray(String[]::new)));
+
+		Map<String, String> mqId2unitId = new HashMap<>();
+		Map<String, ScalarType> mqId2datatype = new HashMap<>();
+
+		for (Result result : queryMq.fetch(filterMq)) {
+			Map<String, Value> mapValues = result.getRecord(meaQuantityEntityType).getValues();
+			mqId2unitId.put(result.getRecord(meaQuantityEntityType).getID(), mapValues.get("Unit").extract());
+			mqId2datatype.put(result.getRecord(meaQuantityEntityType).getID(), mapValues.get("DataType").extract());
+		}
+
+		EntityType unitEntityType = modelManager.getEntityType(Unit.class);
+
+		Query queryUnit = queryService.createQuery().select(unitEntityType.getIDAttribute(),
+				unitEntityType.getNameAttribute());
+		Filter filterUnit = Filter.and().add(ComparisonOperator.IN_SET.create(unitEntityType.getIDAttribute(),
+				mqId2unitId.values().stream().distinct().toArray(String[]::new)));
+
+		Map<String, String> mqId2unitName = new HashMap<>();
+
+		for (Result result : queryUnit.fetch(filterUnit)) {
+			Map<String, Value> mapValues = result.getRecord(unitEntityType).getValues();
+
+			mqId2unitName.put(result.getRecord(unitEntityType).getID(), mapValues.get("Name").extract());
+		}
+
+		columnAttributes.forEach(ca -> {
+			String unitId = mqId2unitId.get(ca.getMeaQuantityId());
+			ca.setUnit(mqId2unitName.get(unitId));
+			ca.setDataType(mqId2datatype.get(ca.getMeaQuantityId()));
+		});
+	}
+
+	/**
 	 * Returns the {@link ValueMatrix} CORBA service object associated with given
 	 * {@link ReadRequest}.
 	 *
diff --git a/api/odsadapter/src/main/java/org/eclipse/mdm/api/odsadapter/transaction/WriteRequestHandler.java b/api/odsadapter/src/main/java/org/eclipse/mdm/api/odsadapter/transaction/WriteRequestHandler.java
index 9e3a884..4f97478 100644
--- a/api/odsadapter/src/main/java/org/eclipse/mdm/api/odsadapter/transaction/WriteRequestHandler.java
+++ b/api/odsadapter/src/main/java/org/eclipse/mdm/api/odsadapter/transaction/WriteRequestHandler.java
@@ -131,8 +131,9 @@
 		values.get(AE_LC_ATTR_AXISTYPE).set(writeRequest.getAxisType());
 		values.get(AE_LC_ATTR_PARAMETERS).set(writeRequest.getGenerationParameters());
 
+		ValueType<?> valueType = writeRequest.getRawScalarType().toValueType();
+
 		if (writeRequest.hasValues()) {
-			ValueType<?> valueType = writeRequest.getRawScalarType().toValueType();
 			String unitName = writeRequest.getChannel().getUnit().getName();
 			values.put(AE_LC_ATTR_VALUES,
 					valueType.create(AE_LC_ATTR_VALUES, unitName, true, writeRequest.getValues()));
@@ -152,6 +153,9 @@
 				IntStream.range(0, genParamD.length)
 						.forEach(i -> genParamD[i] = ((Number) Array.get(genParamValues, i)).doubleValue());
 				values.get(AE_LC_ATTR_PARAMETERS).set(genParamD);
+
+				// remove GenerationParameters from Values
+				values.put(AE_LC_ATTR_VALUES, valueType.create(AE_LC_ATTR_VALUES));
 			}
 
 			// flags
@@ -162,9 +166,10 @@
 				values.get(AE_LC_ATTR_FLAGS).set(flags);
 			}
 		} else if (writeRequest.hasExternalComponents()) {
-			// No values to write (ext comps are used instead), so remove Values attribute from map:
-			values.remove(AE_LC_ATTR_VALUES); 
-			
+			// No values to write (ext comps are used instead), but we have to set the
+			// Values attribute to empty, based on the rawDataType
+			values.put(AE_LC_ATTR_VALUES, valueType.create(AE_LC_ATTR_VALUES));
+
 			// Set global flag as specified in the WriteRequest:
 			values.get(AE_LC_ATTR_GLOBAL_FLAG).set(writeRequest.getGlobalFlag());
 			
@@ -172,15 +177,17 @@
 					.getEntityType("ExternalComponent");
 			
 			List<Attribute> listAttrsExtComp = externalComponentEntityType.getAttributes();
-			boolean hasMimeType = (listAttrsExtComp.stream().filter(a -> Entity.ATTR_MIMETYPE.equals(a.getName())).count() > 0);
-			boolean hasBitCount = (listAttrsExtComp.stream().filter(a -> AE_EC_ATTR_BITCOUNT.equals(a.getName())).count() > 0);
-			boolean hasBitOffset = (listAttrsExtComp.stream().filter(a -> AE_EC_ATTR_BITOFFSET.equals(a.getName())).count() > 0);
+			boolean hasMimeType = (listAttrsExtComp.stream().filter(a -> Entity.ATTR_MIMETYPE.equals(a.getName()))
+					.count() > 0);
+			boolean hasBitCount = (listAttrsExtComp.stream().filter(a -> AE_EC_ATTR_BITCOUNT.equals(a.getName()))
+					.count() > 0);
+			boolean hasBitOffset = (listAttrsExtComp.stream().filter(a -> AE_EC_ATTR_BITOFFSET.equals(a.getName()))
+					.count() > 0);
 
 			int ordinalNumber = 1;
 			for (ExternalComponent extComp : writeRequest.getExternalComponents()) {
 				Core extCompCore = new DefaultCore(externalComponentEntityType);
-				ExternalComponentEntity extCompEntity = new ExternalComponentEntity(
-						extCompCore);
+				ExternalComponentEntity extCompEntity = new ExternalComponentEntity(extCompCore);
 				extCompEntity.setName(writeRequest.getChannel().getName());
 				if (hasMimeType) {
 					extCompEntity.setMimeType(new MimeType("application/x-asam.aoexternalcomponent"));
@@ -203,7 +210,8 @@
 				}
 
 				core.getChildrenStore().add(extCompEntity);
-				extCompCore.getPermanentStore().set(new BaseEntity(core) {});
+				extCompCore.getPermanentStore().set(new BaseEntity(core) {
+				});
 			}
 		} else {
 			throw new IllegalStateException("Given write request neither has measured values nor external components");
diff --git a/api/odsadapter/src/main/java/org/eclipse/mdm/api/odsadapter/utils/ODSConverter.java b/api/odsadapter/src/main/java/org/eclipse/mdm/api/odsadapter/utils/ODSConverter.java
index 1ecf311..dd023e6 100644
--- a/api/odsadapter/src/main/java/org/eclipse/mdm/api/odsadapter/utils/ODSConverter.java
+++ b/api/odsadapter/src/main/java/org/eclipse/mdm/api/odsadapter/utils/ODSConverter.java
@@ -758,8 +758,8 @@
 		}
 
 		if (null == columnAttributes) {
-			columnAttributes = new ReadRequestHandler.ColumnAttributes("", null, null, new double[0], false, null,
-					null);
+			columnAttributes = new ReadRequestHandler.ColumnAttributes("", null, null, new double[0], false, null, null,
+					null, null);
 		}
 
 		return scalarType.createMeasuredValues(odsMeasuredValues.valName, odsMeasuredValues.unit,
@@ -775,46 +775,18 @@
 	 * @return The converted {@code MeasuredValues} is returned.
 	 * @throws DataAccessException Thrown on conversion errors.
 	 */
-	public static MeasuredValues fromExternalComponents(List<ExternalComponent> externalComponents, DataType dataType,
-			String unit, ReadRequestHandler.ColumnAttributes columnAttributes) throws DataAccessException {
-		ScalarType scalarType;
-
-		if (DataType.DT_STRING == dataType) {
-			scalarType = ScalarType.STRING;
-		} else if (DataType.DT_DATE == dataType) {
-			scalarType = ScalarType.DATE;
-		} else if (DataType.DT_BOOLEAN == dataType) {
-			scalarType = ScalarType.BOOLEAN;
-		} else if (DataType.DT_BYTE == dataType) {
-			scalarType = ScalarType.BYTE;
-		} else if (DataType.DT_SHORT == dataType) {
-			scalarType = ScalarType.SHORT;
-		} else if (DataType.DT_LONG == dataType) {
-			scalarType = ScalarType.INTEGER;
-		} else if (DataType.DT_LONGLONG == dataType) {
-			scalarType = ScalarType.LONG;
-		} else if (DataType.DT_FLOAT == dataType) {
-			scalarType = ScalarType.FLOAT;
-		} else if (DataType.DT_DOUBLE == dataType) {
-			scalarType = ScalarType.DOUBLE;
-		} else if (DataType.DT_BYTESTR == dataType) {
-			scalarType = ScalarType.BYTE_STREAM;
-		} else if (DataType.DT_COMPLEX == dataType) {
-			scalarType = ScalarType.FLOAT_COMPLEX;
-		} else if (DataType.DT_DCOMPLEX == dataType) {
-			scalarType = ScalarType.DOUBLE_COMPLEX;
-		} else if (DataType.DT_EXTERNALREFERENCE == dataType) {
-			scalarType = ScalarType.FILE_LINK;
-		} else {
-			throw new DataAccessException(
-					"Conversion for ODS measured points of type '" + dataType.toString() + "' does not exist.");
-		}
-
+	public static MeasuredValues fromExternalComponents(List<ExternalComponent> externalComponents,
+			ReadRequestHandler.ColumnAttributes columnAttributes) throws DataAccessException {
 		if (null == columnAttributes) {
 			throw new DataAccessException("No ColumnAttributes provided.");
 		}
 
-		return scalarType.createMeasuredValues(columnAttributes.getName(), unit,
+		ScalarType scalarType = columnAttributes.getRawDataType();
+		if (scalarType == ScalarType.UNKNOWN) {
+			scalarType = columnAttributes.getDataType();
+		}
+
+		return scalarType.createMeasuredValues(columnAttributes.getName(), columnAttributes.getUnit(),
 				columnAttributes.getSequenceRepresentation(), columnAttributes.getGenerationParameters(),
 				columnAttributes.isIndependentColumn(), columnAttributes.getAxisType(),
 				columnAttributes.getGlobalFlag(), externalComponents);
diff --git a/api/odsadapter/src/main/java/org/eclipse/mdm/api/odsadapter/utils/ODSEnumerations.java b/api/odsadapter/src/main/java/org/eclipse/mdm/api/odsadapter/utils/ODSEnumerations.java
index 36b686f..a40ad9e 100644
--- a/api/odsadapter/src/main/java/org/eclipse/mdm/api/odsadapter/utils/ODSEnumerations.java
+++ b/api/odsadapter/src/main/java/org/eclipse/mdm/api/odsadapter/utils/ODSEnumerations.java
@@ -190,7 +190,7 @@
 		} else if (constant instanceof ScalarType) {
 			return toODSScalarType((ScalarType) constant);
 		} else if (constant instanceof VersionState || constant instanceof Interpolation || constant instanceof AxisType
-				|| constant instanceof TypeSpecification) {
+				|| constant instanceof TypeSpecification || constant instanceof ODSEnum) {
 			// NOTE: Ordinal numbers map directly to the corresponding ODS
 			// enumeration constant value.
 			return constant.ordinal();
diff --git a/api/odsadapter/src/test/java/org/eclipse/mdm/api/odsadapter/ODSAdapterExtCompTest.java b/api/odsadapter/src/test/java/org/eclipse/mdm/api/odsadapter/ODSAdapterExtCompTest.java
index 00d7d20..4f4d4bd 100644
--- a/api/odsadapter/src/test/java/org/eclipse/mdm/api/odsadapter/ODSAdapterExtCompTest.java
+++ b/api/odsadapter/src/test/java/org/eclipse/mdm/api/odsadapter/ODSAdapterExtCompTest.java
@@ -398,46 +398,26 @@
 			FileLink fileLinkFlags2 = FileLink.newLocal(fileFlags2.toPath());
 
 			wrb = WriteRequest.create(channelGroup, channels.get(3), AxisType.Y_AXIS);
-			writeRequests.add(wrb.explicit().externalComponents(
-					ScalarType.BYTE,
-					Lists.newArrayList((new ExternalComponent()).setTypeSpecification(TypeSpecification.BYTE)
-							.setLength(5)
-							.setStartOffset(0)
-							.setBlocksize(5)
-							.setValuesPerBlock(5)
-							.setValueOffset(0)
-							.setFileLink(fileLinkValues1)
-							.setFlagsStartOffset(0L)
-							.setFlagsFileLink(fileLinkFlags1),
-							(new ExternalComponent()).setTypeSpecification(TypeSpecification.BYTE)
-							.setLength(5)
-							.setStartOffset(0)
-							.setBlocksize(5)
-							.setValuesPerBlock(5)
-							.setValueOffset(0)
-							.setFileLink(fileLinkValues2)
-							.setFlagsStartOffset(0L)
-							.setFlagsFileLink(fileLinkFlags2)))
+			writeRequests.add(wrb.explicitExternal().externalComponents(ScalarType.BYTE, Lists.newArrayList(
+					(new ExternalComponent()).setTypeSpecification(TypeSpecification.BYTE).setLength(5)
+							.setStartOffset(0).setBlocksize(5).setValuesPerBlock(5).setValueOffset(0)
+							.setFileLink(fileLinkValues1).setFlagsStartOffset(0L).setFlagsFileLink(fileLinkFlags1),
+					(new ExternalComponent()).setTypeSpecification(TypeSpecification.BYTE).setLength(5)
+							.setStartOffset(0).setBlocksize(5).setValuesPerBlock(5).setValueOffset(0)
+							.setFileLink(fileLinkValues2).setFlagsStartOffset(0L).setFlagsFileLink(fileLinkFlags2)))
 					.build());
 
 			wrb = WriteRequest.create(channelGroup, channels.get(4), AxisType.Y_AXIS);
-			writeRequests.add(wrb.explicit().externalComponents(
-					ScalarType.INTEGER,
-					Lists.newArrayList((new ExternalComponent()).setTypeSpecification(TypeSpecification.INTEGER)
-							.setLength(5)
-							.setStartOffset(5L)
-							.setBlocksize(20)
-							.setValuesPerBlock(5)
-							.setValueOffset(0)
-							.setFileLink(fileLinkValues1),
-							(new ExternalComponent()).setTypeSpecification(TypeSpecification.INTEGER)
-							.setLength(5)
-							.setStartOffset(5L)
-							.setBlocksize(20)
-							.setValuesPerBlock(5)
-							.setValueOffset(0)
-							.setFileLink(fileLinkValues2)))
-					.build());
+			writeRequests
+					.add(wrb.explicitExternal()
+							.externalComponents(ScalarType.INTEGER, Lists.newArrayList(
+									(new ExternalComponent()).setTypeSpecification(TypeSpecification.INTEGER)
+											.setLength(5).setStartOffset(5L).setBlocksize(20).setValuesPerBlock(5)
+											.setValueOffset(0).setFileLink(fileLinkValues1),
+									(new ExternalComponent()).setTypeSpecification(TypeSpecification.INTEGER)
+											.setLength(5).setStartOffset(5L).setBlocksize(20).setValuesPerBlock(5)
+											.setValueOffset(0).setFileLink(fileLinkValues2)))
+							.build());
 		} catch (IOException exc) {
 
 		}
diff --git a/nucleus/apicopy/src/main/java/org/eclipse/mdm/apicopy/boundary/ExportResource.java b/nucleus/apicopy/src/main/java/org/eclipse/mdm/apicopy/boundary/ExportResource.java
index 1c93e87..1634346 100644
--- a/nucleus/apicopy/src/main/java/org/eclipse/mdm/apicopy/boundary/ExportResource.java
+++ b/nucleus/apicopy/src/main/java/org/eclipse/mdm/apicopy/boundary/ExportResource.java
@@ -14,7 +14,6 @@
 package org.eclipse.mdm.apicopy.boundary;
 
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -23,6 +22,7 @@
 import java.util.Comparator;
 import java.util.List;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipOutputStream;
 
@@ -66,17 +66,21 @@
 
 	@POST
 	@Consumes(MediaType.APPLICATION_XML)
-	@Produces(MediaType.APPLICATION_XML)
+	@Produces({ MediaType.APPLICATION_XML, "application/zip" })
 	public Response exportAtfx(ShoppingBasket basket) throws IOException {
 
 		final Path exportAtfx = exportService.exportAtfx(basket);
 
-		List<File> files = Files.list(exportAtfx).map(p -> p.toFile()).collect(Collectors.toList());
-		if (files.size() == 1) {
+		final List<Path> paths;
+		try (Stream<Path> files = Files.walk(exportAtfx)) {
+			paths = files.filter(p -> p.toFile().isFile()).collect(Collectors.toList());
+		}
+
+		if (paths.size() == 1) {
 			StreamingOutput output = new StreamingOutput() {
 				@Override
 				public void write(OutputStream out) throws IOException {
-					try (final InputStream responseStream = new FileInputStream(files.get(0))) {
+					try (final InputStream responseStream = Files.newInputStream(paths.get(0))) {
 						int length;
 						byte[] buffer = new byte[1024];
 						while ((length = responseStream.read(buffer)) != -1) {
@@ -96,16 +100,16 @@
 				}
 			};
 
-			return Response.ok(output, MediaType.APPLICATION_XML).build();
+			return Response.ok(output, MediaType.APPLICATION_XML)
+					.header("content-disposition", "attachment; filename = export.atfx").build();
 		} else {
 			StreamingOutput out = new StreamingOutput() {
 				@Override
 				public void write(OutputStream output) throws IOException, WebApplicationException {
 					try (ZipOutputStream out = new ZipOutputStream(output)) {
-						List<File> files = Files.list(exportAtfx).map(p -> p.toFile()).collect(Collectors.toList());
-						for (File f : files) {
-							out.putNextEntry(new ZipEntry(f.getName()));
-							try (InputStream in = new FileInputStream(f)) {
+						for (Path p : paths) {
+							out.putNextEntry(new ZipEntry(exportAtfx.relativize(p).toString()));
+							try (InputStream in = Files.newInputStream(p)) {
 								int read = 0;
 								byte[] bytes = new byte[1024];
 
@@ -114,8 +118,6 @@
 								}
 							}
 						}
-					} catch (Exception e) {
-						e.printStackTrace();
 					} finally {
 						if (exportAtfx != null) {
 							try {
@@ -129,7 +131,8 @@
 					}
 				}
 			};
-			return Response.ok(out, "application/zip").build();
+			return Response.ok(out, "application/zip")
+					.header("content-disposition", "attachment; filename = export.zip").build();
 		}
 	}
 
@@ -140,6 +143,8 @@
 	 * @throws IOException
 	 */
 	private void deleteDirectory(java.nio.file.Path pathToBeDeleted) throws IOException {
-		Files.walk(pathToBeDeleted).sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
+		try (Stream<Path> stream = Files.walk(pathToBeDeleted)) {
+			stream.sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
+		}
 	}
 }
diff --git a/nucleus/apicopy/src/main/java/org/eclipse/mdm/apicopy/boundary/ImportService.java b/nucleus/apicopy/src/main/java/org/eclipse/mdm/apicopy/boundary/ImportService.java
index d6db379..4a7a944 100644
--- a/nucleus/apicopy/src/main/java/org/eclipse/mdm/apicopy/boundary/ImportService.java
+++ b/nucleus/apicopy/src/main/java/org/eclipse/mdm/apicopy/boundary/ImportService.java
@@ -21,6 +21,7 @@
 import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
+import java.util.stream.Stream;
 
 import javax.ejb.Stateless;
 import javax.inject.Inject;
@@ -130,7 +131,9 @@
 	 * @throws IOException
 	 */
 	private void deleteDirectory(java.nio.file.Path pathToBeDeleted) throws IOException {
-		Files.walk(pathToBeDeleted).sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
+		try (Stream<Path> paths = Files.walk(pathToBeDeleted)) {
+			paths.sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
+		}
 	}
 
 	/**
diff --git a/nucleus/apicopy/src/main/java/org/eclipse/mdm/apicopy/control/TransferBase.java b/nucleus/apicopy/src/main/java/org/eclipse/mdm/apicopy/control/TransferBase.java
index 417dfca..4d933d6 100644
--- a/nucleus/apicopy/src/main/java/org/eclipse/mdm/apicopy/control/TransferBase.java
+++ b/nucleus/apicopy/src/main/java/org/eclipse/mdm/apicopy/control/TransferBase.java
@@ -403,15 +403,11 @@
 			}
 		}
 
-		if (SequenceRepresentation.EXPLICIT.equals(seqRep) || SequenceRepresentation.EXPLICIT_EXTERNAL.equals(seqRep)) {
+		if (SequenceRepresentation.EXPLICIT.equals(seqRep)) {
 			builder = wrb.explicit();
-
-			if (SequenceRepresentation.EXPLICIT_EXTERNAL.equals(seqRep)) {
-
-				return castBuilder(builder, AnyTypeValuesBuilder.class)
-						.externalComponents(scalarType, measuredValues.getExternalComponents()).independent(independent)
-						.build();
-			}
+		} else if (SequenceRepresentation.EXPLICIT_EXTERNAL.equals(seqRep)) {
+			return wrb.explicitExternal().externalComponents(scalarType, measuredValues.getExternalComponents())
+					.independent(independent).build();
 		} else if (SequenceRepresentation.IMPLICIT_CONSTANT.equals(seqRep)) {
 			checkGenerationParameters(generationParameters, 1);
 
@@ -427,38 +423,36 @@
 			return wrb
 					.implicitSaw(scalarType, generationParameters[0], generationParameters[1], generationParameters[2])
 					.build();
-		} else if (SequenceRepresentation.RAW_LINEAR.equals(seqRep)
-				|| SequenceRepresentation.RAW_LINEAR_EXTERNAL.equals(seqRep)) {
+		} else if (SequenceRepresentation.RAW_LINEAR.equals(seqRep)) {
 			checkGenerationParameters(generationParameters, 2);
 
 			builder = wrb.rawLinear(generationParameters[0], generationParameters[1]);
 
-			if (SequenceRepresentation.RAW_LINEAR_EXTERNAL.equals(seqRep)) {
-				return castBuilder(builder, AnyTypeValuesBuilder.class)
-						.externalComponents(scalarType, measuredValues.getExternalComponents()).independent(independent)
-						.build();
-			}
-		} else if (SequenceRepresentation.RAW_LINEAR_CALIBRATED.equals(seqRep)
-				|| SequenceRepresentation.RAW_LINEAR_CALIBRATED_EXTERNAL.equals(seqRep)) {
+		} else if (SequenceRepresentation.RAW_LINEAR_EXTERNAL.equals(seqRep)) {
+			checkGenerationParameters(generationParameters, 2);
+
+			return wrb.rawLinearExternal(generationParameters[0], generationParameters[1])
+					.externalComponents(scalarType, measuredValues.getExternalComponents()).independent(independent)
+					.build();
+		} else if (SequenceRepresentation.RAW_LINEAR_CALIBRATED.equals(seqRep)) {
 			checkGenerationParameters(generationParameters, 3);
 
 			builder = wrb.rawLinearCalibrated(generationParameters[0], generationParameters[1],
 					generationParameters[2]);
+		} else if (SequenceRepresentation.RAW_LINEAR_CALIBRATED_EXTERNAL.equals(seqRep)) {
+			checkGenerationParameters(generationParameters, 3);
 
-			if (SequenceRepresentation.RAW_LINEAR_CALIBRATED_EXTERNAL.equals(seqRep)) {
-				return castBuilder(builder, AnyTypeValuesBuilder.class)
-						.externalComponents(scalarType, measuredValues.getExternalComponents()).independent(independent)
-						.build();
-			}
-		} else if (SequenceRepresentation.RAW_POLYNOMIAL.equals(seqRep)
-				|| SequenceRepresentation.RAW_POLYNOMIAL_EXTERNAL.equals(seqRep)) {
+			return wrb
+					.rawLinearCalibratedExternal(generationParameters[0], generationParameters[1],
+							generationParameters[2])
+					.externalComponents(scalarType, measuredValues.getExternalComponents()).independent(independent)
+					.build();
+		} else if (SequenceRepresentation.RAW_POLYNOMIAL.equals(seqRep)) {
 			builder = wrb.rawPolynomial(generationParameters);
-
-			if (SequenceRepresentation.RAW_POLYNOMIAL_EXTERNAL.equals(seqRep)) {
-				return castBuilder(builder, AnyTypeValuesBuilder.class)
-						.externalComponents(scalarType, measuredValues.getExternalComponents()).independent(independent)
-						.build();
-			}
+		} else if (SequenceRepresentation.RAW_POLYNOMIAL_EXTERNAL.equals(seqRep)) {
+			return wrb.rawPolynomialExternal(generationParameters)
+					.externalComponents(scalarType, measuredValues.getExternalComponents()).independent(independent)
+					.build();
 		}
 
 		if (scalarType.isString()) {
@@ -629,7 +623,7 @@
 	}
 
 	<T extends NumericalValuesBuilder> T castBuilder(NumericalValuesBuilder builder, Class<T> cls) {
-		if (!cls.isInstance(builder)) {
+		if (!builder.getClass().isAssignableFrom(cls)) {
 			throw new IllegalStateException(String.format(
 					"Error creating the write values builder, expected class is %s, actual class is %s (likely column data type and sequence representation mismatch)!",
 					cls.getName(), (null == builder ? "???" : builder.getClass().getName())));
diff --git a/nucleus/apicopy/src/main/resources/emptyAtfx.xml b/nucleus/apicopy/src/main/resources/emptyAtfx.xml
index a6a14e7..a40e264 100644
--- a/nucleus/apicopy/src/main/resources/emptyAtfx.xml
+++ b/nucleus/apicopy/src/main/resources/emptyAtfx.xml
@@ -4338,7 +4338,7 @@
 			<application_attribute>
 				<name>FlagsStartOffset</name>
 				<base_attribute>flags_start_offset</base_attribute>
-				<datatype>DT_LONG</datatype>
+				<datatype>DT_LONGLONG</datatype>
 				<autogenerate>false</autogenerate>
 				<obligatory>false</obligatory>
 				<unique>false</unique>
diff --git a/nucleus/apicopy/src/test/java/org/eclipse/mdm/apicopy/FileUtils.java b/nucleus/apicopy/src/test/java/org/eclipse/mdm/apicopy/FileUtils.java
index 19e8542..3494439 100644
--- a/nucleus/apicopy/src/test/java/org/eclipse/mdm/apicopy/FileUtils.java
+++ b/nucleus/apicopy/src/test/java/org/eclipse/mdm/apicopy/FileUtils.java
@@ -22,6 +22,7 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.Comparator;
+import java.util.stream.Stream;
 
 public class FileUtils {
 	/**
@@ -49,6 +50,8 @@
 	 * @throws IOException
 	 */
 	public static void deleteDirectory(java.nio.file.Path pathToBeDeleted) throws IOException {
-		Files.walk(pathToBeDeleted).sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
+		try (Stream<Path> paths = Files.walk(pathToBeDeleted)) {
+			paths.sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
+		}
 	}
 }
diff --git a/nucleus/businessobjects/src/main/java/org/eclipse/mdm/businessobjects/service/ContextService.java b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/businessobjects/service/ContextService.java
index 3eaf779..f41fb33 100644
--- a/nucleus/businessobjects/src/main/java/org/eclipse/mdm/businessobjects/service/ContextService.java
+++ b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/businessobjects/service/ContextService.java
@@ -58,6 +58,7 @@
 import io.vavr.collection.HashMap;
 import io.vavr.collection.List;
 import io.vavr.collection.Map;
+import io.vavr.control.Option.Some;
 import io.vavr.control.Try;
 
 /**
@@ -400,26 +401,38 @@
 		return ec;
 	}
 
+	@SuppressWarnings("unchecked")
 	private java.util.Map<ContextType, ContextRoot> updateContextDescribableContext(
 			ContextDescribable contextDescribable, Map<ContextType, Object> rootMap, ContextType[] contextTypes) {
 		java.util.Map<ContextType, ContextRoot> existingRootMap = getEntityManager(contextDescribable.getSourceName())
 				.loadContexts(contextDescribable, contextTypes);
 
 		for (ContextType contextType : contextTypes) {
-			ContextRoot existingRoot = existingRootMap.computeIfAbsent(contextType,
-					ct -> createContextRoot(contextDescribable, ct));
-
-			rootMap.get(contextType).forEach(newContextRoot -> updateContextRoot(existingRoot, newContextRoot));
+			boolean hasValues;
+			Object obj = rootMap.get(contextType);
+			if (obj instanceof Some && ((Some) obj).get() instanceof ArrayList && !((Some<ArrayList<?>>) obj).get().isEmpty()) {
+				hasValues = true;
+			} else {
+				hasValues = false;
+			}
+		    ContextRoot existingRoot = existingRootMap.computeIfAbsent(contextType,
+				ct -> createContextRoot(contextDescribable, ct, hasValues));
+		    rootMap.get(contextType).forEach(newContextRoot -> updateContextRoot(existingRoot, newContextRoot));
 		}
 		return existingRootMap;
 	}
 
-	private ContextRoot createContextRoot(ContextDescribable contextDescribable, ContextType contextType) {
+	private ContextRoot createContextRoot(ContextDescribable contextDescribable, ContextType contextType, boolean hasValues) {
 		EntityFactory factory = getEntityFactory(contextDescribable.getSourceName());
 
 		TemplateRoot templateRoot = findTemplateRoot(contextDescribable, contextType);
 
-		if (contextDescribable instanceof Measurement) {
+	    if (templateRoot == null) {
+	    	if (hasValues) {
+	    		throw new MDMEntityAccessException("Found measurements for '" + contextType + "' without a Template defined!");
+	    	}
+	    	return null;
+	    } else if (contextDescribable instanceof Measurement) {
 			return factory.createContextRoot((Measurement) contextDescribable, templateRoot);
 		} else if (contextDescribable instanceof TestStep) {
 			return factory.createContextRoot((TestStep) contextDescribable, templateRoot);
@@ -456,8 +469,7 @@
 		TemplateTestStep tpl = TemplateTestStep.of(testStep).orElseThrow(
 				() -> new MDMEntityAccessException("Cannot find TemplateTestStep for TestStep: " + testStep));
 
-		return tpl.getTemplateRoot(contextType).orElseThrow(() -> new MDMEntityAccessException(
-				"Cannot find TemplateRoot for ContextType " + contextType + " on Template TestStep " + tpl));
+		return tpl.getTemplateRoot(contextType).orElse(null);
 	}
 
 	public TestStep getTestStep(ContextDescribable contextDescribable) {
diff --git a/nucleus/businessobjects/src/main/resources/org/eclipse/mdm/businessobjects/control/i18n/locale/localization.properties b/nucleus/businessobjects/src/main/resources/org/eclipse/mdm/businessobjects/control/i18n/locale/localization.properties
index 3d78479..8455e99 100644
--- a/nucleus/businessobjects/src/main/resources/org/eclipse/mdm/businessobjects/control/i18n/locale/localization.properties
+++ b/nucleus/businessobjects/src/main/resources/org/eclipse/mdm/businessobjects/control/i18n/locale/localization.properties
@@ -53,6 +53,11 @@
 ChannelGroup.MimeType = MimeType
 ChannelGroup.SubMatrixNoRows = Rows
 
+SubMatrix = ChannelGroup
+SubMatrix.Name = Name
+SubMatrix.MimeType = MimeType
+SubMatrix.SubMatrixNoRows = Rows
+
 Channel = Channel
 Channel.Name = Name
 Channel.Description = MimeType
@@ -60,4 +65,13 @@
 Channel.Minimum = Minimum
 Channel.Maximum = Maximum
 Channel.Average = Average
-Channel.Deviation = Deviation
\ No newline at end of file
+Channel.Deviation = Deviation
+
+MeaQuantity = Channel
+MeaQuantity.Name = Name
+MeaQuantity.Description = MimeType
+MeaQuantity.DataType = DataType
+MeaQuantity.Minimum = Minimum
+MeaQuantity.Maximum = Maximum
+MeaQuantity.Average = Average
+MeaQuantity.Deviation = Deviation
\ No newline at end of file
diff --git a/nucleus/webclient/src/main/webapp/src/app/basket/basket.service.ts b/nucleus/webclient/src/main/webapp/src/app/basket/basket.service.ts
index 783a868..d9cc26b 100644
--- a/nucleus/webclient/src/main/webapp/src/app/basket/basket.service.ts
+++ b/nucleus/webclient/src/main/webapp/src/app/basket/basket.service.ts
@@ -14,7 +14,7 @@
 
 
 import {Injectable, Output, EventEmitter} from '@angular/core';
-import { Http, Response, Headers, RequestOptions } from '@angular/http';
+import { Http, Response, Headers, RequestOptions, ResponseContentType } from '@angular/http';
 
 import {of as observableOf, Observable} from 'rxjs';
 import {defaultIfEmpty, catchError, mergeMap, map} from 'rxjs/operators';
@@ -116,8 +116,7 @@
     headers.append('Content-Type', 'application/xml');
 
     return this.getBasketAsXml(basket)
-      .flatMap(b => this.http.post(this._prop.getUrl('mdm/export'), b, { headers: headers }))
-      .map(r => r.text());
+      .flatMap(b => this.http.post(this._prop.getUrl('mdm/export'), b, { responseType: ResponseContentType.Blob, headers: headers }));
   }
 
   getFileExtensions() {
diff --git a/nucleus/webclient/src/main/webapp/src/app/basket/mdm-basket.component.ts b/nucleus/webclient/src/main/webapp/src/app/basket/mdm-basket.component.ts
index 40fa2ba..2ef1da6 100644
--- a/nucleus/webclient/src/main/webapp/src/app/basket/mdm-basket.component.ts
+++ b/nucleus/webclient/src/main/webapp/src/app/basket/mdm-basket.component.ts
@@ -309,9 +309,13 @@
   exportItemsToAtfx(basket: MDMItem[]) {
     let downloadContent = new Basket(this.basketName, basket);
 
-    this._basketService.getBasketAsAtfx(downloadContent)
-      .map(atfx => new Blob([atfx], { type: 'application/xml' }))
-      .subscribe(atfx => this.saveAsFile(atfx, '.atfx'));
+    this._basketService.getBasketAsAtfx(downloadContent).pipe(
+      map(resp => { 
+        var contentDisposition = resp.headers.get('content-disposition');
+        var filename = contentDisposition.split(';')[1].split('filename')[1].split('=')[1].trim();
+        return { filename: filename, data: resp.blob() }; })
+    )
+    .subscribe( (r: { filename: string, data: Blob }) => FileSaver.saveAs(r.data, r.filename));
   }
 
   saveAsFile(blob: Blob, fileExtension: string, defaultFilename = 'export') {
diff --git a/nucleus/webclient/src/main/webapp/src/app/chartviewer/chartviewer.module.ts b/nucleus/webclient/src/main/webapp/src/app/chartviewer/chartviewer.module.ts
index efd8d58..849fce2 100644
--- a/nucleus/webclient/src/main/webapp/src/app/chartviewer/chartviewer.module.ts
+++ b/nucleus/webclient/src/main/webapp/src/app/chartviewer/chartviewer.module.ts
@@ -40,6 +40,7 @@
 import { XyChartViewerToolbarComponent } from './components/xy-chart-viewer-toolbar/xy-chart-viewer-toolbar.component';
 import { XyChartDataSelectionPanelComponent } from './components/xy-chart-data-selection-panel/xy-chart-data-selection-panel.component';
 import { ConfirmationService } from 'primeng/api';
+import { TooltipModule } from 'primeng/primeng';
 
 @NgModule({
   imports: [
@@ -59,6 +60,7 @@
     ListboxModule,
     PanelModule,
     ConfirmDialogModule,
+    TooltipModule
   ],
   declarations: [
     ChartViewerNavCardComponent,
diff --git a/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/chartviewer/chart-viewer.component.html b/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/chartviewer/chart-viewer.component.html
index fcb98ad..fe12308 100644
--- a/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/chartviewer/chart-viewer.component.html
+++ b/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/chartviewer/chart-viewer.component.html
@@ -14,7 +14,7 @@
 
 <p-chart #lineChart type="line" [data]="data" [options]="options"></p-chart>
 
-<p-accordion>
+<!-- <p-accordion>
     <p-accordionTab header="{{'chartviewer.request-options.options' | translate}}">
         
         {{'chartviewer.request-options.from' | translate}} <input type="number" numbersOnly pInputText [(ngModel)]="rangeValues[0]" />
@@ -28,4 +28,4 @@
         </div>
         <p-button label="{{'chartviewer.request-options.apply' | translate}}" (onClick)="applySettings($event)"></p-button>
     </p-accordionTab>
-</p-accordion>
+</p-accordion> -->
diff --git a/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/request-options/request-options.component.html b/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/request-options/request-options.component.html
index 5101f26..3e4a3b4 100644
--- a/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/request-options/request-options.component.html
+++ b/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/request-options/request-options.component.html
@@ -66,10 +66,12 @@
       </div>
 
       <!-- Row 3 -->
-      <div class="p-col-12" style="text-align: right;">
-        <button class="btn btn-mdm" (click)="onApplySettings($event)">
-          <span class="fa fa-check"></span>
-          {{'chartviewer.request-options.apply' | translate}}
+      <div class="p-col-2 p-offset-8">
+        <button pButton class="btn-mdm" (click)="onResetSettings($event)" style="width: 100%;" icon="fa fa-undo" [label]="'chartviewer.request-options.reset' | translate" >
+        </button>
+      </div>
+      <div class="p-col-2">
+        <button pButton class="btn-mdm" (click)="onApplySettings($event)" style="width: 100%;" icon="fa fa-check" [label]="'chartviewer.request-options.apply' | translate" >
         </button>
       </div>
     </div>
diff --git a/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/request-options/request-options.component.ts b/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/request-options/request-options.component.ts
index 556cdac..53ee8a2 100644
--- a/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/request-options/request-options.component.ts
+++ b/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/request-options/request-options.component.ts
@@ -34,9 +34,9 @@
   private maxRequestedValues = 10000;
 
   public numberOfRows = 0;
-  public numberOfChunks = 600;
+  public numberOfChunks: number;
   public step: number;
-  public previewEnabled = true;
+  public previewEnabled: boolean;
 
   @Input()
   public measurement: Measurement;
@@ -48,6 +48,7 @@
     private chartService: ChartViewerDataService) { }
 
   ngOnInit() {
+    this.initSettings();
     this.reload();
     this.emitRequestOptionsChanged();
   }
@@ -59,6 +60,11 @@
     }
   }
 
+  private initSettings() {
+    this.numberOfChunks = 600;
+    this.previewEnabled = true;
+  }
+
   private reload() {
     if (this.measurement != undefined && this.measurement.channelGroups.length > 0) {
       this.numberOfRows = this.measurement.getFirstChannelGroup().numberOfRows;
@@ -90,6 +96,12 @@
     this.emitRequestOptionsChanged();
   }
 
+  public onResetSettings(event: Event) {
+    this.initSettings();
+    this.reload();
+    this.emitRequestOptionsChanged();
+  }
+
   private emitRequestOptionsChanged() {
     this.onRequestOptionsChanged.emit(
       new RequestOptions(this.rangeValues[1] - this.rangeValues[0], this.rangeValues[0] - 1, this.previewEnabled, this.numberOfChunks));
diff --git a/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/xy-chart-data-selection-panel/xy-chart-data-selection-panel.component.css b/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/xy-chart-data-selection-panel/xy-chart-data-selection-panel.component.css
new file mode 100644
index 0000000..692ef09
--- /dev/null
+++ b/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/xy-chart-data-selection-panel/xy-chart-data-selection-panel.component.css
@@ -0,0 +1,36 @@
+/********************************************************************************
+ * Copyright (c) 2015-2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ ********************************************************************************/
+ 
+.filter-container {
+  position: relative;
+}
+
+.filter-input-group {
+  line-height: 1;
+  position: absolute;
+  left: 2.3rem;
+  z-index: 99;
+  top: 2.4rem;
+  right: .25rem;
+}
+
+.filter-input-field {
+  width: calc(100% - 2.2rem);
+}
+
+p-listbox ::ng-deep .ui-listbox-header.ui-listbox-header-w-checkbox {
+  height: 2.5rem;
+  display: flex;
+  align-items: center;
+}
\ No newline at end of file
diff --git a/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/xy-chart-data-selection-panel/xy-chart-data-selection-panel.component.html b/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/xy-chart-data-selection-panel/xy-chart-data-selection-panel.component.html
index d91ae6e..ffabd1f 100644
--- a/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/xy-chart-data-selection-panel/xy-chart-data-selection-panel.component.html
+++ b/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/xy-chart-data-selection-panel/xy-chart-data-selection-panel.component.html
@@ -11,15 +11,26 @@
  * SPDX-License-Identifier: EPL-2.0
  *
  ********************************************************************************-->
-
 <div class="p-grid">
-  <div class="p-col-12" style="margin-top: 4px">
+  <div class="p-col-12" style="margin-top: 4px;">
+    <!-- Filter for Listbox. Is overlay via absolute positioning. Enables regex filter, which is not available in prime component -->
+    <div class="filter-container">
+      <span class="filter-input-group">
+        <span class="ui-inputgroup">
+          <input type="text" pInputText [(ngModel)]="channelGroupFilter" [pTooltip]="'chartviewer.xy-chart-data-selection-panel.tooltip-regexp-filter' | translate"
+          [placeholder]="'chartviewer.xy-chart-data-selection-panel.filter' | translate" class="filter-input-field"
+          (keyup)="onFilterChannelGroups($event)"/>
+          <span class="ui-inputgroup-addon">
+            <span class="fa fa-filter"></span>
+          </span>
+        </span>
+      </span>
+    </div>
     <p-listbox
-      [options]="measurement.channelGroups"
+      [options]="channelGroupOptions"
       [(ngModel)]="selectedChannelGroups"
       multiple="multiple"
       checkbox="checkbox"
-      filter="filter"
       optionLabel="name"
       (onChange)="onSelectedChannelGroupsChanged($event)"
       [ngClass]="{'hiddenList': hiddenGroups}"
@@ -36,18 +47,30 @@
     </p-listbox>
   </div>
   <div class="p-col-12">
-    <p-listbox [options]="yChannelOptions"
+    <!-- Filter for Listbox. Is overlay via absolute positioning. Enables regex filter, which is not available in prime component -->
+    <div class="filter-container">
+      <span class="filter-input-group">
+        <span class="ui-inputgroup">
+          <input type="text" pInputText [(ngModel)]="yChannelFilter" [pTooltip]="'chartviewer.xy-chart-data-selection-panel.tooltip-regexp-filter' | translate"
+          [placeholder]="'chartviewer.xy-chart-data-selection-panel.filter' | translate" class="filter-input-field"
+          (keyup)="onFilterYChannels($event)"/>
+          <span class="ui-inputgroup-addon">
+            <span class="fa fa-filter"></span>
+          </span>
+        </span>
+      </span>
+    </div>
+    <p-listbox [options]="yChannelOptionsFiltered"
       [(ngModel)]="selectedYChannels"
       multiple="multiple"
       checkbox="checkbox"
-      filter="filter"
       optionLabel="name"
       (onChange)="onSelectedYChannelsChanged($event)"
       [ngClass]="{'hiddenList': hiddenYChannels}"
       [style]="{'width':'100%'}"
       [styleClass]="'ypanel'"
       [listStyle]="{'height':'200px'}"
-      [showToggleAll]="false">
+      [showToggleAll]="true">
       <p-header>
         <span class="icon channel"></span>
         Y-{{'MeaQuantity' | mdmdatasourcetranslate}}
diff --git a/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/xy-chart-data-selection-panel/xy-chart-data-selection-panel.component.ts b/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/xy-chart-data-selection-panel/xy-chart-data-selection-panel.component.ts
index 31fe6da..663a013 100644
--- a/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/xy-chart-data-selection-panel/xy-chart-data-selection-panel.component.ts
+++ b/nucleus/webclient/src/main/webapp/src/app/chartviewer/components/xy-chart-data-selection-panel/xy-chart-data-selection-panel.component.ts
@@ -20,7 +20,6 @@
 import { ArrayUtilService } from 'src/app/core/services/array-util.service';
 import { MDMNotificationService } from 'src/app/core/mdm-notification.service';
 import { TYPE_MEASUREMENT, TYPE_CHANNELGROUP, TYPE_CHANNEL } from '../../model/constants';
-import { tap } from 'rxjs/operators';
 import { Node } from '../../../navigator/node';
 import {ConfirmationService} from 'primeng/api';
 import { TranslateService } from '@ngx-translate/core';
@@ -31,7 +30,7 @@
 @Component({
   selector: 'mdm5-xy-chart-data-selection-panel',
   templateUrl: './xy-chart-data-selection-panel.component.html',
-  styleUrls: ['../../chart-viewer.style.css']
+  styleUrls: ['../../chart-viewer.style.css', './xy-chart-data-selection-panel.component.css']
 })
 export class XyChartDataSelectionPanelComponent implements OnInit, OnDestroy, OnChanges {
 
@@ -48,6 +47,9 @@
   public yChannelOptions: Channel[] = [];
   public xChannelOptions: {[key: string]: Channel[]} = {};
 
+  public channelGroupOptions: ChannelGroup[] = [];
+  public yChannelOptionsFiltered: Channel[] = [];
+
   // channel(group) selection for chart
   // !! change via setter to update cache for workarround !!
   public selectedChannelGroups: ChannelGroup[] = [];
@@ -65,6 +67,10 @@
   public hiddenGroups = false;
   public hiddenYChannels = false;
 
+  // Filterstrings
+  public channelGroupFilter: string;
+  public yChannelFilter: string;
+
   // for x-channel default value
   private independentChannels = new Map<string, Channel>();
 
@@ -97,6 +103,9 @@
         this.selectionChanged = false;
       }
     }
+    if(changes['measurement']) {
+      this.initChannelGroupOptions();
+    }
   }
 
   /**************** Html-template listeners *****************************/
@@ -169,6 +178,18 @@
     this.hiddenYChannels = !this.hiddenYChannels;
   }
 
+  public onFilterChannelGroups(event: KeyboardEvent ) {
+    this.initChannelGroupOptions();
+  }
+
+  public onFilterYChannels(event: KeyboardEvent ) {
+    this.filterYChannelOptions();
+  }
+
+  
+
+  /**************** Private methods *****************************/
+
   private handleDeselectChannelGroup(channelGroup: ChannelGroup) {
     // remove rows
     if (this.arrayUtil.isNotEmpty(this.selectedChannelRows)) {
@@ -196,11 +217,13 @@
     if (node != undefined) {
       switch (node.type) {
         case TYPE_MEASUREMENT:
+          /** measurement is set via @input and thus new ChannelGroupoption are set set via ngOnChanges */
           this.resetChannelData();
           this.fireSelectionChanged();
           break;
         case TYPE_CHANNELGROUP:
           this.cleanOldState();
+          this.initChannelGroupOptions();
           this.addChannelGroupToSelection(node.id, this.measurement.findChannelGroup(node.id));
           break;
         case TYPE_CHANNEL:
@@ -315,6 +338,7 @@
         this.yChannelOptions = this.yChannelOptions.concat(channels);
       }
     }
+    this.filterYChannelOptions()
   }
 
   /**
@@ -326,6 +350,7 @@
     this.setSelectedYChannels([]);
     this.xChannelOptions = {};
     this.yChannelOptions = [];
+    this.yChannelOptionsFiltered = [];
   }
 
     /**
@@ -362,7 +387,7 @@
     const index = this.selectedChannelRows.findIndex(row => row.yChannel.id === yChannel.id);
     if (index > -1) {
       this.selectedChannelRows.splice(index, 1);
-      this.selectedChannelRows = [].concat(this.selectedChannelRows);
+      this.selectedChannelRows = [].concat(this.selectedChannelRows).sort((cs1, cs2) => cs1.yChannel.name.localeCompare(cs2.yChannel.name));
     } else {
       this.addRow(channelGroup, yChannel);
     }
@@ -376,6 +401,7 @@
   private addRow(channelGroup: ChannelGroup, yChannel: Channel) {
     const row = new ChannelSelectionRow(channelGroup, yChannel, this.getDefaultXChannel(channelGroup));
     this.selectedChannelRows.push(row);
+    this.selectedChannelRows.sort((cs1, cs2) => cs1.yChannel.name.localeCompare(cs2.yChannel.name))
   }
 
   /**
@@ -434,4 +460,29 @@
     this.selectedChannelGroups = channelGroups;
     this.lastChannelGroupSelection = channelGroups;
   }
+
+  private initChannelGroupOptions() {
+    this.channelGroupOptions = this.filter(this.measurement.channelGroups, this.channelGroupFilter);
+  }
+
+  private filterYChannelOptions() {
+    this.yChannelOptionsFiltered = this.filter(this.yChannelOptions, this.yChannelFilter);
+  }
+
+  private filter<T extends {name: string}>(options: T[], filterString: string) {
+    let result: T[];
+    if(filterString != undefined && filterString !== '') {
+      try {
+        const regEx = new RegExp(filterString);
+        result = options.filter(opt => regEx != undefined ? regEx.test(opt.name) : false);
+      } catch {
+        result = options.filter(opt => opt.name.includes(filterString));
+      }
+    } else {
+      result = options;
+    }
+
+    return result.sort((c1, c2) => c1.name.localeCompare(c2.name));
+  }
+
 }
diff --git a/nucleus/webclient/src/main/webapp/src/app/details/components/mdm-detail-descriptive-data/mdm-detail-descriptive-data.component.ts b/nucleus/webclient/src/main/webapp/src/app/details/components/mdm-detail-descriptive-data/mdm-detail-descriptive-data.component.ts
index d64ca38..e1e4f40 100644
--- a/nucleus/webclient/src/main/webapp/src/app/details/components/mdm-detail-descriptive-data/mdm-detail-descriptive-data.component.ts
+++ b/nucleus/webclient/src/main/webapp/src/app/details/components/mdm-detail-descriptive-data/mdm-detail-descriptive-data.component.ts
@@ -287,21 +287,26 @@
       };
       list.push(tmp);
     }
-
-    if (node.relations != null && node.relations.length > 0) {
-      for (let relation of node.relations) {
-        if (relation.ids != null && relation.ids.length > 0) {
-          for (let id of relation.ids) {
-            let nodes = this.getNodes(children, id);
-            for (let n of nodes) {
-              list.push(this.createTreeNode(n, children));
-            }
-          }
-        }
+    
+    let tplCompRel = node.relations.find(r => r.entityType === "TemplateComponent")
+    if (tplCompRel && tplCompRel.ids.length > 0) {
+      let nodes = this.getNodesWithTplCompId(children, tplCompRel.ids[0]);
+      for (let n of nodes) {
+        list.push(this.createTreeNode(n, children));
       }
     }
+
     return list;
   }
+  
+  /**
+   * Searches for nodes which have a relation to the given TemplateComponent ID.
+   * @param nodes List with nodes to search
+   * @param tplCompId  TemplateComponent ID
+   */
+  getNodesWithTplCompId(nodes: Node[], tplCompId: string) {
+    return nodes.filter(n => n.relations[0].parentId == tplCompId);
+  }
 
   /**
    * Method to create a tree structure from the flat context entity and attribute map
diff --git a/nucleus/webclient/src/main/webapp/src/app/search/edit-searchFields.component.html b/nucleus/webclient/src/main/webapp/src/app/search/edit-searchFields.component.html
index 15cad3e..32cd04e 100644
--- a/nucleus/webclient/src/main/webapp/src/app/search/edit-searchFields.component.html
+++ b/nucleus/webclient/src/main/webapp/src/app/search/edit-searchFields.component.html
@@ -57,7 +57,8 @@
                   typeaheadOptionsLimit="15"
                   placeholder="{{ 'search.edit-searchfields.enter-search-text' | translate }}"
                   class="form-control"
-                  style="margin-bottom: 15px;">
+                  style="margin-bottom: 15px;"
+                  [title]="'search.edit-searchfields.tooltip-no-wildcards' | translate">
               <searchattribute-tree [searchAttributes]="searchAttributes" [environments]="environments"></searchattribute-tree>
             </div>
             <div class="col-md-8" style="min-height: 50vh; max-height: 77vh; overflow-y:auto;">
diff --git a/nucleus/webclient/src/main/webapp/src/app/search/filter.service.ts b/nucleus/webclient/src/main/webapp/src/app/search/filter.service.ts
index 64bef6b..bbddadd 100644
--- a/nucleus/webclient/src/main/webapp/src/app/search/filter.service.ts
+++ b/nucleus/webclient/src/main/webapp/src/app/search/filter.service.ts
@@ -24,25 +24,37 @@
 
 export enum Operator {
   EQUALS,
+  NOT_EQUALS,
   LESS_THAN,
   GREATER_THAN,
-  LIKE
+  LIKE,
+  GREATER_THAN_OR_EQUAL,
+  LESS_THAN_OR_EQUAL,
+  BETWEEN
 }
 
 export namespace OperatorUtil {
   export function values() {
-    return [ Operator.EQUALS, Operator.LESS_THAN, Operator.GREATER_THAN, Operator.LIKE ];
+    return Object.keys(Operator).filter(k => isNaN(+k)).map(key => Operator[key]);
   }
   export function toString(operator: Operator) {
       switch (operator) {
           case Operator.EQUALS:
             return '=';
+          case Operator.NOT_EQUALS:
+            return '!=';
           case Operator.LESS_THAN:
             return '<';
           case Operator.GREATER_THAN:
             return '>';
           case Operator.LIKE:
             return 'like';
+          case Operator.GREATER_THAN_OR_EQUAL:
+            return '≥';
+          case Operator.LESS_THAN_OR_EQUAL:
+            return '≤';
+          case Operator.BETWEEN:
+            return 'between';
           default:
             return undefined;
       }
@@ -51,12 +63,20 @@
       switch (operator) {
           case Operator.EQUALS:
             return 'eq';
+          case Operator.NOT_EQUALS:
+            return 'ne';
           case Operator.LESS_THAN:
             return 'lt';
           case Operator.GREATER_THAN:
             return 'gt';
           case Operator.LIKE:
             return 'lk';
+          case Operator.GREATER_THAN_OR_EQUAL:
+            return 'ge';
+          case Operator.LESS_THAN_OR_EQUAL:
+            return 'le';
+          case Operator.BETWEEN:
+            return 'bw';
           default:
             return undefined;
       }
@@ -71,18 +91,20 @@
   valueType: string;
 
   @Exclude()
+  isCurrentlyLinked: boolean;
+  @Exclude()
+  isCreatedViaLink: boolean;
+  @Exclude()
   sortIndex: number;
 
-  constructor(type: string, attribute: string, operator: Operator, value: string[], valueType?: string) {
+  constructor(type: string, attribute: string, operator: Operator, value: string[], valueType = 'string', isCurrentlyLinked = false, isCreatedViaLink = false) {
     this.type = type;
     this.attribute = attribute;
     this.operator = operator;
     this.value = value;
-    if (valueType) {
-      this.valueType = valueType.toLowerCase();
-    } else {
-      this.valueType = 'string';
-    }
+    this.valueType = valueType.toLowerCase();
+    this.isCreatedViaLink = isCreatedViaLink;
+    this.isCurrentlyLinked = isCurrentlyLinked;
   }
 }
 
diff --git a/nucleus/webclient/src/main/webapp/src/app/search/mdm-search.component.html b/nucleus/webclient/src/main/webapp/src/app/search/mdm-search.component.html
index 5252208..5cd0ddf 100644
--- a/nucleus/webclient/src/main/webapp/src/app/search/mdm-search.component.html
+++ b/nucleus/webclient/src/main/webapp/src/app/search/mdm-search.component.html
@@ -18,6 +18,40 @@
     outline: 0;
     box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(0, 0, 0,.6);
 }
+p-togglebutton >>> .ui-button {
+  font-size: 14px;
+  margin: 0 3px;
+  color: #6c757d;
+  background-color: transparent;
+  background-image: none;
+  border-color: #6c757d;
+  height: 100%;
+  width: 2.4rem;
+  margin-top: -2px;
+}
+p-togglebutton ::ng-deep .ui-button-icon-only .ui-button-icon-left {
+  margin-top: -.45em;
+}
+p-togglebutton >>> .ui-button.ui-state-active {
+  /* color: #333333!important; */
+  color: #fff!important;
+  background-color: #a0a0a0;
+}
+
+p-togglebutton >>> .ui-button.ui-state-active >>> .ui-button-icon-left {
+  /* color: #333333!important; */
+  color: #fff!important;
+  background-color: #a0a0a0;
+}
+
+p-togglebutton >>> .ui-button:not(.ui-state-active).ui-state-focus {
+  border-color: #6c757d;
+  background-color: transparent!important;
+}
+
+p-togglebutton >>> .ui-button:not(.ui-state-active).ui-state-focus >>> .ui-button-icon-left {
+  color: #6c757d!important;
+}
 
 /* :host /deep/ .ui-multiselect .ui-multiselect-label {
   padding: .25em 2em .25em .25em;
@@ -36,7 +70,20 @@
     </div>
     <div class="col-sm">
       <label for="search-resulttype" class="col-form-label">{{ 'search.mdm-search.lbl-result-type' | translate }}</label>
-      <p-dropdown [options]="definitions" optionLabel="label" (onChange)="onResultTypeChane($event)" [style]="{'width': '100%'}"></p-dropdown>
+      <p-dropdown [options]="definitions" (onChange)="onResultTypeChanged($event)" [style]="{'width': '100%'}">
+        <ng-template let-option pTemplate="selectedItem">
+          <div style="display: flex; align-items: center;">
+            <img [src]="option?.type | typeIcon" style="padding-right: .5rem;"/>
+            <span>{{option?.label}}</span>
+          </div>
+        </ng-template>
+        <ng-template let-option pTemplate="item">
+          <div style="display: flex; align-items: center;">
+            <img [src]="option?.type | typeIcon" style="padding-right: .5rem;"/>
+            <span>{{option?.label}}</span>
+          </div>
+        </ng-template>
+      </p-dropdown>
     </div>
   </div>
   <div class="row" style="margin-top: 5px; margin-bottom: 10px;">
@@ -62,6 +109,9 @@
           <button type="button" class="btn btn-mdm" (click)="deleteFilter($event)" title="{{ 'search.mdm-search.tooltip-delete-search-filter' | translate }}"><span class="fa fa-times"></span></button>
         </div>
         <div class="col text-right">
+          <p-toggleButton [(ngModel)]="isSearchLinkedToNavigator" [onIcon]="'fa fa-link'" offIcon="fa fa-unlink" onLabel="" offLabel="" (onChange)="onToggleSearchLinkedToNavigator($event)"
+            [title]="(isSearchLinkedToNavigator ? 'search.mdm-search.tooltip-unlink-search-attributes-from-navigator' : 'search.mdm-search.tooltip-link-search-attributes-to-navigator')| translate">
+          </p-toggleButton>
           <button type="button" class="btn btn-mdm" (click)="resetConditions($event)" title="{{ 'search.mdm-search.tooltip-reset-search-conditions' | translate }}" [disabled]=!isAdvancedSearchActive><span class="fa fa-eraser"></span></button>
           <span class="fa" style="color: #333 !important; padding-left: 15px;" [ngClass]="{'fa-chevron-down': advancedSearch?.isOpen, 'fa-chevron-right': !advancedSearch?.isOpen}"></span>
         </div>
diff --git a/nucleus/webclient/src/main/webapp/src/app/search/mdm-search.component.ts b/nucleus/webclient/src/main/webapp/src/app/search/mdm-search.component.ts
index ca98675..a3e91cb 100644
--- a/nucleus/webclient/src/main/webapp/src/app/search/mdm-search.component.ts
+++ b/nucleus/webclient/src/main/webapp/src/app/search/mdm-search.component.ts
@@ -16,11 +16,11 @@
 import {Component, ViewChild, OnInit, OnDestroy} from '@angular/core';
 
 import {SearchService, SearchDefinition, SearchAttribute, SearchLayout} from './search.service';
-import {of as observableOf, combineLatest as observableCombineLatest} from 'rxjs';
+import {of as observableOf, combineLatest as observableCombineLatest, Subscription} from 'rxjs';
 
 import {defaultIfEmpty, mergeMap, tap} from 'rxjs/operators';
 
-import {FilterService, SearchFilter, Condition} from './filter.service';
+import {FilterService, SearchFilter, Condition, Operator} from './filter.service';
 import {NodeService} from '../navigator/node.service';
 import {BasketService} from '../basket/basket.service';
 import {QueryService, SearchResult } from '../tableview/query.service';
@@ -45,6 +45,7 @@
 
 import { TranslateService } from '@ngx-translate/core';
 import { streamTranslate, TRANSLATE } from '../core/mdm-core.module';
+import { NavigatorService } from '@navigator/navigator.service';
 
 @Component({
   selector: 'mdm-search',
@@ -89,6 +90,9 @@
     { label: 'Add to shopping basket', icon: 'fa fa-shopping-cart', command: (event) => this.addSelectionToBasket() }
   ];
 
+  public isSearchLinkedToNavigator: false;
+  private linkedToNavigatorSub = new Subscription();
+
   @ViewChild(ViewComponent)
   viewComponent: ViewComponent;
 
@@ -117,7 +121,8 @@
     private localService: LocalizationService,
     private notificationService: MDMNotificationService,
     private basketService: BasketService,
-    private translateService: TranslateService) { }
+    private translateService: TranslateService,
+    private navigatorService: NavigatorService) { }
 
   ngOnInit() {
     this.currentFilter = this.filterService.EMPTY_FILTER;
@@ -146,6 +151,7 @@
 
   ngOnDestroy() {
      this.saveState();
+     this.linkedToNavigatorSub.unsubscribe();
   }
 
   init(envs: Node[], attrs: { [type: string]: { [env: string]: SearchAttribute[] }},
@@ -231,13 +237,12 @@
     this.searchFields.splice(index, 1);
   }
 
-  onResultTypeChane(e: any) {
-    this.selectResultType(e.value);
-  }
-
-  selectResultType(type: any) {
-    this.currentFilter.resultType = type.type;
-    this.updateSearchAttributesForCurrentResultType();
+  onResultTypeChanged(event: {value: string, originalEvent: Event}) {
+    const searchDef = this.definitions.find(sd => sd.value === event.value);
+    if (searchDef) {
+      this.currentFilter.resultType = searchDef.type;
+      this.updateSearchAttributesForCurrentResultType();
+    }
   }
 
   updateSearchAttributesForCurrentResultType() {
@@ -456,4 +461,45 @@
     }
     this.lazySelectedRow = this.selectedRow;
   }
+
+  public onToggleSearchLinkedToNavigator(event: {checked: boolean, originalEvent: MouseEvent}) {
+    // prevent advanced-search-tab to toggle.
+    event.originalEvent.stopPropagation();
+
+    if(event.checked) {
+      const current = this.navigatorService.getSelectedNode();
+      if(current) {
+        this.addLinkedConditions(current);
+      }
+      this.linkedToNavigatorSub = this.navigatorService.selectedNodeChanged.subscribe(node => this.addLinkedConditions(node));
+    } else {
+      this.linkedToNavigatorSub.unsubscribe();
+      this.unlinkConditions();
+    }
+  }
+
+  private addLinkedConditions(node: Node) {
+    // remove other conditions created via link
+    const oldIndex = this.currentFilter.conditions.findIndex(cond => cond.isCreatedViaLink);
+    if (oldIndex > -1) {
+      this.currentFilter.conditions.splice(oldIndex, 1);
+    }
+    // unlink other conditions (the ones that where not created via link but got linked)
+    this.unlinkConditions();
+    // link the related condition or create linked condition if none exists
+    if (node.type != 'Environment') {
+      const linkedCondition = this.currentFilter.conditions.find(cond => cond.type === node.type && cond.attribute === 'Id');
+      if (linkedCondition) {
+        linkedCondition.value = [node.id];
+        linkedCondition.isCurrentlyLinked = true;
+      } else {
+        this.currentFilter.conditions.push(new Condition(node.type, 'Id', Operator.EQUALS, [node.id], 'string', true, true));
+      }
+    }
+    this.calcCurrentSearch();
+  }
+
+  private unlinkConditions() {
+    this.currentFilter.conditions.forEach(cond => cond.isCurrentlyLinked = false);
+  }
 }
diff --git a/nucleus/webclient/src/main/webapp/src/app/search/mdm-search.module.ts b/nucleus/webclient/src/main/webapp/src/app/search/mdm-search.module.ts
index 760c623..4473b42 100644
--- a/nucleus/webclient/src/main/webapp/src/app/search/mdm-search.module.ts
+++ b/nucleus/webclient/src/main/webapp/src/app/search/mdm-search.module.ts
@@ -27,19 +27,25 @@
 import {FilterService} from './filter.service';
 
 import {SearchattributeTreeModule} from '../searchattribute-tree/searchattribute-tree.module';
-import {AutoCompleteModule} from 'primeng/primeng';
+import {AutoCompleteModule, InputTextModule, ToggleButtonModule} from 'primeng/primeng';
+import {TooltipModule} from 'primeng/tooltip';
+import { TypeIconPipe } from './pipes/type-icon.pipe';
 
 @NgModule({
   imports: [
     MDMCoreModule,
     TableViewModule,
     SearchattributeTreeModule,
-    AutoCompleteModule
+    AutoCompleteModule,
+    TooltipModule,
+    ToggleButtonModule,
+    InputTextModule
   ],
   declarations: [
     MDMSearchComponent,
     SearchConditionComponent,
     EditSearchFieldsComponent,
+    TypeIconPipe,
   ],
   exports: [
     MDMSearchComponent,
@@ -48,7 +54,8 @@
   providers: [
     SearchService,
     FilterService,
-    DatePipe
+    DatePipe,
+    TypeIconPipe
   ],
 })
 export class MDMSearchModule {
diff --git a/nucleus/webclient/src/main/webapp/src/app/search/pipes/type-icon.pipe.spec.ts b/nucleus/webclient/src/main/webapp/src/app/search/pipes/type-icon.pipe.spec.ts
new file mode 100644
index 0000000..fb1779a
--- /dev/null
+++ b/nucleus/webclient/src/main/webapp/src/app/search/pipes/type-icon.pipe.spec.ts
@@ -0,0 +1,22 @@
+/********************************************************************************
+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ ********************************************************************************/
+ 
+ import { TypeIconPipe } from './type-icon.pipe';
+
+describe('TypeIconPipe', () => {
+  it('create an instance', () => {
+    const pipe = new TypeIconPipe();
+    expect(pipe).toBeTruthy();
+  });
+});
diff --git a/nucleus/webclient/src/main/webapp/src/app/search/pipes/type-icon.pipe.ts b/nucleus/webclient/src/main/webapp/src/app/search/pipes/type-icon.pipe.ts
new file mode 100644
index 0000000..fd2ae37
--- /dev/null
+++ b/nucleus/webclient/src/main/webapp/src/app/search/pipes/type-icon.pipe.ts
@@ -0,0 +1,65 @@
+/********************************************************************************
+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ ********************************************************************************/
+
+import { Pipe, PipeTransform } from '@angular/core';
+import { SearchDefinition } from '../search.service';
+
+type nodeType = 'Environment' | 'Pool' | 'Project' | 'Test' | 'TestStep' | 'Measurement' | 'ChannelGroup' | 'Channel' | 'Quantity' | 'Unit';
+
+@Pipe({
+  name: 'typeIcon'
+})
+export class TypeIconPipe implements PipeTransform {
+
+  transform(value: nodeType | SearchDefinition): any {
+    let type: string;
+    if (value instanceof SearchDefinition) {
+      type = value.type
+    } else {
+      type = value;
+    }
+    let imagePath = 'assets/famfamfam_icons/';
+
+    switch (type as nodeType) {
+      case 'Environment':
+        imagePath += 'database.png';
+        break;
+      case 'Project':
+        imagePath += 'house.png';
+        break;
+      case 'Pool':
+        imagePath += 'paste_plain.png';
+        break;
+      case 'Test':
+        imagePath += 'brick_add.png';
+        break;
+      case 'TestStep':
+        imagePath += 'brick.png';
+        break;
+      case 'Measurement':
+        imagePath += 'chart_curve.png';
+        break;
+      case 'ChannelGroup':
+        imagePath += 'calendar.png';
+        break;
+      case 'Channel':
+      case 'Unit':
+      case 'Quantity':
+        imagePath += 'chart_curve_go.png';
+        break;
+    }
+    return imagePath;
+  }
+
+}
diff --git a/nucleus/webclient/src/main/webapp/src/app/search/search-condition.component.css b/nucleus/webclient/src/main/webapp/src/app/search/search-condition.component.css
index 7cda7f7..dca279d 100644
--- a/nucleus/webclient/src/main/webapp/src/app/search/search-condition.component.css
+++ b/nucleus/webclient/src/main/webapp/src/app/search/search-condition.component.css
@@ -15,37 +15,32 @@
 
 table.searchdefinition td { vertical-align: middle; padding: 4px 8px; }
 
-p-autoComplete >>> .ui-inputtext.ui-state-focus {
-  border: 1px solid #66afe9 !important;
-  outline: 0 !important;
-  box-shadow: 0 0 8px rgba(102,175,233,.6) !important;
-}
-
-p-autoComplete >>> .ui-autocomplete.ui-autocomplete-multiple .ui-autocomplete-multiple-container {
-  padding: 2.5px 8px !important;
-  border: 1px solid #ccc;
-  color: #555;
-  box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
-  width: 100%;
-}
-
-p-autoComplete >>> .ui-autocomplete-multiple-container.ui-inputtext {
+p-autoComplete ::ng-deep .ui-autocomplete-multiple-container.ui-inputtext {
   overflow: inherit !important;
 }
 
-p-autoComplete >>> .ui-autocomplete {
+p-autoComplete ::ng-deep ul {
   width: 100%;
 }
 
-p-autoComplete >>> .ui-widget:disabled{
-  cursor: not-allowed !important;
-  background-color: #eee;
-  color: #555 !important;
-  opacity: 1;
+p-autoComplete ::ng-deep .ui-widget:disabled,
+p-autoComplete ::ng-deep .ui-state-disabled,
+p-autoComplete ::ng-deep input {
+  opacity: .5;
 }
-p-autoComplete >>> .ui-state-disabled{
-  cursor: not-allowed !important;
-  background-color: #eee !important;
-  border-color: #e3e3e3 !important;
-  opacity: 1;
+
+::ng-deep .ui-tooltip-text {
+  width: 20rem;
+}
+
+.linked-icon {
+  margin-left: 4px;
+  color:gray;
+}
+
+.linked-container {
+  display: inline-grid;
+  grid-template-columns: 1.8rem auto;
+  align-items: center;
+  width: 100%;
 }
diff --git a/nucleus/webclient/src/main/webapp/src/app/search/search-condition.component.html b/nucleus/webclient/src/main/webapp/src/app/search/search-condition.component.html
index deccb96..669e46e 100644
--- a/nucleus/webclient/src/main/webapp/src/app/search/search-condition.component.html
+++ b/nucleus/webclient/src/main/webapp/src/app/search/search-condition.component.html
@@ -12,10 +12,10 @@
  *
  ********************************************************************************-->
 
-<td style="vertical-align: middle">{{condition.type | mdmdatasourcetranslate}}</td> <!-- | translate -->
+<td style="vertical-align: middle"><span>{{condition.type | mdmdatasourcetranslate}}</span></td> <!-- | translate -->
 <td style="vertical-align: middle">{{condition.type | mdmdatasourcetranslate: condition.attribute }}</td> <!--  | translate: condition.attribute -->
 <td style="vertical-align: middle">
-  <div class="btn-group" dropdown style="width: 66px;">
+  <div class="btn-group" dropdown style="width: 6rem;">
     <button id="operator" type="button" title="{{ 'search.search-condition.tooltip-select-search-operator' | translate }}" class="btn btn-default btn-sm dropdown-toggle" dropdownToggle aria-haspopup="true" aria-expanded="false" [disabled]="disabled">
       {{getOperatorName(condition.operator)}} <span class="caret"></span>
     </button>
@@ -27,29 +27,111 @@
   </div>
 </td>
 <td [ngSwitch]="condition.valueType">
-  <p-autoComplete *ngSwitchCase="'string'"
-    [(ngModel)]="condition.value"
-    [suggestions]="displayedSuggestions"
-    (completeMethod)="updateSuggestions($event)"
-    [multiple]="true"
-    [delay]="0"
-    [size]="500"
-    [scrollHeight]="'50vh'"
-    placeholder="Wert"
-    [disabled]="disabled"
-    (keyup.enter)="onEnter($event)">
-  </p-autoComplete>
-  <p-calendar *ngSwitchCase="'date'" 
-    (ngModelChange)="setDateValue($event)" 
-    [ngModel]="dateValue" 
-    dateFormat="dd.mm.yy" 
-    showTime="true" 
-    hourFormat="24" 
-    hideOnDateTimeSelect="true" 
-    [showIcon]="true" 
-    [disabled]="disabled"
-    [ngStyle]="{ 'width': '100%' }">
-  </p-calendar>
-  <input *ngSwitchDefault type="text" class="form-control input-sm" placeholder="Wert" [value]="condition.value" (input)="setValue($event.target.value)" [disabled]="disabled">
+  <ng-container *ngSwitchCase="'string'">
+    <div [ngClass]="{'linked-container': condition?.isCurrentlyLinked}">
+      <span *ngIf="condition?.isCurrentlyLinked" class="fa fa-link linked-icon" pTooltip="{{ 'search.search-condition.condition-is-linked' | translate }}"></span>
+      <p-autoComplete #multi *ngIf="isHandlingMultipleValues"
+        [(ngModel)]="condition.value"
+        [suggestions]="displayedSuggestions"
+        (completeMethod)="updateSuggestions($event)"
+        [multiple]="true"
+        [delay]="0"
+        [size]="500"
+        [scrollHeight]="'50vh'"
+        placeholder="Wert"
+        [disabled]="disabled"
+        (keyup.enter)="onEnter($event, multi)"
+        (onBlur)="onAutocompleteBlur($event, multi)"
+        [style]="{ 'width': '100%'}"
+        [inputStyle]="{ 'width': '100%' }"
+        pTooltip="{{ 'search.search-condition.multi-value-input' | translate }}" tooltipPosition="top">
+      </p-autoComplete>
+    </div>
+    <p-autoComplete #first *ngIf="!isHandlingMultipleValues"
+      [(ngModel)]="stringValueStart"
+      [suggestions]="displayedSuggestions"
+      (completeMethod)="updateSuggestions($event)"
+      [multiple]="false"
+      [delay]="0"
+      [size]="500"
+      [scrollHeight]="'50vh'"
+      placeholder="Wert"
+      [disabled]="disabled"
+      (keyup.enter)="onEnter($event, first)"
+      (onBlur)="onAutocompleteBlur($event, first)"
+      [style]="{ 'width': isBinaryOperator ? 'calc(50% - 1.5rem)' : '100%'}"
+      [inputStyle]="{ 'width': '100%' }"
+      pTooltip="{{ 'search.search-condition.single-value-input' | translate }}" tooltipPosition="top">
+    </p-autoComplete>
+    <ng-container *ngIf="isBinaryOperator">
+      <span style="width: 3rem; text-align: center; display: inline-block;">{{'search.search-condition.and' | translate}}</span>
+      <p-autoComplete #second
+        [(ngModel)]="stringValueEnd"
+        [suggestions]="displayedSuggestions"
+        (completeMethod)="updateSuggestions($event)"
+        [multiple]="false"
+        [delay]="0"
+        [size]="500"
+        [scrollHeight]="'50vh'"
+        placeholder="Wert"
+        [disabled]="disabled"
+        (keyup.enter)="onEnter($event, second)"
+        (onBlur)="onAutocompleteBlur($event, second)"
+        [style]="{ 'width': 'calc(50% - 1.5rem)'}"
+        [inputStyle]="{ 'width': '100%' }"
+        pTooltip="{{ 'search.search-condition.single-value-input' | translate }}" tooltipPosition="top">
+      </p-autoComplete>
+    </ng-container>
+  </ng-container>
+
+  <ng-container *ngSwitchCase="'date'" >
+    <p-calendar
+      (ngModelChange)="setDateValue($event, 0)" 
+      [ngModel]="dateValue" 
+      dateFormat="dd.mm.yy" 
+      showTime="true" 
+      hourFormat="24" 
+      hideOnDateTimeSelect="true" 
+      [showIcon]="true" 
+      [disabled]="disabled"
+      [style]="{ 'width': isBinaryOperator ? 'calc(50% - 1.5rem)' : '100%' }"
+      [inputStyle]="{ 'width': 'calc(100% - 2.2rem)' }"
+      pTooltip="{{ 'search.search-condition.single-value-input' | translate }}" tooltipPosition="top">
+    </p-calendar>
+    <ng-container *ngIf="isBinaryOperator">
+      <span style="width: 3rem; text-align: center; display: inline-block;">{{'search.search-condition.and' | translate}}</span>
+      <p-calendar *ngSwitchCase="'date'" 
+        (ngModelChange)="setDateValue($event, 1)" 
+        [ngModel]="dateValueEnd" 
+        dateFormat="dd.mm.yy" 
+        showTime="true" 
+        hourFormat="24" 
+        hideOnDateTimeSelect="true" 
+        [showIcon]="true" 
+        [disabled]="disabled"
+        [style]="{ 'width': 'calc(50% - 1.5rem)' }"
+        [inputStyle]="{ 'width': 'calc(100% - 2.2rem)' }"
+        pTooltip="{{ 'search.search-condition.single-value-input' | translate }}" tooltipPosition="top">
+      </p-calendar>
+    </ng-container>
+  </ng-container>
+
+  <ng-container *ngSwitchDefault>
+    <input pInputText type="text" placeholder="Wert"
+      [disabled]="disabled"
+      [(ngModel)]="stringValueStart"
+      (input)="setValue($event)"
+      pTooltip="{{ 'search.search-condition.single-value-input' | translate }}" tooltipPosition="top"
+      [ngStyle]="{ 'width': isBinaryOperator ? 'calc(50% - 1.5rem)' : '100%' }">
+    <ng-container *ngIf="isBinaryOperator">
+      <span style="width: 3rem; text-align: center; display: inline-block;">{{'search.search-condition.and' | translate}}</span>
+      <input pInputText type="text" placeholder="Wert"
+        [disabled]="disabled"
+        [(ngModel)]="stringValueEnd"
+        (input)="setValue($event)"
+        pTooltip="{{ 'search.search-condition.single-value-input' | translate }}" tooltipPosition="top"
+        [ngStyle]="{'width':'calc(50% - 1.5rem)'}">
+    </ng-container>
+  </ng-container>
 </td>
 <td style="vertical-align: middle"><button id="remove" type="button" class="btn btn-default btn-sm fa fa-times remove" (click)="remove()" [disabled]="disabled"></button></td>
diff --git a/nucleus/webclient/src/main/webapp/src/app/search/search-condition.component.ts b/nucleus/webclient/src/main/webapp/src/app/search/search-condition.component.ts
index 0fabadb..a97fee6 100644
--- a/nucleus/webclient/src/main/webapp/src/app/search/search-condition.component.ts
+++ b/nucleus/webclient/src/main/webapp/src/app/search/search-condition.component.ts
@@ -13,13 +13,11 @@
  ********************************************************************************/
 
 
-import {Component, Input, Output, EventEmitter, OnChanges, SimpleChanges, ViewChild, AfterViewInit} from '@angular/core';
+import {Component, Input, Output, EventEmitter, OnChanges, SimpleChanges, OnInit } from '@angular/core';
 import {FormGroup} from '@angular/forms';
-import {LocalizationService} from '../localization/localization.service';
 import {Condition, Operator, OperatorUtil} from './filter.service';
 import {Node} from '../navigator/node';
 
-import {PropertyService} from '../core/property.service';
 import {QueryService} from '../tableview/query.service';
 import {AutoComplete} from 'primeng/primeng';
 
@@ -27,13 +25,17 @@
 
 import {TranslateService} from '@ngx-translate/core';
 import { DatePipe } from '@angular/common';
+import { NavigatorService } from '@navigator/navigator.service';
+
 
 @Component({
   selector: '[search-condition]',
   templateUrl: 'search-condition.component.html',
   styleUrls: ['search-condition.component.css'],
 })
-export class SearchConditionComponent implements OnChanges, AfterViewInit {
+export class SearchConditionComponent implements OnChanges, OnInit {
+
+  private readonly multiValueOperators = [Operator.LIKE, Operator.EQUALS, Operator.NOT_EQUALS];
 
   @Input() env: string;
   @Input() condition: Condition;
@@ -45,34 +47,27 @@
   suggestions: string[];
   displayedSuggestions: string[] = [];
   lastQuery: string;
+
+  // date values for two input fields
   dateValue: Date;
+  dateValueEnd: Date;
 
-  @ViewChild(AutoComplete) primeAutoCompleteComponent: AutoComplete;
+  // string values for two input fields
+  stringValueStart: string = '';
+  stringValueEnd: string = '';
 
-  constructor(private localservice: LocalizationService,
-              private prop: PropertyService,
-              private queryService: QueryService,
+  public isBinaryOperator = false;
+  public isHandlingMultipleValues = false;
+
+  constructor(private queryService: QueryService,
               private notificationService: MDMNotificationService,
               private translateService: TranslateService,
-              private datePipe: DatePipe) { }
+              private datePipe: DatePipe,
+              private navigatorService: NavigatorService) { }
 
-  ngAfterViewInit() {
-    if (this.primeAutoCompleteComponent) {
-      /* Workaround for missing onBlur handler in primeng version 2.x.
-       * We overwrite the existing implementation and additional call our own event handler.
-       * In later versions this feature was added https://github.com/primefaces/primeng/issues/2256
-       * and this workaround should be removed when using primeng version 4 or later
-       */
-      this.primeAutoCompleteComponent.onBlur = function() {
-        this.primeAutoCompleteComponent.focus = false;
-        this.primeAutoCompleteComponent.onModelTouched();
-        this.onAutocompleteBlur();
-      }.bind(this);
-    }
-  }
-
-  onAutocompleteBlur() {
-    this.onEnter(new Event('blur'));
+  ngOnInit() {
+    // set default operator if empty and evaluates operator properties like multiple input, binary eg.
+    this.setOperator(this.condition.operator === undefined ? Operator.EQUALS : this.condition.operator);
   }
 
   ngOnChanges(changes: SimpleChanges) {
@@ -80,27 +75,46 @@
       this.setAutoCompleteValues();
     }
 
-    if (this.condition.valueType === 'date') {
-      console.log(this.condition.value[0]);
-      if (this.condition.value === undefined || this.condition.value[0] === undefined) {
-        this.dateValue = new Date();
-        (<Date> this.dateValue).setHours(0, 0, 0, 0);
-      } else {
-        this.dateValue = new Date(this.condition.value[0]);
+    if (changes['condition']) {
+      // set initial date to today, otherwise primeNg calender breaks on undefined input.
+      if (this.condition.valueType === 'date') {
+        if (this.condition.value === undefined || this.condition.value[0] === undefined) {
+          this.dateValue = new Date();
+          this.dateValue.setHours(0, 0, 0, 0);
+        } else {
+          this.dateValue = new Date(this.condition.value[0]);
+        }
+        this.setDateValue(this.dateValue, 0);
+        if (this.condition.value === undefined || this.condition.value[1] === undefined) {
+          this.dateValueEnd = new Date();
+          this.dateValueEnd.setHours(0, 0, 0, 0);
+          this.dateValueEnd.setDate(this.dateValueEnd.getDate() + 1)
+        } else {
+          this.dateValueEnd = new Date(this.condition.value[1]);
+        }
+        this.setDateValue(this.dateValue, 1);
       }
     }
   }
 
-  onEnter(e: Event) {
-    let hl = this.primeAutoCompleteComponent.highlightOption;
-    if (hl == undefined && this.lastQuery !== '' || hl != undefined && this.displayedSuggestions.find(s => s === hl) == undefined) {
-      if (this.primeAutoCompleteComponent.value[this.primeAutoCompleteComponent.value.length - 1] === hl) {
-        this.primeAutoCompleteComponent.value.pop();
+  onAutocompleteBlur(event: Event, htmlElement: AutoComplete) {
+    this.onEnter(undefined, htmlElement);
+  }
+
+  onEnter(e: Event, autoComplete: AutoComplete) {
+    if(this.isHandlingMultipleValues) {
+      let hl = autoComplete.highlightOption;
+      if (hl == undefined && this.lastQuery != undefined  && this.lastQuery !== '' || hl != undefined && this.displayedSuggestions.find(s => s === hl) == undefined) {
+        if (autoComplete.value[autoComplete.value.length - 1] === hl) {
+          autoComplete.value.pop();
+        }
+        autoComplete.selectItem(this.lastQuery);
+        this.lastQuery = '';
       }
-      this.primeAutoCompleteComponent.selectItem(this.lastQuery);
-      this.lastQuery = '';
+      autoComplete.highlightOption = undefined;
+    } else {
+      this.condition.value = [this.stringValueStart, this.stringValueEnd];
     }
-    this.primeAutoCompleteComponent.highlightOption = undefined;
     this.displayedSuggestions = [];
   }
 
@@ -133,6 +147,20 @@
 
   setOperator(operator: Operator) {
     this.condition.operator = operator;
+    this.isBinaryOperator = operator === Operator.BETWEEN;
+    this.isHandlingMultipleValues = this.multiValueOperators.some(op => op === operator);
+    this.adjustInput();
+  }
+
+  private adjustInput() {
+    if (this.condition.valueType === 'string') {
+      if (!this.isHandlingMultipleValues) {
+        this.stringValueStart = this.condition.value[0];
+        this.stringValueEnd = this.condition.value[1];
+      } else {
+        this.condition.value = this.condition.value.filter(v => v != undefined);
+      }
+    }
   }
 
   setValue(value: string) {
@@ -143,9 +171,11 @@
     this.condition.value = value;
   }
 
-  setDateValue(value: Date) {
-    console.log(value);
-    this.condition.value = [this.datePipe.transform(value, 'yyyy-MM-dd' + 'T' + 'HH:mm:ss')];
+  setDateValue(value: Date, index: number) {
+    if (this.condition.value == undefined) {
+      this.condition.value = [];
+    }
+    this.condition.value[index] = this.datePipe.transform(value, 'yyyy-MM-dd' + 'T' + 'HH:mm:ss');
   }
 
   remove() {
diff --git a/nucleus/webclient/src/main/webapp/src/app/search/search.service.spec.ts b/nucleus/webclient/src/main/webapp/src/app/search/search.service.spec.ts
index 77c1c18..d43f278 100644
--- a/nucleus/webclient/src/main/webapp/src/app/search/search.service.spec.ts
+++ b/nucleus/webclient/src/main/webapp/src/app/search/search.service.spec.ts
@@ -145,7 +145,7 @@
       let filter = service.convertEnv('env1', [cond1, cond2, cond3], attributes, 'test');
 
       expect(filter.sourceName).toEqual('env1');
-      expect(filter.filter).toEqual('Test.Name ci_lk "PBN*" and Vehicle.Number eq 12 and Vehicle.Created eq "2017-07-17T12:13:14"');
+      expect(filter.filter).toEqual('( Test.Name ci_lk "PBN*" ) and ( Vehicle.Number eq 12 ) and ( Vehicle.Created eq "2017-07-17T12:13:14" )');
       expect(filter.searchString).toEqual('test');
 
     })));
diff --git a/nucleus/webclient/src/main/webapp/src/app/search/search.service.ts b/nucleus/webclient/src/main/webapp/src/app/search/search.service.ts
index 1a461e0..e10499e 100644
--- a/nucleus/webclient/src/main/webapp/src/app/search/search.service.ts
+++ b/nucleus/webclient/src/main/webapp/src/app/search/search.service.ts
@@ -25,7 +25,7 @@
 import { Preference, PreferenceService, Scope } from '../core/preference.service';
 import {HttpErrorHandler} from '../core/http-error-handler';
 
-import {SearchFilter, Condition, OperatorUtil} from './filter.service';
+import {SearchFilter, Condition, OperatorUtil, Operator} from './filter.service';
 import {Query, Filter} from '../tableview/query.service';
 import {View} from '../tableview/tableview.service';
 
@@ -95,7 +95,7 @@
   }
 
   getConditions(sourceName: string) {
-    return this.map[sourceName].sort((c1, c2) => c1.sortIndex - c2.sortIndex) || [];
+    return (this.map[sourceName] || []).sort((c1, c2) => c1.sortIndex - c2.sortIndex);
   }
 
   getSourceName(condition: Condition) {
@@ -253,9 +253,22 @@
   convertEnv(env: string, conditions: Condition[], attrs: SearchAttribute[], fullTextQuery: string): Filter {
 
     let filterString = conditions
-      .map(c => c.value.map(value => c.type + '.' + c.attribute + ' ' + this.adjustOperator(OperatorUtil.toFilterString(c.operator),
-         this.getValueType(c, attrs)) + ' ' + this.quoteValue(value, this.getValueType(c, attrs))).join(' or '))
+      .map(c => {
+        if (c.operator === Operator.BETWEEN) {
+          if (c.value[0] != undefined && c.value[0].length > 0 && c.value[1] != undefined && c.value[1].length > 0) {
+            console.log(c.value)
+            return c.type + '.' + c.attribute + ' ' + OperatorUtil.toFilterString(c.operator)
+            + ' (' +this.quoteValue(c.value[0], this.getValueType(c, attrs)) + ', ' + this.quoteValue(c.value[1], this.getValueType(c, attrs)) +')'
+          } else {
+            return '';
+          }
+        } else {
+          return c.value.map(value => c.type + '.' + c.attribute + ' ' + this.adjustOperator(OperatorUtil.toFilterString(c.operator),
+            this.getValueType(c, attrs)) + ' ' + this.quoteValue(value, this.getValueType(c, attrs))).join(c.operator === Operator.NOT_EQUALS ? ' and ' : ' or ')
+        }
+      })
       .filter(c => c.length > 0)
+      .map(c => '( ' + c + ' )')
       .join(' and ');
 
     return new Filter(env, filterString, fullTextQuery);
diff --git a/nucleus/webclient/src/main/webapp/src/app/tableview/editview.component.html b/nucleus/webclient/src/main/webapp/src/app/tableview/editview.component.html
index 76148ed..4e784bd 100644
--- a/nucleus/webclient/src/main/webapp/src/app/tableview/editview.component.html
+++ b/nucleus/webclient/src/main/webapp/src/app/tableview/editview.component.html
@@ -61,7 +61,8 @@
                       typeaheadOptionsLimit="15"
                       placeholder="{{ 'tableview.editview.enter-search-text' | translate }}"
                       class="form-control"
-                      style="margin-bottom: 15px;">
+                      style="margin-bottom: 15px;"
+                      [title]="'tableview.editview.tooltip-no-wildcards' | translate">
               <searchattribute-tree [searchAttributes]="searchAttributes" [environments]="environments"></searchattribute-tree>
             </div>
             <div class="col-md-8" style="min-height: 50vh; max-height: 72vh; overflow-y:auto;">
diff --git a/nucleus/webclient/src/main/webapp/src/assets/i18n/de.json b/nucleus/webclient/src/main/webapp/src/assets/i18n/de.json
index 0917129..26e24c8 100644
--- a/nucleus/webclient/src/main/webapp/src/assets/i18n/de.json
+++ b/nucleus/webclient/src/main/webapp/src/assets/i18n/de.json
@@ -312,6 +312,7 @@
 	},
 	"search": {
 		"edit-searchfields": {
+			"tooltip-no-wildcards": "Im Suchtext sind keine Wildcards erlaubt.",
 			"btn-apply-changes": "Änderungen übernehmen",
 			"enter-search-text": "Suchtext eingeben",
 			"err-cannot-update-selected-node": "Ausgewählter Knoten kann nicht aktualisiert werden.",
@@ -362,15 +363,21 @@
 			"tooltip-edit-search-filter": "Suchfilter bearbeiten",
 			"tooltip-enable-advanced-search": "Erweiterte Suche aktivieren",
 			"tooltip-global-search-attributes": "Globale Suchattribute sind Attribute, die in allen ausgewählten Datenquellen vorhanden sind. Global Suchattribute werden nur einmal dargestellt und die definierte Bedingung wird in allen Datenquellen angewendet.",
+			"tooltip-link-search-attributes-to-navigator": "Suchattribute an den Navigator koppeln.",
 			"tooltip-no-name-set": "Name nicht gesetzt!",
 			"tooltip-reset-search-conditions": "Suchkriterien zurücksetzen",
 			"tooltip-save-search-filter": "Suchfilter speichern",
-			"tooltip-search-text": "Mögliche Operatoren:\n+ steht für einen UND Operator\n| steht für einen ODER Operator\n- negiert einen einzelnen Term\n\" fasst einen Menge von Termen zu einer Phrase zusammen\n* am Ende eines Suchterms beschreibt eine Präfixsuche\n( und ) beschreibt die Operatorpräferenz\n~N nach einem Wort beschreibt den Editierabstand (Unschärfe)\n~N nach einer Phrase beschreibt den Wortabstand\nWenn nach einem der Spezialsymbole gesucht werden soll, müssen diese mit \\ escaped werden.",
-			"tooltip-select-sources": "Quellen auswählen"
+			"tooltip-search-text": "Mögliche Operatoren:\n+ steht für einen UND Operator\n| steht für einen ODER Operator\n- negiert einen einzelnen Term\n\" fasst einen Menge von Termen zu einer Phrase zusammen\n* am Ende eines Suchterms beschreibt eine Präfixsuche\n( und ) beschreibt die Operatorpräferenz\n~N nach einem Wort beschreibt den Editierabstand (Unschärfe)\n~N nach einer Phrase beschreibt den Wortabstand\n\nWenn nach einem der Spezialsymbole gesucht werden soll, müssen diese mit \\ escaped werden.",
+			"tooltip-select-sources": "Quellen auswählen",
+			"tooltip-unlink-search-attributes-from-navigator": "Suchattributen vom Navigator entkoppeln."
 		},
 		"search-condition": {
+			"and": "und",
+			"condition-is-linked": "Dieses Suchbedingung ist an den Navigator gekoppelt. Die Kopplung kann über den entsprechenden Knopf (gleiches Symbol) in der Kopfzeile 'Erweiterte Suche' gelöst werden.",
 			"err-cannot-initialize-autocompletion": "Autotvervollständigung kann nicht initialisiert werden.",
-			"tooltip-select-search-operator": "Suchoperator auswählen"
+			"tooltip-select-search-operator": "Suchoperator auswählen",
+			"multi-value-input": "Bitte Werte für Suchattribute eingeben. Die Eingabe von mehreren Werten ist möglich. Für die Operatoren 'like' und '=' werden diese logisch mit einem 'ODER' verknüpft. Für den Operator '!=' werden diese logisch mit einem 'UND' verknüpft.\n\nMögliche Wildcards sind '?' für genau ein beliebiges Zeichen und '*' für eine beliebigen Zeichenkette.",
+			"single-value-input": "Bitte einen Wert für Suchattribute eingeben. Die Eingabe von mehreren Werten ist nicht möglich.\n\nMögliche Wildcards sind '?' für genau ein beliebiges Zeichen und '*' für eine beliebigen Zeichenkette."
 		},
 		"search-datepicker": {
 			"placeholder-dateformat": "TT.MM.JJJJ HH:MM",
@@ -391,7 +398,8 @@
 			"err-cannot-select-node": "Knoten kann nicht ausgewählt werden.",
 			"lbl-selected-attributes": "Ausgewählte Attribute",
 			"no-attributes-selected": "Keine Attribute ausgewählt.",
-			"title-view-editor": "Ansichtseditor"
+			"title-view-editor": "Ansichtseditor",
+			"tooltip-no-wildcards": "Im Suchtext sind keine Wildcards erlaubt."
 		},
 		"tableview": {
 			"err-cannot-calculate-node": "Knoten konnte nicht berechnet werden.",
@@ -437,12 +445,15 @@
       "overall": "Gesamt",
       "step-size": "Schrittweite",
       "preview-values": "Vorschauwerte",
-      "apply": "Übernehmen"
+      "apply": "Übernehmen",
+      "reset": "Zurücksetzen"
     },
     "chart-viewer-nav-card": {
       "load-missing-channels": "{{limit}} Kanäle nachladen ({{offset}}/{{total}})"
     },
     "xy-chart-data-selection-panel": {
+			"filter": "Filter",
+			"tooltip-regexp-filter": "Reguläre Ausdrücke als Filtereingabe möglich.",
       "select-channel-placeholder": "Kanal wählen",
       "no-x-channel-dialog-header": "Information",
       "no-x-channel-dialog-message": "Die aktuelle Kanalgruppe hat keine Kanäle die als X-Achse bzw. XY-Achse definiert sind. Wenn Sie auch als Y-Achse definierte Kanäle zulassen möchten, deaktivieren Sie bitte den Filter in der Werkzeugleiste."
diff --git a/nucleus/webclient/src/main/webapp/src/assets/i18n/en.json b/nucleus/webclient/src/main/webapp/src/assets/i18n/en.json
index 7f59191..dd42758 100644
--- a/nucleus/webclient/src/main/webapp/src/assets/i18n/en.json
+++ b/nucleus/webclient/src/main/webapp/src/assets/i18n/en.json
@@ -312,6 +312,7 @@
 	},
 	"search": {
 		"edit-searchfields": {
+			"tooltip-no-wildcards": "No wildcards allowed in search text.",
 			"btn-apply-changes": "Apply changes",
 			"enter-search-text": "Enter search text",
 			"err-cannot-update-selected-node": "Cannot update the selected node.",
@@ -362,15 +363,21 @@
 			"tooltip-edit-search-filter": "Edit search filter",
 			"tooltip-enable-advanced-search": "Enable advanced search",
 			"tooltip-global-search-attributes": "Global search attributes are attributes present in all data sources selected. Global search attributes are displayed only once and the condition defined in them is applied to all data sources.",
+			"tooltip-link-search-attributes-to-navigator": "Link search attribtues to the navigator.",
 			"tooltip-no-name-set": "No name set!",
 			"tooltip-reset-search-conditions": "Reset search conditions",
 			"tooltip-save-search-filter": "Save search filter",
-			"tooltip-search-text": "Operators available:\n+ represents an AND operator\n| represents an OR operator\n- negates a single term\n\" combines a set of terms into a phrase\n* at the end of a search term describes a prefix search\n( and ) describe the operator preference\n~N after a word describes the edit distance (fuzziness)\n~N after a phrase describes the word distance\nIf one of the special symbols is to be included in a search, they must be escaped with \\.",
-			"tooltip-select-sources": "Select sources"
+			"tooltip-search-text": "Operators available:\n+ represents an AND operator\n| represents an OR operator\n- negates a single term\n\" combines a set of terms into a phrase\n* at the end of a search term describes a prefix search\n( and ) describe the operator preference\n~N after a word describes the edit distance (fuzziness)\n~N after a phrase describes the word distance\n\nIf one of the special symbols is to be included in a search, they must be escaped with \\.",
+			"tooltip-select-sources": "Select sources",
+			"tooltip-unlink-search-attributes-from-navigator": "Unlink search attribtues from the navigator."
 		},
 		"search-condition": {
+			"and": "and",
+			"condition-is-linked": "This search condition is linked to the navigator. It can be unliked via the related button (same symbol) in the 'Advanced Search' header.",
 			"err-cannot-initialize-autocompletion": "Cannot initialize autocompletion.",
-			"tooltip-select-search-operator": "Select search operator"
+			"tooltip-select-search-operator": "Select search operator",
+			"multi-value-input": "Please enter search values. Multiple input values are allowed. For operators 'like' and '=' the values are connected by a logical 'OR'. However, for operator '!=' the values are connected by a logical 'AND'.\n\nPossible wildcards are '?' for one matching character and '*' for a sequence of matching characters.",
+			"single-value-input": "Please enter a search value. Multiple input values are not allowed.\n\nPossible wildcards are '?' for one matching character and '*' for a sequence of matching characters."
 		},
 		"search-datepicker": {
 			"placeholder-dateformat": "DD.MM.YYYY HH:MM",
@@ -391,7 +398,8 @@
 			"err-cannot-select-node": "Cannot select node.",
 			"lbl-selected-attributes": "Selected attributes",
 			"no-attributes-selected": "No attributes selected.",
-			"title-view-editor": "View editor"
+			"title-view-editor": "View editor",
+			"tooltip-no-wildcards": "No wildcards allowed in search text."
 		},
 		"tableview": {
 			"err-cannot-calculate-node": "Cannot calculate node.",
@@ -437,12 +445,15 @@
       "overall": "overall",
       "step-size": "step size",
       "preview-values": "preview values",
-      "apply": "Apply"
+      "apply": "Apply",
+      "reset": "Reset"
     },
     "chart-viewer-nav-card": {
       "load-missing-channels": "Load {{limit}} channels ({{offset}}/{{total}})"
     },
     "xy-chart-data-selection-panel": {
+			"filter": "Filter",
+			"tooltip-regexp-filter": "Regular expressions are enabled for filtering.",
       "select-channel-placeholder": "Select Channel",
       "no-x-channel-dialog-header": "Information",
       "no-x-channel-dialog-message": "The current ChannelGroup has no Channels defined as X-axis or XY-axis. If you want to also allow Channels marked as Y-axis, toggle the corresponding filter in the toolbar."