/** * Copyright (C) 2013-2014 Olaf Lessenich * Copyright (C) 2014-2015 University of Passau, Germany * * This library 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 2.1 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 library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301 USA * * Contributors: * Olaf Lessenich <lessenic@fim.uni-passau.de> * Georg Seibt <seibt@fim.uni-passau.de> */ package de.fosd.jdime.config.merge; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.StringWriter; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.function.Supplier; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; import de.fosd.jdime.artifact.Artifact; import de.fosd.jdime.artifact.ArtifactList; import de.fosd.jdime.artifact.ast.ASTNodeArtifact; import de.fosd.jdime.artifact.file.FileArtifact; import de.fosd.jdime.config.CommandLineConfigSource; import de.fosd.jdime.config.JDimeConfig; import de.fosd.jdime.execption.AbortException; import de.fosd.jdime.matcher.cost_model.CMMode; import de.fosd.jdime.stats.KeyEnums; import de.fosd.jdime.stats.Statistics; import de.fosd.jdime.strategy.LinebasedStrategy; import de.fosd.jdime.strategy.MergeStrategy; import de.fosd.jdime.strategy.NWayStrategy; import de.fosd.jdime.strdump.DumpMode; import static de.fosd.jdime.config.CommandLineConfigSource.*; import static de.fosd.jdime.config.JDimeConfig.FILTER_INPUT_DIRECTORIES; import static de.fosd.jdime.config.JDimeConfig.USE_MCESUBTREE_MATCHER; import static java.util.logging.Level.WARNING; /** * @author Olaf Lessenich */ public class MergeContext implements Cloneable { private static final Logger LOG = Logger.getLogger(MergeContext.class.getCanonicalName()); /** * Do look at all nodes in the subtree even if the compared nodes are not * equal. */ public static final int LOOKAHEAD_FULL = Integer.MAX_VALUE; /** * Stop looking for subtree matches if the two nodes compared are not equal. */ public static final int LOOKAHEAD_OFF = 0; /** * Whether merge inserts choice nodes instead of direct merging. */ private boolean conditionalMerge; /** * Whether conditional merge should be performed outside of methods. */ private boolean conditionalOutsideMethods; /** * Whether to run only the diff. */ private boolean diffOnly; /** * Whether to treat two input versions as consecutive versions in the revision history. */ private boolean consecutive; /** * If set the input <code>Artifact</code>s will be dumped in the given format instead of merging. */ private DumpMode dumpMode; /** * The number of an artifact that should be inspected. If this is set, no merge will be executed. */ private int inspectArtifact; /** * The scope of inspection. */ private KeyEnums.Type inspectionScope; /** * Force overwriting of existing output files. */ private boolean forceOverwriting; /** * Input Files. */ private ArtifactList<FileArtifact> inputFiles; /** * If true and the input files are directories, any files not representing java source code files or directories * (possibly indirectly) containing such files will be removed from the input FileArtifact trees. */ private boolean filterInputDirectories; /** * If true, merging will continue (skipping the failed files) after exceptions if exit-on-error is not set. */ private boolean keepGoing; /** * If true, merge will be aborted if there is an exception merging files. */ private boolean exitOnError; /** * Strategy to apply for the merge. */ private MergeStrategy<FileArtifact> mergeStrategy; /** * Output file. */ private FileArtifact outputFile; /** * If true, the output is quiet. */ private boolean quiet; /** * If true, output is not written to an output file. */ private boolean pretend; /** * Merge directories recursively. Can be set with the '-r' argument. */ private boolean recursive; /** * Whether to collect statistics after merging. */ private boolean collectStatistics; private Statistics statistics; /** * Whether to use the <code>MCESubtreeMatcher</code> in the matching phase of the merge. */ private boolean useMCESubtreeMatcher; /** * The standard out/error streams used during the merge. */ private StringWriter stdErr; private StringWriter stdIn; /** * How many levels to keep searching for matches in the subtree if the * currently compared nodes are not equal. If there are no matches within * the specified number of levels, do not look for matches deeper in the * subtree. If this is set to LOOKAHEAD_OFF, the matcher will stop looking * for subtree matches if two nodes do not match. If this is set to * LOOKAHEAD_FULL, the matcher will look at the entire subtree. * The default ist to do no look-ahead matching. */ private int lookAhead; private Map<KeyEnums.Type, Integer> lookAheads; private Map<MergeScenario<?>, Throwable> crashes; private CMMode cmMatcherMode; private float cmReMatchBound; private float wr, wn, wa, ws, wo; private float pAssign; private float fixLower, fixUpper; private Optional<Long> seed; private int costModelIterations; private boolean cmMatcherParallel; private boolean cmMatcherFixRandomPercentage; /** * Constructs a new <code>MergeContext</code> initializing all options to their default values. */ public MergeContext() { this.conditionalMerge = false; this.conditionalOutsideMethods = true; this.diffOnly = false; this.consecutive = false; this.dumpMode = DumpMode.NONE; this.forceOverwriting = false; this.inputFiles = new ArtifactList<>(); this.filterInputDirectories = true; this.keepGoing = false; this.exitOnError = false; this.mergeStrategy = new LinebasedStrategy(); this.outputFile = null; this.quiet = false; this.pretend = true; this.recursive = false; this.collectStatistics = false; this.statistics = null; this.useMCESubtreeMatcher = false; this.stdErr = new StringWriter(); this.stdIn = new StringWriter(); this.lookAhead = MergeContext.LOOKAHEAD_OFF; this.lookAheads = new HashMap<>(); this.crashes = new HashMap<>(); this.cmMatcherMode = CMMode.OFF; this.cmReMatchBound = .3f; this.wr = 1; this.wn = 1; this.wa = 1; this.ws = 1; this.wo = 1; this.pAssign = .7f; this.fixLower = .25f; this.fixUpper = .50f; this.seed = Optional.of(42L); this.costModelIterations = 100; this.cmMatcherParallel = true; this.cmMatcherFixRandomPercentage = true; } /** * Copy constructor. * * @param toCopy * the <code>MergeContext</code> to copy */ public MergeContext(MergeContext toCopy) { this.conditionalMerge = toCopy.conditionalMerge; this.conditionalOutsideMethods = toCopy.conditionalOutsideMethods; this.diffOnly = toCopy.diffOnly; this.consecutive = toCopy.consecutive; this.dumpMode = toCopy.dumpMode; this.inspectArtifact = toCopy.inspectArtifact; this.inspectionScope = toCopy.inspectionScope; this.forceOverwriting = toCopy.forceOverwriting; this.inputFiles = new ArtifactList<>(); this.inputFiles.addAll(toCopy.inputFiles.stream().map(FileArtifact::clone).collect(Collectors.toList())); this.filterInputDirectories = toCopy.filterInputDirectories; this.keepGoing = toCopy.keepGoing; this.exitOnError = toCopy.exitOnError; this.mergeStrategy = toCopy.mergeStrategy; // MergeStrategy should be stateless this.outputFile = (toCopy.outputFile != null) ? toCopy.outputFile.clone() : null; this.quiet = toCopy.quiet; this.pretend = toCopy.pretend; this.recursive = toCopy.recursive; this.collectStatistics = toCopy.collectStatistics; this.statistics = (toCopy.statistics != null) ? new Statistics(toCopy.statistics) : null; this.useMCESubtreeMatcher = toCopy.useMCESubtreeMatcher; this.stdErr = new StringWriter(); this.stdErr.append(toCopy.stdErr.toString()); this.stdIn = new StringWriter(); this.stdIn.append(toCopy.stdIn.toString()); this.lookAhead = toCopy.lookAhead; this.lookAheads = new HashMap<>(toCopy.lookAheads); this.crashes = new HashMap<>(toCopy.crashes); this.cmMatcherMode = toCopy.cmMatcherMode; this.cmReMatchBound = toCopy.cmReMatchBound; this.wr = toCopy.wr; this.wn = toCopy.wn; this.wa = toCopy.wa; this.ws = toCopy.ws; this.wo = toCopy.wo; this.pAssign = toCopy.pAssign; this.fixLower = toCopy.fixLower; this.fixUpper = toCopy.fixUpper; this.seed = toCopy.seed; this.costModelIterations = toCopy.costModelIterations; this.cmMatcherParallel = toCopy.cmMatcherParallel; this.cmMatcherFixRandomPercentage = toCopy.cmMatcherFixRandomPercentage; } /** * Initializes the configuration options stored in the <code>MergeContext</code> from the given * <code>JDimeConfig</code>. * * @param config * the <code>JDimeConfig</code> to query for config values */ public void configureFrom(JDimeConfig config) { setUseMCESubtreeMatcher(config.getBoolean(USE_MCESUBTREE_MATCHER).orElse(false)); config.getBoolean(CLI_DIFFONLY).ifPresent(diffOnly -> { setDiffOnly(diffOnly); config.getBoolean(CLI_CONSECUTIVE).ifPresent(this::setConsecutive); }); config.get(CLI_LOOKAHEAD, val -> { try { return Optional.of(Integer.parseInt(val)); } catch (NumberFormatException e) { String lcVal = val.trim().toLowerCase(); if ("off".equals(lcVal)) { return Optional.of(MergeContext.LOOKAHEAD_OFF); } else if ("full".equals(lcVal)) { return Optional.of(MergeContext.LOOKAHEAD_FULL); } else { return Optional.empty(); } } }).ifPresent(this::setLookAhead); for (KeyEnums.Type type : KeyEnums.Type.values()) { Optional<Integer> lah = config.getInteger(JDimeConfig.LOOKAHEAD_PREFIX + type.name()); lah.ifPresent(val -> setLookAhead(type, val)); } config.getBoolean(CLI_STATS).ifPresent(this::collectStatistics); config.getBoolean(CLI_FORCE_OVERWRITE).ifPresent(this::setForceOverwriting); config.getBoolean(CLI_RECURSIVE).ifPresent(this::setRecursive); if (config.getBoolean(CLI_PRINT).orElse(false)) { setPretend(true); setQuiet(false); } else if (config.getBoolean(CLI_QUIET).orElse(false)) { setQuiet(true); } config.getBoolean(FILTER_INPUT_DIRECTORIES).ifPresent(this::setFilterInputDirectories); config.getBoolean(CLI_KEEPGOING).ifPresent(this::setKeepGoing); config.getBoolean(CLI_EXIT_ON_ERROR).ifPresent(this::setExitOnError); Optional<String> args = config.get(CommandLineConfigSource.ARG_LIST); if (args.isPresent()) { List<String> paths = Arrays.asList(args.get().split(CommandLineConfigSource.ARG_LIST_SEP)); ArtifactList<FileArtifact> inputArtifacts = new ArtifactList<>(); Supplier<Revision> revSupplier; if (isConditionalMerge()) { revSupplier = new Revision.SuccessiveRevSupplier(); } else { if (paths.size() == MergeType.TWOWAY_FILES) { revSupplier = Arrays.asList(MergeScenario.LEFT, MergeScenario.RIGHT).iterator()::next; } else if (paths.size() == MergeType.THREEWAY_FILES) { revSupplier = Arrays.asList(MergeScenario.LEFT, MergeScenario.BASE, MergeScenario.RIGHT).iterator()::next; } else { revSupplier = new Revision.SuccessiveRevSupplier(); } } for (String path : paths) { String fileName = path.trim(); try { FileArtifact artifact = new FileArtifact(revSupplier.get(), new File(fileName)); inputArtifacts.add(artifact); } catch (FileNotFoundException e) { LOG.log(Level.SEVERE, () -> String.format("Input file %s not found.", fileName)); throw new AbortException(e); } catch (IOException e) { LOG.log(Level.SEVERE, () -> String.format("Input file %s could not be accessed.", fileName)); throw new AbortException(e); } } setInputFiles(inputArtifacts); } /* * TODO[low priority] * The default should in a later, rock-stable version be changed to be overwriting file1 so that we are * compatible with gnu merge call syntax. */ config.get(CLI_OUTPUT).ifPresent(outputFileName -> { boolean targetIsFile = inputFiles.stream().anyMatch(FileArtifact::isFile); try { File out = new File(outputFileName); FileArtifact outArtifact = new FileArtifact(MergeScenario.MERGE, out, true, targetIsFile); setOutputFile(outArtifact); setPretend(false); } catch (IOException e) { LOG.log(Level.SEVERE, e, () -> "Could not create the output FileArtifact."); } }); config.get(CLI_CM, mode -> { try { return Optional.of(CMMode.valueOf(mode.toUpperCase())); } catch (IllegalArgumentException e) { LOG.log(WARNING, e, () -> "Invalid CostModelMatcher mode " + mode); return Optional.empty(); } }).ifPresent(this::setCmMatcherMode); config.getFloat(CLI_CM_REMATCH_BOUND).ifPresent(this::setCmReMatchBound); config.get(CLI_CM_OPTIONS).ifPresent(opts -> { String[] split = opts.trim().split("\\s*,\\s*"); if (split.length != 7) { LOG.warning(() -> "The cost model options have an invalid format. Using defaults."); return; } int costModelIterations; float pAssign, wr, wn, wa, ws, wo; try { costModelIterations = Integer.parseInt(split[0]); pAssign = Float.parseFloat(split[1]); wr = Float.parseFloat(split[2]); wn = Float.parseFloat(split[3]); wa = Float.parseFloat(split[4]); ws = Float.parseFloat(split[5]); wo = Float.parseFloat(split[6]); } catch (NumberFormatException e) { LOG.log(WARNING, e, () -> "The cost model options have an invalid format. Using defaults."); return; } setCostModelIterations(costModelIterations); setpAssign(pAssign); setWr(wr); setWn(wn); setWa(wa); setWs(ws); setWo(wo); }); config.getBoolean(CLI_CM_PARALLEL).ifPresent(this::setCmMatcherParallel); config.get(CLI_CM_FIX_PERCENTAGE).ifPresent(opts -> { String[] split = opts.trim().split("\\s*,\\s*"); if (split.length != 2) { LOG.warning(() -> "The cost model fix percentages have an invalid format."); return; } float fixLower, fixUpper; try { fixLower = Float.parseFloat(split[0]); fixUpper = Float.parseFloat(split[1]); } catch (NumberFormatException e) { LOG.log(WARNING, e, () -> "The cost model fix percentages have an invalid format."); return; } setCmMatcherFixRandomPercentage(true); setFixLower(fixLower); setFixUpper(fixUpper); }); config.get(CLI_CM_SEED).ifPresent(opt -> { if ("none".equals(opt.trim().toLowerCase())) { setSeed(Optional.empty()); } else { try { setSeed(Optional.of(Long.parseLong(opt))); } catch (NumberFormatException e) { LOG.log(WARNING, e, () -> "The cost model seed has an invalid format. Using the default."); } } }); } /** * Append a String to stdIN. * * @param s * String to append */ public void append(String s) { stdIn.append(s); } /** * Append a String to stdERR. * * @param s * String to append */ public void appendError(String s) { stdErr.append(s); } /** * Appends a line to the saved stdin buffer. * * @param line * to be appended */ public void appendLine(String line) { stdIn.append(line); stdIn.append(System.lineSeparator()); } /** * Appends a line to the saved stderr buffer. * * @param line * to be appended */ public void appendErrorLine(String line) { stdErr.append(line); stdErr.append(System.lineSeparator()); } /** * Returns the input files for the merge. * * @return the input files */ public ArtifactList<FileArtifact> getInputFiles() { return inputFiles; } /** * Sets the input files to the new value. * * @param inputFiles * the new input files */ public void setInputFiles(ArtifactList<FileArtifact> inputFiles) { this.inputFiles = inputFiles; } /** * Returns the merge strategy. * * @return the merge strategy */ public MergeStrategy<FileArtifact> getMergeStrategy() { return mergeStrategy; } /** * Sets the merge strategy. * * @param mergeStrategy * merge strategy */ public void setMergeStrategy(MergeStrategy<FileArtifact> mergeStrategy) { this.mergeStrategy = mergeStrategy; if (mergeStrategy instanceof NWayStrategy) { setConditionalMerge(true); } } /** * Returns the output file for the merge. * * @return the outputFile */ public FileArtifact getOutputFile() { return outputFile; } /** * Sets the output file to the new value. * * @param outputFile * the new output file */ public void setOutputFile(FileArtifact outputFile) { this.outputFile = outputFile; } /** * Returns the <code>Statistics</code> object used to collect statistical data. This method <u>may</u> return * <code>null</code> if {@link #hasStatistics()} returns <code>false</code>. * * @return the <code>Statistics</code> object currently in use */ public Statistics getStatistics() { return statistics; } /** * Returns whether statistical data should be collected using the <code>Statistics</code> object returned by * {@link #getStatistics()}. * * @return whether statistical data should be collected */ public boolean hasStatistics() { return collectStatistics; } /** * Returns the saved standard error buffer as a <code>String</code>. * * @return the stdErr buffer as a <code>String</code> */ public String getStdErr() { return stdErr.toString(); } /** * Returns the saved standard input buffer as a <code>String</code>. * * @return the stdIn buffer as a <code>String</code> */ public String getStdIn() { return stdIn.toString(); } /** * Returns true if stdErr is not empty. * * @return true if stdErr is not empty */ public boolean hasErrors() { return stdErr.getBuffer().length() != 0; } /** * Returns true if stdIn is not empty. * * @return true if stdIn is not empty */ public boolean hasOutput() { return stdIn.getBuffer().length() != 0; } /** * Returns whether to only perform the diff stage of the merge. * * @return true iff only the diff stage shall be performed */ public boolean isDiffOnly() { return diffOnly; } /** * Sets whether to only perform the diff stage of the merge. * * @param diffOnly * whether to diff only */ public void setDiffOnly(boolean diffOnly) { this.diffOnly = diffOnly; } /** * Returns the <code>DumpMode</code> if it is set. * * @return the <code>DumpMode</code> */ public DumpMode getDumpMode() { return dumpMode; } /** * Sets the <code>DumpMode</code> to the new value. * * @param dumpMode * the new <code>DumpMode</code> */ public void setDumpMode(DumpMode dumpMode) { this.dumpMode = dumpMode; } /** * Returns true if overwriting of files in the output directory is forced. * * @return whether overwriting of output files is forced */ public boolean isForceOverwriting() { return forceOverwriting; } /** * Sets whether overwriting of files in the output directory is forced. * * @param forceOverwriting * overwrite files in the output directory */ public void setForceOverwriting(boolean forceOverwriting) { this.forceOverwriting = forceOverwriting; } /** * Whether to filter out any <code>FileArtifact</code>s not representing java source code files or directories * (possibly indirectly) containing such files before merging. Defaults to true. * * @return true iff the filter should be applied to input directories before merging */ public boolean isFilterInputDirectories() { return filterInputDirectories; } /** * Whether to filter out any <code>FileArtifact</code>s not representing java source code files or directories * (possibly indirectly) containing such files before merging. Defaults to true. * * @param filterInputDirectories * whether to applie the filter to input directories before merging */ public void setFilterInputDirectories(boolean filterInputDirectories) { this.filterInputDirectories = filterInputDirectories; } /** * If true, merging will continue (skipping the failed files) after exceptions if exit-on-error is not set. * * @return true iff the merge should keep going */ public boolean isKeepGoing() { return keepGoing; } /** * Sets whether to keep going to the new value. * * @param keepGoing * whether to keep going */ public void setKeepGoing(boolean keepGoing) { this.keepGoing = keepGoing; } /** * Gets whether to abort the merge if merging a set of files fails. * * @return whether to abort */ public boolean isExitOnError() { return exitOnError; } /** * Sets whether to abort the merge if merging a set of files fails. * * @param exitOnError * the new value */ public void setExitOnError(boolean exitOnError) { this.exitOnError = exitOnError; } /** * Returns true if the output is quiet. * * @return if output is quiet */ public boolean isQuiet() { return quiet; } /** * Sets whether the output is quiet or not. * * @param quiet * do not print merge results to stdout */ public void setQuiet(boolean quiet) { this.quiet = quiet; } /** * Returns true if the merge is only simulated but not written to an output file. * * @return true, if the merge is only simulated but not written to an output file. */ public boolean isPretend() { return pretend; } /** * Sets whether the merge is only simulated and not written to an output file. * * @param pretend * do not write the merge result to an output file */ public void setPretend(boolean pretend) { this.pretend = pretend; } /** * Returns whether directories are merged recursively. * * @return true, if directories are merged recursively */ public boolean isRecursive() { return recursive; } /** * Set whether directories are merged recursively. * * @param recursive * directories are merged recursively */ public void setRecursive(boolean recursive) { this.recursive = recursive; } /** * Resets the input streams. */ public void resetStreams() { stdIn = new StringWriter(); stdErr = new StringWriter(); } /** * Sets whether statistical data should be collected during the next run using this <code>MergeContext</code> * * @param collectStatistics * whether to collect statistical data */ public void collectStatistics(boolean collectStatistics) { this.collectStatistics = collectStatistics; if (collectStatistics && statistics == null) { statistics = new Statistics(); } } /** * Whether to treat two input versions as consecutive versions in the revision history. * * @return true iff two input versions should be treated as consecutive */ public boolean isConsecutive() { return consecutive; } /** * Sets whether to do consecutive merging. * * @param consecutive * whether to do consecutive merging */ public void setConsecutive(boolean consecutive) { this.consecutive = consecutive; } /** * Whether merge inserts choice nodes instead of direct merging. */ public boolean isConditionalMerge() { return conditionalMerge; } /** * Whether merge the given artifact inserts choice nodes instead of direct merging. * * @param artifact * the artifact to check * @return true iff conditional merge is enabled for the given artifact */ public boolean isConditionalMerge(Artifact<?> artifact) { return conditionalMerge && (conditionalOutsideMethods || artifact instanceof ASTNodeArtifact && ( (ASTNodeArtifact) artifact).isWithinMethod()); } /** * Sets whether to insert choice nodes instead of direct merging where appropriate. * * @param conditionalMerge * the new value */ public void setConditionalMerge(boolean conditionalMerge) { this.conditionalMerge = conditionalMerge; } /** * Returns how many levels to keep searching for matches in the subtree if * the currently compared nodes are not equal. If there are no matches * within the specified number of levels, do not look for matches deeper in * the subtree. If this is set to LOOKAHEAD_OFF, the matcher will stop * looking for subtree matches if two nodes do not match. If this is set to * LOOKAHEAD_FULL, the matcher will look at the entire subtree. The default * ist to do no look-ahead matching. * * @return number of levels to look down for subtree matches if the * currently compared nodes do not match */ public int getLookAhead() { return lookAhead; } /** * Returns the specific lookahead for the given type or the generic lookahead as returned by * {@link #getLookAhead()}. * * @param type * the type to get the lookahead for * @return the lookahead */ public int getLookahead(KeyEnums.Type type) { if (lookAheads.containsKey(type)) { return lookAheads.containsKey(type) ? lookAheads.get(type) : LOOKAHEAD_OFF; } else { return lookAhead; } } /** * Returns whether lookahead is enabled. * * @return true iff lookahead is enabled */ public boolean isLookAhead() { return !lookAheads.isEmpty() || lookAhead != MergeContext.LOOKAHEAD_OFF; } /** * Sets how many levels to keep searching for matches in the subtree if * the currently compared nodes are not equal. If there are no matches * within the specified number of levels, do not look for matches deeper in * the subtree. If this is set to LOOKAHEAD_OFF, the matcher will stop * looking for subtree matches if two nodes do not match. If this is set to * LOOKAHEAD_FULL, the matcher will look at the entire subtree. The default * ist to do no look-ahead matching. * * @param lookAhead * number of levels to look down for subtree matches if the * currently compared nodes do not match */ public void setLookAhead(int lookAhead) { this.lookAhead = lookAhead; } /** * Sets the specific lookahead for the given type. * * @param type * the type whose lookahead is to be set * @param lookAhead * the lookahead for the type */ public void setLookAhead(KeyEnums.Type type, int lookAhead) { lookAheads.put(type, lookAhead); } /** * Returns whether conditional merging is used outside of methods. * * @return true if conditional merging is used outside of methods */ public boolean isConditionalOutsideMethods() { return conditionalOutsideMethods; } /** * Sets whether conditional merging is used outside of methods. * * @param conditionalOutsideMethods * use conditional merging outside of methods */ public void setConditionalOutsideMethods(boolean conditionalOutsideMethods) { this.conditionalOutsideMethods = conditionalOutsideMethods; } /** * Returns the list of <code>MergeScenario</code>s on which JDime crashed. * * @return list of merge scenarios that crashed */ public Map<MergeScenario<?>, Throwable> getCrashes() { return crashes; } /** * Add a <code>MergeScenario</code> to the list of crashed scenarios. * * @param scenario * <code>MergeScenario</code> which crashed */ public void addCrash(MergeScenario<?> scenario, Throwable t) { crashes.put(scenario, t); } /** * Returns whether to use the <code>MCESubtreeMatcher</code> during the matching phase of the merge. * * @return true iff the matcher should be used */ public boolean isUseMCESubtreeMatcher() { return useMCESubtreeMatcher; } /** * Sets whether to use the <code>MCESubtreeMatcher</code>. * * @param useMCESubtreeMatcher * the new value */ public void setUseMCESubtreeMatcher(boolean useMCESubtreeMatcher) { this.useMCESubtreeMatcher = useMCESubtreeMatcher; } /** * Returns the number of the artifact that should be inspected. * * @return number of artifact that should be inspected */ public int getInspectArtifact() { return inspectArtifact; } /** * Returns the scope of inspection. * * @return scope of inspection */ public KeyEnums.Type getInspectionScope() { return inspectionScope; } /** * Sets the artifact that should be inspected. * If this is set, no merge will be executed. * * @param inspectArtifact number of the artifact that should be inspected. */ public void setInspectArtifact(int inspectArtifact) { this.inspectArtifact = inspectArtifact; } /** * Sets the scope of inspection. * * @param scope scope of inspection */ public void setInspectionScope(KeyEnums.Type scope) { this.inspectionScope = scope; } /** * Whether to inspect an artifact instead of merging. */ public boolean isInspect() { return inspectArtifact > 0; } public CMMode getCMMatcherMode() { return cmMatcherMode; } public void setCmMatcherMode(CMMode cmMatcher) { this.cmMatcherMode = cmMatcher; } public float getCmReMatchBound() { return cmReMatchBound; } public void setCmReMatchBound(float cmReMatchBound) { this.cmReMatchBound = cmReMatchBound; } public float getWr() { return wr; } public void setWr(float wr) { this.wr = wr; } public float getWn() { return wn; } public void setWn(float wn) { this.wn = wn; } public float getWa() { return wa; } public void setWa(float wa) { this.wa = wa; } public float getWs() { return ws; } public void setWs(float ws) { this.ws = ws; } public float getWo() { return wo; } public void setWo(float wo) { this.wo = wo; } public float getpAssign() { return pAssign; } public void setpAssign(float pAssign) { this.pAssign = pAssign; } public float getFixLower() { return fixLower; } public void setFixLower(float fixLower) { this.fixLower = fixLower; } public float getFixUpper() { return fixUpper; } public void setFixUpper(float fixUpper) { this.fixUpper = fixUpper; } public Optional<Long> getSeed() { return seed; } public void setSeed(Optional<Long> seed) { this.seed = seed; } public int getCostModelIterations() { return costModelIterations; } public void setCostModelIterations(int costModelIterations) { this.costModelIterations = costModelIterations; } public boolean isCmMatcherParallel() { return cmMatcherParallel; } public void setCmMatcherParallel(boolean cmMatcherParallel) { this.cmMatcherParallel = cmMatcherParallel; } public boolean isCmMatcherFixRandomPercentage() { return cmMatcherFixRandomPercentage; } public void setCmMatcherFixRandomPercentage(boolean cmMatcherFixRandomPercentage) { this.cmMatcherFixRandomPercentage = cmMatcherFixRandomPercentage; } }