source: trunk/CrossPare/src/de/ugoe/cs/cpdp/ExperimentConfiguration.java

Last change on this file was 136, checked in by sherbold, 8 years ago
  • more code documentation
  • Property svn:mime-type set to text/plain
File size: 26.5 KB
Line 
1// Copyright 2015 Georg-August-Universität Göttingen, Germany
2//
3//   Licensed under the Apache License, Version 2.0 (the "License");
4//   you may not use this file except in compliance with the License.
5//   You may obtain a copy of the License at
6//
7//       http://www.apache.org/licenses/LICENSE-2.0
8//
9//   Unless required by applicable law or agreed to in writing, software
10//   distributed under the License is distributed on an "AS IS" BASIS,
11//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//   See the License for the specific language governing permissions and
13//   limitations under the License.
14
15package de.ugoe.cs.cpdp;
16
17import java.io.File;
18import java.io.FileInputStream;
19import java.io.FileNotFoundException;
20import java.io.IOException;
21import java.io.InputStreamReader;
22import java.io.UnsupportedEncodingException;
23import java.util.LinkedList;
24import java.util.List;
25import java.util.logging.Level;
26
27import javax.xml.parsers.ParserConfigurationException;
28import javax.xml.parsers.SAXParser;
29import javax.xml.parsers.SAXParserFactory;
30
31import org.xml.sax.Attributes;
32import org.xml.sax.InputSource;
33import org.xml.sax.SAXException;
34import org.xml.sax.helpers.DefaultHandler;
35
36import de.ugoe.cs.cpdp.dataprocessing.IProcessesingStrategy;
37import de.ugoe.cs.cpdp.dataprocessing.ISetWiseProcessingStrategy;
38import de.ugoe.cs.cpdp.dataselection.IPointWiseDataselectionStrategy;
39import de.ugoe.cs.cpdp.dataselection.ISetWiseDataselectionStrategy;
40import de.ugoe.cs.cpdp.eval.IEvaluationStrategy;
41import de.ugoe.cs.cpdp.eval.IResultStorage;
42import de.ugoe.cs.cpdp.loader.IVersionLoader;
43import de.ugoe.cs.cpdp.training.ISetWiseTestdataAwareTrainingStrategy;
44import de.ugoe.cs.cpdp.training.ISetWiseTrainingStrategy;
45import de.ugoe.cs.cpdp.training.ITestAwareTrainingStrategy;
46import de.ugoe.cs.cpdp.training.ITrainingStrategy;
47import de.ugoe.cs.cpdp.versions.IVersionFilter;
48import de.ugoe.cs.util.StringTools;
49import de.ugoe.cs.util.console.Console;
50
51/**
52 * Class that contains all meta information about an experiment, i.e., its configuration. The
53 * configuration is loaded from an XML file. <br>
54 * <br>
55 * In the current implementation, the experiment configuration can only be created using an XML
56 * file. Programmatic creation of experiment configurations is currently not possibly.
57 *
58 * @author Steffen Herbold
59 */
60public class ExperimentConfiguration extends DefaultHandler {
61
62    /**
63     * handle of the file that contains the configuration
64     */
65    private final File configFile;
66
67    /**
68     * name of the experiment (automatically set to the file name without the .xml ending)
69     */
70    private String experimentName = "exp";
71
72    /**
73     * loads instances
74     */
75    private List<IVersionLoader> loaders;
76
77    /**
78     * path were the results of the experiments are stored
79     */
80    private String resultsPath = "results";
81
82    /**
83     * data set filters applied to all data
84     */
85    private List<IVersionFilter> versionFilters;
86
87    /**
88     * data set filters that decide if a data set is used as test data
89     */
90    private List<IVersionFilter> testVersionFilters;
91
92    /**
93     * data set filters that decide if a data is used as candidate training data
94     */
95    private List<IVersionFilter> trainingVersionFilters;
96
97    /**
98     * setwise data processors that are applied before the setwise data selection
99     */
100    private List<ISetWiseProcessingStrategy> setwisepreprocessors;
101
102    /**
103     * setwise data selection strategies
104     */
105    private List<ISetWiseDataselectionStrategy> setwiseselectors;
106
107    /**
108     * setwise data processors that are applied after the setwise data selection
109     */
110    private List<ISetWiseProcessingStrategy> setwisepostprocessors;
111
112    /**
113     * setwise trainers, i.e., trainers that require the selected training data to be separate from
114     * each other
115     */
116    private List<ISetWiseTrainingStrategy> setwiseTrainers;
117
118    /**
119     * setwise testdata aware trainers, i.e., trainers that require the selected training data to be
120     * separate from each other and the current testdata
121     */
122    private List<ISetWiseTestdataAwareTrainingStrategy> setwiseTestdataAwareTrainers;
123
124    /**
125     * data processors that are applied before the pointwise data selection
126     */
127    private List<IProcessesingStrategy> preprocessors;
128
129    /**
130     * pointwise data selection strategies
131     */
132    private List<IPointWiseDataselectionStrategy> pointwiseselectors;
133
134    /**
135     * data processors that are applied before the pointwise data selection
136     */
137    private List<IProcessesingStrategy> postprocessors;
138
139    /**
140     * normal trainers, i.e., trainers that require the selected training data in a single data set
141     */
142    private List<ITrainingStrategy> trainers;
143
144    /**
145     * normal trainers, i.e., trainers that require the selected training data in a single data set
146     */
147    private List<ITestAwareTrainingStrategy> testAwareTrainers;
148
149    /**
150     * evaluators used for the the experiment results
151     */
152    private List<IEvaluationStrategy> evaluators;
153
154    /**
155     * result storages used for experiments
156     */
157    private List<IResultStorage> resultStorages;
158
159    /**
160     * indicates, if the classifier should be saved
161     */
162    private Boolean saveClassifier = null;
163
164    /**
165     * number of repetitions of an experiment (to account for randomness)
166     */
167    private int repetitions = 1;
168
169    /**
170     * indicates, which execution strategy to choose (e.g. CrossProjectExperiment,
171     * ClassifierCreationExecution). Default is CrossProjectExperiment.
172     */
173    private String executionStrategy = "CrossProjectExperiment";
174
175    /**
176     * Constructor. Creates a new configuration from a given file.
177     *
178     * @param filename
179     *            name of the file from the configuration is loaded.
180     * @throws ExperimentConfigurationException
181     *             thrown if there is an error creating the configuration
182     */
183    public ExperimentConfiguration(String filename) throws ExperimentConfigurationException {
184        this(new File(filename));
185    }
186
187    /**
188     * Constructor. Creates a new configuration from a given file.
189     *
190     * @param file
191     *            handle of the file from the configuration is loaded.
192     * @throws ExperimentConfigurationException
193     *             thrown if there is an error creating the configuration
194     */
195    public ExperimentConfiguration(File file) throws ExperimentConfigurationException {
196        loaders = new LinkedList<>();
197        versionFilters = new LinkedList<>();
198        testVersionFilters = new LinkedList<>();
199        trainingVersionFilters = new LinkedList<>();
200        setwisepreprocessors = new LinkedList<>();
201        setwiseselectors = new LinkedList<>();
202        setwisepostprocessors = new LinkedList<>();
203        setwiseTrainers = new LinkedList<>();
204        setwiseTestdataAwareTrainers = new LinkedList<>();
205        preprocessors = new LinkedList<>();
206        pointwiseselectors = new LinkedList<>();
207        postprocessors = new LinkedList<>();
208        trainers = new LinkedList<>();
209        testAwareTrainers = new LinkedList<>();
210        evaluators = new LinkedList<>();
211        resultStorages = new LinkedList<>();
212
213        if (file == null) {
214            throw new IllegalArgumentException("file must not be null");
215        }
216        if (file.isDirectory()) {
217            throw new IllegalArgumentException("file must not be a directory");
218        }
219        configFile = file;
220
221        experimentName = file.getName().split("\\.")[0];
222
223        final SAXParserFactory spf = SAXParserFactory.newInstance();
224        spf.setValidating(true);
225
226        SAXParser saxParser = null;
227        InputSource inputSource = null;
228        try {
229            saxParser = spf.newSAXParser();
230        }
231        catch (ParserConfigurationException | SAXException e) {
232            throw new ExperimentConfigurationException(e);
233        }
234
235        InputStreamReader reader = null;
236        try {
237            reader = new InputStreamReader(new FileInputStream(file), "UTF-8");
238            inputSource = new InputSource(reader);
239        }
240        catch (UnsupportedEncodingException | FileNotFoundException e) {
241            throw new ExperimentConfigurationException("Could not open configuration file.", e);
242        }
243
244        if (inputSource != null) {
245            inputSource.setSystemId("file://" + file.getAbsolutePath());
246            try {
247                saxParser.parse(inputSource, this);
248            }
249            catch (SAXException | IOException e) {
250                throw new ExperimentConfigurationException("Error parsing configuration.", e);
251            }
252        }
253        if (reader != null) {
254            try {
255                reader.close();
256            }
257            catch (IOException e) {
258                throw new ExperimentConfigurationException("Error closing reader.", e);
259            }
260        }
261    }
262
263    /**
264     * returns the name of the experiment
265     *
266     * @return name of the experiment
267     */
268    public String getExperimentName() {
269        return experimentName;
270    }
271
272    /**
273     * returns the loaders for instances
274     *
275     * @return data loaders
276     */
277    public List<IVersionLoader> getLoaders() {
278        return loaders;
279    }
280
281    /**
282     * returns the results path
283     *
284     * @return results path
285     */
286    public String getResultsPath() {
287        return resultsPath;
288    }
289
290    /**
291     * returns the data set filters of the experiment
292     *
293     * @return data set filters of the experiment
294     */
295    public List<IVersionFilter> getVersionFilters() {
296        return versionFilters;
297    }
298
299    /**
300     * returns the test set filters of the experiment
301     *
302     * @return test set filters of the experiment
303     */
304    public List<IVersionFilter> getTestVersionFilters() {
305        return testVersionFilters;
306    }
307
308    /**
309     * returns the candidate training version filters of the experiment
310     *
311     * @return candidate training version filters of the experiment
312     */
313    public List<IVersionFilter> getTrainingVersionFilters() {
314        return trainingVersionFilters;
315    }
316
317    /**
318     * returns the setwise processors applied before the setwise data selection
319     *
320     * @return setwise processors applied before the setwise data selection
321     */
322    public List<ISetWiseProcessingStrategy> getSetWisePreprocessors() {
323        return setwisepreprocessors;
324    }
325
326    /**
327     * returns the setwise data selection strategies
328     *
329     * @return setwise data selection strategies
330     */
331    public List<ISetWiseDataselectionStrategy> getSetWiseSelectors() {
332        return setwiseselectors;
333    }
334
335    /**
336     * returns the setwise processors applied after the setwise data selection
337     *
338     * @return setwise processors applied after the setwise data selection
339     */
340    public List<ISetWiseProcessingStrategy> getSetWisePostprocessors() {
341        return setwisepostprocessors;
342    }
343
344    /**
345     * returns the setwise training algorithms
346     *
347     * @return setwise training algorithms
348     */
349    public List<ISetWiseTrainingStrategy> getSetWiseTrainers() {
350        return setwiseTrainers;
351    }
352
353    /**
354     * returns the setwise training algorithms
355     *
356     * @return setwise training algorithms
357     */
358    public List<ISetWiseTestdataAwareTrainingStrategy> getSetWiseTestdataAwareTrainers() {
359        return setwiseTestdataAwareTrainers;
360    }
361
362    /**
363     * returns the processors applied before the pointwise data selection
364     *
365     * @return processors applied before the pointwise data selection
366     */
367    public List<IProcessesingStrategy> getPreProcessors() {
368        return preprocessors;
369    }
370
371    /**
372     * returns the pointwise data selection strategies
373     *
374     * @return pointwise data selection strategies
375     */
376    public List<IPointWiseDataselectionStrategy> getPointWiseSelectors() {
377        return pointwiseselectors;
378    }
379
380    /**
381     * returns the processors applied after the pointwise data selection
382     *
383     * @return processors applied after the pointwise data selection
384     */
385    public List<IProcessesingStrategy> getPostProcessors() {
386        return postprocessors;
387    }
388
389    /**
390     * returns the normal training algorithm
391     *
392     * @return normal training algorithms
393     */
394    public List<ITrainingStrategy> getTrainers() {
395        return trainers;
396    }
397
398    /**
399     * returns the test aware training algorithms
400     *
401     * @return normal training algorithms
402     */
403    public List<ITestAwareTrainingStrategy> getTestAwareTrainers() {
404        return testAwareTrainers;
405    }
406
407    /**
408     * returns the evaluation strategies
409     *
410     * @return evaluation strategies
411     */
412    public List<IEvaluationStrategy> getEvaluators() {
413        return evaluators;
414    }
415
416    /**
417     * <p>
418     * returns the result storages
419     * </p>
420     *
421     * @return result storages
422     */
423    public List<IResultStorage> getResultStorages() {
424        return resultStorages;
425    }
426
427    /**
428     * returns boolean, if classifier should be saved
429     *
430     * @return boolean
431     */
432    public boolean getSaveClassifier() {
433        return saveClassifier;
434    }
435
436    /**
437     * number of repetitions of an experiment
438     *
439     * @return number of repetitions
440     */
441    public int getRepetitions() {
442        return repetitions;
443    }
444
445    /**
446     * returns the execution strategy
447     *
448     * @return String execution strategy
449     */
450    public String getExecutionStrategy() {
451        return executionStrategy;
452    }
453
454    /*
455     * (non-Javadoc)
456     *
457     * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String,
458     * java.lang.String, org.xml.sax.Attributes)
459     */
460    @Override
461    public void startElement(String uri, String localName, String qName, Attributes attributes)
462        throws SAXException
463    {
464        try {
465            if (qName.equals("config")) {
466                // ingore
467            }
468            else if (qName.equals("loader")) {
469                final IVersionLoader loader = (IVersionLoader) Class
470                    .forName("de.ugoe.cs.cpdp.loader." + attributes.getValue("name")).newInstance();
471                loader.setLocation(attributes.getValue("datalocation"));
472                loaders.add(loader);
473
474                // TODO location as relative
475            }
476            else if (qName.equals("resultspath")) {
477                resultsPath = attributes.getValue("path");
478            }
479            else if (qName.equals("versionfilter")) {
480                final IVersionFilter filter = (IVersionFilter) Class
481                    .forName("de.ugoe.cs.cpdp.versions." + attributes.getValue("name"))
482                    .newInstance();
483                filter.setParameter(attributes.getValue("param"));
484                versionFilters.add(filter);
485            }
486            else if (qName.equals("testVersionfilter")) {
487                final IVersionFilter filter = (IVersionFilter) Class
488                    .forName("de.ugoe.cs.cpdp.versions." + attributes.getValue("name"))
489                    .newInstance();
490                filter.setParameter(attributes.getValue("param"));
491                testVersionFilters.add(filter);
492            }
493            else if (qName.equals("trainVersionfilter")) {
494                final IVersionFilter filter = (IVersionFilter) Class
495                    .forName("de.ugoe.cs.cpdp.versions." + attributes.getValue("name"))
496                    .newInstance();
497                filter.setParameter(attributes.getValue("param"));
498                trainingVersionFilters.add(filter);
499            }
500            else if (qName.equals("setwisepreprocessor")) {
501                final ISetWiseProcessingStrategy processor = (ISetWiseProcessingStrategy) Class
502                    .forName("de.ugoe.cs.cpdp.dataprocessing." + attributes.getValue("name"))
503                    .newInstance();
504                processor.setParameter(attributes.getValue("param"));
505                setwisepreprocessors.add(processor);
506            }
507            else if (qName.equals("setwiseselector")) {
508                final ISetWiseDataselectionStrategy selection =
509                    (ISetWiseDataselectionStrategy) Class
510                        .forName("de.ugoe.cs.cpdp.dataselection." + attributes.getValue("name"))
511                        .newInstance();
512                selection.setParameter(attributes.getValue("param"));
513                setwiseselectors.add(selection);
514            }
515            else if (qName.equals("setwisepostprocessor")) {
516                final ISetWiseProcessingStrategy processor = (ISetWiseProcessingStrategy) Class
517                    .forName("de.ugoe.cs.cpdp.dataprocessing." + attributes.getValue("name"))
518                    .newInstance();
519                processor.setParameter(attributes.getValue("param"));
520                setwisepostprocessors.add(processor);
521            }
522            else if (qName.equals("setwisetrainer")) {
523                final ISetWiseTrainingStrategy trainer = (ISetWiseTrainingStrategy) Class
524                    .forName("de.ugoe.cs.cpdp.training." + attributes.getValue("name"))
525                    .newInstance();
526                trainer.setParameter(attributes.getValue("param"));
527                setwiseTrainers.add(trainer);
528            }
529            else if (qName.equals("setwisetestdataawaretrainer")) {
530                final ISetWiseTestdataAwareTrainingStrategy trainer =
531                    (ISetWiseTestdataAwareTrainingStrategy) Class
532                        .forName("de.ugoe.cs.cpdp.training." + attributes.getValue("name"))
533                        .newInstance();
534                trainer.setParameter(attributes.getValue("param"));
535                trainer.setMethod(attributes.getValue("method"));
536                trainer.setThreshold(attributes.getValue("threshold"));
537                setwiseTestdataAwareTrainers.add(trainer);
538            }
539            else if (qName.equals("preprocessor")) {
540                final IProcessesingStrategy processor = (IProcessesingStrategy) Class
541                    .forName("de.ugoe.cs.cpdp.dataprocessing." + attributes.getValue("name"))
542                    .newInstance();
543                processor.setParameter(attributes.getValue("param"));
544                preprocessors.add(processor);
545            }
546            else if (qName.equals("pointwiseselector")) {
547                final IPointWiseDataselectionStrategy selection =
548                    (IPointWiseDataselectionStrategy) Class
549                        .forName("de.ugoe.cs.cpdp.dataselection." + attributes.getValue("name"))
550                        .newInstance();
551                selection.setParameter(attributes.getValue("param"));
552                pointwiseselectors.add(selection);
553            }
554            else if (qName.equals("postprocessor")) {
555                final IProcessesingStrategy processor = (IProcessesingStrategy) Class
556                    .forName("de.ugoe.cs.cpdp.dataprocessing." + attributes.getValue("name"))
557                    .newInstance();
558                processor.setParameter(attributes.getValue("param"));
559                postprocessors.add(processor);
560            }
561            else if (qName.equals("trainer")) {
562                final ITrainingStrategy trainer = (ITrainingStrategy) Class
563                    .forName("de.ugoe.cs.cpdp.training." + attributes.getValue("name"))
564                    .newInstance();
565                trainer.setParameter(attributes.getValue("param"));
566                trainers.add(trainer);
567            }
568            else if (qName.equals("testawaretrainer")) {
569                final ITestAwareTrainingStrategy trainer = (ITestAwareTrainingStrategy) Class
570                    .forName("de.ugoe.cs.cpdp.training." + attributes.getValue("name"))
571                    .newInstance();
572                trainer.setParameter(attributes.getValue("param"));
573                testAwareTrainers.add(trainer);
574            }
575            else if (qName.equals("eval")) {
576                final IEvaluationStrategy evaluator = (IEvaluationStrategy) Class
577                    .forName("de.ugoe.cs.cpdp.eval." + attributes.getValue("name")).newInstance();
578                evaluators.add(evaluator);
579            }
580            else if (qName.equals("storage")) {
581                final IResultStorage resultStorage = (IResultStorage) Class
582                    .forName("de.ugoe.cs.cpdp.eval." + attributes.getValue("name")).newInstance();
583                resultStorages.add(resultStorage);
584            }
585            else if (qName.equals("saveClassifier")) {
586                saveClassifier = true;
587            }
588            else if (qName.equals("repetitions")) {
589                repetitions = Integer.parseInt(attributes.getValue("number"));
590            }
591            else if (qName.equals("executionStrategy")) {
592                executionStrategy = attributes.getValue("name");
593            }
594            else if (qName.equals("partialconfig")) {
595                String path = attributes.getValue("path");
596                try {
597                    boolean relative = true;
598                    if (attributes.getValue("relative") != null) {
599                        relative = Boolean.parseBoolean(attributes.getValue("relative"));
600                    }
601
602                    if (relative) {
603                        path = configFile.getParentFile().getPath() + "/" + path;
604                    }
605                    addConfigurations(new ExperimentConfiguration(path));
606                }
607                catch (ExperimentConfigurationException e) {
608                    throw new SAXException("Could not load partial configuration: " + path, e);
609                }
610            }
611            else {
612                Console.traceln(Level.WARNING, "element in config-file " + configFile.getName() +
613                    " ignored: " + qName);
614            }
615        }
616        catch (NoClassDefFoundError | ClassNotFoundException | IllegalAccessException
617                | InstantiationException | ClassCastException e)
618        {
619            throw new SAXException("Could not initialize class correctly", (Exception) e);
620        }
621    }
622
623    /**
624     * Adds the information of another experiment configuration to this configuration. This
625     * mechanism allows the usage of partial configuration files. The name of the other
626     * configuration is lost. <br>
627     * <br>
628     * If the current data path is the empty string (&quot;&quot;), it is override by the datapath
629     * of the other configuration. Otherwise, the current data path is kept.
630     *
631     * @param other
632     *            experiment whose information is added
633     * @throws ExperimentConfigurationException
634     */
635    private void addConfigurations(ExperimentConfiguration other)
636        throws ExperimentConfigurationException
637    {
638        if ("results".equals(resultsPath)) {
639            resultsPath = other.resultsPath;
640        }
641        loaders.addAll(other.loaders);
642        versionFilters.addAll(other.versionFilters);
643        testVersionFilters.addAll(other.testVersionFilters);
644        trainingVersionFilters.addAll(other.trainingVersionFilters);
645        setwisepreprocessors.addAll(other.setwisepreprocessors);
646        setwiseselectors.addAll(other.setwiseselectors);
647        setwisepostprocessors.addAll(other.setwisepostprocessors);
648        setwiseTrainers.addAll(other.setwiseTrainers);
649        setwiseTestdataAwareTrainers.addAll(other.setwiseTestdataAwareTrainers);
650        preprocessors.addAll(other.preprocessors);
651        pointwiseselectors.addAll(other.pointwiseselectors);
652        postprocessors.addAll(other.postprocessors);
653        trainers.addAll(other.trainers);
654        evaluators.addAll(other.evaluators);
655
656        if (!executionStrategy.equals(other.executionStrategy)) {
657            throw new ExperimentConfigurationException("Executionstrategies must be the same, if config files should be added.");
658        }
659
660        /*
661         * Only if saveClassifier is not set in the main config and the other configs saveClassifier
662         * is true, it must be set.
663         */
664        if (saveClassifier == null && other.saveClassifier == true) {
665            saveClassifier = other.saveClassifier;
666        }
667
668    }
669
670    /*
671     * (non-Javadoc)
672     *
673     * @see java.lang.Object#toString()
674     */
675    @Override
676    public String toString() {
677        final StringBuilder builder = new StringBuilder();
678        builder.append("Experiment name: " + experimentName + StringTools.ENDLINE);
679        builder.append("Loaders: " + loaders + StringTools.ENDLINE);
680        builder.append("Results path: " + resultsPath + StringTools.ENDLINE);
681        builder.append("Version filters: " + versionFilters.toString() + StringTools.ENDLINE);
682        builder
683            .append("Test version filters: " + testVersionFilters.toString() + StringTools.ENDLINE);
684        builder.append("Training version filters: " + trainingVersionFilters.toString() +
685            StringTools.ENDLINE);
686        builder.append("Setwise preprocessors: " + setwisepreprocessors.toString() +
687            StringTools.ENDLINE);
688        builder.append("Setwise selectors: " + setwiseselectors.toString() + StringTools.ENDLINE);
689        builder.append("Setwise postprocessors: " + setwisepostprocessors.toString() +
690            StringTools.ENDLINE);
691        builder.append("Setwise trainers: " + setwiseTrainers.toString() + StringTools.ENDLINE);
692        builder.append("Setwise Testdata Aware trainers: " +
693            setwiseTestdataAwareTrainers.toString() + StringTools.ENDLINE);
694        builder
695            .append("Pointwise preprocessors: " + preprocessors.toString() + StringTools.ENDLINE);
696        builder
697            .append("Pointwise selectors: " + pointwiseselectors.toString() + StringTools.ENDLINE);
698        builder
699            .append("Pointwise postprocessors: " + postprocessors.toString() + StringTools.ENDLINE);
700        builder.append("Pointwise trainers: " + trainers.toString() + StringTools.ENDLINE);
701        builder.append("Evaluators: " + evaluators.toString() + StringTools.ENDLINE);
702        builder.append("Save Classifier?: " + saveClassifier + StringTools.ENDLINE);
703        builder.append("Execution Strategy: " + executionStrategy + StringTools.ENDLINE);
704
705        return builder.toString();
706    }
707}
Note: See TracBrowser for help on using the repository browser.