001: /*
002: * Copyright (c) 2007, intarsys consulting GmbH
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * - Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * - Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * - Neither the name of intarsys nor the names of its contributors may be used
015: * to endorse or promote products derived from this software without specific
016: * prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
022: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
023: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
024: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
025: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
026: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
027: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
028: * POSSIBILITY OF SUCH DAMAGE.
029: */
030: package de.intarsys.tools.locator;
031:
032: import java.io.File;
033: import java.io.FileInputStream;
034: import java.io.FileNotFoundException;
035: import java.io.FileOutputStream;
036: import java.io.FilenameFilter;
037: import java.io.IOException;
038: import java.io.InputStream;
039: import java.io.InputStreamReader;
040: import java.io.OutputStream;
041: import java.io.OutputStreamWriter;
042: import java.io.Reader;
043: import java.io.Writer;
044: import java.net.MalformedURLException;
045: import java.net.URL;
046: import java.util.logging.Level;
047:
048: import de.intarsys.tools.file.FileTools;
049: import de.intarsys.tools.logging.LogTools;
050: import de.intarsys.tools.randomaccess.IRandomAccess;
051: import de.intarsys.tools.randomaccess.RandomAccessFile;
052: import de.intarsys.tools.stream.StreamTools;
053: import de.intarsys.tools.stream.TempFileOutputStream;
054:
055: /**
056: * File based implementation of {@link ILocator}.
057: */
058: public class FileLocator extends CommonLocator {
059: /**
060: * The file referenced by the locator
061: */
062: private File file;
063:
064: /**
065: * The encoding of the designated source
066: */
067: private String encoding;
068:
069: /**
070: * Flag if the resource is out of synch
071: */
072: private boolean outOfSynch;
073:
074: /**
075: * flag if we synchronize synchronously with every check
076: */
077: private boolean synchSynchronous = false;
078:
079: /**
080: * Timestamp of last modification to the resource.
081: */
082: private long lastModified = 0;
083:
084: /**
085: * flag if output is created initially in a temporary file
086: */
087: private boolean useTempFile = false;
088:
089: private File canonicalFile;
090:
091: public FileLocator(File file) {
092: super ();
093: this .file = file;
094: }
095:
096: public FileLocator(String path) {
097: this (new File(path));
098: }
099:
100: /*
101: * (non-Javadoc)
102: *
103: * @see java.lang.Object#equals(java.lang.Object)
104: */
105: public boolean equals(Object obj) {
106: if (!(obj instanceof FileLocator)) {
107: return false;
108: }
109: return getCanonicalFile().equals(
110: ((FileLocator) obj).getCanonicalFile());
111: }
112:
113: /*
114: * (non-Javadoc)
115: *
116: * @see de.intarsys.tools.locator.ILocator#exists()
117: */
118: public boolean exists() {
119: return getFile().exists();
120: }
121:
122: /**
123: * The canonical file represented by this.
124: *
125: * @return The canonical file represented by this.
126: */
127: public File getCanonicalFile() {
128: if (canonicalFile == null) {
129: try {
130: canonicalFile = getFile().getCanonicalFile();
131: } catch (IOException e) {
132: canonicalFile = getFile();
133: }
134: }
135: return canonicalFile;
136: }
137:
138: /*
139: * (non-Javadoc)
140: *
141: * @see de.intarsys.tools.locator.ILocator#getChild(java.lang.String)
142: */
143: public ILocator getChild(String name) {
144: File childfile = new File(getFile(), name);
145: FileLocator result = new FileLocator(childfile);
146: result.setSynchSynchronous(isSynchSynchronous());
147: return result;
148: }
149:
150: /**
151: * The encoding of the file.
152: *
153: * @return Returns the encoding.
154: */
155: protected String getEncoding() {
156: return encoding;
157: }
158:
159: /**
160: * Answer the file represented by this.
161: *
162: * @return The canonical file represented by this.
163: */
164: public File getFile() {
165: return file;
166: }
167:
168: /*
169: * (non-Javadoc)
170: *
171: * @see de.intarsys.tools.locator.ILocator#getFullName()
172: */
173: public String getFullName() {
174: if (getFile() == null) {
175: return "unknown";
176: }
177: return getFile().getAbsolutePath();
178: }
179:
180: /*
181: * (non-Javadoc)
182: *
183: * @see de.intarsys.tools.locator.ILocator#getInputStream()
184: */
185: public InputStream getInputStream() throws IOException {
186: // trigger timestamp reading
187: getLastModified();
188: return new FileInputStream(getFile());
189: }
190:
191: /**
192: * Answer the timestamp of the last modification.
193: *
194: * @return the timestamp of the last modification.
195: */
196: protected long getLastModified() {
197: if (lastModified == 0) {
198: if (file.exists()) {
199: lastModified = file.lastModified();
200: }
201: }
202: return lastModified;
203: }
204:
205: /*
206: * (non-Javadoc)
207: *
208: * @see de.intarsys.tools.locator.ILocator#getLocalName()
209: */
210: public String getLocalName() {
211: if (getFile() == null) {
212: return "unknown";
213: }
214: return FileTools.getBaseName(getFile());
215: }
216:
217: /*
218: * (non-Javadoc)
219: *
220: * @see de.intarsys.tools.locator.ILocator#getOutputStream()
221: */
222: public OutputStream getOutputStream() throws IOException {
223: // trigger timestamp reading
224: getLastModified();
225: if (!getFile().getParentFile().exists()) {
226: getFile().getParentFile().mkdirs();
227: }
228: if (isUseTempFile()) {
229: return new TempFileOutputStream(getFile(), "tmp_", "."
230: + getType());
231: }
232: return new FileOutputStream(getFile());
233: }
234:
235: /*
236: * (non-Javadoc)
237: *
238: * @see de.intarsys.tools.locator.ILocator#getParent()
239: */
240: public ILocator getParent() {
241: File parentFile = FileTools.getParentFile(getFile());
242: if (parentFile == null) {
243: return null;
244: }
245: FileLocator result = new FileLocator(parentFile);
246: result.setSynchSynchronous(isSynchSynchronous());
247: return result;
248: }
249:
250: /*
251: * (non-Javadoc)
252: *
253: * @see de.intarsys.tools.locator.ILocator#getRandomAccess()
254: */
255: public IRandomAccess getRandomAccess() throws IOException {
256: // trigger timestamp reading
257: getLastModified();
258: if (isUseTempFile()) {
259: throw new UnsupportedOperationException(
260: "no random access to temp file");
261: }
262: return new RandomAccessFile(getFile());
263: }
264:
265: /*
266: * (non-Javadoc)
267: *
268: * @see de.intarsys.tools.locator.ILocator#getReader()
269: */
270: public Reader getReader() throws IOException {
271: // trigger timestamp reading
272: getLastModified();
273: if ((getEncoding() == null) || getEncoding().equals("")) {
274: return new InputStreamReader(getInputStream());
275: }
276: return new InputStreamReader(getInputStream(), getEncoding());
277: }
278:
279: /*
280: * (non-Javadoc)
281: *
282: * @see de.intarsys.tools.locator.ILocator#getReader(java.lang.String)
283: */
284: public Reader getReader(String newEncoding) throws IOException {
285: // trigger timestamp reading
286: getLastModified();
287: if ((newEncoding == null) || newEncoding.equals("")) {
288: return getReader();
289: }
290: return new InputStreamReader(getInputStream(), newEncoding);
291: }
292:
293: /*
294: * (non-Javadoc)
295: *
296: * @see de.intarsys.tools.locator.ILocator#getType()
297: */
298: public String getType() {
299: if (getFile() == null) {
300: return "<unknown>";
301: }
302: return FileTools.getExtension(getFile());
303: }
304:
305: /*
306: * (non-Javadoc)
307: *
308: * @see de.intarsys.tools.locator.ILocator#getTypedName()
309: */
310: public String getTypedName() {
311: if (getFile() == null) {
312: return "unknown";
313: }
314: return getFile().getName();
315: }
316:
317: /*
318: * (non-Javadoc)
319: *
320: * @see de.intarsys.tools.locator.ILocator#getWriter()
321: */
322: public Writer getWriter() throws IOException {
323: // trigger timestamp reading
324: getLastModified();
325: if ((getEncoding() == null) || getEncoding().equals("")) {
326: return new OutputStreamWriter(getOutputStream());
327: }
328: return new OutputStreamWriter(getOutputStream(), getEncoding());
329: }
330:
331: /*
332: * (non-Javadoc)
333: *
334: * @see de.intarsys.tools.locator.ILocator#getWriter(java.lang.String)
335: */
336: public Writer getWriter(String newEncoding) throws IOException {
337: // trigger timestamp reading
338: getLastModified();
339: if ((newEncoding == null) || newEncoding.equals("")) {
340: return getWriter();
341: }
342: return new OutputStreamWriter(getOutputStream(), newEncoding);
343: }
344:
345: /*
346: * (non-Javadoc)
347: *
348: * @see java.lang.Object#hashCode()
349: */
350: public int hashCode() {
351: try {
352: return getFile().getCanonicalFile().hashCode();
353: } catch (IOException e) {
354: return 17;
355: }
356: }
357:
358: /*
359: * (non-Javadoc)
360: *
361: * @see de.intarsys.tools.locator.ILocator#isDirectory()
362: */
363: public boolean isDirectory() {
364: return getFile().isDirectory();
365: }
366:
367: /*
368: * (non-Javadoc)
369: *
370: * @see de.intarsys.tools.component.ISynchronizable#isOutOfSynch()
371: */
372: public synchronized boolean isOutOfSynch() {
373: if (isSynchSynchronous()) {
374: synch();
375: }
376: return outOfSynch;
377: }
378:
379: /*
380: * (non-Javadoc)
381: *
382: * @see de.intarsys.tools.component.ISynchronizable#isSynchSynchronous()
383: */
384: public boolean isSynchSynchronous() {
385: return synchSynchronous;
386: }
387:
388: /**
389: * <code>true</code> if temp file should be used.
390: *
391: * @return <code>true</code> if temp file should be used.
392: */
393: public boolean isUseTempFile() {
394: return useTempFile;
395: }
396:
397: /*
398: * (non-Javadoc)
399: *
400: * @see de.intarsys.tools.locator.ILocator#listLocators(de.intarsys.tools.locator.ILocatorNameFilter)
401: */
402: public ILocator[] listLocators(final ILocatorNameFilter filter)
403: throws IOException {
404: if (!getFile().exists()) {
405: throw new FileNotFoundException(getFile().getName()
406: + " not found");
407: }
408: if (!getFile().isDirectory()) {
409: throw new IOException(getFile().getName()
410: + " not a directory");
411: }
412: File[] candidates;
413: if (filter == null) {
414: candidates = getFile().listFiles();
415: } else {
416: candidates = getFile().listFiles(new FilenameFilter() {
417: public boolean accept(File dir, String current) {
418: return filter.accept(FileLocator.this , current);
419: }
420: });
421: }
422: if (candidates == null) {
423: throw new IOException("error listing resources");
424: }
425:
426: ILocator[] result = new ILocator[candidates.length];
427: for (int i = 0; i < result.length; i++) {
428: FileLocator l = new FileLocator(candidates[i].getPath());
429: l.setSynchSynchronous(isSynchSynchronous());
430: l.setEncoding(getEncoding());
431: result[i] = l;
432: }
433: return result;
434: }
435:
436: protected void setEncoding(String encoding) {
437: this .encoding = encoding;
438: }
439:
440: /**
441: * @param synchSynchronous
442: */
443: public void setSynchSynchronous(boolean synchSynchronous) {
444: this .synchSynchronous = synchSynchronous;
445: }
446:
447: /**
448: * @param useTempFile
449: */
450: public void setUseTempFile(boolean useTempFile) {
451: this .useTempFile = useTempFile;
452: }
453:
454: /*
455: * (non-Javadoc)
456: *
457: * @see de.intarsys.tools.component.ISynchronizable#synch()
458: */
459: public synchronized void synch() {
460: if (getFile() == null) {
461: return;
462: }
463: if ((getLastModified() != getFile().lastModified())) {
464: LogTools.getLogger(this .getClass()).log(Level.FINEST,
465: "'" + getFullName() + "' out of synch!");
466: outOfSynch = true;
467: }
468: }
469:
470: /*
471: * (non-Javadoc)
472: *
473: * @see java.lang.Object#toString()
474: */
475: public String toString() {
476: return getFile().toString();
477: }
478:
479: /*
480: * (non-Javadoc)
481: *
482: * @see de.intarsys.tools.locator.ILocator#toURL()
483: */
484: public URL toURL() {
485: try {
486: return new URL("file", null, getFullName() + "/");
487: } catch (MalformedURLException e) {
488: return null;
489: }
490: }
491:
492: /*
493: * (non-Javadoc)
494: *
495: * @see de.intarsys.tools.locator.ILocator#isReadOnly()
496: */
497: public boolean isReadOnly() {
498: if (super .isReadOnly()) {
499: return true;
500: }
501: IRandomAccess r = null;
502: try {
503: // ensure file will not be created empty accidentally
504: if (!getFile().exists()) {
505: return false;
506: }
507: r = getRandomAccess();
508: if (r == null || r.isReadOnly()) {
509: return true;
510: }
511: return false;
512: } catch (IOException e) {
513: return true;
514: } finally {
515: StreamTools.close(r);
516: }
517: }
518:
519: public void rename(String newName) throws IOException {
520: if (getFile() == null) {
521: return;
522: }
523: File parent = getFile().getParentFile();
524: if (parent == null) {
525: parent = getFile().getAbsoluteFile().getParentFile();
526: }
527: File newFile;
528: if (parent == null) {
529: newFile = new File(newName);
530: } else {
531: newFile = new File(parent, newName);
532: }
533: FileTools.renameFile(getFile(), newFile);
534: file = newFile;
535: canonicalFile = null;
536: lastModified = 0;
537: }
538:
539: public void delete() throws IOException {
540: if (getFile() == null) {
541: return;
542: }
543: getFile().delete();
544: }
545: }
|