1<?php
2
3// Protocol Buffers - Google's data interchange format
4// Copyright 2008 Google Inc.  All rights reserved.
5// https://developers.google.com/protocol-buffers/
6//
7// Redistribution and use in source and binary forms, with or without
8// modification, are permitted provided that the following conditions are
9// met:
10//
11//     * Redistributions of source code must retain the above copyright
12// notice, this list of conditions and the following disclaimer.
13//     * Redistributions in binary form must reproduce the above
14// copyright notice, this list of conditions and the following disclaimer
15// in the documentation and/or other materials provided with the
16// distribution.
17//     * Neither the name of Google Inc. nor the names of its
18// contributors may be used to endorse or promote products derived from
19// this software without specific prior written permission.
20//
21// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
33namespace Google\Protobuf\Internal;
34
35class FieldDescriptor
36{
37    use HasPublicDescriptorTrait;
38
39    private $name;
40    private $json_name;
41    private $setter;
42    private $getter;
43    private $number;
44    private $label;
45    private $type;
46    private $message_type;
47    private $enum_type;
48    private $packed;
49    private $is_map;
50    private $oneof_index = -1;
51
52    public function __construct()
53    {
54        $this->public_desc = new \Google\Protobuf\FieldDescriptor($this);
55    }
56
57    public function setOneofIndex($index)
58    {
59        $this->oneof_index = $index;
60    }
61
62    public function getOneofIndex()
63    {
64        return $this->oneof_index;
65    }
66
67    public function setName($name)
68    {
69        $this->name = $name;
70    }
71
72    public function getName()
73    {
74        return $this->name;
75    }
76
77    public function setJsonName($json_name)
78    {
79        $this->json_name = $json_name;
80    }
81
82    public function getJsonName()
83    {
84        return $this->json_name;
85    }
86
87    public function setSetter($setter)
88    {
89        $this->setter = $setter;
90    }
91
92    public function getSetter()
93    {
94        return $this->setter;
95    }
96
97    public function setGetter($getter)
98    {
99        $this->getter = $getter;
100    }
101
102    public function getGetter()
103    {
104        return $this->getter;
105    }
106
107    public function setNumber($number)
108    {
109        $this->number = $number;
110    }
111
112    public function getNumber()
113    {
114        return $this->number;
115    }
116
117    public function setLabel($label)
118    {
119        $this->label = $label;
120    }
121
122    public function getLabel()
123    {
124        return $this->label;
125    }
126
127    public function isRepeated()
128    {
129        return $this->label === GPBLabel::REPEATED;
130    }
131
132    public function setType($type)
133    {
134        $this->type = $type;
135    }
136
137    public function getType()
138    {
139        return $this->type;
140    }
141
142    public function setMessageType($message_type)
143    {
144        $this->message_type = $message_type;
145    }
146
147    public function getMessageType()
148    {
149        return $this->message_type;
150    }
151
152    public function setEnumType($enum_type)
153    {
154        $this->enum_type = $enum_type;
155    }
156
157    public function getEnumType()
158    {
159        return $this->enum_type;
160    }
161
162    public function setPacked($packed)
163    {
164        $this->packed = $packed;
165    }
166
167    public function getPacked()
168    {
169        return $this->packed;
170    }
171
172    public function isPackable()
173    {
174        return $this->isRepeated() && self::isTypePackable($this->type);
175    }
176
177    public function isMap()
178    {
179        return $this->getType() == GPBType::MESSAGE &&
180               !is_null($this->getMessageType()->getOptions()) &&
181               $this->getMessageType()->getOptions()->getMapEntry();
182    }
183
184    public function isTimestamp()
185    {
186        return $this->getType() == GPBType::MESSAGE &&
187            $this->getMessageType()->getClass() === "Google\Protobuf\Timestamp";
188    }
189
190    public function isWrapperType()
191    {
192        if ($this->getType() == GPBType::MESSAGE) {
193            $class = $this->getMessageType()->getClass();
194            return in_array($class, [
195                "Google\Protobuf\DoubleValue",
196                "Google\Protobuf\FloatValue",
197                "Google\Protobuf\Int64Value",
198                "Google\Protobuf\UInt64Value",
199                "Google\Protobuf\Int32Value",
200                "Google\Protobuf\UInt32Value",
201                "Google\Protobuf\BoolValue",
202                "Google\Protobuf\StringValue",
203                "Google\Protobuf\BytesValue",
204            ]);
205        }
206        return false;
207    }
208
209    private static function isTypePackable($field_type)
210    {
211        return ($field_type !== GPBType::STRING  &&
212            $field_type !== GPBType::GROUP   &&
213            $field_type !== GPBType::MESSAGE &&
214            $field_type !== GPBType::BYTES);
215    }
216
217    public static function getFieldDescriptor($proto)
218    {
219        $type_name = null;
220        $type = $proto->getType();
221        switch ($type) {
222            case GPBType::MESSAGE:
223            case GPBType::GROUP:
224            case GPBType::ENUM:
225                $type_name = $proto->getTypeName();
226                break;
227            default:
228                break;
229        }
230
231        $oneof_index = $proto->hasOneofIndex() ? $proto->getOneofIndex() : -1;
232        $packed = false;
233        $options = $proto->getOptions();
234        if ($options !== null) {
235            $packed = $options->getPacked();
236        }
237
238        $field = new FieldDescriptor();
239        $field->setName($proto->getName());
240
241        $json_name = $proto->hasJsonName() ? $proto->getJsonName() :
242            lcfirst(implode('', array_map('ucwords', explode('_', $proto->getName()))));
243        if ($proto->hasJsonName()) {
244            $json_name = $proto->getJsonName();
245        } else {
246            $proto_name = $proto->getName();
247            $json_name = implode('', array_map('ucwords', explode('_', $proto_name)));
248            if ($proto_name[0] !== "_" && !ctype_upper($proto_name[0])) {
249                $json_name = lcfirst($json_name);
250            }
251        }
252        $field->setJsonName($json_name);
253
254        $camel_name = implode('', array_map('ucwords', explode('_', $proto->getName())));
255        $field->setGetter('get' . $camel_name);
256        $field->setSetter('set' . $camel_name);
257        $field->setType($proto->getType());
258        $field->setNumber($proto->getNumber());
259        $field->setLabel($proto->getLabel());
260        $field->setPacked($packed);
261        $field->setOneofIndex($oneof_index);
262
263        // At this time, the message/enum type may have not been added to pool.
264        // So we use the type name as place holder and will replace it with the
265        // actual descriptor in cross building.
266        switch ($type) {
267            case GPBType::MESSAGE:
268                $field->setMessageType($type_name);
269                break;
270            case GPBType::ENUM:
271                $field->setEnumType($type_name);
272                break;
273            default:
274                break;
275        }
276
277        return $field;
278    }
279
280    public static function buildFromProto($proto)
281    {
282        return FieldDescriptor::getFieldDescriptor($proto);
283    }
284}
285