Completed
Pull Request — master (#297)
by
unknown
05:12
created

SRFGraphNode::addLabel3()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 3
ccs 0
cts 3
cp 0
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 2
1
<?php
2
3
/**
4
 * SMW result printer for graphs using graphViz.
5
 * In order to use this printer you need to have both
6
 * the graphViz library installed on your system and
7
 * have the graphViz MediaWiki extension installed.
8
 *
9
 * @file SRF_Graph.php
10
 * @ingroup SemanticResultFormats
11
 *
12
 * @licence GNU GPL v2+
13
 * @author Frank Dengler
14
 * @author Jeroen De Dauw < [email protected] >
15
 * @author Sebastian Schmid
16
 */
17
class SRFGraph extends SMWResultPrinter {
18
19
	public static $ARROW_HEAD = array(
20
		'none',
21
		'normal',
22
		'inv',
23
		'dot',
24
		'odot',
25
		'tee',
26
		'invdot',
27
		'invodot',
28
		'empty',
29
		'invempty',
30
		'diamond',
31
		'ediamond',
32
		'odiamond',
33
		'crow',
34
		'obox',
35
		'box',
36
		'open',
37
		'vee',
38
		'circle',
39
		'halfopen'
40
	);
41
42
	public static $NODE_SHAPES = array(
43
		'box',
44
		'box3d',
45
		'circle',
46
		'component',
47
		'diamond',
48
		'doublecircle',
49
		'doubleoctagon',
50
		'egg',
51
		'ellipse',
52
		'folder',
53
		'hexagon',
54
		'house',
55
		'invhouse',
56
		'invtrapezium',
57
		'invtriangle',
58
		'Mcircle',
59
		'Mdiamond',
60
		'Msquare',
61
		'none',
62
		'note',
63
		'octagon',
64
		'parallelogram',
65
		'pentagon ',
66
		'plaintext',
67
		'point',
68
		'polygon',
69
		'rect',
70
		'rectangle',
71
		'septagon',
72
		'square',
73
		'tab',
74
		'trapezium',
75
		'triangle',
76
		'tripleoctagon',
77
		'record',
78
		'Mrecord'
79
	);
80
81
	protected $m_graphName;
82
	protected $m_graphLabel;
83
	protected $m_graphColor;
84
	protected $m_graphLegend;
85
	protected $m_graphLink;
86
	protected $m_rankdir;
87
	protected $m_graphSize;
88
	protected $m_labelArray = array();
89
	protected $m_graphColors = array(
90
		'black',
91
		'red',
92
		'green',
93
		'blue',
94
		'darkviolet',
95
		'gold',
96
		'deeppink',
97
		'brown',
98
		'bisque',
99
		'darkgreen',
100
		'yellow',
101
		'darkblue',
102
		'magenta',
103
		'steelblue2'
104
	);
105
	protected $m_nameProperty;
106
	protected $m_nodeShape;
107
	protected $m_parentRelation;
108
	protected $m_wordWrapLimit;
109
	protected $m_arrowHead;
110
111
	protected $m_nodes = array();
112
113
	/**
114
	 * (non-PHPdoc)
115
	 * @see SMWResultPrinter::handleParameters()
116
	 */
117
	protected function handleParameters( array $params, $outputmode ) {
118
		parent::handleParameters( $params, $outputmode );
119
120
		$this->m_graphName = trim( $params['graphname'] );
121
		$this->m_graphSize = trim( $params['graphsize'] );
122
		$this->m_graphLegend = $params['graphlegend'];
123
		$this->m_graphLabel = $params['graphlabel'];
124
		$this->m_rankdir = strtoupper( trim( $params['arrowdirection'] ) );
125
		$this->m_graphLink = $params['graphlink'];
126
		$this->m_graphColor = $params['graphcolor'];
127
		$this->m_arrowHead = $params['arrowhead'];
128
		$this->m_nameProperty = $params['nameproperty'] === false ? false : trim( $params['nameproperty'] );
129
		$this->m_parentRelation = strtolower( trim( $params['relation'] ) ) == 'parent';
130
		$this->m_nodeShape = $params['nodeshape'];
131
		$this->m_wordWrapLimit = $params['wordwraplimit'];
132
	}
133
134
	protected function getResultText( SMWQueryResult $res, $outputmode ) {
0 ignored issues
show
Coding Style introduced by
getResultText uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
135
		if ( !is_callable( 'GraphViz::graphvizParserHook' ) ) {
136
			wfWarn( 'The SRF Graph printer needs the GraphViz extension to be installed.' );
137
138
			return '';
139
		}
140
141
		$this->isHTML = true;
142
143
        ///////////////////////////////////
144
        // GRAPH OPTIONS
145
		///////////////////////////////////        
146
        
147
		$graphInput = "digraph $this->m_graphName {";
148
        
149
        // fontsize and fontname
150
        $graphInput .= "graph [fontsize=10, fontname=\"Verdana\"]\nnode [fontsize=10, fontname=\"Verdana\"];\nedge [fontsize=10, fontname=\"Verdana\"];";
151
        
152
        // size
153
		if ( $this->m_graphSize != '' ) {
154
			$graphInput .= "size=\"$this->m_graphSize\";";
155
		}
156
		
157
        // shape
158
        if ( $this->m_nodeShape ) {
159
			$graphInput .= "node [shape=$this->m_nodeShape];";
160
		}
161
		
162
        // rankdir
163
        $graphInput .= "rankdir=$this->m_rankdir;";
164
165
        // iterate query result
166
		while ( $row = $res->getNext() ) {
167
			$this->processResultRow( $row, $outputmode, $this->m_nodes );
168
		}
169
170
		///////////////////////////////////
171
        // NODES  
172
		///////////////////////////////////
173
        
174
		foreach ( $this->m_nodes as $node ) {
175
176
			// take node ID (title) if we don't have a label1
177
			$nodeName = ( empty( $node->getLabel1() ) ) ? $node->getID() : $node->getLabel1();
178
            
179
            // add the node
180
			$graphInput .= "\"" . $nodeName . "\"";
181
182
			if ( $this->m_graphLink ) {
183
				$nodeLinkURL = "[[" . $node->getID() . "]]";
184
				$graphInput .= "[URL = \"$nodeLinkURL\"] ";
185
			}
186
            
187
            // build the additional labels only for record or Mrecord
188
			if ( ( $node->getLabel2() != "" || $node->getLabel3() != "" ) &&
189
			     ( $this->m_nodeShape == "record" || $this->m_nodeShape == "Mrecord" )
190
			) {
191
192
				$label = ( empty( $node->getLabel1() ) ) ? $node->getID() : $node->getLabel1();
193
				$graphInput .= "[label=\"{" . $label;
194
195
				if ( $node->getLabel2() != "" ) {
196
					$graphInput .= "|" . $node->getLabel2();
197
				}
198
199
				if ( $node->getLabel3() != "" ) {
200
					$graphInput .= "|" . $node->getLabel3();
201
				}
202
203
				$graphInput .= " }\"];";
204
			} else {
205
				$graphInput .= ";";
206
			}
207
		}
208
209
		///////////////////////////////////
210
		// EDGES
211
		///////////////////////////////////
212
        
213
		foreach ( $this->m_nodes as $node ) {
214
215
			if ( count( $node->getParentNode() ) > 0 ) {
216
217
				$nodeName = ( empty( $node->getLabel1() ) ) ? $node->getID() : $node->getLabel1();
218
219
				//was handled with param "relation" type string, child/parent
220
				$i = 0;
221
				foreach ( $node->getParentNode() as $parentNode ) {
222
223
					$graphInput .= $this->m_parentRelation ? " \"" . $parentNode['object'] . "\" -> \"" . $nodeName .
224
					                                         "\""
225
						: " \"" . $nodeName . "\" -> \"" . $parentNode['object'] . "\" ";
226
227
					// Add ArrowHead for every Arrow of Node
228
					$graphInput .= "[arrowhead = " . $this->m_arrowHead . "]";
229
230
					if ( $this->m_graphLabel || $this->m_graphColor ) {
231
						$graphInput .= ' [';
232
233
						if ( array_search( $parentNode['predicate'], $this->m_labelArray, true ) === false ) {
234
							$this->m_labelArray[] = $parentNode['predicate'];
235
						}
236
237
						$color =
238
							$this->m_graphColors[array_search( $parentNode['predicate'], $this->m_labelArray, true )];
239
240
						if ( $this->m_graphLabel ) {
241
							$graphInput .= "label=\"" . $parentNode['predicate'] . "\"";
242
							if ( $this->m_graphColor ) {
243
								$graphInput .= ",fontcolor=$color,";
244
							}
245
						}
246
247
						if ( $this->m_graphColor ) {
248
							$graphInput .= "color=$color";
249
						}
250
						$graphInput .= ']';
251
					}
252
					$i ++;
253
				}
254
				$graphInput .= ';';
255
			}
256
		}
257
		$graphInput .= "}";
258
259
        
260
		// calls graphvizParserHook from GraphViz extension
261
		$result = GraphViz::graphvizParserHook( $graphInput, "", $GLOBALS['wgParser'], true );
262
263
        
264
        // append legend
265
		if ( $this->m_graphLegend && $this->m_graphColor ) {
266
			$arrayCount = 0;
267
			$arraySize = count( $this->m_graphColors );
268
			$result .= "<P>";
269
270
			foreach ( $this->m_labelArray as $m_label ) {
271
				if ( $arrayCount >= $arraySize ) {
272
					$arrayCount = 0;
273
				}
274
275
				$color = $this->m_graphColors[$arrayCount];
276
				$result .= "<font color=$color>$color: $m_label </font><br />";
277
278
				$arrayCount += 1;
279
			}
280
281
			$result .= "</P>";
282
		}
283
284
		return $result;
285
	}
286
287
	/**
288
	 * Process a result row and create SRFGraphNodes
289
	 *
290
	 * @since 2.5.0
291
	 *
292
	 * @param array $row
293
	 * @param $outputmode
294
	 * @param array $nodes
295
	 *
296
	 */
297
	protected function processResultRow( array /* of SMWResultArray */
298
	$row, $outputmode, $nodes ) {
299
300
		// loop through all row fields
301
		foreach ( $row as $i => $resultArray ) {
302
303
			// loop through all values of a multivalue field
304
			while ( ( /* SMWDataValue */
305
			        $object = $resultArray->getNextDataValue() ) !== false ) {
306
307
                // create SRFGraphNode for column 0
308
				if ( $i == 0 ) {
309
					$node = new SRFGraphNode( str_replace( '_', ' ', $object->getShortText( $outputmode ) ) );
310
311
					if ( !in_array( $node, $nodes, true ) ) {
312
						$this->m_nodes[] = $node;
313
					}
314
				} else {
315
316
                    // special handling for labels, all other printout statements will add links to parent nodes
317
					switch ( $resultArray->getPrintRequest()->getLabel() ) {
318
						case 'label1':
319
							if ( $object instanceof SMWWikiPageValue ) {
320
								$node->addLabel1( $object->getDisplayTitle() );
0 ignored issues
show
Bug introduced by
The variable $node 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...
321
							} else {
322
								$node->addLabel1( $object->getShortText( $outputmode ) );
323
							}
324
							break;
325
						case 'label2':
326
							if ( $object instanceof SMWWikiPageValue ) {
327
								$node->addLabel2( $object->getDisplayTitle() );
328
							} else {
329
								$node->addLabel2( $object->getShortText( $outputmode ) );
330
							}
331
							break;
332
						case 'label3':
333
							if ( $object instanceof SMWWikiPageValue ) {
334
								$node->addLabel3( $object->getDisplayTitle() );
335
							} else {
336
								$node->addLabel3( $object->getShortText( $outputmode ) );
337
							}
338
							break;
339
						default:
340
							// add Object (Parent Node) and Predicate (Graph Label) to current node
341
                            // <this node> <is part of> <other node>
342
							$node->addParentNode( $resultArray->getPrintRequest()->getLabel(),
343
								str_replace( '_', ' ', $object->getDBkey() ) );
344
							break;
345
					}
346
				}
347
			}
348
		}
349
350
		return true;
351
	}
352
353
	/**
354
	 * Returns the word wrapped version of the provided text.
355
	 *
356
	 * @since 1.5.4
357
	 *
358
	 * @param string $text
359
	 * @param integer $charLimit
360
	 *
361
	 * @return string
362
	 */
363
	protected function getWordWrappedText( $text, $charLimit ) {
364
		$charLimit = max( array( $charLimit, 1 ) );
365
		$segments = array();
366
367
		while ( strlen( $text ) > $charLimit ) {
368
			// Find the last space in the allowed range.
369
			$splitPosition = strrpos( substr( $text, 0, $charLimit ), ' ' );
370
371
			if ( $splitPosition === false ) {
372
				// If there is no space (lond word), find the next space.
373
				$splitPosition = strpos( $text, ' ' );
374
375
				if ( $splitPosition === false ) {
376
					// If there are no spaces, everything goes on one line.
377
					$splitPosition = strlen( $text ) - 1;
378
				}
379
			}
380
381
			$segments[] = substr( $text, 0, $splitPosition + 1 );
382
			$text = substr( $text, $splitPosition + 1 );
383
		}
384
385
		$segments[] = $text;
386
387
		return implode( '\n', $segments );
388
	}
389
390
	/**
391
	 * (non-PHPdoc)
392
	 * @see SMWResultPrinter::getName()
393
	 */
394
	public function getName() {
395
		return wfMessage( 'srf-printername-graph' )->text();
396
	}
397
398
	/**
399
	 * @see SMWResultPrinter::getParamDefinitions
400
	 *
401
	 * @since 1.8
402
	 *
403
	 * @param $definitions array of IParamDefinition
404
	 *
405
	 * @return array of IParamDefinition|array
406
	 */
407
	public function getParamDefinitions( array $definitions ) {
408
		$params = parent::getParamDefinitions( $definitions );
409
410
		$params['graphname'] = array(
411
			'default' => 'QueryResult',
412
			'message' => 'srf_paramdesc_graphname',
413
		);
414
415
		$params['graphsize'] = array(
416
			'type'              => 'string',
417
			'default'           => '',
418
			'message'           => 'srf_paramdesc_graphsize',
419
			'manipulatedefault' => false,
420
		);
421
422
		$params['graphlegend'] = array(
423
			'type'    => 'boolean',
424
			'default' => false,
425
			'message' => 'srf_paramdesc_graphlegend',
426
		);
427
428
		$params['graphlabel'] = array(
429
			'type'    => 'boolean',
430
			'default' => false,
431
			'message' => 'srf_paramdesc_graphlabel',
432
		);
433
434
		$params['graphlink'] = array(
435
			'type'    => 'boolean',
436
			'default' => false,
437
			'message' => 'srf_paramdesc_graphlink',
438
		);
439
440
		$params['graphcolor'] = array(
441
			'type'    => 'boolean',
442
			'default' => false,
443
			'message' => 'srf_paramdesc_graphcolor',
444
		);
445
446
		$params['arrowdirection'] = array(
447
			'aliases' => 'rankdir',
448
			'default' => 'LR',
449
			'message' => 'srf_paramdesc_rankdir',
450
			'values'  => array( 'LR', 'RL', 'TB', 'BT' ),
451
		);
452
453
		$params['nodeshape'] = array(
454
			'default'           => false,
455
			'message'           => 'srf-paramdesc-graph-nodeshape',
456
			'manipulatedefault' => false,
457
			'values'            => self::$NODE_SHAPES,
458
		);
459
460
		$params['relation'] = array(
461
			'default'           => 'child',
462
			'message'           => 'srf-paramdesc-graph-relation',
463
			'manipulatedefault' => false,
464
			'values'            => array( 'parent', 'child' ),
465
		);
466
467
		$params['nameproperty'] = array(
468
			'default'           => false,
469
			'message'           => 'srf-paramdesc-graph-nameprop',
470
			'manipulatedefault' => false,
471
		);
472
473
		$params['wordwraplimit'] = array(
474
			'type'              => 'integer',
475
			'default'           => 25,
476
			'message'           => 'srf-paramdesc-graph-wwl',
477
			'manipulatedefault' => false,
478
		);
479
480
		$params['arrowhead'] = array(
481
			'type'              => 'string',
482
			'default'           => 'normal',
483
			'message'           => 'srf-paramdesc-graph-arrowhead',
484
			'manipulatedefault' => false
485
		);
486
487
		return $params;
488
	}
489
}
490
491
/*
492
 * Represents a graph node 
493
 *
494
 * @author Sebastian Schmid
495
 */
496
497
class SRFGraphNode {
498
	private $m_id;
499
	private $m_label1;
500
	private $m_label2;
501
	private $m_label3;
502
	private $m_parent = array();
503
504
	/**
505
	 * @var string $id : Node ID including namespace
506
	 */      
507
	function __construct( $id ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Comprehensibility Best Practice introduced by
It is recommend to declare an explicit visibility for __construct.

Generally, we recommend to declare visibility for all methods in your source code. This has the advantage of clearly communication to other developers, and also yourself, how this method should be consumed.

If you are not sure which visibility to choose, it is a good idea to start with the most restrictive visibility, and then raise visibility as needed, i.e. start with private, and only raise it to protected if a sub-class needs to have access, or public if an external class needs access.

Loading history...
508
		$this->m_id = $id;
509
	}
510
    
511
	/**
512
	 * @var string $label : A label, e.g. Display Title, used instead of $m_id
513
	 */
514
	public function addLabel1( $label ) {
515
		$this->m_label1 = $label;
516
	}
517
518
    /**
519
	 * @var string $label : append to label2 plus an '/l' for left align
520
	 *                     the label2 is displayed in the second row of a record shape
521
	 */
522
	public function addLabel2( $label ) {
523
		$this->m_label2 .= $label . "\l";
524
	}
525
526
    /**
527
	 * @var string $label : append to label3 plus an '/l' for left align
528
	 *                     the label3 is displayed in the third row of a record shape
529
	 */
530
	public function addLabel3( $label ) {
531
		$this->m_label3 .= $label . "\l";
532
	}
533
534
    /**
535
	 * @var string $predicate : the "predicate" linking an object to a subject
536
     * @var srting $object: the object, linked to this node
537
	 */
538
	public function addParentNode( $predicate, $object ) {
539
		$this->m_parent[] = array(
540
			"predicate" => $predicate,
541
			"object"    => $object
542
		);
543
	}
544
545
	//public function addGraphLabel( $graphLabel ) {
546
	//	$this->m_graphLabel[] = $graphLabel;
547
	//}
548
549
	public function getGraphLabel() {
550
		return $this->m_graphLabel;
0 ignored issues
show
Bug introduced by
The property m_graphLabel does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
551
	}
552
553
	public function getParentNode() {
554
		return $this->m_parent;
555
	}
556
557
	public function getLabel1() {
558
		return $this->m_label1;
559
	}
560
561
	public function getLabel2() {
562
		return $this->m_label2;
563
	}
564
565
	public function getLabel3() {
566
		return $this->m_label3;
567
	}
568
569
	public function getID() {
570
		return $this->m_id;
571
	}
572
573
}