From 66f523f8ae98eaee3f8e86e6a957fe9423a245d5 Mon Sep 17 00:00:00 2001 From: encrypTimM Date: Thu, 16 Jun 2022 18:34:03 +0200 Subject: [PATCH] Verbesserte Validierung Doppelte Elementnamen/Attribute/Methoden sind nicht mehr erlaubt, Fehlerhafte Eingaben werden hervorgehoben --- .../classifier/basis/Einstellungen.java | 2 + .../classifier/gui/HauptAnsicht.java | 5 +- .../classifier/gui/ProjektAnsicht.java | 33 +- .../classifier/gui/ProjekteAnsicht.java | 15 + .../classifier/gui/ProjekteKontrolle.java | 45 +- .../UMLKlassifiziererBearbeitenDialog.java | 525 +++++++++--------- .../gui/elemente/MenueLeisteKomponente.java | 9 +- .../classifier/gui/util/NodeUtil.java | 142 +++-- .../resources/ressourcen/css/classifier.css | 5 + .../de/MenueLeisteAnsicht_de.properties | 1 + ...assifiziererBearbeitenDialog_de.properties | 3 + 11 files changed, 437 insertions(+), 348 deletions(-) diff --git a/src/main/java/io/github/aid_labor/classifier/basis/Einstellungen.java b/src/main/java/io/github/aid_labor/classifier/basis/Einstellungen.java index 0acdcda..d9748a7 100644 --- a/src/main/java/io/github/aid_labor/classifier/basis/Einstellungen.java +++ b/src/main/java/io/github/aid_labor/classifier/basis/Einstellungen.java @@ -149,6 +149,7 @@ private static Optional laden(Path quelle) { public final JsonBooleanProperty zeigePackageModifier; public final JsonBooleanProperty zeigeVoid; public final JsonBooleanProperty zeigeParameterNamen; + public final JsonBooleanProperty erweiterteValidierungAktivieren; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // * Konstruktoren * @@ -199,6 +200,7 @@ public boolean remove(Object o) { this.zeigePackageModifier = new JsonBooleanProperty(true); this.zeigeVoid = new JsonBooleanProperty(false); this.zeigeParameterNamen = new JsonBooleanProperty(true); + this.erweiterteValidierungAktivieren = new JsonBooleanProperty(true); } /** diff --git a/src/main/java/io/github/aid_labor/classifier/gui/HauptAnsicht.java b/src/main/java/io/github/aid_labor/classifier/gui/HauptAnsicht.java index 7c0b8d8..9f46278 100644 --- a/src/main/java/io/github/aid_labor/classifier/gui/HauptAnsicht.java +++ b/src/main/java/io/github/aid_labor/classifier/gui/HauptAnsicht.java @@ -294,6 +294,9 @@ private void setzeMenueAktionen(MenueLeisteKomponente menue) { .bindBidirectional(Einstellungen.getBenutzerdefiniert().zeigeVoid); menue.getParameternamenAnzeigen().selectedProperty() .bindBidirectional(Einstellungen.getBenutzerdefiniert().zeigeParameterNamen); + menue.getErweiterteValidierungAktivieren().selectedProperty() + .bindBidirectional( + Einstellungen.getBenutzerdefiniert().erweiterteValidierungAktivieren); menue.getInfo().setOnAction(e -> { var info = new InfoAnsicht(programm, rechnerService); FensterUtil.initialisiereElternFenster(wurzel.getScene().getWindow(), info); @@ -618,7 +621,7 @@ private void auswahlEinfuegen() { umlKopie.getPosition().setY(y); return umlKopie; }).toList(); - projekteAnsicht.getAngezeigtesProjekt().getDiagrammElemente().addAll(kopie); + projekteAnsicht.fuegeEin(kopie); } private void auswahlLoeschen() { diff --git a/src/main/java/io/github/aid_labor/classifier/gui/ProjektAnsicht.java b/src/main/java/io/github/aid_labor/classifier/gui/ProjektAnsicht.java index 280c76a..730dd46 100644 --- a/src/main/java/io/github/aid_labor/classifier/gui/ProjektAnsicht.java +++ b/src/main/java/io/github/aid_labor/classifier/gui/ProjektAnsicht.java @@ -6,13 +6,12 @@ package io.github.aid_labor.classifier.gui; -import java.lang.reflect.InvocationTargetException; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.BiConsumer; -import java.util.logging.Level; +import java.util.function.Supplier; import java.util.logging.Logger; import com.dlsc.gemsfx.DialogPane; @@ -151,9 +150,7 @@ public ReadOnlyBooleanProperty kannKleinerZoomenProperty() { } public List getSelektion() { - return selektion.stream().map(node -> { - return node.getUmlElement(); - }).toList(); + return selektion.stream().map(UMLElementBasisAnsicht::getUmlElement).toList(); } public BooleanBinding hatSelektionProperty() { @@ -321,18 +318,17 @@ private void fuegeHinzu(UMLElementBasisAnsicht ans if (ansicht.getUmlElement() instanceof UMLKlassifizierer klassifizierer) { bearbeitenDialogOeffnen(ansicht, klassifizierer, - UMLKlassifiziererBearbeitenDialog.class, projekt.nameProperty().concat(" > ") .concat(new When(klassifizierer.nameProperty().isEmpty()) .then(sprache.getText("unbenannt", "Unbenannt")) - .otherwise(klassifizierer.nameProperty()))); + .otherwise(klassifizierer.nameProperty())), + () -> new UMLKlassifiziererBearbeitenDialog(klassifizierer, projekt)); } else if (ansicht.getUmlElement() instanceof UMLKommentar kommentar) { bearbeitenDialogOeffnen(ansicht, kommentar, - UMLKommentarBearbeitenDialog.class, projekt.nameProperty().concat(" > ") .concat(sprache.getTextProperty("kommentarBearbeitenTitel", - "Kommentar bearbeiten"))); - + "Kommentar bearbeiten")), + () -> new UMLKommentarBearbeitenDialog(kommentar)); } Runnable vorPositionBearbeitung = () -> { @@ -356,25 +352,14 @@ private void fuegeHinzu(UMLElementBasisAnsicht ans } private void bearbeitenDialogOeffnen(Node ansicht, - T element, Class dialogTyp, ObservableValue titel) { + T element, ObservableValue titel, + Supplier dialogKonstruktor) { ansicht.addEventFilter(MouseEvent.MOUSE_CLICKED, event -> { if (event.getClickCount() == 2 && !event.isConsumed()) { event.consume(); var alterStatus = projekt.getUeberwachungsStatus(); projekt.setUeberwachungsStatus(UeberwachungsStatus.ZUSAMMENFASSEN); - Alert dialog; - try { - dialog = dialogTyp.getConstructor(element.getClass()).newInstance(element); - } catch (InstantiationException | IllegalAccessException - | IllegalArgumentException | InvocationTargetException - | NoSuchMethodException | SecurityException e) { - log.log(Level.SEVERE, e, - () -> "Dialog konnte nicht mit dem geforderten Konstruktor " - + ">>public %s(%s)<< instanziiert werden!" - .formatted(dialogTyp.getSimpleName(), - element.getClass().getSimpleName())); - return; - } + Alert dialog = dialogKonstruktor.get(); dialog.initOwner(ansicht.getScene().getWindow()); dialog.titleProperty().bind(titel); dialog.showAndWait().ifPresent(button -> { diff --git a/src/main/java/io/github/aid_labor/classifier/gui/ProjekteAnsicht.java b/src/main/java/io/github/aid_labor/classifier/gui/ProjekteAnsicht.java index 9e89e02..fb0bfeb 100644 --- a/src/main/java/io/github/aid_labor/classifier/gui/ProjekteAnsicht.java +++ b/src/main/java/io/github/aid_labor/classifier/gui/ProjekteAnsicht.java @@ -20,6 +20,9 @@ import io.github.aid_labor.classifier.basis.sprachverwaltung.Umlaute; import io.github.aid_labor.classifier.uml.UMLProjekt; import io.github.aid_labor.classifier.uml.klassendiagramm.KlassifiziererTyp; +import io.github.aid_labor.classifier.uml.klassendiagramm.UMLDiagrammElement; +import io.github.aid_labor.classifier.uml.klassendiagramm.UMLKlassifizierer; +import io.github.aid_labor.classifier.uml.klassendiagramm.UMLKommentar; import javafx.application.Platform; import javafx.beans.property.ReadOnlyObjectProperty; import javafx.beans.property.ReadOnlyObjectWrapper; @@ -295,6 +298,18 @@ public void legeKommentarAn() { this.kontroller.legeKommentarAn(); } + public void fuegeEin(Iterable elemente) { + for (var element : elemente) { + if (element instanceof UMLKlassifizierer klassifizierer) { + this.kontroller.legeNeuenKlassifiziererAn(klassifizierer); + } else if (element instanceof UMLKommentar kommentar) { + this.kontroller.legeKommentarAn(kommentar); + } else { + log.severe(() -> "unbekanntes UMLDiagrammelement: " + element); + } + } + } + // protected ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## // package ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## diff --git a/src/main/java/io/github/aid_labor/classifier/gui/ProjekteKontrolle.java b/src/main/java/io/github/aid_labor/classifier/gui/ProjekteKontrolle.java index 98cd712..d11ba00 100644 --- a/src/main/java/io/github/aid_labor/classifier/gui/ProjekteKontrolle.java +++ b/src/main/java/io/github/aid_labor/classifier/gui/ProjekteKontrolle.java @@ -6,6 +6,7 @@ package io.github.aid_labor.classifier.gui; +import java.util.function.Supplier; import java.util.logging.Logger; import io.github.aid_labor.classifier.basis.projekt.UeberwachungsStatus; @@ -75,37 +76,51 @@ class ProjekteKontrolle { void legeNeuenKlassifiziererAn(KlassifiziererTyp typ) { var projekt = this.ansicht.getAngezeigtesProjektProperty().get(); - var klassifizierer = new UMLKlassifizierer(typ, projekt.getProgrammiersprache(), ""); + legeNeuenKlassifiziererAn( + new UMLKlassifizierer(typ, projekt.getProgrammiersprache(), "")); + } + + void legeNeuenKlassifiziererAn(UMLKlassifizierer klassifizierer) { + var projekt = this.ansicht.getAngezeigtesProjektProperty().get(); - var dialog = new UMLKlassifiziererBearbeitenDialog(klassifizierer); - dialog.titleProperty().bind( - projekt.nameProperty().concat(" > ") - .concat(new When(klassifizierer.nameProperty().isEmpty()) - .then(sprache.getText("unbenannt", "Unbenannt")) - .otherwise(klassifizierer.nameProperty()))); - legeDiagrammElementAn(klassifizierer, dialog); + legeDiagrammElementAn(klassifizierer, () -> { + var dialog = new UMLKlassifiziererBearbeitenDialog(klassifizierer, projekt); + dialog.titleProperty().bind( + projekt.nameProperty().concat(" > ") + .concat(new When(klassifizierer.nameProperty().isEmpty()) + .then(sprache.getText("unbenannt", "Unbenannt")) + .otherwise(klassifizierer.nameProperty()))); + return dialog; + }); } void legeKommentarAn() { + legeKommentarAn(new UMLKommentar()); + } + + void legeKommentarAn(UMLKommentar kommentar) { var projekt = this.ansicht.getAngezeigtesProjektProperty().get(); - var kommentar = new UMLKommentar(); - var dialog = new UMLKommentarBearbeitenDialog(kommentar); - dialog.titleProperty().bind(projekt.nameProperty().concat(" > ") - .concat(sprache.getTextProperty("kommentarBearbeitenTitel", - "Kommentar bearbeiten"))); - legeDiagrammElementAn(kommentar, dialog); + legeDiagrammElementAn(kommentar, () -> { + var dialog = new UMLKommentarBearbeitenDialog(kommentar); + dialog.titleProperty().bind(projekt.nameProperty().concat(" > ") + .concat(sprache.getTextProperty("kommentarBearbeitenTitel", + "Kommentar bearbeiten"))); + return dialog; + }); } // private ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## - private void legeDiagrammElementAn(UMLDiagrammElement element, Alert dialog) { + private void legeDiagrammElementAn(UMLDiagrammElement element, + Supplier dialogKonstruktor) { var projekt = this.ansicht.getAngezeigtesProjektProperty().get(); var alterStatus = projekt.getUeberwachungsStatus(); projekt.setUeberwachungsStatus(UeberwachungsStatus.ZUSAMMENFASSEN); projekt.getDiagrammElemente().add(element); + var dialog = dialogKonstruktor.get(); dialog.initOwner(this.ansicht.getAnsicht().getScene().getWindow()); dialog.showAndWait().ifPresent(button -> { switch (button.getButtonData()) { diff --git a/src/main/java/io/github/aid_labor/classifier/gui/UMLKlassifiziererBearbeitenDialog.java b/src/main/java/io/github/aid_labor/classifier/gui/UMLKlassifiziererBearbeitenDialog.java index d719ad0..5878dbc 100644 --- a/src/main/java/io/github/aid_labor/classifier/gui/UMLKlassifiziererBearbeitenDialog.java +++ b/src/main/java/io/github/aid_labor/classifier/gui/UMLKlassifiziererBearbeitenDialog.java @@ -8,13 +8,12 @@ import java.util.LinkedList; import java.util.List; +import java.util.Objects; import java.util.function.BiFunction; import org.controlsfx.control.PopOver; import org.controlsfx.control.PopOver.ArrowLocation; import org.controlsfx.control.SegmentedButton; -import org.controlsfx.validation.Severity; -import org.controlsfx.validation.ValidationMessage; import org.controlsfx.validation.ValidationResult; import org.controlsfx.validation.ValidationSupport; import org.controlsfx.validation.Validator; @@ -29,12 +28,14 @@ import io.github.aid_labor.classifier.basis.sprachverwaltung.SprachUtil; import io.github.aid_labor.classifier.basis.sprachverwaltung.Sprache; import io.github.aid_labor.classifier.gui.util.NodeUtil; +import io.github.aid_labor.classifier.uml.UMLProjekt; import io.github.aid_labor.classifier.uml.eigenschaften.Attribut; import io.github.aid_labor.classifier.uml.eigenschaften.Methode; import io.github.aid_labor.classifier.uml.eigenschaften.Modifizierer; import io.github.aid_labor.classifier.uml.eigenschaften.Parameter; import io.github.aid_labor.classifier.uml.eigenschaften.ProgrammierEigenschaften; import io.github.aid_labor.classifier.uml.klassendiagramm.KlassifiziererTyp; +import io.github.aid_labor.classifier.uml.klassendiagramm.UMLDiagrammElement; import io.github.aid_labor.classifier.uml.klassendiagramm.UMLKlassifizierer; import javafx.application.Platform; import javafx.beans.binding.Bindings; @@ -42,14 +43,17 @@ import javafx.beans.binding.StringExpression; import javafx.beans.binding.When; import javafx.beans.value.ChangeListener; +import javafx.beans.value.WeakChangeListener; import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; +import javafx.collections.WeakListChangeListener; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.geometry.HPos; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Node; +import javafx.scene.Parent; import javafx.scene.control.Alert; import javafx.scene.control.Button; import javafx.scene.control.ButtonBar.ButtonData; @@ -62,8 +66,10 @@ import javafx.scene.control.ScrollPane; import javafx.scene.control.TextField; import javafx.scene.control.TextInputControl; +import javafx.scene.control.Toggle; import javafx.scene.control.ToggleButton; import javafx.scene.control.ToggleGroup; +import javafx.scene.control.Tooltip; import javafx.scene.control.skin.ScrollPaneSkin; import javafx.scene.layout.BorderPane; import javafx.scene.layout.GridPane; @@ -80,28 +86,13 @@ public class UMLKlassifiziererBearbeitenDialog extends Alert { // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // * Klassenattribute * // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - + + public static final String CSS_EINGABE_FEHLER = "eingabefehler"; + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // * Klassenmethoden * // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - private static ValidationMessage leereValidierungsMeldung = new ValidationMessage() { - @Override - public String getText() { - return null; - } - - @Override - public Control getTarget() { - return null; - } - - @Override - public Severity getSeverity() { - return Severity.OK; - } - }; - // ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## // # # // # Instanzen # @@ -120,22 +111,28 @@ public Severity getSeverity() { private final ToggleButton attribute; private final ToggleButton methoden; private final ValidationSupport eingabeValidierung; - private final LinkedList> typBeobachterListe; + private final List> typBeobachterListe; + private final List> validierungsBeobachter; + private final List> listenBeobachter; + private final List vorhandeneElementNamen; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // * Konstruktoren * // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - public UMLKlassifiziererBearbeitenDialog(UMLKlassifizierer klassifizierer) { + public UMLKlassifiziererBearbeitenDialog(UMLKlassifizierer klassifizierer, UMLProjekt projekt) { super(AlertType.NONE); this.klassifizierer = klassifizierer; this.sicherungskopie = klassifizierer.erzeugeTiefeKopie(); this.sprache = new Sprache(); this.eingabeValidierung = new ValidationSupport(); this.typBeobachterListe = new LinkedList<>(); + this.validierungsBeobachter = new LinkedList<>(); + this.listenBeobachter = new LinkedList<>(); + this.vorhandeneElementNamen = projekt.getDiagrammElemente().parallelStream() + .filter(element -> element.getId() != klassifizierer.getId()).map(UMLDiagrammElement::getName).toList(); - boolean spracheGesetzt = SprachUtil.setUpSprache(sprache, - Ressourcen.get().SPRACHDATEIEN_ORDNER.alsPath(), + boolean spracheGesetzt = SprachUtil.setUpSprache(sprache, Ressourcen.get().SPRACHDATEIEN_ORDNER.alsPath(), "UMLKlassifiziererBearbeitenDialog"); if (!spracheGesetzt) { sprache.ignoriereSprachen(); @@ -162,9 +159,26 @@ public UMLKlassifiziererBearbeitenDialog(UMLKlassifizierer klassifizierer) { for (var beobachter : typBeobachterListe) { klassifizierer.typProperty().removeListener(beobachter); } + for (var beobachter : validierungsBeobachter) { + eingabeValidierung.validationResultProperty().removeListener(beobachter); + } + for (Node n : this.getDialogPane().getChildren()) { + entferneAlleBeobachter(n); + } + listenBeobachter.clear(); }); } + private void entferneAlleBeobachter(Node n) { + NodeUtil.entferneSchwacheBeobachtung(n); + n.getProperties().remove(this); + if (n instanceof Parent p) { + for (Node kind : p.getChildrenUnmodifiable()) { + entferneAlleBeobachter(kind); + } + } + } + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // * Getter und Setter * // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * @@ -198,8 +212,8 @@ private HBox initialisiereButtonBar() { for (var button : buttonBar.getButtons()) { button.setFocusTraversable(false); } - buttonBar.getToggleGroup().selectedToggleProperty() - .addListener((property, alteWahl, neueWahl) -> { + NodeUtil.beobachteSchwach(buttonBar, buttonBar.getToggleGroup().selectedToggleProperty(), + (alteWahl, neueWahl) -> { if (neueWahl == null) { buttonBar.getToggleGroup().selectToggle(alteWahl); } @@ -212,37 +226,29 @@ private HBox initialisiereButtonBar() { private void erzeugeInhalt(BorderPane wurzel) { var allgemeinAnzeige = erzeugeAllgemeinAnzeige(); - var attributeAnzeige = erzeugeTabellenAnzeige(new String[] { - "Sichtbarkeit", "Attributname", "Datentyp", "Initialwert", "Getter", "Setter", - "static" - }, this.klassifizierer.getAttribute(), this::erstelleAttributZeile, event -> { - var programmierEigenschaften = klassifizierer.getProgrammiersprache() - .getEigenschaften(); - this.klassifizierer.getAttribute() - .add(new Attribut( - programmierEigenschaften - .getStandardAttributModifizierer(klassifizierer.getTyp()), - programmierEigenschaften.getLetzerDatentyp())); - }); - var methodenAnzeige = erzeugeTabellenAnzeige(new String[] { - "Sichtbarkeit", "Methodenname", "Parameterliste", "Rueckgabetyp", "abstrakt", - "final", "static" - }, this.klassifizierer.getMethoden(), this::erstelleMethodenZeile, event -> { - var programmierEigenschaften = klassifizierer.getProgrammiersprache() - .getEigenschaften(); - var methode = new Methode( - programmierEigenschaften - .getStandardMethodenModifizierer(klassifizierer.getTyp()), - programmierEigenschaften.getLetzerDatentyp(), - klassifizierer.getProgrammiersprache()); - if (klassifizierer.getTyp().equals(KlassifiziererTyp.Interface)) { - methode.setzeAbstrakt(true); - } - this.klassifizierer.getMethoden().add(methode); - }); + var attributeAnzeige = erzeugeTabellenAnzeige(new String[] { "Sichtbarkeit", "Attributname", "Datentyp", + "Initialwert", "Getter", "Setter", "static" }, this.klassifizierer.getAttribute(), + this::erstelleAttributZeile, event -> { + var programmierEigenschaften = klassifizierer.getProgrammiersprache().getEigenschaften(); + this.klassifizierer.getAttribute() + .add(new Attribut( + programmierEigenschaften.getStandardAttributModifizierer(klassifizierer.getTyp()), + programmierEigenschaften.getLetzerDatentyp())); + }); + var methodenAnzeige = erzeugeTabellenAnzeige(new String[] { "Sichtbarkeit", "Methodenname", "Parameterliste", + "Rueckgabetyp", "abstrakt", "final", "static" }, this.klassifizierer.getMethoden(), + this::erstelleMethodenZeile, event -> { + var programmierEigenschaften = klassifizierer.getProgrammiersprache().getEigenschaften(); + var methode = new Methode( + programmierEigenschaften.getStandardMethodenModifizierer(klassifizierer.getTyp()), + programmierEigenschaften.getLetzerDatentyp(), klassifizierer.getProgrammiersprache()); + if (klassifizierer.getTyp().equals(KlassifiziererTyp.Interface)) { + methode.setzeAbstrakt(true); + } + this.klassifizierer.getMethoden().add(methode); + }); - StackPane container = new StackPane(allgemeinAnzeige, attributeAnzeige, - methodenAnzeige); + StackPane container = new StackPane(allgemeinAnzeige, attributeAnzeige, methodenAnzeige); container.setPadding(new Insets(0, 20, 10, 20)); container.setMaxWidth(Region.USE_PREF_SIZE); container.setAlignment(Pos.TOP_CENTER); @@ -262,14 +268,11 @@ private void ueberwacheSelektion(ToggleButton button, Node anzeige) { private GridPane erzeugeAllgemeinAnzeige() { GridPane tabelle = new GridPane(); - String[] labelBezeichnungen = { - "Typ", "Paket", "Name", "Sichtbarkeit", "Superklasse", "Interfaces" - }; + String[] labelBezeichnungen = { "Typ", "Paket", "Name", "Sichtbarkeit", "Superklasse", "Interfaces" }; for (int zeile = 0; zeile < labelBezeichnungen.length; zeile++) { String bezeichnung = labelBezeichnungen[zeile]; - var label = SprachUtil.bindText(new Label(), sprache, bezeichnung, - bezeichnung + ":"); + var label = SprachUtil.bindText(new Label(), sprache, bezeichnung, bezeichnung + ":"); tabelle.add(label, 0, zeile); } @@ -294,12 +297,10 @@ private GridPane erzeugeAllgemeinAnzeige() { TextField interfaces = new TextField(this.klassifizierer.getInterfaces()); tabelle.add(interfaces, 1, 5); - typ.getSelectionModel().selectedItemProperty() - .addListener((p, alt, neu) -> this.klassifizierer.setTyp(neu)); + NodeUtil.beobachteSchwach(typ, typ.getSelectionModel().selectedItemProperty(), klassifizierer::setTyp); this.klassifizierer.getPaketProperty().bindBidirectional(paket.textProperty()); this.klassifizierer.nameProperty().bindBidirectional(name.textProperty()); - this.klassifizierer.superklasseProperty() - .bindBidirectional(superklasse.textProperty()); + this.klassifizierer.superklasseProperty().bindBidirectional(superklasse.textProperty()); this.klassifizierer.interfacesProperty().bindBidirectional(interfaces.textProperty()); tabelle.setHgap(5); @@ -307,26 +308,27 @@ private GridPane erzeugeAllgemeinAnzeige() { tabelle.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE); Platform.runLater(() -> { - eingabeValidierung.registerValidator(name, Validator - .createEmptyValidator( - sprache.getText("klassennameValidierung", - "Der Klassenname muss angegeben werden"))); + eingabeValidierung.registerValidator(name, + Validator.combine( + Validator.createEmptyValidator( + sprache.getText("klassennameValidierung", "Der Klassenname muss angegeben werden")), + Validator.createPredicateValidator(tf -> !vorhandeneElementNamen.contains(name.getText()), + sprache.getText("klassennameValidierung2", + "Ein Element mit diesem Namen ist bereits vorhanden")))); setzePlatzhalter(name); }); updateSuperklasse(superklasse, klassifizierer.getTyp()); - ChangeListener typBeobachter = (p, alteWahl, - neueWahl) -> updateSuperklasse(superklasse, neueWahl); + ChangeListener typBeobachter = (p, alteWahl, neueWahl) -> updateSuperklasse(superklasse, + neueWahl); this.typBeobachterListe.add(typBeobachter); typ.getSelectionModel().selectedItemProperty().addListener(typBeobachter); return tabelle; } - private Pane erzeugeTabellenAnzeige(String[] labelBezeichnungen, - ObservableList inhalt, - BiFunction erzeugeZeile, - EventHandler neuAktion) { + private Pane erzeugeTabellenAnzeige(String[] labelBezeichnungen, ObservableList inhalt, + BiFunction erzeugeZeile, EventHandler neuAktion) { GridPane tabelle = new GridPane(); fuelleTabelle(tabelle, labelBezeichnungen, inhalt, erzeugeZeile); tabelle.setHgap(5); @@ -350,41 +352,43 @@ private Pane erzeugeTabellenAnzeige(String[] labelBezeichnungen, Platform.runLater(() -> { var vBar = ((ScrollPaneSkin) scrollContainer.getSkin()).getVerticalScrollBar(); - scrollContainer.prefWidthProperty().bind(tabelle.widthProperty().add( - new When(vBar.visibleProperty()).then(vBar.widthProperty()).otherwise(0))); + scrollContainer.prefWidthProperty().bind(tabelle.widthProperty() + .add(new When(vBar.visibleProperty()).then(vBar.widthProperty()).otherwise(0))); }); - tabelle.widthProperty() - .addListener((p, a, n) -> Platform.runLater(scrollContainer::requestLayout)); + NodeUtil.beobachteSchwach(tabelle, tabelle.widthProperty(), + () -> Platform.runLater(scrollContainer::requestLayout)); return ansicht; } - private void fuelleTabelle(GridPane tabelle, String[] labelBezeichnungen, - ObservableList inhalt, BiFunction erzeugeZeile) { + private void fuelleTabelle(GridPane tabelle, String[] labelBezeichnungen, ObservableList inhalt, + BiFunction erzeugeZeile) { for (String bezeichnung : labelBezeichnungen) { - Label spaltenUeberschrift = SprachUtil.bindText(new EnhancedLabel(), sprache, - bezeichnung.toLowerCase(), bezeichnung); + Label spaltenUeberschrift = SprachUtil.bindText(new EnhancedLabel(), sprache, bezeichnung.toLowerCase(), + bezeichnung); tabelle.addRow(0, spaltenUeberschrift); } fuelleListenInhalt(tabelle, inhalt, erzeugeZeile); - inhalt.addListener((ListChangeListener) aenderung -> { + ListChangeListener beobachter = aenderung -> { if (aenderung.next()) { fuelleListenInhalt(tabelle, inhalt, erzeugeZeile); } - }); + }; + listenBeobachter.add(beobachter); + inhalt.addListener(new WeakListChangeListener<>(beobachter)); } private void fuelleListenInhalt(GridPane tabelle, ObservableList inhalt, BiFunction erzeugeZeile) { - var zuEntfernen = tabelle.getChildren().stream() - .filter(kind -> GridPane.getRowIndex(kind) > 0).toList(); + var zuEntfernen = tabelle.getChildren().stream().filter(kind -> GridPane.getRowIndex(kind) > 0).toList(); for (var kind : zuEntfernen) { if (kind instanceof Control c) { - eingabeValidierung.registerValidator(c, false, (control, - wert) -> ValidationResult.fromInfoIf(control, "ungenutzt", false)); + eingabeValidierung.registerValidator(c, false, + (control, wert) -> ValidationResult.fromInfoIf(control, "ungenutzt", false)); + NodeUtil.entferneSchwacheBeobachtung(kind); } } tabelle.getChildren().removeIf(kind -> GridPane.getRowIndex(kind) > 0); @@ -398,11 +402,11 @@ private void fuelleListenInhalt(GridPane tabelle, ObservableList inhalt, private Node[] erstelleAttributZeile(Attribut attribut, int zeile) { ComboBox sichtbarkeit = new ComboBox<>(); - sichtbarkeit.getItems().addAll(klassifizierer.getProgrammiersprache() - .getEigenschaften().getAttributModifizierer(klassifizierer.getTyp())); + sichtbarkeit.getItems().addAll(klassifizierer.getProgrammiersprache().getEigenschaften() + .getAttributModifizierer(klassifizierer.getTyp())); sichtbarkeit.getSelectionModel().select(attribut.getSichtbarkeit()); - sichtbarkeit.getSelectionModel().selectedItemProperty() - .addListener((o, alt, neu) -> attribut.setSichtbarkeit(neu)); + NodeUtil.beobachteSchwach(sichtbarkeit, sichtbarkeit.getSelectionModel().selectedItemProperty(), + attribut::setSichtbarkeit); TextField name = new TextField(); name.textProperty().bindBidirectional(attribut.getNameProperty()); @@ -439,43 +443,49 @@ private Node[] erstelleAttributZeile(Attribut attribut, int zeile) { Label runter = new Label(); NodeUtil.erzeugeIconNode(runter, BootstrapIcons.CARET_DOWN_FILL, 15); - runter.setOnMouseClicked( - e -> tausche(klassifizierer.getAttribute(), zeile, zeile + 1)); + runter.setOnMouseClicked(e -> tausche(klassifizierer.getAttribute(), zeile, zeile + 1)); if (zeile == klassifizierer.getAttribute().size() - 1) { runter.setDisable(true); } Platform.runLater(() -> { - eingabeValidierung.registerValidator(name, Validator - .createEmptyValidator( - sprache.getText("nameValidierung", "Name angeben"))); - eingabeValidierung.registerValidator(datentyp, Validator - .createEmptyValidator( - sprache.getText("datentypValidierung", "Datentyp angeben"))); + if (Einstellungen.getBenutzerdefiniert().erweiterteValidierungAktivieren.get()) { + eingabeValidierung.registerValidator(name, + Validator.combine( + Validator.createEmptyValidator(sprache.getText("nameValidierung", "Name angeben")), + Validator.createPredicateValidator( + tf -> klassifizierer.getAttribute().stream() + .filter(a -> Objects.equals(a.getName(), name.getText())).count() <= 1, + sprache.getText("nameValidierung2", + "Ein Attribut mit diesem Namen ist bereits vorhanden")))); + NodeUtil.beobachteSchwach(name, name.textProperty(), () -> eingabeValidierung.revalidate()); + } else { + eingabeValidierung.registerValidator(name, + Validator.createEmptyValidator(sprache.getText("nameValidierung", "Name angeben"))); + } + + eingabeValidierung.registerValidator(datentyp, + Validator.createEmptyValidator(sprache.getText("datentypValidierung", "Datentyp angeben"))); setzePlatzhalter(name); setzePlatzhalter(datentyp); }); updateStatischAttribut(statisch, klassifizierer.getTyp()); - ChangeListener typBeobachter = (p, alteWahl, - neueWahl) -> updateStatischAttribut(statisch, neueWahl); + ChangeListener typBeobachter = (p, alteWahl, neueWahl) -> updateStatischAttribut(statisch, + neueWahl); this.typBeobachterListe.add(typBeobachter); klassifizierer.typProperty().addListener(typBeobachter); - return new Node[] { sichtbarkeit, name, datentyp, initialwert, getter, setter, - statisch, hoch, runter, loeschen }; + return new Node[] { sichtbarkeit, name, datentyp, initialwert, getter, setter, statisch, hoch, runter, + loeschen }; } private HBox erzeugeSichtbarkeit() { HBox sichtbarkeit = new HBox(); ToggleGroup sichtbarkeitGruppe = new ToggleGroup(); - Modifizierer[] modifizierer = { - Modifizierer.PUBLIC, - Modifizierer.PACKAGE, - Modifizierer.PRIVATE, - Modifizierer.PROTECTED - }; + Modifizierer[] modifizierer = { Modifizierer.PUBLIC, Modifizierer.PACKAGE, Modifizierer.PRIVATE, + Modifizierer.PROTECTED }; for (Modifizierer m : modifizierer) { RadioButton rb = new RadioButton(m.toString()); rb.setUserData(m); @@ -489,74 +499,70 @@ private HBox erzeugeSichtbarkeit() { } sichtbarkeit.setSpacing(10); - sichtbarkeitGruppe.selectedToggleProperty() - .addListener((property, alteWahl, neueWahl) -> { - if (neueWahl.getUserData() instanceof Modifizierer m) { - klassifizierer.setSichtbarkeit(m); - } - }); + ChangeListener beobachter = (property, alteWahl, neueWahl) -> { + if (neueWahl.getUserData() instanceof Modifizierer m) { + klassifizierer.setSichtbarkeit(m); + } + }; + sichtbarkeit.getProperties().put(this, beobachter); + sichtbarkeitGruppe.selectedToggleProperty().addListener(new WeakChangeListener<>(beobachter)); + return sichtbarkeit; } private Node[] erstelleMethodenZeile(Methode methode, int zeile) { ComboBox sichtbarkeit = new ComboBox<>(); - sichtbarkeit.getItems().addAll(klassifizierer.getProgrammiersprache() - .getEigenschaften().getMethodenModifizierer(klassifizierer.getTyp())); + sichtbarkeit.getItems().addAll(klassifizierer.getProgrammiersprache().getEigenschaften() + .getMethodenModifizierer(klassifizierer.getTyp())); sichtbarkeit.getSelectionModel().select(methode.getSichtbarkeit()); - sichtbarkeit.getSelectionModel().selectedItemProperty() - .addListener((o, alt, neu) -> methode.setSichtbarkeit(neu)); + NodeUtil.beobachteSchwach(sichtbarkeit, sichtbarkeit.getSelectionModel().selectedItemProperty(), + methode::setSichtbarkeit); TextField name = new TextField(); name.textProperty().bindBidirectional(methode.getNameProperty()); TextField parameter = new TextField(); - parameter.textProperty().bind(Bindings.concat("(") - .concat(new StringBinding() { - { - super.bind(methode.getParameterListe()); - } - StringExpression stringExpr = null; - - @Override - protected String computeValue() { - if (stringExpr != null) { - super.unbind(stringExpr); - } - var params = methode.getParameterListe().stream() - .map(param -> Bindings.concat(new When(Einstellungen - .getBenutzerdefiniert().zeigeParameterNamen) - .then(param.getNameProperty().concat(": ")) - .otherwise(""), - param.getDatentyp().getTypNameProperty())) - .toArray(); - - if (params.length < 1) { - return ""; - } - - stringExpr = Bindings.concat(params[0]); - for (int i = 1; i < params.length; i++) { - stringExpr = stringExpr.concat(", "); - stringExpr = stringExpr.concat(params[i]); - } - - super.bind(stringExpr); - return stringExpr.get(); - } - }) - .concat(")")); + parameter.textProperty().bind(Bindings.concat("(").concat(new StringBinding() { + { + super.bind(methode.getParameterListe()); + } + StringExpression stringExpr = null; + + @Override + protected String computeValue() { + if (stringExpr != null) { + super.unbind(stringExpr); + } + var params = methode.getParameterListe().stream() + .map(param -> Bindings.concat( + new When(Einstellungen.getBenutzerdefiniert().zeigeParameterNamen) + .then(param.getNameProperty().concat(": ")).otherwise(""), + param.getDatentyp().getTypNameProperty())) + .toArray(); + + if (params.length < 1) { + return ""; + } + + stringExpr = Bindings.concat(params[0]); + for (int i = 1; i < params.length; i++) { + stringExpr = stringExpr.concat(", "); + stringExpr = stringExpr.concat(params[i]); + } + + super.bind(stringExpr); + return stringExpr.get(); + } + }).concat(")")); parameter.setEditable(false); - parameter.prefColumnCountProperty() - .bind(new When(parameter.textProperty().length() - .greaterThanOrEqualTo(TextField.DEFAULT_PREF_COLUMN_COUNT)) - .then(parameter.textProperty().length()) - .otherwise(TextField.DEFAULT_PREF_COLUMN_COUNT)); + parameter.prefColumnCountProperty().bind( + new When(parameter.textProperty().length().greaterThanOrEqualTo(TextField.DEFAULT_PREF_COLUMN_COUNT)) + .then(parameter.textProperty().length()).otherwise(TextField.DEFAULT_PREF_COLUMN_COUNT)); parameter.setOnMousePressed(e -> bearbeiteParameter(parameter, methode)); parameter.setOnAction(e -> bearbeiteParameter(parameter, methode)); TextField rueckgabetyp = new TextField(methode.getRueckgabeTyp().getTypName()); - rueckgabetyp.textProperty() - .bindBidirectional(methode.getRueckgabeTyp().getTypNameProperty()); + rueckgabetyp.textProperty().bindBidirectional(methode.getRueckgabeTyp().getTypNameProperty()); rueckgabetyp.setPrefWidth(70); CheckBox abstrakt = new CheckBox(); @@ -592,19 +598,31 @@ protected String computeValue() { } Platform.runLater(() -> { - eingabeValidierung.registerValidator(name, Validator - .createEmptyValidator( - sprache.getText("nameValidierung", "Name angeben"))); - eingabeValidierung.registerValidator(rueckgabetyp, Validator - .createEmptyValidator( - sprache.getText("datentypValidierung", "Datentyp angeben"))); + if (Einstellungen.getBenutzerdefiniert().erweiterteValidierungAktivieren.get()) { + eingabeValidierung.registerValidator(parameter, + Validator.createPredicateValidator( + tf -> klassifizierer.getMethoden().stream() + .filter(m -> Objects.equals(m.getName(), methode.getName()) && Objects + .deepEquals(m.getParameterListe(), methode.getParameterListe())) + .count() <= 1, + sprache.getText("methodeValidierung", + "Eine Methode mit gleicher Signatur (Name und Parameterliste) " + + "ist bereits vorhanden"))); + setzePlatzhalter(parameter); + NodeUtil.beobachteSchwach(name, name.textProperty(), () -> eingabeValidierung.revalidate()); + } + eingabeValidierung.registerValidator(name, + Validator.createEmptyValidator(sprache.getText("nameValidierung", "Name angeben"))); + eingabeValidierung.registerValidator(rueckgabetyp, + Validator.createEmptyValidator(sprache.getText("datentypValidierung", "Datentyp angeben"))); + setzePlatzhalter(name); setzePlatzhalter(rueckgabetyp); }); updateMethode(abstrakt, statisch, klassifizierer.getTyp(), methode); - ChangeListener typBeobachter = (p, alteWahl, - neueWahl) -> updateMethode(abstrakt, statisch, neueWahl, methode); + ChangeListener typBeobachter = (p, alteWahl, neueWahl) -> updateMethode(abstrakt, statisch, + neueWahl, methode); this.typBeobachterListe.add(typBeobachter); klassifizierer.typProperty().addListener(typBeobachter); @@ -616,78 +634,76 @@ protected String computeValue() { parameter.setDisable(true); } - abstrakt.selectedProperty().addListener((p, alt, istAbstrakt) -> { + NodeUtil.beobachteSchwach(abstrakt, abstrakt.selectedProperty(), (alt, istAbstrakt) -> { if (istAbstrakt.booleanValue()) { statisch.setSelected(false); } }); - statisch.selectedProperty().addListener((p, alt, istStatisch) -> { + NodeUtil.beobachteSchwach(statisch, statisch.selectedProperty(), (alt, istStatisch) -> { if (istStatisch.booleanValue()) { abstrakt.setSelected(false); } }); - return new Node[] { sichtbarkeit, name, parameter, rueckgabetyp, abstrakt, istFinal, - statisch, hoch, runter, loeschen }; + return new Node[] { sichtbarkeit, name, parameter, rueckgabetyp, abstrakt, istFinal, statisch, hoch, runter, + loeschen }; } private void bearbeiteParameter(TextField parameter, Methode methode) { - var parameterListe = erzeugeTabellenAnzeige(new String[] { - "Parametername", "Datentyp" - }, methode.getParameterListe(), (param, zeile) -> { - TextField name = new TextField(); - name.textProperty().bindBidirectional(param.getNameProperty()); - - TextField datentyp = new TextField(param.getDatentyp().getTypName()); - datentyp.textProperty() - .bindBidirectional(param.getDatentyp().getTypNameProperty()); - - Label loeschen = new Label(); - NodeUtil.erzeugeIconNode(loeschen, CarbonIcons.DELETE, 15); - loeschen.setOnMouseClicked(e -> methode.getParameterListe().remove(param)); - - Label hoch = new Label(); - NodeUtil.erzeugeIconNode(hoch, BootstrapIcons.CARET_UP_FILL, 15); - hoch.setOnMouseClicked( - e -> tausche(methode.getParameterListe(), zeile, zeile - 1)); - - if (zeile == 0) { - hoch.setDisable(true); - } - - Label runter = new Label(); - NodeUtil.erzeugeIconNode(runter, BootstrapIcons.CARET_DOWN_FILL, 15); - runter.setOnMouseClicked( - e -> tausche(methode.getParameterListe(), zeile, zeile + 1)); - - if (methode.istGetter() || methode.istSetter()) { - datentyp.setDisable(true); - loeschen.setDisable(true); - hoch.setDisable(true); - runter.setDisable(true); - } - - Platform.runLater(() -> { - eingabeValidierung.registerValidator(name, Validator - .createEmptyValidator( - sprache.getText("nameValidierung", "Name angeben"))); - eingabeValidierung.registerValidator(datentyp, Validator - .createEmptyValidator( - sprache.getText("datentypValidierung", "Datentyp angeben"))); - setzePlatzhalter(name); - setzePlatzhalter(datentyp); - }); - - return new Node[] { name, datentyp, loeschen, hoch, runter }; - }, event -> { - if (!methode.istGetter() && !methode.istSetter()) { - var programmierEigenschaften = klassifizierer.getProgrammiersprache() - .getEigenschaften(); - methode.getParameterListe() - .add(new Parameter(programmierEigenschaften.getLetzerDatentyp())); - } - }); + var parameterListe = erzeugeTabellenAnzeige(new String[] { "Parametername", "Datentyp" }, + methode.getParameterListe(), (param, zeile) -> { + TextField name = new TextField(); + name.textProperty().bindBidirectional(param.getNameProperty()); + + TextField datentyp = new TextField(param.getDatentyp().getTypName()); + datentyp.textProperty().bindBidirectional(param.getDatentyp().getTypNameProperty()); + + Label loeschen = new Label(); + NodeUtil.erzeugeIconNode(loeschen, CarbonIcons.DELETE, 15); + loeschen.setOnMouseClicked(e -> methode.getParameterListe().remove(param)); + + Label hoch = new Label(); + NodeUtil.erzeugeIconNode(hoch, BootstrapIcons.CARET_UP_FILL, 15); + hoch.setOnMouseClicked(e -> tausche(methode.getParameterListe(), zeile, zeile - 1)); + + if (zeile == 0) { + hoch.setDisable(true); + } + + Label runter = new Label(); + NodeUtil.erzeugeIconNode(runter, BootstrapIcons.CARET_DOWN_FILL, 15); + runter.setOnMouseClicked(e -> tausche(methode.getParameterListe(), zeile, zeile + 1)); + + if (methode.istGetter() || methode.istSetter()) { + datentyp.setDisable(true); + loeschen.setDisable(true); + hoch.setDisable(true); + runter.setDisable(true); + } + + Platform.runLater(() -> { + eingabeValidierung.registerValidator(name, + Validator.createEmptyValidator(sprache.getText("nameValidierung", "Name angeben"))); + eingabeValidierung.registerValidator(datentyp, Validator + .createEmptyValidator(sprache.getText("datentypValidierung", "Datentyp angeben"))); + setzePlatzhalter(name); + setzePlatzhalter(datentyp); + + if (Einstellungen.getBenutzerdefiniert().erweiterteValidierungAktivieren.get()) { + NodeUtil.beobachteSchwach(name, name.textProperty(), () -> eingabeValidierung.revalidate()); + NodeUtil.beobachteSchwach(datentyp, datentyp.textProperty(), + () -> eingabeValidierung.revalidate()); + } + }); + + return new Node[] { name, datentyp, loeschen, hoch, runter }; + }, event -> { + if (!methode.istGetter() && !methode.istSetter()) { + var programmierEigenschaften = klassifizierer.getProgrammiersprache().getEigenschaften(); + methode.getParameterListe().add(new Parameter(programmierEigenschaften.getLetzerDatentyp())); + } + }); parameterListe.setPadding(new Insets(15)); PopOver parameterDialog = new PopOver(parameterListe); @@ -696,21 +712,31 @@ private void bearbeiteParameter(TextField parameter, Methode methode) { } private void initialisiereButtons() { - ButtonType[] buttons = { - new ButtonType(sprache.getText("APPLY", "Anwenden"), ButtonData.FINISH), - new ButtonType(sprache.getText("CANCEL_CLOSE", "Abbrechen"), - ButtonData.BACK_PREVIOUS) - }; + ButtonType[] buttons = { new ButtonType(sprache.getText("APPLY", "Anwenden"), ButtonData.FINISH), + new ButtonType(sprache.getText("CANCEL_CLOSE", "Abbrechen"), ButtonData.BACK_PREVIOUS) }; this.getDialogPane().getButtonTypes().addAll(buttons); - this.getDialogPane().lookupButton(buttons[0]).disableProperty() - .bind(eingabeValidierung.invalidProperty()); + this.getDialogPane().lookupButton(buttons[0]).disableProperty().bind(eingabeValidierung.invalidProperty()); } private void setzePlatzhalter(TextInputControl eingabefeld) { - eingabeValidierung.validationResultProperty() - .addListener((p, alt, neu) -> eingabefeld - .setPromptText(eingabeValidierung.getHighestMessage(eingabefeld) - .orElseGet(() -> leereValidierungsMeldung).getText())); + ChangeListener beobachter = (p, alt, neu) -> { + var fehler = eingabeValidierung.getHighestMessage(eingabefeld); + if (fehler.isPresent()) { + eingabefeld.setPromptText(fehler.get().getText()); + if (!eingabefeld.getStyleClass().contains(CSS_EINGABE_FEHLER)) { + eingabefeld.getStyleClass().add(CSS_EINGABE_FEHLER); + } + eingabefeld.setTooltip(new Tooltip(fehler.get().getText())); + } else { + eingabefeld.setPromptText(null); + if (eingabefeld.getStyleClass().contains(CSS_EINGABE_FEHLER)) { + eingabefeld.getStyleClass().remove(CSS_EINGABE_FEHLER); + } + eingabefeld.setTooltip(null); + } + }; + eingabeValidierung.validationResultProperty().addListener(beobachter); + validierungsBeobachter.add(beobachter); } private void tausche(List liste, int indexA, int indexB) { @@ -721,8 +747,7 @@ private void tausche(List liste, int indexA, int indexB) { } private void updateSuperklasse(TextField superklasse, KlassifiziererTyp typ) { - boolean superklasseErlaubt = klassifizierer.getProgrammiersprache() - .getEigenschaften().erlaubtSuperklasse(typ); + boolean superklasseErlaubt = klassifizierer.getProgrammiersprache().getEigenschaften().erlaubtSuperklasse(typ); if (!superklasseErlaubt) { superklasse.setText(""); } @@ -730,8 +755,8 @@ private void updateSuperklasse(TextField superklasse, KlassifiziererTyp typ) { } private void updateStatischAttribut(CheckBox statisch, KlassifiziererTyp typ) { - boolean instanzAttributeErlaubt = klassifizierer.getProgrammiersprache() - .getEigenschaften().erlaubtInstanzAttribute(typ); + boolean instanzAttributeErlaubt = klassifizierer.getProgrammiersprache().getEigenschaften() + .erlaubtInstanzAttribute(typ); if (!instanzAttributeErlaubt) { statisch.setSelected(true); } @@ -741,8 +766,7 @@ private void updateStatischAttribut(CheckBox statisch, KlassifiziererTyp typ) { statisch.setDisable(!instanzAttributeErlaubt); } - private void updateMethode(CheckBox abstrakt, CheckBox statisch, KlassifiziererTyp typ, - Methode methode) { + private void updateMethode(CheckBox abstrakt, CheckBox statisch, KlassifiziererTyp typ, Methode methode) { boolean abstraktErlaubt = klassifizierer.getProgrammiersprache().getEigenschaften() .erlaubtAbstrakteMethode(typ); boolean abstraktErzwungen = !klassifizierer.getProgrammiersprache().getEigenschaften() @@ -760,11 +784,10 @@ private void updateMethode(CheckBox abstrakt, CheckBox statisch, KlassifiziererT abstrakt.setDisable(false); } - if (typ.equals(KlassifiziererTyp.Interface) && !methode.istGetter() - && !methode.istSetter() && !methode.istStatisch()) { + if (typ.equals(KlassifiziererTyp.Interface) && !methode.istGetter() && !methode.istSetter() + && !methode.istStatisch()) { abstrakt.setSelected(true); - } else if (typ.equals(KlassifiziererTyp.Interface) - && (methode.istGetter() || methode.istSetter())) { + } else if (typ.equals(KlassifiziererTyp.Interface) && (methode.istGetter() || methode.istSetter())) { abstrakt.setDisable(true); statisch.setDisable(true); } diff --git a/src/main/java/io/github/aid_labor/classifier/gui/elemente/MenueLeisteKomponente.java b/src/main/java/io/github/aid_labor/classifier/gui/elemente/MenueLeisteKomponente.java index 4834b46..b622247 100644 --- a/src/main/java/io/github/aid_labor/classifier/gui/elemente/MenueLeisteKomponente.java +++ b/src/main/java/io/github/aid_labor/classifier/gui/elemente/MenueLeisteKomponente.java @@ -109,6 +109,7 @@ public class MenueLeisteKomponente { // Menue Einstellungen private CheckMenuItem voidAnzeigen; private CheckMenuItem parameternamenAnzeigen; + private CheckMenuItem erweiterteValidierungAktivieren; private Menu theme; private MenuItem info; private MenuItem konfigurationsordnerOeffnen; @@ -294,6 +295,10 @@ public CheckMenuItem getParameternamenAnzeigen() { return parameternamenAnzeigen; } + public CheckMenuItem getErweiterteValidierungAktivieren() { + return erweiterteValidierungAktivieren; + } + public Menu getTheme() { return theme; } @@ -484,6 +489,8 @@ private Menu erstelleEinstellungenMenue() { "void Anzeigen"); parameternamenAnzeigen = SprachUtil.bindText(new CheckMenuItem(), sprache, "parameternamenAnzeigen", "Parameternamen Anzeigen"); + erweiterteValidierungAktivieren = SprachUtil.bindText(new CheckMenuItem(), sprache, + "erweiterteValidierungAktivieren", "Erweiterte Validierung"); theme = SprachUtil.bindText(new Menu(), sprache, "theme", "Farbschema"); info = SprachUtil.bindText(new MenuItem(), sprache, "info", @@ -494,7 +501,7 @@ private Menu erstelleEinstellungenMenue() { "konfigBereinigen", "Konfigurationsordner bereinigen".formatted(oe)); einstellungenMenue.getItems().addAll(voidAnzeigen, parameternamenAnzeigen, - new SeparatorMenuItem(), theme, + erweiterteValidierungAktivieren, new SeparatorMenuItem(), theme, new SeparatorMenuItem(), info, konfigurationsordnerOeffnen, konfigurationsordnerBereinigen); diff --git a/src/main/java/io/github/aid_labor/classifier/gui/util/NodeUtil.java b/src/main/java/io/github/aid_labor/classifier/gui/util/NodeUtil.java index f234a0a..a2815c9 100644 --- a/src/main/java/io/github/aid_labor/classifier/gui/util/NodeUtil.java +++ b/src/main/java/io/github/aid_labor/classifier/gui/util/NodeUtil.java @@ -6,7 +6,10 @@ package io.github.aid_labor.classifier.gui.util; +import java.util.ArrayList; +import java.util.List; import java.util.function.BiConsumer; +import java.util.function.Consumer; import java.util.logging.Logger; import org.kordamp.ikonli.Ikon; @@ -15,6 +18,9 @@ import io.github.aid_labor.classifier.basis.io.Ressource; import javafx.beans.property.ReadOnlyBooleanProperty; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.beans.value.WeakChangeListener; import javafx.geometry.Bounds; import javafx.geometry.Insets; import javafx.geometry.Point2D; @@ -66,8 +72,7 @@ public final class NodeUtil { * @param anzeigeStil Darstellungsstil von Text und Icon * @return die hinzugefuegte Grafik */ - public static ImageView fuegeGrafikHinzu(Labeled node, Ressource grafik, int hoehe, - ContentDisplay anzeigeStil) { + public static ImageView fuegeGrafikHinzu(Labeled node, Ressource grafik, int hoehe, ContentDisplay anzeigeStil) { ImageView grafikNode = new ImageView(grafik.externeForm()); grafikNode.setPreserveRatio(true); grafikNode.setFitHeight(hoehe); @@ -98,8 +103,7 @@ public static ImageView fuegeGrafikHinzu(Labeled node, Ressource grafik, int hoe * @param anzeigeStil Darstellungsstil von Text und Icon * @return das hinzugefuegte Icon */ - public static FontIcon fuegeIconHinzu(Labeled node, Ikon iconCode, int groesse, - ContentDisplay anzeigeStil) { + public static FontIcon fuegeIconHinzu(Labeled node, Ikon iconCode, int groesse, ContentDisplay anzeigeStil) { FontIcon symbol = new FontIcon(iconCode); symbol.setIconSize(groesse); node.setGraphic(symbol); @@ -129,8 +133,7 @@ public static FontIcon fuegeIconHinzu(Labeled node, Ikon iconCode, int groesse) * @param anzeigeStil Darstellungsstil von Text und Icon * @return das hinzugefuegte Icon */ - public static FontIcon fuegeIconHinzu(Labeled node, Ikon iconCode, - ContentDisplay anzeigeStil) { + public static FontIcon fuegeIconHinzu(Labeled node, Ikon iconCode, ContentDisplay anzeigeStil) { return fuegeIconHinzu(node, iconCode, 24, anzeigeStil); } @@ -258,16 +261,14 @@ public static void setzeHervorhebung(ReadOnlyBooleanProperty hervorheben, Node e * Aufheben * @param elemente Elemente, die die Id erhalten */ - public static void setzeHervorhebung(ReadOnlyBooleanProperty hervorheben, - Node... elemente) { + public static void setzeHervorhebung(ReadOnlyBooleanProperty hervorheben, Node... elemente) { for (Node element : elemente) { setzeHervorhebung(hervorheben.get(), element); } } public static boolean wirdBewegt(Node n) { - Object obj = n.getProperties().getOrDefault("bewegungsEinstellung", - new BewegungsEinstellungen()); + Object obj = n.getProperties().getOrDefault("bewegungsEinstellung", new BewegungsEinstellungen()); if (!(obj instanceof BewegungsEinstellungen bewegung)) { return false; } @@ -275,13 +276,11 @@ public static boolean wirdBewegt(Node n) { return bewegung.wirdBewegt; } - public static void macheBeweglich(Node element, Runnable vorBewegung, - BiConsumer nachBewegung) { - Object obj = element.getProperties().getOrDefault("bewegungsEinstellung", - new BewegungsEinstellungen()); + public static void macheBeweglich(Node element, Runnable vorBewegung, BiConsumer nachBewegung) { + Object obj = element.getProperties().getOrDefault("bewegungsEinstellung", new BewegungsEinstellungen()); if (!(obj instanceof BewegungsEinstellungen bewegung)) { - log.warning(() -> "unbekanntes Objekt fuer Schluessel 'bewegungsEinstellung'. " - + " Node " + element + " kann nicht beweglich gemacht werden!"); + log.warning(() -> "unbekanntes Objekt fuer Schluessel 'bewegungsEinstellung'. " + " Node " + element + + " kann nicht beweglich gemacht werden!"); return; } @@ -294,8 +293,7 @@ public static void macheBeweglich(Node element, Runnable vorBewegung, }); element.addEventFilter(MouseEvent.MOUSE_PRESSED, event -> { - if (bewegung.aktionPosition.equals(AktionPosition.KEINE) - && istEinfacherMausKlick(event)) { + if (bewegung.aktionPosition.equals(AktionPosition.KEINE) && istEinfacherMausKlick(event)) { log.finer(() -> "Starte Bewegung"); if (vorBewegung != null) { vorBewegung.run(); @@ -331,16 +329,14 @@ && istEinfacherMausKlick(event)) { element.setCursor(bewegung.letzterCursor); bewegung.wirdBewegt = false; if (nachBewegung != null) { - nachBewegung.accept(bewegung.ausgangsPosition, - element.getBoundsInParent()); + nachBewegung.accept(bewegung.ausgangsPosition, element.getBoundsInParent()); } } }); } public static boolean wirdGroesseVeraendert(Node n) { - Object obj = n.getProperties().getOrDefault("bewegungsEinstellung", - new BewegungsEinstellungen()); + Object obj = n.getProperties().getOrDefault("bewegungsEinstellung", new BewegungsEinstellungen()); if (!(obj instanceof BewegungsEinstellungen bewegung)) { return false; } @@ -348,13 +344,12 @@ public static boolean wirdGroesseVeraendert(Node n) { return bewegung.wirdGroesseVeraendert; } - public static void macheGroessenVeraenderlich(Region element, - Runnable vorGroessenAenderung, BiConsumer nachGroessenAenderung) { - Object obj = element.getProperties().getOrDefault("bewegungsEinstellung", - new BewegungsEinstellungen()); + public static void macheGroessenVeraenderlich(Region element, Runnable vorGroessenAenderung, + BiConsumer nachGroessenAenderung) { + Object obj = element.getProperties().getOrDefault("bewegungsEinstellung", new BewegungsEinstellungen()); if (!(obj instanceof BewegungsEinstellungen bewegung)) { - log.warning(() -> "unbekanntes Objekt fuer Schluessel 'bewegungsEinstellung'. " - + " Node " + element + " kann nicht beweglich gemacht werden!"); + log.warning(() -> "unbekanntes Objekt fuer Schluessel 'bewegungsEinstellung'. " + " Node " + element + + " kann nicht beweglich gemacht werden!"); return; } @@ -365,8 +360,7 @@ public static void macheGroessenVeraenderlich(Region element, beobachteMausBewegung(element, bewegung); element.addEventFilter(MouseEvent.MOUSE_PRESSED, event -> { - if (!bewegung.aktionPosition.equals(AktionPosition.KEINE) - && istEinfacherMausKlick(event)) { + if (!bewegung.aktionPosition.equals(AktionPosition.KEINE) && istEinfacherMausKlick(event)) { log.finer(() -> "Starte Groessenaenderung"); if (vorGroessenAenderung != null) { vorGroessenAenderung.run(); @@ -387,8 +381,7 @@ && istEinfacherMausKlick(event)) { if (bewegung.wirdGroesseVeraendert) { log.finest(() -> "Veraendere Groesse"); - Point2D mausposition = element.getParent().sceneToLocal(event.getSceneX(), - event.getSceneY()); + Point2D mausposition = element.getParent().sceneToLocal(event.getSceneX(), event.getSceneY()); if (bewegung.aktionPosition.istObenSelektiert()) { double minYNeu = mausposition.getY(); @@ -431,15 +424,13 @@ && istEinfacherMausKlick(event)) { element.setCursor(bewegung.letzterCursor); bewegung.wirdGroesseVeraendert = false; if (nachGroessenAenderung != null) { - nachGroessenAenderung.accept(bewegung.ausgangsPosition, - element.getBoundsInParent()); + nachGroessenAenderung.accept(bewegung.ausgangsPosition, element.getBoundsInParent()); } } }); } - private static void beobachteMausBewegung(Region element, - BewegungsEinstellungen bewegung) { + private static void beobachteMausBewegung(Region element, BewegungsEinstellungen bewegung) { element.addEventHandler(MouseEvent.MOUSE_MOVED, event -> { double minX = element.getBoundsInLocal().getMinX(); double minY = element.getBoundsInLocal().getMinY(); @@ -488,6 +479,57 @@ private static void beobachteMausBewegung(Region element, }); } + private static final String LISTENERS_KEY = "UeberwacherListe"; + + public static void beobachteSchwach(Node node, ObservableValue property, Runnable aktion) { + beobachteSchwach(node, property, (p, a, n) -> aktion.run()); + } + + public static void beobachteSchwach(Node node, ObservableValue property, + BiConsumer aenderungsAuswertung) { + beobachteSchwach(node, property, (p, alt, neu) -> aenderungsAuswertung.accept(alt, neu)); + } + + public static void beobachteSchwach(Node node, ObservableValue property, + Consumer neuAuswertung) { + beobachteSchwach(node, property, (p, alt, neu) -> neuAuswertung.accept(neu)); + } + + @SuppressWarnings("unchecked") + public static void beobachteSchwach(Node node, ObservableValue property, + ChangeListener beobachter) { + var ueberwacherProp = node.getProperties().get(LISTENERS_KEY); + + if (ueberwacherProp == null) { + ueberwacherProp = new ArrayList>(); + node.getProperties().put(LISTENERS_KEY, ueberwacherProp); + } + + List> ueberwacherListe; + try { + ueberwacherListe = (List>) ueberwacherProp; + } catch (Exception e) { + log.severe(() -> "falscher Typ fuer UberwacherListe"); + return; + } + + ueberwacherListe.add(beobachter); + property.addListener(new WeakChangeListener<>(beobachter)); + } + + @SuppressWarnings("unchecked") + public static void entferneSchwacheBeobachtung(Node node) { + var ueberwacherProp = node.getProperties().remove(LISTENERS_KEY); + + List> ueberwacherListe; + try { + ueberwacherListe = (List>) ueberwacherProp; + ueberwacherListe.clear(); + } catch (Exception e) { + log.severe(() -> "falscher Typ fuer UberwacherListe"); + } + } + // protected ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## // package ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## @@ -495,16 +537,9 @@ private static void beobachteMausBewegung(Region element, // private ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## private static boolean istEinfacherMausKlick(MouseEvent event) { - return event.isPrimaryButtonDown() - && !event.isAltDown() - && !event.isBackButtonDown() - && !event.isControlDown() - && !event.isForwardButtonDown() - && !event.isMetaDown() - && !event.isMiddleButtonDown() - && !event.isSecondaryButtonDown() - && !event.isShiftDown() - && !event.isShortcutDown(); + return event.isPrimaryButtonDown() && !event.isAltDown() && !event.isBackButtonDown() && !event.isControlDown() + && !event.isForwardButtonDown() && !event.isMetaDown() && !event.isMiddleButtonDown() + && !event.isSecondaryButtonDown() && !event.isShiftDown() && !event.isShortcutDown(); } private static class BewegungsEinstellungen { @@ -528,14 +563,9 @@ public BewegungsEinstellungen() { } private static enum AktionPosition { - KEINE(false, false, false, false), - OBEN(true, false, false, false), - RECHTS(false, true, false, false), - UNTEN(false, false, true, false), - LINKS(false, false, false, true), - OBEN_LINKS(true, false, false, true), - OBEN_RECHTS(true, true, false, false), - UNTEN_LINKS(false, false, true, true), + KEINE(false, false, false, false), OBEN(true, false, false, false), RECHTS(false, true, false, false), + UNTEN(false, false, true, false), LINKS(false, false, false, true), OBEN_LINKS(true, false, false, true), + OBEN_RECHTS(true, true, false, false), UNTEN_LINKS(false, false, true, true), UNTEN_RECHTS(false, true, true, false); private boolean obenSelektiert; @@ -543,8 +573,8 @@ private static enum AktionPosition { private boolean untenSelektiert; private boolean linksSelektiert; - private AktionPosition(boolean obenSelektiert, boolean rechtsSelektiert, - boolean untenSelektiert, boolean linksSelektiert) { + private AktionPosition(boolean obenSelektiert, boolean rechtsSelektiert, boolean untenSelektiert, + boolean linksSelektiert) { this.obenSelektiert = obenSelektiert; this.rechtsSelektiert = rechtsSelektiert; this.untenSelektiert = untenSelektiert; diff --git a/src/main/resources/ressourcen/css/classifier.css b/src/main/resources/ressourcen/css/classifier.css index 06e3cf1..d237882 100644 --- a/src/main/resources/ressourcen/css/classifier.css +++ b/src/main/resources/ressourcen/css/classifier.css @@ -112,6 +112,11 @@ -fx-underline: true; } +.eingabefehler { + -fx-base: derive(red, 50%); + -fx-prompt-text-fill: derive(silver, -40%) +} + .projekt-inhalt { /* ScrollPane im Tab */ /* -fx-border-width: 2; diff --git a/src/main/resources/ressourcen/sprache/de/MenueLeisteAnsicht_de.properties b/src/main/resources/ressourcen/sprache/de/MenueLeisteAnsicht_de.properties index a3f2d18..757710d 100644 --- a/src/main/resources/ressourcen/sprache/de/MenueLeisteAnsicht_de.properties +++ b/src/main/resources/ressourcen/sprache/de/MenueLeisteAnsicht_de.properties @@ -56,6 +56,7 @@ naechsterTab = nächster Tab einstellungenMenue = Einstellungen voidAnzeigen = void Anzeigen parameternamenAnzeigen = Parameternamen Anzeigen +erweiterteValidierungAktivieren = Erweiterte Validierung theme = Farbschema info = Info konfigOeffnen = Konfigurationsordner öffnen diff --git a/src/main/resources/ressourcen/sprache/de/UMLKlassifiziererBearbeitenDialog_de.properties b/src/main/resources/ressourcen/sprache/de/UMLKlassifiziererBearbeitenDialog_de.properties index 75a5e1c..7526f42 100644 --- a/src/main/resources/ressourcen/sprache/de/UMLKlassifiziererBearbeitenDialog_de.properties +++ b/src/main/resources/ressourcen/sprache/de/UMLKlassifiziererBearbeitenDialog_de.properties @@ -28,8 +28,11 @@ final = final # Validierung klassennameValidierung = Der Klassenname muss angegeben werden +klassennameValidierung2 = Ein Element mit diesem Namen ist bereits vorhanden nameValidierung = Name angeben +nameValidierung2 = Ein Attribut mit diesem Namen ist bereits vorhanden datentypValidierung = Datentyp angeben +methodeValidierung = Eine Methode mit gleicher Signatur (Name und Parameterliste) ist bereits vorhanden # ButtonType APPLY = Anwenden