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