Completed
Push — master ( 934444...bee73a )
by Bálint
10:05
created

JsonLightODataWriter::writeServiceDocument()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 31
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 31
rs 8.8571
cc 2
eloc 21
nc 2
nop 1
1
<?php
2
3
namespace POData\Writers\Json;
4
5
use POData\ObjectModel\ODataFeed;
6
use POData\ObjectModel\ODataEntry;
7
use POData\ObjectModel\ODataURLCollection;
8
use POData\ObjectModel\ODataURL;
9
use POData\ObjectModel\ODataLink;
10
use POData\ObjectModel\ODataPropertyContent;
11
use POData\ObjectModel\ODataBagContent;
12
use POData\ObjectModel\ODataProperty;
13
use POData\ObjectModel\ODataMediaLink;
14
use POData\Writers\Json\JsonWriter;
15
use POData\Common\Version;
16
use POData\Common\ODataConstants;
17
use POData\Common\MimeTypes;
18
use POData\Common\Messages;
19
use POData\Common\ODataException;
20
use POData\Common\InvalidOperationException;
21
use POData\Providers\ProvidersWrapper;
22
23
use POData\Writers\Json\JsonLightMetadataLevel;
24
25
26
/**
27
 * Class JsonLightODataWriter is a writer for the json format in OData V3 also known as JSON Light
28
 * @package POData\Writers\Json
29
 */
30
class JsonLightODataWriter extends JsonODataV2Writer
31
{
32
33
	/**
34
	 * @var JsonLightMetadataLevel
35
	 */
36
	protected $metadataLevel;
37
38
39
	/**
40
	 *
41
	 * The service base uri
42
	 * @var string
43
	 */
44
	protected $baseUri;
45
46
47
	public function __construct(JsonLightMetadataLevel $metadataLevel, $absoluteServiceUri)
48
	{
49
		if(strlen($absoluteServiceUri) == 0)
50
		{
51
			throw new \Exception("absoluteServiceUri must not be empty or null");
52
		}
53
		$this->baseUri = $absoluteServiceUri;
54
55
		$this->_writer = new JsonWriter('');
56
		$this->urlKey = ODataConstants::JSON_URL_STRING;
57
		$this->dataArrayName = ODataConstants::JSON_LIGHT_VALUE_NAME;
58
		$this->rowCountName = ODataConstants::JSON_LIGHT_ROWCOUNT_STRING;
59
		$this->metadataLevel = $metadataLevel;
60
	}
61
62
63
	/**
64
	 * Determines if the given writer is capable of writing the response or not
65
	 * @param Version $responseVersion the OData version of the response
66
	 * @param string $contentType the Content Type of the response
67
	 * @return boolean true if the writer can handle the response, false otherwise
68
	 */
69
	public function canHandle(Version $responseVersion, $contentType)
70
	{
71
		if($responseVersion != Version::v3()){
72
			return false;
73
		}
74
75
		$parts = explode(";", $contentType);
76
77
		//It must be app/json and have the right odata= piece
78
		$metadata = array_filter($parts, function ($item) { return strpos($item, 'odata') !== false; });
79
		return in_array(MimeTypes::MIME_APPLICATION_JSON, $parts) && (empty($metadata) || in_array($this->metadataLevel->getValue(), $metadata));
80
	}
81
82
83
84
	/**
85
	 * Write the given OData model in a specific response format
86
	 *
87
	 * @param  ODataURL|ODataURLCollection|ODataPropertyContent|ODataFeed|ODataEntry $model Object of requested content.
88
	 *
89
	 * @return JsonLightODataWriter
90
	 */
91
	public function write($model){
92
		$this->_writer->startObjectScope();
93
94
		if ($model instanceof ODataURL) {
95
			$this->writeTopLevelMeta("url");
96
			$this->writeURL($model);
97
		} elseif ($model instanceof ODataURLCollection) {
98
			$this->writeTopLevelMeta("urlCollection");
99
			$this->writeURLCollection($model);
100
		} elseif ($model instanceof ODataPropertyContent) {
101
			$this->writeTopLevelMeta( $model->properties[0]->typeName );
102
			$this->writeTopLevelProperty($model->properties[0]);
103
		} elseif ($model instanceof ODataFeed) {
104
			$this->writeTopLevelMeta($model->title);
105
			$this->writeRowCount($model->rowCount);
106
			$this->writeNextPageLink($model->nextPageLink);
107
			$this->_writer
108
				->writeName($this->dataArrayName)
109
				->startArrayScope();
110
			$this->writeFeed($model);
111
			$this->_writer->endScope();
112
		}elseif ($model instanceof ODataEntry) {
113
			$this->writeTopLevelMeta($model->resourceSetName . "/@Element");
114
			$this->writeEntry($model);
115
		}
116
117
		$this->_writer->endScope();
118
119
		return $this;
120
	}
121
122
123
	/**
124
	 *
125
	 * @param ODataProperty $property
126
	 *
127
	 * @return JsonLightODataWriter
128
	 */
129
	protected function writeTopLevelProperty(ODataProperty $property)
130
	{
131
		$this->writePropertyMeta($property);
132
		if ($property->value == null) {
133
			$this->_writer->writeName(ODataConstants::JSON_LIGHT_VALUE_NAME);
134
			$this->_writer->writeValue("null");
135
		} elseif ($property->value instanceof ODataPropertyContent) {
136
			//In the case of complex properties at the top level we don't write the name of the property,
137
			//just the sub properties.
138
			$this->writeComplexPropertyMeta($property)
139
				->writeProperties($property->value);
140
		} elseif ($property->value instanceof ODataBagContent) {
141
			$this->_writer->writeName(ODataConstants::JSON_LIGHT_VALUE_NAME);
142
			$this->writeBagContent($property->value);
143
		} else {
144
			$this->_writer->writeName(ODataConstants::JSON_LIGHT_VALUE_NAME);
145
			$this->_writer->writeValue($property->value, $property->typeName);
146
		}
147
148
		return $this;
149
	}
150
151
152
153
	protected function writeTopLevelMeta($fragment)
154
	{
155
		if($this->metadataLevel == JsonLightMetadataLevel::NONE())
156
		{
157
			return;
158
		}
159
160
		$this->_writer
161
			->writeName(ODataConstants::JSON_LIGHT_METADATA_STRING)
162
			->writeValue($this->baseUri . '/' . ODataConstants::URI_METADATA_SEGMENT . '#' . $fragment);
163
164
	}
165
166
167
	protected function writePropertyMeta(ODataProperty $property)
168
	{
169
		if($this->metadataLevel != JsonLightMetadataLevel::FULL())
170
		{
171
			//Only full meta level outputs this info
172
			return $this;
173
		}
174
175
		if(is_null($property->value))
176
		{
177
			//it appears full metadata doesn't output types for nulls...
178
			return $this;
179
		}
180
181
182
		switch($property->typeName)
183
		{
184
			//The type metadata is only included on certain types of properties
185
			//Note this also excludes Complex types
186
187
			case "Edm.Decimal":
188
			case "Edm.DateTime":
189
				$this->_writer
190
					->writeName($property->name . ODataConstants::JSON_LIGHT_METADATA_PROPERTY_TYPE_SUFFIX_STRING)
191
					->writeValue($property->typeName);
192
		}
193
194
195
		return $this;
196
	}
197
198
	/**
199
	 *
200
	 * @param ODataEntry $entry Entry to write metadata for.
201
	 *
202
	 * @return JsonLightODataWriter
203
	 */
204
	protected function writeEntryMetadata(ODataEntry $entry){
205
206
		if($this->metadataLevel != JsonLightMetadataLevel::FULL())
207
		{
208
			//Only full meta level outputs this info
209
			return $this;
210
		}
211
212
		$this->_writer
213
			->writeName(ODataConstants::JSON_LIGHT_METADATA_TYPE_STRING)
214
			->writeValue($entry->type)
215
			->writeName(ODataConstants::JSON_LIGHT_METADATA_ID_STRING)
216
			->writeValue($entry->id)
217
			->writeName(ODataConstants::JSON_LIGHT_METADATA_ETAG_STRING)
218
			->writeValue($entry->eTag)
219
			->writeName(ODataConstants::JSON_LIGHT_METADATA_EDIT_LINK_STRING)
220
			->writeValue($entry->editLink)
221
		;
222
223
		return $this;
224
	}
225
226
227
	/**
228
	 * @param ODataLink $link Link to write.
229
	 *
230
	 * @return JsonLightODataWriter
231
	 */
232
	protected function writeLink(ODataLink $link){
233
234
		if($this->metadataLevel == JsonLightMetadataLevel::FULL())
235
		{
236
			//Interestingly the fullmetadata outputs this metadata..even if the thing is expanded
237
			$this->_writer
238
				->writeName($link->title . ODataConstants::JSON_LIGHT_METADATA_LINK_NAVIGATION_SUFFIX_STRING)
239
				->writeValue($link->url);;
240
		}
241
242
243
		if($link->isExpanded)
244
		{
245
			$this->_writer->writeName($link->title);
246
247
			if(is_null($link->expandedResult)) {
248
				$this->_writer->writeValue("null");
249
			} else {
250
				$this->writeExpandedLink($link);
251
			}
252
		}
253
254
		return $this;
255
	}
256
257 View Code Duplication
	protected function writeExpandedLink(ODataLink $link)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
258
	{
259
260
261
		if ($link->isCollection) {
262
			$this->_writer->startArrayScope();
263
			$this->writeFeed($link->expandedResult);
0 ignored issues
show
Bug introduced by
It seems like $link->expandedResult can also be of type object<POData\ObjectModel\ODataEntry>; however, POData\Writers\Json\JsonODataV1Writer::writeFeed() does only seem to accept object<POData\ObjectModel\ODataFeed>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
264
		} else {
265
			$this->_writer->startObjectScope();
266
			$this->writeEntry($link->expandedResult);
0 ignored issues
show
Bug introduced by
It seems like $link->expandedResult can also be of type object<POData\ObjectModel\ODataFeed>; however, POData\Writers\Json\Json...aV1Writer::writeEntry() does only seem to accept object<POData\ObjectModel\ODataEntry>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
267
		}
268
269
270
		$this->_writer->endScope();
271
	}
272
273
	/**
274
	 * Writes the next page link.
275
	 *
276
	 * @param ODataLink $nextPageLinkUri Uri for next page link.
277
	 *
278
	 * @return JsonLightODataWriter
279
	 */
280
	protected function writeNextPageLink(ODataLink $nextPageLinkUri = null)
281
	{
282
		/*
0 ignored issues
show
Unused Code Comprehensibility introduced by
52% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
283
		// "__next" : uri
284
		if ($nextPageLinkUri != null) {
285
			$this->_writer
286
				->writeName(ODataConstants::JSON_NEXT_STRING)
287
				->writeValue($nextPageLinkUri->url);
288
		}
289
290
		return $this;
291
		*/
292
	}
293
294
295
	/**
296
	 * Begin write complex property.
297
	 *
298
	 * @param ODataProperty $property property to write.
299
	 *
300
	 * @return JsonLightODataWriter
301
	 */
302
	protected function writeComplexProperty(ODataProperty $property)
303
	{
304
305
		// {
306
		$this->_writer->startObjectScope();
307
308
		$this->writeComplexPropertyMeta($property)
309
			->writeProperties($property->value);
0 ignored issues
show
Bug introduced by
It seems like $property->value can also be of type object<POData\ObjectModel\ODataBagContent> or string; however, POData\Writers\Json\Json...iter::writeProperties() does only seem to accept object<POData\ObjectModel\ODataPropertyContent>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
310
311
		$this->_writer->endScope();
312
313
		return $this;
314
	}
315
316
	protected function writeComplexPropertyMeta(ODataProperty $property)
317
	{
318
		if($this->metadataLevel == JsonLightMetadataLevel::FULL()){
319
			$this->_writer
320
				->writeName(ODataConstants::JSON_LIGHT_METADATA_TYPE_STRING)
321
				->writeValue($property->typeName);
322
		}
323
324
		return $this;
325
	}
326
327
	protected function writeBagContent(ODataBagContent $bag)
328
	{
329
330
		$this->_writer
331
332
333
			/*
0 ignored issues
show
Unused Code Comprehensibility introduced by
52% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
334
			->writeName(ODataConstants::JSON_METADATA_STRING) //__metadata : { Type : "typename" }
335
			->startObjectScope()
336
337
			->writeName(ODataConstants::JSON_TYPE_STRING)
338
			->writeValue($bag->type)
339
			->endScope()  // }
340
			*/
341
342
343
			->startArrayScope(); // [
344
345 View Code Duplication
		foreach ($bag->propertyContents as $content) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
346
			if ($content instanceof ODataPropertyContent) {
347
				$this->_writer->startObjectScope();
348
				$this->writeProperties($content);
349
				$this->_writer->endScope();
350
			} else {
351
				// retrieving the collection datatype in order
352
				//to write in json specific format, with in chords or not
353
				preg_match('#\((.*?)\)#', $bag->type, $type);
354
				$this->_writer->writeValue($content, $type[1]);
355
			}
356
		}
357
358
359
		$this->_writer->endScope();  // ]
360
		return $this;
361
	}
362
363
	/**
364
	 * @param ProvidersWrapper $providers
365
	 * @return IODataWriter
366
	 */
367
	public function writeServiceDocument(ProvidersWrapper $providers){
368
		$writer = $this->_writer;
369
		$writer
370
			->startObjectScope() // {
371
				->writeName("odata.metadata")
372
				->writeValue("{$this->baseUri}/\$metadata")
373
				->writeName("value") // "value" :
374
				// ->writeName(ODataConstants::ENTITY_SET) // "EntitySets"
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
375
				->startArrayScope() // [
376
				;
377
378
		foreach ($providers->getResourceSets() as $resourceSetWrapper) {
379
			$name = $resourceSetWrapper->getName();
380
			$writer
381
					->startObjectScope() // {
382
						->writeName("name") // "name" :
383
						->writeValue($name)
384
						->writeName("url") // "name" :
385
						->writeValue($name)
386
					->endScope() // }
387
					;
388
		}
389
390
		$writer
391
				->endScope() // ]
392
			->endScope() // }
393
		;
394
395
		return $this;
396
397
	}
398
399
}
400