1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  *   http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 
20 #ifndef T_CONST_VALUE_H
21 #define T_CONST_VALUE_H
22 
23 #include "thrift/parse/t_enum.h"
24 #include <stdint.h>
25 #include <map>
26 #include <vector>
27 #include <string>
28 
29 /**
30  * A const value is something parsed that could be a map, set, list, struct
31  * or whatever.
32  *
33  */
34 class t_const_value {
35 public:
36   /**
37    * Comparator to sort fields in ascending order by key.
38    * Make this a functor instead of a function to help GCC inline it.
39    */
40   struct value_compare {
41   public:
operatorvalue_compare42     bool operator()(t_const_value const* const& left, t_const_value const* const& right) const {
43       return *left < *right;
44     }
45   };
46 
47   enum t_const_value_type { CV_INTEGER, CV_DOUBLE, CV_STRING, CV_MAP, CV_LIST, CV_IDENTIFIER, CV_UNKNOWN };
48 
t_const_value()49   t_const_value() : intVal_(0), doubleVal_(0.0f), enum_((t_enum*)nullptr), valType_(CV_UNKNOWN) {}
50 
t_const_value(int64_t val)51   t_const_value(int64_t val) : doubleVal_(0.0f), enum_((t_enum*)nullptr), valType_(CV_UNKNOWN) { set_integer(val); }
52 
t_const_value(std::string val)53   t_const_value(std::string val) : intVal_(0), doubleVal_(0.0f), enum_((t_enum*)nullptr), valType_(CV_UNKNOWN) { set_string(val); }
54 
set_string(std::string val)55   void set_string(std::string val) {
56     valType_ = CV_STRING;
57     stringVal_ = val;
58   }
59 
get_string()60   std::string get_string() const { return stringVal_; }
61 
set_integer(int64_t val)62   void set_integer(int64_t val) {
63     valType_ = CV_INTEGER;
64     intVal_ = val;
65   }
66 
get_integer()67   int64_t get_integer() const {
68     if (valType_ == CV_IDENTIFIER) {
69       if (enum_ == nullptr) {
70         throw "have identifier \"" + get_identifier() + "\", but unset enum on line!";
71       }
72       std::string identifier = get_identifier();
73       std::string::size_type dot = identifier.rfind('.');
74       if (dot != std::string::npos) {
75         identifier = identifier.substr(dot + 1);
76       }
77       t_enum_value* val = enum_->get_constant_by_name(identifier);
78       if (val == nullptr) {
79         throw "Unable to find enum value \"" + identifier + "\" in enum \"" + enum_->get_name()
80             + "\"";
81       }
82       return val->get_value();
83     } else {
84       return intVal_;
85     }
86   }
87 
set_uuid(std::string val)88   void set_uuid(std::string val) {
89     validate_uuid(val);
90     valType_ = CV_STRING;
91     stringVal_ = val;
92   }
93 
get_uuid()94   std::string get_uuid() const {
95     std::string tmp = stringVal_;
96     validate_uuid(tmp);
97     return tmp;
98   }
99 
set_double(double val)100   void set_double(double val) {
101     valType_ = CV_DOUBLE;
102     doubleVal_ = val;
103   }
104 
get_double()105   double get_double() const { return doubleVal_; }
106 
set_map()107   void set_map() { valType_ = CV_MAP; }
108 
add_map(t_const_value * key,t_const_value * val)109   void add_map(t_const_value* key, t_const_value* val) { mapVal_[key] = val; }
110 
get_map()111   const std::map<t_const_value*, t_const_value*, t_const_value::value_compare>& get_map() const { return mapVal_; }
112 
set_list()113   void set_list() { valType_ = CV_LIST; }
114 
add_list(t_const_value * val)115   void add_list(t_const_value* val) { listVal_.push_back(val); }
116 
get_list()117   const std::vector<t_const_value*>& get_list() const { return listVal_; }
118 
set_identifier(std::string val)119   void set_identifier(std::string val) {
120     valType_ = CV_IDENTIFIER;
121     identifierVal_ = val;
122   }
123 
get_identifier()124   std::string get_identifier() const { return identifierVal_; }
125 
get_identifier_name()126   std::string get_identifier_name() const {
127     std::string ret = get_identifier();
128     size_t s = ret.find('.');
129     if (s == std::string::npos) {
130       throw "error: identifier " + ret + " is unqualified!";
131     }
132     ret = ret.substr(s + 1);
133     s = ret.find('.');
134     if (s != std::string::npos) {
135       ret = ret.substr(s + 1);
136     }
137     return ret;
138   }
139 
get_identifier_with_parent()140   std::string get_identifier_with_parent() const {
141     std::string ret = get_identifier();
142     size_t s = ret.find('.');
143     if (s == std::string::npos) {
144       throw "error: identifier " + ret + " is unqualified!";
145     }
146     size_t s2 = ret.find('.', s + 1);
147     if (s2 != std::string::npos) {
148       ret = ret.substr(s + 1);
149     }
150     return ret;
151   }
152 
set_enum(t_enum * tenum)153   void set_enum(t_enum* tenum) { enum_ = tenum; }
154 
get_type()155   t_const_value_type get_type() const { if (valType_ == CV_UNKNOWN) { throw std::string("unknown t_const_value"); } return valType_; }
156 
157   /**
158    * Comparator to sort map fields in ascending order by key and then value.
159    * This is used for map comparison in lexicographic order.
160    */
161   struct map_entry_compare {
162   private:
163     typedef std::pair<t_const_value*, t_const_value*> ConstPair;
164   public:
operatormap_entry_compare165     bool operator()(ConstPair left, ConstPair right) const {
166       if (*(left.first) < *(right.first)) {
167         return true;
168       } else {
169         if (*(right.first) < *(left.first)) {
170           return false;
171         } else {
172           return *(left.second) < *(right.second);
173         }
174       }
175     }
176   };
177 
178   bool operator < (const t_const_value& that) const {
179     ::t_const_value::t_const_value_type t1 = get_type();
180     ::t_const_value::t_const_value_type t2 = that.get_type();
181     if (t1 != t2)
182       return t1 < t2;
183     switch (t1) {
184     case ::t_const_value::CV_INTEGER:
185       return intVal_ < that.intVal_;
186     case ::t_const_value::CV_DOUBLE:
187       return doubleVal_ < that.doubleVal_;
188     case ::t_const_value::CV_STRING:
189       return stringVal_ < that.stringVal_;
190     case ::t_const_value::CV_IDENTIFIER:
191       return identifierVal_ < that.identifierVal_;
192     case ::t_const_value::CV_MAP:
193       return std::lexicographical_compare(
194           mapVal_.begin(), mapVal_.end(), that.mapVal_.begin(), that.mapVal_.end(), map_entry_compare());
195     case ::t_const_value::CV_LIST:
196       return std::lexicographical_compare(
197           listVal_.begin(), listVal_.end(), that.listVal_.begin(), that.listVal_.end(), value_compare());
198     case ::t_const_value::CV_UNKNOWN:
199     default:
200       throw "unknown value type";
201     }
202   }
203 
204 private:
205   std::map<t_const_value*, t_const_value*, value_compare> mapVal_;
206   std::vector<t_const_value*> listVal_;
207   std::string stringVal_;
208   int64_t intVal_;
209   double doubleVal_;
210   std::string identifierVal_;
211   t_enum* enum_;
212 
213   t_const_value_type valType_;
214 
validate_uuid(std::string & uuid)215   void validate_uuid(std::string & uuid) const {
216     const std::string HEXCHARS = std::string("0123456789ABCDEFabcdef");
217 
218     // we also allow for usual "Windows GUID" format "{01234567-9012-4567-9012-456789012345}"
219     if ((uuid.length() == 38) && ('{' == uuid[0]) && ('}' == uuid[37])) {
220       uuid = uuid.substr(1, 36);
221     }
222 
223     // canonical format "01234567-9012-4567-9012-456789012345" expected
224     bool valid = (uuid.length() == 36);
225     for (size_t i = 0; valid && (i < uuid.length()); ++i) {
226       switch(i) {
227         case 8:
228         case 13:
229         case 18:
230         case 23:
231           if(uuid[i] != '-') {
232             valid = false;
233           }
234           break;
235         default:
236           if(HEXCHARS.find(uuid[i]) == std::string::npos) {
237             valid = false;
238           }
239           break;
240       }
241     }
242 
243     if( ! valid) {
244       throw "invalid uuid " + uuid;
245     }
246   }
247 };
248 
249 #endif
250