001package org.andromda.core.common; 002 003import java.io.File; 004import java.io.IOException; 005import java.net.URL; 006import org.apache.commons.io.FileUtils; 007import org.apache.commons.lang.StringUtils; 008 009/** 010 * Used for writing resources for the framework. Also keeps histories of 011 * previous resources generated so that we can avoid regenerating if the 012 * generated resources are current. 013 * 014 * @author Chad Brandon 015 * @author Bob Fields 016 */ 017public class ResourceWriter 018{ 019 /** 020 * The shared instance 021 */ 022 private static final ResourceWriter instance = new ResourceWriter(); 023 024 /** 025 * Gets the shared ResourceWriter instance. Normally you'll want to retrieve 026 * the instance through this method. 027 * 028 * @return the shared instance. 029 */ 030 public static ResourceWriter instance() 031 { 032 return instance; 033 } 034 035 /** 036 * Writes the string to the file specified by the fileLocation argument. 037 * 038 * @param string the string to write to the file 039 * @param file the file to which to write. 040 * @param namespace the current namespace for which this resource is being 041 * written. 042 * @throws IOException 043 */ 044 public void writeStringToFile( 045 final String string, 046 final File file, 047 final String namespace) 048 throws IOException 049 { 050 ExceptionUtils.checkNull( 051 "file", 052 file); 053 this.writeStringToFile( 054 string, 055 file.toString(), 056 namespace, 057 true); 058 } 059 060 /** 061 * Writes the string to the file specified by the fileLocation argument. 062 * 063 * @param string the string to write to the file 064 * @param fileLocation the location of the file which to write. 065 * @throws IOException 066 */ 067 public void writeStringToFile( 068 final String string, 069 final String fileLocation) 070 throws IOException 071 { 072 this.writeStringToFile( 073 string, 074 fileLocation, 075 true); 076 } 077 078 /** 079 * Writes the string to the file specified by the fileLocation argument. 080 * 081 * @param string the string to write to the file 082 * @param file the file which to write. 083 * @throws IOException 084 */ 085 public void writeStringToFile( 086 final String string, 087 final File file) 088 throws IOException 089 { 090 this.writeStringToFile( 091 string, 092 file != null ? file.toString() : null, 093 true); 094 } 095 096 /** 097 * Writes the string to the file specified by the fileLocation argument. 098 * 099 * @param string the string to write to the file 100 * @param fileLocation the location of the file which to write. 101 * @param recordHistory whether or not the history of the file should be 102 * recorded. 103 */ 104 private void writeStringToFile( 105 final String string, 106 final String fileLocation, 107 final boolean recordHistory) 108 throws IOException 109 { 110 this.writeStringToFile( 111 string, 112 fileLocation, 113 null, 114 recordHistory); 115 } 116 117 /** 118 * Writes the string to the file specified by the fileLocation argument. 119 * 120 * @param string the string to write to the file 121 * @param fileLocation the location of the file which to write. 122 * @param namespace the current namespace for which this resource is being 123 * written. 124 * @throws IOException 125 */ 126 public void writeStringToFile( 127 final String string, 128 final String fileLocation, 129 final String namespace) 130 throws IOException 131 { 132 this.writeStringToFile( 133 string, 134 fileLocation, 135 namespace, 136 true); 137 } 138 139 /** 140 * Writes the string to the file specified by the fileLocation argument. 141 * 142 * @param string the string to write to the file 143 * @param fileLocation the location of the file which to write. 144 * @param namespace the current namespace for which this resource is being 145 * written. 146 * @param recordHistory whether or not the history of this file should be 147 * recorded. 148 * @throws IOException 149 */ 150 private void writeStringToFile( 151 String string, 152 final String fileLocation, 153 final String namespace, 154 final boolean recordHistory) 155 throws IOException 156 { 157 if (string == null) 158 { 159 string = ""; 160 } 161 ExceptionUtils.checkEmpty( 162 "fileLocation", 163 fileLocation); 164 165 ResourceUtils.makeDirectories(fileLocation); 166 final Merger merger = Merger.instance(); 167 if (merger.requiresMerge(namespace)) 168 { 169 string = Merger.instance().getMergedString( 170 string, 171 namespace); 172 } 173 174 final File file = new File(fileLocation); 175 FileUtils.writeStringToFile(file, string, this.encoding); 176 177 if (recordHistory) 178 { 179 this.recordHistory(file); 180 } 181 } 182 183 /** 184 * Writes the URL contents to a file specified by the fileLocation argument. 185 * 186 * @param url the URL to read 187 * @param fileLocation the location which to write. 188 * @throws IOException 189 */ 190 public void writeUrlToFile( 191 final URL url, 192 final String fileLocation) 193 throws IOException 194 { 195 ResourceUtils.writeUrlToFile(url, fileLocation); 196 this.recordHistory(new File(fileLocation)); 197 } 198 199 /** 200 * Stores the encoding to be used for output. 201 */ 202 private String encoding = null; 203 204 /** 205 * Sets the encoding to which all output written from this class will be 206 * written. 207 * 208 * @param encoding the encoding type (UTF-8, ISO-8859-1, etc). 209 */ 210 public void setEncoding(String encoding) 211 { 212 this.encoding = StringUtils.trimToNull(encoding); 213 } 214 215 private StringBuffer history = new StringBuffer(); 216 217 /** 218 * Resets the a history file, to write the history {@link #writeHistory()} must be called. 219 * 220 * @param modelUri used to construct the file name from the modelUri where the history is stored 221 */ 222 public void resetHistory(final String modelUri) 223 { 224 String modelFile = modelUri.replace( 225 '\\', 226 '/'); 227 int lastSlash = modelFile.lastIndexOf('/'); 228 if (lastSlash != -1) 229 { 230 modelFile = modelFile.substring( 231 lastSlash + 1, 232 modelFile.length()); 233 } 234 this.modelFile = modelFile; 235 this.history = new StringBuffer(); 236 this.writtenCount = 0; 237 } 238 239 private String modelFile = null; 240 241 /** 242 * Stores the count of the resources written over this instance's history. 243 */ 244 private long writtenCount = 0; 245 246 /** 247 * Gets the number of currently written resources over the course of this instance's history. 248 * 249 * @return the number of written resources. 250 */ 251 public long getWrittenCount() 252 { 253 return this.writtenCount; 254 } 255 256 /** 257 * The location to which history is written. 258 */ 259 //private static final String HISTORY_LOCATION = Constants.TEMPORARY_DIRECTORY + "history/"; 260 private String historyDir = null; 261 262 /** 263 * Gets the file history storage location. 264 * @return model generation history storage location 265 */ 266 public String getHistoryStorage() 267 { 268 return this.historyDir + '/' + this.modelFile; 269 } 270 271 /** 272 * Sets the file history storage location. 273 * @param historyDirIn the history file storage location 274 */ 275 public void setHistoryStorage(String historyDirIn) 276 { 277 this.historyDir = historyDirIn; 278 } 279 280 /** 281 * Writes the output history to disk. 282 * 283 * @throws IOException 284 */ 285 public void writeHistory() 286 throws IOException 287 { 288 writeStringToFile( 289 history.toString(), 290 getHistoryStorage(), 291 false); 292 } 293 294 /** 295 * Writes the string to the file specified by the fileLocation argument. 296 * 297 * @param file the file to which to record the history to 298 */ 299 private void recordHistory(File file) 300 { 301 // Resource files may be merged multiple times to the temp directory, before the final output file is written 302 if (this.history != null && !file.getName().endsWith(".vsl")) 303 { 304 this.history.append(file).append(','); 305 } 306 this.writtenCount++; 307 } 308 309 /** 310 * Checks to see if the history is before the given <code>time</code>. 311 * 312 * @param time the time in milliseconds to check against. 313 * @return true/false 314 */ 315 public boolean isHistoryBefore(long time) 316 { 317 boolean before = true; 318 try 319 { 320 final File historyFile = new File(getHistoryStorage()); 321 if (historyFile.exists() && historyFile.lastModified() >= time) 322 { 323 final String history = ResourceUtils.getContents(new File(getHistoryStorage()).toURI().toURL()); 324 final String[] fileNames = history.split(","); 325 long lastModified = 0; 326 for (String fileName : fileNames) 327 { 328 if (StringUtils.isNotBlank(fileName)) 329 { 330 File file = new File(fileName.trim()); 331 332 // if we find one file that doesn't exist then 333 // before is automatically false 334 if (!file.exists()) 335 { 336 lastModified = 0; 337 break; 338 } 339 if (file.lastModified() > lastModified) 340 { 341 lastModified = file.lastModified(); 342 } 343 } 344 } 345 before = time > lastModified; 346 } 347 } 348 catch (IOException ex) 349 { 350 before = true; 351 } 352 return before; 353 } 354}