private @Nullable XmlAttributeDelegate.VolatileState recalculate() {
    XmlAttributeValue value = myAttribute.getValueElement();
    if (value == null) return null;
    PsiElement firstChild = value.getFirstChild();
    if (firstChild == null) return null;
    ASTNode child = firstChild.getNode();
    TextRange valueTextRange = new TextRange(0, value.getTextLength());
    if (child != null && child.getElementType() == XmlTokenType.XML_ATTRIBUTE_VALUE_START_DELIMITER) {
      valueTextRange = new TextRange(child.getTextLength(), valueTextRange.getEndOffset());
      child = child.getTreeNext();
    }
    final IntList gapsStarts = new IntArrayList();
    final IntList gapsShifts = new IntArrayList();
    StringBuilder buffer = new StringBuilder(myAttribute.getTextLength());
    while (child != null) {
      final int start = buffer.length();
      IElementType elementType = child.getElementType();
      if (elementType == XmlTokenType.XML_ATTRIBUTE_VALUE_END_DELIMITER) {
        valueTextRange =
          new TextRange(valueTextRange.getStartOffset(), child.getTextRange().getStartOffset() - value.getTextRange().getStartOffset());
        break;
      }
      if (elementType == XmlTokenType.XML_CHAR_ENTITY_REF) {
        buffer.append(XmlUtil.getCharFromEntityRef(child.getText()));
      }
      else if (elementType == XmlElementType.XML_ENTITY_REF) {
        buffer.append(getEntityValue((XmlEntityRef)child));
      }
      else {
        appendChildToDisplayValue(buffer, child);
      }

      int end = buffer.length();
      int originalLength = child.getTextLength();
      if (end - start != originalLength) {
        gapsStarts.add(start);
        gapsShifts.add(originalLength - (end - start));
      }
      child = child.getTreeNext();
    }
    int[] gapDisplayStarts = ArrayUtil.newIntArray(gapsShifts.size());
    int[] gapPhysicalStarts = ArrayUtil.newIntArray(gapsShifts.size());
    int currentGapsSum = 0;
    for (int i = 0; i < gapDisplayStarts.length; i++) {
      currentGapsSum += gapsShifts.getInt(i);
      gapDisplayStarts[i] = gapsStarts.getInt(i);
      gapPhysicalStarts[i] = gapDisplayStarts[i] + currentGapsSum;
    }
    final XmlAttributeDelegate.VolatileState
      volatileState = new XmlAttributeDelegate.VolatileState(buffer.toString(), gapDisplayStarts, gapPhysicalStarts, valueTextRange);
    myVolatileState = volatileState;
    return volatileState;
  }
private @Nullable XmlAttributeDelegate.VolatileState recalculate()