From 5d605e10c1a9114f85ae8e150b66ea2dc4bb6d77 Mon Sep 17 00:00:00 2001 From: ssides Date: Mon, 23 Nov 2020 08:25:24 -0700 Subject: [PATCH 01/28] Removed compile and deprication warnings. Fixes #4110 --- .../objs/StatCumProbDistDynCalc/unitTest.cpp | 5 +++- isis/src/base/objs/Statistics/unitTest.cpp | 5 +++- .../control/objs/BundleResults/unitTest.cpp | 5 +++- .../control/objs/BundleSettings/unitTest.cpp | 6 ++++- .../control/objs/BundleUtilities/unitTest.cpp | 25 +++++++++++++++---- isis/tests/AngleTest.cpp | 25 +++++++++++-------- .../BundleObservationSolveSettingsTests.cpp | 4 +-- isis/tests/BundleSettingsTests.cpp | 6 ++--- isis/tests/ColorTests.cpp | 19 +++++++------- isis/tests/ColumnTests.cpp | 8 +++--- isis/tests/DisplacementTests.cpp | 8 ++++-- isis/tests/EndianTests.cpp | 2 +- isis/tests/FileNameTests.cpp | 4 +-- isis/tests/FunctionalTestsJigsaw.cpp | 6 ++--- isis/tests/GaussianDistributionTests.cpp | 6 ++--- isis/tests/PixelTypeTests.cpp | 6 ++--- 16 files changed, 88 insertions(+), 52 deletions(-) diff --git a/isis/src/base/objs/StatCumProbDistDynCalc/unitTest.cpp b/isis/src/base/objs/StatCumProbDistDynCalc/unitTest.cpp index bc6ddea551..6090a51f47 100644 --- a/isis/src/base/objs/StatCumProbDistDynCalc/unitTest.cpp +++ b/isis/src/base/objs/StatCumProbDistDynCalc/unitTest.cpp @@ -394,7 +394,10 @@ int main(int argc, char *argv[]) { qDebug() << ""; qDebug() << "Testing assignment operator=..."; - copyStats = copyStats; + { + StatCumProbDistDynCalc &c = copyStats; + copyStats = c; + } qDebug() << "Min = " << copyStats.min(); qDebug() << "Max = " << copyStats.max(); qDebug() << ""; diff --git a/isis/src/base/objs/Statistics/unitTest.cpp b/isis/src/base/objs/Statistics/unitTest.cpp index 6cdd2579c5..aaae973d2e 100644 --- a/isis/src/base/objs/Statistics/unitTest.cpp +++ b/isis/src/base/objs/Statistics/unitTest.cpp @@ -212,7 +212,10 @@ int main(int argc, char *argv[]) { qDebug() << ""; qDebug() << "Testing assignment operator=..."; - s = s; + { + Statistics &tstats = s; + s = tstats; + } qDebug() << "Average: " << s.Average(); qDebug() << "Variance: " << s.Variance(); qDebug() << "Rms: " << s.Rms(); diff --git a/isis/src/control/objs/BundleResults/unitTest.cpp b/isis/src/control/objs/BundleResults/unitTest.cpp index 3c45479843..fb3d05fd19 100755 --- a/isis/src/control/objs/BundleResults/unitTest.cpp +++ b/isis/src/control/objs/BundleResults/unitTest.cpp @@ -127,7 +127,10 @@ int main(int argc, char *argv[]) { qDebug() << ""; qDebug() << "Testing assignment operator=..."; - results = results; + { + BundleResults &r = results; + results = r; + } printXml(results); BundleResults assignmentOpResults; diff --git a/isis/src/control/objs/BundleSettings/unitTest.cpp b/isis/src/control/objs/BundleSettings/unitTest.cpp index a3b7ea89c0..3dc1caa844 100755 --- a/isis/src/control/objs/BundleSettings/unitTest.cpp +++ b/isis/src/control/objs/BundleSettings/unitTest.cpp @@ -159,7 +159,11 @@ int main(int argc, char *argv[]) { printXml(copySettings); qDebug() << "Testing assignment operator to set this equal to itself..."; - settings = settings; + { + BundleSettings &s = settings; + settings = s; + } + printXml(settings); qDebug() << "Testing assignment operator to create a new settings object..."; diff --git a/isis/src/control/objs/BundleUtilities/unitTest.cpp b/isis/src/control/objs/BundleUtilities/unitTest.cpp index 7472573949..b765dbb86d 100755 --- a/isis/src/control/objs/BundleUtilities/unitTest.cpp +++ b/isis/src/control/objs/BundleUtilities/unitTest.cpp @@ -151,7 +151,10 @@ int main(int argc, char *argv[]) { printXml(copySettings); qDebug() << "Testing assignment operator to set this equal to itself..."; - boss = boss; + { + BundleObservationSolveSettings &tboss = boss; + boss = tboss; + } printXml(boss); qDebug() << "Testing assignment operator to create a new settings object..."; @@ -386,7 +389,10 @@ int main(int argc, char *argv[]) { qDebug() << "serial number = " << bi2->serialNumber(); qDebug() << "file name = " << bi2->fileName(); qDebug() << "Testing assignment operator to set this equal to itself..."; - bi = bi; + { + BundleImage &tbi = bi; + bi = tbi; + } qDebug() << "serial number = " << bi.serialNumber(); qDebug() << "file name = " << bi.fileName(); qDebug() << "Testing assignment operator to create a new object..."; @@ -430,7 +436,10 @@ int main(int argc, char *argv[]) { bundleTargetBody); qDebug() << "Testing assignment operator to set this equal to itself..."; - bo2 = bo2; + { + BundleObservation &tbo2 = bo2; + bo2 = tbo2; + } qDebug() << "Testing assignment operator to create a new object..."; bo = bo2; qDebug() << "Testing copy constructor..."; @@ -1102,7 +1111,10 @@ settings->setSolveOptions(false, false, false, false, SurfacePoint::Rectangular, bundleMeasureRejected.setRejected(true); // Test self-assignment - bundleMeasure = bundleMeasure; + { + BundleMeasure &tbundleMeasure = bundleMeasure; + bundleMeasure = tbundleMeasure; + } qDebug() << ""; // Verify state and copies @@ -1394,7 +1406,10 @@ settings->setSolveOptions(false, false, false, false, SurfacePoint::Rectangular, qDebug() << "Test assignment operator"; qDebug() << ""; qDebug() << "Self assignment"; - btb3 = btb3; + { + BundleTargetBody &tbtb3 = btb3; + btb3 = tbtb3; + } qDebug().noquote() << btb3.formatBundleOutputString(true); qDebug() << "Assignment to other"; btb3 = btb1; diff --git a/isis/tests/AngleTest.cpp b/isis/tests/AngleTest.cpp index befdfa8c30..a559ceeb3f 100644 --- a/isis/tests/AngleTest.cpp +++ b/isis/tests/AngleTest.cpp @@ -139,8 +139,9 @@ TEST(AngleExceptions, LessThanNullAngle){ Isis::Angle angle1(30., Isis::Angle::Degrees ); try { - angle1 < Isis::Angle(); - FAIL() << "Expected an error"; + if (angle1 < Isis::Angle()) { + FAIL() << "Expected an error"; + } } catch(Isis::IException &e) { EXPECT_TRUE(e.toString().contains("Cannot compare a invalid angles with the < operator")) @@ -155,8 +156,9 @@ TEST(AngleExceptions, NullAngleLessThan){ Isis::Angle angle1(30., Isis::Angle::Degrees ); try { - Isis::Angle() < angle1; - FAIL() << "Expected an error"; + if (Isis::Angle() < angle1) { + FAIL() << "Expected an error"; + } } catch(Isis::IException &e) { EXPECT_TRUE(e.toString().contains("Cannot compare a invalid angles with the < operator")) @@ -171,8 +173,9 @@ TEST(AngleExceptions, NullAngleLessThanOrEqual){ Isis::Angle angle1(30., Isis::Angle::Degrees ); try { - Isis::Angle() <= angle1; - FAIL() << "Expected an error"; + if (Isis::Angle() <= angle1) { + FAIL() << "Expected an error"; + } } catch(Isis::IException &e) { EXPECT_TRUE(e.toString().contains("Cannot compare a invalid angles with the < operator")) @@ -188,8 +191,9 @@ TEST(AngleExceptions, GreaterThanNullAngle){ Isis::Angle angle1(30., Isis::Angle::Degrees ); try { - angle1 > Isis::Angle(); - FAIL() << "Expected an error"; + if (angle1 > Isis::Angle()) { + FAIL() << "Expected an error"; + } } catch(Isis::IException &e) { EXPECT_TRUE(e.toString().contains("Cannot compare a invalid angles with the > operator")) @@ -205,8 +209,9 @@ TEST(AngleExceptions, GreaterThanOrEqualToNullAngle){ Isis::Angle angle1(30., Isis::Angle::Degrees ); try { - angle1 >= Isis::Angle(); - FAIL() << "Expected an error"; + if (angle1 >= Isis::Angle()) { + FAIL() << "Expected an error"; + } } catch(Isis::IException &e) { EXPECT_TRUE(e.toString().contains("Cannot compare a invalid angles with the > operator")) diff --git a/isis/tests/BundleObservationSolveSettingsTests.cpp b/isis/tests/BundleObservationSolveSettingsTests.cpp index 761eeb53ba..7c0e748fc9 100644 --- a/isis/tests/BundleObservationSolveSettingsTests.cpp +++ b/isis/tests/BundleObservationSolveSettingsTests.cpp @@ -373,7 +373,7 @@ TEST_P(PositionSolveOptionStrings, OptionToString) { BundleObservationSolveSettings::instrumentPositionSolveOptionToString(GetParam().first)); } -INSTANTIATE_TEST_CASE_P(BundleObservationSolveSettings, PointingSolveOptionStrings, ::testing::Values( +INSTANTIATE_TEST_SUITE_P(BundleObservationSolveSettings, PointingSolveOptionStrings, ::testing::Values( qMakePair(BundleObservationSolveSettings::NoPointingFactors, QString("None")), qMakePair(BundleObservationSolveSettings::AnglesOnly, QString("AnglesOnly")), qMakePair(BundleObservationSolveSettings::AnglesVelocity, QString("AnglesAndVelocity")), @@ -382,7 +382,7 @@ INSTANTIATE_TEST_CASE_P(BundleObservationSolveSettings, PointingSolveOptionStrin qMakePair(BundleObservationSolveSettings::AllPointingCoefficients, QString("AllPolynomialCoefficients")))); -INSTANTIATE_TEST_CASE_P(BundleObservationSolveSettings, PositionSolveOptionStrings, ::testing::Values( +INSTANTIATE_TEST_SUITE_P(BundleObservationSolveSettings, PositionSolveOptionStrings, ::testing::Values( qMakePair(BundleObservationSolveSettings::NoPositionFactors, QString("None")), qMakePair(BundleObservationSolveSettings::PositionOnly, QString("PositionOnly")), qMakePair(BundleObservationSolveSettings::PositionVelocity, QString("PositionAndVelocity")), diff --git a/isis/tests/BundleSettingsTests.cpp b/isis/tests/BundleSettingsTests.cpp index 3cdf389047..81ea31b275 100644 --- a/isis/tests/BundleSettingsTests.cpp +++ b/isis/tests/BundleSettingsTests.cpp @@ -362,7 +362,7 @@ TEST_P(BoolTest, saveSolveOptions) { ); } -INSTANTIATE_TEST_CASE_P( +INSTANTIATE_TEST_SUITE_P( BundleSettings, BoolTest, ::testing::Bool() @@ -411,7 +411,7 @@ TEST_P(BoolTest, saveCoordinateTypes) { ); } -INSTANTIATE_TEST_CASE_P( +INSTANTIATE_TEST_SUITE_P( BundleSettings, CoordinateTypeTest, ::testing::Values(SurfacePoint::Latitudinal, SurfacePoint::Rectangular) @@ -631,7 +631,7 @@ TEST_P(ConvergenceCriteriaTest, saveConvergenceCriteria) { ); } -INSTANTIATE_TEST_CASE_P( +INSTANTIATE_TEST_SUITE_P( BundleSettings, ConvergenceCriteriaTest, ::testing::Values(BundleSettings::Sigma0, BundleSettings::ParameterCorrections) diff --git a/isis/tests/ColorTests.cpp b/isis/tests/ColorTests.cpp index e4aac67823..bd84bb3e8c 100644 --- a/isis/tests/ColorTests.cpp +++ b/isis/tests/ColorTests.cpp @@ -68,20 +68,19 @@ namespace Isis{ EXPECT_FALSE(Color::colorRGBAFormat().exactMatch(GetParam())); } - - INSTANTIATE_TEST_CASE_P (Color, + INSTANTIATE_TEST_SUITE_P (Color, QStringQColorPair, ::testing::Values(std::make_pair(QString("#000000ff"), QColor(0, 0, 0)), - std::make_pair(QString("#00000000"), QColor(0, 0, 0, 0)), - std::make_pair(QString("#ff000000"), QColor(255, 0, 0, 0)), - std::make_pair(QString("#00ff0000"), QColor(0, 255, 0, 0)), - std::make_pair(QString("#0000ff00"), QColor(0, 0, 255, 0)), - std::make_pair(QString("#000000ff"), QColor(0, 0, 0, 255)), - std::make_pair(QString("#ffffffff"), QColor(255, 255, 255, 255)), - std::make_pair(QString("#0a141e28"), QColor(10, 20, 30, 40)))); + std::make_pair(QString("#00000000"), QColor(0, 0, 0, 0)), + std::make_pair(QString("#ff000000"), QColor(255, 0, 0, 0)), + std::make_pair(QString("#00ff0000"), QColor(0, 255, 0, 0)), + std::make_pair(QString("#0000ff00"), QColor(0, 0, 255, 0)), + std::make_pair(QString("#000000ff"), QColor(0, 0, 0, 255)), + std::make_pair(QString("#ffffffff"), QColor(255, 255, 255, 255)), + std::make_pair(QString("#0a141e28"), QColor(10, 20, 30, 40)))); - INSTANTIATE_TEST_CASE_P (Color, + INSTANTIATE_TEST_SUITE_P (Color, InvalidColorString, ::testing::Values(QString("#rrggbbaa"), QString(" 00112233"), diff --git a/isis/tests/ColumnTests.cpp b/isis/tests/ColumnTests.cpp index fd99be8820..c73ff866c0 100644 --- a/isis/tests/ColumnTests.cpp +++ b/isis/tests/ColumnTests.cpp @@ -63,7 +63,7 @@ TEST_P(Types, Type) { EXPECT_EQ(column.DataType(), GetParam()); } -INSTANTIATE_TEST_CASE_P(Column, Types, ::testing::Values( +INSTANTIATE_TEST_SUITE_P(Column, Types, ::testing::Values( Column::NoType, Column::Integer, Column::Real, Column::String, Column::Pixel)); //Tests SetAlignment & Alignment functions with every member of the Align enum @@ -74,7 +74,7 @@ TEST_P(Align, Alignment) { EXPECT_EQ(column.Alignment(), GetParam()); } -INSTANTIATE_TEST_CASE_P(Column, Align, ::testing::Values( +INSTANTIATE_TEST_SUITE_P(Column, Align, ::testing::Values( Column::NoAlign, Column::Right, Column::Left, Column::Decimal)); //Tests SetPrecision & Precision functions with Real type and Pixel type. @@ -166,7 +166,7 @@ TEST_P(TypeError, SetAlignmentError) { } } -INSTANTIATE_TEST_CASE_P(Column, TypeError, ::testing::Values( +INSTANTIATE_TEST_SUITE_P(Column, TypeError, ::testing::Values( Column::Integer, Column::String)); //Tests that Precision's exceptions are working correctly @@ -187,5 +187,5 @@ TEST_P(PrecisionError, SetPrecisionError) { << message.toStdString() <<"\""; } } -INSTANTIATE_TEST_CASE_P(Column, PrecisionError, ::testing::Values( +INSTANTIATE_TEST_SUITE_P(Column, PrecisionError, ::testing::Values( Column::NoAlign, Column::Right, Column::Left)); diff --git a/isis/tests/DisplacementTests.cpp b/isis/tests/DisplacementTests.cpp index 71e65818ce..3a18327fc0 100644 --- a/isis/tests/DisplacementTests.cpp +++ b/isis/tests/DisplacementTests.cpp @@ -170,7 +170,9 @@ TEST(Displacement, UninitializedComparison) { try { - Isis::Displacement() > Isis::Displacement(); + if (Isis::Displacement() > Isis::Displacement()) { + FAIL() << "Expected an error"; + } } catch(Isis::IException &e) { @@ -186,7 +188,9 @@ TEST(Displacement, UninitializedComparison) try { - Isis::Displacement() < Isis::Displacement(); + if (Isis::Displacement() < Isis::Displacement()) { + FAIL() << "Expected an error"; + } } catch(Isis::IException &e) { diff --git a/isis/tests/EndianTests.cpp b/isis/tests/EndianTests.cpp index 5b6783d6bb..59957674ba 100644 --- a/isis/tests/EndianTests.cpp +++ b/isis/tests/EndianTests.cpp @@ -18,4 +18,4 @@ QPair noOrder("None", Isis::ByteOrder::NoByteOrder); QPair lsb("Lsb", Isis::ByteOrder::Lsb); QPair msb("Msb", Isis::ByteOrder::Msb); -INSTANTIATE_TEST_CASE_P(Endian, ConversionTest, ::testing::Values(noOrder, lsb, msb)); +INSTANTIATE_TEST_SUITE_P(Endian, ConversionTest, ::testing::Values(noOrder, lsb, msb)); diff --git a/isis/tests/FileNameTests.cpp b/isis/tests/FileNameTests.cpp index 9af580f107..fc8aa4b345 100644 --- a/isis/tests/FileNameTests.cpp +++ b/isis/tests/FileNameTests.cpp @@ -240,7 +240,7 @@ const char* versionedFiles[] = {"tttt??????", "tttt??????.tmp", "tttt_?.tmp", "? "tt{MMM}tt{dd}yy{yy}.tmp", "tt{d}tt{MMM}.tmp", "tt{d}tt{MMMM}.tmp", "tt{dd}.tmp", "tttt{dd}.tmp", "$TEMPORARY/{MMM}-{dd}-{yy}_v???.tmp"}; -INSTANTIATE_TEST_CASE_P(FileName, FileName_Fixture_Versioned, ::testing::ValuesIn(versionedFiles)); +INSTANTIATE_TEST_SUITE_P(FileName, FileName_Fixture_Versioned, ::testing::ValuesIn(versionedFiles)); TEST_P(FileName_Fixture_NotVersioned, IsVersioned) { FileName file(GetParam()); @@ -252,4 +252,4 @@ const char* notVersionedFiles[] = {"tttt"}; //TODO These actually throw errors so they cannot be checked like this //"tttt{}.tmp", "ttttt{}.tmp", "??tttt??", "tttt{aaaa}.tmp"}; -INSTANTIATE_TEST_CASE_P(FileName, FileName_Fixture_NotVersioned, ::testing::ValuesIn(notVersionedFiles)); +INSTANTIATE_TEST_SUITE_P(FileName, FileName_Fixture_NotVersioned, ::testing::ValuesIn(notVersionedFiles)); diff --git a/isis/tests/FunctionalTestsJigsaw.cpp b/isis/tests/FunctionalTestsJigsaw.cpp index 9cf451a008..2d9d9a9d74 100644 --- a/isis/tests/FunctionalTestsJigsaw.cpp +++ b/isis/tests/FunctionalTestsJigsaw.cpp @@ -290,7 +290,7 @@ TEST_F(ApolloNetwork, FunctionalTestJigsawBundleXYZ) { for (int i=0; i < latLatPoints.length(); i++) { ControlPoint* latLatPoint = latLatPoints[i]; - ControlPoint* rectLatPoint; + ControlPoint *rectLatPoint = nullptr; EXPECT_NO_THROW({ rectLatPoint = rectLatNet.GetPoint(latLatPoint->GetId()); } @@ -368,7 +368,7 @@ TEST_F(ApolloNetwork, FunctionalTestJigsawBundleXYZ) { for (int i=0; i < rectLatPoints.length(); i++) { ControlPoint* rectLatPoint = rectLatPoints[i]; - ControlPoint* rectRectPoint; + ControlPoint* rectRectPoint = nullptr; EXPECT_NO_THROW({ rectRectPoint = rectRectNet.GetPoint(rectLatPoint->GetId()); } @@ -447,7 +447,7 @@ TEST_F(ApolloNetwork, FunctionalTestJigsawBundleXYZ) { for (int i=0; i < latRectPoints.length(); i++) { ControlPoint* latRectPoint = latRectPoints[i]; - ControlPoint* latLatPoint; + ControlPoint *latLatPoint = nullptr; EXPECT_NO_THROW({ latLatPoint = latLatNet.GetPoint(latRectPoint->GetId()); } diff --git a/isis/tests/GaussianDistributionTests.cpp b/isis/tests/GaussianDistributionTests.cpp index f4af2510cb..4a7ea78dee 100644 --- a/isis/tests/GaussianDistributionTests.cpp +++ b/isis/tests/GaussianDistributionTests.cpp @@ -62,6 +62,6 @@ QPair pos2point5(2.5, 99.379033467422431); QPair pos3(3.0, 99.865010196837048); -INSTANTIATE_TEST_CASE_P(GaussianDistribution, DoubleTest, ::testing::Values(neg3, neg2point5, - neg2, neg1point5, neg1, negpoint5, zero, pospoint5, pos1, pos1point5, - pos2, pos2point5, pos3)); \ No newline at end of file +INSTANTIATE_TEST_SUITE_P(GaussianDistribution, DoubleTest, ::testing::Values(neg3, neg2point5, + neg2, neg1point5, neg1, negpoint5, zero, pospoint5, pos1, pos1point5, + pos2, pos2point5, pos3)); \ No newline at end of file diff --git a/isis/tests/PixelTypeTests.cpp b/isis/tests/PixelTypeTests.cpp index 8f46fc5e5b..c1e37e19f1 100644 --- a/isis/tests/PixelTypeTests.cpp +++ b/isis/tests/PixelTypeTests.cpp @@ -28,7 +28,7 @@ TEST_P(PixelEnum, TestEnum) { EXPECT_EQ(PixelTypeEnumeration(GetParam().first), GetParam().second); } -INSTANTIATE_TEST_CASE_P(PixelType, PixelSize, ::testing::Values( +INSTANTIATE_TEST_SUITE_P(PixelType, PixelSize, ::testing::Values( qMakePair(PixelType::None, 0), qMakePair(PixelType::UnsignedByte, 1), qMakePair(PixelType::SignedByte, 1), @@ -39,7 +39,7 @@ INSTANTIATE_TEST_CASE_P(PixelType, PixelSize, ::testing::Values( qMakePair(PixelType::Real, 4), qMakePair(PixelType::Double, 8))); -INSTANTIATE_TEST_CASE_P(PixelType, PixelName, ::testing::Values( +INSTANTIATE_TEST_SUITE_P(PixelType, PixelName, ::testing::Values( qMakePair(PixelType::None, QString("None")), qMakePair(PixelType::UnsignedByte, QString("UnsignedByte")), qMakePair(PixelType::SignedByte, QString("SignedByte")), @@ -50,7 +50,7 @@ INSTANTIATE_TEST_CASE_P(PixelType, PixelName, ::testing::Values( qMakePair(PixelType::Real, QString("Real")), qMakePair(PixelType::Double, QString("Double")))); -INSTANTIATE_TEST_CASE_P(PixelType, PixelEnum, ::testing::Values( +INSTANTIATE_TEST_SUITE_P(PixelType, PixelEnum, ::testing::Values( qMakePair(QString("None"), 0), qMakePair(QString("UnsignedByte"), 1), qMakePair(QString("SignedByte"), 2), From c6e3d5841c9c7142f834bd287b3bbf17a0bb5ab8 Mon Sep 17 00:00:00 2001 From: ssides Date: Wed, 16 Dec 2020 15:41:31 -0700 Subject: [PATCH 02/28] temp add of to pds 4 test --- isis/src/base/apps/topds4/config.h | 76 + isis/src/base/apps/topds4/environment2.h | 228 +++ isis/src/base/apps/topds4/exceptions.h | 50 + isis/src/base/apps/topds4/function_storage.h | 139 ++ isis/src/base/apps/topds4/inja.h | 15 + isis/src/base/apps/topds4/lexer.h | 418 ++++++ isis/src/base/apps/topds4/main.cpp | 12 + isis/src/base/apps/topds4/node.h | 328 ++++ isis/src/base/apps/topds4/parser.h | 582 +++++++ isis/src/base/apps/topds4/renderer.h | 607 ++++++++ isis/src/base/apps/topds4/statistics2.h | 68 + isis/src/base/apps/topds4/string_view.h | 1416 ++++++++++++++++++ isis/src/base/apps/topds4/template2.h | 39 + isis/src/base/apps/topds4/token.h | 76 + isis/src/base/apps/topds4/topds4.cpp | 42 + isis/src/base/apps/topds4/topds4.h | 18 + isis/src/base/apps/topds4/topds4.xml | 63 + isis/src/base/apps/topds4/utils.h | 70 + 18 files changed, 4247 insertions(+) create mode 100644 isis/src/base/apps/topds4/config.h create mode 100644 isis/src/base/apps/topds4/environment2.h create mode 100644 isis/src/base/apps/topds4/exceptions.h create mode 100644 isis/src/base/apps/topds4/function_storage.h create mode 100644 isis/src/base/apps/topds4/inja.h create mode 100644 isis/src/base/apps/topds4/lexer.h create mode 100644 isis/src/base/apps/topds4/main.cpp create mode 100644 isis/src/base/apps/topds4/node.h create mode 100644 isis/src/base/apps/topds4/parser.h create mode 100644 isis/src/base/apps/topds4/renderer.h create mode 100644 isis/src/base/apps/topds4/statistics2.h create mode 100644 isis/src/base/apps/topds4/string_view.h create mode 100644 isis/src/base/apps/topds4/template2.h create mode 100644 isis/src/base/apps/topds4/token.h create mode 100644 isis/src/base/apps/topds4/topds4.cpp create mode 100644 isis/src/base/apps/topds4/topds4.h create mode 100644 isis/src/base/apps/topds4/topds4.xml create mode 100644 isis/src/base/apps/topds4/utils.h diff --git a/isis/src/base/apps/topds4/config.h b/isis/src/base/apps/topds4/config.h new file mode 100644 index 0000000000..201a429f36 --- /dev/null +++ b/isis/src/base/apps/topds4/config.h @@ -0,0 +1,76 @@ +// Copyright (c) 2019 Pantor. All rights reserved. + +#ifndef INCLUDE_INJA_CONFIG_HPP_ +#define INCLUDE_INJA_CONFIG_HPP_ + +#include +#include + +#include "string_view.h" + +namespace inja { + +/*! + * \brief Class for lexer configuration. + */ +struct LexerConfig { + std::string statement_open {"{%"}; + std::string statement_open_no_lstrip {"{%+"}; + std::string statement_open_force_lstrip {"{%-"}; + std::string statement_close {"%}"}; + std::string statement_close_force_rstrip {"-%}"}; + std::string line_statement {"##"}; + std::string expression_open {"{{"}; + std::string expression_open_force_lstrip {"{{-"}; + std::string expression_close {"}}"}; + std::string expression_close_force_rstrip {"-}}"}; + std::string comment_open {"{#"}; + std::string comment_close {"#}"}; + std::string open_chars {"#{"}; + + bool trim_blocks {false}; + bool lstrip_blocks {false}; + + void update_open_chars() { + open_chars = ""; + if (open_chars.find(line_statement[0]) == std::string::npos) { + open_chars += line_statement[0]; + } + if (open_chars.find(statement_open[0]) == std::string::npos) { + open_chars += statement_open[0]; + } + if (open_chars.find(statement_open_no_lstrip[0]) == std::string::npos) { + open_chars += statement_open_no_lstrip[0]; + } + if (open_chars.find(statement_open_force_lstrip[0]) == std::string::npos) { + open_chars += statement_open_force_lstrip[0]; + } + if (open_chars.find(expression_open[0]) == std::string::npos) { + open_chars += expression_open[0]; + } + if (open_chars.find(expression_open_force_lstrip[0]) == std::string::npos) { + open_chars += expression_open_force_lstrip[0]; + } + if (open_chars.find(comment_open[0]) == std::string::npos) { + open_chars += comment_open[0]; + } + } +}; + +/*! + * \brief Class for parser configuration. + */ +struct ParserConfig { + bool search_included_templates_in_files {true}; +}; + +/*! + * \brief Class for render configuration. + */ +struct RenderConfig { + bool throw_at_missing_includes {true}; +}; + +} // namespace inja + +#endif // INCLUDE_INJA_CONFIG_HPP_ diff --git a/isis/src/base/apps/topds4/environment2.h b/isis/src/base/apps/topds4/environment2.h new file mode 100644 index 0000000000..730496be7d --- /dev/null +++ b/isis/src/base/apps/topds4/environment2.h @@ -0,0 +1,228 @@ +// Copyright (c) 2019 Pantor. All rights reserved. + +#ifndef INCLUDE_INJA_ENVIRONMENT_HPP_ +#define INCLUDE_INJA_ENVIRONMENT_HPP_ + +#include +#include +#include +#include +#include + +#include + +#include "config.h" +#include "function_storage.h" +#include "parser.h" +#include "renderer.h" +#include "string_view.h" +#include "template2.h" +#include "utils.h" + +namespace inja { + +using json = nlohmann::json; + +/*! + * \brief Class for changing the configuration. + */ +class Environment { + std::string input_path; + std::string output_path; + + LexerConfig lexer_config; + ParserConfig parser_config; + RenderConfig render_config; + + FunctionStorage function_storage; + TemplateStorage template_storage; + +public: + Environment() : Environment("") {} + + explicit Environment(const std::string &global_path) : input_path(global_path), output_path(global_path) {} + + Environment(const std::string &input_path, const std::string &output_path) + : input_path(input_path), output_path(output_path) {} + + /// Sets the opener and closer for template statements + void set_statement(const std::string &open, const std::string &close) { + lexer_config.statement_open = open; + lexer_config.statement_open_no_lstrip = open + "+"; + lexer_config.statement_open_force_lstrip = open + "-"; + lexer_config.statement_close = close; + lexer_config.statement_close_force_rstrip = "-" + close; + lexer_config.update_open_chars(); + } + + /// Sets the opener for template line statements + void set_line_statement(const std::string &open) { + lexer_config.line_statement = open; + lexer_config.update_open_chars(); + } + + /// Sets the opener and closer for template expressions + void set_expression(const std::string &open, const std::string &close) { + lexer_config.expression_open = open; + lexer_config.expression_open_force_lstrip = open + "-"; + lexer_config.expression_close = close; + lexer_config.expression_close_force_rstrip = "-" + close; + lexer_config.update_open_chars(); + } + + /// Sets the opener and closer for template comments + void set_comment(const std::string &open, const std::string &close) { + lexer_config.comment_open = open; + lexer_config.comment_close = close; + lexer_config.update_open_chars(); + } + + /// Sets whether to remove the first newline after a block + void set_trim_blocks(bool trim_blocks) { + lexer_config.trim_blocks = trim_blocks; + } + + /// Sets whether to strip the spaces and tabs from the start of a line to a block + void set_lstrip_blocks(bool lstrip_blocks) { + lexer_config.lstrip_blocks = lstrip_blocks; + } + + /// Sets the element notation syntax + void set_search_included_templates_in_files(bool search_in_files) { + parser_config.search_included_templates_in_files = search_in_files; + } + + /// Sets whether a missing include will throw an error + void set_throw_at_missing_includes(bool will_throw) { + render_config.throw_at_missing_includes = will_throw; + } + + Template parse(nonstd::string_view input) { + Parser parser(parser_config, lexer_config, template_storage, function_storage); + return parser.parse(input); + } + + Template parse_template(const std::string &filename) { + Parser parser(parser_config, lexer_config, template_storage, function_storage); + auto result = Template(parser.load_file(input_path + static_cast(filename))); + parser.parse_into_template(result, input_path + static_cast(filename)); + return result; + } + + Template parse_file(const std::string &filename) { + return parse_template(filename); + } + + std::string render(nonstd::string_view input, const json &data) { return render(parse(input), data); } + + std::string render(const Template &tmpl, const json &data) { + std::stringstream os; + render_to(os, tmpl, data); + return os.str(); + } + + std::string render_file(const std::string &filename, const json &data) { + return render(parse_template(filename), data); + } + + std::string render_file_with_json_file(const std::string &filename, const std::string &filename_data) { + const json data = load_json(filename_data); + return render_file(filename, data); + } + + void write(const std::string &filename, const json &data, const std::string &filename_out) { + std::ofstream file(output_path + filename_out); + file << render_file(filename, data); + file.close(); + } + + void write(const Template &temp, const json &data, const std::string &filename_out) { + std::ofstream file(output_path + filename_out); + file << render(temp, data); + file.close(); + } + + void write_with_json_file(const std::string &filename, const std::string &filename_data, + const std::string &filename_out) { + const json data = load_json(filename_data); + write(filename, data, filename_out); + } + + void write_with_json_file(const Template &temp, const std::string &filename_data, const std::string &filename_out) { + const json data = load_json(filename_data); + write(temp, data, filename_out); + } + + std::ostream &render_to(std::ostream &os, const Template &tmpl, const json &data) { + Renderer(render_config, template_storage, function_storage).render_to(os, tmpl, data); + return os; + } + + std::string load_file(const std::string &filename) { + Parser parser(parser_config, lexer_config, template_storage, function_storage); + return parser.load_file(input_path + filename); + } + + json load_json(const std::string &filename) { + std::ifstream file; + open_file_or_throw(input_path + filename, file); + json j; + file >> j; + return j; + } + + /*! + @brief Adds a variadic callback + */ + void add_callback(const std::string &name, const CallbackFunction &callback) { + add_callback(name, -1, callback); + } + + /*! + @brief Adds a variadic void callback + */ + void add_void_callback(const std::string &name, const VoidCallbackFunction &callback) { + add_void_callback(name, -1, callback); + } + + /*! + @brief Adds a callback with given number or arguments + */ + void add_callback(const std::string &name, int num_args, const CallbackFunction &callback) { + function_storage.add_callback(name, num_args, callback); + } + + /*! + @brief Adds a void callback with given number or arguments + */ + void add_void_callback(const std::string &name, int num_args, const VoidCallbackFunction &callback) { + function_storage.add_callback(name, num_args, [callback](Arguments& args) { callback(args); return json(); }); + } + + /** Includes a template with a given name into the environment. + * Then, a template can be rendered in another template using the + * include "" syntax. + */ + void include_template(const std::string &name, const Template &tmpl) { + template_storage[name] = tmpl; + } +}; + +/*! +@brief render with default settings to a string +*/ +inline std::string render(nonstd::string_view input, const json &data) { + return Environment().render(input, data); +} + +/*! +@brief render with default settings to the given output stream +*/ +inline void render_to(std::ostream &os, nonstd::string_view input, const json &data) { + Environment env; + env.render_to(os, env.parse(input), data); +} + +} // namespace inja + +#endif // INCLUDE_INJA_ENVIRONMENT_HPP_ diff --git a/isis/src/base/apps/topds4/exceptions.h b/isis/src/base/apps/topds4/exceptions.h new file mode 100644 index 0000000000..2784da8188 --- /dev/null +++ b/isis/src/base/apps/topds4/exceptions.h @@ -0,0 +1,50 @@ +// Copyright (c) 2020 Pantor. All rights reserved. + +#ifndef INCLUDE_INJA_EXCEPTIONS_HPP_ +#define INCLUDE_INJA_EXCEPTIONS_HPP_ + +#include +#include + +namespace inja { + +struct SourceLocation { + size_t line; + size_t column; +}; + +struct InjaError : public std::runtime_error { + const std::string type; + const std::string message; + + const SourceLocation location; + + explicit InjaError(const std::string &type, const std::string &message) + : std::runtime_error("[inja.exception." + type + "] " + message), type(type), message(message), location({0, 0}) {} + + explicit InjaError(const std::string &type, const std::string &message, SourceLocation location) + : std::runtime_error("[inja.exception." + type + "] (at " + std::to_string(location.line) + ":" + + std::to_string(location.column) + ") " + message), + type(type), message(message), location(location) {} +}; + +struct ParserError : public InjaError { + explicit ParserError(const std::string &message, SourceLocation location) : InjaError("parser_error", message, location) {} +}; + +struct RenderError : public InjaError { + explicit RenderError(const std::string &message, SourceLocation location) : InjaError("render_error", message, location) {} +}; + +struct FileError : public InjaError { + explicit FileError(const std::string &message) : InjaError("file_error", message) {} + explicit FileError(const std::string &message, SourceLocation location) : InjaError("file_error", message, location) {} +}; + +struct JsonError : public InjaError { + explicit JsonError(const std::string &message, SourceLocation location) : InjaError("json_error", message, location) {} +}; + +} // namespace inja + +#endif // INCLUDE_INJA_EXCEPTIONS_HPP_ diff --git a/isis/src/base/apps/topds4/function_storage.h b/isis/src/base/apps/topds4/function_storage.h new file mode 100644 index 0000000000..3aec3c7014 --- /dev/null +++ b/isis/src/base/apps/topds4/function_storage.h @@ -0,0 +1,139 @@ +// Copyright (c) 2020 Pantor. All rights reserved. + +#ifndef INCLUDE_INJA_FUNCTION_STORAGE_HPP_ +#define INCLUDE_INJA_FUNCTION_STORAGE_HPP_ + +#include + +#include "string_view.h" + +namespace inja { + +using json = nlohmann::json; + +using Arguments = std::vector; +using CallbackFunction = std::function; +using VoidCallbackFunction = std::function; + +/*! + * \brief Class for builtin functions and user-defined callbacks. + */ +class FunctionStorage { +public: + enum class Operation { + Not, + And, + Or, + In, + Equal, + NotEqual, + Greater, + GreaterEqual, + Less, + LessEqual, + Add, + Subtract, + Multiplication, + Division, + Power, + Modulo, + AtId, + At, + Default, + DivisibleBy, + Even, + Exists, + ExistsInObject, + First, + Float, + Int, + IsArray, + IsBoolean, + IsFloat, + IsInteger, + IsNumber, + IsObject, + IsString, + Last, + Length, + Lower, + Max, + Min, + Odd, + Range, + Round, + Sort, + Upper, + Callback, + ParenLeft, + ParenRight, + None, + }; + + struct FunctionData { + explicit FunctionData(const Operation &op, const CallbackFunction &cb = CallbackFunction{}) : operation(op), callback(cb) {} + const Operation operation; + const CallbackFunction callback; + }; + +private: + const int VARIADIC {-1}; + + std::map, FunctionData> function_storage = { + {std::make_pair("at", 2), FunctionData { Operation::At }}, + {std::make_pair("default", 2), FunctionData { Operation::Default }}, + {std::make_pair("divisibleBy", 2), FunctionData { Operation::DivisibleBy }}, + {std::make_pair("even", 1), FunctionData { Operation::Even }}, + {std::make_pair("exists", 1), FunctionData { Operation::Exists }}, + {std::make_pair("existsIn", 2), FunctionData { Operation::ExistsInObject }}, + {std::make_pair("first", 1), FunctionData { Operation::First }}, + {std::make_pair("float", 1), FunctionData { Operation::Float }}, + {std::make_pair("int", 1), FunctionData { Operation::Int }}, + {std::make_pair("isArray", 1), FunctionData { Operation::IsArray }}, + {std::make_pair("isBoolean", 1), FunctionData { Operation::IsBoolean }}, + {std::make_pair("isFloat", 1), FunctionData { Operation::IsFloat }}, + {std::make_pair("isInteger", 1), FunctionData { Operation::IsInteger }}, + {std::make_pair("isNumber", 1), FunctionData { Operation::IsNumber }}, + {std::make_pair("isObject", 1), FunctionData { Operation::IsObject }}, + {std::make_pair("isString", 1), FunctionData { Operation::IsString }}, + {std::make_pair("last", 1), FunctionData { Operation::Last }}, + {std::make_pair("length", 1), FunctionData { Operation::Length }}, + {std::make_pair("lower", 1), FunctionData { Operation::Lower }}, + {std::make_pair("max", 1), FunctionData { Operation::Max }}, + {std::make_pair("min", 1), FunctionData { Operation::Min }}, + {std::make_pair("odd", 1), FunctionData { Operation::Odd }}, + {std::make_pair("range", 1), FunctionData { Operation::Range }}, + {std::make_pair("round", 2), FunctionData { Operation::Round }}, + {std::make_pair("sort", 1), FunctionData { Operation::Sort }}, + {std::make_pair("upper", 1), FunctionData { Operation::Upper }}, + }; + +public: + void add_builtin(nonstd::string_view name, int num_args, Operation op) { + function_storage.emplace(std::make_pair(static_cast(name), num_args), FunctionData { op }); + } + + void add_callback(nonstd::string_view name, int num_args, const CallbackFunction &callback) { + function_storage.emplace(std::make_pair(static_cast(name), num_args), FunctionData { Operation::Callback, callback }); + } + + FunctionData find_function(nonstd::string_view name, int num_args) const { + auto it = function_storage.find(std::make_pair(static_cast(name), num_args)); + if (it != function_storage.end()) { + return it->second; + + // Find variadic function + } else if (num_args > 0) { + it = function_storage.find(std::make_pair(static_cast(name), VARIADIC)); + if (it != function_storage.end()) { + return it->second; + } + } + + return FunctionData { Operation::None }; + } +}; + +} // namespace inja + +#endif // INCLUDE_INJA_FUNCTION_STORAGE_HPP_ diff --git a/isis/src/base/apps/topds4/inja.h b/isis/src/base/apps/topds4/inja.h new file mode 100644 index 0000000000..bb0d6e8f33 --- /dev/null +++ b/isis/src/base/apps/topds4/inja.h @@ -0,0 +1,15 @@ +// Copyright (c) 2020 Pantor. All rights reserved. + +#ifndef INCLUDE_INJA_INJA_HPP_ +#define INCLUDE_INJA_INJA_HPP_ + +#include + +#include "environment2.h" +#include "exceptions.h" +#include "parser.h" +#include "renderer.h" +#include "string_view.h" +#include "template2.h" + +#endif // INCLUDE_INJA_INJA_HPP_ diff --git a/isis/src/base/apps/topds4/lexer.h b/isis/src/base/apps/topds4/lexer.h new file mode 100644 index 0000000000..3fbaa96218 --- /dev/null +++ b/isis/src/base/apps/topds4/lexer.h @@ -0,0 +1,418 @@ +// Copyright (c) 2020 Pantor. All rights reserved. + +#ifndef INCLUDE_INJA_LEXER_HPP_ +#define INCLUDE_INJA_LEXER_HPP_ + +#include +#include + +#include "config.h" +#include "token.h" +#include "utils.h" + +namespace inja { + +/*! + * \brief Class for lexing an inja Template. + */ +class Lexer { + enum class State { + Text, + ExpressionStart, + ExpressionStartForceLstrip, + ExpressionBody, + LineStart, + LineBody, + StatementStart, + StatementStartNoLstrip, + StatementStartForceLstrip, + StatementBody, + CommentStart, + CommentBody, + }; + + enum class MinusState { + Operator, + Number, + }; + + const LexerConfig &config; + + State state; + MinusState minus_state; + nonstd::string_view m_in; + size_t tok_start; + size_t pos; + + + Token scan_body(nonstd::string_view close, Token::Kind closeKind, nonstd::string_view close_trim = nonstd::string_view(), bool trim = false) { + again: + // skip whitespace (except for \n as it might be a close) + if (tok_start >= m_in.size()) { + return make_token(Token::Kind::Eof); + } + char ch = m_in[tok_start]; + if (ch == ' ' || ch == '\t' || ch == '\r') { + tok_start += 1; + goto again; + } + + // check for close + if (!close_trim.empty() && inja::string_view::starts_with(m_in.substr(tok_start), close_trim)) { + state = State::Text; + pos = tok_start + close_trim.size(); + Token tok = make_token(closeKind); + skip_whitespaces_and_newlines(); + return tok; + } + + if (inja::string_view::starts_with(m_in.substr(tok_start), close)) { + state = State::Text; + pos = tok_start + close.size(); + Token tok = make_token(closeKind); + if (trim) { + skip_whitespaces_and_first_newline(); + } + return tok; + } + + // skip \n + if (ch == '\n') { + tok_start += 1; + goto again; + } + + pos = tok_start + 1; + if (std::isalpha(ch)) { + minus_state = MinusState::Operator; + return scan_id(); + } + + MinusState current_minus_state = minus_state; + if (minus_state == MinusState::Operator) { + minus_state = MinusState::Number; + } + + switch (ch) { + case '+': + return make_token(Token::Kind::Plus); + case '-': + if (current_minus_state == MinusState::Operator) { + return make_token(Token::Kind::Minus); + } + return scan_number(); + case '*': + return make_token(Token::Kind::Times); + case '/': + return make_token(Token::Kind::Slash); + case '^': + return make_token(Token::Kind::Power); + case '%': + return make_token(Token::Kind::Percent); + case '.': + return make_token(Token::Kind::Dot); + case ',': + return make_token(Token::Kind::Comma); + case ':': + return make_token(Token::Kind::Colon); + case '(': + return make_token(Token::Kind::LeftParen); + case ')': + minus_state = MinusState::Operator; + return make_token(Token::Kind::RightParen); + case '[': + return make_token(Token::Kind::LeftBracket); + case ']': + minus_state = MinusState::Operator; + return make_token(Token::Kind::RightBracket); + case '{': + return make_token(Token::Kind::LeftBrace); + case '}': + minus_state = MinusState::Operator; + return make_token(Token::Kind::RightBrace); + case '>': + if (pos < m_in.size() && m_in[pos] == '=') { + pos += 1; + return make_token(Token::Kind::GreaterEqual); + } + return make_token(Token::Kind::GreaterThan); + case '<': + if (pos < m_in.size() && m_in[pos] == '=') { + pos += 1; + return make_token(Token::Kind::LessEqual); + } + return make_token(Token::Kind::LessThan); + case '=': + if (pos < m_in.size() && m_in[pos] == '=') { + pos += 1; + return make_token(Token::Kind::Equal); + } + return make_token(Token::Kind::Unknown); + case '!': + if (pos < m_in.size() && m_in[pos] == '=') { + pos += 1; + return make_token(Token::Kind::NotEqual); + } + return make_token(Token::Kind::Unknown); + case '\"': + return scan_string(); + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + minus_state = MinusState::Operator; + return scan_number(); + case '_': + minus_state = MinusState::Operator; + return scan_id(); + default: + return make_token(Token::Kind::Unknown); + } + } + + Token scan_id() { + for (;;) { + if (pos >= m_in.size()) { + break; + } + char ch = m_in[pos]; + if (!std::isalnum(ch) && ch != '.' && ch != '/' && ch != '_' && ch != '-') { + break; + } + pos += 1; + } + return make_token(Token::Kind::Id); + } + + Token scan_number() { + for (;;) { + if (pos >= m_in.size()) { + break; + } + char ch = m_in[pos]; + // be very permissive in lexer (we'll catch errors when conversion happens) + if (!std::isdigit(ch) && ch != '.' && ch != 'e' && ch != 'E' && ch != '+' && ch != '-') { + break; + } + pos += 1; + } + return make_token(Token::Kind::Number); + } + + Token scan_string() { + bool escape {false}; + for (;;) { + if (pos >= m_in.size()) { + break; + } + char ch = m_in[pos++]; + if (ch == '\\') { + escape = true; + } else if (!escape && ch == m_in[tok_start]) { + break; + } else { + escape = false; + } + } + return make_token(Token::Kind::String); + } + + Token make_token(Token::Kind kind) const { return Token(kind, string_view::slice(m_in, tok_start, pos)); } + + void skip_whitespaces_and_newlines() { + if (pos < m_in.size()) { + while (pos < m_in.size() && (m_in[pos] == ' ' || m_in[pos] == '\t' || m_in[pos] == '\n' || m_in[pos] == '\r')) { + pos += 1; + } + } + } + + void skip_whitespaces_and_first_newline() { + if (pos < m_in.size()) { + while (pos < m_in.size() && (m_in[pos] == ' ' || m_in[pos] == '\t')) { + pos += 1; + } + } + + if (pos < m_in.size()) { + char ch = m_in[pos]; + if (ch == '\n') { + pos += 1; + } else if (ch == '\r') { + pos += 1; + if (pos < m_in.size() && m_in[pos] == '\n') { + pos += 1; + } + } + } + } + + static nonstd::string_view clear_final_line_if_whitespace(nonstd::string_view text) { + nonstd::string_view result = text; + while (!result.empty()) { + char ch = result.back(); + if (ch == ' ' || ch == '\t') { + result.remove_suffix(1); + } else if (ch == '\n' || ch == '\r') { + break; + } else { + return text; + } + } + return result; + } + +public: + explicit Lexer(const LexerConfig &config) : config(config), state(State::Text), minus_state(MinusState::Number) {} + + SourceLocation current_position() const { + return get_source_location(m_in, tok_start); + } + + void start(nonstd::string_view input) { + m_in = input; + tok_start = 0; + pos = 0; + state = State::Text; + minus_state = MinusState::Number; + + // Consume byte order mark (BOM) for UTF-8 + if (inja::string_view::starts_with(m_in, "\xEF\xBB\xBF")) { + m_in = m_in.substr(3); + } + } + + Token scan() { + tok_start = pos; + + again: + if (tok_start >= m_in.size()) { + return make_token(Token::Kind::Eof); + } + + switch (state) { + default: + case State::Text: { + // fast-scan to first open character + size_t open_start = m_in.substr(pos).find_first_of(config.open_chars); + if (open_start == nonstd::string_view::npos) { + // didn't find open, return remaining text as text token + pos = m_in.size(); + return make_token(Token::Kind::Text); + } + pos += open_start; + + // try to match one of the opening sequences, and get the close + nonstd::string_view open_str = m_in.substr(pos); + bool must_lstrip = false; + if (inja::string_view::starts_with(open_str, config.expression_open)) { + if (inja::string_view::starts_with(open_str, config.expression_open_force_lstrip)) { + state = State::ExpressionStartForceLstrip; + must_lstrip = true; + } else { + state = State::ExpressionStart; + } + } else if (inja::string_view::starts_with(open_str, config.statement_open)) { + if (inja::string_view::starts_with(open_str, config.statement_open_no_lstrip)) { + state = State::StatementStartNoLstrip; + } else if (inja::string_view::starts_with(open_str, config.statement_open_force_lstrip )) { + state = State::StatementStartForceLstrip; + must_lstrip = true; + } else { + state = State::StatementStart; + must_lstrip = config.lstrip_blocks; + } + } else if (inja::string_view::starts_with(open_str, config.comment_open)) { + state = State::CommentStart; + must_lstrip = config.lstrip_blocks; + } else if ((pos == 0 || m_in[pos - 1] == '\n') && inja::string_view::starts_with(open_str, config.line_statement)) { + state = State::LineStart; + } else { + pos += 1; // wasn't actually an opening sequence + goto again; + } + + nonstd::string_view text = string_view::slice(m_in, tok_start, pos); + if (must_lstrip) { + text = clear_final_line_if_whitespace(text); + } + + if (text.empty()) { + goto again; // don't generate empty token + } + return Token(Token::Kind::Text, text); + } + case State::ExpressionStart: { + state = State::ExpressionBody; + pos += config.expression_open.size(); + return make_token(Token::Kind::ExpressionOpen); + } + case State::ExpressionStartForceLstrip: { + state = State::ExpressionBody; + pos += config.expression_open_force_lstrip.size(); + return make_token(Token::Kind::ExpressionOpen); + } + case State::LineStart: { + state = State::LineBody; + pos += config.line_statement.size(); + return make_token(Token::Kind::LineStatementOpen); + } + case State::StatementStart: { + state = State::StatementBody; + pos += config.statement_open.size(); + return make_token(Token::Kind::StatementOpen); + } + case State::StatementStartNoLstrip: { + state = State::StatementBody; + pos += config.statement_open_no_lstrip.size(); + return make_token(Token::Kind::StatementOpen); + } + case State::StatementStartForceLstrip: { + state = State::StatementBody; + pos += config.statement_open_force_lstrip.size(); + return make_token(Token::Kind::StatementOpen); + } + case State::CommentStart: { + state = State::CommentBody; + pos += config.comment_open.size(); + return make_token(Token::Kind::CommentOpen); + } + case State::ExpressionBody: + return scan_body(config.expression_close, Token::Kind::ExpressionClose, config.expression_close_force_rstrip); + case State::LineBody: + return scan_body("\n", Token::Kind::LineStatementClose); + case State::StatementBody: + return scan_body(config.statement_close, Token::Kind::StatementClose, config.statement_close_force_rstrip, config.trim_blocks); + case State::CommentBody: { + // fast-scan to comment close + size_t end = m_in.substr(pos).find(config.comment_close); + if (end == nonstd::string_view::npos) { + pos = m_in.size(); + return make_token(Token::Kind::Eof); + } + // return the entire comment in the close token + state = State::Text; + pos += end + config.comment_close.size(); + Token tok = make_token(Token::Kind::CommentClose); + if (config.trim_blocks) { + skip_whitespaces_and_first_newline(); + } + return tok; + } + } + } + + const LexerConfig &get_config() const { + return config; + } +}; + +} // namespace inja + +#endif // INCLUDE_INJA_LEXER_HPP_ diff --git a/isis/src/base/apps/topds4/main.cpp b/isis/src/base/apps/topds4/main.cpp new file mode 100644 index 0000000000..ae335f60f4 --- /dev/null +++ b/isis/src/base/apps/topds4/main.cpp @@ -0,0 +1,12 @@ +#include "Isis.h" + +#include "topds4.h" + +using namespace std; +using namespace Isis; + +void IsisMain() { + UserInterface &ui = Application::GetUserInterface(); + PvlGroup results = topds4(ui); + Application::Log(results); +} diff --git a/isis/src/base/apps/topds4/node.h b/isis/src/base/apps/topds4/node.h new file mode 100644 index 0000000000..d7aa441871 --- /dev/null +++ b/isis/src/base/apps/topds4/node.h @@ -0,0 +1,328 @@ +// Copyright (c) 2020 Pantor. All rights reserved. + +#ifndef INCLUDE_INJA_NODE_HPP_ +#define INCLUDE_INJA_NODE_HPP_ + +#include +#include + +#include + +#include "function_storage.h" +#include "string_view.h" + + +namespace inja { + +class NodeVisitor; +class BlockNode; +class TextNode; +class ExpressionNode; +class LiteralNode; +class JsonNode; +class FunctionNode; +class ExpressionListNode; +class StatementNode; +class ForStatementNode; +class ForArrayStatementNode; +class ForObjectStatementNode; +class IfStatementNode; +class IncludeStatementNode; +class SetStatementNode; + + +class NodeVisitor { +public: + virtual void visit(const BlockNode& node) = 0; + virtual void visit(const TextNode& node) = 0; + virtual void visit(const ExpressionNode& node) = 0; + virtual void visit(const LiteralNode& node) = 0; + virtual void visit(const JsonNode& node) = 0; + virtual void visit(const FunctionNode& node) = 0; + virtual void visit(const ExpressionListNode& node) = 0; + virtual void visit(const StatementNode& node) = 0; + virtual void visit(const ForStatementNode& node) = 0; + virtual void visit(const ForArrayStatementNode& node) = 0; + virtual void visit(const ForObjectStatementNode& node) = 0; + virtual void visit(const IfStatementNode& node) = 0; + virtual void visit(const IncludeStatementNode& node) = 0; + virtual void visit(const SetStatementNode& node) = 0; +}; + +/*! + * \brief Base node class for the abstract syntax tree (AST). + */ +class AstNode { +public: + virtual void accept(NodeVisitor& v) const = 0; + + size_t pos; + + AstNode(size_t pos) : pos(pos) { } + virtual ~AstNode() { }; +}; + + +class BlockNode : public AstNode { +public: + std::vector> nodes; + + explicit BlockNode() : AstNode(0) {} + + void accept(NodeVisitor& v) const { + v.visit(*this); + } +}; + +class TextNode : public AstNode { +public: + const size_t length; + + explicit TextNode(size_t pos, size_t length): AstNode(pos), length(length) { } + + void accept(NodeVisitor& v) const { + v.visit(*this); + } +}; + +class ExpressionNode : public AstNode { +public: + explicit ExpressionNode(size_t pos) : AstNode(pos) {} + + void accept(NodeVisitor& v) const { + v.visit(*this); + } +}; + +class LiteralNode : public ExpressionNode { +public: + const nlohmann::json value; + + explicit LiteralNode(const nlohmann::json& value, size_t pos) : ExpressionNode(pos), value(value) { } + + void accept(NodeVisitor& v) const { + v.visit(*this); + } +}; + +class JsonNode : public ExpressionNode { +public: + const std::string name; + const json::json_pointer ptr; + + static std::string convert_dot_to_json_ptr(nonstd::string_view ptr_name) { + std::string result; + do { + nonstd::string_view part; + std::tie(part, ptr_name) = string_view::split(ptr_name, '.'); + result.push_back('/'); + result.append(part.begin(), part.end()); + } while (!ptr_name.empty()); + return result; + } + + explicit JsonNode(nonstd::string_view ptr_name, size_t pos) : ExpressionNode(pos), name(ptr_name), ptr(json::json_pointer(convert_dot_to_json_ptr(ptr_name))) { } + + void accept(NodeVisitor& v) const { + v.visit(*this); + } +}; + +class FunctionNode : public ExpressionNode { + using Op = FunctionStorage::Operation; + +public: + enum class Associativity { + Left, + Right, + }; + + unsigned int precedence; + Associativity associativity; + + Op operation; + + std::string name; + int number_args; // Should also be negative -> -1 for unknown number + CallbackFunction callback; + + explicit FunctionNode(nonstd::string_view name, size_t pos) : ExpressionNode(pos), precedence(8), associativity(Associativity::Left), operation(Op::Callback), name(name), number_args(1) { } + explicit FunctionNode(Op operation, size_t pos) : ExpressionNode(pos), operation(operation), number_args(1) { + switch (operation) { + case Op::Not: { + precedence = 4; + associativity = Associativity::Left; + } break; + case Op::And: { + precedence = 1; + associativity = Associativity::Left; + } break; + case Op::Or: { + precedence = 1; + associativity = Associativity::Left; + } break; + case Op::In: { + precedence = 2; + associativity = Associativity::Left; + } break; + case Op::Equal: { + precedence = 2; + associativity = Associativity::Left; + } break; + case Op::NotEqual: { + precedence = 2; + associativity = Associativity::Left; + } break; + case Op::Greater: { + precedence = 2; + associativity = Associativity::Left; + } break; + case Op::GreaterEqual: { + precedence = 2; + associativity = Associativity::Left; + } break; + case Op::Less: { + precedence = 2; + associativity = Associativity::Left; + } break; + case Op::LessEqual: { + precedence = 2; + associativity = Associativity::Left; + } break; + case Op::Add: { + precedence = 3; + associativity = Associativity::Left; + } break; + case Op::Subtract: { + precedence = 3; + associativity = Associativity::Left; + } break; + case Op::Multiplication: { + precedence = 4; + associativity = Associativity::Left; + } break; + case Op::Division: { + precedence = 4; + associativity = Associativity::Left; + } break; + case Op::Power: { + precedence = 5; + associativity = Associativity::Right; + } break; + case Op::Modulo: { + precedence = 4; + associativity = Associativity::Left; + } break; + case Op::AtId: { + precedence = 8; + associativity = Associativity::Left; + } break; + default: { + precedence = 1; + associativity = Associativity::Left; + } + } + } + + void accept(NodeVisitor& v) const { + v.visit(*this); + } +}; + +class ExpressionListNode : public AstNode { +public: + std::vector> rpn_output; + + explicit ExpressionListNode() : AstNode(0) { } + explicit ExpressionListNode(size_t pos) : AstNode(pos) { } + + void accept(NodeVisitor& v) const { + v.visit(*this); + } +}; + +class StatementNode : public AstNode { +public: + StatementNode(size_t pos) : AstNode(pos) { } + + virtual void accept(NodeVisitor& v) const = 0; +}; + +class ForStatementNode : public StatementNode { +public: + ExpressionListNode condition; + BlockNode body; + BlockNode *const parent; + + ForStatementNode(BlockNode *const parent, size_t pos) : StatementNode(pos), parent(parent) { } + + virtual void accept(NodeVisitor& v) const = 0; +}; + +class ForArrayStatementNode : public ForStatementNode { +public: + const std::string value; + + explicit ForArrayStatementNode(const std::string& value, BlockNode *const parent, size_t pos) : ForStatementNode(parent, pos), value(value) { } + + void accept(NodeVisitor& v) const { + v.visit(*this); + } +}; + +class ForObjectStatementNode : public ForStatementNode { +public: + const std::string key; + const std::string value; + + explicit ForObjectStatementNode(const std::string& key, const std::string& value, BlockNode *const parent, size_t pos) : ForStatementNode(parent, pos), key(key), value(value) { } + + void accept(NodeVisitor& v) const { + v.visit(*this); + } +}; + +class IfStatementNode : public StatementNode { +public: + ExpressionListNode condition; + BlockNode true_statement; + BlockNode false_statement; + BlockNode *const parent; + + const bool is_nested; + bool has_false_statement {false}; + + explicit IfStatementNode(BlockNode *const parent, size_t pos) : StatementNode(pos), parent(parent), is_nested(false) { } + explicit IfStatementNode(bool is_nested, BlockNode *const parent, size_t pos) : StatementNode(pos), parent(parent), is_nested(is_nested) { } + + void accept(NodeVisitor& v) const { + v.visit(*this); + } +}; + +class IncludeStatementNode : public StatementNode { +public: + const std::string file; + + explicit IncludeStatementNode(const std::string& file, size_t pos) : StatementNode(pos), file(file) { } + + void accept(NodeVisitor& v) const { + v.visit(*this); + }; +}; + +class SetStatementNode : public StatementNode { +public: + const std::string key; + ExpressionListNode expression; + + explicit SetStatementNode(const std::string& key, size_t pos) : StatementNode(pos), key(key) { } + + void accept(NodeVisitor& v) const { + v.visit(*this); + }; +}; + +} // namespace inja + +#endif // INCLUDE_INJA_NODE_HPP_ diff --git a/isis/src/base/apps/topds4/parser.h b/isis/src/base/apps/topds4/parser.h new file mode 100644 index 0000000000..c333668f2c --- /dev/null +++ b/isis/src/base/apps/topds4/parser.h @@ -0,0 +1,582 @@ +// Copyright (c) 2020 Pantor. All rights reserved. + +#ifndef INCLUDE_INJA_PARSER_HPP_ +#define INCLUDE_INJA_PARSER_HPP_ + +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "exceptions.h" +#include "function_storage.h" +#include "lexer.h" +#include "node.h" +#include "template2.h" +#include "token.h" +#include "utils.h" + +#include + +namespace inja { + +/*! + * \brief Class for parsing an inja Template. + */ +class Parser { + const ParserConfig &config; + + Lexer lexer; + TemplateStorage &template_storage; + const FunctionStorage &function_storage; + + Token tok, peek_tok; + bool have_peek_tok {false}; + + size_t current_paren_level {0}; + size_t current_bracket_level {0}; + size_t current_brace_level {0}; + + nonstd::string_view json_literal_start; + + BlockNode *current_block {nullptr}; + ExpressionListNode *current_expression_list {nullptr}; + std::stack> function_stack; + + std::stack> operator_stack; + std::stack if_statement_stack; + std::stack for_statement_stack; + + void throw_parser_error(const std::string &message) { + throw ParserError(message, lexer.current_position()); + } + + void get_next_token() { + if (have_peek_tok) { + tok = peek_tok; + have_peek_tok = false; + } else { + tok = lexer.scan(); + } + } + + void get_peek_token() { + if (!have_peek_tok) { + peek_tok = lexer.scan(); + have_peek_tok = true; + } + } + + void add_json_literal(const char* content_ptr) { + nonstd::string_view json_text(json_literal_start.data(), tok.text.data() - json_literal_start.data() + tok.text.size()); + current_expression_list->rpn_output.emplace_back(std::make_shared(json::parse(json_text), json_text.data() - content_ptr)); + } + + bool parse_expression(Template &tmpl, Token::Kind closing) { + while (tok.kind != closing && tok.kind != Token::Kind::Eof) { + // Literals + switch (tok.kind) { + case Token::Kind::String: { + if (current_brace_level == 0 && current_bracket_level == 0) { + json_literal_start = tok.text; + add_json_literal(tmpl.content.c_str()); + } + + } break; + case Token::Kind::Number: { + if (current_brace_level == 0 && current_bracket_level == 0) { + json_literal_start = tok.text; + add_json_literal(tmpl.content.c_str()); + } + + } break; + case Token::Kind::LeftBracket: { + if (current_brace_level == 0 && current_bracket_level == 0) { + json_literal_start = tok.text; + } + current_bracket_level += 1; + + } break; + case Token::Kind::LeftBrace: { + if (current_brace_level == 0 && current_bracket_level == 0) { + json_literal_start = tok.text; + } + current_brace_level += 1; + + } break; + case Token::Kind::RightBracket: { + if (current_bracket_level == 0) { + throw_parser_error("unexpected ']'"); + } + + current_bracket_level -= 1; + if (current_brace_level == 0 && current_bracket_level == 0) { + add_json_literal(tmpl.content.c_str()); + } + + } break; + case Token::Kind::RightBrace: { + if (current_brace_level == 0) { + throw_parser_error("unexpected '}'"); + } + + current_brace_level -= 1; + if (current_brace_level == 0 && current_bracket_level == 0) { + add_json_literal(tmpl.content.c_str()); + } + + } break; + case Token::Kind::Id: { + get_peek_token(); + + // Json Literal + if (tok.text == static_cast("true") || tok.text == static_cast("false") || tok.text == static_cast("null")) { + if (current_brace_level == 0 && current_bracket_level == 0) { + json_literal_start = tok.text; + add_json_literal(tmpl.content.c_str()); + } + + // Operator + } else if (tok.text == "and" || tok.text == "or" || tok.text == "in" || tok.text == "not") { + goto parse_operator; + + // Functions + } else if (peek_tok.kind == Token::Kind::LeftParen) { + operator_stack.emplace(std::make_shared(static_cast(tok.text), tok.text.data() - tmpl.content.c_str())); + function_stack.emplace(operator_stack.top().get(), current_paren_level); + + // Variables + } else { + current_expression_list->rpn_output.emplace_back(std::make_shared(static_cast(tok.text), tok.text.data() - tmpl.content.c_str())); + } + + // Operators + } break; + case Token::Kind::Equal: + case Token::Kind::NotEqual: + case Token::Kind::GreaterThan: + case Token::Kind::GreaterEqual: + case Token::Kind::LessThan: + case Token::Kind::LessEqual: + case Token::Kind::Plus: + case Token::Kind::Minus: + case Token::Kind::Times: + case Token::Kind::Slash: + case Token::Kind::Power: + case Token::Kind::Percent: + case Token::Kind::Dot: { + + parse_operator: + FunctionStorage::Operation operation; + switch (tok.kind) { + case Token::Kind::Id: { + if (tok.text == "and") { + operation = FunctionStorage::Operation::And; + } else if (tok.text == "or") { + operation = FunctionStorage::Operation::Or; + } else if (tok.text == "in") { + operation = FunctionStorage::Operation::In; + } else if (tok.text == "not") { + operation = FunctionStorage::Operation::Not; + } else { + throw_parser_error("unknown operator in parser."); + } + } break; + case Token::Kind::Equal: { + operation = FunctionStorage::Operation::Equal; + } break; + case Token::Kind::NotEqual: { + operation = FunctionStorage::Operation::NotEqual; + } break; + case Token::Kind::GreaterThan: { + operation = FunctionStorage::Operation::Greater; + } break; + case Token::Kind::GreaterEqual: { + operation = FunctionStorage::Operation::GreaterEqual; + } break; + case Token::Kind::LessThan: { + operation = FunctionStorage::Operation::Less; + } break; + case Token::Kind::LessEqual: { + operation = FunctionStorage::Operation::LessEqual; + } break; + case Token::Kind::Plus: { + operation = FunctionStorage::Operation::Add; + } break; + case Token::Kind::Minus: { + operation = FunctionStorage::Operation::Subtract; + } break; + case Token::Kind::Times: { + operation = FunctionStorage::Operation::Multiplication; + } break; + case Token::Kind::Slash: { + operation = FunctionStorage::Operation::Division; + } break; + case Token::Kind::Power: { + operation = FunctionStorage::Operation::Power; + } break; + case Token::Kind::Percent: { + operation = FunctionStorage::Operation::Modulo; + } break; + case Token::Kind::Dot: { + operation = FunctionStorage::Operation::AtId; + } break; + default: { + throw_parser_error("unknown operator in parser."); + } + } + auto function_node = std::make_shared(operation, tok.text.data() - tmpl.content.c_str()); + + while (!operator_stack.empty() && ((operator_stack.top()->precedence > function_node->precedence) || (operator_stack.top()->precedence == function_node->precedence && function_node->associativity == FunctionNode::Associativity::Left)) && (operator_stack.top()->operation != FunctionStorage::Operation::ParenLeft)) { + current_expression_list->rpn_output.emplace_back(operator_stack.top()); + operator_stack.pop(); + } + + operator_stack.emplace(function_node); + + } break; + case Token::Kind::Comma: { + if (current_brace_level == 0 && current_bracket_level == 0) { + if (function_stack.empty()) { + throw_parser_error("unexpected ','"); + } + + function_stack.top().first->number_args += 1; + } + + } break; + case Token::Kind::Colon: { + if (current_brace_level == 0 && current_bracket_level == 0) { + throw_parser_error("unexpected ':'"); + } + + } break; + case Token::Kind::LeftParen: { + current_paren_level += 1; + operator_stack.emplace(std::make_shared(FunctionStorage::Operation::ParenLeft, tok.text.data() - tmpl.content.c_str())); + + get_peek_token(); + if (peek_tok.kind == Token::Kind::RightParen) { + if (!function_stack.empty() && function_stack.top().second == current_paren_level - 1) { + function_stack.top().first->number_args = 0; + } + } + + } break; + case Token::Kind::RightParen: { + current_paren_level -= 1; + while (!operator_stack.empty() && operator_stack.top()->operation != FunctionStorage::Operation::ParenLeft) { + current_expression_list->rpn_output.emplace_back(operator_stack.top()); + operator_stack.pop(); + } + + if (!operator_stack.empty() && operator_stack.top()->operation == FunctionStorage::Operation::ParenLeft) { + operator_stack.pop(); + } + + if (!function_stack.empty() && function_stack.top().second == current_paren_level) { + auto func = function_stack.top().first; + auto function_data = function_storage.find_function(func->name, func->number_args); + if (function_data.operation == FunctionStorage::Operation::None) { + throw_parser_error("unknown function " + func->name); + } + func->operation = function_data.operation; + if (function_data.operation == FunctionStorage::Operation::Callback) { + func->callback = function_data.callback; + } + + if (operator_stack.empty()) { + throw_parser_error("internal error at function " + func->name); + } + + current_expression_list->rpn_output.emplace_back(operator_stack.top()); + operator_stack.pop(); + function_stack.pop(); + } + } + default: + break; + } + + get_next_token(); + } + + while (!operator_stack.empty()) { + current_expression_list->rpn_output.emplace_back(operator_stack.top()); + operator_stack.pop(); + } + + return true; + } + + bool parse_statement(Template &tmpl, Token::Kind closing, nonstd::string_view path) { + if (tok.kind != Token::Kind::Id) { + return false; + } + + if (tok.text == static_cast("if")) { + get_next_token(); + + auto if_statement_node = std::make_shared(current_block, tok.text.data() - tmpl.content.c_str()); + current_block->nodes.emplace_back(if_statement_node); + if_statement_stack.emplace(if_statement_node.get()); + current_block = &if_statement_node->true_statement; + current_expression_list = &if_statement_node->condition; + + if (!parse_expression(tmpl, closing)) { + return false; + } + + } else if (tok.text == static_cast("else")) { + if (if_statement_stack.empty()) { + throw_parser_error("else without matching if"); + } + auto &if_statement_data = if_statement_stack.top(); + get_next_token(); + + if_statement_data->has_false_statement = true; + current_block = &if_statement_data->false_statement; + + // Chained else if + if (tok.kind == Token::Kind::Id && tok.text == static_cast("if")) { + get_next_token(); + + auto if_statement_node = std::make_shared(true, current_block, tok.text.data() - tmpl.content.c_str()); + current_block->nodes.emplace_back(if_statement_node); + if_statement_stack.emplace(if_statement_node.get()); + current_block = &if_statement_node->true_statement; + current_expression_list = &if_statement_node->condition; + + if (!parse_expression(tmpl, closing)) { + return false; + } + } + + } else if (tok.text == static_cast("endif")) { + if (if_statement_stack.empty()) { + throw_parser_error("endif without matching if"); + } + + // Nested if statements + while (if_statement_stack.top()->is_nested) { + if_statement_stack.pop(); + } + + auto &if_statement_data = if_statement_stack.top(); + get_next_token(); + + current_block = if_statement_data->parent; + if_statement_stack.pop(); + + } else if (tok.text == static_cast("for")) { + get_next_token(); + + // options: for a in arr; for a, b in obj + if (tok.kind != Token::Kind::Id) { + throw_parser_error("expected id, got '" + tok.describe() + "'"); + } + + Token value_token = tok; + get_next_token(); + + // Object type + std::shared_ptr for_statement_node; + if (tok.kind == Token::Kind::Comma) { + get_next_token(); + if (tok.kind != Token::Kind::Id) { + throw_parser_error("expected id, got '" + tok.describe() + "'"); + } + + Token key_token = std::move(value_token); + value_token = tok; + get_next_token(); + + for_statement_node = std::make_shared(static_cast(key_token.text), static_cast(value_token.text), current_block, tok.text.data() - tmpl.content.c_str()); + + // Array type + } else { + for_statement_node = std::make_shared(static_cast(value_token.text), current_block, tok.text.data() - tmpl.content.c_str()); + } + + current_block->nodes.emplace_back(for_statement_node); + for_statement_stack.emplace(for_statement_node.get()); + current_block = &for_statement_node->body; + current_expression_list = &for_statement_node->condition; + + if (tok.kind != Token::Kind::Id || tok.text != static_cast("in")) { + throw_parser_error("expected 'in', got '" + tok.describe() + "'"); + } + get_next_token(); + + if (!parse_expression(tmpl, closing)) { + return false; + } + + } else if (tok.text == static_cast("endfor")) { + if (for_statement_stack.empty()) { + throw_parser_error("endfor without matching for"); + } + + auto &for_statement_data = for_statement_stack.top(); + get_next_token(); + + current_block = for_statement_data->parent; + for_statement_stack.pop(); + + } else if (tok.text == static_cast("include")) { + get_next_token(); + + if (tok.kind != Token::Kind::String) { + throw_parser_error("expected string, got '" + tok.describe() + "'"); + } + + // Build the relative path + json json_name = json::parse(tok.text); + std::string pathname = static_cast(path); + pathname += json_name.get_ref(); + if (pathname.compare(0, 2, "./") == 0) { + pathname.erase(0, 2); + } + // sys::path::remove_dots(pathname, true, sys::path::Style::posix); + + if (config.search_included_templates_in_files && template_storage.find(pathname) == template_storage.end()) { + auto include_template = Template(load_file(pathname)); + template_storage.emplace(pathname, include_template); + parse_into_template(template_storage[pathname], pathname); + } + + current_block->nodes.emplace_back(std::make_shared(pathname, tok.text.data() - tmpl.content.c_str())); + + get_next_token(); + + } else if (tok.text == static_cast("set")) { + get_next_token(); + + if (tok.kind != Token::Kind::Id) { + throw_parser_error("expected variable name, got '" + tok.describe() + "'"); + } + + std::string key = static_cast(tok.text); + get_next_token(); + + auto set_statement_node = std::make_shared(key, tok.text.data() - tmpl.content.c_str()); + current_block->nodes.emplace_back(set_statement_node); + current_expression_list = &set_statement_node->expression; + + if (tok.text != static_cast("=")) { + throw_parser_error("expected '=', got '" + tok.describe() + "'"); + } + get_next_token(); + + if (!parse_expression(tmpl, closing)) { + return false; + } + + } else { + return false; + } + return true; + } + + void parse_into(Template &tmpl, nonstd::string_view path) { + lexer.start(tmpl.content); + current_block = &tmpl.root; + + for (;;) { + get_next_token(); + switch (tok.kind) { + case Token::Kind::Eof: { + if (!if_statement_stack.empty()) { + throw_parser_error("unmatched if"); + } + if (!for_statement_stack.empty()) { + throw_parser_error("unmatched for"); + } + } return; + case Token::Kind::Text: { + current_block->nodes.emplace_back(std::make_shared(tok.text.data() - tmpl.content.c_str(), tok.text.size())); + } break; + case Token::Kind::StatementOpen: { + get_next_token(); + if (!parse_statement(tmpl, Token::Kind::StatementClose, path)) { + throw_parser_error("expected statement, got '" + tok.describe() + "'"); + } + if (tok.kind != Token::Kind::StatementClose) { + throw_parser_error("expected statement close, got '" + tok.describe() + "'"); + } + } break; + case Token::Kind::LineStatementOpen: { + get_next_token(); + if (!parse_statement(tmpl, Token::Kind::LineStatementClose, path)) { + throw_parser_error("expected statement, got '" + tok.describe() + "'"); + } + if (tok.kind != Token::Kind::LineStatementClose && tok.kind != Token::Kind::Eof) { + throw_parser_error("expected line statement close, got '" + tok.describe() + "'"); + } + } break; + case Token::Kind::ExpressionOpen: { + get_next_token(); + + auto expression_list_node = std::make_shared(tok.text.data() - tmpl.content.c_str()); + current_block->nodes.emplace_back(expression_list_node); + current_expression_list = expression_list_node.get(); + + if (!parse_expression(tmpl, Token::Kind::ExpressionClose)) { + throw_parser_error("expected expression, got '" + tok.describe() + "'"); + } + + if (tok.kind != Token::Kind::ExpressionClose) { + throw_parser_error("expected expression close, got '" + tok.describe() + "'"); + } + } break; + case Token::Kind::CommentOpen: { + get_next_token(); + if (tok.kind != Token::Kind::CommentClose) { + throw_parser_error("expected comment close, got '" + tok.describe() + "'"); + } + } break; + default: { + throw_parser_error("unexpected token '" + tok.describe() + "'"); + } break; + } + } + } + + +public: + explicit Parser(const ParserConfig &parser_config, const LexerConfig &lexer_config, + TemplateStorage &template_storage, const FunctionStorage &function_storage) + : config(parser_config), lexer(lexer_config), template_storage(template_storage), function_storage(function_storage) { } + + Template parse(nonstd::string_view input, nonstd::string_view path) { + auto result = Template(static_cast(input)); + parse_into(result, path); + return result; + } + + Template parse(nonstd::string_view input) { + return parse(input, "./"); + } + + void parse_into_template(Template& tmpl, nonstd::string_view filename) { + nonstd::string_view path = filename.substr(0, filename.find_last_of("/\\") + 1); + + // StringRef path = sys::path::parent_path(filename); + auto sub_parser = Parser(config, lexer.get_config(), template_storage, function_storage); + sub_parser.parse_into(tmpl, path); + } + + std::string load_file(nonstd::string_view filename) { + std::ifstream file; + open_file_or_throw(static_cast(filename), file); + std::string text((std::istreambuf_iterator(file)), std::istreambuf_iterator()); + return text; + } +}; + +} // namespace inja + +#endif // INCLUDE_INJA_PARSER_HPP_ diff --git a/isis/src/base/apps/topds4/renderer.h b/isis/src/base/apps/topds4/renderer.h new file mode 100644 index 0000000000..076bb780af --- /dev/null +++ b/isis/src/base/apps/topds4/renderer.h @@ -0,0 +1,607 @@ +// Copyright (c) 2020 Pantor. All rights reserved. + +#ifndef INCLUDE_INJA_RENDERER_HPP_ +#define INCLUDE_INJA_RENDERER_HPP_ + +#include +#include +#include +#include +#include + +#include + +#include "config.h" +#include "exceptions.h" +#include "node.h" +#include "template2.h" +#include "utils.h" + +namespace inja { + +/*! + * \brief Class for rendering a Template with data. + */ +class Renderer : public NodeVisitor { + using Op = FunctionStorage::Operation; + + const RenderConfig config; + const Template *current_template; + const TemplateStorage &template_storage; + const FunctionStorage &function_storage; + + const json *json_input; + std::ostream *output_stream; + + json json_additional_data; + json* current_loop_data = &json_additional_data["loop"]; + + std::vector> json_tmp_stack; + std::stack json_eval_stack; + std::stack not_found_stack; + + bool truthy(const json* data) const { + if (data->is_boolean()) { + return data->get(); + } else if (data->is_number()) { + return (*data != 0); + } else if (data->is_null()) { + return false; + } + return !data->empty(); + } + + void print_json(const std::shared_ptr value) { + if (value->is_string()) { + *output_stream << value->get_ref(); + } else if (value->is_number_integer()) { + *output_stream << value->get(); + } else if (value->is_null()) { + } else { + *output_stream << value->dump(); + } + } + + const std::shared_ptr eval_expression_list(const ExpressionListNode& expression_list) { + for (auto& expression : expression_list.rpn_output) { + expression->accept(*this); + } + + if (json_eval_stack.empty()) { + throw_renderer_error("empty expression", expression_list); + } else if (json_eval_stack.size() != 1) { + throw_renderer_error("malformed expression", expression_list); + } + + auto result = json_eval_stack.top(); + json_eval_stack.pop(); + + if (!result) { + if (not_found_stack.empty()) { + throw_renderer_error("expression could not be evaluated", expression_list); + } + + auto node = not_found_stack.top(); + not_found_stack.pop(); + + throw_renderer_error("variable '" + static_cast(node->name) + "' not found", *node); + } + return std::make_shared(*result); + } + + void throw_renderer_error(const std::string &message, const AstNode& node) { + SourceLocation loc = get_source_location(current_template->content, node.pos); + throw RenderError(message, loc); + } + + template + std::array get_arguments(const AstNode& node) { + if (json_eval_stack.size() < N) { + throw_renderer_error("function needs " + std::to_string(N) + " variables, but has only found " + std::to_string(json_eval_stack.size()), node); + } + + std::array result; + for (size_t i = 0; i < N; i += 1) { + result[N - i - 1] = json_eval_stack.top(); + json_eval_stack.pop(); + + if (!result[N - i - 1]) { + auto json_node = not_found_stack.top(); + not_found_stack.pop(); + + if (throw_not_found) { + throw_renderer_error("variable '" + static_cast(json_node->name) + "' not found", *json_node); + } + } + } + return result; + } + + template + Arguments get_argument_vector(size_t N, const AstNode& node) { + Arguments result {N}; + for (size_t i = 0; i < N; i += 1) { + result[N - i - 1] = json_eval_stack.top(); + json_eval_stack.pop(); + + if (!result[N - i - 1]) { + auto json_node = not_found_stack.top(); + not_found_stack.pop(); + + if (throw_not_found) { + throw_renderer_error("variable '" + static_cast(json_node->name) + "' not found", *json_node); + } + } + } + return result; + } + + void visit(const BlockNode& node) { + for (auto& n : node.nodes) { + n->accept(*this); + } + } + + void visit(const TextNode& node) { + output_stream->write(current_template->content.c_str() + node.pos, node.length); + } + + void visit(const ExpressionNode&) { } + + void visit(const LiteralNode& node) { + json_eval_stack.push(&node.value); + } + + void visit(const JsonNode& node) { + if (json_additional_data.contains(node.ptr)) { + json_eval_stack.push(&(json_additional_data[node.ptr])); + + } else if (json_input->contains(node.ptr)) { + json_eval_stack.push(&(*json_input)[node.ptr]); + + } else { + // Try to evaluate as a no-argument callback + auto function_data = function_storage.find_function(node.name, 0); + if (function_data.operation == FunctionStorage::Operation::Callback) { + Arguments empty_args {}; + auto value = std::make_shared(function_data.callback(empty_args)); + json_tmp_stack.push_back(value); + json_eval_stack.push(value.get()); + + } else { + json_eval_stack.push(nullptr); + not_found_stack.emplace(&node); + } + } + } + + void visit(const FunctionNode& node) { + std::shared_ptr result_ptr; + + switch (node.operation) { + case Op::Not: { + auto args = get_arguments<1>(node); + result_ptr = std::make_shared(!truthy(args[0])); + json_tmp_stack.push_back(result_ptr); + json_eval_stack.push(result_ptr.get()); + } break; + case Op::And: { + auto args = get_arguments<2>(node); + result_ptr = std::make_shared(truthy(args[0]) && truthy(args[1])); + json_tmp_stack.push_back(result_ptr); + json_eval_stack.push(result_ptr.get()); + } break; + case Op::Or: { + auto args = get_arguments<2>(node); + result_ptr = std::make_shared(truthy(args[0]) || truthy(args[1])); + json_tmp_stack.push_back(result_ptr); + json_eval_stack.push(result_ptr.get()); + } break; + case Op::In: { + auto args = get_arguments<2>(node); + result_ptr = std::make_shared(std::find(args[1]->begin(), args[1]->end(), *args[0]) != args[1]->end()); + json_tmp_stack.push_back(result_ptr); + json_eval_stack.push(result_ptr.get()); + } break; + case Op::Equal: { + auto args = get_arguments<2>(node); + result_ptr = std::make_shared(*args[0] == *args[1]); + json_tmp_stack.push_back(result_ptr); + json_eval_stack.push(result_ptr.get()); + } break; + case Op::NotEqual: { + auto args = get_arguments<2>(node); + result_ptr = std::make_shared(*args[0] != *args[1]); + json_tmp_stack.push_back(result_ptr); + json_eval_stack.push(result_ptr.get()); + } break; + case Op::Greater: { + auto args = get_arguments<2>(node); + result_ptr = std::make_shared(*args[0] > *args[1]); + json_tmp_stack.push_back(result_ptr); + json_eval_stack.push(result_ptr.get()); + } break; + case Op::GreaterEqual: { + auto args = get_arguments<2>(node); + result_ptr = std::make_shared(*args[0] >= *args[1]); + json_tmp_stack.push_back(result_ptr); + json_eval_stack.push(result_ptr.get()); + } break; + case Op::Less: { + auto args = get_arguments<2>(node); + result_ptr = std::make_shared(*args[0] < *args[1]); + json_tmp_stack.push_back(result_ptr); + json_eval_stack.push(result_ptr.get()); + } break; + case Op::LessEqual: { + auto args = get_arguments<2>(node); + result_ptr = std::make_shared(*args[0] <= *args[1]); + json_tmp_stack.push_back(result_ptr); + json_eval_stack.push(result_ptr.get()); + } break; + case Op::Add: { + auto args = get_arguments<2>(node); + if (args[0]->is_string() && args[1]->is_string()) { + result_ptr = std::make_shared(args[0]->get_ref() + args[1]->get_ref()); + json_tmp_stack.push_back(result_ptr); + } else if (args[0]->is_number_integer() && args[1]->is_number_integer()) { + result_ptr = std::make_shared(args[0]->get() + args[1]->get()); + json_tmp_stack.push_back(result_ptr); + } else { + result_ptr = std::make_shared(args[0]->get() + args[1]->get()); + json_tmp_stack.push_back(result_ptr); + } + json_eval_stack.push(result_ptr.get()); + } break; + case Op::Subtract: { + auto args = get_arguments<2>(node); + if (args[0]->is_number_integer() && args[1]->is_number_integer()) { + result_ptr = std::make_shared(args[0]->get() - args[1]->get()); + json_tmp_stack.push_back(result_ptr); + } else { + result_ptr = std::make_shared(args[0]->get() - args[1]->get()); + json_tmp_stack.push_back(result_ptr); + } + json_eval_stack.push(result_ptr.get()); + } break; + case Op::Multiplication: { + auto args = get_arguments<2>(node); + if (args[0]->is_number_integer() && args[1]->is_number_integer()) { + result_ptr = std::make_shared(args[0]->get() * args[1]->get()); + json_tmp_stack.push_back(result_ptr); + } else { + result_ptr = std::make_shared(args[0]->get() * args[1]->get()); + json_tmp_stack.push_back(result_ptr); + } + json_eval_stack.push(result_ptr.get()); + } break; + case Op::Division: { + auto args = get_arguments<2>(node); + if (args[1]->get() == 0) { + throw_renderer_error("division by zero", node); + } + result_ptr = std::make_shared(args[0]->get() / args[1]->get()); + json_tmp_stack.push_back(result_ptr); + json_eval_stack.push(result_ptr.get()); + } break; + case Op::Power: { + auto args = get_arguments<2>(node); + if (args[0]->is_number_integer() && args[1]->get() >= 0) { + int result = std::pow(args[0]->get(), args[1]->get()); + result_ptr = std::make_shared(std::move(result)); + json_tmp_stack.push_back(result_ptr); + } else { + double result = std::pow(args[0]->get(), args[1]->get()); + result_ptr = std::make_shared(std::move(result)); + json_tmp_stack.push_back(result_ptr); + } + json_eval_stack.push(result_ptr.get()); + } break; + case Op::Modulo: { + auto args = get_arguments<2>(node); + result_ptr = std::make_shared(args[0]->get() % args[1]->get()); + json_tmp_stack.push_back(result_ptr); + json_eval_stack.push(result_ptr.get()); + } break; + case Op::AtId: { + json_eval_stack.pop(); // Pop id nullptr + auto container = get_arguments<1, false>(node)[0]; + if (not_found_stack.empty()) { + throw_renderer_error("could not find element with given name", node); + } + auto id_node = not_found_stack.top(); + not_found_stack.pop(); + json_eval_stack.push(&container->at(id_node->name)); + } break; + case Op::At: { + auto args = get_arguments<2>(node); + json_eval_stack.push(&args[0]->at(args[1]->get())); + } break; + case Op::Default: { + auto default_arg = get_arguments<1>(node)[0]; + auto test_arg = get_arguments<1, false>(node)[0]; + json_eval_stack.push(test_arg ? test_arg : default_arg); + } break; + case Op::DivisibleBy: { + auto args = get_arguments<2>(node); + int divisor = args[1]->get(); + result_ptr = std::make_shared((divisor != 0) && (args[0]->get() % divisor == 0)); + json_tmp_stack.push_back(result_ptr); + json_eval_stack.push(result_ptr.get()); + } break; + case Op::Even: { + result_ptr = std::make_shared(get_arguments<1>(node)[0]->get() % 2 == 0); + json_tmp_stack.push_back(result_ptr); + json_eval_stack.push(result_ptr.get()); + } break; + case Op::Exists: { + auto &&name = get_arguments<1>(node)[0]->get_ref(); + result_ptr = std::make_shared(json_input->contains(json::json_pointer(JsonNode::convert_dot_to_json_ptr(name)))); + json_tmp_stack.push_back(result_ptr); + json_eval_stack.push(result_ptr.get()); + } break; + case Op::ExistsInObject: { + auto args = get_arguments<2>(node); + auto &&name = args[1]->get_ref(); + result_ptr = std::make_shared(args[0]->find(name) != args[0]->end()); + json_tmp_stack.push_back(result_ptr); + json_eval_stack.push(result_ptr.get()); + } break; + case Op::First: { + auto result = &get_arguments<1>(node)[0]->front(); + json_eval_stack.push(result); + } break; + case Op::Float: { + result_ptr = std::make_shared(std::stod(get_arguments<1>(node)[0]->get_ref())); + json_tmp_stack.push_back(result_ptr); + json_eval_stack.push(result_ptr.get()); + } break; + case Op::Int: { + result_ptr = std::make_shared(std::stoi(get_arguments<1>(node)[0]->get_ref())); + json_tmp_stack.push_back(result_ptr); + json_eval_stack.push(result_ptr.get()); + } break; + case Op::Last: { + auto result = &get_arguments<1>(node)[0]->back(); + json_eval_stack.push(result); + } break; + case Op::Length: { + auto val = get_arguments<1>(node)[0]; + if (val->is_string()) { + result_ptr = std::make_shared(val->get_ref().length()); + } else { + result_ptr = std::make_shared(val->size()); + } + json_tmp_stack.push_back(result_ptr); + json_eval_stack.push(result_ptr.get()); + } break; + case Op::Lower: { + std::string result = get_arguments<1>(node)[0]->get(); + std::transform(result.begin(), result.end(), result.begin(), ::tolower); + result_ptr = std::make_shared(std::move(result)); + json_tmp_stack.push_back(result_ptr); + json_eval_stack.push(result_ptr.get()); + } break; + case Op::Max: { + auto args = get_arguments<1>(node); + auto result = std::max_element(args[0]->begin(), args[0]->end()); + json_eval_stack.push(&(*result)); + } break; + case Op::Min: { + auto args = get_arguments<1>(node); + auto result = std::min_element(args[0]->begin(), args[0]->end()); + json_eval_stack.push(&(*result)); + } break; + case Op::Odd: { + result_ptr = std::make_shared(get_arguments<1>(node)[0]->get() % 2 != 0); + json_tmp_stack.push_back(result_ptr); + json_eval_stack.push(result_ptr.get()); + } break; + case Op::Range: { + std::vector result(get_arguments<1>(node)[0]->get()); + std::iota(result.begin(), result.end(), 0); + result_ptr = std::make_shared(std::move(result)); + json_tmp_stack.push_back(result_ptr); + json_eval_stack.push(result_ptr.get()); + } break; + case Op::Round: { + auto args = get_arguments<2>(node); + int precision = args[1]->get(); + double result = std::round(args[0]->get() * std::pow(10.0, precision)) / std::pow(10.0, precision); + result_ptr = std::make_shared(std::move(result)); + json_tmp_stack.push_back(result_ptr); + json_eval_stack.push(result_ptr.get()); + } break; + case Op::Sort: { + result_ptr = std::make_shared(get_arguments<1>(node)[0]->get>()); + std::sort(result_ptr->begin(), result_ptr->end()); + json_tmp_stack.push_back(result_ptr); + json_eval_stack.push(result_ptr.get()); + } break; + case Op::Upper: { + std::string result = get_arguments<1>(node)[0]->get(); + std::transform(result.begin(), result.end(), result.begin(), ::toupper); + result_ptr = std::make_shared(std::move(result)); + json_tmp_stack.push_back(result_ptr); + json_eval_stack.push(result_ptr.get()); + } break; + case Op::IsBoolean: { + result_ptr = std::make_shared(get_arguments<1>(node)[0]->is_boolean()); + json_tmp_stack.push_back(result_ptr); + json_eval_stack.push(result_ptr.get()); + } break; + case Op::IsNumber: { + result_ptr = std::make_shared(get_arguments<1>(node)[0]->is_number()); + json_tmp_stack.push_back(result_ptr); + json_eval_stack.push(result_ptr.get()); + } break; + case Op::IsInteger: { + result_ptr = std::make_shared(get_arguments<1>(node)[0]->is_number_integer()); + json_tmp_stack.push_back(result_ptr); + json_eval_stack.push(result_ptr.get()); + } break; + case Op::IsFloat: { + result_ptr = std::make_shared(get_arguments<1>(node)[0]->is_number_float()); + json_tmp_stack.push_back(result_ptr); + json_eval_stack.push(result_ptr.get()); + } break; + case Op::IsObject: { + result_ptr = std::make_shared(get_arguments<1>(node)[0]->is_object()); + json_tmp_stack.push_back(result_ptr); + json_eval_stack.push(result_ptr.get()); + } break; + case Op::IsArray: { + result_ptr = std::make_shared(get_arguments<1>(node)[0]->is_array()); + json_tmp_stack.push_back(result_ptr); + json_eval_stack.push(result_ptr.get()); + } break; + case Op::IsString: { + result_ptr = std::make_shared(get_arguments<1>(node)[0]->is_string()); + json_tmp_stack.push_back(result_ptr); + json_eval_stack.push(result_ptr.get()); + } break; + case Op::Callback: { + auto args = get_argument_vector(node.number_args, node); + result_ptr = std::make_shared(node.callback(args)); + json_tmp_stack.push_back(result_ptr); + json_eval_stack.push(result_ptr.get()); + } break; + case Op::ParenLeft: + case Op::ParenRight: + case Op::None: + break; + } + } + + void visit(const ExpressionListNode& node) { + print_json(eval_expression_list(node)); + } + + void visit(const StatementNode&) { } + + void visit(const ForStatementNode&) { } + + void visit(const ForArrayStatementNode& node) { + auto result = eval_expression_list(node.condition); + if (!result->is_array()) { + throw_renderer_error("object must be an array", node); + } + + if (!current_loop_data->empty()) { + auto tmp = *current_loop_data; // Because of clang-3 + (*current_loop_data)["parent"] = std::move(tmp); + } + + size_t index = 0; + (*current_loop_data)["is_first"] = true; + (*current_loop_data)["is_last"] = (result->size() <= 1); + for (auto it = result->begin(); it != result->end(); ++it) { + json_additional_data[static_cast(node.value)] = *it; + + (*current_loop_data)["index"] = index; + (*current_loop_data)["index1"] = index + 1; + if (index == 1) { + (*current_loop_data)["is_first"] = false; + } + if (index == result->size() - 1) { + (*current_loop_data)["is_last"] = true; + } + + node.body.accept(*this); + ++index; + } + + json_additional_data[static_cast(node.value)].clear(); + if (!(*current_loop_data)["parent"].empty()) { + auto tmp = (*current_loop_data)["parent"]; + *current_loop_data = std::move(tmp); + } else { + current_loop_data = &json_additional_data["loop"]; + } + } + + void visit(const ForObjectStatementNode& node) { + auto result = eval_expression_list(node.condition); + if (!result->is_object()) { + throw_renderer_error("object must be an object", node); + } + + if (!current_loop_data->empty()) { + (*current_loop_data)["parent"] = std::move(*current_loop_data); + } + + size_t index = 0; + (*current_loop_data)["is_first"] = true; + (*current_loop_data)["is_last"] = (result->size() <= 1); + for (auto it = result->begin(); it != result->end(); ++it) { + json_additional_data[static_cast(node.key)] = it.key(); + json_additional_data[static_cast(node.value)] = it.value(); + + (*current_loop_data)["index"] = index; + (*current_loop_data)["index1"] = index + 1; + if (index == 1) { + (*current_loop_data)["is_first"] = false; + } + if (index == result->size() - 1) { + (*current_loop_data)["is_last"] = true; + } + + node.body.accept(*this); + ++index; + } + + json_additional_data[static_cast(node.key)].clear(); + json_additional_data[static_cast(node.value)].clear(); + if (!(*current_loop_data)["parent"].empty()) { + *current_loop_data = std::move((*current_loop_data)["parent"]); + } else { + current_loop_data = &json_additional_data["loop"]; + } + } + + void visit(const IfStatementNode& node) { + auto result = eval_expression_list(node.condition); + if (truthy(result.get())) { + node.true_statement.accept(*this); + } else if (node.has_false_statement) { + node.false_statement.accept(*this); + } + } + + void visit(const IncludeStatementNode& node) { + auto sub_renderer = Renderer(config, template_storage, function_storage); + auto included_template_it = template_storage.find(node.file); + + if (included_template_it != template_storage.end()) { + sub_renderer.render_to(*output_stream, included_template_it->second, *json_input, &json_additional_data); + } else if (config.throw_at_missing_includes) { + throw_renderer_error("include '" + node.file + "' not found", node); + } + } + + void visit(const SetStatementNode& node) { + json_additional_data[node.key] = *eval_expression_list(node.expression); + } + +public: + Renderer(const RenderConfig& config, const TemplateStorage &template_storage, const FunctionStorage &function_storage) + : config(config), template_storage(template_storage), function_storage(function_storage) { } + + void render_to(std::ostream &os, const Template &tmpl, const json &data, json *loop_data = nullptr) { + output_stream = &os; + current_template = &tmpl; + json_input = &data; + if (loop_data) { + json_additional_data = *loop_data; + current_loop_data = &json_additional_data["loop"]; + } + + current_template->root.accept(*this); + + json_tmp_stack.clear(); + } +}; + +} // namespace inja + +#endif // INCLUDE_INJA_RENDERER_HPP_ diff --git a/isis/src/base/apps/topds4/statistics2.h b/isis/src/base/apps/topds4/statistics2.h new file mode 100644 index 0000000000..be7eefe541 --- /dev/null +++ b/isis/src/base/apps/topds4/statistics2.h @@ -0,0 +1,68 @@ +// Copyright (c) 2019 Pantor. All rights reserved. + +#ifndef INCLUDE_INJA_STATISTICS_HPP_ +#define INCLUDE_INJA_STATISTICS_HPP_ + +#include "node.h" + + +namespace inja { + +/*! + * \brief A class for counting statistics on a Template. + */ +class StatisticsVisitor : public NodeVisitor { + void visit(const BlockNode& node) { + for (auto& n : node.nodes) { + n->accept(*this); + } + } + + void visit(const TextNode&) { } + void visit(const ExpressionNode&) { } + void visit(const LiteralNode&) { } + + void visit(const JsonNode&) { + variable_counter += 1; + } + + void visit(const FunctionNode&) { } + + void visit(const ExpressionListNode& node) { + for (auto& n : node.rpn_output) { + n->accept(*this); + } + } + + void visit(const StatementNode&) { } + void visit(const ForStatementNode&) { } + + void visit(const ForArrayStatementNode& node) { + node.condition.accept(*this); + node.body.accept(*this); + } + + void visit(const ForObjectStatementNode& node) { + node.condition.accept(*this); + node.body.accept(*this); + } + + void visit(const IfStatementNode& node) { + node.condition.accept(*this); + node.true_statement.accept(*this); + node.false_statement.accept(*this); + } + + void visit(const IncludeStatementNode&) { } + + void visit(const SetStatementNode&) { } + +public: + unsigned int variable_counter; + + explicit StatisticsVisitor() : variable_counter(0) { } +}; + +} // namespace inja + +#endif // INCLUDE_INJA_STATISTICS_HPP_ diff --git a/isis/src/base/apps/topds4/string_view.h b/isis/src/base/apps/topds4/string_view.h new file mode 100644 index 0000000000..2bb50c91b7 --- /dev/null +++ b/isis/src/base/apps/topds4/string_view.h @@ -0,0 +1,1416 @@ +// Copyright 2017-2019 by Martin Moene +// +// string-view lite, a C++17-like string_view for C++98 and later. +// For more information see https://github.com/martinmoene/string-view-lite +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#pragma once + +#ifndef NONSTD_SV_LITE_H_INCLUDED +#define NONSTD_SV_LITE_H_INCLUDED + +#define string_view_lite_MAJOR 1 +#define string_view_lite_MINOR 4 +#define string_view_lite_PATCH 0 + +#define string_view_lite_VERSION \ + nssv_STRINGIFY(string_view_lite_MAJOR) "." nssv_STRINGIFY(string_view_lite_MINOR) "." nssv_STRINGIFY( \ + string_view_lite_PATCH) + +#define nssv_STRINGIFY(x) nssv_STRINGIFY_(x) +#define nssv_STRINGIFY_(x) #x + +// string-view lite configuration: + +#define nssv_STRING_VIEW_DEFAULT 0 +#define nssv_STRING_VIEW_NONSTD 1 +#define nssv_STRING_VIEW_STD 2 + +#if !defined(nssv_CONFIG_SELECT_STRING_VIEW) +#define nssv_CONFIG_SELECT_STRING_VIEW (nssv_HAVE_STD_STRING_VIEW ? nssv_STRING_VIEW_STD : nssv_STRING_VIEW_NONSTD) +#endif + +#if defined(nssv_CONFIG_SELECT_STD_STRING_VIEW) || defined(nssv_CONFIG_SELECT_NONSTD_STRING_VIEW) +#error nssv_CONFIG_SELECT_STD_STRING_VIEW and nssv_CONFIG_SELECT_NONSTD_STRING_VIEW are deprecated and removed, please use nssv_CONFIG_SELECT_STRING_VIEW=nssv_STRING_VIEW_... +#endif + +#ifndef nssv_CONFIG_STD_SV_OPERATOR +#define nssv_CONFIG_STD_SV_OPERATOR 0 +#endif + +#ifndef nssv_CONFIG_USR_SV_OPERATOR +#define nssv_CONFIG_USR_SV_OPERATOR 1 +#endif + +#ifdef nssv_CONFIG_CONVERSION_STD_STRING +#define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS nssv_CONFIG_CONVERSION_STD_STRING +#define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS nssv_CONFIG_CONVERSION_STD_STRING +#endif + +#ifndef nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS +#define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS 1 +#endif + +#ifndef nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS +#define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS 1 +#endif + +// Control presence of exception handling (try and auto discover): + +#ifndef nssv_CONFIG_NO_EXCEPTIONS +#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) +#define nssv_CONFIG_NO_EXCEPTIONS 0 +#else +#define nssv_CONFIG_NO_EXCEPTIONS 1 +#endif +#endif + +// C++ language version detection (C++20 is speculative): +// Note: VC14.0/1900 (VS2015) lacks too much from C++14. + +#ifndef nssv_CPLUSPLUS +#if defined(_MSVC_LANG) && !defined(__clang__) +#define nssv_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG) +#else +#define nssv_CPLUSPLUS __cplusplus +#endif +#endif + +#define nssv_CPP98_OR_GREATER (nssv_CPLUSPLUS >= 199711L) +#define nssv_CPP11_OR_GREATER (nssv_CPLUSPLUS >= 201103L) +#define nssv_CPP11_OR_GREATER_ (nssv_CPLUSPLUS >= 201103L) +#define nssv_CPP14_OR_GREATER (nssv_CPLUSPLUS >= 201402L) +#define nssv_CPP17_OR_GREATER (nssv_CPLUSPLUS >= 201703L) +#define nssv_CPP20_OR_GREATER (nssv_CPLUSPLUS >= 202000L) + +// use C++17 std::string_view if available and requested: + +#if nssv_CPP17_OR_GREATER && defined(__has_include) +#if __has_include( ) +#define nssv_HAVE_STD_STRING_VIEW 1 +#else +#define nssv_HAVE_STD_STRING_VIEW 0 +#endif +#else +#define nssv_HAVE_STD_STRING_VIEW 0 +#endif + +#define nssv_USES_STD_STRING_VIEW \ + ((nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_STD) || \ + ((nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_DEFAULT) && nssv_HAVE_STD_STRING_VIEW)) + +#define nssv_HAVE_STARTS_WITH (nssv_CPP20_OR_GREATER || !nssv_USES_STD_STRING_VIEW) +#define nssv_HAVE_ENDS_WITH nssv_HAVE_STARTS_WITH + +// +// Use C++17 std::string_view: +// + +#if nssv_USES_STD_STRING_VIEW + +#include + +// Extensions for std::string: + +#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS + +namespace nonstd { + +template > +std::basic_string to_string(std::basic_string_view v, + Allocator const &a = Allocator()) { + return std::basic_string(v.begin(), v.end(), a); +} + +template +std::basic_string_view to_string_view(std::basic_string const &s) { + return std::basic_string_view(s.data(), s.size()); +} + +// Literal operators sv and _sv: + +#if nssv_CONFIG_STD_SV_OPERATOR + +using namespace std::literals::string_view_literals; + +#endif + +#if nssv_CONFIG_USR_SV_OPERATOR + +inline namespace literals { +inline namespace string_view_literals { + +constexpr std::string_view operator"" _sv(const char *str, size_t len) noexcept // (1) +{ + return std::string_view {str, len}; +} + +constexpr std::u16string_view operator"" _sv(const char16_t *str, size_t len) noexcept // (2) +{ + return std::u16string_view {str, len}; +} + +constexpr std::u32string_view operator"" _sv(const char32_t *str, size_t len) noexcept // (3) +{ + return std::u32string_view {str, len}; +} + +constexpr std::wstring_view operator"" _sv(const wchar_t *str, size_t len) noexcept // (4) +{ + return std::wstring_view {str, len}; +} + +} // namespace string_view_literals +} // namespace literals + +#endif // nssv_CONFIG_USR_SV_OPERATOR + +} // namespace nonstd + +#endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS + +namespace nonstd { + +using std::basic_string_view; +using std::string_view; +using std::u16string_view; +using std::u32string_view; +using std::wstring_view; + +// literal "sv" and "_sv", see above + +using std::operator==; +using std::operator!=; +using std::operator<; +using std::operator<=; +using std::operator>; +using std::operator>=; + +using std::operator<<; + +} // namespace nonstd + +#else // nssv_HAVE_STD_STRING_VIEW + +// +// Before C++17: use string_view lite: +// + +// Compiler versions: +// +// MSVC++ 6.0 _MSC_VER == 1200 (Visual Studio 6.0) +// MSVC++ 7.0 _MSC_VER == 1300 (Visual Studio .NET 2002) +// MSVC++ 7.1 _MSC_VER == 1310 (Visual Studio .NET 2003) +// MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005) +// MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008) +// MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010) +// MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012) +// MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013) +// MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015) +// MSVC++ 14.1 _MSC_VER >= 1910 (Visual Studio 2017) + +#if defined(_MSC_VER) && !defined(__clang__) +#define nssv_COMPILER_MSVC_VER (_MSC_VER) +#define nssv_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * (5 + (_MSC_VER < 1900))) +#else +#define nssv_COMPILER_MSVC_VER 0 +#define nssv_COMPILER_MSVC_VERSION 0 +#endif + +#define nssv_COMPILER_VERSION(major, minor, patch) (10 * (10 * (major) + (minor)) + (patch)) + +#if defined(__clang__) +#define nssv_COMPILER_CLANG_VERSION nssv_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__) +#else +#define nssv_COMPILER_CLANG_VERSION 0 +#endif + +#if defined(__GNUC__) && !defined(__clang__) +#define nssv_COMPILER_GNUC_VERSION nssv_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#else +#define nssv_COMPILER_GNUC_VERSION 0 +#endif + +// half-open range [lo..hi): +#define nssv_BETWEEN(v, lo, hi) ((lo) <= (v) && (v) < (hi)) + +// Presence of language and library features: + +#ifdef _HAS_CPP0X +#define nssv_HAS_CPP0X _HAS_CPP0X +#else +#define nssv_HAS_CPP0X 0 +#endif + +// Unless defined otherwise below, consider VC14 as C++11 for variant-lite: + +#if nssv_COMPILER_MSVC_VER >= 1900 +#undef nssv_CPP11_OR_GREATER +#define nssv_CPP11_OR_GREATER 1 +#endif + +#define nssv_CPP11_90 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1500) +#define nssv_CPP11_100 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1600) +#define nssv_CPP11_110 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1700) +#define nssv_CPP11_120 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1800) +#define nssv_CPP11_140 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1900) +#define nssv_CPP11_141 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1910) + +#define nssv_CPP14_000 (nssv_CPP14_OR_GREATER) +#define nssv_CPP17_000 (nssv_CPP17_OR_GREATER) + +// Presence of C++11 language features: + +#define nssv_HAVE_CONSTEXPR_11 nssv_CPP11_140 +#define nssv_HAVE_EXPLICIT_CONVERSION nssv_CPP11_140 +#define nssv_HAVE_INLINE_NAMESPACE nssv_CPP11_140 +#define nssv_HAVE_NOEXCEPT nssv_CPP11_140 +#define nssv_HAVE_NULLPTR nssv_CPP11_100 +#define nssv_HAVE_REF_QUALIFIER nssv_CPP11_140 +#define nssv_HAVE_UNICODE_LITERALS nssv_CPP11_140 +#define nssv_HAVE_USER_DEFINED_LITERALS nssv_CPP11_140 +#define nssv_HAVE_WCHAR16_T nssv_CPP11_100 +#define nssv_HAVE_WCHAR32_T nssv_CPP11_100 + +#if !((nssv_CPP11_OR_GREATER && nssv_COMPILER_CLANG_VERSION) || nssv_BETWEEN(nssv_COMPILER_CLANG_VERSION, 300, 400)) +#define nssv_HAVE_STD_DEFINED_LITERALS nssv_CPP11_140 +#else +#define nssv_HAVE_STD_DEFINED_LITERALS 0 +#endif + +// Presence of C++14 language features: + +#define nssv_HAVE_CONSTEXPR_14 nssv_CPP14_000 + +// Presence of C++17 language features: + +#define nssv_HAVE_NODISCARD nssv_CPP17_000 + +// Presence of C++ library features: + +#define nssv_HAVE_STD_HASH nssv_CPP11_120 + +// C++ feature usage: + +#if nssv_HAVE_CONSTEXPR_11 +#define nssv_constexpr constexpr +#else +#define nssv_constexpr /*constexpr*/ +#endif + +#if nssv_HAVE_CONSTEXPR_14 +#define nssv_constexpr14 constexpr +#else +#define nssv_constexpr14 /*constexpr*/ +#endif + +#if nssv_HAVE_EXPLICIT_CONVERSION +#define nssv_explicit explicit +#else +#define nssv_explicit /*explicit*/ +#endif + +#if nssv_HAVE_INLINE_NAMESPACE +#define nssv_inline_ns inline +#else +#define nssv_inline_ns /*inline*/ +#endif + +#if nssv_HAVE_NOEXCEPT +#define nssv_noexcept noexcept +#else +#define nssv_noexcept /*noexcept*/ +#endif + +//#if nssv_HAVE_REF_QUALIFIER +//# define nssv_ref_qual & +//# define nssv_refref_qual && +//#else +//# define nssv_ref_qual /*&*/ +//# define nssv_refref_qual /*&&*/ +//#endif + +#if nssv_HAVE_NULLPTR +#define nssv_nullptr nullptr +#else +#define nssv_nullptr NULL +#endif + +#if nssv_HAVE_NODISCARD +#define nssv_nodiscard [[nodiscard]] +#else +#define nssv_nodiscard /*[[nodiscard]]*/ +#endif + +// Additional includes: + +#include +#include +#include +#include +#include +#include // std::char_traits<> + +#if !nssv_CONFIG_NO_EXCEPTIONS +#include +#endif + +#if nssv_CPP11_OR_GREATER +#include +#endif + +// Clang, GNUC, MSVC warning suppression macros: + +#if defined(__clang__) +#pragma clang diagnostic ignored "-Wreserved-user-defined-literal" +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wuser-defined-literals" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wliteral-suffix" +#endif // __clang__ + +#if nssv_COMPILER_MSVC_VERSION >= 140 +#define nssv_SUPPRESS_MSGSL_WARNING(expr) [[gsl::suppress(expr)]] +#define nssv_SUPPRESS_MSVC_WARNING(code, descr) __pragma(warning(suppress : code)) +#define nssv_DISABLE_MSVC_WARNINGS(codes) __pragma(warning(push)) __pragma(warning(disable : codes)) +#else +#define nssv_SUPPRESS_MSGSL_WARNING(expr) +#define nssv_SUPPRESS_MSVC_WARNING(code, descr) +#define nssv_DISABLE_MSVC_WARNINGS(codes) +#endif + +#if defined(__clang__) +#define nssv_RESTORE_WARNINGS() _Pragma("clang diagnostic pop") +#elif defined(__GNUC__) +#define nssv_RESTORE_WARNINGS() _Pragma("GCC diagnostic pop") +#elif nssv_COMPILER_MSVC_VERSION >= 140 +#define nssv_RESTORE_WARNINGS() __pragma(warning(pop)) +#else +#define nssv_RESTORE_WARNINGS() +#endif + +// Suppress the following MSVC (GSL) warnings: +// - C4455, non-gsl : 'operator ""sv': literal suffix identifiers that do not +// start with an underscore are reserved +// - C26472, gsl::t.1 : don't use a static_cast for arithmetic conversions; +// use brace initialization, gsl::narrow_cast or gsl::narow +// - C26481: gsl::b.1 : don't use pointer arithmetic. Use span instead + +nssv_DISABLE_MSVC_WARNINGS(4455 26481 26472) + // nssv_DISABLE_CLANG_WARNINGS( "-Wuser-defined-literals" ) + // nssv_DISABLE_GNUC_WARNINGS( -Wliteral-suffix ) + + namespace nonstd { + namespace sv_lite { + +#if nssv_CPP11_OR_GREATER + + namespace detail { + + // Expect tail call optimization to make length() non-recursive: + + template inline constexpr std::size_t length(CharT *s, std::size_t result = 0) { + return *s == '\0' ? result : length(s + 1, result + 1); + } + + } // namespace detail + +#endif // nssv_CPP11_OR_GREATER + + template > class basic_string_view; + + // + // basic_string_view: + // + + template */ + > + class basic_string_view { + public: + // Member types: + + typedef Traits traits_type; + typedef CharT value_type; + + typedef CharT *pointer; + typedef CharT const *const_pointer; + typedef CharT &reference; + typedef CharT const &const_reference; + + typedef const_pointer iterator; + typedef const_pointer const_iterator; + typedef std::reverse_iterator reverse_iterator; + typedef std::reverse_iterator const_reverse_iterator; + + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + + // 24.4.2.1 Construction and assignment: + + nssv_constexpr basic_string_view() nssv_noexcept : data_(nssv_nullptr), size_(0) {} + +#if nssv_CPP11_OR_GREATER + nssv_constexpr basic_string_view(basic_string_view const &other) nssv_noexcept = default; +#else + nssv_constexpr basic_string_view(basic_string_view const &other) nssv_noexcept : data_(other.data_), + size_(other.size_) {} +#endif + + nssv_constexpr basic_string_view(CharT const *s, size_type count) nssv_noexcept // non-standard noexcept + : data_(s), + size_(count) {} + + nssv_constexpr basic_string_view(CharT const *s) nssv_noexcept // non-standard noexcept + : data_(s) +#if nssv_CPP17_OR_GREATER + , + size_(Traits::length(s)) +#elif nssv_CPP11_OR_GREATER + , + size_(detail::length(s)) +#else + , + size_(Traits::length(s)) +#endif + { + } + + // Assignment: + +#if nssv_CPP11_OR_GREATER + nssv_constexpr14 basic_string_view &operator=(basic_string_view const &other) nssv_noexcept = default; +#else + nssv_constexpr14 basic_string_view &operator=(basic_string_view const &other) nssv_noexcept { + data_ = other.data_; + size_ = other.size_; + return *this; + } +#endif + + // 24.4.2.2 Iterator support: + + nssv_constexpr const_iterator begin() const nssv_noexcept { return data_; } + nssv_constexpr const_iterator end() const nssv_noexcept { return data_ + size_; } + + nssv_constexpr const_iterator cbegin() const nssv_noexcept { return begin(); } + nssv_constexpr const_iterator cend() const nssv_noexcept { return end(); } + + nssv_constexpr const_reverse_iterator rbegin() const nssv_noexcept { return const_reverse_iterator(end()); } + nssv_constexpr const_reverse_iterator rend() const nssv_noexcept { return const_reverse_iterator(begin()); } + + nssv_constexpr const_reverse_iterator crbegin() const nssv_noexcept { return rbegin(); } + nssv_constexpr const_reverse_iterator crend() const nssv_noexcept { return rend(); } + + // 24.4.2.3 Capacity: + + nssv_constexpr size_type size() const nssv_noexcept { return size_; } + nssv_constexpr size_type length() const nssv_noexcept { return size_; } + nssv_constexpr size_type max_size() const nssv_noexcept { return (std::numeric_limits::max)(); } + + // since C++20 + nssv_nodiscard nssv_constexpr bool empty() const nssv_noexcept { return 0 == size_; } + + // 24.4.2.4 Element access: + + nssv_constexpr const_reference operator[](size_type pos) const { return data_at(pos); } + + nssv_constexpr14 const_reference at(size_type pos) const { +#if nssv_CONFIG_NO_EXCEPTIONS + assert(pos < size()); +#else + if (pos >= size()) { + throw std::out_of_range("nonstd::string_view::at()"); + } +#endif + return data_at(pos); + } + + nssv_constexpr const_reference front() const { return data_at(0); } + nssv_constexpr const_reference back() const { return data_at(size() - 1); } + + nssv_constexpr const_pointer data() const nssv_noexcept { return data_; } + + // 24.4.2.5 Modifiers: + + nssv_constexpr14 void remove_prefix(size_type n) { + assert(n <= size()); + data_ += n; + size_ -= n; + } + + nssv_constexpr14 void remove_suffix(size_type n) { + assert(n <= size()); + size_ -= n; + } + + nssv_constexpr14 void swap(basic_string_view &other) nssv_noexcept { + using std::swap; + swap(data_, other.data_); + swap(size_, other.size_); + } + + // 24.4.2.6 String operations: + + size_type copy(CharT *dest, size_type n, size_type pos = 0) const { +#if nssv_CONFIG_NO_EXCEPTIONS + assert(pos <= size()); +#else + if (pos > size()) { + throw std::out_of_range("nonstd::string_view::copy()"); + } +#endif + const size_type rlen = (std::min)(n, size() - pos); + + (void)Traits::copy(dest, data() + pos, rlen); + + return rlen; + } + + nssv_constexpr14 basic_string_view substr(size_type pos = 0, size_type n = npos) const { +#if nssv_CONFIG_NO_EXCEPTIONS + assert(pos <= size()); +#else + if (pos > size()) { + throw std::out_of_range("nonstd::string_view::substr()"); + } +#endif + return basic_string_view(data() + pos, (std::min)(n, size() - pos)); + } + + // compare(), 6x: + + nssv_constexpr14 int compare(basic_string_view other) const nssv_noexcept // (1) + { + if (const int result = Traits::compare(data(), other.data(), (std::min)(size(), other.size()))) { + return result; + } + + return size() == other.size() ? 0 : size() < other.size() ? -1 : 1; + } + + nssv_constexpr int compare(size_type pos1, size_type n1, basic_string_view other) const // (2) + { + return substr(pos1, n1).compare(other); + } + + nssv_constexpr int compare(size_type pos1, size_type n1, basic_string_view other, size_type pos2, + size_type n2) const // (3) + { + return substr(pos1, n1).compare(other.substr(pos2, n2)); + } + + nssv_constexpr int compare(CharT const *s) const // (4) + { + return compare(basic_string_view(s)); + } + + nssv_constexpr int compare(size_type pos1, size_type n1, CharT const *s) const // (5) + { + return substr(pos1, n1).compare(basic_string_view(s)); + } + + nssv_constexpr int compare(size_type pos1, size_type n1, CharT const *s, size_type n2) const // (6) + { + return substr(pos1, n1).compare(basic_string_view(s, n2)); + } + + // 24.4.2.7 Searching: + + // starts_with(), 3x, since C++20: + + nssv_constexpr bool starts_with(basic_string_view v) const nssv_noexcept // (1) + { + return size() >= v.size() && compare(0, v.size(), v) == 0; + } + + nssv_constexpr bool starts_with(CharT c) const nssv_noexcept // (2) + { + return starts_with(basic_string_view(&c, 1)); + } + + nssv_constexpr bool starts_with(CharT const *s) const // (3) + { + return starts_with(basic_string_view(s)); + } + + // ends_with(), 3x, since C++20: + + nssv_constexpr bool ends_with(basic_string_view v) const nssv_noexcept // (1) + { + return size() >= v.size() && compare(size() - v.size(), npos, v) == 0; + } + + nssv_constexpr bool ends_with(CharT c) const nssv_noexcept // (2) + { + return ends_with(basic_string_view(&c, 1)); + } + + nssv_constexpr bool ends_with(CharT const *s) const // (3) + { + return ends_with(basic_string_view(s)); + } + + // find(), 4x: + + nssv_constexpr14 size_type find(basic_string_view v, size_type pos = 0) const nssv_noexcept // (1) + { + return assert(v.size() == 0 || v.data() != nssv_nullptr), + pos >= size() ? npos : to_pos(std::search(cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq)); + } + + nssv_constexpr14 size_type find(CharT c, size_type pos = 0) const nssv_noexcept // (2) + { + return find(basic_string_view(&c, 1), pos); + } + + nssv_constexpr14 size_type find(CharT const *s, size_type pos, size_type n) const // (3) + { + return find(basic_string_view(s, n), pos); + } + + nssv_constexpr14 size_type find(CharT const *s, size_type pos = 0) const // (4) + { + return find(basic_string_view(s), pos); + } + + // rfind(), 4x: + + nssv_constexpr14 size_type rfind(basic_string_view v, size_type pos = npos) const nssv_noexcept // (1) + { + if (size() < v.size()) { + return npos; + } + + if (v.empty()) { + return (std::min)(size(), pos); + } + + const_iterator last = cbegin() + (std::min)(size() - v.size(), pos) + v.size(); + const_iterator result = std::find_end(cbegin(), last, v.cbegin(), v.cend(), Traits::eq); + + return result != last ? size_type(result - cbegin()) : npos; + } + + nssv_constexpr14 size_type rfind(CharT c, size_type pos = npos) const nssv_noexcept // (2) + { + return rfind(basic_string_view(&c, 1), pos); + } + + nssv_constexpr14 size_type rfind(CharT const *s, size_type pos, size_type n) const // (3) + { + return rfind(basic_string_view(s, n), pos); + } + + nssv_constexpr14 size_type rfind(CharT const *s, size_type pos = npos) const // (4) + { + return rfind(basic_string_view(s), pos); + } + + // find_first_of(), 4x: + + nssv_constexpr size_type find_first_of(basic_string_view v, size_type pos = 0) const nssv_noexcept // (1) + { + return pos >= size() ? npos + : to_pos(std::find_first_of(cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq)); + } + + nssv_constexpr size_type find_first_of(CharT c, size_type pos = 0) const nssv_noexcept // (2) + { + return find_first_of(basic_string_view(&c, 1), pos); + } + + nssv_constexpr size_type find_first_of(CharT const *s, size_type pos, size_type n) const // (3) + { + return find_first_of(basic_string_view(s, n), pos); + } + + nssv_constexpr size_type find_first_of(CharT const *s, size_type pos = 0) const // (4) + { + return find_first_of(basic_string_view(s), pos); + } + + // find_last_of(), 4x: + + nssv_constexpr size_type find_last_of(basic_string_view v, size_type pos = npos) const nssv_noexcept // (1) + { + return empty() ? npos + : pos >= size() ? find_last_of(v, size() - 1) + : to_pos(std::find_first_of(const_reverse_iterator(cbegin() + pos + 1), crend(), + v.cbegin(), v.cend(), Traits::eq)); + } + + nssv_constexpr size_type find_last_of(CharT c, size_type pos = npos) const nssv_noexcept // (2) + { + return find_last_of(basic_string_view(&c, 1), pos); + } + + nssv_constexpr size_type find_last_of(CharT const *s, size_type pos, size_type count) const // (3) + { + return find_last_of(basic_string_view(s, count), pos); + } + + nssv_constexpr size_type find_last_of(CharT const *s, size_type pos = npos) const // (4) + { + return find_last_of(basic_string_view(s), pos); + } + + // find_first_not_of(), 4x: + + nssv_constexpr size_type find_first_not_of(basic_string_view v, size_type pos = 0) const nssv_noexcept // (1) + { + return pos >= size() ? npos : to_pos(std::find_if(cbegin() + pos, cend(), not_in_view(v))); + } + + nssv_constexpr size_type find_first_not_of(CharT c, size_type pos = 0) const nssv_noexcept // (2) + { + return find_first_not_of(basic_string_view(&c, 1), pos); + } + + nssv_constexpr size_type find_first_not_of(CharT const *s, size_type pos, size_type count) const // (3) + { + return find_first_not_of(basic_string_view(s, count), pos); + } + + nssv_constexpr size_type find_first_not_of(CharT const *s, size_type pos = 0) const // (4) + { + return find_first_not_of(basic_string_view(s), pos); + } + + // find_last_not_of(), 4x: + + nssv_constexpr size_type find_last_not_of(basic_string_view v, size_type pos = npos) const nssv_noexcept // (1) + { + return empty() ? npos + : pos >= size() + ? find_last_not_of(v, size() - 1) + : to_pos(std::find_if(const_reverse_iterator(cbegin() + pos + 1), crend(), not_in_view(v))); + } + + nssv_constexpr size_type find_last_not_of(CharT c, size_type pos = npos) const nssv_noexcept // (2) + { + return find_last_not_of(basic_string_view(&c, 1), pos); + } + + nssv_constexpr size_type find_last_not_of(CharT const *s, size_type pos, size_type count) const // (3) + { + return find_last_not_of(basic_string_view(s, count), pos); + } + + nssv_constexpr size_type find_last_not_of(CharT const *s, size_type pos = npos) const // (4) + { + return find_last_not_of(basic_string_view(s), pos); + } + + // Constants: + +#if nssv_CPP17_OR_GREATER + static nssv_constexpr size_type npos = size_type(-1); +#elif nssv_CPP11_OR_GREATER + enum : size_type { npos = size_type(-1) }; +#else + enum { npos = size_type(-1) }; +#endif + + private: + struct not_in_view { + const basic_string_view v; + + nssv_constexpr explicit not_in_view(basic_string_view v) : v(v) {} + + nssv_constexpr bool operator()(CharT c) const { return npos == v.find_first_of(c); } + }; + + nssv_constexpr size_type to_pos(const_iterator it) const { return it == cend() ? npos : size_type(it - cbegin()); } + + nssv_constexpr size_type to_pos(const_reverse_iterator it) const { + return it == crend() ? npos : size_type(crend() - it - 1); + } + + nssv_constexpr const_reference data_at(size_type pos) const { +#if nssv_BETWEEN(nssv_COMPILER_GNUC_VERSION, 1, 500) + return data_[pos]; +#else + return assert(pos < size()), data_[pos]; +#endif + } + + private: + const_pointer data_; + size_type size_; + + public: +#if nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS + + template + basic_string_view(std::basic_string const &s) nssv_noexcept : data_(s.data()), + size_(s.size()) {} + +#if nssv_HAVE_EXPLICIT_CONVERSION + + template explicit operator std::basic_string() const { + return to_string(Allocator()); + } + +#endif // nssv_HAVE_EXPLICIT_CONVERSION + +#if nssv_CPP11_OR_GREATER + + template > + std::basic_string to_string(Allocator const &a = Allocator()) const { + return std::basic_string(begin(), end(), a); + } + +#else + + std::basic_string to_string() const { return std::basic_string(begin(), end()); } + + template std::basic_string to_string(Allocator const &a) const { + return std::basic_string(begin(), end(), a); + } + +#endif // nssv_CPP11_OR_GREATER + +#endif // nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS + }; + + // + // Non-member functions: + // + + // 24.4.3 Non-member comparison functions: + // lexicographically compare two string views (function template): + + template + nssv_constexpr bool operator==(basic_string_view lhs, + basic_string_view rhs) nssv_noexcept { + return lhs.compare(rhs) == 0; + } + + template + nssv_constexpr bool operator!=(basic_string_view lhs, + basic_string_view rhs) nssv_noexcept { + return lhs.compare(rhs) != 0; + } + + template + nssv_constexpr bool operator<(basic_string_view lhs, + basic_string_view rhs) nssv_noexcept { + return lhs.compare(rhs) < 0; + } + + template + nssv_constexpr bool operator<=(basic_string_view lhs, + basic_string_view rhs) nssv_noexcept { + return lhs.compare(rhs) <= 0; + } + + template + nssv_constexpr bool operator>(basic_string_view lhs, + basic_string_view rhs) nssv_noexcept { + return lhs.compare(rhs) > 0; + } + + template + nssv_constexpr bool operator>=(basic_string_view lhs, + basic_string_view rhs) nssv_noexcept { + return lhs.compare(rhs) >= 0; + } + + // Let S be basic_string_view, and sv be an instance of S. + // Implementations shall provide sufficient additional overloads marked + // constexpr and noexcept so that an object t with an implicit conversion + // to S can be compared according to Table 67. + +#if !nssv_CPP11_OR_GREATER || nssv_BETWEEN(nssv_COMPILER_MSVC_VERSION, 100, 141) + + // accomodate for older compilers: + + // == + + template + nssv_constexpr bool operator==(basic_string_view lhs, char const *rhs) nssv_noexcept { + return lhs.compare(rhs) == 0; + } + + template + nssv_constexpr bool operator==(char const *lhs, basic_string_view rhs) nssv_noexcept { + return rhs.compare(lhs) == 0; + } + + template + nssv_constexpr bool operator==(basic_string_view lhs, + std::basic_string rhs) nssv_noexcept { + return lhs.size() == rhs.size() && lhs.compare(rhs) == 0; + } + + template + nssv_constexpr bool operator==(std::basic_string rhs, + basic_string_view lhs) nssv_noexcept { + return lhs.size() == rhs.size() && lhs.compare(rhs) == 0; + } + + // != + + template + nssv_constexpr bool operator!=(basic_string_view lhs, char const *rhs) nssv_noexcept { + return lhs.compare(rhs) != 0; + } + + template + nssv_constexpr bool operator!=(char const *lhs, basic_string_view rhs) nssv_noexcept { + return rhs.compare(lhs) != 0; + } + + template + nssv_constexpr bool operator!=(basic_string_view lhs, + std::basic_string rhs) nssv_noexcept { + return lhs.size() != rhs.size() && lhs.compare(rhs) != 0; + } + + template + nssv_constexpr bool operator!=(std::basic_string rhs, + basic_string_view lhs) nssv_noexcept { + return lhs.size() != rhs.size() || rhs.compare(lhs) != 0; + } + + // < + + template + nssv_constexpr bool operator<(basic_string_view lhs, char const *rhs) nssv_noexcept { + return lhs.compare(rhs) < 0; + } + + template + nssv_constexpr bool operator<(char const *lhs, basic_string_view rhs) nssv_noexcept { + return rhs.compare(lhs) > 0; + } + + template + nssv_constexpr bool operator<(basic_string_view lhs, + std::basic_string rhs) nssv_noexcept { + return lhs.compare(rhs) < 0; + } + + template + nssv_constexpr bool operator<(std::basic_string rhs, + basic_string_view lhs) nssv_noexcept { + return rhs.compare(lhs) > 0; + } + + // <= + + template + nssv_constexpr bool operator<=(basic_string_view lhs, char const *rhs) nssv_noexcept { + return lhs.compare(rhs) <= 0; + } + + template + nssv_constexpr bool operator<=(char const *lhs, basic_string_view rhs) nssv_noexcept { + return rhs.compare(lhs) >= 0; + } + + template + nssv_constexpr bool operator<=(basic_string_view lhs, + std::basic_string rhs) nssv_noexcept { + return lhs.compare(rhs) <= 0; + } + + template + nssv_constexpr bool operator<=(std::basic_string rhs, + basic_string_view lhs) nssv_noexcept { + return rhs.compare(lhs) >= 0; + } + + // > + + template + nssv_constexpr bool operator>(basic_string_view lhs, char const *rhs) nssv_noexcept { + return lhs.compare(rhs) > 0; + } + + template + nssv_constexpr bool operator>(char const *lhs, basic_string_view rhs) nssv_noexcept { + return rhs.compare(lhs) < 0; + } + + template + nssv_constexpr bool operator>(basic_string_view lhs, + std::basic_string rhs) nssv_noexcept { + return lhs.compare(rhs) > 0; + } + + template + nssv_constexpr bool operator>(std::basic_string rhs, + basic_string_view lhs) nssv_noexcept { + return rhs.compare(lhs) < 0; + } + + // >= + + template + nssv_constexpr bool operator>=(basic_string_view lhs, char const *rhs) nssv_noexcept { + return lhs.compare(rhs) >= 0; + } + + template + nssv_constexpr bool operator>=(char const *lhs, basic_string_view rhs) nssv_noexcept { + return rhs.compare(lhs) <= 0; + } + + template + nssv_constexpr bool operator>=(basic_string_view lhs, + std::basic_string rhs) nssv_noexcept { + return lhs.compare(rhs) >= 0; + } + + template + nssv_constexpr bool operator>=(std::basic_string rhs, + basic_string_view lhs) nssv_noexcept { + return rhs.compare(lhs) <= 0; + } + +#else // newer compilers: + +#define nssv_BASIC_STRING_VIEW_I(T, U) typename std::decay>::type + +#if nssv_BETWEEN(nssv_COMPILER_MSVC_VERSION, 140, 150) +#define nssv_MSVC_ORDER(x) , int = x +#else +#define nssv_MSVC_ORDER(x) /*, int=x*/ +#endif + + // == + + template + nssv_constexpr bool operator==(basic_string_view lhs, + nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs) nssv_noexcept { + return lhs.compare(rhs) == 0; + } + + template + nssv_constexpr bool operator==(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs, + basic_string_view rhs) nssv_noexcept { + return lhs.size() == rhs.size() && lhs.compare(rhs) == 0; + } + + // != + + template + nssv_constexpr bool operator!=(basic_string_view lhs, + nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs) nssv_noexcept { + return lhs.size() != rhs.size() || lhs.compare(rhs) != 0; + } + + template + nssv_constexpr bool operator!=(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs, + basic_string_view rhs) nssv_noexcept { + return lhs.compare(rhs) != 0; + } + + // < + + template + nssv_constexpr bool operator<(basic_string_view lhs, + nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs) nssv_noexcept { + return lhs.compare(rhs) < 0; + } + + template + nssv_constexpr bool operator<(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs, + basic_string_view rhs) nssv_noexcept { + return lhs.compare(rhs) < 0; + } + + // <= + + template + nssv_constexpr bool operator<=(basic_string_view lhs, + nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs) nssv_noexcept { + return lhs.compare(rhs) <= 0; + } + + template + nssv_constexpr bool operator<=(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs, + basic_string_view rhs) nssv_noexcept { + return lhs.compare(rhs) <= 0; + } + + // > + + template + nssv_constexpr bool operator>(basic_string_view lhs, + nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs) nssv_noexcept { + return lhs.compare(rhs) > 0; + } + + template + nssv_constexpr bool operator>(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs, + basic_string_view rhs) nssv_noexcept { + return lhs.compare(rhs) > 0; + } + + // >= + + template + nssv_constexpr bool operator>=(basic_string_view lhs, + nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs) nssv_noexcept { + return lhs.compare(rhs) >= 0; + } + + template + nssv_constexpr bool operator>=(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs, + basic_string_view rhs) nssv_noexcept { + return lhs.compare(rhs) >= 0; + } + +#undef nssv_MSVC_ORDER +#undef nssv_BASIC_STRING_VIEW_I + +#endif // compiler-dependent approach to comparisons + + // 24.4.4 Inserters and extractors: + + namespace detail { + + template void write_padding(Stream &os, std::streamsize n) { + for (std::streamsize i = 0; i < n; ++i) + os.rdbuf()->sputc(os.fill()); + } + + template Stream &write_to_stream(Stream &os, View const &sv) { + typename Stream::sentry sentry(os); + + if (!os) + return os; + + const std::streamsize length = static_cast(sv.length()); + + // Whether, and how, to pad: + const bool pad = (length < os.width()); + const bool left_pad = pad && (os.flags() & std::ios_base::adjustfield) == std::ios_base::right; + + if (left_pad) + write_padding(os, os.width() - length); + + // Write span characters: + os.rdbuf()->sputn(sv.begin(), length); + + if (pad && !left_pad) + write_padding(os, os.width() - length); + + // Reset output stream width: + os.width(0); + + return os; + } + + } // namespace detail + + template + std::basic_ostream &operator<<(std::basic_ostream &os, + basic_string_view sv) { + return detail::write_to_stream(os, sv); + } + + // Several typedefs for common character types are provided: + + typedef basic_string_view string_view; + typedef basic_string_view wstring_view; +#if nssv_HAVE_WCHAR16_T + typedef basic_string_view u16string_view; + typedef basic_string_view u32string_view; +#endif + + } // namespace sv_lite +} // namespace nonstd::sv_lite + +// +// 24.4.6 Suffix for basic_string_view literals: +// + +#if nssv_HAVE_USER_DEFINED_LITERALS + +namespace nonstd { +nssv_inline_ns namespace literals { + nssv_inline_ns namespace string_view_literals { + +#if nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS + + nssv_constexpr nonstd::sv_lite::string_view operator"" sv(const char *str, size_t len) nssv_noexcept // (1) + { + return nonstd::sv_lite::string_view {str, len}; + } + + nssv_constexpr nonstd::sv_lite::u16string_view operator"" sv(const char16_t *str, size_t len) nssv_noexcept // (2) + { + return nonstd::sv_lite::u16string_view {str, len}; + } + + nssv_constexpr nonstd::sv_lite::u32string_view operator"" sv(const char32_t *str, size_t len) nssv_noexcept // (3) + { + return nonstd::sv_lite::u32string_view {str, len}; + } + + nssv_constexpr nonstd::sv_lite::wstring_view operator"" sv(const wchar_t *str, size_t len) nssv_noexcept // (4) + { + return nonstd::sv_lite::wstring_view {str, len}; + } + +#endif // nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS + +#if nssv_CONFIG_USR_SV_OPERATOR + + nssv_constexpr nonstd::sv_lite::string_view operator"" _sv(const char *str, size_t len) nssv_noexcept // (1) + { + return nonstd::sv_lite::string_view {str, len}; + } + + nssv_constexpr nonstd::sv_lite::u16string_view operator"" _sv(const char16_t *str, size_t len) nssv_noexcept // (2) + { + return nonstd::sv_lite::u16string_view {str, len}; + } + + nssv_constexpr nonstd::sv_lite::u32string_view operator"" _sv(const char32_t *str, size_t len) nssv_noexcept // (3) + { + return nonstd::sv_lite::u32string_view {str, len}; + } + + nssv_constexpr nonstd::sv_lite::wstring_view operator"" _sv(const wchar_t *str, size_t len) nssv_noexcept // (4) + { + return nonstd::sv_lite::wstring_view {str, len}; + } + +#endif // nssv_CONFIG_USR_SV_OPERATOR + } +} +} // namespace nonstd + +#endif + +// +// Extensions for std::string: +// + +#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS + +namespace nonstd { +namespace sv_lite { + +// Exclude MSVC 14 (19.00): it yields ambiguous to_string(): + +#if nssv_CPP11_OR_GREATER && nssv_COMPILER_MSVC_VERSION != 140 + +template > +std::basic_string to_string(basic_string_view v, + Allocator const &a = Allocator()) { + return std::basic_string(v.begin(), v.end(), a); +} + +#else + +template std::basic_string to_string(basic_string_view v) { + return std::basic_string(v.begin(), v.end()); +} + +template +std::basic_string to_string(basic_string_view v, Allocator const &a) { + return std::basic_string(v.begin(), v.end(), a); +} + +#endif // nssv_CPP11_OR_GREATER + +template +basic_string_view to_string_view(std::basic_string const &s) { + return basic_string_view(s.data(), s.size()); +} + +} // namespace sv_lite +} // namespace nonstd + +#endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS + +// +// make types and algorithms available in namespace nonstd: +// + +namespace nonstd { + +using sv_lite::basic_string_view; +using sv_lite::string_view; +using sv_lite::wstring_view; + +#if nssv_HAVE_WCHAR16_T +using sv_lite::u16string_view; +#endif +#if nssv_HAVE_WCHAR32_T +using sv_lite::u32string_view; +#endif + +// literal "sv" + +using sv_lite::operator==; +using sv_lite::operator!=; +using sv_lite::operator<; +using sv_lite::operator<=; +using sv_lite::operator>; +using sv_lite::operator>=; + +using sv_lite::operator<<; + +#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS +using sv_lite::to_string; +using sv_lite::to_string_view; +#endif + +} // namespace nonstd + +// 24.4.5 Hash support (C++11): + +// Note: The hash value of a string view object is equal to the hash value of +// the corresponding string object. + +#if nssv_HAVE_STD_HASH + +#include + +namespace std { + +template <> struct hash { +public: + std::size_t operator()(nonstd::string_view v) const nssv_noexcept { + return std::hash()(std::string(v.data(), v.size())); + } +}; + +template <> struct hash { +public: + std::size_t operator()(nonstd::wstring_view v) const nssv_noexcept { + return std::hash()(std::wstring(v.data(), v.size())); + } +}; + +template <> struct hash { +public: + std::size_t operator()(nonstd::u16string_view v) const nssv_noexcept { + return std::hash()(std::u16string(v.data(), v.size())); + } +}; + +template <> struct hash { +public: + std::size_t operator()(nonstd::u32string_view v) const nssv_noexcept { + return std::hash()(std::u32string(v.data(), v.size())); + } +}; + +} // namespace std + +#endif // nssv_HAVE_STD_HASH + +nssv_RESTORE_WARNINGS() + +#endif // nssv_HAVE_STD_STRING_VIEW +#endif // NONSTD_SV_LITE_H_INCLUDED diff --git a/isis/src/base/apps/topds4/template2.h b/isis/src/base/apps/topds4/template2.h new file mode 100644 index 0000000000..6c7078ac04 --- /dev/null +++ b/isis/src/base/apps/topds4/template2.h @@ -0,0 +1,39 @@ +// Copyright (c) 2019 Pantor. All rights reserved. + +#ifndef INCLUDE_INJA_TEMPLATE_HPP_ +#define INCLUDE_INJA_TEMPLATE_HPP_ + +#include +#include +#include +#include + +#include "node.h" +#include "statistics2.h" + + +namespace inja { + +/*! + * \brief The main inja Template. + */ +struct Template { + BlockNode root; + std::string content; + + explicit Template() { } + explicit Template(const std::string& content): content(content) { } + + /// Return number of variables (total number, not distinct ones) in the template + int count_variables() { + auto statistic_visitor = StatisticsVisitor(); + root.accept(statistic_visitor); + return statistic_visitor.variable_counter; + } +}; + +using TemplateStorage = std::map; + +} // namespace inja + +#endif // INCLUDE_INJA_TEMPLATE_HPP_ diff --git a/isis/src/base/apps/topds4/token.h b/isis/src/base/apps/topds4/token.h new file mode 100644 index 0000000000..72b899f7d8 --- /dev/null +++ b/isis/src/base/apps/topds4/token.h @@ -0,0 +1,76 @@ +// Copyright (c) 2020 Pantor. All rights reserved. + +#ifndef INCLUDE_INJA_TOKEN_HPP_ +#define INCLUDE_INJA_TOKEN_HPP_ + +#include + +#include "string_view.h" + +namespace inja { + +/*! + * \brief Helper-class for the inja Lexer. + */ +struct Token { + enum class Kind { + Text, + ExpressionOpen, // {{ + ExpressionClose, // }} + LineStatementOpen, // ## + LineStatementClose, // \n + StatementOpen, // {% + StatementClose, // %} + CommentOpen, // {# + CommentClose, // #} + Id, // this, this.foo + Number, // 1, 2, -1, 5.2, -5.3 + String, // "this" + Plus, // + + Minus, // - + Times, // * + Slash, // / + Percent, // % + Power, // ^ + Comma, // , + Dot, // . + Colon, // : + LeftParen, // ( + RightParen, // ) + LeftBracket, // [ + RightBracket, // ] + LeftBrace, // { + RightBrace, // } + Equal, // == + NotEqual, // != + GreaterThan, // > + GreaterEqual, // >= + LessThan, // < + LessEqual, // <= + Unknown, + Eof, + }; + + Kind kind {Kind::Unknown}; + nonstd::string_view text; + + explicit constexpr Token() = default; + explicit constexpr Token(Kind kind, nonstd::string_view text) : kind(kind), text(text) {} + + std::string describe() const { + switch (kind) { + case Kind::Text: + return ""; + case Kind::LineStatementClose: + return ""; + case Kind::Eof: + return ""; + default: + return static_cast(text); + } + } +}; + +} // namespace inja + +#endif // INCLUDE_INJA_TOKEN_HPP_ diff --git a/isis/src/base/apps/topds4/topds4.cpp b/isis/src/base/apps/topds4/topds4.cpp new file mode 100644 index 0000000000..c364ca4af2 --- /dev/null +++ b/isis/src/base/apps/topds4/topds4.cpp @@ -0,0 +1,42 @@ +#include + +#include + +#include "topds4.h" + +using namespace std; +using namespace inja; +using json = nlohmann::json; + + +namespace Isis { + + + + PvlGroup topds4(UserInterface &ui) { + Cube *icube = new Cube(); + icube->open(ui.GetFileName("FROM")); + return topds4(icube, ui); + } + + + PvlGroup topds4(Cube *cube, UserInterface &ui) { + Process p; + p.SetInputCube(cube); + + Pvl &label = *cube->label(); + + + + json data; + data["name"] = "world"; + render("Hello {{ name }}!", data); // Returns std::string "Hello world!" + render_to(std::cout, "Hello {{ name }}!", data); // Writes "Hello world!" to stream + + + + + std::cout << label << std::endl; + return label.findGroup("Dimensions", Pvl::Traverse); + } +} diff --git a/isis/src/base/apps/topds4/topds4.h b/isis/src/base/apps/topds4/topds4.h new file mode 100644 index 0000000000..6b7d0ea09b --- /dev/null +++ b/isis/src/base/apps/topds4/topds4.h @@ -0,0 +1,18 @@ +#ifndef topds4_h +#define topds4_h + +#include "Cube.h" +#include "Pvl.h" +#include "Process.h" +#include "SpecialPixel.h" +#include "LineManager.h" +#include "FileName.h" +#include "IException.h" +#include "UserInterface.h" + +namespace Isis { + extern PvlGroup topds4(Cube* cube, UserInterface &ui); + extern PvlGroup topds4(UserInterface &ui); +} + +#endif diff --git a/isis/src/base/apps/topds4/topds4.xml b/isis/src/base/apps/topds4/topds4.xml new file mode 100644 index 0000000000..40d0850461 --- /dev/null +++ b/isis/src/base/apps/topds4/topds4.xml @@ -0,0 +1,63 @@ + + + + + dummy + + + + dummy + + + + Trim and Mask + + + + + Removed unreachable code. + + + + + + + cube + input + + Input cube + + + This is the input cube that will be cropped. + + + *.cub + + + + + input + + Input template + + + Input template + + + * + + + + + file + output + + Output PDS4 label + + + The. + + + + + diff --git a/isis/src/base/apps/topds4/utils.h b/isis/src/base/apps/topds4/utils.h new file mode 100644 index 0000000000..458f8c86a2 --- /dev/null +++ b/isis/src/base/apps/topds4/utils.h @@ -0,0 +1,70 @@ +// Copyright (c) 2020 Pantor. All rights reserved. + +#ifndef INCLUDE_INJA_UTILS_HPP_ +#define INCLUDE_INJA_UTILS_HPP_ + +#include +#include +#include +#include + +#include "exceptions.h" +#include "string_view.h" + +namespace inja { + +inline void open_file_or_throw(const std::string &path, std::ifstream &file) { + file.exceptions(std::ifstream::failbit | std::ifstream::badbit); + try { + file.open(path); + } catch (const std::ios_base::failure & /*e*/) { + throw FileError("failed accessing file at '" + path + "'"); + } +} + +namespace string_view { +inline nonstd::string_view slice(nonstd::string_view view, size_t start, size_t end) { + start = std::min(start, view.size()); + end = std::min(std::max(start, end), view.size()); + return view.substr(start, end - start); +} + +inline std::pair split(nonstd::string_view view, char Separator) { + size_t idx = view.find(Separator); + if (idx == nonstd::string_view::npos) { + return std::make_pair(view, nonstd::string_view()); + } + return std::make_pair(slice(view, 0, idx), slice(view, idx + 1, nonstd::string_view::npos)); +} + +inline bool starts_with(nonstd::string_view view, nonstd::string_view prefix) { + return (view.size() >= prefix.size() && view.compare(0, prefix.size(), prefix) == 0); +} +} // namespace string_view + +inline SourceLocation get_source_location(nonstd::string_view content, size_t pos) { + // Get line and offset position (starts at 1:1) + auto sliced = string_view::slice(content, 0, pos); + std::size_t last_newline = sliced.rfind("\n"); + + if (last_newline == nonstd::string_view::npos) { + return {1, sliced.length() + 1}; + } + + // Count newlines + size_t count_lines = 0; + size_t search_start = 0; + while (search_start <= sliced.size()) { + search_start = sliced.find("\n", search_start) + 1; + if (search_start == 0) { + break; + } + count_lines += 1; + } + + return {count_lines + 1, sliced.length() - last_newline}; +} + +} // namespace inja + +#endif // INCLUDE_INJA_UTILS_HPP_ From 16319940191c5a5344e28e2666bed3f79999b0aa Mon Sep 17 00:00:00 2001 From: Kristin Berry Date: Thu, 17 Dec 2020 15:56:18 -0700 Subject: [PATCH 03/28] Initial topds4 app and cmake updates to include inja (#4207) * Removed compile and deprication warnings. Fixes #4110 * temp add of to pds 4 test * Inintal topds4 app and cmake updates to include inja * Delete extra headers * Fixed inja include Co-authored-by: ssides --- isis/CMakeLists.txt | 1 + isis/cmake/FindInja.cmake | 14 +++++++ isis/src/base/apps/topds4/main.cpp | 12 ++++++ isis/src/base/apps/topds4/topds4.cpp | 42 +++++++++++++++++++ isis/src/base/apps/topds4/topds4.h | 18 ++++++++ isis/src/base/apps/topds4/topds4.xml | 63 ++++++++++++++++++++++++++++ 6 files changed, 150 insertions(+) create mode 100644 isis/cmake/FindInja.cmake create mode 100644 isis/src/base/apps/topds4/main.cpp create mode 100644 isis/src/base/apps/topds4/topds4.cpp create mode 100644 isis/src/base/apps/topds4/topds4.h create mode 100644 isis/src/base/apps/topds4/topds4.xml diff --git a/isis/CMakeLists.txt b/isis/CMakeLists.txt index 541f1bf5bb..37c208f63f 100644 --- a/isis/CMakeLists.txt +++ b/isis/CMakeLists.txt @@ -293,6 +293,7 @@ find_package(Kakadu) find_package(Geos 3.5.0 REQUIRED) find_package(Armadillo REQUIRED) find_package(Threads) +find_package(Inja REQUIRED) # In this case, we specify the version numbers being searched for in the non-traditional installs. diff --git a/isis/cmake/FindInja.cmake b/isis/cmake/FindInja.cmake new file mode 100644 index 0000000000..21466f8bb3 --- /dev/null +++ b/isis/cmake/FindInja.cmake @@ -0,0 +1,14 @@ +# CMake module for find_package(Inja) +# Finds include directory and all applicable libraries +# +# Sets the following: +# INJA_INCLUDE_DIR +# INJA_LIBRARY + +find_path(INJA_INCLUDE_DIR + NAME inja.hpp + PATH_SUFFIXES "inja" +) + +message(STATUS "INJA INCLUDE DIR: " ${INJA_INCLUDE_DIR} ) + diff --git a/isis/src/base/apps/topds4/main.cpp b/isis/src/base/apps/topds4/main.cpp new file mode 100644 index 0000000000..ae335f60f4 --- /dev/null +++ b/isis/src/base/apps/topds4/main.cpp @@ -0,0 +1,12 @@ +#include "Isis.h" + +#include "topds4.h" + +using namespace std; +using namespace Isis; + +void IsisMain() { + UserInterface &ui = Application::GetUserInterface(); + PvlGroup results = topds4(ui); + Application::Log(results); +} diff --git a/isis/src/base/apps/topds4/topds4.cpp b/isis/src/base/apps/topds4/topds4.cpp new file mode 100644 index 0000000000..c5481301ab --- /dev/null +++ b/isis/src/base/apps/topds4/topds4.cpp @@ -0,0 +1,42 @@ +#include + +#include + +#include "topds4.h" + +using namespace std; +using namespace inja; +using json = nlohmann::json; + + +namespace Isis { + + + + PvlGroup topds4(UserInterface &ui) { + Cube *icube = new Cube(); + icube->open(ui.GetFileName("FROM")); + return topds4(icube, ui); + } + + + PvlGroup topds4(Cube *cube, UserInterface &ui) { + Process p; + p.SetInputCube(cube); + + Pvl &label = *cube->label(); + + + + json data; + data["name"] = "world"; + render("Hello {{ name }}!", data); // Returns std::string "Hello world!" + render_to(std::cout, "Hello {{ name }}!", data); // Writes "Hello world!" to stream + + + + + std::cout << label << std::endl; + return label.findGroup("Dimensions", Pvl::Traverse); + } +} diff --git a/isis/src/base/apps/topds4/topds4.h b/isis/src/base/apps/topds4/topds4.h new file mode 100644 index 0000000000..6b7d0ea09b --- /dev/null +++ b/isis/src/base/apps/topds4/topds4.h @@ -0,0 +1,18 @@ +#ifndef topds4_h +#define topds4_h + +#include "Cube.h" +#include "Pvl.h" +#include "Process.h" +#include "SpecialPixel.h" +#include "LineManager.h" +#include "FileName.h" +#include "IException.h" +#include "UserInterface.h" + +namespace Isis { + extern PvlGroup topds4(Cube* cube, UserInterface &ui); + extern PvlGroup topds4(UserInterface &ui); +} + +#endif diff --git a/isis/src/base/apps/topds4/topds4.xml b/isis/src/base/apps/topds4/topds4.xml new file mode 100644 index 0000000000..40d0850461 --- /dev/null +++ b/isis/src/base/apps/topds4/topds4.xml @@ -0,0 +1,63 @@ + + + + + dummy + + + + dummy + + + + Trim and Mask + + + + + Removed unreachable code. + + + + + + + cube + input + + Input cube + + + This is the input cube that will be cropped. + + + *.cub + + + + + input + + Input template + + + Input template + + + * + + + + + file + output + + Output PDS4 label + + + The. + + + + + From 5fcc94ce0aecf64dcecbab2178024195ff37285f Mon Sep 17 00:00:00 2001 From: Jesse Mapel Date: Mon, 21 Dec 2020 08:49:58 -0700 Subject: [PATCH 04/28] Added conda-forge inja --- environment.yml | 1 + environment_gcc4.yml | 1 + isis/CMakeLists.txt | 7 +++++-- isis/cmake/FindInja.cmake | 14 -------------- isis/src/base/apps/topds4/topds4.cpp | 12 ++++++------ 5 files changed, 13 insertions(+), 22 deletions(-) delete mode 100644 isis/cmake/FindInja.cmake diff --git a/environment.yml b/environment.yml index e6aca798b5..51720f1cf3 100644 --- a/environment.yml +++ b/environment.yml @@ -24,6 +24,7 @@ dependencies: - gsl>=2.6 - hdf5 - icu + - inja - jama - jpeg==9b - kakadu==1 diff --git a/environment_gcc4.yml b/environment_gcc4.yml index 2dcb4c39b3..217cc4b93b 100644 --- a/environment_gcc4.yml +++ b/environment_gcc4.yml @@ -26,6 +26,7 @@ dependencies: - gsl - hdf5 - icu + - inja - jama - jpeg==9b - kakadu==1 diff --git a/isis/CMakeLists.txt b/isis/CMakeLists.txt index 37c208f63f..cfc7f89a3b 100644 --- a/isis/CMakeLists.txt +++ b/isis/CMakeLists.txt @@ -80,7 +80,7 @@ option(JP2KFLAG "Whether or not to build using JPEG2000 support" OFF ) option(pybindings "Turn on to build Python bindings" OFF ) # if cmake install prefix is not set, and conda env is activated, use the -# conda env as the install directory +# conda env as the install directory. if(DEFINED ENV{CONDA_PREFIX} AND CMAKE_INSTALL_PREFIX STREQUAL "/usr/local") set(CMAKE_INSTALL_PREFIX $ENV{CONDA_PREFIX}) endif() @@ -293,7 +293,7 @@ find_package(Kakadu) find_package(Geos 3.5.0 REQUIRED) find_package(Armadillo REQUIRED) find_package(Threads) -find_package(Inja REQUIRED) +find_package(inja REQUIRED) # In this case, we specify the version numbers being searched for in the non-traditional installs. @@ -348,6 +348,9 @@ foreach (_variableName ${_variableNames}) endif() endforeach() +# add target based linkages to ALLLIBS variable +list(APPEND ALLLIBS pantor::inja) + # Sometimes we add the same lib more than once (especially with LIBDIRS) list(REMOVE_DUPLICATES ALLLIBDIRS) list(REMOVE_DUPLICATES ALLLIBS) diff --git a/isis/cmake/FindInja.cmake b/isis/cmake/FindInja.cmake deleted file mode 100644 index 21466f8bb3..0000000000 --- a/isis/cmake/FindInja.cmake +++ /dev/null @@ -1,14 +0,0 @@ -# CMake module for find_package(Inja) -# Finds include directory and all applicable libraries -# -# Sets the following: -# INJA_INCLUDE_DIR -# INJA_LIBRARY - -find_path(INJA_INCLUDE_DIR - NAME inja.hpp - PATH_SUFFIXES "inja" -) - -message(STATUS "INJA INCLUDE DIR: " ${INJA_INCLUDE_DIR} ) - diff --git a/isis/src/base/apps/topds4/topds4.cpp b/isis/src/base/apps/topds4/topds4.cpp index c5481301ab..0f348a67e0 100644 --- a/isis/src/base/apps/topds4/topds4.cpp +++ b/isis/src/base/apps/topds4/topds4.cpp @@ -1,6 +1,6 @@ #include -#include +#include #include "topds4.h" @@ -10,16 +10,16 @@ using json = nlohmann::json; namespace Isis { - - - + + + PvlGroup topds4(UserInterface &ui) { Cube *icube = new Cube(); - icube->open(ui.GetFileName("FROM")); + icube->open(ui.GetFileName("FROM")); return topds4(icube, ui); } - + PvlGroup topds4(Cube *cube, UserInterface &ui) { Process p; p.SetInputCube(cube); From 290d28056c3bedecad2b775c92536f2cb6d18727 Mon Sep 17 00:00:00 2001 From: ssides Date: Tue, 22 Dec 2020 07:56:29 -0700 Subject: [PATCH 05/28] add template file --- isis/src/base/apps/topds4/topds4.cpp | 10 +++++++--- isis/src/base/apps/topds4/topds4.xml | 6 ++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/isis/src/base/apps/topds4/topds4.cpp b/isis/src/base/apps/topds4/topds4.cpp index c5481301ab..a46f27da02 100644 --- a/isis/src/base/apps/topds4/topds4.cpp +++ b/isis/src/base/apps/topds4/topds4.cpp @@ -29,14 +29,18 @@ namespace Isis { json data; + data["IsisCube"]["Core"]["Dimensions"]["Samples"] = "1023"; + data["IsisCube"]["Core"]["Dimensions"]["Lines"] = "1024"; data["name"] = "world"; render("Hello {{ name }}!", data); // Returns std::string "Hello world!" render_to(std::cout, "Hello {{ name }}!", data); // Writes "Hello world!" to stream + Environment env; + std::string inputTemplate = ui.GetFileName("TEMPLATE").toStdString(); + std::string result = env.render_file(inputTemplate, data); + std::cout << result << std::endl; - - - std::cout << label << std::endl; + //std::cout << label << std::endl; return label.findGroup("Dimensions", Pvl::Traverse); } } diff --git a/isis/src/base/apps/topds4/topds4.xml b/isis/src/base/apps/topds4/topds4.xml index 40d0850461..656f30ad84 100644 --- a/isis/src/base/apps/topds4/topds4.xml +++ b/isis/src/base/apps/topds4/topds4.xml @@ -36,6 +36,7 @@ + filename input Input template @@ -43,13 +44,10 @@ Input template - - * - - file + filename output Output PDS4 label From c6926047808b159c42ef3a114d99d9fb93d0f2e3 Mon Sep 17 00:00:00 2001 From: Jesse Mapel Date: Tue, 22 Dec 2020 08:01:56 -0700 Subject: [PATCH 06/28] Adds PVL to JSON converters (#4212) * Pvl to JSON conversion * Renamed file * Added repeated elements and examples --- isis/src/base/objs/PvlToJSON/Makefile | 7 + isis/src/base/objs/PvlToJSON/PvlToJSON.cpp | 352 +++++++++++++++++++++ isis/src/base/objs/PvlToJSON/PvlToJSON.h | 27 ++ isis/tests/PvlToJSONTests.cpp | 215 +++++++++++++ 4 files changed, 601 insertions(+) create mode 100644 isis/src/base/objs/PvlToJSON/Makefile create mode 100644 isis/src/base/objs/PvlToJSON/PvlToJSON.cpp create mode 100644 isis/src/base/objs/PvlToJSON/PvlToJSON.h create mode 100644 isis/tests/PvlToJSONTests.cpp diff --git a/isis/src/base/objs/PvlToJSON/Makefile b/isis/src/base/objs/PvlToJSON/Makefile new file mode 100644 index 0000000000..f122bc8822 --- /dev/null +++ b/isis/src/base/objs/PvlToJSON/Makefile @@ -0,0 +1,7 @@ +ifeq ($(ISISROOT), $(BLANK)) +.SILENT: +error: + echo "Please set ISISROOT"; +else + include $(ISISROOT)/make/isismake.objs +endif \ No newline at end of file diff --git a/isis/src/base/objs/PvlToJSON/PvlToJSON.cpp b/isis/src/base/objs/PvlToJSON/PvlToJSON.cpp new file mode 100644 index 0000000000..a9f2c9ba31 --- /dev/null +++ b/isis/src/base/objs/PvlToJSON/PvlToJSON.cpp @@ -0,0 +1,352 @@ +/** This is free and unencumbered software released into the public domain. +The authors of ISIS do not claim copyright on the contents of this file. +For more details about the LICENSE terms and the AUTHORS, you will +find files of those names at the top level of this repository. **/ + +/* SPDX-License-Identifier: CC0-1.0 */ +#include "PvlToJSON.h" + +#include + +#include "Pvl.h" +#include "PvlKeyword.h" + +using json = nlohmann::json; +using namespace std; + +namespace Isis { + + /** + * Convert the contents of a PvlKeyword to a JSON object. + * All values from the keyword will be stored in "Value", all units will be + * stored in "Units", and all comments will be stored in "Comment". + * + * How a keyword with only a value is converted + * + * PvlKeyword: + *
+   * ExposureDuration = 10
+   * 
+ * + * JSON: + *
+   * {"Value":"10"}
+   * 
+ * + * How single values, units, and comments are converted + * + * PvlKeyword: + *
+   * # The exposure duration of the image
+   * ExposureDuration = 10 
+   * 
+ * + * JSON: + *
+   * {"Comment":"# The exposure duration of the image",
+   *  "Units":"ms",
+   *  "Value":"10"}
+   * 
+ * + * How multiple values, units, and comments are converted + * + * PvlKeyword: + *
+   * # First comment
+   * # Second comment
+   * TestKey2 = ("This keyword has multiple comments" ,
+   *             "It also has multiple values",
+   *             "It even has values with and without units" )
+   * 
+ * + * JSON: + *
+   * {"Comment":["# First comment",
+   *             "# Second comment"],
+   *  "Units":["first unit",
+   *           "",
+   *           "third unit"],
+   *  "Value":["This keyword has multiple comments",
+   *           "It also has multiple values",
+   *           "It even has values with and without units"]}
+   * 
+ * + * @param keyword The keyword to convert + * @return @b json The contents of the keyword as a JSON object + */ + json pvlKeywordToJSON(PvlKeyword &keyword) { + json jsonKeyword; + + // Convert values + if (keyword.size() == 1) { + jsonKeyword["Value"] = keyword[0].toStdString(); + } + else if (keyword.size() > 1) { + json valueList; + for (int i = 0; i < keyword.size(); i++) { + valueList.push_back(keyword[i].toStdString()); + } + jsonKeyword["Value"] = valueList; + } + + // Optionally convert units + if (keyword.size() == 1 && !keyword.unit(0).isEmpty()) { + jsonKeyword["Units"] = keyword.unit(0).toStdString(); + } + else if (keyword.size() > 1 && !keyword.unit(0).isEmpty()) { + json valueList; + for (int i = 0; i < keyword.size(); i++) { + valueList.push_back(keyword.unit(i).toStdString()); + } + jsonKeyword["Units"] = valueList; + } + + // Optionally convert comments + if (keyword.comments() == 1) { + jsonKeyword["Comment"] = keyword.comment(0).toStdString(); + } + else if (keyword.comments() > 1) { + json commentList; + for (int i = 0; i < keyword.comments(); i++) { + commentList.push_back(keyword.comment(i).toStdString()); + } + jsonKeyword["Comment"] = commentList; + } + return jsonKeyword; + } + + + /** + * Convert the contents of a PvlContainer to a JSON object. + * Any comments in the container will be stored in "Comment". + * Comments associated with keywords will be stored inside their json object. + * If a keyword is repeated in the container, then the instances will be + * packed into an array in the order that they occur. + * + * This function is used by the PvlGroup, PvlObject, and Pvl conversion + * functions; see their documentation for examples. + * + * @param container The container to convert + * @return @b json The contents of the container as a JSON object + */ + json pvlContainerToJSON(PvlContainer &container) { + json jsonContainer; + + // Convert keywords + PvlContainer::PvlKeywordIterator keywordIt; + for (keywordIt = container.begin(); keywordIt != container.end(); keywordIt++) { + // Handle repeated keywords by packing them into an array + if ( jsonContainer.contains(keywordIt->name().toStdString()) ) { + if (!jsonContainer[keywordIt->name().toStdString()].is_array()) { + json repeatedArray; + repeatedArray.push_back(jsonContainer[keywordIt->name().toStdString()]); + jsonContainer[keywordIt->name().toStdString()] = repeatedArray; + } + jsonContainer[keywordIt->name().toStdString()].push_back(pvlKeywordToJSON(*keywordIt)); + } + else { + jsonContainer[keywordIt->name().toStdString()] = pvlKeywordToJSON(*keywordIt); + } + } + + // Optionally convert comments + if (container.comments() == 1) { + jsonContainer["Comment"] = container.comment(0).toStdString(); + } + else if (container.comments() > 1) { + json commentList; + for (int i = 0; i < container.comments(); i++) { + commentList.push_back(container.comment(i).toStdString()); + } + jsonContainer["Comment"] = commentList; + } + return jsonContainer; + + } + + + /** + * Convert the contents of a PvlGroup to a JSON object. + * Any comments in the group will be stored in "Comment". + * Comments associated with keywords will be stored inside their json object. + * + * A simple example group + * + * PvlGroup: + *
+   * Group = TestGroup
+   *   TestKey1 = A
+   *   TestKey2 = 1
+   * End_Group
+   * 
+ * + * JSON: + *
+   * {"TestKey1":{"Value":"A"},
+   *  "TestKey2":{"Value":"1"}}
+   * 
+ * + * If a keyword is repeated in the group, then the instances will be packed + * into an array in the order that they occur. + * + * An example group with repeated keywords + * + * PvlGroup: + *
+   * Group = TestGroup
+   *   TestKey1 = A
+   *   TestKey2 = 1
+   *   TestKey2 = 2
+   * End_Group
+   * 
+ * + * JSON: + *
+   * {"TestKey1":{"Value":"A"},
+   *  "TestKey2":[{"Value":"1"},
+   *              {"Value":"2"}]}
+   * 
+ * + * @param group The group to convert + * @return @b json The contents of the group as a JSON object + */ + json pvlGroupToJSON(PvlGroup &group) { + // PvlGroups are just PvlContainers with extra input/output options + // so we can just use the PvlContainer conversion directly. + return pvlContainerToJSON(group);; + } + + + /** + * Convert the contents of a PvlObject to a JSON object. + * Any comments in the base object will be stored in "Comment". + * Comments associated with keywords, groups, or nested objects will be + * stored inside their associated JSON object. + * + * An example demonstrating how nested objects and groups are converted + * + * PvlObject: + *
+   * Object = TestObject2
+  *    TestKey3 = "hello world"
+  *
+  *    Object = TestObject1
+  *      TestKey1 = A
+  *      TestKey2 = 1
+  *    End_Object
+  *
+  *    Group = TestGroup
+  *      TestKey3 = "hello world"
+  *    End_Group
+  *  End_Object
+   * 
+ * + * JSON: + *
+   * {"TestGroup":{"TestKey3":{"Value":"hello world"}},
+   *  "TestKey3":{"Value":"hello world"},
+   *  "TestObject1":{"TestKey1":{"Value":"A"},
+   *                 "TestKey2":{"Value":"1"}}}
+   * 
+ * + * If there are keywords, groups, and/or nested objects with the same name at + * the same level in the object, then they will be stored in an array starting + * with the keywords, followed by the groups, and then finally the objects. + * Within each subset, the repeated elements will be ordered the same as they + * occur in the object. + * + * An example with repeated element names at the same level + * + * PvlObject: + *
+   * Object = TestObject2
+   *   TestKey3  = "hello world"
+   *   TestGroup = Q
+   *
+   *   Object = TestObject1
+   *     TestKey1 = A
+   *     TestKey2 = 1
+   *   End_Object
+   *
+   *   Object = TestGroup
+   *     TestKey2 = 1
+   *   End_Object
+   *
+   *   Group = TestGroup
+   *     TestKey3 = "hello world"
+   *   End_Group
+   * End_Object
+   * 
+ * + * JSON: + *
+   * {"TestGroup":[{"Value":"Q"},
+   *               {"TestKey3":{"Value":"hello world"}},
+   *               {"TestKey2":{"Value":"1"}}],
+   *  "TestKey3":{"Value":"hello world"},
+   *  "TestObject1":{"TestKey1":{"Value":"A"},
+   *                 "TestKey2":{"Value":"1"}}}
+   * 
+ * + * @param object The object to convert + * @return @b json The contents of the object as a JSON object + */ + json pvlObjectToJSON(PvlObject &object) { + // Convert keywords and comments + json jsonObject = pvlContainerToJSON(object); + + // Convert groups + PvlObject::PvlGroupIterator groupIt; + for (groupIt = object.beginGroup(); groupIt != object.endGroup(); groupIt++) { + // Handle repeated elements by packing them into an array + if ( jsonObject.contains(groupIt->name().toStdString()) ) { + if (!jsonObject[groupIt->name().toStdString()].is_array()) { + json repeatedArray; + repeatedArray.push_back(jsonObject[groupIt->name().toStdString()]); + jsonObject[groupIt->name().toStdString()] = repeatedArray; + } + jsonObject[groupIt->name().toStdString()].push_back(pvlGroupToJSON(*groupIt)); + } + else { + jsonObject[groupIt->name().toStdString()] = pvlGroupToJSON(*groupIt); + } + } + + // Convert nested objects + PvlObject::PvlObjectIterator objectIt; + for (objectIt = object.beginObject(); objectIt != object.endObject(); objectIt++) { + // Handle repeated elements by packing them into an array + if ( jsonObject.contains(objectIt->name().toStdString()) ) { + if (!jsonObject[objectIt->name().toStdString()].is_array()) { + json repeatedArray; + repeatedArray.push_back(jsonObject[objectIt->name().toStdString()]); + jsonObject[objectIt->name().toStdString()] = repeatedArray; + } + jsonObject[objectIt->name().toStdString()].push_back(pvlObjectToJSON(*objectIt)); + } + else { + jsonObject[objectIt->name().toStdString()] = pvlObjectToJSON(*objectIt); + } + } + return jsonObject; + } + + + /** + * Convert the contents of a Pvl to a JSON object. + * Any comments in the root of the Pvl will be stored in "Comment". + * Comments associated with keywords, groups, or objects will be stored + * inside their associated JSON object. If there are keywords, groups, + * and/or objects with the same name at the same level in the Pvl, then + * they will be stored in an array starting with the keywords, followed by the + * groups, and then finally the objects. Within each subset, the repeated + * elements will be ordered the same as they occur in the object. + * + * @param pvl The Pvl to convert + * @return @b json The contents of the Pvl as a JSON object + */ + json pvlToJSON(Pvl &pvl) { + return pvlObjectToJSON(pvl); + } + +} diff --git a/isis/src/base/objs/PvlToJSON/PvlToJSON.h b/isis/src/base/objs/PvlToJSON/PvlToJSON.h new file mode 100644 index 0000000000..9b46a3b269 --- /dev/null +++ b/isis/src/base/objs/PvlToJSON/PvlToJSON.h @@ -0,0 +1,27 @@ +#ifndef PvlToJSON_h +#define PvlToJSON_h + +/** This is free and unencumbered software released into the public domain. +The authors of ISIS do not claim copyright on the contents of this file. +For more details about the LICENSE terms and the AUTHORS, you will +find files of those names at the top level of this repository. **/ + +/* SPDX-License-Identifier: CC0-1.0 */ + +#include + +namespace Isis { + class Pvl; + class PvlContainer; + class PvlGroup; + class PvlKeyword; + class PvlObject; + + nlohmann::json pvlToJSON(Pvl &pvl); + nlohmann::json pvlContainerToJSON(PvlContainer &container); + nlohmann::json pvlKeywordToJSON(PvlKeyword &keyword); + nlohmann::json pvlGroupToJSON(PvlGroup &group); + nlohmann::json pvlObjectToJSON(PvlObject &object); +} + +#endif diff --git a/isis/tests/PvlToJSONTests.cpp b/isis/tests/PvlToJSONTests.cpp new file mode 100644 index 0000000000..171130f60f --- /dev/null +++ b/isis/tests/PvlToJSONTests.cpp @@ -0,0 +1,215 @@ +#include + +#include "PvlToJSON.h" +#include "Pvl.h" +#include "PvlGroup.h" +#include "PvlKeyword.h" +#include "PvlObject.h" + +#include "gmock/gmock.h" + +using json = nlohmann::json; +using namespace Isis; + + +TEST(PvlToJSONTest, KeywordConversion) { + PvlKeyword testKey1("TestKey1", "A"); + PvlKeyword testKey2("TestKey2", "1"); + testKey2 += "2"; + + json testJson1 = pvlKeywordToJSON(testKey1); + json testJson2 = pvlKeywordToJSON(testKey2); + + EXPECT_EQ(testJson1["Value"], testKey1[0].toStdString()); + EXPECT_EQ(testJson2["Value"][0], testKey2[0].toStdString()); + EXPECT_EQ(testJson2["Value"][1], testKey2[1].toStdString()); +} + + +TEST(PvlToJSONTest, KeywordCommentConversion) { + PvlKeyword testKey1("TestKey1", "This keyword has 1 comment"); + testKey1.addComment("Test comment"); + PvlKeyword testKey2("TestKey2", "This keyword has multiple comments"); + testKey2.addComment("First comment"); + testKey2.addComment("Second comment"); + PvlKeyword testKey3("TestKey3", "This keywords has no comments"); + + json testJson1 = pvlKeywordToJSON(testKey1); + json testJson2 = pvlKeywordToJSON(testKey2); + json testJson3 = pvlKeywordToJSON(testKey3); + + EXPECT_EQ(testJson1["Comment"], testKey1.comment(0).toStdString()); + EXPECT_EQ(testJson2["Comment"][0], testKey2.comment(0).toStdString()); + EXPECT_EQ(testJson2["Comment"][1], testKey2.comment(1).toStdString()); + EXPECT_FALSE(testJson3.contains("Comment")); +} + + +TEST(PvlToJSONTest, KeywordUnitConversion) { + PvlKeyword testKey1("TestKey1", "1", "m"); + PvlKeyword testKey2("TestKey2", "2", "m"); + testKey2.addValue("Hello World"); + testKey2.addValue("3.14", "r"); + PvlKeyword testKey3("TestKey3", "2"); + testKey3.addValue("Hello World"); + testKey3.addValue("3.14"); + + json testJson1 = pvlKeywordToJSON(testKey1); + json testJson2 = pvlKeywordToJSON(testKey2); + json testJson3 = pvlKeywordToJSON(testKey3); + + EXPECT_EQ(testJson1["Units"], testKey1.unit(0).toStdString()); + EXPECT_EQ(testJson2["Units"][0], testKey2.unit(0).toStdString()); + EXPECT_EQ(testJson2["Units"][1], testKey2.unit(1).toStdString()); + EXPECT_EQ(testJson2["Units"][2], testKey2.unit(2).toStdString()); + EXPECT_FALSE(testJson3.contains("Units")); +} + + +TEST(PvlToJSONTest, GroupConversion) { + PvlKeyword testKey1("TestKey1", "A"); + PvlKeyword testKey2("TestKey2", "1"); + PvlGroup testGroup("TestGroup"); + testGroup += testKey1; + testGroup += testKey2; + + json testJson = pvlGroupToJSON(testGroup); + + EXPECT_TRUE(testJson.contains(testKey1.name().toStdString())); + EXPECT_TRUE(testJson.contains(testKey2.name().toStdString())); +} + + +TEST(PvlToJSONTest, GroupRepeatedKeysConversion) { + PvlKeyword testKey1("TestKey2", "1"); + PvlKeyword testKey2(testKey1.name(), "2"); + PvlGroup testGroup("TestGroup"); + testGroup += testKey1; + testGroup += testKey2; + + json testJson = pvlGroupToJSON(testGroup); + + EXPECT_TRUE(testJson.contains(testKey2.name().toStdString())); + EXPECT_EQ(testJson[testKey1.name().toStdString()].size(), 2); + EXPECT_EQ(testJson[testKey1.name().toStdString()][0]["Value"], testKey1[0].toStdString()); + EXPECT_EQ(testJson[testKey2.name().toStdString()][1]["Value"], testKey2[0].toStdString()); +} + + +TEST(PvlToJSONTest, GroupCommentConversion) { + PvlGroup testGroup1("TestGroup1"); + testGroup1.addComment("Test comment"); + PvlGroup testGroup2("TestGroup2"); + testGroup2.addComment("First Comment"); + testGroup2.addComment("Second Comment"); + PvlGroup testGroup3("TestGroup3"); + + json testJson1 = pvlGroupToJSON(testGroup1); + json testJson2 = pvlGroupToJSON(testGroup2); + json testJson3 = pvlGroupToJSON(testGroup3); + + EXPECT_EQ(testJson1["Comment"], testGroup1.comment(0).toStdString()); + EXPECT_EQ(testJson2["Comment"][0], testGroup2.comment(0).toStdString()); + EXPECT_EQ(testJson2["Comment"][1], testGroup2.comment(1).toStdString()); + EXPECT_FALSE(testJson3.contains("Comment")); +} + + +TEST(PvlToJSONTest, ObjectConversion) { + PvlKeyword testKey1("TestKey1", "A"); + PvlKeyword testKey2("TestKey2", "1"); + PvlKeyword testKey3("TestKey3", "hello world"); + PvlGroup testGroup("TestGroup"); + testGroup += testKey1; + testGroup += testKey2; + PvlObject testObject("TestObject"); + testObject += testGroup; + testObject += testKey3; + + json testJson = pvlObjectToJSON(testObject); + + EXPECT_TRUE(testJson.contains(testGroup.name().toStdString())); + EXPECT_TRUE(testJson.contains(testKey3.name().toStdString())); +} + + +TEST(PvlToJSONTest, ObjectNestedConversion) { + PvlKeyword testKey1("TestKey1", "A"); + PvlKeyword testKey2("TestKey2", "1"); + PvlKeyword testKey3("TestKey3", "hello world"); + PvlObject testObject1("TestObject1"); + testObject1 += testKey1; + testObject1 += testKey2; + PvlObject testObject2("TestObject2"); + testObject2 += testObject1; + testObject2 += testKey3; + + json testJson = pvlObjectToJSON(testObject2); + + EXPECT_TRUE(testJson.contains(testKey3.name().toStdString())); + EXPECT_TRUE(testJson.contains(testObject1.name().toStdString())); + EXPECT_TRUE(testJson[testObject1.name().toStdString()].contains(testKey1.name().toStdString())); + EXPECT_TRUE(testJson[testObject1.name().toStdString()].contains(testKey2.name().toStdString())); +} + + +TEST(PvlToJSONTest, ObjectRepeatedConversion) { + PvlKeyword testKey1("TestKey1", "A"); + PvlKeyword testKey2("TestKey2", "1"); + PvlGroup testGroup(testKey1.name()); + testGroup += testKey2; + PvlObject testObject("TestObject"); + testObject += testGroup; + testObject += testKey1; + testObject += testKey2; + + json testJson = pvlObjectToJSON(testObject); + + EXPECT_TRUE(testJson.contains(testKey1.name().toStdString())); + EXPECT_TRUE(testJson.contains(testKey2.name().toStdString())); + EXPECT_EQ(testJson[testKey1.name().toStdString()].size(), 2); + EXPECT_TRUE(testJson[testKey1.name().toStdString()][0].contains("Value")); + EXPECT_TRUE(testJson[testKey1.name().toStdString()][1].contains(testKey2.name().toStdString())); +} + + +TEST(PvlToJSONTest, ObjectCommentConversion) { + PvlObject testObject1("TestObject1"); + testObject1.addComment("Test comment"); + PvlObject testObject2("TestObject2"); + testObject2.addComment("First Comment"); + testObject2.addComment("Second Comment"); + PvlObject testObject3("TestObject3"); + + json testJson1 = pvlObjectToJSON(testObject1); + json testJson2 = pvlObjectToJSON(testObject2); + json testJson3 = pvlObjectToJSON(testObject3); + + EXPECT_EQ(testJson1["Comment"], testObject1.comment(0).toStdString()); + EXPECT_EQ(testJson2["Comment"][0], testObject2.comment(0).toStdString()); + EXPECT_EQ(testJson2["Comment"][1], testObject2.comment(1).toStdString()); + EXPECT_FALSE(testJson3.contains("Comment")); +} + + +TEST(PvlToJSONTest, PvlConversion) { + PvlKeyword testKey1("TestKey1", "A"); + PvlKeyword testKey2("TestKey2", "1"); + PvlKeyword testKey3("TestKey3", "hello world"); + PvlKeyword testKey4("TestKey4", "3.14"); + PvlObject testObject("TestObject"); + testObject += testKey1; + testObject += testKey2; + PvlGroup testGroup("TestGroup"); + testGroup += testKey3; + Pvl testPvl; + testPvl += testObject; + testPvl += testGroup; + testPvl += testKey4; + + json testJson = pvlToJSON(testPvl); + + EXPECT_TRUE(testJson.contains(testGroup.name().toStdString())); + EXPECT_TRUE(testJson.contains(testObject.name().toStdString())); + EXPECT_TRUE(testJson.contains(testKey4.name().toStdString())); +} From 6618a0d40ae0be1700bd44085eca497c9b62a5f2 Mon Sep 17 00:00:00 2001 From: Stuart Sides Date: Mon, 4 Jan 2021 09:47:21 -0700 Subject: [PATCH 07/28] Topds4 real parameters (#4223) * now has some real parameters * removed inja files --- .../20190612T090019S776_map_L0pan.xml.tpl | 53 + isis/src/base/apps/topds4/config.h | 76 - isis/src/base/apps/topds4/environment2.h | 228 --- isis/src/base/apps/topds4/exceptions.h | 50 - isis/src/base/apps/topds4/function_storage.h | 139 -- isis/src/base/apps/topds4/inja.h | 15 - isis/src/base/apps/topds4/lexer.h | 418 ----- isis/src/base/apps/topds4/node.h | 328 ---- isis/src/base/apps/topds4/parser.h | 582 ------- isis/src/base/apps/topds4/renderer.h | 607 ------- isis/src/base/apps/topds4/statistics2.h | 68 - isis/src/base/apps/topds4/string_view.h | 1416 ----------------- isis/src/base/apps/topds4/template2.h | 39 - isis/src/base/apps/topds4/token.h | 76 - isis/src/base/apps/topds4/topds4.cpp | 65 +- isis/src/base/apps/topds4/topds4.xml | 30 +- isis/src/base/apps/topds4/utils.h | 70 - 17 files changed, 131 insertions(+), 4129 deletions(-) create mode 100644 isis/src/base/apps/topds4/20190612T090019S776_map_L0pan.xml.tpl delete mode 100644 isis/src/base/apps/topds4/config.h delete mode 100644 isis/src/base/apps/topds4/environment2.h delete mode 100644 isis/src/base/apps/topds4/exceptions.h delete mode 100644 isis/src/base/apps/topds4/function_storage.h delete mode 100644 isis/src/base/apps/topds4/inja.h delete mode 100644 isis/src/base/apps/topds4/lexer.h delete mode 100644 isis/src/base/apps/topds4/node.h delete mode 100644 isis/src/base/apps/topds4/parser.h delete mode 100644 isis/src/base/apps/topds4/renderer.h delete mode 100644 isis/src/base/apps/topds4/statistics2.h delete mode 100644 isis/src/base/apps/topds4/string_view.h delete mode 100644 isis/src/base/apps/topds4/template2.h delete mode 100644 isis/src/base/apps/topds4/token.h delete mode 100644 isis/src/base/apps/topds4/utils.h diff --git a/isis/src/base/apps/topds4/20190612T090019S776_map_L0pan.xml.tpl b/isis/src/base/apps/topds4/20190612T090019S776_map_L0pan.xml.tpl new file mode 100644 index 0000000000..4eddb57ad9 --- /dev/null +++ b/isis/src/base/apps/topds4/20190612T090019S776_map_L0pan.xml.tpl @@ -0,0 +1,53 @@ + + + + + + + + + filename + dateandtime + filesize + +
+ headeroffset + headerLength + fileType +
+ + Active Area + imageOffsetBytes + numAxes + Last Index Fastest + OCAMS image 1024 by 1024 pixel active array. + + bitType + DN + 1 + WHAT IS THIS + + + Line + {{MainLabel.IsisCube.Core.Dimensions.Lines.Value}} + 1 + + + Sample + {{MainLabel.IsisCube.Core.Dimensions.Samples.Value}} + 2 + + +
+
diff --git a/isis/src/base/apps/topds4/config.h b/isis/src/base/apps/topds4/config.h deleted file mode 100644 index 201a429f36..0000000000 --- a/isis/src/base/apps/topds4/config.h +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) 2019 Pantor. All rights reserved. - -#ifndef INCLUDE_INJA_CONFIG_HPP_ -#define INCLUDE_INJA_CONFIG_HPP_ - -#include -#include - -#include "string_view.h" - -namespace inja { - -/*! - * \brief Class for lexer configuration. - */ -struct LexerConfig { - std::string statement_open {"{%"}; - std::string statement_open_no_lstrip {"{%+"}; - std::string statement_open_force_lstrip {"{%-"}; - std::string statement_close {"%}"}; - std::string statement_close_force_rstrip {"-%}"}; - std::string line_statement {"##"}; - std::string expression_open {"{{"}; - std::string expression_open_force_lstrip {"{{-"}; - std::string expression_close {"}}"}; - std::string expression_close_force_rstrip {"-}}"}; - std::string comment_open {"{#"}; - std::string comment_close {"#}"}; - std::string open_chars {"#{"}; - - bool trim_blocks {false}; - bool lstrip_blocks {false}; - - void update_open_chars() { - open_chars = ""; - if (open_chars.find(line_statement[0]) == std::string::npos) { - open_chars += line_statement[0]; - } - if (open_chars.find(statement_open[0]) == std::string::npos) { - open_chars += statement_open[0]; - } - if (open_chars.find(statement_open_no_lstrip[0]) == std::string::npos) { - open_chars += statement_open_no_lstrip[0]; - } - if (open_chars.find(statement_open_force_lstrip[0]) == std::string::npos) { - open_chars += statement_open_force_lstrip[0]; - } - if (open_chars.find(expression_open[0]) == std::string::npos) { - open_chars += expression_open[0]; - } - if (open_chars.find(expression_open_force_lstrip[0]) == std::string::npos) { - open_chars += expression_open_force_lstrip[0]; - } - if (open_chars.find(comment_open[0]) == std::string::npos) { - open_chars += comment_open[0]; - } - } -}; - -/*! - * \brief Class for parser configuration. - */ -struct ParserConfig { - bool search_included_templates_in_files {true}; -}; - -/*! - * \brief Class for render configuration. - */ -struct RenderConfig { - bool throw_at_missing_includes {true}; -}; - -} // namespace inja - -#endif // INCLUDE_INJA_CONFIG_HPP_ diff --git a/isis/src/base/apps/topds4/environment2.h b/isis/src/base/apps/topds4/environment2.h deleted file mode 100644 index 730496be7d..0000000000 --- a/isis/src/base/apps/topds4/environment2.h +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright (c) 2019 Pantor. All rights reserved. - -#ifndef INCLUDE_INJA_ENVIRONMENT_HPP_ -#define INCLUDE_INJA_ENVIRONMENT_HPP_ - -#include -#include -#include -#include -#include - -#include - -#include "config.h" -#include "function_storage.h" -#include "parser.h" -#include "renderer.h" -#include "string_view.h" -#include "template2.h" -#include "utils.h" - -namespace inja { - -using json = nlohmann::json; - -/*! - * \brief Class for changing the configuration. - */ -class Environment { - std::string input_path; - std::string output_path; - - LexerConfig lexer_config; - ParserConfig parser_config; - RenderConfig render_config; - - FunctionStorage function_storage; - TemplateStorage template_storage; - -public: - Environment() : Environment("") {} - - explicit Environment(const std::string &global_path) : input_path(global_path), output_path(global_path) {} - - Environment(const std::string &input_path, const std::string &output_path) - : input_path(input_path), output_path(output_path) {} - - /// Sets the opener and closer for template statements - void set_statement(const std::string &open, const std::string &close) { - lexer_config.statement_open = open; - lexer_config.statement_open_no_lstrip = open + "+"; - lexer_config.statement_open_force_lstrip = open + "-"; - lexer_config.statement_close = close; - lexer_config.statement_close_force_rstrip = "-" + close; - lexer_config.update_open_chars(); - } - - /// Sets the opener for template line statements - void set_line_statement(const std::string &open) { - lexer_config.line_statement = open; - lexer_config.update_open_chars(); - } - - /// Sets the opener and closer for template expressions - void set_expression(const std::string &open, const std::string &close) { - lexer_config.expression_open = open; - lexer_config.expression_open_force_lstrip = open + "-"; - lexer_config.expression_close = close; - lexer_config.expression_close_force_rstrip = "-" + close; - lexer_config.update_open_chars(); - } - - /// Sets the opener and closer for template comments - void set_comment(const std::string &open, const std::string &close) { - lexer_config.comment_open = open; - lexer_config.comment_close = close; - lexer_config.update_open_chars(); - } - - /// Sets whether to remove the first newline after a block - void set_trim_blocks(bool trim_blocks) { - lexer_config.trim_blocks = trim_blocks; - } - - /// Sets whether to strip the spaces and tabs from the start of a line to a block - void set_lstrip_blocks(bool lstrip_blocks) { - lexer_config.lstrip_blocks = lstrip_blocks; - } - - /// Sets the element notation syntax - void set_search_included_templates_in_files(bool search_in_files) { - parser_config.search_included_templates_in_files = search_in_files; - } - - /// Sets whether a missing include will throw an error - void set_throw_at_missing_includes(bool will_throw) { - render_config.throw_at_missing_includes = will_throw; - } - - Template parse(nonstd::string_view input) { - Parser parser(parser_config, lexer_config, template_storage, function_storage); - return parser.parse(input); - } - - Template parse_template(const std::string &filename) { - Parser parser(parser_config, lexer_config, template_storage, function_storage); - auto result = Template(parser.load_file(input_path + static_cast(filename))); - parser.parse_into_template(result, input_path + static_cast(filename)); - return result; - } - - Template parse_file(const std::string &filename) { - return parse_template(filename); - } - - std::string render(nonstd::string_view input, const json &data) { return render(parse(input), data); } - - std::string render(const Template &tmpl, const json &data) { - std::stringstream os; - render_to(os, tmpl, data); - return os.str(); - } - - std::string render_file(const std::string &filename, const json &data) { - return render(parse_template(filename), data); - } - - std::string render_file_with_json_file(const std::string &filename, const std::string &filename_data) { - const json data = load_json(filename_data); - return render_file(filename, data); - } - - void write(const std::string &filename, const json &data, const std::string &filename_out) { - std::ofstream file(output_path + filename_out); - file << render_file(filename, data); - file.close(); - } - - void write(const Template &temp, const json &data, const std::string &filename_out) { - std::ofstream file(output_path + filename_out); - file << render(temp, data); - file.close(); - } - - void write_with_json_file(const std::string &filename, const std::string &filename_data, - const std::string &filename_out) { - const json data = load_json(filename_data); - write(filename, data, filename_out); - } - - void write_with_json_file(const Template &temp, const std::string &filename_data, const std::string &filename_out) { - const json data = load_json(filename_data); - write(temp, data, filename_out); - } - - std::ostream &render_to(std::ostream &os, const Template &tmpl, const json &data) { - Renderer(render_config, template_storage, function_storage).render_to(os, tmpl, data); - return os; - } - - std::string load_file(const std::string &filename) { - Parser parser(parser_config, lexer_config, template_storage, function_storage); - return parser.load_file(input_path + filename); - } - - json load_json(const std::string &filename) { - std::ifstream file; - open_file_or_throw(input_path + filename, file); - json j; - file >> j; - return j; - } - - /*! - @brief Adds a variadic callback - */ - void add_callback(const std::string &name, const CallbackFunction &callback) { - add_callback(name, -1, callback); - } - - /*! - @brief Adds a variadic void callback - */ - void add_void_callback(const std::string &name, const VoidCallbackFunction &callback) { - add_void_callback(name, -1, callback); - } - - /*! - @brief Adds a callback with given number or arguments - */ - void add_callback(const std::string &name, int num_args, const CallbackFunction &callback) { - function_storage.add_callback(name, num_args, callback); - } - - /*! - @brief Adds a void callback with given number or arguments - */ - void add_void_callback(const std::string &name, int num_args, const VoidCallbackFunction &callback) { - function_storage.add_callback(name, num_args, [callback](Arguments& args) { callback(args); return json(); }); - } - - /** Includes a template with a given name into the environment. - * Then, a template can be rendered in another template using the - * include "" syntax. - */ - void include_template(const std::string &name, const Template &tmpl) { - template_storage[name] = tmpl; - } -}; - -/*! -@brief render with default settings to a string -*/ -inline std::string render(nonstd::string_view input, const json &data) { - return Environment().render(input, data); -} - -/*! -@brief render with default settings to the given output stream -*/ -inline void render_to(std::ostream &os, nonstd::string_view input, const json &data) { - Environment env; - env.render_to(os, env.parse(input), data); -} - -} // namespace inja - -#endif // INCLUDE_INJA_ENVIRONMENT_HPP_ diff --git a/isis/src/base/apps/topds4/exceptions.h b/isis/src/base/apps/topds4/exceptions.h deleted file mode 100644 index 2784da8188..0000000000 --- a/isis/src/base/apps/topds4/exceptions.h +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) 2020 Pantor. All rights reserved. - -#ifndef INCLUDE_INJA_EXCEPTIONS_HPP_ -#define INCLUDE_INJA_EXCEPTIONS_HPP_ - -#include -#include - -namespace inja { - -struct SourceLocation { - size_t line; - size_t column; -}; - -struct InjaError : public std::runtime_error { - const std::string type; - const std::string message; - - const SourceLocation location; - - explicit InjaError(const std::string &type, const std::string &message) - : std::runtime_error("[inja.exception." + type + "] " + message), type(type), message(message), location({0, 0}) {} - - explicit InjaError(const std::string &type, const std::string &message, SourceLocation location) - : std::runtime_error("[inja.exception." + type + "] (at " + std::to_string(location.line) + ":" + - std::to_string(location.column) + ") " + message), - type(type), message(message), location(location) {} -}; - -struct ParserError : public InjaError { - explicit ParserError(const std::string &message, SourceLocation location) : InjaError("parser_error", message, location) {} -}; - -struct RenderError : public InjaError { - explicit RenderError(const std::string &message, SourceLocation location) : InjaError("render_error", message, location) {} -}; - -struct FileError : public InjaError { - explicit FileError(const std::string &message) : InjaError("file_error", message) {} - explicit FileError(const std::string &message, SourceLocation location) : InjaError("file_error", message, location) {} -}; - -struct JsonError : public InjaError { - explicit JsonError(const std::string &message, SourceLocation location) : InjaError("json_error", message, location) {} -}; - -} // namespace inja - -#endif // INCLUDE_INJA_EXCEPTIONS_HPP_ diff --git a/isis/src/base/apps/topds4/function_storage.h b/isis/src/base/apps/topds4/function_storage.h deleted file mode 100644 index 3aec3c7014..0000000000 --- a/isis/src/base/apps/topds4/function_storage.h +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright (c) 2020 Pantor. All rights reserved. - -#ifndef INCLUDE_INJA_FUNCTION_STORAGE_HPP_ -#define INCLUDE_INJA_FUNCTION_STORAGE_HPP_ - -#include - -#include "string_view.h" - -namespace inja { - -using json = nlohmann::json; - -using Arguments = std::vector; -using CallbackFunction = std::function; -using VoidCallbackFunction = std::function; - -/*! - * \brief Class for builtin functions and user-defined callbacks. - */ -class FunctionStorage { -public: - enum class Operation { - Not, - And, - Or, - In, - Equal, - NotEqual, - Greater, - GreaterEqual, - Less, - LessEqual, - Add, - Subtract, - Multiplication, - Division, - Power, - Modulo, - AtId, - At, - Default, - DivisibleBy, - Even, - Exists, - ExistsInObject, - First, - Float, - Int, - IsArray, - IsBoolean, - IsFloat, - IsInteger, - IsNumber, - IsObject, - IsString, - Last, - Length, - Lower, - Max, - Min, - Odd, - Range, - Round, - Sort, - Upper, - Callback, - ParenLeft, - ParenRight, - None, - }; - - struct FunctionData { - explicit FunctionData(const Operation &op, const CallbackFunction &cb = CallbackFunction{}) : operation(op), callback(cb) {} - const Operation operation; - const CallbackFunction callback; - }; - -private: - const int VARIADIC {-1}; - - std::map, FunctionData> function_storage = { - {std::make_pair("at", 2), FunctionData { Operation::At }}, - {std::make_pair("default", 2), FunctionData { Operation::Default }}, - {std::make_pair("divisibleBy", 2), FunctionData { Operation::DivisibleBy }}, - {std::make_pair("even", 1), FunctionData { Operation::Even }}, - {std::make_pair("exists", 1), FunctionData { Operation::Exists }}, - {std::make_pair("existsIn", 2), FunctionData { Operation::ExistsInObject }}, - {std::make_pair("first", 1), FunctionData { Operation::First }}, - {std::make_pair("float", 1), FunctionData { Operation::Float }}, - {std::make_pair("int", 1), FunctionData { Operation::Int }}, - {std::make_pair("isArray", 1), FunctionData { Operation::IsArray }}, - {std::make_pair("isBoolean", 1), FunctionData { Operation::IsBoolean }}, - {std::make_pair("isFloat", 1), FunctionData { Operation::IsFloat }}, - {std::make_pair("isInteger", 1), FunctionData { Operation::IsInteger }}, - {std::make_pair("isNumber", 1), FunctionData { Operation::IsNumber }}, - {std::make_pair("isObject", 1), FunctionData { Operation::IsObject }}, - {std::make_pair("isString", 1), FunctionData { Operation::IsString }}, - {std::make_pair("last", 1), FunctionData { Operation::Last }}, - {std::make_pair("length", 1), FunctionData { Operation::Length }}, - {std::make_pair("lower", 1), FunctionData { Operation::Lower }}, - {std::make_pair("max", 1), FunctionData { Operation::Max }}, - {std::make_pair("min", 1), FunctionData { Operation::Min }}, - {std::make_pair("odd", 1), FunctionData { Operation::Odd }}, - {std::make_pair("range", 1), FunctionData { Operation::Range }}, - {std::make_pair("round", 2), FunctionData { Operation::Round }}, - {std::make_pair("sort", 1), FunctionData { Operation::Sort }}, - {std::make_pair("upper", 1), FunctionData { Operation::Upper }}, - }; - -public: - void add_builtin(nonstd::string_view name, int num_args, Operation op) { - function_storage.emplace(std::make_pair(static_cast(name), num_args), FunctionData { op }); - } - - void add_callback(nonstd::string_view name, int num_args, const CallbackFunction &callback) { - function_storage.emplace(std::make_pair(static_cast(name), num_args), FunctionData { Operation::Callback, callback }); - } - - FunctionData find_function(nonstd::string_view name, int num_args) const { - auto it = function_storage.find(std::make_pair(static_cast(name), num_args)); - if (it != function_storage.end()) { - return it->second; - - // Find variadic function - } else if (num_args > 0) { - it = function_storage.find(std::make_pair(static_cast(name), VARIADIC)); - if (it != function_storage.end()) { - return it->second; - } - } - - return FunctionData { Operation::None }; - } -}; - -} // namespace inja - -#endif // INCLUDE_INJA_FUNCTION_STORAGE_HPP_ diff --git a/isis/src/base/apps/topds4/inja.h b/isis/src/base/apps/topds4/inja.h deleted file mode 100644 index bb0d6e8f33..0000000000 --- a/isis/src/base/apps/topds4/inja.h +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) 2020 Pantor. All rights reserved. - -#ifndef INCLUDE_INJA_INJA_HPP_ -#define INCLUDE_INJA_INJA_HPP_ - -#include - -#include "environment2.h" -#include "exceptions.h" -#include "parser.h" -#include "renderer.h" -#include "string_view.h" -#include "template2.h" - -#endif // INCLUDE_INJA_INJA_HPP_ diff --git a/isis/src/base/apps/topds4/lexer.h b/isis/src/base/apps/topds4/lexer.h deleted file mode 100644 index 3fbaa96218..0000000000 --- a/isis/src/base/apps/topds4/lexer.h +++ /dev/null @@ -1,418 +0,0 @@ -// Copyright (c) 2020 Pantor. All rights reserved. - -#ifndef INCLUDE_INJA_LEXER_HPP_ -#define INCLUDE_INJA_LEXER_HPP_ - -#include -#include - -#include "config.h" -#include "token.h" -#include "utils.h" - -namespace inja { - -/*! - * \brief Class for lexing an inja Template. - */ -class Lexer { - enum class State { - Text, - ExpressionStart, - ExpressionStartForceLstrip, - ExpressionBody, - LineStart, - LineBody, - StatementStart, - StatementStartNoLstrip, - StatementStartForceLstrip, - StatementBody, - CommentStart, - CommentBody, - }; - - enum class MinusState { - Operator, - Number, - }; - - const LexerConfig &config; - - State state; - MinusState minus_state; - nonstd::string_view m_in; - size_t tok_start; - size_t pos; - - - Token scan_body(nonstd::string_view close, Token::Kind closeKind, nonstd::string_view close_trim = nonstd::string_view(), bool trim = false) { - again: - // skip whitespace (except for \n as it might be a close) - if (tok_start >= m_in.size()) { - return make_token(Token::Kind::Eof); - } - char ch = m_in[tok_start]; - if (ch == ' ' || ch == '\t' || ch == '\r') { - tok_start += 1; - goto again; - } - - // check for close - if (!close_trim.empty() && inja::string_view::starts_with(m_in.substr(tok_start), close_trim)) { - state = State::Text; - pos = tok_start + close_trim.size(); - Token tok = make_token(closeKind); - skip_whitespaces_and_newlines(); - return tok; - } - - if (inja::string_view::starts_with(m_in.substr(tok_start), close)) { - state = State::Text; - pos = tok_start + close.size(); - Token tok = make_token(closeKind); - if (trim) { - skip_whitespaces_and_first_newline(); - } - return tok; - } - - // skip \n - if (ch == '\n') { - tok_start += 1; - goto again; - } - - pos = tok_start + 1; - if (std::isalpha(ch)) { - minus_state = MinusState::Operator; - return scan_id(); - } - - MinusState current_minus_state = minus_state; - if (minus_state == MinusState::Operator) { - minus_state = MinusState::Number; - } - - switch (ch) { - case '+': - return make_token(Token::Kind::Plus); - case '-': - if (current_minus_state == MinusState::Operator) { - return make_token(Token::Kind::Minus); - } - return scan_number(); - case '*': - return make_token(Token::Kind::Times); - case '/': - return make_token(Token::Kind::Slash); - case '^': - return make_token(Token::Kind::Power); - case '%': - return make_token(Token::Kind::Percent); - case '.': - return make_token(Token::Kind::Dot); - case ',': - return make_token(Token::Kind::Comma); - case ':': - return make_token(Token::Kind::Colon); - case '(': - return make_token(Token::Kind::LeftParen); - case ')': - minus_state = MinusState::Operator; - return make_token(Token::Kind::RightParen); - case '[': - return make_token(Token::Kind::LeftBracket); - case ']': - minus_state = MinusState::Operator; - return make_token(Token::Kind::RightBracket); - case '{': - return make_token(Token::Kind::LeftBrace); - case '}': - minus_state = MinusState::Operator; - return make_token(Token::Kind::RightBrace); - case '>': - if (pos < m_in.size() && m_in[pos] == '=') { - pos += 1; - return make_token(Token::Kind::GreaterEqual); - } - return make_token(Token::Kind::GreaterThan); - case '<': - if (pos < m_in.size() && m_in[pos] == '=') { - pos += 1; - return make_token(Token::Kind::LessEqual); - } - return make_token(Token::Kind::LessThan); - case '=': - if (pos < m_in.size() && m_in[pos] == '=') { - pos += 1; - return make_token(Token::Kind::Equal); - } - return make_token(Token::Kind::Unknown); - case '!': - if (pos < m_in.size() && m_in[pos] == '=') { - pos += 1; - return make_token(Token::Kind::NotEqual); - } - return make_token(Token::Kind::Unknown); - case '\"': - return scan_string(); - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - minus_state = MinusState::Operator; - return scan_number(); - case '_': - minus_state = MinusState::Operator; - return scan_id(); - default: - return make_token(Token::Kind::Unknown); - } - } - - Token scan_id() { - for (;;) { - if (pos >= m_in.size()) { - break; - } - char ch = m_in[pos]; - if (!std::isalnum(ch) && ch != '.' && ch != '/' && ch != '_' && ch != '-') { - break; - } - pos += 1; - } - return make_token(Token::Kind::Id); - } - - Token scan_number() { - for (;;) { - if (pos >= m_in.size()) { - break; - } - char ch = m_in[pos]; - // be very permissive in lexer (we'll catch errors when conversion happens) - if (!std::isdigit(ch) && ch != '.' && ch != 'e' && ch != 'E' && ch != '+' && ch != '-') { - break; - } - pos += 1; - } - return make_token(Token::Kind::Number); - } - - Token scan_string() { - bool escape {false}; - for (;;) { - if (pos >= m_in.size()) { - break; - } - char ch = m_in[pos++]; - if (ch == '\\') { - escape = true; - } else if (!escape && ch == m_in[tok_start]) { - break; - } else { - escape = false; - } - } - return make_token(Token::Kind::String); - } - - Token make_token(Token::Kind kind) const { return Token(kind, string_view::slice(m_in, tok_start, pos)); } - - void skip_whitespaces_and_newlines() { - if (pos < m_in.size()) { - while (pos < m_in.size() && (m_in[pos] == ' ' || m_in[pos] == '\t' || m_in[pos] == '\n' || m_in[pos] == '\r')) { - pos += 1; - } - } - } - - void skip_whitespaces_and_first_newline() { - if (pos < m_in.size()) { - while (pos < m_in.size() && (m_in[pos] == ' ' || m_in[pos] == '\t')) { - pos += 1; - } - } - - if (pos < m_in.size()) { - char ch = m_in[pos]; - if (ch == '\n') { - pos += 1; - } else if (ch == '\r') { - pos += 1; - if (pos < m_in.size() && m_in[pos] == '\n') { - pos += 1; - } - } - } - } - - static nonstd::string_view clear_final_line_if_whitespace(nonstd::string_view text) { - nonstd::string_view result = text; - while (!result.empty()) { - char ch = result.back(); - if (ch == ' ' || ch == '\t') { - result.remove_suffix(1); - } else if (ch == '\n' || ch == '\r') { - break; - } else { - return text; - } - } - return result; - } - -public: - explicit Lexer(const LexerConfig &config) : config(config), state(State::Text), minus_state(MinusState::Number) {} - - SourceLocation current_position() const { - return get_source_location(m_in, tok_start); - } - - void start(nonstd::string_view input) { - m_in = input; - tok_start = 0; - pos = 0; - state = State::Text; - minus_state = MinusState::Number; - - // Consume byte order mark (BOM) for UTF-8 - if (inja::string_view::starts_with(m_in, "\xEF\xBB\xBF")) { - m_in = m_in.substr(3); - } - } - - Token scan() { - tok_start = pos; - - again: - if (tok_start >= m_in.size()) { - return make_token(Token::Kind::Eof); - } - - switch (state) { - default: - case State::Text: { - // fast-scan to first open character - size_t open_start = m_in.substr(pos).find_first_of(config.open_chars); - if (open_start == nonstd::string_view::npos) { - // didn't find open, return remaining text as text token - pos = m_in.size(); - return make_token(Token::Kind::Text); - } - pos += open_start; - - // try to match one of the opening sequences, and get the close - nonstd::string_view open_str = m_in.substr(pos); - bool must_lstrip = false; - if (inja::string_view::starts_with(open_str, config.expression_open)) { - if (inja::string_view::starts_with(open_str, config.expression_open_force_lstrip)) { - state = State::ExpressionStartForceLstrip; - must_lstrip = true; - } else { - state = State::ExpressionStart; - } - } else if (inja::string_view::starts_with(open_str, config.statement_open)) { - if (inja::string_view::starts_with(open_str, config.statement_open_no_lstrip)) { - state = State::StatementStartNoLstrip; - } else if (inja::string_view::starts_with(open_str, config.statement_open_force_lstrip )) { - state = State::StatementStartForceLstrip; - must_lstrip = true; - } else { - state = State::StatementStart; - must_lstrip = config.lstrip_blocks; - } - } else if (inja::string_view::starts_with(open_str, config.comment_open)) { - state = State::CommentStart; - must_lstrip = config.lstrip_blocks; - } else if ((pos == 0 || m_in[pos - 1] == '\n') && inja::string_view::starts_with(open_str, config.line_statement)) { - state = State::LineStart; - } else { - pos += 1; // wasn't actually an opening sequence - goto again; - } - - nonstd::string_view text = string_view::slice(m_in, tok_start, pos); - if (must_lstrip) { - text = clear_final_line_if_whitespace(text); - } - - if (text.empty()) { - goto again; // don't generate empty token - } - return Token(Token::Kind::Text, text); - } - case State::ExpressionStart: { - state = State::ExpressionBody; - pos += config.expression_open.size(); - return make_token(Token::Kind::ExpressionOpen); - } - case State::ExpressionStartForceLstrip: { - state = State::ExpressionBody; - pos += config.expression_open_force_lstrip.size(); - return make_token(Token::Kind::ExpressionOpen); - } - case State::LineStart: { - state = State::LineBody; - pos += config.line_statement.size(); - return make_token(Token::Kind::LineStatementOpen); - } - case State::StatementStart: { - state = State::StatementBody; - pos += config.statement_open.size(); - return make_token(Token::Kind::StatementOpen); - } - case State::StatementStartNoLstrip: { - state = State::StatementBody; - pos += config.statement_open_no_lstrip.size(); - return make_token(Token::Kind::StatementOpen); - } - case State::StatementStartForceLstrip: { - state = State::StatementBody; - pos += config.statement_open_force_lstrip.size(); - return make_token(Token::Kind::StatementOpen); - } - case State::CommentStart: { - state = State::CommentBody; - pos += config.comment_open.size(); - return make_token(Token::Kind::CommentOpen); - } - case State::ExpressionBody: - return scan_body(config.expression_close, Token::Kind::ExpressionClose, config.expression_close_force_rstrip); - case State::LineBody: - return scan_body("\n", Token::Kind::LineStatementClose); - case State::StatementBody: - return scan_body(config.statement_close, Token::Kind::StatementClose, config.statement_close_force_rstrip, config.trim_blocks); - case State::CommentBody: { - // fast-scan to comment close - size_t end = m_in.substr(pos).find(config.comment_close); - if (end == nonstd::string_view::npos) { - pos = m_in.size(); - return make_token(Token::Kind::Eof); - } - // return the entire comment in the close token - state = State::Text; - pos += end + config.comment_close.size(); - Token tok = make_token(Token::Kind::CommentClose); - if (config.trim_blocks) { - skip_whitespaces_and_first_newline(); - } - return tok; - } - } - } - - const LexerConfig &get_config() const { - return config; - } -}; - -} // namespace inja - -#endif // INCLUDE_INJA_LEXER_HPP_ diff --git a/isis/src/base/apps/topds4/node.h b/isis/src/base/apps/topds4/node.h deleted file mode 100644 index d7aa441871..0000000000 --- a/isis/src/base/apps/topds4/node.h +++ /dev/null @@ -1,328 +0,0 @@ -// Copyright (c) 2020 Pantor. All rights reserved. - -#ifndef INCLUDE_INJA_NODE_HPP_ -#define INCLUDE_INJA_NODE_HPP_ - -#include -#include - -#include - -#include "function_storage.h" -#include "string_view.h" - - -namespace inja { - -class NodeVisitor; -class BlockNode; -class TextNode; -class ExpressionNode; -class LiteralNode; -class JsonNode; -class FunctionNode; -class ExpressionListNode; -class StatementNode; -class ForStatementNode; -class ForArrayStatementNode; -class ForObjectStatementNode; -class IfStatementNode; -class IncludeStatementNode; -class SetStatementNode; - - -class NodeVisitor { -public: - virtual void visit(const BlockNode& node) = 0; - virtual void visit(const TextNode& node) = 0; - virtual void visit(const ExpressionNode& node) = 0; - virtual void visit(const LiteralNode& node) = 0; - virtual void visit(const JsonNode& node) = 0; - virtual void visit(const FunctionNode& node) = 0; - virtual void visit(const ExpressionListNode& node) = 0; - virtual void visit(const StatementNode& node) = 0; - virtual void visit(const ForStatementNode& node) = 0; - virtual void visit(const ForArrayStatementNode& node) = 0; - virtual void visit(const ForObjectStatementNode& node) = 0; - virtual void visit(const IfStatementNode& node) = 0; - virtual void visit(const IncludeStatementNode& node) = 0; - virtual void visit(const SetStatementNode& node) = 0; -}; - -/*! - * \brief Base node class for the abstract syntax tree (AST). - */ -class AstNode { -public: - virtual void accept(NodeVisitor& v) const = 0; - - size_t pos; - - AstNode(size_t pos) : pos(pos) { } - virtual ~AstNode() { }; -}; - - -class BlockNode : public AstNode { -public: - std::vector> nodes; - - explicit BlockNode() : AstNode(0) {} - - void accept(NodeVisitor& v) const { - v.visit(*this); - } -}; - -class TextNode : public AstNode { -public: - const size_t length; - - explicit TextNode(size_t pos, size_t length): AstNode(pos), length(length) { } - - void accept(NodeVisitor& v) const { - v.visit(*this); - } -}; - -class ExpressionNode : public AstNode { -public: - explicit ExpressionNode(size_t pos) : AstNode(pos) {} - - void accept(NodeVisitor& v) const { - v.visit(*this); - } -}; - -class LiteralNode : public ExpressionNode { -public: - const nlohmann::json value; - - explicit LiteralNode(const nlohmann::json& value, size_t pos) : ExpressionNode(pos), value(value) { } - - void accept(NodeVisitor& v) const { - v.visit(*this); - } -}; - -class JsonNode : public ExpressionNode { -public: - const std::string name; - const json::json_pointer ptr; - - static std::string convert_dot_to_json_ptr(nonstd::string_view ptr_name) { - std::string result; - do { - nonstd::string_view part; - std::tie(part, ptr_name) = string_view::split(ptr_name, '.'); - result.push_back('/'); - result.append(part.begin(), part.end()); - } while (!ptr_name.empty()); - return result; - } - - explicit JsonNode(nonstd::string_view ptr_name, size_t pos) : ExpressionNode(pos), name(ptr_name), ptr(json::json_pointer(convert_dot_to_json_ptr(ptr_name))) { } - - void accept(NodeVisitor& v) const { - v.visit(*this); - } -}; - -class FunctionNode : public ExpressionNode { - using Op = FunctionStorage::Operation; - -public: - enum class Associativity { - Left, - Right, - }; - - unsigned int precedence; - Associativity associativity; - - Op operation; - - std::string name; - int number_args; // Should also be negative -> -1 for unknown number - CallbackFunction callback; - - explicit FunctionNode(nonstd::string_view name, size_t pos) : ExpressionNode(pos), precedence(8), associativity(Associativity::Left), operation(Op::Callback), name(name), number_args(1) { } - explicit FunctionNode(Op operation, size_t pos) : ExpressionNode(pos), operation(operation), number_args(1) { - switch (operation) { - case Op::Not: { - precedence = 4; - associativity = Associativity::Left; - } break; - case Op::And: { - precedence = 1; - associativity = Associativity::Left; - } break; - case Op::Or: { - precedence = 1; - associativity = Associativity::Left; - } break; - case Op::In: { - precedence = 2; - associativity = Associativity::Left; - } break; - case Op::Equal: { - precedence = 2; - associativity = Associativity::Left; - } break; - case Op::NotEqual: { - precedence = 2; - associativity = Associativity::Left; - } break; - case Op::Greater: { - precedence = 2; - associativity = Associativity::Left; - } break; - case Op::GreaterEqual: { - precedence = 2; - associativity = Associativity::Left; - } break; - case Op::Less: { - precedence = 2; - associativity = Associativity::Left; - } break; - case Op::LessEqual: { - precedence = 2; - associativity = Associativity::Left; - } break; - case Op::Add: { - precedence = 3; - associativity = Associativity::Left; - } break; - case Op::Subtract: { - precedence = 3; - associativity = Associativity::Left; - } break; - case Op::Multiplication: { - precedence = 4; - associativity = Associativity::Left; - } break; - case Op::Division: { - precedence = 4; - associativity = Associativity::Left; - } break; - case Op::Power: { - precedence = 5; - associativity = Associativity::Right; - } break; - case Op::Modulo: { - precedence = 4; - associativity = Associativity::Left; - } break; - case Op::AtId: { - precedence = 8; - associativity = Associativity::Left; - } break; - default: { - precedence = 1; - associativity = Associativity::Left; - } - } - } - - void accept(NodeVisitor& v) const { - v.visit(*this); - } -}; - -class ExpressionListNode : public AstNode { -public: - std::vector> rpn_output; - - explicit ExpressionListNode() : AstNode(0) { } - explicit ExpressionListNode(size_t pos) : AstNode(pos) { } - - void accept(NodeVisitor& v) const { - v.visit(*this); - } -}; - -class StatementNode : public AstNode { -public: - StatementNode(size_t pos) : AstNode(pos) { } - - virtual void accept(NodeVisitor& v) const = 0; -}; - -class ForStatementNode : public StatementNode { -public: - ExpressionListNode condition; - BlockNode body; - BlockNode *const parent; - - ForStatementNode(BlockNode *const parent, size_t pos) : StatementNode(pos), parent(parent) { } - - virtual void accept(NodeVisitor& v) const = 0; -}; - -class ForArrayStatementNode : public ForStatementNode { -public: - const std::string value; - - explicit ForArrayStatementNode(const std::string& value, BlockNode *const parent, size_t pos) : ForStatementNode(parent, pos), value(value) { } - - void accept(NodeVisitor& v) const { - v.visit(*this); - } -}; - -class ForObjectStatementNode : public ForStatementNode { -public: - const std::string key; - const std::string value; - - explicit ForObjectStatementNode(const std::string& key, const std::string& value, BlockNode *const parent, size_t pos) : ForStatementNode(parent, pos), key(key), value(value) { } - - void accept(NodeVisitor& v) const { - v.visit(*this); - } -}; - -class IfStatementNode : public StatementNode { -public: - ExpressionListNode condition; - BlockNode true_statement; - BlockNode false_statement; - BlockNode *const parent; - - const bool is_nested; - bool has_false_statement {false}; - - explicit IfStatementNode(BlockNode *const parent, size_t pos) : StatementNode(pos), parent(parent), is_nested(false) { } - explicit IfStatementNode(bool is_nested, BlockNode *const parent, size_t pos) : StatementNode(pos), parent(parent), is_nested(is_nested) { } - - void accept(NodeVisitor& v) const { - v.visit(*this); - } -}; - -class IncludeStatementNode : public StatementNode { -public: - const std::string file; - - explicit IncludeStatementNode(const std::string& file, size_t pos) : StatementNode(pos), file(file) { } - - void accept(NodeVisitor& v) const { - v.visit(*this); - }; -}; - -class SetStatementNode : public StatementNode { -public: - const std::string key; - ExpressionListNode expression; - - explicit SetStatementNode(const std::string& key, size_t pos) : StatementNode(pos), key(key) { } - - void accept(NodeVisitor& v) const { - v.visit(*this); - }; -}; - -} // namespace inja - -#endif // INCLUDE_INJA_NODE_HPP_ diff --git a/isis/src/base/apps/topds4/parser.h b/isis/src/base/apps/topds4/parser.h deleted file mode 100644 index c333668f2c..0000000000 --- a/isis/src/base/apps/topds4/parser.h +++ /dev/null @@ -1,582 +0,0 @@ -// Copyright (c) 2020 Pantor. All rights reserved. - -#ifndef INCLUDE_INJA_PARSER_HPP_ -#define INCLUDE_INJA_PARSER_HPP_ - -#include -#include -#include -#include -#include -#include - -#include "config.h" -#include "exceptions.h" -#include "function_storage.h" -#include "lexer.h" -#include "node.h" -#include "template2.h" -#include "token.h" -#include "utils.h" - -#include - -namespace inja { - -/*! - * \brief Class for parsing an inja Template. - */ -class Parser { - const ParserConfig &config; - - Lexer lexer; - TemplateStorage &template_storage; - const FunctionStorage &function_storage; - - Token tok, peek_tok; - bool have_peek_tok {false}; - - size_t current_paren_level {0}; - size_t current_bracket_level {0}; - size_t current_brace_level {0}; - - nonstd::string_view json_literal_start; - - BlockNode *current_block {nullptr}; - ExpressionListNode *current_expression_list {nullptr}; - std::stack> function_stack; - - std::stack> operator_stack; - std::stack if_statement_stack; - std::stack for_statement_stack; - - void throw_parser_error(const std::string &message) { - throw ParserError(message, lexer.current_position()); - } - - void get_next_token() { - if (have_peek_tok) { - tok = peek_tok; - have_peek_tok = false; - } else { - tok = lexer.scan(); - } - } - - void get_peek_token() { - if (!have_peek_tok) { - peek_tok = lexer.scan(); - have_peek_tok = true; - } - } - - void add_json_literal(const char* content_ptr) { - nonstd::string_view json_text(json_literal_start.data(), tok.text.data() - json_literal_start.data() + tok.text.size()); - current_expression_list->rpn_output.emplace_back(std::make_shared(json::parse(json_text), json_text.data() - content_ptr)); - } - - bool parse_expression(Template &tmpl, Token::Kind closing) { - while (tok.kind != closing && tok.kind != Token::Kind::Eof) { - // Literals - switch (tok.kind) { - case Token::Kind::String: { - if (current_brace_level == 0 && current_bracket_level == 0) { - json_literal_start = tok.text; - add_json_literal(tmpl.content.c_str()); - } - - } break; - case Token::Kind::Number: { - if (current_brace_level == 0 && current_bracket_level == 0) { - json_literal_start = tok.text; - add_json_literal(tmpl.content.c_str()); - } - - } break; - case Token::Kind::LeftBracket: { - if (current_brace_level == 0 && current_bracket_level == 0) { - json_literal_start = tok.text; - } - current_bracket_level += 1; - - } break; - case Token::Kind::LeftBrace: { - if (current_brace_level == 0 && current_bracket_level == 0) { - json_literal_start = tok.text; - } - current_brace_level += 1; - - } break; - case Token::Kind::RightBracket: { - if (current_bracket_level == 0) { - throw_parser_error("unexpected ']'"); - } - - current_bracket_level -= 1; - if (current_brace_level == 0 && current_bracket_level == 0) { - add_json_literal(tmpl.content.c_str()); - } - - } break; - case Token::Kind::RightBrace: { - if (current_brace_level == 0) { - throw_parser_error("unexpected '}'"); - } - - current_brace_level -= 1; - if (current_brace_level == 0 && current_bracket_level == 0) { - add_json_literal(tmpl.content.c_str()); - } - - } break; - case Token::Kind::Id: { - get_peek_token(); - - // Json Literal - if (tok.text == static_cast("true") || tok.text == static_cast("false") || tok.text == static_cast("null")) { - if (current_brace_level == 0 && current_bracket_level == 0) { - json_literal_start = tok.text; - add_json_literal(tmpl.content.c_str()); - } - - // Operator - } else if (tok.text == "and" || tok.text == "or" || tok.text == "in" || tok.text == "not") { - goto parse_operator; - - // Functions - } else if (peek_tok.kind == Token::Kind::LeftParen) { - operator_stack.emplace(std::make_shared(static_cast(tok.text), tok.text.data() - tmpl.content.c_str())); - function_stack.emplace(operator_stack.top().get(), current_paren_level); - - // Variables - } else { - current_expression_list->rpn_output.emplace_back(std::make_shared(static_cast(tok.text), tok.text.data() - tmpl.content.c_str())); - } - - // Operators - } break; - case Token::Kind::Equal: - case Token::Kind::NotEqual: - case Token::Kind::GreaterThan: - case Token::Kind::GreaterEqual: - case Token::Kind::LessThan: - case Token::Kind::LessEqual: - case Token::Kind::Plus: - case Token::Kind::Minus: - case Token::Kind::Times: - case Token::Kind::Slash: - case Token::Kind::Power: - case Token::Kind::Percent: - case Token::Kind::Dot: { - - parse_operator: - FunctionStorage::Operation operation; - switch (tok.kind) { - case Token::Kind::Id: { - if (tok.text == "and") { - operation = FunctionStorage::Operation::And; - } else if (tok.text == "or") { - operation = FunctionStorage::Operation::Or; - } else if (tok.text == "in") { - operation = FunctionStorage::Operation::In; - } else if (tok.text == "not") { - operation = FunctionStorage::Operation::Not; - } else { - throw_parser_error("unknown operator in parser."); - } - } break; - case Token::Kind::Equal: { - operation = FunctionStorage::Operation::Equal; - } break; - case Token::Kind::NotEqual: { - operation = FunctionStorage::Operation::NotEqual; - } break; - case Token::Kind::GreaterThan: { - operation = FunctionStorage::Operation::Greater; - } break; - case Token::Kind::GreaterEqual: { - operation = FunctionStorage::Operation::GreaterEqual; - } break; - case Token::Kind::LessThan: { - operation = FunctionStorage::Operation::Less; - } break; - case Token::Kind::LessEqual: { - operation = FunctionStorage::Operation::LessEqual; - } break; - case Token::Kind::Plus: { - operation = FunctionStorage::Operation::Add; - } break; - case Token::Kind::Minus: { - operation = FunctionStorage::Operation::Subtract; - } break; - case Token::Kind::Times: { - operation = FunctionStorage::Operation::Multiplication; - } break; - case Token::Kind::Slash: { - operation = FunctionStorage::Operation::Division; - } break; - case Token::Kind::Power: { - operation = FunctionStorage::Operation::Power; - } break; - case Token::Kind::Percent: { - operation = FunctionStorage::Operation::Modulo; - } break; - case Token::Kind::Dot: { - operation = FunctionStorage::Operation::AtId; - } break; - default: { - throw_parser_error("unknown operator in parser."); - } - } - auto function_node = std::make_shared(operation, tok.text.data() - tmpl.content.c_str()); - - while (!operator_stack.empty() && ((operator_stack.top()->precedence > function_node->precedence) || (operator_stack.top()->precedence == function_node->precedence && function_node->associativity == FunctionNode::Associativity::Left)) && (operator_stack.top()->operation != FunctionStorage::Operation::ParenLeft)) { - current_expression_list->rpn_output.emplace_back(operator_stack.top()); - operator_stack.pop(); - } - - operator_stack.emplace(function_node); - - } break; - case Token::Kind::Comma: { - if (current_brace_level == 0 && current_bracket_level == 0) { - if (function_stack.empty()) { - throw_parser_error("unexpected ','"); - } - - function_stack.top().first->number_args += 1; - } - - } break; - case Token::Kind::Colon: { - if (current_brace_level == 0 && current_bracket_level == 0) { - throw_parser_error("unexpected ':'"); - } - - } break; - case Token::Kind::LeftParen: { - current_paren_level += 1; - operator_stack.emplace(std::make_shared(FunctionStorage::Operation::ParenLeft, tok.text.data() - tmpl.content.c_str())); - - get_peek_token(); - if (peek_tok.kind == Token::Kind::RightParen) { - if (!function_stack.empty() && function_stack.top().second == current_paren_level - 1) { - function_stack.top().first->number_args = 0; - } - } - - } break; - case Token::Kind::RightParen: { - current_paren_level -= 1; - while (!operator_stack.empty() && operator_stack.top()->operation != FunctionStorage::Operation::ParenLeft) { - current_expression_list->rpn_output.emplace_back(operator_stack.top()); - operator_stack.pop(); - } - - if (!operator_stack.empty() && operator_stack.top()->operation == FunctionStorage::Operation::ParenLeft) { - operator_stack.pop(); - } - - if (!function_stack.empty() && function_stack.top().second == current_paren_level) { - auto func = function_stack.top().first; - auto function_data = function_storage.find_function(func->name, func->number_args); - if (function_data.operation == FunctionStorage::Operation::None) { - throw_parser_error("unknown function " + func->name); - } - func->operation = function_data.operation; - if (function_data.operation == FunctionStorage::Operation::Callback) { - func->callback = function_data.callback; - } - - if (operator_stack.empty()) { - throw_parser_error("internal error at function " + func->name); - } - - current_expression_list->rpn_output.emplace_back(operator_stack.top()); - operator_stack.pop(); - function_stack.pop(); - } - } - default: - break; - } - - get_next_token(); - } - - while (!operator_stack.empty()) { - current_expression_list->rpn_output.emplace_back(operator_stack.top()); - operator_stack.pop(); - } - - return true; - } - - bool parse_statement(Template &tmpl, Token::Kind closing, nonstd::string_view path) { - if (tok.kind != Token::Kind::Id) { - return false; - } - - if (tok.text == static_cast("if")) { - get_next_token(); - - auto if_statement_node = std::make_shared(current_block, tok.text.data() - tmpl.content.c_str()); - current_block->nodes.emplace_back(if_statement_node); - if_statement_stack.emplace(if_statement_node.get()); - current_block = &if_statement_node->true_statement; - current_expression_list = &if_statement_node->condition; - - if (!parse_expression(tmpl, closing)) { - return false; - } - - } else if (tok.text == static_cast("else")) { - if (if_statement_stack.empty()) { - throw_parser_error("else without matching if"); - } - auto &if_statement_data = if_statement_stack.top(); - get_next_token(); - - if_statement_data->has_false_statement = true; - current_block = &if_statement_data->false_statement; - - // Chained else if - if (tok.kind == Token::Kind::Id && tok.text == static_cast("if")) { - get_next_token(); - - auto if_statement_node = std::make_shared(true, current_block, tok.text.data() - tmpl.content.c_str()); - current_block->nodes.emplace_back(if_statement_node); - if_statement_stack.emplace(if_statement_node.get()); - current_block = &if_statement_node->true_statement; - current_expression_list = &if_statement_node->condition; - - if (!parse_expression(tmpl, closing)) { - return false; - } - } - - } else if (tok.text == static_cast("endif")) { - if (if_statement_stack.empty()) { - throw_parser_error("endif without matching if"); - } - - // Nested if statements - while (if_statement_stack.top()->is_nested) { - if_statement_stack.pop(); - } - - auto &if_statement_data = if_statement_stack.top(); - get_next_token(); - - current_block = if_statement_data->parent; - if_statement_stack.pop(); - - } else if (tok.text == static_cast("for")) { - get_next_token(); - - // options: for a in arr; for a, b in obj - if (tok.kind != Token::Kind::Id) { - throw_parser_error("expected id, got '" + tok.describe() + "'"); - } - - Token value_token = tok; - get_next_token(); - - // Object type - std::shared_ptr for_statement_node; - if (tok.kind == Token::Kind::Comma) { - get_next_token(); - if (tok.kind != Token::Kind::Id) { - throw_parser_error("expected id, got '" + tok.describe() + "'"); - } - - Token key_token = std::move(value_token); - value_token = tok; - get_next_token(); - - for_statement_node = std::make_shared(static_cast(key_token.text), static_cast(value_token.text), current_block, tok.text.data() - tmpl.content.c_str()); - - // Array type - } else { - for_statement_node = std::make_shared(static_cast(value_token.text), current_block, tok.text.data() - tmpl.content.c_str()); - } - - current_block->nodes.emplace_back(for_statement_node); - for_statement_stack.emplace(for_statement_node.get()); - current_block = &for_statement_node->body; - current_expression_list = &for_statement_node->condition; - - if (tok.kind != Token::Kind::Id || tok.text != static_cast("in")) { - throw_parser_error("expected 'in', got '" + tok.describe() + "'"); - } - get_next_token(); - - if (!parse_expression(tmpl, closing)) { - return false; - } - - } else if (tok.text == static_cast("endfor")) { - if (for_statement_stack.empty()) { - throw_parser_error("endfor without matching for"); - } - - auto &for_statement_data = for_statement_stack.top(); - get_next_token(); - - current_block = for_statement_data->parent; - for_statement_stack.pop(); - - } else if (tok.text == static_cast("include")) { - get_next_token(); - - if (tok.kind != Token::Kind::String) { - throw_parser_error("expected string, got '" + tok.describe() + "'"); - } - - // Build the relative path - json json_name = json::parse(tok.text); - std::string pathname = static_cast(path); - pathname += json_name.get_ref(); - if (pathname.compare(0, 2, "./") == 0) { - pathname.erase(0, 2); - } - // sys::path::remove_dots(pathname, true, sys::path::Style::posix); - - if (config.search_included_templates_in_files && template_storage.find(pathname) == template_storage.end()) { - auto include_template = Template(load_file(pathname)); - template_storage.emplace(pathname, include_template); - parse_into_template(template_storage[pathname], pathname); - } - - current_block->nodes.emplace_back(std::make_shared(pathname, tok.text.data() - tmpl.content.c_str())); - - get_next_token(); - - } else if (tok.text == static_cast("set")) { - get_next_token(); - - if (tok.kind != Token::Kind::Id) { - throw_parser_error("expected variable name, got '" + tok.describe() + "'"); - } - - std::string key = static_cast(tok.text); - get_next_token(); - - auto set_statement_node = std::make_shared(key, tok.text.data() - tmpl.content.c_str()); - current_block->nodes.emplace_back(set_statement_node); - current_expression_list = &set_statement_node->expression; - - if (tok.text != static_cast("=")) { - throw_parser_error("expected '=', got '" + tok.describe() + "'"); - } - get_next_token(); - - if (!parse_expression(tmpl, closing)) { - return false; - } - - } else { - return false; - } - return true; - } - - void parse_into(Template &tmpl, nonstd::string_view path) { - lexer.start(tmpl.content); - current_block = &tmpl.root; - - for (;;) { - get_next_token(); - switch (tok.kind) { - case Token::Kind::Eof: { - if (!if_statement_stack.empty()) { - throw_parser_error("unmatched if"); - } - if (!for_statement_stack.empty()) { - throw_parser_error("unmatched for"); - } - } return; - case Token::Kind::Text: { - current_block->nodes.emplace_back(std::make_shared(tok.text.data() - tmpl.content.c_str(), tok.text.size())); - } break; - case Token::Kind::StatementOpen: { - get_next_token(); - if (!parse_statement(tmpl, Token::Kind::StatementClose, path)) { - throw_parser_error("expected statement, got '" + tok.describe() + "'"); - } - if (tok.kind != Token::Kind::StatementClose) { - throw_parser_error("expected statement close, got '" + tok.describe() + "'"); - } - } break; - case Token::Kind::LineStatementOpen: { - get_next_token(); - if (!parse_statement(tmpl, Token::Kind::LineStatementClose, path)) { - throw_parser_error("expected statement, got '" + tok.describe() + "'"); - } - if (tok.kind != Token::Kind::LineStatementClose && tok.kind != Token::Kind::Eof) { - throw_parser_error("expected line statement close, got '" + tok.describe() + "'"); - } - } break; - case Token::Kind::ExpressionOpen: { - get_next_token(); - - auto expression_list_node = std::make_shared(tok.text.data() - tmpl.content.c_str()); - current_block->nodes.emplace_back(expression_list_node); - current_expression_list = expression_list_node.get(); - - if (!parse_expression(tmpl, Token::Kind::ExpressionClose)) { - throw_parser_error("expected expression, got '" + tok.describe() + "'"); - } - - if (tok.kind != Token::Kind::ExpressionClose) { - throw_parser_error("expected expression close, got '" + tok.describe() + "'"); - } - } break; - case Token::Kind::CommentOpen: { - get_next_token(); - if (tok.kind != Token::Kind::CommentClose) { - throw_parser_error("expected comment close, got '" + tok.describe() + "'"); - } - } break; - default: { - throw_parser_error("unexpected token '" + tok.describe() + "'"); - } break; - } - } - } - - -public: - explicit Parser(const ParserConfig &parser_config, const LexerConfig &lexer_config, - TemplateStorage &template_storage, const FunctionStorage &function_storage) - : config(parser_config), lexer(lexer_config), template_storage(template_storage), function_storage(function_storage) { } - - Template parse(nonstd::string_view input, nonstd::string_view path) { - auto result = Template(static_cast(input)); - parse_into(result, path); - return result; - } - - Template parse(nonstd::string_view input) { - return parse(input, "./"); - } - - void parse_into_template(Template& tmpl, nonstd::string_view filename) { - nonstd::string_view path = filename.substr(0, filename.find_last_of("/\\") + 1); - - // StringRef path = sys::path::parent_path(filename); - auto sub_parser = Parser(config, lexer.get_config(), template_storage, function_storage); - sub_parser.parse_into(tmpl, path); - } - - std::string load_file(nonstd::string_view filename) { - std::ifstream file; - open_file_or_throw(static_cast(filename), file); - std::string text((std::istreambuf_iterator(file)), std::istreambuf_iterator()); - return text; - } -}; - -} // namespace inja - -#endif // INCLUDE_INJA_PARSER_HPP_ diff --git a/isis/src/base/apps/topds4/renderer.h b/isis/src/base/apps/topds4/renderer.h deleted file mode 100644 index 076bb780af..0000000000 --- a/isis/src/base/apps/topds4/renderer.h +++ /dev/null @@ -1,607 +0,0 @@ -// Copyright (c) 2020 Pantor. All rights reserved. - -#ifndef INCLUDE_INJA_RENDERER_HPP_ -#define INCLUDE_INJA_RENDERER_HPP_ - -#include -#include -#include -#include -#include - -#include - -#include "config.h" -#include "exceptions.h" -#include "node.h" -#include "template2.h" -#include "utils.h" - -namespace inja { - -/*! - * \brief Class for rendering a Template with data. - */ -class Renderer : public NodeVisitor { - using Op = FunctionStorage::Operation; - - const RenderConfig config; - const Template *current_template; - const TemplateStorage &template_storage; - const FunctionStorage &function_storage; - - const json *json_input; - std::ostream *output_stream; - - json json_additional_data; - json* current_loop_data = &json_additional_data["loop"]; - - std::vector> json_tmp_stack; - std::stack json_eval_stack; - std::stack not_found_stack; - - bool truthy(const json* data) const { - if (data->is_boolean()) { - return data->get(); - } else if (data->is_number()) { - return (*data != 0); - } else if (data->is_null()) { - return false; - } - return !data->empty(); - } - - void print_json(const std::shared_ptr value) { - if (value->is_string()) { - *output_stream << value->get_ref(); - } else if (value->is_number_integer()) { - *output_stream << value->get(); - } else if (value->is_null()) { - } else { - *output_stream << value->dump(); - } - } - - const std::shared_ptr eval_expression_list(const ExpressionListNode& expression_list) { - for (auto& expression : expression_list.rpn_output) { - expression->accept(*this); - } - - if (json_eval_stack.empty()) { - throw_renderer_error("empty expression", expression_list); - } else if (json_eval_stack.size() != 1) { - throw_renderer_error("malformed expression", expression_list); - } - - auto result = json_eval_stack.top(); - json_eval_stack.pop(); - - if (!result) { - if (not_found_stack.empty()) { - throw_renderer_error("expression could not be evaluated", expression_list); - } - - auto node = not_found_stack.top(); - not_found_stack.pop(); - - throw_renderer_error("variable '" + static_cast(node->name) + "' not found", *node); - } - return std::make_shared(*result); - } - - void throw_renderer_error(const std::string &message, const AstNode& node) { - SourceLocation loc = get_source_location(current_template->content, node.pos); - throw RenderError(message, loc); - } - - template - std::array get_arguments(const AstNode& node) { - if (json_eval_stack.size() < N) { - throw_renderer_error("function needs " + std::to_string(N) + " variables, but has only found " + std::to_string(json_eval_stack.size()), node); - } - - std::array result; - for (size_t i = 0; i < N; i += 1) { - result[N - i - 1] = json_eval_stack.top(); - json_eval_stack.pop(); - - if (!result[N - i - 1]) { - auto json_node = not_found_stack.top(); - not_found_stack.pop(); - - if (throw_not_found) { - throw_renderer_error("variable '" + static_cast(json_node->name) + "' not found", *json_node); - } - } - } - return result; - } - - template - Arguments get_argument_vector(size_t N, const AstNode& node) { - Arguments result {N}; - for (size_t i = 0; i < N; i += 1) { - result[N - i - 1] = json_eval_stack.top(); - json_eval_stack.pop(); - - if (!result[N - i - 1]) { - auto json_node = not_found_stack.top(); - not_found_stack.pop(); - - if (throw_not_found) { - throw_renderer_error("variable '" + static_cast(json_node->name) + "' not found", *json_node); - } - } - } - return result; - } - - void visit(const BlockNode& node) { - for (auto& n : node.nodes) { - n->accept(*this); - } - } - - void visit(const TextNode& node) { - output_stream->write(current_template->content.c_str() + node.pos, node.length); - } - - void visit(const ExpressionNode&) { } - - void visit(const LiteralNode& node) { - json_eval_stack.push(&node.value); - } - - void visit(const JsonNode& node) { - if (json_additional_data.contains(node.ptr)) { - json_eval_stack.push(&(json_additional_data[node.ptr])); - - } else if (json_input->contains(node.ptr)) { - json_eval_stack.push(&(*json_input)[node.ptr]); - - } else { - // Try to evaluate as a no-argument callback - auto function_data = function_storage.find_function(node.name, 0); - if (function_data.operation == FunctionStorage::Operation::Callback) { - Arguments empty_args {}; - auto value = std::make_shared(function_data.callback(empty_args)); - json_tmp_stack.push_back(value); - json_eval_stack.push(value.get()); - - } else { - json_eval_stack.push(nullptr); - not_found_stack.emplace(&node); - } - } - } - - void visit(const FunctionNode& node) { - std::shared_ptr result_ptr; - - switch (node.operation) { - case Op::Not: { - auto args = get_arguments<1>(node); - result_ptr = std::make_shared(!truthy(args[0])); - json_tmp_stack.push_back(result_ptr); - json_eval_stack.push(result_ptr.get()); - } break; - case Op::And: { - auto args = get_arguments<2>(node); - result_ptr = std::make_shared(truthy(args[0]) && truthy(args[1])); - json_tmp_stack.push_back(result_ptr); - json_eval_stack.push(result_ptr.get()); - } break; - case Op::Or: { - auto args = get_arguments<2>(node); - result_ptr = std::make_shared(truthy(args[0]) || truthy(args[1])); - json_tmp_stack.push_back(result_ptr); - json_eval_stack.push(result_ptr.get()); - } break; - case Op::In: { - auto args = get_arguments<2>(node); - result_ptr = std::make_shared(std::find(args[1]->begin(), args[1]->end(), *args[0]) != args[1]->end()); - json_tmp_stack.push_back(result_ptr); - json_eval_stack.push(result_ptr.get()); - } break; - case Op::Equal: { - auto args = get_arguments<2>(node); - result_ptr = std::make_shared(*args[0] == *args[1]); - json_tmp_stack.push_back(result_ptr); - json_eval_stack.push(result_ptr.get()); - } break; - case Op::NotEqual: { - auto args = get_arguments<2>(node); - result_ptr = std::make_shared(*args[0] != *args[1]); - json_tmp_stack.push_back(result_ptr); - json_eval_stack.push(result_ptr.get()); - } break; - case Op::Greater: { - auto args = get_arguments<2>(node); - result_ptr = std::make_shared(*args[0] > *args[1]); - json_tmp_stack.push_back(result_ptr); - json_eval_stack.push(result_ptr.get()); - } break; - case Op::GreaterEqual: { - auto args = get_arguments<2>(node); - result_ptr = std::make_shared(*args[0] >= *args[1]); - json_tmp_stack.push_back(result_ptr); - json_eval_stack.push(result_ptr.get()); - } break; - case Op::Less: { - auto args = get_arguments<2>(node); - result_ptr = std::make_shared(*args[0] < *args[1]); - json_tmp_stack.push_back(result_ptr); - json_eval_stack.push(result_ptr.get()); - } break; - case Op::LessEqual: { - auto args = get_arguments<2>(node); - result_ptr = std::make_shared(*args[0] <= *args[1]); - json_tmp_stack.push_back(result_ptr); - json_eval_stack.push(result_ptr.get()); - } break; - case Op::Add: { - auto args = get_arguments<2>(node); - if (args[0]->is_string() && args[1]->is_string()) { - result_ptr = std::make_shared(args[0]->get_ref() + args[1]->get_ref()); - json_tmp_stack.push_back(result_ptr); - } else if (args[0]->is_number_integer() && args[1]->is_number_integer()) { - result_ptr = std::make_shared(args[0]->get() + args[1]->get()); - json_tmp_stack.push_back(result_ptr); - } else { - result_ptr = std::make_shared(args[0]->get() + args[1]->get()); - json_tmp_stack.push_back(result_ptr); - } - json_eval_stack.push(result_ptr.get()); - } break; - case Op::Subtract: { - auto args = get_arguments<2>(node); - if (args[0]->is_number_integer() && args[1]->is_number_integer()) { - result_ptr = std::make_shared(args[0]->get() - args[1]->get()); - json_tmp_stack.push_back(result_ptr); - } else { - result_ptr = std::make_shared(args[0]->get() - args[1]->get()); - json_tmp_stack.push_back(result_ptr); - } - json_eval_stack.push(result_ptr.get()); - } break; - case Op::Multiplication: { - auto args = get_arguments<2>(node); - if (args[0]->is_number_integer() && args[1]->is_number_integer()) { - result_ptr = std::make_shared(args[0]->get() * args[1]->get()); - json_tmp_stack.push_back(result_ptr); - } else { - result_ptr = std::make_shared(args[0]->get() * args[1]->get()); - json_tmp_stack.push_back(result_ptr); - } - json_eval_stack.push(result_ptr.get()); - } break; - case Op::Division: { - auto args = get_arguments<2>(node); - if (args[1]->get() == 0) { - throw_renderer_error("division by zero", node); - } - result_ptr = std::make_shared(args[0]->get() / args[1]->get()); - json_tmp_stack.push_back(result_ptr); - json_eval_stack.push(result_ptr.get()); - } break; - case Op::Power: { - auto args = get_arguments<2>(node); - if (args[0]->is_number_integer() && args[1]->get() >= 0) { - int result = std::pow(args[0]->get(), args[1]->get()); - result_ptr = std::make_shared(std::move(result)); - json_tmp_stack.push_back(result_ptr); - } else { - double result = std::pow(args[0]->get(), args[1]->get()); - result_ptr = std::make_shared(std::move(result)); - json_tmp_stack.push_back(result_ptr); - } - json_eval_stack.push(result_ptr.get()); - } break; - case Op::Modulo: { - auto args = get_arguments<2>(node); - result_ptr = std::make_shared(args[0]->get() % args[1]->get()); - json_tmp_stack.push_back(result_ptr); - json_eval_stack.push(result_ptr.get()); - } break; - case Op::AtId: { - json_eval_stack.pop(); // Pop id nullptr - auto container = get_arguments<1, false>(node)[0]; - if (not_found_stack.empty()) { - throw_renderer_error("could not find element with given name", node); - } - auto id_node = not_found_stack.top(); - not_found_stack.pop(); - json_eval_stack.push(&container->at(id_node->name)); - } break; - case Op::At: { - auto args = get_arguments<2>(node); - json_eval_stack.push(&args[0]->at(args[1]->get())); - } break; - case Op::Default: { - auto default_arg = get_arguments<1>(node)[0]; - auto test_arg = get_arguments<1, false>(node)[0]; - json_eval_stack.push(test_arg ? test_arg : default_arg); - } break; - case Op::DivisibleBy: { - auto args = get_arguments<2>(node); - int divisor = args[1]->get(); - result_ptr = std::make_shared((divisor != 0) && (args[0]->get() % divisor == 0)); - json_tmp_stack.push_back(result_ptr); - json_eval_stack.push(result_ptr.get()); - } break; - case Op::Even: { - result_ptr = std::make_shared(get_arguments<1>(node)[0]->get() % 2 == 0); - json_tmp_stack.push_back(result_ptr); - json_eval_stack.push(result_ptr.get()); - } break; - case Op::Exists: { - auto &&name = get_arguments<1>(node)[0]->get_ref(); - result_ptr = std::make_shared(json_input->contains(json::json_pointer(JsonNode::convert_dot_to_json_ptr(name)))); - json_tmp_stack.push_back(result_ptr); - json_eval_stack.push(result_ptr.get()); - } break; - case Op::ExistsInObject: { - auto args = get_arguments<2>(node); - auto &&name = args[1]->get_ref(); - result_ptr = std::make_shared(args[0]->find(name) != args[0]->end()); - json_tmp_stack.push_back(result_ptr); - json_eval_stack.push(result_ptr.get()); - } break; - case Op::First: { - auto result = &get_arguments<1>(node)[0]->front(); - json_eval_stack.push(result); - } break; - case Op::Float: { - result_ptr = std::make_shared(std::stod(get_arguments<1>(node)[0]->get_ref())); - json_tmp_stack.push_back(result_ptr); - json_eval_stack.push(result_ptr.get()); - } break; - case Op::Int: { - result_ptr = std::make_shared(std::stoi(get_arguments<1>(node)[0]->get_ref())); - json_tmp_stack.push_back(result_ptr); - json_eval_stack.push(result_ptr.get()); - } break; - case Op::Last: { - auto result = &get_arguments<1>(node)[0]->back(); - json_eval_stack.push(result); - } break; - case Op::Length: { - auto val = get_arguments<1>(node)[0]; - if (val->is_string()) { - result_ptr = std::make_shared(val->get_ref().length()); - } else { - result_ptr = std::make_shared(val->size()); - } - json_tmp_stack.push_back(result_ptr); - json_eval_stack.push(result_ptr.get()); - } break; - case Op::Lower: { - std::string result = get_arguments<1>(node)[0]->get(); - std::transform(result.begin(), result.end(), result.begin(), ::tolower); - result_ptr = std::make_shared(std::move(result)); - json_tmp_stack.push_back(result_ptr); - json_eval_stack.push(result_ptr.get()); - } break; - case Op::Max: { - auto args = get_arguments<1>(node); - auto result = std::max_element(args[0]->begin(), args[0]->end()); - json_eval_stack.push(&(*result)); - } break; - case Op::Min: { - auto args = get_arguments<1>(node); - auto result = std::min_element(args[0]->begin(), args[0]->end()); - json_eval_stack.push(&(*result)); - } break; - case Op::Odd: { - result_ptr = std::make_shared(get_arguments<1>(node)[0]->get() % 2 != 0); - json_tmp_stack.push_back(result_ptr); - json_eval_stack.push(result_ptr.get()); - } break; - case Op::Range: { - std::vector result(get_arguments<1>(node)[0]->get()); - std::iota(result.begin(), result.end(), 0); - result_ptr = std::make_shared(std::move(result)); - json_tmp_stack.push_back(result_ptr); - json_eval_stack.push(result_ptr.get()); - } break; - case Op::Round: { - auto args = get_arguments<2>(node); - int precision = args[1]->get(); - double result = std::round(args[0]->get() * std::pow(10.0, precision)) / std::pow(10.0, precision); - result_ptr = std::make_shared(std::move(result)); - json_tmp_stack.push_back(result_ptr); - json_eval_stack.push(result_ptr.get()); - } break; - case Op::Sort: { - result_ptr = std::make_shared(get_arguments<1>(node)[0]->get>()); - std::sort(result_ptr->begin(), result_ptr->end()); - json_tmp_stack.push_back(result_ptr); - json_eval_stack.push(result_ptr.get()); - } break; - case Op::Upper: { - std::string result = get_arguments<1>(node)[0]->get(); - std::transform(result.begin(), result.end(), result.begin(), ::toupper); - result_ptr = std::make_shared(std::move(result)); - json_tmp_stack.push_back(result_ptr); - json_eval_stack.push(result_ptr.get()); - } break; - case Op::IsBoolean: { - result_ptr = std::make_shared(get_arguments<1>(node)[0]->is_boolean()); - json_tmp_stack.push_back(result_ptr); - json_eval_stack.push(result_ptr.get()); - } break; - case Op::IsNumber: { - result_ptr = std::make_shared(get_arguments<1>(node)[0]->is_number()); - json_tmp_stack.push_back(result_ptr); - json_eval_stack.push(result_ptr.get()); - } break; - case Op::IsInteger: { - result_ptr = std::make_shared(get_arguments<1>(node)[0]->is_number_integer()); - json_tmp_stack.push_back(result_ptr); - json_eval_stack.push(result_ptr.get()); - } break; - case Op::IsFloat: { - result_ptr = std::make_shared(get_arguments<1>(node)[0]->is_number_float()); - json_tmp_stack.push_back(result_ptr); - json_eval_stack.push(result_ptr.get()); - } break; - case Op::IsObject: { - result_ptr = std::make_shared(get_arguments<1>(node)[0]->is_object()); - json_tmp_stack.push_back(result_ptr); - json_eval_stack.push(result_ptr.get()); - } break; - case Op::IsArray: { - result_ptr = std::make_shared(get_arguments<1>(node)[0]->is_array()); - json_tmp_stack.push_back(result_ptr); - json_eval_stack.push(result_ptr.get()); - } break; - case Op::IsString: { - result_ptr = std::make_shared(get_arguments<1>(node)[0]->is_string()); - json_tmp_stack.push_back(result_ptr); - json_eval_stack.push(result_ptr.get()); - } break; - case Op::Callback: { - auto args = get_argument_vector(node.number_args, node); - result_ptr = std::make_shared(node.callback(args)); - json_tmp_stack.push_back(result_ptr); - json_eval_stack.push(result_ptr.get()); - } break; - case Op::ParenLeft: - case Op::ParenRight: - case Op::None: - break; - } - } - - void visit(const ExpressionListNode& node) { - print_json(eval_expression_list(node)); - } - - void visit(const StatementNode&) { } - - void visit(const ForStatementNode&) { } - - void visit(const ForArrayStatementNode& node) { - auto result = eval_expression_list(node.condition); - if (!result->is_array()) { - throw_renderer_error("object must be an array", node); - } - - if (!current_loop_data->empty()) { - auto tmp = *current_loop_data; // Because of clang-3 - (*current_loop_data)["parent"] = std::move(tmp); - } - - size_t index = 0; - (*current_loop_data)["is_first"] = true; - (*current_loop_data)["is_last"] = (result->size() <= 1); - for (auto it = result->begin(); it != result->end(); ++it) { - json_additional_data[static_cast(node.value)] = *it; - - (*current_loop_data)["index"] = index; - (*current_loop_data)["index1"] = index + 1; - if (index == 1) { - (*current_loop_data)["is_first"] = false; - } - if (index == result->size() - 1) { - (*current_loop_data)["is_last"] = true; - } - - node.body.accept(*this); - ++index; - } - - json_additional_data[static_cast(node.value)].clear(); - if (!(*current_loop_data)["parent"].empty()) { - auto tmp = (*current_loop_data)["parent"]; - *current_loop_data = std::move(tmp); - } else { - current_loop_data = &json_additional_data["loop"]; - } - } - - void visit(const ForObjectStatementNode& node) { - auto result = eval_expression_list(node.condition); - if (!result->is_object()) { - throw_renderer_error("object must be an object", node); - } - - if (!current_loop_data->empty()) { - (*current_loop_data)["parent"] = std::move(*current_loop_data); - } - - size_t index = 0; - (*current_loop_data)["is_first"] = true; - (*current_loop_data)["is_last"] = (result->size() <= 1); - for (auto it = result->begin(); it != result->end(); ++it) { - json_additional_data[static_cast(node.key)] = it.key(); - json_additional_data[static_cast(node.value)] = it.value(); - - (*current_loop_data)["index"] = index; - (*current_loop_data)["index1"] = index + 1; - if (index == 1) { - (*current_loop_data)["is_first"] = false; - } - if (index == result->size() - 1) { - (*current_loop_data)["is_last"] = true; - } - - node.body.accept(*this); - ++index; - } - - json_additional_data[static_cast(node.key)].clear(); - json_additional_data[static_cast(node.value)].clear(); - if (!(*current_loop_data)["parent"].empty()) { - *current_loop_data = std::move((*current_loop_data)["parent"]); - } else { - current_loop_data = &json_additional_data["loop"]; - } - } - - void visit(const IfStatementNode& node) { - auto result = eval_expression_list(node.condition); - if (truthy(result.get())) { - node.true_statement.accept(*this); - } else if (node.has_false_statement) { - node.false_statement.accept(*this); - } - } - - void visit(const IncludeStatementNode& node) { - auto sub_renderer = Renderer(config, template_storage, function_storage); - auto included_template_it = template_storage.find(node.file); - - if (included_template_it != template_storage.end()) { - sub_renderer.render_to(*output_stream, included_template_it->second, *json_input, &json_additional_data); - } else if (config.throw_at_missing_includes) { - throw_renderer_error("include '" + node.file + "' not found", node); - } - } - - void visit(const SetStatementNode& node) { - json_additional_data[node.key] = *eval_expression_list(node.expression); - } - -public: - Renderer(const RenderConfig& config, const TemplateStorage &template_storage, const FunctionStorage &function_storage) - : config(config), template_storage(template_storage), function_storage(function_storage) { } - - void render_to(std::ostream &os, const Template &tmpl, const json &data, json *loop_data = nullptr) { - output_stream = &os; - current_template = &tmpl; - json_input = &data; - if (loop_data) { - json_additional_data = *loop_data; - current_loop_data = &json_additional_data["loop"]; - } - - current_template->root.accept(*this); - - json_tmp_stack.clear(); - } -}; - -} // namespace inja - -#endif // INCLUDE_INJA_RENDERER_HPP_ diff --git a/isis/src/base/apps/topds4/statistics2.h b/isis/src/base/apps/topds4/statistics2.h deleted file mode 100644 index be7eefe541..0000000000 --- a/isis/src/base/apps/topds4/statistics2.h +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) 2019 Pantor. All rights reserved. - -#ifndef INCLUDE_INJA_STATISTICS_HPP_ -#define INCLUDE_INJA_STATISTICS_HPP_ - -#include "node.h" - - -namespace inja { - -/*! - * \brief A class for counting statistics on a Template. - */ -class StatisticsVisitor : public NodeVisitor { - void visit(const BlockNode& node) { - for (auto& n : node.nodes) { - n->accept(*this); - } - } - - void visit(const TextNode&) { } - void visit(const ExpressionNode&) { } - void visit(const LiteralNode&) { } - - void visit(const JsonNode&) { - variable_counter += 1; - } - - void visit(const FunctionNode&) { } - - void visit(const ExpressionListNode& node) { - for (auto& n : node.rpn_output) { - n->accept(*this); - } - } - - void visit(const StatementNode&) { } - void visit(const ForStatementNode&) { } - - void visit(const ForArrayStatementNode& node) { - node.condition.accept(*this); - node.body.accept(*this); - } - - void visit(const ForObjectStatementNode& node) { - node.condition.accept(*this); - node.body.accept(*this); - } - - void visit(const IfStatementNode& node) { - node.condition.accept(*this); - node.true_statement.accept(*this); - node.false_statement.accept(*this); - } - - void visit(const IncludeStatementNode&) { } - - void visit(const SetStatementNode&) { } - -public: - unsigned int variable_counter; - - explicit StatisticsVisitor() : variable_counter(0) { } -}; - -} // namespace inja - -#endif // INCLUDE_INJA_STATISTICS_HPP_ diff --git a/isis/src/base/apps/topds4/string_view.h b/isis/src/base/apps/topds4/string_view.h deleted file mode 100644 index 2bb50c91b7..0000000000 --- a/isis/src/base/apps/topds4/string_view.h +++ /dev/null @@ -1,1416 +0,0 @@ -// Copyright 2017-2019 by Martin Moene -// -// string-view lite, a C++17-like string_view for C++98 and later. -// For more information see https://github.com/martinmoene/string-view-lite -// -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#pragma once - -#ifndef NONSTD_SV_LITE_H_INCLUDED -#define NONSTD_SV_LITE_H_INCLUDED - -#define string_view_lite_MAJOR 1 -#define string_view_lite_MINOR 4 -#define string_view_lite_PATCH 0 - -#define string_view_lite_VERSION \ - nssv_STRINGIFY(string_view_lite_MAJOR) "." nssv_STRINGIFY(string_view_lite_MINOR) "." nssv_STRINGIFY( \ - string_view_lite_PATCH) - -#define nssv_STRINGIFY(x) nssv_STRINGIFY_(x) -#define nssv_STRINGIFY_(x) #x - -// string-view lite configuration: - -#define nssv_STRING_VIEW_DEFAULT 0 -#define nssv_STRING_VIEW_NONSTD 1 -#define nssv_STRING_VIEW_STD 2 - -#if !defined(nssv_CONFIG_SELECT_STRING_VIEW) -#define nssv_CONFIG_SELECT_STRING_VIEW (nssv_HAVE_STD_STRING_VIEW ? nssv_STRING_VIEW_STD : nssv_STRING_VIEW_NONSTD) -#endif - -#if defined(nssv_CONFIG_SELECT_STD_STRING_VIEW) || defined(nssv_CONFIG_SELECT_NONSTD_STRING_VIEW) -#error nssv_CONFIG_SELECT_STD_STRING_VIEW and nssv_CONFIG_SELECT_NONSTD_STRING_VIEW are deprecated and removed, please use nssv_CONFIG_SELECT_STRING_VIEW=nssv_STRING_VIEW_... -#endif - -#ifndef nssv_CONFIG_STD_SV_OPERATOR -#define nssv_CONFIG_STD_SV_OPERATOR 0 -#endif - -#ifndef nssv_CONFIG_USR_SV_OPERATOR -#define nssv_CONFIG_USR_SV_OPERATOR 1 -#endif - -#ifdef nssv_CONFIG_CONVERSION_STD_STRING -#define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS nssv_CONFIG_CONVERSION_STD_STRING -#define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS nssv_CONFIG_CONVERSION_STD_STRING -#endif - -#ifndef nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS -#define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS 1 -#endif - -#ifndef nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS -#define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS 1 -#endif - -// Control presence of exception handling (try and auto discover): - -#ifndef nssv_CONFIG_NO_EXCEPTIONS -#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) -#define nssv_CONFIG_NO_EXCEPTIONS 0 -#else -#define nssv_CONFIG_NO_EXCEPTIONS 1 -#endif -#endif - -// C++ language version detection (C++20 is speculative): -// Note: VC14.0/1900 (VS2015) lacks too much from C++14. - -#ifndef nssv_CPLUSPLUS -#if defined(_MSVC_LANG) && !defined(__clang__) -#define nssv_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG) -#else -#define nssv_CPLUSPLUS __cplusplus -#endif -#endif - -#define nssv_CPP98_OR_GREATER (nssv_CPLUSPLUS >= 199711L) -#define nssv_CPP11_OR_GREATER (nssv_CPLUSPLUS >= 201103L) -#define nssv_CPP11_OR_GREATER_ (nssv_CPLUSPLUS >= 201103L) -#define nssv_CPP14_OR_GREATER (nssv_CPLUSPLUS >= 201402L) -#define nssv_CPP17_OR_GREATER (nssv_CPLUSPLUS >= 201703L) -#define nssv_CPP20_OR_GREATER (nssv_CPLUSPLUS >= 202000L) - -// use C++17 std::string_view if available and requested: - -#if nssv_CPP17_OR_GREATER && defined(__has_include) -#if __has_include( ) -#define nssv_HAVE_STD_STRING_VIEW 1 -#else -#define nssv_HAVE_STD_STRING_VIEW 0 -#endif -#else -#define nssv_HAVE_STD_STRING_VIEW 0 -#endif - -#define nssv_USES_STD_STRING_VIEW \ - ((nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_STD) || \ - ((nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_DEFAULT) && nssv_HAVE_STD_STRING_VIEW)) - -#define nssv_HAVE_STARTS_WITH (nssv_CPP20_OR_GREATER || !nssv_USES_STD_STRING_VIEW) -#define nssv_HAVE_ENDS_WITH nssv_HAVE_STARTS_WITH - -// -// Use C++17 std::string_view: -// - -#if nssv_USES_STD_STRING_VIEW - -#include - -// Extensions for std::string: - -#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS - -namespace nonstd { - -template > -std::basic_string to_string(std::basic_string_view v, - Allocator const &a = Allocator()) { - return std::basic_string(v.begin(), v.end(), a); -} - -template -std::basic_string_view to_string_view(std::basic_string const &s) { - return std::basic_string_view(s.data(), s.size()); -} - -// Literal operators sv and _sv: - -#if nssv_CONFIG_STD_SV_OPERATOR - -using namespace std::literals::string_view_literals; - -#endif - -#if nssv_CONFIG_USR_SV_OPERATOR - -inline namespace literals { -inline namespace string_view_literals { - -constexpr std::string_view operator"" _sv(const char *str, size_t len) noexcept // (1) -{ - return std::string_view {str, len}; -} - -constexpr std::u16string_view operator"" _sv(const char16_t *str, size_t len) noexcept // (2) -{ - return std::u16string_view {str, len}; -} - -constexpr std::u32string_view operator"" _sv(const char32_t *str, size_t len) noexcept // (3) -{ - return std::u32string_view {str, len}; -} - -constexpr std::wstring_view operator"" _sv(const wchar_t *str, size_t len) noexcept // (4) -{ - return std::wstring_view {str, len}; -} - -} // namespace string_view_literals -} // namespace literals - -#endif // nssv_CONFIG_USR_SV_OPERATOR - -} // namespace nonstd - -#endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS - -namespace nonstd { - -using std::basic_string_view; -using std::string_view; -using std::u16string_view; -using std::u32string_view; -using std::wstring_view; - -// literal "sv" and "_sv", see above - -using std::operator==; -using std::operator!=; -using std::operator<; -using std::operator<=; -using std::operator>; -using std::operator>=; - -using std::operator<<; - -} // namespace nonstd - -#else // nssv_HAVE_STD_STRING_VIEW - -// -// Before C++17: use string_view lite: -// - -// Compiler versions: -// -// MSVC++ 6.0 _MSC_VER == 1200 (Visual Studio 6.0) -// MSVC++ 7.0 _MSC_VER == 1300 (Visual Studio .NET 2002) -// MSVC++ 7.1 _MSC_VER == 1310 (Visual Studio .NET 2003) -// MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005) -// MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008) -// MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010) -// MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012) -// MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013) -// MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015) -// MSVC++ 14.1 _MSC_VER >= 1910 (Visual Studio 2017) - -#if defined(_MSC_VER) && !defined(__clang__) -#define nssv_COMPILER_MSVC_VER (_MSC_VER) -#define nssv_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * (5 + (_MSC_VER < 1900))) -#else -#define nssv_COMPILER_MSVC_VER 0 -#define nssv_COMPILER_MSVC_VERSION 0 -#endif - -#define nssv_COMPILER_VERSION(major, minor, patch) (10 * (10 * (major) + (minor)) + (patch)) - -#if defined(__clang__) -#define nssv_COMPILER_CLANG_VERSION nssv_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__) -#else -#define nssv_COMPILER_CLANG_VERSION 0 -#endif - -#if defined(__GNUC__) && !defined(__clang__) -#define nssv_COMPILER_GNUC_VERSION nssv_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) -#else -#define nssv_COMPILER_GNUC_VERSION 0 -#endif - -// half-open range [lo..hi): -#define nssv_BETWEEN(v, lo, hi) ((lo) <= (v) && (v) < (hi)) - -// Presence of language and library features: - -#ifdef _HAS_CPP0X -#define nssv_HAS_CPP0X _HAS_CPP0X -#else -#define nssv_HAS_CPP0X 0 -#endif - -// Unless defined otherwise below, consider VC14 as C++11 for variant-lite: - -#if nssv_COMPILER_MSVC_VER >= 1900 -#undef nssv_CPP11_OR_GREATER -#define nssv_CPP11_OR_GREATER 1 -#endif - -#define nssv_CPP11_90 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1500) -#define nssv_CPP11_100 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1600) -#define nssv_CPP11_110 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1700) -#define nssv_CPP11_120 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1800) -#define nssv_CPP11_140 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1900) -#define nssv_CPP11_141 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1910) - -#define nssv_CPP14_000 (nssv_CPP14_OR_GREATER) -#define nssv_CPP17_000 (nssv_CPP17_OR_GREATER) - -// Presence of C++11 language features: - -#define nssv_HAVE_CONSTEXPR_11 nssv_CPP11_140 -#define nssv_HAVE_EXPLICIT_CONVERSION nssv_CPP11_140 -#define nssv_HAVE_INLINE_NAMESPACE nssv_CPP11_140 -#define nssv_HAVE_NOEXCEPT nssv_CPP11_140 -#define nssv_HAVE_NULLPTR nssv_CPP11_100 -#define nssv_HAVE_REF_QUALIFIER nssv_CPP11_140 -#define nssv_HAVE_UNICODE_LITERALS nssv_CPP11_140 -#define nssv_HAVE_USER_DEFINED_LITERALS nssv_CPP11_140 -#define nssv_HAVE_WCHAR16_T nssv_CPP11_100 -#define nssv_HAVE_WCHAR32_T nssv_CPP11_100 - -#if !((nssv_CPP11_OR_GREATER && nssv_COMPILER_CLANG_VERSION) || nssv_BETWEEN(nssv_COMPILER_CLANG_VERSION, 300, 400)) -#define nssv_HAVE_STD_DEFINED_LITERALS nssv_CPP11_140 -#else -#define nssv_HAVE_STD_DEFINED_LITERALS 0 -#endif - -// Presence of C++14 language features: - -#define nssv_HAVE_CONSTEXPR_14 nssv_CPP14_000 - -// Presence of C++17 language features: - -#define nssv_HAVE_NODISCARD nssv_CPP17_000 - -// Presence of C++ library features: - -#define nssv_HAVE_STD_HASH nssv_CPP11_120 - -// C++ feature usage: - -#if nssv_HAVE_CONSTEXPR_11 -#define nssv_constexpr constexpr -#else -#define nssv_constexpr /*constexpr*/ -#endif - -#if nssv_HAVE_CONSTEXPR_14 -#define nssv_constexpr14 constexpr -#else -#define nssv_constexpr14 /*constexpr*/ -#endif - -#if nssv_HAVE_EXPLICIT_CONVERSION -#define nssv_explicit explicit -#else -#define nssv_explicit /*explicit*/ -#endif - -#if nssv_HAVE_INLINE_NAMESPACE -#define nssv_inline_ns inline -#else -#define nssv_inline_ns /*inline*/ -#endif - -#if nssv_HAVE_NOEXCEPT -#define nssv_noexcept noexcept -#else -#define nssv_noexcept /*noexcept*/ -#endif - -//#if nssv_HAVE_REF_QUALIFIER -//# define nssv_ref_qual & -//# define nssv_refref_qual && -//#else -//# define nssv_ref_qual /*&*/ -//# define nssv_refref_qual /*&&*/ -//#endif - -#if nssv_HAVE_NULLPTR -#define nssv_nullptr nullptr -#else -#define nssv_nullptr NULL -#endif - -#if nssv_HAVE_NODISCARD -#define nssv_nodiscard [[nodiscard]] -#else -#define nssv_nodiscard /*[[nodiscard]]*/ -#endif - -// Additional includes: - -#include -#include -#include -#include -#include -#include // std::char_traits<> - -#if !nssv_CONFIG_NO_EXCEPTIONS -#include -#endif - -#if nssv_CPP11_OR_GREATER -#include -#endif - -// Clang, GNUC, MSVC warning suppression macros: - -#if defined(__clang__) -#pragma clang diagnostic ignored "-Wreserved-user-defined-literal" -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wuser-defined-literals" -#elif defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wliteral-suffix" -#endif // __clang__ - -#if nssv_COMPILER_MSVC_VERSION >= 140 -#define nssv_SUPPRESS_MSGSL_WARNING(expr) [[gsl::suppress(expr)]] -#define nssv_SUPPRESS_MSVC_WARNING(code, descr) __pragma(warning(suppress : code)) -#define nssv_DISABLE_MSVC_WARNINGS(codes) __pragma(warning(push)) __pragma(warning(disable : codes)) -#else -#define nssv_SUPPRESS_MSGSL_WARNING(expr) -#define nssv_SUPPRESS_MSVC_WARNING(code, descr) -#define nssv_DISABLE_MSVC_WARNINGS(codes) -#endif - -#if defined(__clang__) -#define nssv_RESTORE_WARNINGS() _Pragma("clang diagnostic pop") -#elif defined(__GNUC__) -#define nssv_RESTORE_WARNINGS() _Pragma("GCC diagnostic pop") -#elif nssv_COMPILER_MSVC_VERSION >= 140 -#define nssv_RESTORE_WARNINGS() __pragma(warning(pop)) -#else -#define nssv_RESTORE_WARNINGS() -#endif - -// Suppress the following MSVC (GSL) warnings: -// - C4455, non-gsl : 'operator ""sv': literal suffix identifiers that do not -// start with an underscore are reserved -// - C26472, gsl::t.1 : don't use a static_cast for arithmetic conversions; -// use brace initialization, gsl::narrow_cast or gsl::narow -// - C26481: gsl::b.1 : don't use pointer arithmetic. Use span instead - -nssv_DISABLE_MSVC_WARNINGS(4455 26481 26472) - // nssv_DISABLE_CLANG_WARNINGS( "-Wuser-defined-literals" ) - // nssv_DISABLE_GNUC_WARNINGS( -Wliteral-suffix ) - - namespace nonstd { - namespace sv_lite { - -#if nssv_CPP11_OR_GREATER - - namespace detail { - - // Expect tail call optimization to make length() non-recursive: - - template inline constexpr std::size_t length(CharT *s, std::size_t result = 0) { - return *s == '\0' ? result : length(s + 1, result + 1); - } - - } // namespace detail - -#endif // nssv_CPP11_OR_GREATER - - template > class basic_string_view; - - // - // basic_string_view: - // - - template */ - > - class basic_string_view { - public: - // Member types: - - typedef Traits traits_type; - typedef CharT value_type; - - typedef CharT *pointer; - typedef CharT const *const_pointer; - typedef CharT &reference; - typedef CharT const &const_reference; - - typedef const_pointer iterator; - typedef const_pointer const_iterator; - typedef std::reverse_iterator reverse_iterator; - typedef std::reverse_iterator const_reverse_iterator; - - typedef std::size_t size_type; - typedef std::ptrdiff_t difference_type; - - // 24.4.2.1 Construction and assignment: - - nssv_constexpr basic_string_view() nssv_noexcept : data_(nssv_nullptr), size_(0) {} - -#if nssv_CPP11_OR_GREATER - nssv_constexpr basic_string_view(basic_string_view const &other) nssv_noexcept = default; -#else - nssv_constexpr basic_string_view(basic_string_view const &other) nssv_noexcept : data_(other.data_), - size_(other.size_) {} -#endif - - nssv_constexpr basic_string_view(CharT const *s, size_type count) nssv_noexcept // non-standard noexcept - : data_(s), - size_(count) {} - - nssv_constexpr basic_string_view(CharT const *s) nssv_noexcept // non-standard noexcept - : data_(s) -#if nssv_CPP17_OR_GREATER - , - size_(Traits::length(s)) -#elif nssv_CPP11_OR_GREATER - , - size_(detail::length(s)) -#else - , - size_(Traits::length(s)) -#endif - { - } - - // Assignment: - -#if nssv_CPP11_OR_GREATER - nssv_constexpr14 basic_string_view &operator=(basic_string_view const &other) nssv_noexcept = default; -#else - nssv_constexpr14 basic_string_view &operator=(basic_string_view const &other) nssv_noexcept { - data_ = other.data_; - size_ = other.size_; - return *this; - } -#endif - - // 24.4.2.2 Iterator support: - - nssv_constexpr const_iterator begin() const nssv_noexcept { return data_; } - nssv_constexpr const_iterator end() const nssv_noexcept { return data_ + size_; } - - nssv_constexpr const_iterator cbegin() const nssv_noexcept { return begin(); } - nssv_constexpr const_iterator cend() const nssv_noexcept { return end(); } - - nssv_constexpr const_reverse_iterator rbegin() const nssv_noexcept { return const_reverse_iterator(end()); } - nssv_constexpr const_reverse_iterator rend() const nssv_noexcept { return const_reverse_iterator(begin()); } - - nssv_constexpr const_reverse_iterator crbegin() const nssv_noexcept { return rbegin(); } - nssv_constexpr const_reverse_iterator crend() const nssv_noexcept { return rend(); } - - // 24.4.2.3 Capacity: - - nssv_constexpr size_type size() const nssv_noexcept { return size_; } - nssv_constexpr size_type length() const nssv_noexcept { return size_; } - nssv_constexpr size_type max_size() const nssv_noexcept { return (std::numeric_limits::max)(); } - - // since C++20 - nssv_nodiscard nssv_constexpr bool empty() const nssv_noexcept { return 0 == size_; } - - // 24.4.2.4 Element access: - - nssv_constexpr const_reference operator[](size_type pos) const { return data_at(pos); } - - nssv_constexpr14 const_reference at(size_type pos) const { -#if nssv_CONFIG_NO_EXCEPTIONS - assert(pos < size()); -#else - if (pos >= size()) { - throw std::out_of_range("nonstd::string_view::at()"); - } -#endif - return data_at(pos); - } - - nssv_constexpr const_reference front() const { return data_at(0); } - nssv_constexpr const_reference back() const { return data_at(size() - 1); } - - nssv_constexpr const_pointer data() const nssv_noexcept { return data_; } - - // 24.4.2.5 Modifiers: - - nssv_constexpr14 void remove_prefix(size_type n) { - assert(n <= size()); - data_ += n; - size_ -= n; - } - - nssv_constexpr14 void remove_suffix(size_type n) { - assert(n <= size()); - size_ -= n; - } - - nssv_constexpr14 void swap(basic_string_view &other) nssv_noexcept { - using std::swap; - swap(data_, other.data_); - swap(size_, other.size_); - } - - // 24.4.2.6 String operations: - - size_type copy(CharT *dest, size_type n, size_type pos = 0) const { -#if nssv_CONFIG_NO_EXCEPTIONS - assert(pos <= size()); -#else - if (pos > size()) { - throw std::out_of_range("nonstd::string_view::copy()"); - } -#endif - const size_type rlen = (std::min)(n, size() - pos); - - (void)Traits::copy(dest, data() + pos, rlen); - - return rlen; - } - - nssv_constexpr14 basic_string_view substr(size_type pos = 0, size_type n = npos) const { -#if nssv_CONFIG_NO_EXCEPTIONS - assert(pos <= size()); -#else - if (pos > size()) { - throw std::out_of_range("nonstd::string_view::substr()"); - } -#endif - return basic_string_view(data() + pos, (std::min)(n, size() - pos)); - } - - // compare(), 6x: - - nssv_constexpr14 int compare(basic_string_view other) const nssv_noexcept // (1) - { - if (const int result = Traits::compare(data(), other.data(), (std::min)(size(), other.size()))) { - return result; - } - - return size() == other.size() ? 0 : size() < other.size() ? -1 : 1; - } - - nssv_constexpr int compare(size_type pos1, size_type n1, basic_string_view other) const // (2) - { - return substr(pos1, n1).compare(other); - } - - nssv_constexpr int compare(size_type pos1, size_type n1, basic_string_view other, size_type pos2, - size_type n2) const // (3) - { - return substr(pos1, n1).compare(other.substr(pos2, n2)); - } - - nssv_constexpr int compare(CharT const *s) const // (4) - { - return compare(basic_string_view(s)); - } - - nssv_constexpr int compare(size_type pos1, size_type n1, CharT const *s) const // (5) - { - return substr(pos1, n1).compare(basic_string_view(s)); - } - - nssv_constexpr int compare(size_type pos1, size_type n1, CharT const *s, size_type n2) const // (6) - { - return substr(pos1, n1).compare(basic_string_view(s, n2)); - } - - // 24.4.2.7 Searching: - - // starts_with(), 3x, since C++20: - - nssv_constexpr bool starts_with(basic_string_view v) const nssv_noexcept // (1) - { - return size() >= v.size() && compare(0, v.size(), v) == 0; - } - - nssv_constexpr bool starts_with(CharT c) const nssv_noexcept // (2) - { - return starts_with(basic_string_view(&c, 1)); - } - - nssv_constexpr bool starts_with(CharT const *s) const // (3) - { - return starts_with(basic_string_view(s)); - } - - // ends_with(), 3x, since C++20: - - nssv_constexpr bool ends_with(basic_string_view v) const nssv_noexcept // (1) - { - return size() >= v.size() && compare(size() - v.size(), npos, v) == 0; - } - - nssv_constexpr bool ends_with(CharT c) const nssv_noexcept // (2) - { - return ends_with(basic_string_view(&c, 1)); - } - - nssv_constexpr bool ends_with(CharT const *s) const // (3) - { - return ends_with(basic_string_view(s)); - } - - // find(), 4x: - - nssv_constexpr14 size_type find(basic_string_view v, size_type pos = 0) const nssv_noexcept // (1) - { - return assert(v.size() == 0 || v.data() != nssv_nullptr), - pos >= size() ? npos : to_pos(std::search(cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq)); - } - - nssv_constexpr14 size_type find(CharT c, size_type pos = 0) const nssv_noexcept // (2) - { - return find(basic_string_view(&c, 1), pos); - } - - nssv_constexpr14 size_type find(CharT const *s, size_type pos, size_type n) const // (3) - { - return find(basic_string_view(s, n), pos); - } - - nssv_constexpr14 size_type find(CharT const *s, size_type pos = 0) const // (4) - { - return find(basic_string_view(s), pos); - } - - // rfind(), 4x: - - nssv_constexpr14 size_type rfind(basic_string_view v, size_type pos = npos) const nssv_noexcept // (1) - { - if (size() < v.size()) { - return npos; - } - - if (v.empty()) { - return (std::min)(size(), pos); - } - - const_iterator last = cbegin() + (std::min)(size() - v.size(), pos) + v.size(); - const_iterator result = std::find_end(cbegin(), last, v.cbegin(), v.cend(), Traits::eq); - - return result != last ? size_type(result - cbegin()) : npos; - } - - nssv_constexpr14 size_type rfind(CharT c, size_type pos = npos) const nssv_noexcept // (2) - { - return rfind(basic_string_view(&c, 1), pos); - } - - nssv_constexpr14 size_type rfind(CharT const *s, size_type pos, size_type n) const // (3) - { - return rfind(basic_string_view(s, n), pos); - } - - nssv_constexpr14 size_type rfind(CharT const *s, size_type pos = npos) const // (4) - { - return rfind(basic_string_view(s), pos); - } - - // find_first_of(), 4x: - - nssv_constexpr size_type find_first_of(basic_string_view v, size_type pos = 0) const nssv_noexcept // (1) - { - return pos >= size() ? npos - : to_pos(std::find_first_of(cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq)); - } - - nssv_constexpr size_type find_first_of(CharT c, size_type pos = 0) const nssv_noexcept // (2) - { - return find_first_of(basic_string_view(&c, 1), pos); - } - - nssv_constexpr size_type find_first_of(CharT const *s, size_type pos, size_type n) const // (3) - { - return find_first_of(basic_string_view(s, n), pos); - } - - nssv_constexpr size_type find_first_of(CharT const *s, size_type pos = 0) const // (4) - { - return find_first_of(basic_string_view(s), pos); - } - - // find_last_of(), 4x: - - nssv_constexpr size_type find_last_of(basic_string_view v, size_type pos = npos) const nssv_noexcept // (1) - { - return empty() ? npos - : pos >= size() ? find_last_of(v, size() - 1) - : to_pos(std::find_first_of(const_reverse_iterator(cbegin() + pos + 1), crend(), - v.cbegin(), v.cend(), Traits::eq)); - } - - nssv_constexpr size_type find_last_of(CharT c, size_type pos = npos) const nssv_noexcept // (2) - { - return find_last_of(basic_string_view(&c, 1), pos); - } - - nssv_constexpr size_type find_last_of(CharT const *s, size_type pos, size_type count) const // (3) - { - return find_last_of(basic_string_view(s, count), pos); - } - - nssv_constexpr size_type find_last_of(CharT const *s, size_type pos = npos) const // (4) - { - return find_last_of(basic_string_view(s), pos); - } - - // find_first_not_of(), 4x: - - nssv_constexpr size_type find_first_not_of(basic_string_view v, size_type pos = 0) const nssv_noexcept // (1) - { - return pos >= size() ? npos : to_pos(std::find_if(cbegin() + pos, cend(), not_in_view(v))); - } - - nssv_constexpr size_type find_first_not_of(CharT c, size_type pos = 0) const nssv_noexcept // (2) - { - return find_first_not_of(basic_string_view(&c, 1), pos); - } - - nssv_constexpr size_type find_first_not_of(CharT const *s, size_type pos, size_type count) const // (3) - { - return find_first_not_of(basic_string_view(s, count), pos); - } - - nssv_constexpr size_type find_first_not_of(CharT const *s, size_type pos = 0) const // (4) - { - return find_first_not_of(basic_string_view(s), pos); - } - - // find_last_not_of(), 4x: - - nssv_constexpr size_type find_last_not_of(basic_string_view v, size_type pos = npos) const nssv_noexcept // (1) - { - return empty() ? npos - : pos >= size() - ? find_last_not_of(v, size() - 1) - : to_pos(std::find_if(const_reverse_iterator(cbegin() + pos + 1), crend(), not_in_view(v))); - } - - nssv_constexpr size_type find_last_not_of(CharT c, size_type pos = npos) const nssv_noexcept // (2) - { - return find_last_not_of(basic_string_view(&c, 1), pos); - } - - nssv_constexpr size_type find_last_not_of(CharT const *s, size_type pos, size_type count) const // (3) - { - return find_last_not_of(basic_string_view(s, count), pos); - } - - nssv_constexpr size_type find_last_not_of(CharT const *s, size_type pos = npos) const // (4) - { - return find_last_not_of(basic_string_view(s), pos); - } - - // Constants: - -#if nssv_CPP17_OR_GREATER - static nssv_constexpr size_type npos = size_type(-1); -#elif nssv_CPP11_OR_GREATER - enum : size_type { npos = size_type(-1) }; -#else - enum { npos = size_type(-1) }; -#endif - - private: - struct not_in_view { - const basic_string_view v; - - nssv_constexpr explicit not_in_view(basic_string_view v) : v(v) {} - - nssv_constexpr bool operator()(CharT c) const { return npos == v.find_first_of(c); } - }; - - nssv_constexpr size_type to_pos(const_iterator it) const { return it == cend() ? npos : size_type(it - cbegin()); } - - nssv_constexpr size_type to_pos(const_reverse_iterator it) const { - return it == crend() ? npos : size_type(crend() - it - 1); - } - - nssv_constexpr const_reference data_at(size_type pos) const { -#if nssv_BETWEEN(nssv_COMPILER_GNUC_VERSION, 1, 500) - return data_[pos]; -#else - return assert(pos < size()), data_[pos]; -#endif - } - - private: - const_pointer data_; - size_type size_; - - public: -#if nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS - - template - basic_string_view(std::basic_string const &s) nssv_noexcept : data_(s.data()), - size_(s.size()) {} - -#if nssv_HAVE_EXPLICIT_CONVERSION - - template explicit operator std::basic_string() const { - return to_string(Allocator()); - } - -#endif // nssv_HAVE_EXPLICIT_CONVERSION - -#if nssv_CPP11_OR_GREATER - - template > - std::basic_string to_string(Allocator const &a = Allocator()) const { - return std::basic_string(begin(), end(), a); - } - -#else - - std::basic_string to_string() const { return std::basic_string(begin(), end()); } - - template std::basic_string to_string(Allocator const &a) const { - return std::basic_string(begin(), end(), a); - } - -#endif // nssv_CPP11_OR_GREATER - -#endif // nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS - }; - - // - // Non-member functions: - // - - // 24.4.3 Non-member comparison functions: - // lexicographically compare two string views (function template): - - template - nssv_constexpr bool operator==(basic_string_view lhs, - basic_string_view rhs) nssv_noexcept { - return lhs.compare(rhs) == 0; - } - - template - nssv_constexpr bool operator!=(basic_string_view lhs, - basic_string_view rhs) nssv_noexcept { - return lhs.compare(rhs) != 0; - } - - template - nssv_constexpr bool operator<(basic_string_view lhs, - basic_string_view rhs) nssv_noexcept { - return lhs.compare(rhs) < 0; - } - - template - nssv_constexpr bool operator<=(basic_string_view lhs, - basic_string_view rhs) nssv_noexcept { - return lhs.compare(rhs) <= 0; - } - - template - nssv_constexpr bool operator>(basic_string_view lhs, - basic_string_view rhs) nssv_noexcept { - return lhs.compare(rhs) > 0; - } - - template - nssv_constexpr bool operator>=(basic_string_view lhs, - basic_string_view rhs) nssv_noexcept { - return lhs.compare(rhs) >= 0; - } - - // Let S be basic_string_view, and sv be an instance of S. - // Implementations shall provide sufficient additional overloads marked - // constexpr and noexcept so that an object t with an implicit conversion - // to S can be compared according to Table 67. - -#if !nssv_CPP11_OR_GREATER || nssv_BETWEEN(nssv_COMPILER_MSVC_VERSION, 100, 141) - - // accomodate for older compilers: - - // == - - template - nssv_constexpr bool operator==(basic_string_view lhs, char const *rhs) nssv_noexcept { - return lhs.compare(rhs) == 0; - } - - template - nssv_constexpr bool operator==(char const *lhs, basic_string_view rhs) nssv_noexcept { - return rhs.compare(lhs) == 0; - } - - template - nssv_constexpr bool operator==(basic_string_view lhs, - std::basic_string rhs) nssv_noexcept { - return lhs.size() == rhs.size() && lhs.compare(rhs) == 0; - } - - template - nssv_constexpr bool operator==(std::basic_string rhs, - basic_string_view lhs) nssv_noexcept { - return lhs.size() == rhs.size() && lhs.compare(rhs) == 0; - } - - // != - - template - nssv_constexpr bool operator!=(basic_string_view lhs, char const *rhs) nssv_noexcept { - return lhs.compare(rhs) != 0; - } - - template - nssv_constexpr bool operator!=(char const *lhs, basic_string_view rhs) nssv_noexcept { - return rhs.compare(lhs) != 0; - } - - template - nssv_constexpr bool operator!=(basic_string_view lhs, - std::basic_string rhs) nssv_noexcept { - return lhs.size() != rhs.size() && lhs.compare(rhs) != 0; - } - - template - nssv_constexpr bool operator!=(std::basic_string rhs, - basic_string_view lhs) nssv_noexcept { - return lhs.size() != rhs.size() || rhs.compare(lhs) != 0; - } - - // < - - template - nssv_constexpr bool operator<(basic_string_view lhs, char const *rhs) nssv_noexcept { - return lhs.compare(rhs) < 0; - } - - template - nssv_constexpr bool operator<(char const *lhs, basic_string_view rhs) nssv_noexcept { - return rhs.compare(lhs) > 0; - } - - template - nssv_constexpr bool operator<(basic_string_view lhs, - std::basic_string rhs) nssv_noexcept { - return lhs.compare(rhs) < 0; - } - - template - nssv_constexpr bool operator<(std::basic_string rhs, - basic_string_view lhs) nssv_noexcept { - return rhs.compare(lhs) > 0; - } - - // <= - - template - nssv_constexpr bool operator<=(basic_string_view lhs, char const *rhs) nssv_noexcept { - return lhs.compare(rhs) <= 0; - } - - template - nssv_constexpr bool operator<=(char const *lhs, basic_string_view rhs) nssv_noexcept { - return rhs.compare(lhs) >= 0; - } - - template - nssv_constexpr bool operator<=(basic_string_view lhs, - std::basic_string rhs) nssv_noexcept { - return lhs.compare(rhs) <= 0; - } - - template - nssv_constexpr bool operator<=(std::basic_string rhs, - basic_string_view lhs) nssv_noexcept { - return rhs.compare(lhs) >= 0; - } - - // > - - template - nssv_constexpr bool operator>(basic_string_view lhs, char const *rhs) nssv_noexcept { - return lhs.compare(rhs) > 0; - } - - template - nssv_constexpr bool operator>(char const *lhs, basic_string_view rhs) nssv_noexcept { - return rhs.compare(lhs) < 0; - } - - template - nssv_constexpr bool operator>(basic_string_view lhs, - std::basic_string rhs) nssv_noexcept { - return lhs.compare(rhs) > 0; - } - - template - nssv_constexpr bool operator>(std::basic_string rhs, - basic_string_view lhs) nssv_noexcept { - return rhs.compare(lhs) < 0; - } - - // >= - - template - nssv_constexpr bool operator>=(basic_string_view lhs, char const *rhs) nssv_noexcept { - return lhs.compare(rhs) >= 0; - } - - template - nssv_constexpr bool operator>=(char const *lhs, basic_string_view rhs) nssv_noexcept { - return rhs.compare(lhs) <= 0; - } - - template - nssv_constexpr bool operator>=(basic_string_view lhs, - std::basic_string rhs) nssv_noexcept { - return lhs.compare(rhs) >= 0; - } - - template - nssv_constexpr bool operator>=(std::basic_string rhs, - basic_string_view lhs) nssv_noexcept { - return rhs.compare(lhs) <= 0; - } - -#else // newer compilers: - -#define nssv_BASIC_STRING_VIEW_I(T, U) typename std::decay>::type - -#if nssv_BETWEEN(nssv_COMPILER_MSVC_VERSION, 140, 150) -#define nssv_MSVC_ORDER(x) , int = x -#else -#define nssv_MSVC_ORDER(x) /*, int=x*/ -#endif - - // == - - template - nssv_constexpr bool operator==(basic_string_view lhs, - nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs) nssv_noexcept { - return lhs.compare(rhs) == 0; - } - - template - nssv_constexpr bool operator==(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs, - basic_string_view rhs) nssv_noexcept { - return lhs.size() == rhs.size() && lhs.compare(rhs) == 0; - } - - // != - - template - nssv_constexpr bool operator!=(basic_string_view lhs, - nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs) nssv_noexcept { - return lhs.size() != rhs.size() || lhs.compare(rhs) != 0; - } - - template - nssv_constexpr bool operator!=(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs, - basic_string_view rhs) nssv_noexcept { - return lhs.compare(rhs) != 0; - } - - // < - - template - nssv_constexpr bool operator<(basic_string_view lhs, - nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs) nssv_noexcept { - return lhs.compare(rhs) < 0; - } - - template - nssv_constexpr bool operator<(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs, - basic_string_view rhs) nssv_noexcept { - return lhs.compare(rhs) < 0; - } - - // <= - - template - nssv_constexpr bool operator<=(basic_string_view lhs, - nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs) nssv_noexcept { - return lhs.compare(rhs) <= 0; - } - - template - nssv_constexpr bool operator<=(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs, - basic_string_view rhs) nssv_noexcept { - return lhs.compare(rhs) <= 0; - } - - // > - - template - nssv_constexpr bool operator>(basic_string_view lhs, - nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs) nssv_noexcept { - return lhs.compare(rhs) > 0; - } - - template - nssv_constexpr bool operator>(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs, - basic_string_view rhs) nssv_noexcept { - return lhs.compare(rhs) > 0; - } - - // >= - - template - nssv_constexpr bool operator>=(basic_string_view lhs, - nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs) nssv_noexcept { - return lhs.compare(rhs) >= 0; - } - - template - nssv_constexpr bool operator>=(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs, - basic_string_view rhs) nssv_noexcept { - return lhs.compare(rhs) >= 0; - } - -#undef nssv_MSVC_ORDER -#undef nssv_BASIC_STRING_VIEW_I - -#endif // compiler-dependent approach to comparisons - - // 24.4.4 Inserters and extractors: - - namespace detail { - - template void write_padding(Stream &os, std::streamsize n) { - for (std::streamsize i = 0; i < n; ++i) - os.rdbuf()->sputc(os.fill()); - } - - template Stream &write_to_stream(Stream &os, View const &sv) { - typename Stream::sentry sentry(os); - - if (!os) - return os; - - const std::streamsize length = static_cast(sv.length()); - - // Whether, and how, to pad: - const bool pad = (length < os.width()); - const bool left_pad = pad && (os.flags() & std::ios_base::adjustfield) == std::ios_base::right; - - if (left_pad) - write_padding(os, os.width() - length); - - // Write span characters: - os.rdbuf()->sputn(sv.begin(), length); - - if (pad && !left_pad) - write_padding(os, os.width() - length); - - // Reset output stream width: - os.width(0); - - return os; - } - - } // namespace detail - - template - std::basic_ostream &operator<<(std::basic_ostream &os, - basic_string_view sv) { - return detail::write_to_stream(os, sv); - } - - // Several typedefs for common character types are provided: - - typedef basic_string_view string_view; - typedef basic_string_view wstring_view; -#if nssv_HAVE_WCHAR16_T - typedef basic_string_view u16string_view; - typedef basic_string_view u32string_view; -#endif - - } // namespace sv_lite -} // namespace nonstd::sv_lite - -// -// 24.4.6 Suffix for basic_string_view literals: -// - -#if nssv_HAVE_USER_DEFINED_LITERALS - -namespace nonstd { -nssv_inline_ns namespace literals { - nssv_inline_ns namespace string_view_literals { - -#if nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS - - nssv_constexpr nonstd::sv_lite::string_view operator"" sv(const char *str, size_t len) nssv_noexcept // (1) - { - return nonstd::sv_lite::string_view {str, len}; - } - - nssv_constexpr nonstd::sv_lite::u16string_view operator"" sv(const char16_t *str, size_t len) nssv_noexcept // (2) - { - return nonstd::sv_lite::u16string_view {str, len}; - } - - nssv_constexpr nonstd::sv_lite::u32string_view operator"" sv(const char32_t *str, size_t len) nssv_noexcept // (3) - { - return nonstd::sv_lite::u32string_view {str, len}; - } - - nssv_constexpr nonstd::sv_lite::wstring_view operator"" sv(const wchar_t *str, size_t len) nssv_noexcept // (4) - { - return nonstd::sv_lite::wstring_view {str, len}; - } - -#endif // nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS - -#if nssv_CONFIG_USR_SV_OPERATOR - - nssv_constexpr nonstd::sv_lite::string_view operator"" _sv(const char *str, size_t len) nssv_noexcept // (1) - { - return nonstd::sv_lite::string_view {str, len}; - } - - nssv_constexpr nonstd::sv_lite::u16string_view operator"" _sv(const char16_t *str, size_t len) nssv_noexcept // (2) - { - return nonstd::sv_lite::u16string_view {str, len}; - } - - nssv_constexpr nonstd::sv_lite::u32string_view operator"" _sv(const char32_t *str, size_t len) nssv_noexcept // (3) - { - return nonstd::sv_lite::u32string_view {str, len}; - } - - nssv_constexpr nonstd::sv_lite::wstring_view operator"" _sv(const wchar_t *str, size_t len) nssv_noexcept // (4) - { - return nonstd::sv_lite::wstring_view {str, len}; - } - -#endif // nssv_CONFIG_USR_SV_OPERATOR - } -} -} // namespace nonstd - -#endif - -// -// Extensions for std::string: -// - -#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS - -namespace nonstd { -namespace sv_lite { - -// Exclude MSVC 14 (19.00): it yields ambiguous to_string(): - -#if nssv_CPP11_OR_GREATER && nssv_COMPILER_MSVC_VERSION != 140 - -template > -std::basic_string to_string(basic_string_view v, - Allocator const &a = Allocator()) { - return std::basic_string(v.begin(), v.end(), a); -} - -#else - -template std::basic_string to_string(basic_string_view v) { - return std::basic_string(v.begin(), v.end()); -} - -template -std::basic_string to_string(basic_string_view v, Allocator const &a) { - return std::basic_string(v.begin(), v.end(), a); -} - -#endif // nssv_CPP11_OR_GREATER - -template -basic_string_view to_string_view(std::basic_string const &s) { - return basic_string_view(s.data(), s.size()); -} - -} // namespace sv_lite -} // namespace nonstd - -#endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS - -// -// make types and algorithms available in namespace nonstd: -// - -namespace nonstd { - -using sv_lite::basic_string_view; -using sv_lite::string_view; -using sv_lite::wstring_view; - -#if nssv_HAVE_WCHAR16_T -using sv_lite::u16string_view; -#endif -#if nssv_HAVE_WCHAR32_T -using sv_lite::u32string_view; -#endif - -// literal "sv" - -using sv_lite::operator==; -using sv_lite::operator!=; -using sv_lite::operator<; -using sv_lite::operator<=; -using sv_lite::operator>; -using sv_lite::operator>=; - -using sv_lite::operator<<; - -#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS -using sv_lite::to_string; -using sv_lite::to_string_view; -#endif - -} // namespace nonstd - -// 24.4.5 Hash support (C++11): - -// Note: The hash value of a string view object is equal to the hash value of -// the corresponding string object. - -#if nssv_HAVE_STD_HASH - -#include - -namespace std { - -template <> struct hash { -public: - std::size_t operator()(nonstd::string_view v) const nssv_noexcept { - return std::hash()(std::string(v.data(), v.size())); - } -}; - -template <> struct hash { -public: - std::size_t operator()(nonstd::wstring_view v) const nssv_noexcept { - return std::hash()(std::wstring(v.data(), v.size())); - } -}; - -template <> struct hash { -public: - std::size_t operator()(nonstd::u16string_view v) const nssv_noexcept { - return std::hash()(std::u16string(v.data(), v.size())); - } -}; - -template <> struct hash { -public: - std::size_t operator()(nonstd::u32string_view v) const nssv_noexcept { - return std::hash()(std::u32string(v.data(), v.size())); - } -}; - -} // namespace std - -#endif // nssv_HAVE_STD_HASH - -nssv_RESTORE_WARNINGS() - -#endif // nssv_HAVE_STD_STRING_VIEW -#endif // NONSTD_SV_LITE_H_INCLUDED diff --git a/isis/src/base/apps/topds4/template2.h b/isis/src/base/apps/topds4/template2.h deleted file mode 100644 index 6c7078ac04..0000000000 --- a/isis/src/base/apps/topds4/template2.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2019 Pantor. All rights reserved. - -#ifndef INCLUDE_INJA_TEMPLATE_HPP_ -#define INCLUDE_INJA_TEMPLATE_HPP_ - -#include -#include -#include -#include - -#include "node.h" -#include "statistics2.h" - - -namespace inja { - -/*! - * \brief The main inja Template. - */ -struct Template { - BlockNode root; - std::string content; - - explicit Template() { } - explicit Template(const std::string& content): content(content) { } - - /// Return number of variables (total number, not distinct ones) in the template - int count_variables() { - auto statistic_visitor = StatisticsVisitor(); - root.accept(statistic_visitor); - return statistic_visitor.variable_counter; - } -}; - -using TemplateStorage = std::map; - -} // namespace inja - -#endif // INCLUDE_INJA_TEMPLATE_HPP_ diff --git a/isis/src/base/apps/topds4/token.h b/isis/src/base/apps/topds4/token.h deleted file mode 100644 index 72b899f7d8..0000000000 --- a/isis/src/base/apps/topds4/token.h +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) 2020 Pantor. All rights reserved. - -#ifndef INCLUDE_INJA_TOKEN_HPP_ -#define INCLUDE_INJA_TOKEN_HPP_ - -#include - -#include "string_view.h" - -namespace inja { - -/*! - * \brief Helper-class for the inja Lexer. - */ -struct Token { - enum class Kind { - Text, - ExpressionOpen, // {{ - ExpressionClose, // }} - LineStatementOpen, // ## - LineStatementClose, // \n - StatementOpen, // {% - StatementClose, // %} - CommentOpen, // {# - CommentClose, // #} - Id, // this, this.foo - Number, // 1, 2, -1, 5.2, -5.3 - String, // "this" - Plus, // + - Minus, // - - Times, // * - Slash, // / - Percent, // % - Power, // ^ - Comma, // , - Dot, // . - Colon, // : - LeftParen, // ( - RightParen, // ) - LeftBracket, // [ - RightBracket, // ] - LeftBrace, // { - RightBrace, // } - Equal, // == - NotEqual, // != - GreaterThan, // > - GreaterEqual, // >= - LessThan, // < - LessEqual, // <= - Unknown, - Eof, - }; - - Kind kind {Kind::Unknown}; - nonstd::string_view text; - - explicit constexpr Token() = default; - explicit constexpr Token(Kind kind, nonstd::string_view text) : kind(kind), text(text) {} - - std::string describe() const { - switch (kind) { - case Kind::Text: - return ""; - case Kind::LineStatementClose: - return ""; - case Kind::Eof: - return ""; - default: - return static_cast(text); - } - } -}; - -} // namespace inja - -#endif // INCLUDE_INJA_TOKEN_HPP_ diff --git a/isis/src/base/apps/topds4/topds4.cpp b/isis/src/base/apps/topds4/topds4.cpp index 13d09d8b70..5df98c9437 100644 --- a/isis/src/base/apps/topds4/topds4.cpp +++ b/isis/src/base/apps/topds4/topds4.cpp @@ -1,9 +1,14 @@ +#include #include #include #include "topds4.h" +#include "FileName.h" +#include "OriginalLabel.h" +#include "PvlToJSON.h" + using namespace std; using namespace inja; using json = nlohmann::json; @@ -20,27 +25,55 @@ namespace Isis { } - PvlGroup topds4(Cube *cube, UserInterface &ui) { - Process p; - p.SetInputCube(cube); - - Pvl &label = *cube->label(); - + PvlGroup topds4(Cube *icube, UserInterface &ui) { - - json data; - data["IsisCube"]["Core"]["Dimensions"]["Samples"] = "1023"; - data["IsisCube"]["Core"]["Dimensions"]["Lines"] = "1024"; - data["name"] = "world"; - render("Hello {{ name }}!", data); // Returns std::string "Hello world!" - render_to(std::cout, "Hello {{ name }}!", data); // Writes "Hello world!" to stream + Process p; + p.SetInputCube(icube); + + json dataSource; + + // We will need the main label even if it is not used as a template data source + Pvl &cubeLabel = *icube->label(); + + // Add the input cube PVL label to template engine data + // *** TODO: make sure this label is inside a unique JSON element so nothing in it can + // conflict with other data sources + if (ui.GetBoolean("MAINLABEL")) { + dataSource["MainLabel"].update(pvlToJSON(cubeLabel)); + } + + // Add the original label (from an ingestion app) to the template engine data + // Wrap it in an OriginalLabel so existing elements don't get overwritten + // *** TODO: make sure this label is inside a unique JSON element so nothing in it can + // conflict with other data sources + if (ui.GetBoolean("ORIGINALLABEL")) { + if (cubeLabel.hasObject("OriginalLabel")) { + OriginalLabel origBlob; + icube->read(origBlob); + Pvl origLabel; + origLabel = origBlob.ReturnLabels(); + dataSource["OriginalLabel"].update(pvlToJSON(origLabel)); + } + else if (cubeLabel.hasObject("OriginalXmlLabel")) { + // get the xml label and add it to the template data + } + } + + std::cout << "=================================" << std::endl; + std::cout << dataSource.dump(4) << std::endl; + std::cout << "=================================" << std::endl; Environment env; std::string inputTemplate = ui.GetFileName("TEMPLATE").toStdString(); - std::string result = env.render_file(inputTemplate, data); + std::string result = env.render_file(inputTemplate, dataSource); + + std::ofstream outFile(ui.GetFileName("TO").toStdString()); + outFile << result; + outFile.close(); + std::cout << result << std::endl; - //std::cout << label << std::endl; - return label.findGroup("Dimensions", Pvl::Traverse); + // TODO: return something useful + return cubeLabel.findGroup("Dimensions", Pvl::Traverse); } } diff --git a/isis/src/base/apps/topds4/topds4.xml b/isis/src/base/apps/topds4/topds4.xml index 656f30ad84..a14f2f2a3c 100644 --- a/isis/src/base/apps/topds4/topds4.xml +++ b/isis/src/base/apps/topds4/topds4.xml @@ -28,7 +28,7 @@ Input cube
- This is the input cube that will be cropped. + This is the input cube that will be cropped. *.cub @@ -57,5 +57,33 @@
+ + + + boolean + true + + Use the main cube label as input data + + + Use the main input cube label as a source of data to replace template elements with. + + + + + boolean + false + + Use the original label as input data + + + Use the original label attached to the input cube as a source of data + to replace template elements with. This is the label that was saved when + the cube was first ingested into ISIS as a cube. + + + + + diff --git a/isis/src/base/apps/topds4/utils.h b/isis/src/base/apps/topds4/utils.h deleted file mode 100644 index 458f8c86a2..0000000000 --- a/isis/src/base/apps/topds4/utils.h +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) 2020 Pantor. All rights reserved. - -#ifndef INCLUDE_INJA_UTILS_HPP_ -#define INCLUDE_INJA_UTILS_HPP_ - -#include -#include -#include -#include - -#include "exceptions.h" -#include "string_view.h" - -namespace inja { - -inline void open_file_or_throw(const std::string &path, std::ifstream &file) { - file.exceptions(std::ifstream::failbit | std::ifstream::badbit); - try { - file.open(path); - } catch (const std::ios_base::failure & /*e*/) { - throw FileError("failed accessing file at '" + path + "'"); - } -} - -namespace string_view { -inline nonstd::string_view slice(nonstd::string_view view, size_t start, size_t end) { - start = std::min(start, view.size()); - end = std::min(std::max(start, end), view.size()); - return view.substr(start, end - start); -} - -inline std::pair split(nonstd::string_view view, char Separator) { - size_t idx = view.find(Separator); - if (idx == nonstd::string_view::npos) { - return std::make_pair(view, nonstd::string_view()); - } - return std::make_pair(slice(view, 0, idx), slice(view, idx + 1, nonstd::string_view::npos)); -} - -inline bool starts_with(nonstd::string_view view, nonstd::string_view prefix) { - return (view.size() >= prefix.size() && view.compare(0, prefix.size(), prefix) == 0); -} -} // namespace string_view - -inline SourceLocation get_source_location(nonstd::string_view content, size_t pos) { - // Get line and offset position (starts at 1:1) - auto sliced = string_view::slice(content, 0, pos); - std::size_t last_newline = sliced.rfind("\n"); - - if (last_newline == nonstd::string_view::npos) { - return {1, sliced.length() + 1}; - } - - // Count newlines - size_t count_lines = 0; - size_t search_start = 0; - while (search_start <= sliced.size()) { - search_start = sliced.find("\n", search_start) + 1; - if (search_start == 0) { - break; - } - count_lines += 1; - } - - return {count_lines + 1, sliced.length() - last_newline}; -} - -} // namespace inja - -#endif // INCLUDE_INJA_UTILS_HPP_ From b4f07c95e0607770410ae20dad6c2fb2767eb115 Mon Sep 17 00:00:00 2001 From: Stuart Sides Date: Mon, 4 Jan 2021 10:59:27 -0700 Subject: [PATCH 08/28] Add full orex pds4 template (#4233) * now has some real parameters * removed inja files * full orex xml * add full orex template --- .../20190612T090019S776_map_L0pan.xml.full | 453 ++++++++++++++++++ 1 file changed, 453 insertions(+) create mode 100644 isis/src/base/apps/topds4/20190612T090019S776_map_L0pan.xml.full diff --git a/isis/src/base/apps/topds4/20190612T090019S776_map_L0pan.xml.full b/isis/src/base/apps/topds4/20190612T090019S776_map_L0pan.xml.full new file mode 100644 index 0000000000..c9eea369c0 --- /dev/null +++ b/isis/src/base/apps/topds4/20190612T090019S776_map_L0pan.xml.full @@ -0,0 +1,453 @@ + + + + + + + + urn:nasa:pds:orex.ocams:data_raw:20190612t090019s776_map_l0pan.fits + 1.0 + OSIRIS-REx OCAMS Level 0 (Raw) Science Image Data Product 2019-06-12T09:00:19.776Z + 1.7.0.0 + Product_Observational + + + 2019-02-01 + 1.0 + Initial version. + + + + + + 2019-06-12T09:00:19.776Z + 2019-06-12T09:00:19.776Z + + + Navigation + Raw + + + OSIRIS-REx + Mission + + urn:nasa:pds:context:investigation:mission.orex + data_to_investigation + + + + OSIRIS-REx Camera Suite (OCAMS) + + OSIRIS-REx + Spacecraft + Origins, Spectral Interpretation, Resource Identification, Security - Regolith Explorer Spacecraft + + urn:nasa:pds:context:instrument_host:spacecraft.orex + is_instrument_host + + + + OCAMS + Instrument + + urn:nasa:pds:context:instrument:ocams.orex + is_instrument + + + + + (101955) BENNU + 1999 RQ36 + Asteroid + + urn:nasa:pds:context:target:asteroid.101955_bennu + data_to_target + + + + + 1 + 1 + 0 + 1 + 0 + 0 + 0 + 3 + 1 + 1 + 238 + 255 + 237 + 255 + 60 + 14 + 96 + 120 + 842 + 84.24651416662068 + 719 + 43.8526184712785 + 1059 + 429.230742563867 + 46 + -1.1373317099755598 + 4 + -0.5355396 + 3 + -0.27067044 + 4 + 0.124204 + 248 + 13 + 72 + 1870 + 7.54142128487916 + 4.5 + 1 + PAN + 14 + 0 + 0 + 8 + 1871 + 7.56243576238131 + 3 + 0.003663 + 1 + 5 + 0 + 0 + 1674 + 12.059041040441798 + 97 + 0 + 751 + -23.390592459093 + 0 + 0 + 3232 + -26.7914936257828 + 3165 + -24.332170096750897 + 3370 + -32.754124351166396 + 0 + 0 + 3138 + -23.3990822840391 + 0 + 1872 + 7.54040302308914 + 270 + 0 + 13 + 97 + 97 + 0 + 807 + -24.7333747890882 + 0 + 0 + 3205 + -25.7736252190564 + 3213 + -26.071207848051596 + 3267 + -28.172561670290598 + 3726 + 0 + 0 + 3152 + -23.8791462673093 + 0 + 0 + 3725 + 0 + 0 + 0 + 0 + L13H08 + 1 + 0 + 844 + -18.835592356994898 + 0 + 0 + 3262 + -27.970663212435397 + 3257 + -27.7703534433846 + 3228 + -26.638223638577198 + 0 + 0 + 3145 + -23.638126058871002 + 2 + -64364 + 613601975 + 1 + 0 + 8217 + 5775 + 60 + 16 + 4 + 1551 + 4.5450550530000005 + 1551 + 4.5450550530000005 + 1781 + -12.115658567999999 + 1642 + -24.05860042 + 2146 + 11.97837112 + 1785 + 23.97435285 + 2026 + 4.947496052 + 3715 + 31.99133865 + 2049 + 5.003662098 + R13H08 + + + 2019-06-12T09:00:19.776Z + 0.0026426375 + 0.005285275 + 5.285275 + 2019-06-12T14:32:51.302Z + 5 + 2019-06-12T09:00:19.778Z + 613602088.962634 + 3/0613601975.37847 + + + 3549 + + + LIGHT + + + RECONSTRUCT + spoc-digest-2019-06-17T00_48_23.231Z.mk + 0 + 0 + 0 + PASS + + + 78 + + + 282.469051278191 + -65.8502362992963 + -316.966816351563 + -321.997774969665 + 296.28478659027 + BEST: SPK + BENNU + -46.0083163134579 + -40.9783428662411 + -0.00051406896314878 + 0.00385569171518446 + 0.00385569171518446 + 0.00051406896314878 + RA---TAN + DEC--TAN + deg + deg + 2000 + BENNU + ICRS + -12.2640686780359 + 192379784.023255 + 0.00196952834966507 + 0.977178977312025 + 0.0000335902873759028 + -0.21241761972849 + 192379784.650832 + SUCCESS + BENNU + 165861882.428622 + 85156422.1781311 + 47415200.7414764 + + + control.csv + SPOC-b94243cbd36c20997d72136d48d6a44f08455dba + FLIGHT + bottom-left + 2020-01-17T19:16:15.652Z + 15575 + 353 + b94243cbd36c20997d72136d48d6a44f08455dba + DN + + + orb + Orbit B + + + + + + Active Area + display_settings_to_array + + + Sample + Left to Right + Line + Bottom to Top + + + + + Active Array and Extended Pixel Regions + display_settings_to_array + + + Sample + Left to Right + Line + Bottom to Top + + + + + + MK + spoc-digest-2019-06-17T00_48_23.231Z.mk + Reconstructed + + + + + Sample + Left to Right + Line + Bottom to Top + + + + 512.5 + 512.5 + + 298.920801462901 + -64.4854892107989 + 82.4056963199464 + + J2000 + + + + 0.209125099096568 + 0.959028055505082 + 0.178608607528731 + 0.0680503269546892 + + J2000 + + + Spacecraft + + + + 0.209704337661567 + 0.958472331073572 + 0.180468563530547 + 0.0691807697957836 + + J2000 + + + Instrument + + + + + 2019-06-12T09:00:19.778Z + 613602088.962634 + + + + + + + 20190612T090019S776_map_L0pan.fits + 2020-01-17T19:16:18.256Z + 4443840 + +
+ 0 + 17280 + FITS 3.0 +
+ + Active Area + 17280 + 2 + Last Index Fastest + OCAMS image 1024 by 1024 pixel active array. + + UnsignedMSB2 + DN + 1 + 32768 + + + Line + 1024 + 1 + + + Sample + 1024 + 2 + + +
+ 2116800 + 2880 + FITS 3.0 +
+ + Active Array and Extended Pixel Regions + 2119680 + 2 + Last Index Fastest + OCAMS 1044 sample by 1112 line extended pixel array. The extended pixel array contains the detector overscan and dark pixels. + + UnsignedMSB2 + DN + 1 + 32768 + + + Line + {{MainLabel.IsisCube.Core.Dimensions.Lines.Value}} + 1 + + + Sample + 1112{{MainLabel.IsisCube.Core.Dimensions.Samples.Value}} + 2 + + +
+
From 6ca277d447c1f246fad520d476727be498e5f20e Mon Sep 17 00:00:00 2001 From: Jesse Mapel Date: Mon, 4 Jan 2021 11:19:24 -0700 Subject: [PATCH 09/28] Added callbacks and tests (#4226) --- isis/src/base/apps/topds4/topds4.cpp | 42 ++++++++++++-- isis/src/base/apps/topds4/topds4.xml | 2 +- isis/tests/FunctionalTestsTopds4.cpp | 85 ++++++++++++++++++++++++++++ 3 files changed, 122 insertions(+), 7 deletions(-) create mode 100644 isis/tests/FunctionalTestsTopds4.cpp diff --git a/isis/src/base/apps/topds4/topds4.cpp b/isis/src/base/apps/topds4/topds4.cpp index 5df98c9437..2bf356ab5e 100644 --- a/isis/src/base/apps/topds4/topds4.cpp +++ b/isis/src/base/apps/topds4/topds4.cpp @@ -1,8 +1,11 @@ #include #include +#include #include +#include "md5wrapper.h" + #include "topds4.h" #include "FileName.h" @@ -24,7 +27,6 @@ namespace Isis { return topds4(icube, ui); } - PvlGroup topds4(Cube *icube, UserInterface &ui) { Process p; @@ -36,15 +38,15 @@ namespace Isis { Pvl &cubeLabel = *icube->label(); // Add the input cube PVL label to template engine data - // *** TODO: make sure this label is inside a unique JSON element so nothing in it can + // *** TODO: make sure this label is inside a unique JSON element so nothing in it can // conflict with other data sources if (ui.GetBoolean("MAINLABEL")) { dataSource["MainLabel"].update(pvlToJSON(cubeLabel)); } - + // Add the original label (from an ingestion app) to the template engine data // Wrap it in an OriginalLabel so existing elements don't get overwritten - // *** TODO: make sure this label is inside a unique JSON element so nothing in it can + // *** TODO: make sure this label is inside a unique JSON element so nothing in it can // conflict with other data sources if (ui.GetBoolean("ORIGINALLABEL")) { if (cubeLabel.hasObject("OriginalLabel")) { @@ -58,13 +60,41 @@ namespace Isis { // get the xml label and add it to the template data } } - + std::cout << "=================================" << std::endl; std::cout << dataSource.dump(4) << std::endl; std::cout << "=================================" << std::endl; - Environment env; std::string inputTemplate = ui.GetFileName("TEMPLATE").toStdString(); + + Environment env; + // Call back functions + /** + * Renders to the current UTC time formatted as YYYY-MM-DDTHH:MM:SS + */ + env.add_callback("currentTime", 0, [](Arguments& args) { + time_t startTime = time(NULL); + struct tm *tmbuf = gmtime(&startTime); + char timestr[80]; + strftime(timestr, 80, "%Y-%m-%dT%H:%M:%S", tmbuf); + return string(timestr); + }); + + /** + * Renders to the filename of the output img + */ + env.add_callback("imageFileName", 0, [icube](Arguments& args) { + QString cubeFilename = icube->fileName().split("/").back(); + return (cubeFilename.split(".")[0] + ".img").toStdString(); + }); + + /** + * Renders to the MD5 hash for the input cube + */ + env.add_callback("md5Hash", 0, [icube](Arguments& args) { + md5wrapper md5; + return md5.getHashFromFile(icube->fileName()).toStdString(); + }); std::string result = env.render_file(inputTemplate, dataSource); std::ofstream outFile(ui.GetFileName("TO").toStdString()); diff --git a/isis/src/base/apps/topds4/topds4.xml b/isis/src/base/apps/topds4/topds4.xml index a14f2f2a3c..3ef1366bfe 100644 --- a/isis/src/base/apps/topds4/topds4.xml +++ b/isis/src/base/apps/topds4/topds4.xml @@ -2,7 +2,7 @@ - dummy + dummy diff --git a/isis/tests/FunctionalTestsTopds4.cpp b/isis/tests/FunctionalTestsTopds4.cpp new file mode 100644 index 0000000000..5b615f6b57 --- /dev/null +++ b/isis/tests/FunctionalTestsTopds4.cpp @@ -0,0 +1,85 @@ +#include +#include + +#include +#include + +#include "Fixtures.h" +#include "md5wrapper.h" + +#include "topds4.h" + +#include "gmock/gmock.h" + +using namespace Isis; + +static QString APP_XML = FileName("$ISISROOT/bin/xml/topds4.xml").expanded(); + +TEST_F(SmallCube, FunctionalTestTopds4CurrentTime) { + QString templateFile = tempDir.path()+"/current_time.tpl"; + QString renderedFile = tempDir.path()+"/current_time.txt"; + std::ofstream of; + of.open(templateFile.toStdString()); + of << "{{currentTime()}}"; + of.close(); + QVector args = {"template=" + templateFile, "to=" + renderedFile}; + UserInterface options(APP_XML, args); + + topds4(testCube, options); + + std::ifstream renderedStream; + renderedStream.open(renderedFile.toStdString()); + std::string line; + std::getline(renderedStream, line); + time_t startTime = time(NULL); + struct tm *tmbuf = gmtime(&startTime); + char yearstr[80]; + // Just check that the year is correct + strftime(yearstr, 80, "%Y", tmbuf); + EXPECT_EQ(yearstr, line.substr(0, 4)); + // Check that the rest is the correct format + // YYYY-MM-DDTHH:MM:SS + QRegExp timeRegex("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}"); + EXPECT_TRUE(QString::fromStdString(line).contains(timeRegex)) << "String [" << line << "] " + << "does not match the time format " + << "[YYYY-MM-DDTHH:MM:SS]."; +} + +TEST_F(SmallCube, FunctionalTestTopds4ImageFileName) { + QString templateFile = tempDir.path()+"/current_time.tpl"; + QString renderedFile = tempDir.path()+"/current_time.txt"; + std::ofstream of; + of.open(templateFile.toStdString()); + of << "{{imageFileName()}}"; + of.close(); + QVector args = {"template=" + templateFile, "to=" + renderedFile}; + UserInterface options(APP_XML, args); + + topds4(testCube, options); + + std::ifstream renderedStream; + renderedStream.open(renderedFile.toStdString()); + std::string line; + std::getline(renderedStream, line); + EXPECT_EQ("small.img", line); +} + +TEST_F(SmallCube, FunctionalTestTopds4MD5Hash) { + QString templateFile = tempDir.path()+"/current_time.tpl"; + QString renderedFile = tempDir.path()+"/current_time.txt"; + std::ofstream of; + of.open(templateFile.toStdString()); + of << "{{md5Hash()}}"; + of.close(); + QVector args = {"template=" + templateFile, "to=" + renderedFile}; + UserInterface options(APP_XML, args); + + topds4(testCube, options); + + std::ifstream renderedStream; + renderedStream.open(renderedFile.toStdString()); + std::string line; + std::getline(renderedStream, line); + md5wrapper md5; + EXPECT_EQ(md5.getHashFromFile(testCube->fileName()).toStdString(), line); +} From 132c36660d9f77eb2c566ce61b7270df7b08e5e5 Mon Sep 17 00:00:00 2001 From: Kristin Berry Date: Mon, 4 Jan 2021 12:34:27 -0700 Subject: [PATCH 10/28] Adds Xml2Json capability to topds4 branch (#4228) * Inintal topds4 app and cmake updates to include inja * Delete extra headers * Fixed inja include * Added basic working XMLToJson functionality * Updated to work with many nested levels of different amounts of repeated tags * Add support for unsupported xml edge cases * Add tests that work to test mutliple nested repeated values conversion * cleanup * Update to include no-text value cases to work in repeated-value cases * Update test * Break up tests * Fix typoes in comments --- isis/CMakeLists.txt | 1 + isis/src/base/objs/XmlToJson/Makefile | 7 + isis/src/base/objs/XmlToJson/XmlToJson.cpp | 201 ++++++++++++++++ isis/src/base/objs/XmlToJson/XmlToJson.h | 20 ++ isis/tests/XmlToJsonTests.cpp | 257 +++++++++++++++++++++ 5 files changed, 486 insertions(+) create mode 100644 isis/src/base/objs/XmlToJson/Makefile create mode 100644 isis/src/base/objs/XmlToJson/XmlToJson.cpp create mode 100644 isis/src/base/objs/XmlToJson/XmlToJson.h create mode 100644 isis/tests/XmlToJsonTests.cpp diff --git a/isis/CMakeLists.txt b/isis/CMakeLists.txt index cfc7f89a3b..7eb5d5d583 100644 --- a/isis/CMakeLists.txt +++ b/isis/CMakeLists.txt @@ -296,6 +296,7 @@ find_package(Threads) find_package(inja REQUIRED) + # In this case, we specify the version numbers being searched for in the non-traditional installs. if(APPLE) find_package(OpenGL REQUIRED) diff --git a/isis/src/base/objs/XmlToJson/Makefile b/isis/src/base/objs/XmlToJson/Makefile new file mode 100644 index 0000000000..f122bc8822 --- /dev/null +++ b/isis/src/base/objs/XmlToJson/Makefile @@ -0,0 +1,7 @@ +ifeq ($(ISISROOT), $(BLANK)) +.SILENT: +error: + echo "Please set ISISROOT"; +else + include $(ISISROOT)/make/isismake.objs +endif \ No newline at end of file diff --git a/isis/src/base/objs/XmlToJson/XmlToJson.cpp b/isis/src/base/objs/XmlToJson/XmlToJson.cpp new file mode 100644 index 0000000000..02b613ae48 --- /dev/null +++ b/isis/src/base/objs/XmlToJson/XmlToJson.cpp @@ -0,0 +1,201 @@ +/** This is free and unencumbered software released into the public domain. +The authors of ISIS do not claim copyright on the contents of this file. +For more details about the LICENSE terms and the AUTHORS, you will +find files of those names at the top level of this repository. **/ + +/* SPDX-License-Identifier: CC0-1.0 */ + +#include "XmlToJson.h" +#include "IException.h" + +#include +#include + +#include +#include +#include + +using json = nlohmann::json; + +namespace Isis { + json convertLastChildNodeToJson(QDomElement& element); + json convertXmlToJson(QDomElement& element, json& output); + + /** + * Converts an XML file to a json object. + * + * Please see other functions for details about how XML elements are + * converted to corresponding json elements. + * + * @param xmlFile Path to an XML file. + * + * @return json The xml file converted to a json object. + */ + json xmlToJson(QString xmlFile) { + QDomDocument doc("xmlInput"); + QFile file(xmlFile); + + if (!file.open(QIODevice::ReadOnly)) { + QString message = QString("Failed to open file for XML Input: [%1]").arg(xmlFile); + throw IException(IException::Io, message, _FILEINFO_); + } + + if (!doc.setContent(&file)) { + file.close(); + QString message = QString("Failed to use file for XML Input: [%1]").arg(xmlFile); + throw IException(IException::Io, message, _FILEINFO_); + } + + file.close(); + + return xmlToJson(doc); + } + + + /** + * Converts an XML document stored in a QDomDocument into a JSON + * object. + * + * @param doc A QDomDocument with an XML file loaded into it. + * + * @return json The XMl file converted to a json object. + */ + json xmlToJson(QDomDocument& doc) { + QDomElement docElem = doc.documentElement(); + json output; + return convertXmlToJson(docElem, output); + } + + + /** + * Not intended to be used directly. Converts a QDomElement to JSON + * and returns. Only called when a QDomElement + * has no further child nodes + * + * Used for the following situations: + * + * XML: value + * JSON: {tag: value} + * + * XML: textValue + * JSON: {tag: {@attributeName: "attributeValue, "#text":textValue } } + * + * XML: + * JSON: {tag: {@attributeName: "attributeValue"} } + * + * XML: + * JSON: tag: null + * + * @param element A QDomElement to be converted to JSON and added to the JSON object. + */ + json convertLastChildNodeToJson(QDomElement& element){ + json newJson; + if (element.hasAttributes()) { + // If there are attributes, add them + // textValue + json attributeSection; + QDomNamedNodeMap attrMap = element.attributes(); + for (int i=0; i < attrMap.size(); i++) { + QDomAttr attr = attrMap.item(i).toAttr(); + attributeSection["@"+attr.name().toStdString()] = attr.value().toStdString(); + } + // If there is no textValue, don't include it + // + if (!element.text().isEmpty()) { + attributeSection["#text"] = element.text().toStdString(); + } + newJson[element.tagName().toStdString()] = attributeSection; + } + else { + // Just add element and its value + // value + if (!element.text().isEmpty()) { + newJson[element.tagName().toStdString()] = element.text().toStdString(); + } + else { + // no value case + newJson[element.tagName().toStdString()]; + } + } + return newJson; + } + + + /** + * Not intended to be used directly. Intended to be used by xmlToJson to convert + * an input XML document to JSON. + * + * This function does the following conversions: + * + * XML: val1val2 + * JSON: a : {b: val1, c: val2} + * + * XML: value1 value2 + * JSON: a: [ {first:value1, second:value2} ] + * + * XML: val1val2 + * JSON: a:[val1, val2] + * + * @param element A QDomElement representing the whole or some subset of a QDomDocument + * @param output A JSON object constructed from XML input. + * + * @return json + */ + json convertXmlToJson(QDomElement& element, json& output) { + while (!element.isNull()) { + QDomElement next = element.firstChildElement(); + if (next.isNull()){ + json converted = convertLastChildNodeToJson(element); + // Simple case with no repeated tags at the same level + if (!output.contains(element.tagName().toStdString())){ + output.update(converted); + } + else { + // There is a repeated tag at the same level in the XML, i.e: val1val2 + // Translated json goal: a:[val1, val2] + // If the converted json has an array already, append, else make it an array + if (!output[element.tagName().toStdString()].is_array()) { + output[element.tagName().toStdString()] = {output[element.tagName().toStdString()]}; + } + output[element.tagName().toStdString()].push_back(converted[element.tagName().toStdString()]); + } + } + else { + // If there is already an element with this tag name at any level besides the same one, + // add it to a list rather than + // overwriting. This is the following situation: + // XML: value1 value2 + // JSON: a: [ {first:value1, second:value2} ] + if (output.contains(element.tagName().toStdString())) { + // If it's an array already, append, else make it an array + json temporaryJson; + convertXmlToJson(next, temporaryJson); + if (!output[element.tagName().toStdString()].is_array()) { + output[element.tagName().toStdString()] = {output[element.tagName().toStdString()]}; + } + output[element.tagName().toStdString()].push_back(temporaryJson); + } + else { + if (element.hasAttributes()) { + json tempArea; + QDomNamedNodeMap attrMap = element.attributes(); + for (int j=0; j < attrMap.size(); j++) { + QDomAttr attr = attrMap.item(j).toAttr(); + tempArea["@"+attr.name().toStdString()] = attr.value().toStdString(); + } + tempArea.update( + convertXmlToJson(next, output[element.tagName().toStdString()])); + output[element.tagName().toStdString()] = tempArea; + } + else { + output[element.tagName().toStdString()] = + convertXmlToJson(next, output[element.tagName().toStdString()]); + } + } + } + element = element.nextSiblingElement(); + } + return output; + } +} + diff --git a/isis/src/base/objs/XmlToJson/XmlToJson.h b/isis/src/base/objs/XmlToJson/XmlToJson.h new file mode 100644 index 0000000000..d3ebc681f0 --- /dev/null +++ b/isis/src/base/objs/XmlToJson/XmlToJson.h @@ -0,0 +1,20 @@ +#ifndef XmlToJson_h +#define XmlToJson_h + +/** This is free and unencumbered software released into the public domain. +The authors of ISIS do not claim copyright on the contents of this file. +For more details about the LICENSE terms and the AUTHORS, you will +find files of those names at the top level of this repository. **/ + +/* SPDX-License-Identifier: CC0-1.0 */ +#include "QDomElement" +#include "QString" + +#include + +namespace Isis { + nlohmann::json xmlToJson(QString xmlFile); + nlohmann::json xmlToJson(QDomDocument& doc); +} + +#endif diff --git a/isis/tests/XmlToJsonTests.cpp b/isis/tests/XmlToJsonTests.cpp new file mode 100644 index 0000000000..18dbf4975a --- /dev/null +++ b/isis/tests/XmlToJsonTests.cpp @@ -0,0 +1,257 @@ +#include + +#include "XmlToJson.h" + +#include "gmock/gmock.h" + +using json = nlohmann::json; +using namespace Isis; + +// XML: value +// JSON: {tag: value} +TEST(XmlToJson, XmlNoAttributeWithTextValue) { + QString xmlInput = R"( + textValue + )"; + + QDomDocument xmlDocument("TestDocument"); + xmlDocument.setContent(xmlInput); + json result = xmlToJson(xmlDocument); + + EXPECT_EQ(result["TagWithoutAttribute"], "textValue"); +} + +// XML: textValue +// JSON: {tag: {@attributeName: "attributeValue, "#text":textValue } } +TEST(XmlToJson, XmlAttributeWithTextValue) { + QString xmlInput = R"( + textValue + )"; + + QDomDocument xmlDocument("TestDocument"); + xmlDocument.setContent(xmlInput); + json result = xmlToJson(xmlDocument); + + EXPECT_EQ(result["Tag"]["TagWithAttribute"]["@attribute"], "attributeValue"); + EXPECT_EQ(result["Tag"]["TagWithAttribute"]["#text"], "textValue"); +} + +// XML: +// JSON: {tag: {@attributeName: "attributeValue"} } +TEST(XmlToJson, XmlAttributeButNoText) { + QString xmlInput = R"( + > + )"; + + QDomDocument xmlDocument("TestDocument"); + xmlDocument.setContent(xmlInput); + json result = xmlToJson(xmlDocument); + + EXPECT_EQ(result["Tag"]["TagWithAttribute"]["@attribute"], "attributeValue"); + EXPECT_EQ(result["Tag"]["TagWithAttribute"]["#text"], nullptr); +} + +// XML: +// JSON: tag: null +TEST(XmlToJson, XmlNoTextValueNoAttribute) { + QString xmlInput = R"( + + )"; + + QDomDocument xmlDocument("TestDocument"); + xmlDocument.setContent(xmlInput); + json result = xmlToJson(xmlDocument); + + EXPECT_EQ(result["Tag"]["TagWithoutAnythingElse"], nullptr); +} + +// XML: valueotherValue +// JSON: {tag: {a:value, b:otherValue}} +TEST(XmlToJson, XmlNestedTags) { + QString xmlInput = R"( + + TagLevel2AValue + TagLevel2BValue + + + DeepValue + + + + )"; + + QDomDocument xmlDocument("TestDocument"); + xmlDocument.setContent(xmlInput); + json result = xmlToJson(xmlDocument); + + EXPECT_EQ(result["OuterTag"]["TagLevel1"]["TagLevel2A"], "TagLevel2AValue"); + EXPECT_EQ(result["OuterTag"]["TagLevel1"]["TagLevel2B"], "TagLevel2BValue"); + EXPECT_EQ(result["OuterTag"]["TagLevel1"]["TagLevel2C"]["TagLevel3"]["TagLevel4"], "DeepValue"); +} + +// XML: valueotherValue +// JSON: {tag: {a: [value, otherValue]}} +TEST(XmlToJson, TestRepeatedTagNoChildren){ + QString xmlInput = R"( + A1 + A2 + + textValue + + b1 + b2 + c1 + + A3 + )"; + + QDomDocument xmlDocument("TestDocument"); + xmlDocument.setContent(xmlInput); + json result = xmlToJson(xmlDocument); + + EXPECT_EQ(result["Tag"]["A"][0], "A1"); + EXPECT_EQ(result["Tag"]["A"][1], "A2"); + EXPECT_EQ(result["Tag"]["A"][2]["@attribute"], "value"); + EXPECT_EQ(result["Tag"]["A"][3]["@otherAttribute"], "otherValue"); + EXPECT_EQ(result["Tag"]["A"][3]["#text"], "textValue"); + EXPECT_EQ(result["Tag"]["A"][4]["B"][0], "b1"); + EXPECT_EQ(result["Tag"]["A"][4]["B"][1], "b2"); + EXPECT_EQ(result["Tag"]["A"][4]["C"], "c1"); + EXPECT_EQ(result["Tag"]["A"][5], "A3"); + EXPECT_EQ(result["Tag"]["A"][6], nullptr); +} + +// XML: valueotherValue +// JSON: {tag: { a: [{b: value}, {c: otherValue}]} } +TEST(XmlToJson, TestRepeatedTagWithChildren){ + QString xmlInput = R"( + value + otherValue + + )"; + + QDomDocument xmlDocument("TestDocument"); + xmlDocument.setContent(xmlInput); + json result = xmlToJson(xmlDocument); + + EXPECT_EQ(result["Tag"]["a"][0]["b"], "value"); + EXPECT_EQ(result["Tag"]["a"][1]["c"], "otherValue"); + EXPECT_EQ(result["Tag"]["a"][2]["justTag"], nullptr); +} + +// This tests that all the sub-pieces tested above work together in a single XMl document +TEST(XmlToJson, TestXMLEverythingTogether) { + QString xmlInput = R"( + + TagLevel2AValue + TagLevel2BValue + + + + + TagLevel4AValue + TagLevel4BValue + DeepValue + + + TagLevel2DValue + + + + A1 + A2 + + zoom + + b1 + b2 + notlist + + A3 + + 10 + TEN + notrepeated + + + 12 + 13 + + + 14 + 15 + + + ElementValue + + + + AlphaValue + BetaValue + + GammaValue + + + + + )"; + + QDomDocument xmlDocument("TestDocument"); + xmlDocument.setContent(xmlInput); + json result = xmlToJson(xmlDocument); + + // Test deeply nested value retrieval + EXPECT_EQ(result["TagLevel0"]["TagLevel1A"]["TagLevel2C"]["TagLevel3"]["TagLevel4C"]["TagLevel4D"]["TagLevel4E"], "DeepValue"); + + // Test attributes (uncomplicated) + EXPECT_EQ(result["TagLevel0"]["TagLevel1A"]["TagLevel2D"]["@attributeTag2D"], "Attribute value"); + EXPECT_EQ(result["TagLevel0"]["TagLevel1A"]["TagLevel2D"]["#text"], "TagLevel2DValue"); + + // Test no-text value cases and + EXPECT_EQ(result["TagLevel0"]["TagLevel1A"]["TagLevel2ExtraExtra"], nullptr); + EXPECT_EQ(result["TagLevel0"]["TagLevel1A"]["TagLevel2Extra"]["@attr"], "justAnAttribute"); + + // Test list creation for repeated tags at the same level + + // Case A: bcontents cContents JSON: a: [ {b: bcontents}, {c: cContents} ] + EXPECT_EQ(result["TagLevel0"]["TagLevel1B"]["First"][1]["tweleve"], "12"); + EXPECT_EQ(result["TagLevel0"]["TagLevel1B"]["First"][1]["thirteen"], "13"); + EXPECT_EQ(result["TagLevel0"]["TagLevel1B"]["First"][2]["fourteen"], "14"); + EXPECT_EQ(result["TagLevel0"]["TagLevel1B"]["First"][2]["fifteen"], "15"); + + // Case B: aContents1aContents2 JSON: z: {a: [aContents1, aContents2] } + // including lots of possible combinations + EXPECT_EQ(result["TagLevel0"]["TagLevel1B"]["First"][0]["A"][0], "A1"); + EXPECT_EQ(result["TagLevel0"]["TagLevel1B"]["First"][0]["A"][1], "A2"); + EXPECT_EQ(result["TagLevel0"]["TagLevel1B"]["First"][0]["A"][2]["@attribute"], "value"); + EXPECT_EQ(result["TagLevel0"]["TagLevel1B"]["First"][0]["A"][3]["@attr"], "val"); + EXPECT_EQ(result["TagLevel0"]["TagLevel1B"]["First"][0]["A"][3]["#text"], "zoom"); + EXPECT_EQ(result["TagLevel0"]["TagLevel1B"]["First"][0]["A"][5], "A3"); + EXPECT_EQ(result["TagLevel0"]["TagLevel1B"]["First"][0]["A"][6], nullptr); + + EXPECT_EQ(result["TagLevel0"]["TagLevel1B"]["First"][0]["oddball"], "notrepeated"); + EXPECT_EQ(result["TagLevel0"]["TagLevel1B"]["First"][0]["A"][4]["B"][0], "b1"); + EXPECT_EQ(result["TagLevel0"]["TagLevel1B"]["First"][0]["A"][4]["B"][1], "b2"); + EXPECT_EQ(result["TagLevel0"]["TagLevel1B"]["First"][0]["A"][4]["C"], "notlist"); + EXPECT_EQ(result["TagLevel0"]["TagLevel1B"]["First"][0]["ten"][0], "10"); + EXPECT_EQ(result["TagLevel0"]["TagLevel1B"]["First"][0]["ten"][1], "TEN"); + + // Test many attributes at one level + EXPECT_EQ(result["TagLevel0"]["TagLevel1B"]["Second"]["A"]["@attributeA"], "A"); + EXPECT_EQ(result["TagLevel0"]["TagLevel1B"]["Second"]["A"]["@attributeB"], "B"); + EXPECT_EQ(result["TagLevel0"]["TagLevel1B"]["Second"]["A"]["@attributeC"], "C"); + EXPECT_EQ(result["TagLevel0"]["TagLevel1B"]["Second"]["A"]["#text"], "ElementValue"); + + // Test multiple attributes at multiple levels + EXPECT_EQ(result["TagLevel0"]["TagLevel1B"]["Third"]["Greek"]["@otherattr"], "firstLetter"); + EXPECT_EQ(result["TagLevel0"]["TagLevel1B"]["Third"]["Greek"]["@attr"], "alphabet"); + EXPECT_EQ(result["TagLevel0"]["TagLevel1B"]["Third"]["Greek"]["Alpha"]["@attr2"], "attr2"); + EXPECT_EQ(result["TagLevel0"]["TagLevel1B"]["Third"]["Greek"]["Alpha"]["@attr1"], "attr1"); + EXPECT_EQ(result["TagLevel0"]["TagLevel1B"]["Third"]["Greek"]["Alpha"]["#text"], "AlphaValue"); + EXPECT_EQ(result["TagLevel0"]["TagLevel1B"]["Third"]["Greek"]["Beta"]["#text"], "BetaValue"); + EXPECT_EQ(result["TagLevel0"]["TagLevel1B"]["Third"]["Greek"]["Beta"]["@attrbeta2"], "beta2"); + EXPECT_EQ(result["TagLevel0"]["TagLevel1B"]["Third"]["Greek"]["AnotherLevel"]["Gamma"]["@attrgamma"], "gamma"); + EXPECT_EQ(result["TagLevel0"]["TagLevel1B"]["Third"]["Greek"]["AnotherLevel"]["Gamma"]["#text"], "GammaValue"); +} + + From cc2cbb79dc743311676989eb1755ba4febba63cd Mon Sep 17 00:00:00 2001 From: Jesse Mapel Date: Mon, 4 Jan 2021 22:22:09 -0700 Subject: [PATCH 11/28] Normalized topds4 and added tests (#4234) * Removed data source args and added app log * Added simple tests for topds4 * Moved line --- isis/src/base/apps/topds4/main.cpp | 17 ++++++- isis/src/base/apps/topds4/topds4.cpp | 48 ++++++++------------ isis/src/base/apps/topds4/topds4.h | 4 +- isis/src/base/apps/topds4/topds4.xml | 28 ------------ isis/tests/FunctionalTestsTopds4.cpp | 66 ++++++++++++++++++++++++++++ 5 files changed, 102 insertions(+), 61 deletions(-) diff --git a/isis/src/base/apps/topds4/main.cpp b/isis/src/base/apps/topds4/main.cpp index ae335f60f4..d82c68e63a 100644 --- a/isis/src/base/apps/topds4/main.cpp +++ b/isis/src/base/apps/topds4/main.cpp @@ -7,6 +7,19 @@ using namespace Isis; void IsisMain() { UserInterface &ui = Application::GetUserInterface(); - PvlGroup results = topds4(ui); - Application::Log(results); + Pvl appLog; + + try { + topds4(ui, &appLog); + } + catch (...) { + for (auto grpIt = appLog.beginGroup(); grpIt!= appLog.endGroup(); grpIt++) { + Application::Log(*grpIt); + } + throw; + } + + for (auto grpIt = appLog.beginGroup(); grpIt!= appLog.endGroup(); grpIt++) { + Application::Log(*grpIt); + } } diff --git a/isis/src/base/apps/topds4/topds4.cpp b/isis/src/base/apps/topds4/topds4.cpp index 2bf356ab5e..84b8abf217 100644 --- a/isis/src/base/apps/topds4/topds4.cpp +++ b/isis/src/base/apps/topds4/topds4.cpp @@ -19,53 +19,41 @@ using json = nlohmann::json; namespace Isis { - - - PvlGroup topds4(UserInterface &ui) { + void topds4(UserInterface &ui, Pvl *log) { Cube *icube = new Cube(); icube->open(ui.GetFileName("FROM")); - return topds4(icube, ui); + topds4(icube, ui); } - PvlGroup topds4(Cube *icube, UserInterface &ui) { + void topds4(Cube *icube, UserInterface &ui, Pvl *log) { Process p; p.SetInputCube(icube); json dataSource; - // We will need the main label even if it is not used as a template data source Pvl &cubeLabel = *icube->label(); // Add the input cube PVL label to template engine data - // *** TODO: make sure this label is inside a unique JSON element so nothing in it can - // conflict with other data sources - if (ui.GetBoolean("MAINLABEL")) { - dataSource["MainLabel"].update(pvlToJSON(cubeLabel)); - } + dataSource["MainLabel"].update(pvlToJSON(cubeLabel)); // Add the original label (from an ingestion app) to the template engine data // Wrap it in an OriginalLabel so existing elements don't get overwritten - // *** TODO: make sure this label is inside a unique JSON element so nothing in it can - // conflict with other data sources - if (ui.GetBoolean("ORIGINALLABEL")) { - if (cubeLabel.hasObject("OriginalLabel")) { - OriginalLabel origBlob; - icube->read(origBlob); - Pvl origLabel; - origLabel = origBlob.ReturnLabels(); - dataSource["OriginalLabel"].update(pvlToJSON(origLabel)); - } - else if (cubeLabel.hasObject("OriginalXmlLabel")) { - // get the xml label and add it to the template data - } + if (cubeLabel.hasObject("OriginalLabel")) { + OriginalLabel origBlob; + icube->read(origBlob); + Pvl origLabel; + origLabel = origBlob.ReturnLabels(); + dataSource["OriginalLabel"].update(pvlToJSON(origLabel)); + } + else if (cubeLabel.hasObject("OriginalXmlLabel")) { + // get the xml label and add it to the template data } - std::cout << "=================================" << std::endl; + std::cout << "=============Data================" << std::endl; std::cout << dataSource.dump(4) << std::endl; std::cout << "=================================" << std::endl; - std::string inputTemplate = ui.GetFileName("TEMPLATE").toStdString(); Environment env; // Call back functions @@ -95,15 +83,17 @@ namespace Isis { md5wrapper md5; return md5.getHashFromFile(icube->fileName()).toStdString(); }); + + std::string inputTemplate = ui.GetFileName("TEMPLATE").toStdString(); + std::string result = env.render_file(inputTemplate, dataSource); std::ofstream outFile(ui.GetFileName("TO").toStdString()); outFile << result; outFile.close(); + std::cout << "============Result===============" << std::endl; std::cout << result << std::endl; - - // TODO: return something useful - return cubeLabel.findGroup("Dimensions", Pvl::Traverse); + std::cout << "=================================" << std::endl; } } diff --git a/isis/src/base/apps/topds4/topds4.h b/isis/src/base/apps/topds4/topds4.h index 6b7d0ea09b..5551cce7a5 100644 --- a/isis/src/base/apps/topds4/topds4.h +++ b/isis/src/base/apps/topds4/topds4.h @@ -11,8 +11,8 @@ #include "UserInterface.h" namespace Isis { - extern PvlGroup topds4(Cube* cube, UserInterface &ui); - extern PvlGroup topds4(UserInterface &ui); + extern void topds4(Cube* cube, UserInterface &ui, Pvl *log=nullptr); + extern void topds4(UserInterface &ui, Pvl *log=nullptr); } #endif diff --git a/isis/src/base/apps/topds4/topds4.xml b/isis/src/base/apps/topds4/topds4.xml index 3ef1366bfe..c29862bc69 100644 --- a/isis/src/base/apps/topds4/topds4.xml +++ b/isis/src/base/apps/topds4/topds4.xml @@ -57,33 +57,5 @@ - - - - boolean - true - - Use the main cube label as input data - - - Use the main input cube label as a source of data to replace template elements with. - - - - - boolean - false - - Use the original label as input data - - - Use the original label attached to the input cube as a source of data - to replace template elements with. This is the label that was saved when - the cube was first ingested into ISIS as a cube. - - - - - diff --git a/isis/tests/FunctionalTestsTopds4.cpp b/isis/tests/FunctionalTestsTopds4.cpp index 5b615f6b57..9d5a0d3278 100644 --- a/isis/tests/FunctionalTestsTopds4.cpp +++ b/isis/tests/FunctionalTestsTopds4.cpp @@ -6,6 +6,10 @@ #include "Fixtures.h" #include "md5wrapper.h" +#include "OriginalLabel.h" +#include "Pvl.h" +#include "PvlGroup.h" +#include "PvlKeyword.h" #include "topds4.h" @@ -15,6 +19,68 @@ using namespace Isis; static QString APP_XML = FileName("$ISISROOT/bin/xml/topds4.xml").expanded(); +TEST_F(SmallCube, FunctionalTestTopds4MainLabel) { + PvlGroup testGroup("TestGroup"); + PvlKeyword testKey("TestValue", "a"); + testGroup += testKey; + testCube->putGroup(testGroup); + + QString templateFile = tempDir.path()+"/test_result.tpl"; + QString renderedFile = tempDir.path()+"/test_result.txt"; + std::ofstream of; + of.open(templateFile.toStdString()); + of << "{{MainLabel.IsisCube.TestGroup.TestValue.Value}}"; + of.close(); + QVector args = {"template=" + templateFile, "to=" + renderedFile}; + UserInterface options(APP_XML, args); + + topds4(testCube, options); + + std::ifstream renderedStream; + renderedStream.open(renderedFile.toStdString()); + std::string line; + std::getline(renderedStream, line); + EXPECT_EQ(testKey[0].toStdString(), line); +} + +TEST_F(SmallCube, FunctionalTestTopds4OriginalLabel) { + Pvl testLabel; + PvlKeyword testKey("TestValue", "a"); + testLabel += testKey; + OriginalLabel testOrigLab(testLabel); + testCube->write(testOrigLab); + + QString templateFile = tempDir.path()+"/test_result.tpl"; + QString renderedFile = tempDir.path()+"/test_result.txt"; + std::ofstream of; + of.open(templateFile.toStdString()); + of << "{{OriginalLabel.TestValue.Value}}"; + of.close(); + QVector args = {"template=" + templateFile, "to=" + renderedFile}; + UserInterface options(APP_XML, args); + + topds4(testCube, options); + + std::ifstream renderedStream; + renderedStream.open(renderedFile.toStdString()); + std::string line; + std::getline(renderedStream, line); + EXPECT_EQ(testKey[0].toStdString(), line); +} + +TEST_F(SmallCube, FunctionalTestTopds4NoOriginalLabel) { + QString templateFile = tempDir.path()+"/bad_value.tpl"; + QString renderedFile = tempDir.path()+"/bad_value.txt"; + std::ofstream of; + of.open(templateFile.toStdString()); + of << "{{OriginalLabel.TestValue.Value}}"; + of.close(); + QVector args = {"template=" + templateFile, "to=" + renderedFile}; + UserInterface options(APP_XML, args); + + EXPECT_ANY_THROW(topds4(testCube, options)); +} + TEST_F(SmallCube, FunctionalTestTopds4CurrentTime) { QString templateFile = tempDir.path()+"/current_time.tpl"; QString renderedFile = tempDir.path()+"/current_time.txt"; From 2975cb16a0b67cfc549877dbe8bb06e2480eddf2 Mon Sep 17 00:00:00 2001 From: Stuart Sides Date: Tue, 5 Jan 2021 09:48:40 -0700 Subject: [PATCH 12/28] Added more template elements and callback for file size (#4236) * now has some real parameters * removed inja files * full orex xml * add full orex template * More template elements, and file size callback --- .../20190612T090019S776_map_L0pan.xml.full | 36 +++++++-------- isis/src/base/apps/topds4/topds4.cpp | 26 ++++++++--- isis/src/base/apps/topds4/topds4.xml | 44 +++++++++++++++---- 3 files changed, 73 insertions(+), 33 deletions(-) diff --git a/isis/src/base/apps/topds4/20190612T090019S776_map_L0pan.xml.full b/isis/src/base/apps/topds4/20190612T090019S776_map_L0pan.xml.full index c9eea369c0..9c742be987 100644 --- a/isis/src/base/apps/topds4/20190612T090019S776_map_L0pan.xml.full +++ b/isis/src/base/apps/topds4/20190612T090019S776_map_L0pan.xml.full @@ -389,63 +389,63 @@ - 20190612T090019S776_map_L0pan.fits - 2020-01-17T19:16:18.256Z - 4443840 + {{imageFileName()}} + {{currentTime()}} + {{outputFileSize()}}
0 - 17280 - FITS 3.0 + {{ int(MainLabel.IsisCube.Core.StartByte.Value) - 2}} + ISIS3
Active Area - 17280 + {{ int(MainLabel.IsisCube.Core.Pixels.Multiplier.Value) - 1}} 2 Last Index Fastest OCAMS image 1024 by 1024 pixel active array. UnsignedMSB2 DN - 1 - 32768 + {{MainLabel.IsisCube.Core.Pixels.Multiplier.Value}} + {{MainLabel.IsisCube.Core.Pixels.Base.Value}} Line - 1024 + {{MainLabel.IsisCube.Core.Dimensions.Lines.Value}} 1 Sample - 1024 + {{MainLabel.IsisCube.Core.Dimensions.Samples.Value}} 2
- 2116800 - 2880 - FITS 3.0 + 0 + TBD + ISIS3
Active Array and Extended Pixel Regions - 2119680 + TBD 2 Last Index Fastest OCAMS 1044 sample by 1112 line extended pixel array. The extended pixel array contains the detector overscan and dark pixels. UnsignedMSB2 DN - 1 - 32768 + TBD + TBD Line - {{MainLabel.IsisCube.Core.Dimensions.Lines.Value}} + TBD 1 Sample - 1112{{MainLabel.IsisCube.Core.Dimensions.Samples.Value}} + TBD 2 diff --git a/isis/src/base/apps/topds4/topds4.cpp b/isis/src/base/apps/topds4/topds4.cpp index 84b8abf217..51b39d4d45 100644 --- a/isis/src/base/apps/topds4/topds4.cpp +++ b/isis/src/base/apps/topds4/topds4.cpp @@ -5,6 +5,7 @@ #include #include "md5wrapper.h" +#include "QFile.h" #include "topds4.h" @@ -30,6 +31,9 @@ namespace Isis { Process p; p.SetInputCube(icube); + // NEED TO WRITE AND CLOSE THE OUTPUT FILE BEFORE RENDERING SO THE FILE SIZE CALLBACK CAN GET THE FINAL FILE SIZE + QString outputFile = ui.GetFileName("TO"); + json dataSource; Pvl &cubeLabel = *icube->label(); @@ -50,13 +54,8 @@ namespace Isis { // get the xml label and add it to the template data } - std::cout << "=============Data================" << std::endl; - std::cout << dataSource.dump(4) << std::endl; - std::cout << "=================================" << std::endl; - - Environment env; - // Call back functions + // Template engine call back functions /** * Renders to the current UTC time formatted as YYYY-MM-DDTHH:MM:SS */ @@ -76,6 +75,14 @@ namespace Isis { return (cubeFilename.split(".")[0] + ".img").toStdString(); }); + /** + * Renders to the final file size in bytes of the output cube or img + */ + env.add_callback("outputFileSize", 0, [outputFile](Arguments& args) { + FileName cubeFilename = outputFile; + return QFile(outputFile).size(); + }); + /** * Renders to the MD5 hash for the input cube */ @@ -85,13 +92,18 @@ namespace Isis { }); std::string inputTemplate = ui.GetFileName("TEMPLATE").toStdString(); - std::string result = env.render_file(inputTemplate, dataSource); std::ofstream outFile(ui.GetFileName("TO").toStdString()); outFile << result; outFile.close(); + if (ui.WasEntered("DATA")) { + std::ofstream jsonDataFile(FileName(ui.GetFileName("DATA")).expanded().toStdString()); + jsonDataFile << dataSource.dump(4); + jsonDataFile.close(); + } + std::cout << "============Result===============" << std::endl; std::cout << result << std::endl; std::cout << "=================================" << std::endl; diff --git a/isis/src/base/apps/topds4/topds4.xml b/isis/src/base/apps/topds4/topds4.xml index c29862bc69..9f9ef6d0dd 100644 --- a/isis/src/base/apps/topds4/topds4.xml +++ b/isis/src/base/apps/topds4/topds4.xml @@ -2,20 +2,28 @@ - dummy + Convert from ISIS3 cube to PDS 4 format using template XML files - dummy +

+ Writes a PDS4 compatible label file and image file. The contents of the label file are + generated useing the template file specified by the TEMPLATE parameter. +

+

+ The image file is generated from the input ISIS cube specified by the FROM parameter. + The format of the output image file can be either an ISIS cube or a raw binary file containing + only the image DNs (i.e., no header or trailing data). +

- Trim and Mask + Import and Export - - Removed unreachable code. + + Original version @@ -28,7 +36,7 @@ Input cube - This is the input cube that will be cropped. + This is the input cube that will be converted to PDS4 format. *.cub @@ -42,7 +50,10 @@ Input template - Input template + The file name of the input template. This file contains "inja" compatible + template syntax inside of a PDS4 XML label. The data used to replace the + template elements comes from the ISIS cube label, the original label + (PDS3, FITS, PDS4), and other input PVL, XML, or JSON files. @@ -53,9 +64,26 @@ Output PDS4 label - The. + The output file name of the PDS4 label. The output image name will be derived from this + parameter by removing the last extension and adding .cub + + + + + filename + None + output + + Output JSON data + + + The output file name to store the template data source. This is a dump of the JSON data used + by the template engine to fill in the template elements. It is a combination of the ISIS + main cube lable, the original lable, and any other data sources supplied. The purpose of this + parameter is for debugging the templates. +
From d5d4e739c8ffcc2adea0e5dd9863f2fb16e94aab Mon Sep 17 00:00:00 2001 From: Jesse Mapel Date: Wed, 6 Jan 2021 08:54:17 -0700 Subject: [PATCH 13/28] Added Extra data source parameters to topds4 (#4239) * Added extra parameters * Added duplicate warnings * Fixed warning --- isis/src/base/apps/topds4/topds4.cpp | 83 +++++++++- isis/src/base/apps/topds4/topds4.xml | 51 +++++- isis/tests/FunctionalTestsTopds4.cpp | 228 +++++++++++++++++++++++++++ 3 files changed, 353 insertions(+), 9 deletions(-) diff --git a/isis/src/base/apps/topds4/topds4.cpp b/isis/src/base/apps/topds4/topds4.cpp index 51b39d4d45..83c6efc614 100644 --- a/isis/src/base/apps/topds4/topds4.cpp +++ b/isis/src/base/apps/topds4/topds4.cpp @@ -3,15 +3,16 @@ #include #include - -#include "md5wrapper.h" -#include "QFile.h" - -#include "topds4.h" +#include +#include #include "FileName.h" +#include "md5wrapper.h" #include "OriginalLabel.h" #include "PvlToJSON.h" +#include "XmlToJson.h" + +#include "topds4.h" using namespace std; using namespace inja; @@ -54,6 +55,78 @@ namespace Isis { // get the xml label and add it to the template data } + // Add any extra files to the template engine data + if (ui.WasEntered("EXTRAPVL")) { + vector extraPvlFiles; + ui.GetFileName("EXTRAPVL", extraPvlFiles); + for (QString pvlFile : extraPvlFiles) { + Pvl extraPvl(pvlFile); + json extraJson = pvlToJSON(extraPvl); + // Notify users of duplicate keys that will be overwritten + if (log) { + for (auto& element : extraJson.items()) { + if (dataSource["ExtraPvl"].contains(element.key())) { + PvlGroup duplicateWarnings("Warning"); + QString message = "Duplicate key [" + QString::fromStdString(element.key()) + + "] in extra Pvl file [" + pvlFile + "]. " + + "Previous value [" + QString::fromStdString(dataSource["ExtraPvl"][element.key()].dump()) + + "] will be overwritten."; + duplicateWarnings += PvlKeyword("Duplicate", message); + log->addGroup(duplicateWarnings); + } + } + } + dataSource["ExtraPvl"].update(extraJson); + } + } + + if (ui.WasEntered("EXTRAXML")) { + vector extraXmlFiles; + ui.GetFileName("EXTRAXML", extraXmlFiles); + for (QString xmlFile : extraXmlFiles) { + // Notify users of duplicate keys that will be overwritten + json extraJson = xmlToJson(xmlFile); + if (log) { + for (auto& element : extraJson.items()) { + if (dataSource["ExtraXml"].contains(element.key())) { + PvlGroup duplicateWarnings("Warning"); + QString message = "Duplicate element [" + QString::fromStdString(element.key()) + + "] in extra xml file [" + xmlFile + "]. " + + "Previous value [" + QString::fromStdString(dataSource["ExtraXml"][element.key()].dump()) + + "] will be overwritten."; + duplicateWarnings += PvlKeyword("Duplicate", message); + log->addGroup(duplicateWarnings); + } + } + } + dataSource["ExtraXml"].update(extraJson); + } + } + + if (ui.WasEntered("EXTRAJSON")) { + vector extraJsonFiles; + ui.GetFileName("EXTRAJSON", extraJsonFiles); + for (QString jsonFile : extraJsonFiles) { + ifstream extraJsonStream(jsonFile.toStdString()); + json extraJson; + extraJsonStream >> extraJson; + if (log) { + for (auto& element : extraJson.items()) { + if (dataSource["ExtraJson"].contains(element.key())) { + PvlGroup duplicateWarnings("Warning"); + QString message = "Duplicate key [" + QString::fromStdString(element.key()) + + "] in extra json file [" + jsonFile + "]. " + + "Previous value [" + QString::fromStdString(dataSource["ExtraJson"][element.key()].dump()) + + "] will be overwritten."; + duplicateWarnings += PvlKeyword("Duplicate", message); + log->addGroup(duplicateWarnings); + } + } + } + dataSource["ExtraJson"].update(extraJson); + } + } + Environment env; // Template engine call back functions /** diff --git a/isis/src/base/apps/topds4/topds4.xml b/isis/src/base/apps/topds4/topds4.xml index 9f9ef6d0dd..02ba22ea82 100644 --- a/isis/src/base/apps/topds4/topds4.xml +++ b/isis/src/base/apps/topds4/topds4.xml @@ -7,7 +7,7 @@

- Writes a PDS4 compatible label file and image file. The contents of the label file are + Writes a PDS4 compatible label file and image file. The contents of the label file are generated useing the template file specified by the TEMPLATE parameter.

@@ -50,9 +50,9 @@ Input template - The file name of the input template. This file contains "inja" compatible - template syntax inside of a PDS4 XML label. The data used to replace the - template elements comes from the ISIS cube label, the original label + The file name of the input template. This file contains "inja" compatible + template syntax inside of a PDS4 XML label. The data used to replace the + template elements comes from the ISIS cube label, the original label (PDS3, FITS, PDS4), and other input PVL, XML, or JSON files. @@ -68,7 +68,9 @@ parameter by removing the last extension and adding .cub + + filename None @@ -84,6 +86,47 @@ + + filename + None + input + + Extra PVL data + + + The PVL data contained in the file(s) specified by this parameter will + be added to the template data source under ExtraPvl. If multiple PVL files + are specified, then they will be merged together. + + + + + filename + None + input + + Extra XML data + + + The XML data contained in the file(s) specified by this parameter will + be added to the template data source under ExtraXml. If multiple XML files + are specified, then they will be merged together. + + + + + filename + None + input + + Extra JSON data + + + The JSON data contained in the file(s) specified by this parameter will + be added to the template data source under ExtraJson. If multiple JSON files + are specified, then they will be merged together. + + diff --git a/isis/tests/FunctionalTestsTopds4.cpp b/isis/tests/FunctionalTestsTopds4.cpp index 9d5a0d3278..742302b929 100644 --- a/isis/tests/FunctionalTestsTopds4.cpp +++ b/isis/tests/FunctionalTestsTopds4.cpp @@ -3,6 +3,7 @@ #include #include +#include #include "Fixtures.h" #include "md5wrapper.h" @@ -16,6 +17,7 @@ #include "gmock/gmock.h" using namespace Isis; +using json = nlohmann::json; static QString APP_XML = FileName("$ISISROOT/bin/xml/topds4.xml").expanded(); @@ -81,6 +83,232 @@ TEST_F(SmallCube, FunctionalTestTopds4NoOriginalLabel) { EXPECT_ANY_THROW(topds4(testCube, options)); } +TEST_F(SmallCube, FunctionalTestTopds4ExtraPvl) { + QString pvlFile = tempDir.path()+"/extra.pvl"; + Pvl testPvl; + PvlKeyword testKey("TestValue", "a"); + testPvl += testKey; + testPvl.write(pvlFile); + + QString templateFile = tempDir.path()+"/test_result.tpl"; + QString renderedFile = tempDir.path()+"/test_result.txt"; + std::ofstream of; + of.open(templateFile.toStdString()); + of << "{{ExtraPvl.TestValue.Value}}"; + of.close(); + QVector args = {"template=" + templateFile, + "to=" + renderedFile, + "extrapvl=" + pvlFile}; + UserInterface options(APP_XML, args); + + topds4(testCube, options); + + std::ifstream renderedStream; + renderedStream.open(renderedFile.toStdString()); + std::string line; + std::getline(renderedStream, line); + EXPECT_EQ(testKey[0].toStdString(), line); +} + +TEST_F(SmallCube, FunctionalTestTopds4MultipleExtraPvl) { + QString pvlFile1 = tempDir.path()+"/extra1.pvl"; + Pvl testPvl1; + PvlKeyword testKey1("TestValue", "a"); + PvlKeyword safeKey("SafeValue", "true"); + testPvl1 += testKey1; + testPvl1 += safeKey; + testPvl1.write(pvlFile1); + + QString pvlfile2 = tempDir.path()+"/extra2.pvl"; + Pvl testPvl2; + PvlKeyword duplicateKey("TestValue", "b"); + PvlKeyword testKey2("AnotherValue", "10"); + testPvl2 += duplicateKey; + testPvl2 += testKey2; + testPvl2.write(pvlfile2); + + QString templateFile = tempDir.path()+"/test_result.tpl"; + QString renderedFile = tempDir.path()+"/test_result.txt"; + std::ofstream of; + of.open(templateFile.toStdString()); + of << "{{ExtraPvl.TestValue.Value}}\n" + << "{{ExtraPvl.AnotherValue.Value}}\n" + << "{{ExtraPvl.SafeValue.Value}}"; + of.close(); + QVector args = {"template=" + templateFile, + "to=" + renderedFile, + "extrapvl=(" + pvlFile1 + "," + pvlfile2 + ")"}; + UserInterface options(APP_XML, args); + Pvl log; + + topds4(testCube, options, &log); + + std::ifstream renderedStream; + renderedStream.open(renderedFile.toStdString()); + std::string line; + std::getline(renderedStream, line); + EXPECT_EQ(duplicateKey[0].toStdString(), line); + std::getline(renderedStream, line); + EXPECT_EQ(testKey2[0].toStdString(), line); + std::getline(renderedStream, line); + EXPECT_EQ(safeKey[0].toStdString(), line); + + // The duplicate key should generate a warning + EXPECT_TRUE(log.hasGroup("Warning")); +} + +TEST_F(SmallCube, FunctionalTestTopds4ExtraJson) { + QString jsonFile = tempDir.path()+"/extra.json"; + json testJson; + testJson["TestValue"] = "a"; + std::ofstream jsonStream; + jsonStream.open(jsonFile.toStdString()); + jsonStream << testJson.dump(); + jsonStream.close(); + + QString templateFile = tempDir.path()+"/test_result.tpl"; + QString renderedFile = tempDir.path()+"/test_result.txt"; + std::ofstream of; + of.open(templateFile.toStdString()); + of << "{{ExtraJson.TestValue}}"; + of.close(); + QVector args = {"template=" + templateFile, + "to=" + renderedFile, + "extrajson=" + jsonFile}; + UserInterface options(APP_XML, args); + + topds4(testCube, options); + + std::ifstream renderedStream; + renderedStream.open(renderedFile.toStdString()); + std::string line; + std::getline(renderedStream, line); + EXPECT_EQ(testJson["TestValue"], line); +} + +TEST_F(SmallCube, FunctionalTestTopds4MultipleExtraJson) { + QString jsonFile1 = tempDir.path()+"/extra1.json"; + json testJson1; + testJson1["TestValue"] = "a"; + testJson1["SafeValue"] = "true"; + std::ofstream jsonStream1; + jsonStream1.open(jsonFile1.toStdString()); + jsonStream1 << testJson1.dump(); + jsonStream1.close(); + + QString jsonFile2 = tempDir.path()+"/extra2.json"; + json testJson2; + testJson2["TestValue"] = "b"; + testJson2["AdditionalValue"] = "10"; + std::ofstream jsonStream2; + jsonStream2.open(jsonFile2.toStdString()); + jsonStream2 << testJson2.dump(); + jsonStream2.close(); + + QString templateFile = tempDir.path()+"/test_result.tpl"; + QString renderedFile = tempDir.path()+"/test_result.txt"; + std::ofstream of; + of.open(templateFile.toStdString()); + of << "{{ExtraJson.TestValue}}\n" + << "{{ExtraJson.AdditionalValue}}\n" + << "{{ExtraJson.SafeValue}}"; + of.close(); + QVector args = {"template=" + templateFile, + "to=" + renderedFile, + "extrajson=(" + jsonFile1 + "," + jsonFile2 + ")"}; + UserInterface options(APP_XML, args); + Pvl log; + + topds4(testCube, options, &log); + + std::ifstream renderedStream; + renderedStream.open(renderedFile.toStdString()); + std::string line; + std::getline(renderedStream, line); + EXPECT_EQ(testJson2["TestValue"], line); + std::getline(renderedStream, line); + EXPECT_EQ(testJson2["AdditionalValue"], line); + std::getline(renderedStream, line); + EXPECT_EQ(testJson1["SafeValue"], line); + + // The duplicate key should generate a warning + EXPECT_TRUE(log.hasGroup("Warning")); +} + +TEST_F(SmallCube, FunctionalTestTopds4ExtraXml) { + QString xmlFile = tempDir.path()+"/extra.xml"; + std::ofstream xmlStream; + xmlStream.open(xmlFile.toStdString()); + xmlStream << "a"; + xmlStream.close(); + + QString templateFile = tempDir.path()+"/test_result.tpl"; + QString renderedFile = tempDir.path()+"/test_result.txt"; + std::ofstream of; + of.open(templateFile.toStdString()); + of << "{{ExtraXml.TestValue}}"; + of.close(); + QVector args = {"template=" + templateFile, + "to=" + renderedFile, + "extraxml=" + xmlFile}; + UserInterface options(APP_XML, args); + + topds4(testCube, options); + + std::ifstream renderedStream; + renderedStream.open(renderedFile.toStdString()); + std::string line; + std::getline(renderedStream, line); + EXPECT_EQ("a", line); +} + +TEST_F(SmallCube, FunctionalTestTopds4MultipleExtraXml) { + QString xmlFile1 = tempDir.path()+"/extra1.xml"; + std::ofstream xmlStream1; + xmlStream1.open(xmlFile1.toStdString()); + xmlStream1 << "a"; + xmlStream1.close(); + + QString xmlFile2 = tempDir.path()+"/extra2.xml"; + std::ofstream xmlStream2; + xmlStream2.open(xmlFile2.toStdString()); + xmlStream2 << "10"; + xmlStream2.close(); + + QString xmlFile3 = tempDir.path()+"/extra3.xml"; + std::ofstream xmlStream3; + xmlStream3.open(xmlFile3.toStdString()); + xmlStream3 << "b"; + xmlStream3.close(); + + QString templateFile = tempDir.path()+"/test_result.tpl"; + QString renderedFile = tempDir.path()+"/test_result.txt"; + std::ofstream of; + of.open(templateFile.toStdString()); + of << "{{ExtraXml.TestValue}}\n" + << "{{ExtraXml.AdditionalValue}}"; + of.close(); + + QVector args = {"template=" + templateFile, + "to=" + renderedFile, + "extraxml=(" + xmlFile1 + "," + xmlFile2 + ", " + xmlFile3 + ")"}; + UserInterface options(APP_XML, args); + Pvl log; + + topds4(testCube, options, &log); + + std::ifstream renderedStream; + renderedStream.open(renderedFile.toStdString()); + std::string line; + std::getline(renderedStream, line); + EXPECT_EQ("b", line); + std::getline(renderedStream, line); + EXPECT_EQ("10", line); + + // The duplicate key should generate a warning + EXPECT_TRUE(log.hasGroup("Warning")); +} + TEST_F(SmallCube, FunctionalTestTopds4CurrentTime) { QString templateFile = tempDir.path()+"/current_time.tpl"; QString renderedFile = tempDir.path()+"/current_time.txt"; From 5a6586c5eed17952a4a5a7839a0f96709d953dab Mon Sep 17 00:00:00 2001 From: Kristin Berry Date: Thu, 7 Jan 2021 09:51:34 -0700 Subject: [PATCH 14/28] Add writing a cube out to topds4 (#4241) * Basic cubeatt conversion * Add more useful function signature * Removed hardcoded path * Update based on feedback * Add better band test * Updated to fix failing tests and remove old testfiles --- isis/src/base/apps/cubeatt/cubeatt.cpp | 54 ++++++++++ isis/src/base/apps/cubeatt/cubeatt.h | 14 +++ isis/src/base/apps/cubeatt/main.cpp | 33 ++---- isis/src/base/apps/cubeatt/tsts/Makefile | 4 - .../base/apps/cubeatt/tsts/change/Makefile | 7 -- .../base/apps/cubeatt/tsts/noChange/Makefile | 7 -- .../apps/cubeatt/tsts/virtualBands/Makefile | 7 -- isis/src/base/apps/topds4/topds4.cpp | 18 +++- isis/tests/Fixtures.cpp | 22 +++- isis/tests/FunctionalTestsCubeatt.cpp | 100 ++++++++++++++++++ 10 files changed, 213 insertions(+), 53 deletions(-) create mode 100644 isis/src/base/apps/cubeatt/cubeatt.cpp create mode 100644 isis/src/base/apps/cubeatt/cubeatt.h delete mode 100644 isis/src/base/apps/cubeatt/tsts/Makefile delete mode 100644 isis/src/base/apps/cubeatt/tsts/change/Makefile delete mode 100644 isis/src/base/apps/cubeatt/tsts/noChange/Makefile delete mode 100644 isis/src/base/apps/cubeatt/tsts/virtualBands/Makefile create mode 100644 isis/tests/FunctionalTestsCubeatt.cpp diff --git a/isis/src/base/apps/cubeatt/cubeatt.cpp b/isis/src/base/apps/cubeatt/cubeatt.cpp new file mode 100644 index 0000000000..12a2414410 --- /dev/null +++ b/isis/src/base/apps/cubeatt/cubeatt.cpp @@ -0,0 +1,54 @@ +#include "cubeatt.h" + +#include "ProcessByLine.h" + +using namespace std; +using namespace Isis; + +namespace Isis { + void cubeattProcess(Buffer &in, Buffer &out); + + void cubeatt(UserInterface &ui) { + Cube icube; + CubeAttributeInput inAtt = ui.GetInputAttribute("FROM"); + if (inAtt.bands().size() != 0) { + icube.setVirtualBands(inAtt.bands()); + } + icube.open(ui.GetFileName("FROM")); + cubeatt(&icube, ui); + } + + + // Doesn't allow specification of input attributes + void cubeatt(Cube *icube, UserInterface &ui) { + bool propTables = ui.GetBoolean("PROPTABLES"); + QString outputFileName = ui.GetFileName("TO"); + CubeAttributeOutput outputAttributes= ui.GetOutputAttribute("TO"); + cubeatt(icube, outputFileName, outputAttributes, propTables); + } + + // Doesn't allow specification of input attributes + void cubeatt(Cube *icube, QString outputCubePath, CubeAttributeOutput outAttributes, bool propTables) { + // We will be processing by line + ProcessByLine p; + + // Should we propagate tables + p.PropagateTables(propTables); + + // Setup the input and output cubes + p.SetInputCube(icube); + p.SetOutputCube(outputCubePath, outAttributes, + icube->sampleCount(), icube->lineCount(), icube->bandCount()); + + p.StartProcess(cubeattProcess); + p.EndProcess(); + } + + // Line processing routine + void cubeattProcess(Buffer &in, Buffer &out) { + // Loop and copy pixels in the line. + for(int i = 0; i < in.size(); i++) { + out[i] = in[i]; + } + } +} diff --git a/isis/src/base/apps/cubeatt/cubeatt.h b/isis/src/base/apps/cubeatt/cubeatt.h new file mode 100644 index 0000000000..b853cedd7e --- /dev/null +++ b/isis/src/base/apps/cubeatt/cubeatt.h @@ -0,0 +1,14 @@ +#ifndef cubeatt_h +#define cubeatt_h + +#include "Cube.h" +#include "CubeAttribute.h" +#include "UserInterface.h" + +namespace Isis{ + extern void cubeatt(Cube *icube, QString outputCubePath, CubeAttributeOutput outputAttributes, bool propTables=false); + extern void cubeatt(Cube *icube, UserInterface &ui); + extern void cubeatt(UserInterface &ui); +} + +#endif \ No newline at end of file diff --git a/isis/src/base/apps/cubeatt/main.cpp b/isis/src/base/apps/cubeatt/main.cpp index e57dfeb0e6..7e7c0a221a 100644 --- a/isis/src/base/apps/cubeatt/main.cpp +++ b/isis/src/base/apps/cubeatt/main.cpp @@ -1,32 +1,13 @@ #include "Isis.h" -#include "ProcessByLine.h" + +#include "cubeatt.h" + +#include "Application.h" using namespace std; using namespace Isis; -void cubeatt(Buffer &in, Buffer &out); - void IsisMain() { - // We will be processing by line - ProcessByLine p; - - // Should we propagate tables - if(!Application::GetUserInterface().GetBoolean("PROPTABLES")) { - p.PropagateTables(false); - } - - // Setup the input and output cubes - p.SetInputCube("FROM"); - p.SetOutputCube("TO"); - - p.StartProcess(cubeatt); - p.EndProcess(); -} - -// Line processing routine -void cubeatt(Buffer &in, Buffer &out) { - // Loop and copy pixels in the line. - for(int i = 0; i < in.size(); i++) { - out[i] = in[i]; - } -} + UserInterface &ui = Application::GetUserInterface(); + cubeatt(ui); +} \ No newline at end of file diff --git a/isis/src/base/apps/cubeatt/tsts/Makefile b/isis/src/base/apps/cubeatt/tsts/Makefile deleted file mode 100644 index 46d84c74c2..0000000000 --- a/isis/src/base/apps/cubeatt/tsts/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -BLANKS = "%-6s" -LENGTH = "%-40s" - -include $(ISISROOT)/make/isismake.tststree diff --git a/isis/src/base/apps/cubeatt/tsts/change/Makefile b/isis/src/base/apps/cubeatt/tsts/change/Makefile deleted file mode 100644 index 07c1f43c43..0000000000 --- a/isis/src/base/apps/cubeatt/tsts/change/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -APPNAME = cubeatt - -include $(ISISROOT)/make/isismake.tsts - -commands: - $(APPNAME) from=$(INPUT)/isisTruth.cub \ - to=$(OUTPUT)/cubeattTruth2.cub+8bit+0.0:1.0 > /dev/null; diff --git a/isis/src/base/apps/cubeatt/tsts/noChange/Makefile b/isis/src/base/apps/cubeatt/tsts/noChange/Makefile deleted file mode 100644 index a82d15622a..0000000000 --- a/isis/src/base/apps/cubeatt/tsts/noChange/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -APPNAME = cubeatt - -include $(ISISROOT)/make/isismake.tsts - -commands: - $(APPNAME) from=$(INPUT)/isisTruth.cub+1 \ - to=$(OUTPUT)/cubeattTruth1.cub > /dev/null; diff --git a/isis/src/base/apps/cubeatt/tsts/virtualBands/Makefile b/isis/src/base/apps/cubeatt/tsts/virtualBands/Makefile deleted file mode 100644 index 9eb15373f5..0000000000 --- a/isis/src/base/apps/cubeatt/tsts/virtualBands/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -APPNAME = cubeatt - -include $(ISISROOT)/make/isismake.tsts - -commands: - $(APPNAME) from=$(INPUT)/crop5by5_peaks.cub+3,2,4,2,1,5,7,6,4 \ - to=$(OUTPUT)/bands324215764.cub > /dev/null; diff --git a/isis/src/base/apps/topds4/topds4.cpp b/isis/src/base/apps/topds4/topds4.cpp index 83c6efc614..25917a740b 100644 --- a/isis/src/base/apps/topds4/topds4.cpp +++ b/isis/src/base/apps/topds4/topds4.cpp @@ -4,8 +4,14 @@ #include #include + +#include "md5wrapper.h" #include +#include "topds4.h" +#include "cubeatt.h" + +#include "CubeAttribute.h" #include "FileName.h" #include "md5wrapper.h" #include "OriginalLabel.h" @@ -32,9 +38,19 @@ namespace Isis { Process p; p.SetInputCube(icube); - // NEED TO WRITE AND CLOSE THE OUTPUT FILE BEFORE RENDERING SO THE FILE SIZE CALLBACK CAN GET THE FINAL FILE SIZE QString outputFile = ui.GetFileName("TO"); + // NEED TO WRITE AND CLOSE THE OUTPUT FILE BEFORE RENDERING SO THE FILE SIZE CALLBACK CAN GET THE FINAL FILE SIZE + + // Name for output image + FileName outputFileName(outputFile); + QString path(outputFileName.originalPath()); + QString name(outputFileName.baseName()); + QString outputCubePath = path + "/" + name + ".cub"; + + CubeAttributeOutput outputAttributes("+bsq"); + cubeatt(icube, outputCubePath, outputAttributes); + json dataSource; Pvl &cubeLabel = *icube->label(); diff --git a/isis/tests/Fixtures.cpp b/isis/tests/Fixtures.cpp index ab2db70cf6..a5ed734119 100644 --- a/isis/tests/Fixtures.cpp +++ b/isis/tests/Fixtures.cpp @@ -21,7 +21,8 @@ namespace Isis { testCube = new Cube(); testCube->setDimensions(10, 10, 10); - testCube->create(tempDir.path() + "/small.cub"); + QString path = tempDir.path() + "/small.cub"; + testCube->create(path); LineManager line(*testCube); double pixelValue = 0.0; @@ -31,6 +32,25 @@ namespace Isis { } testCube->write(line); } + + // Add a BandBin group to the cube label + Pvl *label = testCube->label(); + PvlObject& cubeLabel = label->findObject("IsisCube"); + PvlGroup bandBin("BandBin"); + PvlKeyword originalBand("OriginalBand", "1"); + originalBand += "2"; + originalBand += "3"; + originalBand += "4"; + originalBand += "5"; + originalBand += "6"; + originalBand += "7"; + originalBand += "8"; + originalBand += "9"; + originalBand += "10"; + bandBin += originalBand; + cubeLabel.addGroup(bandBin); + testCube->close(); + testCube->open(path, "rw"); } void SmallCube::TearDown() { diff --git a/isis/tests/FunctionalTestsCubeatt.cpp b/isis/tests/FunctionalTestsCubeatt.cpp new file mode 100644 index 0000000000..d7d598a16d --- /dev/null +++ b/isis/tests/FunctionalTestsCubeatt.cpp @@ -0,0 +1,100 @@ +#include + +#include "Cube.h" +#include "Fixtures.h" +#include "Statistics.h" + +#include "cubeatt.h" +#include "gmock/gmock.h" + + +using namespace Isis; + +static QString APP_XML = FileName("$ISISROOT/bin/xml/cubeatt.xml").expanded(); + +// Tests setting output attributes: bit type and range +TEST_F(SmallCube, FunctionalTestCubeattBitttypeAndRange) { + QString cubePath = tempDir.path() + "/bitTypeCubeatt.cub+8bit+0.0:1.0"; + + QVector args = {"from=" + testCube->fileName(), "to=" + cubePath}; + UserInterface options(APP_XML, args); + cubeatt(options); + + Cube outputCube(cubePath); + + // Check attributes: pixel type, storage format, label format, storage order, pixel range, bands + EXPECT_EQ(outputCube.pixelType(), PixelType::UnsignedByte); + // Setting the pixel range modifies the base/multiplier, so check those. + EXPECT_NE(outputCube.base(), 0); + EXPECT_NE(outputCube.multiplier(), 1); + + // Test the DNs + Statistics *outputStats = outputCube.statistics(); + EXPECT_FLOAT_EQ(outputStats->Minimum(), 0.0); + EXPECT_FLOAT_EQ(outputStats->Maximum(), 1.0); +} + +TEST_F(SmallCube, FunctionalTestCubeattNoChange) { + QString cubePath = tempDir.path() + "/NoChangeCubeatt.cub"; + QVector args = {"from=" + testCube->fileName(), "to=" + cubePath}; + UserInterface options(APP_XML, args); + cubeatt(options); + + Cube outputCube(cubePath); + + // Check attributes: pixel type, storage format, label format, storage order, pixel range, bands + EXPECT_EQ(outputCube.pixelType(), PixelType::Real); + EXPECT_EQ(outputCube.format(), Cube::Format::Tile); + EXPECT_TRUE(outputCube.labelsAttached()); + EXPECT_EQ(outputCube.byteOrder(), ByteOrder::Lsb); + // Setting the pixel range modifies the base/multiplier, so check those. + EXPECT_EQ(outputCube.base(), 0); + EXPECT_EQ(outputCube.multiplier(), 1); + EXPECT_EQ(outputCube.bandCount(), 10); + + // Test that DNs match in the input and output cubes + Statistics *outputStats = outputCube.statistics(); + Statistics *inputStats = testCube->statistics(); + EXPECT_DOUBLE_EQ(outputStats->Minimum(), inputStats->Minimum()); + EXPECT_DOUBLE_EQ(outputStats->Maximum(), inputStats->Maximum()); + EXPECT_DOUBLE_EQ(outputStats->Average(), inputStats->Average()); +} + + + +TEST_F(SmallCube, FunctionalTestCubeattVirtualBands) { + QString cubePath = tempDir.path() + "/VirtualBandsCubeatt.cub"; + QVector args = {"from=" + testCube->fileName() + "+3,2,4,2,1,5,7,6,4", "to=" + cubePath}; + UserInterface options(APP_XML, args); + cubeatt(options); + Cube outputCube(cubePath); + EXPECT_EQ(outputCube.bandCount(), 9); + + // Do need to check the label for this one, since outputCube.physicalBand() will not work + // in this context: + Pvl *label = outputCube.label(); + PvlGroup bandBin = label->findObject("IsisCube").findGroup("BandBin"); + EXPECT_EQ(QString(bandBin["OriginalBand"][0]), "3"); + EXPECT_EQ(QString(bandBin["OriginalBand"][1]), "2"); + EXPECT_EQ(QString(bandBin["OriginalBand"][2]), "4"); + EXPECT_EQ(QString(bandBin["OriginalBand"][3]), "2"); + EXPECT_EQ(QString(bandBin["OriginalBand"][4]), "1"); + EXPECT_EQ(QString(bandBin["OriginalBand"][5]), "5"); + EXPECT_EQ(QString(bandBin["OriginalBand"][6]), "7"); + EXPECT_EQ(QString(bandBin["OriginalBand"][7]), "6"); + EXPECT_EQ(QString(bandBin["OriginalBand"][8]), "4"); + + + // Test that DNs for each band have moved appropriately + EXPECT_DOUBLE_EQ(outputCube.statistics(1)->Average(), testCube->statistics(3)->Average()); + EXPECT_DOUBLE_EQ(outputCube.statistics(2)->Average(), testCube->statistics(2)->Average()); + EXPECT_DOUBLE_EQ(outputCube.statistics(3)->Average(), testCube->statistics(4)->Average()); + EXPECT_DOUBLE_EQ(outputCube.statistics(4)->Average(), testCube->statistics(2)->Average()); + EXPECT_DOUBLE_EQ(outputCube.statistics(5)->Average(), testCube->statistics(1)->Average()); + EXPECT_DOUBLE_EQ(outputCube.statistics(6)->Average(), testCube->statistics(5)->Average()); + EXPECT_DOUBLE_EQ(outputCube.statistics(7)->Average(), testCube->statistics(7)->Average()); + EXPECT_DOUBLE_EQ(outputCube.statistics(8)->Average(), testCube->statistics(6)->Average()); + EXPECT_DOUBLE_EQ(outputCube.statistics(9)->Average(), testCube->statistics(4)->Average()); +} + + From 67f1e734c2757b1007b5530f52cadfc2e043fd55 Mon Sep 17 00:00:00 2001 From: Kristin Berry Date: Thu, 7 Jan 2021 10:13:39 -0700 Subject: [PATCH 15/28] Add copy of topds4 templates to CMakeLists.txt (#4242) --- isis/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/isis/CMakeLists.txt b/isis/CMakeLists.txt index 7eb5d5d583..ee5d678142 100644 --- a/isis/CMakeLists.txt +++ b/isis/CMakeLists.txt @@ -501,6 +501,7 @@ file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/appdata/translations ${CMAKE_BINARY_DIR}/appdata/templates/jigsaw ${CMAKE_BINARY_DIR}/appdata/templates/kernels ${CMAKE_BINARY_DIR}/appdata/templates/labels + ${CMAKE_BINARY_DIR}/appdata/templates/topds4 ${CMAKE_BINARY_DIR}/appdata/templates/maps ${CMAKE_BINARY_DIR}/appdata/templates/photometry ${CMAKE_BINARY_DIR}/appdata/images/icons @@ -619,6 +620,10 @@ add_custom_target(hidtmgen ALL COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_SOURCE_DIR}/appdata/templates/hidtmgen/* ${CMAKE_BINARY_DIR}/appdata/templates/hidtmgen/) add_dependencies(isis hidtmgen) +add_custom_target(topds4 ALL COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${CMAKE_SOURCE_DIR}/appdata/templates/topds4/* ${CMAKE_BINARY_DIR}/appdata/templates/topds4) +add_dependencies(isis topds4) + # Add a custom build target to clean out everything that gets added to the source # directory during the build process. # - Only a few things are added in order to make the tests work properly so From 05cf919dc3bf58206f39da08437ea0e40a4217b5 Mon Sep 17 00:00:00 2001 From: Jesse Mapel Date: Thu, 7 Jan 2021 15:19:50 -0700 Subject: [PATCH 16/28] Fixed callbacks and doc updates (#4243) * Fixed callbacks and doc updates * added some extra docs --- isis/src/base/apps/topds4/topds4.cpp | 24 +++++++++--------------- isis/src/base/apps/topds4/topds4.xml | 15 ++++++++++++--- isis/tests/FunctionalTestsTopds4.cpp | 5 +++-- 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/isis/src/base/apps/topds4/topds4.cpp b/isis/src/base/apps/topds4/topds4.cpp index 25917a740b..1ce2ec412c 100644 --- a/isis/src/base/apps/topds4/topds4.cpp +++ b/isis/src/base/apps/topds4/topds4.cpp @@ -38,10 +38,9 @@ namespace Isis { Process p; p.SetInputCube(icube); + // Setup the output file so that we can use it in callbacks QString outputFile = ui.GetFileName("TO"); - // NEED TO WRITE AND CLOSE THE OUTPUT FILE BEFORE RENDERING SO THE FILE SIZE CALLBACK CAN GET THE FINAL FILE SIZE - // Name for output image FileName outputFileName(outputFile); QString path(outputFileName.originalPath()); @@ -157,27 +156,26 @@ namespace Isis { }); /** - * Renders to the filename of the output img + * Renders to the filename of the output image file */ - env.add_callback("imageFileName", 0, [icube](Arguments& args) { - QString cubeFilename = icube->fileName().split("/").back(); - return (cubeFilename.split(".")[0] + ".img").toStdString(); + env.add_callback("imageFileName", 0, [outputCubePath](Arguments& args) { + return outputCubePath.split("/").back().toStdString(); }); /** - * Renders to the final file size in bytes of the output cube or img + * Renders to the final file size in bytes of the output image file */ env.add_callback("outputFileSize", 0, [outputFile](Arguments& args) { FileName cubeFilename = outputFile; - return QFile(outputFile).size(); + return QFile(outputFile).size(); }); /** - * Renders to the MD5 hash for the input cube + * Renders to the MD5 hash for the output image file */ - env.add_callback("md5Hash", 0, [icube](Arguments& args) { + env.add_callback("md5Hash", 0, [outputCubePath](Arguments& args) { md5wrapper md5; - return md5.getHashFromFile(icube->fileName()).toStdString(); + return md5.getHashFromFile(outputCubePath).toStdString(); }); std::string inputTemplate = ui.GetFileName("TEMPLATE").toStdString(); @@ -192,9 +190,5 @@ namespace Isis { jsonDataFile << dataSource.dump(4); jsonDataFile.close(); } - - std::cout << "============Result===============" << std::endl; - std::cout << result << std::endl; - std::cout << "=================================" << std::endl; } } diff --git a/isis/src/base/apps/topds4/topds4.xml b/isis/src/base/apps/topds4/topds4.xml index 02ba22ea82..adc58ea721 100644 --- a/isis/src/base/apps/topds4/topds4.xml +++ b/isis/src/base/apps/topds4/topds4.xml @@ -12,8 +12,17 @@

The image file is generated from the input ISIS cube specified by the FROM parameter. - The format of the output image file can be either an ISIS cube or a raw binary file containing - only the image DNs (i.e., no header or trailing data). + The output image file will a copy of the input ISIS cube with the image data stored in + Band Sequential format. +

+

+ This application uses the Inja templating engine + to render the template file. The input data from the cube and extra data sources + is converted to JSON and then it is accessed via the Inja templating syntax. + See the Inja documentation for the full template + syntax. The data from the label of the input cube is accessible under "MainLabel", + the data from the original label of the input cube is accessible under "OriginalLabel", + and the extra data is accessible under "ExtraPvl", "ExtraXml", and "ExtraJson".

@@ -65,7 +74,7 @@ The output file name of the PDS4 label. The output image name will be derived from this - parameter by removing the last extension and adding .cub + parameter by removing the last extension and adding .cub. diff --git a/isis/tests/FunctionalTestsTopds4.cpp b/isis/tests/FunctionalTestsTopds4.cpp index 742302b929..e49be73c16 100644 --- a/isis/tests/FunctionalTestsTopds4.cpp +++ b/isis/tests/FunctionalTestsTopds4.cpp @@ -355,12 +355,13 @@ TEST_F(SmallCube, FunctionalTestTopds4ImageFileName) { renderedStream.open(renderedFile.toStdString()); std::string line; std::getline(renderedStream, line); - EXPECT_EQ("small.img", line); + EXPECT_EQ("current_time.cub", line); } TEST_F(SmallCube, FunctionalTestTopds4MD5Hash) { QString templateFile = tempDir.path()+"/current_time.tpl"; QString renderedFile = tempDir.path()+"/current_time.txt"; + QString renderedCube = tempDir.path()+"/current_time.cub"; std::ofstream of; of.open(templateFile.toStdString()); of << "{{md5Hash()}}"; @@ -375,5 +376,5 @@ TEST_F(SmallCube, FunctionalTestTopds4MD5Hash) { std::string line; std::getline(renderedStream, line); md5wrapper md5; - EXPECT_EQ(md5.getHashFromFile(testCube->fileName()).toStdString(), line); + EXPECT_EQ(md5.getHashFromFile(renderedCube).toStdString(), line); } From a088ca8997b49b051fd84353055a1ed4f5aedd8b Mon Sep 17 00:00:00 2001 From: ssides Date: Fri, 8 Jan 2021 02:51:38 -0700 Subject: [PATCH 17/28] Added original xml label and tests --- .../templates/topds4/osirisrex.xml.tpl | 53 +++++++++++++++++++ isis/src/base/apps/topds4/topds4.cpp | 16 +++--- isis/src/base/apps/topds4/topds4.xml | 7 +-- isis/tests/FunctionalTestsTopds4.cpp | 50 +++++++++++++++++ 4 files changed, 116 insertions(+), 10 deletions(-) create mode 100644 isis/appdata/templates/topds4/osirisrex.xml.tpl diff --git a/isis/appdata/templates/topds4/osirisrex.xml.tpl b/isis/appdata/templates/topds4/osirisrex.xml.tpl new file mode 100644 index 0000000000..4eddb57ad9 --- /dev/null +++ b/isis/appdata/templates/topds4/osirisrex.xml.tpl @@ -0,0 +1,53 @@ + + + + + + + + + filename + dateandtime + filesize + +
+ headeroffset + headerLength + fileType +
+ + Active Area + imageOffsetBytes + numAxes + Last Index Fastest + OCAMS image 1024 by 1024 pixel active array. + + bitType + DN + 1 + WHAT IS THIS + + + Line + {{MainLabel.IsisCube.Core.Dimensions.Lines.Value}} + 1 + + + Sample + {{MainLabel.IsisCube.Core.Dimensions.Samples.Value}} + 2 + + +
+
diff --git a/isis/src/base/apps/topds4/topds4.cpp b/isis/src/base/apps/topds4/topds4.cpp index 1ce2ec412c..c00f69e02e 100644 --- a/isis/src/base/apps/topds4/topds4.cpp +++ b/isis/src/base/apps/topds4/topds4.cpp @@ -5,16 +5,15 @@ #include #include -#include "md5wrapper.h" +#include #include -#include "topds4.h" #include "cubeatt.h" - #include "CubeAttribute.h" #include "FileName.h" #include "md5wrapper.h" #include "OriginalLabel.h" +#include "OriginalXmlLabel.h" #include "PvlToJSON.h" #include "XmlToJson.h" @@ -67,7 +66,10 @@ namespace Isis { dataSource["OriginalLabel"].update(pvlToJSON(origLabel)); } else if (cubeLabel.hasObject("OriginalXmlLabel")) { - // get the xml label and add it to the template data + OriginalXmlLabel origXmlBlob; + icube->read(origXmlBlob); + QDomDocument doc = origXmlBlob.ReturnLabels(); + dataSource["OriginalLabel"].update(xmlToJson(doc)); } // Add any extra files to the template engine data @@ -165,9 +167,9 @@ namespace Isis { /** * Renders to the final file size in bytes of the output image file */ - env.add_callback("outputFileSize", 0, [outputFile](Arguments& args) { - FileName cubeFilename = outputFile; - return QFile(outputFile).size(); + env.add_callback("outputFileSize", 0, [outputCubePath](Arguments& args) { + FileName cubeFileName = outputCubePath; + return QFile(cubeFileName.expanded()).size(); }); /** diff --git a/isis/src/base/apps/topds4/topds4.xml b/isis/src/base/apps/topds4/topds4.xml index adc58ea721..e5a292f660 100644 --- a/isis/src/base/apps/topds4/topds4.xml +++ b/isis/src/base/apps/topds4/topds4.xml @@ -16,12 +16,13 @@ Band Sequential format.

- This application uses the Inja templating engine + This application uses the Inja templating engine to render the template file. The input data from the cube and extra data sources is converted to JSON and then it is accessed via the Inja templating syntax. - See the Inja documentation for the full template + See the Inja documentation for the full template syntax. The data from the label of the input cube is accessible under "MainLabel", - the data from the original label of the input cube is accessible under "OriginalLabel", + the data from the original PVL or XML based label of the input cube is accessible + under "OriginalLabel", and the extra data is accessible under "ExtraPvl", "ExtraXml", and "ExtraJson".

diff --git a/isis/tests/FunctionalTestsTopds4.cpp b/isis/tests/FunctionalTestsTopds4.cpp index e49be73c16..341e75aa0a 100644 --- a/isis/tests/FunctionalTestsTopds4.cpp +++ b/isis/tests/FunctionalTestsTopds4.cpp @@ -8,6 +8,7 @@ #include "Fixtures.h" #include "md5wrapper.h" #include "OriginalLabel.h" +#include "OriginalXmlLabel.h" #include "Pvl.h" #include "PvlGroup.h" #include "PvlKeyword.h" @@ -83,6 +84,35 @@ TEST_F(SmallCube, FunctionalTestTopds4NoOriginalLabel) { EXPECT_ANY_THROW(topds4(testCube, options)); } +TEST_F(SmallCube, FunctionalTestTopds4OriginalXmlLabel) { + + QString labelFileName = tempDir.path()+"/originallabel.xml"; + std::ofstream ofxml; + ofxml.open(labelFileName.toStdString()); + ofxml << R"( Something )"; + ofxml.close(); + OriginalXmlLabel origLabel; + origLabel.readFromXmlFile(labelFileName); + testCube->write(origLabel); + + QString templateFile = tempDir.path()+"/test_result.tpl"; + QString renderedFile = tempDir.path()+"/test_result.txt"; + std::ofstream of; + of.open(templateFile.toStdString()); + of << "{{OriginalLabel.Outside.name}}"; + of.close(); + QVector args = {"template=" + templateFile, "to=" + renderedFile}; + UserInterface options(APP_XML, args); + + topds4(testCube, options); + + std::ifstream renderedStream; + renderedStream.open(renderedFile.toStdString()); + std::string line; + std::getline(renderedStream, line); + EXPECT_EQ("Something", line); +} + TEST_F(SmallCube, FunctionalTestTopds4ExtraPvl) { QString pvlFile = tempDir.path()+"/extra.pvl"; Pvl testPvl; @@ -378,3 +408,23 @@ TEST_F(SmallCube, FunctionalTestTopds4MD5Hash) { md5wrapper md5; EXPECT_EQ(md5.getHashFromFile(renderedCube).toStdString(), line); } + +TEST_F(SmallCube, FunctionalTestTopds4OutputFileSize) { + QString templateFile = tempDir.path()+"/file_size.tpl"; + QString renderedFile = tempDir.path()+"/file_size.txt"; + std::ofstream of; + of.open(templateFile.toStdString()); + of << "{{outputFileSize()}}"; + of.close(); + QVector args = {"template=" + templateFile, "to=" + renderedFile}; + UserInterface options(APP_XML, args); + + topds4(testCube, options); + + std::ifstream renderedStream; + renderedStream.open(renderedFile.toStdString()); + std::string line; + std::getline(renderedStream, line); + EXPECT_EQ("69536", line); +} + From 54efa4e278fd6cc28d149d4f729f9c724b038a93 Mon Sep 17 00:00:00 2001 From: Kristin Berry Date: Fri, 8 Jan 2021 13:40:08 -0700 Subject: [PATCH 18/28] Update cubeattribute to include an option to supply CubeAttributeInput directly (#4244) * Update cubeatt to add cubeattributeinput option. Also add tests for all function signatures available * Remove unnecessary comments * Update to use explicit array conversion. * Remove commented out code --- isis/src/base/apps/cubeatt/cubeatt.cpp | 10 ++++ isis/src/base/apps/cubeatt/cubeatt.h | 1 + isis/src/base/objs/XmlToJson/XmlToJson.cpp | 8 ++- isis/tests/FunctionalTestsCubeatt.cpp | 68 +++++++++++++++++++++- 4 files changed, 84 insertions(+), 3 deletions(-) diff --git a/isis/src/base/apps/cubeatt/cubeatt.cpp b/isis/src/base/apps/cubeatt/cubeatt.cpp index 12a2414410..2412ade40c 100644 --- a/isis/src/base/apps/cubeatt/cubeatt.cpp +++ b/isis/src/base/apps/cubeatt/cubeatt.cpp @@ -44,6 +44,16 @@ namespace Isis { p.EndProcess(); } + // Allows specification of both input and output attributes + void cubeatt(QString inputCubePath, CubeAttributeInput inAtt, QString outputCubePath, CubeAttributeOutput outputAttributes, bool propTables) { + Cube icube; + if (inAtt.bands().size() != 0) { + icube.setVirtualBands(inAtt.bands()); + } + icube.open(inputCubePath); + cubeatt(&icube, outputCubePath, outputAttributes, propTables); + } + // Line processing routine void cubeattProcess(Buffer &in, Buffer &out) { // Loop and copy pixels in the line. diff --git a/isis/src/base/apps/cubeatt/cubeatt.h b/isis/src/base/apps/cubeatt/cubeatt.h index b853cedd7e..4e05b713f6 100644 --- a/isis/src/base/apps/cubeatt/cubeatt.h +++ b/isis/src/base/apps/cubeatt/cubeatt.h @@ -7,6 +7,7 @@ namespace Isis{ extern void cubeatt(Cube *icube, QString outputCubePath, CubeAttributeOutput outputAttributes, bool propTables=false); + extern void cubeatt(QString inputCubePath, CubeAttributeInput inputAttributes, QString outputCubePath, CubeAttributeOutput outputAttributes, bool propTables=false); extern void cubeatt(Cube *icube, UserInterface &ui); extern void cubeatt(UserInterface &ui); } diff --git a/isis/src/base/objs/XmlToJson/XmlToJson.cpp b/isis/src/base/objs/XmlToJson/XmlToJson.cpp index 02b613ae48..8fe8024f82 100644 --- a/isis/src/base/objs/XmlToJson/XmlToJson.cpp +++ b/isis/src/base/objs/XmlToJson/XmlToJson.cpp @@ -155,7 +155,9 @@ namespace Isis { // Translated json goal: a:[val1, val2] // If the converted json has an array already, append, else make it an array if (!output[element.tagName().toStdString()].is_array()) { - output[element.tagName().toStdString()] = {output[element.tagName().toStdString()]}; + json repeatedArray; + repeatedArray.push_back(output[element.tagName().toStdString()]); + output[element.tagName().toStdString()] = repeatedArray; } output[element.tagName().toStdString()].push_back(converted[element.tagName().toStdString()]); } @@ -171,7 +173,9 @@ namespace Isis { json temporaryJson; convertXmlToJson(next, temporaryJson); if (!output[element.tagName().toStdString()].is_array()) { - output[element.tagName().toStdString()] = {output[element.tagName().toStdString()]}; + json repeatedArray; + repeatedArray.push_back(output[element.tagName().toStdString()]); + output[element.tagName().toStdString()] = repeatedArray; } output[element.tagName().toStdString()].push_back(temporaryJson); } diff --git a/isis/tests/FunctionalTestsCubeatt.cpp b/isis/tests/FunctionalTestsCubeatt.cpp index d7d598a16d..8263454da3 100644 --- a/isis/tests/FunctionalTestsCubeatt.cpp +++ b/isis/tests/FunctionalTestsCubeatt.cpp @@ -34,6 +34,7 @@ TEST_F(SmallCube, FunctionalTestCubeattBitttypeAndRange) { EXPECT_FLOAT_EQ(outputStats->Maximum(), 1.0); } + TEST_F(SmallCube, FunctionalTestCubeattNoChange) { QString cubePath = tempDir.path() + "/NoChangeCubeatt.cub"; QVector args = {"from=" + testCube->fileName(), "to=" + cubePath}; @@ -61,7 +62,6 @@ TEST_F(SmallCube, FunctionalTestCubeattNoChange) { } - TEST_F(SmallCube, FunctionalTestCubeattVirtualBands) { QString cubePath = tempDir.path() + "/VirtualBandsCubeatt.cub"; QVector args = {"from=" + testCube->fileName() + "+3,2,4,2,1,5,7,6,4", "to=" + cubePath}; @@ -98,3 +98,69 @@ TEST_F(SmallCube, FunctionalTestCubeattVirtualBands) { } +// Test using an already open cube as input and ui +TEST_F(SmallCube, FunctionalTestCubeattInputCube) { + QString outputCubePath = tempDir.path() + "/bitTypeCubeatt.cub+8bit+0.0:1.0"; + QVector args = {"from=" + testCube->fileName(), "to=" + outputCubePath}; + UserInterface options(APP_XML, args); + + cubeatt(testCube, options); + Cube outputCube(outputCubePath); + + EXPECT_EQ(outputCube.pixelType(), PixelType::UnsignedByte); + // Setting the pixel range modifies the base/multiplier, so check those. + EXPECT_NE(outputCube.base(), 0); + EXPECT_NE(outputCube.multiplier(), 1); + + // Test the DNs + Statistics *outputStats = outputCube.statistics(); + EXPECT_FLOAT_EQ(outputStats->Minimum(), 0.0); + EXPECT_FLOAT_EQ(outputStats->Maximum(), 1.0); +} + + +// Test using an already open cube as input and specifying an output path and output attributes +TEST_F(SmallCube, FunctionalTestCubeattInputCubeOutputPath) { + QString outputCubePath = tempDir.path() + "/bitTypeCubeatt.cub"; + CubeAttributeOutput attributeOutput("+8bit+0.0:1.0"); + + cubeatt(testCube, outputCubePath, attributeOutput); + Cube outputCube(outputCubePath); + + // Setting the pixel range modifies the base/multiplier, so check those. + EXPECT_NE(outputCube.base(), 0); + EXPECT_NE(outputCube.multiplier(), 1); + + // Test the DNs + Statistics *outputStats = outputCube.statistics(); + EXPECT_FLOAT_EQ(outputStats->Minimum(), 0.0); + EXPECT_FLOAT_EQ(outputStats->Maximum(), 1.0); +} + +// Test using the input/output paths and cube attribute input and output passed in directly +TEST_F(SmallCube, FunctionalTestCubeattInputAndOutputAttributes) { + QString inputCubePath = testCube->fileName(); + CubeAttributeInput attributeInput("+3,2,4"); + QString outputCubePath = tempDir.path() + "/bitTypeAndVirtualBandsCubeatt.cub"; + CubeAttributeOutput attributeOutput("+200:300"); + + cubeatt(inputCubePath, attributeInput, outputCubePath, attributeOutput); + + Cube outputCube(outputCubePath); + + Statistics *outputStats = outputCube.statistics(); + EXPECT_GE(outputStats->Minimum(), 200); + EXPECT_LE(outputStats->Maximum(), 300); + EXPECT_EQ(outputCube.bandCount(), 3); + + Pvl *label = outputCube.label(); + PvlGroup bandBin = label->findObject("IsisCube").findGroup("BandBin"); + EXPECT_EQ(QString(bandBin["OriginalBand"][0]), "3"); + EXPECT_EQ(QString(bandBin["OriginalBand"][1]), "2"); + EXPECT_EQ(QString(bandBin["OriginalBand"][2]), "4"); + + // Test that DNs for each band have moved appropriately + EXPECT_DOUBLE_EQ(outputCube.statistics(1)->Average(), testCube->statistics(3)->Average()); + EXPECT_DOUBLE_EQ(outputCube.statistics(2)->Average(), testCube->statistics(2)->Average()); + EXPECT_DOUBLE_EQ(outputCube.statistics(3)->Average(), testCube->statistics(4)->Average()); +} From 0541f57a17d84f4001705cee979059cc5ea1f4c0 Mon Sep 17 00:00:00 2001 From: Kristin Date: Fri, 8 Jan 2021 14:15:32 -0700 Subject: [PATCH 19/28] Update CHANGELOG to include topds4 addition --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a751f50929..e10dd3e994 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,10 @@ update the Unreleased link so that it compares against the latest release tag. - Added documentation to lronaccal and lrowaccal to describe why there are negative DNs in I/F calibrated images. [#3860](https://github.com/USGS-Astrogeology/ISIS3/issues/3860) - Update qview MeasureTool to add an option to calculate distances using RA/DEC and update qview to show DEC/RA rather than LAT/LON in lower-right corner [#3371](https://github.com/USGS-Astrogeology/ISIS3/issues/3371) +- Added a new application, topds4, which generates an output PDS4 XML label and a PDS4-compliant +ISIS Cube from an input Cube, a PDS4 label template, and optionally additional input XML, PVL, or +JSON data. The Inja templating engine is used to render the output PDS4 label from the label template. +[#4246](https://github.com/USGS-Astrogeology/ISIS3/pull/4246) ### Fixed From 5fcd8b918d24a735c3979ae67bd30193db7648ce Mon Sep 17 00:00:00 2001 From: Stuart Sides Date: Mon, 11 Jan 2021 11:17:36 -0700 Subject: [PATCH 20/28] Delete osirisrex.xml.tpl Removed temporary file --- .../templates/topds4/osirisrex.xml.tpl | 53 ------------------- 1 file changed, 53 deletions(-) delete mode 100644 isis/appdata/templates/topds4/osirisrex.xml.tpl diff --git a/isis/appdata/templates/topds4/osirisrex.xml.tpl b/isis/appdata/templates/topds4/osirisrex.xml.tpl deleted file mode 100644 index 4eddb57ad9..0000000000 --- a/isis/appdata/templates/topds4/osirisrex.xml.tpl +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - filename - dateandtime - filesize - -
- headeroffset - headerLength - fileType -
- - Active Area - imageOffsetBytes - numAxes - Last Index Fastest - OCAMS image 1024 by 1024 pixel active array. - - bitType - DN - 1 - WHAT IS THIS - - - Line - {{MainLabel.IsisCube.Core.Dimensions.Lines.Value}} - 1 - - - Sample - {{MainLabel.IsisCube.Core.Dimensions.Samples.Value}} - 2 - - -
-
From 9323a48fe3712aa3b68052994c5da18daf73abfe Mon Sep 17 00:00:00 2001 From: Stuart Sides Date: Mon, 11 Jan 2021 11:19:07 -0700 Subject: [PATCH 21/28] Delete 20190612T090019S776_map_L0pan.xml.full Remove temporary file --- .../20190612T090019S776_map_L0pan.xml.full | 453 ------------------ 1 file changed, 453 deletions(-) delete mode 100644 isis/src/base/apps/topds4/20190612T090019S776_map_L0pan.xml.full diff --git a/isis/src/base/apps/topds4/20190612T090019S776_map_L0pan.xml.full b/isis/src/base/apps/topds4/20190612T090019S776_map_L0pan.xml.full deleted file mode 100644 index 9c742be987..0000000000 --- a/isis/src/base/apps/topds4/20190612T090019S776_map_L0pan.xml.full +++ /dev/null @@ -1,453 +0,0 @@ - - - - - - - - urn:nasa:pds:orex.ocams:data_raw:20190612t090019s776_map_l0pan.fits - 1.0 - OSIRIS-REx OCAMS Level 0 (Raw) Science Image Data Product 2019-06-12T09:00:19.776Z - 1.7.0.0 - Product_Observational - - - 2019-02-01 - 1.0 - Initial version. - - - - - - 2019-06-12T09:00:19.776Z - 2019-06-12T09:00:19.776Z - - - Navigation - Raw - - - OSIRIS-REx - Mission - - urn:nasa:pds:context:investigation:mission.orex - data_to_investigation - - - - OSIRIS-REx Camera Suite (OCAMS) - - OSIRIS-REx - Spacecraft - Origins, Spectral Interpretation, Resource Identification, Security - Regolith Explorer Spacecraft - - urn:nasa:pds:context:instrument_host:spacecraft.orex - is_instrument_host - - - - OCAMS - Instrument - - urn:nasa:pds:context:instrument:ocams.orex - is_instrument - - - - - (101955) BENNU - 1999 RQ36 - Asteroid - - urn:nasa:pds:context:target:asteroid.101955_bennu - data_to_target - - - - - 1 - 1 - 0 - 1 - 0 - 0 - 0 - 3 - 1 - 1 - 238 - 255 - 237 - 255 - 60 - 14 - 96 - 120 - 842 - 84.24651416662068 - 719 - 43.8526184712785 - 1059 - 429.230742563867 - 46 - -1.1373317099755598 - 4 - -0.5355396 - 3 - -0.27067044 - 4 - 0.124204 - 248 - 13 - 72 - 1870 - 7.54142128487916 - 4.5 - 1 - PAN - 14 - 0 - 0 - 8 - 1871 - 7.56243576238131 - 3 - 0.003663 - 1 - 5 - 0 - 0 - 1674 - 12.059041040441798 - 97 - 0 - 751 - -23.390592459093 - 0 - 0 - 3232 - -26.7914936257828 - 3165 - -24.332170096750897 - 3370 - -32.754124351166396 - 0 - 0 - 3138 - -23.3990822840391 - 0 - 1872 - 7.54040302308914 - 270 - 0 - 13 - 97 - 97 - 0 - 807 - -24.7333747890882 - 0 - 0 - 3205 - -25.7736252190564 - 3213 - -26.071207848051596 - 3267 - -28.172561670290598 - 3726 - 0 - 0 - 3152 - -23.8791462673093 - 0 - 0 - 3725 - 0 - 0 - 0 - 0 - L13H08 - 1 - 0 - 844 - -18.835592356994898 - 0 - 0 - 3262 - -27.970663212435397 - 3257 - -27.7703534433846 - 3228 - -26.638223638577198 - 0 - 0 - 3145 - -23.638126058871002 - 2 - -64364 - 613601975 - 1 - 0 - 8217 - 5775 - 60 - 16 - 4 - 1551 - 4.5450550530000005 - 1551 - 4.5450550530000005 - 1781 - -12.115658567999999 - 1642 - -24.05860042 - 2146 - 11.97837112 - 1785 - 23.97435285 - 2026 - 4.947496052 - 3715 - 31.99133865 - 2049 - 5.003662098 - R13H08 - - - 2019-06-12T09:00:19.776Z - 0.0026426375 - 0.005285275 - 5.285275 - 2019-06-12T14:32:51.302Z - 5 - 2019-06-12T09:00:19.778Z - 613602088.962634 - 3/0613601975.37847 - - - 3549 - - - LIGHT - - - RECONSTRUCT - spoc-digest-2019-06-17T00_48_23.231Z.mk - 0 - 0 - 0 - PASS - - - 78 - - - 282.469051278191 - -65.8502362992963 - -316.966816351563 - -321.997774969665 - 296.28478659027 - BEST: SPK - BENNU - -46.0083163134579 - -40.9783428662411 - -0.00051406896314878 - 0.00385569171518446 - 0.00385569171518446 - 0.00051406896314878 - RA---TAN - DEC--TAN - deg - deg - 2000 - BENNU - ICRS - -12.2640686780359 - 192379784.023255 - 0.00196952834966507 - 0.977178977312025 - 0.0000335902873759028 - -0.21241761972849 - 192379784.650832 - SUCCESS - BENNU - 165861882.428622 - 85156422.1781311 - 47415200.7414764 - - - control.csv - SPOC-b94243cbd36c20997d72136d48d6a44f08455dba - FLIGHT - bottom-left - 2020-01-17T19:16:15.652Z - 15575 - 353 - b94243cbd36c20997d72136d48d6a44f08455dba - DN - - - orb - Orbit B - - - - - - Active Area - display_settings_to_array - - - Sample - Left to Right - Line - Bottom to Top - - - - - Active Array and Extended Pixel Regions - display_settings_to_array - - - Sample - Left to Right - Line - Bottom to Top - - - - - - MK - spoc-digest-2019-06-17T00_48_23.231Z.mk - Reconstructed - - - - - Sample - Left to Right - Line - Bottom to Top - - - - 512.5 - 512.5 - - 298.920801462901 - -64.4854892107989 - 82.4056963199464 - - J2000 - - - - 0.209125099096568 - 0.959028055505082 - 0.178608607528731 - 0.0680503269546892 - - J2000 - - - Spacecraft - - - - 0.209704337661567 - 0.958472331073572 - 0.180468563530547 - 0.0691807697957836 - - J2000 - - - Instrument - - - - - 2019-06-12T09:00:19.778Z - 613602088.962634 - - - - - - - {{imageFileName()}} - {{currentTime()}} - {{outputFileSize()}} - -
- 0 - {{ int(MainLabel.IsisCube.Core.StartByte.Value) - 2}} - ISIS3 -
- - Active Area - {{ int(MainLabel.IsisCube.Core.Pixels.Multiplier.Value) - 1}} - 2 - Last Index Fastest - OCAMS image 1024 by 1024 pixel active array. - - UnsignedMSB2 - DN - {{MainLabel.IsisCube.Core.Pixels.Multiplier.Value}} - {{MainLabel.IsisCube.Core.Pixels.Base.Value}} - - - Line - {{MainLabel.IsisCube.Core.Dimensions.Lines.Value}} - 1 - - - Sample - {{MainLabel.IsisCube.Core.Dimensions.Samples.Value}} - 2 - - -
- 0 - TBD - ISIS3 -
- - Active Array and Extended Pixel Regions - TBD - 2 - Last Index Fastest - OCAMS 1044 sample by 1112 line extended pixel array. The extended pixel array contains the detector overscan and dark pixels. - - UnsignedMSB2 - DN - TBD - TBD - - - Line - TBD - 1 - - - Sample - TBD - 2 - - -
-
From 9bc187e76b03fa455be4096f06503028234254b6 Mon Sep 17 00:00:00 2001 From: Stuart Sides Date: Mon, 11 Jan 2021 11:24:28 -0700 Subject: [PATCH 22/28] Delete 20190612T090019S776_map_L0pan.xml.tpl Remove temp file --- .../20190612T090019S776_map_L0pan.xml.tpl | 53 ------------------- 1 file changed, 53 deletions(-) delete mode 100644 isis/src/base/apps/topds4/20190612T090019S776_map_L0pan.xml.tpl diff --git a/isis/src/base/apps/topds4/20190612T090019S776_map_L0pan.xml.tpl b/isis/src/base/apps/topds4/20190612T090019S776_map_L0pan.xml.tpl deleted file mode 100644 index 4eddb57ad9..0000000000 --- a/isis/src/base/apps/topds4/20190612T090019S776_map_L0pan.xml.tpl +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - filename - dateandtime - filesize - -
- headeroffset - headerLength - fileType -
- - Active Area - imageOffsetBytes - numAxes - Last Index Fastest - OCAMS image 1024 by 1024 pixel active array. - - bitType - DN - 1 - WHAT IS THIS - - - Line - {{MainLabel.IsisCube.Core.Dimensions.Lines.Value}} - 1 - - - Sample - {{MainLabel.IsisCube.Core.Dimensions.Samples.Value}} - 2 - - -
-
From c56d5c4407785bc8c86c7eb37fb76c26cc71a955 Mon Sep 17 00:00:00 2001 From: Stuart Sides Date: Mon, 11 Jan 2021 13:13:17 -0700 Subject: [PATCH 23/28] Added mis-deleted file (#4249) --- .../templates/topds4/osirisrex.xml.tpl | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 isis/appdata/templates/topds4/osirisrex.xml.tpl diff --git a/isis/appdata/templates/topds4/osirisrex.xml.tpl b/isis/appdata/templates/topds4/osirisrex.xml.tpl new file mode 100644 index 0000000000..4eddb57ad9 --- /dev/null +++ b/isis/appdata/templates/topds4/osirisrex.xml.tpl @@ -0,0 +1,53 @@ + + + + + + + + + filename + dateandtime + filesize + +
+ headeroffset + headerLength + fileType +
+ + Active Area + imageOffsetBytes + numAxes + Last Index Fastest + OCAMS image 1024 by 1024 pixel active array. + + bitType + DN + 1 + WHAT IS THIS + + + Line + {{MainLabel.IsisCube.Core.Dimensions.Lines.Value}} + 1 + + + Sample + {{MainLabel.IsisCube.Core.Dimensions.Samples.Value}} + 2 + + +
+
From cfc58cfc1dbe555b7229508cba06030d5dfac367 Mon Sep 17 00:00:00 2001 From: Kristin Berry Date: Mon, 11 Jan 2021 14:41:30 -0700 Subject: [PATCH 24/28] Update XmlToJson.cpp --- isis/src/base/objs/XmlToJson/XmlToJson.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isis/src/base/objs/XmlToJson/XmlToJson.cpp b/isis/src/base/objs/XmlToJson/XmlToJson.cpp index 8fe8024f82..71425fb88a 100644 --- a/isis/src/base/objs/XmlToJson/XmlToJson.cpp +++ b/isis/src/base/objs/XmlToJson/XmlToJson.cpp @@ -131,7 +131,7 @@ namespace Isis { * JSON: a : {b: val1, c: val2} * * XML: value1 value2 - * JSON: a: [ {first:value1, second:value2} ] + * JSON: a: [ {first:value1}, {second:value2} ] * * XML: val1val2 * JSON: a:[val1, val2] From 4b4f9cfa702ee28b23779416e75d17bdb0abba0c Mon Sep 17 00:00:00 2001 From: Kristin Berry Date: Mon, 11 Jan 2021 14:59:00 -0700 Subject: [PATCH 25/28] Update topds4.xml --- isis/src/base/apps/topds4/topds4.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isis/src/base/apps/topds4/topds4.xml b/isis/src/base/apps/topds4/topds4.xml index e5a292f660..d3116f6e32 100644 --- a/isis/src/base/apps/topds4/topds4.xml +++ b/isis/src/base/apps/topds4/topds4.xml @@ -2,7 +2,7 @@ - Convert from ISIS3 cube to PDS 4 format using template XML files + Convert from ISIS cube to PDS 4 format using template XML files From 102e32047fcd1629dbd5f2fd78296f52f6589993 Mon Sep 17 00:00:00 2001 From: Kristin Berry Date: Mon, 11 Jan 2021 15:01:18 -0700 Subject: [PATCH 26/28] Update topds4.xml --- isis/src/base/apps/topds4/topds4.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isis/src/base/apps/topds4/topds4.xml b/isis/src/base/apps/topds4/topds4.xml index d3116f6e32..95b2b29308 100644 --- a/isis/src/base/apps/topds4/topds4.xml +++ b/isis/src/base/apps/topds4/topds4.xml @@ -21,7 +21,7 @@ is converted to JSON and then it is accessed via the Inja templating syntax. See the Inja documentation for the full template syntax. The data from the label of the input cube is accessible under "MainLabel", - the data from the original PVL or XML based label of the input cube is accessible + the data from the originally ingested PVL or XML label stored on the input cube is accessible under "OriginalLabel", and the extra data is accessible under "ExtraPvl", "ExtraXml", and "ExtraJson".

From 1abb8d3926323c8e5aade6439a7d4e537d4bbdab Mon Sep 17 00:00:00 2001 From: Kristin Berry Date: Mon, 11 Jan 2021 15:03:38 -0700 Subject: [PATCH 27/28] Update topds4.xml --- isis/src/base/apps/topds4/topds4.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isis/src/base/apps/topds4/topds4.xml b/isis/src/base/apps/topds4/topds4.xml index 95b2b29308..4ddf4947de 100644 --- a/isis/src/base/apps/topds4/topds4.xml +++ b/isis/src/base/apps/topds4/topds4.xml @@ -12,7 +12,7 @@

The image file is generated from the input ISIS cube specified by the FROM parameter. - The output image file will a copy of the input ISIS cube with the image data stored in + The output image file will be a copy of the input ISIS cube with the image data stored in Band Sequential format.

From 33a404dc3cf8b3fbebed168daa0c9953b3080dff Mon Sep 17 00:00:00 2001 From: Kristin Date: Mon, 11 Jan 2021 15:33:55 -0700 Subject: [PATCH 28/28] Add setting of virtual bands to topds4 and remove unneeded header file includes --- isis/src/base/apps/topds4/topds4.cpp | 5 +++++ isis/src/base/apps/topds4/topds4.h | 5 ----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/isis/src/base/apps/topds4/topds4.cpp b/isis/src/base/apps/topds4/topds4.cpp index c00f69e02e..abef32c0be 100644 --- a/isis/src/base/apps/topds4/topds4.cpp +++ b/isis/src/base/apps/topds4/topds4.cpp @@ -14,6 +14,7 @@ #include "md5wrapper.h" #include "OriginalLabel.h" #include "OriginalXmlLabel.h" +#include "Process.h" #include "PvlToJSON.h" #include "XmlToJson.h" @@ -29,6 +30,10 @@ namespace Isis { void topds4(UserInterface &ui, Pvl *log) { Cube *icube = new Cube(); icube->open(ui.GetFileName("FROM")); + CubeAttributeInput inAtt = ui.GetInputAttribute("FROM"); + if (inAtt.bands().size() != 0) { + icube->setVirtualBands(inAtt.bands()); + } topds4(icube, ui); } diff --git a/isis/src/base/apps/topds4/topds4.h b/isis/src/base/apps/topds4/topds4.h index 5551cce7a5..1bc05e1ac6 100644 --- a/isis/src/base/apps/topds4/topds4.h +++ b/isis/src/base/apps/topds4/topds4.h @@ -3,11 +3,6 @@ #include "Cube.h" #include "Pvl.h" -#include "Process.h" -#include "SpecialPixel.h" -#include "LineManager.h" -#include "FileName.h" -#include "IException.h" #include "UserInterface.h" namespace Isis {