LCOV - code coverage report
Current view: top level - jdx - Block.cpp (source / functions) Hit Total Coverage
Test: coverage_filtered.info Lines: 116 116 100.0 %
Date: 2025-06-15 16:18:27 Functions: 25 25 100.0 %

          Line data    Source code
       1             : /* Copyright (C) 2025 Robert Schiwon
       2             :  *
       3             :  * This file is part of libjdx.
       4             :  *
       5             :  * libjdx is free software: you can redistribute it and/or modify it under the
       6             :  * terms of the GNU Lesser General Public License as published by the Free
       7             :  * Software Foundation, either version 3 of the License, or (at your option) any
       8             :  * later version.
       9             :  *
      10             :  * libjdx is distributed in the hope that it will be useful, but WITHOUT ANY
      11             :  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      12             :  * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
      13             :  * details.
      14             :  *
      15             :  * You should have received a copy of the GNU Lesser General Public License
      16             :  * along with libjdx. If not, see <https://www.gnu.org/licenses/>.
      17             :  */
      18             : #include "jdx/Block.hpp"
      19             : #include "util/LdrUtils.hpp"
      20             : #include "util/StringUtils.hpp"
      21             : 
      22          25 : libjdx::jdx::Block::Block(std::unique_ptr<io::TextReader> readerPtr)
      23          25 :     : m_readerPtr{std::move(readerPtr)}
      24          64 :     , m_reader{*m_readerPtr}
      25             : {
      26          50 :     auto firstLine = m_reader.readLine();
      27          47 :     auto titleFirstLine = parseFirstLine(firstLine);
      28          22 :     std::optional<std::string> nextLine;
      29          22 :     parseInput(titleFirstLine, nextLine);
      30          22 : }
      31             : 
      32          20 : libjdx::jdx::Block::Block(io::TextReader& reader)
      33             :     : m_readerPtr{nullptr}
      34         137 :     , m_reader{reader}
      35             : {
      36          40 :     auto firstLine = reader.readLine();
      37          39 :     auto titleFirstLine = parseFirstLine(firstLine);
      38          19 :     std::optional<std::string> nextLine;
      39          19 :     parseInput(titleFirstLine, nextLine);
      40          11 : }
      41             : 
      42         102 : libjdx::jdx::Block::Block(const std::string& title, io::TextReader& reader,
      43         102 :     std::optional<std::string>& nextLine)
      44             :     : m_readerPtr{nullptr}
      45         102 :     , m_reader{reader}
      46             : {
      47         102 :     parseInput(title, nextLine);
      48         102 : }
      49             : 
      50        1159 : std::optional<const libjdx::jdx::StringLdr> libjdx::jdx::Block::getLdr(
      51             :     const std::string& label) const
      52             : {
      53        1159 :     return util::findLdr(m_ldrs, label);
      54             : }
      55             : 
      56          31 : const std::vector<libjdx::jdx::StringLdr>& libjdx::jdx::Block::getLdrs() const
      57             : {
      58          31 :     return m_ldrs;
      59             : }
      60             : 
      61          39 : const std::vector<libjdx::jdx::Block>& libjdx::jdx::Block::getBlocks() const
      62             : {
      63          39 :     return m_blocks;
      64             : }
      65             : 
      66           1 : const std::vector<std::string>& libjdx::jdx::Block::getLdrComments() const
      67             : {
      68           1 :     return m_ldrComments;
      69             : }
      70             : 
      71          30 : const std::optional<libjdx::jdx::XyData>& libjdx::jdx::Block::getXyData() const
      72             : {
      73          30 :     return m_xyData;
      74             : }
      75             : 
      76          27 : const std::optional<libjdx::jdx::RaData>& libjdx::jdx::Block::getRaData() const
      77             : {
      78          27 :     return m_raData;
      79             : }
      80             : 
      81             : const std::optional<libjdx::jdx::XyPoints>&
      82          27 : libjdx::jdx::Block::getXyPoints() const
      83             : {
      84          27 :     return m_xyPoints;
      85             : }
      86             : 
      87             : const std::optional<libjdx::jdx::PeakTable>&
      88          28 : libjdx::jdx::Block::getPeakTable() const
      89             : {
      90          28 :     return m_peakTable;
      91             : }
      92             : 
      93             : const std::optional<libjdx::jdx::PeakAssignments>&
      94          26 : libjdx::jdx::Block::getPeakAssignments() const
      95             : {
      96          26 :     return m_peakAssignments;
      97             : }
      98             : 
      99             : const std::optional<libjdx::jdx::NTuples>&
     100          69 : libjdx::jdx::Block::getNTuples() const
     101             : {
     102          69 :     return m_nTuples;
     103             : }
     104             : 
     105             : const std::optional<libjdx::jdx::AuditTrail>&
     106          55 : libjdx::jdx::Block::getAuditTrail() const
     107             : {
     108          55 :     return m_auditTrail;
     109             : }
     110             : 
     111             : const std::vector<libjdx::jdx::BrukerSpecificParameters>&
     112          55 : libjdx::jdx::Block::getBrukerSpecificParameters() const
     113             : {
     114          55 :     return m_brukerSpecificParameters;
     115             : }
     116             : 
     117             : const std::vector<libjdx::jdx::BrukerRelaxSection>&
     118          54 : libjdx::jdx::Block::getBrukerRelaxSections() const
     119             : {
     120          54 :     return m_brukerRelaxSections;
     121             : }
     122             : 
     123          45 : std::string libjdx::jdx::Block::parseFirstLine(const std::string& firstLine)
     124             : {
     125          45 :     if (!util::isLdrStart(firstLine))
     126             :     {
     127           3 :         throw BlockParseException("Malformed LDR start: " + firstLine);
     128             :     }
     129          84 :     auto [label, value] = util::parseLdrStart(firstLine);
     130          42 :     if (label != s_blockStartLabel)
     131             :     {
     132             :         throw BlockParseException(
     133           1 :             "Malformed Block start, wrong label: " + firstLine);
     134             :     }
     135          82 :     return value;
     136             : }
     137             : 
     138             : // NOLINTBEGIN(readability-function-cognitive-complexity)
     139         143 : void libjdx::jdx::Block::parseInput(
     140             :     const std::string& titleValue, std::optional<std::string>& nextLine)
     141             : {
     142         151 :     std::string title = titleValue;
     143         143 :     nextLine = parseStringValue(title, m_reader);
     144         143 :     m_ldrs.emplace_back(s_blockStartLabel, title);
     145             : 
     146        1306 :     while (nextLine.has_value())
     147             :     {
     148        1305 :         if (util::isPureComment(nextLine.value()))
     149             :         {
     150             :             // check for Bruker quirk
     151          17 :             if (util::isBrukerSpecificSectionStart(nextLine.value()))
     152             :             {
     153          16 :                 m_brukerSpecificParameters.emplace_back(m_reader, nextLine);
     154          17 :                 continue;
     155             :             }
     156           1 :             util::skipPureComments(m_reader, nextLine, true);
     157           1 :             continue;
     158             :         }
     159             :         // "auto [label, value] = util::parseLdrStart(nextLine.value());" cannot
     160             :         // be used as lambdas (below) cannot capture these variables
     161             :         // see:
     162             :         // https://stackoverflow.com/questions/46114214/lambda-implicit-capture-fails-with-variable-declared-from-structured-binding
     163        1295 :         std::string label;
     164        1295 :         std::string value;
     165        1288 :         std::tie(label, value) = util::parseLdrStart(nextLine.value());
     166        1288 :         if (label.empty())
     167             :         {
     168             :             // LDR start is an LDR comment "##="
     169           2 :             nextLine = parseStringValue(value, m_reader);
     170           2 :             m_ldrComments.push_back(value);
     171             :         }
     172        1286 :         else if ("END" == label)
     173             :         {
     174             :             // end of block
     175         135 :             break;
     176             :         }
     177        1151 :         else if (s_blockStartLabel == label)
     178             :         {
     179             :             // nested block
     180         204 :             auto block = Block(value, m_reader, nextLine);
     181         102 :             m_blocks.push_back(std::move(block));
     182             :         }
     183        1049 :         else if ("XYDATA" == label)
     184             :         {
     185          25 :             addLdr<XyData>(title, "XYDATA", m_xyData, [&]() {
     186          18 :                 return XyData(label, value, m_ldrs, m_reader, nextLine);
     187             :             });
     188             :         }
     189        1030 :         else if ("RADATA" == label)
     190             :         {
     191          18 :             addLdr<RaData>(title, "RADATA", m_raData, [&]() {
     192          14 :                 return RaData(label, value, m_ldrs, m_reader, nextLine);
     193             :             });
     194             :         }
     195        1015 :         else if ("XYPOINTS" == label)
     196             :         {
     197          18 :             addLdr<XyPoints>(title, "XYPOINTS", m_xyPoints, [&]() {
     198          14 :                 return XyPoints(label, value, m_ldrs, m_reader, nextLine);
     199             :             });
     200             :         }
     201        1000 :         else if ("PEAKTABLE" == label)
     202             :         {
     203          28 :             addLdr<PeakTable>(title, "PEAKTABLE", m_peakTable,
     204          24 :                 [&]() { return PeakTable(label, value, m_reader, nextLine); });
     205             :         }
     206         975 :         else if ("PEAKASSIGNMENTS" == label)
     207             :         {
     208          31 :             addLdr<PeakAssignments>(
     209          14 :                 title, "PEAKASSIGNMENTS", m_peakAssignments, [&]() {
     210          13 :                     return PeakAssignments(label, value, m_reader, nextLine);
     211             :                 });
     212             :         }
     213         961 :         else if ("NTUPLES" == label)
     214             :         {
     215          31 :             addLdr<NTuples>(title, "NTUPLES", m_nTuples, [&]() {
     216          31 :                 return NTuples(label, value, m_ldrs, m_reader, nextLine);
     217             :             });
     218             :         }
     219         930 :         else if ("AUDITTRAIL" == label)
     220             :         {
     221          12 :             addLdr<AuditTrail>(title, "AUDITTRAIL", m_auditTrail,
     222          12 :                 [&]() { return AuditTrail(label, value, m_reader, nextLine); });
     223             :         }
     224         918 :         else if ("$RELAX" == label)
     225             :         {
     226             :             // RELAX section start
     227             :             auto relaxSection
     228          42 :                 = BrukerRelaxSection(label, value, m_reader, nextLine);
     229             :             // only add non blank sections
     230             :             // section may be blank if ##$RELAX= line is immediately followed by
     231             :             // $$ Bruker specific parameters
     232          21 :             if (!relaxSection.getName().empty()
     233          21 :                 || !relaxSection.getContent().empty())
     234             :             {
     235          14 :                 m_brukerRelaxSections.push_back(std::move(relaxSection));
     236             :             }
     237             :         }
     238             :         else
     239             :         {
     240             :             // LDR is a regular LDR
     241         897 :             nextLine = parseStringValue(value, m_reader);
     242             :             // duplicate?
     243        1794 :             std::optional<StringLdr> existingLdr = getLdr(label);
     244         897 :             if (existingLdr)
     245             :             {
     246             :                 // reference implementation seems to overwrite LDR with
     247             :                 // duplicate, but spec (JCAMP-DX IR 3.2) says
     248             :                 // a duplicate LDR is illegal in a block
     249             :                 // => accept if content is identical
     250           2 :                 if (existingLdr.has_value()
     251           2 :                     && existingLdr.value().getValue() != value)
     252             :                 {
     253             :                     throw BlockParseException(
     254           1 :                         "Multiple non-identical values found for \"" + label
     255           2 :                         + std::string{"\" in block: \"" + title + "\""});
     256             :                 }
     257             :             }
     258         896 :             m_ldrs.emplace_back(label, value);
     259             :         }
     260             :     }
     261             : 
     262         408 :     if (!nextLine.has_value()
     263         136 :         || "END" != util::parseLdrStart(nextLine.value()).first)
     264             :     {
     265           1 :         throw BlockParseException("No", "END", title);
     266             :     }
     267             :     // make nextline the one following the ##END= LDR
     268         372 :     nextLine = m_reader.eof() ? std::nullopt
     269         372 :                               : std::optional<std::string>{m_reader.readLine()};
     270         135 : }
     271             : // NOLINTEND(readability-function-cognitive-complexity)

Generated by: LCOV version 1.14