177b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin#!/usr/bin/python
277b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
377b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin#
477b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin# Copyright (C) 2012 The Android Open Source Project
577b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin#
677b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin# Licensed under the Apache License, Version 2.0 (the "License");
777b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin# you may not use this file except in compliance with the License.
877b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin# You may obtain a copy of the License at
977b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin#
1077b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin#      http://www.apache.org/licenses/LICENSE-2.0
1177b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin#
1277b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin# Unless required by applicable law or agreed to in writing, software
1377b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin# distributed under the License is distributed on an "AS IS" BASIS,
1477b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1577b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin# See the License for the specific language governing permissions and
1677b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin# limitations under the License.
1777b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin#
1877b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
1977b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin"""
2077b63ca0447545a4dac3ac062f218d878ce01ba0Igor MurashkinUsage:
2177b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  metadata_validate.py <filename.xml>
2277b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  - validates that the metadata properties defined in filename.xml are
2377b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin    semantically correct.
2477b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  - does not do any XSD validation, use xmllint for that (in metadata-validate)
2577b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
2677b63ca0447545a4dac3ac062f218d878ce01ba0Igor MurashkinModule:
2777b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  A set of helpful functions for dealing with BeautifulSoup element trees.
2877b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  Especially the find_* and fully_qualified_name functions.
2977b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
3077b63ca0447545a4dac3ac062f218d878ce01ba0Igor MurashkinDependencies:
3177b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  BeautifulSoup - an HTML/XML parser available to download from
3277b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin                  http://www.crummy.com/software/BeautifulSoup/
3377b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin"""
3477b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
3577b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkinfrom bs4 import BeautifulSoup
3696bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkinfrom bs4 import Tag
3777b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkinimport sys
3877b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
3977b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
4077b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin#####################
4177b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin#####################
4277b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
4377b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkindef fully_qualified_name(entry):
4477b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  """
4577b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  Calculates the fully qualified name for an entry by walking the path
4677b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  to the root node.
4777b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
4877b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  Args:
4977b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin    entry: a BeautifulSoup Tag corresponding to an <entry ...> XML node
5077b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
5177b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  Returns:
5277b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin    A string with the full name, e.g. "android.lens.info.availableApertureSizes"
5377b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  """
5477b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  filter_tags = ['namespace', 'section']
5577b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  parents = [i['name'] for i in entry.parents if i.name in filter_tags]
5677b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
5777b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  name = entry['name']
5877b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
5977b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  parents.reverse()
6077b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  parents.append(name)
6177b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
6277b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  fqn = ".".join(parents)
6377b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
6477b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  return fqn
6577b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
6677b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkindef find_parent_by_name(element, names):
6777b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  """
6877b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  Find the ancestor for an element whose name matches one of those
6977b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  in names.
7077b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
7177b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  Args:
7277b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin    element: A BeautifulSoup Tag corresponding to an XML node
7377b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
7477b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  Returns:
7577b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin    A BeautifulSoup element corresponding to the matched parent, or None.
7677b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
7777b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin    For example, assuming the following XML structure:
7877b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin      <static>
7977b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin        <anything>
8077b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin          <entry name="Hello" />   # this is in variable 'Hello'
8177b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin        </anything>
8277b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin      </static>
8377b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
8477b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin      el = find_parent_by_name(Hello, ['static'])
8577b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin      # el is now a value pointing to the '<static>' element
8677b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  """
8777b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  matching_parents = [i.name for i in element.parents if i.name in names]
8877b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
8977b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  if matching_parents:
9077b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin    return matching_parents[0]
9177b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  else:
9277b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin    return None
9377b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
9496bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkindef find_all_child_tags(element, tag):
9596bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin    """
9696bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin    Finds all the children that are a Tag (as opposed to a NavigableString),
9796bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin    with a name of tag. This is useful to filter out the NavigableString out
9896bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin    of the children.
9996bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin
10096bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin    Args:
10196bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin      element: A BeautifulSoup Tag corresponding to an XML node
10296bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin      tag: A string representing the name of the tag
10396bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin
10496bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin    Returns:
10596bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin      A list of Tag instances
10696bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin
10796bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin      For example, given the following XML structure:
10896bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin        <enum>                    # This is the variable el
10996bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin          Hello world             # NavigableString
11096bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin          <value>Apple</value>    # this is the variale apple (Tag)
11196bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin          <value>Orange</value>   # this is the variable orange (Tag)
11296bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin          Hello world again       # NavigableString
11396bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin        </enum>
11496bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin
11596bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin        lst = find_all_child_tags(el, 'value')
11696bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin        # lst is [apple, orange]
11796bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin
11896bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin    """
11996bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin    matching_tags = [i for i in element.children if isinstance(i, Tag) and i.name == tag]
12096bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin    return matching_tags
12196bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin
12296bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkindef find_child_tag(element, tag):
12396bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin    """
12496bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin    Finds the first child that is a Tag with the matching name.
12596bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin
12696bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin    Args:
12796bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin      element: a BeautifulSoup Tag
12896bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin      tag: A String representing the name of the tag
12996bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin
13096bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin    Returns:
13196bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin      An instance of a Tag, or None if there was no matches.
13296bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin
13396bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin      For example, given the following XML structure:
13496bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin        <enum>                    # This is the variable el
13596bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin          Hello world             # NavigableString
13696bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin          <value>Apple</value>    # this is the variale apple (Tag)
13796bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin          <value>Orange</value>   # this is the variable orange (Tag)
13896bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin          Hello world again       # NavigableString
13996bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin        </enum>
14096bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin
14196bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin        res = find_child_tag(el, 'value')
14296bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin        # res is apple
14396bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin    """
14496bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin    matching_tags = find_all_child_tags(element, tag)
14596bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin    if matching_tags:
14696bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin        return matching_tags[0]
14796bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin    else:
14896bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin        return None
14996bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin
15077b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkindef find_kind(element):
15177b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  """
15277b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  Finds the kind Tag ancestor for an element.
15377b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
15477b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  Args:
15577b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin    element: a BeautifulSoup Tag
15677b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
15777b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  Returns:
15877b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin    a BeautifulSoup tag, or None if there was no matches
15977b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
16077b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  Remarks:
16177b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin    This function only makes sense to be called for an Entry, Clone, or
16277b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin    InnerNamespace XML types. It will always return 'None' for other nodes.
16377b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  """
16477b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  kinds = ['dynamic', 'static', 'controls']
16577b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  parent_kind = find_parent_by_name(element, kinds)
16677b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  return parent_kind
16777b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
16877b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkindef validate_error(msg):
16977b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  """
17077b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  Print a validation error to stderr.
17177b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
17277b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  Args:
17377b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin    msg: a string you want to be printed
17477b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  """
17577b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  print >> sys.stderr, "Validation error: " + msg
17677b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
17777b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
17877b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkindef validate_clones(soup):
17977b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  """
18077b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  Validate that all <clone> elements point to an existing <entry> element.
18177b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
18277b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  Args:
18377b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin    soup - an instance of BeautifulSoup
18477b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
18577b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  Returns:
18677b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin    True if the validation succeeds, False otherwise
18777b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  """
18877b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  success = True
18977b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
19077b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  for clone in soup.find_all("clone"):
19177b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin    clone_entry = clone['entry']
19277b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin    clone_kind = clone['kind']
19377b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
19477b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin    parent_kind = find_kind(clone)
19577b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
19677b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin    find_entry = lambda x: x.name == 'entry'                           \
19777b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin                       and find_kind(x) == clone_kind                  \
19877b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin                       and fully_qualified_name(x) == clone_entry
19977b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin    matching_entry = soup.find(find_entry)
20077b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
20177b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin    if matching_entry is None:
20277b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin      error_msg = ("Did not find corresponding clone entry '%s' " +    \
20377b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin               "with kind '%s'") %(clone_entry, clone_kind)
20477b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin      validate_error(error_msg)
20577b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin      success = False
20677b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
20777b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  return success
20877b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
20977b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin# All <entry> elements with container=$foo have a <$foo> child
21096bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin# If type="enum", <enum> tag is present
21196bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin# In <enum> for all <value id="$x">, $x is numeric
21277b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkindef validate_entries(soup):
21377b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  """
21477b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  Validate all <entry> elements with the following rules:
21577b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin    * If there is a container="$foo" attribute, there is a <$foo> child
21696bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin    * If there is a type="enum" attribute, there is an <enum> child
21796bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin    * In the <enum> child, all <value id="$x"> have a numeric $x
21877b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
21977b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  Args:
22077b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin    soup - an instance of BeautifulSoup
22177b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
22277b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  Returns:
22377b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin    True if the validation succeeds, False otherwise
22477b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  """
22577b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  success = True
22677b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  for entry in soup.find_all("entry"):
22777b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin    entry_container = entry.attrs.get('container')
22877b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
22977b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin    if entry_container is not None:
23077b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin      container_tag = entry.find(entry_container)
23177b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
23277b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin      if container_tag is None:
23377b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin        success = False
23477b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin        validate_error(("Entry '%s' in kind '%s' has type '%s' but " +  \
23577b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin                 "missing child element <%s>")                          \
23677b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin                 %(fully_qualified_name(entry), find_kind(entry),       \
23777b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin                 entry_container, entry_container))
23877b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
239b556bc47068d816cb319a5d0e2f6841b007b38f2Igor Murashkin    enum = entry.attrs.get('enum')
240b556bc47068d816cb319a5d0e2f6841b007b38f2Igor Murashkin    if enum and enum == 'true':
24196bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin      if entry.enum is None:
24296bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin        validate_error(("Entry '%s' in kind '%s' is missing enum")     \
24396bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin                               % (fully_qualified_name(entry), find_kind(entry),
24496bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin                                  ))
245b556bc47068d816cb319a5d0e2f6841b007b38f2Igor Murashkin        success = False
24696bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin
247b556bc47068d816cb319a5d0e2f6841b007b38f2Igor Murashkin      else:
24896bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin        for value in entry.enum.find_all('value'):
24996bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin          value_id = value.attrs.get('id')
25096bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin
25196bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin          if value_id is not None:
25296bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin            try:
25396bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin              id_int = int(value_id, 0) #autoguess base
25496bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin            except ValueError:
25596bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin              validate_error(("Entry '%s' has id '%s', which is not" + \
25696bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin                                        " numeric.")                   \
25796bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin                             %(fully_qualified_name(entry), value_id))
25896bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin              success = False
259b556bc47068d816cb319a5d0e2f6841b007b38f2Igor Murashkin    else:
260b556bc47068d816cb319a5d0e2f6841b007b38f2Igor Murashkin      if entry.enum:
261b556bc47068d816cb319a5d0e2f6841b007b38f2Igor Murashkin        validate_error(("Entry '%s' kind '%s' has enum el, but no enum attr")  \
262b556bc47068d816cb319a5d0e2f6841b007b38f2Igor Murashkin                               % (fully_qualified_name(entry), find_kind(entry),
263b556bc47068d816cb319a5d0e2f6841b007b38f2Igor Murashkin                                  ))
264b556bc47068d816cb319a5d0e2f6841b007b38f2Igor Murashkin        success = False
26596bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin
26696bd019289c47433e1b3522500bb166a4662e0f5Igor Murashkin  return success
26777b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
26877b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkindef validate_xml(file_name):
26977b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  """
27077b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  Validate all XML nodes according to the rules in validate_clones and
27177b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  validate_entries.
27277b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
27377b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  Args:
27477b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin    file_name - a string path to an XML file we wish to validate
27577b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
27677b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  Returns:
27777b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin    a BeautifulSoup instance if validation succeeds, None otherwise
27877b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  """
27977b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
28077b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  xml = file(file_name).read()
28177b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  soup = BeautifulSoup(xml, features='xml')
28277b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
28377b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  succ = validate_clones(soup)
28477b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  succ = validate_entries(soup) and succ
28577b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
28677b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  if succ:
28777b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin    return soup
28877b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  else:
28977b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin    return None
29077b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
29177b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin#####################
29277b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin#####################
29377b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
29477b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkinif __name__ == "__main__":
29577b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  if len(sys.argv) <= 1:
29677b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin    print >> sys.stderr, "Usage: %s <filename.xml>" % (sys.argv[0])
29777b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin    sys.exit(0)
29877b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
29977b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  file_name = sys.argv[1]
30077b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  succ = validate_xml(file_name) is not None
30177b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin
30277b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  if succ:
30377b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin    print "%s: SUCCESS! Document validated" %(file_name)
30477b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin    sys.exit(0)
30577b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin  else:
30677b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin    print >> sys.stderr, "%s: ERRORS: Document failed to validate" %(file_name)
30777b63ca0447545a4dac3ac062f218d878ce01ba0Igor Murashkin    sys.exit(1)
308