Completed
Push — master ( 6dc7d8...407c40 )
by Karsten
15:45
created

formats/graphviz/SRF_Process.php (1 issue)

calls to non-existent methods.

Bug Major

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
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() ) {
0 ignored issues
show
The method getUseOtherLabels() does not seem to exist on object<ProcessGraph>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
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;
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
}