QueryProcessor   F
last analyzed

Complexity

Total Complexity 68

Size/Duplication

Total Lines 554
Duplicated Lines 0 %

Test Coverage

Coverage 97.03%

Importance

Changes 2
Bugs 1 Features 0
Metric Value
wmc 68
eloc 181
c 2
b 1
f 0
dl 0
loc 554
ccs 196
cts 202
cp 0.9703
rs 2.96

14 Methods

Rating   Name   Duplication   Size   Complexity  
A _readSkipOrTopOption() 0 29 4
B _checkForEmptyQueryArguments() 0 13 9
A _processFilter() 0 21 5
B _processCount() 0 43 6
B _processExpandAndSelect() 0 41 8
B _processSkipAndTop() 0 35 9
A _checkSetQueryApplicable() 0 5 2
A _processSkipToken() 0 32 4
A _isSSPagingRequired() 0 9 2
A _checkExpandOrSelectApplicable() 0 5 2
B __construct() 0 24 7
A process() 0 10 2
B _processOrderBy() 0 45 7
A _processQuery() 0 8 1

How to fix   Complexity   

Complex Class

Complex classes like QueryProcessor often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use QueryProcessor, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace POData\UriProcessor\QueryProcessor;
4
5
use POData\Providers\Metadata\Type\Int32;
6
use POData\Providers\Metadata\ResourceTypeKind;
7
use POData\UriProcessor\RequestDescription;
8
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\TargetKind;
9
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\TargetSource;
10
use POData\UriProcessor\QueryProcessor\SkipTokenParser\SkipTokenParser;
11
use POData\UriProcessor\QueryProcessor\OrderByParser\OrderByParser;
12
use POData\UriProcessor\QueryProcessor\ExpressionParser\ExpressionParser2;
13
use POData\UriProcessor\QueryProcessor\ExpandProjectionParser\ExpandProjectionParser;
14
use POData\Common\Messages;
15
use POData\Common\ODataException;
16
use POData\Common\ODataConstants;
17
use POData\IService;
18
use POData\Providers\Query\QueryType;
19
20
/**
21
 * Class QueryProcessor
22
 * @package POData\UriProcessor\QueryProcessor
23
 */
24
class QueryProcessor
25
{
26
    /**
27
     * Holds details of the request that client has submitted.
28
     *
29
     * @var RequestDescription
30
     */
31
    private $request;
32
33
    /**
34
     * Holds reference to the underlying data service specific
35
     * instance.
36
     *
37
     * @var IService
38
     */
39
    private $service;
40
41
    /**
42
     * If $orderby, $skip, $top and $count options can be applied to the request.
43
     * @var boolean
44
     */
45
    private $_setQueryApplicable;
46
47
    /**
48
     * Whether the top level request is a candidate for paging
49
     *
50
     * @var boolean
51
     */
52
    private $_pagingApplicable;
53
54
    /**
55
     * Whether $expand, $select can be applied to the request.
56
     *
57
     * @var boolean
58
     */
59
    private $_expandSelectApplicable;
60
61
    /**
62
     * Creates new instance of QueryProcessor
63
     *
64
     * @param RequestDescription $request Description of the request submitted by client.
65
     * @param IService        $service        Reference to the service implementation.
66
     */
67 88
    private function __construct(RequestDescription $request, IService $service) {
68 88
        $this->request = $request;
69 88
        $this->service = $service;
70
71 88
        $isSingleResult = $request->isSingleResult();
72
73
        //$top, $skip, $order, $inlinecount & $count are only applicable if:
74
        //The query targets a resource collection
75 88
        $this->_setQueryApplicable = ($request->getTargetKind() == TargetKind::RESOURCE && !$isSingleResult);
76
        //Or it's a $count resource (although $inlinecount isn't applicable in this case..but there's a check somewhere else for this
77 88
        $this->_setQueryApplicable |= $request->queryType == QueryType::COUNT;
78
79
        //Paging is allowed if
80
        //The request targets a resource collection
81
        //and the request isn't for a $count segment
82 88
        $this->_pagingApplicable = $this->request->getTargetKind() == TargetKind::RESOURCE && !$isSingleResult && ($request->queryType != QueryType::COUNT);
83
84 88
        $targetResourceType = $this->request->getTargetResourceType();
85 88
        $targetResourceSetWrapper = $this->request->getTargetResourceSetWrapper();
86
87 88
        $this->_expandSelectApplicable = !is_null($targetResourceType)
88 88
            && !is_null($targetResourceSetWrapper)
89 88
            && $targetResourceType->getResourceTypeKind() == ResourceTypeKind::ENTITY
0 ignored issues
show
introduced by
The condition $targetResourceType->get...esourceTypeKind::ENTITY is always false.
Loading history...
90 88
            && !$this->request->isLinkUri();
91
92
    }
93
94
    /**
95
     * Process the OData query options and update RequestDescription accordingly.
96
     *
97
     * @param RequestDescription $request Description of the request submitted by client.
98
     * @param IService        $service        Reference to the data service.
99
     *
100
     * @return void
101
     *
102
     * @throws ODataException
103
     */
104 88
    public static function process(RequestDescription $request, IService $service) {
105 88
        $queryProcessor = new QueryProcessor($request, $service);
106 88
        if ($request->getTargetSource() == TargetSource::NONE) {
107
            //A service directory, metadata or batch request
108 1
            $queryProcessor->_checkForEmptyQueryArguments();
109
        } else {
110 88
            $queryProcessor->_processQuery();
111
        }
112
113 66
        unset($queryProcessor);
114
    }
115
116
117
118
119
120
    /**
121
     * Processes the odata query options in the request uri and update the request description instance with processed details.
122
     * @return void
123
     *
124
     * @throws ODataException If any error occured while processing the query options.
125
     *
126
     */
127 88
    private function _processQuery()
128
    {
129 88
        $this->_processSkipAndTop();
130 84
        $this->_processOrderBy();
131 83
        $this->_processFilter();
132 80
        $this->_processCount();
133 73
        $this->_processSkipToken();
134 71
        $this->_processExpandAndSelect();
135
    }
136
137
    /**
138
     * Process $skip and $top options
139
     *
140
     * @return void
141
     *
142
     * @throws ODataException Throws syntax error if the $skip or $top option
143
     *                        is specified with non-integer value, throws
144
     *                        bad request error if the $skip or $top option
145
     *                        is not applicable for the requested resource.
146
     */
147 88
    private function _processSkipAndTop()
148
    {
149 88
        $value = null;
150 88
        if ($this->_readSkipOrTopOption(ODataConstants::HTTPQUERY_STRING_SKIP, $value)) {
151 5
            $this->request->setSkipCount($value);
152
        }
153
154 88
        $pageSize = 0;
155 88
        $isPagingRequired = $this->_isSSPagingRequired();
156 88
        if ($isPagingRequired) {
157 56
            $pageSize = $this->request
158 56
                ->getTargetResourceSetWrapper()
159 56
                ->getResourceSetPageSize();
160
        }
161
162 88
        if ($this->_readSkipOrTopOption(ODataConstants::HTTPQUERY_STRING_TOP, $value)) {
163 7
            $this->request->setTopOptionCount($value);
164 7
            if ($isPagingRequired && $pageSize < $value) {
165
                //If $top is greater than or equal to page size,
166
                //we will need a $skiptoken and thus our response
167
                //will be 2.0
168 2
                $this->request->raiseResponseVersion(2, 0);
169 1
                $this->request->setTopCount($pageSize);
170
            } else {
171 7
                $this->request->setTopCount($value);
172
            }
173 83
        } else if ($isPagingRequired) {
174 54
            $this->request->raiseResponseVersion(2, 0);
175 51
            $this->request->setTopCount($pageSize);
176
        }
177
178 85
        if (!is_null($this->request->getSkipCount())
179 85
            || !is_null($this->request->getTopCount())
180
        ) {
181 57
            $this->_checkSetQueryApplicable();
182
        }
183
    }
184
185
    /**
186
     * Process $orderby option, This function requires _processSkipAndTopOption
187
     * function to be already called as this function need to know whether
188
     * client has requested for skip, top or paging is enabled for the
189
     * requested resource in these cases function generates additional orderby
190
     * expression using keys.
191
     *
192
     * @return void
193
     *
194
     * @throws ODataException If any error occurs while parsing orderby option.
195
     */
196 84
    private function _processOrderBy()
197
    {
198 84
        $orderBy = $this->request->getQueryStringItem(ODataConstants::HTTPQUERY_STRING_ORDERBY);
199
200 84
        if (!is_null($orderBy)) {
201 6
            $this->_checkSetQueryApplicable();
202
        }
203
204 83
        $targetResourceType = $this->request->getTargetResourceType();
205
        //assert($targetResourceType != null)
206
        /**
207
         * We need to do sorting in the folowing cases, irrespective of
208
         * $orderby clause is present or not.
209
         * 1. If $top or $skip is specified
210
         *     skip and take will be applied on sorted list only. If $skip
211
         *     is specified then RequestDescription::getSkipCount will give
212
         *     non-null value. If $top is specified then
213
         *     RequestDescription::getTopCount will give non-null value.
214
         * 2. If server side paging is enabled for the requested resource
215
         *     If server-side paging is enabled for the requested resource then
216
         *     RequestDescription::getTopCount will give non-null value.
217
         *
218
         */
219 83
        if (!is_null($this->request->getSkipCount()) || !is_null($this->request->getTopCount())) {
220 56
            $orderBy = !is_null($orderBy) ? $orderBy . ', ' : null;
221 56
            $keys = array_keys($targetResourceType->getKeyProperties());
222
            //assert(!empty($keys))
223 56
            foreach ($keys as $key) {
224 56
                $orderBy = $orderBy . $key . ', ';
225
            }
226
227 56
            $orderBy = rtrim($orderBy, ', ');
0 ignored issues
show
Bug introduced by
It seems like $orderBy can also be of type null; however, parameter $string of rtrim() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

227
            $orderBy = rtrim(/** @scrutinizer ignore-type */ $orderBy, ', ');
Loading history...
228
        }
229
230 83
        if (!is_null($orderBy)) {
231
232 57
            $internalOrderByInfo = OrderByParser::parseOrderByClause(
233 57
                $this->request->getTargetResourceSetWrapper(),
234 57
                $targetResourceType,
235 57
                $orderBy,
236 57
                $this->service->getProvidersWrapper()
237 57
            );
238
239 57
            $this->request->setInternalOrderByInfo(
240 57
                $internalOrderByInfo
241 57
            );
242
243
        }
244
    }
245
246
    /**
247
     * Process the $filter option in the request and update request decription.
248
     *
249
     * @return void
250
     *
251
     * @throws ODataException Throws error in the following cases:
252
     *                          (1) If $filter cannot be applied to the
253
     *                              resource targeted by the request uri
254
     *                          (2) If any error occured while parsing and
255
     *                              translating the odata $filter expression
256
     *                              to expression tree
257
     *                          (3) If any error occured while generating
258
     *                              php expression from expression tree
259
     */
260 83
    private function _processFilter()
261
    {
262 83
        $filter = $this->request->getQueryStringItem(ODataConstants::HTTPQUERY_STRING_FILTER);
263
264 83
        if (is_null($filter)) {
265 36
            return;
266
        }
267
268 47
        $kind = $this->request->getTargetKind();
269 47
        if (!($kind == TargetKind::RESOURCE
270 5
            || $kind == TargetKind::COMPLEX_OBJECT
271 47
            || $this->request->queryType == QueryType::COUNT)
272
        ) {
273 3
            throw ODataException::createBadRequestError(
274 3
                Messages::queryProcessorQueryFilterOptionNotApplicable()
275 3
            );
276
        }
277 44
        $resourceType = $this->request->getTargetResourceType();
278 44
        $expressionProvider = $this->service->getProvidersWrapper()->getExpressionProvider($this->request);
279 44
        $filterInfo = ExpressionParser2::parseExpression2($filter, $resourceType, $expressionProvider);
280 44
        $this->request->setFilterInfo($filterInfo);
281
282
    }
283
284
    /**
285
     * Process the $inlinecount option and update the request description.
286
     *
287
     * @return void
288
     *
289
     * @throws ODataException Throws bad request error in the following cases
290
     *                          (1) If $inlinecount is disabled by the developer
291
     *                          (2) If both $count and $inlinecount specified
292
     *                          (3) If $inlinecount value is unknown
293
     *                          (4) If capability negotiation over version fails
294
     */
295 80
    private function _processCount()
296
    {
297 80
        $inlineCount = $this->request->getQueryStringItem(ODataConstants::HTTPQUERY_STRING_INLINECOUNT);
298
299
        //If it's not specified, we're done
300 80
        if (is_null($inlineCount)) {
301 69
            return;
302
        }
303
304
        //If the service doesn't allow count requests..then throw an exception
305 11
        if (!$this->service->getConfiguration()->getAcceptCountRequests()) {
306 1
            throw ODataException::createBadRequestError(
307 1
                Messages::configurationCountNotAccepted()
308 1
            );
309
        }
310
311 10
        $inlineCount = trim($inlineCount);
312
313
        //if it's set to none, we don't do inline counts
314 10
        if ($inlineCount === ODataConstants::URI_ROWCOUNT_OFFOPTION) {
315 1
            return;
316
        }
317
318
        //You can't specify $count & $inlinecount together
319
        //TODO: ensure there's a test for this case see #55
320 9
        if ($this->request->queryType == QueryType::COUNT) {
321 1
            throw ODataException::createBadRequestError(
322 1
                Messages::queryProcessorInlineCountWithValueCount()
323 1
            );
324
        }
325
326 8
        $this->_checkSetQueryApplicable(); //TODO: why do we do this check?
327
328
329 7
        if ($inlineCount === ODataConstants::URI_ROWCOUNT_ALLOPTION) {
330 6
            $this->request->queryType = QueryType::ENTITIES_WITH_COUNT;
331
332 6
            $this->request->raiseMinVersionRequirement(2, 0);
333 4
            $this->request->raiseResponseVersion(2, 0);
334
335
        } else {
336 1
            throw ODataException::createBadRequestError(
337 1
                Messages::queryProcessorInvalidInlineCountOptionError()
338 1
            );
339
        }
340
341
    }
342
343
    /**
344
     * Process the $skiptoken option in the request and update the request
345
     * description, this function requires _processOrderBy method to be
346
     * already invoked.
347
     *
348
     * @return void
349
     *
350
     * @throws ODataException Throws bad request error in the following cases
351
     *                          (1) If $skiptoken cannot be applied to the
352
     *                              resource targeted by the request uri
353
     *                          (2) If paging is not enabled for the resource
354
     *                              targeted by the request uri
355
     *                          (3) If parsing of $skiptoken fails
356
     *                          (4) If capability negotiation over version fails
357
     */
358 73
    private function _processSkipToken()
359
    {
360 73
        $skipToken = $this->request->getQueryStringItem(ODataConstants::HTTPQUERY_STRING_SKIPTOKEN);
361 73
        if (is_null($skipToken)) {
362 69
            return;
363
        }
364
365 5
        if (!$this->_pagingApplicable) {
366 2
            throw ODataException::createBadRequestError(
367 2
                Messages::queryProcessorSkipTokenNotAllowed()
368 2
            );
369
        }
370
371 3
        if (!$this->_isSSPagingRequired()) {
372
            throw ODataException::createBadRequestError(
373
                Messages::queryProcessorSkipTokenCannotBeAppliedForNonPagedResourceSet($this->request->getTargetResourceSetWrapper())
0 ignored issues
show
Bug introduced by
$this->request->getTargetResourceSetWrapper() of type POData\Providers\Metadata\ResourceSetWrapper is incompatible with the type string expected by parameter $resourceSetName of POData\Common\Messages::...orNonPagedResourceSet(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

373
                Messages::queryProcessorSkipTokenCannotBeAppliedForNonPagedResourceSet(/** @scrutinizer ignore-type */ $this->request->getTargetResourceSetWrapper())
Loading history...
374
            );
375
        }
376
377 3
        $internalOrderByInfo = $this->request->getInternalOrderByInfo();
378
        //assert($internalOrderByInfo != null)
379 3
        $targetResourceType = $this->request->getTargetResourceType();
380
        //assert($targetResourceType != null)
381
382 3
        $internalSkipTokenInfo = SkipTokenParser::parseSkipTokenClause(
383 3
            $targetResourceType,
384 3
            $internalOrderByInfo,
0 ignored issues
show
Bug introduced by
It seems like $internalOrderByInfo can also be of type null; however, parameter $internalOrderByInfo of POData\UriProcessor\Quer...:parseSkipTokenClause() does only seem to accept POData\UriProcessor\Quer...ser\InternalOrderByInfo, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

384
            /** @scrutinizer ignore-type */ $internalOrderByInfo,
Loading history...
385 3
            $skipToken
386 3
        );
387 3
        $this->request->setInternalSkipTokenInfo($internalSkipTokenInfo);
388 3
        $this->request->raiseMinVersionRequirement(2, 0);
389 3
        $this->request->raiseResponseVersion(2, 0);
390
391
392
    }
393
394
    /**
395
     * Process the $expand and $select option and update the request description.
396
     *
397
     * @return void
398
     *
399
     * @throws ODataException Throws bad request error in the following cases
400
     *                          (1) If $expand or select cannot be applied to the
401
     *                              requested resource.
402
     *                          (2) If projection is disabled by the developer
403
     *                          (3) If some error occurs while parsing the options
404
     */
405 71
    private function _processExpandAndSelect()
406
    {
407 71
        $expand = $this->request->getQueryStringItem(ODataConstants::HTTPQUERY_STRING_EXPAND);
408
409 71
        if (!is_null($expand)) {
410 9
            $this->_checkExpandOrSelectApplicable(ODataConstants::HTTPQUERY_STRING_EXPAND);
411
        }
412
413 71
        $select = $this->request->getQueryStringItem(ODataConstants::HTTPQUERY_STRING_SELECT);
414
415 71
        if (!is_null($select)) {
416 7
            if (!$this->service->getConfiguration()->getAcceptProjectionRequests()) {
417 1
                throw ODataException::createBadRequestError(Messages::configurationProjectionsNotAccepted());
418
            }
419
420 6
            $this->_checkExpandOrSelectApplicable(ODataConstants::HTTPQUERY_STRING_SELECT);
421
        }
422
423
        // We will generate RootProjectionNode in case of $link request also, but
424
        // expand and select in this case must be null (we are ensuring this above)
425
        // 'RootProjectionNode' is required while generating next page Link
426 68
        if ($this->_expandSelectApplicable || $this->request->isLinkUri()) {
427
428 60
            $rootProjectionNode = ExpandProjectionParser::parseExpandAndSelectClause(
429 60
                    $this->request->getTargetResourceSetWrapper(),
430 60
                    $this->request->getTargetResourceType(),
431 60
                    $this->request->getInternalOrderByInfo(),
432 60
                    $this->request->getSkipCount(),
433 60
                    $this->request->getTopCount(),
434 60
                    $expand,
435 60
                    $select,
436 60
                    $this->service->getProvidersWrapper()
437 60
            );
438 60
            if ($rootProjectionNode->isSelectionSpecified()) {
439 4
                $this->request->raiseMinVersionRequirement(2, 0);
440
            }
441
442 59
            if ($rootProjectionNode->hasPagedExpandedResult()) {
443 3
                $this->request->raiseResponseVersion(2, 0);
444
            }
445 58
            $this->request->setRootProjectionNode($rootProjectionNode);
446
447
        }
448
    }
449
450
    /**
451
     * Is server side paging is configured, this function return true
452
     * if the resource targeted by the resource path is applicable
453
     * for paging and paging is enabled for the targeted resource set
454
     * else false.
455
     *
456
     * @return boolean
457
     */
458 88
    private function _isSSPagingRequired()
459
    {
460 88
        if ($this->_pagingApplicable) {
461 62
            $targetResourceSetWrapper = $this->request->getTargetResourceSetWrapper();
462
            //assert($targetResourceSetWrapper != NULL)
463 62
            return ($targetResourceSetWrapper->getResourceSetPageSize() != 0);
464
        }
465
466 26
        return false;
467
    }
468
469
    /**
470
     * Read skip or top query option value which is expected to be positive
471
     * integer.
472
     *
473
     * @param string $queryItem The name of the query item to read from request
474
     *                          uri ($skip or $top).
475
     * @param int    &$value    On return, If the requested query item is
476
     *                          present with a valid integer value then this
477
     *                          argument will holds that integer value
478
     *                          otherwise holds zero.
479
     *
480
     * @return boolean True     If the requested query item with valid integer
481
     *                          value is present in the request, false query
482
     *                          item is absent in the request uri.
483
     *
484
     * @throws ODataException   Throws syntax error if the requested argument
485
     *                          is present and it is not an integer.
486
     */
487 88
    private function _readSkipOrTopOption($queryItem, &$value)
488
    {
489 88
        $value = $this->request->getQueryStringItem($queryItem);
490 88
        if (!is_null($value)) {
491 9
            $int = new Int32();
492 9
            if (!$int->validate($value, $outValue)) {
493 1
                throw ODataException::createSyntaxError(
494 1
                    Messages::queryProcessorIncorrectArgumentFormat(
495 1
                        $queryItem,
496 1
                        $value
497 1
                    )
498 1
                );
499
            }
500
501 9
            $value = intval($value);
502 9
            if ($value < 0) {
503 1
                throw ODataException::createSyntaxError(
504 1
                    Messages::queryProcessorIncorrectArgumentFormat(
505 1
                        $queryItem,
506 1
                        $value
507 1
                    )
508 1
                );
509
            }
510
511 8
            return true;
512
        }
513
514 86
        $value = 0;
515 86
        return false;
516
    }
517
518
    /**
519
     * Checks whether client request contains any odata query options.
520
     *
521
     * @return void
522
     *
523
     * @throws ODataException Throws bad request error if client request
524
     *                        includes any odata query option.
525
     */
526 1
    private function _checkForEmptyQueryArguments()
527
    {
528 1
        if (!is_null($this->request->getQueryStringItem(ODataConstants::HTTPQUERY_STRING_FILTER))
529 1
            || !is_null($this->request->getQueryStringItem(ODataConstants::HTTPQUERY_STRING_EXPAND))
530 1
            || !is_null($this->request->getQueryStringItem(ODataConstants::HTTPQUERY_STRING_INLINECOUNT))
531 1
            || !is_null($this->request->getQueryStringItem(ODataConstants::HTTPQUERY_STRING_ORDERBY))
532 1
            || !is_null($this->request->getQueryStringItem(ODataConstants::HTTPQUERY_STRING_SELECT))
533 1
            || !is_null($this->request->getQueryStringItem(ODataConstants::HTTPQUERY_STRING_SKIP))
534 1
            || !is_null($this->request->getQueryStringItem(ODataConstants::HTTPQUERY_STRING_SKIPTOKEN))
535 1
            || !is_null($this->request->getQueryStringItem(ODataConstants::HTTPQUERY_STRING_TOP))
536
        ) {
537
            throw ODataException::createBadRequestError(
538
                Messages::queryProcessorNoQueryOptionsApplicable()
539
            );
540
        }
541
    }
542
543
    /**
544
     * To check whether the the query options $orderby, $inlinecount, $skip
545
     * or $top is applicable for the current requested resource.
546
     *
547
     * @return void
548
     *
549
     * @throws ODataException Throws bad request error if any of the query options $orderby, $inlinecount, $skip or $top cannot be applied to the requested resource.
550
     *
551
     */
552 63
    private function _checkSetQueryApplicable()
553
    {
554 63
        if (!$this->_setQueryApplicable) {
555 3
            throw ODataException::createBadRequestError(
556 3
                Messages::queryProcessorQuerySetOptionsNotApplicable()
557 3
            );
558
        }
559
    }
560
561
    /**
562
     * To check whether the the query options $select, $expand
563
     * is applicable for the current requested resource.
564
     *
565
     * @param string $queryItem The query option to check.
566
     *
567
     * @return void
568
     *
569
     * @throws ODataException Throws bad request error if the query
570
     *                        options $select, $expand cannot be
571
     *                        applied to the requested resource.
572
     */
573 9
    private function _checkExpandOrSelectApplicable($queryItem)
574
    {
575 9
        if (!$this->_expandSelectApplicable) {
576 2
            throw ODataException::createBadRequestError(
577 2
                Messages::queryProcessorSelectOrExpandOptionNotApplicable($queryItem)
578 2
            );
579
        }
580
    }
581
}
582