/*.................................................................................................................*/
 public void modifyTree(Tree tree, MesquiteTree modified, RandomBetween rng) {
   if (tree == null || modified == null) return;
   if (tree.getTaxa().anySelected()) { // error fixed in 1. 12
     int[] terminals = tree.getTerminalTaxa(tree.getRoot());
     if (terminals == null) return;
     int numTerminals = 0;
     for (int i = 0; i < terminals.length; i++)
       if (tree.getTaxa().getSelected(terminals[i])) numTerminals++;
     if (numTerminals > numExcluded) {
       int[] selTerminals = new int[numTerminals];
       int icount = 0;
       for (int i = 0; i < terminals.length; i++)
         if (tree.getTaxa().getSelected(terminals[i])) {
           selTerminals[icount] = terminals[i];
           icount++;
         }
       terminals = selTerminals;
       for (int it = 0; it < numExcluded; it++) {
         int taxon = -1;
         int count = 0;
         int ntries = 100000;
         while (terminals[taxon = rng.randomIntBetween(0, numTerminals - 1)] < 0
             && count < ntries) {
           count++;
         }
         if (count >= ntries)
           discreetAlert(
               "ERROR: Rarefy tree failed to find taxon to delete in " + ntries + " tries.");
         else {
           int nT = modified.nodeOfTaxonNumber(terminals[taxon]);
           modified.deleteClade(nT, false);
           terminals[taxon] = -1;
         }
       }
     } else
       MesquiteMessage.warnUser(
           "Sorry, the tree could not be rarefied because more taxa are to be excluded than those available");
   } else {
     int numTerminals = tree.numberOfTerminalsInClade(tree.getRoot());
     if (numTerminals > numExcluded) {
       for (int it = 0; it < numExcluded; it++) {
         int taxon = rng.randomIntBetween(0, numTerminals - it - 1);
         int nT = modified.getTerminalNode(modified.getRoot(), taxon);
         modified.deleteClade(nT, false);
       }
     } else
       MesquiteMessage.warnUser(
           "Sorry, the tree could not be rarefied because more taxa are to be excluded than those available");
   }
 }
  public static boolean processNoContigAceFiles(CharacterData data, MesquiteModule ownerModule) {
    DNAData editedData = ChromaseqUtil.getEditedData(data);
    if (editedData == null) return false;
    boolean changed = false;
    MesquiteFile file = data.getProject().getHomeFile();
    int count = 0;
    int originalChars = editedData.getNumChars();
    for (int it = 0; it < editedData.getNumTaxa(); it++)
      if (ChromaseqUtil.reprocessContig(editedData, it)) {
        AceDirectoryProcessor aceDirProcessor = new AceDirectoryProcessor();
        aceDirProcessor.reprocessAceFileDirectory(file, ownerModule, editedData, it);
        count++;
      }
    if (count > 0) {
      editedData.getTaxa().notifyListeners(ownerModule, new Notification(ownerModule.PARTS_ADDED));
      MesquiteMessage.discreetNotifyUser(
          "Some of the contigs have been reprocessed; this will be lost permanently unless you resave the file.");
      changed = true;
    }
    if (originalChars < editedData.getNumChars())
      editedData.notifyListeners(ownerModule, new Notification(ownerModule.PARTS_ADDED));
    else if (originalChars > editedData.getNumChars())
      editedData.notifyListeners(ownerModule, new Notification(ownerModule.PARTS_DELETED));

    ChromaseqUtil.removeAssociatedObjects(editedData, ChromaseqUtil.reprocessContigRef);
    return changed;
  }
  /** monitors the run. */
  public boolean monitorAndCleanUpShell() {
    lastModified = null;
    boolean stillGoing = true;
    if (outputFilePaths != null) {
      lastModified = new long[outputFilePaths.length];
      LongArray.deassignArray(lastModified);
    }

    if (!StringUtil.blank(
        runningFilePath)) // is file at runningFilePath; watch for its disappearance
    while (MesquiteFile.fileExists(runningFilePath) && stillGoing) {
        processOutputFiles();
        try {
          Thread.sleep(sleepTime);
        } catch (InterruptedException e) {
          MesquiteMessage.notifyProgrammer(
              "InterruptedException in shell script executed by " + name);
          return false;
        }
        stillGoing = watcher == null || watcher.continueShellProcess(proc);
      }

    if (outputFileProcessor != null)
      outputFileProcessor.processCompletedOutputFiles(outputFilePaths);
    return true;
  }
 /*.................................................................................................................*/
 boolean checkUsernamePassword(boolean tellUserAboutCipres) {
   if (StringUtil.blank(username) || StringUtil.blank(password)) {
     MesquiteBoolean answer = new MesquiteBoolean(false);
     MesquiteString usernameString = new MesquiteString();
     if (username != null) usernameString.setValue(username);
     MesquiteString passwordString = new MesquiteString();
     if (password != null) passwordString.setValue(password);
     String help =
         "You will need an account on the CIPRes REST system to use this service.  To register, go to https://www.phylo.org/restusers/register.action";
     new UserNamePasswordDialog(
         ownerModule.containerOfModule(),
         "Sign in to CIPRes",
         help,
         "",
         "Username",
         "Password",
         answer,
         usernameString,
         passwordString);
     if (answer.getValue()) {
       username = usernameString.getValue();
       password = passwordString.getValue();
     }
     ownerModule.storePreferences();
   }
   boolean success = StringUtil.notEmpty(username) && StringUtil.notEmpty(password);
   if (!success && tellUserAboutCipres) {
     MesquiteMessage.discreetNotifyUser(
         "Use of the CIPRes service requires an account with CIPRes's REST service.  Go to https://www.phylo.org/restusers/register.action to register for an account");
   }
   return success;
 }
  public boolean clipBoardHasString() {

    Clipboard clip = Toolkit.getDefaultToolkit().getSystemClipboard();
    Transferable t = clip.getContents(this);
    try {
      String s = (String) t.getTransferData(DataFlavor.stringFlavor);
      if (s != null) {
        return true;
      }
    } catch (Exception e) {
      MesquiteMessage.printStackTrace(e);
    }
    return false;
  }
  /*.................................................................................................................*/
  public boolean monitorAndCleanUpShell(String jobURL) {
    boolean stillGoing = true;

    if (!checkUsernamePassword(true)) {
      return false;
    }
    lastModified = null;
    if (outputFilePaths != null) {
      lastModified = new long[outputFilePaths.length];
      LongArray.deassignArray(lastModified);
    }
    String status = "";
    while (!jobCompleted(jobURL) && stillGoing) {
      if (StringUtil.blank(status))
        ownerModule.logln(
            "CIPRes Job Status: " + getJobStatus(jobURL) + "  (" + StringUtil.getDateTime() + ")");

      //	if (jobSubmitted(jobURL))
      //		processOutputFiles();
      try {
        Thread.sleep(minPollIntervalSeconds * 1000);
      } catch (InterruptedException e) {
        MesquiteMessage.notifyProgrammer("InterruptedException in CIPRes monitoring");
        return false;
      }

      stillGoing = watcher == null || watcher.continueShellProcess(null);
      String newStatus = getJobStatus(jobURL);
      if (newStatus != null && !newStatus.equalsIgnoreCase(status)) {
        ownerModule.logln(
            "CIPRes Job Status: " + newStatus + "  (" + StringUtil.getDateTime() + ")");
        status = newStatus;
      } else ownerModule.log(".");
      if (newStatus != null && newStatus.equalsIgnoreCase("SUBMITTED")) { // job is running
        processOutputFiles(jobURL);
      }
    }
    ownerModule.logln("CIPRes job completed.");
    if (outputFileProcessor != null) {
      if (rootDir != null) {
        ownerModule.logln("About to download results from CIPRes.");
        if (downloadResults(jobURL, rootDir, false))
          outputFileProcessor.processCompletedOutputFiles(outputFilePaths);
        else return false;
      }
    }

    return true;
  }
  /*.................................................................................................................*/
  public void fillNameTranslation(DNAData editedData, int it, int numReads) {
    Associable as = editedData.getTaxaInfo(false);
    if (as == null) return;
    String[] fileNames =
        ChromaseqUtil.getStringsAssociated(as, ChromaseqUtil.origReadFileNamesRef, it);
    String[] primerNames =
        ChromaseqUtil.getStringsAssociated(as, ChromaseqUtil.primerForEachReadNamesRef, it);
    String[] sampleCodes =
        ChromaseqUtil.getStringsAssociated(as, ChromaseqUtil.sampleCodeNamesRef, it);

    for (int i = 0; i < numReads; i++) {

      if (fileNames == null) {
        if (i == 0)
          MesquiteMessage.warnProgrammer(
              "fileNames NULL in AceDirectoryProcess.fileNameTranslation");
      } else if (i * 2 + 1 < fileNames.length) {
        fileNameTranslation[0][i] = fileNames[i * 2];
        fileNameTranslation[1][i] = fileNames[i * 2 + 1];
      } else
        MesquiteMessage.warnProgrammer(
            "fileNames.length too small in AceDirectoryProcess.fileNameTranslation: fileNames.length = "
                + fileNames.length
                + ", numReads: "
                + numReads);

      if (primerNames == null) {
        if (i == 0)
          MesquiteMessage.warnProgrammer(
              "primerNames NULL in AceDirectoryProcess.fileNameTranslation");
      } else if (i * 2 + 1 < primerNames.length) fileNameTranslation[2][i] = primerNames[i * 2 + 1];
      else
        MesquiteMessage.warnProgrammer(
            "primerNames.length too small in AceDirectoryProcess.fileNameTranslation: primerNames.length = "
                + primerNames.length
                + ", numReads: "
                + numReads);

      if (sampleCodes == null) {
        if (i == 0)
          MesquiteMessage.warnProgrammer(
              "sampleCodes NULL in AceDirectoryProcess.fileNameTranslation");
      } else if (i * 3 + 2 < sampleCodes.length) {
        fileNameTranslation[3][i] = sampleCodes[i * 3 + 1];
        fileNameTranslation[4][i] = sampleCodes[i * 3 + 2];
      } else
        MesquiteMessage.warnProgrammer(
            "sampleCodes.length too small in AceDirectoryProcess.fileNameTranslation: sampleCodes.length = "
                + sampleCodes.length
                + ", numReads: "
                + numReads);
    }
  }
  public void readTabbedTranslationFile(String translationList) {
    Parser parser = new Parser();
    parser.setString(translationList);
    Parser subParser = new Parser();
    String line = parser.getRawNextDarkLine();

    numCodes = 0;
    while (!StringUtil.blank(line)) {
      numCodes++;
      line = parser.getRawNextDarkLine();
    }
    if (numCodes == 0) {
      MesquiteMessage.discreetNotifyUser("File is empty.");
      return;
    }

    initializeArrays(numCodes);

    int count = -1;
    parser.setPosition(0);
    subParser.setPunctuationString("\t");
    line = parser.getRawNextDarkLine();
    String token = "";

    while (!StringUtil.blank(line)) {
      count++;
      subParser.setString(line);
      token = subParser.getFirstToken();
      if (StringUtil.notEmpty(token)) {
        originalSampleCodes[count] = token;
        token = subParser.getNextToken();
        if (StringUtil.notEmpty(token)) {
          translatedSampleCodes[count] = token;
        }
      }
      line = parser.getRawNextDarkLine();
    }
  }
  /**
   * executes a shell script at "scriptPath". If runningFilePath is not blank and not null, then
   * Mesquite will create a file there that will serve as a flag to Mesquite that the script is
   * running.
   */
  public boolean executeInShell() {
    proc = null;
    try {
      ShellScriptUtil.setScriptFileToBeExecutable(scriptPath);
      if (!StringUtil.blank(runningFilePath)) {
        if (StringUtil.blank(runningFileMessage))
          MesquiteFile.putFileContents(runningFilePath, "Script running...", true);
        else MesquiteFile.putFileContents(runningFilePath, runningFileMessage, true);
        if (appendRemoveCommand && MesquiteFile.fileExists(runningFilePath))
          MesquiteFile.appendFileContents(
              scriptPath,
              StringUtil.lineEnding() + ShellScriptUtil.getRemoveCommand(runningFilePath),
              true); // append remove command to guarantee that the runningFile is deleted
        // +StringUtil.lineEnding()+ShellScriptUtil.getExitCommand()
      }
      proc = ShellScriptUtil.executeScript(scriptPath, visibleTerminal);

    } catch (IOException e) {
      MesquiteMessage.warnProgrammer("IOException in shell script executed by " + name);
      return false;
    }
    return true;
  }