| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
- // Protocol Buffers - Google's data interchange format
- // Copyright 2008 Google Inc. All rights reserved.
- // https://developers.google.com/protocol-buffers/
- //
- // Redistribution and use in source and binary forms, with or without
- // modification, are permitted provided that the following conditions are
- // met:
- //
- // * Redistributions of source code must retain the above copyright
- // notice, this list of conditions and the following disclaimer.
- // * Redistributions in binary form must reproduce the above
- // copyright notice, this list of conditions and the following disclaimer
- // in the documentation and/or other materials provided with the
- // distribution.
- // * Neither the name of Google Inc. nor the names of its
- // contributors may be used to endorse or promote products derived from
- // this software without specific prior written permission.
- //
- // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- #include <google/protobuf/util/internal/field_mask_utility.h>
- #include <google/protobuf/util/internal/utility.h>
- #include <google/protobuf/stubs/strutil.h>
- #include <google/protobuf/stubs/status_macros.h>
- namespace google {
- namespace protobuf {
- namespace util {
- namespace converter {
- namespace {
- inline util::Status CallPathSink(PathSinkCallback path_sink,
- StringPiece arg) {
- return path_sink->Run(arg);
- }
- // Appends a FieldMask path segment to a prefix.
- string AppendPathSegmentToPrefix(StringPiece prefix, StringPiece segment) {
- if (prefix.empty()) {
- return segment.ToString();
- }
- if (segment.empty()) {
- return prefix.ToString();
- }
- // If the segment is a map key, appends it to the prefix without the ".".
- if (StringStartsWith(segment, "[\"")) {
- return StrCat(prefix, segment);
- }
- return StrCat(prefix, ".", segment);
- }
- } // namespace
- string ConvertFieldMaskPath(const StringPiece path,
- ConverterCallback converter) {
- string result;
- result.reserve(path.size() << 1);
- bool is_quoted = false;
- bool is_escaping = false;
- int current_segment_start = 0;
- // Loops until 1 passed the end of the input to make handling the last
- // segment easier.
- for (size_t i = 0; i <= path.size(); ++i) {
- // Outputs quoted string as-is.
- if (is_quoted) {
- if (i == path.size()) {
- break;
- }
- result.push_back(path[i]);
- if (is_escaping) {
- is_escaping = false;
- } else if (path[i] == '\\') {
- is_escaping = true;
- } else if (path[i] == '\"') {
- current_segment_start = i + 1;
- is_quoted = false;
- }
- continue;
- }
- if (i == path.size() || path[i] == '.' || path[i] == '(' ||
- path[i] == ')' || path[i] == '\"') {
- result += converter(
- path.substr(current_segment_start, i - current_segment_start));
- if (i < path.size()) {
- result.push_back(path[i]);
- }
- current_segment_start = i + 1;
- }
- if (i < path.size() && path[i] == '\"') {
- is_quoted = true;
- }
- }
- return result;
- }
- util::Status DecodeCompactFieldMaskPaths(StringPiece paths,
- PathSinkCallback path_sink) {
- std::stack<string> prefix;
- int length = paths.length();
- int previous_position = 0;
- bool in_map_key = false;
- bool is_escaping = false;
- // Loops until 1 passed the end of the input to make the handle of the last
- // segment easier.
- for (int i = 0; i <= length; ++i) {
- if (i != length) {
- // Skips everything in a map key until we hit the end of it, which is
- // marked by an un-escaped '"' immediately followed by a ']'.
- if (in_map_key) {
- if (is_escaping) {
- is_escaping = false;
- continue;
- }
- if (paths[i] == '\\') {
- is_escaping = true;
- continue;
- }
- if (paths[i] != '\"') {
- continue;
- }
- // Un-escaped '"' must be followed with a ']'.
- if (i >= length - 1 || paths[i + 1] != ']') {
- return util::Status(
- util::error::INVALID_ARGUMENT,
- StrCat("Invalid FieldMask '", paths,
- "'. Map keys should be represented as [\"some_key\"]."));
- }
- // The end of the map key ("\"]") has been found.
- in_map_key = false;
- // Skips ']'.
- i++;
- // Checks whether the key ends at the end of a path segment.
- if (i < length - 1 && paths[i + 1] != '.' && paths[i + 1] != ',' &&
- paths[i + 1] != ')' && paths[i + 1] != '(') {
- return util::Status(
- util::error::INVALID_ARGUMENT,
- StrCat("Invalid FieldMask '", paths,
- "'. Map keys should be at the end of a path segment."));
- }
- is_escaping = false;
- continue;
- }
- // We are not in a map key, look for the start of one.
- if (paths[i] == '[') {
- if (i >= length - 1 || paths[i + 1] != '\"') {
- return util::Status(
- util::error::INVALID_ARGUMENT,
- StrCat("Invalid FieldMask '", paths,
- "'. Map keys should be represented as [\"some_key\"]."));
- }
- // "[\"" starts a map key.
- in_map_key = true;
- i++; // Skips the '\"'.
- continue;
- }
- // If the current character is not a special character (',', '(' or ')'),
- // continue to the next.
- if (paths[i] != ',' && paths[i] != ')' && paths[i] != '(') {
- continue;
- }
- }
- // Gets the current segment - sub-string between previous position (after
- // '(', ')', ',', or the beginning of the input) and the current position.
- StringPiece segment =
- paths.substr(previous_position, i - previous_position);
- string current_prefix = prefix.empty() ? "" : prefix.top();
- if (i < length && paths[i] == '(') {
- // Builds a prefix and save it into the stack.
- prefix.push(AppendPathSegmentToPrefix(current_prefix, segment));
- } else if (!segment.empty()) {
- // When the current charactor is ')', ',' or the current position has
- // passed the end of the input, builds and outputs a new paths by
- // concatenating the last prefix with the current segment.
- RETURN_IF_ERROR(CallPathSink(
- path_sink, AppendPathSegmentToPrefix(current_prefix, segment)));
- }
- // Removes the last prefix after seeing a ')'.
- if (i < length && paths[i] == ')') {
- if (prefix.empty()) {
- return util::Status(
- util::error::INVALID_ARGUMENT,
- StrCat("Invalid FieldMask '", paths,
- "'. Cannot find matching '(' for all ')'."));
- }
- prefix.pop();
- }
- previous_position = i + 1;
- }
- if (in_map_key) {
- return util::Status(util::error::INVALID_ARGUMENT,
- StrCat("Invalid FieldMask '", paths,
- "'. Cannot find matching ']' for all '['."));
- }
- if (!prefix.empty()) {
- return util::Status(util::error::INVALID_ARGUMENT,
- StrCat("Invalid FieldMask '", paths,
- "'. Cannot find matching ')' for all '('."));
- }
- return util::Status();
- }
- } // namespace converter
- } // namespace util
- } // namespace protobuf
- } // namespace google
|