# # Copyright 2010 Free Software Foundation, Inc. # # This file is part of GNU Radio # # GNU Radio 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 3, or (at your option) # any later version. # # GNU Radio 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 GNU Radio; see the file COPYING. If not, write to # the Free Software Foundation, Inc., 51 Franklin Street, # Boston, MA 02110-1301, USA. # """ A base class is created. Classes based upon this are used to make more user-friendly interfaces to the doxygen xml docs than the generated classes provide. """ import os import pdb from xml.parsers.expat import ExpatError from generated import compound class Base(object): class Duplicate(StandardError): pass class NoSuchMember(StandardError): pass class ParsingError(StandardError): pass def __init__(self, parse_data, top=None): self._parsed = False self._error = False self._parse_data = parse_data self._members = [] self._dict_members = {} self._in_category = {} self._data = {} if top is not None: self._xml_path = top._xml_path # Set up holder of references else: top = self self._refs = {} self._xml_path = parse_data self.top = top @classmethod def from_refid(cls, refid, top=None): """ Instantiate class from a refid rather than parsing object. """ # First check to see if its already been instantiated. if top is not None and refid in top._refs: return top._refs[refid] # Otherwise create a new instance and set refid. inst = cls(None, top=top) inst.refid = refid inst.add_ref(inst) return inst @classmethod def from_parse_data(cls, parse_data, top=None): refid = getattr(parse_data, 'refid', None) if refid is not None and top is not None and refid in top._refs: return top._refs[refid] inst = cls(parse_data, top=top) if refid is not None: inst.refid = refid inst.add_ref(inst) return inst def add_ref(self, obj): if hasattr(obj, 'refid'): self.top._refs[obj.refid] = obj mem_classes = [] def get_cls(self, mem): for cls in self.mem_classes: if cls.can_parse(mem): return cls raise StandardError(("Did not find a class for object '%s'." \ % (mem.get_name()))) def convert_mem(self, mem): try: cls = self.get_cls(mem) converted = cls.from_parse_data(mem, self.top) if converted is None: raise StandardError('No class matched this object.') self.add_ref(converted) return converted except StandardError, e: print e @classmethod def includes(cls, inst): return isinstance(inst, cls) @classmethod def can_parse(cls, obj): return False def _parse(self): self._parsed = True def _get_dict_members(self, cat=None): """ For given category a dictionary is returned mapping member names to members of that category. For names that are duplicated the name is mapped to None. """ self.confirm_no_error() if cat not in self._dict_members: new_dict = {} for mem in self.in_category(cat): if mem.name() not in new_dict: new_dict[mem.name()] = mem else: new_dict[mem.name()] = self.Duplicate self._dict_members[cat] = new_dict return self._dict_members[cat] def in_category(self, cat): self.confirm_no_error() if cat is None: return self._members if cat not in self._in_category: self._in_category[cat] = [mem for mem in self._members if cat.includes(mem)] return self._in_category[cat] def get_member(self, name, cat=None): self.confirm_no_error() # Check if it's in a namespace or class. bits = name.split('::') first = bits[0] rest = '::'.join(bits[1:]) member = self._get_dict_members(cat).get(first, self.NoSuchMember) # Raise any errors that are returned. if member in set([self.NoSuchMember, self.Duplicate]): raise member() if rest: return member.get_member(rest, cat=cat) return member def has_member(self, name, cat=None): try: mem = self.get_member(name, cat=cat) return True except self.NoSuchMember: return False def data(self): self.confirm_no_error() return self._data def members(self): self.confirm_no_error() return self._members def process_memberdefs(self): mdtss = [] for sec in self._retrieved_data.compounddef.sectiondef: mdtss += sec.memberdef # At the moment we lose all information associated with sections. # Sometimes a memberdef is in several sectiondef. # We make sure we don't get duplicates here. uniques = set([]) for mem in mdtss: converted = self.convert_mem(mem) pair = (mem.name, mem.__class__) if pair not in uniques: uniques.add(pair) self._members.append(converted) def retrieve_data(self): filename = os.path.join(self._xml_path, self.refid + '.xml') try: self._retrieved_data = compound.parse(filename) except ExpatError: print('Error in xml in file %s' % filename) self._error = True self._retrieved_data = None def check_parsed(self): if not self._parsed: self._parse() def confirm_no_error(self): self.check_parsed() if self._error: raise self.ParsingError() def error(self): self.check_parsed() return self._error def name(self): # first see if we can do it without processing. if self._parse_data is not None: return self._parse_data.name self.check_parsed() return self._retrieved_data.compounddef.name