Example #1
0
  def _execute(self, modify_how, what):
    """Executes this BlipRefs object.

    Args:
      modify_how: What to do. Any of the operation declared at the top.
      what: Depending on the operation. For delete, has to be None.
            For the others it is a singleton, a list or a function returning
            what to do; for ANNOTATE tuples of (key, value), for the others
            either string or elements.
            If what is a function, it takes three parameters, the content of
            the blip, the beginning of the matching range and the end.
    Raises:
      IndexError when trying to access content outside of the blip.
      ValueError when called with the wrong values.
    Returns:
      self for chainability.
    """
    blip = self._blip

    if modify_how != BlipRefs.DELETE:
      if type(what) != list:
        what = [what]
      next_index = 0

    matched = []
    # updated_elements is used to store the element type of the
    # element to update
    updated_elements = []
    
    # For now, if we find one markup, we'll use it everywhere.
    next = None
    hit_found = False

    for start, end in self._hits():
      hit_found = True
      if start < 0:
        start += len(blip)
        if end == 0:
          end += len(blip)
      if end < 0:
        end += len(blip)
      if len(blip) == 0:
        if start != 0 or end != 0:
          raise IndexError('Start and end have to be 0 for empty document')
      elif start < 0 or end < 1 or start >= len(blip) or end > len(blip):
        raise IndexError('Position outside the document')
      if modify_how == BlipRefs.DELETE:
        for i in range(start, end):
          if i in blip._elements:
            del blip._elements[i]
        blip._delete_annotations(start, end)
        blip._shift(end, start - end)
        blip._content = blip._content[:start] + blip._content[end:]
      else:
        if callable(what):
          next = what(blip._content, start, end)
          matched.append(next)
        else:
          next = what[next_index]
          next_index = (next_index + 1) % len(what)
        if isinstance(next, str):
          next = next.decode('utf-8')
        if modify_how == BlipRefs.ANNOTATE:
          key, value = next
          blip.annotations._add_internal(key, value, start, end)
        elif modify_how == BlipRefs.CLEAR_ANNOTATION:
          blip.annotations._delete_internal(next, start, end)
        elif modify_how == BlipRefs.UPDATE_ELEMENT:
          el = blip._elements.get(start)
          if not element:
            raise ValueError('No element found at index %s' % start)
          # the passing around of types this way feels a bit dirty:
          updated_elements.append(element.Element(el.type, properties=next))
          for k, b in next.items():
            setattr(el, k, b)
        else:
          if modify_how == BlipRefs.INSERT:
            end = start
          elif modify_how == BlipRefs.INSERT_AFTER:
            start = end
          elif modify_how == BlipRefs.REPLACE:
            pass
          else:
            raise ValueError('Unexpected modify_how: ' + modify_how)

          if isinstance(next, element.Element):
            text = ' '
          else:
            text = next

          # in the case of a replace, and the replacement text is shorter,
          # delete the delta.
          if start != end and len(text) < end - start:
            blip._delete_annotations(start + len(text), end)

          blip._shift(end, len(text) + start - end)
          blip._content = blip._content[:start] + text + blip._content[end:]

          if isinstance(next, element.Element):
            blip._elements[start] = next

    # No match found, return immediately without generating op.
    if not hit_found:
      return

    operation = blip._operation_queue.document_modify(blip.wave_id,
                                                      blip.wavelet_id,
                                                      blip.blip_id)
    for param, value in self._params.items():
      operation.set_param(param, value)

    modify_action = {'modifyHow': modify_how}
    if modify_how == BlipRefs.DELETE:
      pass
    elif modify_how == BlipRefs.UPDATE_ELEMENT:
      modify_action['elements'] = updated_elements
    elif (modify_how == BlipRefs.REPLACE or
          modify_how == BlipRefs.INSERT or
          modify_how == BlipRefs.INSERT_AFTER):
      if callable(what):
        what = matched
      if what:
        if not isinstance(next, element.Element):
          modify_action['values'] = [util.force_string(value) for value in what]
        else:
          modify_action['elements'] = what
    elif modify_how == BlipRefs.ANNOTATE:
      modify_action['values'] = [x[1] for x in what]
      modify_action['annotationKey'] = what[0][0]
    elif modify_how == BlipRefs.CLEAR_ANNOTATION:
      modify_action['annotationKey'] = what[0]
    operation.set_param('modifyAction', modify_action)

    return self
Example #2
0
 def testForceString(self):
   self.assertEquals("aaa", util.force_string("aaa"))
   self.assertEquals("12", util.force_string(12))
   self.assertEquals(u'\u30e6\u30cb\u30b3\u30fc\u30c9',
                     util.force_string(u'\u30e6\u30cb\u30b3\u30fc\u30c9'))