changeset 1759:e268412adcf1 db-changes

removal of old implementation of search
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 28 Oct 2015 12:59:18 +0100
parents 318c2e83c2bd
children 51db4a25a741
files CMakeLists.txt OrthancServer/DicomFindQuery.cpp OrthancServer/DicomFindQuery.h OrthancServer/OrthancFindRequestHandler.cpp OrthancServer/OrthancRestApi/OrthancRestResources.cpp OrthancServer/ResourceFinder.cpp OrthancServer/ResourceFinder.h
diffstat 7 files changed, 8 insertions(+), 1202 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Wed Oct 28 12:53:45 2015 +0100
+++ b/CMakeLists.txt	Wed Oct 28 12:59:18 2015 +0100
@@ -150,7 +150,6 @@
   OrthancServer/DatabaseWrapper.cpp
   OrthancServer/DatabaseWrapperBase.cpp
   OrthancServer/DicomDirWriter.cpp
-  OrthancServer/DicomFindQuery.cpp
   OrthancServer/DicomModification.cpp
   OrthancServer/DicomProtocol/DicomFindAnswers.cpp
   OrthancServer/DicomProtocol/DicomServer.cpp
@@ -179,7 +178,6 @@
   OrthancServer/OrthancRestApi/OrthancRestSystem.cpp
   OrthancServer/ParsedDicomFile.cpp
   OrthancServer/QueryRetrieveHandler.cpp
-  OrthancServer/ResourceFinder.cpp
   OrthancServer/Search/LookupIdentifierQuery.cpp
   OrthancServer/Search/LookupResource.cpp
   OrthancServer/Search/SetOfResources.cpp
--- a/OrthancServer/DicomFindQuery.cpp	Wed Oct 28 12:53:45 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,395 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, 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.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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 "DicomFindQuery.h"
-
-#include "FromDcmtkBridge.h"
-
-#include <boost/regex.hpp> 
-
-
-namespace Orthanc
-{
-  class DicomFindQuery::ValueConstraint : public DicomFindQuery::IConstraint
-  {
-  private:
-    bool          isCaseSensitive_;
-    std::string   expected_;
-
-  public:
-    ValueConstraint(const std::string& value,
-                    bool caseSensitive) :
-      isCaseSensitive_(caseSensitive),
-      expected_(value)
-    {
-    }
-
-    const std::string& GetValue() const
-    {
-      return expected_;
-    }
-
-    virtual bool IsExactConstraint() const
-    {
-      return isCaseSensitive_;
-    }
-
-    virtual bool Apply(const std::string& value) const
-    {
-      if (isCaseSensitive_)
-      {
-        return expected_ == value;
-      }
-      else
-      {
-        std::string v, c;
-        Toolbox::ToLowerCase(v, value);
-        Toolbox::ToLowerCase(c, expected_);
-        return v == c;
-      }
-    }
-  };
-
-
-  class DicomFindQuery::ListConstraint : public DicomFindQuery::IConstraint
-  {
-  private:
-    std::set<std::string>  values_;
-
-  public:
-    ListConstraint(const std::string& values)
-    {
-      std::vector<std::string> items;
-      Toolbox::TokenizeString(items, values, '\\');
-
-      for (size_t i = 0; i < items.size(); i++)
-      {
-        std::string lower;
-        Toolbox::ToLowerCase(lower, items[i]);
-        values_.insert(lower);
-      }
-    }
-
-    virtual bool Apply(const std::string& value) const
-    {
-      std::string tmp;
-      Toolbox::ToLowerCase(tmp, value);
-      return values_.find(tmp) != values_.end();
-    }
-  };
-
-
-  class DicomFindQuery::RangeConstraint : public DicomFindQuery::IConstraint
-  {
-  private:
-    std::string lower_;
-    std::string upper_;
-
-  public:
-    RangeConstraint(const std::string& range)
-    {
-      size_t separator = range.find('-');
-      Toolbox::ToLowerCase(lower_, range.substr(0, separator));
-      Toolbox::ToLowerCase(upper_, range.substr(separator + 1));
-    }
-
-    virtual bool Apply(const std::string& value) const
-    {
-      std::string v;
-      Toolbox::ToLowerCase(v, value);
-
-      if (lower_.size() == 0 && 
-          upper_.size() == 0)
-      {
-        return false;
-      }
-
-      if (lower_.size() == 0)
-      {
-        return v <= upper_;
-      }
-
-      if (upper_.size() == 0)
-      {
-        return v >= lower_;
-      }
-    
-      return (v >= lower_ && v <= upper_);
-    }
-  };
-
-
-  class DicomFindQuery::WildcardConstraint : public DicomFindQuery::IConstraint
-  {
-  private:
-    boost::regex pattern_;
-
-  public:
-    WildcardConstraint(const std::string& wildcard,
-                       bool caseSensitive)
-    {
-      std::string re = Toolbox::WildcardToRegularExpression(wildcard);
-
-      if (caseSensitive)
-      {
-        pattern_ = boost::regex(re);
-      }
-      else
-      {
-        pattern_ = boost::regex(re, boost::regex::icase /* case insensitive search */);
-      }
-    }
-
-    virtual bool Apply(const std::string& value) const
-    {
-      return boost::regex_match(value, pattern_);
-    }
-  };
-
-
-  void DicomFindQuery::PrepareMainDicomTags(ResourceType level)
-  {
-    std::set<DicomTag> tags;
-    DicomMap::GetMainDicomTags(tags, level);
-
-    for (std::set<DicomTag>::const_iterator
-           it = tags.begin(); it != tags.end(); ++it)
-    {
-      mainDicomTags_[*it] = level;
-    }
-  }
-
-
-  DicomFindQuery::DicomFindQuery() : 
-    level_(ResourceType_Patient),
-    filterJson_(false)
-  {
-    PrepareMainDicomTags(ResourceType_Patient);
-    PrepareMainDicomTags(ResourceType_Study);
-    PrepareMainDicomTags(ResourceType_Series);
-    PrepareMainDicomTags(ResourceType_Instance);
-  }
-
-
-  DicomFindQuery::~DicomFindQuery()
-  {
-    for (Constraints::iterator it = constraints_.begin();
-         it != constraints_.end(); ++it)
-    {
-      delete it->second;
-    }
-  }
-
-
-
-
-  void DicomFindQuery::AssignConstraint(const DicomTag& tag,
-                                        IConstraint* constraint)
-  {
-    Constraints::iterator it = constraints_.find(tag);
-
-    if (it != constraints_.end())
-    {
-      constraints_.erase(it);
-    }
-
-    constraints_[tag] = constraint;
-
-    MainDicomTags::const_iterator tmp = mainDicomTags_.find(tag);
-    if (tmp == mainDicomTags_.end())
-    {
-      // The query depends upon a DICOM tag that is not a main tag
-      // from the point of view of Orthanc, we need to decode the
-      // JSON file on the disk.
-      filterJson_ = true;
-    }
-    else
-    {
-      filteredLevels_.insert(tmp->second);
-    }
-  }
-
-
-  void DicomFindQuery::SetConstraint(const DicomTag& tag,
-                                     const std::string& constraint,
-                                     bool caseSensitivePN)
-  {
-    ValueRepresentation vr = FromDcmtkBridge::GetValueRepresentation(tag);
-
-    bool sensitive = true;
-    if (vr == ValueRepresentation_PatientName)
-    {
-      sensitive = caseSensitivePN;
-    }
-
-    // http://www.itk.org/Wiki/DICOM_QueryRetrieve_Explained
-    // http://dicomiseasy.blogspot.be/2012/01/dicom-queryretrieve-part-i.html  
-
-    if ((vr == ValueRepresentation_Date ||
-         vr == ValueRepresentation_DateTime ||
-         vr == ValueRepresentation_Time) &&
-        constraint.find('-') != std::string::npos)
-    {
-      /**
-       * Range matching is only defined for TM, DA and DT value
-       * representations. This code fixes issues 35 and 37.
-       *
-       * Reference: "Range matching is not defined for types of
-       * Attributes other than dates and times", DICOM PS 3.4,
-       * C.2.2.2.5 ("Range Matching").
-       **/
-      AssignConstraint(tag, new RangeConstraint(constraint));
-    }
-    else if (constraint.find('\\') != std::string::npos)
-    {
-      AssignConstraint(tag, new ListConstraint(constraint));
-    }
-    else if (constraint.find('*') != std::string::npos ||
-             constraint.find('?') != std::string::npos)
-    {
-      AssignConstraint(tag, new WildcardConstraint(constraint, sensitive));
-    }
-    else
-    {
-      /**
-       * Case-insensitive match for PN value representation (Patient
-       * Name). Case-senstive match for all the other value
-       * representations.
-       *
-       * Reference: DICOM PS 3.4
-       *   - C.2.2.2.1 ("Single Value Matching") 
-       *   - C.2.2.2.4 ("Wild Card Matching")
-       * http://medical.nema.org/Dicom/2011/11_04pu.pdf
-       *
-       * "Except for Attributes with a PN Value Representation, only
-       * entities with values which match exactly the value specified in the
-       * request shall match. This matching is case-sensitive, i.e.,
-       * sensitive to the exact encoding of the key attribute value in
-       * character sets where a letter may have multiple encodings (e.g.,
-       * based on its case, its position in a word, or whether it is
-       * accented)
-       * 
-       * For Attributes with a PN Value Representation (e.g., Patient Name
-       * (0010,0010)), an application may perform literal matching that is
-       * either case-sensitive, or that is insensitive to some or all
-       * aspects of case, position, accent, or other character encoding
-       * variants."
-       *
-       * (0008,0018) UI SOPInstanceUID     => Case-sensitive
-       * (0008,0050) SH AccessionNumber    => Case-sensitive
-       * (0010,0020) LO PatientID          => Case-sensitive
-       * (0020,000D) UI StudyInstanceUID   => Case-sensitive
-       * (0020,000E) UI SeriesInstanceUID  => Case-sensitive
-      **/
-
-      AssignConstraint(tag, new ValueConstraint(constraint, sensitive));
-    }
-  }
-
-
-  bool DicomFindQuery::RestrictIdentifier(std::string& value,
-                                          DicomTag identifier) const
-  {
-    Constraints::const_iterator it = constraints_.find(identifier);
-    if (it == constraints_.end() ||
-        !it->second->IsExactConstraint())
-    {
-      return false;
-    }
-    else
-    {
-      value = dynamic_cast<ValueConstraint*>(it->second)->GetValue();
-      return true;
-    }
-  }
-
-  bool DicomFindQuery::HasMainDicomTagsFilter(ResourceType level) const
-  {
-    return filteredLevels_.find(level) != filteredLevels_.end();
-  }
-
-  bool DicomFindQuery::FilterMainDicomTags(const std::string& resourceId,
-                                           ResourceType level,
-                                           const DicomMap& mainTags) const
-  {
-    std::set<DicomTag> tags;
-    mainTags.GetTags(tags);
-
-    for (std::set<DicomTag>::const_iterator
-           it = tags.begin(); it != tags.end(); ++it)
-    {
-      const DicomValue& value = mainTags.GetValue(*it);
-      if (value.IsBinary() || value.IsNull())
-      {
-        return false;
-      }
-
-      Constraints::const_iterator constraint = constraints_.find(*it);
-      if (constraint != constraints_.end() &&
-          !constraint->second->Apply(value.GetContent()))
-      {
-        return false;
-      }
-    }
-
-    return true;
-  }
-
-  bool DicomFindQuery::HasInstanceFilter() const
-  {
-    return filterJson_;
-  }
-
-  bool DicomFindQuery::FilterInstance(const std::string& instanceId,
-                                      const Json::Value& content) const
-  {
-    for (Constraints::const_iterator it = constraints_.begin();
-         it != constraints_.end(); ++it)
-    {
-      std::string tag = it->first.Format();
-      std::string value;
-      if (content.isMember(tag))
-      {
-        value = content.get(tag, Json::arrayValue).get("Value", "").asString();
-      }
-
-      if (!it->second->Apply(value))
-      {
-        return false;
-      }
-    }
-
-    return true;
-  }
-}
--- a/OrthancServer/DicomFindQuery.h	Wed Oct 28 12:53:45 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,111 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, 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.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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 "ResourceFinder.h"
-
-namespace Orthanc
-{
-  class DicomFindQuery : public ResourceFinder::IQuery
-  {
-  private:
-    class  IConstraint : public boost::noncopyable
-    {
-    public:
-      virtual ~IConstraint()
-      {
-      }
-
-      virtual bool IsExactConstraint() const
-      {
-        return false;
-      }
-
-      virtual bool Apply(const std::string& value) const = 0;
-    };
-
-
-    class ValueConstraint;
-    class RangeConstraint;
-    class ListConstraint;
-    class WildcardConstraint;
-
-    typedef std::map<DicomTag, IConstraint*>  Constraints;
-    typedef std::map<DicomTag, ResourceType>  MainDicomTags;
-
-    MainDicomTags           mainDicomTags_;
-    ResourceType            level_;
-    bool                    filterJson_;
-    Constraints             constraints_;
-    std::set<ResourceType>  filteredLevels_;
-
-    void AssignConstraint(const DicomTag& tag,
-                          IConstraint* constraint);
-
-    void PrepareMainDicomTags(ResourceType level);
-
-
-  public:
-    DicomFindQuery();
-
-    virtual ~DicomFindQuery();
-
-    void SetLevel(ResourceType level)
-    {
-      level_ = level;
-    }
-
-    virtual ResourceType GetLevel() const
-    {
-      return level_;
-    }
-
-    void SetConstraint(const DicomTag& tag,
-                       const std::string& constraint,
-                       bool caseSensitivePN);
-
-    virtual bool RestrictIdentifier(std::string& value,
-                                    DicomTag identifier) const;
-
-    virtual bool HasMainDicomTagsFilter(ResourceType level) const;
-
-    virtual bool FilterMainDicomTags(const std::string& resourceId,
-                                     ResourceType level,
-                                     const DicomMap& mainTags) const;
-
-    virtual bool HasInstanceFilter() const;
-
-    virtual bool FilterInstance(const std::string& instanceId,
-                                const Json::Value& content) const;
-  };
-}
--- a/OrthancServer/OrthancFindRequestHandler.cpp	Wed Oct 28 12:53:45 2015 +0100
+++ b/OrthancServer/OrthancFindRequestHandler.cpp	Wed Oct 28 12:59:18 2015 +0100
@@ -30,22 +30,15 @@
  **/
 
 
-#define USE_LOOKUP_RESOURCE 1
-
-
-
 #include "PrecompiledHeadersServer.h"
 #include "OrthancFindRequestHandler.h"
 
+#include "../Core/DicomFormat/DicomArray.h"
 #include "../Core/Logging.h"
-#include "../Core/DicomFormat/DicomArray.h"
-#include "ServerToolbox.h"
-#include "OrthancInitialization.h"
 #include "FromDcmtkBridge.h"
-
-#include "ResourceFinder.h"
-#include "DicomFindQuery.h"
+#include "OrthancInitialization.h"
 #include "Search/LookupResource.h"
+#include "ServerToolbox.h"
 
 #include <boost/regex.hpp> 
 
@@ -95,130 +88,6 @@
   }
 
 
-  namespace
-  {
-    class CFindQuery : public DicomFindQuery
-    {
-    private:
-      DicomFindAnswers&      answers_;
-      ServerIndex&           index_;
-      const DicomArray&      query_;
-      bool                   hasModalitiesInStudy_;
-      std::set<std::string>  modalitiesInStudy_;
-
-    public:
-      CFindQuery(DicomFindAnswers& answers,
-                 ServerIndex& index,
-                 const DicomArray& query) :
-        answers_(answers),
-        index_(index),
-        query_(query),
-        hasModalitiesInStudy_(false)
-      {
-      }
-
-      void SetModalitiesInStudy(const std::string& value)
-      {
-        hasModalitiesInStudy_ = true;
-        
-        std::vector<std::string>  tmp;
-        Toolbox::TokenizeString(tmp, value, '\\'); 
-
-        for (size_t i = 0; i < tmp.size(); i++)
-        {
-          modalitiesInStudy_.insert(tmp[i]);
-        }
-      }
-
-      virtual bool HasMainDicomTagsFilter(ResourceType level) const
-      {
-        if (DicomFindQuery::HasMainDicomTagsFilter(level))
-        {
-          return true;
-        }
-
-        return (level == ResourceType_Study &&
-                hasModalitiesInStudy_);
-      }
-
-      virtual bool FilterMainDicomTags(const std::string& resourceId,
-                                       ResourceType level,
-                                       const DicomMap& mainTags) const
-      {
-        if (!DicomFindQuery::FilterMainDicomTags(resourceId, level, mainTags))
-        {
-          return false;
-        }
-
-        if (level != ResourceType_Study ||
-            !hasModalitiesInStudy_)
-        {
-          return true;
-        }
-
-        try
-        {
-          // We are considering a single study, and the
-          // "MODALITIES_IN_STUDY" tag is set in the C-Find. Check
-          // whether one of its child series matches one of the
-          // modalities.
-
-          Json::Value study;
-          if (index_.LookupResource(study, resourceId, ResourceType_Study))
-          {
-            // Loop over the series of the considered study.
-            for (Json::Value::ArrayIndex j = 0; j < study["Series"].size(); j++)
-            {
-              Json::Value series;
-              if (index_.LookupResource(series, study["Series"][j].asString(), ResourceType_Series))
-              {
-                // Get the modality of this series
-                if (series["MainDicomTags"].isMember("Modality"))
-                {
-                  std::string modality = series["MainDicomTags"]["Modality"].asString();
-                  if (modalitiesInStudy_.find(modality) != modalitiesInStudy_.end())
-                  {
-                    // This series of the considered study matches one
-                    // of the required modalities. Take the study into
-                    // consideration for future filtering.
-                    return true;
-                  }
-                }
-              }
-            }
-          }
-        }
-        catch (OrthancException&)
-        {
-          // This resource has probably been deleted during the find request
-        }
-
-        return false;
-      }
-
-      virtual bool HasInstanceFilter() const
-      {
-        return true;
-      }
-
-      virtual bool FilterInstance(const std::string& instanceId,
-                                  const Json::Value& content) const
-      {
-        bool ok = DicomFindQuery::FilterInstance(instanceId, content);
-
-        if (ok)
-        {
-          // Add this resource to the answers
-          AddAnswer(answers_, content, query_);
-        }
-
-        return ok;
-      }
-    };
-  }
-
-
-
   bool OrthancFindRequestHandler::Handle(DicomFindAnswers& answers,
                                          const DicomMap& input,
                                          const std::string& remoteIp,
@@ -281,13 +150,8 @@
      * Build up the query object.
      **/
 
-#if USE_LOOKUP_RESOURCE == 1
     LookupResource finder(level);
-#else
-    CFindQuery findQuery(answers, context_.GetIndex(), query);
-    findQuery.SetLevel(level);
-#endif     
-   
+
     for (size_t i = 0; i < query.GetSize(); i++)
     {
       const DicomTag tag = query.GetElement(i).GetTag();
@@ -306,28 +170,17 @@
         continue;
       }
 
-#if USE_LOOKUP_RESOURCE == 1
-
       ValueRepresentation vr = FromDcmtkBridge::GetValueRepresentation(tag);
 
+      // DICOM specifies that searches must be case sensitive, except
+      // for tags with a PN value representation
       bool sensitive = true;
       if (vr == ValueRepresentation_PatientName)
       {
-      sensitive = caseSensitivePN;
-    }
+        sensitive = caseSensitivePN;
+      }
 
       finder.AddDicomConstraint(tag, value, sensitive);
-#else
-
-      if (tag == DICOM_TAG_MODALITIES_IN_STUDY)
-      {
-        findQuery.SetModalitiesInStudy(value);
-      }
-      else
-      {
-        findQuery.SetConstraint(tag, value, caseSensitivePN);
-      }
-#endif
     }
 
 
@@ -335,30 +188,8 @@
      * Run the query.
      **/
 
-#if USE_LOOKUP_RESOURCE != 1
-    ResourceFinder finder(context_);
-
-    switch (level)
-    {
-      case ResourceType_Patient:
-      case ResourceType_Study:
-      case ResourceType_Series:
-        finder.SetMaxResults(maxResults_);
-        break;
-
-      case ResourceType_Instance:
-        finder.SetMaxResults(maxInstances_);
-        break;
-
-      default:
-        throw OrthancException(ErrorCode_InternalError);
-    }
-#else
     size_t maxResults = (level == ResourceType_Instance) ? maxInstances_ : maxResults_;
 
-#endif
-
-#if USE_LOOKUP_RESOURCE == 1
     std::vector<std::string> resources, instances;
     context_.GetIndex().FindCandidates(resources, instances, finder);
 
@@ -385,11 +216,6 @@
       }
     }
 
-#else
-    std::list<std::string> tmp;
-    bool finished = finder.Apply(tmp, findQuery);
-#endif
-
     LOG(INFO) << "Number of matching resources: " << answers.GetSize();
 
     return finished;
--- a/OrthancServer/OrthancRestApi/OrthancRestResources.cpp	Wed Oct 28 12:53:45 2015 +0100
+++ b/OrthancServer/OrthancRestApi/OrthancRestResources.cpp	Wed Oct 28 12:59:18 2015 +0100
@@ -36,8 +36,6 @@
 #include "../../Core/Logging.h"
 #include "../ServerToolbox.h"
 #include "../FromDcmtkBridge.h"
-#include "../ResourceFinder.h"
-#include "../DicomFindQuery.h"
 #include "../ServerContext.h"
 #include "../SliceOrdering.h"
 
--- a/OrthancServer/ResourceFinder.cpp	Wed Oct 28 12:53:45 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,409 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, 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.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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 "../Core/Logging.h"
-#include "FromDcmtkBridge.h"
-#include "ServerContext.h"
-
-namespace Orthanc
-{
-  class ResourceFinder::CandidateResources
-  {
-  private:
-    typedef std::map<DicomTag, std::string>  Query;
-
-    ResourceFinder&        finder_;
-    ServerIndex&           index_;
-    ResourceType           level_;
-    bool                   isFilterApplied_;
-    std::set<std::string>  filtered_;
-
-     
-    static void ListToSet(std::set<std::string>& target,
-                          const std::list<std::string>& source)
-    {
-      for (std::list<std::string>::const_iterator
-             it = source.begin(); it != source.end(); ++it)
-      {
-        target.insert(*it);
-      }
-    }
-
-
-  public:
-    CandidateResources(ResourceFinder& finder) : 
-      finder_(finder),
-      index_(finder.context_.GetIndex()),
-      level_(ResourceType_Patient), 
-      isFilterApplied_(false)
-    {
-    }
-
-    ResourceType GetLevel() const
-    {
-      return level_;
-    }
-
-    void GoDown()
-    {
-      assert(level_ != ResourceType_Instance);
-
-      if (isFilterApplied_)
-      {
-        std::set<std::string> tmp = filtered_;
-
-        filtered_.clear();
-
-        for (std::set<std::string>::const_iterator 
-               it = tmp.begin(); it != tmp.end(); ++it)
-        {
-          std::list<std::string> children;
-          try
-          {
-            index_.GetChildren(children, *it);
-            ListToSet(filtered_, children);
-          }
-          catch (OrthancException&)
-          {
-            // The resource was removed in the meantime
-          }
-        }
-      }
-
-      switch (level_)
-      {
-        case ResourceType_Patient:
-          level_ = ResourceType_Study;
-          break;
-
-        case ResourceType_Study:
-          level_ = ResourceType_Series;
-          break;
-
-        case ResourceType_Series:
-          level_ = ResourceType_Instance;
-          break;
-
-        default:
-          throw OrthancException(ErrorCode_InternalError);
-      }
-    }
-
-
-    void Flatten(std::list<std::string>& resources) const
-    {
-      resources.clear();
-
-      if (isFilterApplied_)
-      {
-        for (std::set<std::string>::const_iterator 
-               it = filtered_.begin(); it != filtered_.end(); ++it)
-        {
-          resources.push_back(*it);
-        }
-      }
-      else
-      {
-        index_.GetAllUuids(resources, level_);
-      }
-    }
-
-    
-    void RestrictIdentifier(const IQuery& query,
-                            const DicomTag& tag)
-    {
-      assert((level_ == ResourceType_Patient && tag == DICOM_TAG_PATIENT_ID) ||
-             (level_ == ResourceType_Study && tag == DICOM_TAG_STUDY_INSTANCE_UID) ||
-             (level_ == ResourceType_Study && tag == DICOM_TAG_ACCESSION_NUMBER) ||
-             (level_ == ResourceType_Series && tag == DICOM_TAG_SERIES_INSTANCE_UID) ||
-             (level_ == ResourceType_Instance && tag == DICOM_TAG_SOP_INSTANCE_UID));
-
-      std::string value;
-      if (!query.RestrictIdentifier(value, tag))
-      {
-        return;
-      }
-
-      LOG(INFO) << "Lookup for identifier tag "
-                << FromDcmtkBridge::GetName(tag) << " (value: " << value << ")";
-
-      std::list<std::string> resources;
-      index_.LookupIdentifierExact(resources, level_, tag, value);
-
-      if (isFilterApplied_)
-      {
-        std::set<std::string>  s;
-        ListToSet(s, resources);
-
-        std::set<std::string> tmp = filtered_;
-        filtered_.clear();
-
-        for (std::set<std::string>::const_iterator 
-               it = tmp.begin(); it != tmp.end(); ++it)
-        {
-          if (s.find(*it) != s.end())
-          {
-            filtered_.insert(*it);
-          }
-        }
-      }
-      else
-      {
-        assert(filtered_.empty());
-        isFilterApplied_ = true;
-        ListToSet(filtered_, resources);
-      }
-    }
-
-
-    void RestrictMainDicomTags(const IQuery& query,
-                               bool filterPatientTagsAtStudyLevel)
-    {
-      if (filterPatientTagsAtStudyLevel &&
-          level_ == ResourceType_Patient)
-      {
-        return;
-      }
-
-      bool hasTagsAtThisLevel = query.HasMainDicomTagsFilter(level_);
-      bool hasTagsAtPatientLevel = (filterPatientTagsAtStudyLevel &&
-                                    level_ == ResourceType_Study &&
-                                    query.HasMainDicomTagsFilter(ResourceType_Patient));
-
-      if (!hasTagsAtThisLevel && !hasTagsAtPatientLevel)        
-      {
-        return;
-      }
-
-      std::list<std::string> resources;
-      Flatten(resources);
-
-      isFilterApplied_ = true;
-      filtered_.clear();
-
-      for (std::list<std::string>::const_iterator
-             it = resources.begin(); it != resources.end(); ++it)
-      {
-        DicomMap mainTags;
-
-        if (hasTagsAtThisLevel &&
-            (!index_.GetMainDicomTags(mainTags, *it, level_, level_) ||
-             !query.FilterMainDicomTags(*it, level_, mainTags)))
-        {
-          continue;
-        }
-
-        if (hasTagsAtPatientLevel &&
-            (!index_.GetMainDicomTags(mainTags, *it, ResourceType_Study, ResourceType_Patient) ||
-             !query.FilterMainDicomTags(*it, ResourceType_Patient, mainTags)))
-        {
-          continue;
-        }
-
-        filtered_.insert(*it);
-      }
-    }
-  };
-
-
-  ResourceFinder::ResourceFinder(ServerContext& context) : 
-    context_(context),
-    maxResults_(0)
-  {
-  }
-
-
-  void ResourceFinder::ApplyAtLevel(CandidateResources& candidates,
-                                    const IQuery& query,
-                                    ResourceType level)
-  {
-    if (level != ResourceType_Patient)
-    {
-      candidates.GoDown();
-    }
-
-    switch (level)
-    {
-      case ResourceType_Patient:
-      {
-        candidates.RestrictIdentifier(query, DICOM_TAG_PATIENT_ID);
-        break;
-      }
-
-      case ResourceType_Study:
-      {
-        candidates.RestrictIdentifier(query, DICOM_TAG_STUDY_INSTANCE_UID);
-        candidates.RestrictIdentifier(query, DICOM_TAG_ACCESSION_NUMBER);
-        break;
-      }
-
-      case ResourceType_Series:
-      {
-        candidates.RestrictIdentifier(query, DICOM_TAG_SERIES_INSTANCE_UID);
-        break;
-      }
-
-      case ResourceType_Instance:
-      {
-        candidates.RestrictIdentifier(query, DICOM_TAG_SOP_INSTANCE_UID);
-        break;
-      }
-
-      default:
-        throw OrthancException(ErrorCode_InternalError);
-    }
-
-    if (query.GetLevel() == ResourceType_Patient)
-    {
-      candidates.RestrictMainDicomTags(query, false);
-    }
-    else
-    {
-      candidates.RestrictMainDicomTags(query, true);
-    }
-  }
-
-
-
-  static bool LookupOneInstance(std::string& result,
-                                ServerIndex& index,
-                                const std::string& id,
-                                ResourceType type)
-  {
-    if (type == ResourceType_Instance)
-    {
-      result = id;
-      return true;
-    }
-
-    std::string childId;
-    
-    {
-      std::list<std::string> children;
-      index.GetChildInstances(children, id);
-
-      if (children.empty())
-      {
-        return false;
-      }
-
-      childId = children.front();
-    }
-
-    return LookupOneInstance(result, index, childId, GetChildResourceType(type));
-  }
-
-
-  bool ResourceFinder::Apply(std::list<std::string>& result,
-                             const IQuery& query)
-  {
-    CandidateResources candidates(*this);
-
-    ApplyAtLevel(candidates, query, ResourceType_Patient);
-
-    const ResourceType level = query.GetLevel();
-
-    if (level == ResourceType_Study ||
-        level == ResourceType_Series ||
-        level == ResourceType_Instance)
-    {
-      ApplyAtLevel(candidates, query, ResourceType_Study);
-    }
-        
-    if (level == ResourceType_Series ||
-        level == ResourceType_Instance)
-    {
-      ApplyAtLevel(candidates, query, ResourceType_Series);
-    }
-        
-    if (level == ResourceType_Instance)
-    {
-      ApplyAtLevel(candidates, query, ResourceType_Instance);
-    }
-
-    if (!query.HasInstanceFilter())
-    {
-      candidates.Flatten(result);
-
-      if (maxResults_ != 0 &&
-          result.size() >= maxResults_)
-      {
-        result.resize(maxResults_);
-        return false;
-      }
-      else
-      {
-        return true;
-      }
-    }
-    else
-    {
-      std::list<std::string> tmp;
-      candidates.Flatten(tmp);
-      
-      result.clear();
-      for (std::list<std::string>::const_iterator 
-             resource = tmp.begin(); resource != tmp.end(); ++resource)
-      {
-        try
-        {
-          std::string instance;
-          if (LookupOneInstance(instance, context_.GetIndex(), *resource, level))
-          {
-            Json::Value content;
-            context_.ReadJson(content, instance);
-            if (query.FilterInstance(*resource, content))
-            {
-              result.push_back(*resource);
-
-              if (maxResults_ != 0 &&
-                  result.size() >= maxResults_)
-              {
-                // Too many results, stop before recording this new match
-                return false;
-              }
-            }
-          }
-        }
-        catch (OrthancException&)
-        {
-          // This resource has been deleted since the search was started
-        }
-      }      
-    }
-
-    return true;  // All the matching resources have been returned
-  }
-}
--- a/OrthancServer/ResourceFinder.h	Wed Oct 28 12:53:45 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,101 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, 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.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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 "ServerIndex.h"
-
-#include <boost/noncopyable.hpp>
-
-namespace Orthanc
-{
-  class ResourceFinder : public boost::noncopyable
-  {
-  public:
-    class IQuery : public boost::noncopyable
-    {
-    public:
-      virtual ~IQuery()
-      {
-      }
-
-      virtual ResourceType GetLevel() const = 0;
-
-      virtual bool RestrictIdentifier(std::string& value,
-                                      DicomTag identifier) const = 0;
-
-      virtual bool HasMainDicomTagsFilter(ResourceType level) const = 0;
-
-      virtual bool FilterMainDicomTags(const std::string& resourceId,
-                                       ResourceType level,
-                                       const DicomMap& mainTags) const = 0;
-
-      virtual bool HasInstanceFilter() const = 0;
-
-      virtual bool FilterInstance(const std::string& instanceId,
-                                  const Json::Value& content) const = 0;
-    };
-
-
-  private:
-    typedef std::map<DicomTag, std::string>  Identifiers;
-
-    class CandidateResources;
-
-    ServerContext&    context_;
-    size_t            maxResults_;
-
-    void ApplyAtLevel(CandidateResources& candidates,
-                      const IQuery& query,
-                      ResourceType level);
-
-  public:
-    ResourceFinder(ServerContext& context);
-
-    void SetMaxResults(size_t value)
-    {
-      maxResults_ = value;
-    }
-
-    size_t GetMaxResults() const
-    {
-      return maxResults_;
-    }
-
-    // Returns "true" iff. all the matching resources have been
-    // returned. Will be "false" if the results were truncated by
-    // "SetMaxResults()".
-    bool Apply(std::list<std::string>& result,
-               const IQuery& query);
-  };
-
-}