This is an automated email from the ASF dual-hosted git repository.
sergeykamov pushed a commit to branch NLPCRAFT-473
in repository https://gitbox.apache.org/repos/asf/incubator-nlpcraft.git
The following commit(s) were added to refs/heads/NLPCRAFT-473 by this push:
new e7f8d3e WIP.
e7f8d3e is described below
commit e7f8d3e0de649302233aa4eb133a5821d584c8c2
Author: Sergey Kamov <[email protected]>
AuthorDate: Sat Jan 29 12:14:37 2022 +0300
WIP.
---
.../nlpcraft/internal/impl/NCModelScanner.scala | 414 ++++++++++-----------
.../impl/scan/NCModelIntentsInvalidArgsSpec.scala | 22 +-
.../impl/scan/NCModelIntentsNestedSpec.scala | 6 +-
.../internal/impl/scan/NCModelIntentsSpec.scala | 2 +-
4 files changed, 222 insertions(+), 222 deletions(-)
diff --git
a/nlpcraft/src/main/scala/org/apache/nlpcraft/internal/impl/NCModelScanner.scala
b/nlpcraft/src/main/scala/org/apache/nlpcraft/internal/impl/NCModelScanner.scala
index dbc0839..973317b 100644
---
a/nlpcraft/src/main/scala/org/apache/nlpcraft/internal/impl/NCModelScanner.scala
+++
b/nlpcraft/src/main/scala/org/apache/nlpcraft/internal/impl/NCModelScanner.scala
@@ -75,11 +75,23 @@ object NCModelScanner extends LazyLogging:
CLS_JAVA_OPT
)
- private case class CallbackHolder(method: Method, function: NCIntentMatch
=> NCResult)
+ /**
+ *
+ * @param intent
+ * @param function
+ * @param method
+ */
private case class IntentHolder(intent: NCIDLIntent, function:
NCIntentMatch => NCResult, method: Method)
/**
*
+ */
+ private object IntentHolder:
+ def apply(cfg: NCModelConfig, intent: NCIDLIntent, obj: Object, mtd:
Method): IntentHolder =
+ new IntentHolder(intent, prepareCallback(cfg, mtd, obj, intent),
mtd)
+
+ /**
+ *
* @param cls
* @return
*/
@@ -127,14 +139,15 @@ object NCModelScanner extends LazyLogging:
/**
*
- * @param mdlId
+ * @param cfg
* @param mtd
* @param paramClss
* @param argsList
* @param ctxFirstParam
* @return
*/
- private def prepareParams(mdlId: String, mtd: Method, paramClss:
Seq[Class[_]], argsList: Seq[util.List[NCEntity]], ctxFirstParam: Boolean):
Seq[AnyRef] =
+ private def prepareParams(cfg: NCModelConfig, mtd: Method, paramClss:
Seq[Class[_]], argsList: Seq[util.List[NCEntity]], ctxFirstParam: Boolean):
Seq[AnyRef] =
+ val mdlId = cfg.getId
paramClss.zip(argsList).zipWithIndex.map { case ((paramCls, argList),
i) =>
def mkArg(): String = arg2Str(mtd, i, ctxFirstParam)
@@ -174,13 +187,14 @@ object NCModelScanner extends LazyLogging:
/**
*
- * @param mdlId
+ * @param cfg
* @param method
* @param obj
* @param args
* @return
*/
- private def invoke(mdlId: String, method: Method, obj: Object, args:
scala.Array[AnyRef]): NCResult =
+ private def invoke(cfg: NCModelConfig, method: Method, obj: Object, args:
scala.Array[AnyRef]): NCResult =
+ val mdlId = cfg.getId
val methodObj = if Modifier.isStatic(method.getModifiers) then null
else obj
var flag = method.canAccess(methodObj)
@@ -211,12 +225,13 @@ object NCModelScanner extends LazyLogging:
/**
*
- * @param mdlId
+ * @param cfg
* @param field
* @param obj
* @return
*/
- private def getFieldObject(mdlId: String, field: Field, obj: Object):
Object =
+ private def getFieldObject(cfg: NCModelConfig, field: Field, obj: Object):
Object =
+ val mdlId = cfg.getId
lazy val fStr = field2Str(field)
val fieldObj = if Modifier.isStatic(field.getModifiers) then null else
obj
var flag = field.canAccess(fieldObj)
@@ -275,32 +290,72 @@ object NCModelScanner extends LazyLogging:
val claxx = o.getClass
(claxx.getDeclaredFields ++ claxx.getFields).toSet
-import org.apache.nlpcraft.internal.impl.NCModelScanner.*
+ /**
+ *
+ * @param cfg
+ * @param mtd
+ * @return
+ */
+ private def scanSamples(cfg: NCModelConfig, mtd: Method): Map[String,
Seq[Seq[String]]] =
+ val smpAnns = mtd.getAnnotationsByType(CLS_SAMPLE)
+ val smpAnnsRef = mtd.getAnnotationsByType(CLS_SAMPLE_REF)
+ lazy val mtdStr = method2Str(mtd)
+ lazy val intAnns = mtd.getAnnotationsByType(CLS_INTENT)
+ lazy val refAnns = mtd.getAnnotationsByType(CLS_INTENT_REF)
+ lazy val samples = mutable.HashMap.empty[String, Seq[Seq[String]]]
+
+ if smpAnns.nonEmpty || smpAnnsRef.nonEmpty then
+ if intAnns.isEmpty && refAnns.isEmpty then
+ E(s"@NCIntentSample or @NCIntentSampleRef annotations without
corresponding @NCIntent or @NCIntentRef annotations: $mtdStr")
+ else
+ def read[T](annArr: scala.Array[T], annName: String,
getSamples: T => Seq[String], getSource: Option[T => String]): Seq[Seq[String]]
=
+ for (ann <- annArr.toSeq) yield
+ val samples = getSamples(ann).map(_.strip).filter(s =>
s.nonEmpty && s.head != '#')
-/**
- *
- * @param mdl
- */
-class NCModelScanner(mdl: NCModel) extends LazyLogging:
- require(mdl != null)
+ if samples.isEmpty then
+ getSource match
+ case None => logger.warn(s"$annName annotation
has no samples: $mtdStr")
+ case Some(f) => logger.warn(s"$annName
annotation references '${f(ann)}' file that has no samples: $mtdStr")
+
+ Seq.empty
+ else
+ samples
+ .filter(_.nonEmpty)
+
+ val seqSeq =
+ read[NCIntentSample](
+ smpAnns, "@NCIntentSample", _.value.toSeq, None
+ ) ++
+ read[NCIntentSampleRef](
+ smpAnnsRef, "@NCIntentSampleRef", a =>
NCUtils.readResource(a.value), Option(_.value)
+ )
- private final val cfg = mdl.getConfig
- private final val mdlId = cfg.getId
- private final val origin = cfg.getOrigin
+ if NCUtils.containsDups(seqSeq.flatMap(_.toSeq).toList) then
+ logger.warn(s"@NCIntentSample and @NCIntentSampleRef
annotations have duplicates: $mtdStr")
- private final val intents = mutable.Buffer.empty[IntentHolder]
- private final val intentDecls = mutable.Buffer.empty[NCIDLIntent]
- private final val objs = mutable.Buffer.empty[Object]
- private final val samples = mutable.HashMap.empty[Method, Map[String,
Seq[Seq[String]]]]
+ val distinct = seqSeq.map(_.distinct).distinct
+
+ for (ann <- intAnns; intent <-
NCIDLCompiler.compile(ann.value, cfg, mtdStr))
+ samples += intent.id -> distinct
+
+ for (ann <- refAnns) samples += ann.value -> distinct
+ else if intAnns.nonEmpty || refAnns.nonEmpty then
+ logger.warn(s"@NCIntentSample or @NCIntentSampleRef annotations
are missing for: $mtdStr")
+
+ samples.toMap
/**
*
+ * @param cfg
* @param mtd
* @param argClasses
* @param paramGenTypes
* @param ctxFirstParam
*/
- private def checkTypes(mtd: Method, argClasses: Seq[Class[_]],
paramGenTypes: Seq[Type], ctxFirstParam: Boolean): Unit =
+ private def checkTypes(cfg: NCModelConfig, mtd: Method, argClasses:
Seq[Class[_]], paramGenTypes: Seq[Type], ctxFirstParam: Boolean): Unit =
+ val mdlId = cfg.getId
+ val origin = cfg.getOrigin
+
require(argClasses.sizeIs == paramGenTypes.length)
var warned = false
@@ -315,64 +370,65 @@ class NCModelScanner(mdl: NCModel) extends LazyLogging:
if compType != CLS_ENTITY then
E(s"Unexpected array element type for @NCIntentTerm
annotated argument [mdlId=$mdlId, origin=$origin, type=${class2Str(compType)},
arg=${mkArg()}]")
- // Entities collection and optionals.
- else if COMP_CLS.contains(argClass) then
- paramGenType match
- case pt: ParameterizedType =>
- val actTypes = pt.getActualTypeArguments
- val compTypes = if actTypes == null then Seq.empty
else actTypes.toSeq
-
- if compTypes.sizeIs != 1 then
- E(s"Unexpected generic types count for
@NCIntentTerm annotated argument [mdlId=$mdlId, origin=$origin,
count=${compTypes.length}, arg=${mkArg()}]")
-
- val compType = compTypes.head
-
- compType match
- // Java, Scala, Groovy.
- case _: Class[_] =>
- val genClass =
compTypes.head.asInstanceOf[Class[_]]
- if genClass != CLS_ENTITY then
- E(s"Unexpected generic type for
@NCIntentTerm annotated argument [mdlId=$mdlId, origin=$origin,
type=${class2Str(genClass)}, arg=${mkArg()}]")
-
- // Kotlin.
- case _: WildcardType =>
- val wildcardType =
compTypes.head.asInstanceOf[WildcardType]
- val lowBounds = wildcardType.getLowerBounds
- val upBounds = wildcardType.getUpperBounds
- if lowBounds.nonEmpty || upBounds.size != 1 ||
upBounds(0) != CLS_ENTITY then
- E(s"Unexpected Kotlin generic type for
@NCIntentTerm annotated argument [mdlId=$mdlId, origin=$origin,
type=${wc2Str(wildcardType)}, arg=${mkArg()}]")
-
- case _ => E(s"Unexpected generic type for
@NCIntentTerm annotated argument [mdlId=$mdlId, origin=$origin,
type=${compType.getTypeName}, arg=${mkArg()}]")
-
- case _ =>
- // Scala.
- if COMP_CLS.exists(_ == paramGenType) then
- if !warned then
- warned = true // TODO: text
- logger.warn(s"Method arguments types cannot be
detected and checked: ${method2Str(mtd)}")
- end if
- else
- E(s"Unexpected parameter type for @NCIntentTerm
annotated argument [mdlId=$mdlId, origin=$origin,
type=${paramGenType.getTypeName}, arg=${mkArg()}]")
- // Other types.
- else
- E(s"Unexpected parameter type for @NCIntentTerm annotated
argument [mdlId=$mdlId, origin=$origin, type=${class2Str(argClass)},
arg=${mkArg()}]")
+ // Entities collection and optionals.
+ else if COMP_CLS.contains(argClass) then
+ paramGenType match
+ case pt: ParameterizedType =>
+ val actTypes = pt.getActualTypeArguments
+ val compTypes = if actTypes == null then Seq.empty
else actTypes.toSeq
+
+ if compTypes.sizeIs != 1 then
+ E(s"Unexpected generic types count for
@NCIntentTerm annotated argument [mdlId=$mdlId, origin=$origin,
count=${compTypes.length}, arg=${mkArg()}]")
+
+ val compType = compTypes.head
+
+ compType match
+ // Java, Scala, Groovy.
+ case _: Class[_] =>
+ val genClass =
compTypes.head.asInstanceOf[Class[_]]
+ if genClass != CLS_ENTITY then
+ E(s"Unexpected generic type for
@NCIntentTerm annotated argument [mdlId=$mdlId, origin=$origin,
type=${class2Str(genClass)}, arg=${mkArg()}]")
+
+ // Kotlin.
+ case _: WildcardType =>
+ val wildcardType =
compTypes.head.asInstanceOf[WildcardType]
+ val lowBounds = wildcardType.getLowerBounds
+ val upBounds = wildcardType.getUpperBounds
+ if lowBounds.nonEmpty || upBounds.size !=
1 || upBounds(0) != CLS_ENTITY then
+ E(s"Unexpected Kotlin generic type for
@NCIntentTerm annotated argument [mdlId=$mdlId, origin=$origin,
type=${wc2Str(wildcardType)}, arg=${mkArg()}]")
+
+ case _ => E(s"Unexpected generic type for
@NCIntentTerm annotated argument [mdlId=$mdlId, origin=$origin,
type=${compType.getTypeName}, arg=${mkArg()}]")
+
+ case _ =>
+ // Scala.
+ if COMP_CLS.exists(_ == paramGenType) then
+ if !warned then
+ warned = true // TODO: text
+ logger.warn(s"Method arguments types
cannot be detected and checked: ${method2Str(mtd)}")
+ end if
+ else
+ E(s"Unexpected parameter type for
@NCIntentTerm annotated argument [mdlId=$mdlId, origin=$origin,
type=${paramGenType.getTypeName}, arg=${mkArg()}]")
+ // Other types.
+ else
+ E(s"Unexpected parameter type for @NCIntentTerm annotated
argument [mdlId=$mdlId, origin=$origin, type=${class2Str(argClass)},
arg=${mkArg()}]")
}
/**
*
+ * @param cfg
* @param mtd
* @param paramCls
* @param limits
* @param ctxFirstParam
*/
- private def checkMinMax(mtd: Method, paramCls: Seq[Class[_]], limits:
Seq[(Int, Int)], ctxFirstParam: Boolean): Unit =
+ private def checkMinMax(cfg: NCModelConfig, mtd: Method, paramCls:
Seq[Class[_]], limits: Seq[(Int, Int)], ctxFirstParam: Boolean): Unit =
require(paramCls.sizeIs == limits.length)
paramCls.zip(limits).zipWithIndex.foreach { case ((cls, (min, max)),
i) =>
def mkArg(): String = arg2Str(mtd, i, ctxFirstParam)
val p1 = "its @NCIntentTerm annotated argument"
- val p2 = s"[mdlId=$mdlId, origin=$origin, arg=${mkArg()}]"
+ val p2 = s"[mdlId=${cfg.getId}, origin=${cfg.getOrigin},
arg=${mkArg()}]"
// Argument is single entity but defined as not single entity.
if cls == CLS_ENTITY && (min != 1 || max != 1) then
@@ -390,70 +446,15 @@ class NCModelScanner(mdl: NCModel) extends LazyLogging:
/**
*
- * @param intents
- * @param intent
- * @param cb
- * @param mtd
- */
- private def checkBind(intents: mutable.Buffer[IntentHolder], intent:
NCIDLIntent, cb: CallbackHolder, mtd: Method): Unit =
- if intents.exists(i => i._1.id == intent.id && i.method != cb.method)
then
- E(s"The intent cannot be bound to more than one callback
[mdlId=$mdlId, origin=$origin, class=${getClassName(mtd.getDeclaringClass)},
intentId=${intent.id}]")
-
- private def bindIntent(intent: NCIDLIntent, cb: CallbackHolder, mtd:
Method): Unit =
- if intents.exists(i => i._1.id == intent.id && i.method != cb.method)
then
- E(s"The intent cannot be bound to more than one callback
[mdlId=$mdlId, origin=$origin, class=${getClassName(mtd.getDeclaringClass)},
intentId=${intent.id}]")
- else
- intentDecls += intent
- intents += IntentHolder(intent, cb.function, cb.method)
-
-
- /**
- *
- * @param mtd
- * @param obj
- */
- private def processMethod(mtd: Method, obj: Object): Unit =
- samples += mtd -> scanSamples(mtd)
-
- def existsForOtherMethod(id: String): Boolean =
- intents.find(_.intent.id == id) match
- case Some(i) => i.method != mtd
- case None => false
-
- val anns = mtd.getAnnotationsByType(CLS_INTENT)
-
- if anns.nonEmpty then
- val mtdStr = method2Str(mtd)
-
- for (ann <- anns; intent <- NCIDLCompiler.compile(ann.value, cfg,
mtdStr))
- if intentDecls.exists(_.id == intent.id &&
existsForOtherMethod(intent.id)) then
- E(s"Duplicate intent ID [mdlId=$mdlId, origin=$origin,
callback=$mtdStr, id=${intent.id}]")
- else
- bindIntent(intent, prepareCallback(mtd, obj, intent), mtd)
-
- /**
- *
- * @param mtd
- * @param obj
- */
- private def processMethodRefs(mtd: Method, obj: Object): Unit =
- val anns = mtd.getAnnotationsByType(CLS_INTENT_REF)
-
- for (ann <- anns)
- val refId = ann.value.strip
-
- intentDecls.find(_.id == refId) match
- case Some(intent) => bindIntent(intent, prepareCallback(mtd,
obj, intent), mtd)
- case None => E(s"@NCIntentRef(\"$refId\") references unknown
intent ID [mdlId=$mdlId, origin=$origin, refId=$refId,
callback=${method2Str(mtd)}]")
-
- /**
- *
+ * @param cfg
* @param method
* @param obj
* @param intent
* @return
*/
- private def prepareCallback(method: Method, obj: Object, intent:
NCIDLIntent): CallbackHolder =
+ private def prepareCallback(cfg: NCModelConfig, method: Method, obj:
Object, intent: NCIDLIntent): NCIntentMatch => NCResult =
+ val mdlId = cfg.getId
+
// Checks method result type.
if method.getReturnType != CLS_QRY_RES then
E(s"Unexpected result type for @NCIntent annotated method
[mdlId=$mdlId, intentId=${intent.id}, type=${class2Str(method.getReturnType)},
callback=${method2Str(method)}]")
@@ -491,7 +492,7 @@ class NCModelScanner(mdl: NCModel) extends LazyLogging:
E(s"Missing @NCIntentTerm annotation for
[mdlId=$mdlId, intentId=${intent.id}, arg=${mkArg()}]")
case _ => E(s"Too many @NCIntentTerm annotations for
[mdlId=$mdlId, intentId=${intent.id}, arg=${mkArg()}]")
- }
+ }
if NCUtils.containsDups(termIds) then
E(s"Duplicate term IDs in @NCIntentTerm annotations [mdlId=$mdlId,
intentId=${intent.id}, dups=${NCUtils.getDups(termIds).mkString(", ")},
callback=${method2Str(method)}]")
@@ -510,120 +511,119 @@ class NCModelScanner(mdl: NCModel) extends LazyLogging:
val paramGenTypes =
getSeq(method.getGenericParameterTypes.toIndexedSeq)
// Checks parameters.
- checkTypes(method, tokParamTypes, paramGenTypes, ctxFirstParam)
+ checkTypes(cfg, method, tokParamTypes, paramGenTypes, ctxFirstParam)
// Checks limits.
val allLimits = terms.map(t => t.id.orNull -> (t.min, t.max)).toMap
- checkMinMax(method, tokParamTypes, termIds.map(allLimits),
ctxFirstParam)
+ checkMinMax(cfg, method, tokParamTypes, termIds.map(allLimits),
ctxFirstParam)
- CallbackHolder(
- method,
- (ctx: NCIntentMatch) =>
- val args = mutable.Buffer.empty[AnyRef]
- if ctxFirstParam then args += ctx
- args ++= prepareParams(mdlId, method, tokParamTypes,
termIds.map(ctx.getTermEntities), ctxFirstParam)
+ (ctx: NCIntentMatch) =>
+ val args = mutable.Buffer.empty[AnyRef]
+ if ctxFirstParam then args += ctx
+ args ++= prepareParams(cfg, method, tokParamTypes,
termIds.map(ctx.getTermEntities), ctxFirstParam)
- invoke(mdlId, method, obj, args.toArray)
- )
+ invoke(cfg, method, obj, args.toArray)
/**
*
- * @param mtd
* @return
*/
- private def scanSamples(mtd: Method): Map[String, Seq[Seq[String]]] =
- val smpAnns = mtd.getAnnotationsByType(CLS_SAMPLE)
- val smpAnnsRef = mtd.getAnnotationsByType(CLS_SAMPLE_REF)
- lazy val mtdStr = method2Str(mtd)
- lazy val intAnns = mtd.getAnnotationsByType(CLS_INTENT)
- lazy val refAnns = mtd.getAnnotationsByType(CLS_INTENT_REF)
- lazy val samples = mutable.HashMap.empty[String, Seq[Seq[String]]]
-
- if smpAnns.nonEmpty || smpAnnsRef.nonEmpty then
- if intAnns.isEmpty && refAnns.isEmpty then
- E(s"@NCIntentSample or @NCIntentSampleRef annotations without
corresponding @NCIntent or @NCIntentRef annotations: $mtdStr")
- else
- def read[T](annArr: scala.Array[T], annName: String,
getSamples: T => Seq[String], getSource: Option[T => String]): Seq[Seq[String]]
=
- for (ann <- annArr.toSeq) yield
- val samples = getSamples(ann).map(_.strip).filter(s =>
s.nonEmpty && s.head != '#')
-
- if samples.isEmpty then
- getSource match
- case None => logger.warn(s"$annName annotation
has no samples: $mtdStr")
- case Some(f) => logger.warn(s"$annName
annotation references '${f(ann)}' file that has no samples: $mtdStr")
-
- Seq.empty
- else
- samples
- .filter(_.nonEmpty)
-
- val seqSeq =
- read[NCIntentSample](
- smpAnns, "@NCIntentSample", _.value.toSeq, None
- ) ++
- read[NCIntentSampleRef](
- smpAnnsRef, "@NCIntentSampleRef", a =>
NCUtils.readResource(a.value), Option(_.value)
- )
-
- if NCUtils.containsDups(seqSeq.flatMap(_.toSeq).toList) then
- logger.warn(s"@NCIntentSample and @NCIntentSampleRef
annotations have duplicates: $mtdStr")
-
- val distinct = seqSeq.map(_.distinct).distinct
-
- for (ann <- intAnns; intent <-
NCIDLCompiler.compile(ann.value, cfg, mtdStr))
- samples += intent.id -> distinct
-
- for (ann <- refAnns) samples += ann.value -> distinct
- else if intAnns.nonEmpty || refAnns.nonEmpty then
- logger.warn(s"@NCIntentSample or @NCIntentSampleRef annotations
are missing for: $mtdStr")
-
- samples.toMap
-
- /**
- *
- * @return
- */
- def scan(): Seq[NCModelIntent] =
- def processClassHierarchy(claxx: Class[_]): Unit =
- if claxx != null then
+ def scan(mdl: NCModel): Seq[NCModelIntent] =
+ val cfg = mdl.getConfig
+ val mdlId = cfg.getId
+ val origin = cfg.getOrigin
+
+ val intents = mutable.Buffer.empty[IntentHolder]
+ val intentDecls = mutable.HashMap.empty[String, NCIDLIntent]
+ val objs = mutable.Buffer.empty[Object]
+ val parents = mutable.HashSet.empty[Class[_]]
+ val samples = mutable.HashMap.empty[Method, Map[String,
Seq[Seq[String]]]]
+
+ def processClass(claxx: Class[_]): Unit =
+ if claxx != null && parents.add(claxx) then
val anns = claxx.getAnnotationsByType(CLS_INTENT)
if anns.nonEmpty then
val origin = getClassName(claxx)
for (ann <- anns; intent <-
NCIDLCompiler.compile(ann.value, cfg, origin))
- // TODO: duplicate
- intentDecls += intent
+ intentDecls += intent.id -> intent
- processClassHierarchy(claxx.getSuperclass)
- claxx.getInterfaces.foreach(processClassHierarchy)
+ processClass(claxx.getSuperclass)
+ claxx.getInterfaces.foreach(processClass)
- // 1. First phase scan.
+ // First phase scan.
// - For given object finds references via fields (NCIntentObject).
Scans also each reference recursively and collects them.
// - For all methods of processed object collects samples
(NCIntentSample, NCIntentSampleRef) and intents (NCIntent)
def scan(obj: Object): Unit =
objs += obj
- processClassHierarchy(obj.getClass)
+ processClass(obj.getClass)
+
+ for (mtd <- getAllMethods(obj))
+ samples += mtd -> scanSamples(cfg, mtd)
+
+ for (
+ ann <- mtd.getAnnotationsByType(CLS_INTENT);
+ intent <- NCIDLCompiler.compile(ann.value, cfg,
method2Str(mtd))
+ )
+ intentDecls += intent.id -> intent
+ intents += IntentHolder(cfg, intent, obj, mtd)
- for (m <- getAllMethods(obj)) processMethod(m, obj)
- for (f <- getAllFields(obj) if
f.isAnnotationPresent(CLS_INTENT_OBJ)) scan(getFieldObject(mdlId, f, obj))
+ for (f <- getAllFields(obj) if
f.isAnnotationPresent(CLS_INTENT_OBJ))
+ scan(getFieldObject(cfg, f, obj))
scan(mdl)
- // 2. For model and all its references scans each method and finds
intents references (NCIntentRef)
- for (o <- objs; m <- getAllMethods(o)) processMethodRefs(m, o)
+ // Second phase. For model and all its references scans each method
and finds intents references (NCIntentRef)
+ for (
+ obj <- objs;
+ mtd <- getAllMethods(obj);
+ ann <- mtd.getAnnotationsByType(CLS_INTENT_REF)
+ )
+ val refId = ann.value.strip
+
+ val intent = intentDecls.getOrElse(
+ refId,
+ E(s"@NCIntentRef(\"$refId\") references unknown intent ID
[mdlId=$mdlId, origin=$origin, callback=${method2Str(mtd)}]")
+ )
+
+ intentDecls += intent.id -> intent
+ intents += IntentHolder(cfg, intent, obj, mtd)
+
+ // Validation.
+ // IDL with same ID but different IDL bodies.
+ val duplDefs: Set[NCIDLIntent] =
+ intentDecls.values.groupBy(_.id).
+ map { (id, seq) => id -> seq.map(_.idl).toSet }.
+ filter { (_, seq) => seq.sizeIs > 1 }.
+ keySet.
+ map(intentDecls)
+
+ def str(iter: Iterable[_]): String = iter.mkString("{", ",", "}")
+
+ if duplDefs.nonEmpty then
+ val s = str(duplDefs.map(p => s"id=${p.id}, origin=${p.origin}"))
+ E(s"Following IDL has same identifiers with different bodies: $s")
+
+ val duplInts: Map[Method, Seq[String]] =
+ intents.groupBy(_.method).
+ map { case (mtd, seq) => mtd -> seq.map(_.intent.id).toSeq }.
+ filter { case (_, seq) => seq.sizeIs > 1 }
+
+ if duplInts.nonEmpty then
+ val s = str(duplInts.map { (mtd, idlIds) =>
s"method=${method2Str(mtd)}, intents=${str(idlIds)}" })
+ E(s"Some methods have more that one intent: $s")
- // 3. Validation.
- val unusedIntents = intentDecls.filter(i => !intents.exists(_._1.id ==
i.id))
+ val unusedIds = intentDecls.keys.filter(k =>
!intents.exists(_.intent.id == k))
- if unusedIntents.nonEmpty then
- logger.warn(s"Intents are unused (have no callback):
[mdlId=$mdlId, origin=$origin,
intentIds=${unusedIntents.map(_.id).mkString("(", ", ", ")")}]")
+ if unusedIds.nonEmpty then
+ logger.warn(s"Intents are unused (have no callback):
[mdlId=$mdlId, origin=$origin, intentIds=${unusedIds.mkString("(", ", ",
")")}]")
if intents.nonEmpty then
- // Check the uniqueness of intent IDs.
- NCUtils.getDups(intents.map(_._1).toSeq.map(_.id)) match
+ // Check the uniqueness of intent IDs.
+ NCUtils.getDups(intents.map(_.intent.id).toSeq) match
case ids if ids.nonEmpty => E(s"Duplicate intent IDs
[mdlId=$mdlId, origin=$origin, ids=${ids.mkString(",")}]")
case _ => // No-op.
else
diff --git
a/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/impl/scan/NCModelIntentsInvalidArgsSpec.scala
b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/impl/scan/NCModelIntentsInvalidArgsSpec.scala
index 0640627..0c1e2dd 100644
---
a/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/impl/scan/NCModelIntentsInvalidArgsSpec.scala
+++
b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/impl/scan/NCModelIntentsInvalidArgsSpec.scala
@@ -102,12 +102,12 @@ class NCModelIntentsInvalidArgsSpec:
mkResult0(list)
private def testOk(mdl: NCModel, intentId: String): Unit =
- val i = new NCModelScanner(mdl).scan().find(_.intent.id ==
intentId).get
+ val i = NCModelScanner.scan(mdl).find(_.intent.id == intentId).get
println(s"Test finished [modelClass=${mdl.getClass}, intent=$intentId,
result=${i.function(INTENT_MATCH)}")
private def testRuntimeClassCast(mdl: NCModel, intentId: String): Unit =
- val i = new NCModelScanner(mdl).scan().find(_.intent.id ==
intentId).get
+ val i = NCModelScanner.scan(mdl).find(_.intent.id == intentId).get
try
i.function(INTENT_MATCH)
@@ -123,7 +123,7 @@ class NCModelIntentsInvalidArgsSpec:
private def testScanValidation(mdl: NCModel): Unit =
try
- new NCModelScanner(mdl).scan()
+ NCModelScanner.scan(mdl)
require(false)
catch
@@ -133,14 +133,14 @@ class NCModelIntentsInvalidArgsSpec:
@Test
def test(): Unit =
- testOk(CHECKED_MDL_VALID, "validList")
- testOk(CHECKED_MDL_VALID, "validOpt")
+// testOk(CHECKED_MDL_VALID, "validList")
+// testOk(CHECKED_MDL_VALID, "validOpt")
// Errors thrown on scan phase if error found in any intent.
testScanValidation(CHECKED_MDL_INVALID_LST)
- testScanValidation(CHECKED_MDL_INVALID_OPT)
-
- testOk(UNCHECKED_MDL, "validList")
- testOk(UNCHECKED_MDL, "validOpt")
- testRuntimeClassCast(UNCHECKED_MDL, "invalidList")
- testRuntimeClassCast(UNCHECKED_MDL, "invalidOpt")
\ No newline at end of file
+// testScanValidation(CHECKED_MDL_INVALID_OPT)
+//
+// testOk(UNCHECKED_MDL, "validList")
+// testOk(UNCHECKED_MDL, "validOpt")
+// testRuntimeClassCast(UNCHECKED_MDL, "invalidList")
+// testRuntimeClassCast(UNCHECKED_MDL, "invalidOpt")
\ No newline at end of file
diff --git
a/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/impl/scan/NCModelIntentsNestedSpec.scala
b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/impl/scan/NCModelIntentsNestedSpec.scala
index c8e2a4c..e025bbb 100644
---
a/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/impl/scan/NCModelIntentsNestedSpec.scala
+++
b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/impl/scan/NCModelIntentsNestedSpec.scala
@@ -81,13 +81,13 @@ class NCModelIntentsNestedSpec:
@Test
def test(): Unit =
- require(new NCModelScanner(MDL_VALID1).scan().sizeIs == 4)
- require(new NCModelScanner(MDL_VALID2).scan().sizeIs == 4)
+ require(NCModelScanner.scan(MDL_VALID1).sizeIs == 4)
+ require(NCModelScanner.scan(MDL_VALID2).sizeIs == 4)
@Test
def testNull(): Unit =
try
- new NCModelScanner(MDL_INVALID).scan()
+ NCModelScanner.scan(MDL_INVALID)
require(false)
catch
diff --git
a/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/impl/scan/NCModelIntentsSpec.scala
b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/impl/scan/NCModelIntentsSpec.scala
index 36ce1e6..e26fe6e 100644
---
a/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/impl/scan/NCModelIntentsSpec.scala
+++
b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/impl/scan/NCModelIntentsSpec.scala
@@ -34,5 +34,5 @@ class NCModelIntentsSpec:
)
@Test
- def test(): Unit = for (mdl <- mdls) require(new
NCModelScanner(mdl).scan().nonEmpty)
+ def test(): Unit = for (mdl <- mdls)
require(NCModelScanner.scan(mdl).nonEmpty)