From 8589941865118f7cedd12d8dd354f4db4593c7a7 Mon Sep 17 00:00:00 2001 From: pyknic Date: Thu, 25 May 2017 13:54:31 -0700 Subject: [PATCH] enum-generator: Fix #444 Enum Constants can now be populated in UI --- .../plugins/enums/EnumGeneratorComponent.java | 2 +- .../plugins/enums/StringToEnumTypeMapper.java | 2 +- .../enums/internal/EnumGeneratorUtil.java | 2 +- .../internal/GeneratedEntityDecorator.java | 2 +- .../enums/internal/GeneratedEnumType.java | 2 +- .../internal/ui/AddRemoveStringItem.java | 334 +++++++++++++++--- .../ui/CommaSeparatedStringEditor.java | 12 +- 7 files changed, 306 insertions(+), 50 deletions(-) diff --git a/plugin-parent/enum-generator/src/main/java/com/speedment/plugins/enums/EnumGeneratorComponent.java b/plugin-parent/enum-generator/src/main/java/com/speedment/plugins/enums/EnumGeneratorComponent.java index 73ff73698e..6c79d8c147 100644 --- a/plugin-parent/enum-generator/src/main/java/com/speedment/plugins/enums/EnumGeneratorComponent.java +++ b/plugin-parent/enum-generator/src/main/java/com/speedment/plugins/enums/EnumGeneratorComponent.java @@ -47,7 +47,7 @@ * * @author Emil Forslund * @author Simon Jonasson - * @since 1.0.0 + * @since 3.0.0 */ @InjectKey(EnumGeneratorComponent.class) public final class EnumGeneratorComponent { diff --git a/plugin-parent/enum-generator/src/main/java/com/speedment/plugins/enums/StringToEnumTypeMapper.java b/plugin-parent/enum-generator/src/main/java/com/speedment/plugins/enums/StringToEnumTypeMapper.java index 6191084e49..37bafa882c 100644 --- a/plugin-parent/enum-generator/src/main/java/com/speedment/plugins/enums/StringToEnumTypeMapper.java +++ b/plugin-parent/enum-generator/src/main/java/com/speedment/plugins/enums/StringToEnumTypeMapper.java @@ -39,7 +39,7 @@ * * @author Emil Forslund * @author Simon Jonasson - * @since 1.0.0 + * @since 3.0.0 */ public final class StringToEnumTypeMapper> implements TypeMapper { diff --git a/plugin-parent/enum-generator/src/main/java/com/speedment/plugins/enums/internal/EnumGeneratorUtil.java b/plugin-parent/enum-generator/src/main/java/com/speedment/plugins/enums/internal/EnumGeneratorUtil.java index 05389a9d08..4bfa00816d 100644 --- a/plugin-parent/enum-generator/src/main/java/com/speedment/plugins/enums/internal/EnumGeneratorUtil.java +++ b/plugin-parent/enum-generator/src/main/java/com/speedment/plugins/enums/internal/EnumGeneratorUtil.java @@ -30,7 +30,7 @@ * * @author Emil Forslund * @author Simon Jonasson - * @since 1.0.0 + * @since 3.0.0 */ public final class EnumGeneratorUtil { diff --git a/plugin-parent/enum-generator/src/main/java/com/speedment/plugins/enums/internal/GeneratedEntityDecorator.java b/plugin-parent/enum-generator/src/main/java/com/speedment/plugins/enums/internal/GeneratedEntityDecorator.java index 42d466a312..041a925f30 100644 --- a/plugin-parent/enum-generator/src/main/java/com/speedment/plugins/enums/internal/GeneratedEntityDecorator.java +++ b/plugin-parent/enum-generator/src/main/java/com/speedment/plugins/enums/internal/GeneratedEntityDecorator.java @@ -49,7 +49,7 @@ * * @author Emil Forslund * @author Simon Jonasson - * @since 1.0.0 + * @since 3.0.0 */ public final class GeneratedEntityDecorator implements TranslatorDecorator { diff --git a/plugin-parent/enum-generator/src/main/java/com/speedment/plugins/enums/internal/GeneratedEnumType.java b/plugin-parent/enum-generator/src/main/java/com/speedment/plugins/enums/internal/GeneratedEnumType.java index 3b96118705..e7cc7190c6 100644 --- a/plugin-parent/enum-generator/src/main/java/com/speedment/plugins/enums/internal/GeneratedEnumType.java +++ b/plugin-parent/enum-generator/src/main/java/com/speedment/plugins/enums/internal/GeneratedEnumType.java @@ -27,7 +27,7 @@ * * @author Emil Forslund * @author Simon Jonasson - * @since 1.0.0 + * @since 3.0.0 */ public final class GeneratedEnumType implements Type { diff --git a/plugin-parent/enum-generator/src/main/java/com/speedment/plugins/enums/internal/ui/AddRemoveStringItem.java b/plugin-parent/enum-generator/src/main/java/com/speedment/plugins/enums/internal/ui/AddRemoveStringItem.java index aad53c0c80..90a674204e 100644 --- a/plugin-parent/enum-generator/src/main/java/com/speedment/plugins/enums/internal/ui/AddRemoveStringItem.java +++ b/plugin-parent/enum-generator/src/main/java/com/speedment/plugins/enums/internal/ui/AddRemoveStringItem.java @@ -16,8 +16,41 @@ */ package com.speedment.plugins.enums.internal.ui; +import com.speedment.common.injector.Injector; +import com.speedment.common.injector.State; +import com.speedment.common.injector.annotation.Config; +import com.speedment.common.injector.annotation.ExecuteBefore; +import com.speedment.common.injector.annotation.Inject; +import com.speedment.common.injector.annotation.WithState; import com.speedment.common.logger.Logger; import com.speedment.common.logger.LoggerManager; +import com.speedment.common.singletonstream.SingletonStream; +import com.speedment.runtime.config.Schema; +import com.speedment.runtime.config.identifier.ColumnIdentifier; +import com.speedment.runtime.config.identifier.TableIdentifier; +import com.speedment.runtime.config.internal.identifier.TableIdentifierImpl; +import com.speedment.runtime.core.component.ManagerComponent; +import com.speedment.runtime.core.component.PasswordComponent; +import com.speedment.runtime.core.component.ProjectComponent; +import com.speedment.runtime.core.component.StreamSupplierComponent; +import com.speedment.runtime.core.component.sql.SqlStreamSupplierComponent; +import com.speedment.runtime.core.db.DbmsMetadataHandler; +import com.speedment.runtime.core.exception.SpeedmentException; +import com.speedment.runtime.core.internal.component.sql.SqlStreamSupplierComponentImpl; +import com.speedment.runtime.core.manager.Manager; +import com.speedment.runtime.core.manager.Persister; +import com.speedment.runtime.core.manager.Remover; +import com.speedment.runtime.core.manager.Updater; +import com.speedment.runtime.core.stream.parallel.ParallelStrategy; +import com.speedment.runtime.core.util.ProgressMeasure; +import com.speedment.runtime.field.Field; +import com.speedment.runtime.field.StringField; +import com.speedment.runtime.typemapper.TypeMapper; +import com.speedment.tool.config.ColumnProperty; +import com.speedment.tool.config.DbmsProperty; +import com.speedment.tool.core.component.UserInterfaceComponent; +import com.speedment.tool.core.exception.SpeedmentToolException; +import com.speedment.tool.core.resource.FontAwesome; import com.speedment.tool.propertyeditor.item.AbstractLabelTooltipItem; import javafx.beans.binding.Bindings; import javafx.beans.property.SimpleStringProperty; @@ -35,15 +68,20 @@ import javafx.scene.layout.VBox; import javafx.util.StringConverter; +import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; import java.util.stream.Stream; +import static com.speedment.common.injector.State.RESOLVED; +import static com.speedment.runtime.config.util.DocumentUtil.ancestor; import static java.util.Objects.requireNonNull; -import static java.util.stream.Collectors.toSet; +import static java.util.concurrent.CompletableFuture.supplyAsync; +import static java.util.stream.Collectors.joining; import static javafx.application.Platform.runLater; import static javafx.scene.layout.Region.USE_PREF_SIZE; @@ -55,15 +93,13 @@ * to easily edit such a string. * * @author Simon Jonasson - * @since 1.0.0 + * @since 3.0.0 */ public final class AddRemoveStringItem extends AbstractLabelTooltipItem { - - //*********************************************************** - // VARIABLES - //*********************************************************** + private final static Logger LOGGER = LoggerManager.getLogger(AddRemoveStringItem.class); + private final ColumnProperty column; private final ObservableList strings; private final ObservableBooleanValue enabled; @@ -71,21 +107,28 @@ public final class AddRemoveStringItem extends AbstractLabelTooltipItem { private final StringProperty cache; private final String DEFAULT_FIELD = "ENUM_CONSTANT_"; - private final double SPACING = 10.0; + private final double SPACING = 10.0; private final int LIST_HEIGHT = 200; - - //*********************************************************** - // CONSTRUCTOR - //*********************************************************** + + private @Inject ProjectComponent projects; + private @Inject DbmsMetadataHandler metadata; + private @Inject PasswordComponent passwords; + private @Inject UserInterfaceComponent ui; + private @Inject Injector injector; + + //////////////////////////////////////////////////////////////////////////// + // Constructor // + //////////////////////////////////////////////////////////////////////////// - public AddRemoveStringItem( + AddRemoveStringItem( + ColumnProperty column, String label, StringProperty value, String tooltip, ObservableBooleanValue enableThis) { super(label, tooltip, NO_DECORATOR); - + final String currentValue = value.get(); if (currentValue == null) { this.strings = FXCollections.observableArrayList(); @@ -96,7 +139,8 @@ public AddRemoveStringItem( .toArray(String[]::new) ); } - + + this.column = requireNonNull(column); this.enabled = enableThis; this.cache = new SimpleStringProperty(); @@ -106,14 +150,14 @@ public AddRemoveStringItem( value.setValue(getFormatedString(list)); }); } - - //*********************************************************** - // PUBLIC - //*********************************************************** + + //////////////////////////////////////////////////////////////////////////// + // Public // + //////////////////////////////////////////////////////////////////////////// @Override public Node createLabel() { - Node node = super.createLabel(); + final Node node = super.createLabel(); hideShowBehaviour(node); return node; } @@ -131,7 +175,11 @@ protected Node createUndecoratedEditor() { final HBox controls = new HBox(SPACING); controls.setAlignment(Pos.CENTER); - controls.getChildren().addAll(addButton(listView), removeButton(listView)); + controls.getChildren().addAll( + addButton(listView), + removeButton(listView), + populateButton(listView) + ); container.setSpacing(SPACING); container.getChildren().addAll(listView, controls); @@ -141,9 +189,9 @@ protected Node createUndecoratedEditor() { return container; } - //*********************************************************** - // PRIVATE - //*********************************************************** + //////////////////////////////////////////////////////////////////////////// + // Private // + //////////////////////////////////////////////////////////////////////////// /** * Removes any empty substrings and makes sure the entire string is either @@ -154,7 +202,7 @@ protected Node createUndecoratedEditor() { private String getFormatedString(List newValue) { final String formated = newValue.stream() .filter(v -> !v.isEmpty()) - .collect(Collectors.joining(",")); + .collect(joining(",")); if (formated == null || formated.isEmpty()) { return null; @@ -162,7 +210,7 @@ private String getFormatedString(List newValue) { return formated; } } - + private void setValue(String value) { if (value == null) { strings.clear(); @@ -178,7 +226,7 @@ private void hideShowBehaviour(Node node){ } private Button removeButton(final ListView listView) { - final Button button = new Button("Remove Selected"); + final Button button = new Button("Remove Selected", FontAwesome.TIMES.view()); button.setOnAction(e -> { final int selectedIdx = listView.getSelectionModel().getSelectedIndex(); @@ -194,12 +242,12 @@ private Button removeButton(final ListView listView) { } private Button addButton(final ListView listView) { - final Button button = new Button("Add Item"); + final Button button = new Button("Add Item", FontAwesome.PLUS.view()); button.setOnAction(e -> { final int newIndex = listView.getItems().size(); - final Set set = strings.stream().collect(toSet()); + final Set set = new HashSet<>(strings); final AtomicInteger i = new AtomicInteger(0); while (!set.add(DEFAULT_FIELD + i.incrementAndGet())) {} @@ -207,21 +255,223 @@ private Button addButton(final ListView listView) { listView.scrollTo(newIndex); listView.getSelectionModel().select(newIndex); - /* There is a strange behavior in JavaFX if you try to start editing - * a field on the same animation frame as another field lost focus. - * - * Therefore, we wait one animation cycle before setting the field - * into the editing state - */ + // There is a strange behavior in JavaFX if you try to start editing + // a field on the same animation frame as another field lost focus. + // Therefore, we wait one animation cycle before setting the field + // into the editing state runLater(() -> listView.edit(newIndex)); }); return button; } - //*********************************************************** - // PRIVATE CLASS : ENUM CELL - //*********************************************************** + private Button populateButton(final ListView listView) { + final Button button = new Button("Populate", FontAwesome.DATABASE.view()); + + button.setOnAction(e -> { + final DbmsProperty dbms = ancestor(column, DbmsProperty.class).get(); + final String dbmsName = dbms.getName(); + final String schemaName = ancestor(column, Schema.class).get().getName(); + final String tableName = column.getParentOrThrow().getName(); + final String columnName = column.getName(); + + final ProgressMeasure progress = ProgressMeasure.create(); + + if (!passwords.get(dbmsName).isPresent()) { + ui.showPasswordDialog(dbms); + } + + final char[] password = passwords.get(dbmsName).orElseThrow(() -> + new SpeedmentToolException( + "A password is required to populate enum constants field!" + ) + ); + + final CompletableFuture task = supplyAsync(() -> { + + final TableIdentifier tableId = + new TableIdentifierImpl<>( + dbmsName, schemaName, tableName + ); + + try { + // Hack to create a Manager for reading a single column from + // the database. + LOGGER.info("Creating Temporary Speedment..."); + + progress.setProgress(0.2); + final Injector inj = injector.newBuilder() + .withComponent(SqlStreamSupplierComponentImpl.class) + .withParam("temp.dbms", dbmsName) + .withParam("temp.schema", schemaName) + .withParam("temp.table", tableName) + .withParam("temp.column", columnName) + .withComponent(SingleColumnManager.class) + .beforeInitialized(PasswordComponent.class, passw -> { + LOGGER.info("Installing Password..."); + passw.put(dbmsName, password); + }) + .build(); + + LOGGER.info("Temporary Speedment built. Streaming..."); + + progress.setProgress(0.4); + final String constants = + inj.getOrThrow(SingleColumnManager.class).stream() + .distinct().sorted() + .collect(joining(",")); + + LOGGER.info("Streaming complete!"); + + // Run in UI thread: + runLater(() -> { + column.enumConstantsProperty().setValue(constants); + setValue(constants); + progress.setProgress(1.0); + }); + + return true; + } catch (final InstantiationException ex) { + LOGGER.error(ex); + return false; + } + }).handleAsync((res, ex) -> { + if (ex != null) { + LOGGER.error(ex); + return false; + } + + progress.setProgress(1.0); + return true; + }); + + ui.showProgressDialog("Populating Enum Constants", progress, task); + }); + + return button; + } + + //////////////////////////////////////////////////////////////////////////// + // Internal Class // + //////////////////////////////////////////////////////////////////////////// + + private static class SingleColumnManager implements Manager { + + private StringField field; + private TableIdentifier tableId; + + private @Inject StreamSupplierComponent streamSupplierComponent; + private @Config(name="temp.dbms", value="") String dbms; + private @Config(name="temp.schema", value="") String schema; + private @Config(name="temp.table", value="") String table; + private @Config(name="temp.column", value="") String column; + + SingleColumnManager() {} + + @ExecuteBefore(State.INITIALIZED) + void setFieldAndTableId() { + this.tableId = TableIdentifier.of(dbms, schema, table); + this.field = StringField.create( + new ColumnIdentifier() { + @Override + public String getColumnName() { + return column; + } + + @Override + public String getDbmsName() { + return tableId.getDbmsName(); + } + + @Override + public String getSchemaName() { + return tableId.getSchemaName(); + } + + @Override + public String getTableName() { + return tableId.getTableName(); + } + }, + e -> e, (e, s) -> e, + TypeMapper.identity(), + false + ); + } + + @ExecuteBefore(State.INITIALIZED) + void configureManagerComponent( + @WithState(State.INITIALIZED) ManagerComponent managerComponent, + @WithState(State.INITIALIZED) ProjectComponent projectComponent) { + Objects.requireNonNull(projectComponent); + managerComponent.put(this); + } + + @ExecuteBefore(RESOLVED) + void configureSqlStreamSupplier( + @WithState(RESOLVED) SqlStreamSupplierComponent sql) { + sql.install(tableId, in -> in.getString(column)); + } + + @Override + public TableIdentifier getTableIdentifier() { + return tableId; + } + + @Override + public Class getEntityClass() { + return String.class; + } + + @Override + public Stream> fields() { + return SingletonStream.of(field); + } + + @Override + public Stream> primaryKeyFields() { + return SingletonStream.of(field); + } + + @Override + public Stream stream() { + return streamSupplierComponent.stream( + getTableIdentifier(), + ParallelStrategy.computeIntensityDefault() + ); + } + + @Override + public final String persist(String entity) throws SpeedmentException { + throw new UnsupportedOperationException("This manager is read-only."); + } + + @Override + public Persister persister() { + throw new UnsupportedOperationException("This manager is read-only."); + } + + @Override + public final String update(String entity) throws SpeedmentException { + throw new UnsupportedOperationException("This manager is read-only."); + } + + @Override + public Updater updater() { + throw new UnsupportedOperationException("This manager is read-only."); + } + + @Override + public final String remove(String entity) throws SpeedmentException { + throw new UnsupportedOperationException("This manager is read-only."); + } + + @Override + public Remover remover() { + throw new UnsupportedOperationException("This manager is read-only."); + } + } + private final static class EnumCell extends TextFieldListCell { private final ObservableList strings; @@ -257,12 +507,12 @@ public String fromString(String value) { return labelString; } - // Make sure this is not a douplicate entry - final AtomicBoolean douplicate = new AtomicBoolean(false); + // Make sure this is not a duplicate entry + final AtomicBoolean duplicate = new AtomicBoolean(false); strings.stream() .filter(elem -> elem.equalsIgnoreCase(value)) - .forEach(elem -> douplicate.set(true)); - if (douplicate.get()){ + .forEach(elem -> duplicate.set(true)); + if (duplicate.get()){ LOGGER.info("Enum cannot contain the same constant twice"); return labelString; diff --git a/plugin-parent/enum-generator/src/main/java/com/speedment/plugins/enums/internal/ui/CommaSeparatedStringEditor.java b/plugin-parent/enum-generator/src/main/java/com/speedment/plugins/enums/internal/ui/CommaSeparatedStringEditor.java index 5fd74b6f1d..1b6357246a 100644 --- a/plugin-parent/enum-generator/src/main/java/com/speedment/plugins/enums/internal/ui/CommaSeparatedStringEditor.java +++ b/plugin-parent/enum-generator/src/main/java/com/speedment/plugins/enums/internal/ui/CommaSeparatedStringEditor.java @@ -16,12 +16,15 @@ */ package com.speedment.plugins.enums.internal.ui; +import com.speedment.common.injector.Injector; +import com.speedment.common.injector.annotation.Inject; import com.speedment.plugins.enums.StringToEnumTypeMapper; import com.speedment.tool.config.ColumnProperty; import com.speedment.tool.propertyeditor.PropertyEditor; -import java.util.stream.Stream; import javafx.beans.binding.Bindings; +import java.util.stream.Stream; + /** * Editor for generating a comma-separated string. *

@@ -35,17 +38,20 @@ * @since 3.0.0 */ public class CommaSeparatedStringEditor - implements PropertyEditor { +implements PropertyEditor { + + private @Inject Injector injector; @Override public Stream fieldsFor(T document) { return Stream.of( new AddRemoveStringItem( + document, "Enum Constants", document.enumConstantsProperty(), "Used for defining what contants the generated enum can have", Bindings.equal(document.typeMapperProperty(), StringToEnumTypeMapper.class.getName()) ) - ); + ).map(injector::inject); } }