protected boolean getRegionText(BaleRegion br, int off, int len, Segment text)
      throws BadLocationException {
    int soff = br.getStart();
    int eoff = br.getEnd();

    if (region_map != null) {
      validateRegions();
      RegionData rd = region_map.get(br);
      Segment rtext = new Segment();
      base_document.getText(soff, eoff - soff, rtext);
      return rd.getText(rtext, off, len, text);
    }

    int doff = off + soff;
    int dlen = len;

    boolean lastbad = false;
    if (br.includesEol()) {
      if (doff + dlen > eoff) dlen = eoff - off;
      base_document.getText(doff, len, text);
    } else {
      if (doff + dlen > eoff) dlen = eoff - off + 1;
      base_document.getText(doff, len, text);
      lastbad = (doff + len > eoff); // if last character might be bad
    }

    return lastbad;
  }
  /** ***************************************************************************** */
  protected int getRegionLength(BaleRegion br) {
    int len = br.getEnd() - br.getStart();

    if (!br.includesEol()) len += 1;

    if (region_map != null) {
      validateRegions();
      RegionData rd = region_map.get(br);
      len += rd.getDeltaLength();
    }

    return len;
  }
    void fixupOffset(BaleRegion br, int off) {
      // get offset in region given offset in view]
      if (num_lines == 0) return;
      int spos = 0;
      int line = 0;
      for (int i = 1; i < num_lines; ++i) {
        int npos =
            spos
                + line_data[i].getOffset()
                - line_data[i - 1].getOffset()
                + line_data[i - 1].getViewDelta();
        if (off < npos) break;
        spos = npos;
        line = i;
      }
      int delta = off - spos - line_data[line].getAdd();
      if (delta >= 0) return;

      int off0 = line_data[line].getOffset() + br.getStart();
      int len0 = line_data[line].getDelete();
      int len1 = line_data[line].getAdd();
      if (len0 != 0) len1 += indent_size;
      try {
        if (len1 > 0) {
          String s = "";
          for (int i = 0; i < len1; ++i) s += " ";
          base_document.insertString(off0, s, null);
        }
        if (len0 > 0) base_document.remove(off0 + len1, len0);
      } catch (BadLocationException e) {
        BoardLog.logE("BALE", "Problem fixing line indent", e);
        delta = 0;
      }
      regions_valid = false;
    }
 IndentPosition(BaleRegion br, int off) throws BadLocationException {
   RegionData rd = region_map.get(br);
   validateRegions();
   int roff = rd.getRegionOffset(br, off);
   int voff = rd.getViewOffset(roff);
   local_offset = off - voff;
   base_position = base_document.createPosition(br.getStart() + roff);
 }
  private void validateRegions() {
    if (region_map == null) return;

    synchronized (region_map) {
      if (regions_valid) return;

      Segment s = new Segment();

      region_map.clear();

      for (BaleRegion br : fragment_regions) {
        RegionData rd = new RegionData(indent_size);
        region_map.put(br, rd);
        int soff = br.getStart();
        int eoff = br.getEnd();
        if (eoff < soff) continue;
        try {
          base_document.getText(soff, eoff - soff, s);
        } catch (BadLocationException e) {
          BoardLog.logE("BALE", "Problem getting region indentation", e);
          continue;
        }
        int ln = s.length();

        LineData ld;
        boolean sol = true;
        for (int i = 0; i < ln; ++i) {
          char c = s.charAt(i);
          if (sol) {
            ld = computeLineData(i, s);
            rd.add(ld);
            sol = false;
          }
          if (c == '\n') sol = true;
        }
        if (!sol) {
          ld = computeLineData(ln, s);
          rd.add(ld);
          ++ln;
        }
      }

      regions_valid = true;
    }
  }
  /** ***************************************************************************** */
  private void setupSpacing() {
    // get the tab_size from appropriate property
    BoardProperties bp = BoardProperties.getProperties("Bale");
    String v = bp.getProperty("indent.tabulation.size");
    if (v == null)
      v = BumpClient.getBump().getOption("org.eclipse.jdt.core.formatter.tabulation.size");
    if (v == null) v = bp.getProperty("Bale.tabsize");
    tab_size = 8;
    try {
      if (v != null) tab_size = Integer.parseInt(v);
    } catch (NumberFormatException e) {
    }

    region_map = null;
    regions_valid = true;
    indent_size = 0;
    indent_string = null;

    // determine minimum indent
    Segment s = new Segment();
    int minsp = -1;
    for (BaleRegion br : fragment_regions) {
      int soff = br.getStart();
      int eoff = br.getEnd();
      try {
        base_document.getText(soff, eoff - soff, s);
      } catch (BadLocationException e) {
        BoardLog.logE(
            "BALE",
            "Problem getting region text "
                + soff
                + " "
                + eoff
                + " "
                + s.length()
                + " "
                + base_document.getLength(),
            e);
        // should this be "throw e;"
        continue;
      }

      int ln = s.length();

      boolean sol = true;
      int ind = 0;
      for (int i = 0; i < ln && (minsp < 0 || minsp >= MIN_INDENT); ++i) {
        char c = s.charAt(i);
        if (sol) {
          switch (c) {
            case ' ':
              ind += 1;
              break;
            case '\t':
              ind = nextTabPosition(ind);
              break;
            case '\n':
              ind = 0;
              break;
            default:
              if (minsp < 0 || minsp > ind) minsp = ind;
              sol = false;
              break;
          }
        } else if (c == '\n') {
          sol = true;
          ind = 0;
        }
      }

      if (minsp >= 0 && minsp < MIN_INDENT) break;
    }

    if (minsp <= 0 || minsp < MIN_INDENT) return;

    indent_size = minsp;
    indent_string = "";
    for (int i = 0; i < minsp; ++i) indent_string += " ";

    region_map = new HashMap<BaleRegion, RegionData>();
    regions_valid = false;
    for (BaleRegion br : fragment_regions) {
      for (int i = getRegionLength(br) - 1; i >= 0; --i) {
        fixupOffset(br, i, true);
      }
    }
  }