changeset 5608:3d0aa94b44b3 find-refactoring

reorganization
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 08 May 2024 13:37:23 +0200
parents a3732285f8b6
children 4690a0d2b01e
files OrthancServer/CMakeLists.txt OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp OrthancServer/Sources/ResourceFinder.cpp OrthancServer/Sources/ResourceFinder.h
diffstat 4 files changed, 461 insertions(+), 379 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancServer/CMakeLists.txt	Wed May 08 12:58:16 2024 +0200
+++ b/OrthancServer/CMakeLists.txt	Wed May 08 13:37:23 2024 +0200
@@ -122,6 +122,7 @@
   ${CMAKE_SOURCE_DIR}/Sources/OrthancRestApi/OrthancRestSystem.cpp
   ${CMAKE_SOURCE_DIR}/Sources/OrthancWebDav.cpp
   ${CMAKE_SOURCE_DIR}/Sources/QueryRetrieveHandler.cpp
+  ${CMAKE_SOURCE_DIR}/Sources/ResourceFinder.cpp
   ${CMAKE_SOURCE_DIR}/Sources/Search/DatabaseConstraint.cpp
   ${CMAKE_SOURCE_DIR}/Sources/Search/DatabaseLookup.cpp
   ${CMAKE_SOURCE_DIR}/Sources/Search/DicomTagConstraint.cpp
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp	Wed May 08 12:58:16 2024 +0200
+++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp	Wed May 08 13:37:23 2024 +0200
@@ -21,6 +21,8 @@
 
 
 #include "../PrecompiledHeadersServer.h"
+#include "../ResourceFinder.h"
+
 #include "OrthancRestApi.h"
 
 #include "../../../OrthancFramework/Sources/Compression/GzipCompressor.h"
@@ -198,385 +200,6 @@
   }
 
 
-  class ResourceFinder : public boost::noncopyable
-  {
-  private:
-    FindRequest        request_;
-    bool               expand_;
-    std::set<DicomTag> requestedTags_;
-    DicomToJsonFormat  format_;
-    bool               includeAllMetadata_;   // Same as: ExpandResourceFlags_IncludeAllMetadata
-
-    SeriesStatus GetSeriesStatus(uint32_t& expectedNumberOfInstances,
-                                 const FindResponse::Resource& resource) const
-    {
-      if (request_.GetLevel() != ResourceType_Series)
-      {
-        throw OrthancException(ErrorCode_BadParameterType);
-      }
-
-      std::string s;
-      if (!resource.LookupMetadata(s, ResourceType_Series, MetadataType_Series_ExpectedNumberOfInstances) ||
-          !SerializationToolbox::ParseUnsignedInteger32(expectedNumberOfInstances, s))
-      {
-        return SeriesStatus_Unknown;
-      }
-
-      std::list<std::string> values;
-      if (!resource.LookupChildrenMetadata(values, MetadataType_Instance_IndexInSeries))
-      {
-        throw OrthancException(ErrorCode_BadSequenceOfCalls);
-      }
-
-      std::set<int64_t> instances;
-
-      for (std::list<std::string>::const_iterator
-             it = values.begin(); it != values.end(); ++it)
-      {
-        int64_t index;
-
-        if (!SerializationToolbox::ParseInteger64(index, *it))
-        {
-          return SeriesStatus_Unknown;
-        }
-
-        if (index <= 0 ||
-            index > static_cast<int64_t>(expectedNumberOfInstances))
-        {
-          // Out-of-range instance index
-          return SeriesStatus_Inconsistent;
-        }
-
-        if (instances.find(index) != instances.end())
-        {
-          // Twice the same instance index
-          return SeriesStatus_Inconsistent;
-        }
-
-        instances.insert(index);
-      }
-
-      if (instances.size() == static_cast<size_t>(expectedNumberOfInstances))
-      {
-        return SeriesStatus_Complete;
-      }
-      else
-      {
-        return SeriesStatus_Missing;
-      }
-    }
-
-
-    void Expand(Json::Value& target,
-                const FindResponse::Resource& resource,
-                ServerIndex& index) const
-    {
-      /**
-       * This method closely follows "SerializeExpandedResource()" in
-       * "ServerContext.cpp" from Orthanc 1.12.3.
-       **/
-
-      if (resource.GetLevel() != request_.GetLevel())
-      {
-        throw OrthancException(ErrorCode_InternalError);
-      }
-
-      if (!requestedTags_.empty())
-      {
-        throw OrthancException(ErrorCode_NotImplemented);
-      }
-
-      target = Json::objectValue;
-
-      target["Type"] = GetResourceTypeText(resource.GetLevel(), false, true);
-      target["ID"] = resource.GetIdentifier();
-
-      switch (resource.GetLevel())
-      {
-        case ResourceType_Patient:
-          break;
-
-        case ResourceType_Study:
-          target["ParentPatient"] = resource.GetParentIdentifier();
-          break;
-
-        case ResourceType_Series:
-          target["ParentStudy"] = resource.GetParentIdentifier();
-          break;
-
-        case ResourceType_Instance:
-          target["ParentSeries"] = resource.GetParentIdentifier();
-          break;
-
-        default:
-          throw OrthancException(ErrorCode_InternalError);
-      }
-
-      if (resource.GetLevel() != ResourceType_Instance)
-      {
-        const std::set<std::string>& children = resource.GetChildrenIdentifiers();
-
-        Json::Value c = Json::arrayValue;
-        for (std::set<std::string>::const_iterator
-               it = children.begin(); it != children.end(); ++it)
-        {
-          c.append(*it);
-        }
-
-        switch (resource.GetLevel())
-        {
-          case ResourceType_Patient:
-            target["Studies"] = c;
-            break;
-
-          case ResourceType_Study:
-            target["Series"] = c;
-            break;
-
-          case ResourceType_Series:
-            target["Instances"] = c;
-            break;
-
-          default:
-            throw OrthancException(ErrorCode_InternalError);
-        }
-      }
-
-      switch (resource.GetLevel())
-      {
-        case ResourceType_Patient:
-        case ResourceType_Study:
-          break;
-
-        case ResourceType_Series:
-        {
-          uint32_t expectedNumberOfInstances;
-          SeriesStatus status = GetSeriesStatus(expectedNumberOfInstances, resource);
-
-          target["Status"] = EnumerationToString(status);
-
-          static const char* EXPECTED_NUMBER_OF_INSTANCES = "ExpectedNumberOfInstances";
-
-          if (status == SeriesStatus_Unknown)
-          {
-            target[EXPECTED_NUMBER_OF_INSTANCES] = Json::nullValue;
-          }
-          else
-          {
-            target[EXPECTED_NUMBER_OF_INSTANCES] = expectedNumberOfInstances;
-          }
-
-          break;
-        }
-
-        case ResourceType_Instance:
-        {
-          FileInfo info;
-          if (resource.LookupAttachment(info, FileContentType_Dicom))
-          {
-            target["FileSize"] = static_cast<Json::UInt64>(info.GetUncompressedSize());
-            target["FileUuid"] = info.GetUuid();
-          }
-          else
-          {
-            throw OrthancException(ErrorCode_InternalError);
-          }
-
-          static const char* INDEX_IN_SERIES = "IndexInSeries";
-
-          std::string s;
-          uint32_t index;
-          if (resource.LookupMetadata(s, ResourceType_Instance, MetadataType_Instance_IndexInSeries) &&
-              SerializationToolbox::ParseUnsignedInteger32(index, s))
-          {
-            target[INDEX_IN_SERIES] = index;
-          }
-          else
-          {
-            target[INDEX_IN_SERIES] = Json::nullValue;
-          }
-
-          break;
-        }
-
-        default:
-          throw OrthancException(ErrorCode_InternalError);
-      }
-
-      std::string s;
-      if (resource.LookupMetadata(s, resource.GetLevel(), MetadataType_AnonymizedFrom))
-      {
-        target["AnonymizedFrom"] = s;
-      }
-
-      if (resource.LookupMetadata(s, resource.GetLevel(), MetadataType_ModifiedFrom))
-      {
-        target["ModifiedFrom"] = s;
-      }
-
-      if (resource.GetLevel() == ResourceType_Patient ||
-          resource.GetLevel() == ResourceType_Study ||
-          resource.GetLevel() == ResourceType_Series)
-      {
-        target["IsStable"] = !index.IsUnstableResource(resource.GetLevel(), resource.GetInternalId());
-
-        if (resource.LookupMetadata(s, resource.GetLevel(), MetadataType_LastUpdate))
-        {
-          target["LastUpdate"] = s;
-        }
-      }
-
-      {
-        static const char* const MAIN_DICOM_TAGS = "MainDicomTags";
-        static const char* const PATIENT_MAIN_DICOM_TAGS = "PatientMainDicomTags";
-
-        // TODO-FIND : (expandFlags & ExpandResourceFlags_IncludeMainDicomTags)
-        DicomMap allMainDicomTags;
-        resource.GetMainDicomTags(allMainDicomTags, resource.GetLevel());
-
-        DicomMap levelMainDicomTags;
-        allMainDicomTags.ExtractResourceInformation(levelMainDicomTags, resource.GetLevel());
-
-        target[MAIN_DICOM_TAGS] = Json::objectValue;
-        FromDcmtkBridge::ToJson(target[MAIN_DICOM_TAGS], levelMainDicomTags, format_);
-
-        if (resource.GetLevel() == ResourceType_Study)
-        {
-          DicomMap patientMainDicomTags;
-          allMainDicomTags.ExtractPatientInformation(patientMainDicomTags);
-
-          target[PATIENT_MAIN_DICOM_TAGS] = Json::objectValue;
-          FromDcmtkBridge::ToJson(target[PATIENT_MAIN_DICOM_TAGS], patientMainDicomTags, format_);
-        }
-
-        /*
-          TODO-FIND
-
-        if (!requestedTags_.empty())
-        {
-          static const char* const REQUESTED_TAGS = "RequestedTags";
-
-          DicomMap tags;
-          resource.GetMainDicomTags().ExtractTags(tags, requestedTags);
-
-          target[REQUESTED_TAGS] = Json::objectValue;
-          FromDcmtkBridge::ToJson(target[REQUESTED_TAGS], tags, format);
-        }
-        */
-      }
-
-      {
-        Json::Value labels = Json::arrayValue;
-
-        for (std::set<std::string>::const_iterator
-               it = resource.GetLabels().begin(); it != resource.GetLabels().end(); ++it)
-        {
-          labels.append(*it);
-        }
-
-        target["Labels"] = labels;
-      }
-
-      if (includeAllMetadata_)  // new in Orthanc 1.12.4
-      {
-        const std::map<MetadataType, std::string>& m = resource.GetMetadata(resource.GetLevel());
-
-        Json::Value metadata = Json::objectValue;
-
-        for (std::map<MetadataType, std::string>::const_iterator it = m.begin(); it != m.end(); ++it)
-        {
-          metadata[EnumerationToString(it->first)] = it->second;
-        }
-
-        target["Metadata"] = metadata;
-      }
-    }
-
-
-  public:
-    ResourceFinder(ResourceType level,
-                   bool expand) :
-      request_(level),
-      expand_(expand),
-      format_(DicomToJsonFormat_Human),
-      includeAllMetadata_(false)
-    {
-      if (expand)
-      {
-        request_.SetRetrieveMainDicomTags(level, true);
-        request_.SetRetrieveMetadata(level, true);
-        request_.SetRetrieveLabels(true);
-
-        if (level == ResourceType_Series)
-        {
-          request_.AddRetrieveChildrenMetadata(MetadataType_Instance_IndexInSeries); // required for the SeriesStatus
-        }
-
-        if (level == ResourceType_Instance)
-        {
-          request_.SetRetrieveAttachments(true); // for FileSize & FileUuid
-        }
-        else
-        {
-          request_.SetRetrieveChildrenIdentifiers(true);
-        }
-
-        if (level != ResourceType_Patient)
-        {
-          request_.SetRetrieveParentIdentifier(true);
-        }
-      }
-    }
-
-    void SetRequestedTags(const std::set<DicomTag>& tags)
-    {
-      requestedTags_ = tags;
-    }
-
-    void SetFormat(DicomToJsonFormat format)
-    {
-      format_ = format;
-    }
-
-    void SetLimits(uint64_t since,
-                   uint64_t count)
-    {
-      request_.SetLimits(since, count);
-    }
-
-    void SetIncludeAllMetadata(bool include)
-    {
-      includeAllMetadata_ = include;
-    }
-
-    void Execute(Json::Value& target,
-                 ServerContext& context)
-    {
-      FindResponse response;
-      context.GetIndex().ExecuteFind(response, request_);
-
-      target = Json::arrayValue;
-
-      if (expand_)
-      {
-        for (size_t i = 0; i < response.GetSize(); i++)
-        {
-          Json::Value item;
-          Expand(item, response.GetResource(i), context.GetIndex());
-          target.append(item);
-        }
-      }
-      else
-      {
-        for (size_t i = 0; i < response.GetSize(); i++)
-        {
-          target.append(response.GetResource(i).GetIdentifier());
-        }
-      }
-    }
-  };
-
-
   template <enum ResourceType resourceType>
   static void ListResources(RestApiGetCall& call)
   {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancServer/Sources/ResourceFinder.cpp	Wed May 08 13:37:23 2024 +0200
@@ -0,0 +1,381 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2024 Osimis S.A., Belgium
+ * Copyright (C) 2021-2024 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "PrecompiledHeadersServer.h"
+#include "ResourceFinder.h"
+
+#include "../../OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.h"
+#include "../../OrthancFramework/Sources/OrthancException.h"
+#include "../../OrthancFramework/Sources/SerializationToolbox.h"
+#include "ServerContext.h"
+#include "ServerIndex.h"
+
+
+namespace Orthanc
+{
+  SeriesStatus ResourceFinder::GetSeriesStatus(uint32_t& expectedNumberOfInstances,
+                                               const FindResponse::Resource& resource) const
+  {
+    if (request_.GetLevel() != ResourceType_Series)
+    {
+      throw OrthancException(ErrorCode_BadParameterType);
+    }
+
+    std::string s;
+    if (!resource.LookupMetadata(s, ResourceType_Series, MetadataType_Series_ExpectedNumberOfInstances) ||
+        !SerializationToolbox::ParseUnsignedInteger32(expectedNumberOfInstances, s))
+    {
+      return SeriesStatus_Unknown;
+    }
+
+    std::list<std::string> values;
+    if (!resource.LookupChildrenMetadata(values, MetadataType_Instance_IndexInSeries))
+    {
+      throw OrthancException(ErrorCode_BadSequenceOfCalls);
+    }
+
+    std::set<int64_t> instances;
+
+    for (std::list<std::string>::const_iterator
+           it = values.begin(); it != values.end(); ++it)
+    {
+      int64_t index;
+
+      if (!SerializationToolbox::ParseInteger64(index, *it))
+      {
+        return SeriesStatus_Unknown;
+      }
+
+      if (index <= 0 ||
+          index > static_cast<int64_t>(expectedNumberOfInstances))
+      {
+        // Out-of-range instance index
+        return SeriesStatus_Inconsistent;
+      }
+
+      if (instances.find(index) != instances.end())
+      {
+        // Twice the same instance index
+        return SeriesStatus_Inconsistent;
+      }
+
+      instances.insert(index);
+    }
+
+    if (instances.size() == static_cast<size_t>(expectedNumberOfInstances))
+    {
+      return SeriesStatus_Complete;
+    }
+    else
+    {
+      return SeriesStatus_Missing;
+    }
+  }
+
+
+  void ResourceFinder::Expand(Json::Value& target,
+                              const FindResponse::Resource& resource,
+                              ServerIndex& index) const
+  {
+    /**
+     * This method closely follows "SerializeExpandedResource()" in
+     * "ServerContext.cpp" from Orthanc 1.12.3.
+     **/
+
+    if (resource.GetLevel() != request_.GetLevel())
+    {
+      throw OrthancException(ErrorCode_InternalError);
+    }
+
+    if (!requestedTags_.empty())
+    {
+      throw OrthancException(ErrorCode_NotImplemented);
+    }
+
+    target = Json::objectValue;
+
+    target["Type"] = GetResourceTypeText(resource.GetLevel(), false, true);
+    target["ID"] = resource.GetIdentifier();
+
+    switch (resource.GetLevel())
+    {
+      case ResourceType_Patient:
+        break;
+
+      case ResourceType_Study:
+        target["ParentPatient"] = resource.GetParentIdentifier();
+        break;
+
+      case ResourceType_Series:
+        target["ParentStudy"] = resource.GetParentIdentifier();
+        break;
+
+      case ResourceType_Instance:
+        target["ParentSeries"] = resource.GetParentIdentifier();
+        break;
+
+      default:
+        throw OrthancException(ErrorCode_InternalError);
+    }
+
+    if (resource.GetLevel() != ResourceType_Instance)
+    {
+      const std::set<std::string>& children = resource.GetChildrenIdentifiers();
+
+      Json::Value c = Json::arrayValue;
+      for (std::set<std::string>::const_iterator
+             it = children.begin(); it != children.end(); ++it)
+      {
+        c.append(*it);
+      }
+
+      switch (resource.GetLevel())
+      {
+        case ResourceType_Patient:
+          target["Studies"] = c;
+          break;
+
+        case ResourceType_Study:
+          target["Series"] = c;
+          break;
+
+        case ResourceType_Series:
+          target["Instances"] = c;
+          break;
+
+        default:
+          throw OrthancException(ErrorCode_InternalError);
+      }
+    }
+
+    switch (resource.GetLevel())
+    {
+      case ResourceType_Patient:
+      case ResourceType_Study:
+        break;
+
+      case ResourceType_Series:
+      {
+        uint32_t expectedNumberOfInstances;
+        SeriesStatus status = GetSeriesStatus(expectedNumberOfInstances, resource);
+
+        target["Status"] = EnumerationToString(status);
+
+        static const char* EXPECTED_NUMBER_OF_INSTANCES = "ExpectedNumberOfInstances";
+
+        if (status == SeriesStatus_Unknown)
+        {
+          target[EXPECTED_NUMBER_OF_INSTANCES] = Json::nullValue;
+        }
+        else
+        {
+          target[EXPECTED_NUMBER_OF_INSTANCES] = expectedNumberOfInstances;
+        }
+
+        break;
+      }
+
+      case ResourceType_Instance:
+      {
+        FileInfo info;
+        if (resource.LookupAttachment(info, FileContentType_Dicom))
+        {
+          target["FileSize"] = static_cast<Json::UInt64>(info.GetUncompressedSize());
+          target["FileUuid"] = info.GetUuid();
+        }
+        else
+        {
+          throw OrthancException(ErrorCode_InternalError);
+        }
+
+        static const char* INDEX_IN_SERIES = "IndexInSeries";
+
+        std::string s;
+        uint32_t index;
+        if (resource.LookupMetadata(s, ResourceType_Instance, MetadataType_Instance_IndexInSeries) &&
+            SerializationToolbox::ParseUnsignedInteger32(index, s))
+        {
+          target[INDEX_IN_SERIES] = index;
+        }
+        else
+        {
+          target[INDEX_IN_SERIES] = Json::nullValue;
+        }
+
+        break;
+      }
+
+      default:
+        throw OrthancException(ErrorCode_InternalError);
+    }
+
+    std::string s;
+    if (resource.LookupMetadata(s, resource.GetLevel(), MetadataType_AnonymizedFrom))
+    {
+      target["AnonymizedFrom"] = s;
+    }
+
+    if (resource.LookupMetadata(s, resource.GetLevel(), MetadataType_ModifiedFrom))
+    {
+      target["ModifiedFrom"] = s;
+    }
+
+    if (resource.GetLevel() == ResourceType_Patient ||
+        resource.GetLevel() == ResourceType_Study ||
+        resource.GetLevel() == ResourceType_Series)
+    {
+      target["IsStable"] = !index.IsUnstableResource(resource.GetLevel(), resource.GetInternalId());
+
+      if (resource.LookupMetadata(s, resource.GetLevel(), MetadataType_LastUpdate))
+      {
+        target["LastUpdate"] = s;
+      }
+    }
+
+    {
+      static const char* const MAIN_DICOM_TAGS = "MainDicomTags";
+      static const char* const PATIENT_MAIN_DICOM_TAGS = "PatientMainDicomTags";
+
+      // TODO-FIND : (expandFlags & ExpandResourceFlags_IncludeMainDicomTags)
+      DicomMap allMainDicomTags;
+      resource.GetMainDicomTags(allMainDicomTags, resource.GetLevel());
+
+      DicomMap levelMainDicomTags;
+      allMainDicomTags.ExtractResourceInformation(levelMainDicomTags, resource.GetLevel());
+
+      target[MAIN_DICOM_TAGS] = Json::objectValue;
+      FromDcmtkBridge::ToJson(target[MAIN_DICOM_TAGS], levelMainDicomTags, format_);
+
+      if (resource.GetLevel() == ResourceType_Study)
+      {
+        DicomMap patientMainDicomTags;
+        allMainDicomTags.ExtractPatientInformation(patientMainDicomTags);
+
+        target[PATIENT_MAIN_DICOM_TAGS] = Json::objectValue;
+        FromDcmtkBridge::ToJson(target[PATIENT_MAIN_DICOM_TAGS], patientMainDicomTags, format_);
+      }
+
+      /*
+        TODO-FIND
+
+        if (!requestedTags_.empty())
+        {
+        static const char* const REQUESTED_TAGS = "RequestedTags";
+
+        DicomMap tags;
+        resource.GetMainDicomTags().ExtractTags(tags, requestedTags);
+
+        target[REQUESTED_TAGS] = Json::objectValue;
+        FromDcmtkBridge::ToJson(target[REQUESTED_TAGS], tags, format);
+        }
+      */
+    }
+
+    {
+      Json::Value labels = Json::arrayValue;
+
+      for (std::set<std::string>::const_iterator
+             it = resource.GetLabels().begin(); it != resource.GetLabels().end(); ++it)
+      {
+        labels.append(*it);
+      }
+
+      target["Labels"] = labels;
+    }
+
+    if (includeAllMetadata_)  // new in Orthanc 1.12.4
+    {
+      const std::map<MetadataType, std::string>& m = resource.GetMetadata(resource.GetLevel());
+
+      Json::Value metadata = Json::objectValue;
+
+      for (std::map<MetadataType, std::string>::const_iterator it = m.begin(); it != m.end(); ++it)
+      {
+        metadata[EnumerationToString(it->first)] = it->second;
+      }
+
+      target["Metadata"] = metadata;
+    }
+  }
+
+
+  ResourceFinder::ResourceFinder(ResourceType level,
+                                 bool expand) :
+    request_(level),
+    expand_(expand),
+    format_(DicomToJsonFormat_Human),
+    includeAllMetadata_(false)
+  {
+    if (expand)
+    {
+      request_.SetRetrieveMainDicomTags(level, true);
+      request_.SetRetrieveMetadata(level, true);
+      request_.SetRetrieveLabels(true);
+
+      if (level == ResourceType_Series)
+      {
+        request_.AddRetrieveChildrenMetadata(MetadataType_Instance_IndexInSeries); // required for the SeriesStatus
+      }
+
+      if (level == ResourceType_Instance)
+      {
+        request_.SetRetrieveAttachments(true); // for FileSize & FileUuid
+      }
+      else
+      {
+        request_.SetRetrieveChildrenIdentifiers(true);
+      }
+
+      if (level != ResourceType_Patient)
+      {
+        request_.SetRetrieveParentIdentifier(true);
+      }
+    }
+  }
+
+
+  void ResourceFinder::Execute(Json::Value& target,
+                               ServerContext& context)
+  {
+    FindResponse response;
+    context.GetIndex().ExecuteFind(response, request_);
+
+    target = Json::arrayValue;
+
+    if (expand_)
+    {
+      for (size_t i = 0; i < response.GetSize(); i++)
+      {
+        Json::Value item;
+        Expand(item, response.GetResource(i), context.GetIndex());
+        target.append(item);
+      }
+    }
+    else
+    {
+      for (size_t i = 0; i < response.GetSize(); i++)
+      {
+        target.append(response.GetResource(i).GetIdentifier());
+      }
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancServer/Sources/ResourceFinder.h	Wed May 08 13:37:23 2024 +0200
@@ -0,0 +1,77 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2024 Osimis S.A., Belgium
+ * Copyright (C) 2021-2024 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "Database/FindRequest.h"
+#include "Database/FindResponse.h"
+
+namespace Orthanc
+{
+  class ServerContext;
+  class ServerIndex;
+
+  class ResourceFinder : public boost::noncopyable
+  {
+  private:
+    FindRequest        request_;
+    bool               expand_;
+    std::set<DicomTag> requestedTags_;
+    DicomToJsonFormat  format_;
+    bool               includeAllMetadata_;   // Same as: ExpandResourceFlags_IncludeAllMetadata
+
+    SeriesStatus GetSeriesStatus(uint32_t& expectedNumberOfInstances,
+                                 const FindResponse::Resource& resource) const;
+
+    void Expand(Json::Value& target,
+                const FindResponse::Resource& resource,
+                ServerIndex& index) const;
+
+  public:
+    ResourceFinder(ResourceType level,
+                   bool expand);
+
+    void SetRequestedTags(const std::set<DicomTag>& tags)
+    {
+      requestedTags_ = tags;
+    }
+
+    void SetFormat(DicomToJsonFormat format)
+    {
+      format_ = format;
+    }
+
+    void SetLimits(uint64_t since,
+                   uint64_t count)
+    {
+      request_.SetLimits(since, count);
+    }
+
+    void SetIncludeAllMetadata(bool include)
+    {
+      includeAllMetadata_ = include;
+    }
+
+    void Execute(Json::Value& target,
+                 ServerContext& context);
+  };
+}