Completed
Push — master ( 394a71...6f1015 )
by Karsten
18:06
created

GraphPrinter::getWordWrappedText()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 26
rs 9.504
c 0
b 0
f 0
cc 4
nc 4
nop 2
1
<?php
2
3
namespace SRF\Graph;
4
5
use SMWResultPrinter;
6
use SMWQueryResult;
7
use SMWDataValue;
8
use SMWWikiPageValue;
9
10
/**
11
 * SMW result printer for graphs using graphViz.
12
 * In order to use this printer you need to have both
13
 * the graphViz library installed on your system and
14
 * have the graphViz MediaWiki extension installed.
15
 *
16
 * @file SRF_Graph.php
17
 * @ingroup SemanticResultFormats
18
 *
19
 * @licence GNU GPL v2+
20
 * @author Frank Dengler
21
 * @author Jeroen De Dauw < [email protected] >
22
 */
23
class GraphPrinter extends SMWResultPrinter {
24
25
	const NODELABEL_DISPLAYTITLE = 'displaytitle';
26
27
	public static $NODE_LABELS = [
28
		self::NODELABEL_DISPLAYTITLE,
29
	];
30
31
	public static $NODE_SHAPES = [
32
		'box',
33
		'box3d',
34
		'circle',
35
		'component',
36
		'diamond',
37
		'doublecircle',
38
		'doubleoctagon',
39
		'egg',
40
		'ellipse',
41
		'folder',
42
		'hexagon',
43
		'house',
44
		'invhouse',
45
		'invtrapezium',
46
		'invtriangle',
47
		'Mcircle',
48
		'Mdiamond',
49
		'Msquare',
50
		'none',
51
		'note',
52
		'octagon',
53
		'parallelogram',
54
		'pentagon ',
55
		'plaintext',
56
		'point',
57
		'polygon',
58
		'rect',
59
		'rectangle',
60
		'septagon',
61
		'square',
62
		'tab',
63
		'trapezium',
64
		'triangle',
65
		'tripleoctagon',
66
	];
67
68
	protected $m_graphName;
69
	protected $m_graphLabel;
70
	protected $m_graphColor;
71
	protected $m_graphLegend;
72
	protected $m_graphLink;
73
	protected $m_rankdir;
74
	protected $m_graphSize;
75
	protected $m_labelArray = [];
76
	protected $m_graphColors = [
77
		'black',
78
		'red',
79
		'green',
80
		'blue',
81
		'darkviolet',
82
		'gold',
83
		'deeppink',
84
		'brown',
85
		'bisque',
86
		'darkgreen',
87
		'yellow',
88
		'darkblue',
89
		'magenta',
90
		'steelblue2' ];
91
	protected $m_nameProperty;
92
	protected $m_nodeShape;
93
	protected $m_parentRelation;
94
	protected $m_wordWrapLimit;
95
	protected $m_nodeLabel;
96
97
	/**
98
	 * (non-PHPdoc)
99
	 * @see SMWResultPrinter::handleParameters()
100
	 */
101
	protected function handleParameters( array $params, $outputmode ) {
102
		parent::handleParameters( $params, $outputmode );
103
104
		$this->m_graphName = trim( $params['graphname'] );
105
		$this->m_graphSize = trim( $params['graphsize'] );
106
107
		$this->m_graphLegend = $params['graphlegend'];
108
		$this->m_graphLabel = $params['graphlabel'];
109
110
		$this->m_rankdir = strtoupper( trim( $params['arrowdirection'] ) );
111
112
		$this->m_graphLink = $params['graphlink'];
113
		$this->m_graphColor = $params['graphcolor'];
114
115
		$this->m_nameProperty = $params['nameproperty'] === false ? false : trim( $params['nameproperty'] );
116
117
		$this->m_parentRelation = strtolower( trim( $params['relation'] ) ) == 'parent';
118
119
		$this->m_nodeShape = $params['nodeshape'];
120
		$this->m_wordWrapLimit = $params['wordwraplimit'];
121
122
		$this->m_nodeLabel = $params['nodelabel'];
123
	}
124
125
	protected function getResultText( SMWQueryResult $res, $outputmode ) {
126
127
		if ( !class_exists( 'GraphViz' )
128
			&& !class_exists( '\\MediaWiki\\Extension\\GraphViz\\GraphViz' )
129
		) {
130
			wfWarn( 'The SRF Graph printer needs the GraphViz extension to be installed.' );
131
			return '';
132
		}
133
134
		$this->isHTML = true;
135
136
		$graphInput = "digraph $this->m_graphName {";
137
		if ( $this->m_graphSize != '' ) {
138
			$graphInput .= "size=\"$this->m_graphSize\";";
139
		}
140
		if ( $this->m_nodeShape ) {
141
			$graphInput .= "node [shape=$this->m_nodeShape];";
142
		}
143
		$graphInput .= "rankdir=$this->m_rankdir;";
144
145
		while ( $row = $res->getNext() ) {
146
			$graphInput .= $this->getGVForItem( $row, $outputmode );
147
		}
148
149
		$graphInput .= "}";
150
151
		// Calls graphvizParserHook function from MediaWiki GraphViz extension
152
		$result = $GLOBALS['wgParser']->recursiveTagParse( "<graphviz>$graphInput</graphviz>" );
153
154
		if ( $this->m_graphLegend && $this->m_graphColor ) {
155
			$arrayCount = 0;
156
			$arraySize = count( $this->m_graphColors );
157
			$result .= "<P>";
158
159
			foreach ( $this->m_labelArray as $m_label ) {
160
				if ( $arrayCount >= $arraySize ) {
161
					$arrayCount = 0;
162
				}
163
164
				$color = $this->m_graphColors[$arrayCount];
165
				$result .= "<font color=$color>$color: $m_label </font><br />";
166
167
				$arrayCount += 1;
168
			}
169
170
			$result .= "</P>";
171
		}
172
173
		return $result;
174
	}
175
176
	/**
177
	 * Returns the GV for a single subject.
178
	 *
179
	 * @since 1.5.4
180
	 *
181
	 * @param array $row
182
	 * @param $outputmode
183
	 *
184
	 * @return string
185
	 */
186
	protected function getGVForItem( array /* of SMWResultArray */
187
	$row, $outputmode ) {
188
		$segments = [];
189
190
		// Loop throught all fields of the record.
191
		foreach ( $row as $i => $resultArray ) {
192
193
			// Loop throught all the parts of the field value.
194
			while ( ( $object = $resultArray->getNextDataValue() ) !== false ) {
195
				$propName = $resultArray->getPrintRequest()->getLabel();
196
				$isName = $this->m_nameProperty ? ( $i != 0 && $this->m_nameProperty === $propName ) : $i == 0;
197
198
				if ( $isName ) {
199
					$name = $this->getWordWrappedText( $object->getShortText( $outputmode ), $this->m_wordWrapLimit );
200
				}
201
202
				if ( !( $this->m_nameProperty && $i == 0 ) ) {
203
					$segments[] = $this->getGVForDataValue( $object, $outputmode, $isName, $name, $propName );
0 ignored issues
show
Bug introduced by
The variable $name does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
204
				}
205
			}
206
		}
207
208
		return implode( "\n", $segments );
209
	}
210
211
	/**
212
	 * Returns the GV for a single SMWDataValue.
213
	 *
214
	 * @since 1.5.4
215
	 *
216
	 * @param SMWDataValue $object
217
	 * @param $outputmode
218
	 * @param boolean $isName Is this the name that should be used for the node?
219
	 * @param string $name
220
	 * @param string $labelName
221
	 *
222
	 * @return string
223
	 */
224
	protected function getGVForDataValue( SMWDataValue $object, $outputmode, $isName, $name, $labelName ) {
225
		$graphInput = '';
226
		$nodeLabel = '';
227
		$text = $object->getShortText( $outputmode );
228
229
		if ( $this->m_graphLink ) {
230
			$nodeLinkURL = "[[" . $text . "]]";
231
		}
232
233
		$text = $this->getWordWrappedText( $text, $this->m_wordWrapLimit );
234
235
		if ( $this->m_nodeLabel === self::NODELABEL_DISPLAYTITLE && $object instanceof SMWWikiPageValue ) {
236
			$objectDisplayTitle = $object->getDisplayTitle();
237
			if ( !empty( $objectDisplayTitle )) {
238
				$nodeLabel = $this->getWordWrappedText( $objectDisplayTitle, $this->m_wordWrapLimit );
239
			}
240
		}
241
242
		if ( $this->m_graphLink ) {
243
			if( $nodeLabel === '' ) {
244
				$graphInput .= " \"$text\" [URL = \"$nodeLinkURL\"]; ";
0 ignored issues
show
Bug introduced by
The variable $nodeLinkURL does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
245
			} else {
246
				$graphInput .= " \"$text\" [URL = \"$nodeLinkURL\", label = \"$nodeLabel\"]; ";
247
			}
248
		}
249
250
		if ( !$isName ) {
251
			$graphInput .= $this->m_parentRelation ? " \"$text\" -> \"$name\" " : " \"$name\" -> \"$text\" ";
252
253
			if ( $this->m_graphLabel || $this->m_graphColor ) {
254
				$graphInput .= ' [';
255
256
				if ( array_search( $labelName, $this->m_labelArray, true ) === false ) {
257
					$this->m_labelArray[] = $labelName;
258
				}
259
260
				$color = $this->m_graphColors[array_search( $labelName, $this->m_labelArray, true )];
261
262
				if ( $this->m_graphLabel ) {
263
					$graphInput .= "label=\"$labelName\"";
264
					if ( $this->m_graphColor ) {
265
						$graphInput .= ",fontcolor=$color,";
266
					}
267
				}
268
269
				if ( $this->m_graphColor ) {
270
					$graphInput .= "color=$color";
271
				}
272
273
				$graphInput .= ']';
274
275
			}
276
277
			$graphInput .= ';';
278
		}
279
280
		return $graphInput;
281
	}
282
283
	/**
284
	 * Returns the word wrapped version of the provided text.
285
	 *
286
	 * @since 1.5.4
287
	 *
288
	 * @param string $text
289
	 * @param integer $charLimit
290
	 *
291
	 * @return string
292
	 */
293
	protected function getWordWrappedText( $text, $charLimit ) {
294
		$charLimit = max( [ $charLimit, 1 ] );
295
		$segments = [];
296
297
		while ( strlen( $text ) > $charLimit ) {
298
			// Find the last space in the allowed range.
299
			$splitPosition = strrpos( substr( $text, 0, $charLimit ), ' ' );
300
301
			if ( $splitPosition === false ) {
302
				// If there is no space (lond word), find the next space.
303
				$splitPosition = strpos( $text, ' ' );
304
305
				if ( $splitPosition === false ) {
306
					// If there are no spaces, everything goes on one line.
307
					$splitPosition = strlen( $text ) - 1;
308
				}
309
			}
310
311
			$segments[] = substr( $text, 0, $splitPosition + 1 );
312
			$text = substr( $text, $splitPosition + 1 );
313
		}
314
315
		$segments[] = $text;
316
317
		return implode( '\n', $segments );
318
	}
319
320
	/**
321
	 * (non-PHPdoc)
322
	 * @see SMWResultPrinter::getName()
323
	 */
324
	public function getName() {
325
		return wfMessage( 'srf-printername-graph' )->text();
326
	}
327
328
	/**
329
	 * @see SMWResultPrinter::getParamDefinitions
330
	 *
331
	 * @since 1.8
332
	 *
333
	 * @param $definitions array of IParamDefinition
334
	 *
335
	 * @return array of IParamDefinition|array
336
	 */
337
	public function getParamDefinitions( array $definitions ) {
338
		$params = parent::getParamDefinitions( $definitions );
339
340
		$params['graphname'] = [
341
			'default' => 'QueryResult',
342
			'message' => 'srf-paramdesc-graphname',
343
		];
344
345
		$params['graphsize'] = [
346
			'type' => 'string',
347
			'default' => '',
348
			'message' => 'srf-paramdesc-graphsize',
349
			'manipulatedefault' => false,
350
		];
351
352
		$params['graphlegend'] = [
353
			'type' => 'boolean',
354
			'default' => false,
355
			'message' => 'srf-paramdesc-graphlegend',
356
		];
357
358
		$params['graphlabel'] = [
359
			'type' => 'boolean',
360
			'default' => false,
361
			'message' => 'srf-paramdesc-graphlabel',
362
		];
363
364
		$params['graphlink'] = [
365
			'type' => 'boolean',
366
			'default' => false,
367
			'message' => 'srf-paramdesc-graphlink',
368
		];
369
370
		$params['graphcolor'] = [
371
			'type' => 'boolean',
372
			'default' => false,
373
			'message' => 'srf-paramdesc-graphcolor',
374
		];
375
376
		$params['arrowdirection'] = [
377
			'aliases' => 'rankdir',
378
			'default' => 'LR',
379
			'message' => 'srf-paramdesc-rankdir',
380
			'values' => [ 'LR', 'RL', 'TB', 'BT' ],
381
		];
382
383
		$params['nodeshape'] = [
384
			'default' => false,
385
			'message' => 'srf-paramdesc-graph-nodeshape',
386
			'manipulatedefault' => false,
387
			'values' => self::$NODE_SHAPES,
388
		];
389
390
		$params['relation'] = [
391
			'default' => 'child',
392
			'message' => 'srf-paramdesc-graph-relation',
393
			'manipulatedefault' => false,
394
			'values' => [ 'parent', 'child' ],
395
		];
396
397
		$params['nameproperty'] = [
398
			'default' => false,
399
			'message' => 'srf-paramdesc-graph-nameprop',
400
			'manipulatedefault' => false,
401
		];
402
403
		$params['wordwraplimit'] = [
404
			'type' => 'integer',
405
			'default' => 25,
406
			'message' => 'srf-paramdesc-graph-wwl',
407
			'manipulatedefault' => false,
408
		];
409
410
		$params['nodelabel'] = [
411
			'default' => [],
412
			'message' => 'srf-paramdesc-nodelabel',
413
			'values' => self::$NODE_LABELS,
414
		];
415
416
		return $params;
417
	}
418
419
}
420