Completed
Push — master ( ea0e5d...10ff2a )
by mw
02:37
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
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 52 and the first side effect is on line 23.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

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