| /*=============================================================================# |
| # Copyright (c) 2012, 2018 Stephan Wahlbrink and others. |
| # |
| # This program and the accompanying materials are made available under the |
| # terms of the Eclipse Public License 2.0 which is available at |
| # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 |
| # which is available at https://www.apache.org/licenses/LICENSE-2.0. |
| # |
| # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 |
| # |
| # Contributors: |
| # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation |
| #=============================================================================*/ |
| |
| package org.eclipse.statet.rj.renv.runtime; |
| |
| import static org.eclipse.statet.internal.rj.renv.core.REnvCoreInternals.BUNDLE_ID; |
| import static org.eclipse.statet.rj.renv.core.REnvConfiguration.R_HOME_DIRECTORY_VAR_STRING; |
| |
| import java.nio.file.Path; |
| import java.nio.file.Paths; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.eclipse.statet.jcommons.collections.ImCollections; |
| import org.eclipse.statet.jcommons.lang.NonNull; |
| import org.eclipse.statet.jcommons.lang.NonNullByDefault; |
| import org.eclipse.statet.jcommons.lang.Nullable; |
| import org.eclipse.statet.jcommons.status.ErrorStatus; |
| import org.eclipse.statet.jcommons.status.ProgressMonitor; |
| import org.eclipse.statet.jcommons.status.StatusException; |
| |
| import org.eclipse.statet.internal.rj.renv.core.REnvCoreInternals; |
| import org.eclipse.statet.rj.data.RDataUtils; |
| import org.eclipse.statet.rj.data.RIntegerStore; |
| import org.eclipse.statet.rj.data.RNumericStore; |
| import org.eclipse.statet.rj.data.RVector; |
| import org.eclipse.statet.rj.data.UnexpectedRDataException; |
| import org.eclipse.statet.rj.renv.core.BasicRLibGroup; |
| import org.eclipse.statet.rj.renv.core.BasicRLibLocation; |
| import org.eclipse.statet.rj.renv.core.BasicRLibPaths; |
| import org.eclipse.statet.rj.renv.core.REnvConfiguration; |
| import org.eclipse.statet.rj.renv.core.RLibGroup; |
| import org.eclipse.statet.rj.renv.core.RLibLocation; |
| import org.eclipse.statet.rj.renv.core.RLibPaths; |
| import org.eclipse.statet.rj.services.FunctionCall; |
| import org.eclipse.statet.rj.services.RService; |
| |
| |
| @NonNullByDefault |
| public class RuntimeRLibPathsLoader { |
| |
| |
| public static final int WRITABLE_CHECK= 1 << 0; |
| public static final int FIRST_USER_LIB_CHECK= 1 << 1; |
| |
| public static final int FULL_CHECK= WRITABLE_CHECK | FIRST_USER_LIB_CHECK; |
| |
| |
| private static long toJavaStamp(final double rStamp) { |
| return (long) (rStamp * 1000); |
| } |
| |
| private static boolean contains(final List<RLibLocationInfo> locationInfos, final RLibLocation location) { |
| for (final RLibLocationInfo locationInfo : locationInfos) { |
| if (locationInfo.getLibLocation() == location) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| |
| private final RLibPaths rLibPaths; |
| |
| private final boolean isLocal; |
| private final @Nullable String rHomeDirectory; |
| |
| |
| public RuntimeRLibPathsLoader(final REnvConfiguration rEnvConfig) { |
| this.isLocal= (rEnvConfig.isLocal() && rEnvConfig.getRHomeDirectoryPath() != null); |
| this.rHomeDirectory= null; |
| this.rLibPaths= rEnvConfig; |
| } |
| |
| public RuntimeRLibPathsLoader(final String rHomeDirectory, final List<RLibGroup> libGroups) { |
| this.isLocal= false; |
| this.rHomeDirectory= rHomeDirectory; |
| |
| final List<RLibLocation> locations= new ArrayList<>(); |
| final RLibGroup[] encodedLibGroups= new @NonNull RLibGroup[libGroups.size()]; |
| for (int i= 0; i < encodedLibGroups.length; i++) { |
| final RLibGroup libGroup= libGroups.get(i); |
| final List<? extends RLibLocation> libLocations= libGroup.getLibLocations(); |
| locations.clear(); |
| for (final RLibLocation libLocation : libLocations) { |
| final String encodedPath= encodePath(libLocation.getDirectory()); |
| locations.add((encodedPath == libLocation.getDirectory()) ? libLocation : |
| new BasicRLibLocation(libLocation.getSource(), encodedPath, null) ); |
| } |
| encodedLibGroups[i]= new BasicRLibGroup(libGroup.getId(), libGroup.getLabel(), |
| ImCollections.toList(locations) ); |
| } |
| |
| this.rLibPaths= new BasicRLibPaths(ImCollections.newList(encodedLibGroups)); |
| } |
| |
| |
| protected String encodePath(final String pathString) { |
| final String rHomeDirectory= this.rHomeDirectory; |
| if (rHomeDirectory != null && pathString.startsWith(rHomeDirectory) |
| && (pathString.length() == rHomeDirectory.length() |
| || pathString.charAt(rHomeDirectory.length()) == '/')) { |
| return R_HOME_DIRECTORY_VAR_STRING + pathString.substring(rHomeDirectory.length()); |
| } |
| return pathString; |
| } |
| |
| protected @Nullable RLibLocation getLibLocationByRPath(final String pathString) { |
| if (this.isLocal) { |
| try { |
| final Path path= Paths.get(pathString); |
| return this.rLibPaths.getRLibLocationByDirectoryPath(path); |
| } |
| catch (final Exception e) { |
| REnvCoreInternals.log(new ErrorStatus(BUNDLE_ID, |
| "An error occurred when detecting R library location.", |
| e )); |
| } |
| } |
| else { |
| final String encodedPath= encodePath(pathString); |
| return this.rLibPaths.getRLibLocationByDirectory(encodedPath); |
| } |
| return null; |
| } |
| |
| protected @Nullable RLibLocation getFirstUserLibLocation(final RLibPaths rLibPaths) { |
| final RLibGroup libGroup= rLibPaths.getRLibGroup(RLibGroup.R_USER); |
| if (libGroup != null) { |
| final List<? extends RLibLocation> libraries= libGroup.getLibLocations(); |
| return (!libraries.isEmpty()) ? libraries.get(0) : null; |
| } |
| return null; |
| } |
| |
| |
| public RVector<RNumericStore> loadLibStamps( |
| final RService r, final ProgressMonitor m) throws StatusException { |
| try { |
| return RDataUtils.checkRNumVector(r.evalData( |
| "rj:::renv.checkLibs()", m )); |
| } |
| catch (final StatusException | UnexpectedRDataException e) { |
| throw new StatusException(new ErrorStatus(BUNDLE_ID, |
| "An error occurred when checking R library locations.", |
| e )); |
| } |
| } |
| |
| public RuntimeRLibPaths load( |
| final RVector<RNumericStore> rLibsStamps, final int options, |
| final RService r, final ProgressMonitor m) throws StatusException { |
| final int l= (int) rLibsStamps.getLength(); |
| final List<RLibLocationInfo> libLocationInfos= new ArrayList<>(l + 1); |
| |
| if ((options & WRITABLE_CHECK) != 0) { |
| try { |
| final RVector<RIntegerStore> rLibsAccess= RDataUtils.checkRIntVector( |
| r.evalData("file.access(.libPaths(), 3L)", m) ); //$NON-NLS-1$ |
| for (int i= 0; i < l; i++) { |
| final String rPath= rLibsStamps.getNames().getChar(i); |
| final RLibLocation location= getLibLocationByRPath(rPath); |
| if (location != null) { |
| libLocationInfos.add(new RLibLocationInfo(location, rPath, |
| true, (location.getSource() != RLibLocation.EPLUGIN |
| && rLibsAccess.getData().getInt(i) == 0 ), |
| toJavaStamp(rLibsStamps.getData().getNum(i)), |
| i )); |
| } |
| } |
| } |
| catch (final StatusException | UnexpectedRDataException e) { |
| throw new StatusException(new ErrorStatus(BUNDLE_ID, |
| "An error occurred when checking R library locations for access.", |
| e )); |
| } |
| } |
| else { |
| for (int i= 0; i < l; i++) { |
| final String rPath= rLibsStamps.getNames().getChar(i); |
| final RLibLocation location= getLibLocationByRPath(rPath); |
| if (location != null) { |
| libLocationInfos.add(new RLibLocationInfo(location, rPath, |
| true, false, |
| toJavaStamp(rLibsStamps.getData().getNum(i)), |
| i )); |
| } |
| } |
| } |
| |
| if ((options & FIRST_USER_LIB_CHECK) != 0) { |
| try { |
| final RLibLocation location= getFirstUserLibLocation(this.rLibPaths); |
| final Path directoryPath; |
| if (location != null && !contains(libLocationInfos, location) |
| && (directoryPath= location.getDirectoryPath()) != null |
| && directoryPath.toUri().getScheme().equals("file") ) { //$NON-NLS-1$ |
| final FunctionCall call= r.createFunctionCall("rj:::renv.isValidLibLocation"); //$NON-NLS-1$ |
| call.addChar(directoryPath.toString()); |
| final RVector<RIntegerStore> data= RDataUtils.checkRIntVector(call.evalData(m)); |
| final int state= RDataUtils.checkSingleIntValue(data); |
| if (state == 0) { |
| libLocationInfos.add(new RLibLocationInfo(location, |
| data.getNames().getChar(0).replace('\\', '/'), |
| false, true, |
| 0, -1 )); |
| } |
| } |
| } |
| catch (final StatusException | UnexpectedRDataException e) { |
| REnvCoreInternals.log(new ErrorStatus(BUNDLE_ID, |
| "An error occurred when checking missing R user library location.", |
| e )); |
| } |
| } |
| |
| return new RuntimeRLibPaths(this.rLibPaths.getRLibGroups(), libLocationInfos); |
| } |
| |
| public RuntimeRLibPaths load(final int options, |
| final RService r, final ProgressMonitor m) throws StatusException { |
| return load(loadLibStamps(r, m), options, r, m); |
| } |
| |
| |
| } |