blob: 9e9c9eba09e11bdc821e149f8f5c77e06d15be11 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2012, 2020 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);
}
}