/**
   * This method gets the list of update candidates, validates it, removes any failed candidates
   * from the update list, then updates the database with the [validated] update candidate list.
   *
   * @return The number of records updated
   */
  public int validateAndUpdateCandidates() {
    int updateCount = 0;
    List<BibliosDAO> bibliosList =
        bibliosManager.getUpdateCandidateBiblios(); // Get update candidates.
    Map<Integer, BibliosDAO> errorMap = validator.validate(bibliosList); // Validate the list.

    if (!bibliosList.isEmpty()) { // If there are update candidates ...
      if (!errorMap.isEmpty()) { //     If there were validation errors ...
        Iterator<BibliosDAO> iterator = bibliosList.iterator();
        while (iterator.hasNext()) { //     ... remove the failed candidates from the update list.
          BibliosDAO biblio = iterator.next();
          int pk = biblio.getId_biblio();
          if (errorMap.containsKey(pk)) {
            Object[] parms = new Object[] {biblio.getId_biblio()};
            String logMsg = messages.getMessage("Biblios.RemoveFailedCandidate", parms, Locale.UK);
            logger.warn(logMsg);
            iterator.remove();
          }
        }
      }

      bibliosList =
          updateBibliosFromWebService(
              bibliosList); // Update any biblios with valid pubmed_ids from web service.
      updateCount = save(bibliosList); // Save the changes.
    }

    return updateCount;
  }
 /** Test validation: invalid year. */
 @Test
 public void testPubmed_idWithWhitespace() {
   System.out.println("testPubmed_idWithWhitespace");
   EmmaBiblioValidator instance = new EmmaBiblioValidator();
   List<BibliosDAO> inputList = new ArrayList<>();
   BibliosDAO dao = new BibliosDAO();
   inputList.add(dao); // Add an empty dao to the list just to make the list longer than 1 element.
   dao = new BibliosDAO();
   dao.setId_biblio(777);
   dao.setPubmed_id(
       "   12345  "); // NOTE: Leading or trailing whitespace fails the int conversion.
   inputList.add(dao);
   Map<Integer, BibliosDAO> actuals;
   actuals = instance.validate(inputList);
   assert (actuals.isEmpty());
 }
 /** Test validation: invalid year. */
 @Test
 public void testInvalidPubmed_id() {
   System.out.println("testInvalidPubmed_id");
   EmmaBiblioValidator instance = new EmmaBiblioValidator();
   List<BibliosDAO> inputList = new ArrayList<>();
   BibliosDAO dao = new BibliosDAO();
   inputList.add(dao); // Add an empty dao to the list just to make the list longer than 1 element.
   dao = new BibliosDAO();
   dao.setId_biblio(888);
   dao.setPubmed_id("invalid number");
   inputList.add(dao);
   Map<Integer, BibliosDAO> actuals;
   actuals = instance.validate(inputList);
   assert (actuals.size() == 1);
   BibliosDAO resultDAO = actuals.get(888);
   assertEquals(dao, resultDAO);
 }
    /**
     * Save the given biblio record in the database. Transaction management is the caller's
     * responsibility.
     *
     * @param bibliosDAO the data to be saved
     */
    @Override
    public void save(BibliosDAO bibliosDAO) {
      java.sql.Date sqlDate = Utils.tryParseToDbDate(bibliosDAO.getLast_change());

      final String statement =
          "UPDATE biblios SET title = ?, author1 = ?, author2 = ?, year = ?, journal = ?, username = ?, "
              + "volume = ?, pages = ?, pubmed_id = ?, updated = ?, last_change = ?, notes = ?"
              + "WHERE id_biblio = ?";

      jdbcTemplate.update(
          statement,
          new Object[] {
            bibliosDAO.getTitle(),
            bibliosDAO.getAuthor1(),
            bibliosDAO.getAuthor2(),
            bibliosDAO.getYear(),
            bibliosDAO.getJournal(),
            bibliosDAO.getUsername(),
            bibliosDAO.getVolume(),
            bibliosDAO.getPages(),
            bibliosDAO.getPubmed_id(),
            bibliosDAO.getUpdated(),
            sqlDate,
            bibliosDAO.getNotes(),
            bibliosDAO.getId_biblio()
          });
    }
  /**
   * This method replaces any null <code>BibliosDAO</code> fields with the string '&lt;null&gt;' so
   * that proper length computations and display can be performed against null fields. A throw-away
   * copy of the biblio is returned and may be used for field length computations and display
   * without fear of null pointer exceptions.
   *
   * @param biblio the biblio record to be patched
   * @return a new <code>BibliosDAO</code> instance with any null fields replaced by the string
   *     '&lt;null&gt;'
   */
  private BibliosDAO patchNullFields(BibliosDAO biblio) {
    BibliosDAO newBiblio = new BibliosDAO();

    newBiblio.setId_biblio(biblio.getId_biblio());
    newBiblio.setPubmed_id(biblio.getPubmed_id() == null ? "<null>" : biblio.getPubmed_id());
    newBiblio.setYear(biblio.getYear() == null ? "<null>" : biblio.getYear());
    newBiblio.setJournal(biblio.getJournal() == null ? "<null>" : biblio.getJournal());
    newBiblio.setVolume(biblio.getVolume() == null ? "<null>" : biblio.getVolume());
    newBiblio.setPages(biblio.getPages() == null ? "<null>" : biblio.getPages());
    newBiblio.setUsername(biblio.getUsername() == null ? "<null>" : biblio.getUsername());
    newBiblio.setUpdated(biblio.getUpdated() == null ? "<null>" : biblio.getUpdated());
    newBiblio.setLast_change(biblio.getLast_change() == null ? "<null>" : biblio.getLast_change());
    newBiblio.setTitle(biblio.getTitle() == null ? "<null>" : biblio.getTitle());
    newBiblio.setAuthor1(biblio.getAuthor1() == null ? "<null>" : biblio.getAuthor1());
    newBiblio.setAuthor2(biblio.getAuthor2() == null ? "<null>" : biblio.getAuthor2());

    return newBiblio;
  }
  /**
   * Given a <code>List&lt;BibliosDAO&gt;</code>, this method returns a new list, which is a copy of
   * the original list, with updates applied from the biblios web service for valid pubmed_id
   * values.
   *
   * @param oldBiblioList the original list of biblios
   * @return a new list, which is a copy of the original list, with updates applied from the biblios
   *     web service for valid pubmed_id values
   */
  private List<BibliosDAO> createNewBiblioListFromWebService(List<BibliosDAO> oldBiblioList) {
    String volume;
    List<BibliosDAO> newBiblioList = new ArrayList<>();
    DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    String last_change = dateFormat.format(new Date());

    for (BibliosDAO oldBiblio : oldBiblioList) {
      BibliosDAO newBiblio = new BibliosDAO(); // Clone the original BibliosDAO.
      newBiblio.setAuthor1(oldBiblio.getAuthor1());
      newBiblio.setAuthor2(oldBiblio.getAuthor2());
      newBiblio.setId_biblio(oldBiblio.getId_biblio());
      newBiblio.setJournal(oldBiblio.getJournal());
      newBiblio.setLast_change(last_change);
      newBiblio.setNotes(oldBiblio.getNotes());
      newBiblio.setPages(oldBiblio.getPages());
      newBiblio.setPubmed_id(oldBiblio.getPubmed_id());
      newBiblio.setTitle(oldBiblio.getTitle());
      newBiblio.setUpdated(oldBiblio.getUpdated());
      newBiblio.setUsername(oldBiblio.getUsername());
      newBiblio.setVolume(oldBiblio.getVolume());
      newBiblio.setYear(oldBiblio.getYear());

      Integer pubmed_id =
          Utils.tryParseInt(
              oldBiblio
                  .getPubmed_id()); // If we have a valid pubmed_id, load the new BibliosDAO with
      // the paper data.
      if (pubmed_id != null) {
        FetchBiblio paperFromWs = fetchPaper(pubmed_id);
        if (paperFromWs != null) {
          if (!paperFromWs.issue.isEmpty()) {
            volume = paperFromWs.volume + "(" + paperFromWs.issue + ")";
          } else {
            volume = paperFromWs.volume;
          }

          newBiblio.setTitle(paperFromWs.title);
          newBiblio.setAuthor1(paperFromWs.author1);
          newBiblio.setAuthor2(paperFromWs.author2);
          newBiblio.setYear("" + paperFromWs.year);
          newBiblio.setJournal(paperFromWs.journal);
          newBiblio.setVolume(volume);
          newBiblio.setPages(paperFromWs.pages);
          newBiblio.setUsername("EMMA");
          newBiblio.setUpdated("Y");

          newBiblioList.add(newBiblio);
        }
      }
    }

    return newBiblioList;
  }
  /**
   * This method computes the maximum field widths for all logged fields. It is needed for proper
   * logfile formatting output for correctly aligning the old and new <code>BibliosDAO</code>
   * records.
   *
   * @param oldBiblio the old [existing] biblio record
   * @param newBiblio the new biblio record
   * @param allowForQuotes If true, two extra bytes are added to each required field length to
   *     account for leading and trailing quotation marks.
   */
  private void accumulateMaxWidths(BibliosDAO oldBiblio, BibliosDAO newBiblio) {
    String sId_biblio = Integer.toString(oldBiblio.getId_biblio());

    // Add two bytes to each biblio field (old and new) to account for leading and trailing quotes.
    maxWidths[0] = Math.max(Math.max(sId_biblio.length(), heading1[0].length()), maxWidths[0]);
    maxWidths[1] =
        Math.max(
            Math.max(
                Math.max(
                    oldBiblio.getPubmed_id().length() + 2, newBiblio.getPubmed_id().length() + 2),
                heading1[1].length()),
            maxWidths[1]);
    maxWidths[2] =
        Math.max(
            Math.max(
                Math.max(oldBiblio.getYear().length() + 2, newBiblio.getYear().length() + 2),
                heading1[2].length()),
            maxWidths[2]);
    maxWidths[3] =
        Math.max(
            Math.max(
                Math.max(oldBiblio.getJournal().length() + 2, newBiblio.getJournal().length() + 2),
                heading1[3].length()),
            maxWidths[3]);
    maxWidths[4] =
        Math.max(
            Math.max(
                Math.max(oldBiblio.getVolume().length() + 2, newBiblio.getVolume().length() + 2),
                heading1[4].length()),
            maxWidths[4]);
    maxWidths[5] =
        Math.max(
            Math.max(
                Math.max(oldBiblio.getPages().length() + 2, newBiblio.getPages().length() + 2),
                heading1[5].length()),
            maxWidths[5]);
    maxWidths[6] =
        Math.max(
            Math.max(
                Math.max(
                    oldBiblio.getUsername().length() + 2, newBiblio.getUsername().length() + 2),
                heading1[6].length()),
            maxWidths[6]);
    maxWidths[7] =
        Math.max(
            Math.max(
                Math.max(oldBiblio.getUpdated().length() + 2, newBiblio.getUpdated().length() + 2),
                heading1[7].length()),
            maxWidths[7]);
    maxWidths[8] =
        Math.max(
            Math.max(
                Math.max(
                    oldBiblio.getLast_change().length() + 2,
                    newBiblio.getLast_change().length() + 2),
                heading1[8].length()),
            maxWidths[8]);
    maxWidths[9] =
        Math.max(
            Math.max(
                Math.max(oldBiblio.getTitle().length() + 2, newBiblio.getTitle().length()),
                heading1[9].length() + 2),
            maxWidths[9]);
    maxWidths[10] =
        Math.max(
            Math.max(
                Math.max(oldBiblio.getAuthor1().length() + 2, newBiblio.getAuthor1().length()),
                heading1[10].length() + 2),
            maxWidths[10]);
    maxWidths[11] =
        Math.max(
            Math.max(
                Math.max(oldBiblio.getAuthor2().length() + 2, newBiblio.getAuthor2().length()),
                heading1[11].length() + 2),
            maxWidths[11]);
  }