src/main/php/Gomoob/MetadataExtractor/Metadata/Directory.php
<?php
/**
* gomoob/php-metadata-extractor
*
* @copyright Copyright (c) 2016, GOMOOB SARL (http://gomoob.com)
* @license http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE.md file)
*/
namespace Gomoob\MetadataExtractor\Metadata;
use Gomoob\Java\Lang\NullPointerException;
use Gomoob\MetadataExtractor\Lang\Rational;
/**
* Abstract base class for all directory implementations, having methods for getting and setting tag values of various
* data types.
*
* @author Baptiste GAILLARD (baptiste.gaillard@gomoob.com)
*/
abstract class Directory
{
/**
* The name of the directory.
*
* @var string
*/
private $name;
/**
* A convenient list holding tag values in the order in which they were stored. This is used for creation of an
* iterator, and for counting the number of defined tags.
*
* @var array
*/
protected $definedTagList = [];
protected $errorList = [];
/**
* The descriptor used to interpret tag values.
*
* @var \Gomoob\MetadatExtractor\Metadata\TagDescriptor
*/
protected $descriptor;
/**
* Map of values hashed by type identifiers.
*
* @var array
*/
protected $tagMap = [];
/**
* Indicates whether the specified tag type has been set.
*
* @param int tagType the tag type to check for.
*
* @return boolean true if a value exists for the specified tag type, false if not.
*/
public function containsTag($tagType)
{
return array_key_exists(intval($tagType), $this->tagMap);
}
/**
* Provides a description of a tag's value using the descriptor set by <code>setDescriptor(Descriptor)</code>.
*
* @param int tagType the tag type identifier
* @return the tag value's description as a String
*/
public function getDescription($tagType)
{
assert($this->descriptor !== null);
return $this->descriptor.getDescription($tagType);
}
/**
* Returns the specified tag's value as an int, if possible. Every attempt to represent the tag's value as an int
* is taken.
*
* Here is a list of the action taken depending upon the tag's original type:
* * int - Return unchanged.
* * Number - Return an int value (real numbers are truncated).
* * Rational - Truncate any fractional part and returns remaining int.
* * String - Attempt to parse string as an int. If this fails, convert the char[] to an int (using shifts and
* OR).
* * Rational[] - Return int value of first item in array.
* * byte[] - Return int value of first item in array.
* * int[] - Return int value of first item in array.
*
* @throws MetadataException if no value exists for tagType or if it cannot be converted to an int.
*/
public function getInt($tagType)
{
$integer = $this->getInteger($tagType);
if ($integer !== null) {
return $integer;
}
$o = $this->getObject($tagType);
if ($o === null) {
throw new MetadataException(
"Tag '" . $this->getTagName($tagType) . "' has not been set -- check using containsTag() first"
);
}
throw new MetadataException(
"Tag '" . $tagType . "' cannot be converted to int. It is of type '" . $o->getClass() . "'."
);
}
/**
* Returns the specified tag's value as an Integer, if possible. Every attempt to represent the tag's value as an
* Integer is taken.
*
* Here is a list of the action taken depending upon the tag's original type:
* * int - Return unchanged
* * Number - Return an int value (real numbers are truncated)
* * Rational - Truncate any fractional part and returns remaining int
* * String - Attempt to parse string as an int. If this fails, convert the char[] to an int (using shifts and OR)
* * Rational[] - Return int value of first item in array if length > 0
* * byte[] - Return int value of first item in array if length > 0
* * int[] - Return int value of first item in array if length > 0
*
* If the value is not found or cannot be converted to int, <code>null</code> is returned.
*/
public function getInteger($tagType)
{
// FIXME: This method has to be reviewed
$o = $this->getObject($tagType);
if ($o === null) {
return null;
}
if (is_int($o)) {
return $o;
} elseif (is_string($o)) {
return intval($o);
} elseif (is_array($o)) {
if (count($o) === 1) {
return intval($o[0]);
}
}
return null;
}
/**
* Provides the name of the directory, for display purposes. E.g. <code>Exif</code>
*
* @return string the name of the directory
*/
public function getName()
{
return $this->name;
}
/**
* Returns the object hashed for the particular tag type specified, if available.
*
* @param tagType the tag type identifier
* @return the tag's value as an Object if available, else <code>null</code>
*/
public function getObject($tagType)
{
return $this->tagMap[intval($tagType)];
}
/**
* Returns the specified tag's value as a Rational. If the value is unset or cannot be converted,
* <code>null</code> is returned.
*/
public function getRational($tagType)
{
$o = $this->getObject($tagType);
if ($o == null) {
return null;
}
if ($o instanceof Rational) {
return $o;
}
if (is_int($o)) {
return new Rational($o, 1);
}
// NOTE not doing conversions for real number types
return null;
}
/**
* Returns the number of tags set in this Directory.
*
* @return int the number of tags set in this Directory
*/
public function getTagCount()
{
return count($this->definedTagList);
}
/**
* Returns the name of a specified tag as a String.
*
* @param int tagType the tag type identifier.
*
* @return string the tag's name as a String.
*/
public function getTagName($tagType)
{
$nameMap = $this->getTagNameMap();
if (!array_key_exists($tagType, $nameMap)) {
$hex = dechex($tagType);
while (strlen($hex) < 4) {
$hex = '0' . $hex;
}
return 'Unknown tag (0x' . $hex . ')';
}
return $nameMap[$tagType];
}
/**
* Returns an Iterator of Tag instances that have been set in this Directory.
*
* @return an Iterator of Tag instances
*/
public function getTags()
{
return $this->definedTagList;
}
/**
* Gets a value indicating whether the directory is empty, meaning it contains no errors and no tag values.
*
* @return boolean `true` if this directory is empty, `false` otherwise.
*/
public function isEmpty()
{
return empty($this->errorList) && empty($this->definedTagList);
}
/**
* Sets the descriptor used to interpret tag values.
*
* @param descriptor the descriptor used to interpret tag values
*/
public function setDescriptor(TagDescriptor $descriptor)
{
if ($descriptor === null) {
throw new NullPointerException('cannot set a null descriptor');
}
$this->descriptor = $descriptor;
}
/**
* Sets an <code>int</code> value for the specified tag.
*
* @param int tagType the tag's value as an int.
* @param int value the value for the specified tag as an int.
*/
public function setInt($tagType, $value)
{
$this->setObject($tagType, $value);
}
/**
* Sets a <code>Object</code> for the specified tag.
*
* @param int tagType the tag's value as an int.
* @param mixed value the value for the specified tag.
*
* @throws NullPointerException if value is <code>null</code>
*/
public function setObject($tagType, $value)
{
if ($value === null) {
throw new NullPointerException('cannot set a null object');
}
if (!array_key_exists($tagType, $this->tagMap)) {
$this->definedTagList[$tagType] = new Tag($tagType, $this);
}
$this->tagMap[$tagType] = $value;
}
/**
* Sets a <code>Rational</code> value for the specified tag.
*
* @param tagType the tag's value as an int
* @param rational rational number
*/
public function setRational($tagType, Rational $rational)
{
$this->setObject($tagType, $rational);
}
/**
* Provides the map of tag names, hashed by tag type identifier.
*
* @return the map of tag names
*/
abstract protected function getTagNameMap();
}