Relevanzen

Informationen über die Relevanz eines Attributs sind nützlich, um es in einem User Interface sinnvoll darzustellen. So können beispielsweise irrelevante Attribute ausgeblendet werden und Pflichtfelder als solche markiert werden. Um die Faktor-IPS-Relevanzen in Linkki zu nutzen kann der IpsPropertyDispatcher eingesetzt werden (siehe https://doc.linkki-framework.org/1.4.0-vaadin8/09_extending_linkki/05_faktor-ips_extension.html#ips-property-dispatcherFaktor-IPS Property Dispatcher).

Die Relevanz von Vertragsattributen wird über ihre Wertebereiche dargestellt.

  • Ein Attribut gilt als irrelevant, wenn es einen leeren Wertebereich hat. Für irrelevante Attribute dürfen keine Werte gesetzt werden.

  • Ein Attribut gilt als optional, wenn der Wertebereich null enthält. Ein Wert darf hier gesetzt werden, jedoch ist dieser nicht verpflichtend.

  • Ein Attribut gilt als Pflichtfeld, wenn der Wertebereich kein null enthält und nicht leer ist. Für diese Attribute müssen Werte gesetzt werden.

Wenn ein Attribut nicht irrelevant ist, also entweder optional oder ein Pflichtfeld ist, gilt es als relevant. Der Wert des Attributs sollte dann gegen den Wertebereich geprüft werden.

Welche Relevanz ein Attribut hat, kann in der Produktdefinition eingestellt werden. Dafür muss im Modell der Haken "Die Relevanz des Attributs und der Vorbelegungswert sind in der Produktkonfiguration definiert." am Attribut gesetzt werden:

enable relevance product

Anschließend sind im Produkt für das Attribut Radiobuttons zum Einstellen der Relevanz verfügbar. Um die Relevanz zu steuern ist es nicht notwendig zusätzlich den Defaultwert und Wertebereich zu konfigurieren.

relevance options

Generische Validierung

Relevanz-Validierung

Um zu prüfen, ob der Wert eines Attributs zur eingestellten Relevanz passt, mussten bisher an jedem Attribut Validierungregeln mit individuellen Fehlermeldungen angelegt werden. Durch die Aktivierung der generischen Validierung werden die Relevanzprüfungen mit einheitlichen Fehlermeldungen automatisch generiert. Die Checkbox zum Aktivieren befindet sich auf dem Validierungsregel-Tab des Attributs.

enable generic validation

Wenn ein Attribut generisch validiert wird, werden alle drei Relevanzprüfungen durchgeführt:

  • Das Attribut hat einen leeren Wertebereich: Es darf kein Wert gesetzt sein

  • Das Attribut ist ein Pflichtfeld (Der Wertebereich enthält nicht null): Ein Wert muss gesetzt sein.

  • Das Attribut ist relevant: Der gesetzte Wert muss Teil des Wertebereichs sein.

Wenn ein Attribut ein anderes Attribut überschreibt für das die generische Validierung aktiviert ist, dann ist die generische Validierung für das überschreibende Attribut automatisch auch aktiviert und kann auch nicht deaktiviert werden.

In der .ipsproject-Konfigurationsdatei kann über <Setting enabled="true" name="genericValidationDefault"/> eingestellt werden, dass die generische Validierung für alle neuen Attribute automatisch aktiviert sein soll.

Die Fehlermeldungen können über eine eigene Implementierung von IGenericAttributeValidationConfiguration (oder eine Ableitung der DefaultGenericAttributeValidationConfiguration) angepasst werden. Außerdem können dabei bestimmte Attribute von der Validierung ausgeschlossen werden. Die Implementierung wird im ValidationContext übergeben.

IGenericAttributeValidationConfiguration genericValidationConfig
    = new IGenericAttributeValidationConfiguration() {

        @Override
        public boolean shouldValidate(PolicyAttribute policyAttribute,
                IModelObject modelObject) {
            return true;
        }

        @Override
        public Message createMessageForMissingMandatoryValue(PolicyAttribute policyAttribute,
                IModelObject modelObject) {
            return Message.error("Das Attribut muss einen Wert besitzen.")
                    .code("PFLICHTFELD")
                    .invalidObjectWithProperties(modelObject, policyAttribute.getName())
                    .create();
        }

        @Override
        public Message createMessageForValuePresentForIrrelevantAttribute(PolicyAttribute policyAttribute,
                IModelObject modelObject) {
            return Message.error("Das Attribut darf keinen Wert besitzen.")
                    .code("IRRELEVANT")
                    .invalidObjectWithProperties(modelObject, policyAttribute.getName())
                    .create();
        }

        @Override
        public Message createMessageForValueNotInAllowedValueSet(PolicyAttribute policyAttribute,
                IModelObject modelObject) {
            return Message.error("Der Wert ist nicht im Wertebereich.")
                    .code("WERTEBEREICH")
                    .invalidObjectWithProperties(modelObject, policyAttribute.getName())
                    .create();
        }

    };

MessageList messages = vertrag.validate(new ValidationContext(Locale.GERMANY,
        this.getClass().getClassLoader(), genericValidationConfig));

Falls keine eigene Implementierung im ValidationContext übergeben wird, wird automatisch die DefaultGenericAttributeValidationConfiguration genutzt.
Alternativ kann die DefaultGenericAttributeValidationConfiguration auch für eine eigene Implementierung abgeleitet und im ValidationContext übergeben werden um die Fehlermeldungen anzupassen. Um lediglich die Fehlertexte auszutauschen, genügt es im Konstruktor ein eigenes Resourcebundle zu übergeben.

DefaultGenericAttributeValidationConfiguration defaultGenericValidationConfig
    = new DefaultGenericAttributeValidationConfiguration(ResourceBundle.getBundle("myMessages"), Locale.US) {

        @Override
        public boolean shouldValidate(PolicyAttribute policyAttribute,
                IModelObject modelObject) {
            if(modelObject instanceof Fahrzeug
                    && policyAttribute.getName().equals(Fahrzeug.PROPERTY_HUBRAUM)
                    && ((Fahrzeug)modelObject).getEingabeVariante()==FahrzeugAngaben.HSN_TSN)) {
                return false;
            }
            return true;
        }

    };

MessageList messages = vertrag.validate(new ValidationContext(Locale.GERMANY,
        this.getClass().getClassLoader(), defaultGenericValidationConfig));

Wenn ein Marker für die Pflichtfeldvalidierung gesetzt werden soll, muss dieser im Konstruktor von DefaultGenericAttributeValidationConfiguration übergeben werden. Der Marker wird dann in allen Messages für fehlgeschlagene Pflichtfeldprüfungen gesetzt.

IMarker mandatoryMarker = MyMarkers.MissingMandatory; // Java-Enum
DefaultGenericAttributeValidationConfiguration defaultGenericValidationConfig = new DefaultGenericAttributeValidationConfiguration(Locale.GERMANY, mandatoryMarker);
MessageList messages = vertrag.validate(new ValidationContext(Locale.GERMANY,
        this.getClass().getClassLoader(), defaultGenericValidationConfig));

Die DefaultGenericAttributeValidationConfiguration generiert alle Messagecodes nach dem folgenden Pattern: <PREFIX>.<KLASSE>.<ATTRIBUT>

  • <PREFIX>: "IRRELEVANT"/"MANDATORY"/"INVALID", abhängig davon welche Relevanzprüfung fehlgeschlagen ist.

  • <KLASSE>: Der Name der Vertragsklasse, in der die generische Validierung am Attribut aktiviert wurde.

  • <ATTRIBUT>: Der Name des geprüften Attributs.

Wenn beispielsweise die Validierung des Pflichtattributs "wohnflaeche" aus den obigen Screenshots fehlschlägt, weil kein Wert gesetzt wurde, dann wird die generierte Error-Message folgenden Message-Code enthalten: MANDATORY.HausratVertrag.wohnflaeche.

Datenbankfeld-Längen-Validierung

Die Klasse DatabaseLengthValidation stellt Funktionen zur generischen Validierung von Datenbankfeld-Längen bereit. Diese Validierung ermöglicht es, technische Längenbeschränkungen der Datenbank unabhängig von fachlichen Wertebereichen zu prüfen. Dadurch können die Wertebereiche im Produktbaustein rein für fachliche Aspekte verwendet werden, was die Produktkonfiguration übersichtlicher macht. Wichtig zu beachten ist, dass DatabaseLengthValidation nicht gegen Einschränkungen validiert, die möglicherweise durch JPA-Annotationen definiert sind.

Motivation
  • Trennung von technischen und fachlichen Validierungen

  • Vermeidung von unübersichtlichen Wertebereichen in der Produktkonfiguration

  • Zentrale Definition von Datenbank-Maximallängen statt Einzelkonfiguration pro Attribut

  • Bewusste Längenprüfung bei Strings

Längen-Validierung

Über die DefaultDatabaseLengthValidationConfiguration die das Builder-Pattern unterstüzt wird die Validierung konfiguriert. Die Konfigurierte Validierung kann dabei ein Modellobjekt oder über das Visitor-Pattern auch die Kinder eines Modellobjekts überprüfen. Falls das Modell IVisitorSupport nicht selbst implementiert wird der GenericVisitorSupport verwendet.

Hier zum Beispiel eine Stringlängenvalidierung die sicher stellt, dass String-Werte die maximale Byte-Länge nicht überschreiten:

// String-Länge validieren (max. 10 Bytes)
DefaultDatabaseLengthValidationConfiguration config = new DefaultDatabaseLengthValidationConfiguration(Locale.GERMAN)
                .withStringLengthConstraint(10);
DatabaseLengthValidation validation = DatabaseLengthValidation.with(config);

// nur das ModelObject
validation.validateOnly(modelObject);

// das ModelObject mit seinen Kindern
validation.validateWithChildren(modelObject);

Die tatsächliche Byte-Länge eines Strings wird auf Basis der UTF-8-Kodierung (oder gegebenenfalls einer anderen, in der Konfiguration angegebenen) validiert, nicht anhand der Anzahl der Zeichen. UTF-8 kodiert Unicode-Zeichen variabel in 1–4 Bytes.

Numerische-Längen-Validierungen können einzeln oder für alle von Faktor-IPS unterstützen numerischen Datentypen konfiguriert werden. Hier zum Beispiel für eine Konfiguration von allen unterstützen numerischen Attribute mit der gleichen Defintion von Präzision und Nachkommastellen:

// Alle numerischen Attribute validieren (Präzision 10, Nachkommastellen 2)
DefaultDatabaseLengthValidationConfiguration config = new DefaultDatabaseLengthValidationConfiguration(Locale.GERMAN)
                .withNumericConstraintForAllNumbers(10, 2);
DatabaseLengthValidation validation = DatabaseLengthValidation.with(config);

// nur das ModelObject
validation.validateOnly(modelObject);

// das ModelObject mit seinen Kindern
validation.validateWithChildren(modelObject);

Die zu validierenden Attribute können gezielt über ein BiPredicate<PolicyAttribute, IModelObject> gefiltert werden. Hier zum Beispiel wird das Attribute someString am Object Policy mit einer maximalen Länge von 5 validiert, alle anderen Attribute werden ignoriert:

DefaultDatabaseLengthValidationConfiguration config = new DefaultDatabaseLengthValidationConfiguration(Locale.GERMAN)
        .withStringLengthConstraint(5)
        .withAttributeFilter((a, m) -> Policy.PROPERTY_SOMESTRING.equals(a.getName()));

DatabaseLengthValidation validation = DatabaseLengthValidation.with(config);

// nur das ModelObject
validation.validateOnly(modelObject);
Fehlermeldungen

Bei Verletzung der Längenrestriktionen werden folgende Message-Codes erzeugt:

  • DefaultDatabaseLengthValidationConfiguration.MSGCODE_STRING_TOO_LONG - bei Überschreitung der maximalen String-Länge

  • DefaultDatabaseLengthValidationConfiguration.MSGCODE_NUMBER_EXCEEDS_PRECISION - bei Überschreitung der Präzision, also der gesamten Anzahlm an Stellen

  • DefaultDatabaseLengthValidationConfiguration.MSGCODE_NUMBER_EXCEEDS_SCALE - bei Überschreitung der Nachkommastellen