summaryrefslogtreecommitdiff
path: root/www/wiki/extensions/SemanticMediaWiki/src/Deserializers/DVDescriptionDeserializer/DescriptionDeserializer.php
blob: 4230784c109b89d66e9939ae33e903a5e062adb9 (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
<?php

namespace SMW\Deserializers\DVDescriptionDeserializer;

use Deserializers\DispatchableDeserializer;
use SMW\ApplicationFactory;
use SMW\DataItemFactory;
use SMW\Query\DescriptionFactory;
use SMW\Query\QueryComparator;
use SMWDataValue as DataValue;

/**
 * @private
 *
 * Create an Description object based on a value string that was entered
 * in a query. Turning inputs that a user enters in place of a value within
 * a query string into query conditions is often a standard procedure. The
 * processing must take comparators like "<" into account, but otherwise
 * the normal parsing function can be used. However, there can be datatypes
 * where processing is more complicated, e.g. if the input string contains
 * more than one value, each of which may have comparators, as in
 * SMWRecordValue. In this case, it makes sense to overwrite this method.
 * Another reason to do this is to add new forms of comparators or new ways
 * of entering query conditions.
 *
 * The resulting Description may or may not make use of the datavalue
 * object that this function was called on, so it must be ensured that this
 * value is not used elsewhere when calling this method. The function can
 * return ThingDescription to not impose any condition, e.g. if parsing
 * failed. Error messages of this DataValue object are propagated.
 *
 * @license GNU GPL v2+
 * @since 2.3
 *
 * @author mwjames
 */
abstract class DescriptionDeserializer implements DispatchableDeserializer {

	/**
	 * @var DescriptionFactory
	 */
	protected $descriptionFactory;

	/**
	 * @var DataItemFactory
	 */
	protected $dataItemFactory;

	/**
	 * @var array
	 */
	protected $errors = [];

	/**
	 * @var DataValue
	 */
	protected $dataValue;

	/**
	 * @since 2.5
	 *
	 * @param DescriptionFactory|null $descriptionFactory
	 * @param DataItemFactory|null $dataItemFactory
	 */
	public function __construct( DescriptionFactory $descriptionFactory = null, DescriptionFactory $dataItemFactory = null ) {
		$this->descriptionFactory = $descriptionFactory;
		$this->dataItemFactory = $dataItemFactory;

		if ( $this->descriptionFactory === null ) {
			$this->descriptionFactory = ApplicationFactory::getInstance()->getQueryFactory()->newDescriptionFactory();
		}

		if ( $this->dataItemFactory === null ) {
			$this->dataItemFactory = ApplicationFactory::getInstance()->getDataItemFactory();
		}
	}

	/**
	 * @since 2.3
	 *
	 * @param DataValue $dataValue
	 */
	public function setDataValue( DataValue $dataValue ) {
		$this->dataValue = $dataValue;
		$this->errors = [];
	}

	/**
	 * @since 2.3
	 *
	 * @param string $error
	 */
	public function addError( $error ) {

		if ( is_array( $error ) ) {
			return $this->errors = array_merge( $this->errors, $error );
		}

		$this->errors[] = $error;
	}

	/**
	 * @since 2.3
	 *
	 * @return array
	 */
	public function getErrors() {
		return $this->errors;
	}

	/**
	 * Helper function for DescriptionDeserializer::deserialize that prepares a
	 * single value string, possibly extracting comparators. $value is changed
	 * to consist only of the remaining effective value string (without the
	 * comparator).
	 *
	 * @param string $value
	 * @param string|integer $comparator
	 */
	protected function prepareValue( &$value, &$comparator ) {
		$comparator = QueryComparator::getInstance()->extractComparatorFromString( $value );

		// [[in:lorem ipsum]] / [[Has text::in:lorem ipsum]] to be turned into a
		// proximity match where lorem AND ipsum needs to be present in the
		// indexed match field.
		//
		// For those query engines that support those search patterns!
		if ( $comparator === SMW_CMP_IN ) {
			$comparator = SMW_CMP_LIKE;

			// Looking for something like [[in:phrase:foo]]
			if ( strpos( $value, 'phrase:' ) !== false ) {
				$value = str_replace( 'phrase:', '', $value );
				$value = '"' . $value . '"';
			}

			// `in:...` is for the "busy" user to avoid adding wildcards now and
			// then to the value string
			$value = "*$value*";

			// No property and the assumption is [[in:...]] with the expected use
			// of the wide proximity as indicated by an additional `~`
			if ( $this->dataValue->getProperty() === null ) {
				$value = "~$value";
			}
		}

		// [[not:foo bar]]
		// For those query engines that support those text search patterns!
		if ( $comparator === SMW_CMP_NOT ) {
			$comparator = SMW_CMP_NLKE;

			$value = str_replace( '!', '', $value );

			// Opposed to `in:` which includes *, `not:` is intended to match
			// only the exact entered term. It can be extended using *
			// if necessary (e.g. [[Has text::not:foo*]]).

			// Use as phrase to signal an exact term match for a wide proximity
			// search
			if ( $this->dataValue->getProperty() === null ) {
				$value = "~\"$value\"";
			}
		}

		// [[phrase:lorem ipsum]] to be turned into a promixity phrase_match
		// where the entire string (incl. its order) are to be matched.
		//
		// For those query engines that support those search patterns!
		if ( $comparator === SMW_CMP_PHRASE ) {
			$comparator = SMW_CMP_LIKE;
			$value = '"' . $value . '"';

			if ( $this->dataValue->getProperty() === null ) {
				$value = "~$value";
			}
		}
	}

}