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_STRUCT_H
21 #define T_STRUCT_H
22 
23 #include <algorithm>
24 #include <vector>
25 #include <utility>
26 #include <string>
27 
28 #include "thrift/parse/t_type.h"
29 #include "thrift/parse/t_field.h"
30 
31 // Forward declare that puppy
32 class t_program;
33 
34 /**
35  * A struct is a container for a set of member fields that has a name. Structs
36  * are also used to implement exception types.
37  *
38  */
39 class t_struct : public t_type {
40 public:
41   typedef std::vector<t_field*> members_type;
42 
t_struct(t_program * program)43   t_struct(t_program* program)
44     : t_type(program),
45       is_xception_(false),
46       is_union_(false),
47       is_method_xcepts_(false),
48       union_validated_(false),
49       xcepts_validated_(false),
50       members_with_value_(0),
51       xsd_all_(false) {}
52 
t_struct(t_program * program,const std::string & name)53   t_struct(t_program* program, const std::string& name)
54     : t_type(program, name),
55       is_xception_(false),
56       is_union_(false),
57       is_method_xcepts_(false),
58       union_validated_(false),
59       xcepts_validated_(false),
60       members_with_value_(0),
61       xsd_all_(false) {}
62 
set_name(const std::string & name)63   void set_name(const std::string& name) override {
64     name_ = name;
65     union_validated_= false;
66     validate_members();
67   }
68 
set_xception(bool is_xception)69   void set_xception(bool is_xception) { is_xception_ = is_xception; }
70 
set_method_xcepts(bool is_method_xcepts)71   void set_method_xcepts(bool is_method_xcepts) {
72     is_method_xcepts_ = is_method_xcepts;
73     xcepts_validated_ = false;
74     validate_members();
75   }
76 
set_union(bool is_union)77   void set_union(bool is_union) {
78     is_union_ = is_union;
79     union_validated_= false;
80     validate_members();
81   }
82 
set_xsd_all(bool xsd_all)83   void set_xsd_all(bool xsd_all) { xsd_all_ = xsd_all; }
84 
get_xsd_all()85   bool get_xsd_all() const { return xsd_all_; }
86 
append(t_field * elem)87   bool append(t_field* elem) {
88     typedef members_type::iterator iter_type;
89     std::pair<iter_type, iter_type> bounds = std::equal_range(members_in_id_order_.begin(),
90                                                               members_in_id_order_.end(),
91                                                               elem,
92                                                               t_field::key_compare());
93     if (bounds.first != bounds.second) {
94       return false;
95     }
96     // returns false when there is a conflict of field names
97     if (get_field_by_name(elem->get_name()) != nullptr) {
98       return false;
99     }
100     members_.push_back(elem);
101     members_in_id_order_.insert(bounds.second, elem);
102     if (needs_validation()) {
103       validate_members();
104     } else {
105       validate_member_field(elem);
106     }
107     return true;
108   }
109 
get_members()110   const members_type& get_members() const { return members_; }
111 
get_sorted_members()112   const members_type& get_sorted_members() const { return members_in_id_order_; }
113 
is_struct()114   bool is_struct() const override { return !is_xception_; }
115 
is_xception()116   bool is_xception() const override { return is_xception_; }
117 
is_union()118   bool is_union() const { return is_union_; }
119 
get_field_by_name(std::string field_name)120   t_field* get_field_by_name(std::string field_name) {
121     return const_cast<t_field*>(const_cast<const t_struct&>(*this).get_field_by_name(field_name));
122   }
123 
get_field_by_name(std::string field_name)124   const t_field* get_field_by_name(std::string field_name) const {
125     members_type::const_iterator m_iter;
126     for (m_iter = members_in_id_order_.begin(); m_iter != members_in_id_order_.end(); ++m_iter) {
127       if ((*m_iter)->get_name() == field_name) {
128         return *m_iter;
129       }
130     }
131     return nullptr;
132   }
133 
134 private:
135   members_type members_;
136   members_type members_in_id_order_;
137   bool is_xception_;       // struct is an IDL exception
138   bool is_union_;          // struct is an IDL union
139   bool is_method_xcepts_;  // struct holds the exceptions declared at a service method
140   bool union_validated_;
141   bool xcepts_validated_;
142   int members_with_value_;
143 
144   bool xsd_all_;
145 
validate_member_field(t_field * field)146   void validate_member_field(t_field* field) {
147     validate_union_member(field);
148     validate_method_exception_field(field);
149   }
150 
validate_union_member(t_field * field)151   void validate_union_member(t_field* field) {
152     if (is_union_ && (!name_.empty())) {
153       union_validated_ = true;
154 
155       // 1) unions can't have required fields
156       // 2) union members are implicitly optional, otherwise bugs like THRIFT-3650 wait to happen
157       if (field->get_req() != t_field::T_OPTIONAL) {
158         // no warning on default requiredness, but do warn on anything else that is explicitly asked for
159         if(field->get_req() != t_field::T_OPT_IN_REQ_OUT) {
160           pwarning(1,
161                    "Union %s field %s: union members must be optional, ignoring specified requiredness.\n",
162                    name_.c_str(),
163                    field->get_name().c_str());
164         }
165         field->set_req(t_field::T_OPTIONAL);
166       }
167 
168       // unions may have up to one member defaulted, but not more
169       if (field->get_value() != nullptr) {
170         if (1 < ++members_with_value_) {
171           throw "Error: Field " + field->get_name() + " provides another default value for union "
172               + name_;
173         }
174       }
175     }
176   }
177 
validate_method_exception_field(t_field * field)178   void validate_method_exception_field(t_field* field) {
179     if (is_method_xcepts_) {
180       xcepts_validated_ = true;
181 
182       // THRIFT-5669: "required" makes no sense at "throws" clauses
183       if (field->get_req() == t_field::T_REQUIRED) {
184         field->set_req(t_field::T_OPT_IN_REQ_OUT);
185         pwarning(1,
186                  "Exception field %s: \"required\" is illegal here, ignoring.\n",
187                  field->get_name().c_str());
188       }
189     }
190   }
191 
needs_validation()192   bool needs_validation() {
193     if (is_method_xcepts_) {
194       return !xcepts_validated_;
195     }
196     if (is_union_) {
197       return !union_validated_;
198     }
199     return false;
200   }
201 
validate_members()202   void validate_members() {
203     if (needs_validation()) {
204       members_type::const_iterator m_iter;
205       for (m_iter = members_in_id_order_.begin(); m_iter != members_in_id_order_.end(); ++m_iter) {
206         validate_member_field(*m_iter);
207       }
208     }
209   }
210 
211 };
212 
213 #endif
214