Completed
Push — master ( d197f6...1397b8 )
by Morris
32:43 queued 21:43
created

MDB2SchemaReader   C

Complexity

Total Complexity 78

Size/Duplication

Total Lines 312
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 312
rs 5.4563
c 1
b 0
f 0
wmc 78
lcom 1
cbo 1

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
C loadSchemaFromFile() 0 24 7
C loadTable() 0 29 8
A loadDeclaration() 0 18 4
F loadField() 0 112 40
C loadIndex() 0 58 15
A asBool() 0 9 3

How to fix   Complexity   

Complex Class

Complex classes like MDB2SchemaReader 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 MDB2SchemaReader, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Bart Visscher <[email protected]>
6
 * @author Joas Schilling <[email protected]>
7
 * @author Jörn Friedrich Dreyer <[email protected]>
8
 * @author Morris Jobke <[email protected]>
9
 * @author Oliver Gasser <[email protected]>
10
 * @author Robin Appelman <[email protected]>
11
 * @author Robin McCorkell <[email protected]>
12
 * @author Thomas Müller <[email protected]>
13
 * @author Victor Dubiniuk <[email protected]>
14
 * @author Vincent Petry <[email protected]>
15
 *
16
 * @license AGPL-3.0
17
 *
18
 * This code is free software: you can redistribute it and/or modify
19
 * it under the terms of the GNU Affero General Public License, version 3,
20
 * as published by the Free Software Foundation.
21
 *
22
 * This program is distributed in the hope that it will be useful,
23
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25
 * GNU Affero General Public License for more details.
26
 *
27
 * You should have received a copy of the GNU Affero General Public License, version 3,
28
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
29
 *
30
 */
31
32
namespace OC\DB;
33
34
use Doctrine\DBAL\Platforms\AbstractPlatform;
35
use Doctrine\DBAL\Schema\SchemaConfig;
36
use Doctrine\DBAL\Platforms\MySqlPlatform;
37
use Doctrine\DBAL\Schema\Schema;
38
use OCP\IConfig;
39
40
class MDB2SchemaReader {
41
42
	/**
43
	 * @var string $DBTABLEPREFIX
44
	 */
45
	protected $DBTABLEPREFIX;
46
47
	/**
48
	 * @var \Doctrine\DBAL\Platforms\AbstractPlatform $platform
49
	 */
50
	protected $platform;
51
52
	/** @var IConfig */
53
	protected $config;
54
55
	/**
56
	 * @param \OCP\IConfig $config
57
	 * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
58
	 */
59
	public function __construct(IConfig $config, AbstractPlatform $platform) {
60
		$this->platform = $platform;
61
		$this->config = $config;
62
		$this->DBTABLEPREFIX = $config->getSystemValue('dbtableprefix', 'oc_');
63
	}
64
65
	/**
66
	 * @param string $file
67
	 * @param Schema $schema
68
	 * @return Schema
69
	 * @throws \DomainException
70
	 */
71
	public function loadSchemaFromFile($file, Schema $schema) {
72
		$loadEntities = libxml_disable_entity_loader(false);
73
		$xml = simplexml_load_file($file);
74
		libxml_disable_entity_loader($loadEntities);
75
		foreach ($xml->children() as $child) {
76
			/**
77
			 * @var \SimpleXMLElement $child
78
			 */
79
			switch ($child->getName()) {
80
				case 'name':
81
				case 'create':
82
				case 'overwrite':
83
				case 'charset':
84
					break;
85
				case 'table':
86
					$this->loadTable($schema, $child);
87
					break;
88
				default:
89
					throw new \DomainException('Unknown element: ' . $child->getName());
90
91
			}
92
		}
93
		return $schema;
94
	}
95
96
	/**
97
	 * @param \Doctrine\DBAL\Schema\Schema $schema
98
	 * @param \SimpleXMLElement $xml
99
	 * @throws \DomainException
100
	 */
101
	private function loadTable($schema, $xml) {
102
		$table = null;
103
		foreach ($xml->children() as $child) {
104
			/**
105
			 * @var \SimpleXMLElement $child
106
			 */
107
			switch ($child->getName()) {
108
				case 'name':
109
					$name = (string)$child;
110
					$name = str_replace('*dbprefix*', $this->DBTABLEPREFIX, $name);
111
					$name = $this->platform->quoteIdentifier($name);
112
					$table = $schema->createTable($name);
113
					break;
114
				case 'create':
115
				case 'overwrite':
116
				case 'charset':
117
					break;
118
				case 'declaration':
119
					if (is_null($table)) {
120
						throw new \DomainException('Table declaration before table name');
121
					}
122
					$this->loadDeclaration($table, $child);
123
					break;
124
				default:
125
					throw new \DomainException('Unknown element: ' . $child->getName());
126
127
			}
128
		}
129
	}
130
131
	/**
132
	 * @param \Doctrine\DBAL\Schema\Table $table
133
	 * @param \SimpleXMLElement $xml
134
	 * @throws \DomainException
135
	 */
136
	private function loadDeclaration($table, $xml) {
137
		foreach ($xml->children() as $child) {
138
			/**
139
			 * @var \SimpleXMLElement $child
140
			 */
141
			switch ($child->getName()) {
142
				case 'field':
143
					$this->loadField($table, $child);
144
					break;
145
				case 'index':
146
					$this->loadIndex($table, $child);
147
					break;
148
				default:
149
					throw new \DomainException('Unknown element: ' . $child->getName());
150
151
			}
152
		}
153
	}
154
155
	/**
156
	 * @param \Doctrine\DBAL\Schema\Table $table
157
	 * @param \SimpleXMLElement $xml
158
	 * @throws \DomainException
159
	 */
160
	private function loadField($table, $xml) {
161
		$options = array( 'notnull' => false );
162
		foreach ($xml->children() as $child) {
163
			/**
164
			 * @var \SimpleXMLElement $child
165
			 */
166
			switch ($child->getName()) {
167
				case 'name':
168
					$name = (string)$child;
169
					$name = $this->platform->quoteIdentifier($name);
170
					break;
171
				case 'type':
172
					$type = (string)$child;
173
					switch ($type) {
174
						case 'text':
175
							$type = 'string';
176
							break;
177
						case 'clob':
178
							$type = 'text';
179
							break;
180
						case 'timestamp':
181
							$type = 'datetime';
182
							break;
183
						case 'numeric':
184
							$type = 'decimal';
185
							break;
186
					}
187
					break;
188
				case 'length':
189
					$length = (string)$child;
190
					$options['length'] = $length;
191
					break;
192
				case 'unsigned':
193
					$unsigned = $this->asBool($child);
194
					$options['unsigned'] = $unsigned;
195
					break;
196
				case 'notnull':
197
					$notnull = $this->asBool($child);
198
					$options['notnull'] = $notnull;
199
					break;
200
				case 'autoincrement':
201
					$autoincrement = $this->asBool($child);
202
					$options['autoincrement'] = $autoincrement;
203
					break;
204
				case 'default':
205
					$default = (string)$child;
206
					$options['default'] = $default;
207
					break;
208
				case 'comments':
209
					$comment = (string)$child;
210
					$options['comment'] = $comment;
211
					break;
212
				case 'primary':
213
					$primary = $this->asBool($child);
214
					$options['primary'] = $primary;
215
					break;
216
				case 'precision':
217
					$precision = (string)$child;
218
					$options['precision'] = $precision;
219
					break;
220
				case 'scale':
221
					$scale = (string)$child;
222
					$options['scale'] = $scale;
223
					break;
224
				default:
225
					throw new \DomainException('Unknown element: ' . $child->getName());
226
227
			}
228
		}
229
		if (isset($name) && isset($type)) {
230
			if (isset($options['default']) && empty($options['default'])) {
231
				if (empty($options['notnull']) || !$options['notnull']) {
232
					unset($options['default']);
233
					$options['notnull'] = false;
234
				} else {
235
					$options['default'] = '';
236
				}
237
				if ($type == 'integer' || $type == 'decimal') {
238
					$options['default'] = 0;
239
				} elseif ($type == 'boolean') {
240
					$options['default'] = false;
241
				}
242
				if (!empty($options['autoincrement']) && $options['autoincrement']) {
243
					unset($options['default']);
244
				}
245
			}
246
			if ($type === 'integer' && isset($options['default'])) {
247
				$options['default'] = (int)$options['default'];
248
			}
249
			if ($type === 'integer' && isset($options['length'])) {
250
				$length = $options['length'];
251
				if ($length < 4) {
252
					$type = 'smallint';
253
				} else if ($length > 4) {
254
					$type = 'bigint';
255
				}
256
			}
257
			if ($type === 'boolean' && isset($options['default'])) {
258
				$options['default'] = $this->asBool($options['default']);
259
			}
260
			if (!empty($options['autoincrement'])
261
				&& !empty($options['notnull'])
262
			) {
263
				$options['primary'] = true;
264
			}
265
266
			$table->addColumn($name, $type, $options);
267
			if (!empty($options['primary']) && $options['primary']) {
268
				$table->setPrimaryKey(array($name));
269
			}
270
		}
271
	}
272
273
	/**
274
	 * @param \Doctrine\DBAL\Schema\Table $table
275
	 * @param \SimpleXMLElement $xml
276
	 * @throws \DomainException
277
	 */
278
	private function loadIndex($table, $xml) {
279
		$name = null;
280
		$fields = array();
281
		foreach ($xml->children() as $child) {
282
			/**
283
			 * @var \SimpleXMLElement $child
284
			 */
285
			switch ($child->getName()) {
286
				case 'name':
287
					$name = (string)$child;
288
					break;
289
				case 'primary':
290
					$primary = $this->asBool($child);
291
					break;
292
				case 'unique':
293
					$unique = $this->asBool($child);
294
					break;
295
				case 'field':
296
					foreach ($child->children() as $field) {
297
						/**
298
						 * @var \SimpleXMLElement $field
299
						 */
300
						switch ($field->getName()) {
301
							case 'name':
302
								$field_name = (string)$field;
303
								$field_name = $this->platform->quoteIdentifier($field_name);
304
								$fields[] = $field_name;
305
								break;
306
							case 'sorting':
307
								break;
308
							default:
309
								throw new \DomainException('Unknown element: ' . $field->getName());
310
311
						}
312
					}
313
					break;
314
				default:
315
					throw new \DomainException('Unknown element: ' . $child->getName());
316
317
			}
318
		}
319
		if (!empty($fields)) {
320
			if (isset($primary) && $primary) {
321
				if ($table->hasPrimaryKey()) {
322
					return;
323
				}
324
				$table->setPrimaryKey($fields, $name);
325
			} else {
326
				if (isset($unique) && $unique) {
327
					$table->addUniqueIndex($fields, $name);
328
				} else {
329
					$table->addIndex($fields, $name);
330
				}
331
			}
332
		} else {
333
			throw new \DomainException('Empty index definition: ' . $name . ' options:' . print_r($fields, true));
334
		}
335
	}
336
337
	/**
338
	 * @param \SimpleXMLElement|string $xml
339
	 * @return bool
340
	 */
341
	private function asBool($xml) {
342
		$result = (string)$xml;
343
		if ($result == 'true') {
344
			$result = true;
345
		} elseif ($result == 'false') {
346
			$result = false;
347
		}
348
		return (bool)$result;
349
	}
350
351
}
352