summaryrefslogtreecommitdiff
path: root/www/wiki/extensions/SemanticMediaWiki/src/Query/Language/Conjunction.php
blob: be5e8dbe995f4c05783927c2120d186b09257213 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
<?php

namespace SMW\Query\Language;

/**
 * Description of a collection of many descriptions, all of which
 * must be satisfied (AND, conjunction).
 *
 * Corresponds to conjunction in OWL and SPARQL. Not available in RDFS.
 *
 * @license GNU GPL v2+
 * @since 1.6
 *
 * @author Markus Krötzsch
 */
class Conjunction extends Description {

	/**
	 * @var Description[]
	 */
	protected $descriptions = [];

	/**
	 * @since 1.6
	 *
	 * @param array $descriptions
	 */
	public function __construct( array $descriptions = [] ) {
		foreach ( $descriptions as $description ) {
			$this->addDescription( $description );
		}
	}

	/**
	 * @see Description::getFingerprint
	 * @since 2.5
	 *
	 * @return string
	 */
	public function getFingerprint() {

		if ( $this->fingerprint !== null ) {
			return $this->fingerprint;
		}

		$fingerprint = [];

		// Filter equal signatures
		foreach ( $this->descriptions as $description ) {
			$fingerprint[$description->getFingerprint()] = true;
		}

		// Sorting to generate a constant fingerprint independent of its
		// position within a conjunction ( [Foo]][[Bar]], [[Bar]][[Foo]])
		ksort( $fingerprint );

		return $this->fingerprint = 'C:' . md5( implode( '|', array_keys( $fingerprint ) ) );
	}

	public function getDescriptions() {
		return $this->descriptions;
	}

	public function addDescription( Description $description ) {

		$this->fingerprint = null;

		if ( ! ( $description instanceof ThingDescription ) ) {
			if ( $description instanceof Conjunction ) { // absorb sub-conjunctions
				foreach ( $description->getDescriptions() as $subdesc ) {
					$this->descriptions[$subdesc->getFingerprint()] = $subdesc;
				}
			} else {
				$this->descriptions[$description->getFingerprint()] = $description;
			}

			// move print descriptions downwards
			///TODO: This may not be a good solution, since it does modify $description and since it does not react to future changes
			$this->m_printreqs = array_merge( $this->m_printreqs, $description->getPrintRequests() );
			$description->setPrintRequests( [] );
		}

		$fingerprint = $this->getFingerprint();

		foreach ( $this->descriptions as $description ) {
			$description->setMembership( $fingerprint );
		}
	}

	public function getQueryString( $asvalue = false ) {
		$result = '';

		foreach ( $this->descriptions as $desc ) {
			$result .= ( $result ? ' ' : '' ) . $desc->getQueryString( false );
		}

		if ( $result === '' ) {
			return $asvalue ? '+' : '';
		}

		// <q> not needed for stand-alone conjunctions (AND binds stronger than OR)
		return $asvalue ? " <q>{$result}</q> " : $result;
	}

	public function isSingleton() {
		foreach ( $this->descriptions as $d ) {
			if ( $d->isSingleton() ) {
				return true;
			}
		}
		return false;
	}

	public function getSize() {
		$size = 0;

		foreach ( $this->descriptions as $desc ) {
			$size += $desc->getSize();
		}

		return $size;
	}

	public function getDepth() {
		$depth = 0;

		foreach ( $this->descriptions as $desc ) {
			$depth = max( $depth, $desc->getDepth() );
		}

		return $depth;
	}

	public function getQueryFeatures() {
		$result = SMW_CONJUNCTION_QUERY;

		foreach ( $this->descriptions as $desc ) {
			$result = $result | $desc->getQueryFeatures();
		}

		return $result;
	}

	public function prune( &$maxsize, &$maxdepth, &$log ) {
		if ( $maxsize <= 0 ) {
			$log[] = $this->getQueryString();
			return new ThingDescription();
		}

		$prunelog = [];
		$newdepth = $maxdepth;
		$result = new Conjunction();

		foreach ( $this->descriptions as $desc ) {
			$restdepth = $maxdepth;
			$result->addDescription( $desc->prune( $maxsize, $restdepth, $prunelog ) );
			$newdepth = min( $newdepth, $restdepth );
		}

		if ( count( $result->getDescriptions() ) > 0 ) {
			$log = array_merge( $log, $prunelog );
			$maxdepth = $newdepth;

			if ( count( $result->getDescriptions() ) == 1 ) { // simplify unary conjunctions!
				$descriptions = $result->getDescriptions();
				$result = array_shift( $descriptions );
			}

			$result->setPrintRequests( $this->getPrintRequests() );

			return $result;
		} else {
			$log[] = $this->getQueryString();

			$result = new ThingDescription();
			$result->setPrintRequests( $this->getPrintRequests() );

			return $result;
		}
	}

}