1 // Copyright 2001-2018 Crytek GmbH / Crytek Group. All rights reserved.
4 #include "QueryBlueprint.h"
6 // *INDENT-OFF* - <hard to read code and declarations due to inconsistent indentation>
13 //===================================================================================
15 // CTextualQueryBlueprint
17 //===================================================================================
19 CTextualQueryBlueprint::CTextualQueryBlueprint()
20 : m_queryFactoryGUID(CryGUID::Null())
21 , m_maxItemsToKeepInResultSet(1) // default to 1 item in the result set (i.e. the most common use-case), instead of 0 (which would mean: "return me as many items as possible")
26 CTextualQueryBlueprint::~CTextualQueryBlueprint()
28 for (CTextualEvaluatorBlueprint
* pIE
: m_instantEvaluators
)
33 for (CTextualEvaluatorBlueprint
* pDE
: m_deferredEvaluators
)
38 for (CTextualQueryBlueprint
* pChild
: m_children
)
44 void CTextualQueryBlueprint::SetName(const char* szName
)
49 void CTextualQueryBlueprint::SetQueryFactoryName(const char* szFactoryName
)
51 m_queryFactoryName
= szFactoryName
;
54 void CTextualQueryBlueprint::SetQueryFactoryGUID(const CryGUID
& factoryGUID
)
56 m_queryFactoryGUID
= factoryGUID
;
59 void CTextualQueryBlueprint::SetMaxItemsToKeepInResultSet(size_t maxItems
)
61 m_maxItemsToKeepInResultSet
= maxItems
;
64 ITextualGlobalConstantParamsBlueprint
& CTextualQueryBlueprint::GetGlobalConstantParams()
66 return m_globalConstantParams
;
69 ITextualGlobalRuntimeParamsBlueprint
& CTextualQueryBlueprint::GetGlobalRuntimeParams()
71 return m_globalRuntimeParams
;
74 ITextualGeneratorBlueprint
& CTextualQueryBlueprint::SetGenerator()
76 m_pGenerator
.reset(new CTextualGeneratorBlueprint
);
80 ITextualEvaluatorBlueprint
& CTextualQueryBlueprint::AddInstantEvaluator()
82 CTextualEvaluatorBlueprint
* pNewInstantEvaluatorBP
= new CTextualEvaluatorBlueprint
;
83 m_instantEvaluators
.push_back(pNewInstantEvaluatorBP
);
84 return *pNewInstantEvaluatorBP
;
87 size_t CTextualQueryBlueprint::GetInstantEvaluatorCount() const
89 return m_instantEvaluators
.size();
92 ITextualEvaluatorBlueprint
& CTextualQueryBlueprint::AddDeferredEvaluator()
94 CTextualEvaluatorBlueprint
* pNewDeferredEvaluatorBlueprint
= new CTextualEvaluatorBlueprint
;
95 m_deferredEvaluators
.push_back(pNewDeferredEvaluatorBlueprint
);
96 return *pNewDeferredEvaluatorBlueprint
;
99 ITextualQueryBlueprint
& CTextualQueryBlueprint::AddChild()
101 CTextualQueryBlueprint
* pNewChild
= new CTextualQueryBlueprint
;
102 m_children
.push_back(pNewChild
);
106 size_t CTextualQueryBlueprint::GetDeferredEvaluatorCount() const
108 return m_deferredEvaluators
.size();
111 const char* CTextualQueryBlueprint::GetName() const
113 return m_name
.c_str();
116 const char* CTextualQueryBlueprint::GetQueryFactoryName() const
118 return m_queryFactoryName
.c_str();
121 const CryGUID
& CTextualQueryBlueprint::GetQueryFactoryGUID() const
123 return m_queryFactoryGUID
;
126 size_t CTextualQueryBlueprint::GetMaxItemsToKeepInResultSet() const
128 return m_maxItemsToKeepInResultSet
;
131 const ITextualGlobalConstantParamsBlueprint
& CTextualQueryBlueprint::GetGlobalConstantParams() const
133 return m_globalConstantParams
;
136 const ITextualGlobalRuntimeParamsBlueprint
& CTextualQueryBlueprint::GetGlobalRuntimeParams() const
138 return m_globalRuntimeParams
;
141 const ITextualGeneratorBlueprint
* CTextualQueryBlueprint::GetGenerator() const
143 return m_pGenerator
.get();
146 const ITextualEvaluatorBlueprint
& CTextualQueryBlueprint::GetInstantEvaluator(size_t index
) const
148 assert(index
< m_instantEvaluators
.size());
149 return *m_instantEvaluators
[index
];
152 const ITextualEvaluatorBlueprint
& CTextualQueryBlueprint::GetDeferredEvaluator(size_t index
) const
154 assert(index
< m_deferredEvaluators
.size());
155 return *m_deferredEvaluators
[index
];
158 size_t CTextualQueryBlueprint::GetChildCount() const
160 return m_children
.size();
163 const ITextualQueryBlueprint
& CTextualQueryBlueprint::GetChild(size_t index
) const
165 assert(index
< m_children
.size());
166 return *m_children
[index
];
169 void CTextualQueryBlueprint::SetSyntaxErrorCollector(DataSource::SyntaxErrorCollectorUniquePtr pSyntaxErrorCollector
)
171 m_pSyntaxErrorCollector
= std::move(pSyntaxErrorCollector
);
174 DataSource::ISyntaxErrorCollector
* CTextualQueryBlueprint::GetSyntaxErrorCollector() const
176 return m_pSyntaxErrorCollector
.get();
179 //===================================================================================
183 //===================================================================================
185 CQueryBlueprint::CQueryBlueprint()
186 : m_pQueryFactory(nullptr)
187 , m_maxItemsToKeepInResultSet(0)
193 CQueryBlueprint::~CQueryBlueprint()
195 for (CInstantEvaluatorBlueprint
* pIE
: m_instantEvaluators
)
200 for (CDeferredEvaluatorBlueprint
* pDE
: m_deferredEvaluators
)
206 const char* CQueryBlueprint::GetName() const
208 return m_name
.c_str();
211 void CQueryBlueprint::VisitRuntimeParams(Client::IQueryBlueprintRuntimeParamVisitor
& visitor
) const
213 std::map
<string
, Client::IItemFactory
*> allRuntimeParamsInTheHierarchy
;
215 GrabRuntimeParamsRecursively(allRuntimeParamsInTheHierarchy
);
217 for (const auto& pair
: allRuntimeParamsInTheHierarchy
)
219 const char* szParamName
= pair
.first
.c_str();
220 Client::IItemFactory
* pItemFactory
= pair
.second
;
221 assert(pItemFactory
);
222 visitor
.OnRuntimeParamVisited(szParamName
, *pItemFactory
);
226 const Shared::CTypeInfo
& CQueryBlueprint::GetOutputType() const
228 assert(m_pQueryFactory
);
229 return m_pQueryFactory
->GetQueryBlueprintType(*this);
232 bool CQueryBlueprint::Resolve(const ITextualQueryBlueprint
& source
)
235 m_name
= source
.GetName();
237 // query factory: first search by its GUID, then by its name
239 const CryGUID
& queryFactoryGUID
= source
.GetQueryFactoryGUID();
240 const char* szQueryFactoryName
= source
.GetQueryFactoryName();
242 if(!(m_pQueryFactory
= static_cast<CQueryFactoryBase
*>(g_pHub
->GetQueryFactoryDatabase().FindFactoryByGUID(queryFactoryGUID
)))) // the static_cast<> is kinda ok'ish here, since IQueryFactory and its derived class CQueryFactoryBase are _both_ defined in the core, so we definitely know about the inheritance hierarchy
244 if (!(m_pQueryFactory
= static_cast<CQueryFactoryBase
*>(g_pHub
->GetQueryFactoryDatabase().FindFactoryByName(szQueryFactoryName
)))) // ditto
246 if (DataSource::ISyntaxErrorCollector
* pSE
= source
.GetSyntaxErrorCollector())
248 Shared::CUqsString guidAsString
;
249 Shared::Internal::CGUIDHelper::ToString(queryFactoryGUID
, guidAsString
);
250 pSE
->AddErrorMessage("Unknown QueryFactory: GUID = %s, name = '%s'", guidAsString
.c_str(), szQueryFactoryName
);
257 // max. items to keep in result set
258 m_maxItemsToKeepInResultSet
= source
.GetMaxItemsToKeepInResultSet();
260 // ensure no duplicates between constant-params and runtime-params
261 for (size_t i1
= 0; i1
< source
.GetGlobalConstantParams().GetParameterCount(); ++i1
)
263 for (size_t i2
= 0; i2
< source
.GetGlobalRuntimeParams().GetParameterCount(); ++i2
)
265 const CTextualGlobalConstantParamsBlueprint::SParameterInfo p1
= source
.GetGlobalConstantParams().GetParameter(i1
);
266 const CTextualGlobalRuntimeParamsBlueprint::SParameterInfo p2
= source
.GetGlobalRuntimeParams().GetParameter(i2
);
268 if (strcmp(p1
.szName
, p2
.szName
) == 0)
270 // output error to constant-params
271 if (DataSource::ISyntaxErrorCollector
* pSE
= p1
.pSyntaxErrorCollector
)
273 pSE
->AddErrorMessage("Global constant-parameter clashes with runtime-parameter of the same name: '%s'", p1
.szName
);
276 // output error to runtime-params
277 if (DataSource::ISyntaxErrorCollector
* pSE
= p2
.pSyntaxErrorCollector
)
279 pSE
->AddErrorMessage("Global runtime-parameter clashes with constant-parameter of the same name: '%s'", p2
.szName
);
287 // global constant-params
288 if (!m_globalConstantParams
.Resolve(source
.GetGlobalConstantParams()))
293 // global runtime-params
294 if (!m_globalRuntimeParams
.Resolve(source
.GetGlobalRuntimeParams(), m_pParent
))
299 // generator (a generator is optional and typically never exists in composite queries)
300 if (const ITextualGeneratorBlueprint
* pGeneratorBlueprint
= source
.GetGenerator())
302 m_pGenerator
.reset(new CGeneratorBlueprint
);
303 if (!m_pGenerator
->Resolve(*pGeneratorBlueprint
, *this))
305 m_pGenerator
.reset(); // nullify the generator so that CInputBlueprint::Resolve() won't be tempted to use this half-baked object for further checks (and crash becuase it might be lacking a generator-factory)
310 // instant-evaluators
311 for (size_t i
= 0; i
< source
.GetInstantEvaluatorCount(); ++i
)
313 CInstantEvaluatorBlueprint
* pNewInstantEvaluatorBP
= new CInstantEvaluatorBlueprint
;
314 m_instantEvaluators
.push_back(pNewInstantEvaluatorBP
);
315 if (!pNewInstantEvaluatorBP
->Resolve(source
.GetInstantEvaluator(i
), *this))
321 // deferred-evaluators
322 for (size_t i
= 0; i
< source
.GetDeferredEvaluatorCount(); ++i
)
324 CDeferredEvaluatorBlueprint
* pNewDeferredEvaluatorBP
= new CDeferredEvaluatorBlueprint
;
325 m_deferredEvaluators
.push_back(pNewDeferredEvaluatorBP
);
326 if (!pNewDeferredEvaluatorBP
->Resolve(source
.GetDeferredEvaluator(i
), *this))
333 // - ensure that the max. number of instant- and deferred-evaluators is not exceeded
334 // - otherwise, CQuery cannot keep track of all evaluator's states as a bitfield during query execution
338 const size_t numInstantEvaluators
= m_instantEvaluators
.size();
339 if (numInstantEvaluators
> UQS_MAX_EVALUATORS
)
341 if (DataSource::ISyntaxErrorCollector
* pSE
= source
.GetSyntaxErrorCollector())
343 pSE
->AddErrorMessage("Exceeded the maximum number of instant-evaluators in the query blueprint (max %i supported, %i present in the blueprint)", UQS_MAX_EVALUATORS
, (int)numInstantEvaluators
);
350 const size_t numDeferredEvaluators
= m_deferredEvaluators
.size();
351 if (numDeferredEvaluators
> UQS_MAX_EVALUATORS
)
353 if (DataSource::ISyntaxErrorCollector
* pSE
= source
.GetSyntaxErrorCollector())
355 pSE
->AddErrorMessage("Exceeded the maximum number of deferred-evaluators in the query blueprint (max %i supported, %i present in the blueprint)", UQS_MAX_EVALUATORS
, (int)numDeferredEvaluators
);
361 // children (recurse down)
362 for (size_t i
= 0, n
= source
.GetChildCount(); i
< n
; ++i
)
364 const ITextualQueryBlueprint
& childSource
= source
.GetChild(i
);
365 std::shared_ptr
<CQueryBlueprint
> pChildTarget(new CQueryBlueprint
);
366 pChildTarget
->m_pParent
= this;
367 m_children
.push_back(pChildTarget
);
368 if (!pChildTarget
->Resolve(childSource
))
372 // if the query-factory expects to have a generator in the blueprint, then ensure that one was provided (and the other way around)
373 if (m_pQueryFactory
) // might be a nullptr in case of unknown query factory (c. f. code further above)
375 if (m_pQueryFactory
->RequiresGenerator() && !m_pGenerator
)
377 if (DataSource::ISyntaxErrorCollector
* pSE
= source
.GetSyntaxErrorCollector())
379 pSE
->AddErrorMessage("Query factories of type '%s' require a generator, but none is provided", m_pQueryFactory
->GetName());
383 else if (!m_pQueryFactory
->RequiresGenerator() && m_pGenerator
)
385 if (DataSource::ISyntaxErrorCollector
* pSE
= source
.GetSyntaxErrorCollector())
387 pSE
->AddErrorMessage("Query factories of type '%s' don't require a generator, but one was provided", m_pQueryFactory
->GetName());
393 // validate the min/max number of child queries
394 if (m_pQueryFactory
) // might be a nullptr in case of unknown query factory (c. f. code further above)
396 const size_t actualNumChildQueries
= m_children
.size();
398 // min required child queries
400 const size_t minRequiredChildQueries
= m_pQueryFactory
->GetMinRequiredChildren();
401 if (actualNumChildQueries
< minRequiredChildQueries
)
403 if (DataSource::ISyntaxErrorCollector
* pSE
= source
.GetSyntaxErrorCollector())
405 pSE
->AddErrorMessage("Too few child queries: %i (expected at least %i)", (int)actualNumChildQueries
, (int)minRequiredChildQueries
);
411 // max allowed child queries
413 const size_t maxAllowedChildQueries
= m_pQueryFactory
->GetMaxAllowedChildren();
414 if (actualNumChildQueries
> maxAllowedChildQueries
)
416 if (DataSource::ISyntaxErrorCollector
* pSE
= source
.GetSyntaxErrorCollector())
418 pSE
->AddErrorMessage("Too many child queries: %i (expected no more than %i)", (int)actualNumChildQueries
, (int)maxAllowedChildQueries
);
425 // have the query-factory check that if it deals with child-queries that their output types are compatible among each other
428 size_t indexOfChildCausingTheError
= 0;
430 if (!m_pQueryFactory
->CheckOutputTypeCompatibilityAmongChildQueryBlueprints(*this, error
, indexOfChildCausingTheError
))
432 // write the error message into the child that caused the error
433 const ITextualQueryBlueprint
& childSource
= source
.GetChild(indexOfChildCausingTheError
);
434 if (DataSource::ISyntaxErrorCollector
* pSE
= childSource
.GetSyntaxErrorCollector())
436 pSE
->AddErrorMessage("%s", error
.c_str());
442 // sort the instant-evaluator blueprints by cost and evaluation modality such that their order at execution time won't change
443 // (this also helps the user to read the query history as it will show all evaluators in order of how they were executed)
444 SortInstantEvaluatorBlueprintsByCostAndEvaluationModality();
449 const CQueryBlueprint
* CQueryBlueprint::GetParent() const
454 size_t CQueryBlueprint::GetChildCount() const
456 return m_children
.size();
459 std::shared_ptr
<const CQueryBlueprint
> CQueryBlueprint::GetChild(size_t index
) const
461 assert(index
< m_children
.size());
462 return m_children
[index
];
465 int CQueryBlueprint::GetChildIndex(const CQueryBlueprint
* pChildToSearchFor
) const
467 for (size_t i
= 0; i
< m_children
.size(); ++i
)
469 if (m_children
[i
].get() == pChildToSearchFor
)
475 QueryBaseUniquePtr
CQueryBlueprint::CreateQuery(const CQueryBase::SCtorContext
& ctorContext
) const
477 assert(m_pQueryFactory
);
478 return m_pQueryFactory
->CreateQuery(ctorContext
);
481 int CQueryBlueprint::GetMaxItemsToKeepInResultSet() const
483 return m_maxItemsToKeepInResultSet
;
486 const CGlobalConstantParamsBlueprint
& CQueryBlueprint::GetGlobalConstantParamsBlueprint() const
488 return m_globalConstantParams
;
491 const CGlobalRuntimeParamsBlueprint
& CQueryBlueprint::GetGlobalRuntimeParamsBlueprint() const
493 return m_globalRuntimeParams
;
496 const CGeneratorBlueprint
* CQueryBlueprint::GetGeneratorBlueprint() const
498 return m_pGenerator
.get();
501 const std::vector
<CInstantEvaluatorBlueprint
*>& CQueryBlueprint::GetInstantEvaluatorBlueprints() const
503 return m_instantEvaluators
;
506 const std::vector
<CDeferredEvaluatorBlueprint
*>& CQueryBlueprint::GetDeferredEvaluatorBlueprints() const
508 return m_deferredEvaluators
;
511 bool CQueryBlueprint::CheckPresenceAndTypeOfGlobalRuntimeParamsRecursively(const Shared::IVariantDict
& runtimeParamsToValidate
, Shared::CUqsString
& error
) const
514 // ensure that all required runtime-params have been passed in and that their data types match those in this query-blueprint
517 const std::map
<string
, CGlobalRuntimeParamsBlueprint::SParamInfo
>& expectedRuntimeParams
= m_globalRuntimeParams
.GetParams();
519 for (const auto& pair
: expectedRuntimeParams
)
521 const char* szKey
= pair
.first
.c_str();
522 const Client::IItemFactory
* pFoundItemFactory
= runtimeParamsToValidate
.FindItemFactory(szKey
);
524 if (pFoundItemFactory
== nullptr)
526 error
.Format("Missing runtime-param: '%s'", szKey
);
530 const Client::IItemFactory
* pExpectedItemFactory
= pair
.second
.pItemFactory
;
531 assert(pExpectedItemFactory
);
533 if (pFoundItemFactory
!= pExpectedItemFactory
)
535 error
.Format("Runtime-param '%s' mismatches the item-factory: expected '%s', but received '%s'", szKey
, pExpectedItemFactory
->GetName(), pFoundItemFactory
->GetName());
541 for (const std::shared_ptr
<CQueryBlueprint
>& pChild
: m_children
)
543 if (!pChild
->CheckPresenceAndTypeOfGlobalRuntimeParamsRecursively(runtimeParamsToValidate
, error
))
552 const Shared::CTypeInfo
* CQueryBlueprint::GetTypeOfShuttledItemsToExpect() const
554 assert(m_pQueryFactory
);
555 return m_pQueryFactory
->GetTypeOfShuttledItemsToExpect(*this);
558 const CQueryFactoryBase
& CQueryBlueprint::GetQueryFactory() const
560 assert(m_pQueryFactory
);
561 return *m_pQueryFactory
;
564 void CQueryBlueprint::PrintToConsole(CLogger
& logger
) const
567 // name + output type + max items to generate
570 const Shared::CTypeInfo
& outputType
= GetOutputType();
571 logger
.Printf("\"%s\" [%s] (max items in result set = %i)", m_name
.c_str(), outputType
.name(), m_maxItemsToKeepInResultSet
);
572 CLoggerIndentation _indent
;
578 assert(m_pQueryFactory
);
579 logger
.Printf("Query factory = %s", m_pQueryFactory
->GetName());
582 // global constant params
585 m_globalConstantParams
.PrintToConsole(logger
);
588 // global runtime params
591 m_globalRuntimeParams
.PrintToConsole(logger
);
594 // generator (optional - typically never used by composite queries)
599 m_pGenerator
->PrintToConsole(logger
);
603 logger
.Printf("Generator: (none)");
607 // instant-evaluators
610 if (m_instantEvaluators
.empty())
612 logger
.Printf("Instant Evaluators: (none)");
616 logger
.Printf("Instant Evaluators:");
617 CLoggerIndentation _indent
;
618 for (size_t i
= 0, n
= m_instantEvaluators
.size(); i
< n
; ++i
)
621 prefix
.Format("#%i: ", (int)i
);
622 m_instantEvaluators
[i
]->PrintToConsole(logger
, prefix
.c_str());
627 // deferred-evaluators
630 if (m_deferredEvaluators
.empty())
632 logger
.Printf("Deferred Evaluators: (none)");
636 logger
.Printf("Deferred Evaluators:");
637 CLoggerIndentation _indent
;
638 for (size_t i
= 0, n
= m_deferredEvaluators
.size(); i
< n
; ++i
)
641 prefix
.Format("#%i: ", (int)i
);
642 m_deferredEvaluators
[i
]->PrintToConsole(logger
, prefix
.c_str());
650 if (m_children
.empty())
652 logger
.Printf("Children: (none)");
656 logger
.Printf("Children: %i", (int)m_children
.size());
657 for (size_t i
= 0, n
= m_children
.size(); i
< n
; ++i
)
659 logger
.Printf("Child %i:", (int)i
);
660 CLoggerIndentation _indent
;
661 m_children
[i
]->PrintToConsole(logger
);
666 void CQueryBlueprint::SortInstantEvaluatorBlueprintsByCostAndEvaluationModality()
669 // sort all instant-evaluator blueprints such that we end up with the following order:
689 std::vector
<CInstantEvaluatorBlueprint
*> cheapTesters
;
690 std::vector
<CInstantEvaluatorBlueprint
*> cheapScorers
;
691 std::vector
<CInstantEvaluatorBlueprint
*> expensiveTesters
;
692 std::vector
<CInstantEvaluatorBlueprint
*> expensiveScorers
;
694 for (CInstantEvaluatorBlueprint
* pIEBlueprint
: m_instantEvaluators
)
696 const Client::IInstantEvaluatorFactory
& factory
= pIEBlueprint
->GetFactory();
697 const Client::IInstantEvaluatorFactory::ECostCategory costCategory
= factory
.GetCostCategory();
698 const Client::IInstantEvaluatorFactory::EEvaluationModality evaluationModality
= factory
.GetEvaluationModality();
700 switch (costCategory
)
702 case Client::IInstantEvaluatorFactory::ECostCategory::Cheap
:
703 switch (evaluationModality
)
705 case Client::IInstantEvaluatorFactory::EEvaluationModality::Testing
:
706 cheapTesters
.push_back(pIEBlueprint
);
709 case Client::IInstantEvaluatorFactory::EEvaluationModality::Scoring
:
710 cheapScorers
.push_back(pIEBlueprint
);
718 case Client::IInstantEvaluatorFactory::ECostCategory::Expensive
:
719 switch (evaluationModality
)
721 case Client::IInstantEvaluatorFactory::EEvaluationModality::Testing
:
722 expensiveTesters
.push_back(pIEBlueprint
);
725 case Client::IInstantEvaluatorFactory::EEvaluationModality::Scoring
:
726 expensiveScorers
.push_back(pIEBlueprint
);
739 m_instantEvaluators
.clear();
741 m_instantEvaluators
.insert(m_instantEvaluators
.end(), cheapTesters
.begin(), cheapTesters
.end());
742 m_instantEvaluators
.insert(m_instantEvaluators
.end(), cheapScorers
.begin(), cheapScorers
.end());
743 m_instantEvaluators
.insert(m_instantEvaluators
.end(), expensiveTesters
.begin(), expensiveTesters
.end());
744 m_instantEvaluators
.insert(m_instantEvaluators
.end(), expensiveScorers
.begin(), expensiveScorers
.end());
747 void CQueryBlueprint::GrabRuntimeParamsRecursively(std::map
<string
, Client::IItemFactory
*>& out
) const
749 const std::map
<string
, CGlobalRuntimeParamsBlueprint::SParamInfo
>& params
= m_globalRuntimeParams
.GetParams();
751 for (const auto& pair
: params
)
753 const char* szParamName
= pair
.first
.c_str();
754 Client::IItemFactory
* pItemFactory
= pair
.second
.pItemFactory
;
755 assert(pItemFactory
);
756 out
[szParamName
] = pItemFactory
; // potentially overwrite the param from a parent; we presume here that both have the same item type (this is ensured by CGlobalRuntimeParamsBlueprint::Resolve())
759 // recurse down all children
760 for (const std::shared_ptr
<CQueryBlueprint
>& pChild
: m_children
)
762 pChild
->GrabRuntimeParamsRecursively(out
);