package org.rascalmpl.library.util;
import java.io.IOException;
import java.io.StringWriter;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.rascalmpl.interpreter.Configuration;
import org.rascalmpl.library.lang.rascal.boot.IJava2Rascal;
import org.rascalmpl.uri.URIResolverRegistry;
import org.rascalmpl.uri.URIUtil;
import org.rascalmpl.value.IConstructor;
import org.rascalmpl.value.IList;
import org.rascalmpl.value.ISourceLocation;
import org.rascalmpl.value.IValue;
import org.rascalmpl.value.IValueFactory;
import org.rascalmpl.values.ValueFactoryFactory;
public class PathConfig {
private static IValueFactory vf = ValueFactoryFactory.getValueFactory();
List<ISourceLocation> srcs; // List of locations to search for source files
List<ISourceLocation> libs; // List of (library) locations to search for derived files
List<ISourceLocation> courses; // List of (library) locations to search for course source files
ISourceLocation bin; // Global location for derived files outside projects or libraries
ISourceLocation boot; // Location with Rascal boot files
// private RascalSearchPath rascalSearchPath;
private static ISourceLocation defaultStd;
private static List<ISourceLocation> defaultCourses;
private static ISourceLocation defaultBin;
private static ISourceLocation defaultBoot;
static {
try {
// Defaults should be in sync with util::Reflective
defaultStd = vf.sourceLocation("std", "", "");
defaultBin = vf.sourceLocation("home", "", "bin");
defaultBoot = vf.sourceLocation("boot", "", "");
defaultCourses = Arrays.asList(vf.sourceLocation("courses", "", ""));
} catch (URISyntaxException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public PathConfig() throws URISyntaxException{
srcs = Arrays.asList(defaultStd);
courses = defaultCourses;
bin = defaultBin;
boot = defaultBoot;
libs = Arrays.asList(bin, boot);
// makeRascalSearchPath();
}
public PathConfig(List<ISourceLocation> srcs, List<ISourceLocation> libs, ISourceLocation bin) {
this(srcs, libs, bin, defaultBoot, defaultCourses);
}
public PathConfig(List<ISourceLocation> srcs, List<ISourceLocation> libs, ISourceLocation bin, List<ISourceLocation> courses) {
this(srcs, libs, bin, defaultBoot, courses);
}
public PathConfig(List<ISourceLocation> srcs, List<ISourceLocation> libs, ISourceLocation bin, ISourceLocation boot) {
this(srcs, libs, bin, boot, defaultCourses);
}
public PathConfig(List<ISourceLocation> srcs, List<ISourceLocation> libs, ISourceLocation bin, ISourceLocation boot, List<ISourceLocation> courses){
this.srcs = srcs;
this.courses = courses;
this.libs = libs;
this.bin = bin;
this.boot = boot;
// makeRascalSearchPath();
}
public PathConfig(IList srcs, IList libs, ISourceLocation bin, ISourceLocation boot){
this.srcs = convertLocs(srcs);
this.libs = convertLocs(libs);
this.bin = bin;
this.boot = boot;
this.courses = defaultCourses;
// makeRascalSearchPath();
}
public PathConfig(IList srcs, IList libs, ISourceLocation bin, IList courses){
this.srcs = convertLocs(srcs);
this.libs = convertLocs(libs);
this.bin = bin;
this.boot = defaultBoot;
this.courses = convertLocs(courses);
// makeRascalSearchPath();
}
public PathConfig(IList srcs, IList libs, ISourceLocation bin, ISourceLocation boot, IList courses){
this.srcs = convertLocs(srcs);
this.libs = convertLocs(libs);
this.bin = bin;
this.boot = boot;
this.courses = convertLocs(courses);
// makeRascalSearchPath();
}
List<ISourceLocation> convertLocs(IList locs){
List<ISourceLocation> result = new ArrayList<>();
for(IValue p : locs){
if(p instanceof ISourceLocation){
result.add((ISourceLocation) p);
} else {
throw new RuntimeException("Path should contain source locations and not " + p.getClass().getName());
}
}
return result;
}
String makeFileName(String qualifiedModuleName) {
return makeFileName(qualifiedModuleName, "rsc");
}
public static ISourceLocation getDefaultStd(){
return defaultStd;
}
public static ISourceLocation getDefaultBin(){
return defaultBin;
}
public static ISourceLocation getDefaultBoot(){
return defaultBoot;
}
public static List<ISourceLocation> getDefaultCourses(){
return defaultCourses;
}
public IValueFactory getValueFactory() {
return vf;
}
public IList getSrcs() {
return vf.list(srcs.toArray(new IValue[0]));
}
public PathConfig addSourceLoc(ISourceLocation dir) {
List<ISourceLocation> extendedsrcs = new ArrayList<ISourceLocation>(srcs);
extendedsrcs.add(dir);
return new PathConfig(extendedsrcs, libs, bin, boot);
}
public IList getCourses() {
return vf.list(courses.toArray(new IValue[0]));
}
public PathConfig addCourseLoc(ISourceLocation dir) {
List<ISourceLocation> extendedcourses = new ArrayList<ISourceLocation>(courses);
extendedcourses.add(dir);
return new PathConfig(srcs, libs, bin, boot, extendedcourses);
}
public ISourceLocation getCourseLoc(String courseName) throws URISyntaxException, IOException{
for(ISourceLocation dir : courses){
ISourceLocation fileLoc = vf.sourceLocation(dir.getScheme(), dir.getAuthority(), dir.getPath() + courseName);
if(URIResolverRegistry.getInstance().exists(fileLoc)){
return fileLoc;
}
}
throw new IOException("Course " + courseName + " not found");
}
private boolean isCourse(String name){
return name.matches("[A-Z][A-Za-z0-9]*$");
}
private String fileName(ISourceLocation loc){
String[] parts = loc.getPath().split("/");
return parts[parts.length - 1];
}
public List<String> listCourseEntries() throws IOException{
URIResolverRegistry reg = URIResolverRegistry.getInstance();
ArrayList<String> courseList = new ArrayList<>();
for(ISourceLocation dir : courses){
if(reg.exists(dir)){
for(ISourceLocation entry : reg.list(dir)){
if(reg.isDirectory(entry)){
String name = fileName(entry);
if(isCourse(name)){
courseList.add(name);
}
}
}
}
}
return courseList;
}
public IList getLibs() {
return vf.list(libs.toArray(new IValue[0]));
}
public PathConfig addLibLoc(ISourceLocation dir){
List<ISourceLocation> extendedlibs = new ArrayList<ISourceLocation>(libs);
extendedlibs.add(dir);
return new PathConfig(srcs, extendedlibs, bin, boot);
}
public ISourceLocation getBoot() {
return boot;
}
public ISourceLocation getBin() {
return bin;
}
// void makeRascalSearchPath(){
// this.rascalSearchPath = new RascalSearchPath();
// rascalSearchPath.addPathContributor(getSourcePathContributor());
// }
// public IRascalSearchPathContributor getSourcePathContributor() {
// return new PathContributor("srcs", srcs);
// }
String makeFileName(String qualifiedModuleName, String extension) {
return qualifiedModuleName.replaceAll("::", "/") + "." + extension;
}
// public RascalSearchPath getRascalSearchPath(){
// return rascalSearchPath;
// }
ISourceLocation getModuleLoc(String qualifiedModuleName) throws IOException {
ISourceLocation result = resolveModule(qualifiedModuleName);
if(result == null){
throw new IOException("Module " + qualifiedModuleName + " not found");
}
return result;
}
public ISourceLocation resolveModule(String qualifiedModuleName) {
String fileName = makeFileName(qualifiedModuleName);
for(ISourceLocation dir : srcs){
ISourceLocation fileLoc;
try {
getFullURI(fileName, dir);
fileLoc = getFullURI(fileName, dir);
if(URIResolverRegistry.getInstance().exists(fileLoc)){
return fileLoc;
}
}
catch (URISyntaxException e) {
return null;
}
}
return null;
}
String getModuleName(ISourceLocation moduleLoc) throws IOException{
String modulePath = moduleLoc.getPath();
if(!modulePath.endsWith(".rsc")){
throw new IOException("Not a Rascal source file: " + moduleLoc);
}
for(ISourceLocation dir : srcs){
if(modulePath.startsWith(dir.getPath()) && moduleLoc.getScheme() == dir.getScheme()){
String moduleName = modulePath.replaceFirst(dir.getPath(), "").replace(".rsc", "");
if(moduleName.startsWith("/")){
moduleName = moduleName.substring(1, moduleName.length());
}
return moduleName.replace("/", "::");
}
}
throw new IOException("No module name found for " + moduleLoc);
}
private String moduleToDir(String module) {
return module.replaceAll(Configuration.RASCAL_MODULE_SEP, Configuration.RASCAL_PATH_SEP);
}
private String moduleToFile(String module) {
if (!module.endsWith(Configuration.RASCAL_FILE_EXT)) {
module = module.concat(Configuration.RASCAL_FILE_EXT);
}
return module.replaceAll(Configuration.RASCAL_MODULE_SEP, Configuration.RASCAL_PATH_SEP);
}
private ISourceLocation getFullURI(String path, ISourceLocation dir) throws URISyntaxException {
return URIUtil.getChildLocation(dir, path);
}
public List<String> listModuleEntries(String moduleRoot) {
assert !moduleRoot.endsWith("::");
final URIResolverRegistry reg = URIResolverRegistry.getInstance();
try {
String modulePath = moduleToDir(moduleRoot);
List<String> result = new ArrayList<>();
for (ISourceLocation dir : srcs) {
ISourceLocation full = getFullURI(modulePath, dir);
if (reg.exists(full)) {
try {
String[] entries = reg.listEntries(full);
if (entries == null) {
continue;
}
for (String module: entries ) {
if (module.endsWith(Configuration.RASCAL_FILE_EXT)) {
result.add(module.substring(0, module.length() - Configuration.RASCAL_FILE_EXT.length()));
}
else if (module.indexOf('.') == -1 && reg.isDirectory(getFullURI(module, full))) {
// a sub folder path
result.add(module + "::");
}
}
}
catch (IOException e) {
}
}
}
if (result.size() > 0) {
return result;
}
return null;
} catch (URISyntaxException e) {
return null;
}
}
public IConstructor asConstructor(IJava2Rascal j2r){
return j2r.pathConfig(j2r.kw_pathConfig().srcs(getSrcs()).libs(getLibs()).boot(getBoot()).bin(getBin()).courses(getCourses()));
}
public String toString(){
StringWriter w = new StringWriter();
w.append("srcs: ").append(getSrcs().toString()).append("\n")
.append("libs: ").append(getLibs().toString()).append("\n")
.append("courses: ").append(getCourses().toString()).append("\n")
.append("boot: ").append(getBoot().toString()).append("\n")
.append("bin: ").append(getBin().toString()).append("\n");
return w.toString();
}
}
//class PathContributor implements IRascalSearchPathContributor{
//
// private final String name;
// private final List<ISourceLocation> stdPath;
//
// PathContributor(String name, List<ISourceLocation> path){
// this.name = name;
// this.stdPath = path;
// }
// @Override
// public void contributePaths(List<ISourceLocation> path) {
// for(ISourceLocation p : stdPath){
// path.add(p);
// }
// }
//
// @Override
// public String getName() {
// return name;
// }
//
//}