1/*
2 * Copyright 2008 the original author or authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.mockftpserver.fake.filesystem
17
18import java.io.ByteArrayInputStream
19import java.io.ByteArrayOutputStream
20import java.io.IOException
21import java.io.InputStream
22import java.io.OutputStream
23
24/**
25 * File system entry representing a file
26 *
27 * @version $Revision: $ - $Date: $
28 *
29 * @author Chris Mair
30 */
31public class FileEntry extends AbstractFileSystemEntry {
32
33    private static final EMPTY = new byte[0]
34
35    private byte[] bytes = EMPTY
36    private ByteArrayOutputStream out
37
38    /**
39     * Construct a new instance without setting its path
40     */
41    FileEntry() {
42    }
43
44    /**
45     * Construct a new instance with the specified value for its path
46     * @param path - the value for path
47     */
48    FileEntry(String path) {
49        super(path)
50    }
51
52    /**
53     * Construct a new instance with the specified path and file contents
54     * @param path - the value for path
55     * @param contents - the contents of the file, as a String
56     */
57    FileEntry(String path, String contents) {
58        super(path)
59        setContents(contents)
60    }
61
62    /**
63     * Abstract method -- must be implemented within concrete subclasses
64     * @return true if this file system entry represents a directory
65     */
66    boolean isDirectory() {
67        return false
68    }
69
70    /**
71     * Return the size of this file
72     * @return the file size in bytes
73     */
74    long getSize() {
75        return getCurrentBytes().length
76    }
77
78    /**
79     * Set the contents of the file represented by this entry
80     * @param contents - the String whose bytes are used as the contents
81     */
82    void setContents(String contents) {
83        assert contents != null
84        setContentsInternal(contents.getBytes())
85    }
86
87    /**
88     * Set the contents of the file represented by this entry
89     * @param contents - the byte[] used as the contents
90     */
91    void setContents(byte[] contents) {
92        assert contents != null
93        // Copy the bytes[] to guard against subsequent modification of the source array
94        setContentsInternal(new String(contents).getBytes())
95    }
96
97    /**
98     * Create and return an InputStream for reading the contents of the file represented by this entry
99     * @return an InputStream
100     */
101    InputStream createInputStream() {
102        return new ByteArrayInputStream(getCurrentBytes())
103    }
104
105    /**
106     * Create and return an OutputStream for writing the contents of the file represented by this entry
107     * @param append - true if the OutputStream should append to any existing contents false if
108     *      any existing contents should be overwritten
109     * @return an OutputStream
110     * @throws FileSystemException - if an error occurs creating or initializing the OutputStream
111     */
112    OutputStream createOutputStream(boolean append) {
113        // If appending and we already have an OutputStream, then continue to use it
114        if (append && out != null) {
115            return out
116        }
117
118        out = new ByteArrayOutputStream()
119        byte[] initialContents = (append) ? bytes : EMPTY
120        try {
121            out.write(initialContents)
122        }
123        catch (IOException e) {
124            throw new FileSystemException(e)
125        }
126        return out
127    }
128
129    //-------------------------------------------------------------------------
130    // Internal Helper Methods
131    //-------------------------------------------------------------------------
132
133    /**
134     * @return the current contents of this file entry as a byte[]
135     */
136    private byte[] getCurrentBytes() {
137        return (out != null) ? out.toByteArray() : bytes
138    }
139
140    /**
141     * Set the contents of the file represented by this entry
142     * @param contents - the byte[] used as the contents
143     */
144    private void setContentsInternal(byte[] contents) {
145        this.bytes = contents
146
147        // Get rid of any OutputStream
148        this.out = null
149    }
150
151    /**
152     * @see java.lang.Object#toString()
153     */
154    String toString() {
155        "File['${getPath()}' size=${getSize()} lastModified=$lastModified]"
156    }
157
158}
159