| /* |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| 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 "dse_line_string.hpp" |
| #include "dse_validate.hpp" |
| |
| #include "string_ref.hpp" |
| |
| #include <assert.h> |
| #include <iomanip> |
| #include <sstream> |
| |
| using namespace datastax; |
| using namespace datastax::internal::core; |
| using namespace datastax::internal::enterprise; |
| |
| extern "C" { |
| |
| DseLineString* dse_line_string_new() { return DseLineString::to(new LineString()); } |
| |
| void dse_line_string_free(DseLineString* line_string) { delete line_string->from(); } |
| |
| void dse_line_string_reset(DseLineString* line_string) { line_string->reset(); } |
| |
| void dse_line_string_reserve(DseLineString* line_string, cass_uint32_t num_points) { |
| line_string->reserve(num_points); |
| } |
| |
| CassError dse_line_string_add_point(DseLineString* line_string, cass_double_t x, cass_double_t y) { |
| line_string->add_point(x, y); |
| return CASS_OK; |
| } |
| |
| CassError dse_line_string_finish(DseLineString* line_string) { return line_string->finish(); } |
| |
| DseLineStringIterator* dse_line_string_iterator_new() { |
| return DseLineStringIterator::to(new LineStringIterator()); |
| } |
| |
| void dse_line_string_iterator_free(DseLineStringIterator* iterator) { delete iterator->from(); } |
| |
| CassError dse_line_string_iterator_reset(DseLineStringIterator* iterator, const CassValue* value) { |
| return iterator->reset_binary(value); |
| } |
| |
| CassError dse_line_string_iterator_reset_with_wkt_n(DseLineStringIterator* iterator, |
| const char* wkt, size_t wkt_length) { |
| return iterator->reset_text(wkt, wkt_length); |
| } |
| |
| CassError dse_line_string_iterator_reset_with_wkt(DseLineStringIterator* iterator, |
| const char* wkt) { |
| return dse_line_string_iterator_reset_with_wkt_n(iterator, wkt, SAFE_STRLEN(wkt)); |
| } |
| |
| cass_uint32_t dse_line_string_iterator_num_points(const DseLineStringIterator* iterator) { |
| return iterator->num_points(); |
| } |
| |
| CassError dse_line_string_iterator_next_point(DseLineStringIterator* iterator, cass_double_t* x, |
| cass_double_t* y) { |
| return iterator->next_point(x, y); |
| } |
| |
| } // extern "C" |
| |
| String LineString::to_wkt() const { |
| // Special case empty line string |
| if (num_points_ == 0) { |
| return "LINESTRING EMPTY"; |
| } |
| |
| OStringStream ss; |
| ss.precision(WKT_MAX_DIGITS); |
| ss << "LINESTRING ("; |
| const cass_byte_t* pos = bytes_.data() + WKB_HEADER_SIZE + sizeof(cass_uint32_t); |
| for (cass_uint32_t i = 0; i < num_points_; ++i) { |
| if (i > 0) ss << ", "; |
| ss << decode_double(pos, native_byte_order()); |
| pos += sizeof(cass_double_t); |
| ss << " "; |
| ss << decode_double(pos, native_byte_order()); |
| pos += sizeof(cass_double_t); |
| } |
| ss << ")"; |
| return ss.str(); |
| } |
| |
| CassError LineStringIterator::reset_binary(const CassValue* value) { |
| size_t size = 0; |
| const cass_byte_t* pos = NULL; |
| WkbByteOrder byte_order; |
| cass_uint32_t num_points = 0; |
| CassError rc = CASS_OK; |
| |
| rc = validate_data_type(value, DSE_LINE_STRING_TYPE); |
| if (rc != CASS_OK) return rc; |
| |
| rc = cass_value_get_bytes(value, &pos, &size); |
| if (rc != CASS_OK) return rc; |
| |
| if (size < WKB_LINE_STRING_HEADER_SIZE) { |
| return CASS_ERROR_LIB_NOT_ENOUGH_DATA; |
| } |
| size -= WKB_LINE_STRING_HEADER_SIZE; |
| |
| if (decode_header(pos, &byte_order) != WKB_GEOMETRY_TYPE_LINESTRING) { |
| return CASS_ERROR_LIB_INVALID_DATA; |
| } |
| pos += WKB_HEADER_SIZE; |
| |
| num_points = decode_uint32(pos, byte_order); |
| pos += sizeof(cass_uint32_t); |
| |
| if (size < 2 * num_points * sizeof(cass_double_t)) { |
| return CASS_ERROR_LIB_NOT_ENOUGH_DATA; |
| } |
| |
| num_points_ = num_points; |
| binary_iterator_ = BinaryIterator(pos, pos + size, byte_order); |
| iterator_ = &binary_iterator_; |
| |
| return CASS_OK; |
| } |
| |
| bool isnum(int c) { return isdigit(c) || c == '+' || c == '-' || c == '.'; } |
| |
| CassError LineStringIterator::reset_text(const char* text, size_t size) { |
| cass_uint32_t num_points = 0; |
| const bool skip_numbers = true; |
| WktLexer lexer(text, size, skip_numbers); |
| |
| if (lexer.next_token() != WktLexer::TK_TYPE_LINESTRING) { |
| return CASS_ERROR_LIB_BAD_PARAMS; |
| } |
| |
| // Validate format and count the number of points |
| WktLexer::Token token = lexer.next_token(); |
| |
| // Special case "LINESTRING EMPTY" |
| if (token == WktLexer::TK_EMPTY) { |
| return CASS_OK; |
| } |
| |
| if (token != WktLexer::TK_OPEN_PAREN) { |
| return CASS_ERROR_LIB_BAD_PARAMS; |
| } |
| |
| token = lexer.next_token(); |
| while (token != WktLexer::TK_EOF && token != WktLexer::TK_CLOSE_PAREN) { |
| // First number in point |
| if (token != WktLexer::TK_NUMBER) { |
| return CASS_ERROR_LIB_BAD_PARAMS; |
| } |
| |
| // Second number in point |
| token = lexer.next_token(); |
| if (token != WktLexer::TK_NUMBER) { |
| return CASS_ERROR_LIB_BAD_PARAMS; |
| } |
| |
| ++num_points; |
| |
| // Check and skip "," token |
| token = lexer.next_token(); |
| if (token == WktLexer::TK_COMMA) { |
| token = lexer.next_token(); |
| // Verify there are more points |
| if (token != WktLexer::TK_NUMBER) { |
| return CASS_ERROR_LIB_BAD_PARAMS; |
| } |
| } |
| } |
| |
| // Validate closing ")" |
| if (token != WktLexer::TK_CLOSE_PAREN) { |
| return CASS_ERROR_LIB_BAD_PARAMS; |
| } |
| |
| num_points_ = num_points; |
| text_iterator_ = TextIterator(text, size); |
| iterator_ = &text_iterator_; |
| |
| return CASS_OK; |
| } |
| |
| CassError LineStringIterator::BinaryIterator::next_point(cass_double_t* x, cass_double_t* y) { |
| if (position_ >= points_end_) { |
| return CASS_ERROR_LIB_INVALID_STATE; |
| } |
| |
| *x = decode_double(position_, byte_order_); |
| position_ += sizeof(cass_double_t); |
| *y = decode_double(position_, byte_order_); |
| position_ += sizeof(cass_double_t); |
| |
| return CASS_OK; |
| } |
| |
| LineStringIterator::TextIterator::TextIterator(const char* text, size_t size) |
| : lexer_(text, size) { |
| WktLexer::Token token; |
| UNUSED_(token); |
| // Skip over "LINESTRING (" tokens |
| token = lexer_.next_token(); |
| assert(token == WktLexer::TK_TYPE_LINESTRING); |
| token = lexer_.next_token(); |
| assert(token == WktLexer::TK_OPEN_PAREN); |
| } |
| |
| CassError LineStringIterator::TextIterator::next_point(cass_double_t* x, cass_double_t* y) { |
| WktLexer::Token token; |
| |
| // If we're at the end of the text this will return WktLexer::TK_EOF |
| token = lexer_.next_token(); |
| if (token != WktLexer::TK_NUMBER) { |
| return CASS_ERROR_LIB_INVALID_STATE; |
| } |
| *x = lexer_.number(); |
| |
| token = lexer_.next_token(); |
| assert(token == WktLexer::TK_NUMBER); |
| *y = lexer_.number(); |
| |
| // Skip trailing "," or ")" tokens |
| token = lexer_.next_token(); |
| assert(token == WktLexer::TK_COMMA || token == WktLexer::TK_CLOSE_PAREN); |
| |
| return CASS_OK; |
| } |