/* * Copyright (c) 2009 The Jackson Laboratory * * This software was developed by Gary Churchill's Lab at The Jackson * Laboratory (see http://research.jax.org/faculty/churchill). * * This is free software: you can 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. * * This software 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. * * You should have received a copy of the GNU General Public License * along with this software. If not, see <http://www.gnu.org/licenses/>. */ package org.jax.r.rintegration; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * This class finds potential {@link RInstallation}s * @author <A HREF="mailto:keith.sheppard@jax.org">Keith Sheppard</A> */ public class RInstallationScanner { /** * our logger */ private static final Logger LOG = Logger.getLogger(RInstallationScanner.class.getName()); /** * the regular expression i'm using to dig the version out from the dir name. * this pattern accepts digits anywhere, and accepts '.', '-', and '_' on * the internal part of the version string, but not on the border. */ private static final Pattern R_DIR_NAME_TO_VERSION_MATCHER = Pattern.compile("([0-9]+[0-9_\\-\\.]*[0-9]+|[0-9])"); /** * Converts an R_HOME directory into an {@link RInstallation}. * @param rInstallDirStructure * holds info about the directory structure of R installs * @param rHomeDirectory * the home directory (R_HOME) * @return * the {@link RInstallation} for the directory or null * if our validity checks fail for the R_HOME * that's passed in */ public RInstallation rHomeDirectoryToRInstallation( PlatformSpecificRFunctions rInstallDirStructure, File rHomeDirectory) { // start off as null... RInstallation rInstallation = null; if(rHomeDirectory.isDirectory()) { // ok, see if we can see the R lib dir where we expect it to be File rLibraryDirectory = rInstallDirStructure.rHomeToExpectedRLibrary(rHomeDirectory); if(rLibraryDirectory.isDirectory()) { try { // the expected R library exists... this looks like a valid R home // 1st pull out the version string Matcher versionMatcher = R_DIR_NAME_TO_VERSION_MATCHER.matcher( rHomeDirectory.getCanonicalPath()); String versionString = null; // only hold on to the last match (assuming the last match is // most likely to be a version string) while(versionMatcher.find()) { versionString = versionMatcher.group(); } if(versionString == null) { if(LOG.isLoggable(Level.FINE)) { LOG.fine( "could not detect what version of R lives in \"" + rHomeDirectory.getCanonicalPath() + "\""); } } // we have everything we need to make a home! :-) // it's ok if versionString is null rInstallation = new RInstallation( rHomeDirectory, rLibraryDirectory, versionString); } catch(IOException ex) { LOG.log(Level.SEVERE, "caught exception while trying to convert" + " an R_HOME directory into an RInstallation object", ex); } } } else if(LOG.isLoggable(Level.FINE)) { // we already know it's not a directory... see if it even exists if(rHomeDirectory.exists()) { LOG.fine( "expected R home \"" + rHomeDirectory.getPath() + "\" exists but is not a directory"); } else { LOG.fine( "expected R home \"" + rHomeDirectory.getPath() + "\" does not exist"); } } return rInstallation; } /** * Scan for {@link RInstallation}s using the install structure passed in * @param rInstallDirStructure * the install directory structure to use for this scan * @return * the potential {@link RInstallation}s that we find */ public RInstallation[] scanForRInstallations(PlatformSpecificRFunctions rInstallDirStructure) { return this.scanForRInstallations( rInstallDirStructure, null); } /** * Scan for {@link RInstallation}s using the install structure passed in * @param rInstallDirStructure * the install directory structure to use for this scan * @param minimumVersionString * the minimum version string of the {@link RInstallation}s that we can * return. this uses {@link VersionStringComparator} to determine * version ordering. if this string is null then we pass * every {@link RInstallation} through wether or not we can even detect * a version (ie. the version can be null) * @return * a sorted list of potential {@link RInstallation}s that we found */ public RInstallation[] scanForRInstallations( PlatformSpecificRFunctions rInstallDirStructure, String minimumVersionString) { List<RInstallation> discoveredRInstallations = new ArrayList<RInstallation>(); for(File defaultInstallRootDir : rInstallDirStructure.getExpectedInstallRoots()) { if(!defaultInstallRootDir.exists()) { LOG.info( "default R install dir \"" + defaultInstallRootDir + "\" doesn't exist"); } else if(!defaultInstallRootDir.isDirectory()) { LOG.info( "default R install dir \"" + defaultInstallRootDir + "\" exists but is not a directory"); } else { // we're ready to dive into the install root File[] versionRootsArray = defaultInstallRootDir.listFiles(); if(versionRootsArray == null || versionRootsArray.length == 0) { LOG.info( "didn't find any R versions in the default R install directory \"" + defaultInstallRootDir + "\""); } else { Map<File, File> canonicalToAbsoluteVersionRootMap = new HashMap<File, File>(); for(File versionRoot: versionRootsArray) { try { File canonicalPath = versionRoot.getCanonicalFile(); File absolutePath = versionRoot.getAbsoluteFile(); if(!canonicalToAbsoluteVersionRootMap.containsKey(canonicalPath) || absolutePath.equals(canonicalPath)) { canonicalToAbsoluteVersionRootMap.put( canonicalPath, absolutePath); } } catch(IOException ex) { // Fall back on the absolute path File absolutePath = versionRoot.getAbsoluteFile(); canonicalToAbsoluteVersionRootMap.put( absolutePath, absolutePath); } } List<File> versionRoots = new ArrayList<File>( canonicalToAbsoluteVersionRootMap.values()); Collections.sort(versionRoots); for(File currVersionRoot: versionRoots) { // check for the R_HOME File currRHomeDirectory = rInstallDirStructure.versionRootToExpectedRHome( currVersionRoot); RInstallation currRInstallation = this.rHomeDirectoryToRInstallation( rInstallDirStructure, currRHomeDirectory); if(currRInstallation != null) { if(minimumVersionString == null) { // if the minimum version string is null, then we // don't need to go through any more checks. just // pass the R Home through discoveredRInstallations.add(currRInstallation); } else if(currRInstallation.getRVersion() != null && VersionStringComparator.getInstance().compare( currRInstallation.getRVersion(), minimumVersionString) >= 0) { // the versioning test passed, we can put this // R Home through discoveredRInstallations.add(currRInstallation); } } } } } } // return any R_HOME's that we've found RInstallation[] returnVal = discoveredRInstallations.toArray( new RInstallation[discoveredRInstallations.size()]); Arrays.sort(returnVal); return returnVal; } }