From d829dc4a13e7f44ffcccc608cddfef79423e6875 Mon Sep 17 00:00:00 2001
From: Lars Johansson <lars.johansson@ess.eu>
Date: Thu, 24 Feb 2022 14:22:59 +0100
Subject: [PATCH] Added REST API v1 endpoints for names and structures
 (implementation)

---
 .../repository/DeviceGroupRepository.java     |  319 ++++
 .../repository/DeviceTypeRepository.java      |  318 ++++
 .../repository/DisciplineRepository.java      |  315 ++++
 .../names/repository/NameRepository.java      |  297 +++
 .../names/repository/SubsystemRepository.java |  318 ++++
 .../repository/SystemGroupRepository.java     |  311 ++++
 .../names/repository/SystemRepository.java    |  319 ++++
 .../rest/controller/NamesController.java      |  375 ++++
 .../rest/controller/StructuresController.java |  504 ++++++
 .../openepics/names/service/NamesService.java |  692 +++++++
 .../names/service/StructuresService.java      | 1601 +++++++++++++++++
 .../names/util/EssNamingConvention.java       |  173 ++
 .../names/util/HolderRepositories.java        |  133 ++
 .../org/openepics/names/util/LogUtil.java     |  158 ++
 .../openepics/names/util/NameElementUtil.java |  276 +++
 .../names/util/NamingConvention.java          |  152 ++
 .../names/util/StructureElementUtil.java      |  800 ++++++++
 .../openepics/names/util/StructureUtil.java   |  263 +++
 .../openepics/names/util/ValidateUtil.java    | 1385 ++++++++++++++
 .../names/util/EssNamingConventionTest.java   |  988 ++++++++++
 .../names/util/NameElementUtilTest.java       |  101 ++
 .../names/util/StructureElementUtilTest.java  |   98 +
 .../names/util/ValidateUtilTest.java          |  407 +++++
 23 files changed, 10303 insertions(+)
 create mode 100644 src/main/java/org/openepics/names/repository/DeviceGroupRepository.java
 create mode 100644 src/main/java/org/openepics/names/repository/DeviceTypeRepository.java
 create mode 100644 src/main/java/org/openepics/names/repository/DisciplineRepository.java
 create mode 100644 src/main/java/org/openepics/names/repository/NameRepository.java
 create mode 100644 src/main/java/org/openepics/names/repository/SubsystemRepository.java
 create mode 100644 src/main/java/org/openepics/names/repository/SystemGroupRepository.java
 create mode 100644 src/main/java/org/openepics/names/repository/SystemRepository.java
 create mode 100644 src/main/java/org/openepics/names/rest/controller/NamesController.java
 create mode 100644 src/main/java/org/openepics/names/rest/controller/StructuresController.java
 create mode 100644 src/main/java/org/openepics/names/service/NamesService.java
 create mode 100644 src/main/java/org/openepics/names/service/StructuresService.java
 create mode 100644 src/main/java/org/openepics/names/util/EssNamingConvention.java
 create mode 100644 src/main/java/org/openepics/names/util/HolderRepositories.java
 create mode 100644 src/main/java/org/openepics/names/util/LogUtil.java
 create mode 100644 src/main/java/org/openepics/names/util/NameElementUtil.java
 create mode 100644 src/main/java/org/openepics/names/util/NamingConvention.java
 create mode 100644 src/main/java/org/openepics/names/util/StructureElementUtil.java
 create mode 100644 src/main/java/org/openepics/names/util/StructureUtil.java
 create mode 100644 src/main/java/org/openepics/names/util/ValidateUtil.java
 create mode 100644 src/test/java/org/openepics/names/util/EssNamingConventionTest.java
 create mode 100644 src/test/java/org/openepics/names/util/NameElementUtilTest.java
 create mode 100644 src/test/java/org/openepics/names/util/StructureElementUtilTest.java
 create mode 100644 src/test/java/org/openepics/names/util/ValidateUtilTest.java

diff --git a/src/main/java/org/openepics/names/repository/DeviceGroupRepository.java b/src/main/java/org/openepics/names/repository/DeviceGroupRepository.java
new file mode 100644
index 0000000..342a06f
--- /dev/null
+++ b/src/main/java/org/openepics/names/repository/DeviceGroupRepository.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2021 European Spallation Source ERIC.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+package org.openepics.names.repository;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import javax.persistence.TypedQuery;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
+import org.apache.commons.lang3.BooleanUtils;
+import org.openepics.names.repository.model.DeviceGroup;
+import org.openepics.names.rest.beans.FieldStructure;
+import org.openepics.names.rest.beans.Status;
+import org.springframework.stereotype.Repository;
+
+/**
+ * Handle device group information in JPA.
+ *
+ * @author Lars Johansson
+ */
+@Repository
+public class DeviceGroupRepository {
+
+    private static final String PERCENT = "%";
+
+    @PersistenceContext
+    private EntityManager em;
+
+    /**
+     * Count device groups.
+     *
+     * @param statuses statuses
+     * @param deleted deleted
+     * @param queryFields query fields
+     * @param queryValues query values
+     * @return count of device groups
+     */
+    public Long countDeviceGroups(
+            Status[] statuses, Boolean deleted, FieldStructure[] queryFields, String[] queryValues) {
+        return countDeviceGroups(statuses, deleted, queryFields, queryValues, Boolean.FALSE);
+    }
+
+    /**
+     * Count device groups.
+     *
+     * @param statuses statuses
+     * @param deleted deleted
+     * @param queryFields query fields
+     * @param queryValues query values
+     * @param includeHistory include history
+     * @return count of device groups
+     */
+    public Long countDeviceGroups(
+            Status[] statuses, Boolean deleted, FieldStructure[] queryFields, String[] queryValues, Boolean includeHistory) {
+
+        // note
+        //     use of function for mnemonic path
+        // where
+        //     statuses
+        //     deleted
+        //     queryFields, queryValues
+
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+        Root<DeviceGroup> from = cq.from(DeviceGroup.class);
+
+        cq.where(cb.and(preparePredicatesDeviceGroups(cb, from, statuses, deleted, queryFields, queryValues, includeHistory).toArray(new Predicate[0])));
+        cq.select(cb.count(from));
+
+        return em.createQuery(cq).getSingleResult();
+    }
+
+    /**
+     * Find device groups.
+     *
+     * @param status status
+     * @param deleted deleted
+     * @param queryField query field
+     * @param queryValue query value
+     * @return list of device groups
+     */
+    public List<DeviceGroup> readDeviceGroups(
+            Status status, Boolean deleted, FieldStructure queryField, String queryValue) {
+
+        return readDeviceGroups(
+                status != null ? new Status[] {status} : null,
+                deleted,
+                queryField != null ? new FieldStructure[] {queryField} : null,
+                queryValue != null ? new String[] {queryValue} : null,
+                Boolean.FALSE,
+                null, null, null, null);
+    }
+
+    /**
+     * Find device groups.
+     *
+     * @param statuses statuses
+     * @param deleted deleted
+     * @param queryFields query fields
+     * @param queryValues query values
+     * @param orderBy order by
+     * @param isAsc is ascending
+     * @param offset offset
+     * @param limit limit
+     * @return list of device groups
+     */
+    public List<DeviceGroup> readDeviceGroups(
+            Status[] statuses, Boolean deleted, FieldStructure[] queryFields, String[] queryValues,
+            FieldStructure orderBy, Boolean isAsc, Integer offset, Integer limit) {
+        return readDeviceGroups(
+                statuses, deleted, queryFields, queryValues, Boolean.FALSE,
+                orderBy, isAsc, offset, limit);
+    }
+
+    /**
+     * Find device groups.
+     *
+     * @param statuses statuses
+     * @param deleted deleted
+     * @param queryFields query fields
+     * @param queryValues query values
+     * @param includeHistory include history
+     * @param orderBy order by
+     * @param isAsc is ascending
+     * @param offset offset
+     * @param limit limit
+     * @return list of device groups
+     */
+    public List<DeviceGroup> readDeviceGroups(
+            Status[] statuses, Boolean deleted, FieldStructure[] queryFields, String[] queryValues, Boolean includeHistory,
+            FieldStructure orderBy, Boolean isAsc, Integer offset, Integer limit) {
+
+        // note
+        //     use of function for mnemonic path
+        // where
+        //     statuses
+        //     deleted
+        //     queryFields, queryValues
+        // order
+        //     orderBy, isAsc
+        // paging
+        //     offset, limit
+
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<DeviceGroup> cq = cb.createQuery(DeviceGroup.class);
+        Root<DeviceGroup> from = cq.from(DeviceGroup.class);
+
+        cq.where(cb.and(preparePredicatesDeviceGroups(cb, from, statuses, deleted, queryFields, queryValues, includeHistory).toArray(new Predicate[0])));
+        cq.select(from);
+
+        if (orderBy != null) {
+            if (BooleanUtils.toBoolean(isAsc)) {
+                if (FieldStructure.NAME.equals(orderBy)) {
+                    cq.orderBy(cb.asc(from.get("name")));
+                } else if (FieldStructure.MNEMONIC.equals(orderBy)) {
+                    cq.orderBy(cb.asc(from.get("mnemonic")));
+                } else if (FieldStructure.MNEMONICEQUIVALENCE.equals(orderBy)) {
+                    cq.orderBy(cb.asc(from.get("mnemonic_equivalence")));
+                } else if (FieldStructure.MNEMONICPATH.equals(orderBy)) {
+                    cq.orderBy(cb.asc(cb.function("get_mnemonic_path_devicegroup", String.class, from.get("uuid"))));
+                } else if (FieldStructure.DESCRIPTION.equals(orderBy)) {
+                    cq.orderBy(cb.asc(from.get("description")));
+                } else {
+                    cq.orderBy(cb.asc(from.get("convention_name")));
+                }
+            } else {
+                if (FieldStructure.NAME.equals(orderBy)) {
+                    cq.orderBy(cb.desc(from.get("name")));
+                } else if (FieldStructure.MNEMONIC.equals(orderBy)) {
+                    cq.orderBy(cb.desc(from.get("mnemonic")));
+                } else if (FieldStructure.MNEMONICEQUIVALENCE.equals(orderBy)) {
+                    cq.orderBy(cb.desc(from.get("mnemonic_equivalence")));
+                } else if (FieldStructure.MNEMONICPATH.equals(orderBy)) {
+                    cq.orderBy(cb.desc(cb.function("get_mnemonic_path_devicegroup", String.class, from.get("uuid"))));
+                } else if (FieldStructure.DESCRIPTION.equals(orderBy)) {
+                    cq.orderBy(cb.desc(from.get("description")));
+                } else {
+                    cq.orderBy(cb.desc(from.get("name")));
+                }
+            }
+        }
+
+        TypedQuery<DeviceGroup> query = em.createQuery(cq);
+        if (offset != null && limit != null) {
+            query.setFirstResult(offset * limit);
+            query.setMaxResults(limit);
+        }
+
+        return query.getResultList();
+    }
+
+    /**
+     * Prepare predicates for device groups.
+     *
+     * @param cb criteria builder
+     * @param from criteria query root
+     * @param statuses statuses
+     * @param deleted deleted
+     * @param queryFields query fields
+     * @param queryValues query values
+     * @param includeHistory include history
+     * @return list of predicates
+     */
+    private List<Predicate> preparePredicatesDeviceGroups(
+            CriteriaBuilder cb, Root<DeviceGroup> from,
+            Status[] statuses, Boolean deleted, FieldStructure[] queryFields, String[] queryValues, Boolean includeHistory) {
+
+        List<Predicate> predicates = new ArrayList<>();
+
+        if (!Boolean.TRUE.equals(includeHistory)) {
+            // exclude (approved and not latest)
+            Predicate predicateApproved  = cb.equal(from.get("status"), Status.APPROVED);
+            Predicate predicateNotLatest = cb.equal(from.get("latest"), Boolean.FALSE);
+            Predicate predicateExclude   = cb.not(cb.and(predicateApproved, predicateNotLatest));
+            predicates.add(predicateExclude);
+
+        }
+
+        if (statuses != null) {
+            List<Predicate> predicatesStatus = new ArrayList<>();
+            for (Status status : statuses) {
+                predicatesStatus.add(cb.equal(from.get("status"), status));
+            }
+            predicates.add(cb.or((Predicate[]) predicatesStatus.toArray(new Predicate[0])));
+        }
+        if (deleted != null) {
+            predicates.add(cb.equal(from.get("deleted"), deleted));
+        }
+        if (queryFields != null) {
+            for (int i=0; i<queryFields.length; i++) {
+                String queryValue = queryValues[i];
+
+                // jpa query characters % and _
+                // remove excess % characters
+                if (queryValue.startsWith(PERCENT)) {
+                    while (queryValue.startsWith(PERCENT)) {
+                        queryValue = queryValue.substring(1);
+                    }
+                    queryValue = PERCENT + queryValue;
+                }
+                if (queryValue.endsWith(PERCENT)) {
+                    while (queryValue.endsWith(PERCENT)) {
+                        queryValue = queryValue.substring(0, queryValue.length()-1);
+                    }
+                    queryValue = queryValue + PERCENT;
+                }
+
+                switch (queryFields[i]) {
+                    case UUID:
+                        predicates.add(cb.and(cb.equal(from.get("uuid"), queryValue)));
+                        break;
+                    case PARENT:
+                        predicates.add(cb.and(cb.equal(from.get("parent_uuid"), queryValue)));
+                        break;
+                    case NAME:
+                        predicates.add(cb.and(cb.like(from.get("name"), queryValue)));
+                        break;
+                    case MNEMONIC:
+                        predicates.add(cb.and(cb.like(from.get("mnemonic"), queryValue)));
+                        break;
+                    case MNEMONICEQUIVALENCE:
+                        predicates.add(cb.and(cb.like(from.get("mnemonic_equivalence"), queryValue)));
+                        break;
+                    case MNEMONICPATH:
+                        predicates.add(cb.and(cb.like(cb.function("get_mnemonic_path_devicegroup", String.class, from.get("uuid")), queryValue)));
+                        break;
+                    case DESCRIPTION:
+                        predicates.add(cb.and(cb.like(from.get("description"), queryValue)));
+                        break;
+                    default:
+                        continue;
+                }
+            }
+        }
+
+        return predicates;
+    }
+
+    /**
+     * Persist device group into persistence context.
+     *
+     * @param deviceGroup device group
+     */
+    public void createDeviceGroup(DeviceGroup deviceGroup) {
+        em.persist(deviceGroup);
+    }
+
+    /**
+     * Merge device group into persistence context.
+     *
+     * @param deviceGroup device group
+     */
+    public void updateDeviceGroup(DeviceGroup deviceGroup) {
+        em.merge(deviceGroup);
+    }
+
+}
diff --git a/src/main/java/org/openepics/names/repository/DeviceTypeRepository.java b/src/main/java/org/openepics/names/repository/DeviceTypeRepository.java
new file mode 100644
index 0000000..f06a8e1
--- /dev/null
+++ b/src/main/java/org/openepics/names/repository/DeviceTypeRepository.java
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2021 European Spallation Source ERIC.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+package org.openepics.names.repository;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import javax.persistence.TypedQuery;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
+import org.apache.commons.lang3.BooleanUtils;
+import org.openepics.names.repository.model.DeviceType;
+import org.openepics.names.rest.beans.FieldStructure;
+import org.openepics.names.rest.beans.Status;
+import org.springframework.stereotype.Repository;
+
+/**
+ * Handle device type information in JPA.
+ *
+ * @author Lars Johansson
+ */
+@Repository
+public class DeviceTypeRepository {
+
+    private static final String PERCENT = "%";
+
+    @PersistenceContext
+    private EntityManager em;
+
+    /**
+     * Count device types.
+     *
+     * @param statuses statuses
+     * @param deleted deleted
+     * @param queryFields query fields
+     * @param queryValues query values
+     * @return count of device types
+     */
+    public Long countDeviceTypes(
+            Status[] statuses, Boolean deleted, FieldStructure[] queryFields, String[] queryValues) {
+        return countDeviceTypes(statuses, deleted, queryFields, queryValues, Boolean.FALSE);
+    }
+
+    /**
+     * Count device types.
+     *
+     * @param statuses statuses
+     * @param deleted deleted
+     * @param queryFields query fields
+     * @param queryValues query values
+     * @param includeHistory include history
+     * @return count of device types
+     */
+    public Long countDeviceTypes(
+            Status[] statuses, Boolean deleted, FieldStructure[] queryFields, String[] queryValues, Boolean includeHistory) {
+
+        // note
+        //     use of function for mnemonic path
+        // where
+        //     statuses
+        //     deleted
+        //     queryFields, queryValues
+
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+        Root<DeviceType> from = cq.from(DeviceType.class);
+
+        cq.where(cb.and(preparePredicatesDeviceTypes(cb, from, statuses, deleted, queryFields, queryValues, includeHistory).toArray(new Predicate[0])));
+        cq.select(cb.count(from));
+
+        return em.createQuery(cq).getSingleResult();
+    }
+
+    /**
+     * Find device types.
+     *
+     * @param status status
+     * @param deleted deleted
+     * @param queryField query field
+     * @param queryValue query value
+     * @return list of device types
+     */
+    public List<DeviceType> readDeviceTypes(
+            Status status, Boolean deleted, FieldStructure queryField, String queryValue) {
+
+        return readDeviceTypes(
+                status != null ? new Status[] {status} : null,
+                deleted,
+                queryField != null ? new FieldStructure[] {queryField} : null,
+                queryValue != null ? new String[] {queryValue} : null,
+                Boolean.FALSE,
+                null, null, null, null);
+    }
+
+    /**
+     * Find device types.
+     *
+     * @param statuses statuses
+     * @param deleted deleted
+     * @param queryFields query fields
+     * @param queryValues query values
+     * @param orderBy order by
+     * @param isAsc is ascending
+     * @param offset offset
+     * @param limit limit
+     * @return list of device types
+     */
+    public List<DeviceType> readDeviceTypes(
+            Status[] statuses, Boolean deleted, FieldStructure[] queryFields, String[] queryValues,
+            FieldStructure orderBy, Boolean isAsc, Integer offset, Integer limit) {
+        return readDeviceTypes(
+                statuses, deleted, queryFields, queryValues, Boolean.FALSE,
+                orderBy, isAsc, offset, limit);
+    }
+
+    /**
+     * Find device types.
+     *
+     * @param statuses statuses
+     * @param deleted deleted
+     * @param queryFields query fields
+     * @param queryValues query values
+     * @param includeHistory include history
+     * @param orderBy order by
+     * @param isAsc is ascending
+     * @param offset offset
+     * @param limit limit
+     * @return list of device types
+     */
+    public List<DeviceType> readDeviceTypes(
+            Status[] statuses, Boolean deleted, FieldStructure[] queryFields, String[] queryValues, Boolean includeHistory,
+            FieldStructure orderBy, Boolean isAsc, Integer offset, Integer limit) {
+
+        // note
+        //     use of function for mnemonic path
+        // where
+        //     statuses
+        //     deleted
+        //     queryFields, queryValues
+        // order
+        //     orderBy, isAsc
+        // paging
+        //     offset, limit
+
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<DeviceType> cq = cb.createQuery(DeviceType.class);
+        Root<DeviceType> from = cq.from(DeviceType.class);
+
+        cq.where(cb.and(preparePredicatesDeviceTypes(cb, from, statuses, deleted, queryFields, queryValues, includeHistory).toArray(new Predicate[0])));
+        cq.select(from);
+
+        if (orderBy != null) {
+            if (BooleanUtils.toBoolean(isAsc)) {
+                if (FieldStructure.NAME.equals(orderBy)) {
+                    cq.orderBy(cb.asc(from.get("name")));
+                } else if (FieldStructure.MNEMONIC.equals(orderBy)) {
+                    cq.orderBy(cb.asc(from.get("mnemonic")));
+                } else if (FieldStructure.MNEMONICEQUIVALENCE.equals(orderBy)) {
+                    cq.orderBy(cb.asc(from.get("mnemonic_equivalence")));
+                } else if (FieldStructure.MNEMONICPATH.equals(orderBy)) {
+                    cq.orderBy(cb.asc(cb.function("get_mnemonic_path_devicetype", String.class, from.get("uuid"))));
+                } else if (FieldStructure.DESCRIPTION.equals(orderBy)) {
+                    cq.orderBy(cb.asc(from.get("description")));
+                } else {
+                    cq.orderBy(cb.asc(from.get("convention_name")));
+                }
+            } else {
+                if (FieldStructure.NAME.equals(orderBy)) {
+                    cq.orderBy(cb.desc(from.get("name")));
+                } else if (FieldStructure.MNEMONIC.equals(orderBy)) {
+                    cq.orderBy(cb.desc(from.get("mnemonic")));
+                } else if (FieldStructure.MNEMONICEQUIVALENCE.equals(orderBy)) {
+                    cq.orderBy(cb.desc(from.get("mnemonic_equivalence")));
+                } else if (FieldStructure.MNEMONICPATH.equals(orderBy)) {
+                    cq.orderBy(cb.desc(cb.function("get_mnemonic_path_devicetype", String.class, from.get("uuid"))));
+                } else if (FieldStructure.DESCRIPTION.equals(orderBy)) {
+                    cq.orderBy(cb.desc(from.get("description")));
+                } else {
+                    cq.orderBy(cb.desc(from.get("name")));
+                }
+            }
+        }
+
+        TypedQuery<DeviceType> query = em.createQuery(cq);
+        if (offset != null && limit != null) {
+            query.setFirstResult(offset * limit);
+            query.setMaxResults(limit);
+        }
+
+        return query.getResultList();
+    }
+
+    /**
+     * Prepare predicates for device types.
+     *
+     * @param cb criteria builder
+     * @param from criteria query root
+     * @param statuses statuses
+     * @param deleted deleted
+     * @param queryFields query fields
+     * @param queryValues query values
+     * @param includeHistory include history
+     * @return list of predicates
+     */
+    private List<Predicate> preparePredicatesDeviceTypes(
+            CriteriaBuilder cb, Root<DeviceType> from,
+            Status[] statuses, Boolean deleted, FieldStructure[] queryFields, String[] queryValues, Boolean includeHistory) {
+
+        List<Predicate> predicates = new ArrayList<>();
+
+        if (!Boolean.TRUE.equals(includeHistory)) {
+            // exclude (approved and not latest)
+            Predicate predicateApproved  = cb.equal(from.get("status"), Status.APPROVED);
+            Predicate predicateNotLatest = cb.equal(from.get("latest"), Boolean.FALSE);
+            Predicate predicateExclude   = cb.not(cb.and(predicateApproved, predicateNotLatest));
+            predicates.add(predicateExclude);
+        }
+
+        if (statuses != null) {
+            List<Predicate> predicatesStatus = new ArrayList<>();
+            for (Status status : statuses) {
+                predicatesStatus.add(cb.equal(from.get("status"), status));
+            }
+            predicates.add(cb.or((Predicate[]) predicatesStatus.toArray(new Predicate[0])));
+        }
+        if (deleted != null) {
+            predicates.add(cb.equal(from.get("deleted"), deleted));
+        }
+        if (queryFields != null) {
+            for (int i=0; i<queryFields.length; i++) {
+                String queryValue = queryValues[i];
+
+                // jpa query characters % and _
+                // remove excess % characters
+                if (queryValue.startsWith(PERCENT)) {
+                    while (queryValue.startsWith(PERCENT)) {
+                        queryValue = queryValue.substring(1);
+                    }
+                    queryValue = PERCENT + queryValue;
+                }
+                if (queryValue.endsWith(PERCENT)) {
+                    while (queryValue.endsWith(PERCENT)) {
+                        queryValue = queryValue.substring(0, queryValue.length()-1);
+                    }
+                    queryValue = queryValue + PERCENT;
+                }
+
+                switch (queryFields[i]) {
+                    case UUID:
+                        predicates.add(cb.and(cb.equal(from.get("uuid"), queryValue)));
+                        break;
+                    case PARENT:
+                        predicates.add(cb.and(cb.equal(from.get("parent_uuid"), queryValue)));
+                        break;
+                    case NAME:
+                        predicates.add(cb.and(cb.like(from.get("name"), queryValue)));
+                        break;
+                    case MNEMONIC:
+                        predicates.add(cb.and(cb.like(from.get("mnemonic"), queryValue)));
+                        break;
+                    case MNEMONICEQUIVALENCE:
+                        predicates.add(cb.and(cb.like(from.get("mnemonic_equivalence"), queryValue)));
+                        break;
+                    case MNEMONICPATH:
+                        predicates.add(cb.and(cb.like(cb.function("get_mnemonic_path_devicetype", String.class, from.get("uuid")), queryValue)));
+                        break;
+                    case DESCRIPTION:
+                        predicates.add(cb.and(cb.like(from.get("description"), queryValue)));
+                        break;
+                    default:
+                        continue;
+                }
+            }
+        }
+
+        return predicates;
+    }
+
+    /**
+     * Persist device type into persistence context.
+     *
+     * @param deviceType device type
+     */
+    public void createDeviceType(DeviceType deviceType) {
+        em.persist(deviceType);
+    }
+
+    /**
+     * Merge device type into persistence context.
+     *
+     * @param deviceType device type
+     */
+    public void updateDeviceType(DeviceType deviceType) {
+        em.merge(deviceType);
+    }
+
+}
diff --git a/src/main/java/org/openepics/names/repository/DisciplineRepository.java b/src/main/java/org/openepics/names/repository/DisciplineRepository.java
new file mode 100644
index 0000000..77faa2e
--- /dev/null
+++ b/src/main/java/org/openepics/names/repository/DisciplineRepository.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2021 European Spallation Source ERIC.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+package org.openepics.names.repository;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import javax.persistence.TypedQuery;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
+import org.apache.commons.lang3.BooleanUtils;
+import org.openepics.names.repository.model.Discipline;
+import org.openepics.names.rest.beans.FieldStructure;
+import org.openepics.names.rest.beans.Status;
+import org.springframework.stereotype.Repository;
+
+/**
+ * Handle discipline information in JPA.
+ *
+ * @author Lars Johansson
+ */
+@Repository
+public class DisciplineRepository {
+
+    private static final String PERCENT = "%";
+
+    @PersistenceContext
+    private EntityManager em;
+
+    /**
+     * Count disciplines.
+     *
+     * @param statuses statuses
+     * @param deleted deleted
+     * @param queryFields query fields
+     * @param queryValues query values
+     * @return count of disciplines
+     */
+    public Long countDisciplines(
+            Status[] statuses, Boolean deleted, FieldStructure[] queryFields, String[] queryValues) {
+        return countDisciplines(statuses, deleted, queryFields, queryValues, Boolean.FALSE);
+    }
+
+    /**
+     * Count disciplines.
+     *
+     * @param statuses statuses
+     * @param deleted deleted
+     * @param queryFields query fields
+     * @param queryValues query values
+     * @param includeHistory include history
+     * @return count of disciplines
+     */
+    public Long countDisciplines(
+            Status[] statuses, Boolean deleted, FieldStructure[] queryFields, String[] queryValues, Boolean includeHistory) {
+
+        // note
+        //     use of function for mnemonic path
+        // where
+        //     statuses
+        //     deleted
+        //     queryFields, queryValues
+
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+        Root<Discipline> from = cq.from(Discipline.class);
+
+        cq.where(cb.and(preparePredicatesDisciplines(cb, from, statuses, deleted, queryFields, queryValues, includeHistory).toArray(new Predicate[0])));
+        cq.select(cb.count(from));
+
+        return em.createQuery(cq).getSingleResult();
+    }
+
+    /**
+     *Find disciplines.
+     *
+     * @param status status
+     * @param deleted deleted
+     * @param queryField query field
+     * @param queryValue query value
+     * @return list of disciplines
+     */
+    public List<Discipline> readDisciplines(
+            Status status, Boolean deleted, FieldStructure queryField, String queryValue) {
+
+        return readDisciplines(
+                status != null ? new Status[] {status} : null,
+                deleted,
+                queryField != null ? new FieldStructure[] {queryField} : null,
+                queryValue != null ? new String[] {queryValue} : null,
+                Boolean.FALSE,
+                null, null, null, null);
+    }
+
+    /**
+     * Find disciplines.
+     *
+     * @param statuses statuses
+     * @param deleted deleted
+     * @param queryFields query fields
+     * @param queryValues query values
+     * @param orderBy order by
+     * @param isAsc is ascending
+     * @param offset offset
+     * @param limit limit
+     * @return list of disciplines
+     */
+    public List<Discipline> readDisciplines(
+            Status[] statuses, Boolean deleted, FieldStructure[] queryFields, String[] queryValues,
+            FieldStructure orderBy, Boolean isAsc, Integer offset, Integer limit) {
+        return readDisciplines(
+                statuses, deleted, queryFields, queryValues, Boolean.FALSE,
+                orderBy, isAsc, offset, limit);
+    }
+
+    /**
+     * Find disciplines.
+     *
+     * @param statuses statuses
+     * @param deleted deleted
+     * @param queryFields query fields
+     * @param queryValues query values
+     * @param includeHistory include history
+     * @param orderBy order by
+     * @param isAsc is ascending
+     * @param offset offset
+     * @param limit limit
+     * @return list of disciplines
+     */
+    public List<Discipline> readDisciplines(
+            Status[] statuses, Boolean deleted, FieldStructure[] queryFields, String[] queryValues, Boolean includeHistory,
+            FieldStructure orderBy, Boolean isAsc, Integer offset, Integer limit) {
+
+        // note
+        //     use of function for mnemonic path
+        // where
+        //     statuses
+        //     latest, deleted
+        //     queryFields, queryValues
+        // order
+        //     orderBy, isAsc
+        // paging
+        //     offset, limit
+
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<Discipline> cq = cb.createQuery(Discipline.class);
+        Root<Discipline> from = cq.from(Discipline.class);
+
+        cq.where(cb.and(preparePredicatesDisciplines(cb, from, statuses, deleted, queryFields, queryValues, includeHistory).toArray(new Predicate[0])));
+        cq.select(from);
+
+        if (orderBy != null) {
+            if (BooleanUtils.toBoolean(isAsc)) {
+                if (FieldStructure.NAME.equals(orderBy)) {
+                    cq.orderBy(cb.asc(from.get("name")));
+                } else if (FieldStructure.MNEMONIC.equals(orderBy)) {
+                    cq.orderBy(cb.asc(from.get("mnemonic")));
+                } else if (FieldStructure.MNEMONICEQUIVALENCE.equals(orderBy)) {
+                    cq.orderBy(cb.asc(from.get("mnemonic_equivalence")));
+                } else if (FieldStructure.MNEMONICPATH.equals(orderBy)) {
+                    cq.orderBy(cb.asc(from.get("mnemonic")));
+                } else if (FieldStructure.DESCRIPTION.equals(orderBy)) {
+                    cq.orderBy(cb.asc(from.get("description")));
+                } else {
+                    cq.orderBy(cb.asc(from.get("convention_name")));
+                }
+            } else {
+                if (FieldStructure.NAME.equals(orderBy)) {
+                    cq.orderBy(cb.desc(from.get("name")));
+                } else if (FieldStructure.MNEMONIC.equals(orderBy)) {
+                    cq.orderBy(cb.desc(from.get("mnemonic")));
+                } else if (FieldStructure.MNEMONICEQUIVALENCE.equals(orderBy)) {
+                    cq.orderBy(cb.desc(from.get("mnemonic_equivalence")));
+                } else if (FieldStructure.MNEMONICPATH.equals(orderBy)) {
+                    cq.orderBy(cb.asc(from.get("mnemonic")));
+                } else if (FieldStructure.DESCRIPTION.equals(orderBy)) {
+                    cq.orderBy(cb.desc(from.get("description")));
+                } else {
+                    cq.orderBy(cb.desc(from.get("name")));
+                }
+            }
+        }
+
+        TypedQuery<Discipline> query = em.createQuery(cq);
+        if (offset != null && limit != null) {
+            query.setFirstResult(offset * limit);
+            query.setMaxResults(limit);
+        }
+
+        return query.getResultList();
+    }
+
+    /**
+     * Prepare predicates for disciplines.
+     *
+     * @param cb criteria builder
+     * @param from criteria query root
+     * @param statuses statuses
+     * @param deleted deleted
+     * @param queryFields query fields
+     * @param queryValues query values
+     * @param includeHistory include history
+     * @return list of predicates
+     */
+    private List<Predicate> preparePredicatesDisciplines(
+            CriteriaBuilder cb, Root<Discipline> from,
+            Status[] statuses, Boolean deleted, FieldStructure[] queryFields, String[] queryValues, Boolean includeHistory) {
+
+        List<Predicate> predicates = new ArrayList<>();
+
+        if (!Boolean.TRUE.equals(includeHistory)) {
+            // exclude (approved and not latest)
+            Predicate predicateApproved  = cb.equal(from.get("status"), Status.APPROVED);
+            Predicate predicateNotLatest = cb.equal(from.get("latest"), Boolean.FALSE);
+            Predicate predicateExclude   = cb.not(cb.and(predicateApproved, predicateNotLatest));
+            predicates.add(predicateExclude);
+        }
+
+        if (statuses != null) {
+            List<Predicate> predicatesStatus = new ArrayList<>();
+            for (Status status : statuses) {
+                predicatesStatus.add(cb.equal(from.get("status"), status));
+            }
+            predicates.add(cb.or((Predicate[]) predicatesStatus.toArray(new Predicate[0])));
+        }
+        if (deleted != null) {
+            predicates.add(cb.equal(from.get("deleted"), deleted));
+        }
+        if (queryFields != null) {
+            for (int i=0; i<queryFields.length; i++) {
+                String queryValue = queryValues[i];
+
+                // jpa query characters % and _
+                // remove excess % characters
+                if (queryValue.startsWith(PERCENT)) {
+                    while (queryValue.startsWith(PERCENT)) {
+                        queryValue = queryValue.substring(1);
+                    }
+                    queryValue = PERCENT + queryValue;
+                }
+                if (queryValue.endsWith(PERCENT)) {
+                    while (queryValue.endsWith(PERCENT)) {
+                        queryValue = queryValue.substring(0, queryValue.length()-1);
+                    }
+                    queryValue = queryValue + PERCENT;
+                }
+
+                switch (queryFields[i]) {
+                    case UUID:
+                        predicates.add(cb.and(cb.equal(from.get("uuid"), queryValue)));
+                        break;
+                    case NAME:
+                        predicates.add(cb.and(cb.like(from.get("name"), queryValue)));
+                        break;
+                    case MNEMONIC:
+                        predicates.add(cb.and(cb.like(from.get("mnemonic"), queryValue)));
+                        break;
+                    case MNEMONICEQUIVALENCE:
+                        predicates.add(cb.and(cb.like(from.get("mnemonic_equivalence"), queryValue)));
+                        break;
+                    case MNEMONICPATH:
+                        predicates.add(cb.and(cb.like(from.get("mnemonic"), queryValue)));
+                        break;
+                    case DESCRIPTION:
+                        predicates.add(cb.and(cb.like(from.get("description"), queryValue)));
+                        break;
+                    default:
+                        continue;
+                }
+            }
+        }
+
+        return predicates;
+    }
+
+    /**
+     * Persist discipline into persistence context.
+     *
+     * @param discipline discipline
+     */
+    public void createDiscipline(Discipline discipline) {
+        em.persist(discipline);
+    }
+
+    /**
+     * Merge discipline into persistence context.
+     *
+     * @param discipline discipline
+     */
+    public void updateDiscipline(Discipline discipline) {
+        em.merge(discipline);
+    }
+
+}
diff --git a/src/main/java/org/openepics/names/repository/NameRepository.java b/src/main/java/org/openepics/names/repository/NameRepository.java
new file mode 100644
index 0000000..4226d7a
--- /dev/null
+++ b/src/main/java/org/openepics/names/repository/NameRepository.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2021 European Spallation Source ERIC.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+package org.openepics.names.repository;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import javax.persistence.TypedQuery;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
+import org.apache.commons.lang3.BooleanUtils;
+import org.openepics.names.repository.model.Name;
+import org.openepics.names.rest.beans.FieldName;
+import org.springframework.stereotype.Repository;
+
+/**
+ * Handle name information in JPA.
+ *
+ * @author Lars Johansson
+ */
+@Repository
+public class NameRepository {
+
+    private static final String PERCENT = "%";
+
+    @PersistenceContext
+    private EntityManager em;
+
+    /**
+     * Count names.
+     *
+     * @param deleted deleted
+     * @param queryFields query fields
+     * @param queryValues query values
+     * @return count of names
+     */
+    public Long countNames(
+            Boolean deleted, FieldName[] queryFields, String[] queryValues) {
+        return countNames(deleted, queryFields, queryValues, Boolean.FALSE);
+    }
+
+    /**
+     * Count names.
+     *
+     * @param deleted deleted
+     * @param queryFields query fields
+     * @param queryValues query values
+     * @param includeHistory include history
+     * @return count of names
+     */
+    public Long countNames(
+            Boolean deleted, FieldName[] queryFields, String[] queryValues, Boolean includeHistory) {
+
+        // note
+        //     use of function for mnemonic path
+        // where
+        //     latest, deleted
+        //     queryFields, queryValues
+
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+        Root<Name> from = cq.from(Name.class);
+
+        cq.where(cb.and(preparePredicatesNames(cb, from, deleted, queryFields, queryValues, includeHistory).toArray(new Predicate[0])));
+        cq.select(cb.count(from));
+
+        return em.createQuery(cq).getSingleResult();
+    }
+
+    /**
+     * Find names.
+     *
+     * @param deleted deleted
+     * @param queryField query field
+     * @param queryValue query value
+     * @return list of names
+     */
+    public List<Name> readNames(
+            Boolean deleted, FieldName queryField, String queryValue) {
+
+        return readNames(
+                deleted,
+                queryField != null ? new FieldName[] {queryField} : null,
+                queryValue != null ? new String[] {queryValue} : null,
+                Boolean.FALSE,
+                null, null, null, null);
+    }
+
+    /**
+     * Find names.
+     *
+     * @param deleted deleted
+     * @param queryFields query fields
+     * @param queryValues query values
+     * @param orderBy order by
+     * @param isAsc is ascending
+     * @param offset offset
+     * @param limit limit
+     * @return list of names
+     */
+    public List<Name> readNames(
+            Boolean deleted, FieldName[] queryFields, String[] queryValues,
+            FieldName orderBy, Boolean isAsc, Integer offset, Integer limit) {
+        return readNames(
+                deleted, queryFields, queryValues, Boolean.FALSE,
+                orderBy, isAsc, offset, limit);
+    }
+
+    /**
+     * Find names.
+     *
+     * @param deleted deleted
+     * @param queryFields query fields
+     * @param queryValues query values
+     * @param includeHistory include history
+     * @param orderBy order by
+     * @param isAsc is ascending
+     * @param offset offset
+     * @param limit limit
+     * @return list of names
+     */
+    public List<Name> readNames(
+            Boolean deleted, FieldName[] queryFields, String[] queryValues, Boolean includeHistory,
+            FieldName orderBy, Boolean isAsc, Integer offset, Integer limit) {
+
+        // note
+        //     use of function for mnemonic path
+        // where
+        //     latest, deleted
+        //     queryFields, queryValues
+        // order
+        //     orderBy, isAsc
+        // paging
+        //     offset, limit
+
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<Name> cq = cb.createQuery(Name.class);
+        Root<Name> from = cq.from(Name.class);
+
+        cq.where(cb.and(preparePredicatesNames(cb, from, deleted, queryFields, queryValues, includeHistory).toArray(new Predicate[0])));
+        cq.select(from);
+
+        if (orderBy != null) {
+            if (BooleanUtils.toBoolean(isAsc)) {
+                if (FieldName.NAME.equals(orderBy)) {
+                    cq.orderBy(cb.asc(from.get("convention_name")));
+                } else if (FieldName.NAMEEQUIVALENCE.equals(orderBy)) {
+                        cq.orderBy(cb.asc(from.get("convention_name_equivalence")));
+                } else if (FieldName.SYSTEMSTRUCTURE.equals(orderBy)) {
+                    cq.orderBy(cb.asc(cb.function("get_mnemonic_path_system_structure", String.class, from.get("convention_name"))));
+                } else if (FieldName.DEVICESTRUCTURE.equals(orderBy)) {
+                    cq.orderBy(cb.asc(cb.function("get_mnemonic_path_device_structure", String.class, from.get("convention_name"))));
+                } else if (FieldName.DESCRIPTION.equals(orderBy)) {
+                    cq.orderBy(cb.asc(from.get("description")));
+                } else {
+                    cq.orderBy(cb.asc(from.get("convention_name")));
+                }
+            } else {
+                if (FieldName.NAME.equals(orderBy)) {
+                    cq.orderBy(cb.desc(from.get("convention_name")));
+                } else if (FieldName.NAMEEQUIVALENCE.equals(orderBy)) {
+                    cq.orderBy(cb.desc(from.get("convention_name_equivalence")));
+                } else if (FieldName.SYSTEMSTRUCTURE.equals(orderBy)) {
+                    cq.orderBy(cb.desc(cb.function("get_mnemonic_path_system_structure", String.class, from.get("convention_name"))));
+                } else if (FieldName.DEVICESTRUCTURE.equals(orderBy)) {
+                    cq.orderBy(cb.desc(cb.function("get_mnemonic_path_device_structure", String.class, from.get("convention_name"))));
+                } else if (FieldName.DESCRIPTION.equals(orderBy)) {
+                    cq.orderBy(cb.desc(from.get("description")));
+                } else {
+                    cq.orderBy(cb.desc(from.get("convention_name")));
+                }
+            }
+        }
+
+        TypedQuery<Name> query = em.createQuery(cq);
+        if (offset != null && limit != null) {
+            query.setFirstResult(offset * limit);
+            query.setMaxResults(limit);
+        }
+
+        return query.getResultList();
+    }
+
+    /**
+     * Prepare predicates for names.
+     *
+     * @param cb criteria builder
+     * @param from criteria query root
+     * @param deleted deleted
+     * @param queryFields query fields
+     * @param queryValues query values
+     * @param includeHistory include history
+     * @return list of predicates
+     */
+    private List<Predicate> preparePredicatesNames(
+            CriteriaBuilder cb, Root<Name> from,
+            Boolean deleted, FieldName[] queryFields, String[] queryValues, Boolean includeHistory) {
+
+        List<Predicate> predicates = new ArrayList<>();
+
+        if (!Boolean.TRUE.equals(includeHistory)) {
+            // exclude (not latest)
+            Predicate predicateNotLatest = cb.equal(from.get("latest"), Boolean.FALSE);
+            Predicate predicateExclude   = cb.not(cb.and(predicateNotLatest));
+            predicates.add(predicateExclude);
+        }
+
+        if (deleted != null) {
+            predicates.add(cb.equal(from.get("deleted"), deleted));
+        }
+        if (queryFields != null) {
+            for (int i=0; i<queryFields.length; i++) {
+                String queryValue = queryValues[i];
+
+                // jpa query characters % and _
+                // remove excess % characters
+                if (queryValue.startsWith(PERCENT)) {
+                    while (queryValue.startsWith(PERCENT)) {
+                        queryValue = queryValue.substring(1);
+                    }
+                    queryValue = PERCENT + queryValue;
+                }
+                if (queryValue.endsWith(PERCENT)) {
+                    while (queryValue.endsWith(PERCENT)) {
+                        queryValue = queryValue.substring(0, queryValue.length()-1);
+                    }
+                    queryValue = queryValue + PERCENT;
+                }
+
+                switch (queryFields[i]) {
+                    case UUID:
+                        predicates.add(cb.and(cb.equal(from.get("uuid"), queryValue)));
+                        break;
+                    case NAME:
+                        predicates.add(cb.and(cb.like(from.get("convention_name"), queryValue)));
+                        break;
+                    case NAMEEQUIVALENCE:
+                        predicates.add(cb.and(cb.like(from.get("convention_name_equivalence"), queryValue)));
+                        break;
+                    case SYSTEMSTRUCTURE:
+                        predicates.add(cb.and(cb.like(cb.function("get_mnemonic_path_system_structure", String.class, from.get("convention_name")), queryValue)));
+                        break;
+                    case DEVICESTRUCTURE:
+                        predicates.add(cb.and(cb.like(cb.function("get_mnemonic_path_device_structure", String.class, from.get("convention_name")), queryValue)));
+                        break;
+                    case DESCRIPTION:
+                        predicates.add(cb.and(cb.like(from.get("description"), queryValue)));
+                        break;
+                    default:
+                        continue;
+                }
+            }
+        }
+
+        return predicates;
+    }
+
+    /**
+     * Persist name into persistence context.
+     *
+     * @param name name
+     */
+    public void createName(Name name) {
+        em.persist(name);
+    }
+
+    /**
+     * Merge name into persistence context.
+     *
+     * @param name name
+     */
+    public void updateName(Name name) {
+        em.merge(name);
+    }
+
+}
diff --git a/src/main/java/org/openepics/names/repository/SubsystemRepository.java b/src/main/java/org/openepics/names/repository/SubsystemRepository.java
new file mode 100644
index 0000000..461ab13
--- /dev/null
+++ b/src/main/java/org/openepics/names/repository/SubsystemRepository.java
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2021 European Spallation Source ERIC.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+package org.openepics.names.repository;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import javax.persistence.TypedQuery;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
+import org.apache.commons.lang3.BooleanUtils;
+import org.openepics.names.repository.model.Subsystem;
+import org.openepics.names.rest.beans.FieldStructure;
+import org.openepics.names.rest.beans.Status;
+import org.springframework.stereotype.Repository;
+
+/**
+ * Handle subsystem information in JPA.
+ *
+ * @author Lars Johansson
+ */
+@Repository
+public class SubsystemRepository {
+
+    private static final String PERCENT = "%";
+
+    @PersistenceContext
+    private EntityManager em;
+
+    /**
+     * Count subsystems.
+     *
+     * @param statuses statuses
+     * @param deleted deleted
+     * @param queryFields query fields
+     * @param queryValues query values
+     * @return count of subsystems
+     */
+    public Long countSubsystems(
+            Status[] statuses, Boolean deleted, FieldStructure[] queryFields, String[] queryValues) {
+        return countSubsystems(statuses, deleted, queryFields, queryValues, Boolean.FALSE);
+    }
+
+    /**
+     * Count subsystems.
+     *
+     * @param statuses statuses
+     * @param deleted deleted
+     * @param queryFields query fields
+     * @param queryValues query values
+     * @param includeHistory include history
+     * @return count of subsystems
+     */
+    public Long countSubsystems(
+            Status[] statuses, Boolean deleted, FieldStructure[] queryFields, String[] queryValues, Boolean includeHistory) {
+
+        // note
+        //     use of function for mnemonic path
+        // where
+        //     statuses
+        //     deleted
+        //     queryFields, queryValues
+
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+        Root<Subsystem> from = cq.from(Subsystem.class);
+
+        cq.where(cb.and(preparePredicatesSubsystems(cb, from, statuses, deleted, queryFields, queryValues, includeHistory).toArray(new Predicate[0])));
+        cq.select(cb.count(from));
+
+        return em.createQuery(cq).getSingleResult();
+    }
+
+    /**
+     * Find subsystems.
+     *
+     * @param status status
+     * @param deleted deleted
+     * @param queryField query field
+     * @param queryValue query value
+     * @return list of subsystems
+     */
+    public List<Subsystem> readSubsystems(
+            Status status, Boolean deleted, FieldStructure queryField, String queryValue) {
+
+        return readSubsystems(
+                status != null ? new Status[] {status} : null,
+                deleted,
+                queryField != null ? new FieldStructure[] {queryField} : null,
+                queryValue != null ? new String[] {queryValue} : null,
+                Boolean.FALSE,
+                null, null, null, null);
+    }
+
+    /**
+     * Find subsystems.
+     *
+     * @param statuses statuses
+     * @param deleted deleted
+     * @param queryFields query fields
+     * @param queryValues query values
+     * @param orderBy order by
+     * @param isAsc is ascending
+     * @param offset offset
+     * @param limit limit
+     * @return list of subsystems
+     */
+    public List<Subsystem> readSubsystems(
+            Status[] statuses, Boolean deleted, FieldStructure[] queryFields, String[] queryValues,
+            FieldStructure orderBy, Boolean isAsc, Integer offset, Integer limit) {
+        return readSubsystems(
+                statuses, deleted, queryFields, queryValues, Boolean.FALSE,
+                orderBy, isAsc, offset, limit);
+    }
+
+    /**
+     * Find subsystems.
+     *
+     * @param statuses statuses
+     * @param deleted deleted
+     * @param queryFields query fields
+     * @param queryValues query values
+     * @param includeHistory include history
+     * @param orderBy order by
+     * @param isAsc is ascending
+     * @param offset offset
+     * @param limit limit
+     * @return list of subsystems
+     */
+    public List<Subsystem> readSubsystems(
+            Status[] statuses, Boolean deleted, FieldStructure[] queryFields, String[] queryValues, Boolean includeHistory,
+            FieldStructure orderBy, Boolean isAsc, Integer offset, Integer limit) {
+
+        // note
+        //     use of function for mnemonic path
+        // where
+        //     statuses
+        //     deleted
+        //     queryFields, queryValues
+        // order
+        //     orderBy, isAsc
+        // paging
+        //     offset, limit
+
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<Subsystem> cq = cb.createQuery(Subsystem.class);
+        Root<Subsystem> from = cq.from(Subsystem.class);
+
+        cq.where(cb.and(preparePredicatesSubsystems(cb, from, statuses, deleted, queryFields, queryValues, includeHistory).toArray(new Predicate[0])));
+        cq.select(from);
+
+        if (orderBy != null) {
+            if (BooleanUtils.toBoolean(isAsc)) {
+                if (FieldStructure.NAME.equals(orderBy)) {
+                    cq.orderBy(cb.asc(from.get("name")));
+                } else if (FieldStructure.MNEMONIC.equals(orderBy)) {
+                    cq.orderBy(cb.asc(from.get("mnemonic")));
+                } else if (FieldStructure.MNEMONICEQUIVALENCE.equals(orderBy)) {
+                    cq.orderBy(cb.asc(from.get("mnemonic_equivalence")));
+                } else if (FieldStructure.MNEMONICPATH.equals(orderBy)) {
+                    cq.orderBy(cb.asc(cb.function("get_mnemonic_path_subsystem", String.class, from.get("uuid"))));
+                } else if (FieldStructure.DESCRIPTION.equals(orderBy)) {
+                    cq.orderBy(cb.asc(from.get("description")));
+                } else {
+                    cq.orderBy(cb.asc(from.get("convention_name")));
+                }
+            } else {
+                if (FieldStructure.NAME.equals(orderBy)) {
+                    cq.orderBy(cb.desc(from.get("name")));
+                } else if (FieldStructure.MNEMONIC.equals(orderBy)) {
+                    cq.orderBy(cb.desc(from.get("mnemonic")));
+                } else if (FieldStructure.MNEMONICEQUIVALENCE.equals(orderBy)) {
+                    cq.orderBy(cb.desc(from.get("mnemonic_equivalence")));
+                } else if (FieldStructure.MNEMONICPATH.equals(orderBy)) {
+                    cq.orderBy(cb.desc(cb.function("get_mnemonic_path_subsystem", String.class, from.get("uuid"))));
+                } else if (FieldStructure.DESCRIPTION.equals(orderBy)) {
+                    cq.orderBy(cb.desc(from.get("description")));
+                } else {
+                    cq.orderBy(cb.desc(from.get("name")));
+                }
+            }
+        }
+
+        TypedQuery<Subsystem> query = em.createQuery(cq);
+        if (offset != null && limit != null) {
+            query.setFirstResult(offset * limit);
+            query.setMaxResults(limit);
+        }
+
+        return query.getResultList();
+    }
+
+    /**
+     * Prepare predicates for subsystems.
+     *
+     * @param cb criteria builder
+     * @param from criteria query root
+     * @param statuses statuses
+     * @param deleted deleted
+     * @param queryFields query fields
+     * @param queryValues query values
+     * @param includeHistory include history
+     * @return list of predicates
+     */
+    private List<Predicate> preparePredicatesSubsystems(
+            CriteriaBuilder cb, Root<Subsystem> from,
+            Status[] statuses, Boolean deleted, FieldStructure[] queryFields, String[] queryValues, Boolean includeHistory) {
+
+        List<Predicate> predicates = new ArrayList<>();
+
+        if (!Boolean.TRUE.equals(includeHistory)) {
+            // exclude (approved and not latest)
+            Predicate predicateApproved  = cb.equal(from.get("status"), Status.APPROVED);
+            Predicate predicateNotLatest = cb.equal(from.get("latest"), Boolean.FALSE);
+            Predicate predicateExclude   = cb.not(cb.and(predicateApproved, predicateNotLatest));
+            predicates.add(predicateExclude);
+        }
+
+        if (statuses != null) {
+            List<Predicate> predicatesStatus = new ArrayList<>();
+            for (Status status : statuses) {
+                predicatesStatus.add(cb.equal(from.get("status"), status));
+            }
+            predicates.add(cb.or((Predicate[]) predicatesStatus.toArray(new Predicate[0])));
+        }
+        if (deleted != null) {
+            predicates.add(cb.equal(from.get("deleted"), deleted));
+        }
+        if (queryFields != null) {
+            for (int i=0; i<queryFields.length; i++) {
+                String queryValue = queryValues[i];
+
+                // jpa query characters % and _
+                // remove excess % characters
+                if (queryValue.startsWith(PERCENT)) {
+                    while (queryValue.startsWith(PERCENT)) {
+                        queryValue = queryValue.substring(1);
+                    }
+                    queryValue = PERCENT + queryValue;
+                }
+                if (queryValue.endsWith(PERCENT)) {
+                    while (queryValue.endsWith(PERCENT)) {
+                        queryValue = queryValue.substring(0, queryValue.length()-1);
+                    }
+                    queryValue = queryValue + PERCENT;
+                }
+
+                switch (queryFields[i]) {
+                    case UUID:
+                        predicates.add(cb.and(cb.equal(from.get("uuid"), queryValue)));
+                        break;
+                    case PARENT:
+                        predicates.add(cb.and(cb.equal(from.get("parent_uuid"), queryValue)));
+                        break;
+                    case NAME:
+                        predicates.add(cb.and(cb.like(from.get("name"), queryValue)));
+                        break;
+                    case MNEMONIC:
+                        predicates.add(cb.and(cb.like(from.get("mnemonic"), queryValue)));
+                        break;
+                    case MNEMONICEQUIVALENCE:
+                        predicates.add(cb.and(cb.like(from.get("mnemonic_equivalence"), queryValue)));
+                        break;
+                    case MNEMONICPATH:
+                        predicates.add(cb.and(cb.like(cb.function("get_mnemonic_path_subsystem", String.class, from.get("uuid")), queryValue)));
+                        break;
+                    case DESCRIPTION:
+                        predicates.add(cb.and(cb.like(from.get("description"), queryValue)));
+                        break;
+                    default:
+                        continue;
+                }
+            }
+        }
+
+        return predicates;
+    }
+
+    /**
+     * Persist subsystem into persistence context.
+     *
+     * @param subsystem subsystem
+     */
+    public void createSubsystem(Subsystem subsystem) {
+        em.persist(subsystem);
+    }
+
+    /**
+     * Merge subsystem into persistence context.
+     *
+     * @param subsystem subsystem
+     */
+    public void updateSubsystem(Subsystem subsystem) {
+        em.merge(subsystem);
+    }
+
+}
diff --git a/src/main/java/org/openepics/names/repository/SystemGroupRepository.java b/src/main/java/org/openepics/names/repository/SystemGroupRepository.java
new file mode 100644
index 0000000..a87a7c3
--- /dev/null
+++ b/src/main/java/org/openepics/names/repository/SystemGroupRepository.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2021 European Spallation Source ERIC.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+package org.openepics.names.repository;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import javax.persistence.TypedQuery;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
+import org.apache.commons.lang3.BooleanUtils;
+import org.openepics.names.repository.model.SystemGroup;
+import org.openepics.names.rest.beans.FieldStructure;
+import org.openepics.names.rest.beans.Status;
+import org.springframework.stereotype.Repository;
+
+/**
+ * Handle system group information in JPA.
+ *
+ * @author Lars Johansson
+ */
+@Repository
+public class SystemGroupRepository {
+
+    private static final String PERCENT = "%";
+
+    @PersistenceContext
+    private EntityManager em;
+
+    /**
+     * Count system groups.
+     *
+     * @param statuses statuses
+     * @param deleted deleted
+     * @param queryFields query fields
+     * @param queryValues query values
+     * @return count of system groups
+     */
+    public Long countSystemGroups(
+            Status[] statuses, Boolean deleted, FieldStructure[] queryFields, String[] queryValues) {
+        return countSystemGroups(statuses, deleted, queryFields, queryValues, Boolean.FALSE);
+    }
+
+    /**
+     * Count system groups.
+     *
+     * @param statuses statuses
+     * @param deleted deleted
+     * @param queryFields query fields
+     * @param queryValues query values
+     * @param includeHistory include history
+     * @return count of system groups
+     */
+    public Long countSystemGroups(
+            Status[] statuses, Boolean deleted, FieldStructure[] queryFields, String[] queryValues, Boolean includeHistory) {
+
+        // where
+        //     statuses
+        //     deleted
+        //     queryFields, queryValues
+
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+        Root<SystemGroup> from = cq.from(SystemGroup.class);
+
+        cq.where(cb.and(preparePredicatesSystemGroups(cb, from, statuses, deleted, queryFields, queryValues, includeHistory).toArray(new Predicate[0])));
+        cq.select(cb.count(from));
+
+        return em.createQuery(cq).getSingleResult();
+    }
+
+    /**
+     * Find system groups.
+     *
+     * @param status status
+     * @param deleted deleted
+     * @param queryField query field
+     * @param queryValue query value
+     * @return list of system groups
+     */
+    public List<SystemGroup> readSystemGroups(
+            Status status, Boolean deleted, FieldStructure queryField, String queryValue) {
+
+        return readSystemGroups(
+                status != null ? new Status[] {status} : null,
+                deleted,
+                queryField != null ? new FieldStructure[] {queryField} : null,
+                queryValue != null ? new String[] {queryValue} : null,
+                Boolean.FALSE,
+                null, null, null, null);
+    }
+
+    /**
+     * Find system groups.
+     *
+     * @param statuses statuses
+     * @param deleted deleted
+     * @param queryFields query fields
+     * @param queryValues query values
+     * @param orderBy order by
+     * @param isAsc is ascending
+     * @param offset offset
+     * @param limit limit
+     * @return list of system groups
+     */
+    public List<SystemGroup> readSystemGroups(
+            Status[] statuses, Boolean deleted, FieldStructure[] queryFields, String[] queryValues,
+            FieldStructure orderBy, Boolean isAsc, Integer offset, Integer limit) {
+        return readSystemGroups(
+                statuses, deleted, queryFields, queryValues, Boolean.FALSE,
+                orderBy, isAsc, offset, limit);
+    }
+
+    /**
+     * Find system groups.
+     *
+     * @param statuses statuses
+     * @param deleted deleted
+     * @param queryFields query fields
+     * @param queryValues query values
+     * @param includeHistory include history
+     * @param orderBy order by
+     * @param isAsc is ascending
+     * @param offset offset
+     * @param limit limit
+     * @return list of system groups
+     */
+    public List<SystemGroup> readSystemGroups(
+            Status[] statuses, Boolean deleted, FieldStructure[] queryFields, String[] queryValues, Boolean includeHistory,
+            FieldStructure orderBy, Boolean isAsc, Integer offset, Integer limit) {
+
+        // where
+        //     statuses,
+        //     deleted
+        //     queryFields, queryValues
+        // order
+        //     orderBy, isAsc
+        // paging
+        //     offset, limit
+
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<SystemGroup> cq = cb.createQuery(SystemGroup.class);
+        Root<SystemGroup> from = cq.from(SystemGroup.class);
+
+        cq.where(cb.and(preparePredicatesSystemGroups(cb, from, statuses, deleted, queryFields, queryValues, includeHistory).toArray(new Predicate[0])));
+        cq.select(from);
+
+        if (orderBy != null) {
+            if (BooleanUtils.toBoolean(isAsc)) {
+                if (FieldStructure.NAME.equals(orderBy)) {
+                    cq.orderBy(cb.asc(from.get("name")));
+                } else if (FieldStructure.MNEMONIC.equals(orderBy)) {
+                    cq.orderBy(cb.asc(from.get("mnemonic")));
+                } else if (FieldStructure.MNEMONICEQUIVALENCE.equals(orderBy)) {
+                    cq.orderBy(cb.asc(from.get("mnemonic_equivalence")));
+                } else if (FieldStructure.MNEMONICPATH.equals(orderBy)) {
+                    cq.orderBy(cb.asc(from.get("mnemonic")));
+                } else if (FieldStructure.DESCRIPTION.equals(orderBy)) {
+                    cq.orderBy(cb.asc(from.get("description")));
+                } else {
+                    cq.orderBy(cb.asc(from.get("convention_name")));
+                }
+            } else {
+                if (FieldStructure.NAME.equals(orderBy)) {
+                    cq.orderBy(cb.desc(from.get("name")));
+                } else if (FieldStructure.MNEMONIC.equals(orderBy)) {
+                    cq.orderBy(cb.desc(from.get("mnemonic")));
+                } else if (FieldStructure.MNEMONICEQUIVALENCE.equals(orderBy)) {
+                    cq.orderBy(cb.desc(from.get("mnemonic_equivalence")));
+                } else if (FieldStructure.MNEMONICPATH.equals(orderBy)) {
+                    cq.orderBy(cb.desc(from.get("mnemonic")));
+                } else if (FieldStructure.DESCRIPTION.equals(orderBy)) {
+                    cq.orderBy(cb.desc(from.get("description")));
+                } else {
+                    cq.orderBy(cb.desc(from.get("name")));
+                }
+            }
+        }
+
+        TypedQuery<SystemGroup> query = em.createQuery(cq);
+        if (offset != null && limit != null) {
+            query.setFirstResult(offset * limit);
+            query.setMaxResults(limit);
+        }
+
+        return query.getResultList();
+    }
+
+    /**
+     * Prepare predicates for system groups.
+     *
+     * @param cb criteria builder
+     * @param from criteria query root
+     * @param statuses statuses
+     * @param deleted deleted
+     * @param queryFields query fields
+     * @param queryValues query values
+     * @param includeHistory include history
+     * @return list of predicates
+     */
+    private List<Predicate> preparePredicatesSystemGroups(
+            CriteriaBuilder cb, Root<SystemGroup> from,
+            Status[] statuses, Boolean deleted, FieldStructure[] queryFields, String[] queryValues, Boolean includeHistory) {
+
+        List<Predicate> predicates = new ArrayList<>();
+
+        if (!Boolean.TRUE.equals(includeHistory)) {
+            // exclude (approved and not latest)
+            Predicate predicateApproved  = cb.equal(from.get("status"), Status.APPROVED);
+            Predicate predicateNotLatest = cb.equal(from.get("latest"), Boolean.FALSE);
+            Predicate predicateExclude   = cb.not(cb.and(predicateApproved, predicateNotLatest));
+            predicates.add(predicateExclude);
+        }
+
+        if (statuses != null) {
+            List<Predicate> predicatesStatus = new ArrayList<>();
+            for (Status status : statuses) {
+                predicatesStatus.add(cb.equal(from.get("status"), status));
+            }
+            predicates.add(cb.or((Predicate[]) predicatesStatus.toArray(new Predicate[0])));
+        }
+        if (deleted != null) {
+            predicates.add(cb.equal(from.get("deleted"), deleted));
+        }
+        if (queryFields != null) {
+            for (int i=0; i<queryFields.length; i++) {
+                String queryValue = queryValues[i];
+
+                // jpa query characters % and _
+                // remove excess % characters
+                if (queryValue.startsWith(PERCENT)) {
+                    while (queryValue.startsWith(PERCENT)) {
+                        queryValue = queryValue.substring(1);
+                    }
+                    queryValue = PERCENT + queryValue;
+                }
+                if (queryValue.endsWith(PERCENT)) {
+                    while (queryValue.endsWith(PERCENT)) {
+                        queryValue = queryValue.substring(0, queryValue.length()-1);
+                    }
+                    queryValue = queryValue + PERCENT;
+                }
+
+                switch (queryFields[i]) {
+                    case UUID:
+                        predicates.add(cb.and(cb.equal(from.get("uuid"), queryValue)));
+                        break;
+                    case NAME:
+                        predicates.add(cb.and(cb.like(from.get("name"), queryValue)));
+                        break;
+                    case MNEMONIC:
+                        predicates.add(cb.and(cb.like(from.get("mnemonic"), queryValue)));
+                        break;
+                    case MNEMONICEQUIVALENCE:
+                        predicates.add(cb.and(cb.like(from.get("mnemonic_equivalence"), queryValue)));
+                        break;
+                    case MNEMONICPATH:
+                        predicates.add(cb.and(cb.like(from.get("mnemonic"), queryValue)));
+                        break;
+                    case DESCRIPTION:
+                        predicates.add(cb.and(cb.like(from.get("description"), queryValue)));
+                        break;
+                    default:
+                        continue;
+                }
+            }
+        }
+
+        return predicates;
+    }
+
+    /**
+     * Persist system group into persistence context.
+     *
+     * @param systemGroup system group
+     */
+    public void createSystemGroup(SystemGroup systemGroup) {
+        em.persist(systemGroup);
+    }
+
+    /**
+     * Merge system group into persistence context.
+     *
+     * @param systemGroup system group
+     */
+    public void updateSystemGroup(SystemGroup systemGroup) {
+        em.merge(systemGroup);
+    }
+
+}
diff --git a/src/main/java/org/openepics/names/repository/SystemRepository.java b/src/main/java/org/openepics/names/repository/SystemRepository.java
new file mode 100644
index 0000000..de838d4
--- /dev/null
+++ b/src/main/java/org/openepics/names/repository/SystemRepository.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2021 European Spallation Source ERIC.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+package org.openepics.names.repository;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import javax.persistence.TypedQuery;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
+import org.apache.commons.lang3.BooleanUtils;
+import org.openepics.names.repository.model.System;
+import org.openepics.names.rest.beans.FieldStructure;
+import org.openepics.names.rest.beans.Status;
+import org.springframework.stereotype.Repository;
+
+/**
+ * Handle system information in JPA.
+ *
+ * @author Lars Johansson
+ */
+@Repository
+public class SystemRepository {
+
+    private static final String PERCENT = "%";
+
+    @PersistenceContext
+    private EntityManager em;
+
+    /**
+     * Count systems.
+     *
+     * @param statuses statuses
+     * @param deleted deleted
+     * @param queryFields query fields
+     * @param queryValues query values
+     * @return count of systems
+     */
+    public Long countSystems(
+            Status[] statuses, Boolean deleted, FieldStructure[] queryFields, String[] queryValues) {
+        return countSystems(statuses, deleted, queryFields, queryValues, Boolean.FALSE);
+    }
+
+    /**
+     * Count systems.
+     *
+     * @param statuses statuses
+     * @param deleted deleted
+     * @param queryFields query fields
+     * @param queryValues query values
+     * @param includeHistory include history
+     * @return count of systems
+     */
+    public Long countSystems(
+            Status[] statuses, Boolean deleted, FieldStructure[] queryFields, String[] queryValues, Boolean includeHistory) {
+
+        // note
+        //     use of function for mnemonic path
+        // where
+        //     statuses
+        //     deleted
+        //     queryFields, queryValues
+
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+        Root<System> from = cq.from(System.class);
+
+        cq.where(cb.and(preparePredicatesSystems(cb, from, statuses, deleted, queryFields, queryValues, includeHistory).toArray(new Predicate[0])));
+        cq.select(cb.count(from));
+
+        return em.createQuery(cq).getSingleResult();
+    }
+
+    /**
+     * Find systems.
+     *
+     * @param status status
+     * @param deleted deleted
+     * @param queryField query field
+     * @param queryValue query value
+     * @return list of systems
+     */
+    public List<System> readSystems(
+            Status status, Boolean deleted, FieldStructure queryField, String queryValue) {
+
+        return readSystems(
+                status != null ? new Status[] {status} : null,
+                deleted,
+                queryField != null ? new FieldStructure[] {queryField} : null,
+                queryValue != null ? new String[] {queryValue} : null,
+                Boolean.FALSE,
+                null, null, null, null);
+    }
+
+    /**
+     * Find systems.
+     *
+     * @param statuses statuses
+     * @param deleted deleted
+     * @param queryFields query fields
+     * @param queryValues query values
+     * @param orderBy order by
+     * @param isAsc is ascending
+     * @param offset offset
+     * @param limit limit
+     * @return list of systems
+     */
+    public List<System> readSystems(
+            Status[] statuses, Boolean deleted, FieldStructure[] queryFields, String[] queryValues,
+            FieldStructure orderBy, Boolean isAsc, Integer offset, Integer limit) {
+        return readSystems(
+                statuses, deleted, queryFields, queryValues, Boolean.FALSE,
+                orderBy, isAsc, offset, limit);
+    }
+
+    /**
+     * Find systems.
+     *
+     * @param statuses statuses
+     * @param deleted deleted
+     * @param queryFields query fields
+     * @param queryValues query values
+     * @param includeHistory include history
+     * @param orderBy order by
+     * @param isAsc is ascending
+     * @param offset offset
+     * @param limit limit
+     * @return list of systems
+     */
+    public List<System> readSystems(
+            Status[] statuses, Boolean deleted, FieldStructure[] queryFields, String[] queryValues, Boolean includeHistory,
+            FieldStructure orderBy, Boolean isAsc, Integer offset, Integer limit) {
+
+        // note
+        //     use of function for mnemonic path
+        // where
+        //     statuses
+        //     deleted
+        //     queryFields, queryValues
+        // order
+        //     orderBy, isAsc
+        // paging
+        //     offset, limit
+
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<System> cq = cb.createQuery(System.class);
+        Root<System> from = cq.from(System.class);
+
+        cq.where(cb.and(preparePredicatesSystems(cb, from, statuses, deleted, queryFields, queryValues, includeHistory).toArray(new Predicate[0])));
+        cq.select(from);
+
+        if (orderBy != null) {
+            if (BooleanUtils.toBoolean(isAsc)) {
+                if (FieldStructure.NAME.equals(orderBy)) {
+                    cq.orderBy(cb.asc(from.get("name")));
+                } else if (FieldStructure.MNEMONIC.equals(orderBy)) {
+                    cq.orderBy(cb.asc(from.get("mnemonic")));
+                } else if (FieldStructure.MNEMONICEQUIVALENCE.equals(orderBy)) {
+                    cq.orderBy(cb.asc(from.get("mnemonic_equivalence")));
+                } else if (FieldStructure.MNEMONICPATH.equals(orderBy)) {
+                    cq.orderBy(cb.asc(cb.function("get_mnemonic_path_system", String.class, from.get("uuid"))));
+                } else if (FieldStructure.DESCRIPTION.equals(orderBy)) {
+                    cq.orderBy(cb.asc(from.get("description")));
+                } else {
+                    cq.orderBy(cb.asc(from.get("convention_name")));
+                }
+            } else {
+                if (FieldStructure.NAME.equals(orderBy)) {
+                    cq.orderBy(cb.desc(from.get("name")));
+                } else if (FieldStructure.MNEMONIC.equals(orderBy)) {
+                    cq.orderBy(cb.desc(from.get("mnemonic")));
+                } else if (FieldStructure.MNEMONICEQUIVALENCE.equals(orderBy)) {
+                    cq.orderBy(cb.desc(from.get("mnemonic_equivalence")));
+                } else if (FieldStructure.MNEMONICPATH.equals(orderBy)) {
+                    cq.orderBy(cb.desc(cb.function("get_mnemonic_path_system", String.class, from.get("uuid"))));
+                } else if (FieldStructure.DESCRIPTION.equals(orderBy)) {
+                    cq.orderBy(cb.desc(from.get("description")));
+                } else {
+                    cq.orderBy(cb.desc(from.get("name")));
+                }
+            }
+        }
+
+        TypedQuery<System> query = em.createQuery(cq);
+        if (offset != null && limit != null) {
+            query.setFirstResult(offset * limit);
+            query.setMaxResults(limit);
+        }
+
+        return query.getResultList();
+    }
+
+    /**
+     * Prepare predicates for systems.
+     *
+     * @param cb criteria builder
+     * @param from criteria query root
+     * @param statuses statuses
+     * @param deleted deleted
+     * @param queryFields query fields
+     * @param queryValues query values
+     * @param includeHistory include history
+     * @return list of predicates
+     */
+    private List<Predicate> preparePredicatesSystems(
+            CriteriaBuilder cb, Root<System> from,
+            Status[] statuses, Boolean deleted, FieldStructure[] queryFields, String[] queryValues, Boolean includeHistory) {
+
+        List<Predicate> predicates = new ArrayList<>();
+
+        if (!Boolean.TRUE.equals(includeHistory)) {
+            // exclude (approved and not latest)
+            Predicate predicateApproved  = cb.equal(from.get("status"), Status.APPROVED);
+            Predicate predicateNotLatest = cb.equal(from.get("latest"), Boolean.FALSE);
+            Predicate predicateExclude   = cb.not(cb.and(predicateApproved, predicateNotLatest));
+            predicates.add(predicateExclude);
+
+        }
+
+        if (statuses != null) {
+            List<Predicate> predicatesStatus = new ArrayList<>();
+            for (Status status : statuses) {
+                predicatesStatus.add(cb.equal(from.get("status"), status));
+            }
+            predicates.add(cb.or((Predicate[]) predicatesStatus.toArray(new Predicate[0])));
+        }
+        if (deleted != null) {
+            predicates.add(cb.equal(from.get("deleted"), deleted));
+        }
+        if (queryFields != null) {
+            for (int i=0; i<queryFields.length; i++) {
+                String queryValue = queryValues[i];
+
+                // jpa query characters % and _
+                // remove excess % characters
+                if (queryValue.startsWith(PERCENT)) {
+                    while (queryValue.startsWith(PERCENT)) {
+                        queryValue = queryValue.substring(1);
+                    }
+                    queryValue = PERCENT + queryValue;
+                }
+                if (queryValue.endsWith(PERCENT)) {
+                    while (queryValue.endsWith(PERCENT)) {
+                        queryValue = queryValue.substring(0, queryValue.length()-1);
+                    }
+                    queryValue = queryValue + PERCENT;
+                }
+
+                switch (queryFields[i]) {
+                    case UUID:
+                        predicates.add(cb.and(cb.equal(from.get("uuid"), queryValue)));
+                        break;
+                    case PARENT:
+                        predicates.add(cb.and(cb.equal(from.get("parent_uuid"), queryValue)));
+                        break;
+                    case NAME:
+                        predicates.add(cb.and(cb.like(from.get("name"), queryValue)));
+                        break;
+                    case MNEMONIC:
+                        predicates.add(cb.and(cb.like(from.get("mnemonic"), queryValue)));
+                        break;
+                    case MNEMONICEQUIVALENCE:
+                        predicates.add(cb.and(cb.like(from.get("mnemonic_equivalence"), queryValue)));
+                        break;
+                    case MNEMONICPATH:
+                        predicates.add(cb.and(cb.like(cb.function("get_mnemonic_path_system", String.class, from.get("uuid")), queryValue)));
+                        break;
+                    case DESCRIPTION:
+                        predicates.add(cb.and(cb.like(from.get("description"), queryValue)));
+                        break;
+                    default:
+                        continue;
+                }
+            }
+        }
+
+        return predicates;
+    }
+
+    /**
+     * Persist system into persistence context.
+     *
+     * @param system system
+     */
+    public void createSystem(System system) {
+        em.persist(system);
+    }
+
+    /**
+     * Merge system into persistence context.
+     *
+     * @param system system
+     */
+    public void updateSystem(System system) {
+        em.merge(system);
+    }
+
+}
diff --git a/src/main/java/org/openepics/names/rest/controller/NamesController.java b/src/main/java/org/openepics/names/rest/controller/NamesController.java
new file mode 100644
index 0000000..f8cd417
--- /dev/null
+++ b/src/main/java/org/openepics/names/rest/controller/NamesController.java
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2021 European Spallation Source ERIC.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+package org.openepics.names.rest.controller;
+
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.openepics.names.rest.api.v1.INames;
+import org.openepics.names.rest.beans.FieldName;
+import org.openepics.names.rest.beans.NameElement;
+import org.openepics.names.service.NamesService;
+import org.openepics.names.util.ExceptionUtil;
+import org.openepics.names.util.LogUtil;
+import org.openepics.names.util.ServiceHttpStatusException;
+import org.openepics.names.util.response.Response;
+import org.openepics.names.util.response.ResponseBoolean;
+import org.openepics.names.util.response.ResponseBooleanList;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.google.common.collect.Lists;
+
+/**
+ * This part of REST API provides name data for Naming application.
+ *
+ * @author Lars Johansson
+ */
+@RestController
+@EnableAutoConfiguration
+public class NamesController implements INames {
+
+    // note
+    //     global exception handler available
+
+    private static final Logger LOGGER = Logger.getLogger(NamesController.class.getName());
+
+    private NamesService namesService;
+
+    @Autowired
+    public NamesController(
+            NamesService namesService) {
+        this.namesService = namesService;
+    }
+
+    @Override
+    public List<NameElement> createNames(List<NameElement> nameElements) {
+        // validate authority
+        //     naming user & admin
+        // validate
+        // do
+
+        try {
+            namesService.validateNamesCreate(nameElements);
+            return namesService.createNames(nameElements);
+        } catch (ServiceHttpStatusException e) {
+            LogUtil.logServiceHttpStatusException(LOGGER, Level.SEVERE, e);
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        } catch (Exception e) {
+            LOGGER.log(Level.SEVERE, e.getMessage());
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        }
+    }
+
+    // ----------------------------------------------------------------------------------------------------
+
+    @Override
+    public List<NameElement> readNames(
+            Boolean deleted, FieldName[] queryFields, String[] queryValues,
+            FieldName orderBy, Boolean isAsc, Integer offset, Integer limit) {
+        try {
+            return namesService.readNames(
+                    deleted, queryFields, queryValues,
+                    orderBy, isAsc, offset, limit);
+        } catch (ServiceHttpStatusException e) {
+            LogUtil.logServiceHttpStatusException(LOGGER, Level.SEVERE, e);
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        } catch (Exception e) {
+            LOGGER.log(Level.SEVERE, e.getMessage());
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        }
+    }
+
+    @Override
+    public List<NameElement> readNames(
+            String name,
+            FieldName orderBy, Boolean isAsc, Integer offset, Integer limit) {
+        try {
+            return namesService.readNames(name, orderBy, isAsc, offset, limit);
+        } catch (ServiceHttpStatusException e) {
+            LogUtil.logServiceHttpStatusException(LOGGER, Level.SEVERE, e);
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        } catch (Exception e) {
+            LOGGER.log(Level.SEVERE, e.getMessage());
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        }
+    }
+
+    @Override
+    public List<NameElement> readNamesSystemStructure(
+            String mnemonicpath,
+            FieldName orderBy, Boolean isAsc, Integer offset, Integer limit) {
+        try {
+            return namesService.readNamesSystemStructure(mnemonicpath, orderBy, isAsc, offset, limit);
+        } catch (ServiceHttpStatusException e) {
+            LogUtil.logServiceHttpStatusException(LOGGER, Level.SEVERE, e);
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        } catch (Exception e) {
+            LOGGER.log(Level.SEVERE, e.getMessage());
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        }
+    }
+
+    @Override
+    public List<NameElement> readNamesDeviceStructure(
+            String mnemonicpath,
+            FieldName orderBy, Boolean isAsc, Integer offset, Integer limit) {
+        try {
+            return namesService.readNamesDeviceStructure(mnemonicpath, orderBy, isAsc, offset, limit);
+        } catch (ServiceHttpStatusException e) {
+            LogUtil.logServiceHttpStatusException(LOGGER, Level.SEVERE, e);
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        } catch (Exception e) {
+            LOGGER.log(Level.SEVERE, e.getMessage());
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        }
+    }
+
+    @Override
+    public List<NameElement> readNamesHistory(
+            String uuid,
+            FieldName orderBy, Boolean isAsc, Integer offset, Integer limit) {
+        try {
+            return namesService.readNamesHistory(uuid, orderBy, isAsc, offset, limit);
+        } catch (ServiceHttpStatusException e) {
+            LogUtil.logServiceHttpStatusException(LOGGER, Level.SEVERE, e);
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        } catch (Exception e) {
+            LOGGER.log(Level.SEVERE, e.getMessage());
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        }
+    }
+
+    // ----------------------------------------------------------------------------------------------------
+
+    @Override
+    public String equivalenceName(String name) {
+        try {
+            return namesService.equivalenceName(name);
+        } catch (ServiceHttpStatusException e) {
+            LogUtil.logServiceHttpStatusException(LOGGER, Level.SEVERE, e);
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        } catch (Exception e) {
+            LOGGER.log(Level.SEVERE, e.getMessage());
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        }
+    }
+
+    @Override
+    public ResponseEntity<ResponseBoolean> existsName(String name) {
+        try {
+            return new ResponseEntity<>(new ResponseBoolean(namesService.existsName(name)), Response.HEADER_JSON, HttpStatus.OK);
+        } catch (ServiceHttpStatusException e) {
+            LogUtil.logServiceHttpStatusException(LOGGER, Level.SEVERE, e);
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            return new ResponseEntity<>(new ResponseBoolean(Boolean.FALSE, e.getMessage(), e.getDetails()), Response.HEADER_JSON, HttpStatus.OK);
+        } catch (Exception e) {
+            LOGGER.log(Level.SEVERE, e.getMessage());
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            return new ResponseEntity<>(new ResponseBoolean(Boolean.FALSE, ExceptionUtil.OPERATION_COULD_NOT_BE_PERFORMED), Response.HEADER_JSON, HttpStatus.OK);
+        }
+    }
+
+    @Override
+    public ResponseEntity<ResponseBoolean> isLegacyName(String name) {
+        try {
+            return new ResponseEntity<>(new ResponseBoolean(namesService.isLegacyName(name)), Response.HEADER_JSON, HttpStatus.OK);
+        } catch (ServiceHttpStatusException e) {
+            LogUtil.logServiceHttpStatusException(LOGGER, Level.SEVERE, e);
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            return new ResponseEntity<>(new ResponseBoolean(Boolean.FALSE, e.getMessage(), e.getDetails()), Response.HEADER_JSON, HttpStatus.OK);
+        } catch (Exception e) {
+            LOGGER.log(Level.SEVERE, e.getMessage());
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            return new ResponseEntity<>(new ResponseBoolean(Boolean.FALSE, ExceptionUtil.OPERATION_COULD_NOT_BE_PERFORMED), Response.HEADER_JSON, HttpStatus.OK);
+        }
+    }
+
+    @Override
+    public ResponseEntity<ResponseBoolean> isValidToCreateName(String name) {
+        try {
+            return new ResponseEntity<>(new ResponseBoolean(namesService.isValidToCreateName(name)), Response.HEADER_JSON, HttpStatus.OK);
+        } catch (ServiceHttpStatusException e) {
+            LogUtil.logServiceHttpStatusException(LOGGER, Level.SEVERE, e);
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            return new ResponseEntity<>(new ResponseBoolean(Boolean.FALSE, e.getMessage(), e.getDetails()), Response.HEADER_JSON, HttpStatus.OK);
+        } catch (Exception e) {
+            LOGGER.log(Level.SEVERE, e.getMessage());
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            return new ResponseEntity<>(new ResponseBoolean(Boolean.FALSE, ExceptionUtil.OPERATION_COULD_NOT_BE_PERFORMED), Response.HEADER_JSON, HttpStatus.OK);
+        }
+    }
+
+    // ----------------------------------------------------------------------------------------------------
+
+    @Override
+    public ResponseEntity<ResponseBooleanList> validateNamesCreate(List<NameElement> nameElements) {
+        boolean response = true;
+        String reason = "";
+        List<ResponseBoolean> responses = Lists.newArrayList();
+        for (NameElement nameElement : nameElements) {
+            try {
+                namesService.validateNamesCreate(nameElement);
+                responses.add(new ResponseBoolean(Boolean.TRUE));
+            } catch (ServiceHttpStatusException e) {
+                LogUtil.logServiceHttpStatusException(LOGGER, Level.SEVERE, e);
+                LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+                if (response) {
+                    response = false;
+                    reason = ExceptionUtil.ONE_OR_MORE_ELEMENTS_ARE_NOT_CORRECT;
+                }
+                responses.add(new ResponseBoolean(Boolean.FALSE, e.getMessage(), e.getDetails()));
+            } catch (Exception e) {
+                LOGGER.log(Level.SEVERE, e.getMessage());
+                LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+                if (response) {
+                    response = false;
+                    reason = ExceptionUtil.ONE_OR_MORE_ELEMENTS_ARE_NOT_CORRECT;
+                }
+                responses.add(new ResponseBoolean(Boolean.FALSE, ExceptionUtil.OPERATION_COULD_NOT_BE_PERFORMED));
+            }
+        }
+        return new ResponseEntity<>(new ResponseBooleanList(responses, Boolean.valueOf(response), reason), Response.HEADER_JSON, HttpStatus.OK);
+    }
+
+    @Override
+    public ResponseEntity<ResponseBooleanList> validateNamesUpdate(List<NameElement> nameElements) {
+        boolean response = true;
+        String reason = "";
+        List<ResponseBoolean> responses = Lists.newArrayList();
+        for (NameElement nameElement : nameElements) {
+            try {
+                namesService.validateNamesUpdate(nameElement);
+                responses.add(new ResponseBoolean(Boolean.TRUE));
+            } catch (ServiceHttpStatusException e) {
+                LogUtil.logServiceHttpStatusException(LOGGER, Level.SEVERE, e);
+                LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+                if (response) {
+                    response = false;
+                    reason = ExceptionUtil.ONE_OR_MORE_ELEMENTS_ARE_NOT_CORRECT;
+                }
+                responses.add(new ResponseBoolean(Boolean.FALSE, e.getMessage(), e.getDetails()));
+            } catch (Exception e) {
+                LOGGER.log(Level.SEVERE, e.getMessage());
+                LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+                if (response) {
+                    response = false;
+                    reason = ExceptionUtil.ONE_OR_MORE_ELEMENTS_ARE_NOT_CORRECT;
+                }
+                responses.add(new ResponseBoolean(Boolean.FALSE, ExceptionUtil.OPERATION_COULD_NOT_BE_PERFORMED));
+            }
+        }
+        return new ResponseEntity<>(new ResponseBooleanList(responses, Boolean.valueOf(response), reason), Response.HEADER_JSON, HttpStatus.OK);
+    }
+
+    @Override
+    public ResponseEntity<ResponseBooleanList> validateNamesDelete(List<NameElement> nameElements) {
+        boolean response = true;
+        String reason = "";
+        List<ResponseBoolean> responses = Lists.newArrayList();
+        for (NameElement nameElement : nameElements) {
+            try {
+                namesService.validateNamesDelete(nameElement);
+                responses.add(new ResponseBoolean(Boolean.TRUE));
+            } catch (ServiceHttpStatusException e) {
+                LogUtil.logServiceHttpStatusException(LOGGER, Level.SEVERE, e);
+                LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+                if (response) {
+                    response = false;
+                    reason = ExceptionUtil.ONE_OR_MORE_ELEMENTS_ARE_NOT_CORRECT;
+                }
+                responses.add(new ResponseBoolean(Boolean.FALSE, e.getMessage(), e.getDetails()));
+            } catch (Exception e) {
+                LOGGER.log(Level.SEVERE, e.getMessage());
+                LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+                if (response) {
+                    response = false;
+                    reason = ExceptionUtil.ONE_OR_MORE_ELEMENTS_ARE_NOT_CORRECT;
+                }
+                responses.add(new ResponseBoolean(Boolean.FALSE, ExceptionUtil.OPERATION_COULD_NOT_BE_PERFORMED));
+            }
+        }
+        return new ResponseEntity<>(new ResponseBooleanList(responses, Boolean.valueOf(response), reason), Response.HEADER_JSON, HttpStatus.OK);
+    }
+
+    // ----------------------------------------------------------------------------------------------------
+
+    @Override
+    public List<NameElement> updateNames(List<NameElement> nameElements) {
+        // validate authority
+        //     naming user & admin
+        // validate
+        // do
+
+        try {
+            namesService.validateNamesUpdate(nameElements);
+            return namesService.updateNames(nameElements);
+        } catch (ServiceHttpStatusException e) {
+            LogUtil.logServiceHttpStatusException(LOGGER, Level.SEVERE, e);
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        } catch (Exception e) {
+            LOGGER.log(Level.SEVERE, e.getMessage());
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        }
+    }
+
+    // ----------------------------------------------------------------------------------------------------
+
+    @Override
+    public List<NameElement> deleteNames(List<NameElement> nameElements) {
+        // validate authority
+        //     naming user & admin
+        // validate
+        // do
+
+        try {
+          namesService.validateNamesDelete(nameElements);
+          return namesService.deleteNames(nameElements);
+        } catch (ServiceHttpStatusException e) {
+            LogUtil.logServiceHttpStatusException(LOGGER, Level.SEVERE, e);
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        } catch (Exception e) {
+            LOGGER.log(Level.SEVERE, e.getMessage());
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/openepics/names/rest/controller/StructuresController.java b/src/main/java/org/openepics/names/rest/controller/StructuresController.java
new file mode 100644
index 0000000..9599570
--- /dev/null
+++ b/src/main/java/org/openepics/names/rest/controller/StructuresController.java
@@ -0,0 +1,504 @@
+/*
+ * Copyright (C) 2021 European Spallation Source ERIC.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+package org.openepics.names.rest.controller;
+
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.openepics.names.rest.api.v1.IStructures;
+import org.openepics.names.rest.beans.FieldStructure;
+import org.openepics.names.rest.beans.Status;
+import org.openepics.names.rest.beans.StructureElement;
+import org.openepics.names.rest.beans.Type;
+import org.openepics.names.service.StructuresService;
+import org.openepics.names.util.ExceptionUtil;
+import org.openepics.names.util.LogUtil;
+import org.openepics.names.util.ServiceHttpStatusException;
+import org.openepics.names.util.response.Response;
+import org.openepics.names.util.response.ResponseBoolean;
+import org.openepics.names.util.response.ResponseBooleanList;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.google.common.collect.Lists;
+
+/**
+ * This part of REST API provides structures data for Naming application.
+ *
+ * @author Lars Johansson
+ */
+@RestController
+@EnableAutoConfiguration
+public class StructuresController implements IStructures {
+
+    // note
+    //     global exception handler available
+
+    // TODO validate authority
+    //          either
+    //              no or
+    //              naming user
+    //              naming admin
+    //              naming user & admin
+
+    private static final Logger LOGGER = Logger.getLogger(StructuresController.class.getName());
+
+    private StructuresService structuresService;
+
+    @Autowired
+    public StructuresController(
+            StructuresService structuresService) {
+        this.structuresService = structuresService;
+    }
+
+    @Override
+    public List<StructureElement> createStructures(List<StructureElement> structureElements) {
+        // validate authority
+        //     naming user & admin
+        // validate
+        // do
+
+        try {
+            structuresService.validateStructuresCreate(structureElements);
+            return structuresService.createStructures(structureElements);
+        } catch (ServiceHttpStatusException e) {
+            LogUtil.logServiceHttpStatusException(LOGGER, Level.SEVERE, e);
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        } catch (Exception e) {
+            LOGGER.log(Level.SEVERE, e.getMessage());
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        }
+    }
+
+    // ----------------------------------------------------------------------------------------------------
+
+    @Override
+    public List<StructureElement> readStructures(
+            Type type, Status[] statuses, Boolean deleted, FieldStructure[] queryFields, String[] queryValues,
+            FieldStructure orderBy, Boolean isAsc, Integer offset, Integer limit) {
+        try {
+            return structuresService.readStructures(
+                    type, statuses, deleted, queryFields, queryValues,
+                    orderBy, isAsc, offset, limit);
+        } catch (ServiceHttpStatusException e) {
+            LogUtil.logServiceHttpStatusException(LOGGER, Level.SEVERE, e);
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        } catch (Exception e) {
+            LOGGER.log(Level.SEVERE, e.getMessage());
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        }
+    }
+
+    @Override
+    public List<StructureElement> readStructuresChildren(
+            Type type, String uuid,
+            FieldStructure orderBy, Boolean isAsc, Integer offset, Integer limit) {
+        try {
+            return structuresService.readStructuresChildren(
+                    type, uuid,
+                    orderBy, isAsc, offset, limit);
+        } catch (ServiceHttpStatusException e) {
+            LogUtil.logServiceHttpStatusException(LOGGER, Level.SEVERE, e);
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        } catch (Exception e) {
+            LOGGER.log(Level.SEVERE, e.getMessage());
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        }
+    }
+
+    @Override
+    public List<StructureElement> readStructuresMnemonic(
+            String mnemonic,
+            FieldStructure orderBy, Boolean isAsc, Integer offset, Integer limit) {
+        try {
+            return structuresService.readStructuresMnemonic(mnemonic, orderBy, isAsc, offset, limit);
+        } catch (ServiceHttpStatusException e) {
+            LogUtil.logServiceHttpStatusException(LOGGER, Level.SEVERE, e);
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        } catch (Exception e) {
+            LOGGER.log(Level.SEVERE, e.getMessage());
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        }
+    }
+
+    @Override
+    public List<StructureElement> readStructuresMnemonicpath(
+            String mnemonicpath,
+            FieldStructure orderBy, Boolean isAsc, Integer offset, Integer limit) {
+        try {
+            return structuresService.readStructuresMnemonicpath(mnemonicpath, orderBy, isAsc, offset, limit);
+        } catch (ServiceHttpStatusException e) {
+            LogUtil.logServiceHttpStatusException(LOGGER, Level.SEVERE, e);
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        } catch (Exception e) {
+            LOGGER.log(Level.SEVERE, e.getMessage());
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        }
+    }
+
+    @Override
+    public List<StructureElement> readStructuresHistory(
+            String uuid, Type type,
+            FieldStructure orderBy, Boolean isAsc, Integer offset, Integer limit) {
+        try {
+            return structuresService.readStructuresHistory(
+                    uuid, type,
+                    orderBy, isAsc, offset, limit);
+        } catch (ServiceHttpStatusException e) {
+            LogUtil.logServiceHttpStatusException(LOGGER, Level.SEVERE, e);
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        } catch (Exception e) {
+            LOGGER.log(Level.SEVERE, e.getMessage());
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        }
+    }
+
+    // ----------------------------------------------------------------------------------------------------
+
+    @Override
+    public String equivalenceMnemonic(String mnemonic) {
+        try {
+            return structuresService.equivalenceMnemonic(mnemonic);
+        } catch (ServiceHttpStatusException e) {
+            LogUtil.logServiceHttpStatusException(LOGGER, Level.SEVERE, e);
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        } catch (Exception e) {
+            LOGGER.log(Level.SEVERE, e.getMessage());
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        }
+    }
+
+    @Override
+    public ResponseEntity<ResponseBoolean> existsStructure(Type type, String mnemonic) {
+        try {
+            return new ResponseEntity<>(new ResponseBoolean(structuresService.existsStructure(type, mnemonic)), Response.HEADER_JSON, HttpStatus.OK);
+        } catch (ServiceHttpStatusException e) {
+            LogUtil.logServiceHttpStatusException(LOGGER, Level.SEVERE, e);
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            return new ResponseEntity<>(new ResponseBoolean(Boolean.FALSE, e.getMessage(), e.getDetails()), Response.HEADER_JSON, HttpStatus.OK);
+        } catch (Exception e) {
+            LOGGER.log(Level.SEVERE, e.getMessage());
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            return new ResponseEntity<>(new ResponseBoolean(Boolean.FALSE, ExceptionUtil.OPERATION_COULD_NOT_BE_PERFORMED), Response.HEADER_JSON, HttpStatus.OK);
+        }
+    }
+
+    @Override
+    public ResponseEntity<ResponseBoolean> isValidToCreateStructure(Type type, String mnemonicpath) {
+        try {
+            return new ResponseEntity<>(new ResponseBoolean(structuresService.isValidToCreateStructure(type, mnemonicpath)), Response.HEADER_JSON, HttpStatus.OK);
+        } catch (ServiceHttpStatusException e) {
+            LogUtil.logServiceHttpStatusException(LOGGER, Level.SEVERE, e);
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            return new ResponseEntity<>(new ResponseBoolean(Boolean.FALSE, e.getMessage(), e.getDetails()), Response.HEADER_JSON, HttpStatus.OK);
+        } catch (Exception e) {
+            LOGGER.log(Level.SEVERE, e.getMessage());
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            return new ResponseEntity<>(new ResponseBoolean(Boolean.FALSE, ExceptionUtil.OPERATION_COULD_NOT_BE_PERFORMED), Response.HEADER_JSON, HttpStatus.OK);
+        }
+    }
+
+    // ----------------------------------------------------------------------------------------------------
+
+    @Override
+    public ResponseEntity<ResponseBooleanList> validateStructuresCreate(List<StructureElement> structureElements) {
+        boolean response = true;
+        String reason = "";
+        List<ResponseBoolean> responses = Lists.newArrayList();
+        for (StructureElement structureElement : structureElements) {
+            try {
+                structuresService.validateStructuresCreate(structureElement);
+                responses.add(new ResponseBoolean(Boolean.TRUE));
+            } catch (ServiceHttpStatusException e) {
+                LogUtil.logServiceHttpStatusException(LOGGER, Level.SEVERE, e);
+                LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+                if (response) {
+                    response = false;
+                    reason = ExceptionUtil.ONE_OR_MORE_ELEMENTS_ARE_NOT_CORRECT;
+                }
+                responses.add(new ResponseBoolean(Boolean.FALSE, e.getMessage(), e.getDetails()));
+            } catch (Exception e) {
+                LOGGER.log(Level.SEVERE, e.getMessage());
+                LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+                if (response) {
+                    response = false;
+                    reason = ExceptionUtil.ONE_OR_MORE_ELEMENTS_ARE_NOT_CORRECT;
+                }
+                responses.add(new ResponseBoolean(Boolean.FALSE, ExceptionUtil.OPERATION_COULD_NOT_BE_PERFORMED));
+            }
+        }
+        return new ResponseEntity<>(new ResponseBooleanList(responses, Boolean.valueOf(response), reason), Response.HEADER_JSON, HttpStatus.OK);
+    }
+
+    @Override
+    public ResponseEntity<ResponseBooleanList> validateStructuresUpdate(List<StructureElement> structureElements) {
+        boolean response = true;
+        String reason = "";
+        List<ResponseBoolean> responses = Lists.newArrayList();
+        for (StructureElement structureElement : structureElements) {
+            try {
+                structuresService.validateStructuresUpdate(structureElement);
+                responses.add(new ResponseBoolean(Boolean.TRUE));
+            } catch (ServiceHttpStatusException e) {
+                LogUtil.logServiceHttpStatusException(LOGGER, Level.SEVERE, e);
+                LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+                if (response) {
+                    response = false;
+                    reason = ExceptionUtil.ONE_OR_MORE_ELEMENTS_ARE_NOT_CORRECT;
+                }
+                responses.add(new ResponseBoolean(Boolean.FALSE, e.getMessage(), e.getDetails()));
+            } catch (Exception e) {
+                LOGGER.log(Level.SEVERE, e.getMessage());
+                LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+                if (response) {
+                    response = false;
+                    reason = ExceptionUtil.ONE_OR_MORE_ELEMENTS_ARE_NOT_CORRECT;
+                }
+                responses.add(new ResponseBoolean(Boolean.FALSE, ExceptionUtil.OPERATION_COULD_NOT_BE_PERFORMED));
+            }
+        }
+        return new ResponseEntity<>(new ResponseBooleanList(responses, Boolean.valueOf(response), reason), Response.HEADER_JSON, HttpStatus.OK);
+    }
+
+    @Override
+    public ResponseEntity<ResponseBooleanList> validateStructuresDelete(List<StructureElement> structureElements) {
+        boolean response = true;
+        String reason = "";
+        List<ResponseBoolean> responses = Lists.newArrayList();
+        for (StructureElement structureElement : structureElements) {
+            try {
+                structuresService.validateStructuresDelete(structureElement);
+                responses.add(new ResponseBoolean(Boolean.TRUE));
+            } catch (ServiceHttpStatusException e) {
+                LogUtil.logServiceHttpStatusException(LOGGER, Level.SEVERE, e);
+                LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+                if (response) {
+                    response = false;
+                    reason = ExceptionUtil.ONE_OR_MORE_ELEMENTS_ARE_NOT_CORRECT;
+                }
+                responses.add(new ResponseBoolean(Boolean.FALSE, e.getMessage(), e.getDetails()));
+            } catch (Exception e) {
+                LOGGER.log(Level.SEVERE, e.getMessage());
+                LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+                if (response) {
+                    response = false;
+                    reason = ExceptionUtil.ONE_OR_MORE_ELEMENTS_ARE_NOT_CORRECT;
+                }
+                responses.add(new ResponseBoolean(Boolean.FALSE, ExceptionUtil.OPERATION_COULD_NOT_BE_PERFORMED));
+            }
+        }
+        return new ResponseEntity<>(new ResponseBooleanList(responses, Boolean.valueOf(response), reason), Response.HEADER_JSON, HttpStatus.OK);
+    }
+
+    @Override
+    public ResponseEntity<ResponseBooleanList> validateStructuresApprove(List<StructureElement> structureElements) {
+        boolean response = true;
+        String reason = "";
+        List<ResponseBoolean> responses = Lists.newArrayList();
+        for (StructureElement structureElement : structureElements) {
+            try {
+                structuresService.validateStructuresApprove(structureElement);
+                responses.add(new ResponseBoolean(Boolean.TRUE));
+            } catch (ServiceHttpStatusException e) {
+                LogUtil.logServiceHttpStatusException(LOGGER, Level.SEVERE, e);
+                LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+                if (response) {
+                    response = false;
+                    reason = ExceptionUtil.ONE_OR_MORE_ELEMENTS_ARE_NOT_CORRECT;
+                }
+                responses.add(new ResponseBoolean(Boolean.FALSE, e.getMessage(), e.getDetails()));
+            } catch (Exception e) {
+                LOGGER.log(Level.SEVERE, e.getMessage());
+                LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+                if (response) {
+                    response = false;
+                    reason = ExceptionUtil.ONE_OR_MORE_ELEMENTS_ARE_NOT_CORRECT;
+                }
+                responses.add(new ResponseBoolean(Boolean.FALSE, ExceptionUtil.OPERATION_COULD_NOT_BE_PERFORMED));
+            }
+        }
+        return new ResponseEntity<>(new ResponseBooleanList(responses, Boolean.valueOf(response), reason), Response.HEADER_JSON, HttpStatus.OK);
+    }
+
+    @Override
+    public ResponseEntity<ResponseBooleanList> validateStructuresCancel(List<StructureElement> structureElements) {
+        boolean response = true;
+        String reason = "";
+        List<ResponseBoolean> responses = Lists.newArrayList();
+        for (StructureElement structureElement : structureElements) {
+            try {
+                structuresService.validateStructuresCancel(structureElement);
+                responses.add(new ResponseBoolean(Boolean.TRUE));
+            } catch (ServiceHttpStatusException e) {
+                LogUtil.logServiceHttpStatusException(LOGGER, Level.SEVERE, e);
+                LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+                if (response) {
+                    response = false;
+                    reason = ExceptionUtil.ONE_OR_MORE_ELEMENTS_ARE_NOT_CORRECT;
+                }
+                responses.add(new ResponseBoolean(Boolean.FALSE, e.getMessage(), e.getDetails()));
+            } catch (Exception e) {
+                LOGGER.log(Level.SEVERE, e.getMessage());
+                LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+                if (response) {
+                    response = false;
+                    reason = ExceptionUtil.ONE_OR_MORE_ELEMENTS_ARE_NOT_CORRECT;
+                }
+                responses.add(new ResponseBoolean(Boolean.FALSE, ExceptionUtil.OPERATION_COULD_NOT_BE_PERFORMED));
+            }
+        }
+        return new ResponseEntity<>(new ResponseBooleanList(responses, Boolean.valueOf(response), reason), Response.HEADER_JSON, HttpStatus.OK);
+    }
+
+    @Override
+    public ResponseEntity<ResponseBooleanList> validateStructuresReject(List<StructureElement> structureElements) {
+        boolean response = true;
+        String reason = "";
+        List<ResponseBoolean> responses = Lists.newArrayList();
+        for (StructureElement structureElement : structureElements) {
+            try {
+                structuresService.validateStructuresReject(structureElement);
+                responses.add(new ResponseBoolean(Boolean.TRUE));
+            } catch (ServiceHttpStatusException e) {
+                LogUtil.logServiceHttpStatusException(LOGGER, Level.SEVERE, e);
+                LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+                if (response) {
+                    response = false;
+                    reason = ExceptionUtil.ONE_OR_MORE_ELEMENTS_ARE_NOT_CORRECT;
+                }
+                responses.add(new ResponseBoolean(Boolean.FALSE, e.getMessage(), e.getDetails()));
+            } catch (Exception e) {
+                LOGGER.log(Level.SEVERE, e.getMessage());
+                LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+                if (response) {
+                    response = false;
+                    reason = ExceptionUtil.ONE_OR_MORE_ELEMENTS_ARE_NOT_CORRECT;
+                }
+                responses.add(new ResponseBoolean(Boolean.FALSE, ExceptionUtil.OPERATION_COULD_NOT_BE_PERFORMED));
+            }
+        }
+        return new ResponseEntity<>(new ResponseBooleanList(responses, Boolean.valueOf(response), reason), Response.HEADER_JSON, HttpStatus.OK);
+    }
+
+    // ----------------------------------------------------------------------------------------------------
+
+    @Override
+    public List<StructureElement> updateStructures(List<StructureElement> structureElements) {
+        try {
+            structuresService.validateStructuresUpdate(structureElements);
+            return structuresService.updateStructures(structureElements);
+        } catch (ServiceHttpStatusException e) {
+            LogUtil.logServiceHttpStatusException(LOGGER, Level.SEVERE, e);
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        } catch (Exception e) {
+            LOGGER.log(Level.SEVERE, e.getMessage());
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        }
+    }
+
+
+    // ----------------------------------------------------------------------------------------------------
+
+    @Override
+    public List<StructureElement> deleteStructures(List<StructureElement> structureElements) {
+        try {
+            structuresService.validateStructuresDelete(structureElements);
+            return structuresService.deleteStructures(structureElements);
+        } catch (ServiceHttpStatusException e) {
+            LogUtil.logServiceHttpStatusException(LOGGER, Level.SEVERE, e);
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        } catch (Exception e) {
+            LOGGER.log(Level.SEVERE, e.getMessage());
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        }
+    }
+
+    // ----------------------------------------------------------------------------------------------------
+
+    @Override
+    public List<StructureElement> approveStructures(List<StructureElement> structureElements) {
+        try {
+            structuresService.validateStructuresApprove(structureElements);
+            return structuresService.approveStructures(structureElements);
+        } catch (ServiceHttpStatusException e) {
+            LogUtil.logServiceHttpStatusException(LOGGER, Level.SEVERE, e);
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        } catch (Exception e) {
+            LOGGER.log(Level.SEVERE, e.getMessage());
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        }
+    }
+
+    @Override
+    public List<StructureElement> cancelStructures(List<StructureElement> structureElements) {
+        try {
+            structuresService.validateStructuresCancel(structureElements);
+            return structuresService.cancelStructures(structureElements);
+        } catch (ServiceHttpStatusException e) {
+            LogUtil.logServiceHttpStatusException(LOGGER, Level.SEVERE, e);
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        } catch (Exception e) {
+            LOGGER.log(Level.SEVERE, e.getMessage());
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        }
+    }
+
+    @Override
+    public List<StructureElement> rejectStructures(List<StructureElement> structureElements) {
+        try {
+            structuresService.validateStructuresReject(structureElements);
+            return structuresService.rejectStructures(structureElements);
+        } catch (ServiceHttpStatusException e) {
+            LogUtil.logServiceHttpStatusException(LOGGER, Level.SEVERE, e);
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        } catch (Exception e) {
+            LOGGER.log(Level.SEVERE, e.getMessage());
+            LogUtil.logStackTraceElements(LOGGER, Level.SEVERE, e);
+            throw e;
+        }
+    }
+
+}
diff --git a/src/main/java/org/openepics/names/service/NamesService.java b/src/main/java/org/openepics/names/service/NamesService.java
new file mode 100644
index 0000000..0c16cee
--- /dev/null
+++ b/src/main/java/org/openepics/names/service/NamesService.java
@@ -0,0 +1,692 @@
+/*
+ * Copyright (C) 2021 European Spallation Source ERIC.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+package org.openepics.names.service;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+import java.util.UUID;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.openepics.names.repository.IDeviceGroupRepository;
+import org.openepics.names.repository.IDeviceTypeRepository;
+import org.openepics.names.repository.IDisciplineRepository;
+import org.openepics.names.repository.INameRepository;
+import org.openepics.names.repository.ISubsystemRepository;
+import org.openepics.names.repository.ISystemGroupRepository;
+import org.openepics.names.repository.ISystemRepository;
+import org.openepics.names.repository.NameRepository;
+import org.openepics.names.repository.model.DeviceGroup;
+import org.openepics.names.repository.model.DeviceType;
+import org.openepics.names.repository.model.Discipline;
+import org.openepics.names.repository.model.Name;
+import org.openepics.names.repository.model.Subsystem;
+import org.openepics.names.repository.model.SystemGroup;
+import org.openepics.names.rest.beans.FieldName;
+import org.openepics.names.rest.beans.NameElement;
+import org.openepics.names.rest.beans.Status;
+import org.openepics.names.util.EssNamingConvention;
+import org.openepics.names.util.HolderIRepositories;
+import org.openepics.names.util.HolderSystemDeviceStructure;
+import org.openepics.names.util.NameElementUtil;
+import org.openepics.names.util.ValidateUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.google.common.collect.Lists;
+
+/**
+ * This class provides names services.
+ *
+ * @author Lars Johansson
+ */
+@Service
+public class NamesService {
+
+    // HolderIRepositories and HolderSystemDeviceStructure may or may not be used for preparation of what to return
+
+    // for each method
+    //     document what values come from NameElement and what values come from persistence layer
+    //     somehow provide this information to user
+
+    private static final Logger LOGGER = Logger.getLogger(NamesService.class.getName());
+
+    private EssNamingConvention namingConvention;
+
+    private HolderIRepositories holderIRepositories;
+    private NameRepository nameRepository;
+
+    @Autowired
+    public NamesService(
+            INameRepository iNameRepository,
+            ISystemGroupRepository iSystemGroupRepository,
+            ISystemRepository iSystemRepository,
+            ISubsystemRepository iSubsystemRepository,
+            IDisciplineRepository iDisciplineRepository,
+            IDeviceGroupRepository iDeviceGroupRepository,
+            IDeviceTypeRepository iDeviceTypeRepository,
+            NameRepository nameRepository) {
+
+        this.namingConvention = new EssNamingConvention();
+        this.holderIRepositories = new HolderIRepositories(
+                iNameRepository,
+                iSystemGroupRepository,
+                iSystemRepository,
+                iSubsystemRepository,
+                iDisciplineRepository,
+                iDeviceGroupRepository,
+                iDeviceTypeRepository);
+        this.nameRepository = nameRepository;
+    }
+
+    @Transactional
+    public List<NameElement> createNames(List<NameElement> nameElements) {
+        // validate
+        //     outside of @Transactional
+        // transaction
+        //     do
+        //         for each name element
+        //             create name, latest, with data
+        //             add name element for created
+        //     return
+        //         name elements for created names
+
+        LOGGER.log(Level.INFO, "createNames, nameElements.size:        " + String.valueOf(nameElements != null ? nameElements.size() : "null"));
+
+        // do
+        String requestedBy = "test who";
+        final List<NameElement> createdNameElements = Lists.newArrayList();
+        for (NameElement nameElement : nameElements) {
+            // create
+            Name name = new Name();
+            setAttributes(name,
+                    UUID.randomUUID(),
+                    nameElement.getSystemgroup(), nameElement.getSystem(), nameElement.getSubsystem(), nameElement.getDevicetype(),
+                    nameElement.getIndex(), nameElement.getName(), namingConvention.equivalenceClassRepresentative(nameElement.getName()),
+                    nameElement.getDescription(), Status.APPROVED, Boolean.TRUE, Boolean.FALSE,
+                    new Date(), requestedBy, nameElement.getComment());
+
+            nameRepository.createName(name);
+
+            // possibly validate that created
+            //     approved, latest, not deleted, uuid
+
+            // add
+            createdNameElements.add(NameElementUtil.getNameElement(name));
+        }
+
+        LOGGER.log(Level.INFO, "createNames, createdNameElements.size: " + createdNameElements.size());
+        return createdNameElements;
+    }
+
+    // ----------------------------------------------------------------------------------------------------
+
+    public List<NameElement> readNames(
+            Boolean deleted, FieldName[] queryFields, String[] queryValues,
+            FieldName orderBy, Boolean isAsc, Integer offset, Integer limit) {
+        return readNames(deleted, queryFields, queryValues, Boolean.FALSE, orderBy, isAsc, offset, limit);
+    }
+
+    public List<NameElement> readNames(
+            Boolean deleted, FieldName[] queryFields, String[] queryValues, Boolean includeHistory,
+            FieldName orderBy, Boolean isAsc, Integer offset, Integer limit) {
+
+        LOGGER.log(Level.INFO, "readNames, deleted:            " + deleted);
+        LOGGER.log(Level.INFO, "readNames, queryFields.length: " + String.valueOf(queryFields != null ? queryFields.length : "null"));
+        LOGGER.log(Level.INFO, "readNames, queryValues.length: " + String.valueOf(queryValues != null ? queryValues.length : "null"));
+        LOGGER.log(Level.INFO, "readNames, includeHistory:     " + includeHistory);
+        LOGGER.log(Level.INFO, "readNames, orderBy:            " + orderBy);
+        LOGGER.log(Level.INFO, "readNames, isAsc:              " + isAsc);
+        LOGGER.log(Level.INFO, "readNames, offset:             " + offset);
+        LOGGER.log(Level.INFO, "readNames, limit:              " + limit);
+        if (queryFields != null && queryFields.length > 0) {
+            for (FieldName queryField : queryFields) {
+                LOGGER.log(Level.INFO, "readNames, queryField:         " + queryField);
+            }
+        }
+        if (queryValues != null && queryValues.length > 0) {
+            for (String queryValue : queryValues) {
+                LOGGER.log(Level.INFO, "readNames, queryValue:         " + queryValue);
+            }
+        }
+
+        // validate input
+        //     queryFields and queryValues
+        //         uuid
+        // do
+        //     read names
+        // return
+        //     name elements for names
+
+        // validate input
+        ValidateUtil.validateNamesInputRead(
+                deleted, queryFields, queryValues,
+                includeHistory,
+                orderBy, isAsc,
+                offset, limit);
+
+        // do
+        List<Name> names = nameRepository.readNames(deleted, queryFields, queryValues, includeHistory, orderBy, isAsc, offset, limit);
+
+        final List<NameElement> nameElements = Lists.newArrayList();
+        for (Name name : names) {
+            nameElements.add(NameElementUtil.getNameElement(name));
+        }
+
+        LOGGER.log(Level.INFO, "readNames, nameElements.size:  " + nameElements.size());
+        return nameElements;
+    }
+
+    public List<NameElement> readNames(
+            String name,
+            FieldName orderBy, Boolean isAsc, Integer offset, Integer limit) {
+
+        LOGGER.log(Level.INFO, "readNames, name:              " + name);
+
+        // validate input
+        //     name or uuid
+        //     to work with both uuid and name
+        // do
+        //     name or uuid
+        // return
+        //     name elements
+
+        // validate input
+        ValidateUtil.validateInputName(name);
+
+        // do
+        final List<NameElement> nameElements = Lists.newArrayList();
+        try {
+            UUID.fromString(name);
+            Name latestByUuid = holderIRepositories.getNameRepository().findLatestByUuid(name);
+            if (latestByUuid != null) {
+                nameElements.add(NameElementUtil.getNameElement(latestByUuid));
+            }
+        } catch (IllegalArgumentException e) {
+            nameElements.addAll(readNames(false, new FieldName[] {FieldName.NAME}, new String[] {name}, orderBy, isAsc, offset, limit));
+        }
+
+        LOGGER.log(Level.INFO, "readNames, nameElements.size: " + nameElements.size());
+        return nameElements;
+    }
+
+    public List<NameElement> readNamesSystemStructure(
+            String mnemonicpath,
+            FieldName orderBy, Boolean isAsc, Integer offset, Integer limit) {
+
+        LOGGER.log(Level.INFO, "readNamesSystemStructure, mnemonicpath: " + mnemonicpath);
+
+        // validate input
+        //     mnemonic path
+        // do
+        //     mnemonic path
+        // return
+        //     name elements
+
+        // validate input
+        ValidateUtil.validateInputMnemonicpath(mnemonicpath);
+
+        // do
+        return readNames(false, new FieldName[] {FieldName.SYSTEMSTRUCTURE}, new String[] {mnemonicpath}, orderBy, isAsc, offset, limit);
+    }
+
+    public List<NameElement> readNamesDeviceStructure(
+            String mnemonicpath,
+            FieldName orderBy, Boolean isAsc, Integer offset, Integer limit) {
+
+        LOGGER.log(Level.INFO, "readNamesDeviceStructure, mnemonicpath: " + mnemonicpath);
+
+        // validate input
+        //     mnemonic path
+        // do
+        //     mnemonic path
+        // return
+        //     name elements
+
+        // validate input
+        ValidateUtil.validateInputMnemonicpath(mnemonicpath);
+
+        // do
+        return readNames(false, new FieldName[] {FieldName.DEVICESTRUCTURE}, new String[] {mnemonicpath}, orderBy, isAsc, offset, limit);
+    }
+
+    public List<NameElement> readNamesHistory(
+            String uuid,
+            FieldName orderBy, Boolean isAsc, Integer offset, Integer limit) {
+        LOGGER.log(Level.INFO, "readNamesHistory, uuid:              " + uuid);
+
+        // note
+        //     HolderIRepositories and HolderSystemDeviceStructure may or may not be used for preparation of what to return
+        // validate input
+        //     uuid
+        // do
+        //     read history for name
+        // return
+        //     name elements for names
+
+        // validate input
+        ValidateUtil.validateInputUuid(uuid);
+
+        // do
+        List<NameElement> nameElements = readNames(null, new FieldName[] {FieldName.UUID}, new String[] {uuid}, Boolean.TRUE, orderBy, isAsc, offset, limit);
+
+        Collections.sort(nameElements, new Comparator<NameElement>() {
+            @Override
+            public int compare(NameElement e1, NameElement e2) {
+                return e1.getWhen().compareTo(e2.getWhen());
+            }
+        });
+
+        LOGGER.log(Level.INFO, "readNamesHistory, nameElements.size: " + nameElements.size());
+
+        return nameElements;
+    }
+
+    // ----------------------------------------------------------------------------------------------------
+
+    public String equivalenceName(String name) {
+        LOGGER.log(Level.INFO, "equivalenceName, name: " + name);
+
+        // validate input
+        // do
+        //     exists
+
+        // validate input
+        ValidateUtil.validateInputName(name);
+
+        // do
+        return namingConvention.equivalenceClassRepresentative(name);
+    }
+
+    public Boolean existsName(String name) {
+        LOGGER.log(Level.INFO, "existsName, name: " + name);
+
+        // validate input
+        // do
+        //     exists
+
+        // validate input
+        ValidateUtil.validateInputName(name);
+
+        // do
+        List<Name> names = nameRepository.readNames(false, FieldName.NAME, name);
+        return !names.isEmpty();
+    }
+
+    public Boolean isLegacyName(String name) {
+        LOGGER.log(Level.INFO, "isLegacyName, name: " + name);
+
+        // validate input
+        // do
+        //     exists
+
+        // validate input
+        ValidateUtil.validateInputName(name);
+
+        // do
+        List<Name> names = nameRepository.readNames(false, FieldName.NAME, name);
+        ValidateUtil.validateCondition(names != null && names.size() == 1, HttpStatus.BAD_REQUEST, "name not available", name);
+
+        Name toBeChecked = names.get(0);
+
+        // system structure
+        if (toBeChecked.getSystemgroupUuid() != null) {
+            SystemGroup systemGroup = holderIRepositories.getSystemGroupRepository().findLatestNotDeletedByUuid(toBeChecked.getSystemgroupUuid().toString());
+            ValidateUtil.validateCondition(systemGroup != null, HttpStatus.BAD_REQUEST, "system group not available", name);
+
+            if (systemGroup.isDeleted()) {
+                return Boolean.TRUE;
+            }
+        } else if (toBeChecked.getSystemUuid() != null) {
+            org.openepics.names.repository.model.System system = holderIRepositories.getSystemRepository().findLatestNotDeletedByUuid(toBeChecked.getSystemUuid().toString());
+            ValidateUtil.validateCondition(system != null, HttpStatus.BAD_REQUEST, "system not available", name);
+
+            if (system.isDeleted()) {
+                return Boolean.TRUE;
+            }
+
+            SystemGroup systemGroup = holderIRepositories.getSystemGroupRepository().findLatestNotDeletedByUuid(system.getParentUuid().toString());
+            ValidateUtil.validateCondition(systemGroup != null, HttpStatus.BAD_REQUEST, "system group not available", name);
+
+            if (systemGroup.isDeleted()) {
+                return Boolean.TRUE;
+            }
+        } else if (toBeChecked.getSubsystemUuid() != null) {
+            Subsystem subsystem = holderIRepositories.getSubsystemRepository().findLatestNotDeletedByUuid(toBeChecked.getSubsystemUuid().toString());
+            ValidateUtil.validateCondition(subsystem != null, HttpStatus.BAD_REQUEST, "subsystem not available", name);
+
+            if (subsystem.isDeleted()) {
+                return Boolean.TRUE;
+            }
+
+            org.openepics.names.repository.model.System system = holderIRepositories.getSystemRepository().findLatestNotDeletedByUuid(subsystem.getParentUuid().toString());
+            ValidateUtil.validateCondition(system != null, HttpStatus.BAD_REQUEST, "system not available", name);
+
+            if (system.isDeleted()) {
+                return Boolean.TRUE;
+            }
+
+            SystemGroup systemGroup = holderIRepositories.getSystemGroupRepository().findLatestNotDeletedByUuid(system.getParentUuid().toString());
+            ValidateUtil.validateCondition(systemGroup != null, HttpStatus.BAD_REQUEST, "system group not available", name);
+
+            if (systemGroup.isDeleted()) {
+                return Boolean.TRUE;
+            }
+        }
+        // device structure
+        if (toBeChecked.getDevicetypeUuid() != null) {
+            DeviceType deviceType = holderIRepositories.getDeviceTypeRepository().findLatestNotDeletedByUuid(toBeChecked.getDevicetypeUuid().toString());
+            ValidateUtil.validateCondition(deviceType != null, HttpStatus.BAD_REQUEST, "device type not available", name);
+
+            if (deviceType.isDeleted()) {
+                return Boolean.TRUE;
+            }
+
+            DeviceGroup deviceGroup = holderIRepositories.getDeviceGroupRepository().findLatestNotDeletedByUuid(deviceType.getParentUuid().toString());
+            ValidateUtil.validateCondition(deviceGroup != null, HttpStatus.BAD_REQUEST, "device group not available", name);
+
+            if (deviceGroup.isDeleted()) {
+                return Boolean.TRUE;
+            }
+
+            Discipline discipline = holderIRepositories.getDisciplineRepository().findLatestNotDeletedByUuid(deviceType.getParentUuid().toString());
+            ValidateUtil.validateCondition(discipline != null, HttpStatus.BAD_REQUEST, "device group not available", name);
+
+            if (discipline.isDeleted()) {
+                return Boolean.TRUE;
+            }
+        }
+
+        return Boolean.FALSE;
+    }
+
+    public Boolean isValidToCreateName(String name) {
+        LOGGER.log(Level.INFO, "isValidToCreateName, name: " + name);
+
+        // validate input
+        // validate data
+        //     not exists
+
+        // validate input
+        ValidateUtil.validateInputName(name);
+
+        // initiate holder of containers for system and device structure content, for performance reasons
+        //     note false to not include deleted entries
+        HolderSystemDeviceStructure holder = new HolderSystemDeviceStructure(holderIRepositories, false);
+
+        // validate data
+        ValidateUtil.validateNameDataCreate(name, namingConvention, holderIRepositories, nameRepository, holder);
+
+        return Boolean.TRUE;
+    }
+
+    // ----------------------------------------------------------------------------------------------------
+
+    public void validateNamesCreate(NameElement nameElement) {
+        validateNamesCreate(nameElement, new HolderSystemDeviceStructure(holderIRepositories));
+    }
+    public void validateNamesCreate(NameElement nameElement, HolderSystemDeviceStructure holder) {
+        // validate authority
+        //     elsewhere
+        //         naming user & admin
+        // validate input
+        //     name element
+        //         validate input itself
+        // validate data
+        //     name element
+        //         validate towards repository
+
+        // validate input
+        ValidateUtil.validateNameElementInputCreate(nameElement);
+
+        // validate data
+        ValidateUtil.validateNameElementDataCreate(nameElement, namingConvention, holderIRepositories, nameRepository, holder);
+    }
+    public void validateNamesCreate(List<NameElement> nameElements) {
+        // initiate holder of containers for system and device structure content, for performance reasons
+        HolderSystemDeviceStructure holder = new HolderSystemDeviceStructure(holderIRepositories);
+
+        for (NameElement nameElement : nameElements) {
+            validateNamesCreate(nameElement, holder);
+        }
+    }
+
+    public void validateNamesUpdate(NameElement nameElement) {
+        validateNamesUpdate(nameElement, new HolderSystemDeviceStructure(holderIRepositories));
+    }
+    public void validateNamesUpdate(NameElement nameElement, HolderSystemDeviceStructure holder) {
+        // validate authority
+        //     elsewhere
+        //         naming user & admin
+        // validate input
+        //     name element
+        //         validate input itself
+        // validate data
+        //     name element
+        //         validate towards repository
+
+        // validate input
+        ValidateUtil.validateNameElementInputUpdate(nameElement);
+
+        // validate data
+        ValidateUtil.validateNameElementDataUpdate(nameElement, namingConvention, holderIRepositories, nameRepository, holder);
+    }
+    public void validateNamesUpdate(List<NameElement> nameElements) {
+        // initiate holder of containers for system and device structure content, for performance reasons
+        HolderSystemDeviceStructure holder = new HolderSystemDeviceStructure(holderIRepositories);
+
+        for (NameElement nameElement : nameElements) {
+            validateNamesUpdate(nameElement, holder);
+        }
+    }
+
+    public void validateNamesDelete(NameElement nameElement) {
+        validateNamesDelete(nameElement, new HolderSystemDeviceStructure(holderIRepositories));
+    }
+    public void validateNamesDelete(NameElement nameElement, HolderSystemDeviceStructure holder) {
+        //     validate authority
+        //         elsewhere
+        //             naming user & admin
+        //     validate input
+        //         uuid
+        //     validate data
+        //         retrieve name (uuid, latest, not deleted)
+
+        // validate input
+        ValidateUtil.validateNameElementInputDelete(nameElement);
+
+        // validate data
+        ValidateUtil.validateNameElementDataDelete(nameElement, namingConvention, holderIRepositories, nameRepository, holder);
+    }
+    public void validateNamesDelete(List<NameElement> nameElements) {
+        //     validate authority
+        //         elsewhere
+        //             naming user & admin
+        //     validate input
+        //         name element
+        //             validate input itself
+        //     validate data
+        //         name element
+        //             validate towards repository
+
+        // initiate holder of containers for system and device structure content, for performance reasons
+        HolderSystemDeviceStructure holder = new HolderSystemDeviceStructure(holderIRepositories);
+
+        for (NameElement nameElement : nameElements) {
+            validateNamesDelete(nameElement, holder);
+        }
+    }
+
+    // ----------------------------------------------------------------------------------------------------
+
+    @Transactional
+    public List<NameElement> updateNames(List<NameElement> nameElements) {
+        // validate
+        //     outside of @Transactional
+        // transaction
+        //     do
+        //         for each name element
+        //             update name to not latest
+        //             insert name to latest, not deleted, with data
+        //             read
+        //     return
+        //         name elements for updated names
+
+        LOGGER.log(Level.INFO, "updateNames, nameElements.size:        " + String.valueOf(nameElements != null ? nameElements.size() : "null"));
+
+        // do
+        String requestedBy = "test who";
+        final List<NameElement> updatedNameElements = Lists.newArrayList();
+        for (NameElement nameElement : nameElements) {
+            // update not latest, not deleted
+            // create latest, not deleted
+
+            Name name = holderIRepositories.getNameRepository().findLatestByUuid(nameElement.getUuid().toString());
+            if (name == null) {
+                continue;
+            }
+
+            // skip if name element has same content as name
+            //     proceed without fail
+            if (NameElementUtil.hasSameContent(nameElement, name)) {
+                continue;
+            }
+
+            // update
+            name.setLatest(Boolean.FALSE);
+            nameRepository.updateName(name);
+
+            // create
+            name = new Name();
+            setAttributes(name,
+                    nameElement.getUuid(),
+                    nameElement.getSystemgroup(), nameElement.getSystem(), nameElement.getSubsystem(), nameElement.getDevicetype(),
+                    nameElement.getIndex(), nameElement.getName(), namingConvention.equivalenceClassRepresentative(nameElement.getName()),
+                    nameElement.getDescription(), Status.APPROVED, Boolean.TRUE, Boolean.FALSE,
+                    new Date(), requestedBy, nameElement.getComment());
+
+            nameRepository.createName(name);
+
+            // possibly validate that updated
+
+            // add
+            updatedNameElements.add(NameElementUtil.getNameElement(name));
+        }
+
+        LOGGER.log(Level.INFO, "updateNames, updatedNameElements.size: " + updatedNameElements.size());
+        return updatedNameElements;
+    }
+
+    // ----------------------------------------------------------------------------------------------------
+
+    @Transactional
+    public List<NameElement> deleteNames(List<NameElement> nameElements) {
+        // validate
+        //     outside of @Transactional
+        // transaction
+        //     do
+        //         update name to not latest
+        //         insert name to latest, deleted
+        //         read
+        //     return
+        //         name element for deleted name
+
+        LOGGER.log(Level.INFO, "deleteNames, nameElements.size:        " + String.valueOf(nameElements != null ? nameElements.size() : "null"));
+
+        // do
+        String requestedBy = "test who";
+        final List<NameElement> deletedNameElements = Lists.newArrayList();
+        for (NameElement nameElement : nameElements) {
+            Name name = holderIRepositories.getNameRepository().findLatestByUuid(nameElement.getUuid().toString());
+            if (name == null) {
+                continue;
+            }
+
+            name.setLatest(Boolean.FALSE);
+            nameRepository.updateName(name);
+
+            name = new Name();
+            setAttributes(name,
+                    nameElement.getUuid(),
+                    nameElement.getSystemgroup(), nameElement.getSystem(), nameElement.getSubsystem(), nameElement.getDevicetype(),
+                    nameElement.getIndex(), nameElement.getName(), namingConvention.equivalenceClassRepresentative(nameElement.getName()),
+                    nameElement.getDescription(), Status.APPROVED, Boolean.TRUE, Boolean.TRUE,
+                    new Date(), requestedBy, nameElement.getComment());
+
+            nameRepository.createName(name);
+
+            // possibly validate that deleted
+
+            // add
+            deletedNameElements.add(NameElementUtil.getNameElement(name));
+        }
+
+        LOGGER.log(Level.INFO, "deleteNames, deletedNameElements.size: " + deletedNameElements.size());
+        return deletedNameElements;
+    }
+
+    // ----------------------------------------------------------------------------------------------------
+
+    /**
+     * Utility method to help set attributes for Name class.
+     *
+     * @param name name
+     * @param uuid uuid
+     * @param systemgroupUuid system group uuid
+     * @param systemUuid system uuid
+     * @param subsystemUuid subsystem uuid
+     * @param devicetypeUuid device type uuid
+     * @param index index
+     * @param conventionName convention name
+     * @param conventionNameEquivalence convention name equivalence
+     * @param description description
+     * @param status status
+     * @param latest latest
+     * @param deleted deleted
+     * @param requested requested
+     * @param requestedBy requested by
+     * @param requestedComment requested comment
+     */
+    private void setAttributes(Name name,
+            UUID uuid, UUID systemgroupUuid, UUID systemUuid, UUID subsystemUuid, UUID devicetypeUuid,
+            String index, String conventionName, String conventionNameEquivalence,
+            String description, Status status, Boolean latest, Boolean deleted,
+            Date requested, String requestedBy, String requestedComment) {
+        name.setUuid(uuid);
+        name.setSystemgroupUuid(systemgroupUuid);
+        name.setSystemUuid(systemUuid);
+        name.setSubsystemUuid(subsystemUuid);
+        name.setDevicetypeUuid(devicetypeUuid);
+        name.setInstanceIndex(index);
+        name.setConventionName(conventionName);
+        name.setConventionNameEquivalence(conventionNameEquivalence);
+        name.setDescription(description);
+        name.setStatus(Status.APPROVED);
+        name.setLatest(latest);
+        name.setDeleted(deleted);
+        name.setRequested(requested);
+        name.setRequestedBy(requestedBy);
+        name.setRequestedComment(requestedComment);
+
+    }
+}
diff --git a/src/main/java/org/openepics/names/service/StructuresService.java b/src/main/java/org/openepics/names/service/StructuresService.java
new file mode 100644
index 0000000..d598c13
--- /dev/null
+++ b/src/main/java/org/openepics/names/service/StructuresService.java
@@ -0,0 +1,1601 @@
+/*
+ * Copyright (C) 2021 European Spallation Source ERIC.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+package org.openepics.names.service;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+import java.util.UUID;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.commons.lang3.StringUtils;
+import org.openepics.names.repository.DeviceGroupRepository;
+import org.openepics.names.repository.DeviceTypeRepository;
+import org.openepics.names.repository.DisciplineRepository;
+import org.openepics.names.repository.IDeviceGroupRepository;
+import org.openepics.names.repository.IDeviceTypeRepository;
+import org.openepics.names.repository.IDisciplineRepository;
+import org.openepics.names.repository.INameRepository;
+import org.openepics.names.repository.ISubsystemRepository;
+import org.openepics.names.repository.ISystemGroupRepository;
+import org.openepics.names.repository.ISystemRepository;
+import org.openepics.names.repository.NameRepository;
+import org.openepics.names.repository.SubsystemRepository;
+import org.openepics.names.repository.SystemGroupRepository;
+import org.openepics.names.repository.SystemRepository;
+import org.openepics.names.repository.model.DeviceGroup;
+import org.openepics.names.repository.model.DeviceType;
+import org.openepics.names.repository.model.Discipline;
+import org.openepics.names.repository.model.Structure;
+import org.openepics.names.repository.model.Subsystem;
+import org.openepics.names.repository.model.System;
+import org.openepics.names.repository.model.SystemGroup;
+import org.openepics.names.rest.beans.FieldStructure;
+import org.openepics.names.rest.beans.Status;
+import org.openepics.names.rest.beans.StructureElement;
+import org.openepics.names.rest.beans.Type;
+import org.openepics.names.util.EssNamingConvention;
+import org.openepics.names.util.HolderIRepositories;
+import org.openepics.names.util.HolderRepositories;
+import org.openepics.names.util.HolderSystemDeviceStructure;
+import org.openepics.names.util.StructureElementUtil;
+import org.openepics.names.util.StructureElementUtil.StructureChoice;
+import org.openepics.names.util.ValidateUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.google.common.collect.Lists;
+
+/**
+ * This class provides structures services.
+ *
+ * @author Lars Johansson
+ */
+@Service
+public class StructuresService {
+
+    // HolderIRepositories and HolderSystemDeviceStructure may or may not be used for preparation of what to return
+
+    // for each method
+    //     document what values come from StructureElement and what values come from persistence layer
+    //     somehow provide this information to user
+
+    // latest
+    //     automatically not show structures that do not come into play
+    //     = automatically exclude (approved and not latest)
+    //     otherwise refer to history
+
+    private static final Logger LOGGER = Logger.getLogger(StructuresService.class.getName());
+
+    private EssNamingConvention namingConvention;
+
+    private HolderIRepositories holderIRepositories;
+    private HolderRepositories holderRepositories;
+
+    @Autowired
+    public StructuresService(
+            INameRepository iNameRepository,
+            ISystemGroupRepository iSystemGroupRepository,
+            ISystemRepository iSystemRepository,
+            ISubsystemRepository iSubsystemRepository,
+            IDisciplineRepository iDisciplineRepository,
+            IDeviceGroupRepository iDeviceGroupRepository,
+            IDeviceTypeRepository iDeviceTypeRepository,
+            NameRepository nameRepository,
+            SystemGroupRepository systemGroupRepository,
+            SystemRepository systemRepository,
+            SubsystemRepository subsystemRepository,
+            DisciplineRepository disciplineRepository,
+            DeviceGroupRepository deviceGroupRepository,
+            DeviceTypeRepository deviceTypeRepository) {
+
+        this.namingConvention = new EssNamingConvention();
+        this.holderIRepositories = new HolderIRepositories(
+                iNameRepository,
+                iSystemGroupRepository,
+                iSystemRepository,
+                iSubsystemRepository,
+                iDisciplineRepository,
+                iDeviceGroupRepository,
+                iDeviceTypeRepository);
+        this.holderRepositories = new HolderRepositories(
+                nameRepository,
+                systemGroupRepository,
+                systemRepository,
+                subsystemRepository,
+                disciplineRepository,
+                deviceGroupRepository,
+                deviceTypeRepository);
+    }
+
+    @Transactional
+    public List<StructureElement> createStructures(List<StructureElement> structureElements) {
+        // validate
+        //     outside of @Transactional
+        // transaction
+        //     do
+        //         for each structure element
+        //             create structure to pending, not latest, not deleted, with data
+        //             add structure element for created
+        //     return
+        //         structure elements for created structures
+
+        LOGGER.log(Level.INFO, "createStructures, structureElements.size:        " + String.valueOf(structureElements != null ? structureElements.size() : "null"));
+
+        // initiate holder of containers for system and device structure content, for performance reasons
+        HolderSystemDeviceStructure holder = new HolderSystemDeviceStructure(holderIRepositories);
+
+        // do
+        String requestedBy = "test who";
+        final List<StructureElement> createdStructureElements = Lists.newArrayList();
+        for (StructureElement structureElement : structureElements) {
+            if (Type.SYSTEMGROUP.equals(structureElement.getType())) {
+                // note rules for mnemonic for system group
+                String mnemonic = structureElement.getMnemonic();
+                mnemonic = StringUtils.isEmpty(mnemonic) ? null : mnemonic;
+
+                // create
+                SystemGroup systemGroup = new SystemGroup();
+                setAttributes(systemGroup,
+                        UUID.randomUUID(),
+                        structureElement.getName(), mnemonic, !StringUtils.isEmpty(mnemonic) ? namingConvention.equivalenceClassRepresentative(mnemonic) : null,
+                        structureElement.getDescription(), Status.PENDING, Boolean.FALSE, Boolean.FALSE,
+                        new Date(), requestedBy, structureElement.getComment());
+
+                holderRepositories.getSystemGroupRepository().createSystemGroup(systemGroup);
+
+                // possibly validate that created
+
+                // add
+                createdStructureElements.add(StructureElementUtil.getStructureElementRequested(systemGroup, holder));
+            } else if (Type.SYSTEM.equals(structureElement.getType())) {
+                // create
+                System system = new System();
+                setAttributes(system,
+                        UUID.randomUUID(), structureElement.getParent(),
+                        structureElement.getName(), structureElement.getMnemonic(), namingConvention.equivalenceClassRepresentative(structureElement.getMnemonic()),
+                        structureElement.getDescription(), Status.PENDING, Boolean.FALSE, Boolean.FALSE,
+                        new Date(), requestedBy, structureElement.getComment());
+
+                holderRepositories.getSystemRepository().createSystem(system);
+
+                // possibly validate that created
+
+                // add
+                createdStructureElements.add(StructureElementUtil.getStructureElementRequested(system, holder));
+            } else if (Type.SUBSYSTEM.equals(structureElement.getType())) {
+                // create
+                Subsystem subsystem = new Subsystem();
+                setAttributes(subsystem,
+                        UUID.randomUUID(), structureElement.getParent(),
+                        structureElement.getName(), structureElement.getMnemonic(), namingConvention.equivalenceClassRepresentative(structureElement.getMnemonic()),
+                        structureElement.getDescription(), Status.PENDING, Boolean.FALSE, Boolean.FALSE,
+                        new Date(), requestedBy, structureElement.getComment());
+
+                holderRepositories.getSubsystemRepository().createSubsystem(subsystem);
+
+                // possibly validate that created
+
+                // add
+                createdStructureElements.add(StructureElementUtil.getStructureElementRequested(subsystem, holder));
+            } else if (Type.DISCIPLINE.equals(structureElement.getType())) {
+                // create
+                Discipline discipline = new Discipline();
+                setAttributes(discipline,
+                        UUID.randomUUID(),
+                        structureElement.getName(), structureElement.getMnemonic(), namingConvention.equivalenceClassRepresentative(structureElement.getMnemonic()),
+                        structureElement.getDescription(), Status.PENDING, Boolean.FALSE, Boolean.FALSE,
+                        new Date(), requestedBy, structureElement.getComment());
+
+                holderRepositories.getDisciplineRepository().createDiscipline(discipline);
+
+                // possibly validate that created
+
+                // add
+                createdStructureElements.add(StructureElementUtil.getStructureElementRequested(discipline, holder));
+            } else if (Type.DEVICEGROUP.equals(structureElement.getType())) {
+                // note rules for mnemonic for device group
+                String mnemonic = structureElement.getMnemonic();
+                mnemonic = StringUtils.isEmpty(mnemonic) ? null : mnemonic;
+
+                // create
+                DeviceGroup deviceGroup = new DeviceGroup();
+                setAttributes(deviceGroup,
+                        UUID.randomUUID(), structureElement.getParent(),
+                        structureElement.getName(), mnemonic, !StringUtils.isEmpty(mnemonic) ? namingConvention.equivalenceClassRepresentative(mnemonic) : null,
+                        structureElement.getDescription(), Status.PENDING, Boolean.FALSE, Boolean.FALSE,
+                        new Date(), requestedBy, structureElement.getComment());
+
+                holderRepositories.getDeviceGroupRepository().createDeviceGroup(deviceGroup);
+
+                // possibly validate that created
+
+                // add
+                createdStructureElements.add(StructureElementUtil.getStructureElementRequested(deviceGroup, holder));
+            } else if (Type.DEVICETYPE.equals(structureElement.getType())) {
+                // create
+                DeviceType deviceType = new DeviceType();
+                setAttributes(deviceType,
+                        UUID.randomUUID(), structureElement.getParent(),
+                        structureElement.getName(), structureElement.getMnemonic(), namingConvention.equivalenceClassRepresentative(structureElement.getMnemonic()),
+                        structureElement.getDescription(), Status.PENDING, Boolean.FALSE, Boolean.FALSE,
+                        new Date(), requestedBy, structureElement.getComment());
+
+                holderRepositories.getDeviceTypeRepository().createDeviceType(deviceType);
+
+                // possibly validate that created
+
+                // add
+                createdStructureElements.add(StructureElementUtil.getStructureElementRequested(deviceType, holder));
+            }
+        }
+
+        LOGGER.log(Level.INFO, "createStructures, createdStructureElements.size: " + createdStructureElements.size());
+        return createdStructureElements;
+    }
+
+    // ----------------------------------------------------------------------------------------------------
+
+    public List<StructureElement> readStructures(
+            Type type, Status[] statuses, Boolean deleted, FieldStructure[] queryFields, String[] queryValues,
+            FieldStructure orderBy, Boolean isAsc, Integer offset, Integer limit) {
+        return readStructures(type, statuses, deleted, queryFields, queryValues, Boolean.FALSE, orderBy, isAsc, offset, limit, StructureChoice.STRUCTURE);
+    }
+
+    //need to have public static enum StructureChoice {HISTORY, STRUCTURE};
+    public List<StructureElement> readStructures(
+            Type type, Status[] statuses, Boolean deleted, FieldStructure[] queryFields, String[] queryValues, Boolean includeHistory,
+            FieldStructure orderBy, Boolean isAsc, Integer offset, Integer limit,
+            StructureChoice structureChoice) {
+
+        LOGGER.log(Level.INFO, "readStructures, type:                   " + type);
+        LOGGER.log(Level.INFO, "readStructures, statuses.length:        " + String.valueOf(statuses != null ? statuses.length : "null"));
+        LOGGER.log(Level.INFO, "readStructures, deleted:                " + deleted);
+        LOGGER.log(Level.INFO, "readStructures, queryFields.length:     " + String.valueOf(queryFields != null ? queryFields.length : "null"));
+        LOGGER.log(Level.INFO, "readStructures, queryValues.length:     " + String.valueOf(queryValues != null ? queryValues.length : "null"));
+        LOGGER.log(Level.INFO, "readStructures, includeHistory:         " + includeHistory);
+        LOGGER.log(Level.INFO, "readStructures, orderBy:                " + orderBy);
+        LOGGER.log(Level.INFO, "readStructures, isAsc:                  " + isAsc);
+        LOGGER.log(Level.INFO, "readStructures, offset:                 " + offset);
+        LOGGER.log(Level.INFO, "readStructures, limit:                  " + limit);
+        LOGGER.log(Level.INFO, "readStructures, structureChoice:        " + structureChoice);
+        if (statuses != null && statuses.length > 0) {
+            for (Status status : statuses) {
+                LOGGER.log(Level.INFO, "readStructures, status:                 " + status);
+            }
+        }
+        if (queryFields != null && queryFields.length > 0) {
+            for (FieldStructure queryField : queryFields) {
+                LOGGER.log(Level.INFO, "readStructures, queryField:             " + queryField);
+            }
+        }
+        if (queryValues != null && queryValues.length > 0) {
+            for (String queryValue : queryValues) {
+                LOGGER.log(Level.INFO, "readStructures, queryValue:             " + queryValue);
+            }
+        }
+
+        // validate input
+        //     type
+        //     queryFields and queryValues
+        //         uuid
+        // do
+        //     read structures
+        // return
+        //     structure elements for structures
+
+        // validate input
+        ValidateUtil.validateStructuresInputRead(
+                type, statuses, deleted, queryFields, queryValues,
+                includeHistory,
+                orderBy, isAsc,
+                offset, limit);
+
+        // initiate holder of containers for system and device structure content, for performance reasons
+        HolderSystemDeviceStructure holder = new HolderSystemDeviceStructure(holderIRepositories);
+
+        // do
+        final List<StructureElement> structureElements = Lists.newArrayList();
+        if (Type.SYSTEMGROUP.equals(type)) {
+            List<SystemGroup> systemGroups = holderRepositories.getSystemGroupRepository().readSystemGroups(
+                    statuses, deleted, queryFields, queryValues,
+                    includeHistory,
+                    orderBy, isAsc,
+                    offset, limit);
+            LOGGER.log(Level.INFO, "readStructures, systemGroups.size:      " + systemGroups.size());
+            structureElements.addAll(StructureElementUtil.getStructureElementsForSystemGroups(systemGroups, holder, structureChoice));
+        } else if (Type.SYSTEM.equals(type)) {
+            List<System> systems = holderRepositories.getSystemRepository().readSystems(
+                    statuses, deleted, queryFields, queryValues,
+                    includeHistory,
+                    orderBy, isAsc,
+                    offset, limit);
+            LOGGER.log(Level.INFO, "readStructures, systems.size:           " + systems.size());
+            structureElements.addAll(StructureElementUtil.getStructureElementsForSystems(systems, holder, structureChoice));
+        } else if (Type.SUBSYSTEM.equals(type)) {
+            List<Subsystem> subsystems = holderRepositories.getSubsystemRepository().readSubsystems(
+                    statuses, deleted, queryFields, queryValues,
+                    includeHistory,
+                    orderBy, isAsc,
+                    offset, limit);
+            LOGGER.log(Level.INFO, "readStructures, subsystems.size:        " + subsystems.size());
+            structureElements.addAll(StructureElementUtil.getStructureElementsForSubsystems(subsystems, holder, structureChoice));
+        } else if (Type.DISCIPLINE.equals(type)) {
+            List<Discipline> disciplines = holderRepositories.getDisciplineRepository().readDisciplines(
+                    statuses, deleted, queryFields, queryValues,
+                    includeHistory,
+                    orderBy, isAsc,
+                    offset, limit);
+            LOGGER.log(Level.INFO, "readStructures, disciplines.size:       " + disciplines.size());
+            structureElements.addAll(StructureElementUtil.getStructureElementsForDisciplines(disciplines, holder, structureChoice));
+        } else if (Type.DEVICEGROUP.equals(type)) {
+            List<DeviceGroup> deviceGroups = holderRepositories.getDeviceGroupRepository().readDeviceGroups(
+                    statuses, deleted, queryFields, queryValues,
+                    includeHistory,
+                    orderBy, isAsc,
+                    offset, limit);
+            LOGGER.log(Level.INFO, "readStructures, deviceGroups.size:      " + deviceGroups.size());
+            structureElements.addAll(StructureElementUtil.getStructureElementsForDeviceGroups(deviceGroups, holder, structureChoice));
+        } else if (Type.DEVICETYPE.equals(type)) {
+            List<DeviceType> deviceTypes = holderRepositories.getDeviceTypeRepository().readDeviceTypes(
+                    statuses, deleted, queryFields, queryValues,
+                    includeHistory,
+                    orderBy, isAsc,
+                    offset, limit);
+            LOGGER.log(Level.INFO, "readStructures, deviceTypes.size:       " + deviceTypes.size());
+            structureElements.addAll(StructureElementUtil.getStructureElementsForDeviceTypes(deviceTypes, holder, structureChoice));
+        }
+
+        LOGGER.log(Level.INFO, "readStructures, structureElements.size: " + structureElements.size());
+        return structureElements;
+    }
+
+    public List<StructureElement> readStructuresChildren(
+            Type type, String uuid,
+            FieldStructure orderBy, Boolean isAsc, Integer offset, Integer limit) {
+
+        // validate input
+        //     type, uuid
+        // do
+        //     read structure latest by uuid for type
+        // return
+        //     structure elements for structures
+
+        LOGGER.log(Level.INFO, "readStructuresChildren, type:                   " + type);
+        LOGGER.log(Level.INFO, "readStructuresChildren, uuid:                   " + uuid);
+
+        // validate input
+        ValidateUtil.validateInputType(type);
+        ValidateUtil.validateInputUuid(uuid);
+
+        // do
+        List<StructureElement> structureElements = Lists.newArrayList();
+        if (Type.SYSTEMGROUP.equals(type)) {
+            structureElements.addAll(readStructures(Type.SYSTEM, new Status[] {Status.APPROVED}, false, new FieldStructure[] {FieldStructure.PARENT}, new String[] {uuid}, orderBy, isAsc, offset, limit));
+        } else if (Type.SYSTEM.equals(type)) {
+            structureElements.addAll(readStructures(Type.SUBSYSTEM, new Status[] {Status.APPROVED}, false, new FieldStructure[] {FieldStructure.PARENT}, new String[] {uuid}, orderBy, isAsc, offset, limit));
+        } else if (Type.SUBSYSTEM.equals(type)) {
+            // no children in structures
+        } else if (Type.DISCIPLINE.equals(type)) {
+            structureElements.addAll(readStructures(Type.DEVICEGROUP, new Status[] {Status.APPROVED}, false, new FieldStructure[] {FieldStructure.PARENT}, new String[] {uuid}, orderBy, isAsc, offset, limit));
+        } else if (Type.DEVICEGROUP.equals(type)) {
+            structureElements.addAll(readStructures(Type.DEVICETYPE, new Status[] {Status.APPROVED}, false, new FieldStructure[] {FieldStructure.PARENT}, new String[] {uuid}, orderBy, isAsc, offset, limit));
+        } else if (Type.DEVICETYPE.equals(type)) {
+            // no children in structures
+        }
+        LOGGER.log(Level.INFO, "readStructuresChildren, structureElements.size: " + structureElements.size());
+        return structureElements;
+    }
+
+    public List<StructureElement> readStructuresMnemonic(
+            String mnemonic,
+            FieldStructure orderBy, Boolean isAsc, Integer offset, Integer limit) {
+
+        // validate input
+        //     mnemonic
+        // do
+        //     read structure latest by mnemonic
+        // return
+        //     structure elements for structures
+
+        // validate input
+        ValidateUtil.validateInputMnemonic(mnemonic);
+
+        // do
+        List<StructureElement> structureElements = Lists.newArrayList();
+        structureElements.addAll(readStructures(Type.SYSTEMGROUP, new Status[] {Status.APPROVED}, false, new FieldStructure[] {FieldStructure.MNEMONIC}, new String[] {mnemonic}, orderBy, isAsc, offset, limit));
+        structureElements.addAll(readStructures(Type.SYSTEM,      new Status[] {Status.APPROVED}, false, new FieldStructure[] {FieldStructure.MNEMONIC}, new String[] {mnemonic}, orderBy, isAsc, offset, limit));
+        structureElements.addAll(readStructures(Type.SUBSYSTEM,   new Status[] {Status.APPROVED}, false, new FieldStructure[] {FieldStructure.MNEMONIC}, new String[] {mnemonic}, orderBy, isAsc, offset, limit));
+        structureElements.addAll(readStructures(Type.DISCIPLINE,  new Status[] {Status.APPROVED}, false, new FieldStructure[] {FieldStructure.MNEMONIC}, new String[] {mnemonic}, orderBy, isAsc, offset, limit));
+        structureElements.addAll(readStructures(Type.DEVICEGROUP, new Status[] {Status.APPROVED}, false, new FieldStructure[] {FieldStructure.MNEMONIC}, new String[] {mnemonic}, orderBy, isAsc, offset, limit));
+        structureElements.addAll(readStructures(Type.DEVICETYPE,  new Status[] {Status.APPROVED}, false, new FieldStructure[] {FieldStructure.MNEMONIC}, new String[] {mnemonic}, orderBy, isAsc, offset, limit));
+
+        // TODO handle orderBy, isAsc, offset, limit
+
+        return structureElements;
+    }
+
+    public List<StructureElement> readStructuresMnemonicpath(
+            String mnemonicpath,
+            FieldStructure orderBy, Boolean isAsc, Integer offset, Integer limit) {
+
+        // validate input
+        //     mnemonicpath
+        // do
+        //     read structure latest by mnemonicpath
+        // return
+        //     structure elements for structures
+
+        // validate input
+        ValidateUtil.validateInputMnemonic(mnemonicpath);
+
+        // do
+        List<StructureElement> structureElements = Lists.newArrayList();
+        structureElements.addAll(readStructures(Type.SYSTEMGROUP, new Status[] {Status.APPROVED}, false, new FieldStructure[] {FieldStructure.MNEMONICPATH}, new String[] {mnemonicpath}, orderBy, isAsc, offset, limit));
+        structureElements.addAll(readStructures(Type.SYSTEM,      new Status[] {Status.APPROVED}, false, new FieldStructure[] {FieldStructure.MNEMONICPATH}, new String[] {mnemonicpath}, orderBy, isAsc, offset, limit));
+        structureElements.addAll(readStructures(Type.SUBSYSTEM,   new Status[] {Status.APPROVED}, false, new FieldStructure[] {FieldStructure.MNEMONICPATH}, new String[] {mnemonicpath}, orderBy, isAsc, offset, limit));
+        structureElements.addAll(readStructures(Type.DISCIPLINE,  new Status[] {Status.APPROVED}, false, new FieldStructure[] {FieldStructure.MNEMONICPATH}, new String[] {mnemonicpath}, orderBy, isAsc, offset, limit));
+        structureElements.addAll(readStructures(Type.DEVICEGROUP, new Status[] {Status.APPROVED}, false, new FieldStructure[] {FieldStructure.MNEMONICPATH}, new String[] {mnemonicpath}, orderBy, isAsc, offset, limit));
+        structureElements.addAll(readStructures(Type.DEVICETYPE,  new Status[] {Status.APPROVED}, false, new FieldStructure[] {FieldStructure.MNEMONICPATH}, new String[] {mnemonicpath}, orderBy, isAsc, offset, limit));
+
+        // TODO handle orderBy, isAsc, offset, limit
+
+        return structureElements;
+    }
+
+    /**
+     * Read history for structure by uuid.
+     *
+     * @param uuid uuid
+     * @param type type
+     * @return list of structure elements
+     */
+    public List<StructureElement> readStructuresHistory(
+            String uuid, Type type,
+            FieldStructure orderBy, Boolean isAsc, Integer offset, Integer limit) {
+
+        // note
+        //     HolderIRepositories and HolderSystemDeviceStructure may or may not be used for preparation of what to return
+        //     type may speed up read
+        // validate input
+        //     uuid
+        // do
+        //     read history for structure
+        // return
+        //     structure elements for structures
+
+        // validate input
+        ValidateUtil.validateInputUuid(uuid);
+
+        boolean type_systemGroup = type != null && Type.SYSTEMGROUP.equals(type);
+        boolean type_system      = type != null && Type.SYSTEM.equals(type);
+        boolean type_subsystem   = type != null && Type.SUBSYSTEM.equals(type);
+        boolean type_discipline  = type != null && Type.DISCIPLINE.equals(type);
+        boolean type_deviceGroup = type != null && Type.DEVICEGROUP.equals(type);
+        boolean type_deviceType  = type != null && Type.DEVICETYPE.equals(type);
+        boolean type_either      = type_systemGroup || type_system || type_subsystem || type_discipline || type_deviceGroup || type_deviceType;
+
+        // mnemonic path does not make same sense for history
+        // (very) tricky to find mnemonic path for uuid at proper time (history)
+        // therefore empty mnemonic path for history for structure
+
+        // do
+        final List<StructureElement> structureElements = Lists.newArrayList();
+        if (type_either) {
+            if (type_systemGroup) {
+                structureElements.addAll(readStructures(Type.SYSTEMGROUP, null, null, new FieldStructure[] {FieldStructure.UUID}, new String[] {uuid}, Boolean.TRUE, orderBy, isAsc, offset, limit, StructureChoice.HISTORY));
+            } else if (type_system) {
+                structureElements.addAll(readStructures(Type.SYSTEM, null, null, new FieldStructure[] {FieldStructure.UUID}, new String[] {uuid}, Boolean.TRUE, orderBy, isAsc, offset, limit, StructureChoice.HISTORY));
+            } else if (type_subsystem) {
+                structureElements.addAll(readStructures(Type.SUBSYSTEM, null, null, new FieldStructure[] {FieldStructure.UUID}, new String[] {uuid}, Boolean.TRUE, orderBy, isAsc, offset, limit, StructureChoice.HISTORY));
+            } else if (type_discipline) {
+                structureElements.addAll(readStructures(Type.DISCIPLINE, null, null, new FieldStructure[] {FieldStructure.UUID}, new String[] {uuid}, Boolean.TRUE, orderBy, isAsc, offset, limit, StructureChoice.HISTORY));
+            } else if (type_deviceGroup) {
+                structureElements.addAll(readStructures(Type.DEVICEGROUP, null, null, new FieldStructure[] {FieldStructure.UUID}, new String[] {uuid}, Boolean.TRUE, orderBy, isAsc, offset, limit, StructureChoice.HISTORY));
+            } else if (type_deviceType) {
+                structureElements.addAll(readStructures(Type.DEVICETYPE, null, null, new FieldStructure[] {FieldStructure.UUID}, new String[] {uuid}, Boolean.TRUE, orderBy, isAsc, offset, limit, StructureChoice.HISTORY));
+            }
+        } else {
+            // go through all structures and see if / where uuid is found
+
+            structureElements.addAll(readStructures(Type.SYSTEMGROUP, null, null, new FieldStructure[] {FieldStructure.UUID}, new String[] {uuid}, Boolean.TRUE, orderBy, isAsc, offset, limit, StructureChoice.HISTORY));
+            structureElements.addAll(readStructures(Type.SYSTEM, null, null, new FieldStructure[] {FieldStructure.UUID}, new String[] {uuid}, Boolean.TRUE, orderBy, isAsc, offset, limit, StructureChoice.HISTORY));
+            structureElements.addAll(readStructures(Type.SUBSYSTEM, null, null, new FieldStructure[] {FieldStructure.UUID}, new String[] {uuid}, Boolean.TRUE, orderBy, isAsc, offset, limit, StructureChoice.HISTORY));
+            structureElements.addAll(readStructures(Type.DISCIPLINE, null, null, new FieldStructure[] {FieldStructure.UUID}, new String[] {uuid}, Boolean.TRUE, orderBy, isAsc, offset, limit, StructureChoice.HISTORY));
+            structureElements.addAll(readStructures(Type.DEVICEGROUP, null, null, new FieldStructure[] {FieldStructure.UUID}, new String[] {uuid}, Boolean.TRUE, orderBy, isAsc, offset, limit, StructureChoice.HISTORY));
+            structureElements.addAll(readStructures(Type.DEVICETYPE, null, null, new FieldStructure[] {FieldStructure.UUID}, new String[] {uuid}, Boolean.TRUE, orderBy, isAsc, offset, limit, StructureChoice.HISTORY));
+        }
+
+        Collections.sort(structureElements, new Comparator<StructureElement>() {
+            @Override
+            public int compare(StructureElement e1, StructureElement e2) {
+                return e1.getWhen().compareTo(e2.getWhen());
+            }
+        });
+
+        return structureElements;
+    }
+
+    // ----------------------------------------------------------------------------------------------------
+
+    public String equivalenceMnemonic(String mnemonic) {
+        // validate input
+        // do
+        //     exists
+
+        // validate input
+        ValidateUtil.validateInputMnemonic(mnemonic);
+
+        // do
+        return namingConvention.equivalenceClassRepresentative(mnemonic);
+    }
+
+    public Boolean existsStructure(Type type, String mnemonic) {
+        // validate input
+        // do
+        //     exists
+
+        // validate input
+        ValidateUtil.validateInputType(type);
+        ValidateUtil.validateInputMnemonic(mnemonic);
+
+        // do
+        if (Type.SYSTEMGROUP.equals(type)) {
+            List<SystemGroup> systemGroups = holderRepositories.getSystemGroupRepository().readSystemGroups(Status.APPROVED, false, FieldStructure.MNEMONIC, mnemonic);
+            return !systemGroups.isEmpty();
+        } else if (Type.SYSTEM.equals(type)) {
+            List<System> systems = holderRepositories.getSystemRepository().readSystems(Status.APPROVED, false, FieldStructure.MNEMONIC, mnemonic);
+            return !systems.isEmpty();
+        } else if (Type.SUBSYSTEM.equals(type)) {
+            List<Subsystem> subsystems = holderRepositories.getSubsystemRepository().readSubsystems(Status.APPROVED, false, FieldStructure.MNEMONIC, mnemonic);
+            return !subsystems.isEmpty();
+        } else if (Type.DISCIPLINE.equals(type)) {
+            List<Discipline> disciplines = holderRepositories.getDisciplineRepository().readDisciplines(Status.APPROVED, false, FieldStructure.MNEMONIC, mnemonic);
+            return !disciplines.isEmpty();
+        } else if (Type.DEVICEGROUP.equals(type)) {
+            List<DeviceGroup> deviceGroups = holderRepositories.getDeviceGroupRepository().readDeviceGroups(Status.APPROVED, false, FieldStructure.MNEMONIC, mnemonic);
+            return !deviceGroups.isEmpty();
+        } else if (Type.DEVICETYPE.equals(type)) {
+            List<DeviceType> deviceTypes = holderRepositories.getDeviceTypeRepository().readDeviceTypes(Status.APPROVED, false, FieldStructure.MNEMONIC, mnemonic);
+            return !deviceTypes.isEmpty();
+        }
+
+        return Boolean.FALSE;
+    }
+
+    public Boolean isValidToCreateStructure(Type type, String mnemonicpath) {
+        // validate input
+        // validate data
+        //     not exists
+
+        // validate input
+        ValidateUtil.validateInputType(type);
+        ValidateUtil.validateInputMnemonicpath(mnemonicpath);
+
+        // initiate holder of containers for system and device structure content, for performance reasons
+        //     note false to not include deleted entries
+        HolderSystemDeviceStructure holder = new HolderSystemDeviceStructure(holderIRepositories, false);
+
+        // validate data
+        ValidateUtil.validateStructureDataCreate(type, mnemonicpath, namingConvention, holderIRepositories, holder);
+
+        return Boolean.TRUE;
+    }
+
+    // ----------------------------------------------------------------------------------------------------
+
+    public void validateStructuresCreate(StructureElement structureElement) {
+        validateStructuresCreate(structureElement, new HolderSystemDeviceStructure(holderIRepositories));
+    }
+    public void validateStructuresCreate(StructureElement structureElement, HolderSystemDeviceStructure holder) {
+        // validate input
+        //     type
+        //     structure element
+        //         validate input itself
+        // validate data
+        //     structure element
+        //         validate data
+        //             itself
+        //             relative other data
+
+        // validate input
+        ValidateUtil.validateStructureElementInputCreate(structureElement);
+
+        // validate data
+        ValidateUtil.validateStructureElementDataCreate(structureElement, namingConvention, holderRepositories, holder);
+    }
+    public void validateStructuresCreate(List<StructureElement> structureElements) {
+        // initiate holder of containers for system and device structure content, for performance reasons
+        HolderSystemDeviceStructure holder = new HolderSystemDeviceStructure(holderIRepositories);
+
+        for (StructureElement structureElement : structureElements) {
+            validateStructuresCreate(structureElement, holder);
+        }
+    }
+
+    public void validateStructuresUpdate(StructureElement structureElement) {
+        validateStructuresUpdate(structureElement, new HolderSystemDeviceStructure(holderIRepositories));
+    }
+    public void validateStructuresUpdate(StructureElement structureElement, HolderSystemDeviceStructure holder) {
+        // validate input
+        //     type
+        //     structure element
+        //         validate input itself
+        // validate data
+        //     structure element
+        //         validate data
+        //             itself
+        //             relative other data
+
+        // validate input
+        ValidateUtil.validateStructureElementInputUpdate(structureElement);
+
+        // validate data
+        ValidateUtil.validateStructureElementDataUpdate(structureElement, namingConvention, holderRepositories, holder);
+    }
+    public void validateStructuresUpdate(List<StructureElement> structureElements) {
+        // initiate holder of containers for system and device structure content, for performance reasons
+        HolderSystemDeviceStructure holder = new HolderSystemDeviceStructure(holderIRepositories);
+
+        for (StructureElement structureElement : structureElements) {
+            validateStructuresUpdate(structureElement, holder);
+        }
+    }
+
+    public void validateStructuresDelete(StructureElement structureElement) {
+        validateStructuresDelete(structureElement, new HolderSystemDeviceStructure(holderIRepositories));
+    }
+    public void validateStructuresDelete(StructureElement structureElement, HolderSystemDeviceStructure holder) {
+        // validate input
+        //     type
+        //     structure element
+        //         validate input itself
+        // validate data
+        //     structure element
+        //         validate data
+        //             itself
+        //             relative other data
+
+        // validate input
+        ValidateUtil.validateStructureElementInputDelete(structureElement);
+
+        // validate data
+        ValidateUtil.validateStructureElementDataDelete(structureElement, namingConvention, holderRepositories, holder);
+    }
+    public void validateStructuresDelete(List<StructureElement> structureElements) {
+        // initiate holder of containers for system and device structure content, for performance reasons
+        HolderSystemDeviceStructure holder = new HolderSystemDeviceStructure(holderIRepositories);
+
+        for (StructureElement structureElement : structureElements) {
+            validateStructuresDelete(structureElement, holder);
+        }
+    }
+
+    public void validateStructuresApprove(StructureElement structureElement) {
+        validateStructuresApprove(structureElement, new HolderSystemDeviceStructure(holderIRepositories));
+    }
+    public void validateStructuresApprove(StructureElement structureElement, HolderSystemDeviceStructure holder) {
+        // validate input
+        //     type
+        //     structure element
+        //         validate input itself
+        // validate data
+        //     structure element
+        //         validate data
+        //             itself
+        //             relative other data
+
+        // validate input
+        ValidateUtil.validateStructureElementInputApprove(structureElement);
+
+        // validate data
+        ValidateUtil.validateStructureElementDataApprove(structureElement, namingConvention, holderRepositories, holder);
+    }
+    public void validateStructuresApprove(List<StructureElement> structureElements) {
+        // initiate holder of containers for system and device structure content, for performance reasons
+        HolderSystemDeviceStructure holder = new HolderSystemDeviceStructure(holderIRepositories);
+
+        for (StructureElement structureElement : structureElements) {
+            validateStructuresApprove(structureElement, holder);
+        }
+    }
+
+    public void validateStructuresCancel(StructureElement structureElement) {
+        validateStructuresCancel(structureElement, new HolderSystemDeviceStructure(holderIRepositories));
+    }
+    public void validateStructuresCancel(StructureElement structureElement, HolderSystemDeviceStructure holder) {
+        // validate input
+        //     type
+        //     structure element
+        //         validate input itself
+        // validate data
+        //     structure element
+        //         validate data
+        //             itself
+        //             relative other data
+
+        // validate input
+        ValidateUtil.validateStructureElementInputCancel(structureElement);
+
+        // validate data
+        ValidateUtil.validateStructureElementDataCancel(structureElement, namingConvention, holderRepositories, holder);
+    }
+    public void validateStructuresCancel(List<StructureElement> structureElements) {
+        // initiate holder of containers for system and device structure content, for performance reasons
+        HolderSystemDeviceStructure holder = new HolderSystemDeviceStructure(holderIRepositories);
+
+        for (StructureElement structureElement : structureElements) {
+            validateStructuresCancel(structureElement, holder);
+        }
+    }
+
+    public void validateStructuresReject(StructureElement structureElement) {
+        validateStructuresReject(structureElement, new HolderSystemDeviceStructure(holderIRepositories));
+    }
+    public void validateStructuresReject(StructureElement structureElement, HolderSystemDeviceStructure holder) {
+        // validate input
+        //     type
+        //     structure element
+        //         validate input itself
+        // validate data
+        //     structure element
+        //         validate data
+        //             itself
+        //             relative other data
+
+        // validate input
+        ValidateUtil.validateStructureElementInputReject(structureElement);
+
+        // validate data
+        ValidateUtil.validateStructureElementDataReject(structureElement, namingConvention, holderRepositories, holder);
+    }
+    public void validateStructuresReject(List<StructureElement> structureElements) {
+        // initiate holder of containers for system and device structure content, for performance reasons
+        HolderSystemDeviceStructure holder = new HolderSystemDeviceStructure(holderIRepositories);
+
+        for (StructureElement structureElement : structureElements) {
+            validateStructuresReject(structureElement, holder);
+        }
+    }
+
+    // ----------------------------------------------------------------------------------------------------
+
+    @Transactional
+    public List<StructureElement> updateStructures(List<StructureElement> structureElements) {
+        // validate
+        //     outside of @Transactional
+        // transaction
+        //     do
+        //         for each structure element
+        //             create structure to pending, not latest, not deleted, with data
+        //             add structure element for updated
+        //     return
+        //         structure elements for updated structures
+
+        LOGGER.log(Level.INFO, "updateStructures, structureElements.size:        " + String.valueOf(structureElements != null ? structureElements.size() : "null"));
+
+        // initiate holder of containers for system and device structure content, for performance reasons
+        HolderSystemDeviceStructure holder = new HolderSystemDeviceStructure(holderIRepositories);
+
+        // do
+        String requestedBy = "test who";
+        final List<StructureElement> updatedStructureElements = Lists.newArrayList();
+        for (StructureElement structureElement : structureElements) {
+            if (Type.SYSTEMGROUP.equals(structureElement.getType())) {
+                // note rules for mnemonic for system group
+                String mnemonic = structureElement.getMnemonic();
+                mnemonic = StringUtils.isEmpty(mnemonic) ? null : mnemonic;
+
+                // create
+                SystemGroup systemGroup = new SystemGroup();
+                setAttributes(systemGroup,
+                        structureElement.getUuid(),
+                        structureElement.getName(), mnemonic, !StringUtils.isEmpty(mnemonic) ? namingConvention.equivalenceClassRepresentative(mnemonic) : null,
+                        structureElement.getDescription(), Status.PENDING, Boolean.FALSE, Boolean.FALSE,
+                        new Date(), requestedBy, structureElement.getComment());
+
+                holderRepositories.getSystemGroupRepository().createSystemGroup(systemGroup);
+
+                // possibly validate that created
+
+                // add
+                updatedStructureElements.add(StructureElementUtil.getStructureElementRequested(systemGroup, holder));
+            } else if (Type.SYSTEM.equals(structureElement.getType())) {
+                // create
+                System system = new System();
+                setAttributes(system,
+                        structureElement.getUuid(), structureElement.getParent(),
+                        structureElement.getName(), structureElement.getMnemonic(), namingConvention.equivalenceClassRepresentative(structureElement.getMnemonic()),
+                        structureElement.getDescription(), Status.PENDING, Boolean.FALSE, Boolean.FALSE,
+                        new Date(), requestedBy, structureElement.getComment());
+
+                holderRepositories.getSystemRepository().createSystem(system);
+
+                // possibly validate that created
+
+                // add
+                updatedStructureElements.add(StructureElementUtil.getStructureElementRequested(system, holder));
+            } else if (Type.SUBSYSTEM.equals(structureElement.getType())) {
+                // create
+                Subsystem subsystem = new Subsystem();
+                setAttributes(subsystem,
+                        structureElement.getUuid(), structureElement.getParent(),
+                        structureElement.getName(), structureElement.getMnemonic(), namingConvention.equivalenceClassRepresentative(structureElement.getMnemonic()),
+                        structureElement.getDescription(), Status.PENDING, Boolean.FALSE, Boolean.FALSE,
+                        new Date(), requestedBy, structureElement.getComment());
+
+                holderRepositories.getSubsystemRepository().createSubsystem(subsystem);
+
+                // possibly validate that created
+
+                // add
+                updatedStructureElements.add(StructureElementUtil.getStructureElementRequested(subsystem, holder));
+            } else if (Type.DISCIPLINE.equals(structureElement.getType())) {
+                // create
+                Discipline discipline = new Discipline();
+                setAttributes(discipline,
+                        structureElement.getUuid(),
+                        structureElement.getName(), structureElement.getMnemonic(), namingConvention.equivalenceClassRepresentative(structureElement.getMnemonic()),
+                        structureElement.getDescription(), Status.PENDING, Boolean.FALSE, Boolean.FALSE,
+                        new Date(), requestedBy, structureElement.getComment());
+
+                holderRepositories.getDisciplineRepository().createDiscipline(discipline);
+
+                // possibly validate that created
+
+                // add
+                updatedStructureElements.add(StructureElementUtil.getStructureElementRequested(discipline, holder));
+            } else if (Type.DEVICEGROUP.equals(structureElement.getType())) {
+                // note rules for mnemonic for device group
+                String mnemonic = structureElement.getMnemonic();
+                mnemonic = StringUtils.isEmpty(mnemonic) ? null : mnemonic;
+
+                // create
+                DeviceGroup deviceGroup = new DeviceGroup();
+                setAttributes(deviceGroup,
+                        structureElement.getUuid(), structureElement.getParent(),
+                        structureElement.getName(), mnemonic, !StringUtils.isEmpty(mnemonic) ? namingConvention.equivalenceClassRepresentative(mnemonic) : null,
+                        structureElement.getDescription(), Status.PENDING, Boolean.FALSE, Boolean.FALSE,
+                        new Date(), requestedBy, structureElement.getComment());
+
+                holderRepositories.getDeviceGroupRepository().createDeviceGroup(deviceGroup);
+
+                // possibly validate that created
+
+                // add
+                updatedStructureElements.add(StructureElementUtil.getStructureElementRequested(deviceGroup, holder));
+            } else if (Type.DEVICETYPE.equals(structureElement.getType())) {
+                // create
+                DeviceType deviceType = new DeviceType();
+                setAttributes(deviceType,
+                        structureElement.getUuid(), structureElement.getParent(),
+                        structureElement.getName(), structureElement.getMnemonic(), namingConvention.equivalenceClassRepresentative(structureElement.getMnemonic()),
+                        structureElement.getDescription(), Status.PENDING, Boolean.FALSE, Boolean.FALSE,
+                        new Date(), requestedBy, structureElement.getComment());
+
+                holderRepositories.getDeviceTypeRepository().createDeviceType(deviceType);
+
+                // possibly validate that created
+
+                // add
+                updatedStructureElements.add(StructureElementUtil.getStructureElementRequested(deviceType, holder));
+            }
+        }
+
+        LOGGER.log(Level.INFO, "updateStructures, updatedStructureElements.size: " + updatedStructureElements.size());
+        return updatedStructureElements;
+    }
+
+    // ----------------------------------------------------------------------------------------------------
+
+    @Transactional
+    public List<StructureElement> deleteStructures(List<StructureElement> structureElements) {
+        // validate
+        //     outside of @Transactional
+        // transaction
+        //     do
+        //         for each structure element
+        //             find
+        //             create structure to pending, not latest, deleted, with data
+        //             add structure element for deleted
+        //     return
+        //         structure elements for deleted structures
+        // TODO continue ---> validation error
+
+        LOGGER.log(Level.INFO, "deleteStructures, structureElements.size:        " + String.valueOf(structureElements != null ? structureElements.size() : "null"));
+
+        // initiate holder of containers for system and device structure content, for performance reasons
+        HolderSystemDeviceStructure holder = new HolderSystemDeviceStructure(holderIRepositories);
+
+        // do
+        String requestedBy = "test who";
+        final List<StructureElement> deletedStructureElements = Lists.newArrayList();
+        for (StructureElement structureElement : structureElements) {
+            if (Type.SYSTEMGROUP.equals(structureElement.getType())) {
+                // find
+                List<SystemGroup> systemGroups = holderRepositories.getSystemGroupRepository().readSystemGroups(Status.APPROVED, false, FieldStructure.UUID, structureElement.getUuid().toString());
+                if (systemGroups == null || systemGroups.size() != 1) {
+                    continue;
+                }
+                SystemGroup toBeDeleted = systemGroups.get(0);
+
+                // create
+                SystemGroup systemGroup = new SystemGroup();
+                setAttributes(systemGroup,
+                        toBeDeleted.getUuid(),
+                        toBeDeleted.getName(), toBeDeleted.getMnemonic(), namingConvention.equivalenceClassRepresentative(toBeDeleted.getMnemonic()),
+                        toBeDeleted.getDescription(), Status.PENDING, Boolean.FALSE, Boolean.TRUE,
+                        new Date(), requestedBy, structureElement.getComment());
+
+                holderRepositories.getSystemGroupRepository().createSystemGroup(systemGroup);
+
+                // possibly validate that created
+
+                // add
+                deletedStructureElements.add(StructureElementUtil.getStructureElementRequested(systemGroup, holder));
+            } else if (Type.SYSTEM.equals(structureElement.getType())) {
+                // find
+                List<System> systems = holderRepositories.getSystemRepository().readSystems(Status.APPROVED, false, FieldStructure.UUID, structureElement.getUuid().toString());
+                if (systems == null || systems.size() != 1) {
+                    continue;
+                }
+                System toBeDeleted = systems.get(0);
+
+                // create
+                System system = new System();
+                setAttributes(system,
+                        toBeDeleted.getUuid(), toBeDeleted.getParentUuid(),
+                        toBeDeleted.getName(), toBeDeleted.getMnemonic(), namingConvention.equivalenceClassRepresentative(toBeDeleted.getMnemonic()),
+                        toBeDeleted.getDescription(), Status.PENDING, Boolean.FALSE, Boolean.TRUE,
+                        new Date(), requestedBy, structureElement.getComment());
+
+                holderRepositories.getSystemRepository().createSystem(system);
+
+                // possibly validate that created
+
+                // add
+                deletedStructureElements.add(StructureElementUtil.getStructureElementRequested(system, holder));
+            } else if (Type.SUBSYSTEM.equals(structureElement.getType())) {
+                // find
+                List<Subsystem> subsystems = holderRepositories.getSubsystemRepository().readSubsystems(Status.APPROVED, false, FieldStructure.UUID, structureElement.getUuid().toString());
+                if (subsystems == null || subsystems.size() != 1) {
+                    continue;
+                }
+                Subsystem toBeDeleted = subsystems.get(0);
+
+                // create
+                Subsystem subsystem = new Subsystem();
+                setAttributes(subsystem,
+                        toBeDeleted.getUuid(), toBeDeleted.getParentUuid(),
+                        toBeDeleted.getName(), toBeDeleted.getMnemonic(), namingConvention.equivalenceClassRepresentative(toBeDeleted.getMnemonic()),
+                        toBeDeleted.getDescription(), Status.PENDING, Boolean.FALSE, Boolean.TRUE,
+                        new Date(), requestedBy, structureElement.getComment());
+
+                holderRepositories.getSubsystemRepository().createSubsystem(subsystem);
+
+                // possibly validate that created
+
+                // add
+                deletedStructureElements.add(StructureElementUtil.getStructureElementRequested(subsystem, holder));
+            } else if (Type.DISCIPLINE.equals(structureElement.getType())) {
+                // find
+                List<Discipline> disciplines = holderRepositories.getDisciplineRepository().readDisciplines(Status.APPROVED, false, FieldStructure.UUID, structureElement.getUuid().toString());
+                if (disciplines == null || disciplines.size() != 1) {
+                    continue;
+                }
+                Discipline toBeDeleted = disciplines.get(0);
+
+                // create
+                Discipline discipline = new Discipline();
+                setAttributes(discipline,
+                        toBeDeleted.getUuid(),
+                        toBeDeleted.getName(), toBeDeleted.getMnemonic(), namingConvention.equivalenceClassRepresentative(toBeDeleted.getMnemonic()),
+                        toBeDeleted.getDescription(), Status.PENDING, Boolean.FALSE, Boolean.TRUE,
+                        new Date(), requestedBy, structureElement.getComment());
+
+                holderRepositories.getDisciplineRepository().createDiscipline(discipline);
+
+                // possibly validate that created
+
+                // add
+                deletedStructureElements.add(StructureElementUtil.getStructureElementRequested(discipline, holder));
+            } else if (Type.DEVICEGROUP.equals(structureElement.getType())) {
+                // find
+                List<DeviceGroup> deviceGroups = holderRepositories.getDeviceGroupRepository().readDeviceGroups(Status.APPROVED, false, FieldStructure.UUID, structureElement.getUuid().toString());
+                if (deviceGroups == null || deviceGroups.size() != 1) {
+                    continue;
+                }
+                DeviceGroup toBeDeleted = deviceGroups.get(0);
+
+                // create
+                DeviceGroup deviceGroup = new DeviceGroup();
+                setAttributes(deviceGroup,
+                        toBeDeleted.getUuid(), toBeDeleted.getParentUuid(),
+                        toBeDeleted.getName(), toBeDeleted.getMnemonic(), namingConvention.equivalenceClassRepresentative(toBeDeleted.getMnemonic()),
+                        toBeDeleted.getDescription(), Status.PENDING, Boolean.FALSE, Boolean.TRUE,
+                        new Date(), requestedBy, structureElement.getComment());
+
+                holderRepositories.getDeviceGroupRepository().createDeviceGroup(deviceGroup);
+
+                // possibly validate that created
+
+                // add
+                deletedStructureElements.add(StructureElementUtil.getStructureElementRequested(deviceGroup, holder));
+            } else if (Type.DEVICETYPE.equals(structureElement.getType())) {
+                // find
+                List<DeviceType> deviceTypes = holderRepositories.getDeviceTypeRepository().readDeviceTypes(Status.APPROVED, false, FieldStructure.UUID, structureElement.getUuid().toString());
+                if (deviceTypes == null || deviceTypes.size() != 1) {
+                    continue;
+                }
+                DeviceType toBeDeleted = deviceTypes.get(0);
+
+                // create
+                DeviceType deviceType = new DeviceType();
+                setAttributes(deviceType,
+                        toBeDeleted.getUuid(), toBeDeleted.getParentUuid(),
+                        toBeDeleted.getName(), toBeDeleted.getMnemonic(), namingConvention.equivalenceClassRepresentative(toBeDeleted.getMnemonic()),
+                        toBeDeleted.getDescription(), Status.PENDING, Boolean.FALSE, Boolean.TRUE,
+                        new Date(), requestedBy, structureElement.getComment());
+
+                holderRepositories.getDeviceTypeRepository().createDeviceType(deviceType);
+
+                // possibly validate that created
+
+                // add
+                deletedStructureElements.add(StructureElementUtil.getStructureElementRequested(deviceType, holder));
+            }
+        }
+
+        LOGGER.log(Level.INFO, "deleteStructures, deletedStructureElements.size: " + deletedStructureElements.size());
+        return deletedStructureElements;
+    }
+
+    // ----------------------------------------------------------------------------------------------------
+
+    @Transactional
+    public List<StructureElement> approveStructures(List<StructureElement> structureElements) {
+        // validate
+        //     outside of @Transactional
+        // transaction
+        //     do
+        //         update structure to status APPROVED
+        //     return
+        //         updated structure element
+        // TODO continue ---> validation error
+
+        LOGGER.log(Level.INFO, "approveStructures, structureElements.size:         " + String.valueOf(structureElements != null ? structureElements.size() : "null"));
+
+        // initiate holder of containers for system and device structure content, for performance reasons
+        HolderSystemDeviceStructure holder = new HolderSystemDeviceStructure(holderIRepositories);
+
+        // approve
+        //     set not latest for current latest
+        //     set approved, latest for pending
+        //     possibly
+        //         if deleted, names that belong to structure may be deleted, otherwise they are alive but become legacy names - legacy way for now
+        //         TODO if not deleted, rename names that belong to structure if different mnemonic
+        //            if that way, then compare what's about to be approved with what is latest
+
+        // do
+        //     update
+        //         set not latest for current latest
+        //         set approved, latest for pending
+        String processedBy = "test who";
+        String processedComment = "test comment";
+        final List<StructureElement> approvedStructureElements = Lists.newArrayList();
+        for (StructureElement structureElement : structureElements) {
+            if (Type.SYSTEMGROUP.equals(structureElement.getType())) {
+                // find
+                List<SystemGroup> systemGroups = holderRepositories.getSystemGroupRepository().readSystemGroups(Status.APPROVED, Boolean.FALSE, FieldStructure.UUID, structureElement.getUuid().toString());
+                SystemGroup systemGroup = null;
+                if (systemGroups != null && systemGroups.size() == 1) {
+                    systemGroup = systemGroups.get(0);
+
+                    // update not latest
+                    systemGroup.setLatest(Boolean.FALSE);
+                    holderRepositories.getSystemGroupRepository().updateSystemGroup(systemGroup);
+                }
+
+                // find
+                systemGroups = holderRepositories.getSystemGroupRepository().readSystemGroups(Status.PENDING, null, FieldStructure.UUID, structureElement.getUuid().toString());
+                if (systemGroups == null || systemGroups.size() != 1) {
+                    continue;
+                }
+                systemGroup = systemGroups.get(0);
+
+                // approve
+                setAttributesStatusProcessed(systemGroup, Status.APPROVED, new Date(), processedBy, processedComment);
+                systemGroup.setLatest(Boolean.TRUE);
+                holderRepositories.getSystemGroupRepository().updateSystemGroup(systemGroup);
+
+                // possibly validate that approved
+
+                // add
+                approvedStructureElements.add(StructureElementUtil.getStructureElementProcessed(systemGroup, holder));
+            } else if (Type.SYSTEM.equals(structureElement.getType())) {
+                // find
+                List<System> systems = holderRepositories.getSystemRepository().readSystems(Status.APPROVED, Boolean.FALSE, FieldStructure.UUID, structureElement.getUuid().toString());
+                System system = null;
+                if (systems != null && systems.size() == 1) {
+                    system = systems.get(0);
+
+                    // update not latest
+                    system.setLatest(Boolean.FALSE);
+                    holderRepositories.getSystemRepository().updateSystem(system);
+                }
+
+                // find
+                systems = holderRepositories.getSystemRepository().readSystems(Status.PENDING, null, FieldStructure.UUID, structureElement.getUuid().toString());
+                if (systems == null || systems.size() != 1) {
+                    continue;
+                }
+                system = systems.get(0);
+
+                // approve
+                setAttributesStatusProcessed(system, Status.APPROVED, new Date(), processedBy, processedComment);
+                system.setLatest(Boolean.TRUE);
+                holderRepositories.getSystemRepository().updateSystem(system);
+
+                // possibly validate that approved
+
+                // add
+                approvedStructureElements.add(StructureElementUtil.getStructureElementProcessed(system, holder));
+            } else if (Type.SUBSYSTEM.equals(structureElement.getType())) {
+                // find
+                List<Subsystem> subsystems = holderRepositories.getSubsystemRepository().readSubsystems(Status.APPROVED, Boolean.FALSE, FieldStructure.UUID, structureElement.getUuid().toString());
+                Subsystem subsystem = null;
+                if (subsystems != null && subsystems.size() == 1) {
+                    subsystem = subsystems.get(0);
+
+                    // update not latest
+                    subsystem.setLatest(Boolean.FALSE);
+                    holderRepositories.getSubsystemRepository().updateSubsystem(subsystem);
+                }
+
+                // find
+                subsystems = holderRepositories.getSubsystemRepository().readSubsystems(Status.PENDING, null, FieldStructure.UUID, structureElement.getUuid().toString());
+                if (subsystems == null || subsystems.size() != 1) {
+                    continue;
+                }
+                subsystem = subsystems.get(0);
+
+                // approve
+                setAttributesStatusProcessed(subsystem, Status.APPROVED, new Date(), processedBy, processedComment);
+                subsystem.setLatest(Boolean.TRUE);
+                holderRepositories.getSubsystemRepository().updateSubsystem(subsystem);
+
+                // possibly validate that approved
+
+                // add
+                approvedStructureElements.add(StructureElementUtil.getStructureElementProcessed(subsystem, holder));
+            } else if (Type.DISCIPLINE.equals(structureElement.getType())) {
+                List<Discipline> disciplines = holderRepositories.getDisciplineRepository().readDisciplines(Status.APPROVED, Boolean.FALSE, FieldStructure.UUID, structureElement.getUuid().toString());
+                Discipline discipline = null;
+                if (disciplines != null && disciplines.size() == 1) {
+                    discipline = disciplines.get(0);
+
+                    // update not latest
+                    discipline.setLatest(Boolean.FALSE);
+                    holderRepositories.getDisciplineRepository().updateDiscipline(discipline);
+                }
+
+                // find
+                disciplines = holderRepositories.getDisciplineRepository().readDisciplines(Status.PENDING, null, FieldStructure.UUID, structureElement.getUuid().toString());
+                if (disciplines == null || disciplines.size() != 1) {
+                    continue;
+                }
+                discipline = disciplines.get(0);
+
+                // approve
+                setAttributesStatusProcessed(discipline, Status.APPROVED, new Date(), processedBy, processedComment);
+                discipline.setLatest(Boolean.TRUE);
+                holderRepositories.getDisciplineRepository().updateDiscipline(discipline);
+
+                // possibly validate that approved
+
+                // add
+                approvedStructureElements.add(StructureElementUtil.getStructureElementProcessed(discipline, holder));
+            } else if (Type.DEVICEGROUP.equals(structureElement.getType())) {
+                // find
+                List<DeviceGroup> deviceGroups = holderRepositories.getDeviceGroupRepository().readDeviceGroups(Status.APPROVED, Boolean.FALSE, FieldStructure.UUID, structureElement.getUuid().toString());
+                DeviceGroup deviceGroup = null;
+                if (deviceGroups != null && deviceGroups.size() == 1) {
+                    deviceGroup = deviceGroups.get(0);
+
+                    // update not latest
+                    deviceGroup.setLatest(Boolean.FALSE);
+                    holderRepositories.getDeviceGroupRepository().updateDeviceGroup(deviceGroup);
+                }
+
+                // find
+                deviceGroups = holderRepositories.getDeviceGroupRepository().readDeviceGroups(Status.PENDING, null, FieldStructure.UUID, structureElement.getUuid().toString());
+                if (deviceGroups == null || deviceGroups.size() != 1) {
+                    continue;
+                }
+                deviceGroup = deviceGroups.get(0);
+
+                // approve
+                setAttributesStatusProcessed(deviceGroup, Status.APPROVED, new Date(), processedBy, processedComment);
+                deviceGroup.setLatest(Boolean.TRUE);
+                holderRepositories.getDeviceGroupRepository().updateDeviceGroup(deviceGroup);
+
+                // possibly validate that approved
+
+                // add
+                approvedStructureElements.add(StructureElementUtil.getStructureElementProcessed(deviceGroup, holder));
+            } else if (Type.DEVICETYPE.equals(structureElement.getType())) {
+                // find
+                List<DeviceType> deviceTypes = holderRepositories.getDeviceTypeRepository().readDeviceTypes(Status.APPROVED, Boolean.FALSE, FieldStructure.UUID, structureElement.getUuid().toString());
+                DeviceType deviceType = null;
+                if (deviceTypes != null && deviceTypes.size() == 1) {
+                    deviceType = deviceTypes.get(0);
+
+                    // update not latest
+                    deviceType.setLatest(Boolean.FALSE);
+                    holderRepositories.getDeviceTypeRepository().updateDeviceType(deviceType);
+                }
+
+                // find
+                deviceTypes = holderRepositories.getDeviceTypeRepository().readDeviceTypes(Status.PENDING, null, FieldStructure.UUID, structureElement.getUuid().toString());
+                if (deviceTypes == null || deviceTypes.size() != 1) {
+                    continue;
+                }
+                deviceType = deviceTypes.get(0);
+
+                // approve
+                setAttributesStatusProcessed(deviceType, Status.APPROVED, new Date(), processedBy, processedComment);
+                deviceType.setLatest(Boolean.TRUE);
+                holderRepositories.getDeviceTypeRepository().updateDeviceType(deviceType);
+
+                // possibly validate that approved
+
+                // add
+                approvedStructureElements.add(StructureElementUtil.getStructureElementProcessed(deviceType, holder));
+            }
+        }
+
+        LOGGER.log(Level.INFO, "approveStructures, approvedStructureElements.size: " + approvedStructureElements.size());
+        return approvedStructureElements;
+    }
+
+    @Transactional
+    public List<StructureElement> cancelStructures(List<StructureElement> structureElements) {
+        // validate
+        //     outside of @Transactional
+        // transaction
+        //     do
+        //         update structure to status CANCELLED
+        //     return
+        //         updated structure element
+        // TODO continue ---> validation error
+
+        LOGGER.log(Level.INFO, "cancelStructures, structureElements.size:          " + String.valueOf(structureElements != null ? structureElements.size() : "null"));
+
+        // initiate holder of containers for system and device structure content, for performance reasons
+        HolderSystemDeviceStructure holder = new HolderSystemDeviceStructure(holderIRepositories);
+
+        // do
+        String processedBy = "test who";
+        String processedComment = "test comment";
+        final List<StructureElement> cancelledStructureElements = Lists.newArrayList();
+        for (StructureElement structureElement : structureElements) {
+            if (Type.SYSTEMGROUP.equals(structureElement.getType())) {
+                // find
+                List<SystemGroup> systemGroups = holderRepositories.getSystemGroupRepository().readSystemGroups(Status.PENDING, null, FieldStructure.UUID, structureElement.getUuid().toString());
+                if (systemGroups == null || systemGroups.size() != 1) {
+                    continue;
+                }
+                SystemGroup systemGroup = systemGroups.get(0);
+
+                // cancel
+                setAttributesStatusProcessed(systemGroup, Status.CANCELLED, new Date(), processedBy, processedComment);
+                holderRepositories.getSystemGroupRepository().updateSystemGroup(systemGroup);
+
+                // possibly validate that cancelled
+
+                // add
+                cancelledStructureElements.add(StructureElementUtil.getStructureElementProcessed(systemGroup, holder));
+            } else if (Type.SYSTEM.equals(structureElement.getType())) {
+                // find
+                List<System> systems = holderRepositories.getSystemRepository().readSystems(Status.PENDING, null, FieldStructure.UUID, structureElement.getUuid().toString());
+                if (systems == null || systems.size() != 1) {
+                    continue;
+                }
+                System system = systems.get(0);
+
+                // cancel
+                setAttributesStatusProcessed(system, Status.CANCELLED, new Date(), processedBy, processedComment);
+                holderRepositories.getSystemRepository().updateSystem(system);
+
+                // possibly validate that cancelled
+
+                // add
+                cancelledStructureElements.add(StructureElementUtil.getStructureElementProcessed(system, holder));
+            } else if (Type.SUBSYSTEM.equals(structureElement.getType())) {
+                // find
+                List<Subsystem> subsystems = holderRepositories.getSubsystemRepository().readSubsystems(Status.PENDING, null, FieldStructure.UUID, structureElement.getUuid().toString());
+                if (subsystems == null || subsystems.size() != 1) {
+                    continue;
+                }
+                Subsystem subsystem = subsystems.get(0);
+
+                // cancel
+                setAttributesStatusProcessed(subsystem, Status.CANCELLED, new Date(), processedBy, processedComment);
+                holderRepositories.getSubsystemRepository().updateSubsystem(subsystem);
+
+                // possibly validate that cancelled
+
+                // add
+                cancelledStructureElements.add(StructureElementUtil.getStructureElementProcessed(subsystem, holder));
+            } else if (Type.DISCIPLINE.equals(structureElement.getType())) {
+                // find
+                List<Discipline> disciplines = holderRepositories.getDisciplineRepository().readDisciplines(Status.PENDING, null, FieldStructure.UUID, structureElement.getUuid().toString());
+                if (disciplines == null || disciplines.size() != 1) {
+                    continue;
+                }
+                Discipline discipline = disciplines.get(0);
+
+                // cancel
+                setAttributesStatusProcessed(discipline, Status.CANCELLED, new Date(), processedBy, processedComment);
+                holderRepositories.getDisciplineRepository().updateDiscipline(discipline);
+
+                // possibly validate that cancelled
+
+                // add
+                cancelledStructureElements.add(StructureElementUtil.getStructureElementProcessed(discipline, holder));
+            } else if (Type.DEVICEGROUP.equals(structureElement.getType())) {
+                // find
+                List<DeviceGroup> deviceGroups = holderRepositories.getDeviceGroupRepository().readDeviceGroups(Status.PENDING, null, FieldStructure.UUID, structureElement.getUuid().toString());
+                if (deviceGroups == null || deviceGroups.size() != 1) {
+                    continue;
+                }
+                DeviceGroup deviceGroup = deviceGroups.get(0);
+
+                // cancel
+                setAttributesStatusProcessed(deviceGroup, Status.CANCELLED, new Date(), processedBy, processedComment);
+                holderRepositories.getDeviceGroupRepository().updateDeviceGroup(deviceGroup);
+
+                // possibly validate that cancelled
+
+                // add
+                cancelledStructureElements.add(StructureElementUtil.getStructureElementProcessed(deviceGroup, holder));
+            } else if (Type.DEVICETYPE.equals(structureElement.getType())) {
+                List<DeviceType> deviceTypes = holderRepositories.getDeviceTypeRepository().readDeviceTypes(Status.PENDING, null, FieldStructure.UUID, structureElement.getUuid().toString());
+                if (deviceTypes == null || deviceTypes.size() != 1) {
+                    continue;
+                }
+                DeviceType deviceType = deviceTypes.get(0);
+
+                // cancel
+                setAttributesStatusProcessed(deviceType, Status.CANCELLED, new Date(), processedBy, processedComment);
+                holderRepositories.getDeviceTypeRepository().updateDeviceType(deviceType);
+
+                // possibly validate that cancelled
+
+                // add
+                cancelledStructureElements.add(StructureElementUtil.getStructureElementProcessed(deviceType, holder));
+            }
+        }
+
+        LOGGER.log(Level.INFO, "cancelStructures, cancelledStructureElements.size: " + cancelledStructureElements.size());
+        return cancelledStructureElements;
+    }
+
+    @Transactional
+    public List<StructureElement> rejectStructures(List<StructureElement> structureElements) {
+        // validate
+        //     outside of @Transactional
+        // transaction
+        //     do
+        //         update structure to status REJECTED
+        //     return
+        //         updated structure element
+        // TODO continue ---> validation error
+
+        LOGGER.log(Level.INFO, "rejectStructures, structureElements.size:         " + String.valueOf(structureElements != null ? structureElements.size() : "null"));
+
+        // initiate holder of containers for system and device structure content, for performance reasons
+        HolderSystemDeviceStructure holder = new HolderSystemDeviceStructure(holderIRepositories);
+
+        // do
+        String processedBy = "test who";
+        String processedComment = "test comment";
+        final List<StructureElement> rejectedStructureElements = Lists.newArrayList();
+        for (StructureElement structureElement : structureElements) {
+            if (Type.SYSTEMGROUP.equals(structureElement.getType())) {
+                // find
+                List<SystemGroup> systemGroups = holderRepositories.getSystemGroupRepository().readSystemGroups(Status.PENDING, null, FieldStructure.UUID, structureElement.getUuid().toString());
+                if (systemGroups == null || systemGroups.size() != 1) {
+                    continue;
+                }
+                SystemGroup systemGroup = systemGroups.get(0);
+
+                // reject
+                setAttributesStatusProcessed(systemGroup, Status.REJECTED, new Date(), processedBy, processedComment);
+                holderRepositories.getSystemGroupRepository().updateSystemGroup(systemGroup);
+
+                // possibly validate that rejected
+
+                // add
+                rejectedStructureElements.add(StructureElementUtil.getStructureElementProcessed(systemGroup, holder));
+            } else if (Type.SYSTEM.equals(structureElement.getType())) {
+                // find
+                List<System> systems = holderRepositories.getSystemRepository().readSystems(Status.PENDING, null, FieldStructure.UUID, structureElement.getUuid().toString());
+                if (systems == null || systems.size() != 1) {
+                    continue;
+                }
+                System system = systems.get(0);
+
+                // reject
+                setAttributesStatusProcessed(system, Status.REJECTED, new Date(), processedBy, processedComment);
+                holderRepositories.getSystemRepository().updateSystem(system);
+
+                // possibly validate that rejected
+
+                // add
+                rejectedStructureElements.add(StructureElementUtil.getStructureElementProcessed(system, holder));
+            } else if (Type.SUBSYSTEM.equals(structureElement.getType())) {
+                // find
+                List<Subsystem> subsystems = holderRepositories.getSubsystemRepository().readSubsystems(Status.PENDING, null, FieldStructure.UUID, structureElement.getUuid().toString());
+                if (subsystems == null || subsystems.size() != 1) {
+                    continue;
+                }
+                Subsystem subsystem = subsystems.get(0);
+
+                // reject
+                setAttributesStatusProcessed(subsystem, Status.REJECTED, new Date(), processedBy, processedComment);
+                holderRepositories.getSubsystemRepository().updateSubsystem(subsystem);
+
+                // possibly validate that rejected
+
+                // add
+                rejectedStructureElements.add(StructureElementUtil.getStructureElementProcessed(subsystem, holder));
+            } else if (Type.DISCIPLINE.equals(structureElement.getType())) {
+                // find
+                List<Discipline> disciplines = holderRepositories.getDisciplineRepository().readDisciplines(Status.PENDING, null, FieldStructure.UUID, structureElement.getUuid().toString());
+                if (disciplines == null || disciplines.size() != 1) {
+                    continue;
+                }
+                Discipline discipline = disciplines.get(0);
+
+                // reject
+                setAttributesStatusProcessed(discipline, Status.REJECTED, new Date(), processedBy, processedComment);
+                holderRepositories.getDisciplineRepository().updateDiscipline(discipline);
+
+                // possibly validate that rejected
+
+                // add
+                rejectedStructureElements.add(StructureElementUtil.getStructureElementProcessed(discipline, holder));
+            } else if (Type.DEVICEGROUP.equals(structureElement.getType())) {
+                // find
+                List<DeviceGroup> deviceGroups = holderRepositories.getDeviceGroupRepository().readDeviceGroups(Status.PENDING, null, FieldStructure.UUID, structureElement.getUuid().toString());
+                if (deviceGroups == null || deviceGroups.size() != 1) {
+                    continue;
+                }
+                DeviceGroup deviceGroup = deviceGroups.get(0);
+
+                // reject
+                setAttributesStatusProcessed(deviceGroup, Status.REJECTED, new Date(), processedBy, processedComment);
+                holderRepositories.getDeviceGroupRepository().updateDeviceGroup(deviceGroup);
+
+                // possibly validate that rejected
+
+                // add
+                rejectedStructureElements.add(StructureElementUtil.getStructureElementProcessed(deviceGroup, holder));
+            } else if (Type.DEVICETYPE.equals(structureElement.getType())) {
+                List<DeviceType> deviceTypes = holderRepositories.getDeviceTypeRepository().readDeviceTypes(Status.PENDING, null, FieldStructure.UUID, structureElement.getUuid().toString());
+                if (deviceTypes == null || deviceTypes.size() != 1) {
+                    continue;
+                }
+                DeviceType deviceType = deviceTypes.get(0);
+
+                // reject
+                setAttributesStatusProcessed(deviceType, Status.REJECTED, new Date(), processedBy, processedComment);
+                holderRepositories.getDeviceTypeRepository().updateDeviceType(deviceType);
+
+                // possibly validate that rejected
+
+                // add
+                rejectedStructureElements.add(StructureElementUtil.getStructureElementProcessed(deviceType, holder));
+            }
+        }
+
+        LOGGER.log(Level.INFO, "rejectStructures, rejectedStructureElements.size: " + rejectedStructureElements.size());
+        return rejectedStructureElements;
+    }
+
+    // ----------------------------------------------------------------------------------------------------
+
+    /**
+     * Utility method to help set attributes for Structure class, which in practice is either of its sub classes.
+     *
+     * @param structure structure
+     * @param status status
+     * @param requested requested
+     * @param requestedBy requested by
+     * @param requestedComment requested comment
+     */
+    private void setAttributesStatusProcessed(Structure structure, Status status, Date processed, String processedBy, String processedComment) {
+        structure.setStatus(status);
+        structure.setProcessed(processed);
+        structure.setProcessedBy(processedBy);
+        structure.setProcessedComment(processedComment);
+    }
+
+    private void setAttributes(SystemGroup systemGroup, UUID uuid, String name, String mnemonic, String mnemonicEquivalence, String description, Status status, Boolean isLatest, Boolean isDeleted, Date requested, String requestedBy, String requestedComment) {
+        setAttributesStructure(systemGroup, uuid, name, mnemonic, mnemonicEquivalence, description, status, isLatest, isDeleted, requested, requestedBy, requestedComment);
+    }
+    private void setAttributes(System system, UUID uuid, UUID parentUuid, String name, String mnemonic, String mnemonicEquivalence, String description, Status status, Boolean isLatest, Boolean isDeleted, Date requested, String requestedBy, String requestedComment) {
+        system.setParentUuid(parentUuid);
+        setAttributesStructure(system, uuid, name, mnemonic, mnemonicEquivalence, description, status, isLatest, isDeleted, requested, requestedBy, requestedComment);
+    }
+    private void setAttributes(Subsystem subsystem, UUID uuid, UUID parentUuid, String name, String mnemonic, String mnemonicEquivalence, String description, Status status, Boolean isLatest, Boolean isDeleted, Date requested, String requestedBy, String requestedComment) {
+        subsystem.setParentUuid(parentUuid);
+        setAttributesStructure(subsystem, uuid, name, mnemonic, mnemonicEquivalence, description, status, isLatest, isDeleted, requested, requestedBy, requestedComment);
+    }
+    private void setAttributes(Discipline discipline, UUID uuid, String name, String mnemonic, String mnemonicEquivalence, String description, Status status, Boolean isLatest, Boolean isDeleted, Date requested, String requestedBy, String requestedComment) {
+        setAttributesStructure(discipline, uuid, name, mnemonic, mnemonicEquivalence, description, status, isLatest, isDeleted, requested, requestedBy, requestedComment);
+    }
+    private void setAttributes(DeviceGroup deviceGroup, UUID uuid, UUID parentUuid, String name, String mnemonic, String mnemonicEquivalence, String description, Status status, Boolean isLatest, Boolean isDeleted, Date requested, String requestedBy, String requestedComment) {
+        deviceGroup.setParentUuid(parentUuid);
+        setAttributesStructure(deviceGroup, uuid, name, mnemonic, mnemonicEquivalence, description, status, isLatest, isDeleted, requested, requestedBy, requestedComment);
+    }
+    private void setAttributes(DeviceType deviceType, UUID uuid, UUID parentUuid, String name, String mnemonic, String mnemonicEquivalence, String description, Status status, Boolean isLatest, Boolean isDeleted, Date requested, String requestedBy, String requestedComment) {
+        deviceType.setParentUuid(parentUuid);
+        setAttributesStructure(deviceType, uuid, name, mnemonic, mnemonicEquivalence, description, status, isLatest, isDeleted, requested, requestedBy, requestedComment);
+    }
+
+    /**
+     * Utility method to help set attributes for Structure class, which in practice is either of its sub classes.
+     *
+     * @param structure structure
+     * @param uuid uuid
+     * @param name name
+     * @param mnemonic mnemonic
+     * @param mnemonicEquivalence mnemonic equivalence
+     * @param description description
+     * @param status status
+     * @param latest latest
+     * @param deleted deleted
+     * @param requested requested
+     * @param requestedBy requested by
+     * @param requestedComment requested comment
+     */
+    private void setAttributesStructure(Structure structure,
+            UUID uuid,
+            String name, String mnemonic, String mnemonicEquivalence,
+            String description, Status status, Boolean latest, Boolean deleted,
+            Date requested, String requestedBy, String requestedComment) {
+        structure.setUuid(uuid);
+        structure.setName(name);
+        structure.setMnemonic(mnemonic);
+        structure.setMnemonicEquivalence(mnemonicEquivalence);
+        structure.setDescription(description);
+        structure.setStatus(status);
+        structure.setLatest(latest);
+        structure.setDeleted(deleted);
+        structure.setRequested(requested);
+        structure.setRequestedBy(requestedBy);
+        structure.setRequestedComment(requestedComment);
+    }
+
+}
diff --git a/src/main/java/org/openepics/names/util/EssNamingConvention.java b/src/main/java/org/openepics/names/util/EssNamingConvention.java
new file mode 100644
index 0000000..ffd3906
--- /dev/null
+++ b/src/main/java/org/openepics/names/util/EssNamingConvention.java
@@ -0,0 +1,173 @@
+/*-
+ * Copyright (c) 2014 European Spallation Source ERIC.
+ * Copyright (c) 2014 Cosylab d.d.
+ *
+ * This file is part of Naming Service.
+ * Naming Service is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 2 of the License, or any newer version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see https://www.gnu.org/licenses/gpl-2.0.txt
+ */
+
+package org.openepics.names.util;
+
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * A naming convention definition used by ESS.
+ *
+ * @author Marko Kolar
+ * @author Karin Rathsman
+ * @author Lars Johansson
+ *
+ * @see NamingConvention
+ * @see NamingConventionUtil
+ */
+public class EssNamingConvention implements NamingConvention {
+
+    // ----------------------------------------------------------------------------------------------------
+    // rules for characters
+    //     empty
+    //     space
+    //     value
+    //     alphanumeric
+    //     letter case
+    //     length
+    //     equivalenceClassRepresentative
+    // note
+    //     mnemonic can be empty
+    //         system structure - system group
+    //         device structure - device group
+    // note
+    //     mnemonic for system group may be part of ess name
+    //     mnemonic for instance index may be omitted for ess name
+    // ----------------------------------------------------------------------------------------------------
+
+    // Note revision history of file in repository.
+
+    private static final String MNEMONIC_ALPHABETIC_LOWERCASE = "^[a-z]+$";
+    private static final String MNEMONIC_ALPHANUMERIC         = "^[a-zA-Z0-9]+$";
+    private static final String MNEMONIC_NUMERIC              = "^[0-9]+$";
+    private static final String MNEMONIC_NUMERIC_ZERO         = "^[0]+$";
+
+    @Override
+    public boolean isInstanceIndexValid(String conventionName) {
+        // not override ruleset for instanceIndex
+        return isInstanceIndexValid(conventionName, false);
+    }
+
+    @Override
+    public boolean isInstanceIndexValid(String conventionName, boolean overrideRuleset) {
+        String instanceIndex = NamingConventionUtil.extractInstanceIndex(conventionName);
+        if (overrideRuleset) {
+            // previous rules, less restrictions
+
+            if (instanceIndex != null && instanceIndex.length() <= 6) {
+                return instanceIndex.matches(MNEMONIC_ALPHANUMERIC);
+            } else {
+                return false;
+            }
+        } else {
+            // normal path
+
+            // p&id numeric
+            // p&id
+            // scientific
+
+            if (NamingConventionUtil.isDisciplinePID(NamingConventionUtil.extractDiscipline(conventionName))) {
+                if (NamingConventionUtil.isMnemonicPathDeviceStructurePIDNumeric(NamingConventionUtil.extractMnemonicPathDeviceStructure(conventionName))) {
+                    // 3 numeric
+                    return isInstanceIndexValidNumeric(instanceIndex, 3, 3);
+                }
+                // 3 numeric & 0-3 alphabetic
+                return isInstanceIndexValidPID(instanceIndex);
+            } else {
+                // 1-4 numeric & non-zero
+                return isInstanceIndexValidNumeric(instanceIndex, 1, 4);
+            }
+        }
+    }
+
+    /**
+     * Return if instance index is valid PID index.
+     *
+     * @param index instance index
+     * @return if instance index is valid
+     */
+    private boolean isInstanceIndexValidPID(String index) {
+        if (index != null && index.length() >= 3 && index.length() <= 6) {
+            if (index.length() == 3) {
+                return index.matches(MNEMONIC_NUMERIC)
+                        && !index.matches(MNEMONIC_NUMERIC_ZERO);
+            } else {
+                return index.substring(0, 3).matches(MNEMONIC_NUMERIC)
+                        && index.substring(3, index.length()).matches(MNEMONIC_ALPHABETIC_LOWERCASE);
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Return if instance index is valid numeric index.
+     *
+     * @param index instance index
+     * @param nMin minimum length
+     * @param nMax maximum length
+     * @return if instance index is valid
+     */
+    private boolean isInstanceIndexValidNumeric(String index, int nMin, int nMax) {
+        if ((index == null || index.length() == 0)) {
+            return nMin <= 0;
+        } else {
+            return (index.length() >= nMin && index.length() <= nMax)
+                    && index.matches(MNEMONIC_NUMERIC)
+                    && !index.matches(MNEMONIC_NUMERIC_ZERO);
+        }
+    }
+
+    @Override
+    public String equivalenceClassRepresentative(String name) {
+        return name != null
+                ? name.toUpperCase()
+                        .replaceAll("(?<=[A-Za-z])0+", "")
+                        .replace('I', '1').replace('L', '1').replace('O', '0')
+                        .replaceAll("(?<!\\d)0+(?=\\d)", "")
+                : null;
+    }
+
+    @Override
+    public boolean isMnemonicPathValid(String mnemonicPath) {
+        // note
+        //     split with help of utility
+        // valid if
+        //     length 1, 2, 3
+        //     same mnemonic only once in mnemonic path
+        if (!StringUtils.isEmpty(mnemonicPath)) {
+            String[] values = NamingConventionUtil.string2MnemonicPath(mnemonicPath.trim());
+            if (values.length < 0 || values.length > 3) {
+                return false;
+            }
+            switch (values.length) {
+                case 3:
+                    return !StringUtils.equals(values[0], values[1])
+                            && !StringUtils.equals(values[0], values[2])
+                            && !StringUtils.equals(values[1], values[2]);
+                case 2:
+                    return !StringUtils.equals(values[0], values[1]);
+                case 1:
+                    return true;
+                default:
+                    return false;
+            }
+        }
+        return false;
+    }
+
+}
diff --git a/src/main/java/org/openepics/names/util/HolderRepositories.java b/src/main/java/org/openepics/names/util/HolderRepositories.java
new file mode 100644
index 0000000..0fcd1df
--- /dev/null
+++ b/src/main/java/org/openepics/names/util/HolderRepositories.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2021 European Spallation Source ERIC.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+package org.openepics.names.util;
+
+import org.openepics.names.repository.DeviceGroupRepository;
+import org.openepics.names.repository.DeviceTypeRepository;
+import org.openepics.names.repository.DisciplineRepository;
+import org.openepics.names.repository.NameRepository;
+import org.openepics.names.repository.SubsystemRepository;
+import org.openepics.names.repository.SystemGroupRepository;
+import org.openepics.names.repository.SystemRepository;
+
+/**
+ * Utility class and holder of references to repositories.
+ *
+ * @author Lars Johansson
+ */
+public class HolderRepositories {
+
+    private NameRepository nameRepository;
+
+    private SystemGroupRepository systemGroupRepository;
+    private SystemRepository systemRepository;
+    private SubsystemRepository subsystemRepository;
+
+    private DisciplineRepository disciplineRepository;
+    private DeviceGroupRepository deviceGroupRepository;
+    private DeviceTypeRepository deviceTypeRepository;
+
+    /**
+     * Public constructor to populate references to repositories.
+     *
+     * @param nameRepository reference to name repository
+     * @param systemGroupRepository reference to system group repository
+     * @param systemRepository reference to system repository
+     * @param subsystemRepository reference to subsystem repository
+     * @param disciplineRepository reference to discipline repository
+     * @param deviceGroupRepository reference to device group repository
+     * @param deviceTypeRepository reference to device type repository
+     */
+    public HolderRepositories(
+            NameRepository nameRepository,
+            SystemGroupRepository systemGroupRepository,
+            SystemRepository systemRepository,
+            SubsystemRepository subsystemRepository,
+            DisciplineRepository disciplineRepository,
+            DeviceGroupRepository deviceGroupRepository,
+            DeviceTypeRepository deviceTypeRepository) {
+        this.nameRepository = nameRepository;
+        this.systemGroupRepository = systemGroupRepository;
+        this.systemRepository = systemRepository;
+        this.subsystemRepository = subsystemRepository;
+        this.disciplineRepository = disciplineRepository;
+        this.deviceGroupRepository = deviceGroupRepository;
+        this.deviceTypeRepository = deviceTypeRepository;
+    }
+
+    /**
+     * Return reference to name repository.
+     *
+     * @return reference to name repository
+     */
+    public NameRepository getNameRepository() {
+        return nameRepository;
+    };
+
+    /**
+     * Return reference to system group repository.
+     *
+     * @return reference to system group repository
+     */
+    public SystemGroupRepository getSystemGroupRepository() {
+        return systemGroupRepository;
+    };
+    /**
+     * Return reference to system repository.
+     *
+     * @return reference to system repository
+     */
+    public SystemRepository getSystemRepository() {
+        return systemRepository;
+    };
+    /**
+     * Return reference to subsystem repository.
+     *
+     * @return reference to subsystem repository
+     */
+    public SubsystemRepository getSubsystemRepository() {
+        return subsystemRepository;
+    };
+
+    /**
+     * Return reference to discipline repository.
+     *
+     * @return reference to discipline repository
+     */
+    public DisciplineRepository getDisciplineRepository() {
+        return disciplineRepository;
+    };
+    /**
+     * Return reference to device group repository.
+     *
+     * @return reference to device group repository
+     */
+    public DeviceGroupRepository getDeviceGroupRepository() {
+        return deviceGroupRepository;
+    };
+    /**
+     * Return reference to device type repository.
+     *
+     * @return reference to device type repository
+     */
+    public DeviceTypeRepository getDeviceTypeRepository() {
+        return deviceTypeRepository;
+    };
+
+}
diff --git a/src/main/java/org/openepics/names/util/LogUtil.java b/src/main/java/org/openepics/names/util/LogUtil.java
new file mode 100644
index 0000000..a088b95
--- /dev/null
+++ b/src/main/java/org/openepics/names/util/LogUtil.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2022 European Spallation Source ERIC.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+package org.openepics.names.util;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * Utility class to assist in handling of logs.
+ *
+ * @author Lars Johansson
+ */
+public class LogUtil {
+
+    private static final String ORG_OPENEPICS_NAMES                     = "org.openepics.names";
+    private static final String ORG_OPENEPICS_NAMES_UTIL_EXCEPTION_UTIL = "org.openepics.names.util.ExceptionUtil";
+
+    /**
+     * This class is not to be instantiated.
+     */
+    private LogUtil() {
+        throw new IllegalStateException("Utility class");
+    }
+
+    /**
+     * Log service http status exception.
+     *
+     * @param logger logger
+     * @param level log level
+     * @param e service https status exception
+     */
+    public static void logServiceHttpStatusException(Logger logger, Level level, ServiceHttpStatusException e) {
+        if (logger == null || level == null || e == null) {
+            return;
+        }
+
+        String msg = !StringUtils.isEmpty(e.getMessage())
+                ? e.getMessage()
+                : "";
+        String details = !StringUtils.isEmpty(e.getDetails())
+                ? " ### " + e.getDetails()
+                : "";
+        String httpStatus = e.getHttpStatus() != null
+                ? " ### " + e.getHttpStatus().toString()
+                : "";
+        msg = msg + details + httpStatus;
+
+        logger.log(level, msg);
+    }
+
+    /**
+     * Log stack trace elements.
+     *
+     * @param logger logger
+     * @param level log level
+     * @param e service https status exception
+     */
+    public static void logStackTraceElements(Logger logger, Level level, ServiceHttpStatusException e) {
+        logStackTraceElements(logger, Level.SEVERE, e, 10, ORG_OPENEPICS_NAMES, ORG_OPENEPICS_NAMES_UTIL_EXCEPTION_UTIL);
+    }
+
+    /**
+     * Log stack trace elements.
+     *
+     * @param logger logger
+     * @param level log level
+     * @param e exception
+     */
+    public static void logStackTraceElements(Logger logger, Level level, Exception e) {
+        logStackTraceElements(logger, Level.SEVERE, e, 10, ORG_OPENEPICS_NAMES, ORG_OPENEPICS_NAMES_UTIL_EXCEPTION_UTIL);
+    }
+
+    /**
+     * Log stack trace elements.
+     *
+     * @param logger logger
+     * @param level log level
+     * @param e service https status exception
+     * @param maxNumberOfLogs max number of logs
+     * @param filterInclude filter include
+     * @param filterExclude filter exclude
+     */
+    public static void logStackTraceElements(Logger logger, Level level, ServiceHttpStatusException e, int maxNumberOfLogs, String filterInclude, String filterExclude) {
+        if (logger == null || level == null || e == null || maxNumberOfLogs <= 0) {
+            return;
+        }
+
+        logStackTraceElements(logger, level, e.getStackTrace(), maxNumberOfLogs, filterInclude, filterExclude);
+    }
+
+    /**
+     * Log stack trace elements.
+     *
+     * @param logger logger
+     * @param level log level
+     * @param e exception
+     * @param maxNumberOfLogs max number of logs
+     * @param filterInclude filter include
+     * @param filterExclude filter exclude
+     */
+    public static void logStackTraceElements(Logger logger, Level level, Exception e, int maxNumberOfLogs, String filterInclude, String filterExclude) {
+        if (logger == null || level == null || e == null || maxNumberOfLogs <= 0) {
+            return;
+        }
+
+        logStackTraceElements(logger, level, e.getStackTrace(), maxNumberOfLogs, filterInclude, filterExclude);
+    }
+
+    /**
+     * Log stack trace elements.
+     *
+     * @param logger logger
+     * @param level log level
+     * @param stackTraceElements stack trace elements
+     * @param maxNumberOfLogs max number of logs
+     * @param filterInclude filter include
+     * @param filterExclude filter exclude
+     */
+    private static void logStackTraceElements(Logger logger, Level level, StackTraceElement[] stackTraceElements, int maxNumberOfLogs, String filterInclude, String filterExclude) {
+        if (logger == null || level == null || stackTraceElements == null || maxNumberOfLogs <= 0) {
+            return;
+        }
+
+        String str;
+        int count = 0;
+        for (StackTraceElement stackTraceElement : stackTraceElements) {
+            str = stackTraceElement.toString();
+
+            if  ((StringUtils.isEmpty(filterInclude) || str.contains(filterInclude))
+                    && !(!StringUtils.isEmpty(filterExclude) && str.contains(filterExclude))) {
+                count++;
+                logger.log(level, str);
+                if (count == maxNumberOfLogs) {
+                    break;
+                }
+            }
+        }
+    }
+
+}
diff --git a/src/main/java/org/openepics/names/util/NameElementUtil.java b/src/main/java/org/openepics/names/util/NameElementUtil.java
new file mode 100644
index 0000000..6ad6c29
--- /dev/null
+++ b/src/main/java/org/openepics/names/util/NameElementUtil.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2021 European Spallation Source ERIC.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+package org.openepics.names.util;
+
+import java.util.Date;
+import java.util.UUID;
+
+import org.openepics.names.repository.model.Name;
+import org.openepics.names.repository.model.NameStructure;
+import org.openepics.names.rest.beans.NameElement;
+import org.openepics.names.rest.beans.Status;
+
+/**
+ * Utility class to assist in populating name elements based on repository content.
+ * <br/><br/>
+ * Different strategies for population of name elements are used in different methods.
+ * The difference in strategies is for finding out system structure and device structure mnemonic paths.
+ *
+ * @author Lars Johansson
+ */
+public class NameElementUtil {
+
+    /**
+     * This class is not to be instantiated.
+     */
+    private NameElementUtil() {
+        throw new IllegalStateException("Utility class");
+    }
+
+    /**
+     * Populate and return name element for name.
+     *
+     * @param name name
+     * @return name element
+     */
+    public static NameElement getNameElement(Name name) {
+        if (name == null) {
+            return null;
+        }
+
+        return getNameElement(
+                name.getUuid(),
+                name.getSystemgroupUuid(), name.getSystemUuid(), name.getSubsystemUuid(), name.getDevicetypeUuid(),
+                NamingConventionUtil.extractMnemonicPathSystemStructure(name.getConventionName()),
+                NamingConventionUtil.extractMnemonicPathDeviceStructure(name.getConventionName()),
+                name.getInstanceIndex(), name.getConventionName(),
+                name.getDescription(), Status.APPROVED, name.isLatest(), name.isDeleted(),
+                name.getRequested(), name.getRequestedBy(), name.getRequestedComment());
+    }
+
+    /**
+     * Populate and return name element.
+     *
+     * @param uuid uuid
+     * @param systemgroup system group
+     * @param system system
+     * @param subsystem subsystem
+     * @param devicetype device type
+     * @param systemstructure system structure mnemonic path
+     * @param devicestructure device structure mnemonic path
+     * @param index instance index
+     * @param name name
+     * @param description description
+     * @param status status
+     * @param latest latest
+     * @param deleted deleted
+     * @param when when
+     * @param who who
+     * @param comment comment
+     * @return name element
+     */
+    protected static NameElement getNameElement(
+            UUID uuid,
+            UUID systemgroup, UUID system, UUID subsystem, UUID devicetype,
+            String systemstructure, String devicestructure,
+            String index, String name,
+            String description, Status status, Boolean latest, Boolean deleted,
+            Date when, String who, String comment) {
+
+        return new NameElement(
+                uuid,
+                systemgroup, system, subsystem, devicetype,
+                systemstructure, devicestructure,
+                index, name,
+                description, status, latest, deleted,
+                when, who, comment);
+    }
+
+    private static boolean hasSameContent(NameElement nameElement, NameStructure nameStructure) {
+        /*
+           NameElement
+               x uuid
+               x description
+               x status
+               x latest
+               x deleted
+           NameStructure
+               x uuid
+               x description
+               x status
+               x latest
+               x deleted
+         */
+
+        if (nameElement == null && nameStructure == null)
+            return true;
+        if (nameElement == null)
+            return false;
+        if (nameStructure == null)
+            return false;
+
+        if (nameElement.getUuid() == null) {
+            if (nameStructure.getUuid() != null)
+                return false;
+        } else if (!nameElement.getUuid().equals(nameStructure.getUuid()))
+            return false;
+        if (nameElement.getDescription() == null) {
+            if (nameStructure.getDescription() != null)
+                return false;
+        } else if (!nameElement.getDescription().equals(nameStructure.getDescription()))
+            return false;
+        if (nameElement.getStatus() == null) {
+            if (nameStructure.getStatus() != null)
+                return false;
+        } else if (!nameElement.getStatus().equals(nameStructure.getStatus()))
+            return false;
+        if (nameElement.isLatest() == null) {
+            if (nameStructure.isLatest() != null)
+                return false;
+        } else if (!nameElement.isLatest().equals(nameStructure.isLatest()))
+            return false;
+        if (nameElement.isDeleted() == null) {
+            if (nameStructure.isDeleted() != null)
+                return false;
+        } else if (!nameElement.isDeleted().equals(nameStructure.isDeleted()))
+            return false;
+
+        return true;
+    }
+
+    public static boolean hasSameContent(NameElement nameElement, Name name) {
+        /*
+           NameElement
+               x systemgroup
+               x system
+               x subsystem
+               x devicetype
+               x index
+               x name
+           Name
+               x systemgroup_uuid
+               x system_uuid
+               x subsystem_uuid
+               x devicetype_uuid
+               x instance_index
+               x convention_name
+         */
+
+        if (!hasSameContent(nameElement, (NameStructure) name))
+            return false;
+
+        if (nameElement.getSystemgroup() == null) {
+            if (name.getSystemgroupUuid() != null)
+                return false;
+        } else if (!nameElement.getSystemgroup().equals(name.getSystemgroupUuid()))
+            return false;
+        if (nameElement.getSystem() == null) {
+            if (name.getSystemUuid() != null)
+                return false;
+        } else if (!nameElement.getSystem().equals(name.getSystemUuid()))
+            return false;
+        if (nameElement.getSubsystem() == null) {
+            if (name.getSubsystemUuid() != null)
+                return false;
+        } else if (!nameElement.getSubsystem().equals(name.getSubsystemUuid()))
+            return false;
+        if (nameElement.getDevicetype() == null) {
+            if (name.getDevicetypeUuid() != null)
+                return false;
+        } else if (!nameElement.getDevicetype().equals(name.getDevicetypeUuid()))
+            return false;
+        if (nameElement.getIndex() == null) {
+            if (name.getInstanceIndex() != null)
+                return false;
+        } else if (!nameElement.getIndex().equals(name.getInstanceIndex()))
+            return false;
+        if (nameElement.getName() == null) {
+            if (name.getConventionName() != null)
+                return false;
+        } else if (!nameElement.getName().equals(name.getConventionName()))
+            return false;
+
+        return true;
+    }
+
+//    /**
+//     * Populate and return name element for name.
+//     *
+//     * @param name name
+//     * @param holderSystemDeviceStructure holder of containers for system and device structure content
+//     * @return name element
+//     */
+//    public static NameElement getNameElement(Name name, HolderSystemDeviceStructure holderSystemDeviceStructure) {
+//        if (name == null) {
+//            return null;
+//        }
+//
+//        // find out how to populate return element for system structure, device structure
+//        // levelSystemStructure -1 ---> error
+//        // levelDeviceStructure -1 ---> error
+//
+//        return new NameElement(
+//                name.getUuid(),
+//                name.getSystemgroupUuid(), name.getSystemUuid(), name.getSubsystemUuid(), name.getDevicetypeUuid(),
+//                getMnemonicPathSystemStructure(
+//                        name,
+//                        StructureUtil.getLevelSystemStructure(name),
+//                        holderSystemDeviceStructure),
+//                getMnemonicPathDeviceStructure(
+//                        name,
+//                        StructureUtil.getLevelDeviceStructure(name),
+//                        holderSystemDeviceStructure),
+//                name.getInstanceIndex(), name.getConventionName(),
+//                name.getDescription(), Status.APPROVED, name.isLatest(), name.isDeleted(),
+//                name.getRequested(), name.getRequestedBy(), name.getRequestedComment());
+//    }
+//
+//    /**
+//     * Populate and return name element for name.
+//     *
+//     * @param name name
+//     * @param holderIRepositories holder of references to repositories
+//     * @return name element
+//     */
+//    public static NameElement getNameElement(Name name, HolderIRepositories holderIRepositories) {
+//        if (name == null) {
+//            return null;
+//        }
+//
+//        // find out how to populate return element for system structure, device structure
+//        // levelSystemStructure -1 ---> error
+//        // levelDeviceStructure -1 ---> error
+//
+//        return new NameElement(
+//                name.getUuid(),
+//                name.getSystemgroupUuid(), name.getSystemUuid(), name.getSubsystemUuid(), name.getDevicetypeUuid(),
+//                getMnemonicPathSystemStructure(
+//                        name,
+//                        StructureUtil.getLevelSystemStructure(name),
+//                        holderIRepositories),
+//                getMnemonicPathDeviceStructure(
+//                        name,
+//                        StructureUtil.getLevelDeviceStructure(name),
+//                        holderIRepositories),
+//                name.getInstanceIndex(), name.getConventionName(),
+//                name.getDescription(), Status.APPROVED, name.isLatest(), name.isDeleted(),
+//                name.getRequested(), name.getRequestedBy(), name.getRequestedComment());
+//    }
+
+}
diff --git a/src/main/java/org/openepics/names/util/NamingConvention.java b/src/main/java/org/openepics/names/util/NamingConvention.java
new file mode 100644
index 0000000..b99c15e
--- /dev/null
+++ b/src/main/java/org/openepics/names/util/NamingConvention.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2014 European Spallation Source ERIC.
+ * Copyright (c) 2014 Cosylab d.d.
+ *
+ * This file is part of Naming Service.
+ * Naming Service is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 2 of the License, or any newer version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see https://www.gnu.org/licenses/gpl-2.0.txt
+ */
+
+package org.openepics.names.util;
+
+/**
+ * An interface defining the naming convention to be used by the Naming application.
+ * It includes:
+ * <ul>
+ * <li> name validation rules
+ * <li> name uniqueness rules
+ * <li> form of composite names
+ * </ul>
+ *
+ * <p>
+ * The used naming convention is configured through beans.xml using the CDI alternatives mechanism.
+ *
+ * <p>
+ * A device name consists of  elements having general structure
+ * <ul>
+ * <li> <pre>Sys-Sub:Dis-Dev-Idx</pre>
+ * <li> <pre>Sup-Sys-Sub:Dis-Dev-Idx</pre>
+ * </ul>
+ * It consists of of <br/><br/>
+ * <ul>
+ * <li> System structure
+ * <ul>
+ * <li> System group (Sup)(optional)
+ * <li> System (Sys)
+ * <li> Subsystem (Sub)
+ * </ul>
+ * <li> Device structure
+ * <ul>
+ * <li> Discipline (Dis)
+ * <li> Device type (Dev)
+ * </ul>
+ * <li> Device
+ * <ul>
+ * <li> Instance index (Idx)
+ * </ul>
+ * </ul>
+ *
+ * A device name thus consists of an system structure element, a device structure element and an instance index.
+ * Together, elements make a device name. In addition, there is intermediate level in device structure
+ * (device group, between discipline and device type) for grouping purposes.<br/><br/>
+ *
+ * <p>
+ * An element in the name structure is typically handled through its mnemonic path and type.
+ * <ul>
+ * <li> mnemonic path is a list of mnemonics starting from the root of the hierarchy to the intended mnemonic
+ * <li> root of hierarchy is system group (system structure) or discipline (device structure)
+ * <li> type is structure that mnemonic path belongs, system or device structure
+ * </ul>
+ *
+ * <p>
+ * Elements consist of full name, mnemonic, description and follow rules, individually and together
+ * (system structure, device structure, device). Among rules:
+ * <ul>
+ * <li> empty
+ * <li> space
+ * <li> value
+ * <li> alphanumeric
+ * <li> letter case
+ * <li> length
+ * <li> equivalence class representative
+ * </ul>
+ *
+ * <p>
+ * Key concepts
+ * <ul>
+ * <li> mnemonic required or not
+ * <li> mnemonic valid or not
+ * <li> if mnemonic can coexist with other mnemonic
+ * <li> equivalence, uniqueness of names when treating similar looking names
+ * </ul>
+ *
+ * <p>
+ * Note
+ * <ul>
+ * <li> system structure is logical structure
+ * <li> depth of system and device structures is 3
+ * <li> names typically handled and referred to through mnemonics
+ * <li> naming convention also referred to as convention
+ * </ul>
+ *
+ * @author Marko Kolar
+ * @author Karin Rathsman
+ * @author Lars Johansson
+ *
+ * @see <a href="https://confluence.esss.lu.se/display/NC/ESS+Naming+Convention"/>
+ *               https://confluence.esss.lu.se/display/NC/ESS+Naming+Convention</a>
+ * @see <a href="https://chess.esss.lu.se/enovia/tvc-action/showObject/dmg_TechnicalSpecification/ESS-0000757/valid">
+ *               https://chess.esss.lu.se/enovia/tvc-action/showObject/dmg_TechnicalSpecification/ESS-0000757/valid</a>
+ *
+ * @see NamingConventionUtil
+ */
+public interface NamingConvention {
+
+    /**
+     * Return if the convention name's instance index is valid according to convention rules,
+     * in the context of system structure and device structure.
+     *
+     * @param conventionName convention name
+     * @return <tt>true</tt> if the convention name's instance index is valid according to convention rules,
+     *         in the context of system structure and device structure
+     */
+    boolean isInstanceIndexValid(String conventionName);
+
+    /**
+     * Return if the convention name's instance index is valid according to convention rules,
+     * in the context of system structure and device structure.
+     *
+     * @param conventionName convention name
+     * @param overrideRuleset if ruleset for instance index is to be overridden, e.g. for super user
+     * @return <tt>true</tt> if the convention name's instance index is valid according to convention rules,
+     *         in the context of system structure and device structure
+     */
+    boolean isInstanceIndexValid(String conventionName, boolean overrideRuleset);
+
+    /**
+     * Return equivalence class representative for given name. This is used to ensure uniqueness of names
+     * when treating similar looking names, e.g. 0 vs. O, 1 vs. l treated as as equal.
+     *
+     * @param name      name of which to determine the equivalence class representative
+     * @return          equivalence class representative for given name
+     */
+    String equivalenceClassRepresentative(String name);
+
+    /**
+     * Return if mnemonic path is valid within the application according to the convention rules.
+     *
+     * @param mnemonicPath      list of mnemonics starting from the root of the hierarchy to the mnemonic to be tested
+     * @return                  <tt>true</tt> if mnemonic path is valid within the application according to the convention rules
+     */
+    boolean isMnemonicPathValid(String mnemonicPath);
+
+}
diff --git a/src/main/java/org/openepics/names/util/StructureElementUtil.java b/src/main/java/org/openepics/names/util/StructureElementUtil.java
new file mode 100644
index 0000000..1a40fbb
--- /dev/null
+++ b/src/main/java/org/openepics/names/util/StructureElementUtil.java
@@ -0,0 +1,800 @@
+/*
+ * Copyright (C) 2021 European Spallation Source ERIC.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+package org.openepics.names.util;
+
+import java.util.Date;
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.commons.lang3.StringUtils;
+import org.openepics.names.repository.model.DeviceGroup;
+import org.openepics.names.repository.model.DeviceType;
+import org.openepics.names.repository.model.Discipline;
+import org.openepics.names.repository.model.NameStructure;
+import org.openepics.names.repository.model.Structure;
+import org.openepics.names.repository.model.Subsystem;
+import org.openepics.names.repository.model.System;
+import org.openepics.names.repository.model.SystemGroup;
+import org.openepics.names.rest.beans.Status;
+import org.openepics.names.rest.beans.StructureElement;
+import org.openepics.names.rest.beans.Type;
+
+import com.google.common.collect.Lists;
+
+/**
+ * Utility class to assist in populating structure elements based on repository content.
+ *
+ * @author Lars Johansson
+ */
+public class StructureElementUtil {
+
+    public static enum StructureChoice {HISTORY, STRUCTURE};
+
+    private static final long THOUSAND_MILLISECONDS = 1000;
+
+    /**
+     * This class is not to be instantiated.
+     */
+    private StructureElementUtil() {
+        throw new IllegalStateException("Utility class");
+    }
+
+    /**
+     * Populate and return list of structure elements for system groups.
+     *
+     * @param systemGroups system groups
+     * @param holderSystemDeviceStructure holder of containers for system and device structure content
+     * @param structureChoice whether to consider content from structure perspective or history perspective.
+     *                        Structure perspective gives one StructureElement object (processed).
+     *                        History perspective gives two StructureElement objects (processed, requested).
+     *                        If choice not given then default as structure perspective (processed).
+     * @return list of structure elements
+     */
+    public static List<StructureElement> getStructureElementsForSystemGroups(List<SystemGroup> systemGroups, HolderSystemDeviceStructure holderSystemDeviceStructure, StructureChoice structureChoice) {
+        List<StructureElement> structureElements = Lists.newArrayList();
+        for (SystemGroup systemGroup : systemGroups) {
+            // one or two return elements
+            //     processed != null and processed != requested (>  1s difference) --> two entries (processed, requested)
+            //     processed != null and processed == requested (<= 1s difference) --> one entry   (processed initial)
+            //     processed == null                                               --> one entry   (requested)
+
+            if (StructureChoice.HISTORY.equals(structureChoice)) {
+                if (systemGroup.getProcessed() != null && ((systemGroup.getProcessed().getTime() - systemGroup.getRequested().getTime()) > THOUSAND_MILLISECONDS)) {
+                    structureElements.add(getStructureElementProcessed(systemGroup, holderSystemDeviceStructure));
+                    structureElements.add(getStructureElementRequested(systemGroup, holderSystemDeviceStructure));
+                } else if (systemGroup.getProcessed() != null && ((systemGroup.getProcessed().getTime() - systemGroup.getRequested().getTime()) <= THOUSAND_MILLISECONDS)) {
+                    structureElements.add(getStructureElementProcessed(systemGroup, holderSystemDeviceStructure));
+                } else {
+                    structureElements.add(getStructureElementRequested(systemGroup, holderSystemDeviceStructure));
+                }
+            } else {
+                if (Status.PENDING.equals(systemGroup.getStatus())) {
+                    structureElements.add(getStructureElementRequested(systemGroup, holderSystemDeviceStructure));
+                } else {
+                    structureElements.add(getStructureElementProcessed(systemGroup, holderSystemDeviceStructure));
+                }
+            }
+        }
+        return structureElements;
+    }
+    /**
+     * Populate and return list of structure elements for systems.
+     *
+     * @param systems systems
+     * @param holderSystemDeviceStructure holder of containers for system and device structure content
+     * @return list of structure elements
+     */
+    public static List<StructureElement> getStructureElementsForSystems(List<System> systems, HolderSystemDeviceStructure holderSystemDeviceStructure, StructureChoice structureChoice) {
+        List<StructureElement> structureElements = Lists.newArrayList();
+        for (System system : systems) {
+            // one or two return elements
+            //     processed != null and processed != requested (>  1s difference) --> two entries (processed, requested)
+            //     processed != null and processed == requested (<= 1s difference) --> one entry   (processed initial)
+            //     processed == null                                               --> one entry   (requested)
+
+            if (StructureChoice.HISTORY.equals(structureChoice)) {
+                if (system.getProcessed() != null && ((system.getProcessed().getTime() - system.getRequested().getTime()) > THOUSAND_MILLISECONDS)) {
+                    structureElements.add(getStructureElementProcessed(system, holderSystemDeviceStructure));
+                    structureElements.add(getStructureElementRequested(system, holderSystemDeviceStructure));
+                } else if (system.getProcessed() != null && ((system.getProcessed().getTime() - system.getRequested().getTime()) <= THOUSAND_MILLISECONDS)) {
+                    structureElements.add(getStructureElementProcessed(system, holderSystemDeviceStructure));
+                } else {
+                    structureElements.add(getStructureElementRequested(system, holderSystemDeviceStructure));
+                }
+            } else {
+                if (Status.PENDING.equals(system.getStatus())) {
+                    structureElements.add(getStructureElementRequested(system, holderSystemDeviceStructure));
+                } else {
+                    structureElements.add(getStructureElementProcessed(system, holderSystemDeviceStructure));
+                }
+            }
+        }
+        return structureElements;
+    }
+    /**
+     * Populate and return list of structure elements for subsystems.
+     *
+     * @param subsystems subsystems
+     * @param holderSystemDeviceStructure holder of containers for system and device structure content
+     * @return list of structure elements
+     */
+    public static List<StructureElement> getStructureElementsForSubsystems(List<Subsystem> subsystems, HolderSystemDeviceStructure holderSystemDeviceStructure, StructureChoice structureChoice) {
+        List<StructureElement> structureElements = Lists.newArrayList();
+        for (Subsystem subsystem : subsystems) {
+            // one or two return elements
+            //     processed != null and processed != requested (>  1s difference) --> two entries (processed, requested)
+            //     processed != null and processed == requested (<= 1s difference) --> one entry   (processed initial)
+            //     processed == null                                               --> one entry   (requested)
+
+            if (StructureChoice.HISTORY.equals(structureChoice)) {
+                if (subsystem.getProcessed() != null && ((subsystem.getProcessed().getTime() - subsystem.getRequested().getTime()) > THOUSAND_MILLISECONDS)) {
+                    structureElements.add(getStructureElementProcessed(subsystem, holderSystemDeviceStructure));
+                    structureElements.add(getStructureElementRequested(subsystem, holderSystemDeviceStructure));
+                } else if (subsystem.getProcessed() != null && ((subsystem.getProcessed().getTime() - subsystem.getRequested().getTime()) <= THOUSAND_MILLISECONDS)) {
+                    structureElements.add(getStructureElementProcessed(subsystem, holderSystemDeviceStructure));
+                } else {
+                    structureElements.add(getStructureElementRequested(subsystem, holderSystemDeviceStructure));
+                }
+            } else {
+                if (Status.PENDING.equals(subsystem.getStatus())) {
+                    structureElements.add(getStructureElementRequested(subsystem, holderSystemDeviceStructure));
+                } else {
+                    structureElements.add(getStructureElementProcessed(subsystem, holderSystemDeviceStructure));
+                }
+            }
+        }
+        return structureElements;
+    }
+
+    /**
+     * Populate and return list of structure elements for disciplines.
+     *
+     * @param disciplines disciplines
+     * @param holderSystemDeviceStructure holder of containers for system and device structure content
+     * @return list of structure elements
+     */
+    public static List<StructureElement> getStructureElementsForDisciplines(List<Discipline> disciplines, HolderSystemDeviceStructure holderSystemDeviceStructure, StructureChoice structureChoice) {
+        List<StructureElement> structureElements = Lists.newArrayList();
+        for (Discipline discipline : disciplines) {
+            // one or two return elements
+            //     processed != null and processed != requested (>  1s difference) --> two entries (processed, requested)
+            //     processed != null and processed == requested (<= 1s difference) --> one entry   (processed initial)
+            //     processed == null                                               --> one entry   (requested)
+
+            if (StructureChoice.HISTORY.equals(structureChoice)) {
+                if (discipline.getProcessed() != null && ((discipline.getProcessed().getTime() - discipline.getRequested().getTime()) > THOUSAND_MILLISECONDS)) {
+                    structureElements.add(getStructureElementProcessed(discipline, holderSystemDeviceStructure));
+                    structureElements.add(getStructureElementRequested(discipline, holderSystemDeviceStructure));
+                } else if (discipline.getProcessed() != null && ((discipline.getProcessed().getTime() - discipline.getRequested().getTime()) <= THOUSAND_MILLISECONDS)) {
+                    structureElements.add(getStructureElementProcessed(discipline, holderSystemDeviceStructure));
+                } else {
+                    structureElements.add(getStructureElementRequested(discipline, holderSystemDeviceStructure));
+                }
+            } else {
+                if (Status.PENDING.equals(discipline.getStatus())) {
+                    structureElements.add(getStructureElementRequested(discipline, holderSystemDeviceStructure));
+                } else {
+                    structureElements.add(getStructureElementProcessed(discipline, holderSystemDeviceStructure));
+                }
+            }
+        }
+        return structureElements;
+    }
+    /**
+     * Populate and return list of structure elements for device groups.
+     *
+     * @param deviceGroups device groups
+     * @param holderSystemDeviceStructure holder of containers for system and device structure content
+     * @return list of structure elements
+     */
+    public static List<StructureElement> getStructureElementsForDeviceGroups(List<DeviceGroup> deviceGroups, HolderSystemDeviceStructure holderSystemDeviceStructure, StructureChoice structureChoice) {
+        List<StructureElement> structureElements = Lists.newArrayList();
+        for (DeviceGroup deviceGroup : deviceGroups) {
+            // one or two return elements
+            //     processed != null and processed != requested (>  1s difference) --> two entries (processed, requested)
+            //     processed != null and processed == requested (<= 1s difference) --> one entry   (processed initial)
+            //     processed == null                                               --> one entry   (requested)
+
+            if (StructureChoice.HISTORY.equals(structureChoice)) {
+                if (deviceGroup.getProcessed() != null && ((deviceGroup.getProcessed().getTime() - deviceGroup.getRequested().getTime()) > THOUSAND_MILLISECONDS)) {
+                    structureElements.add(getStructureElementProcessed(deviceGroup, holderSystemDeviceStructure));
+                    structureElements.add(getStructureElementRequested(deviceGroup, holderSystemDeviceStructure));
+                } else if (deviceGroup.getProcessed() != null && ((deviceGroup.getProcessed().getTime() - deviceGroup.getRequested().getTime()) <= THOUSAND_MILLISECONDS)) {
+                    structureElements.add(getStructureElementProcessed(deviceGroup, holderSystemDeviceStructure));
+                } else {
+                    structureElements.add(getStructureElementRequested(deviceGroup, holderSystemDeviceStructure));
+                }
+            } else {
+                if (Status.PENDING.equals(deviceGroup.getStatus())) {
+                    structureElements.add(getStructureElementRequested(deviceGroup, holderSystemDeviceStructure));
+                } else {
+                    structureElements.add(getStructureElementProcessed(deviceGroup, holderSystemDeviceStructure));
+                }
+            }
+        }
+        return structureElements;
+    }
+    /**
+     * Populate and return list of structure elements for device types.
+     *
+     * @param deviceTypes device types
+     * @param holderSystemDeviceStructure holder of containers for system and device structure content
+     * @return list of structure elements
+     */
+    public static List<StructureElement> getStructureElementsForDeviceTypes(List<DeviceType> deviceTypes, HolderSystemDeviceStructure holderSystemDeviceStructure, StructureChoice structureChoice) {
+        List<StructureElement> structureElements = Lists.newArrayList();
+        for (DeviceType deviceType : deviceTypes) {
+            // one or two return elements
+            //     processed != null and processed != requested (>  1s difference) --> two entries (processed, requested)
+            //     processed != null and processed == requested (<= 1s difference) --> one entry   (processed initial)
+            //     processed == null                                               --> one entry   (requested)
+
+            if (StructureChoice.HISTORY.equals(structureChoice)) {
+                if (deviceType.getProcessed() != null && ((deviceType.getProcessed().getTime() - deviceType.getRequested().getTime()) > THOUSAND_MILLISECONDS)) {
+                    structureElements.add(getStructureElementProcessed(deviceType, holderSystemDeviceStructure));
+                    structureElements.add(getStructureElementRequested(deviceType, holderSystemDeviceStructure));
+                } else if (deviceType.getProcessed() != null && ((deviceType.getProcessed().getTime() - deviceType.getRequested().getTime()) <= THOUSAND_MILLISECONDS)) {
+                    structureElements.add(getStructureElementProcessed(deviceType, holderSystemDeviceStructure));
+                } else {
+                    structureElements.add(getStructureElementRequested(deviceType, holderSystemDeviceStructure));
+                }
+            } else {
+                if (Status.PENDING.equals(deviceType.getStatus())) {
+                    structureElements.add(getStructureElementRequested(deviceType, holderSystemDeviceStructure));
+                } else {
+                    structureElements.add(getStructureElementProcessed(deviceType, holderSystemDeviceStructure));
+                }
+            }
+        }
+        return structureElements;
+    }
+
+    /**
+     * Populate and return structure element for system group with focus on processed.
+     *
+     * @param systemGroup system group
+     * @param holderSystemDeviceStructure holder of containers for system and device structure content
+     * @return structure element
+     */
+    public static StructureElement getStructureElementProcessed(SystemGroup systemGroup, HolderSystemDeviceStructure holderSystemDeviceStructure) {
+        if (systemGroup == null) {
+            return null;
+        }
+
+//        String mnemonicpath = holderSystemDeviceStructure != null
+//                ? systemGroup.getMnemonic()
+//                : null;
+        String mnemonicpath = !StringUtils.isEmpty(systemGroup.getMnemonic())
+                ? systemGroup.getMnemonic()
+                : null;
+
+        return getStructureElement(
+                Type.SYSTEMGROUP,
+                systemGroup.getUuid(),
+                null,
+                systemGroup.getName(), systemGroup.getMnemonic(), mnemonicpath, 1,
+                systemGroup.getDescription(), systemGroup.getStatus(), systemGroup.isLatest(), systemGroup.isDeleted(),
+                systemGroup.getProcessed(), systemGroup.getProcessedBy(), systemGroup.getProcessedComment());
+    }
+    /**
+     * Populate and return structure element for system group with focus on requested.
+     *
+     * @param systemGroup system group
+     * @param holderSystemDeviceStructure holder of containers for system and device structure content
+     * @return structure element
+     */
+    public static StructureElement getStructureElementRequested(SystemGroup systemGroup, HolderSystemDeviceStructure holderSystemDeviceStructure) {
+        if (systemGroup == null) {
+            return null;
+        }
+
+//        String mnemonicpath = holderSystemDeviceStructure != null
+//                ? systemGroup.getMnemonic()
+//                : null;
+        String mnemonicpath = !StringUtils.isEmpty(systemGroup.getMnemonic())
+                ? systemGroup.getMnemonic()
+                : null;
+
+        return getStructureElement(
+                Type.SYSTEMGROUP,
+                systemGroup.getUuid(),
+                null,
+                systemGroup.getName(), systemGroup.getMnemonic(), mnemonicpath, 1,
+                systemGroup.getDescription(), Status.PENDING, systemGroup.isLatest(), systemGroup.isDeleted(),
+                systemGroup.getRequested(), systemGroup.getRequestedBy(), systemGroup.getRequestedComment());
+    }
+    /**
+     * Populate and return structure element for system with focus on processed.
+     *
+     * @param system system
+     * @param holderSystemDeviceStructure holder of containers for system and device structure content
+     * @return structure element
+     */
+    public static StructureElement getStructureElementProcessed(System system, HolderSystemDeviceStructure holderSystemDeviceStructure) {
+        if (system == null) {
+            return null;
+        }
+
+        String mnemonicpath = StructureUtil.getMnemonicPath(system, holderSystemDeviceStructure);
+
+        return getStructureElement(
+                Type.SYSTEM,
+                system.getUuid(),
+                system.getParentUuid(),
+                system.getName(), system.getMnemonic(), mnemonicpath, 2,
+                system.getDescription(), system.getStatus(), system.isLatest(), system.isDeleted(),
+                system.getProcessed(), system.getProcessedBy(), system.getProcessedComment());
+    }
+    /**
+     * Populate and return structure element for system group with focus on requested.
+     *
+     * @param system system
+     * @param holderSystemDeviceStructure holder of containers for system and device structure content
+     * @return structure element
+     */
+    public static StructureElement getStructureElementRequested(System system, HolderSystemDeviceStructure holderSystemDeviceStructure) {
+        if (system == null) {
+            return null;
+        }
+
+        String mnemonicpath = StructureUtil.getMnemonicPath(system, holderSystemDeviceStructure);
+
+        return getStructureElement(
+                Type.SYSTEM,
+                system.getUuid(),
+                system.getParentUuid(),
+                system.getName(), system.getMnemonic(), mnemonicpath, 2,
+                system.getDescription(), Status.PENDING, system.isLatest(), system.isDeleted(),
+                system.getRequested(), system.getRequestedBy(), system.getRequestedComment());
+    }
+    /**
+     * Populate and return structure element for subsystem with focus on processed.
+     *
+     * @param subsystem subsystem
+     * @param holderSystemDeviceStructure holder of containers for system and device structure content
+     * @return structure element
+     */
+    public static StructureElement getStructureElementProcessed(Subsystem subsystem, HolderSystemDeviceStructure holderSystemDeviceStructure) {
+        if (subsystem == null) {
+            return null;
+        }
+
+        String mnemonicpath = StructureUtil.getMnemonicPath(subsystem, holderSystemDeviceStructure);
+
+        return getStructureElement(
+                Type.SUBSYSTEM,
+                subsystem.getUuid(),
+                subsystem.getParentUuid(),
+                subsystem.getName(), subsystem.getMnemonic(), mnemonicpath, 3,
+                subsystem.getDescription(), subsystem.getStatus(), subsystem.isLatest(), subsystem.isDeleted(),
+                subsystem.getProcessed(), subsystem.getProcessedBy(), subsystem.getProcessedComment());
+    }
+    /**
+     * Populate and return structure element for subsystem with focus on requested.
+     *
+     * @param subsystem subsystem
+     * @param holderSystemDeviceStructure holder of containers for system and device structure content
+     * @return structure element
+     */
+    public static StructureElement getStructureElementRequested(Subsystem subsystem, HolderSystemDeviceStructure holderSystemDeviceStructure) {
+        if (subsystem == null) {
+            return null;
+        }
+
+        String mnemonicpath = StructureUtil.getMnemonicPath(subsystem, holderSystemDeviceStructure);
+
+        return getStructureElement(
+                Type.SUBSYSTEM,
+                subsystem.getUuid(),
+                subsystem.getParentUuid(),
+                subsystem.getName(), subsystem.getMnemonic(), mnemonicpath, 3,
+                subsystem.getDescription(), Status.PENDING, subsystem.isLatest(), subsystem.isDeleted(),
+                subsystem.getRequested(), subsystem.getRequestedBy(), subsystem.getRequestedComment());
+    }
+
+    /**
+     * Populate and return structure element for discipline with focus on processed.
+     *
+     * @param discipline discipline
+     * @param holderSystemDeviceStructure holder of containers for system and device structure content
+     * @return structure element
+     */
+    public static StructureElement getStructureElementProcessed(Discipline discipline, HolderSystemDeviceStructure holderSystemDeviceStructure) {
+        if (discipline == null) {
+            return null;
+        }
+
+//        String mnemonicpath = holderSystemDeviceStructure != null
+//                ? discipline.getMnemonic()
+//                : null;
+        String mnemonicpath = !StringUtils.isEmpty(discipline.getMnemonic())
+                ? discipline.getMnemonic()
+                : null;
+
+        return getStructureElement(
+                Type.DISCIPLINE,
+                discipline.getUuid(),
+                null,
+                discipline.getName(), discipline.getMnemonic(), mnemonicpath, 1,
+                discipline.getDescription(), discipline.getStatus(), discipline.isLatest(), discipline.isDeleted(),
+                discipline.getProcessed(), discipline.getProcessedBy(), discipline.getProcessedComment());
+    }
+    /**
+     * Populate and return structure element for discipline with focus on requested.
+     *
+     * @param discipline discipline
+     * @param holderSystemDeviceStructure holder of containers for system and device structure content
+     * @return structure element
+     */
+    public static StructureElement getStructureElementRequested(Discipline discipline, HolderSystemDeviceStructure holderSystemDeviceStructure) {
+        if (discipline == null) {
+            return null;
+        }
+
+//        String mnemonicpath = holderSystemDeviceStructure != null
+//                ? discipline.getMnemonic()
+//                : null;
+        String mnemonicpath = !StringUtils.isEmpty(discipline.getMnemonic())
+                ? discipline.getMnemonic()
+                : null;
+
+        return getStructureElement(
+                Type.DISCIPLINE,
+                discipline.getUuid(),
+                null,
+                discipline.getName(), discipline.getMnemonic(), mnemonicpath, 1,
+                discipline.getDescription(), Status.PENDING, discipline.isLatest(), discipline.isDeleted(),
+                discipline.getRequested(), discipline.getRequestedBy(), discipline.getRequestedComment());
+    }
+    /**
+     * Populate and return structure element for device group with focus on processed.
+     *
+     * @param deviceGroup device group
+     * @param holderSystemDeviceStructure holder of containers for system and device structure content
+     * @return structure element
+     */
+    public static StructureElement getStructureElementProcessed(DeviceGroup deviceGroup, HolderSystemDeviceStructure holderSystemDeviceStructure) {
+        if (deviceGroup == null) {
+            return null;
+        }
+
+        String mnemonicpath = StructureUtil.getMnemonicPath(deviceGroup, holderSystemDeviceStructure);
+
+        return getStructureElement(
+                Type.DEVICEGROUP,
+                deviceGroup.getUuid(),
+                deviceGroup.getParentUuid(),
+                deviceGroup.getName(), deviceGroup.getMnemonic(), mnemonicpath, 2,
+                deviceGroup.getDescription(), deviceGroup.getStatus(), deviceGroup.isLatest(), deviceGroup.isDeleted(),
+                deviceGroup.getProcessed(), deviceGroup.getProcessedBy(), deviceGroup.getProcessedComment());
+    }
+    /**
+     * Populate and return structure element for device group with focus on requested.
+     *
+     * @param deviceGroup device group
+     * @param holderSystemDeviceStructure holder of containers for system and device structure content
+     * @return structure element
+     */
+    public static StructureElement getStructureElementRequested(DeviceGroup deviceGroup, HolderSystemDeviceStructure holderSystemDeviceStructure) {
+        if (deviceGroup == null) {
+            return null;
+        }
+
+        String mnemonicpath = StructureUtil.getMnemonicPath(deviceGroup, holderSystemDeviceStructure);
+
+        return getStructureElement(
+                Type.DEVICEGROUP,
+                deviceGroup.getUuid(),
+                deviceGroup.getParentUuid(),
+                deviceGroup.getName(), deviceGroup.getMnemonic(), mnemonicpath, 2,
+                deviceGroup.getDescription(), Status.PENDING, deviceGroup.isLatest(), deviceGroup.isDeleted(),
+                deviceGroup.getRequested(), deviceGroup.getRequestedBy(), deviceGroup.getRequestedComment());
+    }
+    /**
+     * Populate and return structure element for device type with focus on processed.
+     *
+     * @param deviceType device type
+     * @param holderSystemDeviceStructure holder of containers for system and device structure content
+     * @return structure element
+     */
+    public static StructureElement getStructureElementProcessed(DeviceType deviceType, HolderSystemDeviceStructure holderSystemDeviceStructure) {
+        if (deviceType == null) {
+            return null;
+        }
+
+        String mnemonicpath = StructureUtil.getMnemonicPath(deviceType, holderSystemDeviceStructure);
+
+        return getStructureElement(
+                Type.DEVICETYPE,
+                deviceType.getUuid(),
+                deviceType.getParentUuid(),
+                deviceType.getName(), deviceType.getMnemonic(), mnemonicpath, 3,
+                deviceType.getDescription(), deviceType.getStatus(), deviceType.isLatest(), deviceType.isDeleted(),
+                deviceType.getProcessed(), deviceType.getProcessedBy(), deviceType.getProcessedComment());
+    }
+    /**
+     * Populate and return structure element for device type with focus on requested.
+     *
+     * @param deviceType device type
+     * @param holderSystemDeviceStructure holder of containers for system and device structure content
+     * @return  structure element
+     */
+    public static StructureElement getStructureElementRequested(DeviceType deviceType, HolderSystemDeviceStructure holderSystemDeviceStructure) {
+        if (deviceType == null) {
+            return null;
+        }
+
+        String mnemonicpath = StructureUtil.getMnemonicPath(deviceType, holderSystemDeviceStructure);
+
+        return getStructureElement(
+                Type.DEVICETYPE,
+                deviceType.getUuid(),
+                deviceType.getParentUuid(),
+                deviceType.getName(), deviceType.getMnemonic(), mnemonicpath, 3,
+                deviceType.getDescription(), Status.PENDING, deviceType.isLatest(), deviceType.isDeleted(),
+                deviceType.getRequested(), deviceType.getRequestedBy(), deviceType.getRequestedComment());
+    }
+
+    /**
+     * Populate and return structure element.
+     *
+     * @param type type
+     * @param uuid uuid
+     * @param parent parent uuid
+     * @param name name
+     * @param mnemonic mnemonic
+     * @param mnemonicpath mnemonic path
+     * @param level level
+     * @param description description
+     * @param status status
+     * @param latest latest
+     * @param deleted deleted
+     * @param when when
+     * @param who who
+     * @param comment comment
+     * @return structure element
+     */
+    protected static StructureElement getStructureElement(
+            Type type,
+            UUID uuid,
+            UUID parent,
+            String name, String mnemonic, String mnemonicpath, Integer level,
+            String description, Status status, Boolean latest, Boolean deleted,
+            Date when, String who, String comment) {
+
+        return new StructureElement(
+                type,
+                uuid,
+                parent,
+                name, mnemonic, mnemonicpath, level,
+                description, status, latest, deleted,
+                when, who, comment);
+    }
+
+
+    private static boolean hasSameContent(StructureElement structureElement, NameStructure nameStructure) {
+        /*
+           StructureElement
+               x uuid
+               x description
+               x status
+               x latest
+               x deleted
+           NameStructure
+               x uuid
+               x description
+               x status
+               x latest
+               x deleted
+         */
+
+        if (structureElement == null && nameStructure == null)
+            return true;
+        if (structureElement == null)
+            return false;
+        if (nameStructure == null)
+            return false;
+
+        if (structureElement.getUuid() == null) {
+            if (nameStructure.getUuid() != null)
+                return false;
+        } else if (!structureElement.getUuid().equals(nameStructure.getUuid()))
+            return false;
+        if (structureElement.getDescription() == null) {
+            if (nameStructure.getDescription() != null)
+                return false;
+        } else if (!structureElement.getDescription().equals(nameStructure.getDescription()))
+            return false;
+        if (structureElement.getStatus() == null) {
+            if (nameStructure.getStatus() != null)
+                return false;
+        } else if (!structureElement.getStatus().equals(nameStructure.getStatus()))
+            return false;
+        if (structureElement.isLatest() == null) {
+            if (nameStructure.isLatest() != null)
+                return false;
+        } else if (!structureElement.isLatest().equals(nameStructure.isLatest()))
+            return false;
+        if (structureElement.isDeleted() == null) {
+            if (nameStructure.isDeleted() != null)
+                return false;
+        } else if (!structureElement.isDeleted().equals(nameStructure.isDeleted()))
+            return false;
+
+        return true;
+    }
+
+    private static boolean hasSameContent(StructureElement structureElement, Structure structure) {
+        /*
+           StructureElement
+               x name
+               x mnemonic
+           Structure
+               x name
+               x mnemonic
+         */
+
+        if (!hasSameContent(structureElement, (NameStructure) structure))
+            return false;
+
+        if (structureElement.getName() == null) {
+            if (structure.getName() != null)
+                return false;
+        } else if (!structureElement.getName().equals(structure.getName()))
+            return false;
+        if (structureElement.getMnemonic() == null) {
+            if (structure.getMnemonic() != null)
+                return false;
+        } else if (!structureElement.getMnemonic().equals(structure.getMnemonic()))
+            return false;
+
+        return true;
+    }
+
+    public static boolean hasSameContent(StructureElement structureElement, SystemGroup systemGroup) {
+        /*
+           StructureElement
+               x type
+           SystemGroup
+         */
+
+        if (!hasSameContent(structureElement, (Structure) systemGroup))
+            return false;
+
+        if (!Type.SYSTEMGROUP.equals(structureElement.getType())) {
+            return false;
+        }
+
+        return true;
+    }
+
+    public static boolean hasSameContent(StructureElement structureElement, System system) {
+        /*
+           StructureElement
+               x type
+               x parent
+           System
+               x parent_uuid
+         */
+
+        if (!hasSameContent(structureElement, (Structure) system))
+            return false;
+
+        if (!Type.SYSTEMGROUP.equals(structureElement.getType())) {
+            return false;
+        }
+        if (structureElement.getParent() == null) {
+            if (system.getParentUuid() != null)
+                return false;
+        } else if (!structureElement.getParent().equals(system.getParentUuid()))
+            return false;
+
+        return true;
+    }
+
+    public static boolean hasSameContent(StructureElement structureElement, Subsystem subsystem) {
+        /*
+           StructureElement
+               x type
+               x parent
+           Subsystem
+               x parent_uuid
+         */
+
+        if (!hasSameContent(structureElement, (Structure) subsystem))
+            return false;
+
+        if (!Type.SYSTEMGROUP.equals(structureElement.getType())) {
+            return false;
+        }
+        if (structureElement.getParent() == null) {
+            if (subsystem.getParentUuid() != null)
+                return false;
+        } else if (!structureElement.getParent().equals(subsystem.getParentUuid()))
+            return false;
+
+        return true;
+    }
+
+    public static boolean hasSameContent(StructureElement structureElement, Discipline discipline) {
+        /*
+           StructureElement
+               x type
+           Discipline
+         */
+
+        if (!hasSameContent(structureElement, (Structure) discipline))
+            return false;
+
+        if (!Type.SYSTEMGROUP.equals(structureElement.getType())) {
+            return false;
+        }
+
+        return true;
+    }
+
+    public static boolean hasSameContent(StructureElement structureElement, DeviceGroup deviceGroup) {
+        /*
+           StructureElement
+               x type
+               x parent
+           Subsystem
+               x parent_uuid
+         */
+
+         if (!hasSameContent(structureElement, (Structure) deviceGroup))
+             return false;
+
+         if (!Type.SYSTEMGROUP.equals(structureElement.getType())) {
+             return false;
+         }
+         if (structureElement.getParent() == null) {
+             if (deviceGroup.getParentUuid() != null)
+                 return false;
+         } else if (!structureElement.getParent().equals(deviceGroup.getParentUuid()))
+             return false;
+
+         return true;
+    }
+
+    public static boolean hasSameContent(StructureElement structureElement, DeviceType deviceType) {
+        /*
+           StructureElement
+               x type
+               x parent
+           DeviceType
+               x parent_uuid
+         */
+
+         if (!hasSameContent(structureElement, (Structure) deviceType))
+             return false;
+
+         if (!Type.SYSTEMGROUP.equals(structureElement.getType())) {
+             return false;
+         }
+         if (structureElement.getParent() == null) {
+             if (deviceType.getParentUuid() != null)
+                 return false;
+         } else if (!structureElement.getParent().equals(deviceType.getParentUuid()))
+             return false;
+
+         return true;
+    }
+
+}
diff --git a/src/main/java/org/openepics/names/util/StructureUtil.java b/src/main/java/org/openepics/names/util/StructureUtil.java
new file mode 100644
index 0000000..076900b
--- /dev/null
+++ b/src/main/java/org/openepics/names/util/StructureUtil.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2021 European Spallation Source ERIC.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+package org.openepics.names.util;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.UUID;
+
+import org.apache.commons.lang3.StringUtils;
+import org.openepics.names.repository.model.DeviceGroup;
+import org.openepics.names.repository.model.DeviceType;
+import org.openepics.names.repository.model.Discipline;
+import org.openepics.names.repository.model.Subsystem;
+import org.openepics.names.repository.model.System;
+import org.openepics.names.repository.model.SystemGroup;
+
+/**
+ * Utility class to assist in handling of structure (name part) content.
+ *
+ * @author Lars Johansson
+ */
+public class StructureUtil {
+
+    /**
+     * This class is not to be instantiated.
+     */
+    private StructureUtil() {
+        throw new IllegalStateException("Utility class");
+    }
+
+    /**
+     * Return mnemonic path for system.
+     *
+     * @param system system
+     * @param holderSystemDeviceStructure holder of containers for system and device structure content
+     * @return mnemonic path
+     */
+    public static String getMnemonicPath(System system, HolderSystemDeviceStructure holderSystemDeviceStructure) {
+        if (system == null || holderSystemDeviceStructure == null) {
+            return null;
+        }
+
+        SystemGroup systemGroup = holderSystemDeviceStructure.findSystemGroupByUuid(system.getParentUuid());
+        return systemGroup != null && !StringUtils.isEmpty(systemGroup.getMnemonic())
+                ? systemGroup.getMnemonic() + "-" + system.getMnemonic()
+                : system.getMnemonic();
+    }
+
+    /**
+     * Return mnemonic path for subsystem.
+     *
+     * @param subsystem subsystem
+     * @param holderSystemDeviceStructure holder of containers for system and device structure content
+     * @return mnemonic path
+     */
+    public static String getMnemonicPath(Subsystem subsystem, HolderSystemDeviceStructure holderSystemDeviceStructure) {
+        if (subsystem == null || holderSystemDeviceStructure == null) {
+            return null;
+        }
+
+        System      system      = holderSystemDeviceStructure.findSystemByUuid(subsystem.getParentUuid());
+        SystemGroup systemGroup = system != null
+                ? holderSystemDeviceStructure.findSystemGroupByUuid(system.getParentUuid())
+                : null;
+        return systemGroup != null && !StringUtils.isEmpty(systemGroup.getMnemonic()) && system != null
+                ? systemGroup.getMnemonic() + "-" + system.getMnemonic() + "-" + subsystem.getMnemonic()
+                : system != null
+                    ? system.getMnemonic() + "-" + subsystem.getMnemonic()
+                    : subsystem.getMnemonic();
+
+    }
+
+    /**
+     * Return mnemonic path for device group.
+     *
+     * @param deviceGroup device group
+     * @param holderSystemDeviceStructure holder of containers for system and device structure content
+     * @return mnemonic path
+     */
+    public static String getMnemonicPath(DeviceGroup deviceGroup, HolderSystemDeviceStructure holderSystemDeviceStructure) {
+        if (deviceGroup == null || holderSystemDeviceStructure == null) {
+            return null;
+        }
+
+        Discipline discipline = holderSystemDeviceStructure.findDisciplineByUuid(deviceGroup.getParentUuid());
+        return discipline != null
+                ? discipline.getMnemonic()
+                : null;
+    }
+
+    /**
+     * Return mnemonic path for device type.
+     *
+     * @param deviceType device type
+     * @param holderSystemDeviceStructure holder of containers for system and device structure content
+     * @return mnemonic path
+     */
+    public static String getMnemonicPath(DeviceType deviceType, HolderSystemDeviceStructure holderSystemDeviceStructure) {
+        if (deviceType == null || holderSystemDeviceStructure == null) {
+            return null;
+        }
+
+        DeviceGroup deviceGroup = holderSystemDeviceStructure.findDeviceGroupByUuid(deviceType.getParentUuid());
+        Discipline  discipline  = deviceGroup != null
+                ? holderSystemDeviceStructure.findDisciplineByUuid(deviceGroup.getParentUuid())
+                : null;
+        return discipline != null && deviceGroup != null
+                ? discipline.getMnemonic() + "-" + deviceType.getMnemonic()
+                : deviceType.getMnemonic();
+    }
+
+    // ---------------------------------------------------
+
+    /**
+     * Return a list of mnemonic paths for system groups.
+     *
+     * @param holder holder of containers for system and device structure content
+     * @param mnemonicEquivalence use mnemonic equivalence instead of mnemonic
+     * @param namingConvention naming convention
+     * @return a list of mnemonic paths for system groups
+     */
+    public static List<String> getMnemonicPathsSystemGroup(HolderSystemDeviceStructure holder, boolean mnemonicEquivalence, EssNamingConvention namingConvention) {
+        List<String> mnemonicPaths = new ArrayList<>();
+        String value = null;
+        for (Entry<UUID, SystemGroup> entry : holder.getUuidSystemGroups().entrySet()) {
+            if (!StringUtils.isEmpty(entry.getValue().getMnemonic())) {
+                value = entry.getValue().getMnemonic();
+            }
+
+            if (mnemonicEquivalence) {
+                mnemonicPaths.add(namingConvention.equivalenceClassRepresentative(value));
+            } else {
+                mnemonicPaths.add(value);
+            }
+        }
+        return mnemonicPaths;
+    }
+
+    /**
+     * Return a list of mnemonic paths for systems.
+     *
+     * @param holder holder of containers for system and device structure content
+     * @param mnemonicEquivalence use mnemonic equivalence instead of mnemonic
+     * @param namingConvention naming convention
+     * @return a list of mnemonic paths for systems
+     */
+    public static List<String> getMnemonicPathsSystem(HolderSystemDeviceStructure holder, boolean mnemonicEquivalence, EssNamingConvention namingConvention) {
+        List<String> mnemonicPaths = new ArrayList<>();
+        String value = null;
+        for (Entry<UUID, System> entry : holder.getUuidSystems().entrySet()) {
+            SystemGroup systemGroup = holder.findSystemGroupByUuid(entry.getValue().getParentUuid());
+            if (!StringUtils.isEmpty(systemGroup.getMnemonic())) {
+                value = systemGroup.getMnemonic() + "-" + entry.getValue().getMnemonic();
+            } else {
+                value = entry.getValue().getMnemonic();
+            }
+
+            if (mnemonicEquivalence) {
+                mnemonicPaths.add(namingConvention.equivalenceClassRepresentative(value));
+            } else {
+                mnemonicPaths.add(value);
+            }
+        }
+        return mnemonicPaths;
+    }
+
+    /**
+     * Return a list of mnemonic paths for subsystems.
+     *
+     * @param holder holder of containers for system and device structure content
+     * @param mnemonicEquivalence use mnemonic equivalence instead of mnemonic
+     * @param namingConvention naming convention
+     * @return a list of mnemonic paths for subsystems
+     */
+    public static List<String> getMnemonicPathsSubsystem(HolderSystemDeviceStructure holder, boolean mnemonicEquivalence, EssNamingConvention namingConvention) {
+        List<String> mnemonicPaths = new ArrayList<>();
+        String value = null;
+        for (Entry<UUID, Subsystem> entry : holder.getUuidSubsystems().entrySet()) {
+            System system = holder.findSystemByUuid(entry.getValue().getParentUuid());
+            SystemGroup systemGroup = holder.findSystemGroupByUuid(system.getParentUuid());
+            if (!StringUtils.isEmpty(systemGroup.getMnemonic())) {
+                value = systemGroup.getMnemonic() + "-" + system.getMnemonic() + "-" + entry.getValue().getMnemonic();
+            } else {
+                value = system.getMnemonic() + "-" + entry.getValue().getMnemonic();
+            }
+
+            if (mnemonicEquivalence) {
+                mnemonicPaths.add(namingConvention.equivalenceClassRepresentative(value));
+            } else {
+                mnemonicPaths.add(value);
+            }
+        }
+        return mnemonicPaths;
+    }
+
+    /**
+     * Return a list of mnemonic paths for disciplines.
+     *
+     * @param holder holder of containers for system and device structure content
+     * @param mnemonicEquivalence use mnemonic equivalence instead of mnemonic
+     * @param namingConvention naming convention
+     * @return a list of mnemonic paths for disciplines
+     */
+    public static List<String> getMnemonicPathsDiscipline(HolderSystemDeviceStructure holder, boolean mnemonicEquivalence, EssNamingConvention namingConvention) {
+        List<String> mnemonicPaths = new ArrayList<>();
+        String value = null;
+        for (Entry<UUID, Discipline> entry : holder.getUuidDisciplines().entrySet()) {
+            if (!StringUtils.isEmpty(entry.getValue().getMnemonic())) {
+                value = entry.getValue().getMnemonic();
+            }
+
+            if (mnemonicEquivalence) {
+                mnemonicPaths.add(namingConvention.equivalenceClassRepresentative(value));
+            } else {
+                mnemonicPaths.add(value);
+            }
+        }
+        return mnemonicPaths;
+    }
+
+    /**
+     * Return a list of mnemonic paths for device types.
+     *
+     * @param holder holder of containers for system and device structure content
+     * @param mnemonicEquivalence use mnemonic equivalence instead of mnemonic
+     * @param namingConvention naming convention
+     * @return a list of mnemonic paths for device types
+     */
+    public static List<String> getMnemonicPathsDeviceType(HolderSystemDeviceStructure holder, boolean mnemonicEquivalence, EssNamingConvention namingConvention) {
+        List<String> mnemonicPaths = new ArrayList<>();
+        String value = null;
+        for (Entry<UUID, DeviceType> entry : holder.getUuidDeviceTypes().entrySet()) {
+            DeviceGroup deviceGroup = holder.findDeviceGroupByUuid(entry.getValue().getParentUuid());
+            Discipline discipline = holder.findDisciplineByUuid(deviceGroup.getParentUuid());
+            value = discipline.getMnemonic() + "-" + entry.getValue().getMnemonic();
+
+            if (mnemonicEquivalence) {
+                mnemonicPaths.add(namingConvention.equivalenceClassRepresentative(value));
+            } else {
+                mnemonicPaths.add(value);
+            }
+        }
+        return mnemonicPaths;
+    }
+
+}
diff --git a/src/main/java/org/openepics/names/util/ValidateUtil.java b/src/main/java/org/openepics/names/util/ValidateUtil.java
new file mode 100644
index 0000000..3ecc043
--- /dev/null
+++ b/src/main/java/org/openepics/names/util/ValidateUtil.java
@@ -0,0 +1,1385 @@
+/*
+ * Copyright (C) 2021 European Spallation Source ERIC.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+package org.openepics.names.util;
+
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.commons.lang3.StringUtils;
+import org.openepics.names.repository.NameRepository;
+import org.openepics.names.repository.model.DeviceGroup;
+import org.openepics.names.repository.model.DeviceType;
+import org.openepics.names.repository.model.Discipline;
+import org.openepics.names.repository.model.Name;
+import org.openepics.names.repository.model.Subsystem;
+import org.openepics.names.repository.model.System;
+import org.openepics.names.repository.model.SystemGroup;
+import org.openepics.names.rest.beans.FieldName;
+import org.openepics.names.rest.beans.FieldStructure;
+import org.openepics.names.rest.beans.NameElement;
+import org.openepics.names.rest.beans.Status;
+import org.openepics.names.rest.beans.StructureElement;
+import org.openepics.names.rest.beans.Type;
+import org.springframework.http.HttpStatus;
+
+/**
+ * Utility class to assist in handling of validation.
+ *
+ * @author Lars Johansson
+ */
+public class ValidateUtil {
+
+    private static enum NameChoice      {CREATE, UPDATE, DELETE};
+    private static enum StructureChoice {CREATE, UPDATE, DELETE, APPROVE, REJECT, CANCEL};
+
+    public static final String STRUCTURE               = "structure";
+    public static final String NAME                    = "name";
+    public static final String SYSTEMGROUP             = "system group";
+    public static final String SYSTEM                  = "system";
+    public static final String SUBSYSTEM               = "subsystem";
+    public static final String DISCIPLINE              = "discipline";
+    public static final String DEVICEGROUP             = "device group";
+    public static final String DEVICETYPE              = "device type";
+
+    public static final String SPACE                   = " ";
+
+    public static final String ARE_NOT_CORRECT         = "are not correct";
+    public static final String EXISTS                  = "exists";
+    public static final String IS_DELETED              = "is deleted";
+    public static final String IS_NOT_AVAILABLE        = "is not available";
+    public static final String IS_NOT_CORRECT          = "is not correct";
+    public static final String IS_NOT_DELETED          = "is not deleted";
+    public static final String IS_NOT_VALID            = "is not valid";
+    public static final String WAS_NOT_UPDATED         = "was not updated";
+
+    // NameElement
+    //     uuid,
+    //     systemgroup, system, subsystem, devicetype, systemstructure, devicestructure,
+    //     index, name,
+    //     description, status, latest, deleted, when, who, comment
+    // StructureElement
+    //     type, uuid, parent uuid,
+    //     name, mnemonic, mnemonic path, level,
+    //     description, status, latest, deleted, when, who, comment
+
+    /**
+     * This class is not to be instantiated.
+     */
+    private ValidateUtil() {
+        throw new IllegalStateException("Utility class");
+    }
+
+    /**
+     * Validate comment.
+     *
+     * @param comment comment
+     */
+    public static void validateInputComment(String comment) {
+        // available
+        validateCondition(!StringUtils.isEmpty(comment), HttpStatus.BAD_REQUEST,
+                "comment " + ValidateUtil.IS_NOT_AVAILABLE, comment);
+    }
+
+    /**
+     * Validate description.
+     *
+     * @param description description
+     */
+    public static void validateInputDescription(String description) {
+        // available
+        validateCondition(!StringUtils.isEmpty(description), HttpStatus.BAD_REQUEST,
+                "description " + ValidateUtil.IS_NOT_AVAILABLE, description);
+    }
+
+    /**
+     * Validate mnemonic.
+     *
+     * @param mnemonic mnemonic
+     */
+    public static void validateInputMnemonic(String mnemonic) {
+        // available
+        validateCondition(!StringUtils.isEmpty(mnemonic), HttpStatus.BAD_REQUEST,
+                "mnemonic " + ValidateUtil.IS_NOT_AVAILABLE, mnemonic);
+    }
+
+    /**
+     * Validate mnemonic path.
+     *
+     * @param mnemonicpath mnemonic path
+     */
+    public static void validateInputMnemonicpath(String mnemonicpath) {
+        // available
+        validateCondition(!StringUtils.isEmpty(mnemonicpath), HttpStatus.BAD_REQUEST,
+                "mnemonicpath " + ValidateUtil.IS_NOT_AVAILABLE, mnemonicpath);
+    }
+
+    /**
+     * Validate name.
+     *
+     * @param name name
+     */
+    public static void validateInputName(String name) {
+        // available
+        validateCondition(!StringUtils.isEmpty(name), HttpStatus.BAD_REQUEST,
+                "name " + ValidateUtil.IS_NOT_AVAILABLE, name);
+    }
+
+    /**
+     * Validate status.
+     *
+     * @param status status
+     */
+    public static void validateInputStatus(Status status) {
+        validateInputStatus(status, null);
+    }
+
+    /**
+     * Validate status.
+     *
+     * @param status status
+     * @param expected expected status
+     */
+    public static void validateInputStatus(Status status, Status expected) {
+        // available
+        validateCondition(status != null, HttpStatus.BAD_REQUEST,
+                "status " + ValidateUtil.IS_NOT_AVAILABLE, null);
+        // expected status
+        if (expected != null) {
+            validateCondition(expected.equals(status), HttpStatus.BAD_REQUEST,
+                    "status " + ValidateUtil.IS_NOT_CORRECT, status.toString());
+        }
+    }
+
+    /**
+     * Validate type.
+     *
+     * @param type type
+     */
+    public static void validateInputType(Type type) {
+        validateInputType(type, null);
+    }
+
+    /**
+     * Validate type.
+     *
+     * @param type type
+     * @param expected expected type
+     */
+    public static void validateInputType(Type type, Type expected) {
+        // available
+        validateCondition(type != null, HttpStatus.BAD_REQUEST,
+                "type " + ValidateUtil.IS_NOT_AVAILABLE, null);
+        // expected type
+        if (expected != null) {
+            validateCondition(expected.equals(type), HttpStatus.BAD_REQUEST,
+                    "type " + ValidateUtil.IS_NOT_CORRECT, type.toString());
+        }
+    }
+
+    /**
+     * Validate uuid.
+     *
+     * @param uuid uuid
+     */
+    public static void validateInputUuid(String uuid) {
+        // available
+        // correct
+        validateCondition(!StringUtils.isEmpty(uuid), HttpStatus.BAD_REQUEST,
+                "uuid " + ValidateUtil.IS_NOT_AVAILABLE, uuid);
+        try {
+            UUID.fromString(uuid);
+        } catch (IllegalArgumentException e) {
+            throw ExceptionUtil.createServiceHttpStatusExceptionBadRequest(
+                    "uuid " + ValidateUtil.IS_NOT_CORRECT, uuid, null);
+        }
+    }
+
+    // ----------------------------------------------------------------------------------------------------
+
+    /**
+     * Validate parameters for read names.
+     *
+     * @param deleted deleted
+     * @param queryFields query fields
+     * @param queryValues query values
+     * @param includeHistory include history
+     * @param orderBy order by
+     * @param isAsc is ascending
+     * @param offset offset
+     * @param limit limit
+     */
+    public static void validateNamesInputRead(
+            Boolean deleted, FieldName[] queryFields, String[] queryValues,
+            Boolean includeHistory,
+            FieldName orderBy, Boolean isAsc,
+            Integer offset, Integer limit) {
+
+        // validate input
+        //     queryFields and queryValues
+        //         either
+        //             both null
+        //             both non-null, same length, non-empty
+        //         uuid
+
+        boolean condition = ((queryFields == null && queryValues == null)
+                || (queryFields != null && queryValues != null && queryFields.length == queryValues.length && queryFields.length > 0));
+        ValidateUtil.validateCondition(condition, HttpStatus.BAD_REQUEST,
+                "url and parameters " + ValidateUtil.ARE_NOT_CORRECT, "queryFields, queryValues with different lengths or empty");
+        if (queryFields != null) {
+            for (int i=0; i<queryFields.length; i++) {
+                if (FieldName.UUID.equals(queryFields[i])) {
+                    ValidateUtil.validateInputUuid(queryValues[i]);
+                }
+            }
+        }
+    }
+
+    /**
+     * Validate name element parameters (input) for create.
+     *
+     * @param nameElement name element
+     */
+    public static void validateNameElementInputCreate(NameElement nameElement) {
+        validateNameElementInput(nameElement, NameChoice.CREATE);
+    }
+
+    /**
+     * Validate name element parameters (input) for update.
+     *
+     * @param nameElement name element
+     */
+    public static void validateNameElementInputUpdate(NameElement nameElement) {
+        validateNameElementInput(nameElement, NameChoice.UPDATE);
+    }
+
+    /**
+     * Validate name element parameters (input) for delete.
+     *
+     * @param nameElement name element
+     */
+    public static void validateNameElementInputDelete(NameElement nameElement) {
+        validateNameElementInput(nameElement, NameChoice.DELETE);
+    }
+
+    /**
+     * Validate name element parameters (input).
+     *
+     * @param nameElement name element
+     * @param nameChoice name choice
+     */
+    private static void validateNameElementInput(NameElement nameElement, NameChoice nameChoice) {
+        // attributes
+        //     not check
+        //    	   uuid                   - n.a set server side
+        //    	   systemstructure        - n.a system structure mnemonic path
+        //    	   devicestructure        - n.a device structure mnemonic path
+        //    	   devicetype             - possibly validate uuid also validate data
+        //    	   index                  - possibly also validate data
+        //    	   name                   - possibly also validate data
+        //    	   status                 - n.a. set server side
+        //    	   latest                 - n.a. set server side
+        //    	   deleted                - n.a. set server side
+        //    	   when                   - n.a. set server side
+        //    	   who                    - n.a. set server side
+        //     check
+        //    	   systemgroup            - 1 either 1,2,3 possibly validate uuid also validate data
+        //    	   system                 - 2 either 1,2,3 possibly validate uuid also validate data
+        //    	   subsystem              - 3 either 1,2,3 possibly validate uuid also validate data
+        //    	   description            - required
+        //    	   comment                - required
+
+        if (nameElement == null || nameChoice == null) {
+            return;
+        }
+
+        if (!NameChoice.CREATE.equals(nameChoice)) {
+            validateInputUuid(nameElement.getUuid() !=  null ? nameElement.getUuid().toString() : null);
+        }
+
+        validateInputDescription(nameElement.getDescription());
+        validateInputComment(nameElement.getComment());
+
+        // uuid vs string
+        int count = 0;
+        if (nameElement.getSystemgroup() != null) {
+            count++;
+        };
+        if (nameElement.getSystem() != null) {
+            count++;
+        }
+        if (nameElement.getSubsystem() != null) {
+            count++;
+        }
+        // correct
+        ValidateUtil.validateCondition(count == 1, HttpStatus.BAD_REQUEST,
+                "system structure uuid " + ValidateUtil.IS_NOT_CORRECT, nameElement.toString());
+    }
+
+    // ----------------------------------------------------------------------------------------------------
+
+    /**
+     * Validate name element data for create.
+     *
+     * @param nameElement name element
+     * @param namingConvention naming convention
+     * @param holderIRepositories holder repositories
+     * @param nameRepository name repository
+     * @param holder holder
+     */
+    public static void validateNameElementDataCreate(NameElement nameElement, EssNamingConvention namingConvention, HolderIRepositories holderIRepositories, NameRepository nameRepository, HolderSystemDeviceStructure holder) {
+        validateNameElementData(nameElement, namingConvention, holderIRepositories, nameRepository, holder, NameChoice.CREATE);
+    }
+
+    /**
+     * Validate name element data for update.
+     *
+     * @param nameElement name element
+     * @param namingConvention naming convention
+     * @param holderIRepositories holder repositories
+     * @param nameRepository name repositories
+     * @param holder holder
+     */
+    public static void validateNameElementDataUpdate(NameElement nameElement, EssNamingConvention namingConvention, HolderIRepositories holderIRepositories, NameRepository nameRepository, HolderSystemDeviceStructure holder) {
+        validateNameElementData(nameElement, namingConvention, holderIRepositories, nameRepository, holder, NameChoice.UPDATE);
+    }
+
+    /**
+     * Validate name element data for delete.
+     *
+     * @param nameElement name element
+     * @param namingConvention naming convention
+     * @param holderIRepositories holder repositories
+     * @param nameRepository name repositories
+     * @param holder holder
+     */
+    public static void validateNameElementDataDelete(NameElement nameElement, EssNamingConvention namingConvention, HolderIRepositories holderIRepositories, NameRepository nameRepository, HolderSystemDeviceStructure holder) {
+        validateNameElementData(nameElement, namingConvention, holderIRepositories, nameRepository, holder, NameChoice.DELETE);
+    }
+
+    /**
+     * Validate name element data.
+     *
+     * @param nameElement name element
+     * @param namingConvention naming convention
+     * @param holderIRepositories holder repositories
+     * @param nameRepository name repository
+     * @param holder holder
+     * @param nameChoice name choice
+     */
+    private static void validateNameElementData(NameElement nameElement, EssNamingConvention namingConvention, HolderIRepositories holderIRepositories, NameRepository nameRepository, HolderSystemDeviceStructure holder, NameChoice nameChoice) {
+        // attributes
+        //     not check
+        //         uuid
+        //         systemstructure
+        //         devicestructure
+        //         description
+        //         status
+        //         latest
+        //         deleted
+        //         when
+        //         who
+        //         comment
+        //     check
+        //         systemgroup, system, subsystem - in repository
+        //             found
+        //             not deleted
+        //         devicetype                     - possibly, in repository
+        //             found
+        //             not deleted
+        //         index                          - possibly, naming convention rules
+        //             index to match name index
+        //             valid
+        //         name                           - naming convention rules
+        //             mnemonic paths for system structure uuid to match name mnemonic paths
+        //             mnemonic paths for device structure uuid to match name mnemonic paths
+        //             name not exists
+        //             name equivalence not exists
+
+        if (nameElement == null || namingConvention == null || holderIRepositories == null || nameRepository == null || holder == null || nameChoice == null) {
+            return;
+        }
+
+        // name
+        //     create or update? same or not?
+        //     update --> retrieve name and check
+        Name name = null;
+        if (NameChoice.UPDATE.equals(nameChoice) || NameChoice.DELETE.equals(nameChoice)) {
+            List<Name> names = nameRepository.readNames(false, FieldName.UUID, nameElement.getUuid().toString());
+            validateCondition(names != null && names.size() == 1, HttpStatus.BAD_REQUEST,
+                    ValidateUtil.NAME + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT, nameElement.toString());
+
+            name = names.get(0);
+        }
+
+        SystemGroup systemGroup = null;
+        System system           = null;
+        Subsystem subsystem     = null;
+        DeviceType deviceType   = null;
+
+        String mnemonicPathSystemStructure = null;
+        String mnemonicPathDeviceStructure = null;
+
+        boolean condition = true;
+
+        if (NameChoice.CREATE.equals(nameChoice) || NameChoice.UPDATE.equals(nameChoice)) {
+            // system structure
+            if (nameElement.getSystemgroup() != null) {
+                systemGroup = holderIRepositories.getSystemGroupRepository().findLatestByUuid(nameElement.getSystemgroup().toString());
+                ValidateUtil.validateCondition(systemGroup != null, HttpStatus.BAD_REQUEST, ValidateUtil.SYSTEMGROUP + ValidateUtil.SPACE + ValidateUtil.IS_NOT_AVAILABLE, nameElement.toString());
+                ValidateUtil.validateCondition(!systemGroup.isDeleted(), HttpStatus.BAD_REQUEST, ValidateUtil.SYSTEMGROUP + ValidateUtil.SPACE + ValidateUtil.IS_DELETED, nameElement.toString());
+                mnemonicPathSystemStructure = systemGroup.getMnemonic();
+            } else if (nameElement.getSystem() != null) {
+                system = holderIRepositories.getSystemRepository().findLatestByUuid(nameElement.getSystem().toString());
+                ValidateUtil.validateCondition(system != null, HttpStatus.BAD_REQUEST, ValidateUtil.SYSTEM + ValidateUtil.SPACE + ValidateUtil.IS_NOT_AVAILABLE, nameElement.toString());
+                ValidateUtil.validateCondition(!system.isDeleted(), HttpStatus.BAD_REQUEST, ValidateUtil.SYSTEM + ValidateUtil.SPACE + ValidateUtil.IS_DELETED, nameElement.toString());
+                mnemonicPathSystemStructure = StructureUtil.getMnemonicPath(system, holder);
+                String[] mnemonicpath = NamingConventionUtil.string2MnemonicPath(mnemonicPathSystemStructure);
+                mnemonicPathSystemStructure = mnemonicpath != null && mnemonicpath.length == 2
+                        ? mnemonicpath[1]
+                        : null;
+            } else if (nameElement.getSubsystem() != null) {
+                subsystem = holderIRepositories.getSubsystemRepository().findLatestByUuid(nameElement.getSubsystem().toString());
+                ValidateUtil.validateCondition(subsystem != null, HttpStatus.BAD_REQUEST, ValidateUtil.SUBSYSTEM + ValidateUtil.SPACE + ValidateUtil.IS_NOT_AVAILABLE, nameElement.toString());
+                ValidateUtil.validateCondition(!subsystem.isDeleted(), HttpStatus.BAD_REQUEST, ValidateUtil.SUBSYSTEM + ValidateUtil.SPACE + ValidateUtil.IS_DELETED, nameElement.toString());
+                mnemonicPathSystemStructure = StructureUtil.getMnemonicPath(subsystem, holder);
+                String[] mnemonicpath = NamingConventionUtil.string2MnemonicPath(mnemonicPathSystemStructure);
+                mnemonicPathSystemStructure = mnemonicpath != null && mnemonicpath.length == 3
+                        ? mnemonicpath[1] + "-" + mnemonicpath[2]
+                        : mnemonicpath != null && mnemonicpath.length == 2
+                            ? mnemonicpath[0] + "-" + mnemonicpath[1]
+                            : null;
+            } else {
+                throw ExceptionUtil.createServiceHttpStatusExceptionBadRequest("system structure uuid " + IS_NOT_CORRECT, nameElement.toString(), null);
+            }
+            // mnemonicPathSystemStructure = NamingConventionUtil.mnemonicPathSystemStructure4Name(mnemonicPathSystemStructure);
+
+            // device structure
+            if (nameElement.getDevicetype() != null) {
+                deviceType = holderIRepositories.getDeviceTypeRepository().findLatestByUuid(nameElement.getDevicetype().toString());
+                ValidateUtil.validateCondition(deviceType != null, HttpStatus.BAD_REQUEST, ValidateUtil.DEVICETYPE + ValidateUtil.SPACE + ValidateUtil.IS_NOT_AVAILABLE, nameElement.toString());
+                ValidateUtil.validateCondition(!deviceType.isDeleted(), HttpStatus.BAD_REQUEST, ValidateUtil.DEVICETYPE + ValidateUtil.SPACE + ValidateUtil.IS_DELETED, nameElement.toString());
+                mnemonicPathDeviceStructure = StructureUtil.getMnemonicPath(deviceType, holder);
+            }
+
+            // index
+            String extractedInstanceIndex = NamingConventionUtil.extractInstanceIndex(nameElement.getName());
+
+            if (!StringUtils.isEmpty(nameElement.getIndex()) && StringUtils.isEmpty(extractedInstanceIndex)) {
+                throw ExceptionUtil.createServiceHttpStatusExceptionBadRequest("convention name " + IS_NOT_CORRECT, nameElement.toString(), null);
+            } else if (StringUtils.isEmpty(nameElement.getIndex()) && !StringUtils.isEmpty(extractedInstanceIndex)) {
+                throw ExceptionUtil.createServiceHttpStatusExceptionBadRequest("instance index " + IS_NOT_CORRECT, nameElement.toString(), null);
+            }
+
+            if (!StringUtils.isEmpty(nameElement.getIndex())) {
+                condition = StringUtils.equals(nameElement.getIndex(), NamingConventionUtil.extractInstanceIndex(nameElement.getName()));
+                validateCondition(condition, HttpStatus.BAD_REQUEST, "instance index " + ValidateUtil.IS_NOT_CORRECT, nameElement.toString());
+
+                // TODO overrideRuleset depend on user authority
+                condition = namingConvention.isInstanceIndexValid(nameElement.getName(), false);
+                validateCondition(condition, HttpStatus.BAD_REQUEST, "instance index " + ValidateUtil.IS_NOT_VALID, nameElement.toString());
+            }
+
+            // name
+            //     system structure
+            //     device structure
+            if (!StringUtils.isEmpty(nameElement.getName())) {
+                condition = StringUtils.equals(mnemonicPathSystemStructure, NamingConventionUtil.extractMnemonicPathSystemStructure(nameElement.getName()));
+                validateCondition(condition, HttpStatus.BAD_REQUEST, "convention name " + ValidateUtil.IS_NOT_CORRECT, nameElement.toString());
+                condition = StringUtils.equals(mnemonicPathDeviceStructure, NamingConventionUtil.extractMnemonicPathDeviceStructure(nameElement.getName()));
+                validateCondition(condition, HttpStatus.BAD_REQUEST, "convention name " + ValidateUtil.IS_NOT_CORRECT, nameElement.toString());
+            } else {
+                // if no name, then no index, we're left with system structure, possibly also device structure
+                if (!StringUtils.isEmpty(mnemonicPathSystemStructure) && !StringUtils.isEmpty(mnemonicPathDeviceStructure)) {
+                    nameElement.setName(mnemonicPathSystemStructure + ":" + mnemonicPathDeviceStructure);
+                } else if (!StringUtils.isEmpty(mnemonicPathSystemStructure)) {
+                    nameElement.setName(mnemonicPathSystemStructure);
+                }
+            }
+
+            // name
+            // name equivalence
+            //     check if (create || update && name not same)
+            //     may also trace & trace, and check name through its parents and index, should result in same name as in nameelement
+            if (NameChoice.CREATE.equals(nameChoice) || (NameChoice.UPDATE.equals(nameChoice) && !StringUtils.equals(nameElement.getName(), name.getConventionName()))) {
+                List<Name> names = nameRepository.readNames(false, FieldName.NAME, nameElement.getName());
+                condition = names == null || names.isEmpty();
+                validateCondition(condition, HttpStatus.BAD_REQUEST, "convention name " + ValidateUtil.EXISTS, nameElement.toString());
+
+                names = nameRepository.readNames(false, FieldName.NAMEEQUIVALENCE, namingConvention.equivalenceClassRepresentative(nameElement.getName()));
+                condition = names == null || names.isEmpty();
+                validateCondition(condition, HttpStatus.BAD_REQUEST, "convention name equivalence " + ValidateUtil.EXISTS, nameElement.toString());
+            }
+        }
+    }
+
+    // ----------------------------------------------------------------------------------------------------
+
+    /**
+     * Validate name data.
+     * This method corresponds to name element data for create, albeit in a different way.
+     *
+     * @param name name
+     * @param namingConvention naming convention
+     * @param holderIRepositories holder repositories
+     * @param nameRepository name repository
+     * @param holder holder
+     *
+     * @see ValidateUtil#validateNameDataCreate(NameElement, EssNamingConvention, HolderIRepositories, NameRepository, HolderSystemDeviceStructure)
+     */
+    public static void validateNameDataCreate(String name, EssNamingConvention namingConvention, HolderIRepositories holderIRepositories, NameRepository nameRepository, HolderSystemDeviceStructure holder) {
+        if (name == null || namingConvention == null || holderIRepositories == null || nameRepository == null || holder == null) {
+            return;
+        }
+
+        // find out system group, system, subsystem       + check if valid
+        // find out discipline, device group, device type + check if valid
+        // find out instance index                        + check if valid
+        // check name equivalence
+
+        String sg  = NamingConventionUtil.extractSystemGroup(name);
+        String sys = NamingConventionUtil.extractSystem(name);
+        String sub = NamingConventionUtil.extractSubsystem(name);
+        String dt  = NamingConventionUtil.extractDeviceType(name);
+        String idx = NamingConventionUtil.extractInstanceIndex(name);
+
+        int count = 0;
+        if (sg != null) {
+            count++;
+        };
+        if (sys != null) {
+            count++;
+        }
+        if (sub != null) {
+            count++;
+        }
+        if (count > 2 || count < 1) {
+            throw ExceptionUtil.createServiceHttpStatusExceptionBadRequest(
+                    "system structure " + ValidateUtil.IS_NOT_CORRECT, name, null);
+        } else if (    (!StringUtils.isEmpty(sg) && !StringUtils.isEmpty(sys))
+                    || (!StringUtils.isEmpty(sg) && !StringUtils.isEmpty(sub))) {
+            throw ExceptionUtil.createServiceHttpStatusExceptionBadRequest(
+                    "system structure " + ValidateUtil.IS_NOT_CORRECT, name, null);
+        }
+
+        SystemGroup systemGroup = null;
+        System system           = null;
+        Subsystem subsystem     = null;
+        DeviceType deviceType   = null;
+
+        String mnemonicPathSystemStructure = null;
+        String mnemonicPathDeviceStructure = null;
+
+        boolean condition = true;
+
+        // ensure that system structure parents and device structure parents are available, latest and not deleted
+        //    if (system group) {
+        //    } else {
+        //        if (system) {
+        //        } else {
+        //             (if not system then error)
+        //        }
+        //        if (subsystem) {
+        //        }
+        //    }
+        //    if (device type) {
+        //    }
+
+        // system structure
+        if (!StringUtils.isEmpty(sg)) {
+            systemGroup = holderIRepositories.getSystemGroupRepository().findLatestNotDeletedByMnemonic(sg);
+            ValidateUtil.validateCondition(systemGroup != null, HttpStatus.BAD_REQUEST,
+                    ValidateUtil.SYSTEMGROUP + ValidateUtil.SPACE + ValidateUtil.IS_NOT_AVAILABLE, name);
+            mnemonicPathSystemStructure = systemGroup.getMnemonic();
+        } else {
+            if (!StringUtils.isEmpty(sys)) {
+                system = holderIRepositories.getSystemRepository().findLatestNotDeletedByMnemonic(sys);
+                ValidateUtil.validateCondition(system != null, HttpStatus.BAD_REQUEST,
+                        ValidateUtil.SYSTEM + ValidateUtil.SPACE + ValidateUtil.IS_NOT_AVAILABLE, name);
+                mnemonicPathSystemStructure = StructureUtil.getMnemonicPath(system, holder);
+            } else {
+                throw ExceptionUtil.createServiceHttpStatusExceptionBadRequest(
+                        ValidateUtil.SYSTEM + ValidateUtil.SPACE + ValidateUtil.IS_NOT_AVAILABLE, name, null);
+            }
+            // TODO is this condition correct?
+            ValidateUtil.validateCondition(system != null, HttpStatus.BAD_REQUEST,
+                    ValidateUtil.SYSTEM + ValidateUtil.SPACE + ValidateUtil.IS_NOT_AVAILABLE, name);
+            if (!StringUtils.isEmpty(sub)) {
+                subsystem = holderIRepositories.getSubsystemRepository().findLatestNotDeletedByParentAndMnemonic(system.getUuid().toString(), sub);
+                ValidateUtil.validateCondition(subsystem != null, HttpStatus.BAD_REQUEST,
+                        ValidateUtil.SUBSYSTEM + ValidateUtil.SPACE + ValidateUtil.IS_NOT_AVAILABLE, name);
+                mnemonicPathSystemStructure = StructureUtil.getMnemonicPath(subsystem, holder);
+            }
+        }
+
+        // device structure
+        if (!StringUtils.isEmpty(dt)) {
+            deviceType = holderIRepositories.getDeviceTypeRepository().findLatestNotDeletedByMnemonic(dt);
+            ValidateUtil.validateCondition(deviceType != null, HttpStatus.BAD_REQUEST,
+                    ValidateUtil.DEVICETYPE + ValidateUtil.SPACE + ValidateUtil.IS_NOT_AVAILABLE, name);
+            mnemonicPathDeviceStructure = StructureUtil.getMnemonicPath(deviceType, holder);
+        }
+
+        // index
+        if (!StringUtils.isEmpty(idx)) {
+            // TODO overrideRuleset depend on user authority
+            condition = namingConvention.isInstanceIndexValid(name, false);
+            validateCondition(condition, HttpStatus.BAD_REQUEST,
+                    "instance index " + ValidateUtil.IS_NOT_VALID, name);
+        }
+
+        // name
+        //     mnemonic paths for found system structure to end with extracted mnemonic path for system structure
+        //     mnemonic paths for found device structure to end with extracted mnemonic path for device structure
+        //     convention name not exists
+        //     convention name equivalence not exists
+        condition = StringUtils.endsWith(mnemonicPathSystemStructure, NamingConventionUtil.extractMnemonicPathSystemStructure(name));
+        validateCondition(condition, HttpStatus.BAD_REQUEST,
+                "convention name " + ValidateUtil.IS_NOT_CORRECT, name);
+
+        condition = StringUtils.endsWith(mnemonicPathDeviceStructure, NamingConventionUtil.extractMnemonicPathDeviceStructure(name));
+        validateCondition(condition, HttpStatus.BAD_REQUEST,
+                "convention name " + ValidateUtil.IS_NOT_CORRECT, name);
+
+        List<Name> names = nameRepository.readNames(false, FieldName.NAME, name);
+        condition = names == null || names.isEmpty();
+        validateCondition(condition, HttpStatus.BAD_REQUEST,
+                "convention name " + ValidateUtil.EXISTS, name);
+
+        names = nameRepository.readNames(false, FieldName.NAMEEQUIVALENCE, namingConvention.equivalenceClassRepresentative(name));
+        condition = names == null || names.isEmpty();
+        validateCondition(condition, HttpStatus.BAD_REQUEST,
+                "convention name equivalence " + ValidateUtil.EXISTS, name);
+    }
+
+    // ----------------------------------------------------------------------------------------------------
+
+    /**
+     * Validate parameters for read structures.
+     *
+     * @param type type
+     * @param statuses statuses
+     * @param deleted deleted
+     * @param queryFields query fields
+     * @param queryValues query values
+     * @param includeHistory include history
+     * @param orderBy order by
+     * @param isAsc is ascending
+     * @param offset offset
+     * @param limit limit
+     */
+    public static void validateStructuresInputRead(
+            Type type, Status[] statuses, Boolean deleted, FieldStructure[] queryFields, String[] queryValues,
+            Boolean includeHistory,
+            FieldStructure orderBy, Boolean isAsc,
+            Integer offset, Integer limit) {
+
+        // validate input
+        //     type
+        //     queryFields and queryValues
+        //         either
+        //             both null
+        //             both non-null, same length, non-empty
+        //         uuid
+
+        ValidateUtil.validateInputType(type);
+        boolean condition = ((queryFields == null && queryValues == null)
+                || (queryFields != null && queryValues != null && queryFields.length == queryValues.length && queryFields.length > 0));
+        ValidateUtil.validateCondition(condition, HttpStatus.BAD_REQUEST,
+                "url and parameters " + ValidateUtil.ARE_NOT_CORRECT, "queryFields, queryValues with different lengths or empty");
+        if (queryFields != null) {
+            for (int i=0; i<queryFields.length; i++) {
+                if (FieldStructure.UUID.equals(queryFields[i])) {
+                    ValidateUtil.validateInputUuid(queryValues[i]);
+                }
+            }
+
+        }
+    }
+
+    /**
+     * Validate structure element parameters (input) for create.
+     *
+     * @param structureElement structure element
+     */
+    public static void validateStructureElementInputCreate(StructureElement structureElement) {
+        validateStructureElementInput(structureElement, StructureChoice.CREATE);
+    }
+
+    /**
+     * Validate structure element parameters (input) for update.
+     *
+     * @param structureElement structure element
+     */
+    public static void validateStructureElementInputUpdate(StructureElement structureElement) {
+        validateStructureElementInput(structureElement, StructureChoice.UPDATE);
+    }
+
+    /**
+     * Validate structure element parameters (input) for delete.
+     *
+     * @param structureElement structure element
+     */
+    public static void validateStructureElementInputDelete(StructureElement structureElement) {
+        validateStructureElementInput(structureElement, StructureChoice.DELETE);
+    }
+
+    /**
+     * Validate structure element parameters (input) for approve.
+     *
+     * @param structureElement structure element
+     */
+    public static void validateStructureElementInputApprove(StructureElement structureElement) {
+        validateStructureElementInput(structureElement, StructureChoice.APPROVE);
+    }
+
+    /**
+     * Validate structure element parameters (input) for cancel.
+     *
+     * @param structureElement structure element
+     */
+    public static void validateStructureElementInputCancel(StructureElement structureElement) {
+        validateStructureElementInput(structureElement, StructureChoice.CANCEL);
+    }
+
+    /**
+     * Validate structure element parameters (input) for reject.
+     *
+     * @param structureElement structure element
+     */
+    public static void validateStructureElementInputReject(StructureElement structureElement) {
+        validateStructureElementInput(structureElement, StructureChoice.REJECT);
+    }
+
+    /**
+     * Validate structure element parameters (input).
+     *
+     * @param structureElement structure element
+     * @param structureChoice structure choice
+     */
+    private static void validateStructureElementInput(StructureElement structureElement, StructureChoice structureChoice) {
+        // check structure element input
+        //     parent uuid            - also validate data, parent uuid available if type is system, subsystem, device group, device type
+        //     name                   - required
+        //     mnemonic               - required except for device group
+        //     description            - required
+        //     comment                - required
+
+        if (structureElement == null || structureChoice == null) {
+            return;
+        }
+
+        if (!StructureChoice.CREATE.equals(structureChoice)) {
+            validateInputUuid(structureElement.getUuid() !=  null ? structureElement.getUuid().toString() : null);
+        }
+
+        validateInputType(structureElement.getType());
+
+        if (Type.SYSTEM.equals(structureElement.getType())) {
+            validateCondition(structureElement.getParent() != null, HttpStatus.BAD_REQUEST, "parent uuid " + ValidateUtil.IS_NOT_AVAILABLE, structureElement.toString());
+            ValidateUtil.validateInputUuid(structureElement.getParent().toString());
+        } else if (Type.SUBSYSTEM.equals(structureElement.getType())) {
+            validateCondition(structureElement.getParent() != null, HttpStatus.BAD_REQUEST, "parent uuid " + ValidateUtil.IS_NOT_AVAILABLE, structureElement.toString());
+            ValidateUtil.validateInputUuid(structureElement.getParent().toString());
+        } else if (Type.DEVICEGROUP.equals(structureElement.getType())) {
+            validateCondition(structureElement.getParent() != null, HttpStatus.BAD_REQUEST, "parent uuid " + ValidateUtil.IS_NOT_AVAILABLE, structureElement.toString());
+            ValidateUtil.validateInputUuid(structureElement.getParent().toString());
+        } else if (Type.DEVICETYPE.equals(structureElement.getType())) {
+            validateCondition(structureElement.getParent() != null, HttpStatus.BAD_REQUEST, "parent uuid " + ValidateUtil.IS_NOT_AVAILABLE, structureElement.toString());
+            ValidateUtil.validateInputUuid(structureElement.getParent().toString());
+        }
+
+        validateInputName(structureElement.getName());
+        if (Type.DEVICEGROUP.equals(structureElement.getType())) {
+            validateCondition(StringUtils.isEmpty(structureElement.getMnemonic()), HttpStatus.BAD_REQUEST, "mnemonic " + ValidateUtil.IS_NOT_CORRECT, structureElement.getMnemonic());
+        } else if (!Type.SYSTEMGROUP.equals(structureElement.getType())) {
+            validateInputMnemonic(structureElement.getMnemonic());
+        }
+        validateInputDescription(structureElement.getDescription());
+        validateInputComment(structureElement.getComment());
+    }
+
+    // ----------------------------------------------------------------------------------------------------
+
+    /**
+     * Validate structure element data for create.
+     *
+     * @param structureElement structure element
+     * @param namingConvention naming convention
+     * @param holderRepositories holder repositories
+     * @param holder holder
+     */
+    public static void validateStructureElementDataCreate(StructureElement structureElement, EssNamingConvention namingConvention, HolderRepositories holderRepositories, HolderSystemDeviceStructure holder) {
+        validateStructureElementDataInItself(structureElement, namingConvention, holderRepositories, holder, StructureChoice.CREATE);
+        validateStructureElementDataRelativeOtherData(structureElement, namingConvention, holderRepositories, holder, StructureChoice.CREATE);
+    }
+
+    /**
+     * Validate structure element data for update.
+     *
+     * @param structureElement structure element
+     * @param namingConvention naming convention
+     * @param holderRepositories holder repositories
+     * @param holder holder
+     */
+    public static void validateStructureElementDataUpdate(StructureElement structureElement, EssNamingConvention namingConvention, HolderRepositories holderRepositories, HolderSystemDeviceStructure holder) {
+        validateStructureElementDataInItself(structureElement, namingConvention, holderRepositories, holder, StructureChoice.UPDATE);
+        validateStructureElementDataRelativeOtherData(structureElement, namingConvention, holderRepositories, holder, StructureChoice.UPDATE);
+    }
+
+    /**
+     * Validate structure element data for delete.
+     *
+     * @param structureElement structure element
+     * @param namingConvention naming convention
+     * @param holderRepositories holder repositories
+     * @param holder holder
+     */
+    public static void validateStructureElementDataDelete(StructureElement structureElement, EssNamingConvention namingConvention, HolderRepositories holderRepositories, HolderSystemDeviceStructure holder) {
+        validateStructureElementDataInItself(structureElement, namingConvention, holderRepositories, holder, StructureChoice.DELETE);
+        validateStructureElementDataRelativeOtherData(structureElement, namingConvention, holderRepositories, holder, StructureChoice.DELETE);
+    }
+
+    /**
+     * Validate structure element data for approve.
+     *
+     * @param structureElement structure element
+     * @param namingConvention naming convention
+     * @param holderRepositories holder repositories
+     * @param holder holder
+     */
+    public static void validateStructureElementDataApprove(StructureElement structureElement, EssNamingConvention namingConvention, HolderRepositories holderRepositories, HolderSystemDeviceStructure holder) {
+        validateStructureElementDataInItself(structureElement, namingConvention, holderRepositories, holder, StructureChoice.APPROVE);
+        validateStructureElementDataRelativeOtherData(structureElement, namingConvention, holderRepositories, holder, StructureChoice.APPROVE);
+    }
+
+    /**
+     * Validate structure element data for cancel.
+     *
+     * @param structureElement structure element
+     * @param namingConvention naming convention
+     * @param holderRepositories holder repositories
+     * @param holder holder
+     */
+    public static void validateStructureElementDataCancel(StructureElement structureElement, EssNamingConvention namingConvention, HolderRepositories holderRepositories, HolderSystemDeviceStructure holder) {
+        validateStructureElementDataInItself(structureElement, namingConvention, holderRepositories, holder, StructureChoice.CANCEL);
+        validateStructureElementDataRelativeOtherData(structureElement, namingConvention, holderRepositories, holder, StructureChoice.CANCEL);
+    }
+
+    /**
+     * Validate structure element data for reject.
+     *
+     * @param structureElement structure element
+     * @param namingConvention naming convention
+     * @param holderRepositories holder repositories
+     * @param holder holder
+     */
+    public static void validateStructureElementDataReject(StructureElement structureElement, EssNamingConvention namingConvention, HolderRepositories holderRepositories, HolderSystemDeviceStructure holder) {
+        validateStructureElementDataInItself(structureElement, namingConvention, holderRepositories, holder, StructureChoice.REJECT);
+        validateStructureElementDataRelativeOtherData(structureElement, namingConvention, holderRepositories, holder, StructureChoice.REJECT);
+    }
+
+    /**
+     * Validate structure element data in itself.
+     *
+     * @param structureElement
+     * @param namingConvention
+     * @param holderRepositories
+     * @param holder
+     * @param structureChoice
+     */
+    public static void validateStructureElementDataInItself(StructureElement structureElement, EssNamingConvention namingConvention, HolderRepositories holderRepositories, HolderSystemDeviceStructure holder, StructureChoice structureChoice) {
+        // check structure element data in itself
+        //     update, delete
+        //         uuid - approved, latest, not deleted - list size 1
+        //         possibly
+        //             no pending entry waiting to be approved, cancelled, rejected (pending with higher id than currently approved)
+        //     approve, reject, cancel
+        //         uuid - pending, latest, not deleted  - list size 1
+
+        if (structureElement == null || namingConvention == null || holderRepositories == null || holder == null || structureChoice == null) {
+            return;
+        }
+
+        if (StructureChoice.UPDATE.equals(structureChoice) || StructureChoice.DELETE.equals(structureChoice)) {
+            if (Type.SYSTEMGROUP.equals(structureElement.getType())) {
+                List<SystemGroup> systemGroups = holderRepositories.getSystemGroupRepository().readSystemGroups(Status.APPROVED, false, FieldStructure.UUID, structureElement.getUuid().toString());
+                ValidateUtil.validateCondition(systemGroups != null && systemGroups.size() == 1, HttpStatus.BAD_REQUEST,
+                        ValidateUtil.SYSTEMGROUP + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT, structureElement.toString());
+            } else if (Type.SYSTEM.equals(structureElement.getType())) {
+                List<System> systems = holderRepositories.getSystemRepository().readSystems(Status.APPROVED, false, FieldStructure.UUID, structureElement.getUuid().toString());
+                ValidateUtil.validateCondition(systems != null && systems.size() == 1, HttpStatus.BAD_REQUEST,
+                        ValidateUtil.SYSTEM + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT, structureElement.toString());
+            } else if (Type.SUBSYSTEM.equals(structureElement.getType())) {
+                List<Subsystem> subsystems = holderRepositories.getSubsystemRepository().readSubsystems(Status.APPROVED, false, FieldStructure.UUID, structureElement.getUuid().toString());
+                ValidateUtil.validateCondition(subsystems != null && subsystems.size() == 1, HttpStatus.BAD_REQUEST,
+                        ValidateUtil.SUBSYSTEM + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT, structureElement.toString());
+            } else if (Type.DISCIPLINE.equals(structureElement.getType())) {
+                List<Discipline> disciplines = holderRepositories.getDisciplineRepository().readDisciplines(Status.APPROVED, false, FieldStructure.UUID, structureElement.getUuid().toString());
+                ValidateUtil.validateCondition(disciplines != null && disciplines.size() == 1, HttpStatus.BAD_REQUEST,
+                        ValidateUtil.DISCIPLINE + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT, structureElement.toString());
+            } else if (Type.DEVICEGROUP.equals(structureElement.getType())) {
+                List<DeviceGroup> deviceGroups = holderRepositories.getDeviceGroupRepository().readDeviceGroups(Status.APPROVED, false, FieldStructure.UUID, structureElement.getUuid().toString());
+                ValidateUtil.validateCondition(deviceGroups != null && deviceGroups.size() == 1, HttpStatus.BAD_REQUEST,
+                        ValidateUtil.DEVICEGROUP + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT, structureElement.toString());
+            } else if (Type.DEVICETYPE.equals(structureElement.getType())) {
+                List<DeviceType> deviceTypes = holderRepositories.getDeviceTypeRepository().readDeviceTypes(Status.APPROVED, false, FieldStructure.UUID, structureElement.getUuid().toString());
+                ValidateUtil.validateCondition(deviceTypes != null && deviceTypes.size() == 1, HttpStatus.BAD_REQUEST,
+                        ValidateUtil.DEVICETYPE + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT, structureElement.toString());
+            }
+        } else if (StructureChoice.APPROVE.equals(structureChoice) || StructureChoice.CANCEL.equals(structureChoice) || StructureChoice.REJECT.equals(structureChoice)) {
+            if (Type.SYSTEMGROUP.equals(structureElement.getType())) {
+                List<SystemGroup> systemGroups = holderRepositories.getSystemGroupRepository().readSystemGroups(Status.PENDING, null, FieldStructure.UUID, structureElement.getUuid().toString());
+                ValidateUtil.validateCondition(systemGroups != null && systemGroups.size() == 1, HttpStatus.BAD_REQUEST,
+                        ValidateUtil.SYSTEMGROUP + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT, structureElement.toString());
+            } else if (Type.SYSTEM.equals(structureElement.getType())) {
+                List<System> systems = holderRepositories.getSystemRepository().readSystems(Status.PENDING, null, FieldStructure.UUID, structureElement.getUuid().toString());
+                ValidateUtil.validateCondition(systems != null && systems.size() == 1, HttpStatus.BAD_REQUEST,
+                        ValidateUtil.SYSTEM + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT, structureElement.toString());
+            } else if (Type.SUBSYSTEM.equals(structureElement.getType())) {
+                List<Subsystem> subsystems = holderRepositories.getSubsystemRepository().readSubsystems(Status.PENDING, null, FieldStructure.UUID, structureElement.getUuid().toString());
+                ValidateUtil.validateCondition(subsystems != null && subsystems.size() == 1, HttpStatus.BAD_REQUEST,
+                        ValidateUtil.SUBSYSTEM + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT, structureElement.toString());
+            } else if (Type.DISCIPLINE.equals(structureElement.getType())) {
+                List<Discipline> disciplines = holderRepositories.getDisciplineRepository().readDisciplines(Status.PENDING, null, FieldStructure.UUID, structureElement.getUuid().toString());
+                ValidateUtil.validateCondition(disciplines != null && disciplines.size() == 1, HttpStatus.BAD_REQUEST,
+                        ValidateUtil.DISCIPLINE + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT, structureElement.toString());
+            } else if (Type.DEVICEGROUP.equals(structureElement.getType())) {
+                List<DeviceGroup> deviceGroups = holderRepositories.getDeviceGroupRepository().readDeviceGroups(Status.PENDING, null, FieldStructure.UUID, structureElement.getUuid().toString());
+                ValidateUtil.validateCondition(deviceGroups != null && deviceGroups.size() == 1, HttpStatus.BAD_REQUEST,
+                        ValidateUtil.DEVICEGROUP + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT, structureElement.toString());
+            } else if (Type.DEVICETYPE.equals(structureElement.getType())) {
+                List<DeviceType> deviceTypes = holderRepositories.getDeviceTypeRepository().readDeviceTypes(Status.PENDING, null, FieldStructure.UUID, structureElement.getUuid().toString());
+                ValidateUtil.validateCondition(deviceTypes != null && deviceTypes.size() == 1, HttpStatus.BAD_REQUEST,
+                        ValidateUtil.DEVICETYPE + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT, structureElement.toString());
+            }
+        }
+    }
+
+    /**
+     * Validate structure element data relative other data.
+     *
+     * @param structureElement structure element
+     * @param namingConvention naming convention
+     * @param holderRepositories holder repositories
+     * @param holder holder
+     * @param structureChoice structure choice
+     */
+    public static void validateStructureElementDataRelativeOtherData(StructureElement structureElement, EssNamingConvention namingConvention, HolderRepositories holderRepositories, HolderSystemDeviceStructure holder, StructureChoice structureChoice) {
+        // check structure element data in relation to other data
+        //     create, update
+        //         parent uuid          - (if applicable) approved, latest, not deleted
+        //         mnemonic             - (if not same)   not exists
+        //         mnemonic equivalence - (if not same)   equivalence not exists
+        //     approve
+        //         uuid - pending, not latest, deleted
+        //         no or less checks if entry to be deleted                - approve (delete)
+        //         more or same checks as above if entry is not be deleted - approve (create), approve (update)
+        //         need checks as content may have changed from time of create, update, delete to time of approve
+        //     checks on mnemonic, mnemonic equivalence are to ensure can coexist, can move
+        //     possibly
+        //         additional checks if names are affected
+        //         comment not same as previous comment
+
+        if (structureElement == null || namingConvention == null || holderRepositories == null || holder == null || structureChoice == null) {
+            return;
+        }
+
+        String details = structureElement.toString();
+        if (StructureChoice.CREATE.equals(structureChoice) || StructureChoice.UPDATE.equals(structureChoice)) {
+            if (Type.SYSTEMGROUP.equals(structureElement.getType())) {
+                // note rules for mnemonic for system group
+                if (!StringUtils.isEmpty(structureElement.getMnemonic())) {
+                    // mnemonic
+                    String message = ValidateUtil.SYSTEMGROUP + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT;
+                    List<SystemGroup> systemGroups = holderRepositories.getSystemGroupRepository().readSystemGroups(Status.APPROVED, false, FieldStructure.MNEMONIC, structureElement.getMnemonic());
+                    validateConditionIfStructureChoice(StructureChoice.CREATE, structureChoice, systemGroups == null || systemGroups.isEmpty(),   HttpStatus.BAD_REQUEST, message, details);
+                    validateConditionIfStructureChoice(StructureChoice.UPDATE, structureChoice, systemGroups == null || systemGroups.isEmpty() || systemGroups.size() == 1 && systemGroups.get(0).getUuid().equals(structureElement.getUuid()), HttpStatus.BAD_REQUEST, message, details);
+
+                    // mnemonic equivalence
+                    systemGroups = holderRepositories.getSystemGroupRepository().readSystemGroups(Status.APPROVED, false, FieldStructure.MNEMONICEQUIVALENCE, namingConvention.equivalenceClassRepresentative(structureElement.getMnemonic()));
+                    validateConditionIfStructureChoice(StructureChoice.CREATE, structureChoice, systemGroups == null || systemGroups.isEmpty(),   HttpStatus.BAD_REQUEST, message, details);
+                    validateConditionIfStructureChoice(StructureChoice.UPDATE, structureChoice, systemGroups == null || systemGroups.isEmpty() || systemGroups.size() == 1 && systemGroups.get(0).getUuid().equals(structureElement.getUuid()), HttpStatus.BAD_REQUEST, message, details);
+                }
+            } else if (Type.SYSTEM.equals(structureElement.getType())) {
+                // parent uuid
+                String message = ValidateUtil.SYSTEMGROUP + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT;
+                List<SystemGroup> systemGroups = holderRepositories.getSystemGroupRepository().readSystemGroups(Status.APPROVED, false, FieldStructure.UUID, structureElement.getParent().toString());
+                ValidateUtil.validateCondition(systemGroups != null && systemGroups.size() == 1, HttpStatus.BAD_REQUEST, message, details);
+
+                // mnemonic
+                message = ValidateUtil.SYSTEM + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT;
+                List<System> systems = holderRepositories.getSystemRepository().readSystems(Status.APPROVED, false, FieldStructure.MNEMONIC, structureElement.getMnemonic());
+                validateConditionIfStructureChoice(StructureChoice.CREATE, structureChoice, systems == null || systems.isEmpty(),   HttpStatus.BAD_REQUEST, message, details);
+                validateConditionIfStructureChoice(StructureChoice.UPDATE, structureChoice, systems == null || systems.isEmpty() || systems.size() == 1 && systems.get(0).getUuid().equals(structureElement.getUuid()), HttpStatus.BAD_REQUEST, message, details);
+
+                // mnemonic equivalence
+                systems = holderRepositories.getSystemRepository().readSystems(Status.APPROVED, false, FieldStructure.MNEMONICEQUIVALENCE, namingConvention.equivalenceClassRepresentative(structureElement.getMnemonic()));
+                validateConditionIfStructureChoice(StructureChoice.CREATE, structureChoice, systems == null || systems.isEmpty(),   HttpStatus.BAD_REQUEST, message, details);
+                validateConditionIfStructureChoice(StructureChoice.UPDATE, structureChoice, systems == null || systems.isEmpty() || systems.size() == 1 && systems.get(0).getUuid().equals(structureElement.getUuid()), HttpStatus.BAD_REQUEST, message, details);
+            } else if (Type.SUBSYSTEM.equals(structureElement.getType())) {
+                // parent uuid
+                String message = ValidateUtil.SYSTEM + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT;
+                List<System> systems = holderRepositories.getSystemRepository().readSystems(Status.APPROVED, false, FieldStructure.UUID, structureElement.getParent().toString());
+                ValidateUtil.validateCondition(systems != null && systems.size() == 1, HttpStatus.BAD_REQUEST, message, details);
+
+                // mnemonic
+                message = ValidateUtil.SUBSYSTEM + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT;
+                List<Subsystem> subsystems = holderRepositories.getSubsystemRepository().readSubsystems(Status.APPROVED, false, FieldStructure.MNEMONIC, structureElement.getMnemonic());
+                validateConditionIfStructureChoice(StructureChoice.CREATE, structureChoice, subsystems == null || subsystems.isEmpty(), HttpStatus.BAD_REQUEST, message, details);
+                validateConditionIfStructureChoice(StructureChoice.UPDATE, structureChoice, subsystems == null || subsystems.isEmpty() || subsystems.size() == 1 && subsystems.get(0).getUuid().equals(structureElement.getUuid()), HttpStatus.BAD_REQUEST, message, details);
+
+                // mnemonic equivalence
+                subsystems = holderRepositories.getSubsystemRepository().readSubsystems(Status.APPROVED, false, FieldStructure.MNEMONICEQUIVALENCE, namingConvention.equivalenceClassRepresentative(structureElement.getMnemonic()));
+                validateConditionIfStructureChoice(StructureChoice.CREATE, structureChoice, subsystems == null || subsystems.isEmpty(),   HttpStatus.BAD_REQUEST, message, details);
+                validateConditionIfStructureChoice(StructureChoice.UPDATE, structureChoice, subsystems == null || subsystems.isEmpty() || subsystems.size() == 1 && subsystems.get(0).getUuid().equals(structureElement.getUuid()), HttpStatus.BAD_REQUEST, message, details);
+            } else if (Type.DISCIPLINE.equals(structureElement.getType())) {
+                // mnemonic
+                String message = ValidateUtil.DISCIPLINE + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT;
+                List<Discipline> disciplines = holderRepositories.getDisciplineRepository().readDisciplines(Status.APPROVED, false, FieldStructure.MNEMONIC, structureElement.getMnemonic());
+                validateConditionIfStructureChoice(StructureChoice.CREATE, structureChoice, disciplines == null || disciplines.isEmpty(), HttpStatus.BAD_REQUEST, message, details);
+                validateConditionIfStructureChoice(StructureChoice.UPDATE, structureChoice, disciplines == null || disciplines.isEmpty() || disciplines.size() == 1 && disciplines.get(0).getUuid().equals(structureElement.getUuid()), HttpStatus.BAD_REQUEST, message, details);
+
+                // mnemonic equivalence
+                disciplines = holderRepositories.getDisciplineRepository().readDisciplines(Status.APPROVED, false, FieldStructure.MNEMONICEQUIVALENCE, namingConvention.equivalenceClassRepresentative(structureElement.getMnemonic()));
+                validateConditionIfStructureChoice(StructureChoice.CREATE, structureChoice, disciplines == null || disciplines.isEmpty(),   HttpStatus.BAD_REQUEST, message, details);
+                validateConditionIfStructureChoice(StructureChoice.UPDATE, structureChoice, disciplines == null || disciplines.isEmpty() || disciplines.size() == 1 && disciplines.get(0).getUuid().equals(structureElement.getUuid()), HttpStatus.BAD_REQUEST, message, details);
+            } else if (Type.DEVICEGROUP.equals(structureElement.getType())) {
+                // parent uuid
+                String message = ValidateUtil.DISCIPLINE + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT;
+                List<Discipline> disciplines = holderRepositories.getDisciplineRepository().readDisciplines(Status.APPROVED, false, FieldStructure.UUID, structureElement.getParent().toString());
+                ValidateUtil.validateCondition(disciplines != null && disciplines.size() == 1, HttpStatus.BAD_REQUEST, message, details);
+
+                // note rules for mnemonic for device group
+
+                // mnemonic
+                message = ValidateUtil.DEVICEGROUP + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT;
+                ValidateUtil.validateCondition(StringUtils.isEmpty(structureElement.getMnemonic()), HttpStatus.BAD_REQUEST, message, details);
+            } else if (Type.DEVICETYPE.equals(structureElement.getType())) {
+                // parent uuid
+                String message = ValidateUtil.DEVICEGROUP + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT;
+                List<DeviceGroup> deviceGroups = holderRepositories.getDeviceGroupRepository().readDeviceGroups(Status.APPROVED, false, FieldStructure.UUID, structureElement.getParent().toString());
+                ValidateUtil.validateCondition(deviceGroups != null && deviceGroups.size() == 1, HttpStatus.BAD_REQUEST, message, details);
+
+                // mnemonic
+                message = ValidateUtil.DEVICETYPE + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT;
+                List<DeviceType> deviceTypes = holderRepositories.getDeviceTypeRepository().readDeviceTypes(Status.APPROVED, false, FieldStructure.MNEMONIC, structureElement.getMnemonic());
+                validateConditionIfStructureChoice(StructureChoice.CREATE, structureChoice, deviceTypes == null || deviceTypes.isEmpty(), HttpStatus.BAD_REQUEST, message, details);
+                validateConditionIfStructureChoice(StructureChoice.UPDATE, structureChoice, deviceTypes == null || deviceTypes.isEmpty() || deviceTypes.size() == 1 && deviceTypes.get(0).getUuid().equals(structureElement.getUuid()), HttpStatus.BAD_REQUEST, message, details);
+
+                // mnemonic equivalence
+                deviceTypes = holderRepositories.getDeviceTypeRepository().readDeviceTypes(Status.APPROVED, false, FieldStructure.MNEMONICEQUIVALENCE, namingConvention.equivalenceClassRepresentative(structureElement.getMnemonic()));
+                validateConditionIfStructureChoice(StructureChoice.CREATE, structureChoice, deviceTypes == null || deviceTypes.isEmpty(),   HttpStatus.BAD_REQUEST, message, details);
+                validateConditionIfStructureChoice(StructureChoice.UPDATE, structureChoice, deviceTypes == null || deviceTypes.isEmpty() || deviceTypes.size() == 1 && deviceTypes.get(0).getUuid().equals(structureElement.getUuid()), HttpStatus.BAD_REQUEST, message, details);
+            }
+        } else if (StructureChoice.APPROVE.equals(structureChoice)) {
+            //
+            if (Type.SYSTEMGROUP.equals(structureElement.getType())) {
+                String message = ValidateUtil.SYSTEMGROUP + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT;
+                List<SystemGroup> toBeApproved = holderRepositories.getSystemGroupRepository().readSystemGroups(Status.PENDING, null, FieldStructure.UUID, structureElement.getUuid().toString());
+                ValidateUtil.validateCondition(toBeApproved != null && toBeApproved.size() == 1, HttpStatus.BAD_REQUEST, message, details);
+
+                // mnemonic
+                List<SystemGroup> systemGroups = holderRepositories.getSystemGroupRepository().readSystemGroups(Status.APPROVED, false, FieldStructure.MNEMONIC, toBeApproved.get(0).getMnemonic());
+                validateConditionIfStructureChoice(StructureChoice.CREATE, structureChoice, systemGroups == null || systemGroups.isEmpty(),   HttpStatus.BAD_REQUEST, message, details);
+                validateConditionIfStructureChoice(StructureChoice.UPDATE, structureChoice, systemGroups == null || systemGroups.isEmpty() || systemGroups.size() == 1 && systemGroups.get(0).getUuid().equals(toBeApproved.get(0).getUuid()), HttpStatus.BAD_REQUEST, message, details);
+
+                // mnemonic equivalence
+                systemGroups = holderRepositories.getSystemGroupRepository().readSystemGroups(Status.APPROVED, false, FieldStructure.MNEMONICEQUIVALENCE, toBeApproved.get(0).getMnemonicEquivalence());
+                validateConditionIfStructureChoice(StructureChoice.CREATE, structureChoice, systemGroups == null || systemGroups.isEmpty(),   HttpStatus.BAD_REQUEST, message, details);
+                validateConditionIfStructureChoice(StructureChoice.UPDATE, structureChoice, systemGroups == null || systemGroups.isEmpty() || systemGroups.size() == 1 && systemGroups.get(0).getUuid().equals(toBeApproved.get(0).getUuid()), HttpStatus.BAD_REQUEST, message, details);
+            } else if (Type.SYSTEM.equals(structureElement.getType())) {
+                String message = ValidateUtil.SYSTEM + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT;
+                List<System> toBeApproved = holderRepositories.getSystemRepository().readSystems(Status.PENDING, null, FieldStructure.UUID, structureElement.getUuid().toString());
+                ValidateUtil.validateCondition(toBeApproved != null && toBeApproved.size() == 1, HttpStatus.BAD_REQUEST, message, details);
+
+                // parent uuid
+                message = ValidateUtil.SYSTEMGROUP + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT;
+                List<SystemGroup> systemGroups = holderRepositories.getSystemGroupRepository().readSystemGroups(Status.APPROVED, false, FieldStructure.UUID, toBeApproved.get(0).getParentUuid().toString());
+                ValidateUtil.validateCondition(systemGroups != null && systemGroups.size() == 1, HttpStatus.BAD_REQUEST, message, details);
+
+                // mnemonic
+                message = ValidateUtil.SYSTEM + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT;
+                List<System> systems = holderRepositories.getSystemRepository().readSystems(Status.APPROVED, false, FieldStructure.MNEMONIC, toBeApproved.get(0).getMnemonic());
+                validateConditionIfStructureChoice(StructureChoice.CREATE, structureChoice, systems == null || systems.isEmpty(),   HttpStatus.BAD_REQUEST, message, details);
+                validateConditionIfStructureChoice(StructureChoice.UPDATE, structureChoice, systems == null || systems.isEmpty() || systems.size() == 1 && systems.get(0).getUuid().equals(toBeApproved.get(0).getUuid()), HttpStatus.BAD_REQUEST, message, details);
+
+                // mnemonic equivalence
+                systems = holderRepositories.getSystemRepository().readSystems(Status.APPROVED, false, FieldStructure.MNEMONICEQUIVALENCE, toBeApproved.get(0).getMnemonicEquivalence());
+                validateConditionIfStructureChoice(StructureChoice.CREATE, structureChoice, systems == null || systems.isEmpty(),   HttpStatus.BAD_REQUEST, message, details);
+                validateConditionIfStructureChoice(StructureChoice.UPDATE, structureChoice, systems == null || systems.isEmpty() || systems.size() == 1 && systems.get(0).getUuid().equals(toBeApproved.get(0).getUuid()), HttpStatus.BAD_REQUEST, message, details);
+            } else if (Type.SUBSYSTEM.equals(structureElement.getType())) {
+                String message = ValidateUtil.SUBSYSTEM + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT;
+                List<Subsystem> toBeApproved = holderRepositories.getSubsystemRepository().readSubsystems(Status.PENDING, null, FieldStructure.UUID, structureElement.getUuid().toString());
+                ValidateUtil.validateCondition(toBeApproved != null && toBeApproved.size() == 1, HttpStatus.BAD_REQUEST, message, details);
+
+                // parent uuid
+                message = ValidateUtil.SYSTEM + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT;
+                List<System> systems = holderRepositories.getSystemRepository().readSystems(Status.APPROVED, false, FieldStructure.UUID, toBeApproved.get(0).getParentUuid().toString());
+                ValidateUtil.validateCondition(systems != null && systems.size() == 1, HttpStatus.BAD_REQUEST, message, details);
+
+                // mnemonic
+                message = ValidateUtil.SUBSYSTEM + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT;
+                List<Subsystem> subsystems = holderRepositories.getSubsystemRepository().readSubsystems(Status.APPROVED, false, FieldStructure.MNEMONIC, toBeApproved.get(0).getMnemonic());
+                validateConditionIfStructureChoice(StructureChoice.CREATE, structureChoice, subsystems == null || subsystems.isEmpty(),   HttpStatus.BAD_REQUEST, message, details);
+                validateConditionIfStructureChoice(StructureChoice.UPDATE, structureChoice, subsystems == null || subsystems.isEmpty() || subsystems.size() == 1 && subsystems.get(0).getUuid().equals(toBeApproved.get(0).getUuid()), HttpStatus.BAD_REQUEST, message, details);
+
+                // mnemonic equivalence
+                subsystems = holderRepositories.getSubsystemRepository().readSubsystems(Status.APPROVED, false, FieldStructure.MNEMONICEQUIVALENCE, toBeApproved.get(0).getMnemonicEquivalence());
+                validateConditionIfStructureChoice(StructureChoice.CREATE, structureChoice, subsystems == null || subsystems.isEmpty(),   HttpStatus.BAD_REQUEST, message, details);
+                validateConditionIfStructureChoice(StructureChoice.UPDATE, structureChoice, subsystems == null || subsystems.isEmpty() || subsystems.size() == 1 && subsystems.get(0).getUuid().equals(toBeApproved.get(0).getUuid()), HttpStatus.BAD_REQUEST, message, details);
+            } else if (Type.DISCIPLINE.equals(structureElement.getType())) {
+                String message = ValidateUtil.DISCIPLINE + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT;
+                List<Discipline> toBeApproved = holderRepositories.getDisciplineRepository().readDisciplines(Status.PENDING, null, FieldStructure.UUID, structureElement.getUuid().toString());
+                ValidateUtil.validateCondition(toBeApproved != null && toBeApproved.size() == 1, HttpStatus.BAD_REQUEST, message, details);
+
+                // mnemonic
+                List<Discipline> disciplines = holderRepositories.getDisciplineRepository().readDisciplines(Status.APPROVED, false, FieldStructure.MNEMONIC, toBeApproved.get(0).getMnemonic());
+                validateConditionIfStructureChoice(StructureChoice.CREATE, structureChoice, disciplines == null || disciplines.isEmpty(),   HttpStatus.BAD_REQUEST, message, details);
+                validateConditionIfStructureChoice(StructureChoice.UPDATE, structureChoice, disciplines == null || disciplines.isEmpty() || disciplines.size() == 1 && disciplines.get(0).getUuid().equals(toBeApproved.get(0).getUuid()), HttpStatus.BAD_REQUEST, message, details);
+
+                // mnemonic equivalence
+                disciplines = holderRepositories.getDisciplineRepository().readDisciplines(Status.APPROVED, false, FieldStructure.MNEMONICEQUIVALENCE, toBeApproved.get(0).getMnemonicEquivalence());
+                validateConditionIfStructureChoice(StructureChoice.CREATE, structureChoice, disciplines == null || disciplines.isEmpty(),   HttpStatus.BAD_REQUEST, message, details);
+                validateConditionIfStructureChoice(StructureChoice.UPDATE, structureChoice, disciplines == null || disciplines.isEmpty() || disciplines.size() == 1 && disciplines.get(0).getUuid().equals(toBeApproved.get(0).getUuid()), HttpStatus.BAD_REQUEST, message, details);
+            } else if (Type.DEVICEGROUP.equals(structureElement.getType())) {
+                String message = ValidateUtil.DEVICEGROUP + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT;
+                List<DeviceGroup> toBeApproved = holderRepositories.getDeviceGroupRepository().readDeviceGroups(Status.PENDING, null, FieldStructure.UUID, structureElement.getUuid().toString());
+                ValidateUtil.validateCondition(toBeApproved != null && toBeApproved.size() == 1, HttpStatus.BAD_REQUEST, message, details);
+
+                // parent uuid
+                message = ValidateUtil.DISCIPLINE + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT;
+                List<Discipline> disciplines = holderRepositories.getDisciplineRepository().readDisciplines(Status.APPROVED, false, FieldStructure.UUID, toBeApproved.get(0).getParentUuid().toString());
+                ValidateUtil.validateCondition(disciplines != null && disciplines.size() == 1, HttpStatus.BAD_REQUEST, message, details);
+
+                // note rules for mnemonic for device group
+
+                // mnemonic
+                message = ValidateUtil.DEVICEGROUP + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT;
+                ValidateUtil.validateCondition(StringUtils.isEmpty(structureElement.getMnemonic()), HttpStatus.BAD_REQUEST, message, details);
+            } else if (Type.DEVICETYPE.equals(structureElement.getType())) {
+                String message = ValidateUtil.DEVICETYPE + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT;
+                List<DeviceType> toBeApproved = holderRepositories.getDeviceTypeRepository().readDeviceTypes(Status.PENDING, null, FieldStructure.UUID, structureElement.getUuid().toString());
+                ValidateUtil.validateCondition(toBeApproved != null && toBeApproved.size() == 1, HttpStatus.BAD_REQUEST, message, details);
+
+                // parent uuid
+                message = ValidateUtil.DEVICEGROUP + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT;
+                List<DeviceGroup> deviceGroups = holderRepositories.getDeviceGroupRepository().readDeviceGroups(Status.APPROVED, false, FieldStructure.UUID, toBeApproved.get(0).getParentUuid().toString());
+                ValidateUtil.validateCondition(deviceGroups != null && deviceGroups.size() == 1, HttpStatus.BAD_REQUEST, message, details);
+
+                // mnemonic
+                message = ValidateUtil.DEVICETYPE + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT;
+                List<DeviceType> deviceTypes = holderRepositories.getDeviceTypeRepository().readDeviceTypes(Status.APPROVED, false, FieldStructure.MNEMONIC, toBeApproved.get(0).getMnemonic());
+                validateConditionIfStructureChoice(StructureChoice.CREATE, structureChoice, deviceTypes == null || deviceTypes.isEmpty(),   HttpStatus.BAD_REQUEST, message, details);
+                validateConditionIfStructureChoice(StructureChoice.UPDATE, structureChoice, deviceTypes == null || deviceTypes.isEmpty() || deviceTypes.size() == 1 && deviceTypes.get(0).getUuid().equals(toBeApproved.get(0).getUuid()), HttpStatus.BAD_REQUEST, message, details);
+
+                // mnemonic equivalence
+                deviceTypes = holderRepositories.getDeviceTypeRepository().readDeviceTypes(Status.APPROVED, false, FieldStructure.MNEMONICEQUIVALENCE, toBeApproved.get(0).getMnemonicEquivalence());
+                validateConditionIfStructureChoice(StructureChoice.CREATE, structureChoice, deviceTypes == null || deviceTypes.isEmpty(),   HttpStatus.BAD_REQUEST, message, details);
+                validateConditionIfStructureChoice(StructureChoice.UPDATE, structureChoice, deviceTypes == null || deviceTypes.isEmpty() || deviceTypes.size() == 1 && deviceTypes.get(0).getUuid().equals(toBeApproved.get(0).getUuid()), HttpStatus.BAD_REQUEST, message, details);
+            }
+        }
+    }
+
+    // ----------------------------------------------------------------------------------------------------
+
+    /**
+     * Validate structure data.
+     * This method corresponds to structure element data for create, albeit in a different way.
+     *
+     * @param type type
+     * @param mnemonicpath mnemonic path
+     * @param namingConvention naming convention
+     * @param holderIRepositories holder repositories
+     * @param holder holder
+     */
+    public static void validateStructureDataCreate(Type type, String mnemonicpath, EssNamingConvention namingConvention, HolderIRepositories holderIRepositories, HolderSystemDeviceStructure holder) {
+        if (type == null || mnemonicpath == null || namingConvention == null || holderIRepositories == null || holder == null) {
+            return;
+        }
+
+        String[] path = NamingConventionUtil.string2MnemonicPath(mnemonicpath);
+        validateCondition(path != null && path.length >= 1 && path.length <= 3,
+                HttpStatus.BAD_REQUEST, ValidateUtil.STRUCTURE + " mnemonic path " + ValidateUtil.IS_NOT_VALID, path + ", " + mnemonicpath);
+
+        if (Type.SYSTEMGROUP.equals(type)) {
+            validateCondition(path.length == 1, HttpStatus.BAD_REQUEST, ValidateUtil.SYSTEMGROUP + " mnemonic path " + ValidateUtil.IS_NOT_VALID, path.length + ", " + mnemonicpath);
+
+            // system group may have empty path but there will be mnemonicpath in this context
+
+            // mnemonic
+            SystemGroup sg = holderIRepositories.getSystemGroupRepository().findLatestNotDeletedByMnemonic(path[0]);
+            ValidateUtil.validateCondition(sg == null, HttpStatus.BAD_REQUEST, ValidateUtil.SYSTEMGROUP + " mnemonic path duplicate", path[0] + ", " + mnemonicpath);
+
+            // mnemonic equivalence
+            List<SystemGroup> systemGroups = holderIRepositories.getSystemGroupRepository().findLatestNotDeleted();
+            for (SystemGroup systemGroup : systemGroups) {
+                validateCondition(!StringUtils.equals(systemGroup.getMnemonicEquivalence(), namingConvention.equivalenceClassRepresentative(path[0])), HttpStatus.BAD_REQUEST, ValidateUtil.SYSTEMGROUP + " mnemonic path equivalence duplicate", path[0] + ", " + mnemonicpath);
+            }
+        } else if (Type.SYSTEM.equals(type)) {
+            validateCondition(path.length == 1 || path.length == 2, HttpStatus.BAD_REQUEST, ValidateUtil.SYSTEM + " mnemonic path " + ValidateUtil.IS_NOT_VALID, path.length + ", " + mnemonicpath);
+
+            // path with 2 elements - system group and system ---> check both as pair
+            // path with 1 element  - system group or  system ---> check both individually
+            // check mnemonic, mnemonic equivalence
+
+            // mnemonic
+            if (path.length == 2) {
+                validateCondition(!StringUtils.equals(path[0], path[1]), HttpStatus.BAD_REQUEST, ValidateUtil.SYSTEM + " mnemonic path duplicate", mnemonicpath);
+            }
+            List<String> mnemonicPaths = StructureUtil.getMnemonicPathsSystem(holder, false, namingConvention);
+            for (String existingPath : mnemonicPaths) {
+                validateCondition(!StringUtils.equals(mnemonicpath, existingPath), HttpStatus.BAD_REQUEST, ValidateUtil.SYSTEM + " mnemonic path duplicate", mnemonicpath);
+            }
+
+            // mnemonic equivalence
+            if (path.length == 2) {
+                validateCondition(!StringUtils.equals(namingConvention.equivalenceClassRepresentative(path[0]), namingConvention.equivalenceClassRepresentative(path[1])), HttpStatus.BAD_REQUEST, ValidateUtil.SYSTEM + " mnemonic path duplicate", mnemonicpath);
+            }
+            String mnemonicpathEquivalence = namingConvention.equivalenceClassRepresentative(mnemonicpath);
+            mnemonicPaths = StructureUtil.getMnemonicPathsSystem(holder, true, namingConvention);
+            for (String existingPath : mnemonicPaths) {
+                validateCondition(!StringUtils.equals(mnemonicpathEquivalence, namingConvention.equivalenceClassRepresentative(existingPath)), HttpStatus.BAD_REQUEST, ValidateUtil.SYSTEM + " mnemonic path equivalence duplicate", mnemonicpath);
+            }
+        } else if (Type.SUBSYSTEM.equals(type)) {
+            validateCondition(path.length == 2 || path.length == 3, HttpStatus.BAD_REQUEST, ValidateUtil.SUBSYSTEM + " mnemonic path " + ValidateUtil.IS_NOT_VALID, path.length + ", " + mnemonicpath);
+
+            // mnemonic
+            if (path.length == 2) {
+                validateCondition(!StringUtils.equals(path[0], path[1]), HttpStatus.BAD_REQUEST, ValidateUtil.SYSTEM + " mnemonic path duplicate", mnemonicpath);
+            } else {
+                validateCondition(!StringUtils.equals(path[0], path[1]), HttpStatus.BAD_REQUEST, ValidateUtil.SYSTEM + " mnemonic path duplicate", mnemonicpath);
+                validateCondition(!StringUtils.equals(path[0], path[2]), HttpStatus.BAD_REQUEST, ValidateUtil.SYSTEM + " mnemonic path duplicate", mnemonicpath);
+                validateCondition(!StringUtils.equals(path[1], path[2]), HttpStatus.BAD_REQUEST, ValidateUtil.SYSTEM + " mnemonic path duplicate", mnemonicpath);
+            }
+            List<String> mnemonicPaths = StructureUtil.getMnemonicPathsSubsystem(holder, false, namingConvention);
+            for (String existingPath : mnemonicPaths) {
+                validateCondition(!StringUtils.equals(mnemonicpath, existingPath), HttpStatus.BAD_REQUEST, ValidateUtil.SYSTEM + " mnemonic path duplicate", mnemonicpath);
+            }
+
+            // mnemonic equivalence
+            if (path.length == 2) {
+                validateCondition(!StringUtils.equals(namingConvention.equivalenceClassRepresentative(path[0]), namingConvention.equivalenceClassRepresentative(path[1])), HttpStatus.BAD_REQUEST, ValidateUtil.SYSTEM + " mnemonic path duplicate", mnemonicpath);
+            } else {
+                validateCondition(!StringUtils.equals(namingConvention.equivalenceClassRepresentative(path[0]), namingConvention.equivalenceClassRepresentative(path[1])), HttpStatus.BAD_REQUEST, ValidateUtil.SYSTEM + " mnemonic path duplicate", mnemonicpath);
+                validateCondition(!StringUtils.equals(namingConvention.equivalenceClassRepresentative(path[0]), namingConvention.equivalenceClassRepresentative(path[2])), HttpStatus.BAD_REQUEST, ValidateUtil.SYSTEM + " mnemonic path duplicate", mnemonicpath);
+                validateCondition(!StringUtils.equals(namingConvention.equivalenceClassRepresentative(path[1]), namingConvention.equivalenceClassRepresentative(path[2])), HttpStatus.BAD_REQUEST, ValidateUtil.SYSTEM + " mnemonic path duplicate", mnemonicpath);
+            }
+            String mnemonicpathEquivalence = namingConvention.equivalenceClassRepresentative(mnemonicpath);
+            mnemonicPaths = StructureUtil.getMnemonicPathsSubsystem(holder, true, namingConvention);
+            for (String existingPath : mnemonicPaths) {
+                validateCondition(!StringUtils.equals(mnemonicpathEquivalence, namingConvention.equivalenceClassRepresentative(existingPath)), HttpStatus.BAD_REQUEST, ValidateUtil.SYSTEM + " mnemonic path equivalence duplicate", mnemonicpath);
+            }
+        } else if (Type.DISCIPLINE.equals(type)) {
+            validateCondition(path.length == 1, HttpStatus.BAD_REQUEST, ValidateUtil.DISCIPLINE + " mnemonic path " + ValidateUtil.IS_NOT_VALID, path[0] + ", " + mnemonicpath);
+
+            // mnemonic
+            Discipline di = holderIRepositories.getDisciplineRepository().findLatestNotDeletedByMnemonic(path[0]);
+            ValidateUtil.validateCondition(di == null, HttpStatus.BAD_REQUEST, ValidateUtil.DISCIPLINE + " mnemonic path duplicate", path[0] + ", " + mnemonicpath);
+
+            // mnemonic equivalence
+            List<Discipline> disciplines = holderIRepositories.getDisciplineRepository().findLatestNotDeleted();
+            for (Discipline discipline : disciplines) {
+                validateCondition(!StringUtils.equals(discipline.getMnemonicEquivalence(), namingConvention.equivalenceClassRepresentative(path[0])),
+                        HttpStatus.BAD_REQUEST, ValidateUtil.DISCIPLINE + " mnemonic path equivalence duplicate", path[0] + ", " + mnemonicpath);
+            }
+        } else if (Type.DEVICEGROUP.equals(type)) {
+            throw ExceptionUtil.createServiceHttpStatusExceptionBadRequest(ValidateUtil.DEVICEGROUP + " mnemonic path " + ValidateUtil.IS_NOT_VALID, path.length + ", " + mnemonicpath, null);
+        } else if (Type.DEVICETYPE.equals(type)) {
+            validateCondition(path.length == 2, HttpStatus.BAD_REQUEST, ValidateUtil.STRUCTURE + " " + ValidateUtil.DEVICETYPE + " mnemonic path " + ValidateUtil.IS_NOT_VALID, path.length + ", " + mnemonicpath);
+
+            // discipline
+            Discipline discipline = holderIRepositories.getDisciplineRepository().findLatestNotDeletedByMnemonic(path[0]);
+            ValidateUtil.validateCondition(discipline != null, HttpStatus.BAD_REQUEST, ValidateUtil.DISCIPLINE + " " + ValidateUtil.IS_NOT_VALID, path[0] + ", " + mnemonicpath);
+
+            // mnemonic
+            if (path.length == 2) {
+                validateCondition(!StringUtils.equals(path[0], path[1]), HttpStatus.BAD_REQUEST, ValidateUtil.SYSTEM + " mnemonic path duplicate", mnemonicpath);
+            }
+
+            // mnemonic equivalence
+            validateCondition(!StringUtils.equals(namingConvention.equivalenceClassRepresentative(path[0]), namingConvention.equivalenceClassRepresentative(path[1])), HttpStatus.BAD_REQUEST, ValidateUtil.SYSTEM + " mnemonic path duplicate", mnemonicpath);
+
+            // since device group is between discipline and device type and device group has no mnemonic,
+            //     it can not be traced to which device group that this device type belongs,
+            //     therefore it can not be known tO which mnemonic line it belongs
+            // therefore this method considered ok as long as mnemonic not empty and mnemonic not duplicate
+            // this is ensured by path length 2
+            // rest of checks in validatecreate
+        }
+    }
+
+    // ----------------------------------------------------------------------------------------------------
+
+    /**
+     * Validate condition and throw ServiceHttpStatusException with reason if validation fails.
+     *
+     * @param condition condition
+     * @param status http status
+     * @param message message
+     * @param details details
+     *
+     * @see ServiceHttpStatusException
+     */
+    public static void validateCondition(boolean condition, HttpStatus status, String message, String details) {
+        if (!condition) {
+            switch(status) {
+                case BAD_REQUEST:
+                    throw ExceptionUtil.createServiceHttpStatusExceptionBadRequest(message, details, null);
+                default:
+                    throw ExceptionUtil.createServiceHttpStatusExceptionInternalServerError(message, details, null);
+            }
+        }
+    }
+
+    /**
+     * Validate condition if precondition is fulfilled and throw ServiceHttpStatusException with reason if validation fails.
+     *
+     * @param precondition precondition
+     * @param condition condition
+     * @param status http status
+     * @param message message
+     * @param details details
+     *
+     * @see ServiceHttpStatusException
+     */
+    public static void validateConditionIfPrecondition(boolean precondition, boolean condition, HttpStatus status, String message, String details) {
+        if (precondition) {
+            validateCondition(condition, status, message, details);
+        }
+    }
+
+    /**
+     * Validate condition if precondition (StructureChoice) is fulfilled and throw ServiceHttpStatusException with reason if validation fails.
+     *
+     * @param expected expected structure choice
+     * @param actual actual structure choice
+     * @param condition condition
+     * @param status http status
+     * @param message message
+     * @param details details
+     *
+     * @see ServiceHttpStatusException
+     */
+    public static void validateConditionIfStructureChoice(StructureChoice expected, StructureChoice actual, boolean condition, HttpStatus status, String message, String details) {
+        validateConditionIfPrecondition(expected != null && expected.equals(actual), condition, status, message, details);
+    }
+
+
+
+}
diff --git a/src/test/java/org/openepics/names/util/EssNamingConventionTest.java b/src/test/java/org/openepics/names/util/EssNamingConventionTest.java
new file mode 100644
index 0000000..384d0c7
--- /dev/null
+++ b/src/test/java/org/openepics/names/util/EssNamingConventionTest.java
@@ -0,0 +1,988 @@
+/*
+* Copyright (c) 2014 European Spallation Source
+* Copyright (c) 2014 Cosylab d.d.
+*
+* This file is part of Naming Service.
+* Naming Service is free software: you can redistribute it and/or modify it under
+* the terms of the GNU General Public License as published by the Free
+* Software Foundation, either version 2 of the License, or any newer version.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+* FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+* more details.
+*
+* You should have received a copy of the GNU General Public License along with
+* this program. If not, see https://www.gnu.org/licenses/gpl-2.0.txt
+*/
+package org.openepics.names.util;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Unit tests for EssNamingConvention class.
+ *
+ * @author Karin Rathsman
+ * @author Lars Johansson
+ *
+ * @see NamingConvention
+ * @see EssNamingConvention
+ */
+public class EssNamingConventionTest {
+
+    // ----------------------------------------------------------------------------------------------------
+    // table of content
+    // ----------------------------------------------------------------------------------------------------
+    // name by itself
+    //     validateMnemonic
+    //         system structure
+    //         device structure
+    //     validateMnemonic (2)
+    //         system structure
+    //         device structure
+    //     isInstanceIndexValid
+    //         device
+    //     isInstanceIndexValid (2)
+    //         device
+    //     equivalenceClassRepresentative
+    //     conventionName
+    // name in relation to other
+    //     canMnemonicsCoexist
+    //         system structure
+    //         device structure
+    //         system structure & device structure
+    //     canMnemonicsCoexist (2)
+    //         system structure
+    //         device structure
+    //         system structure & device structure
+    // ----------------------------------------------------------------------------------------------------
+
+    private static final String EMPTY = "";
+    private static final String NULL  = null;
+    private static final String SPACE = " ";
+
+    private static final String A = "A";
+
+    private static final String SUP = "Sup";
+    private static final String SYS = "Sys";
+    private static final String SUB = "Sub";
+    private static final String DIS = "Dis";
+    private static final String DEV = "Dev";
+    private static final String IDX = "Idx";
+
+    private static final String ECAT10 = "ECAT10";
+    private static final String IOC    = "IOC";
+    private static final String TDS    = "TDS";
+    private static final String TD180  = "TD180";
+
+    private static final String NUM_0         = "0";
+    private static final String NUM_1         = "1";
+    private static final String NUM_12        = "12";
+    private static final String NUM_123       = "123";
+    private static final String NUM_1234      = "1234";
+    private static final String NUM_12345     = "12345";
+    private static final String NUM_123456    = "123456";
+    private static final String NUM_1234567   = "1234567";
+
+    private static final String ALPHABETIC_NOT_OK       = "alphabetic value not allowed";
+    private static final String BLANKS_NOT_OK           = "value with blanks not allowed";
+    private static final String EMPTY_NOT_OK            = "empty value not allowed";
+    private static final String LENGTH_NOT_OK           = "length of value not allowed";
+    private static final String LENGTH_OK               = "length of value allowed";
+    private static final String NON_ALPHANUMERIC_NOT_OK = "non-alphanumeric value not allowed";
+    private static final String NUMERIC_OK              = "numeric value allowed";
+    private static final String NULL_NOT_OK             = "null value not allowed";
+    private static final String ONLY_ZEROS_NOT_OK       = "value with only zeros not allowed";
+
+    private static EssNamingConvention namingConvention;
+
+    /**
+     * One-time initialization code.
+     */
+    @BeforeAll
+    public static void oneTimeSetUp() {
+        namingConvention = new EssNamingConvention();
+    }
+
+    /**
+     * One-time cleanup code.
+     */
+    @AfterAll
+    public static void oneTimeTearDown() {
+        namingConvention = null;
+    }
+
+    // ----------------------------------------------------------------------------------------------------
+    // name by itself
+    //     isInstanceIndexValid
+    //         device
+    //         device override
+    //         device PID
+    //         device PID override
+    // ----------------------------------------------------------------------------------------------------
+
+    /**
+     * Test validity of instance index for device.
+     */
+    @Test
+    public void isInstanceIndexValid() {
+        String conventionNameBase = SYS + "-" + SUB + ":" + DIS + "-" + DEV;
+
+        assertFalse(namingConvention.isInstanceIndexValid(NULL),                                   NULL_NOT_OK);
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase),                     EMPTY_NOT_OK);
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + EMPTY),       EMPTY_NOT_OK);
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + SPACE),       BLANKS_NOT_OK);
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + IDX),         ALPHABETIC_NOT_OK);
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "abcdef"),    ALPHABETIC_NOT_OK);
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + NUM_123),     NUMERIC_OK);
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "0"),         ONLY_ZEROS_NOT_OK);
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "00"),        ONLY_ZEROS_NOT_OK);
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "000"),       ONLY_ZEROS_NOT_OK);
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "0000"),      ONLY_ZEROS_NOT_OK);
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "00000"),     ONLY_ZEROS_NOT_OK);
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "000000"),    ONLY_ZEROS_NOT_OK);
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "0000000"),   ONLY_ZEROS_NOT_OK);
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "abc123"),    ALPHABETIC_NOT_OK);
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "a!"),        NON_ALPHANUMERIC_NOT_OK);
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "01"),        LENGTH_OK);
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "001"),       LENGTH_OK);
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "0001"),      LENGTH_OK);
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "0110"),      LENGTH_OK);
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "10"),        LENGTH_OK);
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "100"),       LENGTH_OK);
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "1000"),      LENGTH_OK);
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + NUM_1),       LENGTH_OK);
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + NUM_12),      LENGTH_OK);
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + NUM_123),     LENGTH_OK);
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + NUM_1234),    LENGTH_OK);
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + NUM_12345),   LENGTH_NOT_OK);
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + NUM_123456),  LENGTH_NOT_OK);
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + NUM_1234567), LENGTH_NOT_OK);
+
+        conventionNameBase = SUP + "-" + SYS + "-" + SUB + ":" + DIS + "-" + DEV;
+
+        assertFalse(namingConvention.isInstanceIndexValid(NULL),                                   NULL_NOT_OK);
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase),                     EMPTY_NOT_OK);
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + EMPTY),       EMPTY_NOT_OK);
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + SPACE),       BLANKS_NOT_OK);
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + IDX),         ALPHABETIC_NOT_OK);
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "abcdef"),    ALPHABETIC_NOT_OK);
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + NUM_123),     NUMERIC_OK);
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "0"),         ONLY_ZEROS_NOT_OK);
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "00"),        ONLY_ZEROS_NOT_OK);
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "000"),       ONLY_ZEROS_NOT_OK);
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "0000"),      ONLY_ZEROS_NOT_OK);
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "00000"),     ONLY_ZEROS_NOT_OK);
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "000000"),    ONLY_ZEROS_NOT_OK);
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "0000000"),   ONLY_ZEROS_NOT_OK);
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "abc123"),    ALPHABETIC_NOT_OK);
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "a!"),        NON_ALPHANUMERIC_NOT_OK);
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "01"),        LENGTH_OK);
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "001"),       LENGTH_OK);
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "0001"),      LENGTH_OK);
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "0110"),      LENGTH_OK);
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "10"),        LENGTH_OK);
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "100"),       LENGTH_OK);
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "1000"),      LENGTH_OK);
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + NUM_1),       LENGTH_OK);
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + NUM_12),      LENGTH_OK);
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + NUM_123),     LENGTH_OK);
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + NUM_1234),    LENGTH_OK);
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + NUM_12345),   LENGTH_NOT_OK);
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + NUM_123456),  LENGTH_NOT_OK);
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + NUM_1234567), LENGTH_NOT_OK);
+    }
+
+    /**
+     * Test validity of instance index for device.
+     */
+    @Test
+    public void isInstanceIndexValidOverride() {
+        // override - length, characters
+
+        String conventionNameBase = SYS + "-" + SUB + ":" + DIS + "-" + DEV;
+
+        assertFalse(namingConvention.isInstanceIndexValid(NULL,                                   true));
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase,                     true));
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + EMPTY,       true));
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + SPACE,       true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + IDX,         true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "abcdef",    true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + NUM_123,     true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "0",         true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "00",        true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "000",       true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "0000",      true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "00000",     true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "000000",    true));
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "0000000",   true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "abc123",    true));
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "a!",        true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "01",        true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "001",       true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "0001",      true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "0110",      true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "10",        true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "100",       true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "1000",      true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + NUM_1,       true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + NUM_12,      true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + NUM_123,     true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + NUM_1234,    true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + NUM_12345,   true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + NUM_123456,  true));
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + NUM_1234567, true));
+
+        conventionNameBase = SUP + "-" + SYS + "-" + SUB + ":" + DIS + "-" + DEV;
+
+        assertFalse(namingConvention.isInstanceIndexValid(NULL,                                   true));
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase,                     true));
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + EMPTY,       true));
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + SPACE,       true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + IDX,         true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "abcdef",    true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + NUM_123,     true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "0",         true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "00",        true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "000",       true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "0000",      true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "00000",     true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "000000",    true));
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "0000000",   true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "abc123",    true));
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "a!",        true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "01",        true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "001",       true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "0001",      true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "0110",      true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "10",        true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "100",       true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + "1000",      true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + NUM_1,       true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + NUM_12,      true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + NUM_123,     true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + NUM_1234,    true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + NUM_12345,   true));
+        assertTrue (namingConvention.isInstanceIndexValid(conventionNameBase + "-" + NUM_123456,  true));
+        assertFalse(namingConvention.isInstanceIndexValid(conventionNameBase + "-" + NUM_1234567, true));
+    }
+
+    /**
+     * Test validity of instance index, for device.
+     */
+    @Test
+    public void isInstanceIndexValidPIDNumeric() {
+        // depend on discipline, device type
+
+        // mnemonic path system structure
+        final String systemStructure  = TDS + "-" + TD180;
+
+        // mnemonic path device structure
+        final String deviceStructure1 = "Mech" + "-" + IOC;
+        final String deviceStructure2 = "Cryo" + "-" + IOC;
+        final String deviceStructure3 = "EMR"  + "-" + IOC;
+        final String deviceStructure4 = "HVAC" + "-" + IOC;
+        final String deviceStructure5 = "Proc" + "-" + IOC;
+        final String deviceStructure6 = "SC"   + "-" + IOC;
+        final String deviceStructure7 = "Vac"  + "-" + IOC;
+        final String deviceStructure8 = "WtrC" + "-" + IOC;
+
+        final String _001a    = "001a";
+        final String _001ab   = "001ab";
+        final String _001abc  = "001abc";
+        final String _001abcd = "001abcd";
+        final String _001A    = "001A";
+        final String _001AB   = "001AB";
+        final String _001ABC  = "001ABC";
+        final String _001ABCD = "001ABCD";
+
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure1));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure2));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure3));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure4));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure5));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure6));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure7));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure8));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure1 + "-" + "000"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure2 + "-" + "000"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure3 + "-" + "000"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure4 + "-" + "000"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure5 + "-" + "000"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure6 + "-" + "000"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure7 + "-" + "000"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure8 + "-" + "000"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure1 + "-" + "000a"));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure2 + "-" + "000a"));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure3 + "-" + "000a"));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure4 + "-" + "000a"));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure5 + "-" + "000a"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure6 + "-" + "000a"));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure7 + "-" + "000a"));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure8 + "-" + "000a"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure1 + "-" + "000A"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure2 + "-" + "000A"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure3 + "-" + "000A"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure4 + "-" + "000A"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure5 + "-" + "000A"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure6 + "-" + "000A"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure7 + "-" + "000A"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure8 + "-" + "000A"));
+
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure1 + "-" + "1"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure2 + "-" + "1"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure3 + "-" + "1"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure4 + "-" + "1"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure5 + "-" + "1"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure6 + "-" + "1"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure7 + "-" + "1"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure8 + "-" + "1"));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure1 + "-" + "01"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure2 + "-" + "01"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure3 + "-" + "01"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure4 + "-" + "01"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure5 + "-" + "01"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure6 + "-" + "01"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure7 + "-" + "01"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure8 + "-" + "01"));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure1 + "-" + "001"));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure2 + "-" + "001"));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure3 + "-" + "001"));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure4 + "-" + "001"));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure5 + "-" + "001"));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure6 + "-" + "001"));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure7 + "-" + "001"));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure8 + "-" + "001"));
+
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure1 + "-" + _001a));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure2 + "-" + _001a));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure3 + "-" + _001a));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure4 + "-" + _001a));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure5 + "-" + _001a));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure6 + "-" + _001a));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure7 + "-" + _001a));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure8 + "-" + _001a));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure1 + "-" + _001ab));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure2 + "-" + _001ab));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure3 + "-" + _001ab));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure4 + "-" + _001ab));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure5 + "-" + _001ab));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure6 + "-" + _001ab));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure7 + "-" + _001ab));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure8 + "-" + _001ab));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure1 + "-" + _001abc));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure2 + "-" + _001abc));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure3 + "-" + _001abc));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure4 + "-" + _001abc));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure5 + "-" + _001abc));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure6 + "-" + _001abc));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure7 + "-" + _001abc));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure8 + "-" + _001abc));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure1 + "-" + _001abcd));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure2 + "-" + _001abcd));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure3 + "-" + _001abcd));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure4 + "-" + _001abcd));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure5 + "-" + _001abcd));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure6 + "-" + _001abcd));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure7 + "-" + _001abcd));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure8 + "-" + _001abcd));
+
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure1 + "-" + _001A));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure2 + "-" + _001A));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure3 + "-" + _001A));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure4 + "-" + _001A));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure5 + "-" + _001A));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure6 + "-" + _001A));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure7 + "-" + _001A));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure8 + "-" + _001A));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure1 + "-" + _001AB));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure2 + "-" + _001AB));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure3 + "-" + _001AB));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure4 + "-" + _001AB));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure5 + "-" + _001AB));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure6 + "-" + _001AB));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure7 + "-" + _001AB));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure8 + "-" + _001AB));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure1 + "-" + _001ABC));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure2 + "-" + _001ABC));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure3 + "-" + _001ABC));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure4 + "-" + _001ABC));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure5 + "-" + _001ABC));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure6 + "-" + _001ABC));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure7 + "-" + _001ABC));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure8 + "-" + _001ABC));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure1 + "-" + _001ABCD));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure2 + "-" + _001ABCD));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure3 + "-" + _001ABCD));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure4 + "-" + _001ABCD));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure5 + "-" + _001ABCD));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure6 + "-" + _001ABCD));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure7 + "-" + _001ABCD));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure + ":" + deviceStructure8 + "-" + _001ABCD));
+    }
+
+    /**
+     * Test validity of instance index, for device.
+     */
+    @Test
+    public void isInstanceIndexValidPID() {
+        // depend on discipline
+
+        // mnemonic paths system structure and device structure
+        final String systemStructure1  = A + "-" + A + "-" + A;
+        final String deviceStructure1  = A + "-" + A;
+
+        // mnemonic paths system structure and device structure
+        final String systemStructure2  = TDS + "-" + TD180;
+        final String deviceStructure21 = "Mech" + "-" + TDS;
+        final String deviceStructure22 = "Cryo" + "-" + TDS;
+        final String deviceStructure23 = "EMR"  + "-" + TDS;
+        final String deviceStructure24 = "HVAC" + "-" + TDS;
+        final String deviceStructure25 = "Proc" + "-" + TDS;
+        final String deviceStructure26 = "SC"   + "-" + TDS;
+        final String deviceStructure27 = "Vac"  + "-" + TDS;
+        final String deviceStructure28 = "WtrC" + "-" + TDS;
+
+        final String _001a    = "001a";
+        final String _001ab   = "001ab";
+        final String _001abc  = "001abc";
+        final String _001abcd = "001abcd";
+        final String _001A    = "001A";
+        final String _001AB   = "001AB";
+        final String _001ABC  = "001ABC";
+        final String _001ABCD = "001ABCD";
+
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure1 + ":" + deviceStructure1));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure1 + ":" + deviceStructure1 + "-" + "0"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure1 + ":" + deviceStructure1 + "-" + "00"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure1 + ":" + deviceStructure1 + "-" + "000"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure1 + ":" + deviceStructure1 + "-" + "0000"));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure1 + ":" + deviceStructure1 + "-" + "1"));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure1 + ":" + deviceStructure1 + "-" + "01"));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure1 + ":" + deviceStructure1 + "-" + "001"));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure1 + ":" + deviceStructure1 + "-" + "001"));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure1 + ":" + deviceStructure1 + "-" + "0001"));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure1 + ":" + deviceStructure1 + "-" + "1001"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure1 + ":" + deviceStructure1 + "-" + "10001"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure1 + ":" + deviceStructure1 + "-" + "abc"));
+
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure21));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure22));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure23));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure24));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure25));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure26));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure27));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure28));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure21 + "-" + "000"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure22 + "-" + "000"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure23 + "-" + "000"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure24 + "-" + "000"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure25 + "-" + "000"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure26 + "-" + "000"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure27 + "-" + "000"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure28 + "-" + "000"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure21 + "-" + "000a"));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure22 + "-" + "000a"));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure23 + "-" + "000a"));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure24 + "-" + "000a"));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure25 + "-" + "000a"));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure26 + "-" + "000a"));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure27 + "-" + "000a"));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure28 + "-" + "000a"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure21 + "-" + "000A"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure22 + "-" + "000A"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure23 + "-" + "000A"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure24 + "-" + "000A"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure25 + "-" + "000A"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure26 + "-" + "000A"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure27 + "-" + "000A"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure28 + "-" + "000A"));
+
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure21 + "-" + "1"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure22 + "-" + "1"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure23 + "-" + "1"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure24 + "-" + "1"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure25 + "-" + "1"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure26 + "-" + "1"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure27 + "-" + "1"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure28 + "-" + "1"));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure21 + "-" + "01"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure22 + "-" + "01"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure23 + "-" + "01"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure24 + "-" + "01"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure25 + "-" + "01"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure26 + "-" + "01"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure27 + "-" + "01"));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure28 + "-" + "01"));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure21 + "-" + "001"));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure22 + "-" + "001"));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure23 + "-" + "001"));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure24 + "-" + "001"));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure25 + "-" + "001"));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure26 + "-" + "001"));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure27 + "-" + "001"));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure28 + "-" + "001"));
+
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure21 + "-" + _001a));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure22 + "-" + _001a));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure23 + "-" + _001a));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure24 + "-" + _001a));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure25 + "-" + _001a));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure26 + "-" + _001a));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure27 + "-" + _001a));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure28 + "-" + _001a));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure21 + "-" + _001ab));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure22 + "-" + _001ab));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure23 + "-" + _001ab));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure24 + "-" + _001ab));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure25 + "-" + _001ab));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure26 + "-" + _001ab));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure27 + "-" + _001ab));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure28 + "-" + _001ab));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure21 + "-" + _001abc));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure22 + "-" + _001abc));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure23 + "-" + _001abc));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure24 + "-" + _001abc));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure25 + "-" + _001abc));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure26 + "-" + _001abc));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure27 + "-" + _001abc));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure28 + "-" + _001abc));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure21 + "-" + _001abcd));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure22 + "-" + _001abcd));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure23 + "-" + _001abcd));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure24 + "-" + _001abcd));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure25 + "-" + _001abcd));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure26 + "-" + _001abcd));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure27 + "-" + _001abcd));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure28 + "-" + _001abcd));
+
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure21 + "-" + _001A));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure22 + "-" + _001A));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure23 + "-" + _001A));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure24 + "-" + _001A));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure25 + "-" + _001A));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure26 + "-" + _001A));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure27 + "-" + _001A));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure28 + "-" + _001A));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure21 + "-" + _001AB));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure22 + "-" + _001AB));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure23 + "-" + _001AB));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure24 + "-" + _001AB));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure25 + "-" + _001AB));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure26 + "-" + _001AB));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure27 + "-" + _001AB));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure28 + "-" + _001AB));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure21 + "-" + _001ABC));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure22 + "-" + _001ABC));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure23 + "-" + _001ABC));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure24 + "-" + _001ABC));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure25 + "-" + _001ABC));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure26 + "-" + _001ABC));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure27 + "-" + _001ABC));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure28 + "-" + _001ABC));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure21 + "-" + _001ABCD));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure22 + "-" + _001ABCD));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure23 + "-" + _001ABCD));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure24 + "-" + _001ABCD));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure25 + "-" + _001ABCD));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure26 + "-" + _001ABCD));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure27 + "-" + _001ABCD));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure28 + "-" + _001ABCD));
+    }
+
+    /**
+     * Test validity of instance index, for device.
+     */
+    @Test
+    public void isInstanceIndexValidPIDOverride() {
+        // depend on discipline
+        // override - length, characters
+
+        // mnemonic paths system structure and device structure
+        final String systemStructure1  = A + "-" + A + "-" + A;
+        final String deviceStructure1  = A + "-" + A;
+
+        // mnemonic paths system structure and device structure
+        final String systemStructure2  = TDS + "-" + TD180;
+        final String deviceStructure21 = "Mech" + "-" + TDS;
+        final String deviceStructure22 = "Cryo" + "-" + TDS;
+        final String deviceStructure23 = "EMR"  + "-" + TDS;
+        final String deviceStructure24 = "HVAC" + "-" + TDS;
+        final String deviceStructure25 = "Proc" + "-" + TDS;
+        final String deviceStructure26 = "SC"   + "-" + TDS;
+        final String deviceStructure27 = "Vac"  + "-" + TDS;
+        final String deviceStructure28 = "WtrC" + "-" + TDS;
+
+        final String _001a    = "001a";
+        final String _001ab   = "001ab";
+        final String _001abc  = "001abc";
+        final String _001abcd = "001abcd";
+        final String _001A    = "001A";
+        final String _001AB   = "001AB";
+        final String _001ABC  = "001ABC";
+        final String _001ABCD = "001ABCD";
+
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure1 + ":" + deviceStructure1,                   true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure1 + ":" + deviceStructure1 + "-" + "0",       true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure1 + ":" + deviceStructure1 + "-" + "00",      true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure1 + ":" + deviceStructure1 + "-" + "000",     true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure1 + ":" + deviceStructure1 + "-" + "0000",    true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure1 + ":" + deviceStructure1 + "-" + "1",       true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure1 + ":" + deviceStructure1 + "-" + "01",      true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure1 + ":" + deviceStructure1 + "-" + "001",     true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure1 + ":" + deviceStructure1 + "-" + "001",     true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure1 + ":" + deviceStructure1 + "-" + "0001",    true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure1 + ":" + deviceStructure1 + "-" + "1001",    true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure1 + ":" + deviceStructure1 + "-" + "10001",   true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure1 + ":" + deviceStructure1 + "-" + "abc",     true));
+
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure21,                  true));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure22,                  true));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure23,                  true));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure24,                  true));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure25,                  true));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure26,                  true));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure27,                  true));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure28,                  true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure21 + "-" + "000",    true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure22 + "-" + "000",    true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure23 + "-" + "000",    true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure24 + "-" + "000",    true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure25 + "-" + "000",    true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure26 + "-" + "000",    true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure27 + "-" + "000",    true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure28 + "-" + "000",    true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure21 + "-" + "000a",   true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure22 + "-" + "000a",   true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure23 + "-" + "000a",   true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure24 + "-" + "000a",   true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure25 + "-" + "000a",   true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure26 + "-" + "000a",   true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure27 + "-" + "000a",   true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure28 + "-" + "000a",   true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure21 + "-" + "000A",   true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure22 + "-" + "000A",   true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure23 + "-" + "000A",   true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure24 + "-" + "000A",   true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure25 + "-" + "000A",   true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure26 + "-" + "000A",   true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure27 + "-" + "000A",   true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure28 + "-" + "000A",   true));
+
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure21 + "-" + "1",      true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure22 + "-" + "1",      true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure23 + "-" + "1",      true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure24 + "-" + "1",      true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure25 + "-" + "1",      true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure26 + "-" + "1",      true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure27 + "-" + "1",      true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure28 + "-" + "1",      true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure21 + "-" + "01",     true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure22 + "-" + "01",     true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure23 + "-" + "01",     true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure24 + "-" + "01",     true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure25 + "-" + "01",     true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure26 + "-" + "01",     true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure27 + "-" + "01",     true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure28 + "-" + "01",     true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure21 + "-" + "001",    true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure22 + "-" + "001",    true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure23 + "-" + "001",    true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure24 + "-" + "001",    true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure25 + "-" + "001",    true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure26 + "-" + "001",    true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure27 + "-" + "001",    true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure28 + "-" + "001",    true));
+
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure21 + "-" + _001a,    true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure22 + "-" + _001a,    true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure23 + "-" + _001a,    true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure24 + "-" + _001a,    true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure25 + "-" + _001a,    true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure26 + "-" + _001a,    true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure27 + "-" + _001a,    true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure28 + "-" + _001a,    true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure21 + "-" + _001ab,   true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure22 + "-" + _001ab,   true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure23 + "-" + _001ab,   true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure24 + "-" + _001ab,   true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure25 + "-" + _001ab,   true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure26 + "-" + _001ab,   true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure27 + "-" + _001ab,   true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure28 + "-" + _001ab,   true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure21 + "-" + _001abc,  true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure22 + "-" + _001abc,  true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure23 + "-" + _001abc,  true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure24 + "-" + _001abc,  true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure25 + "-" + _001abc,  true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure26 + "-" + _001abc,  true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure27 + "-" + _001abc,  true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure28 + "-" + _001abc,  true));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure21 + "-" + _001abcd, true));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure22 + "-" + _001abcd, true));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure23 + "-" + _001abcd, true));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure24 + "-" + _001abcd, true));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure25 + "-" + _001abcd, true));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure26 + "-" + _001abcd, true));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure27 + "-" + _001abcd, true));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure28 + "-" + _001abcd, true));
+
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure21 + "-" + _001A,    true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure22 + "-" + _001A,    true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure23 + "-" + _001A,    true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure24 + "-" + _001A,    true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure25 + "-" + _001A,    true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure26 + "-" + _001A,    true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure27 + "-" + _001A,    true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure28 + "-" + _001A,    true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure21 + "-" + _001AB,   true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure22 + "-" + _001AB,   true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure23 + "-" + _001AB,   true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure24 + "-" + _001AB,   true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure25 + "-" + _001AB,   true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure26 + "-" + _001AB,   true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure27 + "-" + _001AB,   true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure28 + "-" + _001AB,   true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure21 + "-" + _001ABC,  true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure22 + "-" + _001ABC,  true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure23 + "-" + _001ABC,  true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure24 + "-" + _001ABC,  true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure25 + "-" + _001ABC,  true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure26 + "-" + _001ABC,  true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure27 + "-" + _001ABC,  true));
+        assertTrue (namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure28 + "-" + _001ABC,  true));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure21 + "-" + _001ABCD, true));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure22 + "-" + _001ABCD, true));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure23 + "-" + _001ABCD, true));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure24 + "-" + _001ABCD, true));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure25 + "-" + _001ABCD, true));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure26 + "-" + _001ABCD, true));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure27 + "-" + _001ABCD, true));
+        assertFalse(namingConvention.isInstanceIndexValid(systemStructure2 + ":" + deviceStructure28 + "-" + _001ABCD, true));
+    }
+
+    // ----------------------------------------------------------------------------------------------------
+    // name by itself
+    //     equivalenceClassRepresentative
+    // ----------------------------------------------------------------------------------------------------
+
+    /**
+     * Test of equivalence class representative method.
+     */
+    @Test
+    public void equivalenceClassRepresentative() {
+        assertEquals("RFQ-10:EMR-TT-1",  namingConvention.equivalenceClassRepresentative("RFQ-010:EMR-TT-001"));
+        assertEquals("RFQ-10:EMR-TT-10", namingConvention.equivalenceClassRepresentative("RFQ-010:EMR-TT-010"));
+        assertEquals("RFQ-10:EMR-TT-11", namingConvention.equivalenceClassRepresentative("RFQ-010:EMR-TT-011"));
+        assertEquals("RFQ-10:EMR-TT-12", namingConvention.equivalenceClassRepresentative("RFQ-010:EMR-TT-012"));
+        assertEquals("RFQ-10:EMR-TT-13", namingConvention.equivalenceClassRepresentative("RFQ-010:EMR-TT-013"));
+        assertEquals("RFQ-10:EMR-TT-14", namingConvention.equivalenceClassRepresentative("RFQ-010:EMR-TT-014"));
+        assertEquals("RFQ-10:EMR-TT-15", namingConvention.equivalenceClassRepresentative("RFQ-010:EMR-TT-015"));
+        assertEquals("RFQ-10:EMR-TT-16", namingConvention.equivalenceClassRepresentative("RFQ-010:EMR-TT-016"));
+        assertEquals("RFQ-10:EMR-TT-17", namingConvention.equivalenceClassRepresentative("RFQ-010:EMR-TT-017"));
+        assertEquals("RFQ-10:EMR-TT-18", namingConvention.equivalenceClassRepresentative("RFQ-010:EMR-TT-018"));
+        assertEquals("RFQ-10:EMR-TT-19", namingConvention.equivalenceClassRepresentative("RFQ-010:EMR-TT-019"));
+
+        assertEquals(
+                "PB1-FPM1:CTR1-ECAT-100",
+                namingConvention.equivalenceClassRepresentative("PBI-FPM01:Ctrl-ECAT-100"));
+        assertEquals(
+                "PB1-FPM1:CTR1-ECATC-101",
+                namingConvention.equivalenceClassRepresentative("PBI-FPM01:Ctrl-ECATC-101"));
+        assertEquals(
+                "PB1-FPM1:CTR1-ECATE-101",
+                namingConvention.equivalenceClassRepresentative("PBI-FPM01:Ctrl-ECATE-101"));
+        assertEquals(
+                "PB1-FPM1:CTR1-ECAT10-101",
+                namingConvention.equivalenceClassRepresentative("PBI-FPM01:Ctrl-ECATIO-101"));
+        assertEquals(
+                "PB1-FPM1:CTR1-ECAT10-102",
+                namingConvention.equivalenceClassRepresentative("PBI-FPM01:Ctrl-ECATIO-102"));
+        assertEquals(
+                "PB1-FPM1:CTR1-ECAT10-103",
+                namingConvention.equivalenceClassRepresentative("PBI-FPM01:Ctrl-ECATIO-103"));
+        assertEquals(
+                "PB1-FPM1:CTR1-ECAT10-104",
+                namingConvention.equivalenceClassRepresentative("PBI-FPM01:Ctrl-ECATIO-104"));
+        assertEquals(
+                "PB1-FPM1:CTR1-EVR-101",
+                namingConvention.equivalenceClassRepresentative("PBI-FPM01:Ctrl-EVR-101"));
+        assertEquals(
+                "PB1-FPM1:CTR1-EVR-201",
+                namingConvention.equivalenceClassRepresentative("PBI-FPM01:Ctrl-EVR-201"));
+        assertEquals(
+                "PB1-FPM1:CTR1-1PC-100",
+                namingConvention.equivalenceClassRepresentative("PBI-FPM01:Ctrl-IPC-100"));
+        assertEquals(
+                "PB1-FPM1:CTR1-1PC-200",
+                namingConvention.equivalenceClassRepresentative("PBI-FPM01:Ctrl-IPC-200"));
+    }
+
+    /**
+     * Test of equivalence class representative method for similarity to <tt>1</tt>.
+     */
+    @Test
+    public void equivalenceClassRepresentativeSimilarTo1() {
+        assertEquals(
+                namingConvention.equivalenceClassRepresentative(NUM_1),
+                namingConvention.equivalenceClassRepresentative(NUM_1));
+        assertEquals(
+                namingConvention.equivalenceClassRepresentative(NUM_1),
+                namingConvention.equivalenceClassRepresentative("I"));
+        assertEquals(
+                namingConvention.equivalenceClassRepresentative(NUM_1),
+                namingConvention.equivalenceClassRepresentative("l"));
+        assertEquals(
+                namingConvention.equivalenceClassRepresentative(NUM_1),
+                namingConvention.equivalenceClassRepresentative("L"));
+        assertEquals(
+                namingConvention.equivalenceClassRepresentative(NUM_1),
+                namingConvention.equivalenceClassRepresentative("i"));
+        assertFalse(
+                namingConvention.equivalenceClassRepresentative(NUM_1).equals(
+                        namingConvention.equivalenceClassRepresentative("b")));
+    }
+
+    /**
+     * Test of equivalence class representative method for similarity to <tt>0</tt>.
+     */
+    @Test
+    public void equivalenceClassRepresentativeSimilarTo0() {
+        assertEquals(
+                namingConvention.equivalenceClassRepresentative(NUM_0),
+                namingConvention.equivalenceClassRepresentative("o"));
+        assertEquals(
+                namingConvention.equivalenceClassRepresentative(NUM_0),
+                namingConvention.equivalenceClassRepresentative("O"));
+        assertEquals(
+                namingConvention.equivalenceClassRepresentative(NUM_0),
+                namingConvention.equivalenceClassRepresentative(NUM_0));
+        assertFalse(
+                namingConvention.equivalenceClassRepresentative(NUM_0).equals(
+                        namingConvention.equivalenceClassRepresentative("b")));
+    }
+
+    /**
+     * Test of equivalence class representative method for similarity to <tt>V</tt>.
+     */
+    @Test
+    public void equivalenceClassRepresentativeSimilarToV() {
+        assertEquals(
+                namingConvention.equivalenceClassRepresentative("V"),
+                namingConvention.equivalenceClassRepresentative("v"));
+        assertEquals(
+                namingConvention.equivalenceClassRepresentative("V"),
+                namingConvention.equivalenceClassRepresentative("V"));
+        assertFalse(
+                namingConvention.equivalenceClassRepresentative("V1").equals(
+                        namingConvention.equivalenceClassRepresentative("w1")));
+        assertFalse(
+                namingConvention.equivalenceClassRepresentative("V1").equals(
+                        namingConvention.equivalenceClassRepresentative("W1")));
+        assertFalse(
+                namingConvention.equivalenceClassRepresentative("V").equals(
+                        namingConvention.equivalenceClassRepresentative("w")));
+        assertFalse(
+                namingConvention.equivalenceClassRepresentative("V").equals(
+                        namingConvention.equivalenceClassRepresentative("W")));
+        assertFalse(
+                namingConvention.equivalenceClassRepresentative("V").equals(
+                        namingConvention.equivalenceClassRepresentative("b")));
+    }
+
+    /**
+     * Test of equivalence class representative method for lower and upper case characters.
+     */
+    @Test
+    public void equivalenceClassRepresentativeLowerAndUpperCaseCharacters() {
+        assertEquals(
+                namingConvention.equivalenceClassRepresentative("tEsTS"),
+                namingConvention.equivalenceClassRepresentative("TeSts"));
+    }
+
+    /**
+     * Test of equivalence class representative method for zero prefixed number.
+     */
+    @Test
+    public void equivalenceClassRepresentativeZeroPrefixedNumber() {
+        assertEquals(
+                namingConvention.equivalenceClassRepresentative("01"),
+                namingConvention.equivalenceClassRepresentative("001"));
+        assertEquals(
+                namingConvention.equivalenceClassRepresentative("zero01"),
+                namingConvention.equivalenceClassRepresentative("zero1"));
+        assertEquals(
+                namingConvention.equivalenceClassRepresentative("ze01ro"),
+                namingConvention.equivalenceClassRepresentative("ze1ro"));
+    }
+
+    /**
+     * Test of equivalence class representative method for zero after alpha character.
+     */
+    @Test
+    public void equivalenceClassRepresentativeZeroAfterAlphaCharacter() {
+        assertEquals(
+                namingConvention.equivalenceClassRepresentative("Sub0001"),
+                namingConvention.equivalenceClassRepresentative("Sub1"));
+        assertEquals(
+                namingConvention.equivalenceClassRepresentative("01Sub001"),
+                namingConvention.equivalenceClassRepresentative("01Sub1"));
+    }
+
+    /**
+     * Test of equivalence class representative method for chained calls to get equivalence class.
+     */
+    @Test
+    public void equivalenceClassRepresentativeTwiceEquivalence() {
+        // note that of 2 chained calls to equivalenceClassRepresentative, 2nd call may modify
+        // <==> 2nd output not same as 1st output
+        assertEquals(
+                "CRS-T1CP:CRY0-XZ-XXXXX1",
+                namingConvention.equivalenceClassRepresentative("CrS-TICP:Cryo-XZ-XXXXX1"));
+        assertEquals(
+                "CRS-T1CP:CRY-XZ-XXXXX1",
+                namingConvention.equivalenceClassRepresentative("CRS-T1CP:CRY0-XZ-XXXXX1"));
+        assertEquals("CRY0",   namingConvention.equivalenceClassRepresentative("Cryo"));
+        assertEquals("CRY",    namingConvention.equivalenceClassRepresentative("CRY0"));
+    }
+
+    /**
+     * Test of equivalence class representative method for device type.
+     */
+    @Test
+    public void equivalenceClassRepresentativeDeviceType() {
+        // note handling of trailing zeroes
+        assertEquals(ECAT10,   namingConvention.equivalenceClassRepresentative("ECATIO"));
+        assertEquals("ECAT1",  namingConvention.equivalenceClassRepresentative("ECATI0"));
+        assertEquals(ECAT10,   namingConvention.equivalenceClassRepresentative(ECAT10));
+    }
+
+    /**
+     * Test validity of mnemonic path.
+     */
+    @Test
+    public void isMnemonicPathValid() {
+        assertFalse(namingConvention.isMnemonicPathValid(NULL));
+        assertFalse(namingConvention.isMnemonicPathValid(EMPTY));
+        assertFalse(namingConvention.isMnemonicPathValid(SPACE));
+
+        assertTrue (namingConvention.isMnemonicPathValid("A"));
+        assertTrue (namingConvention.isMnemonicPathValid("A-B"));
+        assertTrue (namingConvention.isMnemonicPathValid("A-B-C"));
+        assertFalse(namingConvention.isMnemonicPathValid("A-B-C-D"));
+
+        assertFalse(namingConvention.isMnemonicPathValid("A-A"));
+        assertFalse(namingConvention.isMnemonicPathValid("A-A-A"));
+        assertFalse(namingConvention.isMnemonicPathValid("A-A-B"));
+        assertFalse(namingConvention.isMnemonicPathValid("A-B-A"));
+        assertFalse(namingConvention.isMnemonicPathValid("B-A-A"));
+    }
+
+}
diff --git a/src/test/java/org/openepics/names/util/NameElementUtilTest.java b/src/test/java/org/openepics/names/util/NameElementUtilTest.java
new file mode 100644
index 0000000..569d3e0
--- /dev/null
+++ b/src/test/java/org/openepics/names/util/NameElementUtilTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2021 European Spallation Source ERIC.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+package org.openepics.names.util;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import org.junit.jupiter.api.Test;
+import org.openepics.names.repository.model.Name;
+import org.openepics.names.rest.beans.NameElement;
+import org.openepics.names.rest.beans.Status;
+
+/**
+ * Unit tests for NameElementUtil class.
+ *
+ * @author Lars Johansson
+ *
+ * @see NameElementUtil
+ */
+public class NameElementUtilTest {
+
+    /**
+     * Test of get name element for name.
+     */
+    @Test
+    public void getNameElement() {
+        NameElement nameElement = NameElementUtil.getNameElement(null);
+        assertNull(nameElement);
+
+        Name name = new Name();
+        nameElement = NameElementUtil.getNameElement(name);
+
+        assertNotNull(nameElement);
+        assertEquals(null, nameElement.getUuid());
+        assertEquals(null, nameElement.getSystemgroup());
+        assertEquals(null, nameElement.getSystem());
+        assertEquals(null, nameElement.getSubsystem());
+        assertEquals(null, nameElement.getDevicetype());
+        assertEquals(null, nameElement.getSystemstructure());
+        assertEquals(null, nameElement.getDevicestructure());
+        assertEquals(null, nameElement.getIndex());
+        assertEquals(null, nameElement.getName());
+        assertEquals(null, nameElement.getDescription());
+        assertEquals(Status.APPROVED, nameElement.getStatus());
+        assertEquals(null, nameElement.isLatest());
+        assertEquals(null, nameElement.isDeleted());
+        assertEquals(null, nameElement.getWhen());
+        assertEquals(null, nameElement.getWho());
+        assertEquals(null, nameElement.getComment());
+    }
+
+    /**
+     * Test of get name element for content.
+     */
+    @Test
+    public void getNameElementContent() {
+        NameElement nameElement = NameElementUtil.getNameElement(
+                null,
+                null, null, null, null,
+                null, null,
+                null, null,
+                null, null, null, null,
+                null, null, null);
+
+        assertNotNull(nameElement);
+        assertEquals(null, nameElement.getUuid());
+        assertEquals(null, nameElement.getSystemgroup());
+        assertEquals(null, nameElement.getSystem());
+        assertEquals(null, nameElement.getSubsystem());
+        assertEquals(null, nameElement.getDevicetype());
+        assertEquals(null, nameElement.getSystemstructure());
+        assertEquals(null, nameElement.getDevicestructure());
+        assertEquals(null, nameElement.getIndex());
+        assertEquals(null, nameElement.getName());
+        assertEquals(null, nameElement.getDescription());
+        assertEquals(null, nameElement.getStatus());
+        assertEquals(null, nameElement.isLatest());
+        assertEquals(null, nameElement.isDeleted());
+        assertEquals(null, nameElement.getWhen());
+        assertEquals(null, nameElement.getWho());
+        assertEquals(null, nameElement.getComment());
+    }
+
+}
diff --git a/src/test/java/org/openepics/names/util/StructureElementUtilTest.java b/src/test/java/org/openepics/names/util/StructureElementUtilTest.java
new file mode 100644
index 0000000..b0fca7e
--- /dev/null
+++ b/src/test/java/org/openepics/names/util/StructureElementUtilTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2021 European Spallation Source ERIC.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+package org.openepics.names.util;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+import org.openepics.names.rest.beans.StructureElement;
+
+/**
+ * Unit tests for StructureElementUtil class.
+ *
+ * @author Lars Johansson
+ *
+ * @see StructureElementUtil
+ */
+public class StructureElementUtilTest {
+
+    /**
+     * Test of get structure element for content.
+     */
+    @Test
+    public void getStructureElementContent() {
+        StructureElement structureElement = StructureElementUtil.getStructureElement(
+                null,
+                null,
+                null,
+                null, null, null, null,
+                null, null, null, null,
+                null, null, null);
+
+        assertNotNull(structureElement);
+        assertEquals(null, structureElement.getType());
+        assertEquals(null, structureElement.getUuid());
+        assertEquals(null, structureElement.getParent());
+        assertEquals(null, structureElement.getName());
+        assertEquals(null, structureElement.getMnemonic());
+        assertEquals(null, structureElement.getMnemonicpath());
+        assertEquals(null, structureElement.getLevel());
+        assertEquals(null, structureElement.getDescription());
+        assertEquals(null, structureElement.getStatus());
+        assertEquals(null, structureElement.isLatest());
+        assertEquals(null, structureElement.isDeleted());
+        assertEquals(null, structureElement.getWhen());
+        assertEquals(null, structureElement.getWho());
+        assertEquals(null, structureElement.getComment());
+    }
+
+    @Test
+    public void equals() {
+        StructureElement se1 = new StructureElement();
+        StructureElement se2 = new StructureElement();
+
+        //        Field[] fields = se1.getClass().getFields();
+        //        Field[] declaredFields = se1.getClass().getDeclaredFields();
+        //
+        //        Field[] superclass_fields = se1.getClass().getSuperclass().getFields();
+        //        Field[] superclass_declaredFields = se1.getClass().getSuperclass().getDeclaredFields();
+        //
+        //        for (Field field : fields) {
+        //            System.out.println("fields.field.getName:                    " + field.getName());
+        //        }
+        //        System.out.println("-----------------------------------------");
+        //        for (Field field : declaredFields) {
+        //            System.out.println("declaredFields.field.getName:            " + field.getName());
+        //        }
+        //        System.out.println("-----------------------------------------");
+        //        for (Field field : superclass_fields) {
+        //            System.out.println("superclass_fields.field.getName:         " + field.getName());
+        //        }
+        //        // LOGGER.log(Level.INFO, "-----------------------------------------");
+        //        System.out.println("-----------------------------------------");
+        //        for (Field field : superclass_declaredFields) {
+        //            System.out.println("superclass_declaredFields.field.getName: " + field.getName());
+        //        }
+
+        assertTrue(se1.equals(se2));
+    }
+
+}
diff --git a/src/test/java/org/openepics/names/util/ValidateUtilTest.java b/src/test/java/org/openepics/names/util/ValidateUtilTest.java
new file mode 100644
index 0000000..cf32a4a
--- /dev/null
+++ b/src/test/java/org/openepics/names/util/ValidateUtilTest.java
@@ -0,0 +1,407 @@
+/*
+ * Copyright (c) 2021 European Spallation Source ERIC.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+package org.openepics.names.util;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.util.List;
+import java.util.UUID;
+
+import org.junit.jupiter.api.Test;
+import org.openepics.names.repository.model.Subsystem;
+import org.openepics.names.rest.beans.Type;
+import org.springframework.http.HttpStatus;
+
+import com.google.common.collect.Lists;
+
+/**
+ * Unit tests for ValidateUtil class.
+ *
+ * @author Lars Johansson
+ *
+ * @see ValidateUtil
+ */
+public class ValidateUtilTest {
+
+    /**
+     * Test of validate input comment.
+     */
+    @Test
+    public void validateInputCommentNull() {
+        try {
+            ValidateUtil.validateInputComment(null);
+            fail();
+        } catch (ServiceHttpStatusException e) {
+            assertEquals(HttpStatus.BAD_REQUEST, e.getHttpStatus());
+            assertEquals("comment is not available", e.getMessage());
+            assertEquals(null, e.getCause());
+        }
+    }
+    /**
+     * Test of validate input comment.
+     */
+    @Test
+    public void validateInputCommentEmpty() {
+        try {
+            ValidateUtil.validateInputComment("");
+            fail();
+        } catch (ServiceHttpStatusException e) {
+            assertEquals(HttpStatus.BAD_REQUEST, e.getHttpStatus());
+            assertEquals("comment is not available", e.getMessage());
+            assertEquals(null, e.getCause());
+        }
+    }
+    /**
+     * Test of validate input comment.
+     */
+    @Test
+    public void validateInputComment() {
+        ValidateUtil.validateInputComment("asdf");
+    }
+
+    /**
+     * Test of validate input description.
+     */
+    @Test
+    public void validateInputDescriptionNull() {
+        try {
+            ValidateUtil.validateInputDescription(null);
+            fail();
+        } catch (ServiceHttpStatusException e) {
+            assertEquals(HttpStatus.BAD_REQUEST, e.getHttpStatus());
+            assertEquals("description is not available", e.getMessage());
+            assertEquals(null, e.getCause());
+        }
+    }
+    /**
+     * Test of validate input description.
+     */
+    @Test
+    public void validateInputDescriptionEmpty() {
+        try {
+            ValidateUtil.validateInputDescription("");
+            fail();
+        } catch (ServiceHttpStatusException e) {
+            assertEquals(HttpStatus.BAD_REQUEST, e.getHttpStatus());
+            assertEquals("description is not available", e.getMessage());
+            assertEquals(null, e.getCause());
+        }
+    }
+    /**
+     * Test of validate input description.
+     */
+    @Test
+    public void validateInputDescription() {
+        ValidateUtil.validateInputDescription("asdf");
+    }
+
+    /**
+     * Test of validate input mnemonic.
+     */
+    @Test
+    public void validateInputMnemonicNull() {
+        try {
+            ValidateUtil.validateInputMnemonic(null);
+            fail();
+        } catch (ServiceHttpStatusException e) {
+            assertEquals(HttpStatus.BAD_REQUEST, e.getHttpStatus());
+            assertEquals("mnemonic is not available", e.getMessage());
+            assertEquals(null, e.getCause());
+        }
+    }
+    /**
+     * Test of validate input mnemonic.
+     */
+    @Test
+    public void validateInputMnemonicEmpty() {
+        try {
+            ValidateUtil.validateInputMnemonic("");
+            fail();
+        } catch (ServiceHttpStatusException e) {
+            assertEquals(HttpStatus.BAD_REQUEST, e.getHttpStatus());
+            assertEquals("mnemonic is not available", e.getMessage());
+            assertEquals(null, e.getCause());
+        }
+    }
+    /**
+     * Test of validate input mnemonic.
+     */
+    @Test
+    public void validateInputMnemonic() {
+        ValidateUtil.validateInputMnemonic("asdf");
+    }
+
+    /**
+     * Test of validate input name.
+     */
+    @Test
+    public void validateInputNameNull() {
+        try {
+            ValidateUtil.validateInputName(null);
+            fail();
+        } catch (ServiceHttpStatusException e) {
+            assertEquals(HttpStatus.BAD_REQUEST, e.getHttpStatus());
+            assertEquals("name is not available", e.getMessage());
+            assertEquals(null, e.getCause());
+        }
+    }
+    /**
+     * Test of validate input name.
+     */
+    @Test
+    public void validateInputNameEmpty() {
+        try {
+            ValidateUtil.validateInputName("");
+            fail();
+        } catch (ServiceHttpStatusException e) {
+            assertEquals(HttpStatus.BAD_REQUEST, e.getHttpStatus());
+            assertEquals("name is not available", e.getMessage());
+            assertEquals(null, e.getCause());
+        }
+    }
+    /**
+     * Test of validate input name.
+     */
+    @Test
+    public void validateInputName() {
+        ValidateUtil.validateInputName("asdf");
+    }
+
+    /**
+     * Test of validate input type.
+     */
+    @Test
+    public void validateInputTypeNull() {
+        try {
+            ValidateUtil.validateInputType(null);
+            fail();
+        } catch (ServiceHttpStatusException e) {
+            assertEquals(HttpStatus.BAD_REQUEST, e.getHttpStatus());
+            assertEquals("type is not available", e.getMessage());
+            assertEquals(null, e.getCause());
+        }
+    }
+    /**
+     * Test of validate input type.
+     */
+    @Test
+    public void validateInputType() {
+        ValidateUtil.validateInputType(Type.SYSTEMGROUP);
+        ValidateUtil.validateInputType(Type.SYSTEM);
+        ValidateUtil.validateInputType(Type.SUBSYSTEM);
+        ValidateUtil.validateInputType(Type.DISCIPLINE);
+        ValidateUtil.validateInputType(Type.DEVICEGROUP);
+        ValidateUtil.validateInputType(Type.DEVICETYPE);
+    }
+
+    /**
+     * Test of validate input uuid.
+     */
+    @Test
+    public void validateInputUuidNull() {
+        try {
+            ValidateUtil.validateInputUuid(null);
+            fail();
+        } catch (ServiceHttpStatusException e) {
+            assertEquals(HttpStatus.BAD_REQUEST, e.getHttpStatus());
+            assertEquals("uuid is not available", e.getMessage());
+            assertEquals(null, e.getCause());
+        }
+    }
+    /**
+     * Test of validate input uuid.
+     */
+    @Test
+    public void validateInputUuidEmpty() {
+        try {
+            ValidateUtil.validateInputUuid("");
+            fail();
+        } catch (ServiceHttpStatusException e) {
+            assertEquals(HttpStatus.BAD_REQUEST, e.getHttpStatus());
+            assertEquals("uuid is not available", e.getMessage());
+            assertEquals(null, e.getCause());
+        }
+    }
+    /**
+     * Test of validate input uuid.
+     */
+    @Test
+    public void validateInputUuid() {
+        ValidateUtil.validateInputUuid(UUID.randomUUID().toString());
+    }
+
+    // ----------------------------------------------------------------------------------------------------
+
+    /**
+     * Test of validate input read names.
+     */
+    @Test
+    public void validateInputReadNames() {
+        ValidateUtil.validateNamesInputRead(
+                null, null, null,
+                null,
+                null, null,
+                null, null);
+    }
+
+    /**
+     * Test of validate input create name element.
+     */
+    @Test
+    public void validateInputCreateNameElement() {
+        ValidateUtil.validateNameElementInputCreate(null);
+    }
+
+    /**
+     * Test of validate data create name element.
+     */
+    @Test
+    public void validateDataCreateNameElement() {
+        ValidateUtil.validateNameElementDataCreate(null, null, null, null, null);
+    }
+
+    /**
+     * Test of validate data create name.
+     */
+    @Test
+    public void validateDataCreateName() {
+        ValidateUtil.validateNameDataCreate(null, null, null, null, null);
+    }
+    /**
+     * Test of validate input update name element.
+     */
+    @Test
+    public void validateInputUpdateNameElement() {
+        ValidateUtil.validateNameElementInputUpdate(null);
+    }
+
+    /**
+     * Test of validate data update name element.
+     */
+    @Test
+    public void validateDataUpdateNameElement() {
+        ValidateUtil.validateNameElementDataUpdate(null, null, null, null, null);
+    }
+
+    // ----------------------------------------------------------------------------------------------------
+
+    /**
+     * Test of validate input read structures.
+     */
+    @Test
+    public void validateInputReadStructuresTypeNull() {
+        try {
+            ValidateUtil.validateStructuresInputRead(
+                    null, null, null, null, null,
+                    null,
+                    null, null,
+                    null, null);
+            fail();
+        } catch (ServiceHttpStatusException e) {
+            assertEquals(HttpStatus.BAD_REQUEST, e.getHttpStatus());
+            assertEquals("type is not available", e.getMessage());
+            assertEquals(null, e.getCause());
+        }
+    }
+
+    /**
+     * Test of validate input read structures.
+     */
+    @Test
+    public void validateInputReadStructures() {
+        ValidateUtil.validateStructuresInputRead(
+                Type.SYSTEMGROUP, null, null, null, null,
+                null,
+                null, null,
+                null, null);
+        ValidateUtil.validateStructuresInputRead(
+                Type.SYSTEM, null, null, null, null,
+                null,
+                null, null,
+                null, null);
+        ValidateUtil.validateStructuresInputRead(
+                Type.SUBSYSTEM, null, null, null, null,
+                null,
+                null, null,
+                null, null);
+        ValidateUtil.validateStructuresInputRead(
+                Type.DISCIPLINE, null, null, null, null,
+                null,
+                null, null,
+                null, null);
+        ValidateUtil.validateStructuresInputRead(
+                Type.DEVICEGROUP, null, null, null, null,
+                null,
+                null, null,
+                null, null);
+        ValidateUtil.validateStructuresInputRead(
+                Type.DEVICETYPE, null, null, null, null,
+                null,
+                null, null,
+                null, null);
+    }
+
+    /**
+     * Test of validate data create structure element.
+     */
+    @Test
+    public void validateDataCreateStructureElement() {
+        ValidateUtil.validateStructureElementDataCreate(null, null, null, null);
+    }
+
+    /**
+     * Test of validate data create structure.
+     */
+    @Test
+    public void validateDataCreateStructuret() {
+        ValidateUtil.validateStructureDataCreate(null, null, null, null, null);
+    }
+
+    /**
+     * Test of validate input update structure element.
+     */
+    @Test
+    public void validateInputUpdateStructureElement() {
+        ValidateUtil.validateStructureElementInputUpdate(null);
+    }
+
+    /**
+     * Test of validate data update structure element.
+     */
+    @Test
+    public void validateDataUpdateStructureElement() {
+        ValidateUtil.validateStructureElementDataUpdate(null, null, null, null);
+    }
+
+    @Test
+    public void validateDummy() {
+        final List<Subsystem> subsystems = Lists.newArrayList();
+        try {
+            ValidateUtil.validateCondition(subsystems != null && subsystems.size() == 1, HttpStatus.INTERNAL_SERVER_ERROR,
+                    ValidateUtil.SUBSYSTEM + ValidateUtil.SPACE + ValidateUtil.IS_NOT_CORRECT, null);
+            fail();
+        } catch (ServiceHttpStatusException e) {
+            assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, e.getHttpStatus());
+            assertEquals("subsystem is not correct", e.getMessage());
+            assertEquals(null, e.getCause());
+        }
+    }
+
+}
-- 
GitLab