/**
   * @param xmlInputStream An InputStream which must not return more than {@link
   *     MAX_INTRODUCTIONPUZZLE_BYTE_SIZE} bytes.
   */
  public IntroductionPuzzle importIntroductionPuzzle(
      FreenetURI puzzleURI, InputStream xmlInputStream)
      throws SAXException, IOException, InvalidParameterException, UnknownIdentityException,
          IllegalBase64Exception, ParseException {

    xmlInputStream =
        new OneBytePerReadInputStream(
            xmlInputStream); // Workaround for Java bug, see the stream class for explanation

    // May not be accurate by definition of available(). So the JavaDoc requires the callers to obey
    // the size limit, this is a double-check.
    if (xmlInputStream.available() > MAX_INTRODUCTIONPUZZLE_BYTE_SIZE)
      throw new IllegalArgumentException(
          "XML contains too many bytes: " + xmlInputStream.available());

    String puzzleID;
    IntroductionPuzzle.PuzzleType puzzleType;
    String puzzleMimeType;
    Date puzzleValidUntilDate;
    byte[] puzzleData;

    Document xmlDoc;
    synchronized (
        mDocumentBuilder) { // TODO: Figure out whether the DocumentBuilder is maybe synchronized
                            // anyway
      xmlDoc = mDocumentBuilder.parse(xmlInputStream);
    }
    Element puzzleElement = (Element) xmlDoc.getElementsByTagName("IntroductionPuzzle").item(0);

    if (Integer.parseInt(puzzleElement.getAttribute("Version")) > INTRODUCTION_XML_FORMAT_VERSION)
      throw new InvalidParameterException(
          "Version "
              + puzzleElement.getAttribute("Version")
              + " > "
              + INTRODUCTION_XML_FORMAT_VERSION);

    puzzleID = puzzleElement.getAttribute("ID");
    puzzleType = IntroductionPuzzle.PuzzleType.valueOf(puzzleElement.getAttribute("Type"));
    puzzleMimeType = puzzleElement.getAttribute("MimeType");
    synchronized (mDateFormat) {
      puzzleValidUntilDate = mDateFormat.parse(puzzleElement.getAttribute("ValidUntil"));
    }

    Element dataElement = (Element) puzzleElement.getElementsByTagName("Data").item(0);
    puzzleData = Base64.decodeStandard(dataElement.getAttribute("Value"));

    IntroductionPuzzle puzzle;

    synchronized (mWoT) {
      Identity puzzleInserter = mWoT.getIdentityByURI(puzzleURI);
      puzzle =
          new IntroductionPuzzle(
              mWoT,
              puzzleInserter,
              puzzleID,
              puzzleType,
              puzzleMimeType,
              puzzleData,
              IntroductionPuzzle.getDateFromRequestURI(puzzleURI),
              puzzleValidUntilDate,
              IntroductionPuzzle.getIndexFromRequestURI(puzzleURI));

      mWoT.getIntroductionPuzzleStore().storeAndCommit(puzzle);
    }

    return puzzle;
  }