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

Last change on this file since 99 was 98, checked in by sherbold, 9 years ago
  • added the new configuration parameter repetitions and implemented its usage in the AbstractCrossProjectExperiment?. It now allows for multiple storages of the same result in an IResultContainer to allow for repetitions of experiments with random components.
  • Property svn:mime-type set to text/plain
File size: 26.4 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 filename
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    public List<IResultStorage> getResultStorages() {
417        return resultStorages;
418    }
419
420    /**
421     * returns boolean, if classifier should be saved
422     *
423     * @return boolean
424     */
425    public boolean getSaveClassifier() {
426        return saveClassifier;
427    }
428   
429    /**
430     * number of repetitions of an experiment
431     *
432     * @return number of repetitions
433     */
434    public int getRepetitions() {
435        return repetitions;
436    }
437
438    /**
439     * returns the execution strategy
440     *
441     * @return String execution strategy
442     */
443    public String getExecutionStrategy() {
444        return executionStrategy;
445    }
446
447    /*
448     * (non-Javadoc)
449     *
450     * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String,
451     * java.lang.String, org.xml.sax.Attributes)
452     */
453    @Override
454    public void startElement(String uri, String localName, String qName, Attributes attributes)
455        throws SAXException
456    {
457        try {
458            if (qName.equals("config")) {
459                // ingore
460            }
461            else if (qName.equals("loader")) {
462                final IVersionLoader loader = (IVersionLoader) Class
463                    .forName("de.ugoe.cs.cpdp.loader." + attributes.getValue("name")).newInstance();
464                loader.setLocation(attributes.getValue("datalocation"));
465                loaders.add(loader);
466
467                // TODO location as relative
468            }
469            else if (qName.equals("resultspath")) {
470                resultsPath = attributes.getValue("path");
471            }
472            else if (qName.equals("versionfilter")) {
473                final IVersionFilter filter = (IVersionFilter) Class
474                    .forName("de.ugoe.cs.cpdp.versions." + attributes.getValue("name"))
475                    .newInstance();
476                filter.setParameter(attributes.getValue("param"));
477                versionFilters.add(filter);
478            }
479            else if (qName.equals("testVersionfilter")) {
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                testVersionFilters.add(filter);
485            }
486            else if (qName.equals("trainVersionfilter")) {
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                trainingVersionFilters.add(filter);
492            }
493            else if (qName.equals("setwisepreprocessor")) {
494                final ISetWiseProcessingStrategy processor = (ISetWiseProcessingStrategy) Class
495                    .forName("de.ugoe.cs.cpdp.dataprocessing." + attributes.getValue("name"))
496                    .newInstance();
497                processor.setParameter(attributes.getValue("param"));
498                setwisepreprocessors.add(processor);
499            }
500            else if (qName.equals("setwiseselector")) {
501                final ISetWiseDataselectionStrategy selection =
502                    (ISetWiseDataselectionStrategy) Class
503                        .forName("de.ugoe.cs.cpdp.dataselection." + attributes.getValue("name"))
504                        .newInstance();
505                selection.setParameter(attributes.getValue("param"));
506                setwiseselectors.add(selection);
507            }
508            else if (qName.equals("setwisepostprocessor")) {
509                final ISetWiseProcessingStrategy processor = (ISetWiseProcessingStrategy) Class
510                    .forName("de.ugoe.cs.cpdp.dataprocessing." + attributes.getValue("name"))
511                    .newInstance();
512                processor.setParameter(attributes.getValue("param"));
513                setwisepostprocessors.add(processor);
514            }
515            else if (qName.equals("setwisetrainer")) {
516                final ISetWiseTrainingStrategy trainer = (ISetWiseTrainingStrategy) Class
517                    .forName("de.ugoe.cs.cpdp.training." + attributes.getValue("name"))
518                    .newInstance();
519                trainer.setParameter(attributes.getValue("param"));
520                setwiseTrainers.add(trainer);
521            }
522            else if (qName.equals("setwisetestdataawaretrainer")) {
523                final ISetWiseTestdataAwareTrainingStrategy trainer =
524                    (ISetWiseTestdataAwareTrainingStrategy) Class
525                        .forName("de.ugoe.cs.cpdp.training." + attributes.getValue("name"))
526                        .newInstance();
527                trainer.setParameter(attributes.getValue("param"));
528                trainer.setMethod(attributes.getValue("method"));
529                trainer.setThreshold(attributes.getValue("threshold"));
530                setwiseTestdataAwareTrainers.add(trainer);
531            }
532            else if (qName.equals("preprocessor")) {
533                final IProcessesingStrategy processor = (IProcessesingStrategy) Class
534                    .forName("de.ugoe.cs.cpdp.dataprocessing." + attributes.getValue("name"))
535                    .newInstance();
536                processor.setParameter(attributes.getValue("param"));
537                preprocessors.add(processor);
538            }
539            else if (qName.equals("pointwiseselector")) {
540                final IPointWiseDataselectionStrategy selection =
541                    (IPointWiseDataselectionStrategy) Class
542                        .forName("de.ugoe.cs.cpdp.dataselection." + attributes.getValue("name"))
543                        .newInstance();
544                selection.setParameter(attributes.getValue("param"));
545                pointwiseselectors.add(selection);
546            }
547            else if (qName.equals("postprocessor")) {
548                final IProcessesingStrategy processor = (IProcessesingStrategy) Class
549                    .forName("de.ugoe.cs.cpdp.dataprocessing." + attributes.getValue("name"))
550                    .newInstance();
551                processor.setParameter(attributes.getValue("param"));
552                postprocessors.add(processor);
553            }
554            else if (qName.equals("trainer")) {
555                final ITrainingStrategy trainer = (ITrainingStrategy) Class
556                    .forName("de.ugoe.cs.cpdp.training." + attributes.getValue("name"))
557                    .newInstance();
558                trainer.setParameter(attributes.getValue("param"));
559                trainers.add(trainer);
560            }
561            else if (qName.equals("testawaretrainer")) {
562                final ITestAwareTrainingStrategy trainer = (ITestAwareTrainingStrategy) Class
563                    .forName("de.ugoe.cs.cpdp.training." + attributes.getValue("name"))
564                    .newInstance();
565                trainer.setParameter(attributes.getValue("param"));
566                testAwareTrainers.add(trainer);
567            }
568            else if (qName.equals("eval")) {
569                final IEvaluationStrategy evaluator = (IEvaluationStrategy) Class
570                    .forName("de.ugoe.cs.cpdp.eval." + attributes.getValue("name")).newInstance();
571                evaluators.add(evaluator);
572            }
573            else if (qName.equals("storage")) {
574                final IResultStorage resultStorage = (IResultStorage) Class
575                    .forName("de.ugoe.cs.cpdp.eval." + attributes.getValue("name")).newInstance();
576                resultStorages.add(resultStorage);
577            }
578            else if (qName.equals("saveClassifier")) {
579                saveClassifier = true;
580            }
581            else if( qName.equals("repetitions")) {
582                repetitions = Integer.parseInt(attributes.getValue("number"));
583            }
584            else if (qName.equals("executionStrategy")) {
585                executionStrategy = attributes.getValue("name");
586            }
587            else if (qName.equals("partialconfig")) {
588                String path = attributes.getValue("path");
589                try {
590                    boolean relative = true;
591                    if (attributes.getValue("relative") != null) {
592                        relative = Boolean.parseBoolean(attributes.getValue("relative"));
593                    }
594
595                    if (relative) {
596                        path = configFile.getParentFile().getPath() + "/" + path;
597                    }
598                    addConfigurations(new ExperimentConfiguration(path));
599                }
600                catch (ExperimentConfigurationException e) {
601                    throw new SAXException("Could not load partial configuration: " + path, e);
602                }
603            }
604            else {
605                Console.traceln(Level.WARNING, "element in config-file " + configFile.getName() +
606                    " ignored: " + qName);
607            }
608        }
609        catch (NoClassDefFoundError | ClassNotFoundException | IllegalAccessException
610                | InstantiationException | ClassCastException e)
611        {
612            throw new SAXException("Could not initialize class correctly", (Exception) e);
613        }
614    }
615
616    /**
617     * Adds the information of another experiment configuration to this configuration. This
618     * mechanism allows the usage of partial configuration files. The name of the other
619     * configuration is lost. <br>
620     * <br>
621     * If the current data path is the empty string (&quot;&quot;), it is override by the datapath
622     * of the other configuration. Otherwise, the current data path is kept.
623     *
624     * @param other
625     *            experiment whose information is added
626     * @throws ExperimentConfigurationException
627     */
628    private void addConfigurations(ExperimentConfiguration other)
629        throws ExperimentConfigurationException
630    {
631        if ("results".equals(resultsPath)) {
632            resultsPath = other.resultsPath;
633        }
634        loaders.addAll(other.loaders);
635        versionFilters.addAll(other.versionFilters);
636        testVersionFilters.addAll(other.testVersionFilters);
637        trainingVersionFilters.addAll(other.trainingVersionFilters);
638        setwisepreprocessors.addAll(other.setwisepreprocessors);
639        setwiseselectors.addAll(other.setwiseselectors);
640        setwisepostprocessors.addAll(other.setwisepostprocessors);
641        setwiseTrainers.addAll(other.setwiseTrainers);
642        setwiseTestdataAwareTrainers.addAll(other.setwiseTestdataAwareTrainers);
643        preprocessors.addAll(other.preprocessors);
644        pointwiseselectors.addAll(other.pointwiseselectors);
645        postprocessors.addAll(other.postprocessors);
646        trainers.addAll(other.trainers);
647        evaluators.addAll(other.evaluators);
648
649        if (!executionStrategy.equals(other.executionStrategy)) {
650            throw new ExperimentConfigurationException("Executionstrategies must be the same, if config files should be added.");
651        }
652
653        /*
654         * Only if saveClassifier is not set in the main config and the other configs saveClassifier
655         * is true, it must be set.
656         */
657        if (saveClassifier == null && other.saveClassifier == true) {
658            saveClassifier = other.saveClassifier;
659        }
660
661    }
662
663    /*
664     * (non-Javadoc)
665     *
666     * @see java.lang.Object#toString()
667     */
668    @Override
669    public String toString() {
670        final StringBuilder builder = new StringBuilder();
671        builder.append("Experiment name: " + experimentName + StringTools.ENDLINE);
672        builder.append("Loaders: " + loaders + StringTools.ENDLINE);
673        builder.append("Results path: " + resultsPath + StringTools.ENDLINE);
674        builder.append("Version filters: " + versionFilters.toString() + StringTools.ENDLINE);
675        builder
676            .append("Test version filters: " + testVersionFilters.toString() + StringTools.ENDLINE);
677        builder.append("Training version filters: " + trainingVersionFilters.toString() +
678            StringTools.ENDLINE);
679        builder.append("Setwise preprocessors: " + setwisepreprocessors.toString() +
680            StringTools.ENDLINE);
681        builder.append("Setwise selectors: " + setwiseselectors.toString() + StringTools.ENDLINE);
682        builder.append("Setwise postprocessors: " + setwisepostprocessors.toString() +
683            StringTools.ENDLINE);
684        builder.append("Setwise trainers: " + setwiseTrainers.toString() + StringTools.ENDLINE);
685        builder.append("Setwise Testdata Aware trainers: " +
686            setwiseTestdataAwareTrainers.toString() + StringTools.ENDLINE);
687        builder
688            .append("Pointwise preprocessors: " + preprocessors.toString() + StringTools.ENDLINE);
689        builder
690            .append("Pointwise selectors: " + pointwiseselectors.toString() + StringTools.ENDLINE);
691        builder
692            .append("Pointwise postprocessors: " + postprocessors.toString() + StringTools.ENDLINE);
693        builder.append("Pointwise trainers: " + trainers.toString() + StringTools.ENDLINE);
694        builder.append("Evaluators: " + evaluators.toString() + StringTools.ENDLINE);
695        builder.append("Save Classifier?: " + saveClassifier + StringTools.ENDLINE);
696        builder.append("Execution Strategy: " + executionStrategy + StringTools.ENDLINE);
697
698        return builder.toString();
699    }
700}
Note: See TracBrowser for help on using the repository browser.