@Override
  public void redo() {
    super.redo();

    // Redo the change.
    try {
      if (newValue == null) {
        entry.clearField(field);
      } else {
        entry.setField(field, newValue);
      }

    } catch (IllegalArgumentException ex) {
      LOGGER.info("Cannot perform redo", ex);
    }
  }
  @Override
  public void undo() {
    super.undo();

    // Revert the change.
    try {
      if (oldValue == null) {
        entry.clearField(field);
      } else {
        entry.setField(field, oldValue);
      }

      // this is the only exception explicitly thrown here
    } catch (IllegalArgumentException ex) {
      LOGGER.info("Cannot perform undo", ex);
    }
  }
  @Override
  public void run() {
    if (!goOn) {
      panel.output(
          Localization.lang("This operation requires one or more entries to be selected."));
      return;
    }
    entriesChangedCount = 0;
    panel.frame().setProgressBarValue(0);
    panel.frame().setProgressBarVisible(true);
    int weightAutoSet = 10; // autoSet takes 10 (?) times longer than checkExisting
    int progressBarMax =
        (autoSet ? weightAutoSet * sel.size() : 0) + (checkExisting ? sel.size() : 0);
    panel.frame().setProgressBarMaximum(progressBarMax);
    int progress = 0;
    final NamedCompound ce = new NamedCompound(Localization.lang("Automatically set file links"));

    Set<BibEntry> changedEntries = new HashSet<>();

    // First we try to autoset fields
    if (autoSet) {
      Collection<BibEntry> entries = new ArrayList<>(sel);

      // Start the automatically setting process:
      Runnable r =
          AutoSetLinks.autoSetLinks(
              entries, ce, changedEntries, null, panel.getBibDatabaseContext(), null, null);
      JabRefExecutorService.INSTANCE.executeAndWait(r);
    }
    progress += sel.size() * weightAutoSet;
    panel.frame().setProgressBarValue(progress);
    // The following loop checks all external links that are already set.
    if (checkExisting) {
      boolean removeAllBroken = false;
      mainLoop:
      for (BibEntry aSel : sel) {
        panel.frame().setProgressBarValue(progress++);
        final String old = aSel.getField(Globals.FILE_FIELD);
        // Check if a extension is set:
        if ((old != null) && !(old.isEmpty())) {
          FileListTableModel tableModel = new FileListTableModel();
          tableModel.setContentDontGuessTypes(old);

          // We need to specify which directories to search in for Util.expandFilename:
          List<String> dirsS = panel.getBibDatabaseContext().getFileDirectory();
          List<File> dirs = new ArrayList<>();
          for (String dirs1 : dirsS) {
            dirs.add(new File(dirs1));
          }

          for (int j = 0; j < tableModel.getRowCount(); j++) {
            FileListEntry flEntry = tableModel.getEntry(j);
            // See if the link looks like an URL:
            boolean httpLink = flEntry.link.toLowerCase(Locale.ENGLISH).startsWith("http");
            if (httpLink) {
              continue; // Don't check the remote file.
              // TODO: should there be an option to check remote links?
            }

            // A variable to keep track of whether this link gets deleted:
            boolean deleted = false;

            // Get an absolute path representation:
            Optional<File> file = FileUtil.expandFilename(flEntry.link, dirsS);
            if ((!file.isPresent()) || !file.get().exists()) {
              int answer;
              if (removeAllBroken) {
                answer = 2; // We should delete this link.
              } else {
                answer =
                    JOptionPane.showOptionDialog(
                        panel.frame(),
                        Localization.lang(
                            "<HTML>Could not find file '%0'<BR>linked from entry '%1'</HTML>",
                            flEntry.link, aSel.getCiteKey()),
                        Localization.lang("Broken link"),
                        JOptionPane.YES_NO_CANCEL_OPTION,
                        JOptionPane.QUESTION_MESSAGE,
                        null,
                        brokenLinkOptions,
                        brokenLinkOptions[0]);
              }
              switch (answer) {
                case 1:
                  // Assign new file.
                  FileListEntryEditor flEditor =
                      new FileListEntryEditor(
                          panel.frame(), flEntry, false, true, panel.getBibDatabaseContext());
                  flEditor.setVisible(true, true);
                  break;
                case 2:
                  // Clear field:
                  tableModel.removeEntry(j);
                  deleted = true; // Make sure we don't investigate this link further.
                  j--; // Step back in the iteration, because we removed an entry.
                  break;
                case 3:
                  // Clear field:
                  tableModel.removeEntry(j);
                  deleted = true; // Make sure we don't investigate this link further.
                  j--; // Step back in the iteration, because we removed an entry.
                  removeAllBroken = true; // Notify for further cases.
                  break;
                default:
                  // Cancel
                  break mainLoop;
              }
            }

            // Unless we deleted this link, see if its file type is recognized:
            if (!deleted
                && flEntry.type.isPresent()
                && (flEntry.type.get() instanceof UnknownExternalFileType)) {
              String[] options =
                  new String[] {
                    Localization.lang("Define '%0'", flEntry.type.get().getName()),
                    Localization.lang("Change file type"),
                    Localization.lang("Cancel")
                  };
              String defOption = options[0];
              int answer =
                  JOptionPane.showOptionDialog(
                      panel.frame(),
                      Localization.lang(
                          "One or more file links are of the type '%0', which is undefined. What do you want to do?",
                          flEntry.type.get().getName()),
                      Localization.lang("Undefined file type"),
                      JOptionPane.YES_NO_CANCEL_OPTION,
                      JOptionPane.QUESTION_MESSAGE,
                      null,
                      options,
                      defOption);
              if (answer == JOptionPane.CANCEL_OPTION) {
                // User doesn't want to handle this unknown link type.
              } else if (answer == JOptionPane.YES_OPTION) {
                // User wants to define the new file type. Show the dialog:
                ExternalFileType newType =
                    new ExternalFileType(
                        flEntry.type.get().getName(),
                        "",
                        "",
                        "",
                        "new",
                        IconTheme.JabRefIcon.FILE.getSmallIcon());
                ExternalFileTypeEntryEditor editor =
                    new ExternalFileTypeEntryEditor(panel.frame(), newType);
                editor.setVisible(true);
                if (editor.okPressed()) {
                  // Get the old list of types, add this one, and update the list in prefs:
                  List<ExternalFileType> fileTypes =
                      new ArrayList<>(
                          ExternalFileTypes.getInstance().getExternalFileTypeSelection());
                  fileTypes.add(newType);
                  Collections.sort(fileTypes);
                  ExternalFileTypes.getInstance().setExternalFileTypes(fileTypes);
                  panel.getMainTable().repaint();
                }
              } else {
                // User wants to change the type of this link.
                // First get a model of all file links for this entry:
                FileListEntryEditor editor =
                    new FileListEntryEditor(
                        panel.frame(), flEntry, false, true, panel.getBibDatabaseContext());
                editor.setVisible(true, false);
              }
            }
          }

          if (!tableModel.getStringRepresentation().equals(old)) {
            // The table has been modified. Store the change:
            String toSet = tableModel.getStringRepresentation();
            if (toSet.isEmpty()) {
              ce.addEdit(new UndoableFieldChange(aSel, Globals.FILE_FIELD, old, null));
              aSel.clearField(Globals.FILE_FIELD);
            } else {
              ce.addEdit(new UndoableFieldChange(aSel, Globals.FILE_FIELD, old, toSet));
              aSel.setField(Globals.FILE_FIELD, toSet);
            }
            changedEntries.add(aSel);
          }
        }
      }
    }

    if (!changedEntries.isEmpty()) {
      // Add the undo edit:
      ce.end();
      panel.getUndoManager().addEdit(ce);
      panel.markBaseChanged();
      entriesChangedCount = changedEntries.size();
    }
  }