diff --git a/check/instance/pom.xml b/check/instance/pom.xml
new file mode 100644
index 0000000..cc337f2
--- /dev/null
+++ b/check/instance/pom.xml
@@ -0,0 +1,30 @@
+
+
+
+ 4.0.0
+
+ org.eclipse.daanse
+ org.eclipse.daanse.olap.check
+ ${revision}
+
+ org.eclipse.daanse.olap.check.instance
+ Daanse Model Check Instance
+ Daanse Model Check Instance
+ pom
+
+ tutorial
+ serializer
+
+
diff --git a/check/instance/serializer/.gitignore b/check/instance/serializer/.gitignore
new file mode 100644
index 0000000..ebfe9f3
--- /dev/null
+++ b/check/instance/serializer/.gitignore
@@ -0,0 +1 @@
+/daansetutorials/
diff --git a/check/instance/serializer/pom.xml b/check/instance/serializer/pom.xml
new file mode 100644
index 0000000..37762d7
--- /dev/null
+++ b/check/instance/serializer/pom.xml
@@ -0,0 +1,61 @@
+
+
+
+ 4.0.0
+
+ org.eclipse.daanse
+ org.eclipse.daanse.olap.check.instance
+ ${revision}
+
+ org.eclipse.daanse.olap.check.instance.serializer
+
+
+ 6.2.0
+ 2.30.0
+ 2.36.0
+ 2.37.0
+
+
+
+
+ org.eclipse.daanse
+ org.eclipse.daanse.olap.check.model.emf
+ ${project.version}
+ compile
+
+
+ org.geckoprojects.emf.utils
+ org.gecko.emf.json
+ 1.5.1
+ compile
+
+
+
+ org.eclipse.emf
+ org.eclipse.emf.common
+ ${emf.common.version}
+ test
+
+
+
+
+ org.eclipse.daanse
+ org.eclipse.daanse.olap.check.instance.tutorial.access.cataloggrand
+ ${project.version}
+
+
+
+
+
diff --git a/check/instance/serializer/src/test/java/org/eclipse/daanse/olap/check/instance/serializer/ResourceSetWriteReadTest.java b/check/instance/serializer/src/test/java/org/eclipse/daanse/olap/check/instance/serializer/ResourceSetWriteReadTest.java
new file mode 100644
index 0000000..0cb3cc9
--- /dev/null
+++ b/check/instance/serializer/src/test/java/org/eclipse/daanse/olap/check/instance/serializer/ResourceSetWriteReadTest.java
@@ -0,0 +1,295 @@
+/*
+* Copyright (c) 2025 Contributors to the Eclipse Foundation.
+*
+* This program and the accompanying materials are made
+* available under the terms of the Eclipse Public License 2.0
+* which is available at https://www.eclipse.org/legal/epl-2.0/
+*
+* SPDX-License-Identifier: EPL-2.0
+*
+* Contributors:
+* SmartCity Jena - initial
+* Stefan Bischof (bipolis.org) - initial
+*/
+package org.eclipse.daanse.olap.check.instance.serializer;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.sql.SQLException;
+import java.util.Comparator;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import org.eclipse.daanse.olap.check.model.check.OlapCheckModel;
+import org.eclipse.daanse.olap.check.model.check.OlapCheckPackage;
+import org.eclipse.daanse.olap.check.model.provider.CatalogCheckSupplier;
+import org.eclipse.emf.common.util.TreeIterator;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EStructuralFeature;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.emf.ecore.xmi.XMLResource;
+import org.gecko.emf.osgi.annotation.require.RequireEMF;
+import org.gecko.emf.osgi.constants.EMFNamespaces;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceReference;
+import org.osgi.test.common.annotation.InjectBundleContext;
+import org.osgi.test.common.annotation.InjectService;
+import org.osgi.test.common.service.ServiceAware;
+import org.osgi.test.junit5.cm.ConfigurationExtension;
+import org.osgi.test.junit5.context.BundleContextExtension;
+import org.osgi.test.junit5.service.ServiceExtension;
+
+@ExtendWith(BundleContextExtension.class)
+@ExtendWith(ServiceExtension.class)
+@ExtendWith(ConfigurationExtension.class)
+@RequireEMF
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+public class ResourceSetWriteReadTest {
+
+ static int i = 0;
+ static Path tempDir;
+
+ @BeforeAll
+ public static void beforeAll() throws IOException {
+ tempDir = Path.of("./daansetutorials");
+ deleteDirectory(tempDir);
+ tempDir = Files.createDirectories(tempDir);
+ }
+
+ @Test
+ @Order(1)
+ public void writePopulation(@InjectBundleContext BundleContext bc,
+ @InjectService(cardinality = 1, filter = "(" + EMFNamespaces.EMF_MODEL_NAME + "="
+ + "model" + ")") ResourceSet resourceSet,
+ @InjectService ServiceAware mappingSuppiersSA)
+ throws SQLException, InterruptedException, IOException {
+
+ try {
+
+ List> srs = mappingSuppiersSA.getServiceReferences();
+
+ // Create combined ZIP directory structure
+ Path zipDir = Files.createDirectories(tempDir.resolve("cubeserver/tutorial/zip"));
+ ZipOutputStream combinedZos = new ZipOutputStream(new FileOutputStream(zipDir.resolve("all-tutorials.zip").toFile()));
+
+ srs.sort((o1, o2) -> {
+ Object s1 = o1.getProperty("number");
+ Object s2 = o2.getProperty("number");
+
+ String ss1 = s1 == null ? "9999.9.9" : s1.toString();
+ String ss2 = s2 == null ? "9999.9.9" : s2.toString();
+ return ss1.compareToIgnoreCase(ss2);
+ });
+ for (ServiceReference sr : srs) {
+
+ try {
+ CatalogCheckSupplier catalogMappingSupplier = mappingSuppiersSA.getService(sr);
+
+ serializeCatalog(resourceSet, catalogMappingSupplier, sr.getProperties(), combinedZos);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ // Close combined ZIP
+ combinedZos.close();
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ private void serializeCatalog(ResourceSet resourceSet,
+ CatalogCheckSupplier catalogMappingSupplier, Dictionary dictionary, ZipOutputStream combinedZos) throws IOException {
+
+ String name = catalogMappingSupplier.getClass().getPackageName();
+ name = name.substring(46);
+
+ Path zipDir = Files.createDirectories(tempDir.resolve("cubeserver/tutorial/zip"));
+ ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipDir.resolve(name + ".zip").toFile()));
+
+ Bundle b = FrameworkUtil.getBundle(catalogMappingSupplier.getClass());
+
+ OlapCheckModel cm = catalogMappingSupplier.get();
+
+
+
+ String grp = (String) dictionary.get("group");
+ grp = grp == null ? "Unstrutured" : grp;
+
+ String catName = cm.getName();
+
+ catName = catName.replaceFirst(grp + " - ", "").replaceFirst("Daanse Tutorial - ", "");
+
+
+ String kind = (String) dictionary.get("kind");
+ kind = kind == null ? "other" : kind;
+
+ String nr = (String) dictionary.get("number");
+ nr = nr == null ? "z" + i : nr;
+
+ OlapCheckModel c = cm;
+
+ URI uriCatalog = URI.createFileURI("catalog.xmi");
+ Resource resourceCatalog = resourceSet.createResource(uriCatalog);
+
+ Set set = new HashSet<>();
+
+ set = allRef(set, c);
+
+ // sort
+
+ List sortedList = set.stream().sorted(comparator).toList();
+
+
+ for (EObject eObject : sortedList) {
+
+ if (eObject.eContainer() == null) {
+ resourceCatalog.getContents().add(eObject);
+ }
+
+ }
+ Map
+ */
+public interface CheckExecutor {
+
+ /**
+ * Execute checks defined in the model against the given connection.
+ *
+ * @param model the check model containing check definitions
+ * @param connection the OLAP connection to check against
+ * @return the execution result containing all check results
+ */
+ CheckExecutionResult execute(OlapCheckModel model, Connection connection);
+
+ /**
+ * Load a check model from URI and execute against the given connection.
+ *
+ * @param modelUri URI to the XMI file containing check definitions
+ * @param connection the OLAP connection to check against
+ * @return the execution result containing all check results
+ */
+ CheckExecutionResult execute(URI modelUri, Connection connection);
+}
diff --git a/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/api/CheckModelLoader.java b/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/api/CheckModelLoader.java
new file mode 100644
index 0000000..b173088
--- /dev/null
+++ b/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/api/CheckModelLoader.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2024 Contributors to the Eclipse Foundation.
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.daanse.olap.check.runtime.api;
+
+import java.io.IOException;
+
+import org.eclipse.daanse.olap.check.model.check.OlapCheckModel;
+import org.eclipse.emf.common.util.URI;
+
+/**
+ * Loader for OLAP check models from XMI or JSON files.
+ */
+public interface CheckModelLoader {
+
+ /**
+ * Load a check model from an XMI file.
+ *
+ * @param uri the URI to the XMI file
+ * @return the loaded check model
+ * @throws IOException if the file cannot be read or parsed
+ */
+ OlapCheckModel loadFromXMI(URI uri) throws IOException;
+
+ /**
+ * Load a check model from a JSON file.
+ *
+ * @param uri the URI to the JSON file
+ * @return the loaded check model
+ * @throws IOException if the file cannot be read or parsed
+ */
+ OlapCheckModel loadFromJSON(URI uri) throws IOException;
+}
diff --git a/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/CheckExecutorImpl.java b/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/CheckExecutorImpl.java
new file mode 100644
index 0000000..acc420a
--- /dev/null
+++ b/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/CheckExecutorImpl.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2024 Contributors to the Eclipse Foundation.
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.daanse.olap.check.runtime.impl;
+
+import java.io.IOException;
+import java.util.Date;
+import java.util.List;
+
+import org.eclipse.daanse.olap.api.CatalogReader;
+import org.eclipse.daanse.olap.api.connection.Connection;
+import org.eclipse.daanse.olap.api.element.Cube;
+import org.eclipse.daanse.olap.api.element.Dimension;
+import org.eclipse.daanse.olap.api.element.Hierarchy;
+import org.eclipse.daanse.olap.api.element.Level;
+import org.eclipse.daanse.olap.api.element.Member;
+import org.eclipse.daanse.olap.check.model.check.CatalogCheck;
+import org.eclipse.daanse.olap.check.model.check.CatalogCheckResult;
+import org.eclipse.daanse.olap.check.model.check.CheckExecutionResult;
+import org.eclipse.daanse.olap.check.model.check.CheckFailure;
+import org.eclipse.daanse.olap.check.model.check.CheckResult;
+import org.eclipse.daanse.olap.check.model.check.CheckStatus;
+import org.eclipse.daanse.olap.check.model.check.CubeCheck;
+import org.eclipse.daanse.olap.check.model.check.CubeCheckResult;
+import org.eclipse.daanse.olap.check.model.check.DimensionCheck;
+import org.eclipse.daanse.olap.check.model.check.DimensionCheckResult;
+import org.eclipse.daanse.olap.check.model.check.HierarchyCheck;
+import org.eclipse.daanse.olap.check.model.check.HierarchyCheckResult;
+import org.eclipse.daanse.olap.check.model.check.LevelCheck;
+import org.eclipse.daanse.olap.check.model.check.LevelCheckResult;
+import org.eclipse.daanse.olap.check.model.check.MemberCheck;
+import org.eclipse.daanse.olap.check.model.check.MemberCheckResult;
+import org.eclipse.daanse.olap.check.model.check.OlapCheck;
+import org.eclipse.daanse.olap.check.model.check.OlapCheckFactory;
+import org.eclipse.daanse.olap.check.model.check.OlapCheckModel;
+import org.eclipse.daanse.olap.check.runtime.api.CheckExecutor;
+import org.eclipse.daanse.olap.check.runtime.api.CheckModelLoader;
+import org.eclipse.daanse.olap.check.runtime.impl.executors.CatalogCheckExecutor;
+import org.eclipse.emf.common.util.URI;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+
+/**
+ * Implementation of CheckExecutor that executes OLAP check models.
+ */
+@Component(service = CheckExecutor.class)
+public class CheckExecutorImpl implements CheckExecutor {
+
+ private final OlapCheckFactory factory = OlapCheckFactory.eINSTANCE;
+
+ @Reference
+ private CheckModelLoader modelLoader;
+
+ @Override
+ public CheckExecutionResult execute(OlapCheckModel model, Connection connection) {
+ CheckExecutionResult result = factory.createCheckExecutionResult();
+ result.setName(model.getName());
+ result.setDescription(model.getDescription());
+ result.setStartTime(new Date());
+ result.setSourceModel(model);
+
+ CatalogReader catalogReader = connection.getCatalogReader();
+ int successCount = 0;
+ int failureCount = 0;
+ int skippedCount = 0;
+
+ // Execute all catalog checks
+ for (CatalogCheck catalogCheck : model.getCatalogChecks()) {
+ if (!catalogCheck.isEnabled()) {
+ skippedCount++;
+ continue;
+ }
+
+ CatalogCheckExecutor executor = new CatalogCheckExecutor(
+ catalogCheck, catalogReader, connection, factory
+ );
+ CheckResult checkResult = executor.execute();
+ result.getCheckResults().add(checkResult);
+
+ // Count results
+ if (checkResult.getStatus() == CheckStatus.SUCCESS) {
+ successCount += countSuccesses(checkResult);
+ } else if (checkResult.getStatus() == CheckStatus.FAILURE) {
+ failureCount += countFailures(checkResult);
+ } else {
+ skippedCount++;
+ }
+ }
+
+ result.setEndTime(new Date());
+ result.setTotalExecutionTimeMs(result.getEndTime().getTime() - result.getStartTime().getTime());
+ result.setSuccessCount(successCount);
+ result.setFailureCount(failureCount);
+ result.setSkippedCount(skippedCount);
+ result.setSuccess(failureCount == 0);
+
+ return result;
+ }
+
+ @Override
+ public CheckExecutionResult execute(URI modelUri, Connection connection) {
+ try {
+ OlapCheckModel model = modelLoader.loadFromXMI(modelUri);
+ return execute(model, connection);
+ } catch (IOException e) {
+ // Return a failure result
+ CheckExecutionResult result = factory.createCheckExecutionResult();
+ result.setName("LoadFailure");
+ result.setStartTime(new Date());
+ result.setEndTime(new Date());
+ result.setSuccess(false);
+ result.setFailureCount(1);
+
+ CheckFailure failure = factory.createCheckFailure();
+ failure.setCheckName("ModelLoad");
+ failure.setStatus(CheckStatus.FAILURE);
+ failure.setMessage("Failed to load model from: " + modelUri);
+ failure.setException(e.getMessage());
+ result.getCheckResults().add(failure);
+
+ return result;
+ }
+ }
+
+ private int countSuccesses(CheckResult result) {
+ int count = result.getStatus() == CheckStatus.SUCCESS ? 1 : 0;
+
+ if (result instanceof CatalogCheckResult catalogResult) {
+ for (CubeCheckResult cubeResult : catalogResult.getCubeResults()) {
+ count += countSuccesses(cubeResult);
+ }
+ } else if (result instanceof CubeCheckResult cubeResult) {
+ for (DimensionCheckResult dimResult : cubeResult.getDimensionResults()) {
+ count += countSuccesses(dimResult);
+ }
+ } else if (result instanceof DimensionCheckResult dimResult) {
+ for (HierarchyCheckResult hierResult : dimResult.getHierarchyResults()) {
+ count += countSuccesses(hierResult);
+ }
+ } else if (result instanceof HierarchyCheckResult hierResult) {
+ for (LevelCheckResult levelResult : hierResult.getLevelResults()) {
+ count += countSuccesses(levelResult);
+ }
+ } else if (result instanceof LevelCheckResult levelResult) {
+ for (MemberCheckResult memberResult : levelResult.getMemberResults()) {
+ count += countSuccesses(memberResult);
+ }
+ }
+
+ return count;
+ }
+
+ private int countFailures(CheckResult result) {
+ int count = result.getStatus() == CheckStatus.FAILURE ? 1 : 0;
+
+ if (result instanceof CatalogCheckResult catalogResult) {
+ for (CubeCheckResult cubeResult : catalogResult.getCubeResults()) {
+ count += countFailures(cubeResult);
+ }
+ } else if (result instanceof CubeCheckResult cubeResult) {
+ for (DimensionCheckResult dimResult : cubeResult.getDimensionResults()) {
+ count += countFailures(dimResult);
+ }
+ } else if (result instanceof DimensionCheckResult dimResult) {
+ for (HierarchyCheckResult hierResult : dimResult.getHierarchyResults()) {
+ count += countFailures(hierResult);
+ }
+ } else if (result instanceof HierarchyCheckResult hierResult) {
+ for (LevelCheckResult levelResult : hierResult.getLevelResults()) {
+ count += countFailures(levelResult);
+ }
+ } else if (result instanceof LevelCheckResult levelResult) {
+ for (MemberCheckResult memberResult : levelResult.getMemberResults()) {
+ count += countFailures(memberResult);
+ }
+ }
+
+ return count;
+ }
+}
diff --git a/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/CheckModelLoaderImpl.java b/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/CheckModelLoaderImpl.java
new file mode 100644
index 0000000..883dcab
--- /dev/null
+++ b/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/CheckModelLoaderImpl.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2024 Contributors to the Eclipse Foundation.
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.daanse.olap.check.runtime.impl;
+
+import java.io.IOException;
+
+import org.eclipse.daanse.olap.check.model.check.OlapCheckModel;
+import org.eclipse.daanse.olap.check.model.check.OlapCheckPackage;
+import org.eclipse.daanse.olap.check.runtime.api.CheckModelLoader;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
+import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;
+import org.osgi.service.component.annotations.Component;
+
+/**
+ * Implementation of CheckModelLoader that loads OLAP check models from XMI files.
+ */
+@Component(service = CheckModelLoader.class)
+public class CheckModelLoaderImpl implements CheckModelLoader {
+
+ private final ResourceSet resourceSet;
+
+ public CheckModelLoaderImpl() {
+ this.resourceSet = new ResourceSetImpl();
+ initialize();
+ }
+
+ private void initialize() {
+ // Register the package
+ OlapCheckPackage.eINSTANCE.eClass();
+
+ // Register XMI resource factory for .xmi and .olapcheck extensions
+ resourceSet.getResourceFactoryRegistry()
+ .getExtensionToFactoryMap()
+ .put("xmi", new XMIResourceFactoryImpl());
+
+ resourceSet.getResourceFactoryRegistry()
+ .getExtensionToFactoryMap()
+ .put("olapcheck", new XMIResourceFactoryImpl());
+
+ // Register the package in the resource set
+ resourceSet.getPackageRegistry()
+ .put(OlapCheckPackage.eNS_URI, OlapCheckPackage.eINSTANCE);
+ }
+
+ @Override
+ public OlapCheckModel loadFromXMI(URI uri) throws IOException {
+ Resource resource = resourceSet.getResource(uri, true);
+
+ if (resource.getContents().isEmpty()) {
+ throw new IOException("No content found in resource: " + uri);
+ }
+
+ Object root = resource.getContents().get(0);
+ if (!(root instanceof OlapCheckModel)) {
+ throw new IOException(
+ "Root element is not an OlapCheckModel: " + root.getClass().getName()
+ );
+ }
+
+ return (OlapCheckModel) root;
+ }
+
+ @Override
+ public OlapCheckModel loadFromJSON(URI uri) throws IOException {
+ // TODO: Implement JSON loading with Gecko EMF JSON support
+ throw new UnsupportedOperationException("JSON loading not yet implemented");
+ }
+}
diff --git a/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/AttributeCheckHelper.java b/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/AttributeCheckHelper.java
new file mode 100644
index 0000000..11e3815
--- /dev/null
+++ b/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/AttributeCheckHelper.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2024 Contributors to the Eclipse Foundation.
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.daanse.olap.check.runtime.impl.executors;
+
+import org.eclipse.daanse.olap.check.model.check.MatchMode;
+
+/**
+ * Helper class for comparing attribute values with various match modes.
+ */
+public class AttributeCheckHelper {
+
+ private AttributeCheckHelper() {
+ // Utility class
+ }
+
+ public static boolean compareValues(String expected, String actual, MatchMode matchMode, boolean caseSensitive) {
+ if (expected == null && actual == null) {
+ return true;
+ }
+ if (expected == null || actual == null) {
+ return false;
+ }
+
+ return switch (matchMode) {
+ case EQUALS -> caseSensitive ? expected.equals(actual) : expected.equalsIgnoreCase(actual);
+ case CONTAINS -> caseSensitive ? actual.contains(expected) : actual.toLowerCase().contains(expected.toLowerCase());
+ case STARTS_WITH -> caseSensitive ? actual.startsWith(expected) : actual.toLowerCase().startsWith(expected.toLowerCase());
+ case ENDS_WITH -> caseSensitive ? actual.endsWith(expected) : actual.toLowerCase().endsWith(expected.toLowerCase());
+ case REGEX -> actual.matches(expected);
+ case NOT_EQUALS -> caseSensitive ? !expected.equals(actual) : !expected.equalsIgnoreCase(actual);
+ case NOT_CONTAINS -> caseSensitive ? !actual.contains(expected) : !actual.toLowerCase().contains(expected.toLowerCase());
+ };
+ }
+
+ public static boolean compareBooleans(Boolean expected, Boolean actual) {
+ if (expected == null) {
+ return true; // No expectation set
+ }
+ return expected.equals(actual);
+ }
+
+ public static boolean compareInts(Integer expected, Integer actual) {
+ if (expected == null) {
+ return true; // No expectation set
+ }
+ return expected.equals(actual);
+ }
+}
diff --git a/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/CatalogCheckExecutor.java b/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/CatalogCheckExecutor.java
new file mode 100644
index 0000000..845e398
--- /dev/null
+++ b/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/CatalogCheckExecutor.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2024 Contributors to the Eclipse Foundation.
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.daanse.olap.check.runtime.impl.executors;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+
+import org.eclipse.daanse.olap.api.CatalogReader;
+import org.eclipse.daanse.olap.api.connection.Connection;
+import org.eclipse.daanse.olap.api.element.DatabaseSchema;
+import org.eclipse.daanse.olap.api.element.Cube;
+import org.eclipse.daanse.olap.check.model.check.CatalogCheck;
+import org.eclipse.daanse.olap.check.model.check.CatalogCheckResult;
+import org.eclipse.daanse.olap.check.model.check.CheckResult;
+import org.eclipse.daanse.olap.check.model.check.CheckStatus;
+import org.eclipse.daanse.olap.check.model.check.CubeCheck;
+import org.eclipse.daanse.olap.check.model.check.CubeCheckResult;
+import org.eclipse.daanse.olap.check.model.check.DatabaseSchemaCheck;
+import org.eclipse.daanse.olap.check.model.check.DatabaseSchemaCheckResult;
+import org.eclipse.daanse.olap.check.model.check.OlapCheckFactory;
+import org.eclipse.daanse.olap.check.model.check.QueryCheck;
+import org.eclipse.daanse.olap.check.model.check.QueryCheckResult;
+
+/**
+ * Executor for CatalogCheck that verifies catalog structure.
+ */
+public class CatalogCheckExecutor {
+
+ private final CatalogCheck check;
+ private final CatalogReader catalogReader;
+ private final Connection connection;
+ private final OlapCheckFactory factory;
+
+ public CatalogCheckExecutor(CatalogCheck check, CatalogReader catalogReader,
+ Connection connection, OlapCheckFactory factory) {
+ this.check = check;
+ this.catalogReader = catalogReader;
+ this.connection = connection;
+ this.factory = factory;
+ }
+
+ public CheckResult execute() {
+ long startTime = System.currentTimeMillis();
+ Date start = new Date();
+
+ CatalogCheckResult result = factory.createCatalogCheckResult();
+ result.setCheckName(check.getName());
+ result.setCheckDescription(check.getDescription());
+ result.setCatalogName(check.getCatalogName());
+ result.setStartTime(start);
+ result.setSourceCheck(check);
+
+ try {
+ // Get all cubes from catalog
+ List cubes = catalogReader.getCubes();
+
+ // Mark catalog check as success (we have access)
+ result.setStatus(CheckStatus.SUCCESS);
+
+ // Execute cube checks
+ for (CubeCheck cubeCheck : check.getCubeChecks()) {
+ if (!cubeCheck.isEnabled()) {
+ continue;
+ }
+
+ CubeCheckExecutor cubeExecutor = new CubeCheckExecutor(
+ cubeCheck, cubes, catalogReader, connection, factory
+ );
+ CubeCheckResult cubeResult = cubeExecutor.execute();
+ result.getCubeResults().add(cubeResult);
+
+ // If any child fails, mark parent as failed
+ if (cubeResult.getStatus() == CheckStatus.FAILURE) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+ }
+
+ // Execute database schema checks
+ List extends DatabaseSchema> schemas = catalogReader.getDatabaseSchemas();
+ for (DatabaseSchemaCheck schemaCheck : check.getDatabaseSchemaChecks()) {
+ if (!schemaCheck.isEnabled()) {
+ continue;
+ }
+
+ DatabaseSchemaCheckExecutor schemaExecutor = new DatabaseSchemaCheckExecutor(
+ schemaCheck, schemas, factory
+ );
+ DatabaseSchemaCheckResult schemaResult = schemaExecutor.execute();
+ result.getDatabaseSchemaResults().add(schemaResult);
+
+ if (schemaResult.getStatus() == CheckStatus.FAILURE) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+ }
+
+ // Execute query checks (at catalog/connection level)
+ for (QueryCheck queryCheck : check.getQueryChecks()) {
+ if (!queryCheck.isEnabled()) {
+ continue;
+ }
+
+ QueryCheckExecutor queryExecutor = new QueryCheckExecutor(
+ queryCheck, connection, factory
+ );
+ QueryCheckResult queryResult = queryExecutor.execute();
+ result.getQueryResults().add(queryResult);
+
+ if (queryResult.getStatus() == CheckStatus.FAILURE) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+ }
+
+ // Execute attribute checks
+ // TODO: Add attribute check execution
+
+ } catch (Exception e) {
+ result.setStatus(CheckStatus.FAILURE);
+ // Create a failure message
+ }
+
+ result.setEndTime(new Date());
+ result.setExecutionTimeMs(System.currentTimeMillis() - startTime);
+
+ return result;
+ }
+}
diff --git a/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/CubeCheckExecutor.java b/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/CubeCheckExecutor.java
new file mode 100644
index 0000000..8fcd9ab
--- /dev/null
+++ b/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/CubeCheckExecutor.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 2024 Contributors to the Eclipse Foundation.
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.daanse.olap.check.runtime.impl.executors;
+
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+
+import org.eclipse.daanse.olap.api.CatalogReader;
+import org.eclipse.daanse.olap.api.connection.Connection;
+import org.eclipse.daanse.olap.api.element.Cube;
+import org.eclipse.daanse.olap.api.element.Dimension;
+import org.eclipse.daanse.olap.api.element.Member;
+import org.eclipse.daanse.olap.check.model.check.AttributeCheckResult;
+import org.eclipse.daanse.olap.check.model.check.CheckStatus;
+import org.eclipse.daanse.olap.check.model.check.CubeAttribute;
+import org.eclipse.daanse.olap.check.model.check.CubeAttributeCheck;
+import org.eclipse.daanse.olap.check.model.check.CubeCheck;
+import org.eclipse.daanse.olap.check.model.check.CubeCheckResult;
+import org.eclipse.daanse.olap.check.model.check.DimensionCheck;
+import org.eclipse.daanse.olap.check.model.check.DimensionCheckResult;
+import org.eclipse.daanse.olap.check.model.check.DrillThroughActionCheck;
+import org.eclipse.daanse.olap.check.model.check.DrillThroughActionCheckResult;
+import org.eclipse.daanse.olap.check.model.check.KPICheck;
+import org.eclipse.daanse.olap.check.model.check.KPICheckResult;
+import org.eclipse.daanse.olap.check.model.check.MeasureCheck;
+import org.eclipse.daanse.olap.check.model.check.MeasureCheckResult;
+import org.eclipse.daanse.olap.check.model.check.NamedSetCheck;
+import org.eclipse.daanse.olap.check.model.check.NamedSetCheckResult;
+import org.eclipse.daanse.olap.check.model.check.OlapCheckFactory;
+
+/**
+ * Executor for CubeCheck that verifies cube existence and structure.
+ */
+public class CubeCheckExecutor {
+
+ private final CubeCheck check;
+ private final List cubes;
+ private final CatalogReader catalogReader;
+ private final Connection connection;
+ private final OlapCheckFactory factory;
+
+ public CubeCheckExecutor(CubeCheck check, List cubes, CatalogReader catalogReader,
+ Connection connection, OlapCheckFactory factory) {
+ this.check = check;
+ this.cubes = cubes;
+ this.catalogReader = catalogReader;
+ this.connection = connection;
+ this.factory = factory;
+ }
+
+ public CubeCheckResult execute() {
+ long startTime = System.currentTimeMillis();
+ Date start = new Date();
+
+ CubeCheckResult result = factory.createCubeCheckResult();
+ result.setCheckName(check.getName());
+ result.setCheckDescription(check.getDescription());
+ result.setCubeName(check.getCubeName());
+ result.setStartTime(start);
+ result.setSourceCheck(check);
+
+ try {
+ // Find the cube
+ Optional foundCube = findCube();
+
+ if (foundCube.isEmpty()) {
+ result.setStatus(CheckStatus.FAILURE);
+ result.setEndTime(new Date());
+ result.setExecutionTimeMs(System.currentTimeMillis() - startTime);
+ return result;
+ }
+
+ Cube cube = foundCube.get();
+ result.setCubeUniqueName(cube.getUniqueName());
+ result.setStatus(CheckStatus.SUCCESS);
+
+ // Execute attribute checks
+ for (CubeAttributeCheck attrCheck : check.getCubeAttributeChecks()) {
+ AttributeCheckResult attrResult = executeAttributeCheck(attrCheck, cube);
+ result.getAttributeResults().add(attrResult);
+ if (attrResult.getStatus() == CheckStatus.FAILURE) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+ }
+
+ // Execute dimension checks
+ List dimensions = catalogReader.getCubeDimensions(cube);
+ for (DimensionCheck dimensionCheck : check.getDimensionChecks()) {
+ if (!dimensionCheck.isEnabled()) {
+ continue;
+ }
+
+ DimensionCheckExecutor dimExecutor = new DimensionCheckExecutor(
+ dimensionCheck, dimensions, cube, catalogReader, connection, factory
+ );
+ DimensionCheckResult dimResult = dimExecutor.execute();
+ result.getDimensionResults().add(dimResult);
+
+ if (dimResult.getStatus() == CheckStatus.FAILURE) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+ }
+
+ // Execute measure checks
+ List measures = cube.getMeasures();
+ for (MeasureCheck measureCheck : check.getMeasureChecks()) {
+ if (!measureCheck.isEnabled()) {
+ continue;
+ }
+
+ MeasureCheckExecutor measureExecutor = new MeasureCheckExecutor(
+ measureCheck, measures, cube, catalogReader, connection, factory
+ );
+ MeasureCheckResult measureResult = measureExecutor.execute();
+ result.getMeasureResults().add(measureResult);
+
+ if (measureResult.getStatus() == CheckStatus.FAILURE) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+ }
+
+ // Execute KPI checks
+ for (KPICheck kpiCheck : check.getKpiChecks()) {
+ if (!kpiCheck.isEnabled()) {
+ continue;
+ }
+
+ KPICheckExecutor kpiExecutor = new KPICheckExecutor(
+ kpiCheck, cube.getKPIs(), factory
+ );
+ KPICheckResult kpiResult = kpiExecutor.execute();
+ result.getKpiResults().add(kpiResult);
+
+ if (kpiResult.getStatus() == CheckStatus.FAILURE) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+ }
+
+ // Execute NamedSet checks
+ for (NamedSetCheck namedSetCheck : check.getNamedSetChecks()) {
+ if (!namedSetCheck.isEnabled()) {
+ continue;
+ }
+
+ NamedSetCheckExecutor namedSetExecutor = new NamedSetCheckExecutor(
+ namedSetCheck, Arrays.asList(cube.getNamedSets()), factory
+ );
+ NamedSetCheckResult namedSetResult = namedSetExecutor.execute();
+ result.getNamedSetResults().add(namedSetResult);
+
+ if (namedSetResult.getStatus() == CheckStatus.FAILURE) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+ }
+
+ // Execute DrillThroughAction checks
+ for (DrillThroughActionCheck dtaCheck : check.getDrillThroughActionChecks()) {
+ if (!dtaCheck.isEnabled()) {
+ continue;
+ }
+
+ DrillThroughActionCheckExecutor dtaExecutor = new DrillThroughActionCheckExecutor(
+ dtaCheck, cube.getDrillThroughActions(), factory
+ );
+ DrillThroughActionCheckResult dtaResult = dtaExecutor.execute();
+ result.getDrillThroughActionResults().add(dtaResult);
+
+ if (dtaResult.getStatus() == CheckStatus.FAILURE) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+ }
+
+ } catch (Exception e) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+
+ result.setEndTime(new Date());
+ result.setExecutionTimeMs(System.currentTimeMillis() - startTime);
+
+ return result;
+ }
+
+ private Optional findCube() {
+ String cubeName = check.getCubeName();
+ String cubeUniqueName = check.getCubeUniqueName();
+
+ return cubes.stream()
+ .filter(c -> {
+ if (cubeUniqueName != null && !cubeUniqueName.isEmpty()) {
+ return cubeUniqueName.equals(c.getUniqueName());
+ }
+ return cubeName != null && cubeName.equals(c.getName());
+ })
+ .findFirst();
+ }
+
+ private AttributeCheckResult executeAttributeCheck(CubeAttributeCheck attrCheck, Cube cube) {
+ AttributeCheckResult result = factory.createAttributeCheckResult();
+ result.setCheckName(attrCheck.getName());
+ result.setAttributeName(attrCheck.getAttributeType().getName());
+ result.setExpectedValue(attrCheck.getExpectedValue());
+
+ String actualValue = getCubeAttributeValue(cube, attrCheck.getAttributeType());
+ result.setActualValue(actualValue);
+
+ boolean matches;
+ if (attrCheck.getAttributeType() == CubeAttribute.VISIBLE) {
+ // Boolean comparison
+ Boolean expectedBool = attrCheck.getExpectedBoolean();
+ Boolean actualBool = cube.isVisible();
+ matches = AttributeCheckHelper.compareBooleans(expectedBool, actualBool);
+ } else {
+ // String comparison
+ matches = AttributeCheckHelper.compareValues(
+ attrCheck.getExpectedValue(),
+ actualValue,
+ attrCheck.getMatchMode(),
+ attrCheck.isCaseSensitive()
+ );
+ }
+
+ result.setStatus(matches ? CheckStatus.SUCCESS : CheckStatus.FAILURE);
+ return result;
+ }
+
+ private String getCubeAttributeValue(Cube cube, CubeAttribute attributeType) {
+ return switch (attributeType) {
+ case NAME -> cube.getName();
+ case UNIQUE_NAME -> cube.getUniqueName();
+ case CAPTION -> cube.getCaption();
+ case DESCRIPTION -> cube.getDescription();
+ case VISIBLE -> String.valueOf(cube.isVisible());
+ case CUBE_TYPE -> cube.getClass().getSimpleName();
+ };
+ }
+}
diff --git a/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/DatabaseColumnCheckExecutor.java b/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/DatabaseColumnCheckExecutor.java
new file mode 100644
index 0000000..9928441
--- /dev/null
+++ b/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/DatabaseColumnCheckExecutor.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2024 Contributors to the Eclipse Foundation.
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.daanse.olap.check.runtime.impl.executors;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+
+import org.eclipse.daanse.olap.api.element.DatabaseColumn;
+import org.eclipse.daanse.olap.check.model.check.AttributeCheckResult;
+import org.eclipse.daanse.olap.check.model.check.CheckStatus;
+import org.eclipse.daanse.olap.check.model.check.DatabaseColumnAttribute;
+import org.eclipse.daanse.olap.check.model.check.DatabaseColumnAttributeCheck;
+import org.eclipse.daanse.olap.check.model.check.DatabaseColumnCheck;
+import org.eclipse.daanse.olap.check.model.check.DatabaseColumnCheckResult;
+import org.eclipse.daanse.olap.check.model.check.OlapCheckFactory;
+
+/**
+ * Executor for DatabaseColumnCheck that verifies database column existence and attributes.
+ */
+public class DatabaseColumnCheckExecutor {
+
+ private final DatabaseColumnCheck check;
+ private final List columns;
+ private final OlapCheckFactory factory;
+
+ public DatabaseColumnCheckExecutor(DatabaseColumnCheck check, List columns, OlapCheckFactory factory) {
+ this.check = check;
+ this.columns = columns;
+ this.factory = factory;
+ }
+
+ public DatabaseColumnCheckResult execute() {
+ long startTime = System.currentTimeMillis();
+ Date start = new Date();
+
+ DatabaseColumnCheckResult result = factory.createDatabaseColumnCheckResult();
+ result.setCheckName(check.getName());
+ result.setCheckDescription(check.getDescription());
+ result.setColumnName(check.getColumnName());
+ result.setStartTime(start);
+ result.setSourceCheck(check);
+
+ try {
+ // Find the column
+ Optional foundColumn = findColumn();
+
+ if (foundColumn.isEmpty()) {
+ result.setStatus(CheckStatus.FAILURE);
+ result.setEndTime(new Date());
+ result.setExecutionTimeMs(System.currentTimeMillis() - startTime);
+ return result;
+ }
+
+ DatabaseColumn column = foundColumn.get();
+ result.setStatus(CheckStatus.SUCCESS);
+
+ // Execute attribute checks
+ for (DatabaseColumnAttributeCheck attrCheck : check.getColumnAttributeChecks()) {
+ AttributeCheckResult attrResult = executeAttributeCheck(attrCheck, column);
+ result.getAttributeResults().add(attrResult);
+ if (attrResult.getStatus() == CheckStatus.FAILURE) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+ }
+
+ } catch (Exception e) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+
+ result.setEndTime(new Date());
+ result.setExecutionTimeMs(System.currentTimeMillis() - startTime);
+
+ return result;
+ }
+
+ private Optional findColumn() {
+ String columnName = check.getColumnName();
+
+ return columns.stream()
+ .filter(c -> columnName != null && columnName.equals(c.getName()))
+ .findFirst();
+ }
+
+ private AttributeCheckResult executeAttributeCheck(DatabaseColumnAttributeCheck attrCheck, DatabaseColumn column) {
+ AttributeCheckResult result = factory.createAttributeCheckResult();
+ result.setCheckName(attrCheck.getName());
+ result.setAttributeName(attrCheck.getAttributeType().getName());
+ result.setExpectedValue(attrCheck.getExpectedValue());
+
+ String actualValue = getColumnAttributeValue(column, attrCheck.getAttributeType());
+ result.setActualValue(actualValue);
+
+ boolean matches;
+ if (attrCheck.getAttributeType() == DatabaseColumnAttribute.NULLABLE) {
+ Boolean expectedBool = attrCheck.getExpectedBoolean();
+ Boolean actualBool = column.getNullable();
+ matches = AttributeCheckHelper.compareBooleans(expectedBool, actualBool);
+ } else if (attrCheck.getAttributeType() == DatabaseColumnAttribute.COLUMN_SIZE ||
+ attrCheck.getAttributeType() == DatabaseColumnAttribute.DECIMAL_DIGITS) {
+ Integer expectedInt = attrCheck.getExpectedInt();
+ Integer actualInt = getColumnIntAttribute(column, attrCheck.getAttributeType());
+ matches = AttributeCheckHelper.compareInts(expectedInt, actualInt);
+ } else {
+ matches = AttributeCheckHelper.compareValues(
+ attrCheck.getExpectedValue(),
+ actualValue,
+ attrCheck.getMatchMode(),
+ attrCheck.isCaseSensitive()
+ );
+ }
+
+ result.setStatus(matches ? CheckStatus.SUCCESS : CheckStatus.FAILURE);
+ return result;
+ }
+
+ private String getColumnAttributeValue(DatabaseColumn column, DatabaseColumnAttribute attributeType) {
+ return switch (attributeType) {
+ case NAME -> column.getName();
+ case TYPE -> column.getType() != null ? column.getType().name() : null;
+ case NULLABLE -> String.valueOf(column.getNullable());
+ case COLUMN_SIZE -> String.valueOf(column.getColumnSize());
+ case DECIMAL_DIGITS -> String.valueOf(column.getDecimalDigits());
+ };
+ }
+
+ private Integer getColumnIntAttribute(DatabaseColumn column, DatabaseColumnAttribute attributeType) {
+ return switch (attributeType) {
+ case COLUMN_SIZE -> column.getColumnSize();
+ case DECIMAL_DIGITS -> column.getDecimalDigits();
+ default -> null;
+ };
+ }
+}
diff --git a/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/DatabaseSchemaCheckExecutor.java b/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/DatabaseSchemaCheckExecutor.java
new file mode 100644
index 0000000..20d0966
--- /dev/null
+++ b/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/DatabaseSchemaCheckExecutor.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2024 Contributors to the Eclipse Foundation.
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.daanse.olap.check.runtime.impl.executors;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+
+import org.eclipse.daanse.olap.api.element.DatabaseSchema;
+import org.eclipse.daanse.olap.api.element.DatabaseTable;
+import org.eclipse.daanse.olap.check.model.check.AttributeCheckResult;
+import org.eclipse.daanse.olap.check.model.check.CheckStatus;
+import org.eclipse.daanse.olap.check.model.check.DatabaseSchemaAttribute;
+import org.eclipse.daanse.olap.check.model.check.DatabaseSchemaAttributeCheck;
+import org.eclipse.daanse.olap.check.model.check.DatabaseSchemaCheck;
+import org.eclipse.daanse.olap.check.model.check.DatabaseSchemaCheckResult;
+import org.eclipse.daanse.olap.check.model.check.DatabaseTableCheck;
+import org.eclipse.daanse.olap.check.model.check.DatabaseTableCheckResult;
+import org.eclipse.daanse.olap.check.model.check.OlapCheckFactory;
+
+/**
+ * Executor for DatabaseSchemaCheck that verifies database schema existence and structure.
+ */
+public class DatabaseSchemaCheckExecutor {
+
+ private final DatabaseSchemaCheck check;
+ private final List extends DatabaseSchema> schemas;
+ private final OlapCheckFactory factory;
+
+ public DatabaseSchemaCheckExecutor(DatabaseSchemaCheck check, List extends DatabaseSchema> schemas, OlapCheckFactory factory) {
+ this.check = check;
+ this.schemas = schemas;
+ this.factory = factory;
+ }
+
+ public DatabaseSchemaCheckResult execute() {
+ long startTime = System.currentTimeMillis();
+ Date start = new Date();
+
+ DatabaseSchemaCheckResult result = factory.createDatabaseSchemaCheckResult();
+ result.setCheckName(check.getName());
+ result.setCheckDescription(check.getDescription());
+ result.setSchemaName(check.getSchemaName());
+ result.setStartTime(start);
+ result.setSourceCheck(check);
+
+ try {
+ // Find the schema
+ Optional extends DatabaseSchema> foundSchema = findSchema();
+
+ if (foundSchema.isEmpty()) {
+ result.setStatus(CheckStatus.FAILURE);
+ result.setEndTime(new Date());
+ result.setExecutionTimeMs(System.currentTimeMillis() - startTime);
+ return result;
+ }
+
+ DatabaseSchema schema = foundSchema.get();
+ result.setStatus(CheckStatus.SUCCESS);
+
+ // Execute attribute checks
+ for (DatabaseSchemaAttributeCheck attrCheck : check.getSchemaAttributeChecks()) {
+ AttributeCheckResult attrResult = executeAttributeCheck(attrCheck, schema);
+ result.getAttributeResults().add(attrResult);
+ if (attrResult.getStatus() == CheckStatus.FAILURE) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+ }
+
+ // Execute table checks
+ List tables = schema.getDbTables();
+ for (DatabaseTableCheck tableCheck : check.getTableChecks()) {
+ if (!tableCheck.isEnabled()) {
+ continue;
+ }
+
+ DatabaseTableCheckExecutor tableExecutor = new DatabaseTableCheckExecutor(
+ tableCheck, tables, factory
+ );
+ DatabaseTableCheckResult tableResult = tableExecutor.execute();
+ result.getTableResults().add(tableResult);
+
+ if (tableResult.getStatus() == CheckStatus.FAILURE) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+ }
+
+ } catch (Exception e) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+
+ result.setEndTime(new Date());
+ result.setExecutionTimeMs(System.currentTimeMillis() - startTime);
+
+ return result;
+ }
+
+ private Optional extends DatabaseSchema> findSchema() {
+ String schemaName = check.getSchemaName();
+
+ return schemas.stream()
+ .filter(s -> schemaName != null && schemaName.equals(s.getName()))
+ .findFirst();
+ }
+
+ private AttributeCheckResult executeAttributeCheck(DatabaseSchemaAttributeCheck attrCheck, DatabaseSchema schema) {
+ AttributeCheckResult result = factory.createAttributeCheckResult();
+ result.setCheckName(attrCheck.getName());
+ result.setAttributeName(attrCheck.getAttributeType().getName());
+ result.setExpectedValue(attrCheck.getExpectedValue());
+
+ String actualValue = getSchemaAttributeValue(schema, attrCheck.getAttributeType());
+ result.setActualValue(actualValue);
+
+ boolean matches = AttributeCheckHelper.compareValues(
+ attrCheck.getExpectedValue(),
+ actualValue,
+ attrCheck.getMatchMode(),
+ attrCheck.isCaseSensitive()
+ );
+
+ result.setStatus(matches ? CheckStatus.SUCCESS : CheckStatus.FAILURE);
+ return result;
+ }
+
+ private String getSchemaAttributeValue(DatabaseSchema schema, DatabaseSchemaAttribute attributeType) {
+ return switch (attributeType) {
+ case NAME -> schema.getName();
+ };
+ }
+}
diff --git a/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/DatabaseTableCheckExecutor.java b/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/DatabaseTableCheckExecutor.java
new file mode 100644
index 0000000..c913876
--- /dev/null
+++ b/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/DatabaseTableCheckExecutor.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2024 Contributors to the Eclipse Foundation.
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.daanse.olap.check.runtime.impl.executors;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+
+import org.eclipse.daanse.olap.api.element.DatabaseColumn;
+import org.eclipse.daanse.olap.api.element.DatabaseTable;
+import org.eclipse.daanse.olap.check.model.check.AttributeCheckResult;
+import org.eclipse.daanse.olap.check.model.check.CheckStatus;
+import org.eclipse.daanse.olap.check.model.check.DatabaseColumnCheck;
+import org.eclipse.daanse.olap.check.model.check.DatabaseColumnCheckResult;
+import org.eclipse.daanse.olap.check.model.check.DatabaseTableAttribute;
+import org.eclipse.daanse.olap.check.model.check.DatabaseTableAttributeCheck;
+import org.eclipse.daanse.olap.check.model.check.DatabaseTableCheck;
+import org.eclipse.daanse.olap.check.model.check.DatabaseTableCheckResult;
+import org.eclipse.daanse.olap.check.model.check.OlapCheckFactory;
+
+/**
+ * Executor for DatabaseTableCheck that verifies database table existence and structure.
+ */
+public class DatabaseTableCheckExecutor {
+
+ private final DatabaseTableCheck check;
+ private final List tables;
+ private final OlapCheckFactory factory;
+
+ public DatabaseTableCheckExecutor(DatabaseTableCheck check, List tables, OlapCheckFactory factory) {
+ this.check = check;
+ this.tables = tables;
+ this.factory = factory;
+ }
+
+ public DatabaseTableCheckResult execute() {
+ long startTime = System.currentTimeMillis();
+ Date start = new Date();
+
+ DatabaseTableCheckResult result = factory.createDatabaseTableCheckResult();
+ result.setCheckName(check.getName());
+ result.setCheckDescription(check.getDescription());
+ result.setTableName(check.getTableName());
+ result.setStartTime(start);
+ result.setSourceCheck(check);
+
+ try {
+ // Find the table
+ Optional foundTable = findTable();
+
+ if (foundTable.isEmpty()) {
+ result.setStatus(CheckStatus.FAILURE);
+ result.setEndTime(new Date());
+ result.setExecutionTimeMs(System.currentTimeMillis() - startTime);
+ return result;
+ }
+
+ DatabaseTable table = foundTable.get();
+ result.setStatus(CheckStatus.SUCCESS);
+
+ // Execute attribute checks
+ for (DatabaseTableAttributeCheck attrCheck : check.getTableAttributeChecks()) {
+ AttributeCheckResult attrResult = executeAttributeCheck(attrCheck, table);
+ result.getAttributeResults().add(attrResult);
+ if (attrResult.getStatus() == CheckStatus.FAILURE) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+ }
+
+ // Execute column checks
+ List columns = table.getDbColumns();
+ for (DatabaseColumnCheck columnCheck : check.getColumnChecks()) {
+ if (!columnCheck.isEnabled()) {
+ continue;
+ }
+
+ DatabaseColumnCheckExecutor columnExecutor = new DatabaseColumnCheckExecutor(
+ columnCheck, columns, factory
+ );
+ DatabaseColumnCheckResult columnResult = columnExecutor.execute();
+ result.getColumnResults().add(columnResult);
+
+ if (columnResult.getStatus() == CheckStatus.FAILURE) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+ }
+
+ } catch (Exception e) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+
+ result.setEndTime(new Date());
+ result.setExecutionTimeMs(System.currentTimeMillis() - startTime);
+
+ return result;
+ }
+
+ private Optional findTable() {
+ String tableName = check.getTableName();
+
+ return tables.stream()
+ .filter(t -> tableName != null && tableName.equals(t.getName()))
+ .findFirst();
+ }
+
+ private AttributeCheckResult executeAttributeCheck(DatabaseTableAttributeCheck attrCheck, DatabaseTable table) {
+ AttributeCheckResult result = factory.createAttributeCheckResult();
+ result.setCheckName(attrCheck.getName());
+ result.setAttributeName(attrCheck.getAttributeType().getName());
+ result.setExpectedValue(attrCheck.getExpectedValue());
+
+ String actualValue = getTableAttributeValue(table, attrCheck.getAttributeType());
+ result.setActualValue(actualValue);
+
+ boolean matches = AttributeCheckHelper.compareValues(
+ attrCheck.getExpectedValue(),
+ actualValue,
+ attrCheck.getMatchMode(),
+ attrCheck.isCaseSensitive()
+ );
+
+ result.setStatus(matches ? CheckStatus.SUCCESS : CheckStatus.FAILURE);
+ return result;
+ }
+
+ private String getTableAttributeValue(DatabaseTable table, DatabaseTableAttribute attributeType) {
+ return switch (attributeType) {
+ case NAME -> table.getName();
+ case DESCRIPTION -> table.getDescription();
+ };
+ }
+}
diff --git a/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/DimensionCheckExecutor.java b/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/DimensionCheckExecutor.java
new file mode 100644
index 0000000..f177816
--- /dev/null
+++ b/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/DimensionCheckExecutor.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2024 Contributors to the Eclipse Foundation.
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.daanse.olap.check.runtime.impl.executors;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+
+import org.eclipse.daanse.olap.api.CatalogReader;
+import org.eclipse.daanse.olap.api.connection.Connection;
+import org.eclipse.daanse.olap.api.element.Cube;
+import org.eclipse.daanse.olap.api.element.Dimension;
+import org.eclipse.daanse.olap.api.element.Hierarchy;
+import org.eclipse.daanse.olap.check.model.check.AttributeCheckResult;
+import org.eclipse.daanse.olap.check.model.check.CheckStatus;
+import org.eclipse.daanse.olap.check.model.check.DimensionAttribute;
+import org.eclipse.daanse.olap.check.model.check.DimensionAttributeCheck;
+import org.eclipse.daanse.olap.check.model.check.DimensionCheck;
+import org.eclipse.daanse.olap.check.model.check.DimensionCheckResult;
+import org.eclipse.daanse.olap.check.model.check.HierarchyCheck;
+import org.eclipse.daanse.olap.check.model.check.HierarchyCheckResult;
+import org.eclipse.daanse.olap.check.model.check.OlapCheckFactory;
+
+/**
+ * Executor for DimensionCheck that verifies dimension existence and structure.
+ */
+public class DimensionCheckExecutor {
+
+ private final DimensionCheck check;
+ private final List dimensions;
+ private final Cube cube;
+ private final CatalogReader catalogReader;
+ private final Connection connection;
+ private final OlapCheckFactory factory;
+
+ public DimensionCheckExecutor(DimensionCheck check, List dimensions, Cube cube,
+ CatalogReader catalogReader, Connection connection, OlapCheckFactory factory) {
+ this.check = check;
+ this.dimensions = dimensions;
+ this.cube = cube;
+ this.catalogReader = catalogReader;
+ this.connection = connection;
+ this.factory = factory;
+ }
+
+ public DimensionCheckResult execute() {
+ long startTime = System.currentTimeMillis();
+ Date start = new Date();
+
+ DimensionCheckResult result = factory.createDimensionCheckResult();
+ result.setCheckName(check.getName());
+ result.setCheckDescription(check.getDescription());
+ result.setDimensionName(check.getDimensionName());
+ result.setStartTime(start);
+ result.setSourceCheck(check);
+
+ try {
+ // Find the dimension
+ Optional foundDimension = findDimension();
+
+ if (foundDimension.isEmpty()) {
+ result.setStatus(CheckStatus.FAILURE);
+ result.setEndTime(new Date());
+ result.setExecutionTimeMs(System.currentTimeMillis() - startTime);
+ return result;
+ }
+
+ Dimension dimension = foundDimension.get();
+ result.setDimensionUniqueName(dimension.getUniqueName());
+ result.setStatus(CheckStatus.SUCCESS);
+
+ // Execute attribute checks
+ for (DimensionAttributeCheck attrCheck : check.getDimensionAttributeChecks()) {
+ AttributeCheckResult attrResult = executeAttributeCheck(attrCheck, dimension);
+ result.getAttributeResults().add(attrResult);
+ if (attrResult.getStatus() == CheckStatus.FAILURE) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+ }
+
+ // Execute hierarchy checks
+ List hierarchies = catalogReader.getDimensionHierarchies(dimension);
+ for (HierarchyCheck hierarchyCheck : check.getHierarchyChecks()) {
+ if (!hierarchyCheck.isEnabled()) {
+ continue;
+ }
+
+ HierarchyCheckExecutor hierExecutor = new HierarchyCheckExecutor(
+ hierarchyCheck, hierarchies, cube, catalogReader, connection, factory
+ );
+ HierarchyCheckResult hierResult = hierExecutor.execute();
+ result.getHierarchyResults().add(hierResult);
+
+ if (hierResult.getStatus() == CheckStatus.FAILURE) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+ }
+
+ } catch (Exception e) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+
+ result.setEndTime(new Date());
+ result.setExecutionTimeMs(System.currentTimeMillis() - startTime);
+
+ return result;
+ }
+
+ private Optional findDimension() {
+ String dimensionName = check.getDimensionName();
+ String dimensionUniqueName = check.getDimensionUniqueName();
+
+ return dimensions.stream()
+ .filter(d -> {
+ if (dimensionUniqueName != null && !dimensionUniqueName.isEmpty()) {
+ return dimensionUniqueName.equals(d.getUniqueName());
+ }
+ return dimensionName != null && dimensionName.equals(d.getName());
+ })
+ .findFirst();
+ }
+
+ private AttributeCheckResult executeAttributeCheck(DimensionAttributeCheck attrCheck, Dimension dimension) {
+ AttributeCheckResult result = factory.createAttributeCheckResult();
+ result.setCheckName(attrCheck.getName());
+ result.setAttributeName(attrCheck.getAttributeType().getName());
+ result.setExpectedValue(attrCheck.getExpectedValue());
+
+ String actualValue = getDimensionAttributeValue(dimension, attrCheck.getAttributeType());
+ result.setActualValue(actualValue);
+
+ boolean matches;
+ if (attrCheck.getAttributeType() == DimensionAttribute.VISIBLE ||
+ attrCheck.getAttributeType() == DimensionAttribute.IS_VIRTUAL) {
+ Boolean expectedBool = attrCheck.getExpectedBoolean();
+ Boolean actualBool = getDimensionBooleanAttribute(dimension, attrCheck.getAttributeType());
+ matches = AttributeCheckHelper.compareBooleans(expectedBool, actualBool);
+ } else if (attrCheck.getAttributeType() == DimensionAttribute.ORDINAL ||
+ attrCheck.getAttributeType() == DimensionAttribute.CARDINALITY) {
+ Integer expectedInt = attrCheck.getExpectedInt();
+ Integer actualInt = getDimensionIntAttribute(dimension, attrCheck.getAttributeType());
+ matches = AttributeCheckHelper.compareInts(expectedInt, actualInt);
+ } else {
+ matches = AttributeCheckHelper.compareValues(
+ attrCheck.getExpectedValue(),
+ actualValue,
+ attrCheck.getMatchMode(),
+ attrCheck.isCaseSensitive()
+ );
+ }
+
+ result.setStatus(matches ? CheckStatus.SUCCESS : CheckStatus.FAILURE);
+ return result;
+ }
+
+ private String getDimensionAttributeValue(Dimension dimension, DimensionAttribute attributeType) {
+ return switch (attributeType) {
+ case NAME -> dimension.getName();
+ case UNIQUE_NAME -> dimension.getUniqueName();
+ case CAPTION -> dimension.getCaption();
+ case DESCRIPTION -> dimension.getDescription();
+ case VISIBLE -> String.valueOf(dimension.isVisible());
+ case DIMENSION_TYPE -> dimension.getDimensionType() != null ? dimension.getDimensionType().name() : null;
+ case ORDINAL -> "0"; // Ordinal not directly available on Dimension
+ case IS_VIRTUAL -> "false";
+ case CARDINALITY -> "0";
+ };
+ }
+
+ private Boolean getDimensionBooleanAttribute(Dimension dimension, DimensionAttribute attributeType) {
+ return switch (attributeType) {
+ case VISIBLE -> dimension.isVisible();
+ case IS_VIRTUAL -> false;
+ default -> null;
+ };
+ }
+
+ private Integer getDimensionIntAttribute(Dimension dimension, DimensionAttribute attributeType) {
+ return switch (attributeType) {
+ case ORDINAL -> 0; // Ordinal not directly available on Dimension
+ case CARDINALITY -> 0;
+ default -> null;
+ };
+ }
+}
diff --git a/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/DrillThroughActionCheckExecutor.java b/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/DrillThroughActionCheckExecutor.java
new file mode 100644
index 0000000..fcaa1ab
--- /dev/null
+++ b/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/DrillThroughActionCheckExecutor.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2024 Contributors to the Eclipse Foundation.
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.daanse.olap.check.runtime.impl.executors;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+
+import org.eclipse.daanse.olap.api.element.DrillThroughAction;
+import org.eclipse.daanse.olap.check.model.check.AttributeCheckResult;
+import org.eclipse.daanse.olap.check.model.check.CheckStatus;
+import org.eclipse.daanse.olap.check.model.check.DrillThroughActionAttribute;
+import org.eclipse.daanse.olap.check.model.check.DrillThroughActionAttributeCheck;
+import org.eclipse.daanse.olap.check.model.check.DrillThroughActionCheck;
+import org.eclipse.daanse.olap.check.model.check.DrillThroughActionCheckResult;
+import org.eclipse.daanse.olap.check.model.check.OlapCheckFactory;
+
+/**
+ * Executor for DrillThroughActionCheck that verifies drill-through action existence and attributes.
+ */
+public class DrillThroughActionCheckExecutor {
+
+ private final DrillThroughActionCheck check;
+ private final List extends DrillThroughAction> actions;
+ private final OlapCheckFactory factory;
+
+ public DrillThroughActionCheckExecutor(DrillThroughActionCheck check, List extends DrillThroughAction> actions, OlapCheckFactory factory) {
+ this.check = check;
+ this.actions = actions;
+ this.factory = factory;
+ }
+
+ public DrillThroughActionCheckResult execute() {
+ long startTime = System.currentTimeMillis();
+ Date start = new Date();
+
+ DrillThroughActionCheckResult result = factory.createDrillThroughActionCheckResult();
+ result.setCheckName(check.getName());
+ result.setCheckDescription(check.getDescription());
+ result.setActionName(check.getActionName());
+ result.setStartTime(start);
+ result.setSourceCheck(check);
+
+ try {
+ // Find the DrillThroughAction
+ Optional extends DrillThroughAction> foundAction = findAction();
+
+ if (foundAction.isEmpty()) {
+ result.setStatus(CheckStatus.FAILURE);
+ result.setEndTime(new Date());
+ result.setExecutionTimeMs(System.currentTimeMillis() - startTime);
+ return result;
+ }
+
+ DrillThroughAction action = foundAction.get();
+ result.setStatus(CheckStatus.SUCCESS);
+
+ // Execute attribute checks
+ for (DrillThroughActionAttributeCheck attrCheck : check.getActionAttributeChecks()) {
+ AttributeCheckResult attrResult = executeAttributeCheck(attrCheck, action);
+ result.getAttributeResults().add(attrResult);
+ if (attrResult.getStatus() == CheckStatus.FAILURE) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+ }
+
+ } catch (Exception e) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+
+ result.setEndTime(new Date());
+ result.setExecutionTimeMs(System.currentTimeMillis() - startTime);
+
+ return result;
+ }
+
+ private Optional extends DrillThroughAction> findAction() {
+ String actionName = check.getActionName();
+
+ return actions.stream()
+ .filter(a -> actionName != null && actionName.equals(a.getName()))
+ .findFirst();
+ }
+
+ private AttributeCheckResult executeAttributeCheck(DrillThroughActionAttributeCheck attrCheck, DrillThroughAction action) {
+ AttributeCheckResult result = factory.createAttributeCheckResult();
+ result.setCheckName(attrCheck.getName());
+ result.setAttributeName(attrCheck.getAttributeType().getName());
+ result.setExpectedValue(attrCheck.getExpectedValue());
+
+ String actualValue = getActionAttributeValue(action, attrCheck.getAttributeType());
+ result.setActualValue(actualValue);
+
+ boolean matches;
+ if (attrCheck.getAttributeType() == DrillThroughActionAttribute.IS_DEFAULT) {
+ Boolean expectedBool = attrCheck.getExpectedBoolean();
+ Boolean actualBool = action.getIsDefault();
+ matches = AttributeCheckHelper.compareBooleans(expectedBool, actualBool);
+ } else {
+ matches = AttributeCheckHelper.compareValues(
+ attrCheck.getExpectedValue(),
+ actualValue,
+ attrCheck.getMatchMode(),
+ attrCheck.isCaseSensitive()
+ );
+ }
+
+ result.setStatus(matches ? CheckStatus.SUCCESS : CheckStatus.FAILURE);
+ return result;
+ }
+
+ private String getActionAttributeValue(DrillThroughAction action, DrillThroughActionAttribute attributeType) {
+ return switch (attributeType) {
+ case NAME -> action.getName();
+ case CAPTION -> action.getCaption();
+ case DESCRIPTION -> action.getDescription();
+ case IS_DEFAULT -> String.valueOf(action.getIsDefault());
+ };
+ }
+}
diff --git a/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/HierarchyCheckExecutor.java b/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/HierarchyCheckExecutor.java
new file mode 100644
index 0000000..46ce8d1
--- /dev/null
+++ b/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/HierarchyCheckExecutor.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2024 Contributors to the Eclipse Foundation.
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.daanse.olap.check.runtime.impl.executors;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+
+import org.eclipse.daanse.olap.api.CatalogReader;
+import org.eclipse.daanse.olap.api.connection.Connection;
+import org.eclipse.daanse.olap.api.element.Cube;
+import org.eclipse.daanse.olap.api.element.Hierarchy;
+import org.eclipse.daanse.olap.api.element.Level;
+import org.eclipse.daanse.olap.api.element.Member;
+import org.eclipse.daanse.olap.check.model.check.AttributeCheckResult;
+import org.eclipse.daanse.olap.check.model.check.CheckStatus;
+import org.eclipse.daanse.olap.check.model.check.HierarchyAttribute;
+import org.eclipse.daanse.olap.check.model.check.HierarchyAttributeCheck;
+import org.eclipse.daanse.olap.check.model.check.HierarchyCheck;
+import org.eclipse.daanse.olap.check.model.check.HierarchyCheckResult;
+import org.eclipse.daanse.olap.check.model.check.LevelCheck;
+import org.eclipse.daanse.olap.check.model.check.LevelCheckResult;
+import org.eclipse.daanse.olap.check.model.check.OlapCheckFactory;
+
+/**
+ * Executor for HierarchyCheck that verifies hierarchy existence and structure.
+ */
+public class HierarchyCheckExecutor {
+
+ private final HierarchyCheck check;
+ private final List hierarchies;
+ private final Cube cube;
+ private final CatalogReader catalogReader;
+ private final Connection connection;
+ private final OlapCheckFactory factory;
+
+ public HierarchyCheckExecutor(HierarchyCheck check, List hierarchies, Cube cube,
+ CatalogReader catalogReader, Connection connection, OlapCheckFactory factory) {
+ this.check = check;
+ this.hierarchies = hierarchies;
+ this.cube = cube;
+ this.catalogReader = catalogReader;
+ this.connection = connection;
+ this.factory = factory;
+ }
+
+ public HierarchyCheckResult execute() {
+ long startTime = System.currentTimeMillis();
+ Date start = new Date();
+
+ HierarchyCheckResult result = factory.createHierarchyCheckResult();
+ result.setCheckName(check.getName());
+ result.setCheckDescription(check.getDescription());
+ result.setHierarchyName(check.getHierarchyName());
+ result.setStartTime(start);
+ result.setSourceCheck(check);
+
+ try {
+ // Find the hierarchy
+ Optional foundHierarchy = findHierarchy();
+
+ if (foundHierarchy.isEmpty()) {
+ result.setStatus(CheckStatus.FAILURE);
+ result.setEndTime(new Date());
+ result.setExecutionTimeMs(System.currentTimeMillis() - startTime);
+ return result;
+ }
+
+ Hierarchy hierarchy = foundHierarchy.get();
+ result.setHierarchyUniqueName(hierarchy.getUniqueName());
+ result.setStatus(CheckStatus.SUCCESS);
+
+ // Execute attribute checks
+ for (HierarchyAttributeCheck attrCheck : check.getHierarchyAttributeChecks()) {
+ AttributeCheckResult attrResult = executeAttributeCheck(attrCheck, hierarchy);
+ result.getAttributeResults().add(attrResult);
+ if (attrResult.getStatus() == CheckStatus.FAILURE) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+ }
+
+ // Execute level checks
+ List levels = catalogReader.getHierarchyLevels(hierarchy);
+ for (LevelCheck levelCheck : check.getLevelChecks()) {
+ if (!levelCheck.isEnabled()) {
+ continue;
+ }
+
+ LevelCheckExecutor levelExecutor = new LevelCheckExecutor(
+ levelCheck, levels, cube, catalogReader, connection, factory
+ );
+ LevelCheckResult levelResult = levelExecutor.execute();
+ result.getLevelResults().add(levelResult);
+
+ if (levelResult.getStatus() == CheckStatus.FAILURE) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+ }
+
+ } catch (Exception e) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+
+ result.setEndTime(new Date());
+ result.setExecutionTimeMs(System.currentTimeMillis() - startTime);
+
+ return result;
+ }
+
+ private Optional findHierarchy() {
+ String hierarchyName = check.getHierarchyName();
+ String hierarchyUniqueName = check.getHierarchyUniqueName();
+
+ return hierarchies.stream()
+ .filter(h -> {
+ if (hierarchyUniqueName != null && !hierarchyUniqueName.isEmpty()) {
+ return hierarchyUniqueName.equals(h.getUniqueName());
+ }
+ return hierarchyName != null && hierarchyName.equals(h.getName());
+ })
+ .findFirst();
+ }
+
+ private AttributeCheckResult executeAttributeCheck(HierarchyAttributeCheck attrCheck, Hierarchy hierarchy) {
+ AttributeCheckResult result = factory.createAttributeCheckResult();
+ result.setCheckName(attrCheck.getName());
+ result.setAttributeName(attrCheck.getAttributeType().getName());
+ result.setExpectedValue(attrCheck.getExpectedValue());
+
+ String actualValue = getHierarchyAttributeValue(hierarchy, attrCheck.getAttributeType());
+ result.setActualValue(actualValue);
+
+ boolean matches;
+ if (attrCheck.getAttributeType() == HierarchyAttribute.VISIBLE ||
+ attrCheck.getAttributeType() == HierarchyAttribute.HAS_ALL) {
+ Boolean expectedBool = attrCheck.getExpectedBoolean();
+ Boolean actualBool = getHierarchyBooleanAttribute(hierarchy, attrCheck.getAttributeType());
+ matches = AttributeCheckHelper.compareBooleans(expectedBool, actualBool);
+ } else if (attrCheck.getAttributeType() == HierarchyAttribute.CARDINALITY) {
+ Integer expectedInt = attrCheck.getExpectedInt();
+ Integer actualInt = getHierarchyIntAttribute(hierarchy, attrCheck.getAttributeType());
+ matches = AttributeCheckHelper.compareInts(expectedInt, actualInt);
+ } else {
+ matches = AttributeCheckHelper.compareValues(
+ attrCheck.getExpectedValue(),
+ actualValue,
+ attrCheck.getMatchMode(),
+ attrCheck.isCaseSensitive()
+ );
+ }
+
+ result.setStatus(matches ? CheckStatus.SUCCESS : CheckStatus.FAILURE);
+ return result;
+ }
+
+ private String getHierarchyAttributeValue(Hierarchy hierarchy, HierarchyAttribute attributeType) {
+ return switch (attributeType) {
+ case NAME -> hierarchy.getName();
+ case UNIQUE_NAME -> hierarchy.getUniqueName();
+ case CAPTION -> hierarchy.getCaption();
+ case DESCRIPTION -> hierarchy.getDescription();
+ case VISIBLE -> String.valueOf(hierarchy.isVisible());
+ case HAS_ALL -> String.valueOf(hierarchy.hasAll());
+ case ALL_MEMBER_NAME -> {
+ Member allMember = hierarchy.getAllMember();
+ yield allMember != null ? allMember.getName() : null;
+ }
+ case ALL_MEMBER_UNIQUE_NAME -> {
+ Member allMember = hierarchy.getAllMember();
+ yield allMember != null ? allMember.getUniqueName() : null;
+ }
+ case DEFAULT_MEMBER -> {
+ Member defaultMember = hierarchy.getDefaultMember();
+ yield defaultMember != null ? defaultMember.getUniqueName() : null;
+ }
+ case DISPLAY_FOLDER -> hierarchy.getDisplayFolder();
+ case ORIGIN -> ""; // Not typically available
+ case CARDINALITY -> "0"; // Would need to calculate
+ };
+ }
+
+ private Boolean getHierarchyBooleanAttribute(Hierarchy hierarchy, HierarchyAttribute attributeType) {
+ return switch (attributeType) {
+ case VISIBLE -> hierarchy.isVisible();
+ case HAS_ALL -> hierarchy.hasAll();
+ default -> null;
+ };
+ }
+
+ private Integer getHierarchyIntAttribute(Hierarchy hierarchy, HierarchyAttribute attributeType) {
+ return switch (attributeType) {
+ case CARDINALITY -> 0; // Would need to calculate
+ default -> null;
+ };
+ }
+}
diff --git a/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/KPICheckExecutor.java b/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/KPICheckExecutor.java
new file mode 100644
index 0000000..e5956f2
--- /dev/null
+++ b/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/KPICheckExecutor.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2024 Contributors to the Eclipse Foundation.
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.daanse.olap.check.runtime.impl.executors;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+
+import org.eclipse.daanse.olap.api.element.KPI;
+import org.eclipse.daanse.olap.check.model.check.AttributeCheckResult;
+import org.eclipse.daanse.olap.check.model.check.CheckStatus;
+import org.eclipse.daanse.olap.check.model.check.KPIAttribute;
+import org.eclipse.daanse.olap.check.model.check.KPIAttributeCheck;
+import org.eclipse.daanse.olap.check.model.check.KPICheck;
+import org.eclipse.daanse.olap.check.model.check.KPICheckResult;
+import org.eclipse.daanse.olap.check.model.check.OlapCheckFactory;
+
+/**
+ * Executor for KPICheck that verifies KPI existence and attributes.
+ */
+public class KPICheckExecutor {
+
+ private final KPICheck check;
+ private final List extends KPI> kpis;
+ private final OlapCheckFactory factory;
+
+ public KPICheckExecutor(KPICheck check, List extends KPI> kpis, OlapCheckFactory factory) {
+ this.check = check;
+ this.kpis = kpis;
+ this.factory = factory;
+ }
+
+ public KPICheckResult execute() {
+ long startTime = System.currentTimeMillis();
+ Date start = new Date();
+
+ KPICheckResult result = factory.createKPICheckResult();
+ result.setCheckName(check.getName());
+ result.setCheckDescription(check.getDescription());
+ result.setKpiName(check.getKpiName());
+ result.setStartTime(start);
+ result.setSourceCheck(check);
+
+ try {
+ // Find the KPI
+ Optional extends KPI> foundKpi = findKpi();
+
+ if (foundKpi.isEmpty()) {
+ result.setStatus(CheckStatus.FAILURE);
+ result.setEndTime(new Date());
+ result.setExecutionTimeMs(System.currentTimeMillis() - startTime);
+ return result;
+ }
+
+ KPI kpi = foundKpi.get();
+ result.setStatus(CheckStatus.SUCCESS);
+
+ // Execute attribute checks
+ for (KPIAttributeCheck attrCheck : check.getKpiAttributeChecks()) {
+ AttributeCheckResult attrResult = executeAttributeCheck(attrCheck, kpi);
+ result.getAttributeResults().add(attrResult);
+ if (attrResult.getStatus() == CheckStatus.FAILURE) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+ }
+
+ } catch (Exception e) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+
+ result.setEndTime(new Date());
+ result.setExecutionTimeMs(System.currentTimeMillis() - startTime);
+
+ return result;
+ }
+
+ private Optional extends KPI> findKpi() {
+ String kpiName = check.getKpiName();
+
+ return kpis.stream()
+ .filter(k -> kpiName != null && kpiName.equals(k.getName()))
+ .findFirst();
+ }
+
+ private AttributeCheckResult executeAttributeCheck(KPIAttributeCheck attrCheck, KPI kpi) {
+ AttributeCheckResult result = factory.createAttributeCheckResult();
+ result.setCheckName(attrCheck.getName());
+ result.setAttributeName(attrCheck.getAttributeType().getName());
+ result.setExpectedValue(attrCheck.getExpectedValue());
+
+ String actualValue = getKpiAttributeValue(kpi, attrCheck.getAttributeType());
+ result.setActualValue(actualValue);
+
+ boolean matches = AttributeCheckHelper.compareValues(
+ attrCheck.getExpectedValue(),
+ actualValue,
+ attrCheck.getMatchMode(),
+ attrCheck.isCaseSensitive()
+ );
+
+ result.setStatus(matches ? CheckStatus.SUCCESS : CheckStatus.FAILURE);
+ return result;
+ }
+
+ private String getKpiAttributeValue(KPI kpi, KPIAttribute attributeType) {
+ return switch (attributeType) {
+ case NAME -> kpi.getName();
+ case DESCRIPTION -> kpi.getDescription();
+ case DISPLAY_FOLDER -> kpi.getDisplayFolder();
+ case VALUE -> kpi.getValue();
+ case GOAL -> kpi.getGoal();
+ case STATUS -> kpi.getStatus();
+ case TREND -> kpi.getTrend();
+ case WEIGHT -> kpi.getWeight();
+ case CURRENT_TIME_MEMBER -> kpi.getCurrentTimeMember();
+ case STATUS_GRAPHIC -> kpi.getStatusGraphic();
+ case TREND_GRAPHIC -> kpi.getTrendGraphic();
+ case PARENT_KPI_NAME -> {
+ KPI parent = kpi.getParentKpi();
+ yield parent != null ? parent.getName() : null;
+ }
+ };
+ }
+}
diff --git a/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/LevelCheckExecutor.java b/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/LevelCheckExecutor.java
new file mode 100644
index 0000000..9888cee
--- /dev/null
+++ b/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/LevelCheckExecutor.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 2024 Contributors to the Eclipse Foundation.
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.daanse.olap.check.runtime.impl.executors;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+
+import org.eclipse.daanse.olap.api.CatalogReader;
+import org.eclipse.daanse.olap.api.connection.Connection;
+import org.eclipse.daanse.olap.api.element.Cube;
+import org.eclipse.daanse.olap.api.element.Level;
+import org.eclipse.daanse.olap.api.element.Member;
+import org.eclipse.daanse.olap.check.model.check.AttributeCheckResult;
+import org.eclipse.daanse.olap.check.model.check.CheckStatus;
+import org.eclipse.daanse.olap.check.model.check.LevelAttribute;
+import org.eclipse.daanse.olap.check.model.check.LevelAttributeCheck;
+import org.eclipse.daanse.olap.check.model.check.LevelCheck;
+import org.eclipse.daanse.olap.check.model.check.LevelCheckResult;
+import org.eclipse.daanse.olap.check.model.check.MemberCheck;
+import org.eclipse.daanse.olap.check.model.check.MemberCheckResult;
+import org.eclipse.daanse.olap.check.model.check.OlapCheckFactory;
+
+/**
+ * Executor for LevelCheck that verifies level existence and structure.
+ */
+public class LevelCheckExecutor {
+
+ private final LevelCheck check;
+ private final List levels;
+ private final Cube cube;
+ private final CatalogReader catalogReader;
+ private final Connection connection;
+ private final OlapCheckFactory factory;
+
+ public LevelCheckExecutor(LevelCheck check, List levels, Cube cube,
+ CatalogReader catalogReader, Connection connection, OlapCheckFactory factory) {
+ this.check = check;
+ this.levels = levels;
+ this.cube = cube;
+ this.catalogReader = catalogReader;
+ this.connection = connection;
+ this.factory = factory;
+ }
+
+ public LevelCheckResult execute() {
+ long startTime = System.currentTimeMillis();
+ Date start = new Date();
+
+ LevelCheckResult result = factory.createLevelCheckResult();
+ result.setCheckName(check.getName());
+ result.setCheckDescription(check.getDescription());
+ result.setLevelName(check.getLevelName());
+ result.setStartTime(start);
+ result.setSourceCheck(check);
+
+ try {
+ // Find the level
+ Optional foundLevel = findLevel();
+
+ if (foundLevel.isEmpty()) {
+ result.setStatus(CheckStatus.FAILURE);
+ result.setEndTime(new Date());
+ result.setExecutionTimeMs(System.currentTimeMillis() - startTime);
+ return result;
+ }
+
+ Level level = foundLevel.get();
+ result.setLevelUniqueName(level.getUniqueName());
+ result.setStatus(CheckStatus.SUCCESS);
+
+ // Execute attribute checks
+ for (LevelAttributeCheck attrCheck : check.getLevelAttributeChecks()) {
+ AttributeCheckResult attrResult = executeAttributeCheck(attrCheck, level);
+ result.getAttributeResults().add(attrResult);
+ if (attrResult.getStatus() == CheckStatus.FAILURE) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+ }
+
+ // Execute member checks
+ List members = catalogReader.getLevelMembers(level, true);
+ for (MemberCheck memberCheck : check.getMemberChecks()) {
+ if (!memberCheck.isEnabled()) {
+ continue;
+ }
+
+ MemberCheckExecutor memberExecutor = new MemberCheckExecutor(
+ memberCheck, members, cube, catalogReader, connection, factory
+ );
+ MemberCheckResult memberResult = memberExecutor.execute();
+ result.getMemberResults().add(memberResult);
+
+ if (memberResult.getStatus() == CheckStatus.FAILURE) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+ }
+
+ } catch (Exception e) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+
+ result.setEndTime(new Date());
+ result.setExecutionTimeMs(System.currentTimeMillis() - startTime);
+
+ return result;
+ }
+
+ private Optional findLevel() {
+ String levelName = check.getLevelName();
+ String levelUniqueName = check.getLevelUniqueName();
+
+ return levels.stream()
+ .filter(l -> {
+ if (levelUniqueName != null && !levelUniqueName.isEmpty()) {
+ return levelUniqueName.equals(l.getUniqueName());
+ }
+ return levelName != null && levelName.equals(l.getName());
+ })
+ .findFirst();
+ }
+
+ private AttributeCheckResult executeAttributeCheck(LevelAttributeCheck attrCheck, Level level) {
+ AttributeCheckResult result = factory.createAttributeCheckResult();
+ result.setCheckName(attrCheck.getName());
+ result.setAttributeName(attrCheck.getAttributeType().getName());
+ result.setExpectedValue(attrCheck.getExpectedValue());
+
+ String actualValue = getLevelAttributeValue(level, attrCheck.getAttributeType());
+ result.setActualValue(actualValue);
+
+ boolean matches;
+ if (attrCheck.getAttributeType() == LevelAttribute.VISIBLE ||
+ attrCheck.getAttributeType() == LevelAttribute.IS_ALL ||
+ attrCheck.getAttributeType() == LevelAttribute.IS_UNIQUE) {
+ Boolean expectedBool = attrCheck.getExpectedBoolean();
+ Boolean actualBool = getLevelBooleanAttribute(level, attrCheck.getAttributeType());
+ matches = AttributeCheckHelper.compareBooleans(expectedBool, actualBool);
+ } else if (attrCheck.getAttributeType() == LevelAttribute.DEPTH ||
+ attrCheck.getAttributeType() == LevelAttribute.ORDINAL ||
+ attrCheck.getAttributeType() == LevelAttribute.CARDINALITY ||
+ attrCheck.getAttributeType() == LevelAttribute.MEMBERS_WITH_DATA) {
+ Integer expectedInt = attrCheck.getExpectedInt();
+ Integer actualInt = getLevelIntAttribute(level, attrCheck.getAttributeType());
+ matches = AttributeCheckHelper.compareInts(expectedInt, actualInt);
+ } else {
+ matches = AttributeCheckHelper.compareValues(
+ attrCheck.getExpectedValue(),
+ actualValue,
+ attrCheck.getMatchMode(),
+ attrCheck.isCaseSensitive()
+ );
+ }
+
+ result.setStatus(matches ? CheckStatus.SUCCESS : CheckStatus.FAILURE);
+ return result;
+ }
+
+ private String getLevelAttributeValue(Level level, LevelAttribute attributeType) {
+ return switch (attributeType) {
+ case NAME -> level.getName();
+ case UNIQUE_NAME -> level.getUniqueName();
+ case CAPTION -> level.getCaption();
+ case DESCRIPTION -> level.getDescription();
+ case VISIBLE -> String.valueOf(level.isVisible());
+ case DEPTH -> String.valueOf(level.getDepth());
+ case LEVEL_TYPE -> level.getLevelType() != null ? level.getLevelType().name() : null;
+ case IS_ALL -> String.valueOf(level.isAll());
+ case IS_UNIQUE -> String.valueOf(level.isUnique());
+ case ORDINAL -> String.valueOf(level.getDepth());
+ case CARDINALITY -> String.valueOf(cube.getLevelCardinality(level, true, true));
+ case MEMBERS_WITH_DATA -> "0";
+ };
+ }
+
+ private Boolean getLevelBooleanAttribute(Level level, LevelAttribute attributeType) {
+ return switch (attributeType) {
+ case VISIBLE -> level.isVisible();
+ case IS_ALL -> level.isAll();
+ case IS_UNIQUE -> level.isUnique();
+ default -> null;
+ };
+ }
+
+ private Integer getLevelIntAttribute(Level level, LevelAttribute attributeType) {
+ return switch (attributeType) {
+ case DEPTH -> level.getDepth();
+ case ORDINAL -> level.getDepth();
+ case CARDINALITY -> cube.getLevelCardinality(level, true, true);
+ case MEMBERS_WITH_DATA -> 0;
+ default -> null;
+ };
+ }
+}
diff --git a/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/MeasureCheckExecutor.java b/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/MeasureCheckExecutor.java
new file mode 100644
index 0000000..3150b79
--- /dev/null
+++ b/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/MeasureCheckExecutor.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2024 Contributors to the Eclipse Foundation.
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.daanse.olap.check.runtime.impl.executors;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+
+import org.eclipse.daanse.olap.api.CatalogReader;
+import org.eclipse.daanse.olap.api.connection.Connection;
+import org.eclipse.daanse.olap.api.element.Cube;
+import org.eclipse.daanse.olap.api.element.Member;
+import org.eclipse.daanse.olap.check.model.check.AttributeCheckResult;
+import org.eclipse.daanse.olap.check.model.check.CheckStatus;
+import org.eclipse.daanse.olap.check.model.check.MeasureAttribute;
+import org.eclipse.daanse.olap.check.model.check.MeasureAttributeCheck;
+import org.eclipse.daanse.olap.check.model.check.MeasureCheck;
+import org.eclipse.daanse.olap.check.model.check.MeasureCheckResult;
+import org.eclipse.daanse.olap.check.model.check.OlapCheckFactory;
+
+/**
+ * Executor for MeasureCheck that verifies measure existence and attributes.
+ */
+public class MeasureCheckExecutor {
+
+ private final MeasureCheck check;
+ private final List measures;
+ private final Cube cube;
+ private final CatalogReader catalogReader;
+ private final Connection connection;
+ private final OlapCheckFactory factory;
+
+ public MeasureCheckExecutor(MeasureCheck check, List measures, Cube cube,
+ CatalogReader catalogReader, Connection connection, OlapCheckFactory factory) {
+ this.check = check;
+ this.measures = measures;
+ this.cube = cube;
+ this.catalogReader = catalogReader;
+ this.connection = connection;
+ this.factory = factory;
+ }
+
+ public MeasureCheckResult execute() {
+ long startTime = System.currentTimeMillis();
+ Date start = new Date();
+
+ MeasureCheckResult result = factory.createMeasureCheckResult();
+ result.setCheckName(check.getName());
+ result.setCheckDescription(check.getDescription());
+ result.setMeasureName(check.getMeasureName());
+ result.setStartTime(start);
+ result.setSourceCheck(check);
+
+ try {
+ // Find the measure
+ Optional foundMeasure = findMeasure();
+
+ if (foundMeasure.isEmpty()) {
+ result.setStatus(CheckStatus.FAILURE);
+ result.setEndTime(new Date());
+ result.setExecutionTimeMs(System.currentTimeMillis() - startTime);
+ return result;
+ }
+
+ Member measure = foundMeasure.get();
+ result.setMeasureUniqueName(measure.getUniqueName());
+ result.setStatus(CheckStatus.SUCCESS);
+
+ // Execute attribute checks
+ for (MeasureAttributeCheck attrCheck : check.getMeasureAttributeChecks()) {
+ AttributeCheckResult attrResult = executeAttributeCheck(attrCheck, measure);
+ result.getAttributeResults().add(attrResult);
+ if (attrResult.getStatus() == CheckStatus.FAILURE) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+ }
+
+ } catch (Exception e) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+
+ result.setEndTime(new Date());
+ result.setExecutionTimeMs(System.currentTimeMillis() - startTime);
+
+ return result;
+ }
+
+ private Optional findMeasure() {
+ String measureName = check.getMeasureName();
+ String measureUniqueName = check.getMeasureUniqueName();
+
+ return measures.stream()
+ .filter(m -> {
+ if (measureUniqueName != null && !measureUniqueName.isEmpty()) {
+ return measureUniqueName.equals(m.getUniqueName());
+ }
+ return measureName != null && measureName.equals(m.getName());
+ })
+ .findFirst();
+ }
+
+ private AttributeCheckResult executeAttributeCheck(MeasureAttributeCheck attrCheck, Member measure) {
+ AttributeCheckResult result = factory.createAttributeCheckResult();
+ result.setCheckName(attrCheck.getName());
+ result.setAttributeName(attrCheck.getAttributeType().getName());
+ result.setExpectedValue(attrCheck.getExpectedValue());
+
+ String actualValue = getMeasureAttributeValue(measure, attrCheck.getAttributeType());
+ result.setActualValue(actualValue);
+
+ boolean matches;
+ if (attrCheck.getAttributeType() == MeasureAttribute.VISIBLE) {
+ Boolean expectedBool = attrCheck.getExpectedBoolean();
+ Boolean actualBool = measure.isVisible();
+ matches = AttributeCheckHelper.compareBooleans(expectedBool, actualBool);
+ } else {
+ matches = AttributeCheckHelper.compareValues(
+ attrCheck.getExpectedValue(),
+ actualValue,
+ attrCheck.getMatchMode(),
+ attrCheck.isCaseSensitive()
+ );
+ }
+
+ result.setStatus(matches ? CheckStatus.SUCCESS : CheckStatus.FAILURE);
+ return result;
+ }
+
+ private String getMeasureAttributeValue(Member measure, MeasureAttribute attributeType) {
+ return switch (attributeType) {
+ case NAME -> measure.getName();
+ case UNIQUE_NAME -> measure.getUniqueName();
+ case CAPTION -> measure.getCaption();
+ case DESCRIPTION -> measure.getDescription();
+ case VISIBLE -> String.valueOf(measure.isVisible());
+ case AGGREGATOR -> getPropertyString(measure, "AGGREGATOR");
+ case DATA_TYPE -> getPropertyString(measure, "DATA_TYPE");
+ case EXPRESSION -> getPropertyString(measure, "EXPRESSION");
+ case FORMAT_STRING -> getPropertyString(measure, "FORMAT_STRING");
+ case MEASURE_GROUP_NAME -> getPropertyString(measure, "MEASUREGROUP_NAME");
+ };
+ }
+
+ private String getPropertyString(Member member, String propertyName) {
+ Object value = member.getPropertyValue(propertyName);
+ return value != null ? value.toString() : null;
+ }
+}
diff --git a/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/MemberCheckExecutor.java b/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/MemberCheckExecutor.java
new file mode 100644
index 0000000..99e0972
--- /dev/null
+++ b/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/MemberCheckExecutor.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2024 Contributors to the Eclipse Foundation.
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.daanse.olap.check.runtime.impl.executors;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+
+import org.eclipse.daanse.olap.api.CatalogReader;
+import org.eclipse.daanse.olap.api.connection.Connection;
+import org.eclipse.daanse.olap.api.element.Cube;
+import org.eclipse.daanse.olap.api.element.Member;
+import org.eclipse.daanse.olap.check.model.check.AttributeCheckResult;
+import org.eclipse.daanse.olap.check.model.check.CheckStatus;
+import org.eclipse.daanse.olap.check.model.check.MemberAttribute;
+import org.eclipse.daanse.olap.check.model.check.MemberAttributeCheck;
+import org.eclipse.daanse.olap.check.model.check.MemberCheck;
+import org.eclipse.daanse.olap.check.model.check.MemberCheckResult;
+import org.eclipse.daanse.olap.check.model.check.OlapCheckFactory;
+import org.eclipse.daanse.olap.check.model.check.PropertyCheck;
+import org.eclipse.daanse.olap.check.model.check.PropertyCheckResult;
+
+/**
+ * Executor for MemberCheck that verifies member existence and properties.
+ */
+public class MemberCheckExecutor {
+
+ private final MemberCheck check;
+ private final List members;
+ private final Cube cube;
+ private final CatalogReader catalogReader;
+ private final Connection connection;
+ private final OlapCheckFactory factory;
+
+ public MemberCheckExecutor(MemberCheck check, List members, Cube cube,
+ CatalogReader catalogReader, Connection connection, OlapCheckFactory factory) {
+ this.check = check;
+ this.members = members;
+ this.cube = cube;
+ this.catalogReader = catalogReader;
+ this.connection = connection;
+ this.factory = factory;
+ }
+
+ public MemberCheckResult execute() {
+ long startTime = System.currentTimeMillis();
+ Date start = new Date();
+
+ MemberCheckResult result = factory.createMemberCheckResult();
+ result.setCheckName(check.getName());
+ result.setCheckDescription(check.getDescription());
+ result.setMemberName(check.getMemberName());
+ result.setStartTime(start);
+ result.setSourceCheck(check);
+
+ try {
+ // Find the member
+ Optional foundMember = findMember();
+
+ if (foundMember.isEmpty()) {
+ result.setStatus(CheckStatus.FAILURE);
+ result.setEndTime(new Date());
+ result.setExecutionTimeMs(System.currentTimeMillis() - startTime);
+ return result;
+ }
+
+ Member member = foundMember.get();
+ result.setMemberUniqueName(member.getUniqueName());
+ result.setStatus(CheckStatus.SUCCESS);
+
+ // Execute attribute checks
+ for (MemberAttributeCheck attrCheck : check.getMemberAttributeChecks()) {
+ AttributeCheckResult attrResult = executeAttributeCheck(attrCheck, member);
+ result.getAttributeResults().add(attrResult);
+ if (attrResult.getStatus() == CheckStatus.FAILURE) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+ }
+
+ // Execute property checks
+ for (PropertyCheck propertyCheck : check.getPropertyChecks()) {
+ if (!propertyCheck.isEnabled()) {
+ continue;
+ }
+
+ PropertyCheckResult propResult = executePropertyCheck(propertyCheck, member);
+ result.getPropertyResults().add(propResult);
+
+ if (propResult.getStatus() == CheckStatus.FAILURE) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+ }
+
+ } catch (Exception e) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+
+ result.setEndTime(new Date());
+ result.setExecutionTimeMs(System.currentTimeMillis() - startTime);
+
+ return result;
+ }
+
+ private Optional findMember() {
+ String memberName = check.getMemberName();
+ String memberUniqueName = check.getMemberUniqueName();
+
+ return members.stream()
+ .filter(m -> {
+ if (memberUniqueName != null && !memberUniqueName.isEmpty()) {
+ return memberUniqueName.equals(m.getUniqueName());
+ }
+ return memberName != null && memberName.equals(m.getName());
+ })
+ .findFirst();
+ }
+
+ private AttributeCheckResult executeAttributeCheck(MemberAttributeCheck attrCheck, Member member) {
+ AttributeCheckResult result = factory.createAttributeCheckResult();
+ result.setCheckName(attrCheck.getName());
+ result.setAttributeName(attrCheck.getAttributeType().getName());
+ result.setExpectedValue(attrCheck.getExpectedValue());
+
+ String actualValue = getMemberAttributeValue(member, attrCheck.getAttributeType());
+ result.setActualValue(actualValue);
+
+ boolean matches;
+ if (attrCheck.getAttributeType() == MemberAttribute.VISIBLE ||
+ attrCheck.getAttributeType() == MemberAttribute.IS_CALCULATED ||
+ attrCheck.getAttributeType() == MemberAttribute.IS_DATA_MEMBER) {
+ Boolean expectedBool = attrCheck.getExpectedBoolean();
+ Boolean actualBool = getMemberBooleanAttribute(member, attrCheck.getAttributeType());
+ matches = AttributeCheckHelper.compareBooleans(expectedBool, actualBool);
+ } else if (attrCheck.getAttributeType() == MemberAttribute.ORDINAL ||
+ attrCheck.getAttributeType() == MemberAttribute.DEPTH ||
+ attrCheck.getAttributeType() == MemberAttribute.PARENT_COUNT ||
+ attrCheck.getAttributeType() == MemberAttribute.CHILDREN_CARDINALITY) {
+ Integer expectedInt = attrCheck.getExpectedInt();
+ Integer actualInt = getMemberIntAttribute(member, attrCheck.getAttributeType());
+ matches = AttributeCheckHelper.compareInts(expectedInt, actualInt);
+ } else {
+ matches = AttributeCheckHelper.compareValues(
+ attrCheck.getExpectedValue(),
+ actualValue,
+ attrCheck.getMatchMode(),
+ attrCheck.isCaseSensitive()
+ );
+ }
+
+ result.setStatus(matches ? CheckStatus.SUCCESS : CheckStatus.FAILURE);
+ return result;
+ }
+
+ private String getMemberAttributeValue(Member member, MemberAttribute attributeType) {
+ return switch (attributeType) {
+ case NAME -> member.getName();
+ case UNIQUE_NAME -> member.getUniqueName();
+ case CAPTION -> member.getCaption();
+ case DESCRIPTION -> member.getDescription();
+ case VISIBLE -> String.valueOf(member.isVisible());
+ case MEMBER_TYPE -> member.getMemberType() != null ? member.getMemberType().name() : null;
+ case IS_CALCULATED -> String.valueOf(member.isCalculated());
+ case IS_DATA_MEMBER -> String.valueOf(member.getDataMember() != null && member.getDataMember() != member);
+ case PARENT_UNIQUE_NAME -> {
+ Member parent = member.getParentMember();
+ yield parent != null ? parent.getUniqueName() : null;
+ }
+ case PARENT_COUNT -> "1"; // Typically 1 for non-ragged hierarchies
+ case CHILDREN_CARDINALITY -> "0"; // Would need to calculate
+ case ORDINAL -> String.valueOf(member.getOrdinal());
+ case DEPTH -> String.valueOf(member.getDepth());
+ };
+ }
+
+ private Boolean getMemberBooleanAttribute(Member member, MemberAttribute attributeType) {
+ return switch (attributeType) {
+ case VISIBLE -> member.isVisible();
+ case IS_CALCULATED -> member.isCalculated();
+ case IS_DATA_MEMBER -> member.getDataMember() != null && member.getDataMember() != member;
+ default -> null;
+ };
+ }
+
+ private Integer getMemberIntAttribute(Member member, MemberAttribute attributeType) {
+ return switch (attributeType) {
+ case ORDINAL -> member.getOrdinal();
+ case DEPTH -> member.getDepth();
+ case PARENT_COUNT -> 1;
+ case CHILDREN_CARDINALITY -> 0;
+ default -> null;
+ };
+ }
+
+ private PropertyCheckResult executePropertyCheck(PropertyCheck propertyCheck, Member member) {
+ long startTime = System.currentTimeMillis();
+ Date start = new Date();
+
+ PropertyCheckResult result = factory.createPropertyCheckResult();
+ result.setCheckName(propertyCheck.getName());
+ result.setCheckDescription(propertyCheck.getDescription());
+ result.setPropertyName(propertyCheck.getPropertyName());
+ result.setStartTime(start);
+ result.setSourceCheck(propertyCheck);
+
+ try {
+ // Get property value from member
+ Object actualValueObj = member.getPropertyValue(propertyCheck.getPropertyName());
+ String actualValue = actualValueObj != null ? actualValueObj.toString() : null;
+ result.setActualValue(actualValue);
+
+ // Compare with expected value
+ String expectedValue = propertyCheck.getExpectedValue();
+ result.setExpectedValue(expectedValue);
+
+ boolean matches = AttributeCheckHelper.compareValues(
+ expectedValue,
+ actualValue,
+ propertyCheck.getMatchMode(),
+ propertyCheck.isCaseSensitive()
+ );
+ result.setStatus(matches ? CheckStatus.SUCCESS : CheckStatus.FAILURE);
+
+ } catch (Exception e) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+
+ result.setEndTime(new Date());
+ result.setExecutionTimeMs(System.currentTimeMillis() - startTime);
+
+ return result;
+ }
+}
diff --git a/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/NamedSetCheckExecutor.java b/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/NamedSetCheckExecutor.java
new file mode 100644
index 0000000..5356c6e
--- /dev/null
+++ b/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/NamedSetCheckExecutor.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2024 Contributors to the Eclipse Foundation.
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.daanse.olap.check.runtime.impl.executors;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+
+import org.eclipse.daanse.olap.api.element.NamedSet;
+import org.eclipse.daanse.olap.check.model.check.AttributeCheckResult;
+import org.eclipse.daanse.olap.check.model.check.CheckStatus;
+import org.eclipse.daanse.olap.check.model.check.NamedSetAttribute;
+import org.eclipse.daanse.olap.check.model.check.NamedSetAttributeCheck;
+import org.eclipse.daanse.olap.check.model.check.NamedSetCheck;
+import org.eclipse.daanse.olap.check.model.check.NamedSetCheckResult;
+import org.eclipse.daanse.olap.check.model.check.OlapCheckFactory;
+
+/**
+ * Executor for NamedSetCheck that verifies named set existence and attributes.
+ */
+public class NamedSetCheckExecutor {
+
+ private final NamedSetCheck check;
+ private final List extends NamedSet> namedSets;
+ private final OlapCheckFactory factory;
+
+ public NamedSetCheckExecutor(NamedSetCheck check, List extends NamedSet> namedSets, OlapCheckFactory factory) {
+ this.check = check;
+ this.namedSets = namedSets;
+ this.factory = factory;
+ }
+
+ public NamedSetCheckResult execute() {
+ long startTime = System.currentTimeMillis();
+ Date start = new Date();
+
+ NamedSetCheckResult result = factory.createNamedSetCheckResult();
+ result.setCheckName(check.getName());
+ result.setCheckDescription(check.getDescription());
+ result.setNamedSetName(check.getNamedSetName());
+ result.setStartTime(start);
+ result.setSourceCheck(check);
+
+ try {
+ // Find the NamedSet
+ Optional extends NamedSet> foundNamedSet = findNamedSet();
+
+ if (foundNamedSet.isEmpty()) {
+ result.setStatus(CheckStatus.FAILURE);
+ result.setEndTime(new Date());
+ result.setExecutionTimeMs(System.currentTimeMillis() - startTime);
+ return result;
+ }
+
+ NamedSet namedSet = foundNamedSet.get();
+ result.setStatus(CheckStatus.SUCCESS);
+
+ // Execute attribute checks
+ for (NamedSetAttributeCheck attrCheck : check.getNamedSetAttributeChecks()) {
+ AttributeCheckResult attrResult = executeAttributeCheck(attrCheck, namedSet);
+ result.getAttributeResults().add(attrResult);
+ if (attrResult.getStatus() == CheckStatus.FAILURE) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+ }
+
+ } catch (Exception e) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+
+ result.setEndTime(new Date());
+ result.setExecutionTimeMs(System.currentTimeMillis() - startTime);
+
+ return result;
+ }
+
+ private Optional extends NamedSet> findNamedSet() {
+ String namedSetName = check.getNamedSetName();
+
+ return namedSets.stream()
+ .filter(ns -> namedSetName != null && namedSetName.equals(ns.getName()))
+ .findFirst();
+ }
+
+ private AttributeCheckResult executeAttributeCheck(NamedSetAttributeCheck attrCheck, NamedSet namedSet) {
+ AttributeCheckResult result = factory.createAttributeCheckResult();
+ result.setCheckName(attrCheck.getName());
+ result.setAttributeName(attrCheck.getAttributeType().getName());
+ result.setExpectedValue(attrCheck.getExpectedValue());
+
+ String actualValue = getNamedSetAttributeValue(namedSet, attrCheck.getAttributeType());
+ result.setActualValue(actualValue);
+
+ boolean matches;
+ if (attrCheck.getAttributeType() == NamedSetAttribute.IS_DYNAMIC) {
+ Boolean expectedBool = attrCheck.getExpectedBoolean();
+ Boolean actualBool = namedSet.isDynamic();
+ matches = AttributeCheckHelper.compareBooleans(expectedBool, actualBool);
+ } else {
+ matches = AttributeCheckHelper.compareValues(
+ attrCheck.getExpectedValue(),
+ actualValue,
+ attrCheck.getMatchMode(),
+ attrCheck.isCaseSensitive()
+ );
+ }
+
+ result.setStatus(matches ? CheckStatus.SUCCESS : CheckStatus.FAILURE);
+ return result;
+ }
+
+ private String getNamedSetAttributeValue(NamedSet namedSet, NamedSetAttribute attributeType) {
+ return switch (attributeType) {
+ case NAME -> namedSet.getName();
+ case CAPTION -> namedSet.getCaption();
+ case DESCRIPTION -> namedSet.getDescription();
+ case DISPLAY_FOLDER -> namedSet.getDisplayFolder();
+ case IS_DYNAMIC -> String.valueOf(namedSet.isDynamic());
+ case EXPRESSION -> namedSet.getExp() != null ? namedSet.getExp().toString() : null;
+ };
+ }
+}
diff --git a/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/QueryCheckExecutor.java b/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/QueryCheckExecutor.java
new file mode 100644
index 0000000..2a55a95
--- /dev/null
+++ b/check/runtime/src/main/java/org/eclipse/daanse/olap/check/runtime/impl/executors/QueryCheckExecutor.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2024 Contributors to the Eclipse Foundation.
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.daanse.olap.check.runtime.impl.executors;
+
+import java.util.Date;
+
+import org.eclipse.daanse.olap.api.connection.Connection;
+import org.eclipse.daanse.olap.api.result.Axis;
+import org.eclipse.daanse.olap.api.result.Cell;
+import org.eclipse.daanse.olap.api.result.Result;
+import org.eclipse.daanse.olap.check.model.check.CellCheckResult;
+import org.eclipse.daanse.olap.check.model.check.CellValueCheck;
+import org.eclipse.daanse.olap.check.model.check.CheckStatus;
+import org.eclipse.daanse.olap.check.model.check.OlapCheckFactory;
+import org.eclipse.daanse.olap.check.model.check.QueryCheck;
+import org.eclipse.daanse.olap.check.model.check.QueryCheckResult;
+import org.eclipse.daanse.olap.check.model.check.QueryLanguage;
+import org.eclipse.emf.common.util.EList;
+
+/**
+ * Executor for QueryCheck that verifies query execution and results.
+ * Supports MDX, SQL, and DAX query languages.
+ */
+public class QueryCheckExecutor {
+
+ private final QueryCheck check;
+ private final Connection connection;
+ private final OlapCheckFactory factory;
+
+ public QueryCheckExecutor(QueryCheck check, Connection connection, OlapCheckFactory factory) {
+ this.check = check;
+ this.connection = connection;
+ this.factory = factory;
+ }
+
+ public QueryCheckResult execute() {
+ long startTime = System.currentTimeMillis();
+ Date start = new Date();
+
+ QueryCheckResult result = factory.createQueryCheckResult();
+ result.setCheckName(check.getName());
+ result.setCheckDescription(check.getDescription());
+ result.setQuery(check.getQuery());
+ result.setQueryLanguage(check.getQueryLanguage());
+ result.setStartTime(start);
+ result.setSourceCheck(check);
+
+ try {
+ QueryLanguage language = check.getQueryLanguage();
+ if (language == null) {
+ language = QueryLanguage.MDX; // Default to MDX
+ }
+
+ switch (language) {
+ case MDX:
+ executeMdxQuery(result, startTime);
+ break;
+ case SQL:
+ executeSqlQuery(result, startTime);
+ break;
+ case DAX:
+ executeDaxQuery(result, startTime);
+ break;
+ default:
+ result.setExecutedSuccessfully(false);
+ result.setStatus(CheckStatus.FAILURE);
+ }
+
+ } catch (Exception e) {
+ result.setExecutedSuccessfully(false);
+ result.setStatus(CheckStatus.FAILURE);
+ }
+
+ result.setEndTime(new Date());
+ result.setExecutionTimeMs(System.currentTimeMillis() - startTime);
+
+ return result;
+ }
+
+ private void executeMdxQuery(QueryCheckResult result, long startTime) {
+ try {
+ // Execute the MDX query
+ Result mdxResult = connection.execute(
+ connection.parseQuery(check.getQuery())
+ );
+
+ result.setExecutedSuccessfully(true);
+
+ // Get axes for counting rows/columns
+ Axis[] axes = mdxResult.getAxes();
+ int rowCount = 0;
+ int columnCount = 0;
+
+ if (axes.length > 0) {
+ columnCount = axes[0].getPositions().size();
+ }
+ if (axes.length > 1) {
+ rowCount = axes[1].getPositions().size();
+ }
+
+ result.setRowCount(rowCount);
+ result.setColumnCount(columnCount);
+ result.setStatus(CheckStatus.SUCCESS);
+
+ // Check expected row count (-1 means not specified)
+ int expectedRowCount = check.getExpectedRowCount();
+ if (expectedRowCount >= 0 && rowCount != expectedRowCount) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+
+ // Check expected column count (-1 means not specified)
+ int expectedColumnCount = check.getExpectedColumnCount();
+ if (expectedColumnCount >= 0 && columnCount != expectedColumnCount) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+
+ // Check execution time (-1 means not specified)
+ long executionTime = System.currentTimeMillis() - startTime;
+ long maxExecutionTime = check.getMaxExecutionTimeMs();
+ if (maxExecutionTime > 0 && executionTime > maxExecutionTime) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+
+ // Execute cell value checks
+ for (CellValueCheck cellCheck : check.getCellChecks()) {
+ CellCheckResult cellResult = executeCellCheck(cellCheck, mdxResult);
+ result.getCellResults().add(cellResult);
+ if (cellResult.getStatus() == CheckStatus.FAILURE) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+ }
+
+ } catch (Exception e) {
+ result.setExecutedSuccessfully(false);
+ result.setStatus(CheckStatus.FAILURE);
+ }
+ }
+
+ private void executeSqlQuery(QueryCheckResult result, long startTime) {
+ // TODO: Implement SQL query execution
+ // SQL queries would typically use JDBC to execute against the underlying database
+ result.setExecutedSuccessfully(false);
+ result.setStatus(CheckStatus.FAILURE);
+ }
+
+ private void executeDaxQuery(QueryCheckResult result, long startTime) {
+ // TODO: Implement DAX query execution
+ // DAX queries would need to use appropriate DAX execution mechanism
+ result.setExecutedSuccessfully(false);
+ result.setStatus(CheckStatus.FAILURE);
+ }
+
+ private CellCheckResult executeCellCheck(CellValueCheck cellCheck, Result mdxResult) {
+ CellCheckResult result = factory.createCellCheckResult();
+ result.setCheckName(cellCheck.getName());
+
+ // Copy coordinates
+ EList coords = cellCheck.getCoordinates();
+ result.getCoordinates().addAll(coords);
+ result.setExpectedValue(cellCheck.getExpectedValue());
+
+ try {
+ // Convert EList to int[]
+ int[] coordArray = new int[coords.size()];
+ for (int i = 0; i < coords.size(); i++) {
+ coordArray[i] = coords.get(i);
+ }
+
+ Cell cell = mdxResult.getCell(coordArray);
+
+ Object cellValue = cell.getValue();
+ String actualValue = cellValue != null ? cellValue.toString() : null;
+
+ if (cellCheck.isCheckFormattedValue()) {
+ actualValue = cell.getFormattedValue();
+ }
+
+ result.setActualValue(actualValue);
+
+ // Compare values
+ boolean matches;
+ double expectedNumeric = cellCheck.getExpectedNumericValue();
+
+ // Check if numeric comparison is needed (non-zero expectedNumericValue indicates it's set)
+ if (expectedNumeric != 0.0 || cellCheck.getExpectedValue() == null) {
+ // Numeric comparison with tolerance
+ Double actual = cellValue instanceof Number ? ((Number) cellValue).doubleValue() : null;
+ double tolerance = cellCheck.getTolerance();
+
+ matches = actual != null && Math.abs(expectedNumeric - actual) <= tolerance;
+ } else {
+ // String comparison
+ matches = AttributeCheckHelper.compareValues(
+ cellCheck.getExpectedValue(),
+ actualValue,
+ cellCheck.getMatchMode(),
+ true // cell values are case-sensitive by default
+ );
+ }
+
+ result.setStatus(matches ? CheckStatus.SUCCESS : CheckStatus.FAILURE);
+
+ } catch (Exception e) {
+ result.setStatus(CheckStatus.FAILURE);
+ }
+
+ return result;
+ }
+}
diff --git a/pom.xml b/pom.xml
index cccc029..15e45ae 100644
--- a/pom.xml
+++ b/pom.xml
@@ -59,6 +59,7 @@
spi
format
odc
+ check