PageRenderTime 50ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

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

https://github.com/lib-uoguelph-ca/ocs
PHP | 344 lines | 152 code | 46 blank | 146 comment | 32 complexity | 8ea48cfae910f08f0ad7db2ad64650bc MD5 | raw file
Possible License(s): GPL-2.0
  1. <?php
  2. /**
  3. * @file classes/metadata/MetadataProperty.inc.php
  4. *
  5. * Copyright (c) 2000-2012 John Willinsky
  6. * Distributed under the GNU GPL v2. For full terms see the file docs/COPYING.
  7. *
  8. * @class MetadataProperty
  9. * @ingroup metadata
  10. * @see MetadataSchema
  11. * @see MetadataRecord
  12. *
  13. * @brief Class representing metadata properties. It specifies type and cardinality
  14. * of a meta-data property (=term, field, ...) and whether the property can
  15. * be internationalized. It also provides a validator to test whether input
  16. * conforms to the property specification.
  17. *
  18. * In the DCMI abstract model, this class specifies a property together with its
  19. * allowed range and cardinality.
  20. *
  21. * We also define the resource types (application entities, association types)
  22. * that can be described with the property. This allows us to check that only
  23. * valid resource associations are made. It also allows us to prepare property
  24. * entry forms or displays for a given resource type and integrate these in the
  25. * work-flow of the resource. By dynamically adding or removing assoc types,
  26. * end users will be able to configure the meta-data fields that they wish to
  27. * make available, persist or enter in their application.
  28. */
  29. // $Id$
  30. // literal values (plain)
  31. define('METADATA_PROPERTY_TYPE_STRING', 0x01);
  32. // literal values (typed)
  33. define('METADATA_PROPERTY_TYPE_DATE', 0x02);
  34. define('METADATA_PROPERTY_TYPE_INTEGER', 0x03);
  35. // non-literal value string from a controlled vocabulary
  36. define('METADATA_PROPERTY_TYPE_VOCABULARY', 0x04);
  37. // non-literal value URI
  38. define('METADATA_PROPERTY_TYPE_URI', 0x05);
  39. // non-literal value pointing to a separate description set instance (=another MetadataRecord object)
  40. define('METADATA_PROPERTY_TYPE_COMPOSITE', 0x06);
  41. // allowed cardinality of statements for a given property type in a meta-data schema
  42. define('METADATA_PROPERTY_CARDINALITY_ONE', 0x01);
  43. define('METADATA_PROPERTY_CARDINALITY_MANY', 0x02);
  44. class MetadataProperty {
  45. /** @var string property name */
  46. var $_name;
  47. /** @var string a translation id */
  48. var $_displayName;
  49. /** @var int the resource types that can be described with this property */
  50. var $_assocTypes;
  51. /** @var array allowed property types */
  52. var $_types;
  53. /** @var boolean flag that defines whether the property can be translated */
  54. var $_translated;
  55. /** @var integer property cardinality */
  56. var $_cardinality;
  57. /**
  58. * Constructor
  59. * @param $name string the unique name of the property within a meta-data schema (can be a property URI)
  60. * @param $assocTypes array an array of integers that define the application entities that can
  61. * be described with this property.
  62. * @param $types mixed must be a scalar or an array with the supported types, default: METADATA_PROPERTY_TYPE_STRING
  63. * @param $translated boolean whether the property may have various language versions, default: false
  64. * @param $cardinality integer must be on of the supported cardinalities, default: METADATA_PROPERTY_CARDINALITY_ONE
  65. * @param $compositeType integer an association type, mandatory if $type is METADATA_PROPERTY_TYPE_COMPOSITE
  66. */
  67. function MetadataProperty($name, $assocTypes = array(), $types = METADATA_PROPERTY_TYPE_STRING,
  68. $translated = false, $cardinality = METADATA_PROPERTY_CARDINALITY_ONE, $displayName = null) {
  69. // Validate name and assoc type array
  70. assert(is_string($name));
  71. assert(is_array($assocTypes));
  72. // A single type (scalar or composite) will be
  73. // transformed to an array of types so that we
  74. // can treat them uniformly.
  75. if (is_scalar($types) || count($types) == 1) {
  76. $types = array($types);
  77. }
  78. // Validate types
  79. foreach($types as $type) {
  80. if (is_array($type)) {
  81. // Validate composite types
  82. assert(count($type) == 1 && isset($type[METADATA_PROPERTY_TYPE_COMPOSITE]) && is_integer($type[METADATA_PROPERTY_TYPE_COMPOSITE]));
  83. // Properties that allow composite types cannot be translated
  84. assert(!$translated);
  85. } else {
  86. // Validate all other types
  87. assert($type != METADATA_PROPERTY_TYPE_COMPOSITE && in_array($type, MetadataProperty::getSupportedTypes()));
  88. }
  89. }
  90. // Validate translation and cardinality
  91. assert(is_bool($translated));
  92. assert(in_array($cardinality, MetadataProperty::getSupportedCardinalities()));
  93. // Default display name
  94. if (is_null($displayName)) $displayName = 'metadata.property.displayName.'.$name;
  95. assert(is_string($displayName));
  96. // Initialize the class
  97. $this->_name = (string)$name;
  98. $this->_assocTypes =& $assocTypes;
  99. $this->_types =& $types;
  100. $this->_translated = (boolean)$translated;
  101. $this->_cardinality = (integer)$cardinality;
  102. $this->_displayName = (string)$displayName;
  103. }
  104. /**
  105. * Get the name
  106. * @return string
  107. */
  108. function getName() {
  109. return $this->_name;
  110. }
  111. /**
  112. * Returns a canonical form of the property
  113. * name ready to be used as a property id in an
  114. * external context (e.g. Forms or Templates).
  115. * @return string
  116. */
  117. function getId() {
  118. // Replace special characters in XPath-like names
  119. // as 'person-group[@person-group-type="author"]'.
  120. $from = array(
  121. '[', ']', '@', '"', '='
  122. );
  123. $to = array(
  124. '-', '', '', '', '-'
  125. );
  126. $propertyId = trim(str_replace($from, $to, $this->getName()), '-');
  127. $propertyId = String::camelize($propertyId);
  128. return $propertyId;
  129. }
  130. /**
  131. * Get the translation id representing
  132. * the display name of the property.
  133. * @return string
  134. */
  135. function getDisplayName() {
  136. return $this->_displayName;
  137. }
  138. /**
  139. * Get the allowed association types
  140. * (resources that can be described
  141. * with this property)
  142. * @return array a list of integers representing
  143. * association types.
  144. */
  145. function &getAssocTypes() {
  146. return $this->_assocTypes;
  147. }
  148. /**
  149. * Get the allowed type
  150. * @return integer
  151. */
  152. function getTypes() {
  153. return $this->_types;
  154. }
  155. /**
  156. * Is this property translated?
  157. * @return boolean
  158. */
  159. function getTranslated() {
  160. return $this->_translated;
  161. }
  162. /**
  163. * Get the cardinality
  164. * @return integer
  165. */
  166. function getCardinality() {
  167. return $this->_cardinality;
  168. }
  169. //
  170. // Public methods
  171. //
  172. /**
  173. * Validate a given input against the property specification
  174. *
  175. * @param $value mixed the input to be validated
  176. * @return boolean validation success
  177. */
  178. function isValid($value) {
  179. // We never accept null values or arrays.
  180. if (is_null($value) || is_array($value)) return false;
  181. // The value must validate against at least one type
  182. $isValid = false;
  183. foreach ($this->getTypes() as $type) {
  184. // Extract data from composite type
  185. if (is_array($type)) {
  186. assert(count($type) == 1 && key($type) == METADATA_PROPERTY_TYPE_COMPOSITE);
  187. $compositeType = $type[METADATA_PROPERTY_TYPE_COMPOSITE];
  188. $type = METADATA_PROPERTY_TYPE_COMPOSITE;
  189. }
  190. // Type specific validation
  191. switch ($type) {
  192. case METADATA_PROPERTY_TYPE_STRING:
  193. if (is_string($value)) $isValid = true;
  194. break;
  195. case METADATA_PROPERTY_TYPE_VOCABULARY:
  196. // Interpret the name of this property as a controlled vocabulary triple
  197. $vocabNameParts = explode(':', $this->getName());
  198. assert(count($vocabNameParts) == 3);
  199. list($symbolic, $assocType, $assocId) = $vocabNameParts;
  200. // Validate with controlled vocabulary validator
  201. import('validation.ValidatorControlledVocab');
  202. $validator = new ValidatorControlledVocab($symbolic, $assocType, $assocId);
  203. if ($validator->isValid($value)) $isValid = true;
  204. break;
  205. case METADATA_PROPERTY_TYPE_URI:
  206. // Validate with the URI validator
  207. import('validation.ValidatorUri');
  208. $validator = new ValidatorUri();
  209. if ($validator->isValid($value)) $isValid = true;
  210. break;
  211. case METADATA_PROPERTY_TYPE_DATE:
  212. // We allow the following patterns:
  213. // YYYY-MM-DD, YYYY-MM and YYYY
  214. $datePattern = '/^[0-9]{4}(-[0-9]{2}(-[0-9]{2})?)?$/';
  215. if (!preg_match($datePattern, $value)) break;
  216. // Check whether the given string is really a valid date
  217. $dateParts = explode('-', $value);
  218. // Set the day and/or month to 1 if not set
  219. $dateParts = array_pad($dateParts, 3, 1);
  220. // Extract the date parts
  221. list($year, $month, $day) = $dateParts;
  222. // Validate the date (only leap days will pass unnoticed ;-) )
  223. // Who invented this argument order?
  224. if (checkdate($month, $day, $year)) $isValid = true;
  225. break;
  226. case METADATA_PROPERTY_TYPE_INTEGER:
  227. if (is_integer($value)) $isValid = true;
  228. break;
  229. case METADATA_PROPERTY_TYPE_COMPOSITE:
  230. // Composites can either be represented by a meta-data description
  231. // or by a string of the form AssocType:AssocId if the composite
  232. // has already been persisted in the database.
  233. switch(true) {
  234. // Test for MetadataDescription format
  235. case is_a($value, 'MetadataDescription'):
  236. $assocType = $value->getAssocType();
  237. break;
  238. // Test for AssocType:AssocId format
  239. case is_string($value):
  240. $valueParts = explode(':', $value);
  241. if (count($valueParts) != 2) break 2; // break the outer switch
  242. list($assocType, $assocId) = $valueParts;
  243. if (!(is_numeric($assocType) && is_numeric($assocId))) break 2; // break the outer switch
  244. $assocType = (integer)$assocType;
  245. break;
  246. default:
  247. // None of the allowed types
  248. break;
  249. }
  250. // Check that the association type matches
  251. if (isset($assocType) && $assocType === $compositeType) $isValid = true;
  252. break;
  253. default:
  254. // Unknown type. As we validate type in the setter, this
  255. // should be unreachable code.
  256. assert(false);
  257. }
  258. // The value only has to validate against one of the given
  259. // types: No need to validate against subsequent allowed types.
  260. if ($isValid) break;
  261. }
  262. // Will return false if the value didn't validate against any
  263. // of the types, otherwise true.
  264. return $isValid;
  265. }
  266. //
  267. // Public static methods
  268. //
  269. /**
  270. * Return supported meta-data property types
  271. * NB: PHP4 work-around for a public static class member
  272. * @return array supported meta-data property types
  273. */
  274. function getSupportedTypes() {
  275. static $_supportedTypes = array(
  276. METADATA_PROPERTY_TYPE_STRING,
  277. METADATA_PROPERTY_TYPE_DATE,
  278. METADATA_PROPERTY_TYPE_INTEGER,
  279. METADATA_PROPERTY_TYPE_VOCABULARY,
  280. METADATA_PROPERTY_TYPE_URI,
  281. METADATA_PROPERTY_TYPE_COMPOSITE
  282. );
  283. return $_supportedTypes;
  284. }
  285. /**
  286. * Return supported cardinalities
  287. * NB: PHP4 work-around for a public static class member
  288. * @return array supported cardinalities
  289. */
  290. function getSupportedCardinalities() {
  291. static $_supportedCardinalities = array(
  292. METADATA_PROPERTY_CARDINALITY_ONE,
  293. METADATA_PROPERTY_CARDINALITY_MANY
  294. );
  295. return $_supportedCardinalities;
  296. }
  297. }
  298. ?>