1 package org.andromda.core.common;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.net.URL;
6 import org.apache.commons.io.FileUtils;
7 import org.apache.commons.lang.StringUtils;
8
9 /**
10 * Used for writing resources for the framework. Also keeps histories of
11 * previous resources generated so that we can avoid regenerating if the
12 * generated resources are current.
13 *
14 * @author Chad Brandon
15 * @author Bob Fields
16 */
17 public class ResourceWriter
18 {
19 /**
20 * The shared instance
21 */
22 private static final ResourceWriter instance = new ResourceWriter();
23
24 /**
25 * Gets the shared ResourceWriter instance. Normally you'll want to retrieve
26 * the instance through this method.
27 *
28 * @return the shared instance.
29 */
30 public static ResourceWriter instance()
31 {
32 return instance;
33 }
34
35 /**
36 * Writes the string to the file specified by the fileLocation argument.
37 *
38 * @param string the string to write to the file
39 * @param file the file to which to write.
40 * @param namespace the current namespace for which this resource is being
41 * written.
42 * @throws IOException
43 */
44 public void writeStringToFile(
45 final String string,
46 final File file,
47 final String namespace)
48 throws IOException
49 {
50 ExceptionUtils.checkNull(
51 "file",
52 file);
53 this.writeStringToFile(
54 string,
55 file.toString(),
56 namespace,
57 true);
58 }
59
60 /**
61 * Writes the string to the file specified by the fileLocation argument.
62 *
63 * @param string the string to write to the file
64 * @param fileLocation the location of the file which to write.
65 * @throws IOException
66 */
67 public void writeStringToFile(
68 final String string,
69 final String fileLocation)
70 throws IOException
71 {
72 this.writeStringToFile(
73 string,
74 fileLocation,
75 true);
76 }
77
78 /**
79 * Writes the string to the file specified by the fileLocation argument.
80 *
81 * @param string the string to write to the file
82 * @param file the file which to write.
83 * @throws IOException
84 */
85 public void writeStringToFile(
86 final String string,
87 final File file)
88 throws IOException
89 {
90 this.writeStringToFile(
91 string,
92 file != null ? file.toString() : null,
93 true);
94 }
95
96 /**
97 * Writes the string to the file specified by the fileLocation argument.
98 *
99 * @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 }