edu.byu.ece.rapidSmith.RSEnvironment.java Source code

Java tutorial

Introduction

Here is the source code for edu.byu.ece.rapidSmith.RSEnvironment.java

Source

/*
 * Copyright (c) 2016 Brigham Young University
 *
 * This file is part of the BYU RapidSmith Tools.
 *
 * BYU RapidSmith Tools is free software: you may redistribute it
 * and/or modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation, either version 3 of
 * the License, or (at your option) any later version.
 *
 * BYU RapidSmith Tools is distributed in the hope that it will be
 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * A copy of the GNU General Public License is included with the BYU
 * RapidSmith Tools. It can be found at doc/LICENSE.GPL3.TXT. You may
 * also get a copy of the license at <http://www.gnu.org/licenses/>.
 */

package edu.byu.ece.rapidSmith;

import edu.byu.ece.rapidSmith.device.Device;
import edu.byu.ece.rapidSmith.device.FamilyType;
import edu.byu.ece.rapidSmith.util.FileTools;
import edu.byu.ece.rapidSmith.util.PartNameTools;
import edu.byu.ece.rapidSmith.util.Exceptions;
import edu.byu.ece.rapidSmith.util.Exceptions.EnvironmentException;
import org.jdom2.Document;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;

import java.io.IOException;
import java.lang.ref.SoftReference;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;

/**
 * Class for accessing the RapidSmith environment.  The environment exists in a
 * directory in the users file system (usually specified by the "RAPIDSMITH_PATH"
 * environment variable).  A default environment is accessible with the
 * {@link #defaultEnv()} method.
 */
public class RSEnvironment {
    /** Environment Variable Name which points to the rapidSmith project on disk */
    public static final String RSPATH_ENV_VARIABLE = "RAPIDSMITH_PATH";
    /** Suffix of the device part files */
    public static final String DEVICE_FILE_SUFFIX = "_db.dat";
    /** Name of extended family information */
    public static final String FAMILY_INFO_FILENAME = "familyInfo.xml";
    /** The default environment */
    private static RSEnvironment defaultEnv;

    private final Path rsPath;
    private final Map<String, SoftReference<Device>> loadedDevices = new HashMap<>();
    private final Map<String, FamilyType> supportedParts = new HashMap<>();

    /**
     * Returns the default RapidSmith environment.  Unless overwritten with
     * {@link #setDefaultEnv(RSEnvironment)}, this environment will default to
     * the environment returned by calling {@link #RSEnvironment()}.
     * @return the default RapidSmith environment
     * @throws EnvironmentException if the environment is not set and the default
     *   environment cannot be obtained
     */
    public static RSEnvironment defaultEnv() {
        if (defaultEnv == null)
            defaultEnv = new RSEnvironment();
        return defaultEnv;
    }

    /**
     * Allows the user to change the default RapidSmith environment at runtime.
     * @param env the RapidSmith environment to set as the default
     */
    public static void setDefaultEnv(RSEnvironment env) {
        defaultEnv = env;
    }

    /**
     * Returns a new RapidSmith environment based on the system "RAPIDSMITH_PATH"
     * variable.  This method first looks for the variable in the JVM properties and if not
     * found, searches the environment variables.  If the variable is not found in either
     * location, this method throws an {@link EnvironmentException}.
     *
     * @throws EnvironmentException if the path to the RapidSmith environment cannot be
     *   found
     */
    public RSEnvironment() {
        if (System.getProperty(RSPATH_ENV_VARIABLE) != null) {
            String property = System.getProperty(RSPATH_ENV_VARIABLE);
            rsPath = Paths.get(property);
        } else if (System.getenv(RSPATH_ENV_VARIABLE) != null) {
            String property = System.getenv(RSPATH_ENV_VARIABLE);
            rsPath = Paths.get(property);
        } else {
            throw new EnvironmentException(RSPATH_ENV_VARIABLE + " environment variable is not set.");
        }
        loadSupportedParts();
    }

    /**
     * Returns a new RapidSmith environment configured to the specified path.
     * @param rapidsmithPath path of the environment's directory
     */
    public RSEnvironment(Path rapidsmithPath) {
        Objects.requireNonNull(rapidsmithPath, "rapidsmithPath");
        rsPath = rapidsmithPath;
        loadSupportedParts();
    }

    private void loadSupportedParts() {
        for (FamilyType family : getAvailableFamilies()) {
            for (String part : getAvailableParts(family)) {
                supportedParts.put(part, family);
            }
        }
    }

    /**
     * @return the root of this environment
     */
    public Path getEnvironmentPath() {
        return rsPath;
    }

    /**
     * @return the path to the devices in this environment
     */
    public Path getDevicePath() {
        return rsPath.resolve("devices");
    }

    /**
     * @return the path to the java source files
     */
    public Path getJavaPath() {
        return rsPath.resolve("src").resolve("main").resolve("java");
    }

    /**
     * Returns the loaded device with the specified part name.  Once loaded, devices are
     * cached for quick access.
     *
     * @param partName the name of the part to load
     * @return the loaded device
     */
    public Device getDevice(String partName) {
        return getDevice(partName, false);
    }

    /**
     * Returns the loaded device with the specified part name.  Once loaded, devices are
     * cached for quick access.
     *
     * @param partName name of the part to load
     * @param forceReload if true, forces the part to be reloaded from disk
     * @return the loaded device
     */
    public Device getDevice(String partName, boolean forceReload) {
        String canonicalName = PartNameTools.removeSpeedGrade(partName);

        Device device;
        if (!forceReload && loadedDevices.containsKey(canonicalName)) {
            device = loadedDevices.get(canonicalName).get();
            if (device != null)
                return device;
        }

        Path path = getDeviceFilePath(canonicalName);

        // throw an exception if the device cannot be found
        if (path == null) {
            throw new Exceptions.EnvironmentException("Cannot find device file for part: \"" + partName + "\".\n"
                    + "If the device files for the part exist, make sure your RapidSmithPath variable is properly set. \n"
                    + "If the device files don't exist, view the RapidSmith2 Tech Report for instructions on how to generate a new device file for this part.");
        }

        device = FileTools.loadDevice(path);
        if (device == null)
            return null;

        loadedDevices.put(canonicalName, new SoftReference<>(device));
        return device;
    }

    /**
     * Loads the family info file for the specified family.  The family info file contains
     * additional information not found in the XDLRC for creating device files.
     *
     * @param family family to get the family info file for
     * @return the family info xml document
     */
    public Document loadFamilyInfo(FamilyType family) throws JDOMException, IOException {
        Path path = getPartFolderPath(family).resolve(FAMILY_INFO_FILENAME);
        SAXBuilder builder = new SAXBuilder();
        return builder.build(path.toFile());
    }

    /**
     * Loads the device info file for the specified partname and family. The device info file
     * contains additional information about a specific device not found in the XDLRC file
     * or family info. For example, a list of clock pads for a device are included in the device
     * info file
     *  
     * @param family Family of the part
     * @param partname Name of the part
     * @return A Document object representing the XML device info file. If device info does not exist
     *    for the specified part and family, {@code NULL} is returned.
     */
    public Document loadDeviceInfo(FamilyType family, String partname) {
        String partnameNoDashes = PartNameTools.removeSpeedGrade(partname);
        Path path = getPartFolderPath(family).resolve("deviceInfo_" + partnameNoDashes + ".xml");
        SAXBuilder builder = new SAXBuilder();
        Document doc = null;
        try {
            doc = builder.build(path.toFile());
            return doc;
        } catch (JDOMException | IOException e) {
            return null;
        }
    }

    /**
     * Returns the path of the folder where the device file resides for {@code partName}.
     *
     * @param partName name of the part to get its corresponding folder path.
     * @return the path of the folder where the parts files resides
     */
    public Path getPartFolderPath(String partName) {
        return getPartFolderPath(getFamilyTypeFromPart(partName));
    }

    /**
     * Returns the path of the folder where the family type resides. This
     * function assumes that familyType is not {@code NULL}
     * 
     * @param familyType the family type corresponding folder path
     * @return the path of the folder where the parts of familyType reside
     */
    public Path getPartFolderPath(FamilyType familyType) {
        Objects.requireNonNull(familyType);
        return getDevicePath().resolve(familyType.name().toLowerCase());
    }

    /**
     * Returns a list of all parts for which a device file exists for in this environment.
     *
     * @return a list of available devices in this environment
     */
    public List<String> getAvailableParts() {
        ArrayList<String> allParts = new ArrayList<>();
        for (FamilyType family : getAvailableFamilies()) {
            allParts.addAll(getAvailableParts(family));
        }
        return allParts;
    }

    /**
     * Returns a list of all parts of the family {@code type} for which a device file
     * exists for in this environment.
     *
     * @return a list of available devices in this environment
     */
    public List<String> getAvailableParts(FamilyType type) {
        ArrayList<String> allParts = new ArrayList<>();
        String pattern = "_db.dat";
        Path devFamilyPath = getPartFolderPath(type);
        if (!Files.isDirectory(devFamilyPath))
            return allParts;
        try {
            for (Path partPath : Files.newDirectoryStream(devFamilyPath)) {
                String fileName = partPath.getFileName().toString();
                if (fileName.endsWith(pattern)) {
                    allParts.add(fileName.replace(pattern, ""));
                }
            }
        } catch (IOException ignored) {
        }
        return allParts;
    }

    /**
     * Returns a list of family types supported in this environment.
     *
     * @return list of all family types installed in this environment
     */
    public List<FamilyType> getAvailableFamilies() {
        List<FamilyType> allFamilies = new ArrayList<>();
        Path devicePath = getDevicePath();
        if (!Files.exists(devicePath) || !Files.isDirectory(devicePath))
            return allFamilies;
        try {
            for (Path subPath : Files.newDirectoryStream(devicePath)) {
                if (Files.isDirectory(subPath)) {
                    try {
                        String familyName = subPath.getFileName().toString();
                        FamilyType type = FamilyType.valueOf(familyName.toUpperCase());
                        Path familyInfoFilePath = getPartFolderPath(type).resolve(FAMILY_INFO_FILENAME);
                        if (Files.exists(familyInfoFilePath))
                            allFamilies.add(type);
                    } catch (IllegalArgumentException ignored) {
                    }
                }
            }
        } catch (IOException ignored) {
        }
        return allFamilies;
    }

    /**
     * Returns the path to the corresponding device file for the part {@code partName} in
     * this environment.
     *
     * @param partName name of the part to get corresponding device file for
     * @return the full path to the device file for the specified part
     */
    public Path getDeviceFilePath(String partName) {
        FamilyType family = getFamilyTypeFromPart(partName);
        return family == null ? null : getDeviceFilePath(family, partName);
    }

    /**
     * Internal function that returns the path to the specified device file.
     * This function assumes that family is not {@code NULL}. If the given
     * device file is not found, then null is returned.
     * 
     * @param family {@link FamilyType} of the part
     * @param partName Name of the part 
     */
    private Path getDeviceFilePath(FamilyType family, String partName) {
        Objects.requireNonNull(family);
        Path partFolderPath = getPartFolderPath(family);

        if (partFolderPath == null) {
            return null;
        }

        try {
            Path path = partFolderPath.resolve(PartNameTools.removeSpeedGrade(partName) + DEVICE_FILE_SUFFIX);
            return path;
        } catch (InvalidPathException e) {
            return null;
        }
    }

    /**
     * Writes the given device to a compressed, serialized device file in this
     * environment.
     *
     * @param device the device to write
     * @throws IOException if an exception occurs writing the device file
     */
    public void writeDeviceFile(Device device) throws IOException {
        Path path = getDeviceFilePath(device.getFamily(), device.getPartName());
        FileTools.writeCompressedDeviceFile(device, path);
    }

    public FamilyType getFamilyTypeFromPart(String partName) {
        String canonicalName = PartNameTools.removeSpeedGrade(partName);
        return supportedParts.get(canonicalName);
    }
}