You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by db...@apache.org on 2022/02/18 13:11:16 UTC

[netbeans] branch master updated: Support for generating Micronaut Data entity classes end repository interfaces added. (#3584)

This is an automated email from the ASF dual-hosted git repository.

dbalek pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git


The following commit(s) were added to refs/heads/master by this push:
     new 9310f3d  Support for generating Micronaut Data entity classes end repository interfaces added. (#3584)
9310f3d is described below

commit 9310f3d51c8ea2da7db55f336382ea120610bb20
Author: Dusan Balek <du...@oracle.com>
AuthorDate: Fri Feb 18 14:10:56 2022 +0100

    Support for generating Micronaut Data entity classes end repository interfaces added. (#3584)
---
 enterprise/micronaut/nbproject/project.properties  |   1 +
 enterprise/micronaut/nbproject/project.xml         |  41 +-
 .../modules/micronaut/db/Bundle.properties         |  44 ++
 .../modules/micronaut/db/EntityClassesPanel.form   | 247 ++++++
 .../modules/micronaut/db/EntityClassesPanel.java   | 398 ++++++++++
 .../modules/micronaut/db/MicronautEntity.java      | 831 +++++++++++++++++++++
 .../modules/micronaut/db/MicronautRepository.java  | 357 +++++++++
 .../org/netbeans/modules/micronaut/db/Utils.java   |  41 +
 .../micronaut/resources/EntityDatabase.html        |  29 +
 .../modules/micronaut/resources/Micronaut.html     |  28 +
 .../modules/micronaut/resources/Repository.html    |  29 +
 .../netbeans/modules/micronaut/resources/layer.xml |  28 +
 java/j2ee.core.utilities/manifest.mf               |   2 +-
 java/j2ee.core.utilities/nbproject/project.xml     |   1 +
 java/j2ee.persistence/nbproject/project.properties |   2 +-
 java/j2ee.persistence/nbproject/project.xml        |   1 +
 .../entity/generator/EntitiesFromDBGenerator.java  |  40 +-
 .../wizard/fromdb/DatabaseTablesPanel.java         |   4 +-
 .../wizard/fromdb/EntityClassesPanel.java          |  23 +-
 .../wizard/fromdb/RelatedCMPWizard.java            |  26 +-
 .../netbeans/modules/nbcode/integration/layer.xml  |   1 +
 java/java.lsp.server/nbproject/project.xml         |  10 +-
 .../java/lsp/server/protocol/LspTemplateUI.java    |  89 +--
 .../lsp/server/protocol/WorkspaceUIContext.java    |   5 +
 .../lsp/server/ui/NotifyDescriptorAdapter.java     |  18 +
 .../modules/java/lsp/server/ui/UIContext.java      |  13 +
 .../lsp/server/ui/AbstractDialogDisplayerTest.java |   9 +
 java/java.lsp.server/vscode/src/extension.ts       |   9 +-
 platform/openide.dialogs/apichanges.xml            |  14 +
 platform/openide.dialogs/manifest.mf               |   2 +-
 .../src/org/openide/NotifyDescriptor.java          | 173 +++++
 31 files changed, 2433 insertions(+), 83 deletions(-)

diff --git a/enterprise/micronaut/nbproject/project.properties b/enterprise/micronaut/nbproject/project.properties
index 2f2157d..5ed4049 100644
--- a/enterprise/micronaut/nbproject/project.properties
+++ b/enterprise/micronaut/nbproject/project.properties
@@ -20,3 +20,4 @@ javac.compilerargs=-Xlint -Xlint:-serial
 release.external/spring-boot-configuration-metadata-2.4.4.jar=modules/ext/spring-boot-configuration-metadata-2.4.4.jar
 release.external/android-json-0.0.20131108.vaadin1.jar=modules/ext/android-json-0.0.20131108.vaadin1.jar
 spec.version.base=1.5.0
+requires.nb.javac=true
diff --git a/enterprise/micronaut/nbproject/project.xml b/enterprise/micronaut/nbproject/project.xml
index 61dc5d8..e6d3c51 100644
--- a/enterprise/micronaut/nbproject/project.xml
+++ b/enterprise/micronaut/nbproject/project.xml
@@ -34,6 +34,15 @@
                     </run-dependency>
                 </dependency>
                 <dependency>
+                    <code-name-base>org.netbeans.api.java</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>1</release-version>
+                        <specification-version>1.82</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
                     <code-name-base>org.netbeans.api.java.classpath</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
@@ -80,9 +89,7 @@
                     <code-name-base>org.netbeans.libs.javacapi</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
-                    <run-dependency>
-                        <specification-version>8.33</specification-version>
-                    </run-dependency>
+                    <run-dependency/>
                 </dependency>
                 <dependency>
                     <code-name-base>org.netbeans.modules.csl.api</code-name-base>
@@ -103,6 +110,15 @@
                     </run-dependency>
                 </dependency>
                 <dependency>
+                    <code-name-base>org.netbeans.modules.db</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>1</release-version>
+                        <specification-version>1.85</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
                     <code-name-base>org.netbeans.modules.editor.codetemplates</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
@@ -165,6 +181,23 @@
                     </run-dependency>
                 </dependency>
                 <dependency>
+                    <code-name-base>org.netbeans.modules.j2ee.core.utilities</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>0-1</release-version>
+                        <specification-version>1.50</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.j2ee.persistence</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>1.71</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
                     <code-name-base>org.netbeans.modules.java.lexer</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
@@ -264,7 +297,7 @@
                     <build-prerequisite/>
                     <compile-dependency/>
                     <run-dependency>
-                        <specification-version>7.53</specification-version>
+                        <specification-version>7.60</specification-version>
                     </run-dependency>
                 </dependency>
                 <dependency>
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/db/Bundle.properties b/enterprise/micronaut/src/org/netbeans/modules/micronaut/db/Bundle.properties
new file mode 100644
index 0000000..071d369
--- /dev/null
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/db/Bundle.properties
@@ -0,0 +1,44 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+Templates/Micronaut=Micronaut
+Templates/Micronaut/Entity=Micronaut Data Entity Classes from Database
+Templates/Micronaut/Repository=Micronaut Data Repository Interfaces from Entities
+
+# {0} = entity class name
+TXT_GeneratingClass=Generating the {0} entity class
+# {0} = entity class name
+MSG_Entity_Class=Entity class {0}\n
+
+USG_PERSISTENCE_ENTITY_DB_CREATED={0} entity classes was generated from db
+
+LBL_EntityClasses=Entity Classes
+LBL_AvailableEntities=Available &Entity Classes:
+TXT_AvailableEntities=Available Entity Classes
+ACSN_AvailableEntities=Available entity classes
+ACSD_AvailableEntities=List of all available entity classes
+LBL_SelectedEntities=S&elected Entity Classes:
+TXT_SelectedEntities=Selected Entity Classes
+LBL_AddAll=Add A&ll >>
+LBL_Add=&Add >
+LBL_Remove=< &Remove
+LBL_RemoveAll=<< Re&move All
+ACSN_SelectedEntities=Selected entity classes
+ACSD_SelectedEntities=List of selected entity classes
+ERR_SelectEntities=Select at least one entity class
+# {0} = project name
+ERR_NoEntities=No entity class found in {0}
\ No newline at end of file
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/db/EntityClassesPanel.form b/enterprise/micronaut/src/org/netbeans/modules/micronaut/db/EntityClassesPanel.form
new file mode 100644
index 0000000..0a3bcbb
--- /dev/null
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/db/EntityClassesPanel.form
@@ -0,0 +1,247 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+
+<Form version="1.3" maxVersion="1.3" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
+  <Properties>
+    <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+      <Dimension value="[200, 300]"/>
+    </Property>
+    <Property name="name" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+      <ResourceString bundle="org/netbeans/modules/micronaut/db/Bundle.properties" key="LBL_EntityClasses" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+    </Property>
+    <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+      <Dimension value="[496, 350]"/>
+    </Property>
+  </Properties>
+  <AuxValues>
+    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+    <AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,91,0,0,2,115"/>
+  </AuxValues>
+
+  <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
+  <SubComponents>
+    <Container class="javax.swing.JPanel" name="entitiesPanel">
+      <Properties>
+        <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+          <Dimension value="[440, 174]"/>
+        </Property>
+      </Properties>
+      <AuxValues>
+        <AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new EntitiesPanel();"/>
+      </AuxValues>
+      <Constraints>
+        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+          <GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="80" insetsTop="6" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="2.0"/>
+        </Constraint>
+      </Constraints>
+
+      <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
+      <SubComponents>
+        <Component class="javax.swing.JLabel" name="availableEntitiesLabel">
+          <Properties>
+            <Property name="labelFor" type="java.awt.Component" editor="org.netbeans.modules.form.ComponentChooserEditor">
+              <ComponentRef name="availableEntitiesList"/>
+            </Property>
+            <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+              <ResourceString bundle="org/netbeans/modules/micronaut/db/Bundle.properties" key="LBL_AvailableEntities" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+            </Property>
+            <Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+              <ResourceString bundle="org/netbeans/modules/micronaut/db/Bundle.properties" key="TXT_AvailableEntities" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+            </Property>
+          </Properties>
+          <Constraints>
+            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+              <GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="5" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
+            </Constraint>
+          </Constraints>
+        </Component>
+        <Container class="javax.swing.JScrollPane" name="availableEntitiesScrollPane">
+          <Properties>
+            <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+              <Dimension value="[160, 130]"/>
+            </Property>
+          </Properties>
+          <AuxValues>
+            <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
+          </AuxValues>
+          <Constraints>
+            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+              <GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="17" weightX="1.0" weightY="1.0"/>
+            </Constraint>
+          </Constraints>
+
+          <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+          <SubComponents>
+            <Component class="javax.swing.JList" name="availableEntitiesList">
+              <Properties>
+                <Property name="nextFocusableComponent" type="java.awt.Component" editor="org.netbeans.modules.form.ComponentChooserEditor">
+                  <ComponentRef name="addButton"/>
+                </Property>
+              </Properties>
+              <AccessibilityProperties>
+                <Property name="AccessibleContext.accessibleName" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+                  <ResourceString bundle="org/netbeans/modules/micronaut/db/Bundle.properties" key="ACSN_AvailableEntities" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+                </Property>
+                <Property name="AccessibleContext.accessibleDescription" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+                  <ResourceString bundle="org/netbeans/modules/micronaut/db/Bundle.properties" key="ACSD_AvailableEntities" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+                </Property>
+              </AccessibilityProperties>
+              <AuxValues>
+                <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
+              </AuxValues>
+            </Component>
+          </SubComponents>
+        </Container>
+        <Component class="javax.swing.JLabel" name="selectedEntitiesLabel">
+          <Properties>
+            <Property name="labelFor" type="java.awt.Component" editor="org.netbeans.modules.form.ComponentChooserEditor">
+              <ComponentRef name="selectedEntitiesList"/>
+            </Property>
+            <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+              <ResourceString bundle="org/netbeans/modules/micronaut/db/Bundle.properties" key="LBL_SelectedEntities" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+            </Property>
+            <Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+              <ResourceString bundle="org/netbeans/modules/micronaut/db/Bundle.properties" key="TXT_SelectedEntities" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+            </Property>
+          </Properties>
+          <Constraints>
+            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+              <GridBagConstraints gridX="2" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="5" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
+            </Constraint>
+          </Constraints>
+        </Component>
+        <Container class="javax.swing.JScrollPane" name="selectedEntitiesScrollPane">
+          <Properties>
+            <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+              <Dimension value="[160, 130]"/>
+            </Property>
+          </Properties>
+          <AuxValues>
+            <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
+          </AuxValues>
+          <Constraints>
+            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+              <GridBagConstraints gridX="2" gridY="1" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="17" weightX="1.0" weightY="1.0"/>
+            </Constraint>
+          </Constraints>
+
+          <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+          <SubComponents>
+            <Component class="javax.swing.JList" name="selectedEntitiesList">
+              <AccessibilityProperties>
+                <Property name="AccessibleContext.accessibleName" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+                  <ResourceString bundle="org/netbeans/modules/micronaut/db/Bundle.properties" key="ACSN_SelectedEntities" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+                </Property>
+                <Property name="AccessibleContext.accessibleDescription" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+                  <ResourceString bundle="org/netbeans/modules/micronaut/db/Bundle.properties" key="ACSD_SelectedEntities" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+                </Property>
+              </AccessibilityProperties>
+              <AuxValues>
+                <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
+              </AuxValues>
+            </Component>
+          </SubComponents>
+        </Container>
+        <Container class="javax.swing.JPanel" name="buttonPanel">
+          <Constraints>
+            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+              <GridBagConstraints gridX="1" gridY="0" gridWidth="1" gridHeight="2" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="11" insetsBottom="0" insetsRight="11" anchor="10" weightX="0.0" weightY="0.0"/>
+            </Constraint>
+          </Constraints>
+
+          <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
+          <SubComponents>
+            <Component class="javax.swing.JButton" name="addButton">
+              <Properties>
+                <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+                  <ResourceString bundle="org/netbeans/modules/j2ee/persistence/wizard/fromdb/Bundle.properties" key="LBL_Add" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+                </Property>
+              </Properties>
+              <Events>
+                <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="addButtonActionPerformed"/>
+              </Events>
+              <Constraints>
+                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+                  <GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
+                </Constraint>
+              </Constraints>
+            </Component>
+            <Component class="javax.swing.JButton" name="removeButton">
+              <Properties>
+                <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+                  <ResourceString bundle="org/netbeans/modules/j2ee/persistence/wizard/fromdb/Bundle.properties" key="LBL_Remove" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+                </Property>
+              </Properties>
+              <Events>
+                <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="removeButtonActionPerformed"/>
+              </Events>
+              <Constraints>
+                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+                  <GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="5" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
+                </Constraint>
+              </Constraints>
+            </Component>
+            <Component class="javax.swing.JButton" name="addAllButton">
+              <Properties>
+                <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+                  <ResourceString bundle="org/netbeans/modules/j2ee/persistence/wizard/fromdb/Bundle.properties" key="LBL_AddAll" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+                </Property>
+              </Properties>
+              <Events>
+                <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="addAllButtonActionPerformed"/>
+              </Events>
+              <Constraints>
+                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+                  <GridBagConstraints gridX="0" gridY="3" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="17" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
+                </Constraint>
+              </Constraints>
+            </Component>
+            <Component class="javax.swing.JButton" name="removeAllButton">
+              <Properties>
+                <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+                  <ResourceString bundle="org/netbeans/modules/j2ee/persistence/wizard/fromdb/Bundle.properties" key="LBL_RemoveAll" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+                </Property>
+              </Properties>
+              <Events>
+                <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="removeAllButtonActionPerformed"/>
+              </Events>
+              <Constraints>
+                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+                  <GridBagConstraints gridX="0" gridY="4" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="5" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
+                </Constraint>
+              </Constraints>
+            </Component>
+          </SubComponents>
+        </Container>
+      </SubComponents>
+    </Container>
+  </SubComponents>
+</Form>
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/db/EntityClassesPanel.java b/enterprise/micronaut/src/org/netbeans/modules/micronaut/db/EntityClassesPanel.java
new file mode 100644
index 0000000..ef4515e
--- /dev/null
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/db/EntityClassesPanel.java
@@ -0,0 +1,398 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.netbeans.modules.micronaut.db;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Rectangle;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import javax.swing.JPanel;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import org.netbeans.api.project.ProjectUtils;
+import org.netbeans.spi.project.ui.templates.support.Templates;
+import org.openide.WizardDescriptor;
+import org.openide.util.ChangeSupport;
+import org.openide.util.HelpCtx;
+import org.openide.util.NbBundle;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public class EntityClassesPanel extends javax.swing.JPanel {
+
+    private final ChangeSupport changeSupport = new ChangeSupport(this);
+    private final Set<String> availableEntities = new HashSet<>();
+    private final Set<String> selectedEntities = new HashSet<>();
+
+    public EntityClassesPanel() {
+        initComponents();
+        ListSelectionListener selectionListener = new ListSelectionListener() {
+            @Override
+            public void valueChanged(ListSelectionEvent e) {
+                updateButtons();
+            }
+        };
+        availableEntitiesList.getSelectionModel().addListSelectionListener(selectionListener);
+        selectedEntitiesList.getSelectionModel().addListSelectionListener(selectionListener);
+    }
+
+    public void addChangeListener(ChangeListener listener) {
+        changeSupport.addChangeListener(listener);
+    }
+
+    public void initialize(Set<String> entities) {
+        availableEntities.addAll(entities);
+        availableEntitiesList.setListData(availableEntities.toArray(new String[availableEntities.size()]));
+        selectedEntitiesList.setListData(selectedEntities.toArray(new String[selectedEntities.size()]));
+        updateButtons();
+    }
+
+    public Set<String> getSelectedEntities() {
+        return selectedEntities;
+    }
+
+    private void updateButtons() {
+        addButton.setEnabled(availableEntitiesList.getSelectedIndices().length > 0);
+        addAllButton.setEnabled(!availableEntities.isEmpty());
+        removeButton.setEnabled(selectedEntitiesList.getSelectedIndices().length > 0);
+        removeAllButton.setEnabled(!selectedEntities.isEmpty());
+    }
+
+    /** This method is called from within the constructor to
+     * initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is
+     * always regenerated by the Form Editor.
+     */
+    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+    private void initComponents() {
+        java.awt.GridBagConstraints gridBagConstraints;
+
+        entitiesPanel = new EntitiesPanel();
+        availableEntitiesLabel = new javax.swing.JLabel();
+        availableEntitiesScrollPane = new javax.swing.JScrollPane();
+        availableEntitiesList = new javax.swing.JList<>();
+        selectedEntitiesLabel = new javax.swing.JLabel();
+        selectedEntitiesScrollPane = new javax.swing.JScrollPane();
+        selectedEntitiesList = new javax.swing.JList<>();
+        buttonPanel = new javax.swing.JPanel();
+        addButton = new javax.swing.JButton();
+        removeButton = new javax.swing.JButton();
+        addAllButton = new javax.swing.JButton();
+        removeAllButton = new javax.swing.JButton();
+
+        setMinimumSize(new java.awt.Dimension(200, 300));
+        setName(org.openide.util.NbBundle.getMessage(EntityClassesPanel.class, "LBL_EntityClasses")); // NOI18N
+        setPreferredSize(new java.awt.Dimension(496, 350));
+        setLayout(new java.awt.GridBagLayout());
+
+        entitiesPanel.setPreferredSize(new java.awt.Dimension(440, 174));
+        entitiesPanel.setLayout(new java.awt.GridBagLayout());
+
+        availableEntitiesLabel.setLabelFor(availableEntitiesList);
+        org.openide.awt.Mnemonics.setLocalizedText(availableEntitiesLabel, org.openide.util.NbBundle.getMessage(EntityClassesPanel.class, "LBL_AvailableEntities")); // NOI18N
+        availableEntitiesLabel.setToolTipText(org.openide.util.NbBundle.getMessage(EntityClassesPanel.class, "TXT_AvailableEntities")); // NOI18N
+        gridBagConstraints = new java.awt.GridBagConstraints();
+        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
+        gridBagConstraints.insets = new java.awt.Insets(0, 0, 5, 0);
+        entitiesPanel.add(availableEntitiesLabel, gridBagConstraints);
+
+        availableEntitiesScrollPane.setPreferredSize(new java.awt.Dimension(160, 130));
+
+        availableEntitiesList.setNextFocusableComponent(addButton);
+        availableEntitiesScrollPane.setViewportView(availableEntitiesList);
+        availableEntitiesList.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(EntityClassesPanel.class, "ACSN_AvailableEntities")); // NOI18N
+        availableEntitiesList.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(EntityClassesPanel.class, "ACSD_AvailableEntities")); // NOI18N
+
+        gridBagConstraints = new java.awt.GridBagConstraints();
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 1;
+        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
+        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
+        gridBagConstraints.weightx = 1.0;
+        gridBagConstraints.weighty = 1.0;
+        entitiesPanel.add(availableEntitiesScrollPane, gridBagConstraints);
+
+        selectedEntitiesLabel.setLabelFor(selectedEntitiesList);
+        org.openide.awt.Mnemonics.setLocalizedText(selectedEntitiesLabel, org.openide.util.NbBundle.getMessage(EntityClassesPanel.class, "LBL_SelectedEntities")); // NOI18N
+        selectedEntitiesLabel.setToolTipText(org.openide.util.NbBundle.getMessage(EntityClassesPanel.class, "TXT_SelectedEntities")); // NOI18N
+        gridBagConstraints = new java.awt.GridBagConstraints();
+        gridBagConstraints.gridx = 2;
+        gridBagConstraints.gridy = 0;
+        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
+        gridBagConstraints.insets = new java.awt.Insets(0, 0, 5, 0);
+        entitiesPanel.add(selectedEntitiesLabel, gridBagConstraints);
+
+        selectedEntitiesScrollPane.setPreferredSize(new java.awt.Dimension(160, 130));
+        selectedEntitiesScrollPane.setViewportView(selectedEntitiesList);
+        selectedEntitiesList.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(EntityClassesPanel.class, "ACSN_SelectedEntities")); // NOI18N
+        selectedEntitiesList.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(EntityClassesPanel.class, "ACSD_SelectedEntities")); // NOI18N
+
+        gridBagConstraints = new java.awt.GridBagConstraints();
+        gridBagConstraints.gridx = 2;
+        gridBagConstraints.gridy = 1;
+        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
+        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
+        gridBagConstraints.weightx = 1.0;
+        gridBagConstraints.weighty = 1.0;
+        entitiesPanel.add(selectedEntitiesScrollPane, gridBagConstraints);
+
+        buttonPanel.setLayout(new java.awt.GridBagLayout());
+
+        org.openide.awt.Mnemonics.setLocalizedText(addButton, org.openide.util.NbBundle.getMessage(EntityClassesPanel.class, "LBL_Add")); // NOI18N
+        addButton.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                addButtonActionPerformed(evt);
+            }
+        });
+        gridBagConstraints = new java.awt.GridBagConstraints();
+        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
+        buttonPanel.add(addButton, gridBagConstraints);
+
+        org.openide.awt.Mnemonics.setLocalizedText(removeButton, org.openide.util.NbBundle.getMessage(EntityClassesPanel.class, "LBL_Remove")); // NOI18N
+        removeButton.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                removeButtonActionPerformed(evt);
+            }
+        });
+        gridBagConstraints = new java.awt.GridBagConstraints();
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 1;
+        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
+        gridBagConstraints.insets = new java.awt.Insets(5, 0, 0, 0);
+        buttonPanel.add(removeButton, gridBagConstraints);
+
+        org.openide.awt.Mnemonics.setLocalizedText(addAllButton, org.openide.util.NbBundle.getMessage(EntityClassesPanel.class, "LBL_AddAll")); // NOI18N
+        addAllButton.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                addAllButtonActionPerformed(evt);
+            }
+        });
+        gridBagConstraints = new java.awt.GridBagConstraints();
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 3;
+        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
+        gridBagConstraints.insets = new java.awt.Insets(17, 0, 0, 0);
+        buttonPanel.add(addAllButton, gridBagConstraints);
+
+        org.openide.awt.Mnemonics.setLocalizedText(removeAllButton, org.openide.util.NbBundle.getMessage(EntityClassesPanel.class, "LBL_RemoveAll")); // NOI18N
+        removeAllButton.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                removeAllButtonActionPerformed(evt);
+            }
+        });
+        gridBagConstraints = new java.awt.GridBagConstraints();
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 4;
+        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
+        gridBagConstraints.insets = new java.awt.Insets(5, 0, 0, 0);
+        buttonPanel.add(removeAllButton, gridBagConstraints);
+
+        gridBagConstraints = new java.awt.GridBagConstraints();
+        gridBagConstraints.gridx = 1;
+        gridBagConstraints.gridy = 0;
+        gridBagConstraints.gridheight = 2;
+        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
+        gridBagConstraints.insets = new java.awt.Insets(0, 11, 0, 11);
+        entitiesPanel.add(buttonPanel, gridBagConstraints);
+
+        gridBagConstraints = new java.awt.GridBagConstraints();
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 1;
+        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
+        gridBagConstraints.ipady = 80;
+        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
+        gridBagConstraints.weighty = 2.0;
+        gridBagConstraints.insets = new java.awt.Insets(6, 0, 0, 0);
+        add(entitiesPanel, gridBagConstraints);
+    }// </editor-fold>//GEN-END:initComponents
+
+    private void removeAllButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_removeAllButtonActionPerformed
+        availableEntities.addAll(selectedEntities);
+        selectedEntities.clear();
+        availableEntitiesList.setListData(availableEntities.toArray(new String[availableEntities.size()]));
+        selectedEntitiesList.setListData(selectedEntities.toArray(new String[selectedEntities.size()]));
+        updateButtons();
+        changeSupport.fireChange();
+    }//GEN-LAST:event_removeAllButtonActionPerformed
+
+    private void addAllButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addAllButtonActionPerformed
+        selectedEntities.addAll(availableEntities);
+        availableEntities.clear();
+        availableEntitiesList.setListData(availableEntities.toArray(new String[availableEntities.size()]));
+        selectedEntitiesList.setListData(selectedEntities.toArray(new String[selectedEntities.size()]));
+        updateButtons();
+        changeSupport.fireChange();
+    }//GEN-LAST:event_addAllButtonActionPerformed
+
+    private void removeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_removeButtonActionPerformed
+        availableEntities.addAll(selectedEntitiesList.getSelectedValuesList());
+        selectedEntities.removeAll(selectedEntitiesList.getSelectedValuesList());
+        availableEntitiesList.setListData(availableEntities.toArray(new String[availableEntities.size()]));
+        selectedEntitiesList.setListData(selectedEntities.toArray(new String[selectedEntities.size()]));
+        updateButtons();
+        changeSupport.fireChange();
+    }//GEN-LAST:event_removeButtonActionPerformed
+
+    private void addButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addButtonActionPerformed
+        selectedEntities.addAll(availableEntitiesList.getSelectedValuesList());
+        availableEntities.removeAll(availableEntitiesList.getSelectedValuesList());
+        availableEntitiesList.setListData(availableEntities.toArray(new String[availableEntities.size()]));
+        selectedEntitiesList.setListData(selectedEntities.toArray(new String[selectedEntities.size()]));
+        updateButtons();
+        changeSupport.fireChange();
+    }//GEN-LAST:event_addButtonActionPerformed
+
+
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JButton addAllButton;
+    private javax.swing.JButton addButton;
+    private javax.swing.JLabel availableEntitiesLabel;
+    private javax.swing.JList<String> availableEntitiesList;
+    private javax.swing.JScrollPane availableEntitiesScrollPane;
+    private javax.swing.JPanel buttonPanel;
+    private javax.swing.JPanel entitiesPanel;
+    private javax.swing.JButton removeAllButton;
+    private javax.swing.JButton removeButton;
+    private javax.swing.JLabel selectedEntitiesLabel;
+    private javax.swing.JList<String> selectedEntitiesList;
+    private javax.swing.JScrollPane selectedEntitiesScrollPane;
+    // End of variables declaration//GEN-END:variables
+
+    private final class EntitiesPanel extends JPanel {
+
+        @Override
+        public void doLayout() {
+            super.doLayout();
+
+            Rectangle availableBounds = availableEntitiesScrollPane.getBounds();
+            Rectangle selectedBounds = selectedEntitiesScrollPane.getBounds();
+
+            if (Math.abs(availableBounds.width - selectedBounds.width) > 1) {
+                GridBagConstraints buttonPanelConstraints = ((GridBagLayout)getLayout()).getConstraints(buttonPanel);
+                int totalWidth = getWidth() - buttonPanel.getWidth() - buttonPanelConstraints.insets.left - buttonPanelConstraints.insets.right;
+                int equalWidth = totalWidth / 2;
+                int xOffset = equalWidth - availableBounds.width;
+
+                availableBounds.width = equalWidth;
+                availableEntitiesScrollPane.setBounds(availableBounds);
+
+                Rectangle buttonBounds = buttonPanel.getBounds();
+                buttonBounds.x += xOffset;
+                buttonPanel.setBounds(buttonBounds);
+
+                Rectangle labelBounds = selectedEntitiesLabel.getBounds();
+                labelBounds.x += xOffset;
+                selectedEntitiesLabel.setBounds(labelBounds);
+
+                selectedBounds.x += xOffset;
+                selectedBounds.width = totalWidth - equalWidth;
+                selectedEntitiesScrollPane.setBounds(selectedBounds);
+            }
+        }
+    }
+
+    public static final class WizardPanel implements WizardDescriptor.Panel<WizardDescriptor>, ChangeListener {
+
+        private final ChangeSupport changeSupport = new ChangeSupport(this);
+        private final String title;
+        private EntityClassesPanel component;
+        private boolean componentInitialized;
+        private WizardDescriptor wizardDescriptor;
+        private Map<String, String> entities;
+
+        public WizardPanel(String wizardTitle) {
+            title = wizardTitle;
+        }
+
+        @Override
+        public EntityClassesPanel getComponent() {
+            if (component == null) {
+                component = new EntityClassesPanel();
+                component.addChangeListener(this);
+            }
+            return component;
+        }
+
+        @Override
+        public HelpCtx getHelp() {
+                return new HelpCtx(EntityClassesPanel.class);
+        }
+
+        @Override
+        public void addChangeListener(ChangeListener listener) {
+            changeSupport.addChangeListener(listener);
+        }
+
+        @Override
+        public void removeChangeListener(ChangeListener listener) {
+            changeSupport.removeChangeListener(listener);
+        }
+
+        @Override
+        public void readSettings(WizardDescriptor settings) {
+            wizardDescriptor = settings;
+            if (title != null) {
+                wizardDescriptor.putProperty("NewFileWizard_Title", title); // NOI18N
+            }
+            if (!componentInitialized) {
+                componentInitialized = true;
+                entities = (Map<String, String>) settings.getProperty(MicronautRepository.PROP_ENTITIES);
+                getComponent().initialize(entities.keySet());
+            }
+        }
+
+        @Override
+        public boolean isValid() {
+            if (entities.isEmpty()) {
+                setErrorMessage(NbBundle.getMessage(EntityClassesPanel.class, "ERR_NoEntities", ProjectUtils.getInformation(Templates.getProject(wizardDescriptor)).getDisplayName()));
+                return false;
+            }
+            if (getComponent().getSelectedEntities().isEmpty()) {
+                setErrorMessage(NbBundle.getMessage(EntityClassesPanel.class, "ERR_SelectEntities"));
+                return false;
+            }
+            setErrorMessage(" "); // NOI18N
+            return true;
+        }
+
+        @Override
+        public void storeSettings(WizardDescriptor settings) {
+            Set<String> selected = getComponent().getSelectedEntities();
+            Map<String, String> selEntities = entities.entrySet().stream().filter(entry -> selected.contains(entry.getKey())).collect(Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue()));
+            wizardDescriptor.putProperty(MicronautRepository.PROP_SELECTED_ENTITIES, selEntities);
+        }
+
+        @Override
+        public void stateChanged(ChangeEvent event) {
+            changeSupport.fireChange();
+        }
+
+        private void setErrorMessage(String errorMessage) {
+            wizardDescriptor.putProperty(WizardDescriptor.PROP_ERROR_MESSAGE, errorMessage);
+        }
+    }
+}
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/db/MicronautEntity.java b/enterprise/micronaut/src/org/netbeans/modules/micronaut/db/MicronautEntity.java
new file mode 100644
index 0000000..95c224b
--- /dev/null
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/db/MicronautEntity.java
@@ -0,0 +1,831 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.micronaut.db;
+
+import com.sun.source.tree.AnnotationTree;
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.VariableTree;
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.ModuleElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+import org.netbeans.api.db.explorer.ConnectionManager;
+import org.netbeans.api.db.explorer.DatabaseConnection;
+import org.netbeans.api.db.explorer.DatabaseException;
+import org.netbeans.api.java.classpath.ClassPath;
+import org.netbeans.api.java.classpath.JavaClassPathConstants;
+import org.netbeans.api.java.project.JavaProjectConstants;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.TreeMaker;
+import org.netbeans.api.java.source.WorkingCopy;
+import org.netbeans.api.progress.aggregate.BasicAggregateProgressFactory;
+import org.netbeans.api.progress.aggregate.ProgressContributor;
+import org.netbeans.api.project.FileOwnerQuery;
+import org.netbeans.api.project.Project;
+import org.netbeans.api.project.ProjectUtils;
+import org.netbeans.api.project.SourceGroup;
+import org.netbeans.api.templates.CreateDescriptor;
+import org.netbeans.api.templates.CreateFromTemplateHandler;
+import org.netbeans.modules.j2ee.core.api.support.SourceGroups;
+import org.netbeans.modules.j2ee.core.api.support.java.GenerationUtils;
+import org.netbeans.modules.j2ee.core.api.support.java.SourceUtils;
+import org.netbeans.modules.j2ee.persistence.api.entity.generator.EntitiesFromDBGenerator;
+import org.netbeans.modules.j2ee.persistence.dd.PersistenceUtils;
+import org.netbeans.modules.j2ee.persistence.entitygenerator.CMPMappingModel;
+import org.netbeans.modules.j2ee.persistence.entitygenerator.EntityClass;
+import org.netbeans.modules.j2ee.persistence.entitygenerator.EntityMember;
+import org.netbeans.modules.j2ee.persistence.entitygenerator.EntityRelation;
+import org.netbeans.modules.j2ee.persistence.entitygenerator.RelationshipRole;
+import org.netbeans.modules.j2ee.persistence.util.JPAClassPathHelper;
+import org.netbeans.modules.j2ee.persistence.wizard.fromdb.JavaPersistenceGenerator;
+import org.netbeans.modules.j2ee.persistence.wizard.fromdb.PersistenceGenerator;
+import org.netbeans.modules.j2ee.persistence.wizard.fromdb.PersistenceGeneratorProvider;
+import org.netbeans.modules.j2ee.persistence.wizard.fromdb.ProgressPanel;
+import org.netbeans.modules.j2ee.persistence.wizard.fromdb.RelatedCMPHelper;
+import org.netbeans.modules.j2ee.persistence.wizard.fromdb.RelatedCMPWizard;
+import org.netbeans.modules.j2ee.persistence.wizard.fromdb.UpdateType;
+import org.openide.DialogDescriptor;
+import org.openide.DialogDisplayer;
+import org.openide.NotifyDescriptor;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.loaders.DataObject;
+import org.openide.loaders.TemplateWizard;
+import org.openide.util.NbBundle;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public class MicronautEntity extends RelatedCMPWizard {
+
+    private static final String TYPE_MICRONAUT = "micronaut"; // NOI18N
+
+    public static MicronautEntity create() {
+        return new MicronautEntity(TYPE_MICRONAUT);
+    }
+
+    @NbBundle.Messages({
+        "MSG_NoDbConn=No database connection found",
+        "MSG_NoProject=No project found for {0}",
+        "MSG_NoSourceGroup=No source group found for {0}",
+        "MSG_SelectTables=Select Database Tables",
+        "MSG_NoDbTables=No database table found for {0}"
+    })
+    public static CreateFromTemplateHandler handler() {
+        return new CreateFromTemplateHandler() {
+            @Override
+            protected boolean accept(CreateDescriptor desc) {
+                return true;
+            }
+
+            @Override
+            protected List<FileObject> createFromTemplate(CreateDescriptor desc) throws IOException {
+                try {
+                    FileObject folder = desc.getTarget();
+                    Project project = FileOwnerQuery.getOwner(folder);
+                    if (project == null) {
+                        DialogDisplayer.getDefault().notifyLater(new NotifyDescriptor.Message(Bundle.MSG_NoProject(folder.getPath()), NotifyDescriptor.ERROR_MESSAGE));
+                        return Collections.emptyList();
+                    }
+                    SourceGroup sourceGroup = SourceGroups.getFolderSourceGroup(ProjectUtils.getSources(project).getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA), folder);
+                    if (sourceGroup == null) {
+                        DialogDisplayer.getDefault().notifyLater(new NotifyDescriptor.Message(Bundle.MSG_NoSourceGroup(folder.getPath()), NotifyDescriptor.ERROR_MESSAGE));
+                        return Collections.emptyList();
+                    }
+                    DatabaseConnection connection = ConnectionManager.getDefault().getPreferredConnection(true);
+                    if (connection == null) {
+                        DialogDisplayer.getDefault().notifyLater(new NotifyDescriptor.Message(Bundle.MSG_NoDbConn(), NotifyDescriptor.ERROR_MESSAGE));
+                        return Collections.emptyList();
+                    }
+                    ConnectionManager.getDefault().connect(connection);
+                    Connection conn = connection.getJDBCConnection();
+                    ResultSet rs = conn.getMetaData().getTables(conn.getCatalog(), conn.getSchema(), "%", new String[]{"TABLE", "VIEW"}); //NOI18N
+                    List<NotifyDescriptor.QuickPick.Item> dbItems = new ArrayList<>();
+                    while (rs.next()) {
+                        dbItems.add(new NotifyDescriptor.QuickPick.Item(rs.getString("TABLE_NAME"), null)); //NOI18N
+                    }
+                    if (dbItems.isEmpty()) {
+                        DialogDisplayer.getDefault().notifyLater(new NotifyDescriptor.Message(Bundle.MSG_NoDbTables(connection.getDisplayName()), NotifyDescriptor.ERROR_MESSAGE));
+                        return Collections.emptyList();
+                    }
+                    NotifyDescriptor.QuickPick qp = new NotifyDescriptor.QuickPick(Bundle.MSG_SelectTables(), Bundle.MSG_SelectTables(), dbItems, true);
+                    if (DialogDescriptor.OK_OPTION == DialogDisplayer.getDefault().notify(qp)) {
+                        List<String> selectedItems = qp.getItems().stream().filter(item -> item.isSelected()).map(item -> item.getLabel()).collect(Collectors.toList());
+                        EntitiesFromDBGenerator generator = new EntitiesFromDBGenerator(selectedItems, false, false, false,
+                                EntityRelation.FetchType.DEFAULT, EntityRelation.CollectionType.COLLECTION,
+                                SourceGroups.getPackageForFolder(sourceGroup, folder), sourceGroup, connection, project, null, new Generator());
+                        ProgressContributor pc = BasicAggregateProgressFactory.createProgressContributor("entity"); //NOI18N\
+                        List<FileObject> generated = new ArrayList<>();
+                        for (FileObject fo : generator.generate(pc)) {
+                            if (fo != null) {
+                                generated.add(fo);
+                            }
+                        }
+                        return generated;
+                    }
+                } catch (IOException | SQLException | DatabaseException ex) {
+                    DialogDisplayer.getDefault().notifyLater(new NotifyDescriptor.Message(ex.getMessage(), NotifyDescriptor.ERROR_MESSAGE));
+                }
+                return Collections.emptyList();
+            }
+        };
+    }
+
+    private MicronautEntity(String type) {
+        super(type);
+    }
+
+    @Override
+    public Set<DataObject> instantiate(TemplateWizard wiz) throws IOException {
+        String wizardTitle = NbBundle.getMessage(MicronautEntity.class, "Templates/Micronaut/Entity");
+        wiz.putProperty("NewFileWizard_Title", wizardTitle); // NOI18N
+        return super.instantiate(wiz);
+    }
+
+    @org.openide.util.lookup.ServiceProvider(service=org.netbeans.modules.j2ee.persistence.wizard.fromdb.PersistenceGeneratorProvider.class)
+    public static class GeneratorProvider implements PersistenceGeneratorProvider {
+
+        @Override
+        public String getGeneratorType() {
+            return TYPE_MICRONAUT;
+        }
+
+        @Override
+        public PersistenceGenerator createGenerator() {
+            return new Generator();
+        }
+    }
+
+    private static class Generator extends JavaPersistenceGenerator {
+
+        private final Set<FileObject> generatedEntityFOs = new HashSet<>();
+        private final Map<String, String> replacedNames = new HashMap<>();
+        private final Map<String, String> replacedTypeNames = new HashMap<>();
+        private final Map<String, EntityClass> beanMap = new HashMap<>();
+
+        @Override
+        public Set<FileObject> createdObjects() {
+            return generatedEntityFOs;
+        }
+
+        @Override
+        public void generateBeans(ProgressPanel progressPanel, RelatedCMPHelper helper, FileObject dbschemaFile, ProgressContributor progressContributor) throws IOException {
+            try {
+                doGenerateBeans(progressPanel, helper, progressContributor);
+            } catch (IOException e) {
+                Logger.getLogger(Generator.class.getName()).log(Level.INFO, "IOException, remove generated."); //NOI18N
+                for (FileObject generatedFO : generatedEntityFOs) {
+                    try {
+                        generatedFO.delete();
+                    } catch (IOException ioe) {}
+                }
+                throw e;
+            }
+        }
+
+        private void doGenerateBeans(ProgressPanel progressPanel, RelatedCMPHelper helper, ProgressContributor progressContributor) throws IOException {
+            if (helper.getLocation() != null && helper.getPackageName() != null) {
+                SourceGroups.getFolderForPackage(helper.getLocation(), helper.getPackageName());
+            }
+
+            final boolean jpaSupported = Utils.isJPASupported(helper.getLocation());
+            final boolean beanValidationSupported = isBeanValidationSupported(helper.getLocation());
+
+            EntityClass[] entityClasses = helper.getBeans();
+            int progressMax = entityClasses.length * 2;
+            progressContributor.start(progressMax);
+
+            beanMap.clear();
+            Set<FileObject> generationPackageFOs = new HashSet<>();
+            Set<String> generatedEntityClasses = new HashSet<>();
+
+            for (int i = 0; i < entityClasses.length; i++) {
+                final EntityClass entityClass = entityClasses[i];
+                String entityClassName = entityClass.getClassName();
+                FileObject packageFileObject = entityClass.getPackageFileObject();
+                beanMap.put(entityClassName, entityClass);
+
+                String progressMsg = NbBundle.getMessage(Generator.class, "TXT_GeneratingClass", entityClassName);
+                progressContributor.progress(progressMsg, i);
+                if (progressPanel != null) {
+                    progressPanel.setText(progressMsg);
+                }
+
+                FileObject entity = packageFileObject.getFileObject(entityClassName, "java"); //NOI18N
+                switch (entityClass.getUpdateType()) {
+                    case RECREATE:
+                    case UPDATE:
+                        if (entity == null) {//we hit case when old entity position is different from target package
+                            String fqn = this.getFQClassName(entityClass.getTableName());
+                            int ind = fqn.lastIndexOf(".");
+                            String pkg = ind > -1 ? fqn.substring(0, ind) : "";
+                            String rel = pkg.replaceAll("\\.", "/");
+                            FileObject oldPackage = entityClass.getRootFolder().getFileObject(rel);
+                            entity = oldPackage.getFileObject(entityClassName, "java");
+                        }
+                        entity.delete();
+                        entity = null;
+                        //fall through is expected
+                    case NEW:
+                        generatedEntityClasses.add(entityClassName);
+                        try {
+                            String newName = entityClassName;
+                            int count = 1;
+                            while (packageFileObject.getFileObject(newName, "java") != null && count < 1000) {
+                                newName = entityClassName + "_" + count;
+                                count++;
+                            }
+                            entity = GenerationUtils.createClass(packageFileObject, newName, NbBundle.getMessage(Generator.class, "MSG_Entity_Class", newName));
+                            if (!newName.equals(entityClassName)) {
+                                replacedNames.put(entityClassName, newName);
+                                String pkg = entityClass.getPackage();
+                                String entityClassFQN = pkg == null || pkg.isEmpty() ? entityClassName : pkg + "." + entityClassName;
+                                String newFQN = pkg == null || pkg.isEmpty() ? newName : pkg + "." + newName;
+                                replacedTypeNames.put(entityClassFQN, newFQN);
+                                generatedEntityClasses.remove(entityClassName);
+                                generatedEntityClasses.add(newName);
+                            }
+                        } catch (RuntimeException ex) {
+                            Logger.getLogger(Generator.class.getName()).log(Level.WARNING, "Can't create class {0} from template in package {1} with package fileobject validity {2}.", new Object[]{entityClassName, packageFileObject.getPath(), packageFileObject.isValid()});//NOI18N
+                            throw ex;
+                        }
+                        generatedEntityFOs.add(entity);
+                        generationPackageFOs.add(packageFileObject);
+                }
+            }
+
+            Set<ClassPath> bootCPs = getAllClassPaths(generationPackageFOs, ClassPath.BOOT);
+            Set<ClassPath> compileCPs = getAllClassPaths(generationPackageFOs, ClassPath.COMPILE);
+            Set<ClassPath> sourceCPs = getAllClassPaths(generationPackageFOs, ClassPath.SOURCE);
+            JPAClassPathHelper cpHelper = new JPAClassPathHelper(bootCPs, compileCPs, sourceCPs)
+                    .setModuleBootPaths(getAllClassPaths(generationPackageFOs, JavaClassPathConstants.MODULE_BOOT_PATH))
+                    .setModuleCompilePaths(getAllClassPaths(generationPackageFOs, JavaClassPathConstants.MODULE_COMPILE_PATH))
+                    .setModuleClassPaths(getAllClassPaths(generationPackageFOs, JavaClassPathConstants.MODULE_CLASS_PATH))
+                    .setModuleSourcePaths(getAllClassPaths(generationPackageFOs, JavaClassPathConstants.MODULE_SOURCE_PATH));
+
+            for (int i = 0; i < entityClasses.length; i++) {
+                final EntityClass entityClass = entityClasses[i];
+                String entityClassName = getClassName(entityClass);
+
+                if (!generatedEntityClasses.contains(entityClassName) && !UpdateType.UPDATE.equals(entityClass.getUpdateType())) {
+                    // this entity class already existed, we didn't create it, so we don't want to touch it except Update type
+                    progressContributor.progress(entityClasses.length + i);
+                    continue;
+                }
+                String progressMsg = NbBundle.getMessage(Generator.class, "TXT_GeneratingClass", entityClassName);
+                progressContributor.progress(progressMsg, entityClasses.length + i);
+                if (progressPanel != null) {
+                    progressPanel.setText(progressMsg);
+                }
+                FileObject entityClassPackageFO = entityClass.getPackageFileObject();
+                FileObject entityClassFO = entityClassPackageFO.getFileObject( entityClassName, "java"); // NOI18N
+                try {
+                    JavaSource javaSource = JavaSource.create(cpHelper.createClasspathInfo(), entityClassFO);
+                    javaSource.runModificationTask(copy -> {
+                        new EntityClassGenerator(copy, entityClass, jpaSupported, beanValidationSupported).run();
+                    }).commit();
+                } catch (IOException e) {
+                    String message = e.getMessage();
+                    String newMessage = ((message == null)
+                            ? NbBundle.getMessage(Generator.class, "ERR_GeneratingClass_NoExceptionMessage", entityClassName)
+                            : NbBundle.getMessage(Generator.class, "ERR_GeneratingClass", entityClassName, message));
+                    throw new IOException(newMessage, e);
+                }
+            }
+
+            progressContributor.progress(progressMax);
+            PersistenceUtils.logUsage(Generator.class, "USG_PERSISTENCE_ENTITY_DB_CREATED", new Integer[]{entityClasses.length});
+        }
+
+        private String getClassName(EntityClass entityClass) {
+            return replacedNames.containsKey(entityClass.getClassName()) ? replacedNames.get(entityClass.getClassName()) : entityClass.getClassName();
+        }
+
+        private static Set<ClassPath> getAllClassPaths(Set<FileObject> fileObjects, String id) {
+            Set<ClassPath> classPaths = new HashSet<>();
+            for (FileObject fileObject : fileObjects) {
+                ClassPath cp = ClassPath.getClassPath(fileObject, id);
+                if (cp != null) {
+                    classPaths.add(cp);
+                }
+            }
+            return classPaths;
+        }
+
+        private static boolean isBeanValidationSupported(SourceGroup sg) {
+            if (sg == null) {
+                return false;
+            }
+            ClassPath compile = ClassPath.getClassPath(sg.getRootFolder(), ClassPath.COMPILE);
+            if (compile == null) {
+                return false;
+            }
+            final String notNullAnnotation = "javax.validation.constraints.NotNull"; //NOI18N
+            return compile.findResource(notNullAnnotation.replace('.', '/') + ".class") != null; //NOI18N
+        }
+
+        private abstract class ClassGenerator {
+
+            protected final WorkingCopy copy;
+            protected final GenerationUtils genUtils;
+            // the entity class we are generating
+            protected final EntityClass entityClass;
+            // the mapping of the entity class to the database
+            protected final CMPMappingModel dbMappings;
+            // generated properties
+            protected final List<Property> properties = new ArrayList<>();
+            // generated methods
+            protected final List<MethodTree> methods = new ArrayList<>();
+            // generated constructors
+            protected final List<MethodTree> constructors = new ArrayList<>();
+            // generated fields. does not include fields of properties, just plain fields
+            protected final List<VariableTree> fields = new ArrayList<>();
+            // the original class tree of the class we are generating
+            protected ClassTree originalClassTree;
+            // the modified class tree of the class we are generating
+            protected ClassTree newClassTree;
+            // the TypeElement corresponding to classTree
+            protected TypeElement typeElement;
+            // classTree's module
+            protected ModuleElement moduleElement;
+            // the generating type like New, Update etc
+            protected UpdateType updateType;
+
+            protected final boolean generateJPA;
+            protected final boolean generateValidationConstraints;
+
+            private ClassGenerator(WorkingCopy copy, EntityClass entityClass, boolean jpaSupported, boolean beanValidationSupported) throws IOException {
+                copy.toPhase(JavaSource.Phase.RESOLVED);
+                this.copy = copy;
+                this.entityClass = entityClass;
+                this.updateType = entityClass.getUpdateType();
+                dbMappings = entityClass.getCMPMapping();
+                typeElement = SourceUtils.getPublicTopLevelElement(copy);
+                if (typeElement == null) {
+                    throw new IllegalStateException("Cannot find a public top-level class named " + entityClass.getClassName() + // NOI18N
+                            " in " + FileUtil.getFileDisplayName(copy.getFileObject())); // NOI18N
+                }
+                moduleElement = copy.getElements().getModuleOf(typeElement);
+                originalClassTree = copy.getTrees().getTree(typeElement);
+                assert originalClassTree != null;
+                newClassTree = originalClassTree;
+                genUtils = GenerationUtils.newInstance(copy);
+                generateJPA = jpaSupported;
+                generateValidationConstraints = beanValidationSupported;
+            }
+
+            protected String createFieldName(String capitalizedFieldName) {
+                return createFieldNameImpl(capitalizedFieldName, false);
+            }
+
+            protected String createCapitalizedFieldName(String fieldName) {
+                return createFieldNameImpl(fieldName, true);
+            }
+
+            private String createFieldNameImpl(String fieldName, boolean capitalized) {
+                StringBuilder sb = new StringBuilder(fieldName);
+                char firstChar = sb.charAt(0);
+                sb.setCharAt(0, capitalized ? Character.toUpperCase(firstChar) : Character.toLowerCase(firstChar));
+                return sb.toString();
+            }
+
+            /**
+             * Creates a property for an entity member, that is, is creates
+             * a field, a getter and a setter method.
+             */
+            protected Property createProperty(EntityMember m) throws IOException {
+                boolean isPKMember = m.isPrimaryKey();
+                List<AnnotationTree> annotations = new ArrayList<>();
+
+                //add @Id() only if not in an embeddable PK class
+                if (isPKMember) {
+                    annotations.add(genUtils.createAnnotation(generateJPA ? "javax.persistence.Id" : "io.micronaut.data.annotation.Id")); // NOI18N
+                    if (m.isAutoIncrement()) {
+                        annotations.add(genUtils.createAnnotation(generateJPA ? "javax.persistence.GeneratedValue" : "io.micronaut.data.annotation.GeneratedValue")); //NOI18N
+                    }
+                }
+
+                if (!m.isNullable()) {
+                    //Add @NotNull constraint
+                    if (generateValidationConstraints && !m.isAutoIncrement()) {   //NOI18N
+                        annotations.add(genUtils.createAnnotation("javax.validation.constraints.NotNull")); //NOI18N
+                    }
+                }
+
+                boolean isLobType = m.isLobType();
+                if (isLobType && generateJPA) {
+                    annotations.add(genUtils.createAnnotation("javax.persistence.Lob")); // NOI18N
+                }
+
+                List<ExpressionTree> columnAnnArguments = new ArrayList<>();
+                String memberName = m.getMemberName();
+                String memberType = getMemberType(m);
+
+                String columnName = dbMappings.getCMPFieldMapping().get(memberName);
+                if (!memberName.equalsIgnoreCase(columnName)){
+                    columnAnnArguments.add(genUtils.createAnnotationArgument("name", columnName)); //NOI18N
+                }
+
+                Integer length = m.getLength();
+                if (length != null && isCharacterType(memberType)) {
+                    if (generateValidationConstraints) {
+                        List <ExpressionTree> sizeAnnArguments = new ArrayList<>();
+                        if (!m.isNullable()) {
+                            sizeAnnArguments.add(genUtils.createAnnotationArgument("min", 1));  //NOI18N
+                        }
+                        sizeAnnArguments.add(genUtils.createAnnotationArgument("max", length)); //NOI18N
+                        annotations.add(genUtils.createAnnotation("javax.validation.constraints.Size", sizeAnnArguments));   //NOI18N
+                    }
+                }
+
+                if (!columnAnnArguments.isEmpty() && generateJPA) {
+                    annotations.add(genUtils.createAnnotation("javax.persistence.Column", columnAnnArguments)); //NOI18N
+                }
+
+                String temporalType = getMemberTemporalType(m);
+                if (temporalType != null && generateJPA) {
+                    ExpressionTree temporalAnnValueArgument = genUtils.createAnnotationArgument(null, "javax.persistence.TemporalType", temporalType); //NOI18N
+                    annotations.add(genUtils.createAnnotation("javax.persistence.Temporal", Collections.singletonList(temporalAnnValueArgument)));
+                }
+
+                return new Property(Modifier.PRIVATE, annotations, memberType, memberName);
+            }
+
+            String getMemberType(EntityMember m) {
+                String memberType = m.getMemberType();
+                if ("java.sql.Date".equals(memberType)) { //NOI18N
+                    memberType = "java.util.Date";
+                } else if ("java.sql.Time".equals(memberType)) { //NOI18N
+                    memberType = "java.util.Date";
+                } else if ("java.sql.Timestamp".equals(memberType)) { //NOI18N
+                    memberType = "java.util.Date";
+                }
+                return memberType;
+            }
+
+            private boolean isCharacterType(String type) {
+                return "java.lang.String".equals(type); //NOI18N
+            }
+
+            private String getMemberTemporalType(EntityMember m) {
+                String memberType = m.getMemberType();
+                String temporalType = null;
+                if ("java.sql.Date".equals(memberType)) { //NOI18N
+                    temporalType = "DATE";
+                } else if ("java.sql.Time".equals(memberType)) { //NOI18N
+                    temporalType = "TIME";
+                } else if ("java.sql.Timestamp".equals(memberType)) { //NOI18N
+                    temporalType = "TIMESTAMP";
+                }
+                return temporalType;
+            }
+
+            public void run() throws IOException {
+                initialize();
+                for (Object object : entityClass.getFields()) {
+                    generateMember((EntityMember) object);
+                }
+                for (RelationshipRole roleObject : entityClass.getRoles()) {
+                    generateRelationship(roleObject);
+                }
+                finish();
+
+                // add the generated members
+                TreeMaker make = copy.getTreeMaker();
+                int position = 0;
+                for (VariableTree field : fields) {
+                    newClassTree = make.insertClassMember(newClassTree, position, field);
+                    position++;
+                }
+                for (Property property : properties) {
+                    newClassTree = make.insertClassMember(newClassTree, position, property.getField());
+                    position++;
+                }
+                for (MethodTree constructor : constructors) {
+                    newClassTree = make.addClassMember(newClassTree, constructor);
+                }
+                for (Property property : properties) {
+                    newClassTree = make.addClassMember(newClassTree, property.getGetter());
+                    newClassTree = make.addClassMember(newClassTree, property.getSetter());
+                }
+                for (MethodTree method : methods) {
+                    newClassTree = make.addClassMember(newClassTree, method);
+                }
+                Logger.getLogger(Generator.class.getName()).log(Level.FINE, "Rewrite entity tree with name: {0}", entityClass.getTableName()); //NOI18N
+                Logger.getLogger(Generator.class.getName()).log(Level.FINE, "Rewrite entity tree with annotations: length = {0}, annotations = {1}", new Object[]{newClassTree.getModifiers().getAnnotations().size(),  newClassTree.getModifiers().getAnnotations()}); //NOI18N
+                copy.rewrite(originalClassTree, newClassTree);
+            }
+
+            /**
+             * Called at the beginning of the generation process.
+             */
+            protected abstract void initialize() throws IOException;
+
+            /**
+             * Called for each entity class member.
+             */
+            protected abstract void generateMember(EntityMember m) throws IOException;
+
+            /**
+             * Called for each relationship.
+             */
+            protected abstract void generateRelationship(RelationshipRole role) throws IOException;
+
+            /**
+             * Called at the end of the generation process.
+             */
+            protected abstract void finish() throws IOException;
+
+            /**
+             * Encapsulates a generated property, that is, its field, getter
+             * and setter method.
+             */
+            protected final class Property {
+
+                private final VariableTree field;
+                private final MethodTree getter;
+                private final MethodTree setter;
+
+                public Property(Modifier modifier, List<AnnotationTree> annotations, String type, String name) throws IOException {
+                    this(modifier, annotations, genUtils.createType(type, typeElement), name);
+                }
+
+                public Property(Modifier modifier, List<AnnotationTree> annotations, TypeMirror type, String name) throws IOException {
+                    this(modifier, annotations, copy.getTreeMaker().Type(type), name);
+                }
+
+                private Property(Modifier modifier, List<AnnotationTree> annotations, Tree typeTree, String name) throws IOException {
+                    TreeMaker make = copy.getTreeMaker();
+                    field = make.Variable(
+                            make.Modifiers(EnumSet.of(modifier), annotations),
+                            name,
+                            typeTree,
+                            null);
+                    getter = genUtils.createPropertyGetterMethod(
+                            make.Modifiers(EnumSet.of(Modifier.PUBLIC), Collections.<AnnotationTree>emptyList()),
+                            name,
+                            typeTree);
+                    setter = genUtils.createPropertySetterMethod(
+                            genUtils.createModifiers(Modifier.PUBLIC),
+                            name,
+                            typeTree);
+                }
+
+                public VariableTree getField() {
+                    return field;
+                }
+
+                public MethodTree getGetter() {
+                    return getter;
+                }
+
+                public MethodTree getSetter() {
+                    return setter;
+                }
+            }
+        }
+
+        private class EntityClassGenerator extends ClassGenerator {
+
+            // the simple name of the entity class
+            private final String entityClassName;
+            // the non-nullable properties (not including the primary key ones)
+            private final List<Property> nonNullableProps = new ArrayList<>();
+            // the names of the primary key columns
+            private final List<String> pkColumnNames = new ArrayList<>();
+            private Property pkProperty;
+            private boolean pkGenerated;
+
+            public EntityClassGenerator(WorkingCopy copy, EntityClass entityClass, boolean jpaSupported, boolean beanValidationSupported) throws IOException {
+                super(copy, entityClass, jpaSupported, beanValidationSupported);
+                entityClassName = getClassName(entityClass);
+                assert typeElement.getSimpleName().contentEquals(entityClassName);
+            }
+
+            @Override
+            protected void initialize() throws IOException {
+                if (generateJPA) {
+                    newClassTree = genUtils.addAnnotation(newClassTree, genUtils.createAnnotation("javax.persistence.Entity")); //NOI18N
+                    if (dbMappings.getTableName() != null && !entityClassName.equalsIgnoreCase(dbMappings.getTableName())) {
+                        List<ExpressionTree> tableAnnArgs = Collections.singletonList(genUtils.createAnnotationArgument("name", dbMappings.getTableName())); //NOI18N
+                        newClassTree = genUtils.addAnnotation(newClassTree, genUtils.createAnnotation("javax.persistence.Table", tableAnnArgs)); //NOI18N
+                    }
+                } else if (dbMappings.getTableName() != null && !entityClassName.equalsIgnoreCase(dbMappings.getTableName())) {
+                    List<ExpressionTree> tableAnnArgs = Collections.singletonList(genUtils.createAnnotationArgument(null, dbMappings.getTableName())); //NOI18N
+                    newClassTree = genUtils.addAnnotation(newClassTree, genUtils.createAnnotation("io.micronaut.data.annotation.MappedEntity", tableAnnArgs)); //NOI18N
+                } else {
+                    newClassTree = genUtils.addAnnotation(newClassTree, genUtils.createAnnotation("io.micronaut.data.annotation.MappedEntity")); //NOI18N
+                }
+            }
+
+            @Override
+            protected void generateMember(EntityMember m) throws IOException {
+                //skip generating already exist members for UPDATE type
+                String memberName = m.getMemberName();
+                boolean isPKMember = m.isPrimaryKey();
+                Property property;
+                if (isPKMember) {
+                    pkProperty = property = createProperty(m);
+                    String pkColumnName = dbMappings.getCMPFieldMapping().get(memberName);
+                    pkColumnNames.add(pkColumnName);
+                    pkGenerated = m.isAutoIncrement();
+                } else {
+                    property = createProperty(m);
+                    if (!m.isNullable()) {
+                        nonNullableProps.add(property);
+                    }
+                }
+                properties.add(property);
+            }
+
+            @Override
+            protected void generateRelationship(RelationshipRole role) throws IOException {
+                String memberName = role.getFieldName();
+                if (memberName.endsWith("Collection")) { // NOI18N
+                    memberName = memberName.substring(0, memberName.length() - 10);
+                    memberName += memberName.endsWith("s") ? "es" : "s"; // NOI18N
+                }
+                String typeName = getRelationshipFieldType(role, entityClass.getPackage());
+                if(replacedTypeNames.containsKey(typeName)) {
+                    typeName = replacedTypeNames.get(typeName);
+                }
+                TypeElement typeEl = moduleElement != null
+                        ? copy.getElements().getTypeElement(moduleElement, typeName)
+                        : copy.getElements().getTypeElement(typeName);
+                assert typeEl != null : "null TypeElement for \"" + typeName + "\"";
+                String collectionType = "java.util.Set"; //NOI18N
+                TypeMirror fieldType = typeEl.asType();
+                if (role.isToMany()) {
+                    TypeElement collectionTypeElem = moduleElement != null
+                            ? copy.getElements().getTypeElement(moduleElement, collectionType)
+                            : copy.getElements().getTypeElement(collectionType);
+                    fieldType = copy.getTypes().getDeclaredType(collectionTypeElem, fieldType);
+                }
+                List<AnnotationTree> annotations = new ArrayList<>();
+                List<ExpressionTree> annArguments = new ArrayList<>();
+                if (role.isCascade()) {
+                    annArguments.add(genUtils.createAnnotationArgument("cascade", generateJPA ? "javax.persistence.CascadeType" : "io.micronaut.data.annotation.Relation.Cascade", "ALL")); // NOI18N
+                }
+                if (role.equals(role.getParent().getRoleB())) { // Role B
+                    String fName = role.getParent().getRoleA().getFieldName();
+                    annArguments.add(genUtils.createAnnotationArgument("mappedBy", fName)); // NOI18N
+                } else if (generateJPA) {  // Role A
+                    if (role.isMany() && role.isToMany()) { // ManyToMany
+                        List<ExpressionTree> joinTableAnnArguments = new ArrayList<>();
+                        String jTN = dbMappings.getJoinTableMapping().get(role.getFieldName());
+                        joinTableAnnArguments.add(genUtils.createAnnotationArgument("name", jTN)); //NOI18N
+                        CMPMappingModel.JoinTableColumnMapping joinColumnMap = dbMappings.getJoinTableColumnMppings().get(role.getFieldName());
+                        CMPMappingModel.ColumnData[] columns = joinColumnMap.getColumns();
+                        CMPMappingModel.ColumnData[] refColumns = joinColumnMap.getReferencedColumns();
+                        joinTableAnnArguments.add(genUtils.createAnnotationArgument("joinColumns", createJoinColumnAnnotations(columns, refColumns, null))); // NOI18N
+                        CMPMappingModel.ColumnData[] invColumns = joinColumnMap.getInverseColumns();
+                        CMPMappingModel.ColumnData[] refInvColumns = joinColumnMap.getReferencedInverseColumns();
+                        joinTableAnnArguments.add(genUtils.createAnnotationArgument("inverseJoinColumns", createJoinColumnAnnotations(invColumns, refInvColumns, null))); // NOI18N
+                        annotations.add(genUtils.createAnnotation("javax.persistence.JoinTable", joinTableAnnArguments)); // NOI18N
+                    } else { // ManyToOne, OneToMany, OneToOne
+                        CMPMappingModel.ColumnData[] columns = dbMappings.getCmrFieldMapping().get(role.getFieldName());
+                        CMPMappingModel relatedMappings = beanMap.get(role.getParent().getRoleB().getEntityName()).getCMPMapping();
+                        CMPMappingModel.ColumnData[] invColumns = relatedMappings.getCmrFieldMapping().get(role.getParent().getRoleB().getFieldName());
+                        if (columns.length == 1) {
+                            List<ExpressionTree> attrs = new ArrayList<>();
+                            attrs.add(genUtils.createAnnotationArgument("name", columns[0].getColumnName())); //NOI18N
+                            attrs.add(genUtils.createAnnotationArgument("referencedColumnName", invColumns[0].getColumnName())); //NOI18N
+                            makeReadOnlyIfNecessary(pkColumnNames, columns[0].getColumnName(), attrs);
+                            annotations.add(genUtils.createAnnotation("javax.persistence.JoinColumn", attrs)); //NOI18N
+                        } else {
+                            ExpressionTree joinColumnsNameAttrValue = genUtils.createAnnotationArgument(null, createJoinColumnAnnotations(columns, invColumns, pkColumnNames));
+                            AnnotationTree joinColumnsAnnotation = genUtils.createAnnotation("javax.persistence.JoinColumns", Collections.singletonList(joinColumnsNameAttrValue)); //NOI18N
+                            annotations.add(joinColumnsAnnotation);
+                        }
+                    }
+                }
+
+                if (generateJPA && !role.isToMany()) { // meaning ManyToOne or OneToOne
+                    // Add optional=false on @ManyToOne or the owning side of @OneToOne
+                    // if the relationship is non-optional (or non-nuallable in other words)
+                    if (!role.isOptional() && (role.isMany() || role.equals(role.getParent().getRoleA()))) {
+                        annArguments.add(genUtils.createAnnotationArgument("optional", false)); // NOI18N
+                    }
+                }
+
+                // Create the relationship annotation
+                if (generateJPA) {
+                    String relationAnn;
+                    if (role.isMany() && role.isToMany()) {
+                        relationAnn = "ManyToMany"; //NOI18N
+                    } else if (role.isMany()) {
+                        relationAnn = "ManyToOne"; //NOI18N
+                    } else if (role.isToMany()) {
+                        relationAnn = "OneToMany"; //NOI18N
+                    } else {
+                        relationAnn = "OneToOne";  //NOI18N
+                    }
+                    annotations.add(genUtils.createAnnotation("javax.persistence." + relationAnn, annArguments)); // NOI18N
+                } else {
+                    String relationAnn;
+                    if (role.isMany() && role.isToMany()) {
+                        relationAnn = "MANY_TO_MANY"; //NOI18N
+                    } else if (role.isMany()) {
+                        relationAnn = "MANY_TO_ONE"; //NOI18N
+                    } else if (role.isToMany()) {
+                        relationAnn = "ONE_TO_MANY"; //NOI18N
+                    } else {
+                        relationAnn = "ONE_TO_ONE";  //NOI18N
+                    }
+                    annArguments.add(0, genUtils.createAnnotationArgument("value", "io.micronaut.data.annotation.Relation.Kind", relationAnn)); // NOI18N
+                    annotations.add(genUtils.createAnnotation("io.micronaut.data.annotation.Relation", annArguments)); // NOI18N
+                }
+
+                properties.add(new Property(Modifier.PRIVATE, annotations, fieldType, memberName));
+            }
+
+            @Override
+            protected void finish() {
+                // create a constructor which takes all non-nullable non-relationship fields as args
+                if (nonNullableProps.size() > 0) {
+                    List<VariableTree> nonNullableParams = new ArrayList<>(nonNullableProps.size() + 1);
+                    if (pkProperty != null && !pkGenerated) {
+                        VariableTree pkFieldParam = genUtils.removeModifiers(pkProperty.getField());
+                        nonNullableParams.add(pkFieldParam);
+                    }
+                    for (Property property : nonNullableProps) {
+                        nonNullableParams.add(genUtils.removeModifiers(property.getField()));
+                    }
+                    constructors.add(genUtils.createAssignmentConstructor(genUtils.createModifiers(Modifier.PUBLIC), entityClassName, nonNullableParams));
+                }
+            }
+
+            private List<AnnotationTree> createJoinColumnAnnotations(CMPMappingModel.ColumnData[] columns, CMPMappingModel.ColumnData[] refColumns, List<String> pkcNames) {
+                List<AnnotationTree> joinCols = new ArrayList<>();
+                for (int colIndex = 0; colIndex < columns.length; colIndex++) {
+                    List<ExpressionTree> attrs = new ArrayList<>();
+                    attrs.add(genUtils.createAnnotationArgument("name", columns[colIndex].getColumnName())); //NOI18N
+                    attrs.add(genUtils.createAnnotationArgument("referencedColumnName", refColumns[colIndex].getColumnName())); //NOI18N
+                    if (pkcNames != null) {
+                        makeReadOnlyIfNecessary(pkcNames, columns[colIndex].getColumnName(), attrs);
+                    }
+                    joinCols.add(genUtils.createAnnotation("javax.persistence.JoinColumn", attrs)); //NOI18N
+                }
+                return joinCols;
+            }
+
+            private String getRelationshipFieldType(RelationshipRole role, String pkg) {
+                RelationshipRole rA = role.getParent().getRoleA();
+                RelationshipRole rB = role.getParent().getRoleB();
+                RelationshipRole otherRole = role.equals(rA) ? rB : rA;
+
+                // First, check if the entity package name is set in the role.
+                // If yes, then that's the package
+                // If no, then default to the passed in pkg
+                if (role.getEntityPkgName() != null) {
+                    return otherRole.getEntityPkgName() + "." + otherRole.getEntityName(); // NOI18N
+                } else {
+                    return pkg.length() == 0 ? otherRole.getEntityName() : pkg + "." + otherRole.getEntityName(); // NOI18N
+                }
+            }
+
+            private void makeReadOnlyIfNecessary(List<String> pkColumnNames, String testColumnName, List<ExpressionTree> attrs) {
+                // if the join column is a pk column, add insertable = false, updatable = false
+                if (pkColumnNames.contains(testColumnName)) {
+                    attrs.add(genUtils.createAnnotationArgument("insertable", false)); //NOI18N
+                    attrs.add(genUtils.createAnnotationArgument("updatable", false)); //NOI18N
+                }
+            }
+        }
+    }
+}
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/db/MicronautRepository.java b/enterprise/micronaut/src/org/netbeans/modules/micronaut/db/MicronautRepository.java
new file mode 100644
index 0000000..3bb9c8c
--- /dev/null
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/db/MicronautRepository.java
@@ -0,0 +1,357 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.micronaut.db;
+
+import com.sun.source.tree.BlockTree;
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.ModifiersTree;
+import com.sun.source.tree.ParameterizedTypeTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.TypeParameterTree;
+import com.sun.source.tree.VariableTree;
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.util.ElementFilter;
+import javax.swing.event.ChangeListener;
+import org.netbeans.api.db.explorer.ConnectionManager;
+import org.netbeans.api.db.explorer.DatabaseConnection;
+import org.netbeans.api.db.explorer.DatabaseException;
+import org.netbeans.api.java.project.JavaProjectConstants;
+import org.netbeans.api.java.source.ClassIndex;
+import org.netbeans.api.java.source.ClasspathInfo;
+import org.netbeans.api.java.source.ElementHandle;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.TreeMaker;
+import org.netbeans.api.java.source.TypeUtilities;
+import org.netbeans.api.project.FileOwnerQuery;
+import org.netbeans.api.project.Project;
+import org.netbeans.api.project.ProjectUtils;
+import org.netbeans.api.project.SourceGroup;
+import org.netbeans.api.templates.CreateDescriptor;
+import org.netbeans.api.templates.CreateFromTemplateHandler;
+import org.netbeans.modules.j2ee.core.api.support.SourceGroups;
+import org.netbeans.modules.j2ee.core.api.support.java.GenerationUtils;
+import org.netbeans.modules.j2ee.core.api.support.wizard.Wizards;
+import org.netbeans.spi.project.ui.templates.support.Templates;
+import org.openide.DialogDescriptor;
+import org.openide.DialogDisplayer;
+import org.openide.NotifyDescriptor;
+import org.openide.WizardDescriptor;
+import org.openide.filesystems.FileObject;
+import org.openide.loaders.DataObject;
+import org.openide.loaders.TemplateWizard;
+import org.openide.util.Exceptions;
+import org.openide.util.NbBundle;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public class MicronautRepository implements TemplateWizard.Iterator {
+
+    public static TemplateWizard.Iterator create() {
+        return new MicronautRepository();
+    }
+
+    @NbBundle.Messages({
+        "MSG_SelectEntities=Select Entity Classes",
+        "MSG_NoEntities=No entity class found in {0}"
+    })
+    public static CreateFromTemplateHandler handler() {
+        return new CreateFromTemplateHandler() {
+            @Override
+            protected boolean accept(CreateDescriptor desc) {
+                return true;
+            }
+
+            @Override
+            protected List<FileObject> createFromTemplate(CreateDescriptor desc) throws IOException {
+                try {
+                    final FileObject folder = desc.getTarget();
+                    final Project project = FileOwnerQuery.getOwner(folder);
+                    if (project == null) {
+                        DialogDisplayer.getDefault().notifyLater(new NotifyDescriptor.Message(Bundle.MSG_NoProject(folder.getPath()), NotifyDescriptor.ERROR_MESSAGE));
+                        return Collections.emptyList();
+                    }
+                    final SourceGroup sourceGroup = SourceGroups.getFolderSourceGroup(ProjectUtils.getSources(project).getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA), folder);
+                    if (sourceGroup == null) {
+                        DialogDisplayer.getDefault().notifyLater(new NotifyDescriptor.Message(Bundle.MSG_NoSourceGroup(folder.getPath()), NotifyDescriptor.ERROR_MESSAGE));
+                        return Collections.emptyList();
+                    }
+                    final boolean jpaSupported = Utils.isJPASupported(sourceGroup);
+                    final Map<String, String> entity2idTypes = getEntityClasses(sourceGroup, jpaSupported);
+                    final List<NotifyDescriptor.QuickPick.Item> entities = new ArrayList<>();
+                    for (String entityFQN : entity2idTypes.keySet()) {
+                        int idx = entityFQN.lastIndexOf('.');
+                        if (idx < 0) {
+                            entities.add(new NotifyDescriptor.QuickPick.Item(entityFQN, null));
+                        } else {
+                            entities.add(new NotifyDescriptor.QuickPick.Item(entityFQN.substring(idx + 1), entityFQN.substring(0, idx)));
+                        }
+                    }
+                    if (entities.isEmpty()) {
+                        DialogDisplayer.getDefault().notifyLater(new NotifyDescriptor.Message(Bundle.MSG_NoEntities(sourceGroup.getRootFolder().getPath()), NotifyDescriptor.ERROR_MESSAGE));
+                        return Collections.emptyList();
+                    }
+                    NotifyDescriptor.QuickPick qp = new NotifyDescriptor.QuickPick(Bundle.MSG_SelectEntities(), Bundle.MSG_SelectEntities(), entities, true);
+                    if (DialogDescriptor.OK_OPTION == DialogDisplayer.getDefault().notify(qp)) {
+                        String dialect = getDialect(jpaSupported);
+                        List<FileObject> generated = new ArrayList<>();
+                        for (NotifyDescriptor.QuickPick.Item item : qp.getItems()) {
+                            if (item.isSelected()) {
+                                String fqn = item.getDescription() != null ? item.getDescription() + '.' + item.getLabel() : item.getLabel();
+                                String entityIdType = entity2idTypes.get(fqn);
+                                FileObject fo = generate(folder, item.getLabel(), fqn, entityIdType, dialect);
+                                if (fo != null) {
+                                    generated.add(fo);
+                                }
+                            }
+                        }
+                        return generated;
+                    }
+                } catch (Exception ex) {
+                    DialogDisplayer.getDefault().notifyLater(new NotifyDescriptor.Message(ex.getMessage(), NotifyDescriptor.ERROR_MESSAGE));
+                }
+                return Collections.emptyList();
+            }
+        };
+    }
+
+    static final String PROP_ENTITIES = "wizard-entities"; //NOI18N
+    static final String PROP_SELECTED_ENTITIES = "wizard-selected-entities"; //NOI18N
+    private WizardDescriptor.Panel panel;
+    private WizardDescriptor wizardDescriptor;
+    private FileObject targetFolder;
+    private boolean jpaSupported;
+
+    @Override
+    public Set<DataObject> instantiate(TemplateWizard wiz) throws IOException {
+        String dialect = getDialect(jpaSupported);
+        Set<DataObject> generated = new HashSet<>();
+        Map<String, String> selectedEntities = (Map<String, String>) wiz.getProperty(PROP_SELECTED_ENTITIES);
+        for (Map.Entry<String, String> entry : selectedEntities.entrySet()) {
+            String fqn = entry.getKey();
+            int idx = fqn.lastIndexOf('.');
+            String label = idx < 0 ? fqn : fqn.substring(idx + 1);
+            FileObject fo = generate(targetFolder, label, fqn, entry.getValue(), dialect);
+            if (fo != null) {
+                generated.add(DataObject.find(fo));
+            }
+        }
+        return generated;
+    }
+
+    @Override
+    public void initialize(TemplateWizard wiz) {
+        wizardDescriptor = wiz;
+
+        panel = new EntityClassesPanel.WizardPanel(NbBundle.getMessage(MicronautRepository.class, "Templates/Micronaut/Repository"));
+        Wizards.mergeSteps(wizardDescriptor, new WizardDescriptor.Panel[] {
+            panel
+        }, new String[] {
+            NbBundle.getMessage(MicronautRepository.class, "LBL_EntityClasses")
+        });
+
+        targetFolder = Templates.getTargetFolder(wizardDescriptor);
+        Project project = Templates.getProject(wiz);
+        SourceGroup sourceGroup = SourceGroups.getFolderSourceGroup(ProjectUtils.getSources(project).getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA), targetFolder);
+        if (sourceGroup != null) {
+            jpaSupported = Utils.isJPASupported(sourceGroup);
+            Map<String, String> entities = MicronautRepository.getEntityClasses(sourceGroup, jpaSupported);
+            wiz.putProperty(PROP_ENTITIES, entities);
+        }
+    }
+
+    @Override
+    public void uninitialize(TemplateWizard wiz) {
+    }
+
+    @Override
+    public WizardDescriptor.Panel<WizardDescriptor> current() {
+        return panel;
+    }
+
+    @Override
+    public String name() {
+        return null;
+    }
+
+    @Override
+    public boolean hasNext() {
+        return false;
+    }
+
+    @Override
+    public boolean hasPrevious() {
+        return false;
+    }
+
+    @Override
+    public void nextPanel() {
+        throw new NoSuchElementException();
+    }
+
+    @Override
+    public void previousPanel() {
+        throw new NoSuchElementException();
+    }
+
+    @Override
+    public void addChangeListener(ChangeListener l) {
+    }
+
+    @Override
+    public void removeChangeListener(ChangeListener l) {
+    }
+
+    static Map<String, String> getEntityClasses(SourceGroup sg, boolean jpaSupported) {
+        final Map<String, String> entities = new HashMap<>();
+        JavaSource js = JavaSource.create(ClasspathInfo.create(sg.getRootFolder()));
+        if (js != null) {
+            try {
+                js.runWhenScanFinished(cc -> {
+                    TypeElement typeElement = cc.getElements().getTypeElement(jpaSupported ? "javax.persistence.Entity" : "io.micronaut.data.annotation.MappedEntity"); //NOI18N
+                    if (typeElement != null) {
+                        TypeElement idTypeElement = cc.getElements().getTypeElement(jpaSupported ? "javax.persistence.Id" : "io.micronaut.data.annotation.Id"); //NOI18N
+                        Set<ElementHandle<TypeElement>> elementHandles = cc.getClasspathInfo().getClassIndex().getElements(ElementHandle.create(typeElement), EnumSet.of(ClassIndex.SearchKind.TYPE_REFERENCES), EnumSet.of(ClassIndex.SearchScope.SOURCE));
+                        for (ElementHandle<TypeElement> elementHandle : elementHandles) {
+                            TypeElement type = elementHandle.resolve(cc);
+                            if (type != null) {
+                                String fqn = null;
+                                String idType = null;
+                                for (AnnotationMirror annotationMirror : type.getAnnotationMirrors()) {
+                                    if (fqn == null && typeElement == annotationMirror.getAnnotationType().asElement()) {
+                                        fqn = type.getQualifiedName().toString();
+                                    }
+                                }
+                                if (fqn != null) {
+                                    if (idTypeElement != null) {
+                                        for (VariableElement field : ElementFilter.fieldsIn(type.getEnclosedElements())) {
+                                            if (idType == null) {
+                                                for (AnnotationMirror annotationMirror : field.getAnnotationMirrors()) {
+                                                    if (idType == null && idTypeElement == annotationMirror.getAnnotationType().asElement()) {
+                                                        idType = cc.getTypeUtilities().getTypeName(field.asType(), TypeUtilities.TypeNameOptions.PRINT_FQN).toString();
+                                                    }
+                                                }
+                                            }
+                                        }
+                                    }
+                                    if (idType == null) {
+                                        idType = "java.lang.Object"; //NOI18N
+                                    }
+                                    entities.put(fqn, idType);
+                                }
+                            }
+                        }
+                    }
+                }, true);
+            } catch (IOException ex) {
+                Exceptions.printStackTrace(ex);
+            }
+        }
+        return entities;
+    }
+
+    private static String getDialect(boolean jpaSupported) {
+        if (!jpaSupported) {
+            DatabaseConnection connection = ConnectionManager.getDefault().getPreferredConnection(true);
+            if (connection != null) {
+                try {
+                    ConnectionManager.getDefault().connect(connection);
+                    Connection conn = connection.getJDBCConnection();
+                    String name = conn.getMetaData().getDatabaseProductName();
+                    if (name.matches("(?i).*h2.*")) { //NOI18N
+                        return "H2"; //NOI18N
+                    } else if (name.matches("(?i).*mysql.*")) { //NOI18N
+                        return "MYSQL"; //NOI18N
+                    } else if (name.matches("(?i).*oracle.*")) { //NOI18N
+                        return "ORACLE"; //NOI18N
+                    } else if (name.matches("(?i).*postgresql.*")) { //NOI18N
+                        return "POSTGRES"; //NOI18N
+                    } else if (name.matches("(?i).*microsoft.*")) { //NOI18N
+                        return "SQL_SERVER"; //NOI18N
+                    }
+                } catch (SQLException | DatabaseException ex) {
+                    Exceptions.printStackTrace(ex);
+                }
+            }
+            return ""; //NOI18N
+        }
+        return null;
+    }
+
+    @NbBundle.Messages({
+        "MSG_Repository_Interface=Repository interface {0}\n"
+    })
+    private static FileObject generate(FileObject folder, String entityName, String entityFQN, String entityIdType, String dialect) {
+        try {
+            String name = entityName + "Repository"; // NOI18N
+            FileObject fo = GenerationUtils.createInterface(folder, name, Bundle.MSG_Repository_Interface(name));
+            if (fo != null) {
+                JavaSource js = JavaSource.forFileObject(fo);
+                if (js != null) {
+                    js.runModificationTask(copy -> {
+                        copy.toPhase(JavaSource.Phase.RESOLVED);
+                        Tree origTree = copy.getCompilationUnit().getTypeDecls().get(0);
+                        if (origTree.getKind() == Tree.Kind.INTERFACE) {
+                            GenerationUtils gu = GenerationUtils.newInstance(copy);
+                            TreeMaker tm = copy.getTreeMaker();
+                            List<ExpressionTree> args = Arrays.asList(tm.QualIdent(entityFQN), tm.QualIdent(entityIdType));
+                            ParameterizedTypeTree type = tm.ParameterizedType(tm.QualIdent("io.micronaut.data.repository.CrudRepository"), args); //NOI18N
+                            ClassTree cls = tm.addClassImplementsClause((ClassTree) origTree, type);
+                            if (dialect == null) {
+                                cls = gu.addAnnotation(cls, gu.createAnnotation("io.micronaut.data.annotation.Repository")); //NOI18N
+                            } else if (dialect.isEmpty()) {
+                                cls = gu.addAnnotation(cls, gu.createAnnotation("io.micronaut.data.jdbc.annotation.JdbcRepository")); //NOI18N
+                            } else {
+                                List<ExpressionTree> annArgs = Collections.singletonList(gu.createAnnotationArgument("dialect", "io.micronaut.data.model.query.builder.sql.Dialect", dialect)); //NOI18N
+                                cls = gu.addAnnotation(cls, gu.createAnnotation("io.micronaut.data.jdbc.annotation.JdbcRepository", annArgs)); //NOI18N
+                            }
+                            ModifiersTree mods = tm.Modifiers(Collections.emptySet(), Arrays.asList(gu.createAnnotation("java.lang.Override"), gu.createAnnotation("io.micronaut.core.annotation.NonNull"))); //NOI18N
+                            ParameterizedTypeTree retType = tm.ParameterizedType(tm.QualIdent("java.util.List"), Collections.singletonList(tm.QualIdent(entityFQN))); //NOI18N
+                            MethodTree findAllMethod = tm.Method(mods, "findAll", retType, Collections.<TypeParameterTree>emptyList(), Collections.<VariableTree>emptyList(), Collections.<ExpressionTree>emptyList(), (BlockTree)null, null); //NOI18N
+                            cls = tm.addClassMember(cls, findAllMethod);
+                            copy.rewrite(origTree, cls);
+                        }
+                    }).commit();
+                }
+            }
+            return fo;
+        } catch (IOException ex) {
+            Exceptions.printStackTrace(ex);
+        }
+        return null;
+    }
+}
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/db/Utils.java b/enterprise/micronaut/src/org/netbeans/modules/micronaut/db/Utils.java
new file mode 100644
index 0000000..4e3e08f
--- /dev/null
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/db/Utils.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.micronaut.db;
+
+import org.netbeans.api.java.classpath.ClassPath;
+import org.netbeans.api.project.SourceGroup;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public class Utils {
+
+    public static boolean isJPASupported(SourceGroup sg) {
+        if (sg == null) {
+            return false;
+        }
+        ClassPath compile = ClassPath.getClassPath(sg.getRootFolder(), ClassPath.COMPILE);
+        if (compile == null) {
+            return false;
+        }
+        final String notNullAnnotation = "io.micronaut.data.jpa.repository.JpaRepository"; //NOI18N
+        return compile.findResource(notNullAnnotation.replace('.', '/') + ".class") != null; //NOI18N
+    }
+}
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/resources/EntityDatabase.html b/enterprise/micronaut/src/org/netbeans/modules/micronaut/resources/EntityDatabase.html
new file mode 100644
index 0000000..58e631a
--- /dev/null
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/resources/EntityDatabase.html
@@ -0,0 +1,29 @@
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+
+<html>
+<head>
+	<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+</head>
+<BODY>
+Creates Micronaut Data entity classes based on an existing relational database.
+This template creates an entity class for each selected table.
+</BODY></HTML>
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/resources/Micronaut.html b/enterprise/micronaut/src/org/netbeans/modules/micronaut/resources/Micronaut.html
new file mode 100644
index 0000000..a9a1093
--- /dev/null
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/resources/Micronaut.html
@@ -0,0 +1,28 @@
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+
+<html>
+<head>
+	<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+</head>
+<BODY>
+Templates for creating various types of files for Micronaut framework.
+</BODY></HTML>
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/resources/Repository.html b/enterprise/micronaut/src/org/netbeans/modules/micronaut/resources/Repository.html
new file mode 100644
index 0000000..a209d08
--- /dev/null
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/resources/Repository.html
@@ -0,0 +1,29 @@
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+
+<html>
+<head>
+	<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+</head>
+<BODY>
+Creates Micronaut Data repository interfaces based on existing entity classes.
+This template creates an repository interface for each selected entity.
+</BODY></HTML>
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/resources/layer.xml b/enterprise/micronaut/src/org/netbeans/modules/micronaut/resources/layer.xml
index 31f5f46..b103120 100644
--- a/enterprise/micronaut/src/org/netbeans/modules/micronaut/resources/layer.xml
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/resources/layer.xml
@@ -43,4 +43,32 @@
             </folder>
         </folder>
     </folder>
+    <folder name="Templates">
+        <folder name="Micronaut">
+            <attr name="SystemFileSystem.localizingBundle" stringvalue="org.netbeans.modules.micronaut.db.Bundle"/>
+            <attr name="templateWizardURL" urlvalue="nbresloc:/org/netbeans/modules/micronaut/resources/Micronaut.html"/>
+            <attr name="position" intvalue="1280"/>
+
+            <file name="Entity">
+                <attr name="position" intvalue="220"/>
+                <attr name="template" boolvalue="true"/>
+                <attr name="instantiatingIterator" methodvalue="org.netbeans.modules.micronaut.db.MicronautEntity.create"/>
+                <attr name="template.createTemplateHandler" methodvalue="org.netbeans.modules.micronaut.db.MicronautEntity.handler"/>
+                <attr name="templateCategory" stringvalue="persistence"/>
+                <attr name="templateWizardURL" urlvalue="nbresloc:/org/netbeans/modules/micronaut/resources/EntityDatabase.html"/>
+                <attr name="SystemFileSystem.localizingBundle" stringvalue="org.netbeans.modules.micronaut.db.Bundle"/>
+                <attr name="SystemFileSystem.icon" urlvalue="nbresloc:/org/netbeans/modules/java/resources/class.png"/>
+            </file>
+            <file name="Repository">
+                <attr name="position" intvalue="320"/>
+                <attr name="template" boolvalue="true"/>
+                <attr name="instantiatingIterator" methodvalue="org.netbeans.modules.micronaut.db.MicronautRepository.create"/>
+                <attr name="template.createTemplateHandler" methodvalue="org.netbeans.modules.micronaut.db.MicronautRepository.handler"/>
+                <attr name="templateCategory" stringvalue="persistence"/>
+                <attr name="templateWizardURL" urlvalue="nbresloc:/org/netbeans/modules/micronaut/resources/Repository.html"/>
+                <attr name="SystemFileSystem.localizingBundle" stringvalue="org.netbeans.modules.micronaut.db.Bundle"/>
+                <attr name="SystemFileSystem.icon" urlvalue="nbresloc:/org/netbeans/modules/java/resources/interface_file.png"/>
+            </file>
+        </folder>
+    </folder>
 </filesystem>
diff --git a/java/j2ee.core.utilities/manifest.mf b/java/j2ee.core.utilities/manifest.mf
index ff7c974..9522765 100644
--- a/java/j2ee.core.utilities/manifest.mf
+++ b/java/j2ee.core.utilities/manifest.mf
@@ -1,4 +1,4 @@
 Manifest-Version: 1.0
 OpenIDE-Module: org.netbeans.modules.j2ee.core.utilities/0
 OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/j2ee/core/utilities/Bundle.properties
-OpenIDE-Module-Specification-Version: 1.49
+OpenIDE-Module-Specification-Version: 1.50
diff --git a/java/j2ee.core.utilities/nbproject/project.xml b/java/j2ee.core.utilities/nbproject/project.xml
index 4c2dafc..8b7f58a 100644
--- a/java/j2ee.core.utilities/nbproject/project.xml
+++ b/java/j2ee.core.utilities/nbproject/project.xml
@@ -257,6 +257,7 @@
                 <friend>org.netbeans.modules.javaee.beanvalidation</friend>
                 <friend>org.netbeans.modules.maven.j2ee</friend>
                 <friend>org.netbeans.modules.maven.jaxws</friend>
+                <friend>org.netbeans.modules.micronaut</friend>
                 <friend>org.netbeans.modules.spring.beans</friend>
                 <friend>org.netbeans.modules.spring.webmvc</friend>
                 <friend>org.netbeans.modules.web.core</friend>
diff --git a/java/j2ee.persistence/nbproject/project.properties b/java/j2ee.persistence/nbproject/project.properties
index ea94769..c58bb6a 100644
--- a/java/j2ee.persistence/nbproject/project.properties
+++ b/java/j2ee.persistence/nbproject/project.properties
@@ -17,7 +17,7 @@
 
 javac.compilerargs=-Xlint -Xlint:-serial
 javac.source=1.8
-spec.version.base=1.70.0
+spec.version.base=1.71.0
 
 test.unit.run.cp.extra=\
     ${j2ee.persistence.dir}/modules/ext/eclipselink/org.eclipse.persistence.core-2.7.9.jar:\
diff --git a/java/j2ee.persistence/nbproject/project.xml b/java/j2ee.persistence/nbproject/project.xml
index f8ecc68..e1bff99 100644
--- a/java/j2ee.persistence/nbproject/project.xml
+++ b/java/j2ee.persistence/nbproject/project.xml
@@ -629,6 +629,7 @@
                 <friend>org.netbeans.modules.javaee.project</friend>
                 <friend>org.netbeans.modules.javaee.specs.support</friend>
                 <friend>org.netbeans.modules.maven.j2ee</friend>
+                <friend>org.netbeans.modules.micronaut</friend>
                 <friend>org.netbeans.modules.profiler.j2ee</friend>
                 <friend>org.netbeans.modules.web.jsf</friend>
                 <friend>org.netbeans.modules.web.project</friend>
diff --git a/java/j2ee.persistence/src/org/netbeans/modules/j2ee/persistence/api/entity/generator/EntitiesFromDBGenerator.java b/java/j2ee.persistence/src/org/netbeans/modules/j2ee/persistence/api/entity/generator/EntitiesFromDBGenerator.java
index cc39f45..e75a4b6 100644
--- a/java/j2ee.persistence/src/org/netbeans/modules/j2ee/persistence/api/entity/generator/EntitiesFromDBGenerator.java
+++ b/java/j2ee.persistence/src/org/netbeans/modules/j2ee/persistence/api/entity/generator/EntitiesFromDBGenerator.java
@@ -41,6 +41,7 @@ import org.netbeans.modules.j2ee.persistence.entitygenerator.EntityRelation.Coll
 import org.netbeans.modules.j2ee.persistence.entitygenerator.EntityRelation.FetchType;
 import org.netbeans.modules.j2ee.persistence.wizard.fromdb.JavaPersistenceGenerator;
 import org.netbeans.modules.j2ee.persistence.wizard.fromdb.DBSchemaTableProvider;
+import org.netbeans.modules.j2ee.persistence.wizard.fromdb.PersistenceGenerator;
 import org.netbeans.modules.j2ee.persistence.wizard.fromdb.RelatedCMPHelper;
 import org.netbeans.modules.j2ee.persistence.wizard.fromdb.SelectedTables;
 import org.netbeans.modules.j2ee.persistence.wizard.fromdb.Table;
@@ -63,7 +64,7 @@ public final class EntitiesFromDBGenerator {
     private final DatabaseConnection connection;
     private final Project project;
     private final PersistenceUnit persistenceUnit;
-    private final JavaPersistenceGenerator generator; 
+    private final PersistenceGenerator generator;
     
     // Global mapping options
     private boolean fullyQualifiedTableNames;
@@ -104,6 +105,41 @@ public final class EntitiesFromDBGenerator {
             FetchType fetchType, CollectionType collectionType,
             String packageName, SourceGroup location, DatabaseConnection connection,
             Project project, PersistenceUnit persistenceUnit) {
+        this(tableNames, generateNamedQueries, fullyQualifiedTableNames, regenTablesAttrs, fetchType, collectionType, packageName, location, connection, project, persistenceUnit, new JavaPersistenceGenerator(persistenceUnit));
+    }
+
+    /**
+     * Creates a new instance of EntitiesFromDBGenerator.
+     *
+     * @param tableNames the names of the tables for which entities are generated. Must not be null.
+     * @param generateNamedQueries specifies whether named queries should be generated.
+     * @param fullyQualifiedTableNames specifies whether fully qualified database table names should be used.
+     *        Attribute catalog and schema are added to the Table annotation if true
+     * @param regenTablesAttrs specified whether attributes used for regenerating tables from entity classes
+     *        should be included. If true, unique containtraints are generated on @Table annotation and attributes
+     *        nullable (if false), length (for String type), precision and scale(for decimal type) are added to
+     *        the Column annotation
+     * @param fetchType specifies the fetch type for the associations. Can be <code>FetchType.DEFAULT</code>,
+     *        <code>FetchType.EAGER</code> or <code>FetchType.LAZY</code>. Default to <code>FetchType.DEFAULT</code>,
+     *        meaning no fetch attribute is added to the relationship annotation
+     * @param collectionType specifies the collection type for the OneToMany and ManyToMany fields.
+     *        Can be <code>CollectionType.COLLECTION</code>, <code>CollectionType.LIST</code> or
+     *        <code>CollectionType.SET</code>. Default to <code>CollectionType.COLLECTION</code>.
+     * @param packageName the name of the package for the generated entities. Must not be null.
+     * @param location the location. Must not be null.
+     * @param connection the database connection for the specified tables. Must not be null.
+     * @param project the project to which entities are generated.
+     * @param persistenceUnit the persistenceUnit to which generated entities should be added
+     * as managed classes. May be null, in which case it is up to the client to add
+     * the generated entities to an appropriate persistence unit (if any).
+     * @param persistenceGenerator persistence generator
+     *
+     */
+    public EntitiesFromDBGenerator(List<String> tableNames, boolean generateNamedQueries,
+            boolean fullyQualifiedTableNames, boolean regenTablesAttrs,
+            FetchType fetchType, CollectionType collectionType,
+            String packageName, SourceGroup location, DatabaseConnection connection,
+            Project project, PersistenceUnit persistenceUnit, PersistenceGenerator persistenceGenerator) {
         Parameters.notNull("project", project); //NOI18N
         Parameters.notNull("tableNames", tableNames); //NOI18N
         Parameters.notNull("packageName", packageName); //NOI18N
@@ -121,7 +157,7 @@ public final class EntitiesFromDBGenerator {
         this.connection = connection;
         this.project = project;
         this.persistenceUnit = persistenceUnit;
-        this.generator = new JavaPersistenceGenerator(persistenceUnit);
+        this.generator = persistenceGenerator;
     }
     
     /**
diff --git a/java/j2ee.persistence/src/org/netbeans/modules/j2ee/persistence/wizard/fromdb/DatabaseTablesPanel.java b/java/j2ee.persistence/src/org/netbeans/modules/j2ee/persistence/wizard/fromdb/DatabaseTablesPanel.java
index a652c31..eda500f 100644
--- a/java/j2ee.persistence/src/org/netbeans/modules/j2ee/persistence/wizard/fromdb/DatabaseTablesPanel.java
+++ b/java/j2ee.persistence/src/org/netbeans/modules/j2ee/persistence/wizard/fromdb/DatabaseTablesPanel.java
@@ -1099,7 +1099,9 @@ public class DatabaseTablesPanel extends javax.swing.JPanel implements AncestorL
         @Override
         public void readSettings(WizardDescriptor settings) {
             wizardDescriptor = settings;
-            wizardDescriptor.putProperty("NewFileWizard_Title", title); // NOI18N
+            if (title != null) {
+                wizardDescriptor.putProperty("NewFileWizard_Title", title); // NOI18N
+            }
             
             if (!componentInitialized) {
                 componentInitialized = true;
diff --git a/java/j2ee.persistence/src/org/netbeans/modules/j2ee/persistence/wizard/fromdb/EntityClassesPanel.java b/java/j2ee.persistence/src/org/netbeans/modules/j2ee/persistence/wizard/fromdb/EntityClassesPanel.java
index 1d5ead8..4338264 100644
--- a/java/j2ee.persistence/src/org/netbeans/modules/j2ee/persistence/wizard/fromdb/EntityClassesPanel.java
+++ b/java/j2ee.persistence/src/org/netbeans/modules/j2ee/persistence/wizard/fromdb/EntityClassesPanel.java
@@ -92,7 +92,7 @@ public class EntityClassesPanel extends javax.swing.JPanel {
     private final JMenuItem allToRecreateItem;
 
 
-    private EntityClassesPanel(boolean puRequired, boolean JAXBRequired) {
+    private EntityClassesPanel(boolean puRequired, boolean JAXBRequired, boolean isJPA) {
         this.puRequired = puRequired;
 
         initComponents();
@@ -138,6 +138,12 @@ public class EntityClassesPanel extends javax.swing.JPanel {
                 packageChanged();
             }
         });
+        if (!isJPA) {
+            createPUCheckbox.setVisible(false);
+            generateFinderMethodsCheckBox.setVisible(false);
+            generateJAXBCheckBox.setVisible(false);
+            mappedSuperclassCheckBox.setVisible(false);
+        }
     }
 
     public void addChangeListener(ChangeListener listener) {
@@ -610,6 +616,7 @@ public class EntityClassesPanel extends javax.swing.JPanel {
         private boolean puRequired;
         private boolean JAXBRequired;
         private boolean isFinishable;
+        private boolean isJPA;
 
         private List<Provider> providers;
 
@@ -618,25 +625,31 @@ public class EntityClassesPanel extends javax.swing.JPanel {
         }
         
         public WizardPanel(boolean persistenceUnitRequired, boolean JAXBRequired,
-                boolean isFinishable )
+                boolean isFinishable ) {
+            this(persistenceUnitRequired, JAXBRequired, isFinishable, true);
+        }
+
+        WizardPanel(boolean persistenceUnitRequired, boolean JAXBRequired,
+                boolean isFinishable, boolean isJPA )
         {
             puRequired = persistenceUnitRequired;
             this.JAXBRequired = JAXBRequired;
             this.isFinishable = isFinishable;
+            this.isJPA = isJPA;
         }
 
         public WizardPanel(boolean persistenceUnitRequired, boolean JAXBRequired){
-            this( persistenceUnitRequired , JAXBRequired , true );
+            this( persistenceUnitRequired , JAXBRequired , true, true );
         }
 
         public WizardPanel(boolean persistenceUnitRequired){
-            this( persistenceUnitRequired , false , true );
+            this( persistenceUnitRequired , false , true, true );
         }
         
         @Override
         public EntityClassesPanel getComponent() {
             if (component == null) {
-                component = new EntityClassesPanel(puRequired, JAXBRequired);
+                component = new EntityClassesPanel(puRequired, JAXBRequired, isJPA);
                 component.addChangeListener(this);
             }
             return component;
diff --git a/java/j2ee.persistence/src/org/netbeans/modules/j2ee/persistence/wizard/fromdb/RelatedCMPWizard.java b/java/j2ee.persistence/src/org/netbeans/modules/j2ee/persistence/wizard/fromdb/RelatedCMPWizard.java
index cca3f49..6c4cfe4 100644
--- a/java/j2ee.persistence/src/org/netbeans/modules/j2ee/persistence/wizard/fromdb/RelatedCMPWizard.java
+++ b/java/j2ee.persistence/src/org/netbeans/modules/j2ee/persistence/wizard/fromdb/RelatedCMPWizard.java
@@ -165,12 +165,16 @@ public class RelatedCMPWizard implements TemplateWizard.Iterator {
 //                        new PersistenceUnitWizardDescriptor(project),
 //                };
 //            } else {
-            return new WizardDescriptor.Panel[]{
-                        new DatabaseTablesPanel.WizardPanel(wizardTitle),
-                        new EntityClassesPanel.WizardPanel(),
-                        new MappingOptionsPanel.WizardPanel(),};
-//            }
+            return TYPE_JPA.equals(type) ? new WizardDescriptor.Panel[] {
+                new DatabaseTablesPanel.WizardPanel(wizardTitle),
+                new EntityClassesPanel.WizardPanel(),
+                new MappingOptionsPanel.WizardPanel()
+            } : new WizardDescriptor.Panel[]{
+                new DatabaseTablesPanel.WizardPanel(null),
+                new EntityClassesPanel.WizardPanel(false, false, true, false)
+            } ;
         }
+//            }
     }
 
     private String[] createSteps() {
@@ -189,10 +193,14 @@ public class RelatedCMPWizard implements TemplateWizard.Iterator {
 //                        NbBundle.getMessage(PersistenceUnitWizardDescriptor.class,"LBL_Step1")
 //                };
 //            } else {
-            return new String[]{
-                        NbBundle.getMessage(RelatedCMPWizard.class, "LBL_DatabaseTables"),
-                        NbBundle.getMessage(RelatedCMPWizard.class, "LBL_EntityClasses"),
-                        NbBundle.getMessage(RelatedCMPWizard.class, "LBL_MappingOptions"),};
+            return TYPE_JPA.equals(type) ? new String[] {
+                NbBundle.getMessage(RelatedCMPWizard.class, "LBL_DatabaseTables"),
+                NbBundle.getMessage(RelatedCMPWizard.class, "LBL_EntityClasses"),
+                NbBundle.getMessage(RelatedCMPWizard.class, "LBL_MappingOptions")
+            } : new String[] {
+                NbBundle.getMessage(RelatedCMPWizard.class, "LBL_DatabaseTables"),
+                NbBundle.getMessage(RelatedCMPWizard.class, "LBL_EntityClasses")
+            };
 //            }
         }
     }
diff --git a/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/layer.xml b/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/layer.xml
index 379e4f8..81f18f3 100644
--- a/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/layer.xml
+++ b/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/layer.xml
@@ -98,5 +98,6 @@
         <folder name="XML">
             <file name="XMLCatalog.xml_hidden"/>
         </folder>
+        <folder name="Persistence_hidden"/>
     </folder>
 </filesystem>
diff --git a/java/java.lsp.server/nbproject/project.xml b/java/java.lsp.server/nbproject/project.xml
index 8318c49..5955f01 100644
--- a/java/java.lsp.server/nbproject/project.xml
+++ b/java/java.lsp.server/nbproject/project.xml
@@ -141,6 +141,14 @@
                     </run-dependency>
                 </dependency>
                 <dependency>
+                    <code-name-base>org.netbeans.api.templates</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>1.24</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
                     <code-name-base>org.netbeans.libs.flexmark</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
@@ -537,7 +545,7 @@
                     <build-prerequisite/>
                     <compile-dependency/>
                     <run-dependency>
-                        <specification-version>7.59</specification-version>
+                        <specification-version>7.60</specification-version>
                     </run-dependency>
                 </dependency>
                 <dependency>
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/LspTemplateUI.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/LspTemplateUI.java
index a2decdc..134b355 100644
--- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/LspTemplateUI.java
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/LspTemplateUI.java
@@ -37,6 +37,7 @@ import java.util.concurrent.CompletionStage;
 import java.util.function.Function;
 import java.util.logging.Level;
 import java.util.logging.Logger;
+import java.util.stream.Collectors;
 import org.eclipse.lsp4j.ExecuteCommandParams;
 import org.eclipse.lsp4j.MessageParams;
 import org.eclipse.lsp4j.MessageType;
@@ -46,6 +47,8 @@ import org.netbeans.api.project.Project;
 import org.netbeans.api.project.ProjectUtils;
 import org.netbeans.api.project.SourceGroup;
 import org.netbeans.api.project.SourceGroupModifier;
+import org.netbeans.api.templates.CreateDescriptor;
+import org.netbeans.api.templates.FileBuilder;
 import org.openide.filesystems.FileObject;
 import org.openide.filesystems.FileUtil;
 import org.openide.filesystems.URLMapper;
@@ -73,57 +76,42 @@ import org.openide.util.Utilities;
     "# {0} - path",
     "ERR_ExistingPath={0} already exists",
 })
-abstract class LspTemplateUI {
+final class LspTemplateUI {
     /**
      * Creation thread. All requests are serialized; make sure that no creation process can block e.g. waiting
      * for the client's response.
      */
     private static final RequestProcessor    CREATION_RP = new RequestProcessor(LspTemplateUI.class);
-    
     private static final Logger LOG = Logger.getLogger(LspTemplateUI.class.getName()); 
     
     private LspTemplateUI() {
     }
 
-    abstract CompletionStage<Pair<DataFolder,String>> findTargetAndName(CompletionStage<DataObject> findTemplate, NbCodeLanguageClient client, ExecuteCommandParams params);
-
     static CompletableFuture<Object> createFromTemplate(String templates, NbCodeLanguageClient client, ExecuteCommandParams params) {
         final FileObject fo = FileUtil.getConfigFile(templates);
         final DataFolder folder = DataFolder.findFolder(fo);
-        LspTemplateUI ui = new LspTemplateUI() {
-            @Override
-            CompletionStage<Pair<DataFolder, String>> findTargetAndName(CompletionStage<DataObject> findTemplate, NbCodeLanguageClient client, ExecuteCommandParams params) {
-                return findTargetAndNameForTemplate(findTemplate, client, params);
-            }
-        };
-        return ui.templateUI(folder, client, params);
+        return new LspTemplateUI().templateUI(folder, client, params);
     }
 
     static CompletableFuture<Object> createProject(String templates, NbCodeLanguageClient client, ExecuteCommandParams params) {
         final FileObject fo = FileUtil.getConfigFile(templates);
         final DataFolder folder = DataFolder.findFolder(fo);
-        LspTemplateUI ui = new LspTemplateUI() {
-            @Override
-            CompletionStage<Pair<DataFolder, String>> findTargetAndName(CompletionStage<DataObject> findTemplate, NbCodeLanguageClient client, ExecuteCommandParams params) {
-                return findTargetAndNameForProject(findTemplate, client, params);
-            }
-        };
-        return ui.projectUI(folder, client, params);
+        return new LspTemplateUI().projectUI(folder, client, params);
     }
 
     private CompletableFuture<Object> templateUI(DataFolder templates, NbCodeLanguageClient client, ExecuteCommandParams params) {
         CompletionStage<DataObject> findTemplate = findTemplate(templates, client);
-        CompletionStage<Pair<DataFolder, String>> findTargetFolderAndName = findTargetAndName(findTemplate, client, params);
-        return findTargetFolderAndName.thenCombineAsync(findTemplate, (targetAndName, source) -> {
-            final String name = targetAndName.second();
-            if (name == null || name.isEmpty()) {
-                throw raise(RuntimeException.class, new UserCancelException());
-            }
+        CompletionStage<DataFolder> findTargetFolder = findTargetForTemplate(findTemplate, client, params);
+        return findTargetFolder.thenCombine(findTemplate, (target, source) -> {
+            final FileObject templateFileObject = source.getPrimaryFile();
+            return new FileBuilder(templateFileObject, target.getPrimaryFile()).name(templateFileObject.getName());
+        }).thenCompose(builder -> configure(builder, client)).thenApplyAsync(builder -> {
             try {
-                DataFolder target = targetAndName.first();
-                Map<String,String> prjParams = new HashMap<>();
-                DataObject newObject = source.createFromTemplate(target, name, prjParams);
-                return (Object) newObject.getPrimaryFile().toURI().toString();
+                if (builder != null) {
+                    List<FileObject> created = builder.build();
+                    return created != null ? (Object) created.stream().map(fo -> fo.toURI().toString()).collect(Collectors.toList()) : null;
+                }
+                return null;
             } catch (IOException ex) {
                 throw raise(RuntimeException.class, ex);
             }
@@ -138,7 +126,7 @@ abstract class LspTemplateUI {
 
     private CompletableFuture<Object> projectUI(DataFolder templates, NbCodeLanguageClient client, ExecuteCommandParams params) {
         CompletionStage<DataObject> findTemplate = findTemplate(templates, client);
-        CompletionStage<Pair<DataFolder, String>> findTargetFolderAndName = findTargetAndName(findTemplate, client, params);
+        CompletionStage<Pair<DataFolder, String>> findTargetFolderAndName = findTargetAndNameForProject(findTemplate, client, params);
         CompletionStage<Pair<DataObject, String>> findTemplateAndPackage = findTemplate.thenCombine(findPackage(findTargetFolderAndName, client), Pair::of);
         return findTargetFolderAndName.thenCombineAsync(findTemplateAndPackage, (targetAndName, templateAndPackage) -> {
             try {
@@ -169,10 +157,10 @@ abstract class LspTemplateUI {
         );
     }
 
-    private static CompletionStage<Pair<DataFolder, String>> findTargetAndNameForTemplate(CompletionStage<DataObject> findTemplate, NbCodeLanguageClient client, ExecuteCommandParams params) {
+    private static CompletionStage<DataFolder> findTargetForTemplate(CompletionStage<DataObject> findTemplate, NbCodeLanguageClient client, ExecuteCommandParams params) {
         final DataObject[] templateObject = new DataObject[1];
-        final CompletionStage<DataFolder> findTarget = findTemplate.thenCompose(any -> { 
-                templateObject[0] = any; 
+        return findTemplate.thenCompose(any -> {
+                templateObject[0] = any;
                 return client.workspaceFolders();
             }).thenCompose(folders -> {
                 boolean[] suggestionIsExact = { true };
@@ -197,15 +185,6 @@ abstract class LspTemplateUI {
                 }
                 return client.showInputBox(new ShowInputBoxParams(Bundle.CTL_TemplateUI_SelectTarget(), suggestion.getPrimaryFile().getPath())).thenCompose(new VerifyPath());
         });
-        CompletionStage<String> findTargetName = findTarget.thenCombine(findTemplate, (target, source) -> source).thenCompose((source) -> {
-            return client.showInputBox(new ShowInputBoxParams(Bundle.CTL_TemplateUI_SelectName(), source.getName()));
-        }).thenCombine(findTemplate, (nameWithExtension, source) -> {
-            String templateExtension = source.getPrimaryFile().getExt();
-            return removeExtensionFromFileName(nameWithExtension, templateExtension);
-        });
-        return findTarget.thenCombine(findTargetName, (t, u) -> {
-            return Pair.of(t, u);
-        });
     }
 
     private static CompletionStage<Pair<DataFolder, String>> findTargetAndNameForProject(CompletionStage<DataObject> findTemplate, NbCodeLanguageClient client, ExecuteCommandParams params) {
@@ -232,6 +211,18 @@ abstract class LspTemplateUI {
         });
     }
 
+    private static CompletionStage<FileBuilder> configure(FileBuilder builder, NbCodeLanguageClient client) {
+        CreateDescriptor desc = builder.createDescriptor(false);
+        FileObject template = desc.getTemplate();
+        Object handler = template.getAttribute(FileBuilder.ATTR_TEMPLATE_HANDLER);
+        if (handler == null) {
+            return client.showInputBox(new ShowInputBoxParams(Bundle.CTL_TemplateUI_SelectName(), desc.getProposedName())).thenApply(name -> {
+                return name != null ? builder.name(name) : null;
+            });
+        }
+        return CompletableFuture.completedFuture(builder);
+    }
+
     private static String suggestWorkspaceRoot(List<WorkspaceFolder> folders) throws IllegalArgumentException {
         String suggestion = System.getProperty("user.dir");
         if (folders != null && !folders.isEmpty()) try {
@@ -401,14 +392,6 @@ abstract class LspTemplateUI {
         return findWorkspaceRoot(folders);
     }
 
-    private static String removeExtensionFromFileName(String nameWithExtension, String templateExtension) {
-        if (nameWithExtension != null && nameWithExtension.endsWith('.' + templateExtension)) {
-            return nameWithExtension.substring(0, nameWithExtension.length() - templateExtension.length() - 1);
-        } else {
-            return nameWithExtension;
-        }
-    }
-
     private static List<QuickPickItem> quickPickTemplates(final DataFolder folder) {
         Node[] arr = folder.getNodeDelegate().getChildren().getNodes(true);
         List<QuickPickItem> categories = new ArrayList<>();
@@ -486,12 +469,4 @@ abstract class LspTemplateUI {
     private static <T extends Exception> T raise(Class<T> clazz, Exception ex) throws T {
         throw (T)ex;
     }
-
-    private static String findGroupId(String pkg, String name) {
-        if (pkg.endsWith("." + name)) {
-            return pkg.substring(0, pkg.length() - 1 - name.length());
-        } else {
-            return pkg;
-        }
-    }
 }
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceUIContext.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceUIContext.java
index 1608657..ce29976 100644
--- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceUIContext.java
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceUIContext.java
@@ -18,6 +18,7 @@
  */
 package org.netbeans.modules.java.lsp.server.protocol;
 
+import java.util.List;
 import java.util.concurrent.CompletableFuture;
 import org.eclipse.lsp4j.MessageActionItem;
 import org.eclipse.lsp4j.MessageParams;
@@ -56,6 +57,10 @@ class WorkspaceUIContext extends UIContext {
         return client.showInputBox(params);
     }
 
+    @Override
+    public CompletableFuture<List<QuickPickItem>> showQuickPick(ShowQuickPickParams params) {
+        return client.showQuickPick(params);
+    }
 
     @Override
     protected void logMessage(MessageParams msg) {
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/ui/NotifyDescriptorAdapter.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/ui/NotifyDescriptorAdapter.java
index fae190c..8407538 100644
--- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/ui/NotifyDescriptorAdapter.java
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/ui/NotifyDescriptorAdapter.java
@@ -42,7 +42,9 @@ import javax.swing.JButton;
 import org.eclipse.lsp4j.MessageActionItem;
 import org.eclipse.lsp4j.MessageType;
 import org.eclipse.lsp4j.ShowMessageRequestParams;
+import org.netbeans.modules.java.lsp.server.protocol.QuickPickItem;
 import org.netbeans.modules.java.lsp.server.protocol.ShowInputBoxParams;
+import org.netbeans.modules.java.lsp.server.protocol.ShowQuickPickParams;
 import org.openide.NotifyDescriptor;
 import org.openide.awt.Actions;
 import org.openide.util.NbBundle;
@@ -289,6 +291,22 @@ class NotifyDescriptorAdapter {
                 inp.setInputText(item);
                 return NotifyDescriptor.OK_OPTION;
             });
+        } else if (descriptor instanceof NotifyDescriptor.QuickPick) {
+            NotifyDescriptor.QuickPick qp = (NotifyDescriptor.QuickPick) descriptor;
+            Map<QuickPickItem, NotifyDescriptor.QuickPick.Item> items = new HashMap<>();
+            for (NotifyDescriptor.QuickPick.Item item : qp.getItems()) {
+                items.put(new QuickPickItem(item.getLabel(), item.getDescription(), null, item.isSelected(), null), item);
+            }
+            ShowQuickPickParams params = new ShowQuickPickParams(qp.getTitle(), qp.isMultipleSelection(), new ArrayList<>(items.keySet()));
+            return client.showQuickPick(params).thenApply(selected -> {
+                if (selected == null) {
+                    return NotifyDescriptor.CLOSED_OPTION;
+                }
+                for (Map.Entry<QuickPickItem, NotifyDescriptor.QuickPick.Item> entry : items.entrySet()) {
+                    entry.getValue().setSelected(selected.contains(entry.getKey()));
+                }
+                return NotifyDescriptor.OK_OPTION;
+            });
         } else {
             ShowMessageRequestParams params = createShowMessageRequest();
             if (params == null) {
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/ui/UIContext.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/ui/UIContext.java
index 8b27bf3..0fa9bd7 100644
--- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/ui/UIContext.java
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/ui/UIContext.java
@@ -20,6 +20,8 @@ package org.netbeans.modules.java.lsp.server.ui;
 
 import java.lang.ref.Reference;
 import java.lang.ref.WeakReference;
+import java.util.Collections;
+import java.util.List;
 import java.util.concurrent.CompletableFuture;
 import org.eclipse.lsp4j.MessageActionItem;
 import org.eclipse.lsp4j.MessageParams;
@@ -27,7 +29,9 @@ import org.eclipse.lsp4j.MessageType;
 import org.eclipse.lsp4j.ShowMessageRequestParams;
 import org.netbeans.api.annotations.common.NonNull;
 import org.netbeans.modules.java.lsp.server.protocol.HtmlPageParams;
+import org.netbeans.modules.java.lsp.server.protocol.QuickPickItem;
 import org.netbeans.modules.java.lsp.server.protocol.ShowInputBoxParams;
+import org.netbeans.modules.java.lsp.server.protocol.ShowQuickPickParams;
 import org.netbeans.modules.java.lsp.server.protocol.ShowStatusMessageParams;
 import org.openide.awt.StatusDisplayer.Message;
 import org.openide.util.Lookup;
@@ -98,6 +102,9 @@ public abstract class UIContext {
         throw new AbstractMethodError();
     }
 
+    protected CompletableFuture<List<QuickPickItem>> showQuickPick(ShowQuickPickParams params) {
+        throw new AbstractMethodError();
+    }
 
     private static final class LogImpl extends UIContext {
         static final LogImpl DEFAULT = new LogImpl();
@@ -145,5 +152,11 @@ public abstract class UIContext {
             CompletableFuture<String> ai = CompletableFuture.completedFuture(null);
             return ai;
         }
+
+        @Override
+        protected CompletableFuture<List<QuickPickItem>> showQuickPick(ShowQuickPickParams params) {
+            System.err.println("quickPick: " + params.getPlaceHolder());
+            return CompletableFuture.completedFuture(Collections.emptyList());
+        }
     }
 }
diff --git a/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/ui/AbstractDialogDisplayerTest.java b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/ui/AbstractDialogDisplayerTest.java
index 017fb80..561978f 100644
--- a/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/ui/AbstractDialogDisplayerTest.java
+++ b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/ui/AbstractDialogDisplayerTest.java
@@ -18,7 +18,9 @@
  */
 package org.netbeans.modules.java.lsp.server.ui;
 
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.CompletableFuture;
 import javax.swing.JPanel;
@@ -29,7 +31,9 @@ import org.eclipse.lsp4j.MessageType;
 import org.eclipse.lsp4j.ShowMessageRequestParams;
 import org.netbeans.junit.NbTestCase;
 import org.netbeans.modules.java.lsp.server.protocol.HtmlPageParams;
+import org.netbeans.modules.java.lsp.server.protocol.QuickPickItem;
 import org.netbeans.modules.java.lsp.server.protocol.ShowInputBoxParams;
+import org.netbeans.modules.java.lsp.server.protocol.ShowQuickPickParams;
 import org.netbeans.modules.java.lsp.server.protocol.ShowStatusMessageParams;
 import org.openide.NotifyDescriptor;
 import org.openide.awt.StatusDisplayer;
@@ -65,6 +69,11 @@ public class AbstractDialogDisplayerTest extends NbTestCase {
         }
 
         @Override
+        protected CompletableFuture<List<QuickPickItem>> showQuickPick(ShowQuickPickParams params) {
+            return CompletableFuture.completedFuture(Collections.emptyList());
+        }
+
+        @Override
         protected void logMessage(MessageParams msg) {
         }
 
diff --git a/java/java.lsp.server/vscode/src/extension.ts b/java/java.lsp.server/vscode/src/extension.ts
index 5b6c468..2ea7e43 100644
--- a/java/java.lsp.server/vscode/src/extension.ts
+++ b/java/java.lsp.server/vscode/src/extension.ts
@@ -341,7 +341,14 @@ export function activate(context: ExtensionContext): VSNetBeansAPI {
 
             if (typeof res === 'string') {
                 let newFile = vscode.Uri.parse(res as string);
-                await vscode.window.showTextDocument(newFile);
+                await vscode.window.showTextDocument(newFile, { preview: false });
+            } else if (Array.isArray(res)) {
+                for(let r of res) {
+                    if (typeof r === 'string') {
+                        let newFile = vscode.Uri.parse(r as string);
+                        await vscode.window.showTextDocument(newFile, { preview: false });
+                    }
+                }
             }
         } else {
             throw `Client ${c} doesn't support new from template`;
diff --git a/platform/openide.dialogs/apichanges.xml b/platform/openide.dialogs/apichanges.xml
index 1c272f2..9b417be 100644
--- a/platform/openide.dialogs/apichanges.xml
+++ b/platform/openide.dialogs/apichanges.xml
@@ -28,6 +28,20 @@
 <changes>
     <change>
          <api name="dialogs"/>
+         <summary>NotifyDescriptor.QuickPick added</summary>
+         <version major="7" minor="60"/>
+         <date day="15" month="2" year="2022"/>
+         <author login="dbalek"/>
+         <compatibility addition="yes" binary="compatible" source="compatible" semantic="compatible" deprecation="no" deletion="no" modification="no"/>
+         <description>
+             <p>
+                 Added <code>QuickPick</code> providing a selection list allowing multiple selections.
+             </p>
+         </description>
+         <class package="org.openide" name="NotifyDescriptor"/>
+    </change>
+    <change>
+         <api name="dialogs"/>
          <summary>Allow vetoable Next/Finish validation in wizard</summary>
          <version major="7" minor="59"/>
          <date day="31" month="1" year="2022"/>
diff --git a/platform/openide.dialogs/manifest.mf b/platform/openide.dialogs/manifest.mf
index d44e5ed..e2e94c5 100644
--- a/platform/openide.dialogs/manifest.mf
+++ b/platform/openide.dialogs/manifest.mf
@@ -1,6 +1,6 @@
 Manifest-Version: 1.0
 OpenIDE-Module: org.openide.dialogs
-OpenIDE-Module-Specification-Version: 7.59
+OpenIDE-Module-Specification-Version: 7.60
 OpenIDE-Module-Localizing-Bundle: org/openide/Bundle.properties
 AutoUpdate-Essential-Module: true
 
diff --git a/platform/openide.dialogs/src/org/openide/NotifyDescriptor.java b/platform/openide.dialogs/src/org/openide/NotifyDescriptor.java
index 9320b5a..d4cedc3 100644
--- a/platform/openide.dialogs/src/org/openide/NotifyDescriptor.java
+++ b/platform/openide.dialogs/src/org/openide/NotifyDescriptor.java
@@ -22,19 +22,29 @@ package org.openide;
 import java.awt.Component;
 import java.awt.Dimension;
 import java.awt.Window;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
 import java.beans.PropertyChangeSupport;
 import java.lang.reflect.InvocationTargetException;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
 import javax.swing.BorderFactory;
+import javax.swing.ButtonGroup;
 import javax.swing.GroupLayout;
 import javax.swing.GroupLayout.Alignment;
+import javax.swing.JCheckBox;
 import javax.swing.JLabel;
 import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 import javax.swing.JPasswordField;
+import javax.swing.JRadioButton;
 import javax.swing.JTextArea;
 import javax.swing.JTextField;
+import javax.swing.JToggleButton;
 import javax.swing.LayoutStyle;
 import javax.swing.UIManager;
 import org.openide.awt.Mnemonics;
@@ -1153,4 +1163,167 @@ public class NotifyDescriptor extends Object {
         }
     }
      // end of InputLine
+    
+    /** Notification providing a selection list allowing multiple selections.
+    * @since 7.60
+    */
+    public static final class QuickPick extends NotifyDescriptor {
+
+        private final String text;
+        private final List<Item> items;
+        private final boolean multipleSelection;
+
+        /**
+         * Construct dialog with the specified title and label text.
+         * @param text label text
+         * @param title title of the dialog
+         * @param items a list of items
+         * @param multipleSelection true if multiple selection allowed
+         * @since 7.60
+         */
+        public QuickPick(final String text, final String title, final List<Item> items, final boolean multipleSelection) {
+            super(null, title, OK_CANCEL_OPTION, PLAIN_MESSAGE, null, null);
+            this.text = text;
+            this.items = items;
+            this.multipleSelection = multipleSelection;
+        }
+
+        @Override
+        public Object getMessage() {
+            Object msg = super.getMessage();
+            if (msg != null) {
+                return msg;
+            }
+            JPanel panel = new JPanel();
+            panel.setOpaque (false);
+
+            javax.swing.GroupLayout layout = new javax.swing.GroupLayout(panel);
+            panel.setLayout(layout);
+
+            JLabel label = new JLabel();
+            Mnemonics.setLocalizedText(label, text);
+
+            GroupLayout.ParallelGroup hGroup = layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                    .addComponent(label);
+            GroupLayout.SequentialGroup vGroup = layout.createSequentialGroup()
+                    .addContainerGap()
+                    .addComponent(label);
+
+            final Map<JToggleButton, Item> btn2items = new LinkedHashMap<JToggleButton, Item>();
+            ItemListener listener = new ItemListener() {
+                @Override
+                public void itemStateChanged(ItemEvent e) {
+                    JToggleButton btn = (JToggleButton) e.getItemSelectable();
+                    Item item = btn2items.get(btn);
+                    if (item != null) {
+                        item.setSelected(btn.isSelected());
+                    }
+                }
+            };
+
+            ButtonGroup buttonGroup = this.multipleSelection ? null : new ButtonGroup();
+            for (Item item : items) {
+                JToggleButton btn;
+                if (buttonGroup != null) {
+                    btn = new JRadioButton();
+                    buttonGroup.add(btn);
+                } else {
+                    btn = new JCheckBox();
+                }
+                btn.setText(item.getLabel());
+                btn.setToolTipText(item.getDescription());
+                btn.setSelected(item.isSelected());
+                hGroup.addComponent(btn);
+                vGroup.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                        .addComponent(btn);
+                btn.addItemListener(listener);
+                btn2items.put(btn, item);
+            }
+
+            layout.setHorizontalGroup(layout.createParallelGroup(Alignment.LEADING)
+                .addGroup(layout.createSequentialGroup()
+                    .addContainerGap()
+                    .addGroup(hGroup)
+                    .addContainerGap())
+            );
+            layout.setVerticalGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                .addGroup(vGroup.addContainerGap())
+            );
+
+            this.setMessage(panel);
+            return panel;
+        }
+
+        /**
+         * Get the list of selection items.
+         * @return unmodifiable list of items
+         * @since 7.60
+         */
+        public List<Item> getItems() {
+            return Collections.unmodifiableList(items);
+        }
+
+        /**
+         * Check if the picker accepts multiple selections.
+         * @return true if multiple selection allowed
+         * @since 7.60
+         */
+        public boolean isMultipleSelection() {
+            return multipleSelection;
+        }
+
+        /**
+         * Item that can be selected from a list of items.
+         * @since 7.60
+         */
+        public static final class Item {
+
+            private final String label;
+            private final String description;
+            private boolean selected;
+
+            /**
+             * Creates item that can be selected from a list of items.
+             * @param label item's label
+             * @param description item's description
+             * @since 7.60
+             */
+            public Item(String label, String description) {
+                this.label = label;
+                this.description = description;
+            }
+
+            /**
+             * Item's label.
+             * @since 7.60
+             */
+            public String getLabel() {
+                return label;
+            }
+
+            /**
+             * Item's description.
+             * @since 7.60
+             */
+            public String getDescription() {
+                return description;
+            }
+
+            /**
+             * Flag indicating if this item is selected.
+             * @since 7.60
+             */
+            public boolean isSelected() {
+                return selected;
+            }
+
+            /**
+             * Marks item as selected.
+             * @since 7.60
+             */
+            public void setSelected(boolean selected) {
+                this.selected = selected;
+            }
+        }
+    }
 }

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists