tao-test/app/taoQtiItem/model/qti/metadata/imsManifest/ImsManifestMetadataInjector.php

272 lines
9.4 KiB
PHP

<?php
/**
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; under version 2
* of the License (non-upgradable).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2014 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT);
*
*/
namespace oat\taoQtiItem\model\qti\metadata\imsManifest;
use \DOMDocument;
use \DOMElement;
use oat\taoQtiItem\model\qti\metadata\imsManifest\classificationMetadata\ClassificationMetadataValue;
use oat\taoQtiItem\model\qti\metadata\imsManifest\classificationMetadata\ClassificationValue;
use oat\taoQtiItem\model\qti\metadata\MetadataInjectionException;
use oat\taoQtiItem\model\qti\metadata\MetadataInjector;
use oat\taoQtiItem\model\qti\metadata\MetadataValue;
use \InvalidArgumentException;
/**
* A MetadataExtractor implementation.
*
* This implementation simply iterate through nodes and create an array of MetadataSimpleInstance object
*
* @author Antoine Robin <antoine.robin@vesperiagroup.com>
* @author Jérôme Bogaerts <jerome@taotesting.com>
*/
class ImsManifestMetadataInjector implements MetadataInjector
{
/**
* An array of IMSManifesMapping object.
*
* @var ImsManifestMapping[]
*/
private $mappings;
/**
* Create a new ImsManifestMetadataInjector object.
*
* @param ImsManifestMapping[] $mappings (optional) An array of ImsManifestMapping objects.
*/
public function __construct(array $mappings = [])
{
$this->setMappings($mappings);
}
/**
* Set the ImsManifestMapping objects of this injector.
*
* @param ImsManifestMapping[] $mappings An array of ImsManifestMapping objects.
* @throws InvalidArgumentException If $mappings contains objects/values different from ImsManifestMapping.
*/
protected function setMappings(array $mappings = [])
{
foreach ($mappings as $mapping) {
if (!$mapping instanceof ImsManifestMapping) {
$msg = "The mappings argument must be an array composed of ImsManifestMapping objects";
throw new InvalidArgumentException($msg);
}
}
$this->mappings = $mappings;
}
/**
* Get the registered ImsManifestMapping objects. If no mapping
* already registered, this method returns an empty array.
*
* @return ImsManifestMapping[] An array of ImsManifestMapping.
*/
public function getMappings()
{
return $this->mappings;
}
/**
* Add an XML mapping to this Manifest Extractor.
*
* If a mapping with an already registered XML namespace is given as
* a $mapping, it is simply ignored.
*
* @param ImsManifestMapping $mapping An XML mapping.
*/
public function addMapping(ImsManifestMapping $mapping)
{
$mappings = $this->getMappings();
$ns = $mapping->getNamespace();
if (isset($mappings[$ns]) === false) {
$mappings[$ns] = $mapping;
}
$this->setMappings($mappings);
}
/**
* Remove an already registered ImsManifestMapping.
*
* If $mapping cannot be found as a previously registered mapping, nothing happens.
*
* @param ImsManifestMapping $mapping An ImsManifestMapping object.
*/
public function removeMapping(ImsManifestMapping $mapping)
{
$mappings = $this->getMappings();
if (($key = array_search($mapping, $mappings, true)) !== false) {
unset($mappings[$key]);
}
}
/**
* Remove a previously registered ImsManifestMapping by its namespace.
*
* If no previously registered ImsManifestMapping object can be found
* for the given $namespace, nothing happens.
*
* @param string $namespace An XML namespace.
*/
public function removeMappingByNamespace($namespace)
{
$mappings = $this->getMappings();
if (isset($mappings[$namespace]) === true) {
unset($mappings[$namespace]);
}
}
/**
* Clear all the previously registered ImsManifestMapping objects
* from this injector.
*/
public function clearMappings()
{
$this->setMappings();
}
/**
* Inject some MetadataValue objects into the $target DOMElement object.
*
* The injection will take care of serializing the MetadataValue objects into the correct sections of the
* the IMS Manifest File, by looking at previously registered IMSManifestMapping objects.
*
* @throws MetadataInjectionException If $target is not a DOMDocument object or something goes wrong during the injection process.
*/
public function inject($target, array $values)
{
/** @var $target DOMDocument */
if (! $target instanceof DOMDocument) {
throw new MetadataInjectionException(__('The target must be an instance of DOMDocument'));
}
$map = [];
// Inject the mapping in the root node
foreach ($this->getMappings() as $mapping) {
/** @var $root DOMElement */
$root = $target->getElementsByTagName('manifest')->item(0);
$root->setAttribute('xmlns:' . $mapping->getPrefix(), $mapping->getNamespace());
$root->setAttribute(
'xsi:schemaLocation',
$root->getAttribute('xsi:schemaLocation') . ' ' . $mapping->getNamespace(
) . ' ' . $mapping->getSchemaLocation()
);
$map[$mapping->getNamespace()] = $mapping->getPrefix();
}
// Get all resource nodes
$resources = $target->getElementsByTagName('resource');
// Iterate through values to inject them in the DOMElement
foreach ($values as $identifier => $metadataValues) {
$metadataNode = null;
// Search the node that has the given identifier
/** @var $resource DOMElement */
foreach ($resources as $resource) {
if ($resource->getAttribute('identifier') === $identifier) {
// If metadata already exists we take it
if ($resource->getElementsByTagName('metadata')->length !== 0) {
$metadataNode = $resource->getElementsByTagName('metadata')->item(0);
} else {
$metadataNode = $target->createElement('metadata');
}
if ($resource->getElementsByTagName('file')->length !== 0) {
$fileNode = $resource->getElementsByTagName('file')->item(0);
$resource->insertBefore($metadataNode, $fileNode);
} else {
$resource->appendChild($metadataNode);
}
break;
}
}
if (is_null($metadataNode) || empty($map)) {
continue;
}
// Add the metadata values into the right path
/** @var $metadata MetaDataValue */
foreach ($metadataValues as $metadata) {
$this->createMetadataElement($metadata, $metadataNode, $map, $target);
}
}
}
/**
* Add an element based on MetadataValue object to DomDocument
*
* @param MetadataValue $metadata
* @param DOMElement $metadataNode
* @param $map
* @param DOMDocument $imsManifest
*/
protected function createMetadataElement(MetadataValue $metadata, DOMElement $metadataNode, $map, DOMDocument $imsManifest)
{
$path = $metadata->getPath();
$path = array_reverse($path);
$uniqNodes = [];
if ($metadata instanceof ClassificationValue) {
$uniqNodes = ['taxonPath', 'source'];
}
$oldChildNode = null;
foreach ($path as $index => $element) {
$name = substr($element, (strpos($element, '#') + 1));
$base = substr($element, 0, (strpos($element, '#')));
if (in_array($name, $uniqNodes) || is_null($oldChildNode) || $metadataNode->getElementsByTagName($map[$base] . ':' . $name)->length === 0) {
$node = $imsManifest->createElement($map[$base] . ':' . $name);
} else {
$node = $metadataNode->getElementsByTagName($map[$base] . ':' . $name)->item(0);
}
if ($name == 'string' || $name == 'langstring') {
$node->setAttribute('xml:lang', $metadata->getLanguage());
}
if (isset($oldChildNode)) {
$node->appendChild($oldChildNode);
if ($name == 'taxonPath' && $metadata instanceof ClassificationMetadataValue) {
foreach ($metadata->getEntries() as $entry) {
$this->createMetadataElement($entry, $node, $map, $imsManifest);
}
}
} else {
$node->nodeValue = $metadata->getValue();
}
$oldChildNode = $node;
}
$metadataNode->appendChild($oldChildNode);
}
}