File: /var/www/ojs/lib/pkp/classes/metadata/MetadataDataObjectAdapter.inc.php
<?php
/**
* @file classes/metadata/MetadataDataObjectAdapter.inc.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class MetadataDataObjectAdapter
* @ingroup metadata
* @see DataObject
* @see MetadataSchema
* @see MetadataDescription
*
* @brief Class that injects/extracts a meta-data description
* into/from an application entity object (DataObject).
*
* These adapters have to be persistable as they'll be provided
* by plug-ins via the filter registry.
*/
import('lib.pkp.classes.filter.PersistableFilter');
import('lib.pkp.classes.metadata.MetadataDescription');
define('METADATA_DOA_INJECTION_MODE', 0x01);
define('METADATA_DOA_EXTRACTION_MODE', 0x02);
class MetadataDataObjectAdapter extends PersistableFilter {
/** @var integer */
var $_mode;
/** @var MetadataSchema */
var $_metadataSchema;
/** @var string */
var $_dataObjectClass;
/** @var array */
var $_metadataFieldNames;
/** @var string */
var $_metadataSchemaName;
/** @var integer */
var $_assocType;
/** @var string */
var $_dataObjectName;
/**
* Constructor
* @param $filterGroup FilterGroup
*/
function __construct($filterGroup, $mode = null) {
// Initialize the adapter.
parent::__construct($filterGroup);
// Extract information from the input/output types.
// Find out whether this filter is injecting or
// extracting meta-data.
$metadataTypeDescription = null; /* @var $metadataTypeDescription MetadataTypeDescription */
$dataObjectTypeDescription = null; /* @var $dataObjectTypeDescription ClassTypeDescription */
$inputType =& $this->getInputType();
$outputType =& $this->getOutputType();
if (is_null($mode)) {
if (is_a($inputType, 'MetadataTypeDescription')) {
$mode = METADATA_DOA_INJECTION_MODE;
} else {
$mode = METADATA_DOA_EXTRACTION_MODE;
}
}
$this->_mode = $mode;
if ($mode == METADATA_DOA_INJECTION_MODE) {
// We are in meta-data injection mode (or both input and output are meta-data descriptions).
$metadataTypeDescription =& $inputType; /* @var $metadataTypeDescription MetadataTypeDescription */
assert(is_a($outputType, 'ClassTypeDescription'));
$dataObjectTypeDescription =& $outputType; /* @var $dataObjectTypeDescription ClassTypeDescription */
} else {
// We are in meta-data extraction mode.
assert(is_a($outputType, 'MetadataTypeDescription'));
$metadataTypeDescription =& $outputType;
assert(is_a($inputType, 'ClassTypeDescription'));
$dataObjectTypeDescription =& $inputType;
}
// Extract information from the input/output types.
$this->_metadataSchemaName = $metadataTypeDescription->getMetadataSchemaClass();
$this->_assocType = $metadataTypeDescription->getAssocType();
$this->_dataObjectName = $dataObjectTypeDescription->getTypeName();
// Set the display name.
if ($mode == METADATA_DOA_INJECTION_MODE) {
$this->setDisplayName('Inject metadata into a(n) '.$this->getDataObjectClass());
} else {
$this->setDisplayName('Extract metadata from a(n) '.$this->getDataObjectClass());
}
}
//
// Getters and setters
//
/**
* One of the METADATA_DOA_*_MODE constants.
* @return integer
*/
function getMode() {
return $this->_mode;
}
/**
* Get the fully qualified class name of
* the supported meta-data schema.
* @return string
*/
function getMetadataSchemaName() {
return $this->_metadataSchemaName;
}
/**
* Get the supported meta-data schema (lazy load)
* @return MetadataSchema
*/
function &getMetadataSchema() {
// Lazy-load the meta-data schema if this has
// not been done before.
if (is_null($this->_metadataSchema)) {
$metadataSchemaName = $this->getMetadataSchemaName();
assert(!is_null($metadataSchemaName));
$this->_metadataSchema =& instantiate($metadataSchemaName, 'MetadataSchema');
assert(is_object($this->_metadataSchema));
}
return $this->_metadataSchema;
}
/**
* Convenience method that returns the
* meta-data name space.
* @return string
*/
function getMetadataNamespace() {
$metadataSchema =& $this->getMetadataSchema();
return $metadataSchema->getNamespace();
}
/**
* Get the supported application entity (class) name
* @return string
*/
function getDataObjectName() {
return $this->_dataObjectName;
}
/**
* Return the data object class name
* (without the package prefix)
*
* @return string
*/
function getDataObjectClass() {
if (is_null($this->_dataObjectClass)) {
$dataObjectName = $this->getDataObjectName();
assert(!is_null($dataObjectName));
$dataObjectNameParts = explode('.', $dataObjectName);
$this->_dataObjectClass = array_pop($dataObjectNameParts);
}
return $this->_dataObjectClass;
}
/**
* Get the association type corresponding to the data
* object type.
* @return integer
*/
function getAssocType() {
return $this->_assocType;
}
/**
* Set the target data object for meta-data injection.
* @param $targetDataObject DataObject
*/
function setTargetDataObject(&$targetDataObject) {
$this->_targetDataObject =& $targetDataObject;
}
/**
* Get the target data object for meta-data injection.
* @param $targetDataObject DataObject
*/
function &getTargetDataObject() {
return $this->_targetDataObject;
}
//
// Abstract template methods
//
/**
* Inject a MetadataDescription into the target DataObject
* @param $metadataDescription MetadataDescription
* @param $targetDataObject DataObject
* @return DataObject
*/
function &injectMetadataIntoDataObject(&$metadataDescription, &$targetDataObject) {
// Must be implemented by sub-classes
assert(false);
}
/**
* Extract a MetadataDescription from a source DataObject.
* @param $sourceDataObject DataObject
* @return MetadataDescription
*/
function extractMetadataFromDataObject(&$sourceDataObject) {
// Must be implemented by sub-classes
assert(false);
}
/**
* Return the additional field names introduced by the
* meta-data schema that need to be persisted in the
* ..._settings table corresponding to the DataObject
* which is supported by this adapter.
* NB: The field names must be prefixed with the meta-data
* schema namespace identifier.
* @param $translated boolean if true, return localized field
* names, otherwise return additional field names.
* @return array an array of field names to be persisted.
*/
function getDataObjectMetadataFieldNames($translated = true) {
// By default return all field names
return $this->getMetadataFieldNames($translated);
}
//
// Implement template methods from Filter
//
/**
* Convert a MetadataDescription to an application
* object or vice versa.
* @see Filter::process()
* @param $input mixed either a MetadataDescription or an application object
* @return mixed either a MetadataDescription or an application object
*/
function &process(&$input) {
// Do we inject or extract metadata?
switch ($this->getMode()) {
case METADATA_DOA_INJECTION_MODE:
$targetDataObject =& $this->getTargetDataObject();
// Instantiate a new data object if none was given.
if (is_null($targetDataObject)) {
$targetDataObject =& $this->instantiateDataObject();
assert(is_a($targetDataObject, $this->getDataObjectName()));
}
// Inject meta-data into the data object.
$output =& $this->injectMetadataIntoDataObject($input, $targetDataObject);
break;
case METADATA_DOA_EXTRACTION_MODE:
$output = $this->extractMetadataFromDataObject($input);
break;
default:
// Input should be validated by now.
assert(false);
}
return $output;
}
//
// Protected helper methods
//
/**
* Instantiate a new data object of the
* correct type.
*
* NB: This can be overridden by sub-classes for more complex
* data objects. The standard implementation assumes there are
* no constructor args to be set or configurations to be made.
*
* @return DataObject
*/
function &instantiateDataObject() {
$dataObjectName = $this->getDataObjectName();
assert(!is_null($dataObjectName));
$dataObject =& instantiate($dataObjectName, $this->getDataObjectClass());
return $dataObject;
}
/**
* Instantiate a meta-data description that conforms to the
* settings of this adapter.
* @return MetadataDescription
*/
function &instantiateMetadataDescription() {
$metadataDescription = new MetadataDescription($this->getMetadataSchemaName(), $this->getAssocType());
return $metadataDescription;
}
/**
* Return all field names introduced by the
* meta-data schema that might have to be persisted.
* @param $translated boolean if true, return localized field
* names, otherwise return additional field names.
* @return array an array of field names to be persisted.
*/
function getMetadataFieldNames($translated = true) {
// Do we need to build the field name cache first?
if (is_null($this->_metadataFieldNames)) {
// Initialize the cache array
$this->_metadataFieldNames = array();
// Retrieve all properties and add
// their names to the cache
$metadataSchema =& $this->getMetadataSchema();
$metadataSchemaNamespace = $metadataSchema->getNamespace();
$properties =& $metadataSchema->getProperties();
foreach($properties as $property) {
$propertyAssocTypes = $property->getAssocTypes();
if (in_array($this->_assocType, $propertyAssocTypes)) {
// Separate translated and non-translated property names
// and add the name space so that field names are unique
// across various meta-data schemas.
$this->_metadataFieldNames[$property->getTranslated()][] = $metadataSchemaNamespace.':'.$property->getName();
}
}
}
// Return the field names
return $this->_metadataFieldNames[$translated];
}
/**
* Set several localized statements in a meta-data schema.
* @param $metadataDescription MetadataDescription
* @param $propertyName string
* @param $localizedValues array (keys: locale, values: localized values)
*/
function addLocalizedStatements(&$metadataDescription, $propertyName, $localizedValues) {
if (is_array($localizedValues)) {
foreach ($localizedValues as $locale => $values) {
// Handle cardinality "many" and "one" in the same way.
if (is_scalar($values)) $values = array($values);
foreach($values as $value) {
$metadataDescription->addStatement($propertyName, $value, $locale);
unset($value);
}
}
}
}
/**
* Directly inject all fields that are not mapped to the
* data object into the data object's data array for
* automatic persistence by the meta-data framework.
* @param $metadataDescription MetadataDescription
* @param $dataObject DataObject
*/
function injectUnmappedDataObjectMetadataFields(&$metadataDescription, &$dataObject) {
// Handle translated and non-translated statements separately.
foreach(array(true, false) as $translated) {
// Retrieve the unmapped fields.
foreach($this->getDataObjectMetadataFieldNames($translated) as $unmappedProperty) {
// Identify the corresponding property name.
list($namespace, $propertyName) = explode(':', $unmappedProperty);
// Find out whether we have a statement for this unmapped property.
if ($metadataDescription->hasStatement($propertyName)) {
// Add the unmapped statement directly to the
// data object.
if ($translated) {
$dataObject->setData($unmappedProperty, $metadataDescription->getStatementTranslations($propertyName));
} else {
$dataObject->setData($unmappedProperty, $metadataDescription->getStatement($propertyName));
}
}
}
}
}
/**
* Directly extract all fields that are not mapped to the
* data object from the data object's data array.
* @param $dataObject DataObject
* @param $metadataDescription MetadataDescription
*/
function extractUnmappedDataObjectMetadataFields(&$dataObject, &$metadataDescription) {
$metadataSchema =& $this->getMetadataSchema();
$handledNamespace = $metadataSchema->getNamespace();
// Handle translated and non-translated statements separately.
foreach(array(true, false) as $translated) {
// Retrieve the unmapped fields.
foreach($this->getDataObjectMetadataFieldNames($translated) as $unmappedProperty) {
// Find out whether we have a statement for this unmapped property.
if ($dataObject->hasData($unmappedProperty)) {
// Identify the corresponding property name and namespace.
list($namespace, $propertyName) = explode(':', $unmappedProperty);
// Only extract data if the namespace of the property
// is the same as the one handled by this adapter and the
// property is within the current description.
if ($namespace == $handledNamespace && $metadataSchema->hasProperty($propertyName)) {
// Add the unmapped statement to the metadata description.
if ($translated) {
$this->addLocalizedStatements($metadataDescription, $propertyName, $dataObject->getData($unmappedProperty));
} else {
$metadataDescription->addStatement($propertyName, $dataObject->getData($unmappedProperty));
}
}
}
}
}
}
}