ocs /lib/pkp/classes/metadata/MetadataProperty.inc.php

Language PHP Lines 344
MD5 Hash 8ea48cfae910f08f0ad7db2ad64650bc
Repository https://github.com/lib-uoguelph-ca/ocs.git View Raw File
  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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
<?php

/**
 * @file classes/metadata/MetadataProperty.inc.php
 *
 * Copyright (c) 2000-2012 John Willinsky
 * Distributed under the GNU GPL v2. For full terms see the file docs/COPYING.
 *
 * @class MetadataProperty
 * @ingroup metadata
 * @see MetadataSchema
 * @see MetadataRecord
 *
 * @brief Class representing metadata properties. It specifies type and cardinality
 *  of a meta-data property (=term, field, ...) and whether the property can
 *  be internationalized. It also provides a validator to test whether input
 *  conforms to the property specification.
 *
 *  In the DCMI abstract model, this class specifies a property together with its
 *  allowed range and cardinality.
 *
 *  We also define the resource types (application entities, association types)
 *  that can be described with the property. This allows us to check that only
 *  valid resource associations are made. It also allows us to prepare property
 *  entry forms or displays for a given resource type and integrate these in the
 *  work-flow of the resource. By dynamically adding or removing assoc types,
 *  end users will be able to configure the meta-data fields that they wish to
 *  make available, persist or enter in their application.
 */

// $Id$

// literal values (plain)
define('METADATA_PROPERTY_TYPE_STRING', 0x01);

// literal values (typed)
define('METADATA_PROPERTY_TYPE_DATE', 0x02);
define('METADATA_PROPERTY_TYPE_INTEGER', 0x03);

// non-literal value string from a controlled vocabulary
define('METADATA_PROPERTY_TYPE_VOCABULARY', 0x04);

// non-literal value URI
define('METADATA_PROPERTY_TYPE_URI', 0x05);

// non-literal value pointing to a separate description set instance (=another MetadataRecord object)
define('METADATA_PROPERTY_TYPE_COMPOSITE', 0x06);

// allowed cardinality of statements for a given property type in a meta-data schema
define('METADATA_PROPERTY_CARDINALITY_ONE', 0x01);
define('METADATA_PROPERTY_CARDINALITY_MANY', 0x02);

class MetadataProperty {
	/** @var string property name */
	var $_name;

	/** @var string a translation id */
	var $_displayName;

	/** @var int the resource types that can be described with this property */
	var $_assocTypes;

	/** @var array allowed property types */
	var $_types;

	/** @var boolean flag that defines whether the property can be translated */
	var $_translated;

	/** @var integer property cardinality */
	var $_cardinality;

	/**
	 * Constructor
	 * @param $name string the unique name of the property within a meta-data schema (can be a property URI)
	 * @param $assocTypes array an array of integers that define the application entities that can
	 *  be described with this property.
	 * @param $types mixed must be a scalar or an array with the supported types, default: METADATA_PROPERTY_TYPE_STRING
	 * @param $translated boolean whether the property may have various language versions, default: false
	 * @param $cardinality integer must be on of the supported cardinalities, default: METADATA_PROPERTY_CARDINALITY_ONE
	 * @param $compositeType integer an association type, mandatory if $type is METADATA_PROPERTY_TYPE_COMPOSITE
	 */
	function MetadataProperty($name, $assocTypes = array(), $types = METADATA_PROPERTY_TYPE_STRING,
			$translated = false, $cardinality = METADATA_PROPERTY_CARDINALITY_ONE, $displayName = null) {

		// Validate name and assoc type array
		assert(is_string($name));
		assert(is_array($assocTypes));

		// A single type (scalar or composite) will be
		// transformed to an array of types so that we
		// can treat them uniformly.
		if (is_scalar($types) || count($types) == 1) {
			$types = array($types);
		}

		// Validate types
		foreach($types as $type) {
			if (is_array($type)) {
				// Validate composite types
				assert(count($type) == 1 && isset($type[METADATA_PROPERTY_TYPE_COMPOSITE]) && is_integer($type[METADATA_PROPERTY_TYPE_COMPOSITE]));
				// Properties that allow composite types cannot be translated
				assert(!$translated);
			} else {
				// Validate all other types
				assert($type != METADATA_PROPERTY_TYPE_COMPOSITE && in_array($type, MetadataProperty::getSupportedTypes()));
			}
		}

		// Validate translation and cardinality
		assert(is_bool($translated));
		assert(in_array($cardinality, MetadataProperty::getSupportedCardinalities()));

		// Default display name
		if (is_null($displayName)) $displayName = 'metadata.property.displayName.'.$name;
		assert(is_string($displayName));

		// Initialize the class
		$this->_name = (string)$name;
		$this->_assocTypes =& $assocTypes;
		$this->_types =& $types;
		$this->_translated = (boolean)$translated;
		$this->_cardinality = (integer)$cardinality;
		$this->_displayName = (string)$displayName;
	}

	/**
	 * Get the name
	 * @return string
	 */
	function getName() {
		return $this->_name;
	}

	/**
	 * Returns a canonical form of the property
	 * name ready to be used as a property id in an
	 * external context (e.g. Forms or Templates).
	 * @return string
	 */
	function getId() {
		// Replace special characters in XPath-like names
		// as 'person-group[@person-group-type="author"]'.
		$from = array(
			'[', ']', '@', '"', '='
		);
		$to = array(
			'-', '', '', '', '-'
		);
		$propertyId = trim(str_replace($from, $to, $this->getName()), '-');
		$propertyId = String::camelize($propertyId);
		return $propertyId;
	}

	/**
	 * Get the translation id representing
	 * the display name of the property.
	 * @return string
	 */
	function getDisplayName() {
		return $this->_displayName;
	}

	/**
	 * Get the allowed association types
	 * (resources that can be described
	 * with this property)
	 * @return array a list of integers representing
	 *  association types.
	 */
	function &getAssocTypes() {
		return $this->_assocTypes;
	}

	/**
	 * Get the allowed type
	 * @return integer
	 */
	function getTypes() {
		return $this->_types;
	}

	/**
	 * Is this property translated?
	 * @return boolean
	 */
	function getTranslated() {
		return $this->_translated;
	}

	/**
	 * Get the cardinality
	 * @return integer
	 */
	function getCardinality() {
		return $this->_cardinality;
	}

	//
	// Public methods
	//
	/**
	 * Validate a given input against the property specification
	 *
	 * @param $value mixed the input to be validated
	 * @return boolean validation success
	 */
	function isValid($value) {
		// We never accept null values or arrays.
		if (is_null($value) || is_array($value)) return false;

		// The value must validate against at least one type
		$isValid = false;
		foreach ($this->getTypes() as $type) {
			// Extract data from composite type
			if (is_array($type)) {
				assert(count($type) == 1 && key($type) == METADATA_PROPERTY_TYPE_COMPOSITE);
				$compositeType = $type[METADATA_PROPERTY_TYPE_COMPOSITE];
				$type = METADATA_PROPERTY_TYPE_COMPOSITE;
			}

			// Type specific validation
			switch ($type) {
				case METADATA_PROPERTY_TYPE_STRING:
					if (is_string($value)) $isValid = true;
					break;

				case METADATA_PROPERTY_TYPE_VOCABULARY:
					// Interpret the name of this property as a controlled vocabulary triple
					$vocabNameParts = explode(':', $this->getName());
					assert(count($vocabNameParts) == 3);
					list($symbolic, $assocType, $assocId) = $vocabNameParts;

					// Validate with controlled vocabulary validator
					import('validation.ValidatorControlledVocab');
					$validator = new ValidatorControlledVocab($symbolic, $assocType, $assocId);
					if ($validator->isValid($value)) $isValid = true;
					break;

				case METADATA_PROPERTY_TYPE_URI:
					// Validate with the URI validator
					import('validation.ValidatorUri');
					$validator = new ValidatorUri();
					if ($validator->isValid($value)) $isValid = true;
					break;

				case METADATA_PROPERTY_TYPE_DATE:
					// We allow the following patterns:
					// YYYY-MM-DD, YYYY-MM and YYYY
					$datePattern = '/^[0-9]{4}(-[0-9]{2}(-[0-9]{2})?)?$/';
					if (!preg_match($datePattern, $value)) break;

					// Check whether the given string is really a valid date
					$dateParts = explode('-', $value);
					// Set the day and/or month to 1 if not set
					$dateParts = array_pad($dateParts, 3, 1);
					// Extract the date parts
					list($year, $month, $day) = $dateParts;
					// Validate the date (only leap days will pass unnoticed ;-) )
					// Who invented this argument order?
					if (checkdate($month, $day, $year)) $isValid = true;
					break;

				case METADATA_PROPERTY_TYPE_INTEGER:
					if (is_integer($value)) $isValid = true;
					break;

				case METADATA_PROPERTY_TYPE_COMPOSITE:
					// Composites can either be represented by a meta-data description
					// or by a string of the form AssocType:AssocId if the composite
					// has already been persisted in the database.
					switch(true) {
						// Test for MetadataDescription format
						case is_a($value, 'MetadataDescription'):
							$assocType = $value->getAssocType();
							break;

						// Test for AssocType:AssocId format
						case is_string($value):
							$valueParts = explode(':', $value);
							if (count($valueParts) != 2) break 2; // break the outer switch
							list($assocType, $assocId) = $valueParts;
							if (!(is_numeric($assocType) && is_numeric($assocId))) break 2; // break the outer switch
							$assocType = (integer)$assocType;
							break;

						default:
							// None of the allowed types
							break;
					}

					// Check that the association type matches
					if (isset($assocType) && $assocType === $compositeType) $isValid = true;
					break;

				default:
					// Unknown type. As we validate type in the setter, this
					// should be unreachable code.
					assert(false);
			}

			// The value only has to validate against one of the given
			// types: No need to validate against subsequent allowed types.
			if ($isValid) break;
		}

		// Will return false if the value didn't validate against any
		// of the types, otherwise true.
		return $isValid;
	}

	//
	// Public static methods
	//
	/**
	 * Return supported meta-data property types
	 * NB: PHP4 work-around for a public static class member
	 * @return array supported meta-data property types
	 */
	function getSupportedTypes() {
		static $_supportedTypes = array(
			METADATA_PROPERTY_TYPE_STRING,
			METADATA_PROPERTY_TYPE_DATE,
			METADATA_PROPERTY_TYPE_INTEGER,
			METADATA_PROPERTY_TYPE_VOCABULARY,
			METADATA_PROPERTY_TYPE_URI,
			METADATA_PROPERTY_TYPE_COMPOSITE
		);
		return $_supportedTypes;
	}

	/**
	 * Return supported cardinalities
	 * NB: PHP4 work-around for a public static class member
	 * @return array supported cardinalities
	 */
	function getSupportedCardinalities() {
		static $_supportedCardinalities = array(
			METADATA_PROPERTY_CARDINALITY_ONE,
			METADATA_PROPERTY_CARDINALITY_MANY
		);
		return $_supportedCardinalities;
	}
}
?>
Back to Top