org.eclipse.ice.item.utilities.moose.MOOSEFileHandler.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.ice.item.utilities.moose.MOOSEFileHandler.java

Source

/*******************************************************************************
 * Copyright (c) 2013, 2014- UT-Battelle, LLC.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *   Initial API and implementation and/or initial documentation - Jay Jay Billings,
 *   Jordan H. Deyton, Dasha Gorin, Alexander J. McCaskey, Taylor Patterson,
 *   Claire Saunders, Matthew Wang, Anna Wojtowicz
 *******************************************************************************/
package org.eclipse.ice.item.utilities.moose;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.URI;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;

import javax.naming.OperationNotSupportedException;

import org.apache.commons.io.FilenameUtils;
import org.eclipse.core.resources.IFile;
import org.eclipse.ice.datastructures.ICEObject.Component;
import org.eclipse.ice.datastructures.entry.IEntry;
import org.eclipse.ice.datastructures.form.AdaptiveTreeComposite;
import org.eclipse.ice.datastructures.form.DataComponent;
import org.eclipse.ice.datastructures.form.Form;
import org.eclipse.ice.datastructures.form.TreeComposite;
import org.eclipse.ice.datastructures.form.iterator.BreadthFirstTreeCompositeIterator;
import org.eclipse.ice.io.serializable.IReader;
import org.eclipse.ice.io.serializable.IWriter;
import org.eclipse.ice.item.nuclear.MOOSEModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yaml.snakeyaml.Yaml;

/**
 * <p>
 * This class reads and writes MOOSE Blocks and Parameters to and from the
 * different MOOSE file types, including parsing from YAML and writing to
 * GetPot.
 * </p>
 * <p>
 * There are two primary types of files associated with MOOSE: the YAML file
 * used to specify the possible configuration of an input file and the input
 * file itself, which is a GetPot/Perl configuration file.
 * </p>
 * <p>
 * This class realizes the IComponentVisitor interface to find DataComponents in
 * the TreeComposites that can be converted into a parameter set for a MOOSE
 * input block. If there are other Components in a TreeComposite. They are
 * completely ignored.
 * </p>
 * <p>
 * Blocks and YAMLBlocks are used because each block needs to be converted to or
 * from a TreeComposite. This is complicated in the case of loading the YAML
 * input specification because it is a *specification* (or schema) and not the
 * input itself. The nodes of this tree are what could be configured, not what
 * is, so they must be setup as child exemplars on a TreeComposite.
 * </p>
 * 
 * @author Jay Jay Billings, Anna Wojtowicz, Alex McCaskey
 */
public class MOOSEFileHandler implements IReader, IWriter {

    /**
     * Logger for handling event messages and other information.
     */
    private static final Logger logger = LoggerFactory.getLogger(MOOSEFileHandler.class);

    /**
     * A flag to denote whether or not debugging is enabled
     */
    private static boolean debugFlag = false;

    /**
     * Set the debug flag
     */
    static {
        if (System.getProperty("DebugICE") != null) {
            debugFlag = true;
        }
    }

    /**
     * This operation writes a set of MOOSE blocks to the specified file path.
     * 
     * @param filePath
     *            The file path to which the MOOSE blocks should be dumped. If
     *            the path is null or empty, the operation returns without doing
     *            any work.
     * @param blockSet
     *            The collection of TreeComposites that represent MOOSE blocks
     *            to be dumped to the file. The TreeComposites should only
     *            contain a single DataComponent, id = 1, and other
     *            TreeComposites. Any other components in the TreeComposite will
     *            be ignored.
     */
    public void dumpInputFile(String filePath, ArrayList<TreeComposite> blockSet) {

        // Local Declarations
        File inputFile = null;
        String outputString = "";
        FileWriter fileWriter = null;
        BufferedWriter fileOutputWriter = null;
        ArrayList<Block> blocks = null;

        // Only do this if the file path and data are valid
        if (filePath != null && blockSet != null) {
            // Create the blocks from the incoming tree composites
            blocks = new ArrayList<Block>();
            for (TreeComposite blockTree : blockSet) {
                Block block = new Block();
                block.fromTreeComposite(blockTree);
                blocks.add(block);
            }
            // Reorganize the blocks so that blocks that must come first, such
            // as "Functions" and "Variables" do.
            for (int i = 0; i < blocks.size(); i++) {
                // Grab the block and its name
                Block block = blocks.get(i);
                String name = block.getName();
                // Check for the names of the blocks that have to be written
                // first
                if ("Functions".equals(name) || "Variables".equals(name)) {
                    // Remove the block from the set
                    blocks.remove(i);
                    // Write the block to the output string
                    outputString += block.toGetPot(null);
                    // Decrement the counter so we go back and get any remaining
                    // blocks.
                    --i;
                }
            }
            // Dump the blocks to the output String
            for (int i = 0; i < blocks.size(); i++) {
                outputString += blocks.get(i).toGetPot(null);
            }
            // Try to write the file
            try {
                // Open the file and writers
                inputFile = new File(filePath);
                fileWriter = new FileWriter(inputFile);
                fileOutputWriter = new BufferedWriter(fileWriter);
                // Dump the string to the file
                fileOutputWriter.write(outputString);
                // Flush everything to the file and close it
                fileOutputWriter.flush();
                fileOutputWriter.close();
                fileWriter.close();
            } catch (IOException e) {
                logger.info("MOOSEFileHandler Exception: " + "Unable to write output file.");
                logger.error(getClass().getName() + " Exception!", e);
            }
        }

        return;
    }

    /**
     * This operations loads a MOOSE GetPot file at the specified path and
     * returns a fully-configured set of ICE TreeComposites.
     * 
     * @param filePath
     *            The file path from which the MOOSE blocks written in GetPot
     *            should be read. If the path is null or empty, the operation
     *            returns without doing any work.
     * @return The MOOSE input file specification as read from the GetPot input
     *         and stored in TreeComposites. Each TreeComposite contains both
     *         parameters and exemplar children. Any parameters in a
     *         TreeComposite are contained in a DataComponent. The id of the
     *         data component is 1.
     */
    public ArrayList<TreeComposite> loadFromGetPot(String filePath) {

        // Local Declarations
        ArrayList<TreeComposite> trees = new ArrayList<TreeComposite>();
        byte[] fileByteArray = null;
        String mooseFileString = null, potLine = null;

        // Quit if the path is boned
        if (filePath == null || filePath.isEmpty()) {
            return null;
        }

        // Post some debug info
        if (debugFlag) {
            logger.info("MOOSEFileHandler Message: " + "Attempting to loading GetPot file " + filePath);
        }

        // Load the GetPot file
        try {
            RandomAccessFile mooseFile = new RandomAccessFile(filePath, "r");
            // Put it into a byte array
            fileByteArray = new byte[(int) mooseFile.length()];
            mooseFile.read(fileByteArray);
            // And then a string
            mooseFileString = new String(fileByteArray);
            // Close the file
            mooseFile.close();
            // Post some more debug info
            if (debugFlag) {
                logger.info("MOOSEFileHandler Message: File loaded.");
            }
        } catch (IOException e) {
            // Complain if the file is not found
            System.err.println("MOOSEFileHandler Message: " + "Unable to load GetPot file!");
            logger.error(getClass().getName() + " Exception!", e);
        }

        // Check the string before proceeding
        if (mooseFileString != null && !mooseFileString.isEmpty()) {
            // Create an array list from the string
            ArrayList<String> potLines = new ArrayList<String>(Arrays.asList(mooseFileString.split("\n")));

            // Remove (non-parameter) commented lines and white space
            String trimmedPotLine = "";
            for (int i = 0; i < potLines.size(); i++) {

                trimmedPotLine = potLines.get(i).trim();

                if (trimmedPotLine.startsWith("#") && !trimmedPotLine.contains("=") && !trimmedPotLine.contains("[")
                        && !trimmedPotLine.contains("]")) {
                    // Lines that start with "#" but have no "=" are comments
                    // that aren't parameters and should be removed
                    potLines.remove(i);
                    // Update "i" so that we read correctly
                    --i;
                } else if (potLines.get(i).isEmpty()) {
                    // Remove empty lines
                    potLines.remove(i);
                    // Update "i" so that we read correctly
                    --i;
                } else {
                    // This is a rare scenario to check for, but it's possible
                    // (and has happened at least once) where a line is just a
                    // comment (starts with "#") AND includes a "=" in the text
                    // of the comment
                    if (trimmedPotLine.startsWith("#") && trimmedPotLine.contains("=")) {
                        String[] splitTrimmedPotLine = trimmedPotLine.split("\\s+");
                        if (splitTrimmedPotLine.length > 4) {
                            // Skip this line, it's a comment that's been
                            // mistaken as a parameter
                            potLines.remove(i);
                            --i;
                            continue;
                        }
                    }

                    // Otherwise, the normal behavior is that the line should be
                    // trimmed and be considered a real parameter
                    potLines.set(i, potLines.get(i).trim());
                }
            }

            // Read all of the lines again, create blocks and load them.
            int counter = 0, endCounter = 1;
            while (counter < potLines.size()) {
                // Get the line and shift the counters
                potLine = potLines.get(counter);
                ++counter;

                // The start of a full block is a line with the name in brackets
                // and without the "./" sequence.
                if (potLine.contains("[") && potLine.contains("]")) {
                    // Go to the next line
                    potLine = potLines.get(endCounter);

                    // Loop over the block and find the end
                    while (!potLine.contains("[]")) {
                        // Update the line and the counter
                        potLine = potLines.get(endCounter);
                        ++endCounter;
                    }
                    // Create a new block
                    Block block = new Block();
                    ArrayList<String> blockLines = null;
                    if (endCounter >= counter - 1) {
                        blockLines = new ArrayList<String>(potLines.subList(counter - 1, endCounter));
                    }
                    if (blockLines != null && !blockLines.isEmpty()) {
                        StringBuilder stringBuilder = new StringBuilder(blockLines.get(0));
                        blockLines.set(0, stringBuilder.toString());
                        block.fromGetPot(blockLines);
                        // Add the block to the list
                        trees.add(block.toTreeComposite());
                        // Update the counter to point to the last read line
                        counter = endCounter;
                    }

                    // Print some debug information
                    if (debugFlag) {
                        logger.info("\nMOOSEFileHandler Message: " + "Block output read from GetPot file "
                                + filePath + " follows.");
                        // Dump each line of the newly created block
                        for (String line : blockLines) {
                            logger.info(line);
                        }
                    }

                }
            }
        } else if (debugFlag) {
            System.err.println(
                    "MOOSEFileHandler Message: " + "String loaded from " + filePath + " is null or empty.");
        }

        return trees;
    }

    /**
     * This method recursively goes through the Block and gives the list of
     * files available in the project to any File Entries.
     * 
     * @param block
     * @param projectDir
     */
    private void setFileEntries(Block block, String projectDir) {
        // Local Declarations
        String availableFiles = "";

        // Search the block with no children
        if (block.getSubblocks().isEmpty()) {
            for (Parameter p : block.getParameters()) {
                if (p.getCpp_type().contains("FileName")) {
                    File[] files = new File(projectDir).listFiles();
                    for (File file : files) {
                        if (!file.isHidden() && !file.isDirectory()) {
                            // If it is a mesh file, only add exodus extensions
                            if (p.getCpp_type().contains("Mesh")) {
                                String extension = FilenameUtils.getExtension(file.getName());
                                if (extension.equals("e") || extension.equals("exo")) {
                                    availableFiles += file.getName() + " ";
                                }
                            } else {
                                availableFiles += file.getName() + " ";
                            }
                        }
                    }

                    p.setOptions(availableFiles);
                }
            }
        } else {
            // else loop over all sub blocks
            for (Block subBlock : block.getSubblocks()) {
                setFileEntries(subBlock, projectDir);
            }

            // Once we're through that, we still have to check
            // the top level block
            for (Parameter p : block.getParameters()) {
                if (p.getCpp_type().contains("FileName")) {
                    File[] files = new File(projectDir).listFiles();
                    for (File file : files) {
                        if (!file.isHidden() && !file.isDirectory()) {
                            // If it is a mesh file, only add exodus extensions
                            if (p.getCpp_type().contains("Mesh")) {
                                String extension = FilenameUtils.getExtension(file.getName());
                                if (extension.equals("e") || extension.equals("exo")) {
                                    availableFiles += file.getName() + " ";
                                }
                            } else {
                                availableFiles += file.getName() + " ";
                            }
                        }
                    }

                    p.setOptions(availableFiles);
                }
            }
        }

    }

    /**
     * This operations loads a MOOSE YAML file at the specified path and returns
     * a fully-configured set of ICE TreeComposites.
     * 
     * @param filePath
     *            The file path from which the MOOSE blocks written in YAML
     *            should be read. If the path is null or empty, the operation
     *            returns without doing any work.
     * @return The MOOSE input file specification as read from the YAML input
     *         and stored in TreeComposites. Each TreeComposite contains both
     *         parameters and exemplar children. Any parameters in a
     *         TreeComposite are contained in a DataComponent. The id of the
     *         data component is 1.
     * @throws IOException
     */
    public ArrayList<TreeComposite> loadYAML(String filePath) throws IOException {

        // Local Declarations
        InputStream input = null;
        String syntaxFilePath, treeName;
        ArrayList<String> hardPathsList = null;
        ArrayList<TreeComposite> trees = new ArrayList<TreeComposite>();
        Map<String, TreeComposite> treeMap = null;
        TreeComposite oneUpTree = null;

        // Quit if the path is boned
        if (filePath == null || filePath.isEmpty()) {
            return null;
        }

        // Get a handle on the YAML file
        File yamlFile = new File(filePath);
        input = new FileInputStream(yamlFile);

        // Get the project space directory string
        String projectDir = new File(yamlFile.getParent()).getParent();

        // Load the YAML tree
        if (debugFlag) {
            logger.info("MOOSEFileHandler Message: Loading YAML file " + filePath.toString());
        }
        Yaml yaml = new Yaml();
        ArrayList<?> list = (ArrayList<?>) yaml.load(input);
        if (debugFlag) {
            logger.info("MOOSEFileHandler Message: File loaded.");
        }

        // Check we got a valid YAML file
        if (list == null || list.isEmpty()) {
            logger.error("Invalid YAML at " + yamlFile.getAbsolutePath());
            return trees;
        }

        // Load the block list. Use YAMLBlocks so that they can be converted to
        // TreeComposites appropriately.
        for (int i = 0; i < list.size(); i++) {
            Block block = new YAMLBlock();
            block.loadFromMap((Map<String, Object>) list.get(i));

            // Recursively add Files to any File Entries in
            // this block
            setFileEntries(block, projectDir);

            block.active = true;
            trees.add(block.toTreeComposite());
        }

        // Close the files
        try {
            input.close();
        } catch (IOException e) {
            // Complain
            logger.error(getClass().getName() + " Exception!", e);
        }

        // Put all the names of top-level nodes into a list (we use this later)
        ArrayList<String> topLevelNodes = new ArrayList<String>();
        for (TreeComposite node : trees) {
            topLevelNodes.add(node.getName());
        }
        // Instantiate a HashMap that all TreeComposites and their exemplar
        // children trees can be added to, keyed by absolute path name
        treeMap = new HashMap<String, TreeComposite>();

        // Create empty stack for TreeComposites
        Stack<TreeComposite> treeStack = new Stack<TreeComposite>();
        treeStack.push(null);

        // Push the top level TreeComposites from the ArrayList first on first
        for (TreeComposite tree : trees) {
            treeStack.push(tree);
        }
        // Pop one of the top-level trees off to start.
        TreeComposite tree = treeStack.pop();
        ArrayList<TreeComposite> childExemplars;
        treeName = "";
        int prevNameIndex;
        while (tree != null) {

            // Append to the tree name
            treeName += "/" + tree.getName();

            // Put the tree in the Map, keyed on path name
            treeMap.put((treeName.startsWith("/") ? treeName.substring(1) : treeName), tree);

            // Push child exemplars to the top of the tree stack
            childExemplars = tree.getChildExemplars();
            for (int i = (childExemplars.size() - 1); i >= 0; i--) {
                treeStack.push(childExemplars.get(i));
            }

            // If the next tree in the stack is a top-level tree, clear the
            // path name
            if (trees.contains(treeStack.peek())) {
                treeName = "";
            }

            // Otherwise, if the current tree didn't have child exemplars to
            // push onto the stack, remove the last "part" of the path name, as
            // we'll be going back up one level
            else if (childExemplars.isEmpty()) {

                // Get the name of the tree one level up
                prevNameIndex = treeName.lastIndexOf("/" + tree.getName());
                treeName = treeName.substring(0, prevNameIndex);

                // Go up another level if the next tree in the stack isn't
                // a child exemplar of the current tree referenced by treeName
                oneUpTree = treeMap.get(treeName.substring(1));
                if (oneUpTree != null && !oneUpTree.getChildExemplars().contains(treeStack.peek())) {
                    prevNameIndex = treeName.lastIndexOf("/");
                    treeName = ((prevNameIndex == 0 || prevNameIndex == -1) ? treeName
                            : treeName.substring(0, prevNameIndex));
                }

            }

            // Pop the next tree off the stack
            tree = treeStack.pop();
        }

        // Define the file path of the action syntax file
        int yamlIndex = filePath.indexOf(".yaml");
        syntaxFilePath = filePath.substring(0, yamlIndex) + ".syntax";

        // Load the list of all "hard" paths from the action syntax file
        try {
            hardPathsList = loadActionSyntax(syntaxFilePath);
        } catch (IOException e) {
            logger.error(getClass().getName() + " Exception!", e);
        }

        // Begin looking through the TreeComposites for matches to the list of
        // "hard" paths from the action syntax file
        TreeComposite currTree;
        boolean hasType = false;
        int typeIndex = -1;
        String cleanPath;
        ArrayList<TreeComposite> types, currChildExemplars;
        DataComponent typeParameters = null, treeParameters = null;

        for (String path : hardPathsList) {

            // Clean the path of the excess return carriage at the end
            cleanPath = (path.endsWith("\r") ? path.substring(0, path.length() - 1) : path);

            // Get the tree with the corresponding hard path
            currTree = treeMap.get(cleanPath);

            // Check if there is a corresponding tree at all (depends on the
            // user's YAML file), and if so, if it has child exemplars
            if (currTree != null && !(currTree.getChildExemplars().isEmpty())) {

                // Iterate through the child exemplars, look for one named
                // "<type>"
                currChildExemplars = currTree.getChildExemplars();
                for (int i = 0; i < currChildExemplars.size(); i++) {
                    hasType = "<type>".equals(currChildExemplars.get(i).getName());
                    if (hasType) {
                        typeIndex = i;
                        break;
                    }
                }

                if (hasType) {
                    // Get the exemplars of <type>, these will become
                    // the list of types to chose from
                    types = currTree.getChildExemplars().get(typeIndex).getChildExemplars();

                    // Remove the <type> child exemplar from currTree
                    currChildExemplars.remove(typeIndex);
                    currTree.setChildExemplars(currChildExemplars);

                    // Copy all the parameters from currTree into all to the
                    // childExemplars before we instantiate an
                    // AdaptiveTreeComposite
                    Map<String, IEntry> parameterMap = new HashMap<String, IEntry>();
                    for (TreeComposite currType : types) {

                        // Get the current type's data node
                        typeParameters = (DataComponent) currType.getDataNodes().get(0);
                        // Get the current tree's data node
                        treeParameters = (DataComponent) currTree.getDataNodes().get(0);

                        // Put all the typeParameters in a HashMap, keyed on
                        // name
                        parameterMap.clear();
                        for (IEntry parameter : typeParameters.retrieveAllEntries()) {
                            parameterMap.put(parameter.getName(), parameter);
                        }
                        // Loop through the current tree's parameters, appending
                        // them all onto the type's parameters list
                        for (IEntry currEntry : treeParameters.retrieveAllEntries()) {

                            // Check that the HashMap doesn't already have an
                            // entry with the same name
                            if (!parameterMap.containsKey(currEntry.getName())) {
                                // Append the parameter from one list onto the
                                // other
                                typeParameters.addEntry(currEntry);
                            }
                        }
                    }

                    // Create a new AdaptiveTreeComposite with the list of types
                    AdaptiveTreeComposite adapTree = new AdaptiveTreeComposite(types);

                    // Copy all the TreeComposite data (data node, exemplars,
                    // etc.) from currTree
                    adapTree.copy(currTree);
                    currTree = adapTree;

                    // Overwrite the tree in the map
                    treeMap.put(cleanPath, adapTree);

                    // Since we allocated the AdaptiveTreeComposite with new,
                    // the reference to the original object is lost, so we must
                    // wire it back in

                    // Check if this tree is a child exemplar of something (ie.
                    // isn't a top-level node)
                    if (!topLevelNodes.contains(currTree.getName())) {

                        // Get the name of the tree that this
                        // AdaptiveTreeComposite is a child exemplar of
                        prevNameIndex = cleanPath.indexOf("/" + currTree.getName());
                        treeName = cleanPath.substring(0, prevNameIndex);

                        // Re-set the AdaptiveTreeComposite as a child exemplar
                        // of whatever tree it belongs to
                        TreeComposite exemplarParent = treeMap.get(treeName);
                        if (exemplarParent != null) {
                            exemplarParent.addChildExemplar(currTree);
                        }
                    }
                }
            }

            // Reset flags, indices
            hasType = false;
            typeIndex = -1;
        }

        // Reconstruct the top level nodes into an ArrayList
        ArrayList<TreeComposite> newTrees = new ArrayList<TreeComposite>();
        for (String nodeName : topLevelNodes) {
            newTrees.add(treeMap.get(nodeName));
        }

        return newTrees;
    }

    /**
     * This method is responsible for loading the action syntax file associated
     * with a MOOSE app. It reads through the list of paths and returns the
     * names of any that are "hard" paths (ie. have no asterisks). It also only
     * returns unique paths, as there is no significance to duplicates (to us)
     * in the action syntax file (it's a MOOSE "bug").
     * 
     * @param filePath
     *            The file path pointing to the action syntax file
     * @return A String ArrayList of unique action syntax "hard" paths (ie. does
     *         not end in an asterisk)
     * @throws IOException
     */
    public ArrayList<String> loadActionSyntax(String filePath) throws IOException {

        // Local declarations
        ArrayList<String> actionSyntax = null;
        String currLine, previousLine = "";

        // Check if the filepath is valid
        if (filePath == null || filePath.isEmpty()) {
            if (debugFlag) {
                logger.info("MOOSEFileHandler Error: Could not open " + "action syntax file: " + filePath);
            }
            return actionSyntax;
        }

        // Open the action syntax file
        if (debugFlag) {
            logger.info("MOOSEFileHandler Message: Loading action " + "syntax file: " + filePath);
        }

        actionSyntax = (ArrayList<String>) Files.readAllLines(Paths.get(filePath), Charset.defaultCharset());

        // Iterate through the list and eliminate non-hard-paths and
        // duplicate entries
        int i = 0;
        while (i < actionSyntax.size()) {

            // Get the current line
            currLine = actionSyntax.get(i);

            // Check for an asterisk at the end of the line
            if (currLine.endsWith("*\r") || currLine.endsWith("*")) {
                actionSyntax.remove(currLine);
            }

            // Remove from the ArrayList if it's the same as the last line
            else if (previousLine.equals(currLine)) {
                actionSyntax.remove(currLine);
            }

            // Otherwise it's a unique hard-path, and move to the next line
            else {
                previousLine = currLine;
                i++;
            }
        }

        return actionSyntax;
    }

    /**
     * This realization of IWriter.write() gets a valid TreeComposite from the
     * provided Form and writes it to the given file reference as a valid MOOSE
     * *.i input file. It throws an uncaught IllegalArgumentException if the
     * Form is not valid.
     * 
     * @param formToWrite
     *            The Form containing a valid TreeComposite to be written to the
     *            MOOSE input format.
     * @param file
     *            Reference to the file we are writing to.
     */
    @Override
    public void write(Form formToWrite, IFile file) {

        // Make sure we have a good Form.
        if (formToWrite == null) {
            throw new IllegalArgumentException("Error: MOOSEFileHandler.write() - the provided Form was null.");
        }

        TreeComposite mooseTree = (TreeComposite) formToWrite.getComponent(MOOSEModel.mooseTreeCompositeId);
        ArrayList<TreeComposite> children = new ArrayList<TreeComposite>();

        // We may very well not have a valid TreeComposite in this Form
        // Make sure we do
        if (mooseTree != null) {
            for (int i = 0; i < mooseTree.getNumberOfChildren(); i++) {
                children.add(mooseTree.getChildAtIndex(i));
            }
            URI uri = file.getLocationURI();
            dumpInputFile(uri.getPath(), children);
        } else {
            throw new IllegalArgumentException("Error: MOOSEFileHandler.write() expects a Form with a "
                    + "MOOSE TreeComposite at ID = " + MOOSEModel.mooseTreeCompositeId + ". Write failed.");
        }

        return;

    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.ice.io.serializable.IWriter#replace(org.eclipse.core.
     * resources.IFile, java.lang.String, java.lang.String)
     */
    @Override
    public void replace(IFile file, String regex, String value) {
        try {
            throw new OperationNotSupportedException(
                    "MOOSEFileHandler Error: " + "IWriter.replace() is not supported.");
        } catch (OperationNotSupportedException e) {
            logger.error(getClass().getName() + " Exception!", e);
        }

        return;
    }

    /**
     * Return the Writer type String that the IOService can use as a key in its
     * IWriter mapping.
     * 
     * @return String type indicating the unique name of this IWriter.
     */
    @Override
    public String getWriterType() {
        return "moose";
    }

    /**
     * This realization of IReader.read() takes the given file reference, gets
     * its file extension, and calls the appropriate routines to either load a
     * MOOSE input file or a MOOSE YAML specification.
     * 
     * @param file
     *            Reference to the file to be read.
     */
    @Override
    public Form read(IFile file) {

        // Local declarations
        String fileExt = "";
        Form returnForm = new Form();

        // Make sure we have a valid file reference
        if (file != null && file.exists()) {

            // Local declarations
            File mooseFile = new File(file.getLocationURI());
            ArrayList<TreeComposite> blocks = null;
            TreeComposite rootNode = new TreeComposite();

            String[] splitPath = mooseFile.getAbsolutePath().split("\\.(?=[^\\.]+$)");
            if (splitPath.length > 1) {
                fileExt = splitPath[1];
            } else {
                logger.info("MOOSEFileHandler Message:" + "File did not have file extension: "
                        + mooseFile.getAbsolutePath());
                return null;
            }

            try {
                // Parse the extension to see if we are loading
                // YAML or input files.
                if (fileExt.toLowerCase().equals("yaml")) {
                    blocks = loadYAML(mooseFile.getAbsolutePath());
                } else if (fileExt.toLowerCase().equals("i")) {
                    blocks = loadFromGetPot(mooseFile.getAbsolutePath());
                }

                // If we got a valid file, then construct
                // a Root TreeComposite to return
                if (blocks != null) {
                    for (TreeComposite block : blocks) {
                        // Clone the block
                        TreeComposite blockClone = (TreeComposite) block.clone();

                        // Don't want to do this if the file is a YAML file.
                        if (!fileExt.toLowerCase().equals("yaml")) {
                            // Set the parent and sibling references correctly
                            blockClone.setActive(true);
                            blockClone.setParent(rootNode);
                        }
                        rootNode.setNextChild(blockClone);
                    }

                    // Don't want to do this if the file is a YAML file.
                    if (!fileExt.toLowerCase().equals("yaml")) {
                        // Set the active data nodes
                        setActiveDataNodes(rootNode);

                        // Set the variable entries in the tree to
                        // be discrete based on the available Variables and
                        // AuxVariables
                        setupVariables(rootNode);
                        setupAuxVariables(rootNode);
                    }

                    // Set the Identifiable data on the TreeComposite
                    rootNode.setId(MOOSEModel.mooseTreeCompositeId);
                    rootNode.setDescription("The tree of input data for this problem.");
                    rootNode.setName("Input Data");

                    // Add it to the return Form
                    returnForm.addComponent(rootNode);

                    // Return the tree
                    return returnForm;
                }

            } catch (IOException e) {
                logger.error(getClass().getName() + " Exception!", e);
                return null;
            }
        }

        return null;
    }

    /**
     * This method converts the non-AuxVariable 'variable' Entries in the tree
     * to contain only the discrete list of available Variable sub-blocks.
     * 
     * @param tree
     *            The root node of the Moose tree
     */
    public void setupVariables(TreeComposite tree) {

        // Local Declarations
        TreeComposite variables = null;
        ArrayList<String> vars = new ArrayList<String>();

        // Grab the Variables Block
        for (int i = 0; i < tree.getNumberOfChildren(); i++) {
            if ("Variables".equals(tree.getChildAtIndex(i).getName())) {
                variables = tree.getChildAtIndex(i);
                break;
            }
        }

        // If we even have a variables block...
        if (variables != null) {
            // Add the names of the variables to the vars list
            for (int i = 0; i < variables.getNumberOfChildren(); i++) {
                vars.add(variables.getChildAtIndex(i).getName());
            }

            if (vars.isEmpty()) {
                vars.add("Create a Variable");
            }

            // Walk the tree and search for non-AuxVariable 'variable' Entries
            BreadthFirstTreeCompositeIterator iter = new BreadthFirstTreeCompositeIterator(tree);
            while (iter.hasNext()) {
                TreeComposite block = iter.next();

                // Check that this node has data
                if (!block.getDataNodes().isEmpty()) {
                    DataComponent data = (DataComponent) block.getDataNodes().get(0);

                    // Only operate if this data component is valid, has a
                    // variable
                    // Entry, and is not an AuxVariable
                    if (data != null && data.contains("variable") && !block.getParent().getName().contains("Aux")) {

                        IEntry variableEntry = data.retrieveEntry("variable");
                        String currentValue = variableEntry.getValue();
                        variableEntry.setAllowedValues(vars);
                        if (vars.contains(currentValue)) {
                            variableEntry.setValue(currentValue);
                        } else {
                            variableEntry.setValue(vars.get(0));
                        }
                    }

                }
            }
        }

        return;

    }

    /**
     * This method converts the AuxVariable 'variable' Entries in the tree to
     * contain only the discrete list of available AuxVariable sub-blocks.
     * 
     * @param tree
     *            The root node of the Moose tree
     */
    public void setupAuxVariables(TreeComposite tree) {

        // Local Declarations
        TreeComposite auxVariablesBlock = null;
        ArrayList<String> auxVars = new ArrayList<String>();

        // Grab the AuxVariables Block
        for (int i = 0; i < tree.getNumberOfChildren(); i++) {
            if ("AuxVariables".equals(tree.getChildAtIndex(i).getName())) {
                auxVariablesBlock = tree.getChildAtIndex(i);
                break;
            }
        }

        if (auxVariablesBlock != null) {
            // Add the names of the variables to the vars list
            for (int i = 0; i < auxVariablesBlock.getNumberOfChildren(); i++) {
                auxVars.add(auxVariablesBlock.getChildAtIndex(i).getName());
            }

            // Walk the tree and search for non-AuxVariable 'variable' Entries
            BreadthFirstTreeCompositeIterator iter = new BreadthFirstTreeCompositeIterator(tree);
            while (iter.hasNext()) {
                TreeComposite block = iter.next();

                // Check that this node has data
                if (!block.getDataNodes().isEmpty()) {
                    DataComponent data = (DataComponent) block.getDataNodes().get(0);

                    // Only operate if this data component is valid, has a
                    // variable
                    // Entry, and is not an AuxVariable
                    if (data != null && data.contains("variable")
                            && block.getParent().getName().contains("AuxKernels")) {
                        IEntry variableEntry = data.retrieveEntry("variable");
                        String currentValue = variableEntry.getValue();
                        variableEntry.setAllowedValues(auxVars);
                        if (auxVars.contains(currentValue)) {
                            variableEntry.setValue(currentValue);
                        } else {
                            variableEntry.setValue(auxVars.get(0));
                        }
                    }

                }
            }
        }
        return;
    }

    /**
     * This realization of IReader.findAll() reads a Form in from the given file
     * reference and walks the corresponding TreeComposite for occurrences of
     * the given regular expression.
     * 
     * @param file
     *            The reference to the file we are searching in.
     * @param regex
     *            The regular expression we should search for.
     */
    @Override
    public ArrayList<IEntry> findAll(IFile file, String regex) {

        // Local declarations
        ArrayList<IEntry> retEntries = new ArrayList<IEntry>();
        Form form = read(file);

        TreeComposite tree = (TreeComposite) form.getComponent(MOOSEModel.mooseTreeCompositeId);

        // Make sure the tree is valid
        if (tree == null || tree.getNumberOfChildren() < 1) {
            return retEntries;
        }

        // Walk the tree and get all Entries that may represent a file
        BreadthFirstTreeCompositeIterator iter = new BreadthFirstTreeCompositeIterator(tree);
        while (iter.hasNext()) {
            TreeComposite child = iter.next();

            // Make sure we have a valid DataComponent
            if (child.getActiveDataNode() != null && child.isActive()) {
                DataComponent data = (DataComponent) child.getActiveDataNode();
                for (IEntry e : data.retrieveAllEntries()) {

                    // If the Entry's tag is "false" it is a commented out
                    // parameter.
                    if (!"false".equals(e.getTag()) && e.getValue() != null && !e.getValue().isEmpty()
                            && (e.getName() + " = " + e.getValue()).matches(regex)) {

                        // If this Entry does not have a very descriptive name
                        // we should reset its name to the block it belongs to
                        if ("file".equals(e.getName().toLowerCase())
                                || "data_file".equals(e.getName().toLowerCase())) {
                            e.setName(child.getName());
                        }
                        retEntries.add((IEntry) e.clone());
                    }
                }
            }

        }

        return retEntries;

    }

    /**
     * Return the Reader type String that the IOService can use as a key in its
     * IReader mapping.
     * 
     * @return String type indicating the unique name of this IReader.
     */
    @Override
    public String getReaderType() {
        return "moose";
    }

    /**
     * This method will take in a TreeComposite, traverse through all levels of
     * child, subchild, etc. TreeComposites, and set the active data nodes on
     * all that have activeDataNode=null. This method requires that all parent,
     * sibling and child references be set correctly on all TreeComposites to be
     * successful.
     * 
     * Used exclusively by {@link #reviewEntries(Form)
     * MOOSEModel.reviewEntries(...)}
     * 
     * @param tree
     *            The tree that will have all active data nodes set.
     */
    private void setActiveDataNodes(TreeComposite tree) {

        // Perform a pre-order traversal of the tree. For each TreeComposite, we
        // should set an active data node if none is already set.

        // Create an empty stack. Put in a null value so we do not hit an
        // EmptyStackException and so we can use a null check in the while loop.
        Stack<TreeComposite> treeStack = new Stack<TreeComposite>();
        treeStack.push(null);
        while (tree != null) {

            // Operate on the next TreeComposite. This sets its active data node
            // if a data node exists and is not already set.
            ArrayList<Component> dataNodes = tree.getDataNodes();
            if (tree.getActiveDataNode() == null && !dataNodes.isEmpty()) {
                tree.setActiveDataNode(dataNodes.get(0));
            }

            // Add all of the current tree's children to the stack in reverse.
            for (int i = tree.getNumberOfChildren() - 1; i >= 0; i--) {
                treeStack.push(tree.getChildAtIndex(i));
            }

            // Get the next TreeComposite in the Stack.
            tree = treeStack.pop();
        }

        return;
    }
}