Completed
Pull Request — master (#7914)
by Blizzz
24:07 queued 05:40
created

Comment   B

Complexity

Total Complexity 54

Size/Duplication

Total Lines 388
Duplicated Lines 3.61 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 0
Metric Value
dl 14
loc 388
rs 7.0642
c 0
b 0
f 0
wmc 54
lcom 1
cbo 2

25 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 2
A getId() 0 3 1
B setId() 0 13 5
A getParentId() 0 3 1
A setParentId() 0 7 2
A getTopmostParentId() 0 3 1
A setTopmostParentId() 7 7 2
A getChildrenCount() 0 3 1
A setChildrenCount() 0 7 2
A getMessage() 0 3 1
A setMessage() 0 11 3
B getMentions() 0 12 5
A getVerb() 0 3 1
A setVerb() 7 7 3
A getActorType() 0 3 1
A getActorId() 0 3 1
B setActor() 0 11 5
A getCreationDateTime() 0 3 1
A setCreationDateTime() 0 4 1
A getLatestChildDateTime() 0 3 1
A setLatestChildDateTime() 0 4 1
A getObjectType() 0 3 1
A getObjectId() 0 3 1
B setObject() 0 11 5
B fromArray() 0 20 6

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Comment 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 Comment, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Arthur Schiwon <[email protected]>
6
 * @author Joas Schilling <[email protected]>
7
 * @author Roeland Jago Douma <[email protected]>
8
 * @author Thomas Müller <[email protected]>
9
 *
10
 * @license AGPL-3.0
11
 *
12
 * This code is free software: you can redistribute it and/or modify
13
 * it under the terms of the GNU Affero General Public License, version 3,
14
 * as published by the Free Software Foundation.
15
 *
16
 * This program is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
 * GNU Affero General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU Affero General Public License, version 3,
22
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
23
 *
24
 */
25
namespace OC\Comments;
26
27
use OCP\Comments\IComment;
28
use OCP\Comments\IllegalIDChangeException;
29
use OCP\Comments\MessageTooLongException;
30
31
class Comment implements IComment {
32
33
	protected $data = [
34
		'id'              => '',
35
		'parentId'        => '0',
36
		'topmostParentId' => '0',
37
		'childrenCount'   => '0',
38
		'message'         => '',
39
		'verb'            => '',
40
		'actorType'       => '',
41
		'actorId'         => '',
42
		'objectType'      => '',
43
		'objectId'        => '',
44
		'creationDT'      => null,
45
		'latestChildDT'   => null,
46
	];
47
48
	/**
49
	 * Comment constructor.
50
	 *
51
	 * @param array $data	optional, array with keys according to column names from
52
	 * 						the comments database scheme
53
	 */
54
	public function __construct(array $data = null) {
55
		if(is_array($data)) {
56
			$this->fromArray($data);
57
		}
58
	}
59
60
	/**
61
	 * returns the ID of the comment
62
	 *
63
	 * It may return an empty string, if the comment was not stored.
64
	 * It is expected that the concrete Comment implementation gives an ID
65
	 * by itself (e.g. after saving).
66
	 *
67
	 * @return string
68
	 * @since 9.0.0
69
	 */
70
	public function getId() {
71
		return $this->data['id'];
72
	}
73
74
	/**
75
	 * sets the ID of the comment and returns itself
76
	 *
77
	 * It is only allowed to set the ID only, if the current id is an empty
78
	 * string (which means it is not stored in a database, storage or whatever
79
	 * the concrete implementation does), or vice versa. Changing a given ID is
80
	 * not permitted and must result in an IllegalIDChangeException.
81
	 *
82
	 * @param string $id
83
	 * @return IComment
84
	 * @throws IllegalIDChangeException
85
	 * @since 9.0.0
86
	 */
87
	public function setId($id) {
88
		if(!is_string($id)) {
89
			throw new \InvalidArgumentException('String expected.');
90
		}
91
92
		$id = trim($id);
93
		if($this->data['id'] === '' || ($this->data['id'] !== '' && $id === '')) {
94
			$this->data['id'] = $id;
95
			return $this;
96
		}
97
98
		throw new IllegalIDChangeException('Not allowed to assign a new ID to an already saved comment.');
99
	}
100
101
	/**
102
	 * returns the parent ID of the comment
103
	 *
104
	 * @return string
105
	 * @since 9.0.0
106
	 */
107
	public function getParentId() {
108
		return $this->data['parentId'];
109
	}
110
111
	/**
112
	 * sets the parent ID and returns itself
113
	 *
114
	 * @param string $parentId
115
	 * @return IComment
116
	 * @since 9.0.0
117
	 */
118
	public function setParentId($parentId) {
119
		if(!is_string($parentId)) {
120
			throw new \InvalidArgumentException('String expected.');
121
		}
122
		$this->data['parentId'] = trim($parentId);
123
		return $this;
124
	}
125
126
	/**
127
	 * returns the topmost parent ID of the comment
128
	 *
129
	 * @return string
130
	 * @since 9.0.0
131
	 */
132
	public function getTopmostParentId() {
133
		return $this->data['topmostParentId'];
134
	}
135
136
137
	/**
138
	 * sets the topmost parent ID and returns itself
139
	 *
140
	 * @param string $id
141
	 * @return IComment
142
	 * @since 9.0.0
143
	 */
144 View Code Duplication
	public function setTopmostParentId($id) {
145
		if(!is_string($id)) {
146
			throw new \InvalidArgumentException('String expected.');
147
		}
148
		$this->data['topmostParentId'] = trim($id);
149
		return $this;
150
	}
151
152
	/**
153
	 * returns the number of children
154
	 *
155
	 * @return int
156
	 * @since 9.0.0
157
	 */
158
	public function getChildrenCount() {
159
		return $this->data['childrenCount'];
160
	}
161
162
	/**
163
	 * sets the number of children
164
	 *
165
	 * @param int $count
166
	 * @return IComment
167
	 * @since 9.0.0
168
	 */
169
	public function setChildrenCount($count) {
170
		if(!is_int($count)) {
171
			throw new \InvalidArgumentException('Integer expected.');
172
		}
173
		$this->data['childrenCount'] = $count;
174
		return $this;
175
	}
176
177
	/**
178
	 * returns the message of the comment
179
	 *
180
	 * @return string
181
	 * @since 9.0.0
182
	 */
183
	public function getMessage() {
184
		return $this->data['message'];
185
	}
186
187
	/**
188
	 * sets the message of the comment and returns itself
189
	 *
190
	 * @param string $message
191
	 * @return IComment
192
	 * @throws MessageTooLongException
193
	 * @since 9.0.0
194
	 */
195
	public function setMessage($message) {
196
		if(!is_string($message)) {
197
			throw new \InvalidArgumentException('String expected.');
198
		}
199
		$message = trim($message);
200
		if(mb_strlen($message, 'UTF-8') > IComment::MAX_MESSAGE_LENGTH) {
201
			throw new MessageTooLongException('Comment message must not exceed ' . IComment::MAX_MESSAGE_LENGTH . ' characters');
202
		}
203
		$this->data['message'] = $message;
204
		return $this;
205
	}
206
207
	/**
208
	 * returns an array containing mentions that are included in the comment
209
	 *
210
	 * @return array each mention provides a 'type' and an 'id', see example below
211
	 * @since 11.0.0
212
	 *
213
	 * The return array looks like:
214
	 * [
215
	 *   [
216
	 *     'type' => 'user',
217
	 *     'id' => 'citizen4'
218
	 *   ],
219
	 *   [
220
	 *     'type' => 'group',
221
	 *     'id' => 'media'
222
	 *   ],
223
	 *   …
224
	 * ]
225
	 *
226
	 */
227
	public function getMentions() {
228
		$ok = preg_match_all('/\B@[a-z0-9_\-@\.\']+/i', $this->getMessage(), $mentions);
229
		if(!$ok || !isset($mentions[0]) || !is_array($mentions[0])) {
230
			return [];
231
		}
232
		$uids = array_unique($mentions[0]);
233
		$result = [];
234
		foreach ($uids as $uid) {
235
			$result[] = ['type' => 'user', 'id' => substr($uid, 1)];
236
		}
237
		return $result;
238
	}
239
240
	/**
241
	 * returns the verb of the comment
242
	 *
243
	 * @return string
244
	 * @since 9.0.0
245
	 */
246
	public function getVerb() {
247
		return $this->data['verb'];
248
	}
249
250
	/**
251
	 * sets the verb of the comment, e.g. 'comment' or 'like'
252
	 *
253
	 * @param string $verb
254
	 * @return IComment
255
	 * @since 9.0.0
256
	 */
257 View Code Duplication
	public function setVerb($verb) {
258
		if(!is_string($verb) || !trim($verb)) {
259
			throw new \InvalidArgumentException('Non-empty String expected.');
260
		}
261
		$this->data['verb'] = trim($verb);
262
		return $this;
263
	}
264
265
	/**
266
	 * returns the actor type
267
	 *
268
	 * @return string
269
	 * @since 9.0.0
270
	 */
271
	public function getActorType() {
272
		return $this->data['actorType'];
273
	}
274
275
	/**
276
	 * returns the actor ID
277
	 *
278
	 * @return string
279
	 * @since 9.0.0
280
	 */
281
	public function getActorId() {
282
		return $this->data['actorId'];
283
	}
284
285
	/**
286
	 * sets (overwrites) the actor type and id
287
	 *
288
	 * @param string $actorType e.g. 'users'
289
	 * @param string $actorId e.g. 'zombie234'
290
	 * @return IComment
291
	 * @since 9.0.0
292
	 */
293
	public function setActor($actorType, $actorId) {
294
		if(
295
		       !is_string($actorType) || !trim($actorType)
296
		    || !is_string($actorId)   || !trim($actorId)
297
		) {
298
			throw new \InvalidArgumentException('String expected.');
299
		}
300
		$this->data['actorType'] = trim($actorType);
301
		$this->data['actorId']   = trim($actorId);
302
		return $this;
303
	}
304
305
	/**
306
	 * returns the creation date of the comment.
307
	 *
308
	 * If not explicitly set, it shall default to the time of initialization.
309
	 *
310
	 * @return \DateTime
311
	 * @since 9.0.0
312
	 */
313
	public function getCreationDateTime() {
314
		return $this->data['creationDT'];
315
	}
316
317
	/**
318
	 * sets the creation date of the comment and returns itself
319
	 *
320
	 * @param \DateTime $timestamp
321
	 * @return IComment
322
	 * @since 9.0.0
323
	 */
324
	public function setCreationDateTime(\DateTime $timestamp) {
325
		$this->data['creationDT'] = $timestamp;
326
		return $this;
327
	}
328
329
	/**
330
	 * returns the DateTime of the most recent child, if set, otherwise null
331
	 *
332
	 * @return \DateTime|null
333
	 * @since 9.0.0
334
	 */
335
	public function getLatestChildDateTime() {
336
		return $this->data['latestChildDT'];
337
	}
338
339
	/**
340
	 * sets the date of the most recent child
341
	 *
342
	 * @param \DateTime $dateTime
343
	 * @return IComment
344
	 * @since 9.0.0
345
	 */
346
	public function setLatestChildDateTime(\DateTime $dateTime = null) {
347
		$this->data['latestChildDT'] = $dateTime;
348
		return $this;
349
	}
350
351
	/**
352
	 * returns the object type the comment is attached to
353
	 *
354
	 * @return string
355
	 * @since 9.0.0
356
	 */
357
	public function getObjectType() {
358
		return $this->data['objectType'];
359
	}
360
361
	/**
362
	 * returns the object id the comment is attached to
363
	 *
364
	 * @return string
365
	 * @since 9.0.0
366
	 */
367
	public function getObjectId() {
368
		return $this->data['objectId'];
369
	}
370
371
	/**
372
	 * sets (overwrites) the object of the comment
373
	 *
374
	 * @param string $objectType e.g. 'files'
375
	 * @param string $objectId e.g. '16435'
376
	 * @return IComment
377
	 * @since 9.0.0
378
	 */
379
	public function setObject($objectType, $objectId) {
380
		if(
381
		       !is_string($objectType) || !trim($objectType)
382
		    || !is_string($objectId)   || !trim($objectId)
383
		) {
384
			throw new \InvalidArgumentException('String expected.');
385
		}
386
		$this->data['objectType'] = trim($objectType);
387
		$this->data['objectId']   = trim($objectId);
388
		return $this;
389
	}
390
391
	/**
392
	 * sets the comment data based on an array with keys as taken from the
393
	 * database.
394
	 *
395
	 * @param array $data
396
	 * @return IComment
397
	 */
398
	protected function fromArray($data) {
399
		foreach(array_keys($data) as $key) {
400
			// translate DB keys to internal setter names
401
			$setter = 'set' . implode('', array_map('ucfirst', explode('_', $key)));
402
			$setter = str_replace('Timestamp', 'DateTime', $setter);
403
404
			if(method_exists($this, $setter)) {
405
				$this->$setter($data[$key]);
406
			}
407
		}
408
409
		foreach(['actor', 'object'] as $role) {
410
			if(isset($data[$role . '_type']) && isset($data[$role . '_id'])) {
411
				$setter = 'set' . ucfirst($role);
412
				$this->$setter($data[$role . '_type'], $data[$role . '_id']);
413
			}
414
		}
415
416
		return $this;
417
	}
418
}
419