#!/usr/bin/python # # Copyright (C) 2009 Chia-I Wu # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # on the rights to use, copy, modify, merge, publish, distribute, sub # license, and/or sell copies of the Software, and to permit persons to whom # the Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice (including the next # paragraph) shall be included in all copies or substantial portions of the # Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL # IBM AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. """ A parser for APIspec. """ class SpecError(Exception): """Error in the spec file.""" class Spec(object): """A Spec is an abstraction of the API spec.""" def __init__(self, doc): self.doc = doc self.spec_node = doc.getRootElement() self.tmpl_nodes = {} self.api_nodes = {} self.impl_node = None # parse node = self.spec_node.children while node: if node.type == "element": if node.name == "template": self.tmpl_nodes[node.prop("name")] = node elif node.name == "api": self.api_nodes[node.prop("name")] = node else: raise SpecError("unexpected node %s in apispec" % node.name) node = node.next # find an implementation for name, node in self.api_nodes.iteritems(): if node.prop("implementation") == "true": self.impl_node = node break if not self.impl_node: raise SpecError("unable to find an implementation") def get_impl(self): """Return the implementation.""" return API(self, self.impl_node) def get_api(self, name): """Return an API.""" return API(self, self.api_nodes[name]) class API(object): """An API consists of categories and functions.""" def __init__(self, spec, api_node): self.name = api_node.prop("name") self.is_impl = (api_node.prop("implementation") == "true") self.categories = [] self.functions = [] # parse func_nodes = [] node = api_node.children while node: if node.type == "element": if node.name == "category": cat = node.prop("name") self.categories.append(cat) elif node.name == "function": func_nodes.append(node) else: raise SpecError("unexpected node %s in api" % node.name) node = node.next # realize functions for func_node in func_nodes: tmpl_node = spec.tmpl_nodes[func_node.prop("template")] try: func = Function(tmpl_node, func_node, self.is_impl, self.categories) except SpecError, e: func_name = func_node.prop("name") raise SpecError("failed to parse %s: %s" % (func_name, e)) self.functions.append(func) def match(self, func, conversions={}): """Find a matching function in the API.""" match = None need_conv = False for f in self.functions: matched, conv = f.match(func, conversions) if matched: match = f need_conv = conv # exact match if not need_conv: break return (match, need_conv) class Function(object): """Parse and realize a