/**
   * Decide whether the current title record should be displayed, based on whether it is a duplicate
   * of the previous record, given the combination of output fields included in the ordering. If
   * range fields are included in the output, or if identifying properties change between titles,
   * the title will be shown.
   *
   * <p>If there are neither range fields nor id fields in the output, we can't tell if it is a
   * duplicate so we show it anyway. Output with no id fields is pretty meaningless though.
   *
   * <p>Note that the method also sets the last output title if the response is true. Thus calling
   * this method multiple times on the same title will give false after the first (unless there are
   * no identifying fields).
   *
   * @param currentTitle the current title
   * @return whether to show currentTitle
   */
  public boolean isTitleForOutput(KbartTitle currentTitle) {
    // The approach is to trueify this variable. If it becomes true, at the
    // end of the method the lastOutputTitle is set before the result is
    // returned. Do not return early!
    boolean isOutput = false;
    // Show title if it has no range or id fields by which we can decide duplicates
    if (!rangeFieldsIncludedInDisplay && !idFieldsIncludedInDisplay) {
      isOutput = true;
    }

    // Show the title if it is the first or the output includes range fields
    if (lastOutputTitle == null || rangeFieldsIncludedInDisplay) {
      isOutput = true;
    } else if (!isOutput) { // don't do this check if we've already trueified the var
      // At this point there are no range fields and this is not the first title
      // Show the title if any visible idField differs between titles
      for (Field f : idFields) {
        if (visibleColumnOrdering.getFields().contains(f)
            && !lastOutputTitle.getField(f).equals(currentTitle.getField(f))) {
          isOutput = true;
          break;
        }
      }
    }
    // Finally, we refuse to output the title if it has no id and
    // excludeNoIdTitles is true.
    if (excludeNoIdTitles && StringUtil.isNullString(currentTitle.getField(Field.TITLE_ID)))
      isOutput = false;

    // Record the previous title
    if (isOutput) lastOutputTitle = currentTitle;
    return isOutput;
  }
 /**
  * Calculate which fields have no values across the whole range of titles. Iterates through all
  * the titles for each field value, until an entry is found or the end of the title list is
  * reached.
  *
  * @return a set of fields which are empty
  */
 private EnumSet<Field> findEmptyFields() {
   long s = System.currentTimeMillis();
   EnumSet<Field> empty = EnumSet.allOf(Field.class);
   for (Field f : Field.getFieldSet()) {
     // Check if any title has a value for this field
     for (KbartTitle kbt : titles) {
       if (kbt.hasFieldValue(f)) {
         empty.remove(f);
         break;
       }
     }
   }
   // Log the time, as this is an expensive way to monitor empty fields.
   // It should be done somehow during title conversion.
   log.debug("findEmptyFields() took " + (System.currentTimeMillis() - s) + "s");
   return empty;
 }
 /**
  * Get the value for a particular column, which is either the value of a field of the title, or
  * a constant column value.
  *
  * @param title a KbartTitle providing values for the current row
  * @return the value of a field in the title, or the label of a constant column
  */
 public String getValue(KbartTitle title) {
   return isField() ? title.getField(field) : label;
 }