def _populate(self, entry, stack, intermediates): """Walk down all entries contained within this entry, checking for visibility.""" if entry in stack: # An entry is within itself. This entry will be 'visible' either if # it itself is visible, or it contains a visible child, but at this # stage we don't know for sure (as all of its children haven't been # processed). If so, we'll mark all of its children to a list that # requires them to be re-processed. if not self._has_data[entry]: for inter in stack[stack.index(entry)+1:]: intermediates.add(inter) return if entry in self._has_data: return # This entry isn't yet calculated; do so now. self._has_data[entry] = not entry.is_hidden() stack.append(entry) for child in entry.children: assert child not in self._parents, \ "Found child '%s' in parents '%s' and '%s'! Entries " \ "cannot be repeated unless they are common entries." % ( child, entry, self._parents[child]) self._parents[child] = entry self._populate(child.entry, stack, intermediates) stack.remove(entry) if not isinstance(entry, chc.Choice) and not entry.is_hidden(): # We are visible; check to see if either we (or any of our # children) contain data. for child in entry.children: if not ent.is_hidden(child.name) and self._has_data[child.entry]: break else: # None of the children contain data; this entry will only contain # data if implicitly has data itself. if isinstance(entry, fld.Field) or \ (isinstance(entry, seq.Sequence) and entry.value is not None): # This entry's children don't contain data, but it appears to # have some implicit data. for constraint in entry.constraints: if isinstance(constraint, Equals): # The 'implicit' data has an expected value, so it # isn't considered as new data. self._has_data[entry] = False break elif isinstance(entry, sof.SequenceOf) and \ not ent.is_hidden(entry.children[0].name): self._has_data[entry] = True else: self._has_data[entry] = False if not self._has_data[entry]: self._hide_children(entry)
def _populate_visible(self, entry, common, entries, visible=True): if entry in entries: return if entry in common: # Common entries are visible if their name is public, regardless of # what their parents do. visible = not ent.is_hidden(entry.name) else: # Entries that aren't common are visible if both they and their parents # are visible. visible &= not ent.is_hidden(entry.name) entries[entry] = not visible for child in entry.children: self._populate_visible(child.entry, common, entries, visible)
def child_has_data(self, child): """Does a bdec.entry.Child instance contain data. This is different from contains_data when a referenced entry has been hidden.""" entry = self._parents[child] return self.contains_data(entry) and not ent.is_hidden(child.name) \ and self._has_data[child.entry]
def get_instance(items): """Convert an iterable list of decode items to a python instance.""" stack = [_DecodedItem(None)] for is_starting, name, entry, data, value in items: if is_starting: stack.append(_DecodedItem(entry)) else: item = stack.pop() if not ent.is_hidden(name): stack[-1].add_entry(name, item.get_value(value)) assert len(stack) == 1 return stack[0].get_value(None)
def to_file(decoder, binary, output, encoding="utf-8", verbose=False): handler = _XMLGenerator(output, encoding) offset = 0 is_first = True hidden_count = 0 has_children = False for is_starting, name, entry, data, value in decoder.decode(binary): # If we have an entry that is hidden, all entries under that should # also be hidden. if is_starting: if hidden_count or ent.is_hidden(name): hidden_count += 1 is_hidden = hidden_count != 0 if not is_starting and hidden_count: hidden_count -= 1 if not verbose and (is_hidden or isinstance(entry, chc.Choice)): # By default, we don't output hidden or choice entries. continue if is_starting: if not is_first: _print_whitespace(handler, offset) is_first = False handler.startElement(escape_name(name), xml.sax.xmlreader.AttributesImpl({})) offset = offset + 4 has_children = False else: # An element is ending; we only include the surrounding whitespace # if the entry has visible children (otherwise we try an keep the # value compact with the entries). This means strings with leading # and trailing whitespace can be represented (and produces nicer # xml). if value is not None and not _has_expected_value(entry): if has_children: _print_whitespace(handler, offset) text = xml_strip(unicode(value)) handler.characters(text) if verbose and data: handler.comment(str(data)) offset = offset - 4 if has_children: _print_whitespace(handler, offset) handler.endElement(escape_name(name)) has_children = True handler.ignorableWhitespace('\n')
def decode(decoder, binary): """ Create a python instance representing the decoded data. """ stack = [_DecodedItem(None)] for is_starting, name, entry, data, value in decoder.decode(binary): if is_starting: stack.append(_DecodedItem(entry)) else: item = stack.pop() if not ent.is_hidden(name): stack[-1].add_entry(name, item.get_value(value)) assert len(stack) == 1 return stack[0].get_value(None)
def parseString(self, text): if isinstance(text, unicode): text = text.encode("ascii") token_stack = [[]] name_stack = [{}] for is_starting, name, entry, data, value in self._decode(text, "<string>"): if is_starting: token_stack.append([]) name_stack.append({}) else: if name and not is_hidden(name) and value is not None: token_stack[-1].append(value) names = name_stack.pop() tokens = ParseResults(token_stack.pop(), names) for action in getattr(entry, "actions", []): tokens = action(tokens) if not isinstance(tokens, ParseResults): if not isinstance(tokens, list): tokens = [tokens] names = {} tokens = ParseResults(tokens, names) if name is not None and not is_hidden(name): # FIXME: This isn't entirely correct, as it includes the # default names we use in the decoder (such as 'and', # 'or', etc), instead of just the user defined names. name_stack[-1][name] = tokens name_stack[-1].update(names) # Extend the current tokens list with the child tokens token_stack[-1] += tokens assert len(token_stack) == 1 assert len(name_stack) == 1 return ParseResults(token_stack.pop(), name_stack.pop())
def _mock_query(parent, entry, offset, name): """A mock query object to return data for hidden common entries. It will return null data for fields, etc.""" if isinstance(parent, (int, long)) or parent is None: raise MissingInstanceError(parent, entry) try: return parent[name] except KeyError: pass # Check to see if it's a compound name... result = {} for param, value in parent.items(): names = param.split('.') if names[0] == name: child_name = '.'.join(names[1:]) result[child_name] = value if result: return result if is_hidden(entry.name): raise MissingInstanceError(parent, entry) # We have to return some suitable data for these entries; return a null # data object. if isinstance(entry, Field): length = entry.length.evaluate({}) if entry.format == Field.INTEGER: return 0 elif entry.format == Field.TEXT: return '' elif entry.format == Field.FLOAT: return 0.0 else: return Data('\x00' * (length / 8 + 1), length) elif isinstance(entry, Sequence): return MockSequenceValue() elif isinstance(entry, SequenceOf): return [] elif isinstance(entry, Choice): return {} else: raise NotImplementedError('Unknown entry %s to mock data for...' % entry)
def _decode_visible(spec, data): """ Use the spec to decode the given data, returning all visible entries. """ hidden_count = 0 for (is_starting, name, entry, data, value) in spec.decode(data): is_visible = True if isinstance(entry, Choice): # Choice entries aren't printed... is_visible = False if is_starting: if hidden_count or is_hidden(name): # This entry is hidden; all child entries should also be # hidden, even if their names are visible. hidden_count += 1 is_visible = False else: if hidden_count: # We are finishing a hidden entry... hidden_count -= 1 is_visible = False if is_visible: yield is_starting, name, entry, data, value
def endElement(self, name): assert self._stack[-1][0] == name (name, attrs, breaks, lineno, colno) = self._stack.pop() # We don't pop the children item until after we have called the # handler, as it may be used when creating a value reference. children = self._children[-1] length = None if attrs.has_key('length'): length = self._parse_expression(attrs['length']) entry_name = "" if attrs.has_key('name'): entry_name = attrs['name'] entry = self._handlers[name](attrs, children, entry_name, length, breaks) # Check for value constraints constraints = [] if attrs.has_key('min'): minimum = self._parse_expression(attrs['min']) constraints.append(Minimum(minimum)) if attrs.has_key('max'): maximum = self._parse_expression(attrs['max']) constraints.append(Maximum(maximum)) if attrs.has_key('expected'): expected = self._parse_expression(attrs['expected']) constraints.append(Equals(expected)) if name == 'field' and not isinstance(entry, fld.Field) and attrs.has_key('value'): # Fields that are long integers can be internally converted to # references to Sequences; we need to handle the expected values. expected = self._parse_expression(attrs['value']) constraints.append(Equals(expected)) if constraints: if isinstance(entry, ReferencedEntry): # We found a reference with constraints; create an intermediate # sequence that will have the constraints. reference = entry if not ent.is_hidden(reference.name): reference.name = '%s:' % reference.name value = self._parse_expression("${%s}" % reference.name) entry = seq.Sequence(entry_name, [reference], value=value) entry.constraints += constraints self._children.pop() if attrs.has_key('if'): # This is a 'conditional' entry; only present if the expression in # 'if' is true. To decode this, we create a choice with a 'not # present' option; this option attempts to decode first, with the # condition inverted. try: not_present = exp.parse_conditional_inverse(attrs['if']) not_present.name = 'not present:' except exp.ExpressionError, ex: raise XmlExpressionError(ex, self._filename, self.locator) assert isinstance(not_present, ent.Entry) self.lookup[not_present] = (self._filename, lineno, colno) self.lookup[entry] = (self._filename, lineno, colno) name = 'optional %s' % entry_name if entry_name else 'optional:' optional = chc.Choice(name, [not_present, entry]) entry = optional
def contains_data(self, entry): """Does an entry contain data.""" return not ent.is_hidden(entry.name) and self._has_data[entry]