RPC Base Component
authorJustin Dickow <jjdickow@gmail.com>
Tue, 15 Jul 2014 15:46:37 +0000 (11:46 -0400)
committerJustin Dickow <jjdickow@gmail.com>
Tue, 15 Jul 2014 15:46:37 +0000 (11:46 -0400)
Signed-off-by: Justin Dickow <jjdickow@gmail.com>
src/components/rpc_base/CMakeLists.txt
src/components/rpc_base/include/rpc_base/gtest_support.h [new file with mode: 0644]
src/components/rpc_base/include/rpc_base/rpc_base.h
src/components/rpc_base/include/rpc_base/rpc_base_dbus_inl.h
src/components/rpc_base/include/rpc_base/rpc_base_inl.h
src/components/rpc_base/include/rpc_base/rpc_base_json_inl.h
src/components/rpc_base/include/rpc_base/rpc_message.h
src/components/rpc_base/include/rpc_base/validation_report.h [new file with mode: 0644]

index 03042d8..6734756 100644 (file)
@@ -4,13 +4,17 @@ include_directories(
 )
 
 set (SOURCES
- src/rpc_base/rpc_base.cc
 src/rpc_base/rpc_base.cc
 )
 
 set (HEADERS
-  include/rpc_base/rpc_message.h
+  include/rpc_base/gtest_support.h
+  include/rpc_base/rpc_base_dbus_inl.h
   include/rpc_base/rpc_base.h
   include/rpc_base/rpc_base_inl.h
+  include/rpc_base/rpc_base_json_inl.h
+  include/rpc_base/rpc_message.h
+  include/rpc_base/validation_report.h
 )
 
 add_library(rpc_base ${HEADERS} ${SOURCES})
diff --git a/src/components/rpc_base/include/rpc_base/gtest_support.h b/src/components/rpc_base/include/rpc_base/gtest_support.h
new file mode 100644 (file)
index 0000000..68d1a83
--- /dev/null
@@ -0,0 +1,62 @@
+/**
+ * Copyright (c) 2014, Ford Motor Company
+ * All rights reserved.
+ *
+ * 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 the Ford Motor Company 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 HOLDER 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.
+ */
+
+#ifndef RPC_BASE_GTEST_SUPPORT_H_
+#define RPC_BASE_GTEST_SUPPORT_H_
+
+#include <gtest/gtest.h>
+
+#include "rpc_base/validation_report.h"
+
+// A predicate-formatter for asserting that intergen generated
+// object is valid
+template<typename T>
+::testing::AssertionResult AssertRpcObjValid(const char* obj_expr,
+                                               const T& obj) {
+  if (obj.is_valid())
+    return ::testing::AssertionSuccess();
+
+  rpc::ValidationReport report(obj_expr);
+  obj.ReportErrors(&report);
+
+  return ::testing::AssertionFailure()
+      << obj_expr << " failed validation. Violations are:\n"
+      << rpc::PrettyFormat(report);
+}
+
+#define ASSERT_RPCTYPE_VALID(object) \
+  ASSERT_PRED_FORMAT1(AssertRpcObjValid, object)
+
+#define EXPECT_RPCTYPE_VALID(object) \
+  EXPECT_PRED_FORMAT1(AssertRpcObjValid, object)
+
+#endif /* RPC_BASE_GTEST_SUPPORT_H_ */
index 1784bf6..24bb45b 100644 (file)
@@ -48,6 +48,8 @@ class MessageWriter;
 }  // namespace dbus
 
 namespace rpc {
+class ValidationReport;
+
 template<typename T> class Range;
 class PrimitiveType;
 class CompositeType;
@@ -60,7 +62,6 @@ template<typename T> class Enum;
 template<typename T, size_t minsize, size_t maxsize> class Array;
 template<typename T, size_t minsize, size_t maxsize> class Map;
 template<typename T> class Nullable;
-template<typename T> class Mandatory;
 template<typename T> class Optional;
 
 template<typename T>
@@ -85,13 +86,15 @@ class PrimitiveType {
  public:
   bool is_initialized() const;
   bool is_valid() const;
+  void ReportErrors(ValidationReport* report) const;
  protected:
   enum ValueState {
     kUninitialized,
     kInvalid,
     kValid
   };
-  PrimitiveType(ValueState value_state);
+  explicit PrimitiveType(ValueState value_state);
+  static ValueState InitHelper(bool is_next);
   static ValueState InitHelper(const Json::Value* value,
                                bool (Json::Value::*type_check)() const);
  protected:
@@ -99,32 +102,24 @@ class PrimitiveType {
 };
 
 /*
- * Helper class for all composite types (arrays and all user-defined types)
+ * Base class for all composite types (arrays and all user-defined types)
  */
 class CompositeType {
  public:
-  static const Json::Value* ValueMember(const Json::Value* value,
-                                        const char* member_name);
-  template<class T>
-  static void WriteJsonField(const char* field_name,
-                             const T& field,
-                             Json::Value* json_value);
-  template<class T>
-  static void WriteJsonField(const char* field_name,
-                             const Nullable<T>& field,
-                             Json::Value* json_value);
-  template<class T>
-  static void WriteJsonField(const char* field_name,
-                             const Optional<T>& field,
-                             Json::Value* json_value);
-  template<typename T, size_t minsize, size_t maxsize>
-  static void WriteJsonField(const char* field_name,
-                             const Map<T, minsize, maxsize>& field,
-                             Json::Value* json_value);
-  template<typename T, size_t minsize, size_t maxsize>
-  static void WriteJsonField(const char* field_name,
-                             const Array<T, minsize, maxsize>& field,
-                             Json::Value* json_value);
+  void mark_initialized();
+  void ReportErrors(ValidationReport* report) const;
+ protected:
+  enum InitializationState {
+    kUninitialized,
+    kInitialized,
+    kInvalidInitialized
+  };
+  explicit CompositeType(InitializationState init_state);
+  static InitializationState InitHelper(bool is_next);
+  static InitializationState InitHelper(const Json::Value* value,
+                               bool (Json::Value::*type_check)() const);
+ protected:
+  InitializationState initialization_state__;
 };
 
 /*
@@ -235,7 +230,7 @@ class Enum : public PrimitiveType {
 };
 
 template<typename T, size_t minsize, size_t maxsize>
-class Array : public std::vector<T> {
+class Array : public std::vector<T>, public CompositeType {
  public:
   // Types
   typedef std::vector<T> ArrayType;
@@ -258,16 +253,14 @@ class Array : public std::vector<T> {
 
   bool is_valid() const;
   bool is_initialized() const;
-  bool is_null() const;
-  void set_to_null();
- private:
-  bool marked_as_null_;
+  void ReportErrors(ValidationReport* report) const;
 };
 
 template<typename T, size_t minsize, size_t maxsize>
-class Map : public std::map<std::string, T> {
+class Map : public std::map<std::string, T>, public CompositeType  {
  public:
   // Types
+  typedef Map<T, minsize, maxsize> Frankenbase;
   typedef std::map<std::string, T> MapType;
  public:
   // Methods
@@ -288,10 +281,7 @@ class Map : public std::map<std::string, T> {
 
   bool is_valid() const;
   bool is_initialized() const;
-  bool is_null() const;
-  void set_to_null();
- private:
-  bool marked_as_null_;
+  void ReportErrors(ValidationReport* report) const;
 };
 
 template<typename T>
@@ -299,6 +289,7 @@ class Nullable : public T {
  public:
   // Methods
   Nullable();
+  explicit Nullable(dbus::MessageReader* reader);
   // Need const and non-const versions to beat all-type accepting constructor
   explicit Nullable(Json::Value* value);
   explicit Nullable(const Json::Value* value);
@@ -314,6 +305,7 @@ class Nullable : public T {
   bool is_initialized() const;
   bool is_null() const;
   void set_to_null();
+  void ReportErrors(ValidationReport* report) const;
  private:
   bool marked_null_;
 };
@@ -328,6 +320,7 @@ class Optional {
   explicit Optional(const U& value);
   template<typename U>
   Optional(const Json::Value* value,const U& def_value);
+  Json::Value ToJsonValue() const;
 
   void ToDbusWriter(dbus::MessageWriter* writer) const;
 
@@ -343,6 +336,7 @@ class Optional {
 
   bool is_valid() const;
   bool is_initialized() const;
+  void ReportErrors(ValidationReport* report) const;
  private:
   T value_;
 };
@@ -351,5 +345,6 @@ class Optional {
 
 // Template methods implementation
 #include "rpc_base_inl.h"
+#include "rpc_base_json_inl.h"
 
 #endif /* VALIDATED_TYPES_H_ */
index 2ed0ffa..7d66950 100644 (file)
 #include "rpc_base/rpc_base.h"
 
 namespace rpc {
+
+// static
+inline PrimitiveType::ValueState PrimitiveType::InitHelper(bool is_next) {
+  return is_next ? kValid : kInvalid;
+}
+
+// static
+inline CompositeType::InitializationState CompositeType::InitHelper(bool is_next) {
+  return is_next ? kInitialized : kUninitialized;
+}
+
 namespace impl {
 
 
@@ -243,14 +254,17 @@ struct DbusSignatureHelper<Map<T, minsize, maxsize> > {
 };
 
 template<typename T>
-struct DbusSignatureHelper<Mandatory<T> > {
+struct DbusSignatureHelper<Optional<T> > {
   static void DbusSignature(std::string* signature) {
+    (*signature) += DBUS_STRUCT_BEGIN_CHAR;
+    (*signature) += rpc::impl::DbusTypeCode<bool>();
     rpc::impl::DbusSignatureHelper<T>::DbusSignature(signature);
+    (*signature) += DBUS_STRUCT_END_CHAR;
   }
 };
 
 template<typename T>
-struct DbusSignatureHelper<Optional<T> > {
+struct DbusSignatureHelper<Nullable<T> > {
   static void DbusSignature(std::string* signature) {
     (*signature) += DBUS_STRUCT_BEGIN_CHAR;
     (*signature) += rpc::impl::DbusTypeCode<bool>();
@@ -268,6 +282,14 @@ T TakeOptional(dbus::MessageReader* reader) {
   return available ? value : T();
 }
 
+// Helper Nullable type initialization functipon
+template<typename T>
+bool TakeNullable(dbus::MessageReader* reader) {
+  dbus::MessageReader struct_reader = reader->TakeStructReader();
+  bool is_null = struct_reader.TakeBool();
+  return is_null;
+}
+
 }  // namespace impl
 
 
@@ -279,48 +301,49 @@ void DbusSignature(std::string* signature) {
 
 // Type constructors
 inline Boolean::Boolean(dbus::MessageReader* reader)
-  : PrimitiveType(true, reader->NextIsBool()),
+  : PrimitiveType(InitHelper(reader->NextIsBool())),
     value_(reader->TakeBool()) {
 }
 
 template<typename T, T minval, T maxval>
 inline Integer<T, minval, maxval>::Integer(dbus::MessageReader* reader)
-    : PrimitiveType(true, impl::NextIs<IntType>(*reader)),
+    : PrimitiveType(InitHelper(impl::NextIs<IntType>(*reader))),
       value_(impl::Take<IntType>(reader)) {
-  if (valid_) {
-    valid_ = range_.Includes(value_);
+  if (is_valid()) {
+    value_state_ = range_.Includes(value_) ? kValid : kInvalid;
   }
 }
 
 template<int64_t minnum, int64_t maxnum, int64_t minden, int64_t maxden>
 inline Float<minnum, maxnum, minden, maxden>::Float(dbus::MessageReader* reader)
-    : PrimitiveType(true, reader->NextIsDouble()),
+    : PrimitiveType(InitHelper(reader->NextIsDouble())),
       value_(reader->TakeDouble()) {
-  if (valid_) {
-    valid_ = range_.Includes(value_);
+  if (is_valid()) {
+    value_state_ = range_.Includes(value_) ? kValid : kInvalid;
   }
 }
 
 template<size_t minlen, size_t maxlen>
 inline String<minlen, maxlen>::String(dbus::MessageReader* reader)
-    : PrimitiveType(true, reader->NextIsString()),
+    : PrimitiveType(InitHelper(reader->NextIsString())),
       value_(reader->TakeString()) {
-  if (valid_) {
-    valid_ = length_range_.Includes(value_.length());
+  if (is_valid()) {
+    value_state_ = length_range_.Includes(value_.length()) ? kValid : kInvalid;
   }
 }
 
 template<typename T>
 inline Enum<T>::Enum(dbus::MessageReader* reader)
-    : PrimitiveType(true, reader->NextIsInt32()),
+    : PrimitiveType(InitHelper(reader->NextIsInt32())),
       value_(EnumType(reader->TakeInt32())) {
-  if (valid_) {
-    valid_ = IsValidEnum(value_);
+  if (is_valid()) {
+    value_state_ = IsValidEnum(value_) ? kValid : kInvalid;
   }
 }
 
 template<typename T, size_t minsize, size_t maxsize>
-inline Array<T, minsize, maxsize>::Array(dbus::MessageReader* reader) {
+inline Array<T, minsize, maxsize>::Array(dbus::MessageReader* reader)
+    : CompositeType(InitHelper(reader->NextIsArray())) {
   dbus::MessageReader array_reader = reader->TakeArrayReader();
   if (array_reader.has_failed()) {
     push_back(T());
@@ -332,7 +355,8 @@ inline Array<T, minsize, maxsize>::Array(dbus::MessageReader* reader) {
 }
 
 template<typename T, size_t minsize, size_t maxsize>
-inline Map<T, minsize, maxsize>::Map(dbus::MessageReader* reader) {
+inline Map<T, minsize, maxsize>::Map(dbus::MessageReader* reader)
+    : CompositeType(InitHelper(reader->NextIsStruct())) {
   // Map key-value pairs are serialized into array
   dbus::MessageReader array_reader = reader->TakeArrayReader();
   if (array_reader.has_failed()) {
@@ -348,6 +372,12 @@ inline Map<T, minsize, maxsize>::Map(dbus::MessageReader* reader) {
 }
 
 template<typename T>
+inline Nullable<T>::Nullable(dbus::MessageReader* reader)
+  : T(reader),
+    marked_null_(impl::TakeNullable<T>(reader)){
+}
+
+template<typename T>
 inline Optional<T>::Optional(dbus::MessageReader* reader)
   : value_(impl::TakeOptional<T>(reader)){
 }
index 1a63a63..b6a5473 100644 (file)
@@ -36,6 +36,9 @@
 #include "rpc_base.h"
 
 #include <cassert>
+#include <cstdio>
+
+#include "rpc_base/validation_report.h"
 
 namespace rpc {
 
@@ -64,6 +67,10 @@ bool Range<T>::Includes(U val) const {
   return min() <= val && val <= max();
 }
 
+
+/*
+ * PrimitiveType base class
+ */
 inline PrimitiveType::PrimitiveType(ValueState value_state)
     : value_state_(value_state) {
 }
@@ -76,6 +83,58 @@ inline bool PrimitiveType::is_valid() const {
   return value_state_ == kValid;
 }
 
+inline void PrimitiveType::ReportErrors(ValidationReport* report) const {
+  switch (value_state_) {
+    case kUninitialized: {
+      report->set_validation_info("value is not initialized");
+      break;
+    }
+    case kInvalid: {
+      report->set_validation_info("value initialized incorrectly");
+      break;
+    }
+    case kValid: {
+      // No error
+      break;
+    }
+    default: {
+      assert(!"Unexpected value state");
+      break;
+    }
+  }
+}
+
+/*
+ * CompositeType base class
+ */
+inline void CompositeType::mark_initialized() {
+  initialization_state__ = kInitialized;
+}
+
+inline CompositeType::CompositeType(InitializationState init_state)
+    : initialization_state__(init_state) {
+}
+
+inline void CompositeType::ReportErrors(ValidationReport* report) const {
+  switch (initialization_state__) {
+    case kUninitialized: {
+      report->set_validation_info("object is not initialized");
+      break;
+    }
+    case kInvalidInitialized: {
+      report->set_validation_info("object initialized incorrectly");
+      break;
+    }
+    case kInitialized: {
+      // No error
+      break;
+    }
+    default:
+      assert(!"Unexpected initialization state");
+      break;
+  }
+}
+
 /*
  * Boolean class
  */
@@ -208,7 +267,7 @@ Enum<T>::Enum()
 
 template<typename T>
 Enum<T>::Enum(EnumType value)
-    : PrimitiveType(IsValidEnum(value_) ? kValid : kInvalid),
+    : PrimitiveType(IsValidEnum(value) ? kValid : kInvalid),
       value_(value) {
 }
 
@@ -229,14 +288,14 @@ Enum<T>::operator EnumType() const {
  */
 template<typename T, size_t minsize, size_t maxsize>
 Array<T, minsize, maxsize>::Array()
-  : marked_as_null_(false) {
+    : CompositeType(kUninitialized) {
 }
 
 template<typename T, size_t minsize, size_t maxsize>
 template<typename U>
 Array<T, minsize, maxsize>::Array(const U& value)
     : ArrayType(value.begin(), value.end()),
-      marked_as_null_(false) {
+      CompositeType(kUninitialized) {
 }
 
 template<typename T, size_t minsize, size_t maxsize>
@@ -255,11 +314,15 @@ void Array<T, minsize, maxsize>::push_back(const U& value) {
 
 template<typename T, size_t minsize, size_t maxsize>
 bool Array<T, minsize, maxsize>::is_valid() const {
-  if (is_null()) {
+  // Empty array might be valid only if marked initialized
+  if (this->empty() && (initialization_state__ != kInitialized)) {
     return false;
   }
-  if (!Range<size_t>(minsize, maxsize).Includes(this->size()))
+  // Array size must be within allowed range
+  if (!Range<size_t>(minsize, maxsize).Includes(this->size())) {
     return false;
+  }
+  // All array elements must be valid
   for (typename ArrayType::const_iterator i = this->begin();
       i != this->end(); ++i) {
     if (!i->is_valid())
@@ -270,17 +333,38 @@ bool Array<T, minsize, maxsize>::is_valid() const {
 
 template<typename T, size_t minsize, size_t maxsize>
 bool Array<T, minsize, maxsize>::is_initialized() const {
-  return is_null() || !this->empty();
-}
-
-template<typename T, size_t minsize, size_t maxsize>
-bool Array<T, minsize, maxsize>::is_null() const {
-  return marked_as_null_;
+  // Array that is not empty is initialized for sure
+  if (!this->empty()) {
+    return true;
+  }
+  // Empty array is initialized if not marked as unitialized
+  if (initialization_state__ != kUninitialized) {
+    return true;
+  }
+  return false;
 }
 
 template<typename T, size_t minsize, size_t maxsize>
-void Array<T, minsize, maxsize>::set_to_null() {
-  marked_as_null_ = true;
+void Array<T, minsize, maxsize>::ReportErrors(ValidationReport* report) const {
+  if (this->empty()) {
+    CompositeType::ReportErrors(report);
+  } else {
+    if (!Range<size_t>(minsize, maxsize).Includes(this->size())) {
+      report->set_validation_info("array has invalid size");
+    } else {
+      // No error
+    }
+  }
+  for (size_t i = 0; i != this->size(); ++i) {
+    const T& elem = this->operator [](i);
+    if (!elem.is_valid()) {
+      char elem_idx[32] = {};
+      snprintf(elem_idx, 32, "[%zu]", i);
+      ValidationReport& elem_report =
+          report->ReportSubobject(elem_idx);
+      elem.ReportErrors(&elem_report);
+    }
+  }
 }
 
 /*
@@ -288,13 +372,13 @@ void Array<T, minsize, maxsize>::set_to_null() {
  */
 template<typename T, size_t minsize, size_t maxsize>
 Map<T, minsize, maxsize>::Map()
-    : marked_as_null_(false) {
+    : CompositeType(kUninitialized) {
 }
 
 template<typename T, size_t minsize, size_t maxsize>
 template<typename U>
 Map<T, minsize, maxsize>::Map(const U& value)
-    : marked_as_null_(false) {
+    : CompositeType(kUninitialized) {
   for (typename U::const_iterator i = value.begin(), e = value.end(); i != e;
       ++i) {
     // Explicitly convert that value to T because all rpc_types have explicit
@@ -325,10 +409,15 @@ void Map<T, minsize, maxsize>::insert(const std::pair<std::string, U>& value) {
 
 template<typename T, size_t minsize, size_t maxsize>
 bool Map<T, minsize, maxsize>::is_valid() const {
-  if (is_null())
+  // Empty map might be valid only if marked initialized
+  if (this->empty() && (initialization_state__ != kInitialized)) {
     return false;
-  if (!Range<size_t>(minsize, maxsize).Includes(this->size()))
+  }
+  // Maps size must be within allowed range
+  if (!Range<size_t>(minsize, maxsize).Includes(this->size())) {
     return false;
+  }
+  // All map elements must be valid
   for (typename Map::const_iterator i = this->begin();
       i != this->end(); ++i) {
     if (!i->second.is_valid())
@@ -339,17 +428,36 @@ bool Map<T, minsize, maxsize>::is_valid() const {
 
 template<typename T, size_t minsize, size_t maxsize>
 bool Map<T, minsize, maxsize>::is_initialized() const {
-  return is_null() || !this->empty();
-}
-
-template<typename T, size_t minsize, size_t maxsize>
-bool Map<T, minsize, maxsize>::is_null() const {
-  return marked_as_null_;
+  // Map that is not empty is initialized for sure
+  if (!this->empty()) {
+    return true;
+  }
+  // Empty map might be initialized only if not marked as unitialized
+  if (initialization_state__ != kUninitialized) {
+    return true;
+  }
+  return false;
 }
 
 template<typename T, size_t minsize, size_t maxsize>
-void Map<T, minsize, maxsize>::set_to_null() {
-  marked_as_null_ = true;
+void Map<T, minsize, maxsize>::ReportErrors(ValidationReport* report) const {
+  if (this->empty()) {
+    CompositeType::ReportErrors(report);
+  } else {
+    if (!Range<size_t>(minsize, maxsize).Includes(this->size())) {
+      report->set_validation_info("map has invalid size");
+    } else {
+      // No error
+    }
+  }
+  for (typename Map::const_iterator i = this->begin();
+      i != this->end(); ++i) {
+    if (!i->second.is_valid()) {
+      std::string elem_name = "[\"" + i->first + "\"]";
+      ValidationReport& elem_report = report->ReportSubobject(elem_name);
+      i->second.ReportErrors(&elem_report);
+    }
+  }
 }
 
 /*
@@ -394,6 +502,15 @@ void Nullable<T>::set_to_null() {
   marked_null_ = true;
 }
 
+template<typename T>
+void Nullable<T>::ReportErrors(ValidationReport* report) const {
+  if (marked_null_) {
+    // No error
+  } else {
+    T::ReportErrors(report);
+  }
+}
+
 /*
  * Optional class
  */
@@ -442,6 +559,15 @@ bool Optional<T>::is_initialized() const {
   return value_.is_initialized();
 }
 
+template<typename T>
+void Optional<T>::ReportErrors(ValidationReport* report) const {
+  if (!is_initialized()) {
+    // No error
+  } else {
+    value_.ReportErrors(report);
+  }
+}
+
 }  // namespace rpc
 
 
index d44cd86..93d0729 100644 (file)
@@ -51,68 +51,44 @@ inline PrimitiveType::ValueState PrimitiveType::InitHelper(
   }
 }
 
-/*
- * Composite type
- */
+// static
+inline CompositeType::InitializationState CompositeType::InitHelper(
+    const Json::Value* value,
+    bool (Json::Value::*type_check)() const) {
+  if (!value) {
+    return kUninitialized;
+  } else if ((value->*type_check)()) {
+    // Some value type checks return true when initialized with null
+    if (value->isNull()) {
+      return kInvalidInitialized;
+    } else {
+      return kInitialized;
+    }
+  } else {
+    return kInvalidInitialized;
+  }
+}
 
+namespace impl {
 // static
-inline const Json::Value* CompositeType::ValueMember(const Json::Value* value,
-                                                     const char* member_name) {
+inline const Json::Value* ValueMember(const Json::Value* value,
+                                      const char* member_name) {
   if (value && value->isMember(member_name)) {
     return &(*value)[member_name];
   }
   return NULL;
 }
 
-// static
 template<class T>
-void CompositeType::WriteJsonField(const char* field_name,
+inline void WriteJsonField(const char* field_name,
                            const T& field,
                            Json::Value* json_value) {
-  (*json_value)[field_name] = field.ToJsonValue();
-}
-
-// static
-template<class T>
-void CompositeType::WriteJsonField(const char* field_name,
-                           const Nullable<T>& field,
-                           Json::Value* json_value) {
-  if (field.is_null()) {
-    (*json_value)[field_name] = Json::Value::null;
-  } else {
-    const T& non_nulable = field;
-    WriteJsonField(field_name, non_nulable, json_value);
-  }
-}
-
-// static
-template<typename T, size_t minsize, size_t maxsize>
-void CompositeType::WriteJsonField(const char* field_name,
-                           const Map<T, minsize, maxsize>& field,
-                           Json::Value* json_value) {
   if (field.is_initialized()) {
     (*json_value)[field_name] = field.ToJsonValue();
   }
 }
 
-// static
-template<typename T, size_t minsize, size_t maxsize>
-void CompositeType::WriteJsonField(const char* field_name,
-                           const Array<T, minsize, maxsize>& field,
-                           Json::Value* json_value) {
-  if (field.is_initialized()) {
-    (*json_value)[field_name] = field.ToJsonValue();
-  }
-}
-
-template<class T>
-void CompositeType::WriteJsonField(const char* field_name,
-                           const Optional<T>& field,
-                           Json::Value* json_value) {
-  if (field.is_initialized()) {
-    WriteJsonField(field_name, *field, json_value);
-  }
-}
+}  // namespace impl
 
 inline Boolean::Boolean(const Json::Value* value)
     : PrimitiveType(InitHelper(value, &Json::Value::isBool)),
@@ -248,17 +224,16 @@ Json::Value Enum<T>::ToJsonValue() const {
 // Non-const version
 template<typename T, size_t minsize, size_t maxsize>
 Array<T, minsize, maxsize>::Array(Json::Value* value)
-    : marked_as_null_(value && value->isNull()) {
-  if (value && !marked_as_null_) {
+    : CompositeType(InitHelper(value, &Json::Value::isArray)) {
+  if (value) {
     if (value->isArray()) {
       this->reserve(value->size());
       for (Json::Value::iterator i = value->begin(); i != value->end(); ++i) {
         push_back(&*i);
       }
     } else {
-      // In case of non-array value initialize array with non-initialized value
-      // so it handled as initialized but invalid
-      push_back(T());
+      // Array is empty, empty initialized or uninitialized
+      // depending on InitHelper result
     }
   }
 }
@@ -266,26 +241,22 @@ Array<T, minsize, maxsize>::Array(Json::Value* value)
 // Const version, must be identical to the non-const version
 template<typename T, size_t minsize, size_t maxsize>
 Array<T, minsize, maxsize>::Array(const Json::Value* value)
-    : marked_as_null_(value && value->isNull()) {
-  if (value && !marked_as_null_) {
+    : CompositeType(InitHelper(value, &Json::Value::isArray)) {
+  if (value) {
     if (value->isArray()) {
       this->reserve(value->size());
       for (Json::Value::const_iterator i = value->begin(); i != value->end(); ++i) {
         push_back(&*i);
       }
     } else {
-      // In case of non-array value initialize array with non-initialized value
-      // so it handled as initialized but invalid
-      push_back(T());
+      // Array is empty, empty initialized or uninitialized
+      // depending on InitHelper result
     }
   }
 }
 
 template<typename T, size_t minsize, size_t maxsize>
 Json::Value Array<T, minsize, maxsize>::ToJsonValue() const {
-  if (is_null()) {
-    return Json::Value::null;
-  }
   Json::Value array(Json::arrayValue);
   array.resize(this->size());
   for (size_t i = 0; i != this->size(); ++i) {
@@ -297,41 +268,36 @@ Json::Value Array<T, minsize, maxsize>::ToJsonValue() const {
 // Non-const version
 template<typename T, size_t minsize, size_t maxsize>
 Map<T, minsize, maxsize>::Map(Json::Value* value)
-    : marked_as_null_(value && value->isNull()) {
-  if (value && !marked_as_null_) {
+    : CompositeType(InitHelper(value, &Json::Value::isObject)) {
+  if (value) {
     if (value->isObject()) {
       for (Json::Value::iterator i = value->begin(); i != value->end(); ++i) {
         this->insert(typename MapType::value_type(i.key().asString(), T(&*i)));
       }
     } else {
-      // In case of non-array value initialize array with null value
-      // so it handled as initialized but invalid
-      this->insert(typename MapType::value_type("", T()));
+      // Map is empty, empty initialized or uninitialized
+      // depending on InitHelper result
     }
   }
 }
 
 template<typename T, size_t minsize, size_t maxsize>
 Map<T, minsize, maxsize>::Map(const Json::Value* value)
-    : marked_as_null_(value && value->isNull()) {
-  if (value && !marked_as_null_) {
+    : CompositeType(InitHelper(value, &Json::Value::isObject)) {
+  if (value) {
     if (value->isObject()) {
       for (Json::Value::const_iterator i = value->begin(); i != value->end(); ++i) {
         this->insert(typename MapType::value_type(i.key().asString(), T(&*i)));
       }
     } else {
-      // In case of non-array value initialize array with null value
-      // so it handled as initialized but invalid
-      this->insert(typename MapType::value_type("", T()));
+      // Map is empty, empty initialized or uninitialized
+      // depending on InitHelper result
     }
   }
 }
 
 template<typename T, size_t minsize, size_t maxsize>
 Json::Value Map<T, minsize, maxsize>::ToJsonValue() const {
-  if (is_null()) {
-    return Json::Value::null;
-  }
   Json::Value map(Json::objectValue);
   for (typename MapType::const_iterator i = this->begin(); i != this->end(); ++i) {
     map[i->first] = i->second.ToJsonValue();
@@ -369,6 +335,11 @@ Optional<T>::Optional(const Json::Value* value, const U& def_value)
     : value_(value, def_value) {
 }
 
+template<typename T>
+inline Json::Value Optional<T>::ToJsonValue() const {
+  return value_.ToJsonValue();
+}
+
 }  // namespace rpc
 
 #endif /* VALIDATED_TYPES_JSON_INL_H_ */
index 25188e0..48ef5ff 100644 (file)
@@ -67,24 +67,27 @@ public:
 };
 
 // Base class for all interface-specific requests
-class RequestBase : public Message {
+class RequestBase : public Message, public CompositeType {
  public:
+  explicit RequestBase(InitializationState init_state): CompositeType(init_state) {}
   // Message interface
   MessageType message_type() const { return kRequest; }
   virtual ~RequestBase() {}
 };
 
 // Base class for all interface-specific responses
-class ResponseBase : public Message {
+class ResponseBase : public Message, public CompositeType {
  public:
+  ResponseBase(InitializationState init_state): CompositeType(init_state) {}
   // Message interface
   MessageType message_type() const { return kResponse; }
   virtual ~ResponseBase() {}
 };
 
 // Base class for all interface-specific notifications
-class NotificationBase : public Message {
+class NotificationBase : public Message, public CompositeType {
  public:
+  explicit NotificationBase(InitializationState init_state): CompositeType(init_state) {}
   MessageType message_type() const { return kNotification; }
   // Message interface
   virtual ~NotificationBase() {}
diff --git a/src/components/rpc_base/include/rpc_base/validation_report.h b/src/components/rpc_base/include/rpc_base/validation_report.h
new file mode 100644 (file)
index 0000000..eeadb35
--- /dev/null
@@ -0,0 +1,119 @@
+/**
+ * Copyright (c) 2014, Ford Motor Company
+ * All rights reserved.
+ *
+ * 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 the Ford Motor Company 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 HOLDER 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.
+ */
+
+#ifndef RPC_BASE_VALIDATION_REPORT_H_
+#define RPC_BASE_VALIDATION_REPORT_H_
+
+#include <string>
+#include <list>
+
+namespace rpc {
+
+class ValidationReport;
+typedef std::list<ValidationReport> ValidationReports;
+
+class ValidationReport {
+ public:
+  explicit ValidationReport(const std::string& object_name);
+  const std::string& object_name() const;
+  const std::string& validation_info() const;
+  void set_validation_info(const std::string& info);
+  const ValidationReports& subobject_reports() const;
+  ValidationReport& ReportSubobject(const std::string& object_name);
+private:
+  std::string object_name_;
+  std::string validation_info_;
+  ValidationReports subobject_reports_;
+};
+
+std::string PrettyFormat(const ValidationReport& report);
+
+// Implementation
+
+namespace impl {
+inline void PrettyFormat(const ValidationReport& report,
+                  const std::string& parent_path,
+                  std::string* result) {
+  std::string object_path = parent_path;
+  if (!object_path.empty() && report.object_name()[0] != '[') {
+    object_path.append(".");
+  }
+  object_path.append(report.object_name());
+  if (!report.validation_info().empty()) {
+    result->append(object_path);
+    result->append(": ");
+    result->append(report.validation_info());
+    result->append("\n");
+  }
+  const ValidationReports& subreports = report.subobject_reports();
+  for (ValidationReports::const_iterator i = subreports.begin(),
+       end = subreports.end(); i != end; ++i) {
+    PrettyFormat(*i, object_path, result);
+  }
+}
+}  // namespace impl
+
+inline ValidationReport::ValidationReport(const std::string& object_name)
+  : object_name_(object_name) {
+}
+
+inline const std::string& ValidationReport::object_name() const {
+  return object_name_;
+}
+
+inline const std::string& ValidationReport::validation_info() const {
+  return validation_info_;
+}
+
+inline void ValidationReport::set_validation_info(const std::string& info) {
+  validation_info_ = info;
+}
+
+inline const std::list<ValidationReport>& ValidationReport::subobject_reports() const {
+  return subobject_reports_;
+}
+
+inline ValidationReport& ValidationReport::ReportSubobject(
+    const std::string& object_name) {
+  subobject_reports_.push_back(ValidationReport(object_name));
+  return subobject_reports_.back();
+}
+
+inline std::string PrettyFormat(const ValidationReport& report) {
+  std::string result;
+  impl::PrettyFormat(report, "", &result);
+  return result;
+}
+
+}  // namespace rpc
+
+#endif /* RPC_BASE_VALIDATION_REPORT_H_ */