1 /*
2  * This file is part of ELKI:
3  * Environment for Developing KDD-Applications Supported by Index-Structures
4  *
5  * Copyright (C) 2018
6  * ELKI Development Team
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Affero General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU Affero General Public License for more details.
17  *
18  * You should have received a copy of the GNU Affero General Public License
19  * along with this program. If not, see <http://www.gnu.org/licenses/>.
20  */
21 package de.lmu.ifi.dbs.elki.result;
22 
23 import java.io.File;
24 import java.io.IOException;
25 import java.util.regex.Pattern;
26 
27 import de.lmu.ifi.dbs.elki.logging.Logging;
28 import de.lmu.ifi.dbs.elki.result.textwriter.MultipleFilesOutput;
29 import de.lmu.ifi.dbs.elki.result.textwriter.SingleStreamOutput;
30 import de.lmu.ifi.dbs.elki.result.textwriter.StreamFactory;
31 import de.lmu.ifi.dbs.elki.result.textwriter.TextWriter;
32 import de.lmu.ifi.dbs.elki.utilities.Priority;
33 import de.lmu.ifi.dbs.elki.utilities.io.FileUtil;
34 import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
35 import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
36 import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
37 import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.FileParameter;
38 import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Flag;
39 import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.PatternParameter;
40 import de.lmu.ifi.dbs.elki.workflow.OutputStep;
41 
42 /**
43  * Result handler that feeds the data into a TextWriter.
44  * <p>
45  * Note: these classes need to be rewritten. Contributions welcome!
46  *
47  * @author Erich Schubert
48  * @since 0.2
49  */
50 @Priority(Priority.IMPORTANT)
51 public class ResultWriter implements ResultHandler {
52   /**
53    * The logger for this class.
54    */
55   private static final Logging LOG = Logging.getLogger(ResultWriter.class);
56 
57   /**
58    * Holds the file to print results to.
59    */
60   private File out;
61 
62   /**
63    * Whether or not to do gzip compression on output.
64    */
65   private boolean gzip = false;
66 
67   /**
68    * Whether or not to warn on overwrite
69    */
70   private boolean warnoverwrite = true;
71 
72   /**
73    * Result filter pattern. Optional!
74    */
75   private Pattern filter = null;
76 
77   /**
78    * Constructor.
79    *
80    * @param out Output file
81    * @param gzip Gzip compression
82    * @param warnoverwrite Warn before overwriting files
83    * @param filter Filter pattern
84    */
ResultWriter(File out, boolean gzip, boolean warnoverwrite, Pattern filter)85   public ResultWriter(File out, boolean gzip, boolean warnoverwrite, Pattern filter) {
86     super();
87     this.out = out;
88     this.gzip = gzip;
89     this.warnoverwrite = warnoverwrite;
90     this.filter = filter;
91   }
92 
93   @Override
processNewResult(ResultHierarchy hier, Result result)94   public void processNewResult(ResultHierarchy hier, Result result) {
95     TextWriter writer = new TextWriter();
96 
97     try (StreamFactory output = openStreamFactory()) {
98       writer.output(ResultUtil.findDatabase(hier), result, output, filter);
99     }
100     catch(IOException e) {
101       throw new IllegalStateException("Input/Output error while writing result.", e);
102     }
103   }
104 
openStreamFactory()105   private StreamFactory openStreamFactory() throws IOException {
106     if(out == null) {
107       return new SingleStreamOutput(gzip);
108     }
109     // If it does not exist, make a folder.
110     final String ext = FileUtil.getFilenameExtension(out);
111     if(!(out.exists() || "gz".equals(ext) || "csv".equals(ext) || "ascii".equals(ext) || "txt".equals(ext))) {
112       LOG.info("Creating output directory: " + out);
113       out.mkdirs();
114     }
115     if(out.isDirectory()) {
116       if(warnoverwrite && out.listFiles().length > 0) {
117         LOG.warning("Output directory specified is not empty. Files will be overwritten and old files may be left over.");
118       }
119       return new MultipleFilesOutput(out, gzip);
120     }
121     else {
122       if(warnoverwrite && out.exists() && out.length() > 0) {
123         LOG.warning("Output file exists and will be overwritten!");
124       }
125       return new SingleStreamOutput(out, gzip);
126     }
127   }
128 
129   /**
130    * Parameterization class.
131    *
132    * @author Erich Schubert
133    */
134   public static class Parameterizer extends AbstractParameterizer {
135     /**
136      * Flag to control GZIP compression.
137      */
138     public static final OptionID GZIP_OUTPUT_ID = new OptionID("out.gzip", "Enable gzip compression of output files.");
139 
140     /**
141      * Flag to suppress overwrite warning.
142      */
143     public static final OptionID OVERWRITE_OPTION_ID = new OptionID("out.silentoverwrite", "Silently overwrite output files.");
144 
145     /**
146      * Pattern to filter the output
147      */
148     public static final OptionID FILTER_PATTERN_ID = new OptionID("out.filter", "Filter pattern for output selection. Only output streams that match the given pattern will be written.");
149 
150     /**
151      * Holds the file to print results to.
152      */
153     private File out = null;
154 
155     /**
156      * Whether or not to do gzip compression on output.
157      */
158     private boolean gzip = false;
159 
160     /**
161      * Whether or not to warn on overwrite
162      */
163     private boolean warnoverwrite = true;
164 
165     /**
166      * Result filter pattern. Optional!
167      */
168     private Pattern filter = null;
169 
170     @Override
makeOptions(Parameterization config)171     protected void makeOptions(Parameterization config) {
172       super.makeOptions(config);
173       FileParameter outputP = new FileParameter(OutputStep.Parameterizer.OUTPUT_ID, FileParameter.FileType.OUTPUT_FILE, true);
174       if(config.grab(outputP)) {
175         out = outputP.getValue();
176       }
177 
178       Flag gzipF = new Flag(GZIP_OUTPUT_ID);
179       if(config.grab(gzipF)) {
180         gzip = gzipF.getValue();
181       }
182 
183       Flag overwriteF = new Flag(OVERWRITE_OPTION_ID);
184       if(config.grab(overwriteF)) {
185         // note: inversed meaning
186         warnoverwrite = !overwriteF.getValue();
187       }
188 
189       PatternParameter filterP = new PatternParameter(FILTER_PATTERN_ID) //
190           .setOptional(true);
191       if(config.grab(filterP)) {
192         filter = filterP.getValue();
193       }
194     }
195 
196     @Override
makeInstance()197     protected ResultWriter makeInstance() {
198       return new ResultWriter(out, gzip, warnoverwrite, filter);
199     }
200   }
201 }
202