<?php
/*
** Zabbix
** Copyright (C) 2001-2021 Zabbix SIA
**
** 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; either version 2 of the License, or
** (at your option) any later version.
**
** 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.
**/


/**
 * Validate import data from Zabbix 1.8.
 */
class C10XmlValidator {

	/**
	 * @var string
	 */
	private $format;

	/**
	 * @param string $format format of import source
	 */
	public function __construct($format) {
		$this->format = $format;
	}

	/**
	 * Base validation function.
	 *
	 * @param array  $data	import data
	 * @param string $path	XML path (for error reporting)
	 *
	 * @return array		Validator does some manipulation for the incoming data. For example, converts empty tags to
	 *						an array, if desired. Converted array is returned.
	 */
	public function validate(array $data, $path) {
		$rules = ['type' => XML_ARRAY, 'rules' => [
			'version' =>				['type' => XML_STRING | XML_REQUIRED],
			'date' =>					['type' => XML_STRING, 'ex_validate' => [$this, 'validateDate']],
			'time' =>					['type' => XML_STRING, 'ex_validate' => [$this, 'validateTime']],
			'hosts' =>					['type' => XML_INDEXED_ARRAY, 'prefix' => 'host', 'rules' => [
				'host' =>					['type' => XML_ARRAY, 'rules' => [
					'name' =>					['type' => XML_STRING | XML_REQUIRED],
					'proxy_hostid' =>			['type' => XML_STRING],
					'useip' =>					['type' => XML_STRING | XML_REQUIRED],
					'dns' =>					['type' => XML_STRING | XML_REQUIRED],
					'ip' =>						['type' => XML_STRING | XML_REQUIRED],
					'port' =>					['type' => XML_STRING | XML_REQUIRED],
					'status' =>					['type' => XML_STRING | XML_REQUIRED],
					'useipmi' =>				['type' => XML_STRING],
					'ipmi_ip' =>				['type' => XML_STRING],
					'ipmi_port' =>				['type' => XML_STRING],
					'ipmi_authtype' =>			['type' => XML_STRING],
					'ipmi_privilege' =>			['type' => XML_STRING],
					'ipmi_username' =>			['type' => XML_STRING],
					'ipmi_password' =>			['type' => XML_STRING],
					'groups' =>					['type' => XML_INDEXED_ARRAY | XML_REQUIRED, 'prefix' => 'group', 'rules' => [
						'group' =>					['type' => XML_STRING]
					]],
					'items' =>					['type' => XML_INDEXED_ARRAY, 'prefix' => 'item', 'rules' => [
						'item' =>					['type' => XML_ARRAY, 'rules' => [
							'type' =>					['type' => XML_STRING | XML_REQUIRED],
							'key' =>					['type' => XML_STRING | XML_REQUIRED],
							'value_type' =>				['type' => XML_STRING | XML_REQUIRED],
							'description' =>			['type' => XML_STRING | XML_REQUIRED],
							'ipmi_sensor' =>			['type' => XML_STRING | XML_REQUIRED],
							'delay' =>					['type' => XML_STRING | XML_REQUIRED],
							'history' =>				['type' => XML_STRING | XML_REQUIRED],
							'trends' =>					['type' => XML_STRING | XML_REQUIRED],
							'status' =>					['type' => XML_STRING | XML_REQUIRED],
							'data_type' =>				['type' => XML_STRING | XML_REQUIRED],
							'units' =>					['type' => XML_STRING | XML_REQUIRED],
							'multiplier' =>				['type' => XML_STRING | XML_REQUIRED],
							'delta' =>					['type' => XML_STRING | XML_REQUIRED],
							'formula' =>				['type' => XML_STRING | XML_REQUIRED],
							'lastlogsize' =>			['type' => XML_STRING | XML_REQUIRED],
							'logtimefmt' =>				['type' => XML_STRING | XML_REQUIRED],
							'delay_flex' =>				['type' => XML_STRING | XML_REQUIRED],
							'authtype' =>				['type' => XML_STRING | XML_REQUIRED],
							'username' =>				['type' => XML_STRING | XML_REQUIRED],
							'password' =>				['type' => XML_STRING | XML_REQUIRED],
							'publickey' =>				['type' => XML_STRING | XML_REQUIRED],
							'privatekey' =>				['type' => XML_STRING | XML_REQUIRED],
							'params' =>					['type' => XML_STRING | XML_REQUIRED],
							'trapper_hosts' =>			['type' => XML_STRING | XML_REQUIRED],
							'snmp_community' =>			['type' => XML_STRING | XML_REQUIRED],
							'snmp_oid' =>				['type' => XML_STRING | XML_REQUIRED],
							'snmp_port' =>				['type' => XML_STRING | XML_REQUIRED],
							'snmpv3_securityname' =>	['type' => XML_STRING | XML_REQUIRED],
							'snmpv3_securitylevel' =>	['type' => XML_STRING | XML_REQUIRED],
							'snmpv3_authpassphrase' =>	['type' => XML_STRING | XML_REQUIRED],
							'snmpv3_privpassphrase' =>	['type' => XML_STRING | XML_REQUIRED],
							'valuemapid' =>				['type' => XML_STRING],
							'applications' =>			['type' => XML_INDEXED_ARRAY | XML_REQUIRED, 'prefix' => 'application', 'rules' => [
								'application' =>			['type' => XML_STRING]
							]]
						]]
					]],
					'triggers' =>				['type' => XML_INDEXED_ARRAY, 'prefix' => 'trigger', 'rules' => [
						'trigger' =>				['type' => XML_ARRAY, 'rules' => [
							'description' =>			['type' => XML_STRING | XML_REQUIRED],
							'type' =>					['type' => XML_STRING | XML_REQUIRED],
							'expression' =>				['type' => XML_STRING | XML_REQUIRED],
							'url' =>					['type' => XML_STRING | XML_REQUIRED],
							'status' =>					['type' => XML_STRING | XML_REQUIRED],
							'priority' =>				['type' => XML_STRING | XML_REQUIRED],
							'comments' =>				['type' => XML_STRING | XML_REQUIRED]
						]]
					]],
					'templates' =>				['type' => XML_INDEXED_ARRAY, 'prefix' => 'template', 'rules' => [
						'template' =>				['type' => XML_STRING]
					]],
					'graphs' =>					['type' => XML_INDEXED_ARRAY, 'prefix' => 'graph', 'rules' => [
						'graph' =>					['type' => XML_ARRAY, 'rules' => [
							'name' =>					['type' => XML_STRING | XML_REQUIRED],
							'width' =>					['type' => XML_STRING | XML_REQUIRED],
							'height' =>					['type' => XML_STRING | XML_REQUIRED],
							// The tag 'ymin_type' should be validated before the 'ymin_item_key' because it is used in 'ex_validate' method.
							'ymin_type' =>				['type' => XML_STRING | XML_REQUIRED],
							'ymin_item_key' =>			['type' => XML_STRING | XML_REQUIRED, 'ex_validate' => [$this, 'validateYMinItem']],
							// The tag 'ymax_type' should be validated before the 'ymax_item_key' because it is used in 'ex_validate' method.
							'ymax_type' =>				['type' => XML_STRING | XML_REQUIRED],
							'ymax_item_key' =>			['type' => XML_STRING | XML_REQUIRED, 'ex_validate' => [$this, 'validateYMaxItem']],
							'show_work_period' =>		['type' => XML_STRING | XML_REQUIRED],
							'show_triggers' =>			['type' => XML_STRING | XML_REQUIRED],
							'graphtype' =>				['type' => XML_STRING | XML_REQUIRED],
							'yaxismin' =>				['type' => XML_STRING | XML_REQUIRED],
							'yaxismax' =>				['type' => XML_STRING | XML_REQUIRED],
							'show_legend' =>			['type' => XML_STRING | XML_REQUIRED],
							'show_3d' =>				['type' => XML_STRING | XML_REQUIRED],
							'percent_left' =>			['type' => XML_STRING | XML_REQUIRED],
							'percent_right' =>			['type' => XML_STRING | XML_REQUIRED],
							'graph_elements' =>			['type' => XML_INDEXED_ARRAY | XML_REQUIRED, 'prefix' => 'graph_element', 'rules' => [
								'graph_element' =>			['type' => XML_ARRAY, 'rules' => [
									'item' =>					['type' => XML_STRING | XML_REQUIRED, 'ex_validate' => [$this, 'validateGraphItem']],
									'drawtype' =>				['type' => XML_STRING | XML_REQUIRED],
									'sortorder' =>				['type' => XML_STRING | XML_REQUIRED],
									'color' =>					['type' => XML_STRING | XML_REQUIRED],
									'yaxisside' =>				['type' => XML_STRING | XML_REQUIRED],
									'calc_fnc' =>				['type' => XML_STRING | XML_REQUIRED],
									'type' =>					['type' => XML_STRING | XML_REQUIRED],
									'periods_cnt' =>			['type' => XML_STRING | XML_REQUIRED]
								]]
							]]
						]]
					]],
					'macros' =>					['type' => XML_INDEXED_ARRAY, 'prefix' => 'macro', 'rules' => [
						'macro' =>					['type' => XML_ARRAY, 'rules' => [
							'value' =>					['type' => XML_STRING | XML_REQUIRED],
							'name' =>					['type' => XML_STRING | XML_REQUIRED]
						]]
					]],
					'host_profile' =>			['type' => XML_ARRAY, 'rules' => [
						'devicetype' =>				['type' => XML_STRING | XML_REQUIRED],
						'name' =>					['type' => XML_STRING | XML_REQUIRED],
						'os' =>						['type' => XML_STRING | XML_REQUIRED],
						'serialno' =>				['type' => XML_STRING | XML_REQUIRED],
						'tag' =>					['type' => XML_STRING | XML_REQUIRED],
						'macaddress' =>				['type' => XML_STRING | XML_REQUIRED],
						'hardware' =>				['type' => XML_STRING | XML_REQUIRED],
						'software' =>				['type' => XML_STRING | XML_REQUIRED],
						'contact' =>				['type' => XML_STRING | XML_REQUIRED],
						'location' =>				['type' => XML_STRING | XML_REQUIRED],
						'notes' =>					['type' => XML_STRING | XML_REQUIRED]
					]],
					'host_profiles_ext' =>		['type' => XML_ARRAY, 'rules' => [
						'device_alias' =>			['type' => XML_STRING | XML_REQUIRED],
						'device_type' =>			['type' => XML_STRING | XML_REQUIRED],
						'device_chassis' =>			['type' => XML_STRING | XML_REQUIRED],
						'device_os' =>				['type' => XML_STRING | XML_REQUIRED],
						'device_os_short' =>		['type' => XML_STRING | XML_REQUIRED],
						'device_hw_arch' =>			['type' => XML_STRING | XML_REQUIRED],
						'device_serial' =>			['type' => XML_STRING | XML_REQUIRED],
						'device_model' =>			['type' => XML_STRING | XML_REQUIRED],
						'device_tag' =>				['type' => XML_STRING | XML_REQUIRED],
						'device_vendor' =>			['type' => XML_STRING | XML_REQUIRED],
						'device_contract' =>		['type' => XML_STRING | XML_REQUIRED],
						'device_who' =>				['type' => XML_STRING | XML_REQUIRED],
						'device_status' =>			['type' => XML_STRING | XML_REQUIRED],
						'device_app_01' =>			['type' => XML_STRING | XML_REQUIRED],
						'device_app_02' =>			['type' => XML_STRING | XML_REQUIRED],
						'device_app_03' =>			['type' => XML_STRING | XML_REQUIRED],
						'device_app_04' =>			['type' => XML_STRING | XML_REQUIRED],
						'device_app_05' =>			['type' => XML_STRING | XML_REQUIRED],
						'device_url_1' =>			['type' => XML_STRING | XML_REQUIRED],
						'device_url_2' =>			['type' => XML_STRING | XML_REQUIRED],
						'device_url_3' =>			['type' => XML_STRING | XML_REQUIRED],
						'device_networks' =>		['type' => XML_STRING | XML_REQUIRED],
						'device_notes' =>			['type' => XML_STRING | XML_REQUIRED],
						'device_hardware' =>		['type' => XML_STRING | XML_REQUIRED],
						'device_software' =>		['type' => XML_STRING | XML_REQUIRED],
						'ip_subnet_mask' =>			['type' => XML_STRING | XML_REQUIRED],
						'ip_router' =>				['type' => XML_STRING | XML_REQUIRED],
						'ip_macaddress' =>			['type' => XML_STRING | XML_REQUIRED],
						'oob_ip' =>					['type' => XML_STRING | XML_REQUIRED],
						'oob_subnet_mask' =>		['type' => XML_STRING | XML_REQUIRED],
						'oob_router' =>				['type' => XML_STRING | XML_REQUIRED],
						'date_hw_buy' =>			['type' => XML_STRING | XML_REQUIRED],
						'date_hw_install' =>		['type' => XML_STRING | XML_REQUIRED],
						'date_hw_expiry' =>			['type' => XML_STRING | XML_REQUIRED],
						'date_hw_decomm' =>			['type' => XML_STRING | XML_REQUIRED],
						'site_street_1' =>			['type' => XML_STRING | XML_REQUIRED],
						'site_street_2' =>			['type' => XML_STRING | XML_REQUIRED],
						'site_street_3' =>			['type' => XML_STRING | XML_REQUIRED],
						'site_city' =>				['type' => XML_STRING | XML_REQUIRED],
						'site_state' =>				['type' => XML_STRING | XML_REQUIRED],
						'site_country' =>			['type' => XML_STRING | XML_REQUIRED],
						'site_zip' =>				['type' => XML_STRING | XML_REQUIRED],
						'site_rack' =>				['type' => XML_STRING | XML_REQUIRED],
						'site_notes' =>				['type' => XML_STRING | XML_REQUIRED],
						'poc_1_name' =>				['type' => XML_STRING | XML_REQUIRED],
						'poc_1_email' =>			['type' => XML_STRING | XML_REQUIRED],
						'poc_1_phone_1' =>			['type' => XML_STRING | XML_REQUIRED],
						'poc_1_phone_2' =>			['type' => XML_STRING | XML_REQUIRED],
						'poc_1_cell' =>				['type' => XML_STRING | XML_REQUIRED],
						'poc_1_screen' =>			['type' => XML_STRING | XML_REQUIRED],
						'poc_1_notes' =>			['type' => XML_STRING | XML_REQUIRED],
						'poc_2_name' =>				['type' => XML_STRING | XML_REQUIRED],
						'poc_2_email' =>			['type' => XML_STRING | XML_REQUIRED],
						'poc_2_phone_1' =>			['type' => XML_STRING | XML_REQUIRED],
						'poc_2_phone_2' =>			['type' => XML_STRING | XML_REQUIRED],
						'poc_2_cell' =>				['type' => XML_STRING | XML_REQUIRED],
						'poc_2_screen' =>			['type' => XML_STRING | XML_REQUIRED],
						'poc_2_notes' =>			['type' => XML_STRING | XML_REQUIRED]
					]]
				]]
			]],
			'dependencies' =>			['type' => XML_INDEXED_ARRAY, 'prefix' => 'dependency', 'rules' => [
				'dependency' =>				['type' => XML_INDEXED_ARRAY, 'prefix' => 'depends', 'extra' => 'description', 'rules' => [
					'depends' =>				['type' => XML_STRING],
					'description' =>			['type' => XML_STRING | XML_REQUIRED]
				]]
			]],
			'sysmaps' =>				['type' => XML_INDEXED_ARRAY, 'prefix' => 'sysmap', 'rules' => [
				'sysmap' =>					['type' => XML_ARRAY, 'rules' => [
					'selements' =>				['type' => XML_INDEXED_ARRAY | XML_REQUIRED, 'prefix' => 'selement', 'rules' => [
						'selement' =>				['type' => XML_ARRAY, 'rules' => [
							'selementid' =>				['type' => XML_STRING | XML_REQUIRED],
							// The tag 'elementtype' should be validated before the 'elementid' because it is used in 'ex_required' and 'ex_validate' methods.
							'elementtype' =>			['type' => XML_STRING | XML_REQUIRED],
							'elementid' =>				['type' => 0, 'ex_required' => [$this, 'requiredMapElement'], 'ex_validate' => [$this, 'validateMapElement']],
							'iconid_on' =>				['type' => XML_ARRAY, 'rules' => [
								'name' =>					['type' => XML_STRING | XML_REQUIRED]
							]],
							'iconid_off' =>				['type' => XML_ARRAY | XML_REQUIRED, 'rules' => [
								'name' =>					['type' => XML_STRING | XML_REQUIRED]
							]],
							'iconid_unknown' =>			['type' => XML_ARRAY, 'rules' => [
								'name' =>					['type' => XML_STRING | XML_REQUIRED]
							]],
							'iconid_disabled' =>		['type' => XML_ARRAY, 'rules' => [
								'name' =>					['type' => XML_STRING | XML_REQUIRED]
							]],
							'iconid_maintenance' =>		['type' => XML_ARRAY, 'rules' => [
								'name' =>					['type' => XML_STRING | XML_REQUIRED]
							]],
							'label' =>					['type' => XML_STRING | XML_REQUIRED],
							'label_location' =>			['type' => XML_STRING],
							'x' =>						['type' => XML_STRING | XML_REQUIRED],
							'y' =>						['type' => XML_STRING | XML_REQUIRED],
							'url' =>					['type' => XML_STRING]
						]]
					]],
					'links' =>					['type' => XML_INDEXED_ARRAY | XML_REQUIRED, 'prefix' => 'link', 'rules' => [
						'link' =>					['type' => XML_ARRAY, 'rules' => [
							'selementid1' =>			['type' => XML_STRING | XML_REQUIRED],
							'selementid2' =>			['type' => XML_STRING | XML_REQUIRED],
							'drawtype' =>				['type' => XML_STRING | XML_REQUIRED],
							'color' =>					['type' => XML_STRING | XML_REQUIRED],
							'label' =>					['type' => XML_STRING],
							'linktriggers' =>			['type' => XML_INDEXED_ARRAY, 'prefix' => 'linktrigger', 'rules' => [
								'linktrigger' =>			['type' => XML_ARRAY, 'rules' => [
									'drawtype' =>				['type' => XML_STRING | XML_REQUIRED],
									'color' =>					['type' => XML_STRING | XML_REQUIRED],
									'triggerid' =>				['type' => XML_ARRAY | XML_REQUIRED, 'rules' => [
										'host' =>					['type' => XML_STRING],
										'description' =>			['type' => XML_STRING | XML_REQUIRED],
										'expression' =>				['type' => XML_STRING | XML_REQUIRED]
									]]
								]]
							]]
						]]
					]],
					'name' =>					['type' => XML_STRING | XML_REQUIRED],
					'width' =>					['type' => XML_STRING | XML_REQUIRED],
					'height' =>					['type' => XML_STRING | XML_REQUIRED],
					'backgroundid' =>			['type' => XML_ARRAY, 'rules' => [
						'name' =>					['type' => XML_STRING | XML_REQUIRED]
					]],
					'label_type' =>				['type' => XML_STRING | XML_REQUIRED],
					'label_location' =>			['type' => XML_STRING | XML_REQUIRED],
					'highlight' =>				['type' => XML_STRING | XML_REQUIRED],
					'expandproblem' =>			['type' => XML_STRING | XML_REQUIRED],
					'markelements' =>			['type' => XML_STRING | XML_REQUIRED],
					'show_unack' =>				['type' => XML_STRING | XML_REQUIRED]
				]]
			]],
			'screens' =>				['type' => XML_INDEXED_ARRAY, 'prefix' => 'screen', 'rules' => [
				'screen' =>					['type' => XML_ARRAY, 'rules' => [
					'name' =>					['type' => XML_STRING | XML_REQUIRED],
					'hsize' =>					['type' => XML_STRING | XML_REQUIRED],
					'vsize' =>					['type' => XML_STRING | XML_REQUIRED],
					'screenitems' =>			['type' => XML_INDEXED_ARRAY | XML_REQUIRED, 'prefix' => 'screenitem', 'rules' => [
						'screenitem' =>				['type' => XML_ARRAY, 'rules' => [
							'resourcetype' =>			['type' => XML_STRING | XML_REQUIRED],
							'resourceid' =>				['type' => XML_REQUIRED],
							'width' =>					['type' => XML_STRING | XML_REQUIRED],
							'height' =>					['type' => XML_STRING | XML_REQUIRED],
							'x' =>						['type' => XML_STRING | XML_REQUIRED],
							'y' =>						['type' => XML_STRING | XML_REQUIRED],
							'colspan' =>				['type' => XML_STRING | XML_REQUIRED],
							'rowspan' =>				['type' => XML_STRING | XML_REQUIRED],
							'elements' =>				['type' => XML_STRING | XML_REQUIRED],
							'valign' =>					['type' => XML_STRING | XML_REQUIRED],
							'halign' =>					['type' => XML_STRING | XML_REQUIRED],
							'style' =>					['type' => XML_STRING | XML_REQUIRED],
							'dynamic' =>				['type' => XML_STRING | XML_REQUIRED],
							'url' =>					['type' => XML_STRING]
						]]
					]]
				]]
			]],
			'images' =>					['type' => XML_INDEXED_ARRAY, 'prefix' => 'image', 'rules' => [
				'image' =>					['type' => XML_ARRAY, 'rules' => [
					'name' =>					['type' => XML_STRING | XML_REQUIRED],
					'imagetype' =>				['type' => XML_STRING | XML_REQUIRED],
					'encodedImage' =>			['type' => XML_STRING | XML_REQUIRED]
				]]
			]]
		]];

		return (new CXmlValidatorGeneral($rules, $this->format))->validate($data, $path);
	}

	/**
	 * Validate date format.
	 *
	 * @param string $data			import data
	 * @param array  $parent_data	data's parent array
	 * @param string $path			XML path
	 *
	 * @throws Exception			if the date is invalid
	 */
	public function validateDate($data, array $parent_data = null, $path) {
		if (!preg_match('/^(0[1-9]|[1-2][0-9]|3[01])\.(0[1-9]|1[0-2])\.[0-9]{2}$/', $data)) {
			throw new Exception(_s('Invalid tag "%1$s": %2$s.', $path, _s('"%1$s" is expected', _x('DD.MM.YY', 'XML date format'))));
		}

		return $data;
	}

	/**
	 * Validate time format.
	 *
	 * @param string $data			import data
	 * @param array  $parent_data	data's parent array
	 * @param string $path			XML path
	 *
	 * @throws Exception			if the time is invalid
	 */
	public function validateTime($data, array $parent_data = null, $path) {
		if (!preg_match('/^(2[0-3]|[01][0-9])\.[0-5][0-9]$/', $data)) {
			throw new Exception(_s('Invalid tag "%1$s": %2$s.', $path, _s('"%1$s" is expected', _x('hh.mm', 'XML time format'))));
		}

		return $data;
	}

	/**
	 * Validate Y Axis value.
	 *
	 * @param string $data			import data
	 * @param array  $parent_data	data's parent array
	 * @param string $path			XML path
	 *
	 * @throws Exception			if tag is invalid
	 */
	public function validateYMinItem($data, array $parent_data = null, $path) {
		if (zbx_is_int($parent_data['ymin_type']) && $parent_data['ymin_type'] == GRAPH_YAXIS_TYPE_ITEM_VALUE) {
			if (strpos($data, ':') === false) {
				throw new Exception(_s('Invalid tag "%1$s": %2$s.', $path, _('"host:key" pair is expected')));
			}
		}
		elseif ($data !== '') {
			throw new Exception(_s('Invalid tag "%1$s": %2$s.', $path, _('an empty string is expected')));
		}

		return $data;
	}

	/**
	 * Validate Y Axis value.
	 *
	 * @param string $data			import data
	 * @param array  $parent_data	data's parent array
	 * @param string $path			XML path
	 *
	 * @throws Exception			if tag is invalid
	 */
	public function validateYMaxItem($data, array $parent_data = null, $path) {
		if (zbx_is_int($parent_data['ymax_type']) && $parent_data['ymax_type'] == GRAPH_YAXIS_TYPE_ITEM_VALUE) {
			if (strpos($data, ':') === false) {
				throw new Exception(_s('Invalid tag "%1$s": %2$s.', $path, _('"host:key" pair is expected')));
			}
		}
		elseif ($data !== '') {
			throw new Exception(_s('Invalid tag "%1$s": %2$s.', $path, _('an empty string is expected')));
		}

		return $data;
	}

	/**
	 * Validate graph item.
	 *
	 * @param string $data			import data
	 * @param array  $parent_data	data's parent array
	 * @param string $path			XML path
	 *
	 * @throws Exception			if tag is invalid
	 */
	public function validateGraphItem($data, array $parent_data = null, $path) {
		if (strpos($data, ':') === false) {
			throw new Exception(_s('Invalid tag "%1$s": %2$s.', $path, _('"host:key" pair is expected')));
		}

		return $data;
	}

	/**
	 * Checking the map element for requirement.
	 *
	 * @param array  $parent_data	data's parent array
	 *
	 * @throws Exception			if the check is failed
	 */
	public function requiredMapElement(array $parent_data = null) {
		if (zbx_is_int($parent_data['elementtype'])) {
			switch ($parent_data['elementtype']) {
				case SYSMAP_ELEMENT_TYPE_HOST:
				case SYSMAP_ELEMENT_TYPE_MAP:
				case SYSMAP_ELEMENT_TYPE_TRIGGER:
				case SYSMAP_ELEMENT_TYPE_HOST_GROUP:
					return true;
			}
		}

		return false;
	}

	/**
	 * Validate map element.
	 *
	 * @param string $data			import data
	 * @param array  $parent_data	data's parent array
	 * @param string $path			XML path
	 *
	 * @throws Exception			if the map element is invalid
	 */
	public function validateMapElement($data, array $parent_data = null, $path) {
		if (zbx_is_int($parent_data['elementtype'])) {
			switch ($parent_data['elementtype']) {
				case SYSMAP_ELEMENT_TYPE_HOST:
					$rules = ['type' => XML_ARRAY, 'rules' => [
						'host' =>			['type' => XML_STRING | XML_REQUIRED]
					]];
					break;

				case SYSMAP_ELEMENT_TYPE_MAP:
					$rules = ['type' => XML_ARRAY, 'rules' => [
						'name' =>			['type' => XML_STRING | XML_REQUIRED]
					]];
					break;

				case SYSMAP_ELEMENT_TYPE_TRIGGER:
					$rules = ['type' => XML_ARRAY, 'rules' => [
						'host' =>			['type' => XML_STRING],
						'description' =>	['type' => XML_STRING | XML_REQUIRED],
						'expression' =>		['type' => XML_STRING | XML_REQUIRED]
					]];
					break;

				case SYSMAP_ELEMENT_TYPE_HOST_GROUP:
					$rules = ['type' => XML_ARRAY, 'rules' => [
						'name' =>			['type' => XML_STRING | XML_REQUIRED]
					]];
					break;

				default:
					return $data;
			}

			$data = (new CXmlValidatorGeneral($rules, $this->format))->validate($data, $path);
		}

		return $data;
	}

}
