@Override
    public void characters(final char ch[], final int start, final int length) {

      TemplatePreprocessingReader.removeEntitySubstitutions(ch, start, length);
      if (this.cdataMode) {

        while (this.cdataBufferLen + length > this.cdataBuffer.length) {
          this.cdataBuffer = ArrayUtils.copyOf(this.cdataBuffer, this.cdataBuffer.length * 2);
        }
        System.arraycopy(ch, start, this.cdataBuffer, this.cdataBufferLen, length);
        this.cdataBufferLen += length;

      } else {

        while (this.textBufferLen + length > this.textBuffer.length) {
          this.textBuffer = ArrayUtils.copyOf(this.textBuffer, this.textBuffer.length * 2);
        }
        System.arraycopy(ch, start, this.textBuffer, this.textBufferLen, length);
        this.textBufferLen += length;
      }
    }
    @Override
    public void comment(final char[] ch, final int start, final int length) throws SAXException {

      if (!this.dtdMode) {

        final Comment comment = new Comment(ArrayUtils.copyOfRange(ch, start, start + length));

        if (this.elementStack.isEmpty()) {
          this.rootNodes.add(comment);
        } else {
          final NestableNode parent = this.elementStack.peek();
          parent.addChild(comment);
        }
      }
    }
    /*
     * Buffer is used for accumulating text that is read in between elements,
     * and should be flushed before creating any non-text element.
     *
     * Note there also is a 'CDATA' buffer, similar in use to this, containing
     * all the contents read for a CDATA section.
     */
    private void flushBuffer() {

      if (this.textBufferLen > 0) {

        final Node textNode =
            new Text(ArrayUtils.copyOf(this.textBuffer, this.textBufferLen), false);

        if (this.elementStack.isEmpty()) {
          this.rootNodes.add(textNode);
        } else {
          final NestableNode parent = this.elementStack.peek();
          parent.addChild(textNode);
        }

        this.textBufferLen = 0;
      }
    }
    @Override
    public void endCDATA() throws SAXException {

      super.endCDATA();

      this.cdataMode = false;
      if (this.cdataBufferLen > 0) {
        Node cdata =
            new CDATASection(ArrayUtils.copyOf(this.cdataBuffer, this.cdataBufferLen), false);
        if (this.elementStack.isEmpty()) {
          this.rootNodes.add(cdata);
        } else {
          final NestableNode parent = this.elementStack.peek();
          parent.addChild(cdata);
        }
        this.cdataBufferLen = 0;
      }
    }