org.kitodo.dataeditor.ruleset.RulesetManagementIT.java Source code

Java tutorial

Introduction

Here is the source code for org.kitodo.dataeditor.ruleset.RulesetManagementIT.java

Source

/*
 * (c) Kitodo. Key to digital objects e. V. <contact@kitodo.org>
 *
 * This file is part of the Kitodo project.
 *
 * It is licensed under GNU General Public License version 3 or later.
 *
 * For the full copyright and license information, please read the
 * GPL3-License.txt file that was distributed with this source code.
 */

package org.kitodo.dataeditor.ruleset;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.File;
import java.io.IOException;
import java.time.Month;
import java.time.MonthDay;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale.LanguageRange;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.junit.Test;
import org.kitodo.api.dataeditor.rulesetmanagement.ComplexMetadataViewInterface;
import org.kitodo.api.dataeditor.rulesetmanagement.DatesSimpleMetadataViewInterface;
import org.kitodo.api.dataeditor.rulesetmanagement.Domain;
import org.kitodo.api.dataeditor.rulesetmanagement.InputType;
import org.kitodo.api.dataeditor.rulesetmanagement.MetadataViewInterface;
import org.kitodo.api.dataeditor.rulesetmanagement.MetadataViewWithValuesInterface;
import org.kitodo.api.dataeditor.rulesetmanagement.SimpleMetadataViewInterface;
import org.kitodo.api.dataeditor.rulesetmanagement.StructuralElementViewInterface;

/**
 * Here, the ruleset management is put through its paces.
 */
public class RulesetManagementIT {

    private static final String BOOK = "book";
    private static final String HELLO_WORLD = "Hello World!";
    private static final String OPT = "opt1";
    private static final String TEST = "test";

    /**
     * English as the only language. This is the default case we use all the
     * time, except for tests that explicitly refer to the language.
     */
    private static final List<LanguageRange> ENGL = LanguageRange.parse("en");

    /**
     * This test checks if a ruleset can be loaded from a file. It loads a
     * complex rule set that contains all sorts of possible cases to test
     * whether the parser has been implemented correctly. The only purpose of
     * this test is that the load command does not throw an exception.
     *
     * <p>
     * The ruleset for this test is more practice-oriented and also includes
     * documentation, if you are looking for it.
     */
    @Test
    public void testAnExtensiveRulesetCanBeLoaded() throws IOException {
        RulesetManagement underTest = new RulesetManagement();
        underTest.load(new File("src/test/resources/testAnExtensiveRulesetCanBeLoaded.xml"));
    }

    /**
     * The ruleset contains presets. This test checks if the presets in the view
     * can be retrieved. That needs to be tested for the different codomain
     * types.
     */
    @Test
    public void testAvailabilityOfPresets() throws IOException {
        RulesetManagement underTest = new RulesetManagement();
        underTest.load(new File("src/test/resources/testAvailabilityOfPresets.xml"));

        StructuralElementViewInterface sevi = underTest.getStructuralElementView(BOOK, "", ENGL);
        List<MetadataViewWithValuesInterface<Void>> mvwviList = sevi
                .getSortedVisibleMetadata(Collections.emptyMap(), Arrays.asList("defaultStringKey", "anyURIKey",
                        "booleanKey", "dateKey", "namespaceDefaultAnyURIKey", "namespaceStringKey"));

        MetadataViewInterface booleanMvi = mvwviList.get(0).getMetadata().get();
        assertFalse(booleanMvi.isComplex());
        assertTrue(booleanMvi instanceof SimpleMetadataViewInterface);
        SimpleMetadataViewInterface booleanSmvi = (SimpleMetadataViewInterface) booleanMvi;
        assertTrue(booleanSmvi.getBooleanDefaultValue());

        MetadataViewInterface dateMvi = mvwviList.get(1).getMetadata().get();
        assertFalse(dateMvi.isComplex());
        assertTrue(dateMvi instanceof SimpleMetadataViewInterface);
        SimpleMetadataViewInterface dateSmvi = (SimpleMetadataViewInterface) dateMvi;
        assertEquals("1993-09-01", dateSmvi.getDefaultValue());

        MetadataViewInterface defaultStringMvi = mvwviList.get(2).getMetadata().get();
        assertFalse(defaultStringMvi.isComplex());
        assertTrue(defaultStringMvi instanceof SimpleMetadataViewInterface);
        SimpleMetadataViewInterface defaultStringSmvi = (SimpleMetadataViewInterface) defaultStringMvi;
        assertEquals(HELLO_WORLD, defaultStringSmvi.getDefaultValue());

        MetadataViewInterface namespaceDefaultAnyURIMvi = mvwviList.get(3).getMetadata().get();
        assertFalse(namespaceDefaultAnyURIMvi.isComplex());
        assertTrue(namespaceDefaultAnyURIMvi instanceof SimpleMetadataViewInterface);
        SimpleMetadataViewInterface namespaceDefaultAnyURISmvi = (SimpleMetadataViewInterface) namespaceDefaultAnyURIMvi;
        assertEquals("http://test.example/non-existent-namespace/", namespaceDefaultAnyURISmvi.getDefaultValue());

        MetadataViewInterface namespaceStringMvi = mvwviList.get(4).getMetadata().get();
        assertFalse(namespaceStringMvi.isComplex());
        assertTrue(namespaceStringMvi instanceof SimpleMetadataViewInterface);
        SimpleMetadataViewInterface namespaceStringSmvi = (SimpleMetadataViewInterface) namespaceStringMvi;
        assertEquals("http://test.example/non-existent-namespace/", namespaceStringSmvi.getDefaultValue());

        MetadataViewInterface anyURIMvi = mvwviList.get(5).getMetadata().get();
        assertFalse(anyURIMvi.isComplex());
        assertTrue(anyURIMvi instanceof SimpleMetadataViewInterface);
        SimpleMetadataViewInterface anyURISmvi = (SimpleMetadataViewInterface) anyURIMvi;
        assertEquals("https://en.wikipedia.org/wiki/%22Hello,_World!%22_program", anyURISmvi.getDefaultValue());
    }

    /**
     * Check that a subkey view is available for subkeys.
     */
    @Test
    public void testAvailabilityOfSubkeyViews() throws IOException {
        RulesetManagement underTest = new RulesetManagement();
        underTest.load(new File("src/test/resources/testAvailabilityOfSubkeyViews.xml"));

        StructuralElementViewInterface sevi = underTest.getStructuralElementView(BOOK, "", ENGL);
        List<MetadataViewWithValuesInterface<Void>> mvwviList = sevi
                .getSortedVisibleMetadata(Collections.emptyMap(), Collections.singletonList("contributor"));

        MetadataViewInterface contributorMvi = mvwviList.get(0).getMetadata().get();
        assertEquals("contributor", contributorMvi.getId());
        assertEquals("Contributor person", contributorMvi.getLabel());
        assertTrue(contributorMvi.isComplex());
        assertTrue(contributorMvi instanceof ComplexMetadataViewInterface);
        ComplexMetadataViewInterface contributorCmvi = (ComplexMetadataViewInterface) contributorMvi;

        Collection<MetadataViewInterface> mvic = contributorCmvi.getAddableMetadata(Collections.emptyMap(),
                Collections.emptyList());

        Iterator<MetadataViewInterface> mvici = mvic.iterator();
        for (int i = 0; i < mvic.size(); i++) {
            if (!mvici.hasNext()) {
                break;
            }
            MetadataViewInterface mvi = mvici.next();

            assertFalse(mvi.isComplex());
            assertTrue(mvi instanceof SimpleMetadataViewInterface);
            SimpleMetadataViewInterface smvi = (SimpleMetadataViewInterface) mvi;

            /*
             * We do not have a restriction rule here, so the subkeys appear in
             * alphabetical order after the translated label. Thats wanted.
             */
            switch (i) {
            case 0:
                assertEquals("givenName", smvi.getId());
                assertEquals("Given name", smvi.getLabel());
                break;
            case 1:
                assertEquals("role", smvi.getId());
                assertEquals("Role", smvi.getLabel());
                assertThat(smvi.getSelectItems(), hasEntry("aut", "Author"));
                assertThat(smvi.getSelectItems(), hasEntry("edt", "Editor"));
                break;
            case 2:
                assertEquals("surname", smvi.getId());
                assertEquals("Surname", smvi.getLabel());
                break;
            default:
                fail("Too many elements in the view!");
            }
        }
    }

    /**
     * Checks if the options are returned correctly. On the one hand there is
     * the translation, on the other hand the DisplayMode has to be set to
     * MULTI_LINE_SINGLE_SELECTION, MULTIPLE_SELECTION or
     * ONE_LINE_SINGLE_SELECTION accordingly. In cases
     * MULTI_LINE_SINGLE_SELECTION and ONE_LINE_SINGLE_SELECTION, the
     * distinction is made as to whether minOccurs is 1 or not, if not, there
     * must not be an empty selection element for no input, otherwise not.
     */
    @Test
    public void testCorrectReturnOfOptions() throws IOException {
        RulesetManagement underTest = new RulesetManagement();
        underTest.load(new File("src/test/resources/testCorrectReturnOfOptions.xml"));

        // 1. options are sorted as to their labels alphabetically
        StructuralElementViewInterface sevi = underTest.getStructuralElementView(BOOK, "", ENGL);
        List<MetadataViewWithValuesInterface<Void>> mvwviList = sevi
                .getSortedVisibleMetadata(Collections.emptyMap(), Collections.emptyList());

        StructuralElementViewInterface seviDe = underTest.getStructuralElementView(BOOK, "",
                LanguageRange.parse("de"));
        List<MetadataViewWithValuesInterface<Void>> mvwviListDe = seviDe
                .getSortedVisibleMetadata(Collections.emptyMap(), Collections.emptyList());

        assertThat(((SimpleMetadataViewInterface) mvwviList.get(0).getMetadata().get()).getSelectItems().keySet(),
                contains("dan", "dut", "eng", "fre", "ger"));
        assertThat(((SimpleMetadataViewInterface) mvwviListDe.get(0).getMetadata().get()).getSelectItems().keySet(),
                contains("dan", "ger", "eng", "fre", "dut"));

        // 2. The input elements that have a minimum occurrence greater than
        // zero will appear on the display by themselves, the others may be
        // added.
        assertThat(ids(mvwviList), contains("mandatoryMultiLineSingleSelection", "mandatoryOneLineSingleSelection",
                "mandatoryMultipleSelection"));

        Collection<MetadataViewInterface> mviColl = sevi.getAddableMetadata(Collections.emptyMap(),
                Collections.emptyList());

        assertThat(ids(mviColl), contains("optionalMultiLineSingleSelection", "optionalOneLineSingleSelection",
                "optionalMultipleSelection"));

        // 3. The input types have been calculated correctly
        assertThat(
                mvwviList.stream()
                        .map(mvwvi -> ((SimpleMetadataViewInterface) mvwvi.getMetadata().get()).getInputType())
                        .collect(Collectors.toList()),
                contains(InputType.MULTI_LINE_SINGLE_SELECTION, InputType.ONE_LINE_SINGLE_SELECTION,
                        InputType.MULTIPLE_SELECTION));

        assertThat(
                mviColl.stream().map(mvi -> ((SimpleMetadataViewInterface) mvi).getInputType())
                        .collect(Collectors.toList()),
                contains(InputType.MULTI_LINE_SINGLE_SELECTION, InputType.ONE_LINE_SINGLE_SELECTION,
                        InputType.MULTIPLE_SELECTION));

        // 4. In the optional single selections, one empty element is in it, not
        // in the others.

        assertThat(((SimpleMetadataViewInterface) mvwviList.get(0).getMetadata().get()).getSelectItems(),
                not(hasEntry("", "")));
        assertThat(((SimpleMetadataViewInterface) mvwviList.get(1).getMetadata().get()).getSelectItems(),
                not(hasEntry("", "")));
        assertThat(((SimpleMetadataViewInterface) mvwviList.get(2).getMetadata().get()).getSelectItems(),
                not(hasEntry("", "")));
        List<MetadataViewInterface> mviList = (List<MetadataViewInterface>) mviColl;
        assertThat(((SimpleMetadataViewInterface) mviList.get(0)).getSelectItems(), hasEntry("", ""));
        assertThat(((SimpleMetadataViewInterface) mviList.get(1)).getSelectItems(), hasEntry("", ""));
        assertThat(((SimpleMetadataViewInterface) mviList.get(2)).getSelectItems(), not(hasEntry("", "")));
    }

    /**
     * The test verifies that only the options that are valid according to the
     * ruleset are valid.
     */
    @Test
    public void testCorrectValidationOfOptions() throws IOException {
        RulesetManagement underTest = new RulesetManagement();
        underTest.load(new File("src/test/resources/testCorrectValidationOfOptions.xml"));

        StructuralElementViewInterface sevi = underTest.getStructuralElementView(BOOK, "", ENGL);
        Collection<MetadataViewInterface> mviColl = sevi.getAddableMetadata(Collections.emptyMap(),
                Collections.emptyList());
        SimpleMetadataViewInterface smvi = (SimpleMetadataViewInterface) mviColl.iterator().next();
        assertTrue(smvi.isValid(OPT));
        assertFalse(smvi.isValid(StringUtils.capitalize(OPT)));
    }

    /**
     * The test checks if the validation by regular expressions works.
     */
    @Test
    public void testCorrectValidationOfRegularExpressions() throws IOException {
        RulesetManagement underTest = new RulesetManagement();
        underTest.load(new File("src/test/resources/testCorrectValidationOfRegularExpressions.xml"));

        StructuralElementViewInterface sevi = underTest.getStructuralElementView(BOOK, "", ENGL);
        List<MetadataViewWithValuesInterface<Void>> mvwviList = sevi.getSortedVisibleMetadata(
                Collections.emptyMap(), Arrays.asList("defaultStringKey", "anyURIKey", "booleanKey", "dateKey",
                        "namespaceDefaultAnyURIKey", "namespaceStringKey", "integerKey", "optionsKey"));

        MetadataViewInterface booleanMvi = mvwviList.get(0).getMetadata().get();
        assertFalse(booleanMvi.isComplex());
        assertTrue(booleanMvi instanceof SimpleMetadataViewInterface);
        SimpleMetadataViewInterface booleanSmvi = (SimpleMetadataViewInterface) booleanMvi;
        assertTrue(booleanSmvi.isValid(booleanSmvi.convertBoolean(true).get()));
        assertFalse(booleanSmvi.isValid(""));
        assertFalse(booleanSmvi.isValid("botch"));

        MetadataViewInterface dateMvi = mvwviList.get(1).getMetadata().get();
        assertFalse(dateMvi.isComplex());
        assertTrue(dateMvi instanceof SimpleMetadataViewInterface);
        SimpleMetadataViewInterface dateSmvi = (SimpleMetadataViewInterface) dateMvi;
        assertFalse(dateSmvi.isValid(""));
        assertFalse(dateSmvi.isValid("1803-05-12"));
        assertTrue(dateSmvi.isValid("1993-09-01"));

        MetadataViewInterface defaultStringMvi = mvwviList.get(2).getMetadata().get();
        assertFalse(defaultStringMvi.isComplex());
        assertTrue(defaultStringMvi instanceof SimpleMetadataViewInterface);
        SimpleMetadataViewInterface defaultStringSmvi = (SimpleMetadataViewInterface) defaultStringMvi;
        assertFalse(defaultStringSmvi.isValid(HELLO_WORLD));
        assertTrue(defaultStringSmvi.isValid("1234567X"));

        MetadataViewInterface integerMvi = mvwviList.get(3).getMetadata().get();
        assertFalse(integerMvi.isComplex());
        assertTrue(integerMvi instanceof SimpleMetadataViewInterface);
        SimpleMetadataViewInterface integerSmvi = (SimpleMetadataViewInterface) integerMvi;
        assertFalse(integerSmvi.isValid("22"));
        assertTrue(integerSmvi.isValid("1748"));

        MetadataViewInterface namespaceDefaultAnyURIMvi = mvwviList.get(4).getMetadata().get();
        assertFalse(namespaceDefaultAnyURIMvi.isComplex());
        assertTrue(namespaceDefaultAnyURIMvi instanceof SimpleMetadataViewInterface);
        SimpleMetadataViewInterface namespaceDefaultAnyURISmvi = (SimpleMetadataViewInterface) namespaceDefaultAnyURIMvi;
        assertFalse(namespaceDefaultAnyURISmvi
                .isValid("http://test.example/non-existent-namespace/%22Hello,_World!%22_program"));
        assertTrue(namespaceDefaultAnyURISmvi
                .isValid("http://test.example/non-existent-namespace/Hello_World_program"));

        MetadataViewInterface namespaceStringMvi = mvwviList.get(5).getMetadata().get();
        assertFalse(namespaceStringMvi.isComplex());
        assertTrue(namespaceStringMvi instanceof SimpleMetadataViewInterface);
        SimpleMetadataViewInterface namespaceStringSmvi = (SimpleMetadataViewInterface) namespaceStringMvi;
        assertFalse(namespaceStringSmvi
                .isValid("http://test.example/non-existent-namespace/%22Hello,_World!%22_program"));
        assertTrue(namespaceStringSmvi.isValid("http://test.example/non-existent-namespace/Hello_World_program"));

        MetadataViewInterface optionsMvi = mvwviList.get(6).getMetadata().get();
        assertFalse(optionsMvi.isComplex());
        assertTrue(optionsMvi instanceof SimpleMetadataViewInterface);
        SimpleMetadataViewInterface optionsSmvi = (SimpleMetadataViewInterface) optionsMvi;
        assertFalse(optionsSmvi.isValid("opt88"));
        assertTrue(optionsSmvi.isValid("opt2"));

        MetadataViewInterface anyURIMvi = mvwviList.get(7).getMetadata().get();
        assertFalse(anyURIMvi.isComplex());
        assertTrue(anyURIMvi instanceof SimpleMetadataViewInterface);
        SimpleMetadataViewInterface anyURISmvi = (SimpleMetadataViewInterface) anyURIMvi;
        assertFalse(anyURISmvi.isValid("mailto:e-mail@example.org"));
        assertTrue(anyURISmvi.isValid("https://en.wikipedia.org/wiki/%22Hello,_World!%22_program"));
    }

    /**
     * This test verifies that acquisition stage edit settings correctly
     * override the common edit settings.
     */
    @Test
    public void testDisplaySettingsWithAcquisitionStage() throws IOException {
        RulesetManagement underTest = new RulesetManagement();
        underTest.load(new File("src/test/resources/testDisplaySettingsWithAcquisitionStage.xml"));

        StructuralElementViewInterface sevi = underTest.getStructuralElementView(BOOK, "The acquisition stage",
                ENGL);

        // always showing
        List<MetadataViewWithValuesInterface<Void>> mvwviListAlwaysShowing = sevi
                .getSortedVisibleMetadata(Collections.emptyMap(), Collections.emptyList());
        assertThat(
                mvwviListAlwaysShowing.stream().map(mvwvi -> mvwvi.getMetadata().get().getId())
                        .collect(Collectors.toList()),
                containsInAnyOrder("alwaysShowingUnchangedTrue", "alwaysShowingTrueUnchanged",
                        "alwaysShowingTrueOtherchanges", "alwaysShowingTrueTrue", "multilineTrueOtherchanges"));

        // excluded
        Map<Object, String> metadataForExcluded = new HashMap<>();
        metadataForExcluded.put("exclude1", "excludedUnchangedTrue");
        metadataForExcluded.put("exclude2", "excludedTrueUnchanged");
        metadataForExcluded.put("exclude3", "excludedTrueOtherchanges");
        metadataForExcluded.put("exclude4", "excludedTrueTrue");
        metadataForExcluded.put("n#*703=]", "excludedTrueFalse");
        List<MetadataViewWithValuesInterface<Object>> mvwviListExcluded = sevi
                .getSortedVisibleMetadata(metadataForExcluded, Collections.emptyList());
        assertTrue(mvwviListExcluded.stream().filter(mvwvi -> mvwvi.getMetadata().isPresent())
                .map(mvwvi -> mvwvi.getMetadata().get().getId()).filter(keyId -> keyId.startsWith("excluded"))
                .collect(Collectors.toList()).contains("excludedTrueFalse"));
        assertThat(
                mvwviListExcluded.stream().filter(mvwvi -> !mvwvi.getMetadata().isPresent())
                        .flatMap(mvwvi -> mvwvi.getValues().stream()).collect(Collectors.toList()),
                containsInAnyOrder("exclude1", "exclude2", "exclude3", "exclude4"));
        Collection<MetadataViewInterface> mviCollExcluded = sevi.getAddableMetadata(metadataForExcluded,
                Collections.emptyList());
        assertTrue(mviCollExcluded.stream().map(mvi -> mvi.getId()).filter(keyId -> keyId.startsWith("excluded"))
                .collect(Collectors.toList()).contains("excludedTrueFalse"));

        // editable
        List<MetadataViewWithValuesInterface<Object>> mvwviList = sevi.getSortedVisibleMetadata(
                Collections.emptyMap(),
                Arrays.asList("editableUnchangedFalse", "editableFalseUnchanged", "editableFalseOtherchanges",
                        "editableFalseFalse", "editableFalseTrue", "multilineUnchangedTrue",
                        "multilineTrueUnchanged", "multilineTrueOtherchanges", "multilineTrueTrue",
                        "multilineTrueFalse"));

        SimpleMetadataViewInterface editableUnchangedFalse = getSmvi(mvwviList, "editableUnchangedFalse");
        assertFalse(editableUnchangedFalse.isEditable());
        SimpleMetadataViewInterface editableFalseUnchanged = getSmvi(mvwviList, "editableFalseUnchanged");
        assertFalse(editableFalseUnchanged.isEditable());
        SimpleMetadataViewInterface editableFalseOtherChanges = getSmvi(mvwviList, "editableFalseOtherchanges");
        assertFalse(editableFalseOtherChanges.isEditable());
        SimpleMetadataViewInterface editableFalseFalse = getSmvi(mvwviList, "editableFalseFalse");
        assertFalse(editableFalseFalse.isEditable());
        SimpleMetadataViewInterface editableFalseTrue = getSmvi(mvwviList, "editableFalseTrue");
        assertTrue(editableFalseTrue.isEditable());

        // multi-line
        SimpleMetadataViewInterface multilineUnchangedTrue = getSmvi(mvwviList, "multilineUnchangedTrue");
        assertEquals(InputType.MULTI_LINE_TEXT, multilineUnchangedTrue.getInputType());
        SimpleMetadataViewInterface multilineTrueUnchanged = getSmvi(mvwviList, "multilineTrueUnchanged");
        assertEquals(InputType.MULTI_LINE_TEXT, multilineTrueUnchanged.getInputType());
        SimpleMetadataViewInterface multilineTrueOtherchanges = getSmvi(mvwviList, "multilineTrueOtherchanges");
        assertEquals(InputType.MULTI_LINE_TEXT, multilineTrueOtherchanges.getInputType());
        SimpleMetadataViewInterface multilineTrueTrue = getSmvi(mvwviList, "multilineTrueTrue");
        assertEquals(InputType.MULTI_LINE_TEXT, multilineTrueTrue.getInputType());
        SimpleMetadataViewInterface multilineTrueFalse = getSmvi(mvwviList, "multilineTrueFalse");
        assertEquals(InputType.ONE_LINE_TEXT, multilineTrueFalse.getInputType());
    }

    /**
     * This test verifies the working of the editing settings, including their
     * interaction and nesting.
     */
    @Test
    public void testDisplaySettingsWithoutAcquisitionStage() throws IOException {
        RulesetManagement underTest = new RulesetManagement();
        underTest.load(new File("src/test/resources/testDisplaySettingsWithoutAcquisitionStage.xml"));
        StructuralElementViewInterface sevi = underTest.getStructuralElementView(BOOK, "", ENGL);

        // 1. Without meta-data, and without additional fields being selected,
        // you should see exactly the fields that are always showing, minus
        // those that are excluded (excluded overrules always showing).
        List<MetadataViewWithValuesInterface<Void>> mvwviListNoMetadata = sevi
                .getSortedVisibleMetadata(Collections.emptyMap(), Collections.emptyList());
        assertThat(ids(mvwviListNoMetadata),
                containsInAnyOrder("testAlwaysShowing", "testAlwaysShowingEditable", "testAlwaysShowingMultiline"));

        // 2. All fields except those that are excluded should be allowed to be
        // added.
        Collection<MetadataViewInterface> mviCollNoMetadata = sevi.getAddableMetadata(Collections.emptyMap(),
                Collections.emptyList());
        assertThat(ids(mviCollNoMetadata),
                containsInAnyOrder("testAlwaysShowing", "testEditable", "testMultiline",
                        "testAlwaysShowingEditable", "testAlwaysShowingMultiline", "testEditableMultiline",
                        "testNestedSettings"));

        // 1a. In the nested meta-data field, the only value that should be
        // visible is the one marked as always showing. The field has to be
        // added first:
        List<MetadataViewWithValuesInterface<Void>> mvwviListNestedSettings = sevi
                .getSortedVisibleMetadata(Collections.emptyMap(), Collections.singletonList("testNestedSettings"));
        ComplexMetadataViewInterface nestedSettings = (ComplexMetadataViewInterface) mvwviListNestedSettings
                .stream().filter(mvwvi -> mvwvi.getMetadata().get().getId().equals("testNestedSettings")).findAny()
                .get().getMetadata().get();
        List<MetadataViewWithValuesInterface<Void>> nestedMvwviList = nestedSettings
                .getSortedVisibleMetadata(Collections.emptyMap(), Collections.emptyList());
        assertEquals(1, nestedMvwviList.size());
        assertEquals("testAlwaysShowing", nestedMvwviList.get(0).getMetadata().get().getId());

        // 2a. Also with nested meta-data, all fields except those that are
        // excluded should be allowed to be added.
        Collection<MetadataViewInterface> nestedMviColl = nestedSettings.getAddableMetadata(Collections.emptyMap(),
                Collections.emptyList());
        assertThat(ids(nestedMviColl), containsInAnyOrder("testAlwaysShowing", "testEditable", "testMultiline"));

        // 3. With meta-data, all fields should be visible except those that are
        // excluded. There should be an entry without a key in the list
        // containing the values of the excluded keys.
        Map<Object, String> metadata = new HashMap<>();
        metadata.put("udv-q@bC", "testAlwaysShowing");
        metadata.put("/F5Mu=/1", "testEditable");
        metadata.put("exclude1", "testExcluded");
        metadata.put("WP&~O$YV", "testMultiline");
        metadata.put("n#*703=]", "testAlwaysShowingEditable");
        metadata.put("exclude2", "testAlwaysShowingExcluded");
        metadata.put("Mu{lp'n1", "testAlwaysShowingMultiline");
        metadata.put("exclude3", "testEditableExcluded");
        metadata.put("qP'Jc:.R", "testEditableMultiline");
        metadata.put("exclude4", "testExcludedMultiline");
        metadata.put("4J[~UgHp", "testNestedSettings");

        List<MetadataViewWithValuesInterface<Object>> mvwviListWithMetadata = sevi
                .getSortedVisibleMetadata(metadata, Collections.emptyList());
        assertThat(ids(mvwviListWithMetadata),
                containsInAnyOrder("testAlwaysShowing", "testEditable", "testMultiline",
                        "testAlwaysShowingEditable", "testAlwaysShowingMultiline", "testEditableMultiline",
                        "testNestedSettings"));
        assertThat(
                mvwviListWithMetadata.stream().filter(mvwvi -> !mvwvi.getMetadata().isPresent())
                        .flatMap(mvwvi -> mvwvi.getValues().stream()).collect(Collectors.toList()),
                containsInAnyOrder("exclude1", "exclude2", "exclude3", "exclude4"));

        // 3a. Also with nested meta-data, that should work that way.
        Map<Object, String> nestedMetadata = new HashMap<>();
        nestedMetadata.put("udv-q@bC", "testAlwaysShowing");
        nestedMetadata.put("/F5Mu=/1", "testEditable");
        nestedMetadata.put("excluded", "testExcluded");
        nestedMetadata.put("WP&~O$YV", "testMultiline");

        List<MetadataViewWithValuesInterface<Object>> nestedMvwviListWithMetadata = nestedSettings
                .getSortedVisibleMetadata(nestedMetadata, Collections.emptyList());
        assertThat(ids(nestedMvwviListWithMetadata),
                containsInAnyOrder("testAlwaysShowing", "testEditable", "testMultiline"));
        assertTrue(nestedMvwviListWithMetadata.stream().filter(mvwvi -> !mvwvi.getMetadata().isPresent())
                .flatMap(mvwvi -> mvwvi.getValues().stream()).collect(Collectors.toList()).contains("excluded"));

        // 4. The property multiline should manipulate the input type
        SimpleMetadataViewInterface testAlwaysShowing = getSmvi(mvwviListWithMetadata, "testAlwaysShowing");
        assertEquals(InputType.ONE_LINE_TEXT, testAlwaysShowing.getInputType());
        SimpleMetadataViewInterface testEditable = getSmvi(mvwviListWithMetadata, "testEditable");
        assertEquals(InputType.ONE_LINE_TEXT, testEditable.getInputType());
        SimpleMetadataViewInterface testMultiline = getSmvi(mvwviListWithMetadata, "testMultiline");
        assertEquals(InputType.MULTI_LINE_TEXT, testMultiline.getInputType());
        SimpleMetadataViewInterface testAlwaysShowingEditable = getSmvi(mvwviListWithMetadata,
                "testAlwaysShowingEditable");
        assertEquals(InputType.ONE_LINE_TEXT, testAlwaysShowingEditable.getInputType());
        SimpleMetadataViewInterface testAlwaysShowingMultiline = getSmvi(mvwviListWithMetadata,
                "testAlwaysShowingMultiline");
        assertEquals(InputType.MULTI_LINE_TEXT, testAlwaysShowingMultiline.getInputType());
        SimpleMetadataViewInterface testEditableMultiline = getSmvi(mvwviListWithMetadata, "testEditableMultiline");
        assertEquals(InputType.MULTI_LINE_TEXT, testEditableMultiline.getInputType());

        SimpleMetadataViewInterface nestedTestAlwaysShowing = getSmvi(nestedMvwviListWithMetadata,
                "testAlwaysShowing");
        assertEquals(InputType.ONE_LINE_TEXT, nestedTestAlwaysShowing.getInputType());
        SimpleMetadataViewInterface nestedTestEditable = getSmvi(nestedMvwviListWithMetadata, "testEditable");
        assertEquals(InputType.ONE_LINE_TEXT, nestedTestEditable.getInputType());
        SimpleMetadataViewInterface nestedTestMultiline = getSmvi(nestedMvwviListWithMetadata, "testMultiline");
        assertEquals(InputType.MULTI_LINE_TEXT, nestedTestMultiline.getInputType());

        // 5. The property editable should be reflected in the value of
        // isEditable()
        assertTrue(testAlwaysShowing.isEditable());
        assertFalse(testEditable.isEditable());
        assertTrue(testMultiline.isEditable());
        assertFalse(testAlwaysShowingEditable.isEditable());
        assertTrue(testAlwaysShowingMultiline.isEditable());
        assertFalse(testEditableMultiline.isEditable());

        assertTrue(nestedTestAlwaysShowing.isEditable());
        assertFalse(nestedTestEditable.isEditable());
        assertTrue(nestedTestMultiline.isEditable());
    }

    /**
     * This test verifies that the labels for divisions are translated
     * correctly. For this purpose, various cases with different language
     * requirements (existing, nonexistent and overspecified) are performed.
     */
    @Test
    public void testDivisionsAreCorrectlyTranslated() throws IOException {
        RulesetManagement underTest = new RulesetManagement();
        underTest.load(new File("src/test/resources/testDivisionsAreCorrectlyTranslated.xml"));
        assertTrue(underTest.getAcquisitionStages().isEmpty());

        // Here we test the translation, whether the expected language always
        // comes.
        Map<String, String> divisionsNoLanguage = underTest.getStructuralElements(Collections.emptyList());
        assertEquals(1, divisionsNoLanguage.entrySet().size());
        assertTrue(divisionsNoLanguage.containsKey(BOOK));
        assertTrue(divisionsNoLanguage.containsValue(StringUtils.capitalize(BOOK)));

        Map<String, String> divisionsDe = underTest
                .getStructuralElements(LanguageRange.parse("de;q=1.0,cn;q=0.75,fr;q=0.5,ru;q=0.25"));
        assertEquals(1, divisionsDe.entrySet().size());
        assertTrue(divisionsDe.containsKey(BOOK));
        assertTrue(divisionsDe.containsValue("Buch"));

        Map<String, String> divisionsDeDe = underTest
                .getStructuralElements(LanguageRange.parse("de-DE;q=1.0,cn;q=0.75,fr;q=0.5,ru;q=0.25"));
        assertEquals(1, divisionsDeDe.entrySet().size());
        assertTrue(divisionsDeDe.containsKey(BOOK));
        assertTrue(divisionsDeDe.containsValue("Buch"));

        Map<String, String> divisionsEn = underTest
                .getStructuralElements(LanguageRange.parse("en;q=1.0,fr;q=0.75,de;q=0.5,cn;q=0.25"));
        assertEquals(1, divisionsEn.entrySet().size());
        assertTrue(divisionsEn.containsKey(BOOK));
        assertTrue(divisionsEn.containsValue(StringUtils.capitalize(BOOK)));

        Map<String, String> divisionsEnUs = underTest
                .getStructuralElements(LanguageRange.parse("en-US;q=1.0,de;q=0.667,fr;q=0.333"));
        assertEquals(1, divisionsEnUs.entrySet().size());
        assertTrue(divisionsEnUs.containsKey(BOOK));
        assertTrue(divisionsEnUs.containsValue(StringUtils.capitalize(BOOK)));

        Map<String, String> divisionsCnRu = underTest
                .getStructuralElements(LanguageRange.parse("cn;q=1.0,ru;q=0.5"));
        assertEquals(1, divisionsCnRu.entrySet().size());
        assertTrue(divisionsCnRu.containsKey(BOOK));
        assertTrue(divisionsCnRu.containsValue(StringUtils.capitalize(BOOK)));

        // Now a first view on a book
        StructuralElementViewInterface view = underTest.getStructuralElementView(BOOK, "", ENGL);
        assertEquals(1, view.getAllowedSubstructuralElements().entrySet().size());
        assertEquals(5, view.getAddableMetadata(Collections.emptyMap(), Collections.emptyList()).size());
        assertTrue(view.isComplex());
        assertFalse(view.isUndefined());

        // Now a nonsense view
        StructuralElementViewInterface nonsenseView = underTest.getStructuralElementView("bosh", "", ENGL);
        assertEquals(1, nonsenseView.getAllowedSubstructuralElements().entrySet().size());
        assertEquals(5, nonsenseView.getAddableMetadata(Collections.emptyMap(), Collections.emptyList()).size());
        assertTrue(nonsenseView.isUndefined());
    }

    /**
     * This test verifies that the selection of child divisions in divisions by
     * date works correctly.
     */
    @Test
    public void testDivisionsSubdividedByDateHaveOnlyTheCorrectChildren() throws IOException {
        RulesetManagement underTest = new RulesetManagement();
        underTest.load(new File("src/test/resources/testDivisionsSubdividedByDateHaveOnlyTheCorrectChildren.xml"));

        // Also the divisions structuring by date are to come out in the total
        // list:
        assertThat(underTest.getStructuralElements(ENGL).keySet(),
                containsInAnyOrder("newspaper", "newspaperYear", "newspaperMonth", "newspaperDay", "newspaperIssue",
                        "newspaperLimitedTest", "newspaperYearLimitedTest", "newspaperMonthLimitedTest",
                        "newspaperDayLimitedTest"));
        assertThat(underTest.getStructuralElements(ENGL).values(),
                containsInAnyOrder("Newspaper complete edition", "Years issues newspaper",
                        "Months issues newspaper", "Days issues newspaper",
                        "Issue newspaper", "Newspaper complete editionlimited test",
                        "Years issues newspaperlimited test",
                        "Months issues newspaperlimited test",
                        "Days issues newspaperlimited test"));

        // It should always come the right children:
        StructuralElementViewInterface newspaperSevi = underTest.getStructuralElementView("newspaper", "", ENGL);
        Map<String, String> newspaperAse = newspaperSevi.getAllowedSubstructuralElements();
        assertEquals(1, newspaperAse.entrySet().size());
        assertThat(newspaperAse, hasEntry("newspaperYear", "Years issues newspaper"));

        StructuralElementViewInterface yearSevi = underTest.getStructuralElementView("newspaperYear", "", ENGL);
        Map<String, String> yearAse = yearSevi.getAllowedSubstructuralElements();
        assertEquals(1, yearAse.entrySet().size());
        assertThat(yearAse, hasEntry("newspaperMonth", "Months issues newspaper"));

        StructuralElementViewInterface monthSevi = underTest.getStructuralElementView("newspaperMonth", "", ENGL);
        Map<String, String> monthAse = monthSevi.getAllowedSubstructuralElements();
        assertEquals(1, monthAse.entrySet().size());
        assertThat(monthAse, hasEntry("newspaperDay", "Days issues newspaper"));

        StructuralElementViewInterface daySevi = underTest.getStructuralElementView("newspaperDay", "", ENGL);
        Map<String, String> dayAse = daySevi.getAllowedSubstructuralElements();
        assertEquals(3, dayAse.entrySet().size());
        assertThat(dayAse, hasEntry("newspaper", "Newspaper complete edition"));
        assertThat(dayAse, hasEntry("newspaperLimitedTest", "Newspaper complete editionlimited test"));
        assertThat(dayAse, hasEntry("newspaperIssue", "Issue newspaper"));

        StructuralElementViewInterface newspaperLimitedTestSevi = underTest
                .getStructuralElementView("newspaperDayLimitedTest", "", ENGL);
        Map<String, String> newspaperLimitedTestAse = newspaperLimitedTestSevi.getAllowedSubstructuralElements();
        assertEquals(1, newspaperLimitedTestAse.entrySet().size());
        assertThat(newspaperLimitedTestAse, hasEntry("newspaperIssue", "Issue newspaper"));
    }

    /**
     * This test checks whether the date scheme and the field are correctly read
     * out for divisions that are subdivided by date.
     */
    @Test
    public void testDivisionsSubdividedByDateReportDateSchemeAndField() throws IOException {
        RulesetManagement underTest = new RulesetManagement();
        underTest.load(new File("src/test/resources/testDivisionsSubdividedByDateReportDateSchemeAndField.xml"));

        StructuralElementViewInterface playtimeSevi = underTest.getStructuralElementView("playtime", "", ENGL);
        assertThat(playtimeSevi.getDatesSimpleMetadata(), is(Optional.empty()));

        StructuralElementViewInterface yearSevi = underTest.getStructuralElementView("playtimeYear", "", ENGL);
        assertThat(yearSevi.getDatesSimpleMetadata(), is(not(Optional.empty())));
        DatesSimpleMetadataViewInterface yearDsmvi = yearSevi.getDatesSimpleMetadata().get();
        assertEquals("ORDERLABEL", yearDsmvi.getId());
        assertEquals("yyyy/yyyy", yearDsmvi.getScheme());
        assertEquals(MonthDay.of(Month.AUGUST, 1), yearDsmvi.getYearBegin());

        StructuralElementViewInterface monthSevi = underTest.getStructuralElementView("playtimeMonth", "", ENGL);
        assertThat(monthSevi.getDatesSimpleMetadata(), is(not(Optional.empty())));
        DatesSimpleMetadataViewInterface monthDsmvi = monthSevi.getDatesSimpleMetadata().get();
        assertEquals("ORDERLABEL", monthDsmvi.getId());
        assertEquals("yyyy-MM", monthDsmvi.getScheme());

        StructuralElementViewInterface daySevi = underTest.getStructuralElementView("playtimeDay", "", ENGL);
        assertThat(daySevi.getDatesSimpleMetadata(), is(not(Optional.empty())));
        DatesSimpleMetadataViewInterface dayDsmvi = daySevi.getDatesSimpleMetadata().get();
        assertEquals("ORDERLABEL", dayDsmvi.getId());
        assertEquals("yyyy-MM-dd", dayDsmvi.getScheme());
    }

    /**
     * The test verifies that fields with minOccurs greater than zero appear at
     * least as often as they need to be completed. An exception exists for
     * multiple selection, which of course nevertheless only once may be
     * displayed (here only several must be selected).
     */
    @Test
    public void testFieldsWithMinOccursGreaterZeroAreAlwaysShown() throws IOException {
        RulesetManagement underTest = new RulesetManagement();
        underTest.load(new File("src/test/resources/testFieldsWithMinOccursGreaterZeroAreAlwaysShown.xml"));

        StructuralElementViewInterface sevi = underTest.getStructuralElementView(BOOK, "", ENGL);
        List<MetadataViewWithValuesInterface<Object>> mvwviList = sevi
                .getSortedVisibleMetadata(Collections.emptyMap(), Collections.emptyList());

        assertThat(ids(mvwviList), contains("test1", "test2", "test2", "test2options"));
    }

    /**
     * The test verifies that all keys pass the domain correctly.
     */
    @Test
    public void testKeysReturnTheSpecifiedDomain() throws IOException {
        RulesetManagement underTest = new RulesetManagement();
        underTest.load(new File("src/test/resources/testKeysReturnTheSpecifiedDomain.xml"));

        StructuralElementViewInterface sevi = underTest.getStructuralElementView(BOOK, "", ENGL);
        Map<Object, String> metadata = new HashMap<>();
        metadata.put("....", "unspecifiedKey");
        List<MetadataViewWithValuesInterface<Object>> mvwviList = sevi.getSortedVisibleMetadata(metadata,
                Arrays.asList("description", "digitalProvenance", "noDomainSpecified", "rights", "source",
                        "technical", "metsDiv"));

        SimpleMetadataViewInterface description = getSmvi(mvwviList, "description");
        assertEquals(Domain.DESCRIPTION, description.getDomain().get());

        SimpleMetadataViewInterface digitalProvenance = getSmvi(mvwviList, "digitalProvenance");
        assertEquals(Domain.DIGITAL_PROVENANCE, digitalProvenance.getDomain().get());

        SimpleMetadataViewInterface metsDiv = getSmvi(mvwviList, "metsDiv");
        assertEquals(Domain.METS_DIV, metsDiv.getDomain().get());

        SimpleMetadataViewInterface noDomainSpecified = getSmvi(mvwviList, "noDomainSpecified");
        assertThat(noDomainSpecified.getDomain(), is(Optional.empty()));

        SimpleMetadataViewInterface rights = getSmvi(mvwviList, "rights");
        assertEquals(Domain.RIGHTS, rights.getDomain().get());

        SimpleMetadataViewInterface source = getSmvi(mvwviList, "source");
        assertEquals(Domain.SOURCE, source.getDomain().get());

        SimpleMetadataViewInterface technical = getSmvi(mvwviList, "technical");
        assertEquals(Domain.TECHNICAL, technical.getDomain().get());

        SimpleMetadataViewInterface unspecifiedKey = getSmvi(mvwviList, "unspecifiedKey");
        assertThat(unspecifiedKey.getDomain(), is(Optional.empty()));
    }

    /**
     * This test verifies that different rules (key-based and division-based)
     * are merged correctly.
     */
    @Test
    public void testRulesAreCorrectlyMerged() throws IOException {
        RulesetManagement underTest = new RulesetManagement();
        underTest.load(new File("src/test/resources/testRulesAreCorrectlyMerged.xml"));

        StructuralElementViewInterface sevi = underTest.getStructuralElementView(BOOK, "", ENGL);
        List<MetadataViewWithValuesInterface<Object>> mvwviList = sevi
                .getSortedVisibleMetadata(Collections.emptyMap(), Collections.singletonList("personContributor"));
        ComplexMetadataViewInterface personContributor = getCmvi(mvwviList, "personContributor");
        List<MetadataViewWithValuesInterface<Object>> visible = personContributor
                .getSortedVisibleMetadata(Collections.emptyMap(), Collections.emptyList());
        assertThat(ids(visible), contains("role", "gndRecord", "givenName", "surname"));
        assertThat(getSmvi(visible, "role").getSelectItems().keySet(), contains("author", "editor"));
    }

    /**
     * This test verifies that unspecified unrestricted rules remove keys that
     * have a maxOccurs of zero.
     */
    @Test
    public void testRulesRemoveKeysWithZeroMaxOccurs() throws IOException {
        RulesetManagement underTest = new RulesetManagement();
        underTest.load(new File("src/test/resources/testRulesRemoveKeysWithZeroMaxOccurs.xml"));
        StructuralElementViewInterface sevi = underTest.getStructuralElementView(BOOK, "", ENGL);
        Collection<MetadataViewInterface> mviColl = sevi.getAddableMetadata(Collections.emptyMap(),
                Collections.emptyList());
        assertTrue(ids(mviColl).contains("keep"));
    }

    /**
     * This test verifies that the input type is set depending on the codomain.
     */
    @Test
    public void testTheDisplayModeIsSetUsingTheCodomain() throws IOException {
        RulesetManagement underTest = new RulesetManagement();
        underTest.load(new File("src/test/resources/testTheDisplayModeIsSetUsingTheCodomain.xml"));

        StructuralElementViewInterface sevi = underTest.getStructuralElementView(BOOK, "", ENGL);
        List<MetadataViewWithValuesInterface<Object>> mvwviList = sevi
                .getSortedVisibleMetadata(Collections.emptyMap(), Arrays.asList("defaultString", "anyURI",
                        "boolean", "date", "integer", "namespace", "namespaceString"));

        assertEquals(InputType.ONE_LINE_TEXT, getSmvi(mvwviList, "defaultString").getInputType());
        assertEquals(InputType.ONE_LINE_TEXT, getSmvi(mvwviList, "anyURI").getInputType());
        assertEquals(InputType.BOOLEAN, getSmvi(mvwviList, "boolean").getInputType());
        assertEquals(InputType.DATE, getSmvi(mvwviList, "date").getInputType());
        assertEquals(InputType.INTEGER, getSmvi(mvwviList, "integer").getInputType());
        assertEquals(InputType.ONE_LINE_TEXT, getSmvi(mvwviList, "namespace").getInputType());
        assertEquals(InputType.ONE_LINE_TEXT, getSmvi(mvwviList, "namespaceString").getInputType());
    }

    /**
     * This test verifies that unspecified forbidden rules restrict the allowed
     * divisions.
     */
    @Test
    public void testUnspecifiedForbiddenRulesRestrictDivisions() throws IOException {
        RulesetManagement underTest = new RulesetManagement();
        underTest.load(new File("src/test/resources/testUnspecifiedForbiddenRulesRestrictDivisions.xml"));
        StructuralElementViewInterface sevi = underTest.getStructuralElementView(BOOK, "", ENGL);
        assertTrue(sevi.getAllowedSubstructuralElements().keySet().contains("chapter"));
    }

    /**
     * This test verifies that unspecified forbidden rules restrict the allowed
     * keys.
     */
    @Test
    public void testUnspecifiedForbiddenRulesRestrictKeys() throws IOException {
        RulesetManagement underTest = new RulesetManagement();
        underTest.load(new File("src/test/resources/testUnspecifiedForbiddenRulesRestrictKeys.xml"));
        StructuralElementViewInterface sevi = underTest.getStructuralElementView(BOOK, "", ENGL);
        Collection<MetadataViewInterface> mviColl = sevi.getAddableMetadata(Collections.emptyMap(),
                Collections.emptyList());
        assertTrue(ids(mviColl).contains("allowed"));
    }

    /**
     * This test verifies that unspecified forbidden rules restrict the allowed
     * options.
     */
    @Test
    public void testUnspecifiedForbiddenRulesRestrictOptions() throws IOException {
        RulesetManagement underTest = new RulesetManagement();
        underTest.load(new File("src/test/resources/testUnspecifiedForbiddenRulesRestrictOptions.xml"));
        StructuralElementViewInterface sevi = underTest.getStructuralElementView(BOOK, "", ENGL);

        List<MetadataViewWithValuesInterface<Object>> mvwviList = sevi
                .getSortedVisibleMetadata(Collections.emptyMap(), Collections.singletonList(TEST));
        SimpleMetadataViewInterface test = getSmvi(mvwviList, TEST);
        assertThat(test.getSelectItems().keySet(), contains(OPT, "opt3", "opt5", "opt7"));

    }

    /**
     * This test verifies that unspecified forbidden rules restrict the allowed
     * options during validation.
     */
    @Test
    public void testUnspecifiedForbiddenRulesRestrictOptionsAlsoInTheValidation() throws IOException {
        RulesetManagement underTest = new RulesetManagement();
        underTest.load(
                new File("src/test/resources/testUnspecifiedForbiddenRulesRestrictOptionsAlsoInTheValidation.xml"));
        StructuralElementViewInterface sevi = underTest.getStructuralElementView(BOOK, "", ENGL);

        List<MetadataViewWithValuesInterface<Object>> mvwviList = sevi
                .getSortedVisibleMetadata(Collections.emptyMap(), Collections.singletonList(TEST));
        SimpleMetadataViewInterface test = getSmvi(mvwviList, TEST);
        assertTrue(test.isValid(OPT));
        assertFalse(test.isValid("opt2"));
        assertTrue(test.isValid("opt3"));
        assertFalse(test.isValid("opt4"));
        assertTrue(test.isValid("opt5"));
        assertFalse(test.isValid("opt6"));
        assertTrue(test.isValid("opt7"));
        assertFalse(test.isValid("mischief"));
    }

    /**
     * This test verifies that unspecified unrestricted rules are resorting but
     * not restricting the divisions.
     */
    @Test
    public void testUnspecifiedUnrestrictedRulesSortDivisionsWithoutRestrictingThem() throws IOException {
        RulesetManagement underTest = new RulesetManagement();
        underTest.load(new File(
                "src/test/resources/testUnspecifiedUnrestrictedRulesSortDivisionsWithoutRestrictingThem.xml"));

        StructuralElementViewInterface sevi = underTest.getStructuralElementView("box", "", ENGL);
        assertThat(sevi.getAllowedSubstructuralElements().keySet(), contains(
                // in order as in the ruleset
                "phonographicRecord", "cassette", "cd", "gramophoneRecord", "pianoRoll",

                // the rest in alphabetical order by its label
                "article", BOOK, "box", "chalcography", "colorChart", "dvd", "frontCover", "film", "multiVolume",
                "serial", "side", "videoTape", "wireRecording"));
    }

    /**
     * This test verifies that unspecified unrestricted rules are resorting but
     * not restricting the keys.
     */
    @Test
    public void testUnspecifiedUnrestrictedRulesSortKeysWithoutRestrictingThem() throws IOException {
        RulesetManagement underTest = new RulesetManagement();
        underTest.load(
                new File("src/test/resources/testUnspecifiedUnrestrictedRulesSortKeysWithoutRestrictingThem.xml"));
        StructuralElementViewInterface sevi = underTest.getStructuralElementView("article", "", ENGL);
        Collection<MetadataViewInterface> mviColl = sevi.getAddableMetadata(Collections.emptyMap(),
                Collections.emptyList());

        assertThat(ids(mviColl),
                contains("author", "year", "title", "journal", "journalAbbr", "issue", "abstract"));
    }

    /**
     * The test verifies that unspecified unrestricted rules are resorting but
     * not restricting the options.
     */
    @Test
    public void testUnspecifiedUnrestrictedRulesSortOptionsWithoutRestrictingThem() throws IOException {
        RulesetManagement underTest = new RulesetManagement();
        underTest.load(new File(
                "src/test/resources/testUnspecifiedUnrestrictedRulesSortOptionsWithoutRestrictingThem.xml"));
        StructuralElementViewInterface sevi = underTest.getStructuralElementView(BOOK, "", ENGL);
        List<MetadataViewWithValuesInterface<Object>> mvwviList = sevi
                .getSortedVisibleMetadata(Collections.emptyMap(), Collections.singletonList(TEST));
        SimpleMetadataViewInterface test = getSmvi(mvwviList, TEST);
        assertThat(test.getSelectItems().keySet(), contains("opt4", "opt7", OPT, "opt2", "opt3", "opt5", "opt6"));
    }

    /**
     * The test verifies that the codomain is validated correctly.
     */
    @Test
    public void testValidationByCodomain() throws IOException {
        RulesetManagement underTest = new RulesetManagement();
        underTest.load(new File("src/test/resources/testValidationByCodomain.xml"));

        StructuralElementViewInterface sevi = underTest.getStructuralElementView(BOOK, "", ENGL);
        List<MetadataViewWithValuesInterface<Object>> mvwviList = sevi.getSortedVisibleMetadata(
                Collections.emptyMap(),
                Arrays.asList("default", "defaultOpt", "anyUri", "boolean", "date", "integer", "namespaceDefault",
                        "namespaceString", "namespaceDefaultOpt", "namespaceStringOpt", "namespaceDefaultExternal",
                        "namespaceStringExternal"));

        SimpleMetadataViewInterface defaultMv = getSmvi(mvwviList, "default");
        assertTrue(defaultMv.isValid(HELLO_WORLD));

        SimpleMetadataViewInterface defaultOpt = getSmvi(mvwviList, "defaultOpt");
        assertTrue(defaultOpt.isValid("val1"));
        assertFalse(defaultOpt.isValid("val9"));

        SimpleMetadataViewInterface anyUri = getSmvi(mvwviList, "anyUri");
        assertTrue(anyUri.isValid("https://www.kitodo.org/software/kitodoproduction/"));
        assertTrue(anyUri.isValid("mailto:contact@kitodo.org"));
        assertTrue(anyUri.isValid("urn:nbn:de-9999-12345678X"));
        assertFalse(anyUri.isValid(HELLO_WORLD));

        SimpleMetadataViewInterface booleanMv = getSmvi(mvwviList, "boolean");
        assertTrue(booleanMv.isValid("on"));
        assertFalse(booleanMv.isValid(HELLO_WORLD));

        SimpleMetadataViewInterface date = getSmvi(mvwviList, "date");
        assertTrue(date.isValid("1492-10-12"));
        assertFalse(date.isValid("1900-02-29"));

        SimpleMetadataViewInterface integer = getSmvi(mvwviList, "integer");
        assertTrue(integer.isValid("1234567"));
        assertFalse(integer.isValid("1 + 1i"));

        SimpleMetadataViewInterface namespaceDefault = getSmvi(mvwviList, "namespaceDefault");
        assertTrue(namespaceDefault.isValid("http://test.example/testValidation/alice"));
        assertFalse(namespaceDefault.isValid("http://test.example/testValidation#bob"));
        assertFalse(namespaceDefault.isValid("https://www.wdrmaus.de/"));

        SimpleMetadataViewInterface namespaceString = getSmvi(mvwviList, "namespaceString");
        assertTrue(namespaceString.isValid("http://test.example/testValidation/alice"));
        assertTrue(namespaceString.isValid("{http://test.example/testValidation/}bob"));
        assertFalse(namespaceString.isValid("https://www.wdrmaus.de/"));

        SimpleMetadataViewInterface namespaceOpt = getSmvi(mvwviList, "namespaceDefaultOpt");
        assertTrue(namespaceOpt.isValid("http://test.example/testValidation/value1"));
        assertFalse(namespaceOpt.isValid("http://test.example/testValidation/value4"));

        SimpleMetadataViewInterface namespaceStrOpt = getSmvi(mvwviList, "namespaceStringOpt");
        assertTrue(namespaceStrOpt.isValid("http://test.example/testValidation/value1"));
        assertFalse(namespaceStrOpt.isValid("http://test.example/testValidation/value4"));

        SimpleMetadataViewInterface namespaceExt = getSmvi(mvwviList, "namespaceDefaultExternal");
        assertTrue(namespaceExt.isValid("http://test.example/testValidationByCodomainNamespace#val1"));
        assertFalse(namespaceExt.isValid("http://test.example/testValidationByCodomainNamespace#val4"));

        SimpleMetadataViewInterface namespaceStrExt = getSmvi(mvwviList, "namespaceStringExternal");
        assertTrue(namespaceStrExt.isValid("http://test.example/testValidationByCodomainNamespace#val1"));
        assertFalse(namespaceStrExt.isValid("http://test.example/testValidationByCodomainNamespace#val4"));
    }

    /**
     * The method provides a simple access to a meta-data key in a list of
     * MetadataViewWithValuesInterface.
     * 
     * @param mvwviList
     *            list of MetadataViewWithValuesInterface to extract from
     * @param keyId
     *            ID of key to extract
     * @return meta-data key
     */
    private SimpleMetadataViewInterface getSmvi(List<MetadataViewWithValuesInterface<Object>> mvwviList,
            String keyId) {
        return (SimpleMetadataViewInterface) mvwviList.stream().filter(mvwvi -> mvwvi.getMetadata().isPresent())
                .filter(mvwvi -> keyId.equals(mvwvi.getMetadata().get().getId())).findAny().get().getMetadata()
                .get();
    }

    /**
     * The method provides a simple access to a meta-data key in a list of
     * MetadataViewWithValuesInterface.
     * 
     * @param metadataViewWithValuesInterfaceList
     *            list of MetadataViewWithValuesInterface to extract from
     * @param keyId
     *            ID of key to extract
     * @return meta-data key
     */
    private ComplexMetadataViewInterface getCmvi(
            List<MetadataViewWithValuesInterface<Object>> metadataViewWithValuesInterfaceList, String keyId) {
        return (ComplexMetadataViewInterface) metadataViewWithValuesInterfaceList.stream()
                .filter(mvwvi -> mvwvi.getMetadata().isPresent())
                .filter(metadataViewWithValuesInterface -> keyId
                        .equals(metadataViewWithValuesInterface.getMetadata().get().getId()))
                .findAny().get().getMetadata().get();
    }

    /**
     * Returns the IDs of the meta-data keys in a collection of meta-data view
     * interfaces.
     * 
     * @param mviColl
     *            collection of meta-data view interfaces to return the IDs of
     *            the meta-data keys from
     * @return the IDs of the meta-data keys
     */
    private List<String> ids(Collection<MetadataViewInterface> mviColl) {
        return mviColl.stream().map(MetadataViewInterface::getId).collect(Collectors.toList());
    }

    /**
     * Returns the IDs of the meta-data keys in a meta-data view with values
     * interface list.
     * 
     * @param metadataViewWithValuesInterfaceList
     *            meta-data view with values interface list to return the IDs of
     *            the meta-data keys from
     * @return the IDs of the meta-data keys
     */
    private <T> List<String> ids(List<MetadataViewWithValuesInterface<T>> metadataViewWithValuesInterfaceList) {
        return metadataViewWithValuesInterfaceList.stream().filter(
                metadataViewWithValuesInterface -> metadataViewWithValuesInterface.getMetadata().isPresent())
                .map(metadataViewWithValuesInterface -> metadataViewWithValuesInterface.getMetadata().get().getId())
                .collect(Collectors.toList());
    }
}