Completed
Push — master ( 9b2ed5...981814 )
by Jeroen De
76:07
created

formats/graphviz/SRF_Process.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/*******************************************************************************
3
 *    This file contains the Process Printer for SemanticResultFormats
4
 *   (https://www.mediawiki.org/wiki/Extension:Semantic_Result_Formats)
5
 *
6
 *    Copyright (c) 2008 - 2009 Frank Dengler and Hans-Jörg Happel
7
 *
8
 *   Process Printer is free software: you can redistribute it and/or modify
9
 *   it under the terms of the GNU General Public License as published by
10
 *   the Free Software Foundation, either version 3 of the License, or
11
 *   (at your option) any later version.
12
 *
13
 *   Process Printer is distributed in the hope that it will be useful,
14
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 *   GNU General Public License for more details.
17
 *
18
 *   You should have received a copy of the GNU General Public License
19
 *   along with Process Printer. If not, see <http://www.gnu.org/licenses/>.
20
 *******************************************************************************/
21
22
if ( !defined( 'MEDIAWIKI' ) ) {
23
	die( 'Not an entry point.' );
24
}
25
26
/**
27
 * This is a contribution to Semtantic Result Formats (SRF) which are an
28
 * extension of Semantic MediaWiki (SMW) which in turn is an extension
29
 * of MediaWiki
30
 *
31
 * SRF defines certain "printers" to render the results of SMW semantic
32
 * "ASK"-queries. Some of these printers make use of the GraphViz/dot
33
 * library (which is wrapped by a separate MediaWiki extension).
34
 *
35
 * The purpose of this extension, is to render results of ASK-Queries
36
 * (e.g. Classes with Attributes) as GraphViz-layouted process graphs
37
 *
38
 *
39
 * @author Frank Dengler
40
 * @author Hans-Jörg Happel
41
 * @ingroup SemanticResultFormats
42
 *
43
 * @note AUTOLOADED
44
 */
45
46
// global variable defining picture path
47
48
$srfgPicturePath = "formats/graphviz/images/";
49
50
class SRFProcess extends SMWResultPrinter {
51
52
	// configuration variables
53
	protected $m_graphValidation = false;
54
	protected $m_isDebugSet = false;
55
	protected $m_processCategory = 'Process'; // Category for processes - required for rendering compound nodes
56
57
	// internal variables
58
	protected $m_process;    // process to be rendered
59
60
	/**
61
	 * (non-PHPdoc)
62
	 * @see SMWResultPrinter::handleParameters()
63
	 */
64
	protected function handleParameters( array $params, $outputmode ) {
65
		parent::handleParameters( $params, $outputmode );
66
67
		// init process graph instance
68
		$this->m_process = new ProcessGraph();
69
70
		$this->m_process->setGraphName( trim( $params['graphname'] ) );
71
		$this->m_process->setGraphSize( trim( $params['graphsize'] ) );
72
		$this->m_process->setClusterColor( trim( $params['clustercolor'] ) );
73
		$this->m_process->setRankdir( strtoupper( trim( $params['rankdir'] ) ) );
74
		$this->m_process->setHighlightNode( trim( $params['highlight'] ) );
75
		$this->m_process->setHighlightColor( trim( $params['highlightcolor'] ) );
76
		$this->m_process->setHighlightColor( trim( $params['redlinkcolor'] ) );
77
78
		$this->m_process->setShowRoles( $params['showroles'] );
79
		$this->m_process->setShowStatus( $params['showstatus'] );
80
		$this->m_process->setShowRessources( $params['showresources'] );
81
		$this->m_process->setShowDiscussion( $params['showdiscussion'] );
82
		$this->m_process->setShowRedLinks( $params['showredlinks'] );
83
		$this->m_process->setShowCompound( $params['showcompound'] );
84
85
		$this->m_processCategory = $params['processcat'];
86
		$this->m_isDebugSet = $params['debug'];
87
		$this->m_graphValidation = $params['graphvalidation'];
88
	}
89
90
	/**
91
	 * @see SMWResultPrinter::getParamDefinitions
92
	 *
93
	 * @since 1.8
94
	 *
95
	 * @param $definitions array of IParamDefinition
96
	 *
97
	 * @return array of IParamDefinition|array
98
	 */
99
	public function getParamDefinitions( array $definitions ) {
100
		$params = parent::getParamDefinitions( $definitions );
101
102
		$params['graphname'] = [
103
			'default' => '',
104
			'message' => 'srf-paramdesc-graphname',
105
		];
106
107
		$params['rankdir'] = [
108
			'default' => 'TB',
109
			'message' => 'srf-paramdesc-rankdir',
110
		];
111
112
		$params['graphsize'] = [
113
			'default' => '',
114
			'message' => 'srf-paramdesc-graphsize',
115
		];
116
117
		$params['clustercolor'] = [
118
			'default' => 'lightgrey',
119
			'message' => 'srf-paramdesc-clustercolor',
120
		];
121
122
		$params['highlight'] = [
123
			'default' => '',
124
			'message' => 'srf-paramdesc-highlight',
125
		];
126
127
		$params['highlightcolor'] = [
128
			'default' => 'blue',
129
			'message' => 'srf-paramdesc-highlightcolor',
130
		];
131
132
		$params['redlinkcolor'] = [
133
			'default' => 'red',
134
			'message' => 'srf-paramdesc-redlinkcolor',
135
		];
136
137
		$params['processcat'] = [
138
			'default' => 'Process',
139
			'message' => 'srf-paramdesc-processcategory',
140
		];
141
142
		$params['showroles'] = [
143
			'type' => 'boolean',
144
			'default' => false,
145
			'message' => 'srf-paramdesc-showroles',
146
		];
147
148
		$params['showstatus'] = [
149
			'type' => 'boolean',
150
			'default' => false,
151
			'message' => 'srf-paramdesc-showstatus',
152
		];
153
154
		$params['showresources'] = [
155
			'type' => 'boolean',
156
			'default' => false,
157
			'message' => 'srf-paramdesc-showresources',
158
		];
159
160
		$params['showdiscussion'] = [
161
			'type' => 'boolean',
162
			'default' => false,
163
			'message' => 'srf-paramdesc-showdiscussion',
164
		];
165
166
		$params['showredlinks'] = [
167
			'type' => 'boolean',
168
			'default' => false,
169
			'message' => 'srf-paramdesc-showredlinks',
170
		];
171
172
		$params['showcompound'] = [
173
			'type' => 'boolean',
174
			'default' => true,
175
			'message' => 'srf-paramdesc-showcompound',
176
		];
177
178
		$params['debug'] = [
179
			'type' => 'boolean',
180
			'default' => false,
181
			'message' => 'srf-paramdesc-debug',
182
		];
183
184
		$params['graphvalidation'] = [
185
			'type' => 'boolean',
186
			'default' => false,
187
			'message' => 'srf-paramdesc-graphvalidation',
188
		];
189
190
		return $params;
191
	}
192
193
	/**
194
	 *    This method renders the result set provided by SMW according to the printer
195
	 *
196
	 * @param res                SMWQueryResult, result set of the ask query provided by SMW
197
	 * @param outputmode        ?
198
	 *
199
	 * @return                String, rendered HTML output of this printer for the ask-query
0 ignored issues
show
The doc-type String, could not be parsed: Expected "|" or "end of type", but got "," at position 6. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
200
	 *
201
	 */
202
	protected function getResultText( SMWQueryResult $res, $outputmode ) {
203
		if ( !is_callable( 'renderGraphviz' ) ) {
204
			wfWarn( 'The SRF Graph printer needs the GraphViz extension to be installed.' );
205
			return '';
206
		}
207
208
		global $wgContLang; // content language object
209
210
		//
211
		//	GraphViz settings
212
		//
213
		$this->isHTML = true;
214
215
		//
216
		//	Iterate all rows in result set
217
		//
218
219
		$row = $res->getNext(); // get initial row (i.e. array of SMWResultArray)
220
221
		while ( $row !== false ) {
222
			/* SMWDataItem */
223
			$subject = $row[0]->getResultSubject(); // get Subject of the Result
224
			// creates a new node if $val has type wikipage
225
			if ( $subject->getDIType() == SMWDataItem::TYPE_WIKIPAGE ) {
226
				$wikiPageValue = new SMWWikiPageValue( '_wpg' );
227
				$wikiPageValue->setDataItem( $subject );
228
				$node = $this->m_process->makeNode(
229
					$wikiPageValue->getShortWikiText(),
230
					$wikiPageValue->getShortWikiText()
231
				);
232
			}
233
234
			//
235
			//	Iterate all colums of the row (which describe properties of the proces node)
236
			//
237
238
			// FIXME: this does not work with SMW >= 1.6, see
239
			// https://bugzilla.wikimedia.org/show_bug.cgi?id=35003
240
241
			// FIXME: got _a bit_ of redundancy here looks like... :/
242
243
			/**
244
			 * @var SMWResultArray $field
245
			 */
246
			foreach ( $row as $field ) {
247
248
				// check column title
249
				$req = $field->getPrintRequest();
250
				switch ( ( strtolower( $req->getLabel() ) ) ) {
251
252
					case strtolower( $wgContLang->getNsText( NS_CATEGORY ) ):
253
						foreach ( $field->getContent() as $value ) {
254
							$wikiPageValue = new SMWWikiPageValue( '_wpg' );
255
							$wikiPageValue->setDataItem( $value );
256
							$val = $wikiPageValue->getShortWikiText();
257
258
							if ( $val == ( $wgContLang->getNsText( NS_CATEGORY ) . ':' . $this->m_processCategory ) ) {
259
								$node->setAtomic( false );
260
							}
261
						}
262
263
						break;
264
265
					case "haslabel":
266
						$value = current( $field->getContent() ); // save only the first
267
268
						if ( ( $value !== false ) ) {
269
							$wikiPageValue = new SMWWikiPageValue( '_wpg' );
270
							$wikiPageValue->setDataItem( $value );
271
							$val = $wikiPageValue->getLongWikiText();
272
273
							if ( $this->m_process->getUseOtherLabels() ) {
274
								$val = str_replace( "&", "and", $val );
275
								$node->setLabel( $val );
276
							}
277
						}
278
						break;
279
280
					case "hasrole":
281
						foreach ( $field->getContent() as $value ) {
282
							$wikiPageValue = new SMWWikiPageValue( $field->getPrintRequest()->getTypeID() );
283
							$wikiPageValue->setDataItem( $value );
284
							$val = $wikiPageValue->getShortWikiText();
285
286
							$role = $this->m_process->makeRole( $val, $val );
287
							$node->addRole( $role );
288
						}
289
						break;
290
291
					case "usesresource":
292
						foreach ( $field->getContent() as $value ) {
293
							$wikiPageValue = new SMWWikiPageValue( $field->getPrintRequest()->getTypeID() );
294
							$wikiPageValue->setDataItem( $value );
295
							$val = $wikiPageValue->getShortWikiText();
296
297
							$xres = $this->m_process->makeRessource( $val, $val );
298
							$node->addUsedRessource( $xres );
299
						}
300
						break;
301
302
					case "producesresource":
303
						foreach ( $field->getContent() as $value ) {
304
							$wikiPageValue = new SMWWikiPageValue( $field->getPrintRequest()->getTypeID() );
305
							$wikiPageValue->setDataItem( $value );
306
							$val = $wikiPageValue->getShortWikiText();
307
308
							$xres = $this->m_process->makeRessource( $val, $val );
309
							$node->addProducedRessource( $xres );
310
						}
311
						break;
312
313
					case "hassuccessor":
314
315
						if ( count( $field->getContent() ) > 1 ) {
316
317
							// SplitParallel
318
							$edge = new SplitParallelEdge();
319
							$edge->setFrom( $node );
320
							foreach ( $field->getContent() as $value ) {
321
								$wikiPageValue = new SMWWikiPageValue( $field->getPrintRequest()->getTypeID() );
322
								$wikiPageValue->setDataItem( $value );
323
								$val = $wikiPageValue->getShortWikiText();
324
325
								$edge->addTo( $this->m_process->makeNode( $val, $val ) );
326
							}
327
328
						} else {
329
330
							// Sequence
331
							foreach ( $field->getContent() as $value ) {
332
								$wikiPageValue = new SMWWikiPageValue( $field->getPrintRequest()->getTypeID() );
333
								$wikiPageValue->setDataItem( $value );
334
								$val = $wikiPageValue->getShortWikiText();
335
336
								$edge = new SequentialEdge();
337
								$edge->setFrom( $node );
338
								$edge->setTo( $this->m_process->makeNode( $val, $val ) );
339
							}
340
						}
341
342
						break;
343
344
					case "hasorsuccessor":
345
346
						if ( count( $field->getContent() ) > 0 ) {
347
348
							// SplitExclusiveOr
349
							$edge = new SplitExclusiveOrEdge();
350
							$edge->setFrom( $node );
351
							foreach ( $field->getContent() as $value ) {
352
								$wikiPageValue = new SMWWikiPageValue( $field->getPrintRequest()->getTypeID() );
353
								$wikiPageValue->setDataItem( $value );
354
								$val = $wikiPageValue->getShortWikiText();
355
356
								$edge->addTo( $this->m_process->makeNode( $val, $val ) );
357
							}
358
						}
359
360
						break;
361
362
					case "hascontruesuccessor":
363
364
						if ( count( $field->getContent() ) > 0 ) {
365
366
							// SplitConditional
367
							if ( !isset( $cond_edge ) ) {
368
								$cond_edge = new SplitConditionalOrEdge();
369
								$cond_edge->setFrom( $node );
370
							}
371
372
							// should be only one
373
							foreach ( $field->getContent() as $value ) {
374
								$wikiPageValue = new SMWWikiPageValue( $field->getPrintRequest()->getTypeID() );
375
								$wikiPageValue->setDataItem( $value );
376
								$val = $wikiPageValue->getShortWikiText();
377
378
								$cond_edge->setToTrue( $this->m_process->makeNode( $val, $val ) );
379
							}
380
381
						}
382
383
						break;
384
385
					case "hasconfalsesuccessor":
386
387
						if ( count( $field->getContent() ) > 0 ) {
388
389
							// SplitConditional
390
							if ( !isset( $cond_edge ) ) {
391
								$cond_edge = new SplitConditionalOrEdge();
392
								$cond_edge->setFrom( $node );
393
							}
394
395
							// should be only one
396
							foreach ( $field->getContent() as $value ) {
397
								$wikiPageValue = new SMWWikiPageValue( $field->getPrintRequest()->getTypeID() );
398
								$wikiPageValue->setDataItem( $value );
399
								$val = $wikiPageValue->getShortWikiText();
400
401
								$cond_edge->setToFalse( $this->m_process->makeNode( $val, $val ) );
402
							}
403
						}
404
405
						break;
406
407
					case "hascondition":
408
409
						if ( count( $field->getContent() ) > 0 ) {
410
411
							// SplitConditional
412
							if ( !isset( $cond_edge ) ) {
413
								$cond_edge = new SplitConditionalOrEdge();
414
								$cond_edge->setFrom( $node );
415
							}
416
417
							// should be only one
418
							foreach ( $field->getContent() as $value ) {
419
								$wikiPageValue = new SMWWikiPageValue( $field->getPrintRequest()->getTypeID() );
420
								$wikiPageValue->setDataItem( $value );
421
								$val = $wikiPageValue->getShortWikiText();
422
423
								$cond_edge->setConditionText( $val );
424
425
							}
426
						}
427
428
						break;
429
430
					case "hasstatus":
431
432
						// should be only one
433
						foreach ( $field->getContent() as $value ) {
434
							$wikiPageValue = new SMWWikiPageValue( $field->getPrintRequest()->getTypeID() );
435
							$wikiPageValue->setDataItem( $value );
436
							$val = $wikiPageValue->getShortWikiText();
437
438
							$node->setStatus( $val );
439
						}
440
441
						break;
442
443
					default:
444
445
						// TODO - redundant column in result
446
447
				}
448
			}
449
450
			// reset row variables
451
			unset( $node );
452
			unset( $cond_edge );
453
454
			$row = $res->getNext();        // switch to next row
455
		}
456
457
		//
458
		// generate graphInput
459
		//
460
		$graphInput = $this->m_process->getGraphVizCode();
461
462
		//
463
		// render graphViz code
464
		//
465
		$result = renderGraphviz( $graphInput );
466
467
		$debug = '';
468
		if ( $this->m_isDebugSet ) {
469
			$debug = '<pre>' . $graphInput . '</pre>';
470
		}
471
472
		return $result . $debug;
473
	}
474
}
475
476
/**
477
 * Class representing a process graph
478
 */
479
class ProcessGraph {
480
481
	// configuration variables
482
	protected $m_graphName = '';
483
	protected $m_rankdir = 'TB';
484
	protected $m_graphSize = '';
485
	protected $m_clusterColor = 'lightgrey';
486
	protected $m_showStatus = false;    // should status be rendered?
487
	protected $m_showRoles = false;    // should roles be rendered?
488
	protected $m_showRessources = false;    // should ressources be rendered?
489
	protected $m_showDiscussion = false;    // should discussion be rendered?
490
	protected $m_highlightNode = '';        // node to be highlighted
491
	protected $m_highlightColor = 'blue';    // highlight font color
492
	protected $m_showRedLinks = false;    // check and highlight red links?
493
	protected $m_redLinkColor = 'red';    // red link font color
494
	protected $m_showCompound = true;        // highlight compound nodes (=subprocesses)
495
496
	public $m_useHtmlNodes = true;            // Set to false if you do not want to use HTML table nodes
497
498
	// instance variables
499
	protected $m_nodes = [];    // list of all nodes
500
	protected $m_startnodes = [];    // list of start nodes
501
	protected $m_endnodes = [];    // list of end nodes
502
	protected $m_ressources = [];    // list of ressources
503
	protected $m_roles = [];    // list of roles
504
	protected $m_errors = [];    // list of errors
505
506
	/**
507
	 * This method should be used for getting new or existing nodes
508
	 * If a node does not exist yet, it will be created
509
	 *
510
	 * @param $id            string, node id
511
	 * @param $label        string, node label
512
	 *
513
	 * @return                Object of type ProcessNode
514
	 */
515
	public function makeNode( $id, $label ) {
516
		// check if node exists
517
		if ( isset( $this->m_nodes[$id] ) ) {
518
			// take existing node
519
			$node = $this->m_nodes[$id];
520
521
		} else {
522
			// create new node
523
524
			$node = new ProcessNode();
525
			$node->setId( $id );
526
			$node->setLabel( $label );
527
			$node->setProcess( $this );
528
529
			// is actual node name the same like the one to highlight?
530
			if ( strcasecmp( $id, $this->m_highlightNode ) == 0 ) {
531
				$node->setFontColor( $this->m_highlightColor );
532
			}
533
534
			// is the node a red link (i.e. corresponding wiki page does not yet exist)?
535
			if ( $this->m_showRedLinks ) {
536
				$title = Title::newFromDBkey( $id );
537
				if ( isset( $title ) && ( !$title->exists() ) ) {
538
					$node->setFontColor( $this->m_redLinkColor );
539
				}
540
			}
541
542
			// add new node to process
543
			$this->m_nodes[$id] = $node;
544
		}
545
546
		return $node;
547
548
	}
549
550
	public function makeRole( $id, $label ) {
551
		// check if role exists
552
		if ( isset( $this->m_roles[$id] ) ) {
553
			// take existing roles
554
			$role = $this->m_roles[$id];
555
556
		} else {
557
			$role = new ProcessRole();
558
			$role->setId( $id );
559
			$role->setLabel( $label );
560
561
			// add new role to process
562
			$this->m_roles[$id] = $role;
563
		}
564
565
		return $role;
566
567
	}
568
569
	public function makeRessource( $id, $label ) {
570
		// check if res exists
571
		if ( isset( $this->m_ressources[$id] ) ) {
572
			// take existing res
573
			$res = $this->m_ressources[$id];
574
575
		} else {
576
			$res = new ProcessRessource();
577
			$res->setId( $id );
578
			$res->setLabel( $label );
579
580
			// add new res to process
581
			$this->m_ressources[$id] = $res;
582
583
		}
584
585
		return $res;
586
587
	}
588
589
	public function getEndNodes() {
590
		if ( count( $this->m_endnodes ) == 0 ) {
591
			foreach ( $this->m_nodes as $node ) {
592
				if ( count( $node->getSucc() ) == 0 ) {
593
					$this->m_endnodes[] = $node;
594
				}
595
			}
596
		}
597
598
		return $this->m_endnodes;
599
	}
600
601
	public function getStartNodes() {
602
603
		if ( count( $this->m_startnodes ) == 0 ) {
604
			foreach ( $this->m_nodes as $node ) {
605
				if ( count( $node->getPred() ) == 0 ) {
606
					$this->m_startnodes[] = $node;
607
				}
608
			}
609
		}
610
611
		return $this->m_startnodes;
612
	}
613
614
	public function setShowStatus( $show ) {
615
		$this->m_showStatus = $show;
616
	}
617
618
	public function getShowStatus() {
619
		return $this->m_showStatus;
620
	}
621
622
	public function setShowRoles( $show ) {
623
		$this->m_showRoles = $show;
624
	}
625
626
	public function getShowRoles() {
627
		return $this->m_showRoles;
628
	}
629
630
	public function setShowCompound( $show ) {
631
		$this->m_showCompound = $show;
632
	}
633
634
	public function getShowCompound() {
635
		return $this->m_showCompound;
636
	}
637
638
	public function setShowDiscussion( $show ) {
639
		$this->m_showDiscussion = $show;
640
	}
641
642
	public function getShowDiscussion() {
643
		return $this->m_showDiscussion;
644
	}
645
646
	public function setShowRessources( $show ) {
647
		$this->m_showRessources = $show;
648
	}
649
650
	public function getShowRessources() {
651
		return $this->m_showRessources;
652
	}
653
654
	public function setGraphName( $name ) {
655
		$this->m_graphName = $name;
656
	}
657
658
	public function getGraphName() {
659
		if ( $this->m_graphName == '' ) {
660
			$this->m_graphName = 'ProcessQueryResult' . rand( 1, 99999 );
661
		}
662
		return $this->m_graphName;
663
	}
664
665
	public function setGraphSize( $size ) {
666
		$this->m_graphSize = $size;
667
	}
668
669
	public function setRankdir( $rankdir ) {
670
		$this->m_rankdir = $rankdir;
671
	}
672
673
	public function setClusterColor( $color ) {
674
		$this->m_clusterColor = $color;
675
	}
676
677
	public function setHighlightColor( $color ) {
678
		$this->m_highlightColor = $color;
679
	}
680
681
	public function setRedLinkColor( $color ) {
682
		$this->m_redLinkColor = $color;
683
	}
684
685
	public function setShowRedLinks( $show ) {
686
		$this->m_showRedLinks = $show;
687
	}
688
689
	public function setHighlightNode( $name ) {
690
		$this->m_highlightNode = $name;
691
	}
692
693
	public function addError( $error ) {
694
		$this->m_errors[] = $error;
695
	}
696
697
	public function getErrors() {
698
		return $this->m_errors;
699
	}
700
701
	public function getGraphVizCode() {
702
		//
703
		// header
704
		//
705
		$res = 'digraph ' . $this->getGraphName() . ' {
706
707
	ranksep="0.5";';
708
		if ( $this->m_graphSize != '' ) {
709
			$res .= '
710
	size="' . $this->m_graphSize . '";';
711
		}
712
		$res .= '
713
	rankdir=' . $this->m_rankdir . ';
714
	';
715
716
		//
717
		// add startnodes
718
		//
719
		// TODO I18N
720
		$res .= '
721
	{rank=source; "Start";}
722
		"Start"[shape=box,label="Start",style=filled,color=green];';
723
724
		foreach ( $this->getStartNodes() as $node ) {
725
			$res .= '
726
		"Start" -> "' . $node->getId() . '":port1:n;';
727
		}
728
729
		$res .= '
730
		';
731
732
		//
733
		// add endnodes
734
		//
735
		// TODO I18N
736
		$res .= '
737
	{rank=sink; "End"; }
738
		"End"[shape=box,label="End",style=filled,color=green];';
739
740
		foreach ( $this->getEndNodes() as $node ) {
741
			$res .= '
742
		"' . $node->getId() . '":port1:s -> "End";';
743
		}
744
745
		$res .= '
746
747
	';
748
749
		//
750
		// add subnodes
751
		//
752
		foreach ( $this->m_nodes as $node ) {
753
			$res .= $node->getGraphVizCode();
754
		}
755
756
		//
757
		// add final stuff
758
		//
759
		$res .=
760
			'
761
	}';
762
763
		return $res;
764
765
	}
766
767
}
768
769
abstract class ProcessElement {
770
771
	// TODO I18N
772
	private $m_id = 'no_id';
773
	private $m_label = 'unlabeled';
774
	private $m_uid;
775
776
	public function getUUID() {
777
		if ( !isset( $this->m_uid ) ) {
778
			$this->m_uid = sprintf(
779
				'%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
780
				mt_rand( 0, 0xffff ),
781
				mt_rand( 0, 0xffff ),
782
				mt_rand( 0, 0xffff ),
783
				mt_rand( 0, 0x0fff ) | 0x4000,
784
				mt_rand( 0, 0x3fff ) | 0x8000,
785
				mt_rand( 0, 0xffff ),
786
				mt_rand( 0, 0xffff ),
787
				mt_rand( 0, 0xffff )
788
			);
789
		}
790
791
		return $this->m_uid;
792
	}
793
794
	public function getId() {
795
		return $this->m_id;
796
	}
797
798
	public function setId( $id ) {
799
		$this->m_id = $id;
800
	}
801
802
	public function getLabel() {
803
		return $this->m_label;
804
	}
805
806
	public function setLabel( $label ) {
807
		$this->m_label = $label;
808
	}
809
810
}
811
812
class ProcessRessource extends ProcessElement {
813
814
	private $m_usedby = [];
815
	private $m_producedby = [];
816
817
	public function getProducers() {
818
		return $this->m_producedby;
819
	}
820
821
	public function getUsers() {
822
		return $this->m_usedby;
823
	}
824
825
	public function addProducer( $node ) {
826
		$this->m_producedby[] = $node;
827
	}
828
829
	public function addUser( $node ) {
830
		$this->m_usedby[] = $node;
831
	}
832
833
}
834
835
class ProcessRole extends ProcessElement {
836
837
	private $m_nodes = [];
838
839
	public function getNodes() {
840
		return $this->m_nodes;
841
	}
842
843
	public function addNode( $node ) {
844
		$this->m_nodes[] = $node;
845
	}
846
847
}
848
849
class ProcessNode extends ProcessElement {
850
851
	private $m_status;                    // status value
852
	private $m_is_atomic = true;        // set false if this is a compound node
853
854
	private $m_process;                    // reference to parent process
855
856
	private $m_fontColor = '';            // font color to render
857
858
	private $m_usedressources = [];    // ressources used by this node
859
	private $m_producedressources = [];    // ressources produces by this node
860
	private $m_roles = [];    // roles related to this node
861
862
	private $m_edgeout;                    // outgoing edge (can be only one)
863
	private $m_edgesin = [];    // incoming edges (can be many)
864
865
	public function setStatus( $status ) {
866
		$this->m_status = $status;
867
	}
868
869
	public function getStatus() {
870
		return $this->m_status;
871
	}
872
873
	public function setFontColor( $color ) {
874
		$this->m_fontColor = $color;
875
	}
876
877
	public function setProcess( $proc ) {
878
		$this->m_process = $proc;
879
	}
880
881
	public function getProcess() {
882
		return $this->m_process;
883
	}
884
885
	public function getPred() {
886
		$res = [];
887
888
		foreach ( $this->m_edgesin as $edge ) {
889
			$res = array_merge( $res, $edge->getPred() );
890
		}
891
892
		return $res;
893
	}
894
895
	public function getSucc() {
896
		$res = [];
897
898
		if ( isset( $this->m_edgeout ) ) {
899
			$res = $this->m_edgeout->getSucc();
900
		}
901
902
		return $res;
903
	}
904
905
	public function setEdgeOut( $edge ) {
906
		$this->m_edgeout = $edge;
907
	}
908
909
	public function getEdgeOut() {
910
		return $this->m_edgeout;
911
	}
912
913
	public function addEdgeIn( $edge ) {
914
		$this->m_edgesin[] = $edge;
915
	}
916
917
	public function getEdgesIn() {
918
		return $this->m_edgesin;
919
	}
920
921
	public function addRole( $role ) {
922
		$this->m_roles[] = $role;
923
		$role->addNode( $this );
924
	}
925
926
	public function getRoles() {
927
		return $this->m_roles;
928
	}
929
930
	public function addUsedRessource( $res ) {
931
		$this->m_usedressources[] = $res;
932
		$res->addUser( $this );
933
	}
934
935
	public function getUsedRessources() {
936
		return $this->m_usedressources;
937
	}
938
939
	public function addProducedRessource( $res ) {
940
		$this->m_producedressources[] = $res;
941
		$res->addProducer( $this );
942
	}
943
944
	public function getProducedRessources() {
945
		return $this->m_producedressources;
946
	}
947
948
	public function isAtomic() {
949
		return $this->m_is_atomic;
950
	}
951
952
	public function setAtomic( $atomic ) {
953
		$this->m_is_atomic = $atomic;
954
	}
955
956
	public function getGraphVizCode() {
957
		global $IP, $srfgPicturePath, $srfgIP;
958
		//
959
		// show node status
960
		//
961
		$status = '';
962
		if ( $this->getProcess()->getShowStatus() ) {
963
964
			if ( file_exists( $IP . "/images/p000.png" ) ) {
965
				$PicturePath = $IP . "/images/";
966
			} elseif ( file_exists( $srfgIP . "/formats/graphviz/images/p000.png" ) ) {
967
				$PicturePath = $srfgIP . "/formats/graphviz/images/";
968
			} else {
969
				$PicturePath = $IP . $srfgPicturePath;
970
			}
971
			// $color = 'grey' . $this->getStatus();
972
			// $color = 'grey' . rand(1, 100);
973
			// $status = ',style=filled,color=' . $color;
974
			if ( $this->getStatus() != '' ) {
975
				if ( $this->getStatus() < 25 ) {
976
					$status = ' HREF="[[' . $this->getId() . ']]" TOOLTIP="status ' . $this->getStatus(
977
						) . '%"><IMG SRC="' . $PicturePath . 'p000.png" /';
978
				} elseif ( $this->getStatus() < 50 ) {
979
					$status = ' HREF="[[' . $this->getId() . ']]" TOOLTIP="status ' . $this->getStatus(
980
						) . '%"><IMG SRC="' . $PicturePath . 'p025.png" /';
981
				} elseif ( $this->getStatus() < 75 ) {
982
					$status = ' HREF="[[' . $this->getId() . ']]" TOOLTIP="status ' . $this->getStatus(
983
						) . '%"><IMG SRC="' . $PicturePath . 'p050.png" /';
984
				} elseif ( $this->getStatus() < 100 ) {
985
					$status = ' HREF="[[' . $this->getId() . ']]" TOOLTIP="status ' . $this->getStatus(
986
						) . '%"><IMG SRC="' . $PicturePath . 'p075.png" /';
987
				} elseif ( $this->getStatus() == 100 ) {
988
					$status = ' HREF="[[' . $this->getId() . ']]" TOOLTIP="status ' . $this->getStatus(
989
						) . '%"><IMG SRC="' . $PicturePath . 'p100.png" /';
990
				}
991
			}
992
993
		}
994
995
		//
996
		// show discussion page
997
		//
998
		$discussion = '';
999
		if ( $this->getProcess()->getShowDiscussion() ) {
1000
1001
			if ( file_exists( $IP . "/images/discuss_icon.png" ) ) {
1002
				$PicturePath = $IP . "/images/";
1003
			} elseif ( file_exists( $srfgIP . "/formats/graphviz/images/discuss_icon.png" ) ) {
1004
				$PicturePath = $srfgIP . "/formats/graphviz/images/";
1005
			} else {
1006
				$PicturePath = $IP . $srfgPicturePath;
1007
			}
1008
			$discussionTitle = Title::newFromText( 'Talk:' . $this->getId() . '' );
1009
			if ( $discussionTitle->isKnown() ) {
1010
				$discussion = ' HREF="[[Talk:' . $this->getId() . ']]" TOOLTIP="Talk:' . $this->getId(
1011
					) . '"><IMG SRC="' . $PicturePath . 'discuss_icon.png" /';
1012
			} else {
1013
				$discussion = ' HREF="[[Talk:' . $this->getId() . ']]" TOOLTIP="Talk:' . $this->getId(
1014
					) . '"><IMG SRC="' . $PicturePath . 'discuss_icon_grey.png" /';
1015
			}
1016
1017
		}
1018
1019
		// use highlight color if set (either CURRENTPAGE or REDLINK highlighting - see ProcessGraph::makeNode()
1020
		$high = '';
1021
		if ( $this->m_fontColor !== '' ) {
1022
			$high = ',fontcolor=' . $this->m_fontColor;
1023
		}
1024
1025
		// insert icon for non-atomic nodes (i.e. subprocesses)
1026
		$compound = '<TR><TD ALIGN="LEFT" BORDER="0" WIDTH="20px">';
1027
		if ( $this->getProcess()->getShowCompound() ) {
1028
			if ( file_exists( $IP . "/images/subprocess.png" ) ) {
1029
				$PicturePath = $IP . "/images/";
1030
			} elseif ( file_exists( $srfgIP . "/formats/graphviz/images/subprocess.png" ) ) {
1031
				$PicturePath = $srfgIP . "/formats/graphviz/images/";
1032
			} else {
1033
				$PicturePath = $IP . $srfgPicturePath;
1034
			}
1035
			if ( !$this->isAtomic() ) {
1036
				$compound = '<TR><TD ALIGN="LEFT" BORDER="0" WIDTH="20px" HREF="[[' . $this->getId(
1037
					) . ']]" TOOLTIP="sub process"><IMG SRC="' . $PicturePath . 'subprocess.png"/>';
1038
			}
1039
		}
1040
1041
		//
1042
		// render node itself
1043
		//
1044
		if ( $this->m_process->m_useHtmlNodes ) {
1045
			$res =
1046
				'"' . $this->getId(
1047
				) . '" [shape=plaintext,label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">' . $compound . '</TD><TD BORDER="0" WIDTH="80%"></TD><TD ALIGN="RIGHT" BORDER="0" WIDTH="20px"' . $status . '></TD><TD ALIGN="RIGHT" BORDER="0" WIDTH="20px"' . $discussion . '></TD></TR><TR><TD COLSPAN="4" PORT="port1" HREF="[[' . $this->getId(
1048
				) . ']]" TOOLTIP="' . $this->getLabel() . '"><FONT' . $high . '>' . $this->getLabel() . '</FONT></TD> </TR></TABLE>>];
1049
			';
1050
		} else {
1051
			$res =
1052
				'"' . $this->getId() . '"[label="' . $this->getLabel(
1053
				) . '",shape=rect, height=1.5, URL="[[' . $this->getId() . ']]"];
1054
			';
1055
		}
1056
1057
		//
1058
		// render outgoing node
1059
		//
1060
		if ( isset( $this->m_edgeout ) ) {
1061
			$res .= $this->m_edgeout->getGraphVizCode();
1062
		}
1063
1064
		//
1065
		// show cluster for roles and ressources
1066
		//
1067
		$rrcluster = false;
1068
		$rrcode = 'subgraph "cluster_role' . rand( 1, 9999 ) . '" { style=filled;color=lightgrey;';
1069
1070
		// show roles
1071
		if ( $this->getProcess()->getShowRoles() ) {
1072
1073
			foreach ( $this->getRoles() as $role ) {
1074
				$rrcluster = true;
1075
				$rrcode .= '
1076
				"' . $role->getId() . '"[label="' . $role->getLabel(
1077
					) . '",shape=doubleoctagon, color=red, URL="[[' . $role->getId() . ']]"];
1078
				"' . $role->getId() . '" -> "' . $this->getId() . '":port1 [color=red,arrowhead = none,constraint=false];
1079
				';
1080
1081
			}
1082
		}
1083
1084
		if ( $this->getProcess()->getShowRessources() ) {
1085
1086
			foreach ( $this->getUsedRessources() as $xres ) {
1087
				$rrcluster = true;
1088
				$rrcode .= '
1089
			"' . $xres->getId() . '"[label="' . $xres->getLabel(
1090
					) . '",shape=folder, color=blue, URL="[[' . $xres->getId() . ']]"];
1091
			"' . $xres->getId() . '" -> "' . $this->getId() . '":port1 [color=blue,constraint=false];
1092
				';
1093
			}
1094
1095
			foreach ( $this->getProducedRessources() as $xres ) {
1096
				$rrcluster = true;
1097
				$rrcode .= '
1098
			"' . $xres->getId() . '"[label="' . $xres->getLabel(
1099
					) . '",shape=folder, color=blue, URL="[[' . $xres->getId() . ']]"];
1100
			"' . $this->getId() . '":port1 -> "' . $xres->getId() . '" [color=blue,constraint=false];
1101
				';
1102
			}
1103
1104
		}
1105
1106
		if ( $rrcluster ) {
1107
			$res .= $rrcode . '}';
1108
		}
1109
1110
		$res .= '
1111
	';
1112
1113
		return $res;
1114
	}
1115
1116
}
1117
1118
/**
1119
 * Abstract base class for edges in a process graph
1120
 */
1121
abstract class ProcessEdge {
1122
1123
	private $m_id;
1124
	private $m_uid;
1125
1126
	public function getId() {
1127
		if ( !isset( $this->m_id ) ) {
1128
			$this->m_id = 'edge' . rand( 1, 99999 );
1129
		}
1130
1131
		return $this->m_id;
1132
	}
1133
1134
	public function getUUID() {
1135
		if ( !isset( $this->m_uid ) ) {
1136
			$this->m_uid = sprintf(
1137
				'%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
1138
				mt_rand( 0, 0xffff ),
1139
				mt_rand( 0, 0xffff ),
1140
				mt_rand( 0, 0xffff ),
1141
				mt_rand( 0, 0x0fff ) | 0x4000,
1142
				mt_rand( 0, 0x3fff ) | 0x8000,
1143
				mt_rand( 0, 0xffff ),
1144
				mt_rand( 0, 0xffff ),
1145
				mt_rand( 0, 0xffff )
1146
			);
1147
		}
1148
1149
		return $this->m_uid;
1150
	}
1151
1152
	abstract public function getSucc();
1153
1154
	abstract public function getPred();
1155
1156
	abstract public function getGraphVizCode();
1157
}
1158
1159
abstract class SplitEdge extends ProcessEdge {
1160
1161
	protected $m_from;
1162
	protected $m_to = [];
1163
1164
	public function setFrom( $node ) {
1165
		$this->m_from = $node;
1166
		$node->setEdgeOut( $this );
1167
	}
1168
1169
	public function addTo( $node ) {
1170
		$this->m_to[] = $node;
1171
		$node->addEdgeIn( $this );
1172
	}
1173
1174
	public function getPred() {
1175
		return [ $this->m_from ];
1176
	}
1177
1178
	public function getSucc() {
1179
		return $this->m_to;
1180
	}
1181
1182
}
1183
1184
class SplitConditionalOrEdge extends ProcessEdge {
1185
1186
	protected $m_from;
1187
	protected $m_to_true;
1188
	protected $m_to_false;
1189
	protected $m_con_text = 'empty_condition';
1190
1191
	public function getSucc() {
1192
		return [ $this->m_to_false, $this->m_to_true ];
1193
	}
1194
1195
	public function getPred() {
1196
		return [ $this->m_from ];
1197
	}
1198
1199
	public function setFrom( $node ) {
1200
		$this->m_from = $node;
1201
		$node->setEdgeOut( $this );
1202
	}
1203
1204
	public function setToFalse( $node ) {
1205
		$this->m_to_false = $node;
1206
		$node->addEdgeIn( $this );
1207
	}
1208
1209
	public function setToTrue( $node ) {
1210
		$this->m_to_true = $node;
1211
		$node->addEdgeIn( $this );
1212
	}
1213
1214
	public function setConditionText( $cond ) {
1215
		$this->m_con_text = $cond;
1216
	}
1217
1218
	public function getGraphVizCode() {
1219
1220
		$p = $this->m_from;
1221
1222
		if ( ( !isset( $this->m_from ) ) || ( !isset( $this->m_to_false ) ) || ( !isset( $this->m_to_true ) ) ) {
1223
1224
			echo "error with SplitConditionalOrEdge"; // TODO
1225
			exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method getGraphVizCode() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1226
		}
1227
1228
		$res =
1229
			'subgraph "clus_' . $this->getId() . '" {
1230
		';
1231
1232
		// cond-Shape
1233
		$con = 'con' . rand( 1, 99999 );
1234
		$res .=
1235
			'"' . $con . '"[shape=diamond,label="' . $this->m_con_text . '",style=filled,color=skyblue];
1236
		"' . $p->getId() . '":port1:s -> "' . $con . '";
1237
		';
1238
1239
		// True Succ
1240
		$res .=
1241
			'"' . $this->m_to_true->getId() . '" [URL = "[[' . $this->m_to_true->getId() . ']]"];
1242
		';
1243
1244
		$res .=
1245
			'"' . $con . '" -> "' . $this->m_to_true->getId() . '":port1:n [label="true"];
1246
		';
1247
1248
		// False Succ
1249
		$res .=
1250
			'"' . $this->m_to_false->getId() . '" [URL = "[[' . $this->m_to_false->getId() . ']]"];
1251
		';
1252
1253
		$res .=
1254
			'"' . $con . '" -> "' . $this->m_to_false->getId() . '":port1:n [label="false"];';
1255
1256
		$res .= '
1257
	}
1258
	';
1259
1260
		return $res;
1261
	}
1262
1263
}
1264
1265
class SplitExclusiveOrEdge extends SplitEdge {
1266
1267
	public function getGraphVizCode() {
1268
		global $srfgShapeStyle;
1269
		$p = $this->getPred();
1270
		$p = $p[0];
1271
		if ( $srfgShapeStyle == '' ) {
1272
			$srfgShapeStyle = "box";
1273
		}
1274
		$res =
1275
			'subgraph "clus_' . $this->getId() . '" {
1276
		';
1277
1278
		// add OR-Shape
1279
		$orx = 'or' . rand( 1, 99999 );
1280
		$res .=
1281
			'"' . $orx . '"[shape=' . $srfgShapeStyle . ',label="+",style=filled,color=gold];
1282
		"' . $p->getId() . '":port1:s -> "' . $orx . '";
1283
		';
1284
1285
		foreach ( $this->getSucc() as $s ) {
1286
			$res .=
1287
				'"' . $s->getId() . '" [URL="[[' . $s->getId() . ']]"];
1288
		';
1289
1290
			$res .=
1291
				'"' . $orx . '" -> "' . $s->getId() . '":port1:n;
1292
		';
1293
		}
1294
1295
		$res .= '
1296
	}
1297
	';
1298
1299
		return $res;
1300
	}
1301
1302
}
1303
1304
class SplitParallelEdge extends SplitEdge {
1305
1306
	public function getGraphVizCode() {
1307
		global $srfgShapeStyle;
1308
		if ( $srfgShapeStyle == '' ) {
1309
			$srfgShapeStyle = "box";
1310
		}
1311
		$p = $this->getPred();
1312
		$p = $p[0];
1313
1314
		$res =
1315
			'subgraph "clus_' . $this->getId() . '" {
1316
		';
1317
1318
		// add AND-Shape
1319
		$and = 'and' . rand( 1, 99999 );
1320
		$res .=
1321
			'"' . $and . '"[shape=' . $srfgShapeStyle . ',label="||",style=filled,color=palegreen];
1322
		"' . $p->getId() . '":port1:s -> "' . $and . '";
1323
		';
1324
1325
		foreach ( $this->getSucc() as $s ) {
1326
			$res .=
1327
				'"' . $s->getId() . '" [URL = "[[' . $s->getId() . ']]"];
1328
		';
1329
1330
			$res .=
1331
				'"' . $and . '" -> "' . $s->getId() . '":port1:n;
1332
		';
1333
		}
1334
1335
		$res .= '
1336
	}
1337
	';
1338
1339
		return $res;
1340
	}
1341
1342
}
1343
1344
class SequentialEdge extends ProcessEdge {
1345
1346
	private $m_from;
1347
	private $m_to;
1348
1349
	public function setFrom( $node ) {
1350
		$this->m_from = $node;
1351
		$node->setEdgeOut( $this );
1352
	}
1353
1354
	public function setTo( $node ) {
1355
		$this->m_to = $node;
1356
		$node->addEdgeIn( $this );
1357
	}
1358
1359
	public function getPred() {
1360
		return [ $this->m_from ];
1361
	}
1362
1363
	public function getSucc() {
1364
		return [ $this->m_to ];
1365
	}
1366
1367
	public function getGraphVizCode() {
1368
1369
		$p = $this->m_from;
1370
		$s = $this->m_to;
1371
1372
		$res =
1373
			'subgraph "clus_' . $this->getId() . '" {
1374
		';
1375
1376
		$res .=
1377
			'"' . $s->getId() . '" [URL = "[[' . $s->getId() . ']]"];
1378
		';
1379
1380
		$res .=
1381
			'"' . $p->getId() . '":port1:s -> "' . $s->getId() . '":port1:n;';
1382
1383
		$res .= '
1384
	}
1385
	';
1386
1387
		return $res;
1388
	}
1389
1390
}