Validate the taxonomy following the XBRL 2.1 specification
However while this function does validate elements it is really a second pass over the elements
to continue processing them by, for example, pulling out the tuple elements
482377
public function validateTaxonomy21()
	{
		if ( XBRL::isValidating() )
		{
			$this->validateAppInfo();
			$this->validatedPresentationLinkbasePreferredLabels();
		}

		// BMS 2018-04-20 This test was too simplistic after adding the many prefixes to support formulas
		// $isTaxonomySchema = $this->xbrlDocument->getName() == 'schema' && ! isset( XBRL_Constants::$standardPrefixes[ $this->getPrefix() ] );
		$isTaxonomySchema = $this->xbrlDocument->getName() == 'schema' && ! ( isset( XBRL_Constants::$standardPrefixes[ $this->getPrefix() ] ) && XBRL_Constants::$standardPrefixes[ $this->getPrefix() ] == $this->getNamespace() );

		$types = $this->context->types;

		foreach ( $this->elementIndex as $elementKey => &$element )
		{
			$node = &$element['node'];
			unset( $element['node'] );

			// If not validating then at least the SimpleXMLNode references need to be removed
			if ( /* ! XBRL::isValidating() || */ ! $isTaxonomySchema )
			{
				continue;
			}

			$id = isset( $element['id'] ) ? $element['id'] : "";
			$name = isset( $element['name'] ) ? $element['name'] : "";

			if ( ! empty( $element['substitutionGroup'] ) ) // Only applies when a substitution group is expressed
			{
				// Try resolving the group using type information but element information may be missing
				if ( XBRL::isValidating() && ! $types->resolveToSubstitutionGroup( $element['substitutionGroup'], XBRL_Constants::$xbrliSubstitutionHeads ) )
				{
					// If the type is not one of the XBRL base types then ignore it
					// BMS 2018-04-09 Test candidates changed.
					if ( ! $types->resolvesToBaseType( strpos( $element['type'], ":" ) ? $element['type'] : "xs:{$element['type']}" , $types->xbrlItemTypeNames() ) )
					{
						continue;
					}

					$this->log()->taxonomy_validation(
						"4.6",
						"The substitution group of concepts MUST lead to xbrli:item or xbrli:tuple ",
						array(
							'name' => $name,
							'schema' => $this->getSchemaLocation(),
						)
					);
				}

				if ( $types->resolveToSubstitutionGroup( $element['substitutionGroup'], array( XBRL_Constants::$xbrliItem ) ) )
				{
					// If $element[substitutionGroup] is not xbrli:item OR xbrldt:dimensionItem make sure any missing attributes are available
					if ( ! in_array( $element['substitutionGroup'], array( XBRL_Constants::$xbrliItem ) ) )
					{
						$element = $this->getCompleteDescendantElement( $element, $types );
					}

					if ( XBRL::isValidating() && XBRL_Instance::isEmpty( $element, 'periodType' ) )
					{
						$this->log()->taxonomy_validation(
							"5.1.1.1",
							"A period type attribute MUST exist on concepts in the xbrli:item substitution group",
							array(
								'concept' => $name,
								'schema' => $this->getSchemaLocation(),
							)
						);
					}

					if ( XBRL::isValidating() && ! XBRL_Instance::isEmpty( $element, 'balance' ) )
					{
						// Check the balance is credit or debit
						if ( ! in_array( $element['balance'], array( 'credit', 'debit' ) ) )
						{
							$this->log()->taxonomy_validation(
								"5.1.1.2",
								"The value of a balance attribute MUST be either 'credit' or 'debit'",
								array(
									'concept' => $name,
									'value' => $element['balance'],
								)
							);
						}

						// Check the items with a balance attribute has a type that is or is decended from monetaryItemType
						if ( ! isset( $element['type'] ) || ! $types->resolvesToBaseType( $element['type'], array( 'xbrli:monetaryItemType' ) ) )
						{
							$this->log()->taxonomy_validation(
								"5.1.1.2",
								"The type of a balance attribute MUST be or be derived from 'monetaryItemType'",
								array(
									'concept' => $name,
									'type' => $element['type'],
								)
							);
						}
					}

					// The type must be simple, or complex and derived from fractionItemType
					$type = $types->getType( $element['type'] );
					if ( XBRL::isValidating() )
					{
						if ( $type )
						{
							if ( isset( $type['class'] ) )
							{
								if ( $type['class'] == 'complex' && ! ( $type['name'] == 'fractionItemType' &&  $type['prefix'] == 'xbrli' ) )
								{
									// If its complex then the type MUST be derived by restriction from fractionItemType
									$this->log()->taxonomy_validation(
										"5.1.1.3",
										"Items should only use simple types unless derived by restriction from fractionItemType",
										array(
											'concept' => $id,
											'type' => $element['type'],
										)
									);
								}
							}
							else
							{
								// BMS 2018-04-23 Why is this necessary?  XBRL 2.1 conformance test 160 V-05 fails because of this for no obviously good reason.
								// $this->log()->taxonomy_validation(
								// 	"1.4",
								// 	"Unable to find the class for the type",
								// 	array(
								// 		'type' => $element['type'],
								// 	)
								// );
							}
						}
						else
						{
							$this->log()->taxonomy_validation(
								"1.4",
								"Unable to find the type for an item concept",
								array(
									'concept' => $id,
								)
							);
						}
					}
				}
				// According to the XML schema specification an existing global element IS a substitution group.
				// If it exists then it has already been checked so it cam be assumed it already leads to one of the permitted types
				else if ( $types->resolveToSubstitutionGroup( $element['substitutionGroup'], array( XBRL_Constants::$xbrliTuple ) ) )
				{
					$refs = array();
					$permittedTypes = array();

					if ( XBRL::isValidating() && $node )
					{
						$this->validateLegalAttributes( $node );
					}

					if ( XBRL::isValidating() && ! XBRL_Instance::isEmpty( $element, 'periodType' ) )
					{
						// Tuples should not have a period type
						$this->log()->taxonomy_validation(
							"5.1.1.1",
							"A period type attribute MUST NOT exist on concepts in the xbrli:tuple substitution group",
							array(
								'schema' => $this->getTaxonomyXSD(),
								'concept' => $name,
							)
						);
					}

					if ( XBRL::isValidating() && ! XBRL_Instance::isEmpty( $element, 'balance' ) )
					{
						// Tuples should not have a balance
						$this->log()->taxonomy_validation(
							"5.1.1.1",
							"A balance attribute MUST NOT exist on concepts in the xbrli:tuple substitution group",
							array(
								'schema' => $this->getTaxonomyXSD(),
								'concept' => $name,
							)
						);
					}

					// Get the definition of the node
					$definition = $types->getElement( $name, $this->getPrefix() );
					if ( ! $definition )
					{
						if ( XBRL::isValidating() )
						$this->log()->taxonomy_validation(
							"4.9",
							"Unable to locate the schema element for tuple",
							array(
								'schema' => $this->getTaxonomyXSD(),
								'name' => $name,
							)
						);

						continue;
					}

					// Use the types in the definition to fill out the tuple element details
					if ( empty( $definition['types'] ) )
					{
						$permittedTypes[] = XBRL_Constants::$xbrliItem;
					}
					else
					{
						foreach ( $definition['types'] as $type )
						{
							if ( is_string( $type ) )
							{
								$type = $types->getType( $type );
							}

							if ( is_array( $type ) )
							{
								$me =& $this;
								$fn = function( $type ) use( &$fn, &$me, &$types, &$permittedTypes, &$refs, $name )
								{
									$result = false;

									// Check that any xbrli attributes do not have attributes themsevles.
									if ( isset( $type['attributes'] ) )
									foreach ( $type['attributes'] as $qname => $attribute )
									{
										// If there is a prefix then see if it is one of the XBRL ones
										if ( strpos( $qname, ":" ) !== false && in_array( strstr( $qname, ":", true ), array_keys( XBRL_Constants::$standardPrefixes ) ) )
										// if ( ! XBRL::startsWith( $qname, STANDARD_PREFIX_XBRLI ) ) continue;
										// The attribute begins with 'xbrli' so it cannot exist in a tuple definition

										$this->log()->taxonomy_validation( "4.9", "Attributes assigned to tuples should not be ones defined in XBRL specification namespaces",
											array(
												'name' => $qname,
											)
										);
									}

									// A complex type definition may not have any elements directly under it
									// in which case the validations regarding global elements do not apply
									// and this flag captures whether or not there are elements to test
									$hasElements = false;

									foreach ( array( 'elements', 'sequence', 'choice', 'group' ) as $typeComponent )
									{
										if ( isset( $type[ $typeComponent ] ) )
										{
											$hasElements |= $typeComponent == 'elements';

											if ( XBRL::isValidating() && isset( $type['mixed'] ) && $type['mixed'] )
											{
												XBRL_Log::getInstance()->taxonomy_validation( "4.9", 'Tuple types MUST NOT include mixed content',
													array(
														'schema' => $me->getTaxonomyXSD(),
														'concept' => $name,
													)
												);
											}

											if ( XBRL::isValidating() && isset( $type['class'] ) && $type['class'] == 'simple' )
											{
												XBRL_Log::getInstance()->taxonomy_validation( "4.9", 'Tuple types MUST NOT include simple content',
													array(
														'schema' => $me->getTaxonomyXSD(),
														'concept' => $name,
													)
												);
											}

											// If there is a restriction then dig out the type
											$base = isset( $type['base'] )
												? $type['base']
												: null;

											$baseType = $base
												? $types->getType( $base )
												: null;

											$contentType = isset( $type['contentType'] )
												? $type['contentType']
												: 'extension'; // Assume extension which is the more forgiving option but still a hack

											$elements = array();
											$basetypeElements = null;

											foreach ( $type[ $typeComponent ] as $id => $details )
											{
												if ( $id == 'restrictionType' ) continue;

												if ( $typeComponent == 'sequence' && $id == 'elements' )
												{
													if ( $fn( $type[ $typeComponent ] ) ) continue;
												}
												else if ( $fn( $details ) ) continue;

												// BMS 2018-04-14	Added the condition to exclude complex types from further checking
												//					as any element definitions should now be in the $refs array
												$isComplex = isset( $type['class'] ) && $type['class'] == 'complex';

												if ( ! $hasElements && $isComplex ) continue;

												if ( XBRL::isValidating() )
												{
													$isGlobal = isset( $details['global'] ) && $details['global'];

													if ( ! $isGlobal )
													{
														// Tuple sub-elements cannot be anonymous
														XBRL_Log::getInstance()->taxonomy_validation( "4.9", "Tuple sub-elements cannot be anonymous (they must have top-level instance)",
															array(
																'schema' => $me->getTaxonomyXSD(),
																'concept' => $name,
																'element' => $details['name'],
															)
														);
													}

													// Tuple sub-elements must be global, have a substitutionGroup and the group must resolve to item or tuple or the type *is* item or tuple
													if ( ! isset( $details['substitutionGroup'] ) ||
														 ! $types->resolveToSubstitutionGroup( $details['substitutionGroup'], XBRL_Constants::$xbrliSubstitutionHeads )
													)
													{
														if ( ! in_array( "{$details['prefix']}:{$details['name']}", XBRL_Constants::$xbrliSubstitutionHeads ) )
														{
															XBRL_Log::getInstance()->taxonomy_validation( "4.9", "Tuple sub-elements must be global, have a substitutionGroup and the group must resolve to item or tuple",
																array(
																	'schema' => $me->getTaxonomyXSD(),
																	'concept' => $name,
																)
															);
														}
													}

													if ( isset( $details['substitutionGroup'] ) &&
														 ( ! isset( $details['global'] ) || ! $details['global'] )
													)
													{
														XBRL_Log::getInstance()->taxonomy_validation( "4.9", 'Substitution groups are only valid on global element definitions',
															array(
																'schema' => $me->getTaxonomyXSD(),
																'name' => $details['name'],
																'concept' => $name,
															)
														);
													}
												}

												// If there is a base type and the base type is a type defined in this
												// taxonomy validate that the these members are members of the base type.
												if ( $baseType && $baseType['prefix'] == $me->getPrefix() )
												{
													if ( is_null( $basetypeElements ) )
													{
														$basetypeElements = $types->gatherElementsFromType( $baseType );
													}

													if ( $basetypeElements )
													{
														// If the $contentType is 'restriction' then the detail is retricted to the
														// members in $typeElements

														switch ( $contentType )
														{
															case 'restriction':

																if ( XBRL::isValidating() && ! isset( $basetypeElements[ "{$details['prefix']}:{$details['name']}" ] ) )
																{
																	XBRL_Log::getInstance()->taxonomy_validation( "schema", "The type does not match the type restriction",
																		array(
																			'container' => "'{$type['name']}'",
																			'type' => "'{$details['prefix']}:{$details['name']}'",
																			'valid types' => "'" . join( "', '", array_keys( $basetypeElements ) ) . "'",
																		)
																	);
																}

																break;

															case 'extension':

																// In this case the $typeElements are added to the base types
																$basetypeElements = $basetypeElements + $types->gatherElementsFromType( "{$type['prefix']}:{$type['name']}" );

																if ( XBRL::isValidating() && ! isset( $basetypeElements[ "{$details['prefix']}:{$details['name']}" ] ) )
																{
																	XBRL_Log::getInstance()->taxonomy_validation( "schema", "The type does not match the type extension",
																		array(
																			'container' => "'{$type['name']}'",
																			'type' => "'{$details['prefix']}:{$details['name']}'",
																			'valid types' => "'" . join( "', '", array_keys( $basetypeElements ) ) . "'",
																		)
																	);
																}

																break;

														}
													}
												}

												if ( $details['abstract'] )
												{
													$permittedTypes[] = "{$details['prefix']}:{$details['name']}";
												}
												else
												{
													$ref = array(
														'name' => $details['name'],
														'namespace' => $me->getNamespaceForPrefix( $details['prefix'] ),
													);

													if ( isset( $details['minOccurs'] ) )
													{
														$ref['minOccurs'] = $details['minOccurs'];
													}

													if ( isset( $details['maxOccurs'] ) )
													{
														$ref['maxOccurs'] = $details['maxOccurs'];
													}

													$elements[ count( $refs ) + count( $elements ) ] = $ref;
													$result = true;
												}
											}

											$refs = array_merge( $refs, $elements );
										}
									}

									return  $result;
								};

								$fn( $type );
								unset( $me );
							}
						}
					}

					// Record the references
					$element['tuple_elements'] = $refs;
					$element['permitted_types'] = $permittedTypes;

				}

				if ( isset( $element['custom'] ) )
				{
					// Check that the custom attribute has a valid value.  For now limit to numeric check.
					foreach( $element['custom'] as $attribute => $value )
					{
						// If there is no type then ignore the element
						$attributePrefix = strstr( $attribute, ":", true );
						$attributeNamespace = $this->getDocumentNamespaces()[ $attributePrefix ];
						$tax = $this->getTaxonomyForNamespace( $attributeNamespace );
						// If there is no taxonomy it ma be a built-in
						// if ( ! $tax ) continue;
						$prefix = $tax
							? $tax->getPrefix()
							:	(
									isset( XBRL_Constants::$standardNamespaces[ $attributeNamespace ] )
										? XBRL_Constants::$standardNamespaces[ $attributeNamespace ]
										: null
								);
						if ( ! $prefix ) continue;
						if ( ! ( $attributeType = $types->getAttribute( trim( strstr( $attribute, ":" ), ":" ), $prefix ) ) ) continue;
						if ( ! isset( $attributeType['types'][0] ) ) continue;

						// Check to see if the element type resolves to a decimal (such as an integer)
						if ( ! $types->resolvesToBaseType( $attributeType['types'][0], array( 'xs:decimal' ) ) ) continue;

						// If it does, can the value be coerced to a numeric?
						if ( is_numeric( $value ) ) continue;

						$this->log()->taxonomy_validation( "4.9", "Element attribute value type mismatch",
							array(
								'element' => $element['name'],
								'attribute' => $attribute,
								'value' => $value
							)
						);

					}
				}
			}

			$this->validateXDTElements( $types, $id, $name, $element);

		}
	}
364352
public static function compare_arrays_ordinal( $array1, $array2 )
	{
		if ( count( $array1 ) != count( $array2 ) ) return false;
		if ( ! count( $array1 ) ) return true;

		$types = XBRL_Types::getInstance();

		for( $i = 0; $i < count( $array1 ); $i++ )
		{
			$key1 = key( $array1 );
			$key2 = key( $array2 );

			if (  $key1 != $key2 ) return false;

			$value1 = current( $array1 );
			$value2 = current( $array2 );

			next( $array1 );
			next( $array2 );

			if ( $key1 == 'id' )
			{
				$pattern = "/^" . SchemaTypes::$ncName . "$/u";
				if ( ! preg_match( $pattern, $value1, $matches ) )
				{
					$this->log()->taxonomy_validation( "context", "id attribute is not a valid NCName", array( 'id' => $value1 ) );
				}
				if ( ! preg_match( $pattern, $value2, $matches ) )
				{
					$this->log()->taxonomy_validation( "context", "id attribute is not a valid NCName", array( 'id' => $value2 ) );
				}
			}

			if ( is_array( $value1 ) )
			{
				if ( ! is_array( $value2 ) ) return false;

				// These arrays will represent elements so compare the contents by name
				if ( ! XBRL_Equality::compare_arrays( $value1, $value2 ) )
				{
					return false;
				}
			}
			else if ( in_array( $key1, array( 'id', 'name', 'prefix', 'type' ) ) )
			{
				if ( $value1 != $value2 )
				{
					return false;
				}
			}
			else
			{
				$value1Type = XBRL_Equality::xEqualComparisonType( $value1, $types );
				$value2Type = XBRL_Equality::xEqualComparisonType( $value2, $types );
				if ( ! XBRL_Equality::xequal( $value1, $value2, $value1Type, $value2Type, $types ) )
				{
					return false;
				}
			}
		}

		return true;
	}
567369
def validate(self, value):
        
        if not isinstance(value, (list, tuple)) or isinstance(value, str_types):
            self.raise_error('Only lists and tuples may be used in the ListField vs provided {0}'
                             .format(type(value).__name__))
        super(ListField, self).validate(value)
993793
def get_validation_errors(self, xml_input):
        
        errors = []
        try:
            parsed_xml = etree.parse(self._handle_xml(xml_input))
            self.xmlschema.assertValid(parsed_xml)
        except (etree.DocumentInvalid, etree.XMLSyntaxError), e:
            errors = self._handle_errors(e.error_log)
        except AttributeError:
            raise CannotValidate('Set XSD to validate the XML')
        return errors
100214
private function indexElements( $forceNodeProcessing )
	{
		$types = $this->context->types;
		$isTaxonomySchema = $this->xbrlDocument->getName() == 'schema' && ! isset( XBRL_Constants::$standardPrefixes[ $this->getPrefix() ] );

		if ( $forceNodeProcessing || ! $types->hasProcessedSchema( $this->prefix ) || ! $types->getPrefixForNamespace( $this->getNamespace() ) )
		{
			$types->processNode( $this->xbrlDocument, $this->xbrlDocument->getName(), $this->getPrefix(), true );
		}

		foreach ( $this->xbrlDocument->children( XBRL_Constants::$standardPrefixes[ STANDARD_PREFIX_SCHEMA ] ) as $nodeKey => $node )
		{
			if ( $nodeKey == 'redefine' )
			{
				if ( XBRL::isValidating() )
				$this->log()->taxonomy_validation( "5.1.5", "The 'redefine' tag is prohibited in XBRL schema taxonomies", array() );
				continue;
			}

			if ( $nodeKey == 'element' )
			{
				$attributes = $node->attributes();

				$id = (string) $attributes['id'];
				$name = (string) $attributes['name'];

				if ( empty( $id ) )
				{
					$id = $name;
				}
				else if ( empty( $name ) )
				{
					$name = $id;
				}

				$element = array(
					'id' => $id,
					'name' => $name,
					'type' => (string) $attributes['type'],
					'substitutionGroup' => $this->normalizePrefix( (string) $attributes['substitutionGroup'], $this ),
					'abstract' => (string) $attributes['abstract'] === 'true' ? 1 : 0,
					'nillable' => (string) $attributes['nillable'] === 'true' ? 1 : 0,
				);

				$this->indexXDT( $types, $node, $element );
				$this->indexCustom( $types, $node, $element );

				if ( $isTaxonomySchema )
				{
					$element['node'] = $node; // Keep this temporarily
					// $element['nodeKey'] = $nodeKey; // Keep this temporarily
				}

				$xbrliAttributes = $node->attributes( XBRL_Constants::$standardPrefixes[ STANDARD_PREFIX_XBRLI ] );

				if ( property_exists( $xbrliAttributes, 'periodType' ) )
				{
					$element['periodType'] = (string) $xbrliAttributes['periodType'];
				}

				if ( property_exists( $xbrliAttributes, 'balance' ) )
				{
					$element['balance'] = (string) $xbrliAttributes['balance'];
				}

				$this->elementIndex[ $id ] = $element;

			}
		}

		$taxonomy_namespace = $this->getNamespace();
		// Its possible that when multiple versions of a taxonomy are loaded that the
		// same prefix will appear.  Assume the first loaded is the correct one to use.
		if ( ! $types->hasProcessedSchema( $this->prefix ) )
		{
			$types->setProcessedSchema( $this->prefix, $taxonomy_namespace );
		}
	}
79971
private function validatedPresentationLinkbasePreferredLabels()
	{
		// This test is only needed by the XBRL 2.1 conformance suite test 299
		if ( ! defined( 'CONFORMANCE_TEST_SUITE_XBRL_21') ) return;

		$roleRefs = $this->getPresentationRoleRefs();
		$step = 0;
		// Iterate over all the role refs
		foreach ( $roleRefs as $roleUri => $roleRef )
		{
			$step = "1 - $roleUri";
			// Traverse all the nodes to look for any with a preferred label
			$this->processAllNodes( $roleRef['hierarchy'], function( $node, $id ) use( $roleRef, $roleUri )
			{
				// To distinguish presentation hiearchy nodes that are periodStart or periodEnd but have the same id
				// (such as uk-gaap-pt-2004-12-01.xsd#uk-gaap-pt_NetDebtFunds) the process of creating the hierarchy
				// generates two ids with the 'periodEndLabel' or 'periodStartLabel'.  It also adds these to the
				// element index of the respective taxonomy (uk-gaap-pt-2004-12-01.xsd in this case).  Because there
				// will be no label for a manufactured id (such as uk-gaap-pt_NetDebtFundsperiodEndLabel) it's
				// necessary to lookup the manufactured element in the respective taxonmy and use the id of the element
				// array that's returned.
				$nodeTaxonomy = $this->getTaxonomyForXSD($node['label']);
				$nodeElement = $nodeTaxonomy->getelementById($node['label']);
				$nodeLabel = "{$nodeTaxonomy->getTaxonomyXSD()}#{$nodeElement['id']}";

				if ( ! isset( $node['preferredLabel'] ) ) return true;

				$step = "2 - {$node['preferredLabel']}";

				// The label should have an arc
				// BMS 2018-05-08 Labels use the default link role
				// if ( isset( $this->context->labels[ $roleUri ]['arcs'][ $node['label'] ] ) )
				// if ( isset( $this->context->labels[ \XBRL_Constants::$defaultLinkRole ]['arcs'][ $node['label'] ] ) )
				if ( isset( $this->context->labels[ \XBRL_Constants::$defaultLinkRole ]['arcs'][ $nodeLabel ] ) )
				{
					$step = "3 - {$node['label']}";

					// Create a shortcut to a sub-element
					// $labels =& $this->context->labels[ $roleUri ];
					$labels =& $this->context->labels[ \XBRL_Constants::$defaultLinkRole ];

					// Check the preferred label exists
					if ( isset( $labels['labels'][ $node['preferredLabel'] ] ) )
					{
						$step = "4";

						// Now check there is a preferred label for the arc target
						// foreach ( $labels['arcs'][ $node['label'] ] as $targetLabel => $details )
						foreach ( $labels['arcs'][ $nodeLabel ] as $targetLabel => $details )
						{
							$step = "5 - {$targetLabel}";
							// and locate the label in any lanuage
							foreach ( $labels['labels'][ $node['preferredLabel'] ] as $lang => $labelLabel )
							{
								$step = "6 - {$lang}";

								// If it exists then
								if ( isset( $labelLabel[ $targetLabel ] ) )
								{
									return true;
								}
							}
						}
					}
				}

				$this->log()->taxonomy_validation( "5.2.4.2.1", "A presentation arc preferred label role assigment is not defined as a label",
					array(
						'preferred label' => $node['preferredLabel'],
						'arc to' => $node['label']
					)
				);

			} );
		}
	}
532884
public function validateDFR( $formulas )
	{
		$log = XBRL_Log::getInstance();

		// Makes sure they are reset in case the same taxonomy is validated twice.
		$this->calculationNetworks = array();
		$this->presentationNetworks = array();
		$this->definitionNetworks = $this->getAllDefinitionRoles();

		foreach ( $this->definitionNetworks as $elr => &$roleRef )
		{
			$roleRef = $this->getDefinitionRoleRef( $elr );

			if ( property_exists( $this, 'definitionRoles' ) && ! in_array( $elr, $this->definitionRoles ) )
			{
				unset( $this->definitionNetworks[ $elr ] );
				continue;
			}

			// Capture primary items
			$this->definitionPIs[ $elr ] = array_filter( array_keys( $roleRef['primaryitems'] ), function( $label )
			{
				$taxonomy = $this->getTaxonomyForXSD( $label );
				$element = $taxonomy->getElementById( $label );
				return ! $element['abstract' ];
			} );

			sort( $this->definitionPIs[ $elr ] );

			// Check members
			foreach ( $roleRef['members'] as $memberLabel => $member )
			{
				$memberTaxonomy = $this->getTaxonomyForXSD( $memberLabel );
				$memberElement = $memberTaxonomy->getElementById( $memberLabel );

				if ( ! $memberElement['abstract' ] )
				{
					$log->business_rules_validation('Model Structure Rules', 'All dimension member elements MUST be abstract',
						array(
							'member' => $memberLabel,
							'role' => $elr,
							'error' => 'error:MemberRequiredToBeAbstract'
						)
					);
				}

				// BMS 2019-03-23 TODO typed members MUST NOT use complex types

				unset( $memberTaxonomy );
				unset( $memberElement );
			}

			// Check hypercube
			foreach ( $roleRef['hypercubes'] as $hypercubeLabel => $hypercube )
			{
				foreach ( $hypercube['parents'] as $primaryItemLabel => $primaryItem )
				{
					if ( ! $primaryItem['closed'] )
					{
						if ( ! isset( $this->definitionNetworks[ $elr ]['primaryitems'][ $primaryItemLabel ]['parents']  ) ) // Only report the error on the line items node
						{
							$log->business_rules_validation('Model Structure Rules', 'All line items to hypercubes MUST be closed',
								array(
									'hypercube' => $hypercubeLabel,
									'primary item' => $primaryItemLabel,
									'role' => $elr,
									'error' => 'error:HypercubesRequiredToBeClosed'
								)
							);
						}
					}

					if ( $primaryItem['arcrole'] == XBRL_Constants::$arcRoleNotAll )
					{
						$log->business_rules_validation('Model Structure Rules', 'All line items to hypercubes MUST be \'all\'',
							array(
								'hypercube' => $hypercubeLabel,
								'primary item' => $primaryItemLabel,
								'role' => $elr,
								'error' => 'error:HypercubeMustUseAllArcrole'
							)
						);
					}

					if ( $primaryItem['contextElement'] != XBRL_Constants::$xbrliSegment )
					{
						$log->business_rules_validation('Model Structure Rules', 'Dimensions in contexts MUST use the segment container',
							array(
								'hypercube' => $hypercubeLabel,
								'primary item' => $primaryItemLabel,
								'role' => $elr,
								'error' => 'error:DimensionsMustUseSegmentContainer'
							)
						);
					}
				}
			}
		}

		unset( $roleRef );

		$this->calculationNetworks = $this->getCalculationRoleRefs();
		$this->calculationNetworks = array_filter( $this->calculationNetworks, function( $roleRef ) { return isset( $roleRef['calculations'] ); } );
		foreach ( $this->calculationNetworks as $elr => $role )
		{
			if ( property_exists( $this, 'calculationRoles' ) && ! in_array( $elr, $this->calculationRoles ) )
			{
				unset( $this->calculationNetworks[ $elr ] );
				continue;
			}

			if ( ! isset( $role['calculations'] ) ) continue;

			foreach ( $role['calculations'] as $totalLabel => $components )
			{
				$calculationELRPIs = array_keys( $components );
				$calculationELRPIs[] = $totalLabel;

				$this->calculationPIs[ $elr ] = isset( $this->calculationPIs[ $elr ] )
					? array_merge( $this->calculationPIs[ $elr ], $calculationELRPIs )
					: $calculationELRPIs;
			}

			unset( $calculationELRPIs );
		}

		$this->presentationNetworks = &$this->getPresentationRoleRefs();

		if ( property_exists( $this, 'presentationRoles' ) )
		foreach ( $this->presentationNetworks as $elr => $role )
		{
			if ( in_array( $elr, $this->presentationRoles ) ) continue;
			unset( $this->presentationNetworks[ $elr ] );
		}

		// Check the definition and presentation roles are consistent then make sure the calculation roles are a sub-set
		if ( $this->definitionNetworks && array_diff_key( $this->presentationNetworks, $this->definitionNetworks ) || array_diff_key( $this->definitionNetworks, $this->presentationNetworks ) )
		{
			$log->business_rules_validation('Model Structure Rules', 'Networks in defintion and presentation linkbases MUST be the same',
				array(
					'presentation' => implode( ', ', array_keys( array_diff_key( $this->presentationNetworks, $this->definitionNetworks ) ) ),
					'definition' => implode( ', ', array_keys( array_diff_key( $this->definitionNetworks, $this->presentationNetworks ) ) ),
					'error' => 'error:NetworksMustBeTheSame'
				)
			);
		}
		else
		{
			if ( array_diff_key( $this->calculationNetworks, $this->presentationNetworks ) )
			{
				$log->business_rules_validation('Model Structure Rules', 'Networks in calculation linkbases MUST be a sub-set of those in definition and presentation linkbases',
					array(
						'calculation' => implode( ', ', array_keys( array_diff_key( $this->calculationNetworks, $this->presentationNetworks ) ) ),
						'error' => 'error:NetworksMustBeTheSame'
					)
				);
			}
		}

		$presentationRollupPIs = array();

		foreach ( $this->presentationNetworks as $elr => &$role )
		{
			$this->presentationPIs[$elr] = array();

			foreach ( $role['locators'] as $id => $label )
			{
				$taxonomy = $this->getTaxonomyForXSD( $label );
				$element = $taxonomy->getElementById( $label );

				if ( $element['abstract'] || $element['type'] == 'nonnum:domainItemType' ) continue;

				// One or more of the labels may include the preferred label role so convert all PIs back to their id
				$this->presentationPIs[$elr][] = $taxonomy->getTaxonomyXSD() . "#{$element['id']}";

				// BMS 2019-03-23 TODO Check the concept is not a tuple
			}

			// If there were preferred label roles in any of the PIs then there will be duplicates.  This also sorts the list.
			$this->presentationPIs[ $elr ] = array_unique( $this->presentationPIs[ $elr ] );

			// This set of closures will become methods in a class
			$formulasForELR = array();
			if ( $formulas )
			{
				$variableSets = $formulas->getVariableSets();
				foreach ( $variableSets as $variableSetQName => $variableSetForQName )
				{
					foreach ( $variableSetForQName as /** @var ValueAssertion $variableSet */ $variableSet )
					{
						if ( $variableSet->extendedLinkRoleUri != $elr ) continue;
						if ( ! $variableSet instanceof ValueAssertion ) continue;
						$formulasForELR[] = $variableSet;
					}
				}
			}

			$calculationELRPIs = isset( $this->calculationPIs[ $elr ] ) ? $this->calculationPIs[ $elr ] : array();

			$axes = array();
			$lineItems = array();
			$tables = array();
			$concepts = array();

			$this->processNodes( $role['hierarchy'], null, false, $this->allowed['cm.xsd#cm_Network'], false, $calculationELRPIs, $elr, $presentationRollupPIs, $tables, $lineItems, $axes, $concepts, $formulasForELR );

			if ( isset( $this->definitionNetworks[ $elr ] ) )
			{
				if ( count( $tables ) != 1 )
				{
					XBRL_Log::getInstance()->business_rules_validation('Model Structure Rules', 'There MUST be one and only one table per network',
						array(
							'tables' => $tables ? implode( ', ', $tables ) : 'There is no table',
							'role' => $elr,
							'error' => 'error:MustBeOnlyOneTablePerNetwork'
						)
					);
				}

				if (  count( $lineItems ) != 1 )
				{
					XBRL_Log::getInstance()->business_rules_validation('Model Structure Rules', 'There MUST be one and only one line items node per table',
						array(
							'lineitems' => $lineItems ? implode( ', ', $lineItems ) : 'There is no line item node',
							'role' => $elr,
							'error' => 'error:OneAndOnlyOneLineItems'
						)
					);
				}
			}
			else if ( $tables )
			{
				// If there are tables defined in the presentation but no tables in the defintion then drop the presentation
				unset( $this->presentationNetworks['$elr'] );
			}

			$role['axes'] = $axes;
			$role['tables'] = $tables;
			$role['lineitems'] = $lineItems;
			$role['concepts'] = $concepts;
		}

		unset( $role );


		// The set of line items used in calculation, definition and presentation linkbases should be the same
		// First check there are consistent networks
		$commonRoles = array_intersect_key( $this->definitionPIs, $this->presentationPIs );

		foreach ( $commonRoles as $elr => $role )
		{
			if ( isset( $presentationRollupPIs[ $elr ] ) )
			{
				$diff = array_unique( array_merge( array_diff( $presentationRollupPIs[ $elr ], $this->calculationPIs[ $elr ] ), array_diff( $this->calculationPIs[ $elr ], $presentationRollupPIs[ $elr ] ) ) );
				if ( $diff )
				{
					$log->business_rules_validation('Model Structure Rules', 'Calculation primary items MUST be the same as presentation items that are used in rollup blocks',
						array(
							'primary item' => implode( ',', $diff ),
							'role' => $elr,
							'error' => 'error:CalculationRelationsMissingConcept'
						)
					);
				}
			}

			$diff = array_unique( array_diff( $this->definitionPIs[ $elr ], $this->presentationPIs[ $elr ] ) );
			if ( $diff )
			{
				$log->business_rules_validation('Model Structure Rules', 'Presentation primary items MUST be the same as definition primary items',
					array(
						'primary item' => implode( ',', $diff ),
						'role' => $elr,
						'error' => 'error:PresentationRelationsMissingConcept'
					)
				);
			}

			$diff = array_unique( array_diff( $this->presentationPIs[ $elr ], $this->definitionPIs[ $elr ] ) );
			if ( $diff )
			{
				$log->business_rules_validation('Model Structure Rules', 'Definition primary items MUST be the same as presentation primary items',
					array(
						'primary item' => implode( ',', $diff ),
						'role' => $elr,
						'error' => 'error:DefinitionRelationsMissingConcept'
					)
				);
			}
		}

		return $this->presentationNetworks;
	}
829664
private function validateGeneralSpecial( XBRL_Types &$types )
	{
		$roles =& $this->getInstanceTaxonomy()->getGeneralSpecialRoleRefs();

		/* ---------------------------------------------------------
		 * Look for directed cycles
		 * ---------------------------------------------------------
		 */

		/**
		 * Examine the arcs recursively to look for directed cycles.
		 * If there are directed cyles then the $alias of an arc will appear in the list of $parents
		 *
		 * @var function $detectDirected
		 * @param string $source
		 * @param array $parents
		 * @return bool
		 */
		$detectDirected = function( $roleKey, $source, $parents = array() ) use( &$detectDirected, &$roles )
		{
			foreach ( $roles[ $roleKey ]['rules'][ $source ] as $target => $details )
			{
				if ( $target == $source || in_array( $target, $parents ) )
				{
					XBRL_Log::getInstance()->instance_validation( "5.2.6.2.1", "The general special arcs contain directed cycles",
						array(
							'path' => implode( '->', array_merge( $parents, array( $target ) ) ),
						)
					);
					return true;
				}

				if ( ! isset( $roles[ $roleKey ]['rules'][ $target ] ) ) continue;

				if ( $detectDirected( $roleKey, $target, array_merge( $parents, array( $source ) ) ) ) return true;
			}

			return false;
		};

		foreach ( $roles as $roleKey => $role )
		{
			if ( ! isset( $role['rules'] ) ) continue;

			foreach ( $role['rules'] as $from => $items )
			{
				if ( $detectDirected( $roleKey, $from ) )
				{
					break;
				}
			}
		}

	}
158713
public function validate(): bool
    {
        $this->error = '';
        try {
            // create the schemas collection
            $schemas = $this->buildSchemas();
            // validate the document against the schema collection
            $this->validateWithSchemas($schemas);
        } catch (LibXmlException $ex) {
            $this->error = $ex->getMessage();
            return false;
        }
        return true;
    }
821354
protected function performValidation()
    {
        // All the fixed fields declared above are objects that MUST use keys that match the regular expression: ^[a-zA-Z0-9\.\-_]+$.
        foreach (array_keys($this->attributes()) as $attribute) {
            if (is_array($this->$attribute)) {
                foreach ($this->$attribute as $k => $v) {
                    if (!preg_match('~^[a-zA-Z0-9\.\-_]+$~', $k)) {
                        $this->addError("Invalid key '$k' used in Components Object for attribute '$attribute', does not match ^[a-zA-Z0-9\.\-_]+\$.");
                    }
                }
            }
        }
    }
1191418
public function validate()
    {
        $result = array();
        if(empty($this->slug)) $this->slug = $this->title;
        $this->slug = \lib\MyFunctions::slug($this->slug);
        if (empty($this->slug)) {
            $result['slug'] = Validate::cantLeaveBlank(_('Slug'));
        } else {
            $slugExists = self::checkSlug($this->slug,$this->id);
            if ($slugExists) {
                $result['slug'] = _('Repeated slug');
            }
        }
        if (empty($this->title)) {
            $result['title'] = Validate::cantLeaveBlank(_('Title'));
        }
        if (empty($this->description)) {
            $result['description'] = Validate::cantLeaveBlank(_('Description'));
        }

        return $result;
    }
802309
private void validateXmlTree(Node received, Node source, 
            XmlMessageValidationContext validationContext, NamespaceContext namespaceContext, TestContext context) {
        switch(received.getNodeType()) {
            case Node.DOCUMENT_TYPE_NODE:
                doDocumentTypeDefinition(received, source, validationContext, namespaceContext, context);
                break;
            case Node.DOCUMENT_NODE:
                validateXmlTree(received.getFirstChild(), source.getFirstChild(),
                        validationContext, namespaceContext, context);
                break;
            case Node.ELEMENT_NODE:
                doElement(received, source, validationContext, namespaceContext, context);
                break;
            case Node.ATTRIBUTE_NODE:
                throw new IllegalStateException();
            case Node.COMMENT_NODE:
                validateXmlTree(received.getNextSibling(), source,
                        validationContext, namespaceContext, context);
                break;
            case Node.PROCESSING_INSTRUCTION_NODE:
                doPI(received);
                break;
        }
    }
179465
def validateElement(self, doc, elem):
        
        if doc is None: doc__o = None
        else: doc__o = doc._o
        if elem is None: elem__o = None
        else: elem__o = elem._o
        ret = libxml2mod.xmlValidateElement(self._o, doc__o, elem__o)
        return ret
5477
public function valid() : bool
    {
        if ($this->privateErrors()) {
            return false;
        }
        foreach ((array)$this as $element) {
            if (is_string($element)) {
                continue;
            }
            if (!$element->getElement()->valid()) {
                return false;
            }
        }
        return true;
    }
715465
def _ensure_element(tup, elem):
    
    try:
        return tup, tup.index(elem)
    except ValueError:
        return tuple(chain(tup, (elem,))), len(tup)
294332
def one_child(self, tup_tree, acceptable):
        

        k = kids(tup_tree)

        if not k:
            raise CIMXMLParseError(
                _format("Element {0!A} missing required child element {1!A}",
                        name(tup_tree), acceptable),
                conn_id=self.conn_id)
        if len(k) > 1:
            raise CIMXMLParseError(
                _format("Element {0!A} has too many child elements {1!A} "
                        "(allowed is one child element {2!A})",
                        name(tup_tree), [name(t) for t in k], acceptable),
                conn_id=self.conn_id)

        child = k[0]

        if name(child) not in acceptable:
            raise CIMXMLParseError(
                _format("Element {0!A} has invalid child element {1!A} "
                        "(allowed is one child element {2!A})",
                        name(tup_tree), name(child), acceptable),
                conn_id=self.conn_id)

        return self.parse_any(child)
771632
def _validate(self, val: list, log: Optional[Logger] = None) -> Tuple[bool, List[str]]:
        
        errors = []
        if not isinstance(val, list):
            errors.append(f"{self._variable_name}: {repr(val)} is not an array")
        else:
            for i in range(0, len(val)):
                v = val[i]
                if not conforms(v, self._type, self._context.NAMESPACE):
                    errors.append(f"{self._variable_name} element {i}: {v} is not a {self._type.__name__}")

            if len(val) < self._min:
                errors.append(
                    f"{self._variable_name}: at least {self._min} value{'s' if self._min > 1 else ''} required - "
                    f"element has {len(val) if len(val) else 'none'}")
            if self._max is not None and len(val) > self._max:
                errors.append(
                    f"{self._variable_name}: no more than {self._max} values permitted - element has {len(val)}")

        if log:
            for error in errors:
                log.log(error)
        return not bool(errors), errors