/*
* ******************************************************************************
* MontiCore Language Workbench
* Copyright (c) 2015, MontiCore, All rights reserved.
*
* This project is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this project. If not, see <http://www.gnu.org/licenses/>.
* ******************************************************************************
*/
package de.monticore;
import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import com.google.common.collect.Sets;
import de.monticore.io.paths.IterablePath;
import de.monticore.io.paths.ModelPath;
import de.se_rwth.commons.configuration.Configuration;
import de.se_rwth.commons.configuration.ConfigurationContributorChainBuilder;
import de.se_rwth.commons.configuration.DelegatingConfigurationContributor;
import de.se_rwth.commons.logging.Log;
/**
* Provides access to the aggregated configuration of a MontiCore instance
* derived from (1) its command line arguments, and (2) system properties (not
* implemented yet).
*
* @author (last commit) $Author$
* @version $Revision$, $Date$
*/
public final class MontiCoreConfiguration implements Configuration {
public static final String MC4_EXTENSION = "mc4";
public static final String JAVA_EXTENSION = "java";
public static final String FTL_EXTENSION = "ftl";
public static final Set<String> MC4_EXTENSIONS = Sets.newHashSet(MC4_EXTENSION);
public static final Set<String> HWC_EXTENSIONS = Sets.newHashSet(JAVA_EXTENSION);
public static final Set<String> FTL_EXTENSIONS = Sets.newHashSet(FTL_EXTENSION);
public static final String CONFIGURATION_PROPERTY = "_configuration";
public static final String DEFAULT_OUTPUT_DIRECTORY = "out";
/**
* The names of the specific MontiCore options used in this configuration.
*/
public enum Options {
GRAMMARS("grammars"), GRAMMARS_SHORT("g"), MODELPATH("modelPath"), MODELPATH_SHORT("mp"),
OUT("out"), OUT_SHORT("o"), HANDCODEDPATH("handcodedPath"), HANDCODEDPATH_SHORT("hcp"),
TEMPLATEPATH("templatePath"), TEMPLATEPATH_SHORT("fp"), OUTTOMODELPATH("addOutToModelpath"),
OUTTOMODELPATH_SHORT("otm"), FORCE("force"), FORCE_SHORT("f");
String name;
Options(String name) {
this.name = name;
}
/**
* @see java.lang.Enum#toString()
*/
@Override
public String toString() {
return this.name;
}
}
/**
* Factory method for {@link MontiCoreConfiguration}.
*/
public static MontiCoreConfiguration withConfiguration(Configuration configuration) {
return new MontiCoreConfiguration(configuration);
}
private final Configuration configuration;
/**
* Constructor for {@link MontiCoreConfiguration}
*/
private MontiCoreConfiguration(Configuration internal) {
// ConfigurationSystemPropertiesContributor systemPropertiesContributor =
// ConfigurationSystemPropertiesContributor.withPrefix("monticore");
this.configuration = ConfigurationContributorChainBuilder.newChain()
// .add(systemPropertiesContributor)
.add(DelegatingConfigurationContributor.with(internal))
.build();
}
/**
* @see de.se_rwth.commons.configuration.Configuration#getAllValues()
*/
@Override
public Map<String, Object> getAllValues() {
return this.configuration.getAllValues();
}
/**
* @see de.se_rwth.commons.configuration.Configuration#getAllValuesAsStrings()
*/
@Override
public Map<String, String> getAllValuesAsStrings() {
return this.configuration.getAllValuesAsStrings();
}
/**
* @see de.se_rwth.commons.configuration.Configuration#getAsBoolean(java.lang.String)
*/
@Override
public Optional<Boolean> getAsBoolean(String key) {
return this.configuration.getAsBoolean(key);
}
public Optional<Boolean> getAsBoolean(Enum<?> key) {
return getAsBoolean(key.toString());
}
/**
* @see de.se_rwth.commons.configuration.Configuration#getAsBooleans(java.lang.String)
*/
@Override
public Optional<List<Boolean>> getAsBooleans(String key) {
return this.configuration.getAsBooleans(key);
}
public Optional<List<Boolean>> getAsBooleans(Enum<?> key) {
return getAsBooleans(key.toString());
}
/**
* @see de.se_rwth.commons.configuration.Configuration#getAsDouble(java.lang.String)
*/
@Override
public Optional<Double> getAsDouble(String key) {
return this.configuration.getAsDouble(key);
}
public Optional<Double> getAsDouble(Enum<?> key) {
return getAsDouble(key.toString());
}
/**
* @see de.se_rwth.commons.configuration.Configuration#getAsDoubles(java.lang.String)
*/
@Override
public Optional<List<Double>> getAsDoubles(String key) {
return this.configuration.getAsDoubles(key);
}
public Optional<List<Double>> getAsDoubles(Enum<?> key) {
return getAsDoubles(key.toString());
}
/**
* @see de.se_rwth.commons.configuration.Configuration#getAsInteger(java.lang.String)
*/
@Override
public Optional<Integer> getAsInteger(String key) {
return this.configuration.getAsInteger(key);
}
public Optional<Integer> getAsInteger(Enum<?> key) {
return getAsInteger(key.toString());
}
/**
* @see de.se_rwth.commons.configuration.Configuration#getAsIntegers(java.lang.String)
*/
@Override
public Optional<List<Integer>> getAsIntegers(String key) {
return this.configuration.getAsIntegers(key);
}
public Optional<List<Integer>> getAsIntegers(Enum<?> key) {
return getAsIntegers(key.toString());
}
/**
* @see de.se_rwth.commons.configuration.Configuration#getAsString(java.lang.String)
*/
@Override
public Optional<String> getAsString(String key) {
return this.configuration.getAsString(key);
}
public Optional<String> getAsString(Enum<?> key) {
return getAsString(key.toString());
}
/**
* @see de.se_rwth.commons.configuration.Configuration#getAsStrings(java.lang.String)
*/
@Override
public Optional<List<String>> getAsStrings(String key) {
return this.configuration.getAsStrings(key);
}
public Optional<List<String>> getAsStrings(Enum<?> key) {
return getAsStrings(key.toString());
}
/**
* @see de.se_rwth.commons.configuration.Configuration#getValue(java.lang.String)
*/
@Override
public Optional<Object> getValue(String key) {
return this.configuration.getValue(key);
}
public Optional<Object> getValue(Enum<?> key) {
return getValue(key.toString());
}
/**
* @see de.se_rwth.commons.configuration.Configuration#getValues(java.lang.String)
*/
@Override
public Optional<List<Object>> getValues(String key) {
return this.configuration.getValues(key);
}
public Optional<List<Object>> getValues(Enum<?> key) {
return getValues(key.toString());
}
/**
* @see de.se_rwth.commons.configuration.Configuration#hasProperty(java.lang.String)
*/
@Override
public boolean hasProperty(String key) {
return this.configuration.hasProperty(key);
}
public boolean hasProperty(Enum<?> key) {
return hasProperty(key.toString());
}
/**
* Getter for the {@link IterablePath} consisting of grammar files stored in
* this configuration.
*
* @return iterable grammar files
*/
public IterablePath getGrammars() {
Optional<List<String>> grammars = getAsStrings(Options.GRAMMARS);
if (grammars.isPresent()) {
return IterablePath.from(toFileList(grammars.get()), MC4_EXTENSIONS);
}
grammars = getAsStrings(Options.GRAMMARS_SHORT);
if (grammars.isPresent()) {
return IterablePath.from(toFileList(grammars.get()), MC4_EXTENSIONS);
}
// no default; must specify grammar files/directories to process
Log.error("0xA1013 Please specify the grammar file(s).");
return IterablePath.empty();
}
/**
* Getter for the actual value of the grammar argument. This is not the
* prepared {@link IterablePath} as in
* {@link MontiCoreConfiguration#getGrammars()} but the raw input arguments.
*
* @return
*/
public List<String> getGrammarsAsStrings() {
Optional<List<String>> grammars = getAsStrings(Options.GRAMMARS);
if (grammars.isPresent()) {
return grammars.get();
}
grammars = getAsStrings(Options.GRAMMARS_SHORT);
if (grammars.isPresent()) {
return grammars.get();
}
// no default; must specify grammar files/directories to process
Log.error("0xA1014 Please specify the grammar file(s).");
return Collections.emptyList();
}
/**
* Getter for the list of model path elements (files and directories) stored
* in this configuration.
*
* @return list of model path files
*/
public ModelPath getModelPath() {
Optional<ModelPath> modelPath = getAsStrings(Options.MODELPATH)
.map(this::convertEntryNamesToModelPath);
if (modelPath.isPresent()) {
return modelPath.get();
}
modelPath = getAsStrings(Options.MODELPATH_SHORT).map(this::convertEntryNamesToModelPath);
if (modelPath.isPresent()) {
return modelPath.get();
}
// default model path is empty (but contains the output directory by
// default)
return getAddOutToModelPath() ?
new ModelPath(getOut().toPath().toAbsolutePath()) :
new ModelPath();
}
private ModelPath convertEntryNamesToModelPath(List<String> modelPathEntryNames) {
List<File> modelPathFiles = toFileList(modelPathEntryNames);
if (getAddOutToModelPath()) {
modelPathFiles.add(getOut());
}
List<Path> modelPathEntries = modelPathFiles.stream()
.map(File::toPath)
.map(Path::toAbsolutePath)
.collect(Collectors.toList());
return new ModelPath(modelPathEntries);
}
/**
* Getter for the actual value of the model path argument. This is not the
* prepared {@link ModelPath} as in
* {@link MontiCoreConfiguration#getModelPath()} but the raw input arguments.
*
* @return
*/
public List<String> getModelPathAsStrings() {
Optional<List<String>> modelPath = getAsStrings(Options.MODELPATH);
if (modelPath.isPresent()) {
List<String> result = new ArrayList<>(modelPath.get());
if (getAddOutToModelPath()) {
result.add(getOut().toString());
}
return result;
}
modelPath = getAsStrings(Options.MODELPATH_SHORT);
if (modelPath.isPresent()) {
List<String> result = new ArrayList<>(modelPath.get());
if (getAddOutToModelPath()) {
result.add(getOut().toString());
}
return result;
}
// default model path is empty
if (getAddOutToModelPath()) {
return Collections.singletonList(getOut().toString());
}
return Collections.emptyList();
}
/**
* Getter for the output directory stored in this configuration. A fallback
* default is "monticore/sourcecode".
*
* @return output directory file
*/
public File getOut() {
Optional<String> out = getAsString(Options.OUT);
if (out.isPresent()) {
return new File(out.get());
}
out = getAsString(Options.OUT_SHORT);
if (out.isPresent()) {
return new File(out.get());
}
// fallback default is "out"
return new File(DEFAULT_OUTPUT_DIRECTORY);
}
/**
* Getter for the handcoded path directories stored in this configuration.
*
* @return iterable handcoded files
*/
public IterablePath getHandcodedPath() {
Optional<List<String>> handcodedPath = getAsStrings(Options.HANDCODEDPATH);
if (handcodedPath.isPresent()) {
return IterablePath.from(toFileList(handcodedPath.get()), HWC_EXTENSIONS);
}
handcodedPath = getAsStrings(Options.HANDCODEDPATH_SHORT);
if (handcodedPath.isPresent()) {
return IterablePath.from(toFileList(handcodedPath.get()), HWC_EXTENSIONS);
}
// default handcoded path is empty
return IterablePath.empty();
}
/**
* Getter for the actual value of the handcoded path argument. This is not the
* prepared {@link IterablePath} as in
* {@link MontiCoreConfiguration#getHandcodedPath()} but the raw input
* arguments.
*
* @return
*/
public List<String> getHandcodedPathAsStrings() {
Optional<List<String>> handcodedPath = getAsStrings(Options.HANDCODEDPATH);
if (handcodedPath.isPresent()) {
return handcodedPath.get();
}
handcodedPath = getAsStrings(Options.HANDCODEDPATH_SHORT);
if (handcodedPath.isPresent()) {
return handcodedPath.get();
}
// default handcoded path is empty
return Collections.emptyList();
}
/**
* Getter for the target path directories stored in this configuration.
*
* @return iterable template files
*/
public IterablePath getTemplatePath() {
Optional<List<String>> templatePath = getAsStrings(Options.TEMPLATEPATH);
if (templatePath.isPresent()) {
return IterablePath.from(toFileList(templatePath.get()), FTL_EXTENSIONS);
}
templatePath = getAsStrings(Options.TEMPLATEPATH_SHORT);
if (templatePath.isPresent()) {
return IterablePath.from(toFileList(templatePath.get()), FTL_EXTENSIONS);
}
// default template path is empty
return IterablePath.empty();
}
/**
* Getter for the actual value of the template path argument. This is not the
* prepared {@link IterablePath} as in
* {@link MontiCoreConfiguration#getTemplatePath()} but the raw input
* arguments.
*
* @return
*/
public List<String> getTemplatePathAsStrings() {
Optional<List<String>> templatePath = getAsStrings(Options.TEMPLATEPATH);
if (templatePath.isPresent()) {
return templatePath.get();
}
templatePath = getAsStrings(Options.TEMPLATEPATH_SHORT);
if (templatePath.isPresent()) {
return templatePath.get();
}
// default template path is empty
return Collections.emptyList();
}
/**
* Getter for the add out to model path argument; defaults to true.
*
* @return
*/
public boolean getAddOutToModelPath() {
Optional<Boolean> addOutToModelPath = getAsBoolean(Options.OUTTOMODELPATH);
if (addOutToModelPath.isPresent()) {
return addOutToModelPath.get();
}
addOutToModelPath = getAsBoolean(Options.OUTTOMODELPATH_SHORT);
if (addOutToModelPath.isPresent()) {
return addOutToModelPath.get();
}
return true;
}
/**
* Getter for the incremental generation switch in this configuration. By
* default incremental generation is enabled, i.e., unless this property is
* present incremental checks are performed and processing is skipped if
* necessary.
*
* @return whether generation should be forced, i.e., whether incremental
* checks should be skipped
*/
public boolean getForce() {
return hasProperty(Options.FORCE) || hasProperty(Options.FORCE_SHORT);
}
/**
* @param files as String names to convert
* @return list of files by creating file objects from the Strings
*/
protected static List<File> toFileList(List<String> files) {
return files.stream().collect(
Collectors.mapping(file -> new File(file).getAbsoluteFile(), Collectors.toList()));
}
}