/** Add the given source file sequence to the to be loaded set. */
  private void addSourceSeq(
      Integer order,
      String sname,
      FileSeq fseq,
      DoubleMap<Integer, String, TreeSet<FileSeq>> sources)
      throws PipelineException {
    if (order != null) {
      String suffix = fseq.getFilePattern().getSuffix();
      if (!fseq.isSingle() || (suffix == null) || !suffix.equals("hip"))
        throw new PipelineException(
            "The file sequence ("
                + fseq
                + ") of source node ("
                + sname
                + ") "
                + "does not contain a single Houdini scene (.hip) file!");

      TreeSet<FileSeq> fseqs = sources.get(order, sname);
      if (fseqs == null) {
        fseqs = new TreeSet<FileSeq>();
        sources.put(order, sname, fseqs);
      }

      fseqs.add(fseq);
    }
  }
  /** @return The path of a node */
  private Path getNodePath(String nodeName) throws PipelineException {

    NodeID targetID = new NodeID(w.user, w.view, nodeName);
    NodeStatus targetStat = mclient.status(targetID);
    NodeMod targetMod = targetStat.getHeavyDetails().getWorkingVersion();
    if (targetMod == null)
      throw new PipelineException(
          "No working version of the Target Scene Node ("
              + nodeName
              + ") exists "
              + "in the ("
              + w.view
              + ") working area owned by ("
              + PackageInfo.sUser
              + ")!");

    Path targetPath;

    FileSeq fseq = targetMod.getPrimarySequence();
    String suffix = fseq.getFilePattern().getSuffix();
    if (!fseq.isSingle() || (suffix == null) || (!suffix.equals("ma") && !suffix.equals("mb")))
      throw new PipelineException("The target node (" + nodeName + ") must be a maya scene!");
    targetPath =
        new Path(PackageInfo.sProdPath, targetID.getWorkingParent() + "/" + fseq.getFile(0));

    // System.err.println("$WORKING"+nodeName+"."+suffix);
    return targetPath;
  } // end getNodePath(String)
Beispiel #3
0
  /**
   * Construct a {@link SubProcessHeavy SubProcessHeavy} instance which when executed will fulfill
   * the given action agenda.
   *
   * <p>
   *
   * @param agenda The agenda to be accomplished by the action.
   * @param outFile The file to which all STDOUT output is redirected.
   * @param errFile The file to which all STDERR output is redirected.
   * @return The SubProcess which will fulfill the agenda.
   * @throws PipelineException If unable to prepare a SubProcess due to illegal, missing or
   *     imcompatable information in the action agenda or a general failure of the prep method code.
   */
  public SubProcessHeavy prep(ActionAgenda agenda, File outFile, File errFile)
      throws PipelineException {
    /* create the process to run the action */
    try {
      ArrayList<String> args = new ArrayList<String>();

      for (File file : agenda.getPrimaryTarget().getFiles()) args.add(file.toString());

      for (FileSeq fseq : agenda.getSecondaryTargets()) {
        for (File file : fseq.getFiles()) args.add(file.toString());
      }

      return new SubProcessHeavy(
          agenda.getNodeID().getAuthor(),
          getName() + "-" + agenda.getJobID(),
          "touch",
          args,
          agenda.getEnvironment(),
          agenda.getWorkingDir(),
          outFile,
          errFile);
    } catch (Exception ex) {
      throw new PipelineException(
          "Unable to generate the SubProcess to perform this Action!\n" + ex.getMessage());
    }
  }
Beispiel #4
0
  /**
   * Construct a {@link SubProcessLight} instance which when executed will launch an editor program
   * to view the given file sequence as arguments.
   *
   * <p>The default implementation executes the editor program obtained with {@link #getProgram
   * getProgram} method under the given environment. Subclasses should override this method if more
   * specialized behavior or different command line arguments are needed in order to launch the
   * editor for the given file sequence.
   *
   * <p>
   *
   * @param author The name of the user owning the files.
   * @param fseq The file sequence to edit.
   * @param env The environment under which the editor is run.
   * @param dir The working directory where the editor is run.
   * @return The controlling <CODE>SubProcessLight</CODE> instance.
   * @throws PipelineException If unable to launch the editor.
   * @see SubProcessLight
   */
  public SubProcessLight prep(String author, FileSeq fseq, Map<String, String> env, File dir)
      throws PipelineException {
    if (!fseq.isSingle())
      throw new PipelineException(
          "The " + getName() + " Editor can only edit a single file at a time!");

    ArrayList<String> args = new ArrayList<String>();
    args.add("-eol.mode=LF");
    args.add(fseq.getPath(0).toOsString());

    return new SubProcessLight(author, getName(), getProgram(), args, env, dir);
  }
  /**
   * Construct a {@link SubProcessHeavy SubProcessHeavy} instance which when executed will fulfill
   * the given action agenda.
   *
   * <p>
   *
   * @param agenda The agenda to be accomplished by the action.
   * @param outFile The file to which all STDOUT output is redirected.
   * @param errFile The file to which all STDERR output is redirected.
   * @return The SubProcess which will fulfill the agenda.
   * @throws PipelineException If unable to prepare a SubProcess due to illegal, missing or
   *     imcompatable information in the action agenda or a general failure of the prep method code.
   */
  public SubProcessHeavy prep(ActionAgenda agenda, File outFile, File errFile)
      throws PipelineException {
    NodeID nodeID = agenda.getNodeID();

    /* sanity checks */
    Path fromPath = null;
    FileSeq fromSeq = null;
    FileSeq toSeq = null;
    {
      ArrayList<String> formats = new ArrayList<String>();
      formats.add("xml");

      String sname = (String) getSingleParamValue("XmlSource");

      if (sname == null) throw new PipelineException("The Image Source was not set!");

      String xmlpath = null;
      if (sname != null) {
        FileSeq fxml = agenda.getPrimarySource(sname);
        xmlpath = fxml.toString();
      }

      NodeID snodeID = new NodeID(nodeID, sname);

      ArrayList<String> suffixes = new ArrayList<String>();
      suffixes.add("html");
      suffixes.add("htm");
      Path targetPath = getPrimaryTargetPath(agenda, suffixes, "Html fil");

      // ********************************************************************
      // Build the mencoder command
      // ********************************************************************
      ArrayList<String> args = new ArrayList<String>();

      args.add("/intranet/jpt/phpCliPrograms/Edit2pipe.php");
      args.add("none");
      args.add(xmlpath);
      args.add(xmlpath);

      args.add(targetPath.toOsString());

      // Create sub process
      return createSubProcess(
          agenda,
          "php5",
          args,
          agenda.getEnvironment(),
          agenda.getTargetPath().toFile(),
          outFile,
          errFile);
    }
  }
  /**
   * Using a list of shots and the assets to be changed per shot, gets each shot node and modifies
   * the references as needed.
   */
  private void processNodes() throws PipelineException {
    /*each asset, its replacement and the lo-res versions*/
    for (String asset : pAssetManager.keySet()) {
      String newAsset = pAssetManager.get(asset).getNewAsset();
      String hrAsset = asset.replace(lr, "");
      String newHrAsset = newAsset.replace(lr, "");
      logLine(
          "Checking out nodes:\n\t"
              + asset
              + "\n\t"
              + hrAsset
              + "\n\t"
              + newAsset
              + "\n\t"
              + newHrAsset);

      mclient.checkOut(w.user, w.view, asset, null, over, froz);
      mclient.checkOut(w.user, w.view, newAsset, null, over, froz);
      mclient.checkOut(w.user, w.view, hrAsset, null, over, froz);
      mclient.checkOut(w.user, w.view, newHrAsset, null, over, froz);
    } // end for

    TreeMap<String, TreeSet<String>> shotBased = convertListToShotBased();
    TreeMap<String, String> nameMap = SonyConstants.getCustomNamespaces(project);
    TreeSet<String> oldRef = new TreeSet<String>();
    TreeSet<String> newRef = new TreeSet<String>();

    for (String shot : shotBased.keySet()) {
      // check out the shot

      if (shot.endsWith("anim")) mclient.checkOut(w.user, w.view, shot, null, keep, frozU);
      else {
        continue;
        // mclient.checkOut(w.user, w.view, shot, null, keep, pFroz);
      }
      logLine("Checking out: " + shot);

      NodeMod targetMod = mclient.getWorkingVersion(w.user, w.view, shot);
      if (!shot.matches(lgtPattern)) {

        if (targetMod.isActionEnabled()) {
          System.err.println("Anim node with action enabled");
          FileSeq fseq = targetMod.getPrimarySequence();
          VersionID targetID = targetMod.getWorkingID();
          TreeMap<String, VersionID> files = new TreeMap<String, VersionID>();
          files.put(fseq.getFile(0).getPath(), targetID);
          mclient.revertFiles(w.user, w.view, shot, files);
          targetMod.setActionEnabled(false);
        }

        w.mclient.modifyProperties(w.user, w.view, targetMod);
      }

      /*--checking the shot to be modified---*/

      for (String assetToReplace : shotBased.get(shot)) {
        if (assetToReplace.endsWith(lr)) {
          AssetInfo temp = pAssetManager.get(assetToReplace);
          oldRef.add(assetToReplace);
          newRef.add(temp.getNewAsset());
        } else {
          String hiRes = assetToReplace.replace(lr, "");
          AssetInfo temp = pAssetManager.get(hiRes);
          if (temp == null) continue;
          oldRef.add(assetToReplace);
          newRef.add(temp.getNewAsset() + lr);
        } // end else
      } // end for

      if (oldRef.isEmpty() || newRef.isEmpty()) {
        logLine("Shot " + shot + " somehow does not need any changes.");
        continue;
      } // end if

      editShotReferences(shot, targetMod, oldRef, newRef, nameMap);
    } // end for

    /*for(String shot: shotBased.keySet()){
    	//TODO: Check in nodes
    	String msg = "Checked in with UpdateAssetGUI. Removed:\n\t";
    	for(String asset: oldRef)
    		msg+= (asset + " ");

    	msg+="\nAdded:\n\t";
    	for(String asset: newRef)
    		msg+= (asset + " ");

    	mclient.checkIn(w.user, w.view,shot, msg, VersionID.Level.Major);
    }*/

  } // end processShots
  /**
   * Construct a {@link SubProcessHeavy SubProcessHeavy} instance which when executed will fulfill
   * the given action agenda.
   *
   * <p>
   *
   * @param agenda The agenda to be accomplished by the action.
   * @param outFile The file to which all STDOUT output is redirected.
   * @param errFile The file to which all STDERR output is redirected.
   * @return The SubProcess which will fulfill the agenda.
   * @throws PipelineException If unable to prepare a SubProcess due to illegal, missing or
   *     imcompatable information in the action agenda or a general failure of the prep method code.
   */
  public SubProcessHeavy prep(ActionAgenda agenda, File outFile, File errFile)
      throws PipelineException {
    NodeID nodeID = agenda.getNodeID();

    /* sanity checks */
    String opname = null;
    File source = null;
    File preRender = null;
    File postRender = null;
    File preFrame = null;
    File postFrame = null;
    {
      {
        FileSeq fseq = agenda.getPrimaryTarget();
        if (!fseq.hasFrameNumbers())
          throw new PipelineException(
              "The HfsComposite Action requires that the output images have frame numbers.");
      }

      /* generate the filename of the Houdini scene to load */
      {
        String sname = (String) getSingleParamValue("HoudiniScene");
        if (sname != null) {
          FileSeq fseq = agenda.getPrimarySource(sname);
          if (fseq == null)
            throw new PipelineException(
                "Somehow the Houdini Scene node ("
                    + sname
                    + ") was not one of the source "
                    + "nodes!");

          String suffix = fseq.getFilePattern().getSuffix();
          if (!fseq.isSingle() || (suffix == null) || !suffix.equals("hip"))
            throw new PipelineException(
                "The HfsComposite Action requires that the source node specified by the "
                    + "Houdini Scene parameter ("
                    + sname
                    + ") must have a single Houdini scene "
                    + "file (.hip) as its primary file sequence!");

          NodeID snodeID = new NodeID(nodeID, sname);
          source =
              new File(PackageInfo.sProdDir, snodeID.getWorkingParent() + "/" + fseq.getFile(0));
        } else {
          throw new PipelineException(
              "The HfsComposite Action requires the Houdini Scene parameter to be set!");
        }
      }

      /* the full name of the geometry output operator */
      {
        String name = (String) getSingleParamValue("OutputOperator");
        if ((name == null) || (name.length() == 0))
          throw new PipelineException(
              "The HfsComposite Action requires a valid Output Operator name!");

        opname = ("/out/" + name);
      }

      /* command script files */
      {
        String sname = (String) getSingleParamValue("PreRenderScript");
        if (sname != null) {
          FileSeq fseq = agenda.getPrimarySource(sname);
          if (fseq == null)
            throw new PipelineException(
                "Somehow the Pre Render Script node ("
                    + sname
                    + ") was not one of the "
                    + "source nodes!");

          String suffix = fseq.getFilePattern().getSuffix();
          if (!fseq.isSingle() || (suffix == null) || !(suffix.equals("cmd")))
            throw new PipelineException(
                "The HfsComposite Action requires that the source node specified by the Pre "
                    + "Render Script parameter ("
                    + sname
                    + ") must have a single command script "
                    + "(.cmd) as its primary file sequence!");

          NodeID snodeID = new NodeID(nodeID, sname);
          preRender =
              new File(PackageInfo.sProdDir, snodeID.getWorkingParent() + "/" + fseq.getFile(0));
        }
      }

      {
        String sname = (String) getSingleParamValue("PostRenderScript");
        if (sname != null) {
          FileSeq fseq = agenda.getPrimarySource(sname);
          if (fseq == null)
            throw new PipelineException(
                "Somehow the Post Render Script node ("
                    + sname
                    + ") was not one of the "
                    + "source nodes!");

          String suffix = fseq.getFilePattern().getSuffix();
          if (!fseq.isSingle() || (suffix == null) || !(suffix.equals("cmd")))
            throw new PipelineException(
                "The HfsComposite Action requires that the source node specified by the "
                    + "Post Render Script parameter ("
                    + sname
                    + ") must have a single command "
                    + "script (.cmd) as its primary file sequence!");

          NodeID snodeID = new NodeID(nodeID, sname);
          postRender =
              new File(PackageInfo.sProdDir, snodeID.getWorkingParent() + "/" + fseq.getFile(0));
        }
      }

      {
        String sname = (String) getSingleParamValue("PreFrameScript");
        if (sname != null) {
          FileSeq fseq = agenda.getPrimarySource(sname);
          if (fseq == null)
            throw new PipelineException(
                "Somehow the Pre Frame Script node ("
                    + sname
                    + ") was not one of the "
                    + "source nodes!");

          String suffix = fseq.getFilePattern().getSuffix();
          if (!fseq.isSingle() || (suffix == null) || !(suffix.equals("cmd")))
            throw new PipelineException(
                "The HfsComposite Action requires that the source node specified by the Pre "
                    + "Frame Script parameter ("
                    + sname
                    + ") must have a single command script "
                    + "(.cmd) as its primary file sequence!");

          NodeID snodeID = new NodeID(nodeID, sname);
          preFrame =
              new File(PackageInfo.sProdDir, snodeID.getWorkingParent() + "/" + fseq.getFile(0));
        }
      }

      {
        String sname = (String) getSingleParamValue("PostFrameScript");
        if (sname != null) {
          FileSeq fseq = agenda.getPrimarySource(sname);
          if (fseq == null)
            throw new PipelineException(
                "Somehow the Post Frame Script node ("
                    + sname
                    + ") was not one of the "
                    + "source nodes!");

          String suffix = fseq.getFilePattern().getSuffix();
          if (!fseq.isSingle() || (suffix == null) || !(suffix.equals("cmd")))
            throw new PipelineException(
                "The HfsComposite Action requires that the source node specified by the "
                    + "Post Frame Script parameter ("
                    + sname
                    + ") must have a single command "
                    + "script (.cmd) as its primary file sequence!");

          NodeID snodeID = new NodeID(nodeID, sname);
          postFrame =
              new File(PackageInfo.sProdDir, snodeID.getWorkingParent() + "/" + fseq.getFile(0));
        }
      }
    }

    /* create the temporary Houdini command script */
    File hscript = createTemp(agenda, 0644, "cmd");
    try {
      FileWriter out = new FileWriter(hscript);

      FileSeq fseq =
          new FileSeq(
              PackageInfo.sProdDir.getPath() + nodeID.getWorkingParent(),
              agenda.getPrimaryTarget());
      if (fseq.hasFrameNumbers()) {
        FilePattern fpat = fseq.getFilePattern();
        FrameRange frange = fseq.getFrameRange();
        out.write(
            "opparm "
                + opname
                + " trange on\n"
                + "opparm "
                + opname
                + " f1 "
                + frange.getStart()
                + "\n"
                + "opparm "
                + opname
                + " f2 "
                + frange.getEnd()
                + "\n"
                + "opparm "
                + opname
                + " f3 "
                + frange.getBy()
                + "\n"
                + "opparm "
                + opname
                + " copoutput '"
                + fpat.getPrefix()
                + ".$F");

        if (fpat.getPadding() > 1) out.write(String.valueOf(fpat.getPadding()));

        out.write("." + fpat.getSuffix() + "'\n");
      } else {
        out.write("opparm " + opname + " trange off\n" + "opparm " + opname + " " + fseq + "\n");
      }

      if (preRender != null) out.write("opparm " + opname + " prerender '" + preRender + "'\n");

      if (postRender != null) out.write("opparm " + opname + " postrender '" + postRender + "'\n");

      if (preFrame != null) out.write("opparm " + opname + " preframe '" + preFrame + "'\n");

      if (postFrame != null) out.write("opparm " + opname + " postframe '" + postFrame + "'\n");

      out.write("opparm -c " + opname + " execute\n");

      out.close();
    } catch (IOException ex) {
      throw new PipelineException(
          "Unable to write temporary script file ("
              + hscript
              + ") for Job "
              + "("
              + agenda.getJobID()
              + ")!\n"
              + ex.getMessage());
    }

    /* create the wrapper shell script */
    File script = createTemp(agenda, 0755, "bash");
    try {
      FileWriter out = new FileWriter(script);
      out.write("#!/bin/bash\n\n" + "cat " + hscript + " | hscript -v " + source);
      out.close();
    } catch (IOException ex) {
      throw new PipelineException(
          "Unable to write temporary script file ("
              + script
              + ") for Job "
              + "("
              + agenda.getJobID()
              + ")!\n"
              + ex.getMessage());
    }

    try {
      return new SubProcessHeavy(
          agenda.getNodeID().getAuthor(),
          getName() + "-" + agenda.getJobID(),
          script.getPath(),
          new ArrayList<String>(),
          agenda.getEnvironment(),
          agenda.getWorkingDir(),
          outFile,
          errFile);
    } catch (Exception ex) {
      throw new PipelineException(
          "Unable to generate the SubProcess to perform this Action!\n" + ex.getMessage());
    }
  }
  /**
   * Construct a {@link SubProcessHeavy SubProcessHeavy} instance which when executed will fulfill
   * the given action agenda.
   *
   * <p>
   *
   * @param agenda The agenda to be accomplished by the action.
   * @param outFile The file to which all STDOUT output is redirected.
   * @param errFile The file to which all STDERR output is redirected.
   * @return The SubProcess which will fulfill the agenda.
   * @throws PipelineException If unable to prepare a SubProcess due to illegal, missing or
   *     imcompatable information in the action agenda or a general failure of the prep method code.
   */
  public SubProcessHeavy prep(ActionAgenda agenda, File outFile, File errFile)
      throws PipelineException {
    NodeID nodeID = agenda.getNodeID();

    /* sanity checks */
    Path loadScene = null;
    Path saveScene = null;
    boolean isAscii = false;
    TreeMap<Integer, LinkedList<Path>> mel = new TreeMap<Integer, LinkedList<Path>>();
    {
      /* generate the filename of the Maya scene to load */
      {
        String sname = (String) getSingleParamValue("MayaScene");
        if (sname != null) {
          FileSeq fseq = agenda.getPrimarySource(sname);
          if (fseq == null)
            throw new PipelineException(
                "Somehow the Maya Scene node ("
                    + sname
                    + ") was not one of the source "
                    + "nodes!");

          String suffix = fseq.getFilePattern().getSuffix();
          if (!fseq.isSingle() || (suffix == null) || !(suffix.equals("ma") || suffix.equals("mb")))
            throw new PipelineException(
                "The MayaMEL Action requires that the source node specified by the Maya "
                    + "Scene parameter ("
                    + sname
                    + ") must have a single Maya scene file as "
                    + "its primary file sequence!");

          NodeID snodeID = new NodeID(nodeID, sname);
          loadScene =
              new Path(PackageInfo.sProdPath, snodeID.getWorkingParent() + "/" + fseq.getPath(0));
        }
      }

      /* generate the name of the Maya scene to save */
      {
        Boolean save = (Boolean) getSingleParamValue("SaveResult");
        if ((save != null) && save) {
          FileSeq fseq = agenda.getPrimaryTarget();

          String suffix = fseq.getFilePattern().getSuffix();
          if (!fseq.isSingle() || (suffix == null) || !(suffix.equals("ma") || suffix.equals("mb")))
            throw new PipelineException(
                "The MayaMEL Action requires that the primary target file sequence must "
                    + "be a single Maya scene file if the Save Result parameter is set!");

          isAscii = suffix.equals("ma");
          saveScene =
              new Path(PackageInfo.sProdPath, nodeID.getWorkingParent() + "/" + fseq.getPath(0));
        }
      }

      /* generate the table of MEL script files to evaluate */
      for (String sname : getSourceNames()) {
        Integer order = (Integer) getSourceParamValue(sname, "Order");
        FileSeq fseq = agenda.getPrimarySource(sname);
        if (fseq == null)
          throw new PipelineException(
              "Somehow an per-source Order parameter exists for a node ("
                  + sname
                  + ") "
                  + "which was not one of the source nodes!");

        String suffix = fseq.getFilePattern().getSuffix();
        if (!fseq.isSingle() || (suffix == null) || !suffix.equals("mel"))
          throw new PipelineException(
              "The MayaMEL Action requires that the source node ("
                  + sname
                  + ") with "
                  + "per-source Order parameter must have a single MEL script file as its "
                  + "primary file sequence!");

        NodeID snodeID = new NodeID(nodeID, sname);
        Path script =
            new Path(PackageInfo.sProdPath, snodeID.getWorkingParent() + "/" + fseq.getPath(0));

        LinkedList<Path> scripts = mel.get(order);
        if (scripts == null) {
          scripts = new LinkedList<Path>();
          mel.put(order, scripts);
        }

        scripts.add(script);
      }
    }

    /* create a temporary MEL script file */
    File script = createTemp(agenda, 0755, "mel");
    try {
      FileWriter out = new FileWriter(script);

      /* a workaround needed in "maya -batch" mode */
      out.write("// WORK AROUNDS:\n" + "lightlink -q;\n\n");

      /* rename the current scene as the output scene */
      if (saveScene != null) {
        out.write(
            "// SCENE SETUP\n"
                + "file -rename \""
                + saveScene
                + "\";\n"
                + "file -type \""
                + (isAscii ? "mayaAscii" : "mayaBinary")
                + "\";\n\n");
      }

      /* evaluate the MEL scripts */
      if (!mel.isEmpty()) {
        out.write("// MEL SCRIPTS \n");
        for (LinkedList<Path> scripts : mel.values()) {
          for (Path spath : scripts) out.write("source \"" + spath + "\";\n");
        }
        out.write("\n");
      }

      /* save the file */
      if (saveScene != null) out.write("// SAVE\n" + "file -save;\n");

      out.close();
    } catch (IOException ex) {
      throw new PipelineException(
          "Unable to write temporary MEL script file ("
              + script
              + ") for Job "
              + "("
              + agenda.getJobID()
              + ")!\n"
              + ex.getMessage());
    }

    /* create the process to run the action */
    try {
      ArrayList<String> args = new ArrayList<String>();
      args.add("-batch");
      args.add("-script");
      args.add(script.getPath());

      if (loadScene != null) {
        args.add("-file");
        args.add(loadScene.toOsString());
      }

      String program = "maya";
      if (PackageInfo.sOsType == OsType.Windows) program = (program + ".exe");

      return new SubProcessHeavy(
          agenda.getNodeID().getAuthor(),
          getName() + "-" + agenda.getJobID(),
          program,
          args,
          agenda.getEnvironment(),
          agenda.getWorkingDir(),
          outFile,
          errFile);
    } catch (Exception ex) {
      throw new PipelineException(
          "Unable to generate the SubProcess to perform this Action!\n" + ex.getMessage());
    }
  }
  /** Add the UI components for the given file sequence to the panel. */
  private void addFileSeqPanel(FileSeq fseq) {
    boolean isPresentInWorking = false;
    if ((pStatus != null) && pStatus.hasLightDetails()) {
      NodeDetailsLight details = pStatus.getLightDetails();
      NodeMod mod = details.getWorkingVersion();
      if (mod != null) {
        if (mod.getPrimarySequence().equals(fseq)) isPresentInWorking = true;
        else {
          for (FileSeq sfseq : mod.getSecondarySequences()) {
            if (sfseq.equals(fseq)) {
              isPresentInWorking = true;
              break;
            }
          }
        }
      }
    }

    /* collate the row information */
    ArrayList<VersionID> vids = new ArrayList<VersionID>();
    ArrayList<FileSeq> singles = new ArrayList<FileSeq>();
    TreeSet<FileSeq> enabled = new TreeSet<FileSeq>();
    TreeMap<FileSeq, FileState> fstates = new TreeMap<FileSeq, FileState>();
    TreeMap<FileSeq, NativeFileInfo> finfos = new TreeMap<FileSeq, NativeFileInfo>();
    TreeMap<FileSeq, QueueState> qstates = new TreeMap<FileSeq, QueueState>();
    TreeMap<FileSeq, Boolean[]> novel = new TreeMap<FileSeq, Boolean[]>();
    {
      TreeMap<FileSeq, Integer> wsingles = new TreeMap<FileSeq, Integer>();
      if ((pStatus != null) && pStatus.hasLightDetails()) {
        if (isPresentInWorking) {
          if (pStatus.hasHeavyDetails()) {
            NodeDetailsHeavy details = pStatus.getHeavyDetails();

            FileState[] fs = details.getFileStates(fseq);
            QueueState[] qs = details.getQueueStates();
            NativeFileInfo[] infos = details.getFileInfos(fseq);
            if ((fs != null) && (qs != null) && (infos != null)) {
              int wk;
              for (wk = 0; wk < fs.length; wk++) {
                FileSeq sfseq = new FileSeq(fseq, wk);
                wsingles.put(sfseq, wk);

                fstates.put(sfseq, fs[wk]);
                finfos.put(sfseq, infos[wk]);
                qstates.put(sfseq, qs[wk]);

                if (fs[wk] != FileState.CheckedIn) enabled.add(sfseq);
              }
            }
          } else {
            NodeDetailsLight details = pStatus.getLightDetails();

            int wk;
            for (wk = 0; wk < fseq.numFrames(); wk++) {
              FileSeq sfseq = new FileSeq(fseq, wk);
              wsingles.put(sfseq, wk);

              if (details.getVersionState() == VersionState.CheckedIn) {
                fstates.put(sfseq, FileState.CheckedIn);
                qstates.put(sfseq, QueueState.Undefined);
              } else {
                enabled.add(sfseq);
              }
            }
          }
        }

        {
          vids.addAll(pNovelty.keySet());
          Collections.reverse(vids);

          int idx = 0;
          for (VersionID vid : vids) {
            TreeMap<FileSeq, boolean[]> table = pNovelty.get(vid);
            for (FileSeq nfseq : table.keySet()) {
              if (fseq.similarTo(nfseq)) {
                boolean[] flags = table.get(nfseq);

                int wk;
                for (wk = 0; wk < flags.length; wk++) {
                  FileSeq sfseq = new FileSeq(nfseq, wk);
                  if (!wsingles.containsKey(sfseq)) wsingles.put(sfseq, null);

                  Boolean[] rflags = novel.get(sfseq);
                  if (rflags == null) {
                    rflags = new Boolean[pNovelty.size()];
                    novel.put(sfseq, rflags);
                  }

                  rflags[idx] = new Boolean(flags[wk]);
                }

                break;
              }
            }

            idx++;
          }
        }
      }

      TreeMap<Integer, FileSeq> order = new TreeMap<Integer, FileSeq>();
      for (FileSeq sfseq : wsingles.keySet()) {
        int frame = -1;
        if (sfseq.hasFrameNumbers()) frame = sfseq.getFrameRange().getStart();

        order.put(frame, sfseq);
      }

      singles.addAll(order.values());
    }

    /* add the panel */
    {
      JFileSeqPanel panel =
          new JFileSeqPanel(
              this,
              pManagerPanel,
              pStatus,
              pPrivilegeDetails,
              fseq,
              vids,
              pOffline,
              singles,
              fstates,
              finfos,
              qstates,
              enabled,
              novel,
              pIsListLayout);

      if (pIsListLayout) pFileSeqsBox.add(panel);
      else pFileSeqsTab.addTab(fseq.getFilePattern().toString(), sTabIcon, panel);

      pFileSeqPanels.put(fseq, panel);
    }
  }
  /**
   * Update the UI components to reflect the current per-file status.
   *
   * @param status The current node status.
   * @param novelty The per-file novelty flags.
   * @param offline The revision numbers of the offline checked-in versions.
   */
  protected synchronized void updateNodeStatus(
      NodeStatus status,
      TreeMap<VersionID, TreeMap<FileSeq, boolean[]>> novelty,
      TreeSet<VersionID> offline) {
    super.updateNodeStatus(status, false);

    pNovelty = novelty;
    pOffline = offline;

    NodeDetailsLight details = null;
    if (pStatus != null) details = pStatus.getLightDetails();

    /* files */
    {
      pFileSeqsTab.removeAll();
      pFileSeqsBox.removeAll();
      pFileSeqPanels.clear();

      if ((pNovelty != null) && (details != null)) {
        NodeMod mod = details.getWorkingVersion();
        NodeVersion vsn = details.getLatestVersion();

        NodeCommon com = null;
        if (mod != null) com = mod;
        else if (vsn != null) com = vsn;
        else assert (false);

        /* get the primary and unique secondary file sequences */
        FileSeq primary = com.getPrimarySequence();
        TreeSet<FileSeq> secondary = new TreeSet<FileSeq>();
        {
          secondary.addAll(com.getSecondarySequences());

          TreeSet<FileSeq> unique = new TreeSet<FileSeq>();
          for (TreeMap<FileSeq, boolean[]> table : pNovelty.values()) unique.addAll(table.keySet());

          for (FileSeq ufseq : unique) {
            boolean found = false;

            if (ufseq.similarTo(primary)) found = true;
            else {
              for (FileSeq fseq : secondary) {
                if (ufseq.similarTo(fseq)) {
                  found = true;
                  break;
                }
              }
            }

            if (!found) secondary.add(ufseq);
          }
        }

        /* add the file sequence UI components */
        addFileSeqPanel(primary);
        for (FileSeq fseq : secondary) addFileSeqPanel(fseq);

        if (pIsListLayout) pFileSeqsBox.add(UIFactory.createFiller(sSize));
      }
    }

    pFileSeqsTab.setVisible(!pIsListLayout);
    pFileSeqsScroll.setVisible(pIsListLayout);

    if (pIsListLayout) pFileSeqsScroll.revalidate();
    else pFileSeqsTab.revalidate();
  }
  /**
   * Construct a {@link SubProcessHeavy SubProcessHeavy} instance which when executed will fulfill
   * the given action agenda.
   *
   * <p>
   *
   * @param agenda The agenda to be accomplished by the action.
   * @param outFile The file to which all STDOUT output is redirected.
   * @param errFile The file to which all STDERR output is redirected.
   * @return The SubProcess which will fulfill the agenda.
   * @throws PipelineException If unable to prepare a SubProcess due to illegal, missing or
   *     imcompatable information in the action agenda or a general failure of the prep method code.
   */
  public SubProcessHeavy prep(ActionAgenda agenda, File outFile, File errFile)
      throws PipelineException {
    NodeID nodeID = agenda.getNodeID();

    /* sanity checks */
    File targetScene = null;
    ArrayList<File> sourceScenes = new ArrayList<File>();
    ArrayList<String> mergePatterns = new ArrayList<String>();
    File preBuild = null;
    File postBuild = null;
    File preScene = null;
    File postScene = null;
    {
      DoubleMap<Integer, String, TreeSet<FileSeq>> sources =
          new DoubleMap<Integer, String, TreeSet<FileSeq>>();

      for (String sname : agenda.getSourceNames()) {
        if (hasSourceParams(sname)) {
          FileSeq fseq = agenda.getPrimarySource(sname);
          Integer order = (Integer) getSourceParamValue(sname, "Order");
          addSourceSeq(order, sname, fseq, sources);
        }

        for (FileSeq fseq : agenda.getSecondarySources(sname)) {
          FilePattern fpat = fseq.getFilePattern();
          if (hasSecondarySourceParams(sname, fpat)) {
            Integer order = (Integer) getSecondarySourceParamValue(sname, fpat, "Order");
            addSourceSeq(order, sname, fseq, sources);
          }
        }
      }

      for (Integer order : sources.keySet()) {
        for (String sname : sources.get(order).keySet()) {
          for (FileSeq fseq : sources.get(order).get(sname)) {
            String pattern = null;
            if (fseq.equals(agenda.getPrimarySource(sname)))
              pattern = (String) getSourceParamValue(sname, "MergePattern");
            else {
              FilePattern fpat = fseq.getFilePattern();
              pattern = (String) getSecondarySourceParamValue(sname, fpat, "MergePattern");
            }

            if ((pattern == null) || (pattern.length() == 0))
              throw new PipelineException(
                  "The Merge Pattern for file sequence ("
                      + fseq
                      + ") of source node "
                      + "("
                      + sname
                      + ") was not specified!");

            NodeID snodeID = new NodeID(nodeID, sname);
            File sfile =
                new File(PackageInfo.sProdDir, snodeID.getWorkingParent() + "/" + fseq.getFile(0));

            sourceScenes.add(sfile);
            mergePatterns.add(pattern);
          }
        }
      }

      /* generate the name of the Houdini scene to save */
      {
        FileSeq fseq = agenda.getPrimaryTarget();
        String suffix = fseq.getFilePattern().getSuffix();
        if (!fseq.isSingle() || (suffix == null) || !suffix.equals("hip"))
          throw new PipelineException(
              "The HfsBuild Action requires that the primary target file sequence must "
                  + "be a single Houdini scene file!");

        targetScene =
            new File(PackageInfo.sProdDir, nodeID.getWorkingParent() + "/" + fseq.getFile(0));
      }

      /* command script files */
      {
        String sname = (String) getSingleParamValue("PreBuildScript");
        if (sname != null) {
          FileSeq fseq = agenda.getPrimarySource(sname);
          if (fseq == null)
            throw new PipelineException(
                "Somehow the Pre Build Script node ("
                    + sname
                    + ") was not one of the "
                    + "source nodes!");

          String suffix = fseq.getFilePattern().getSuffix();
          if (!fseq.isSingle() || (suffix == null) || !(suffix.equals("cmd")))
            throw new PipelineException(
                "The HfsBuild Action requires that the source node specified by the Pre "
                    + "Build Script parameter ("
                    + sname
                    + ") must have a single command "
                    + "script (.cmd) as its primary file sequence!");

          NodeID snodeID = new NodeID(nodeID, sname);
          preBuild =
              new File(PackageInfo.sProdDir, snodeID.getWorkingParent() + "/" + fseq.getFile(0));
        }
      }

      {
        String sname = (String) getSingleParamValue("PostBuildScript");
        if (sname != null) {
          FileSeq fseq = agenda.getPrimarySource(sname);
          if (fseq == null)
            throw new PipelineException(
                "Somehow the Post Build Script node ("
                    + sname
                    + ") was not one of the "
                    + "source nodes!");

          String suffix = fseq.getFilePattern().getSuffix();
          if (!fseq.isSingle() || (suffix == null) || !(suffix.equals("cmd")))
            throw new PipelineException(
                "The HfsBuild Action requires that the source node specified by the Post "
                    + "Build Script parameter ("
                    + sname
                    + ") must have a single command "
                    + "script (.cmd) as its primary file sequence!");

          NodeID snodeID = new NodeID(nodeID, sname);
          postBuild =
              new File(PackageInfo.sProdDir, snodeID.getWorkingParent() + "/" + fseq.getFile(0));
        }
      }

      {
        String sname = (String) getSingleParamValue("PreSceneScript");
        if (sname != null) {
          FileSeq fseq = agenda.getPrimarySource(sname);
          if (fseq == null)
            throw new PipelineException(
                "Somehow the Pre Scene Script node ("
                    + sname
                    + ") was not one of the "
                    + "source nodes!");

          String suffix = fseq.getFilePattern().getSuffix();
          if (!fseq.isSingle() || (suffix == null) || !(suffix.equals("cmd")))
            throw new PipelineException(
                "The HfsBuild Action requires that the source node specified by the Pre "
                    + "Scene Script parameter ("
                    + sname
                    + ") must have a single command script "
                    + "(.cmd) as its primary file sequence!");

          NodeID snodeID = new NodeID(nodeID, sname);
          preScene =
              new File(PackageInfo.sProdDir, snodeID.getWorkingParent() + "/" + fseq.getFile(0));
        }
      }

      {
        String sname = (String) getSingleParamValue("PostSceneScript");
        if (sname != null) {
          FileSeq fseq = agenda.getPrimarySource(sname);
          if (fseq == null)
            throw new PipelineException(
                "Somehow the Post Scene Script node ("
                    + sname
                    + ") was not one of the "
                    + "source nodes!");

          String suffix = fseq.getFilePattern().getSuffix();
          if (!fseq.isSingle() || (suffix == null) || !(suffix.equals("cmd")))
            throw new PipelineException(
                "The HfsBuild Action requires that the source node specified by the Post "
                    + "Scene Script parameter ("
                    + sname
                    + ") must have a single command script "
                    + "(.cmd) as its primary file sequence!");

          NodeID snodeID = new NodeID(nodeID, sname);
          postScene =
              new File(PackageInfo.sProdDir, snodeID.getWorkingParent() + "/" + fseq.getFile(0));
        }
      }
    }

    /* create the temporary Houdini command script */
    File hscript = createTemp(agenda, 0644, "cmd");
    try {
      FileWriter out = new FileWriter(hscript);

      if (preBuild != null) out.write("source " + preBuild + "\n");

      /* load the source scenes */
      int wk;
      for (wk = 0; wk < sourceScenes.size(); wk++) {
        File sourceScene = sourceScenes.get(wk);
        String pattern = mergePatterns.get(wk);

        if (preScene != null) out.write("source " + preScene + "\n");

        out.write("mread");
        if (wk > 0) out.write(" -o -m " + pattern);
        out.write(" " + sourceScene + "\n");

        if (postScene != null) out.write("source " + postScene + "\n");
      }

      if (postBuild != null) out.write("source " + postBuild + "\n");

      /* save the file */
      out.write("mwrite " + targetScene + "\n");

      out.close();
    } catch (IOException ex) {
      throw new PipelineException(
          "Unable to write temporary command file ("
              + hscript
              + ") for Job "
              + "("
              + agenda.getJobID()
              + ")!\n"
              + ex.getMessage());
    }

    /* create the wrapper shell script */
    File script = createTemp(agenda, 0755, "bash");
    try {
      FileWriter out = new FileWriter(script);
      out.write("#!/bin/bash\n\n" + "cat " + hscript + " | hscript");
      out.close();
    } catch (IOException ex) {
      throw new PipelineException(
          "Unable to write temporary script file ("
              + script
              + ") for Job "
              + "("
              + agenda.getJobID()
              + ")!\n"
              + ex.getMessage());
    }

    try {
      return new SubProcessHeavy(
          agenda.getNodeID().getAuthor(),
          getName() + "-" + agenda.getJobID(),
          script.getPath(),
          new ArrayList<String>(),
          agenda.getEnvironment(),
          agenda.getWorkingDir(),
          outFile,
          errFile);
    } catch (Exception ex) {
      throw new PipelineException(
          "Unable to generate the SubProcess to perform this Action!\n" + ex.getMessage());
    }
  }
  /**
   * Construct a {@link SubProcessHeavy SubProcessHeavy} instance which when executed will fulfill
   * the given action agenda.
   *
   * <p>
   *
   * @param agenda The agenda to be accomplished by the action.
   * @param outFile The file to which all STDOUT output is redirected.
   * @param errFile The file to which all STDERR output is redirected.
   * @return The SubProcess which will fulfill the agenda.
   * @throws PipelineException If unable to prepare a SubProcess due to illegal, missing or
   *     imcompatable information in the action agenda or a general failure of the prep method code.
   */
  public SubProcessHeavy prep(ActionAgenda agenda, File outFile, File errFile)
      throws PipelineException {
    /* model filenames */
    TreeMap<String, Path> modelPaths = new TreeMap<String, Path>();
    TreeMap<String, Path> animPaths = new TreeMap<String, Path>();

    TreeMap<String, String> nameSpaces = new TreeMap<String, String>();
    TreeMap<String, String> reverseNameSpaces = new TreeMap<String, String>();
    TreeMap<String, Boolean> usesNameSpace = new TreeMap<String, Boolean>();
    TreeMap<String, String> buildTypes = new TreeMap<String, String>();

    for (String sname : agenda.getSourceNames()) {
      if (hasSourceParams(sname)) {
        FileSeq fseq = agenda.getPrimarySource(sname);
        String suffix = fseq.getFilePattern().getSuffix();
        if (fseq.isSingle() && (suffix != null)) {
          if (suffix.equals("ma") || suffix.equals("mb")) {
            Path npath = new Path(sname);
            String type = getSourceStringParamValue(sname, aSceneType);
            String nspace = getSourceStringParamValue(sname, aPrefixName);
            if (nspace == null) {
              nspace = npath.getName();
            }
            if (type.equals(aModel))
              modelPaths.put(sname, new Path(npath.getParentPath(), fseq.getPath(0)));
            else {
              animPaths.put(sname, new Path(npath.getParentPath(), fseq.getPath(0)));
              nspace += "_a";
            }

            boolean useNSpace = false;
            {
              Boolean tf = (Boolean) getSourceParamValue(sname, aNameSpace);
              useNSpace = ((tf != null) && tf);
            }

            String buildType = getSourceStringParamValue(sname, aBuildType);
            if (buildType == null)
              throw new PipelineException(
                  "The value of the "
                      + aBuildType
                      + " source parameter for node "
                      + "("
                      + sname
                      + ") was not set!");

            buildTypes.put(sname, buildType);
            nameSpaces.put(sname, nspace);
            reverseNameSpaces.put(nspace, sname);
            usesNameSpace.put(sname, useNSpace);
          }
        }
      }
    }

    /* the target Maya scene */
    Path targetScene = getMayaSceneTargetPath(agenda);
    String sceneType = getMayaSceneType(agenda);

    /* create a temporary MEL script file */
    File script = createTemp(agenda, "mel");
    try {
      FileWriter out = new FileWriter(script);

      /* provide parameters as MEL variables */
      out.write(genFrameRangeVarsMEL());

      /* rename the current scene as the output scene */
      out.write(
          "// SCENE SETUP\n"
              + "file -rename \""
              + targetScene
              + "\";\n"
              + "file -type \""
              + sceneType
              + "\";\n\n");

      out.write(genUnitsMEL());

      writeInitialMEL(agenda, out);

      /* the model file reference imports */
      TreeMap<String, Path> all = new TreeMap<String, Path>(modelPaths);
      all.putAll(animPaths);
      for (String sname : all.keySet()) {
        Path mpath = all.get(sname);

        String buildType = buildTypes.get(sname);

        out.write(
            "// MODEL: "
                + sname
                + "\n"
                + "print \""
                + buildType
                + " Model: "
                + mpath
                + "\\n\";\n"
                + "file\n");

        String nspace = nameSpaces.get(sname);

        boolean useNSpace = usesNameSpace.get(sname);

        if (buildType.equals(aImport)) {
          out.write("  -import\n");

          if (useNSpace) out.write("  -namespace \"" + nspace + "\"\n");
        } else if (buildType.equals(aReference)) {
          out.write("  -reference\n");

          if (useNSpace) out.write("  -namespace \"" + nspace + "\"\n");
          else out.write("  -renamingPrefix \"" + nspace + "\"\n");
        } else {
          throw new PipelineException(
              "Unknown value for the "
                  + aBuildType
                  + " source parameter for node "
                  + "("
                  + sname
                  + ") encountered!");
        }

        {
          String fname = mpath.getName();
          if (fname.endsWith("ma")) out.write("  -type \"mayaAscii\"\n");
          else if (fname.endsWith("mb")) out.write("  -type \"mayaBinary\"\n");
          else
            throw new PipelineException(
                "Unknown Maya scene format for source file (" + mpath + ")!");
        }

        out.write("  -options \"v=0\"\n" + "  \"$WORKING" + mpath + "\";\n\n");
      }

      writeModelMEL(agenda, out);

      out.write(
          "global proc string removePrefix(string $name)\n"
              + "{\n"
              + "  string $toReturn;\n"
              + "  string $buffer[];\n"
              + "  tokenize($name, \"|\", $buffer);\n"
              + "  string $part;\n"
              + "  for ($part in $buffer) {\n"
              + "    string $buffer2[];\n"
              + "    tokenize($part, \":\", $buffer2);\n"
              + "    if ($toReturn == \"\")\n"
              + "      $toReturn += $buffer2[(size($buffer2) -1)];\n"
              + "    else\n"
              + "      $toReturn += \"|\" + $buffer2[(size($buffer2) -1)];\n"
              + "  }\n"
              + "  return $toReturn;    \n"
              + "}\n");

      out.write(
          "global proc string addPrefix(string $name, string $prefix)\n"
              + "{\n"
              + "  string $toReturn;\n"
              + "  string $buffer[];\n"
              + "  tokenize($name, \"|\", $buffer);\n"
              + "  string $part;\n"
              + "  for ($part in $buffer) {\n"
              + "    if ($toReturn == \"\")\n"
              + "      $toReturn += $prefix + $buffer[(size($buffer) -1)];\n"
              + "    else\n"
              + "      $toReturn += \"|\" + $prefix + $buffer[(size($buffer) -1)];\n"
              + "  }\n"
              + "  return $toReturn;\n"
              + "}\n");

      for (String sname : animPaths.keySet()) {
        String nspace = nameSpaces.get(sname);
        String otherNspace = nspace.substring(0, nspace.length() - 2);

        String modelName = reverseNameSpaces.get(otherNspace);

        if (modelName != null) {
          boolean useNSpace = usesNameSpace.get(sname);
          boolean otherUseNSpace = usesNameSpace.get(modelName);

          String buildType = buildTypes.get(sname);
          String otherBuildType = buildTypes.get(modelName);
          String prefix = generatePrefix(nspace, useNSpace, buildType);
          String otherPrefix = generatePrefix(otherNspace, otherUseNSpace, otherBuildType);

          out.write(
              "{\n"
                  + "  string $crvPrefix = \""
                  + prefix
                  + "\";\n"
                  + "  string $objPrefix = \""
                  + otherPrefix
                  + "\";\n"
                  + "  string $obj = $crvPrefix + \"curveInfo\";\n"
                  + "  if (`objExists $obj`) {\n"
                  + "    int $size = `getAttr -s ($obj + \".cn\")`;\n"
                  + "    int $i = 0;\n"
                  + "    for ($i = 0; $i < $size; $i++) {\n"
                  + "      string $curve = `getAttr  ($obj + \".cn[\" + $i + \"]\")`;\n"
                  + "      string $attr = `getAttr  ($obj + \".an[\" + $i + \"]\")`;\n"
                  + "      $curve =  addPrefixToName($crvPrefix, $curve);\n"
                  + "      $attr  =  addPrefixToName($objPrefix, $attr);\n"
                  + "      print(\"connectAttr -f \" + $curve + \" \" + $attr + \"\\n\");\n"
                  + "      connectAttr -f $curve $attr;   \n"
                  + "    }\n"
                  + "  }\n"
                  + "  else {\n"
                  + "    string $curves[] = `ls -type animCurve ($crvPrefix + \"*\")`;\n"
                  + "    string $curve;\n"
                  + "    for ($curve in $curves) {\n"
                  + "      string $shortName = removePrefix($curve);\n"
                  + "      string $buffer[];\n"
                  + "      tokenize($shortName, \"_\", $buffer);\n"
                  + "      string $attr = $buffer[(size($buffer) -1)];\n"
                  + "      string $name = \"\";\n"
                  + "      int $length = (size($buffer) -1);\n"
                  + "      int $i;\n"
                  + "      for ($i = 0; $i < $length; $i++) {\n"
                  + "        $name += $buffer[$i];\n"
                  + "        if ($i != ($length - 1) )\n"
                  + "          $name += \"_\";\n"
                  + "      }\n"
                  + "      string $channel = addPrefix($name, $objPrefix) + \".\" + $attr;\n"
                  + "      print ($curve + \"\\t\" + $channel + \"\\n\");\n"
                  + "      if (!`getAttr -l $channel`)\n"
                  + "        connectAttr -f ($curve + \".output\") $channel;\n"
                  + "    }\n"
                  + "  }\n"
                  + "}\n");
        }
      }

      /* set the time options */
      out.write(genPlaybackOptionsMEL());

      writeAnimMEL(agenda, out);

      /* save the file */
      out.write(
          "// SAVE\n"
              + "print \"Saving Generated Scene: "
              + targetScene
              + "\\n\";\n"
              + "file -save;\n\n");

      writeFinalMEL(agenda, out);

      out.close();
    } catch (IOException ex) {
      throw new PipelineException(
          "Unable to write temporary MEL script file ("
              + script
              + ") for Job "
              + "("
              + agenda.getJobID()
              + ")!\n"
              + ex.getMessage());
    }

    /* create the process to run the action */
    return createMayaSubProcess(null, script, true, agenda, outFile, errFile);
  }