001/*
002 * Copyright (c) 2012-2017 Institut National des Sciences Appliquées de Lyon (INSA-Lyon)
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the Eclipse Public License v1.0
006 * which accompanies this distribution, and is available at
007 * http://www.eclipse.org/legal/epl-v10.html
008 */
009
010package org.eclipse.golo.cli.command;
011
012import com.beust.jcommander.Parameter;
013import com.beust.jcommander.Parameters;
014import com.beust.jcommander.converters.IParameterSplitter;
015
016import java.io.File;
017import java.net.MalformedURLException;
018import java.net.URL;
019import java.net.URLClassLoader;
020import java.util.List;
021import java.util.LinkedList;
022import java.util.Collections;
023import java.util.Arrays;
024
025import org.eclipse.golo.compiler.GoloClassLoader;
026
027/**
028 * Manage the classpath and initialize the class loader.
029 */
030@Parameters(resourceBundle = "commands")
031public class ClasspathOption {
032
033  /**
034   * The system property to define the path.
035   */
036  public static final String PROPERTY = "golo.class.path";
037
038  /**
039   * The environment variable used to define the path.
040   */
041  public static final String ENV = "GOLOPATH";
042  private static final List<String> DEFAULT = Collections.singletonList(".");
043  private static final String SEP = System.getProperty("path.separator", ":");
044
045  private static class ClasspathSplitter implements IParameterSplitter {
046    private static final String RE = String.format("[,%s]", SEP);
047    public List<String> split(String value) {
048      return Arrays.asList(value.split(RE));
049    }
050  }
051
052  @Parameter(names = "--classpath", variableArity = true, descriptionKey = "classpath", splitter = ClasspathSplitter.class)
053  List<String> classpath = new LinkedList<>();
054
055  private static URLClassLoader primaryClassLoader(List<String> classpath) throws MalformedURLException {
056    URL[] urls = new URL[classpath.size()];
057    for (int i = 0; i < classpath.size(); i++) {
058      urls[i] = new File(classpath.get(i)).toURI().toURL();
059    }
060    return new URLClassLoader(urls);
061  }
062
063  /**
064   * Create a golo classloader using the given classpath list.
065   * <p>
066   * If {@code classpath} is empty, packages from the {@code PROPERTY} system property are used, then from
067   * {@code ENV} environment variable if no property is defined. Finally, the current directory is used if no other path
068   * is defined.
069   * The current thread class loader is set to the created golo class loader.
070   */
071  public static GoloClassLoader initGoloClassLoader(List<String> classpath) throws MalformedURLException {
072    URLClassLoader primaryClassLoader = primaryClassLoader(initClassPath(classpath));
073    GoloClassLoader loader = new GoloClassLoader(primaryClassLoader);
074    Thread.currentThread().setContextClassLoader(loader);
075    return loader;
076  }
077
078  private static List<String> initClassPath(List<String> init) {
079    // priority CLI > property > env > default
080    List<String> classpath = init;
081    if (classpath.isEmpty()) {
082      classpath = getFromEnv(System.getProperty(PROPERTY));
083    }
084    if (classpath.isEmpty()) {
085      classpath = getFromEnv(System.getenv(ENV));
086    }
087    if (classpath.isEmpty()) {
088      // XXX: should "." be used in any case or only if no other path is given?
089      classpath = DEFAULT;
090    }
091    System.setProperty(PROPERTY, String.join(SEP, classpath));
092    return classpath;
093  }
094
095  private static List<String> getFromEnv(String value) {
096    if (value == null) {
097      return Collections.emptyList();
098    }
099    return Arrays.asList(value.split(SEP));
100  }
101
102  /**
103   * Init the class loader using the parsed command line option.
104   */
105  public GoloClassLoader initGoloClassLoader() throws MalformedURLException {
106    return initGoloClassLoader(this.classpath);
107  }
108}