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 * @author Chris Mair
28 * @version $Revision$ - $Date$
29 */
30public class FileEntry extends AbstractFileSystemEntry {
31
32    private static final byte[] EMPTY = new byte[0];
33
34    private byte[] bytes = EMPTY;
35    private ByteArrayOutputStream out;
36
37    /**
38     * Construct a new instance without setting its path
39     */
40    public FileEntry() {
41    }
42
43    /**
44     * Construct a new instance with the specified value for its path
45     *
46     * @param path - the value for path
47     */
48    public FileEntry(String path) {
49        super(path);
50    }
51
52    /**
53     * Construct a new instance with the specified path and file contents
54     *
55     * @param path     - the value for path
56     * @param contents - the contents of the file, as a String
57     */
58    public FileEntry(String path, String contents) {
59        super(path);
60        setContents(contents);
61    }
62
63    /**
64     * Return false to indicate that this entry represents a file
65     *
66     * @return false
67     */
68    public boolean isDirectory() {
69        return false;
70    }
71
72    /**
73     * Return the size of this file
74     *
75     * @return the file size in bytes
76     */
77    public long getSize() {
78        return getCurrentBytes().length;
79    }
80
81    /**
82     * Set the contents of the file represented by this entry
83     *
84     * @param contents - the String whose bytes are used as the contents
85     */
86    public void setContents(String contents) {
87        byte[] newBytes = (contents != null) ? contents.getBytes() : EMPTY;
88        setContentsInternal(newBytes);
89    }
90
91    /**
92     * Set the contents of the file represented by this entry
93     *
94     * @param contents - the byte[] used as the contents
95     */
96    public void setContents(byte[] contents) {
97        // Copy the bytes[] to guard against subsequent modification of the source array
98        byte[] newBytes = (contents != null) ? new String(contents).getBytes() : EMPTY;
99        setContentsInternal(newBytes);
100    }
101
102    /**
103     * Create and return an InputStream for reading the contents of the file represented by this entry
104     *
105     * @return an InputStream
106     */
107    public InputStream createInputStream() {
108        return new ByteArrayInputStream(getCurrentBytes());
109    }
110
111    /**
112     * Create and return an OutputStream for writing the contents of the file represented by this entry
113     *
114     * @param append - true if the OutputStream should append to any existing contents false if
115     *               any existing contents should be overwritten
116     * @return an OutputStream
117     * @throws FileSystemException - if an error occurs creating or initializing the OutputStream
118     */
119    public OutputStream createOutputStream(boolean append) {
120        // If appending and we already have an OutputStream, then continue to use it
121        if (append && out != null) {
122            return out;
123        }
124
125        out = new ByteArrayOutputStream();
126        byte[] initialContents = (append) ? bytes : EMPTY;
127        try {
128            out.write(initialContents);
129        }
130        catch (IOException e) {
131            throw new FileSystemException(getPath(), null, e);
132        }
133        return out;
134    }
135
136    /**
137     * Return a new FileSystemEntry that is a clone of this object, except having the specified path
138     *
139     * @param path - the new path value for the cloned file system entry
140     * @return a new FileSystemEntry that has all the same values as this object except for its path
141     */
142    public FileSystemEntry cloneWithNewPath(String path) {
143        FileEntry clone = new FileEntry(path);
144        clone.setLastModified(getLastModified());
145        clone.setOwner(getOwner());
146        clone.setGroup(getGroup());
147        clone.setPermissions(getPermissions());
148        clone.setContents(bytes);
149        return clone;
150    }
151
152    //-------------------------------------------------------------------------
153    // Internal Helper Methods
154    //-------------------------------------------------------------------------
155
156    /**
157     * @return the current contents of this file entry as a byte[]
158     */
159    private byte[] getCurrentBytes() {
160        return (out != null) ? out.toByteArray() : bytes;
161    }
162
163    /**
164     * Set the contents of the file represented by this entry
165     *
166     * @param contents - the byte[] used as the contents
167     */
168    private void setContentsInternal(byte[] contents) {
169        this.bytes = contents;
170
171        // Get rid of any OutputStream
172        this.out = null;
173    }
174
175    /**
176     * @see java.lang.Object#toString()
177     */
178    public String toString() {
179        return "File['" + getPath() + "' size=" + getSize() + " lastModified=" + getLastModified() + " owner="
180                + getOwner() + " group=" + getGroup() + " permissions=" + getPermissions() + "]";
181    }
182
183}
184