All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dev.jbang.source.Project Maven / Gradle / Ivy

There is a newer version: 0.121.0
Show newest version
package dev.jbang.source;

import java.nio.file.Path;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import dev.jbang.dependencies.DependencyResolver;
import dev.jbang.dependencies.MavenRepo;
import dev.jbang.util.ModuleUtil;
import dev.jbang.util.Util;

/**
 * This class gives access to all information necessary to turn source files
 * into something that can be executed. Typically, this means that it holds
 * references to source files, resources and dependencies which can be used by
 * the AppBuilder to create a JAR file, for example.
 */
public class Project {
	@Nonnull
	private final ResourceRef resourceRef;
	private Source mainSource;

	// Public (user) input values (can be changed from the outside at any time)
	private final SourceSet mainSourceSet = new SourceSet();
	private final List repositories = new ArrayList<>();
	// TODO move runtimeOptions to CmdGenerator!
	private final List runtimeOptions = new ArrayList<>();
	private Map properties = new HashMap<>();
	private final Map manifestAttributes = new LinkedHashMap<>();
	private String javaVersion;
	private String description;
	private String gav;
	private String mainClass;
	private String moduleName;
	private boolean nativeImage;
	private boolean enablePreviewRequested;

	private final List subProjects = new ArrayList<>();

	// Cached values
	private String stableId;

	public static final String ATTR_PREMAIN_CLASS = "Premain-Class";
	public static final String ATTR_AGENT_CLASS = "Agent-Class";

	public boolean enablePreview() {
		return enablePreviewRequested || (mainSource != null && mainSource.enablePreview());
	}

	public enum BuildFile {
		jbang("build.jbang");

		public final String fileName;

		BuildFile(String fileName) {
			this.fileName = fileName;
		}

		public static List fileNames() {
			return Arrays.stream(values()).map(v -> v.fileName).collect(Collectors.toList());
		}
	}

	public static ProjectBuilder builder() {
		return new ProjectBuilder();
	}

	public Project(@Nonnull ResourceRef resourceRef) {
		this.resourceRef = resourceRef;
	}

	// TODO This should be refactored and removed
	public Project(@Nonnull Source mainSource) {
		this.resourceRef = mainSource.getResourceRef();
		this.mainSource = mainSource;
	}

	@Nonnull
	public ResourceRef getResourceRef() {
		return resourceRef;
	}

	@Nonnull
	public SourceSet getMainSourceSet() {
		return mainSourceSet;
	}

	@Nonnull
	public List getRepositories() {
		return Collections.unmodifiableList(repositories);
	}

	@Nonnull
	public Project addRepository(@Nonnull MavenRepo repository) {
		repositories.add(repository);
		return this;
	}

	@Nonnull
	public Project addRepositories(@Nonnull Collection repositories) {
		this.repositories.addAll(repositories);
		return this;
	}

	@Nonnull
	public List getRuntimeOptions() {
		return Collections.unmodifiableList(runtimeOptions);
	}

	@Nonnull
	public Project addRuntimeOption(@Nonnull String option) {
		runtimeOptions.add(option);
		return this;
	}

	@Nonnull
	public Project addRuntimeOptions(@Nonnull Collection options) {
		runtimeOptions.addAll(options);
		return this;
	}

	@Nonnull
	public List getSubProjects() {
		return Collections.unmodifiableList(subProjects);
	}

	@Nonnull
	public void addSubProject(@Nonnull Project subProject) {
		subProjects.add(subProject);
	}

	public Map getProperties() {
		return Collections.unmodifiableMap(properties);
	}

	public void putProperties(@Nonnull Map properties) {
		this.properties = properties;
	}

	@Nonnull
	public Map getManifestAttributes() {
		return manifestAttributes;
	}

	public void setAgentMainClass(String agentMainClass) {
		manifestAttributes.put(ATTR_AGENT_CLASS, agentMainClass);
	}

	public void setPreMainClass(String preMainClass) {
		manifestAttributes.put(ATTR_PREMAIN_CLASS, preMainClass);
	}

	@Nullable
	public String getJavaVersion() {
		return javaVersion;
	}

	@Nonnull
	public Project setJavaVersion(String javaVersion) {
		this.javaVersion = javaVersion;
		return this;
	}

	@Nonnull
	public Optional getDescription() {
		return Optional.ofNullable(description);
	}

	@Nonnull
	public Project setDescription(String description) {
		this.description = description;
		return this;
	}

	@Nonnull
	public Optional getGav() {
		return Optional.ofNullable(gav);
	}

	@Nonnull
	public Project setGav(String gav) {
		this.gav = gav;
		return this;
	}

	public String getMainClass() {
		return mainClass;
	}

	public void setMainClass(String mainClass) {
		this.mainClass = mainClass;
	}

	public void setEnablePreviewRequested(boolean enablePreview) {
		this.enablePreviewRequested = enablePreview;
	}

	@Nonnull
	public Optional getModuleName() {
		return Optional.ofNullable(moduleName);
	}

	@Nonnull
	public Project setModuleName(String moduleName) {
		this.moduleName = moduleName;
		return this;
	}

	public boolean isNativeImage() {
		return nativeImage;
	}

	public void setNativeImage(boolean isNative) {
		this.nativeImage = isNative;
	}

	public boolean enableCDS() {
		return mainSource != null && mainSource.enableCDS();
	}

	@Nullable
	public Source getMainSource() {
		return mainSource;
	}

	public void setMainSource(Source mainSource) {
		this.mainSource = mainSource;
	}

	protected String getStableId() {
		if (stableId == null) {
			Stream sss = mainSourceSet.getStableIdInfo();
			if (moduleName != null) {
				Stream s = Stream.of(ModuleUtil.getModuleName(this));
				sss = Stream.concat(sss, s);
			}
			stableId = Util.getStableID(sss);
		}
		return stableId;
	}

	protected void updateDependencyResolver(DependencyResolver resolver) {
		resolver.addRepositories(repositories);
		getMainSourceSet().updateDependencyResolver(resolver);
	}

	/**
	 * Returns a Builder that can be used to turn this
	 * Project into executable code.
	 *
	 * @return A Builder
	 */
	@Nonnull
	public Builder codeBuilder() {
		return CodeBuilderProvider.create(this).get();
	}

	/**
	 * Returns a Builder that can be used to turn this
	 * Project into executable code.
	 *
	 * @param ctx will use the given BuildContext to store target files
	 *            and intermediate results
	 * @return A Builder
	 */
	@Nonnull
	public static Builder codeBuilder(BuildContext ctx) {
		return CodeBuilderProvider.create(ctx).get();
	}

	public boolean isJar() {
		return Project.isJar(getResourceRef().getFile());
	}

	static boolean isJar(Path backingFile) {
		return backingFile != null && backingFile.toString().endsWith(".jar");
	}

	public boolean isJShell() {
		return Project.isJShell(getResourceRef().getFile());
	}

	static boolean isJShell(Path backingFile) {
		return backingFile != null && backingFile.toString().endsWith(".jsh");
	}

	// https://stackoverflow.com/questions/366202/regex-for-splitting-a-string-using-space-when-not-surrounded-by-single-or-double
	static List quotedStringToList(String subjectString) {
		List matchList = new ArrayList<>();
		Pattern regex = Pattern.compile("[^\\s\"']+|\"([^\"]*)\"|'([^']*)'");
		Matcher regexMatcher = regex.matcher(subjectString);
		while (regexMatcher.find()) {
			if (regexMatcher.group(1) != null) {
				// Add double-quoted string without the quotes
				matchList.add(regexMatcher.group(1));
			} else if (regexMatcher.group(2) != null) {
				// Add single-quoted string without the quotes
				matchList.add(regexMatcher.group(2));
			} else {
				// Add unquoted word
				matchList.add(regexMatcher.group());
			}
		}
		return matchList;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy