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

sergeykamov pushed a commit to branch NLPCRAFT-398
in repository https://gitbox.apache.org/repos/asf/incubator-nlpcraft.git

commit 3aa83dc01a8f7699bffe0ed0bb14d646f47c9001
Author: Sergey Kamov <[email protected]>
AuthorDate: Mon Aug 9 15:57:07 2021 +0300

    WIP.
---
 .../apache/nlpcraft/model/NCModelAddClasses.java   |  30 ++++
 .../apache/nlpcraft/model/NCModelAddPackage.java   |  30 ++++
 .../probe/mgrs/deploy/NCDeployManager.scala        | 199 ++++++++++++++-------
 .../apache/nlpcraft/model/NCIntentSampleSpec.scala |   2 +-
 .../probe/mgrs/deploy/NCDeployManagerSpec.scala    |  81 +++++++++
 .../mgrs/deploy/NCModelPackagesWrapperMix.scala    |  19 ++
 .../mgrs/deploy/jv/NCModelClassesWrapper.java      |  28 +++
 .../mgrs/deploy/jv/NCModelDeploySpecAdapter.java   |  20 +++
 .../mgrs/deploy/jv/NCModelPackagesWrapper.java     |  25 +++
 .../probe/mgrs/deploy/jv/NCNestedClass.java        |  28 +++
 .../probe/mgrs/deploy/jv/NCNestedStatic.java       |  28 +++
 .../mgrs/deploy/sc/NCModelClassesWrapper.scala     |   9 +
 .../mgrs/deploy/sc/NCModelDeploySpecAdapter.scala  |  11 ++
 .../mgrs/deploy/sc/NCModelPackagesWrapper.scala    |   6 +
 .../probe/mgrs/deploy/sc/NCNestedClass.scala       |   9 +
 .../probe/mgrs/deploy/sc/NCNestedStatic.scala      |   8 +
 .../model/NCEnricherNestedModelSpec5.scala         |   1 -
 17 files changed, 472 insertions(+), 62 deletions(-)

diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCModelAddClasses.java 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCModelAddClasses.java
new file mode 100644
index 0000000..7f21e7b
--- /dev/null
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCModelAddClasses.java
@@ -0,0 +1,30 @@
+/*
+ * 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.apache.nlpcraft.model;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Retention(value=RUNTIME)
+@Target(value=TYPE)
+public @interface NCModelAddClasses {
+    String[] value();
+}
diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCModelAddPackage.java 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCModelAddPackage.java
new file mode 100644
index 0000000..0717965
--- /dev/null
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCModelAddPackage.java
@@ -0,0 +1,30 @@
+/*
+ * 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.apache.nlpcraft.model;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Retention(value=RUNTIME)
+@Target(value=TYPE)
+public @interface NCModelAddPackage {
+    String[] value();
+}
diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/deploy/NCDeployManager.scala
 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/deploy/NCDeployManager.scala
index f6ee6eb..e2a762d 100644
--- 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/deploy/NCDeployManager.scala
+++ 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/deploy/NCDeployManager.scala
@@ -17,6 +17,8 @@
 
 package org.apache.nlpcraft.probe.mgrs.deploy
 
+import com.google.common.reflect.ClassPath
+
 import java.io._
 import java.lang.reflect.{InvocationTargetException, Method, Modifier, 
ParameterizedType, Type, WildcardType}
 import java.util
@@ -39,6 +41,7 @@ import org.apache.nlpcraft.model.intent._
 import org.apache.nlpcraft.probe.mgrs.NCProbeSynonymChunkKind.{IDL, REGEX, 
TEXT}
 import org.apache.nlpcraft.probe.mgrs.{NCProbeModel, NCProbeModelCallback, 
NCProbeSynonym, NCProbeSynonymChunk, NCProbeSynonymsWrapper}
 
+import java.lang.annotation.Annotation
 import scala.util.Using
 import scala.compat.java8.OptionConverters._
 import scala.collection.mutable
@@ -58,6 +61,8 @@ object NCDeployManager extends NCService {
     private final val CLS_SLV_CTX = classOf[NCIntentMatch]
     private final val CLS_SAMPLE = classOf[NCIntentSample]
     private final val CLS_SAMPLE_REF = classOf[NCIntentSampleRef]
+    private final val CLS_MDL_CLASSES_REF = classOf[NCModelAddClasses]
+    private final val CLS_MDL_PACKAGES_REF = classOf[NCModelAddPackage]
 
     // Java and scala lists.
     private final val CLS_SCALA_SEQ = classOf[Seq[_]]
@@ -104,6 +109,24 @@ object NCDeployManager extends NCService {
       */
     case class SynonymHolder(elmId: String, syn: NCProbeSynonym)
 
+    case class MethodHolder(objClass: Class[_], obj: Any, method: Method) {
+        require(objClass != null ^ obj != null)
+
+        private var o: Any = obj
+
+        def getMethodObject: Any = {
+            if (o == null)
+                try
+                    o = objClass.getDeclaredConstructor().newInstance()
+                catch {
+                    // TODO:
+                    case e: Throwable => throw new NCE(s"Error initializing 
object of type: $objClass", e)
+                }
+
+            o
+        }
+    }
+
     /**
       * Gives a list of JAR files at given path.
       *
@@ -668,7 +691,7 @@ object NCDeployManager extends NCService {
                 val mf = makeModelFactory(mft)
 
                 
mf.initialize(Config.modelFactoryProps.getOrElse(Map.empty[String, 
String]).asJava)
-                
+
                 mf
 
             case None => new NCBasicModelFactory
@@ -987,7 +1010,7 @@ object NCDeployManager extends NCService {
     @throws[NCE]
     private def mkChunk(mdl: NCModel, chunk: String): NCProbeSynonymChunk = {
         def stripSuffix(fix: String, s: String): String = s.slice(fix.length, 
s.length - fix.length)
-        
+
         val mdlId = mdl.getId
 
         // Regex synonym.
@@ -1087,14 +1110,14 @@ object NCDeployManager extends NCService {
         s"#${argIdx + (if (cxtFirstParam) 1 else 0)} of ${method2Str(mtd)}"
 
     /**
-      *
-      * @param mtd
+      * @param mh
       * @param mdl
       * @param intent
       */
     @throws[NCE]
-    private def prepareCallback(mtd: Method, mdl: NCModel, intent: 
NCIdlIntent): Callback = {
+    private def prepareCallback(mh: MethodHolder, mdl: NCModel, intent: 
NCIdlIntent): Callback = {
         val mdlId = mdl.getId
+        val mtd = mh.method
 
         // Checks method result type.
         if (mtd.getReturnType != CLS_QRY_RES)
@@ -1200,8 +1223,8 @@ object NCDeployManager extends NCService {
             mtd.getName,
             (ctx: NCIntentMatch) => {
                 invoke(
-                    mtd,
-                    mdl,
+                    mdl.getId,
+                    mh,
                     (
                         (if (ctxFirstParam) Seq(ctx)
                         else Seq.empty) ++
@@ -1213,29 +1236,26 @@ object NCDeployManager extends NCService {
     }
 
     /**
-      *
-      * @param mtd
-      * @param mdl
+      * @param mdlId
+      * @param mh
       * @param args
       */
     @throws[NCE]
-    private def invoke(mtd: Method, mdl: NCModel, args: Array[AnyRef]): 
NCResult = {
-        val mdlId = mdl.getId
-
-        val obj = if (Modifier.isStatic(mtd.getModifiers)) null else mdl
+    private def invoke(mdlId: String, mh: MethodHolder, args: Array[AnyRef]): 
NCResult = {
+        val obj = if (Modifier.isStatic(mh.method.getModifiers)) null else 
mh.getMethodObject
 
-        var flag = mtd.canAccess(obj)
+        var flag = mh.method.canAccess(obj)
 
         try {
             if (!flag) {
-                mtd.setAccessible(true)
+                mh.method.setAccessible(true)
 
                 flag = true
             }
             else
                 flag = false
 
-            mtd.invoke(obj, args: _*).asInstanceOf[NCResult]
+            mh.method.invoke(obj, args: _*).asInstanceOf[NCResult]
         }
         catch {
             case e: InvocationTargetException => e.getTargetException match {
@@ -1245,25 +1265,25 @@ object NCDeployManager extends NCService {
                 case e: Throwable =>
                     throw new NCE(s"Intent callback invocation error [" +
                         s"mdlId=$mdlId, " +
-                        s"callback=${method2Str(mtd)}" +
+                        s"callback=${method2Str(mh.method)}" +
                     s"]", e)
             }
 
             case e: Throwable =>
                 throw new NCE(s"Unexpected intent callback invocation error [" 
+
                     s"mdlId=$mdlId, " +
-                    s"callback=${method2Str(mtd)}" +
+                    s"callback=${method2Str(mh.method)}" +
                 s"]", e)
         }
         finally
             if (flag)
                 try
-                    mtd.setAccessible(false)
+                    mh.method.setAccessible(false)
                 catch {
                     case e: SecurityException =>
                         throw new NCE(s"Access or security error in intent 
callback [" +
                             s"mdlId=$mdlId, " +
-                            s"callback=${method2Str(mtd)}" +
+                            s"callback=${method2Str(mh.method)}" +
                         s"]", e)
                 }
     }
@@ -1498,18 +1518,24 @@ object NCDeployManager extends NCService {
       * @param o Object.
       * @return Methods.
       */
-    private def getAllMethods(o: AnyRef): Set[Method] = {
-        val claxx = o.getClass
+    private def getAllMethods(o: AnyRef): Set[Method] = 
getAllMethods(o.getClass)
+
+    /**
+      * Gets its own methods including private and accessible from parents.
+      *
+      * @param claxx Class.
+      * @return Methods.
+      */
+    private def getAllMethods(claxx: Class[_]): Set[Method] = 
(claxx.getDeclaredMethods ++ claxx.getMethods).toSet
 
-        (claxx.getDeclaredMethods ++ claxx.getMethods).toSet
-    }
-    
     /**
       *
       * @param mdl
       */
     @throws[NCE]
     private def scanIntents(mdl: NCModel): Set[Intent] = {
+        val cl = Thread.currentThread().getContextClassLoader
+
         val mdlId = mdl.getId
         val intentDecls = mutable.Buffer.empty[NCIdlIntent]
         val intents = mutable.Buffer.empty[Intent]
@@ -1517,39 +1543,36 @@ object NCDeployManager extends NCService {
         // First, get intent declarations from the JSON/YAML file, if any.
         mdl match {
             case adapter: NCModelFileAdapter =>
-                intentDecls ++= adapter
-                    .getIntents
-                    .asScala
-                    .flatMap(NCIdlCompiler.compileIntents(_, mdl, 
mdl.getOrigin))
-
+                intentDecls ++= 
adapter.getIntents.asScala.flatMap(NCIdlCompiler.compileIntents(_, mdl, 
mdl.getOrigin))
             case _ => ()
         }
 
+        def processClass(cls: Class[_]): Unit =
+            if (cls != null)
+                try
+                    for (
+                        ann <- cls.getAnnotationsByType(CLS_INTENT);
+                        intent <- NCIdlCompiler.compileIntents(ann.value(), 
mdl, cls.getName)
+                    )
+                        if (intentDecls.exists(_.id == intent.id))
+                            throw new NCE(s"Duplicate intent ID [" +
+                                s"mdlId=$mdlId, " +
+                                s"origin=${mdl.getOrigin}, " +
+                                s"class=$cls, " +
+                                s"id=${intent.id}" +
+                                s"]")
+                        else
+                            intentDecls += intent
+                catch {
+                    case _: ClassNotFoundException => throw new NCE(s"Failed 
to scan class for @NCIntent annotation: $cls")
+                }
+
         // Second, scan class for class-level @NCIntent annotations (intent 
declarations).
-        val mdlCls = mdl.meta[String](MDL_META_MODEL_CLASS_KEY)
+        processClass(Class.forName(mdl.meta[String](MDL_META_MODEL_CLASS_KEY)))
 
-        if (mdlCls != null) {
-            try {
-                val cls = Class.forName(mdlCls)
+        def processMethod(mh: MethodHolder): Unit = {
+            val m = mh.method
 
-                for (ann <- cls.getAnnotationsByType(CLS_INTENT); intent <- 
NCIdlCompiler.compileIntents(ann.value(), mdl, mdlCls))
-                    if (intentDecls.exists(_.id == intent.id))
-                        throw new NCE(s"Duplicate intent ID [" +
-                            s"mdlId=$mdlId, " +
-                            s"origin=${mdl.getOrigin}, " +
-                            s"class=$mdlCls, " +
-                            s"id=${intent.id}" +
-                        s"]")
-                    else
-                        intentDecls += intent
-            }
-            catch {
-                case _: ClassNotFoundException => throw new NCE(s"Failed to 
scan class for @NCIntent annotation: $mdlCls")
-            }
-        }
-
-        // Third, scan all methods for intent-callback bindings.
-        for (m <- getAllMethods(mdl)) {
             val mtdStr = method2Str(m)
 
             def bindIntent(intent: NCIdlIntent, cb: Callback): Unit = {
@@ -1557,12 +1580,12 @@ object NCDeployManager extends NCService {
                     throw new NCE(s"The intent cannot be bound to more than 
one callback [" +
                         s"mdlId=$mdlId, " +
                         s"origin=${mdl.getOrigin}, " +
-                        s"class=$mdlCls, " +
+                        s"class=${mh.objClass}, " +
                         s"intentId=${intent.id}" +
-                    s"]")
+                        s"]")
                 else {
                     intentDecls += intent
-                    intents += (intent -> prepareCallback(m, mdl, intent))
+                    intents += (intent -> prepareCallback(mh, mdl, intent))
                 }
             }
 
@@ -1574,27 +1597,83 @@ object NCDeployManager extends NCService {
                         s"origin=${mdl.getOrigin}, " +
                         s"callback=$mtdStr, " +
                         s"id=${intent.id}" +
-                    s"]")
+                        s"]")
                 else
-                    bindIntent(intent, prepareCallback(m, mdl, intent))
+                    bindIntent(intent, prepareCallback(mh, mdl, intent))
 
             // Process intent references from @NCIntentRef annotation.
             for (ann <- m.getAnnotationsByType(CLS_INTENT_REF)) {
                 val refId = ann.value().trim
 
                 intentDecls.find(_.id == refId) match {
-                    case Some(intent) => bindIntent(intent, prepareCallback(m, 
mdl, intent))
+                    case Some(intent) => bindIntent(intent, 
prepareCallback(mh, mdl, intent))
                     case None => throw new NCE(
                         s"""@NCIntentRef("$refId") references unknown intent 
ID [""" +
                             s"mdlId=$mdlId, " +
                             s"origin=${mdl.getOrigin}, " +
                             s"refId=$refId, " +
                             s"callback=$mtdStr" +
-                        s"]")
+                            s"]")
                 }
             }
         }
 
+        // Third, scan all methods for intent-callback bindings.
+        for (m <- getAllMethods(mdl))
+            processMethod(MethodHolder(objClass = null, obj = mdl, method = m))
+
+        // External references.
+        def getReferences[T <: Annotation](clazz: Class[T], getValues: T => 
Seq[String]): Option[Seq[String]] = {
+            val anns = mdl.getClass.getAnnotationsByType(clazz)
+
+            if (anns != null && anns.nonEmpty) {
+                val refs = getValues(anns.head)
+
+                if (refs == null || refs.isEmpty)
+                     // TODO:
+                    throw new NCE(
+                        s"Invalid empty reference [" +
+                        s"mdlId=$mdlId, " +
+                        s"origin=${mdl.getOrigin}, " +
+                        s"reference=${clazz.getName}" +
+                        s"]"
+                    )
+                Some(refs)
+            }
+            else
+                None
+        }
+
+        def processClassAndMethods(cls :Class[_]): Unit = {
+            processClass(cls)
+            getAllMethods(cls).foreach(m => 
processMethod(MethodHolder(objClass = cls, obj = null, method = m)))
+        }
+
+        getReferences(CLS_MDL_CLASSES_REF, (a: NCModelAddClasses) => 
a.value()) match {
+            case Some(refs) => refs.foreach(ref => 
processClassAndMethods(cl.loadClass(ref)))
+            case None => // No-op.
+        }
+
+        getReferences(CLS_MDL_PACKAGES_REF, (a: NCModelAddPackage) => 
a.value()) match {
+            case Some(refs) =>
+                refs.foreach(ref => {
+                    if (cl.getDefinedPackage(ref) == null)
+                        // TODO:
+                        throw new NCE(
+                            s"Invalid package reference [" +
+                            s"mdlId=$mdlId, " +
+                            s"origin=${mdl.getOrigin}, " +
+                            s"reference=$ref" +
+                            s"]"
+                        )
+
+                    for (info <- 
ClassPath.from(cl).getTopLevelClassesRecursive(ref).asScala)
+                        processClassAndMethods(info.load())
+                }
+            )
+            case None => // No-op.
+        }
+
         val unusedIntents = intentDecls.filter(i => !intents.exists(_._1.id == 
i.id))
 
         if (unusedIntents.nonEmpty)
diff --git 
a/nlpcraft/src/test/scala/org/apache/nlpcraft/model/NCIntentSampleSpec.scala 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/model/NCIntentSampleSpec.scala
index 5e14b07..2efbbd2 100644
--- a/nlpcraft/src/test/scala/org/apache/nlpcraft/model/NCIntentSampleSpec.scala
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/model/NCIntentSampleSpec.scala
@@ -19,7 +19,7 @@ package org.apache.nlpcraft.model
 
 import org.apache.nlpcraft.NCTestElement
 import org.apache.nlpcraft.model.tools.test.NCTestAutoModelValidator
-import org.junit.jupiter.api.{Assertions, Test}
+import org.junit.jupiter.api.Test
 
 import java.util
 import scala.language.implicitConversions
diff --git 
a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/NCDeployManagerSpec.scala
 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/NCDeployManagerSpec.scala
new file mode 100644
index 0000000..2eb6f0c
--- /dev/null
+++ 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/NCDeployManagerSpec.scala
@@ -0,0 +1,81 @@
+/*
+ * 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
+ *
+ *      https://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.apache.nlpcraft.probe.mgrs.deploy
+
+import org.apache.nlpcraft.model.{NCElement, NCModelAdapter}
+import org.apache.nlpcraft.{NCTestContext, NCTestEnvironment}
+import org.junit.jupiter.api.Test
+
+@NCTestEnvironment(model = 
classOf[org.apache.nlpcraft.probe.mgrs.deploy.sc.NCModelClassesWrapper], 
startClient = true)
+class NCModelClassesWrapperScalaSpec extends NCTestContext {
+    @Test
+    def test(): Unit = {
+        checkIntent("scalaClass", "scalaClass")
+        checkIntent("scalaStatic", "scalaStatic")
+        checkFail("javaClass")
+        checkFail("javaStatic")
+    }
+}
+
+@NCTestEnvironment(model = 
classOf[org.apache.nlpcraft.probe.mgrs.deploy.sc.NCModelPackagesWrapper], 
startClient = true)
+class NCModelPackagesWrapperScalaSpec extends NCTestContext {
+    @Test
+    def test(): Unit = {
+        checkIntent("scalaClass", "scalaClass")
+        checkIntent("scalaStatic", "scalaStatic")
+        checkFail("javaClass")
+        checkFail("javaStatic")
+    }
+}
+
+@NCTestEnvironment(model = 
classOf[org.apache.nlpcraft.probe.mgrs.deploy.jv.NCModelClassesWrapper], 
startClient = true)
+class NCModelClassesWrapperJavaSpec extends NCTestContext {
+    @Test
+    def test(): Unit = {
+        checkIntent("javaClass", "javaClass")
+        checkIntent("javaStatic", "javaStatic")
+        checkFail("scalaClass")
+        checkFail("scalaStatic")
+    }
+}
+
+@NCTestEnvironment(model = 
classOf[org.apache.nlpcraft.probe.mgrs.deploy.jv.NCModelPackagesWrapper], 
startClient = true)
+class NCModelPackagesWrapperJavaSpec extends NCTestContext {
+    @Test
+    def test(): Unit = {
+        checkIntent("javaClass", "javaClass")
+        checkIntent("javaStatic", "javaStatic")
+        checkFail("scalaClass")
+        checkFail("scalaStatic")
+    }
+}
+
+class NCModelMix extends NCModelAdapter("nlpcraft.deploy.test.mdl", "Test 
Model", "1.0") {
+    override def getElements: java.util.Set[NCElement] = super.getElements
+}
+
+@NCTestEnvironment(model = 
classOf[org.apache.nlpcraft.probe.mgrs.deploy.NCModelPackagesWrapperMix], 
startClient = true)
+class NCModelPackagesWrapperMixSpec extends NCTestContext {
+    @Test
+    def test(): Unit = {
+        checkIntent("scalaClass", "scalaClass")
+        checkIntent("scalaStatic", "scalaStatic")
+        checkIntent("javaClass", "javaClass")
+        checkIntent("javaStatic", "javaStatic")
+    }
+}
diff --git 
a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/NCModelPackagesWrapperMix.scala
 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/NCModelPackagesWrapperMix.scala
new file mode 100644
index 0000000..4f43600
--- /dev/null
+++ 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/NCModelPackagesWrapperMix.scala
@@ -0,0 +1,19 @@
+package org.apache.nlpcraft.probe.mgrs.deploy
+
+import org.apache.nlpcraft.NCTestElement
+import org.apache.nlpcraft.NCTestElement._
+import org.apache.nlpcraft.model.{NCElement, NCModelAdapter, NCModelAddPackage}
+
+import java.util
+
+@NCModelAddPackage(Array("org.apache.nlpcraft.probe.mgrs.deploy"))
+class NCModelPackagesWrapperMix extends 
NCModelAdapter("nlpcraft.deploy.test.mdl", "Test Model", "1.0") {
+    override def getElements: util.Set[NCElement] = {
+        Set(
+            NCTestElement("scalaClass"),
+            NCTestElement("scalaStatic"),
+            NCTestElement("javaClass"),
+            NCTestElement("javaStatic")
+        )
+    }
+}
diff --git 
a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/jv/NCModelClassesWrapper.java
 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/jv/NCModelClassesWrapper.java
new file mode 100644
index 0000000..b894a66
--- /dev/null
+++ 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/jv/NCModelClassesWrapper.java
@@ -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
+ *
+ *      https://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.apache.nlpcraft.probe.mgrs.deploy.jv;
+
+import org.apache.nlpcraft.model.NCModelAddClasses;
+
+@NCModelAddClasses({
+    "org.apache.nlpcraft.probe.mgrs.deploy.jv.NCNestedClass",
+    "org.apache.nlpcraft.probe.mgrs.deploy.jv.NCNestedStatic"
+})
+public class NCModelClassesWrapper extends NCModelDeploySpecAdapter {
+    // No-op.
+}
diff --git 
a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/jv/NCModelDeploySpecAdapter.java
 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/jv/NCModelDeploySpecAdapter.java
new file mode 100644
index 0000000..65c7ac1
--- /dev/null
+++ 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/jv/NCModelDeploySpecAdapter.java
@@ -0,0 +1,20 @@
+package org.apache.nlpcraft.probe.mgrs.deploy.jv;
+
+import org.apache.nlpcraft.model.NCElement;
+import org.apache.nlpcraft.model.NCModelAdapter;
+
+import java.util.Set;
+
+class NCModelDeploySpecAdapter extends NCModelAdapter {
+    public NCModelDeploySpecAdapter() {
+        super("nlpcraft.deploy.test.mdl", "Test Model", "1.0");
+    }
+
+    @Override
+    public Set<NCElement> getElements() {
+        return Set.of(
+            (NCElement) () -> "javaClass",
+            (NCElement) () -> "javaStatic"
+        );
+    }
+}
diff --git 
a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/jv/NCModelPackagesWrapper.java
 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/jv/NCModelPackagesWrapper.java
new file mode 100644
index 0000000..db2dac6
--- /dev/null
+++ 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/jv/NCModelPackagesWrapper.java
@@ -0,0 +1,25 @@
+/*
+ * 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
+ *
+ *      https://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.apache.nlpcraft.probe.mgrs.deploy.jv;
+
+import org.apache.nlpcraft.model.NCModelAddPackage;
+
+@NCModelAddPackage("org.apache.nlpcraft.probe.mgrs.deploy.jv")
+public class NCModelPackagesWrapper extends NCModelDeploySpecAdapter {
+    // No-op.
+}
diff --git 
a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/jv/NCNestedClass.java
 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/jv/NCNestedClass.java
new file mode 100644
index 0000000..8e47de2
--- /dev/null
+++ 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/jv/NCNestedClass.java
@@ -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
+ *
+ *      https://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.apache.nlpcraft.probe.mgrs.deploy.jv;
+
+import org.apache.nlpcraft.model.NCIntent;
+import org.apache.nlpcraft.model.NCResult;
+
+public class NCNestedClass {
+    @NCIntent("intent=javaClass term={tok_id() == 'javaClass'}")
+    public NCResult m() {
+        return NCResult.text("OK");
+    }
+}
diff --git 
a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/jv/NCNestedStatic.java
 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/jv/NCNestedStatic.java
new file mode 100644
index 0000000..c2c86bd
--- /dev/null
+++ 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/jv/NCNestedStatic.java
@@ -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
+ *
+ *      https://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.apache.nlpcraft.probe.mgrs.deploy.jv;
+
+import org.apache.nlpcraft.model.NCIntent;
+import org.apache.nlpcraft.model.NCResult;
+
+public class NCNestedStatic {
+    @NCIntent("intent=javaStatic term={tok_id() == 'javaStatic'}")
+    public static NCResult m() {
+        return NCResult.text("OK");
+    }
+}
diff --git 
a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/sc/NCModelClassesWrapper.scala
 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/sc/NCModelClassesWrapper.scala
new file mode 100644
index 0000000..994a36d
--- /dev/null
+++ 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/sc/NCModelClassesWrapper.scala
@@ -0,0 +1,9 @@
+package org.apache.nlpcraft.probe.mgrs.deploy.sc
+
+import org.apache.nlpcraft.model.NCModelAddClasses
+
+@NCModelAddClasses(Array(
+    "org.apache.nlpcraft.probe.mgrs.deploy.sc.NCNestedClass",
+    "org.apache.nlpcraft.probe.mgrs.deploy.sc.NCNestedStatic"
+))
+class NCModelClassesWrapper extends NCModelDeploySpecAdapter
diff --git 
a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/sc/NCModelDeploySpecAdapter.scala
 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/sc/NCModelDeploySpecAdapter.scala
new file mode 100644
index 0000000..1e42320
--- /dev/null
+++ 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/sc/NCModelDeploySpecAdapter.scala
@@ -0,0 +1,11 @@
+package org.apache.nlpcraft.probe.mgrs.deploy.sc
+
+import org.apache.nlpcraft.NCTestElement
+import org.apache.nlpcraft.model.{NCElement, NCModelAdapter}
+
+import java.util
+import scala.language.implicitConversions
+
+class NCModelDeploySpecAdapter extends 
NCModelAdapter("nlpcraft.deploy.test.mdl", "Test Model", "1.0") {
+    override def getElements: util.Set[NCElement] = 
Set(NCTestElement("scalaClass"), NCTestElement("scalaStatic"))
+}
diff --git 
a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/sc/NCModelPackagesWrapper.scala
 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/sc/NCModelPackagesWrapper.scala
new file mode 100644
index 0000000..ca8f1e4
--- /dev/null
+++ 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/sc/NCModelPackagesWrapper.scala
@@ -0,0 +1,6 @@
+package org.apache.nlpcraft.probe.mgrs.deploy.sc
+
+import org.apache.nlpcraft.model.NCModelAddPackage
+
+@NCModelAddPackage(Array("org.apache.nlpcraft.probe.mgrs.deploy.sc"))
+class NCModelPackagesWrapper extends NCModelDeploySpecAdapter
diff --git 
a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/sc/NCNestedClass.scala
 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/sc/NCNestedClass.scala
new file mode 100644
index 0000000..3d410eb
--- /dev/null
+++ 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/sc/NCNestedClass.scala
@@ -0,0 +1,9 @@
+package org.apache.nlpcraft.probe.mgrs.deploy.sc
+
+import org.apache.nlpcraft.model.{NCIntent, NCResult}
+
+class NCNestedClass {
+    @NCIntent("intent=scalaClass term={tok_id() == 'scalaClass'}")
+    def a(): NCResult = NCResult.text("OK")
+
+}
diff --git 
a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/sc/NCNestedStatic.scala
 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/sc/NCNestedStatic.scala
new file mode 100644
index 0000000..645aa12
--- /dev/null
+++ 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/sc/NCNestedStatic.scala
@@ -0,0 +1,8 @@
+package org.apache.nlpcraft.probe.mgrs.deploy.sc
+
+import org.apache.nlpcraft.model.{NCIntent, NCResult}
+
+object NCNestedStatic {
+    @NCIntent("intent=scalaStatic term={tok_id() == 'scalaStatic'}")
+    def b(): NCResult = NCResult.text("OK")
+}
\ No newline at end of file
diff --git 
a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/NCEnricherNestedModelSpec5.scala
 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/NCEnricherNestedModelSpec5.scala
index f4ceeb5..a6a7e81 100644
--- 
a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/NCEnricherNestedModelSpec5.scala
+++ 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/NCEnricherNestedModelSpec5.scala
@@ -42,7 +42,6 @@ class NCNestedTestModel5 extends NCModelAdapter(
     )
     private def onBigCity(ctx: NCIntentMatch): NCResult = NCResult.text("OK")
 }
-
 /**
   *
   */

Reply via email to