diff --git a/include/rapidjson/cursorstreamwrapper.h b/include/rapidjson/cursorstreamwrapper.h new file mode 100644 index 0000000..52c11a7 --- /dev/null +++ b/include/rapidjson/cursorstreamwrapper.h @@ -0,0 +1,78 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_CURSORSTREAMWRAPPER_H_ +#define RAPIDJSON_CURSORSTREAMWRAPPER_H_ + +#include "stream.h" + +#if defined(__GNUC__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4702) // unreachable code +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +RAPIDJSON_NAMESPACE_BEGIN + + +//! Cursor stream wrapper for counting line and column number if error exists. +/*! + \tparam InputStream Any stream that implements Stream Concept +*/ +template > +class CursorStreamWrapper : public GenericStreamWrapper { +public: + typedef typename Encoding::Ch Ch; + + CursorStreamWrapper(InputStream& is): + GenericStreamWrapper(is), line_(1), col_(0) {} + + // counting line and column number + Ch Take() { + Ch ch = this->is_.Take(); + if(ch == '\n') { + line_ ++; + col_ = 0; + } else { + col_ ++; + } + return ch; + } + + //! Get the error line number, if error exists. + size_t GetLine() const { return line_; } + //! Get the error column number, if error exists. + size_t GetColumn() const { return col_; } + +private: + size_t line_; //!< Current Line + size_t col_; //!< Current Column +}; + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_POP +#endif + +#if defined(__GNUC__) +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_CURSORSTREAMWRAPPER_H_ diff --git a/include/rapidjson/stream.h b/include/rapidjson/stream.h index fef82c2..7f2643e 100644 --- a/include/rapidjson/stream.h +++ b/include/rapidjson/stream.h @@ -1,5 +1,5 @@ // Tencent is pleased to support the open source community by making RapidJSON available. -// +// // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except @@ -7,9 +7,9 @@ // // http://opensource.org/licenses/MIT // -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #include "rapidjson.h" @@ -100,6 +100,50 @@ inline void PutN(Stream& stream, Ch c, size_t n) { PutUnsafe(stream, c); } +/////////////////////////////////////////////////////////////////////////////// +// GenericStreamWrapper + +//! A Stream Wrapper +/*! \tThis string stream is a wrapper for any stream by just forwarding any + \treceived message to the origin stream. + \note implements Stream concept +*/ + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4702) // unreachable code +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +template > +class GenericStreamWrapper { +public: + typedef typename Encoding::Ch Ch; + GenericStreamWrapper(InputStream& is): is_(is) {} + + Ch Peek() const { return is_.Peek(); } + Ch Take() { return is_.Take(); } + size_t Tell() { return is_.Tell(); } + Ch* PutBegin() { return is_.PutBegin(); } + void Put(Ch ch) { is_.Put(ch); } + void Flush() { is_.Flush(); } + size_t PutEnd(Ch* ch) { return is_.PutEnd(ch); } + + // wrapper for MemoryStream + const Ch* Peek4() const { return is_.Peek4(); } + + // wrapper for AutoUTFInputStream + UTFType GetType() const { return is_.GetType(); } + bool HasBOM() const { return is_.HasBOM(); } + +protected: + InputStream& is_; +}; + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_POP +#endif + /////////////////////////////////////////////////////////////////////////////// // StringStream diff --git a/test/unittest/CMakeLists.txt b/test/unittest/CMakeLists.txt index fdf0ad0..072b7b1 100644 --- a/test/unittest/CMakeLists.txt +++ b/test/unittest/CMakeLists.txt @@ -3,6 +3,7 @@ include(CheckCXXCompilerFlag) set(UNITTEST_SOURCES allocatorstest.cpp bigintegertest.cpp + cursorstreamwrappertest.cpp documenttest.cpp dtoatest.cpp encodedstreamtest.cpp diff --git a/test/unittest/cursorstreamwrappertest.cpp b/test/unittest/cursorstreamwrappertest.cpp new file mode 100644 index 0000000..a116248 --- /dev/null +++ b/test/unittest/cursorstreamwrappertest.cpp @@ -0,0 +1,115 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#include "unittest.h" +#include "rapidjson/document.h" +#include "rapidjson/cursorstreamwrapper.h" + +using namespace rapidjson; + +// static const char json[] = "{\"string\"\n\n:\"my string\",\"array\"\n:[\"1\", \"2\", \"3\"]}"; + +bool testJson(const char *json, size_t &line, size_t &col) { + StringStream ss(json); + CursorStreamWrapper csw(ss); + Document document; + document.ParseStream(csw); + bool ret = document.HasParseError(); + if (ret) { + col = csw.GetColumn(); + line = csw.GetLine(); + } + return ret; +} + +TEST(CursorStreamWrapper, MissingFirstBracket) { + const char json[] = "\"string\"\n\n:\"my string\",\"array\"\n:[\"1\", \"2\", \"3\"]}"; + size_t col, line; + bool ret = testJson(json, line, col); + EXPECT_TRUE(ret); + EXPECT_EQ(line, 3); + EXPECT_EQ(col, 0); +} + +TEST(CursorStreamWrapper, MissingQuotes) { + const char json[] = "{\"string\n\n:\"my string\",\"array\"\n:[\"1\", \"2\", \"3\"]}"; + size_t col, line; + bool ret = testJson(json, line, col); + EXPECT_TRUE(ret); + EXPECT_EQ(line, 1); + EXPECT_EQ(col, 8); +} + +TEST(CursorStreamWrapper, MissingColon) { + const char json[] = "{\"string\"\n\n\"my string\",\"array\"\n:[\"1\", \"2\", \"3\"]}"; + size_t col, line; + bool ret = testJson(json, line, col); + EXPECT_TRUE(ret); + EXPECT_EQ(line, 3); + EXPECT_EQ(col, 0); +} + +TEST(CursorStreamWrapper, MissingSecondQuotes) { + const char json[] = "{\"string\"\n\n:my string\",\"array\"\n:[\"1\", \"2\", \"3\"]}"; + size_t col, line; + bool ret = testJson(json, line, col); + EXPECT_TRUE(ret); + EXPECT_EQ(line, 3); + EXPECT_EQ(col, 1); +} + +TEST(CursorStreamWrapper, MissingComma) { + const char json[] = "{\"string\"\n\n:\"my string\"\"array\"\n:[\"1\", \"2\", \"3\"]}"; + size_t col, line; + bool ret = testJson(json, line, col); + EXPECT_TRUE(ret); + EXPECT_EQ(line, 3); + EXPECT_EQ(col, 12); +} + +TEST(CursorStreamWrapper, MissingArrayBracket) { + const char json[] = "{\"string\"\n\n:\"my string\",\"array\"\n:\"1\", \"2\", \"3\"]}"; + size_t col, line; + bool ret = testJson(json, line, col); + EXPECT_TRUE(ret); + EXPECT_EQ(line, 4); + EXPECT_EQ(col, 9); +} + +TEST(CursorStreamWrapper, MissingArrayComma) { + const char json[] = "{\"string\"\n\n:\"my string\",\"array\"\n:[\"1\" \"2\", \"3\"]}"; + size_t col, line; + bool ret = testJson(json, line, col); + EXPECT_TRUE(ret); + EXPECT_EQ(line, 4); + EXPECT_EQ(col, 6); +} + +TEST(CursorStreamWrapper, MissingLastArrayBracket) { + const char json8[] = "{\"string\"\n\n:\"my string\",\"array\"\n:[\"1\", \"2\", \"3\"}"; + size_t col, line; + bool ret = testJson(json8, line, col); + EXPECT_TRUE(ret); + EXPECT_EQ(line, 4); + EXPECT_EQ(col, 15); +} + +TEST(CursorStreamWrapper, MissingLastBracket) { + const char json9[] = "{\"string\"\n\n:\"my string\",\"array\"\n:[\"1\", \"2\", \"3\"]"; + size_t col, line; + bool ret = testJson(json9, line, col); + EXPECT_TRUE(ret); + EXPECT_EQ(line, 4); + EXPECT_EQ(col, 16); +}