001 /*
002 * $Revision: 15006 $
003 * $Date: 2009-03-25 10:28:39 +0100 (Wed, 25 Mar 2009) $
004 *
005 * This file is part of *** M y C o R e ***
006 * See http://www.mycore.de/ for details.
007 *
008 * This program is free software; you can use it, redistribute it
009 * and / or modify it under the terms of the GNU General Public License
010 * (GPL) as published by the Free Software Foundation; either version 2
011 * of the License or (at your option) any later version.
012 *
013 * This program is distributed in the hope that it will be useful, but
014 * WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program, in a file called gpl.txt or license.txt.
020 * If not, write to the Free Software Foundation Inc.,
021 * 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA
022 */
023
024 package org.mycore.datamodel.ifs2;
025
026 import java.util.ArrayList;
027 import java.util.Date;
028 import java.util.List;
029 import java.util.StringTokenizer;
030
031 import org.apache.commons.vfs.FileContent;
032 import org.apache.commons.vfs.FileObject;
033 import org.apache.commons.vfs.FileType;
034 import org.apache.commons.vfs.RandomAccessContent;
035 import org.apache.commons.vfs.VFS;
036 import org.apache.commons.vfs.util.RandomAccessMode;
037
038 /**
039 * Represents a file, directory or file collection within a file store. Files
040 * and directories can be either really stored, or virtually existing as a child
041 * node contained within a stored container file like zip or tar.
042 *
043 * @author Frank Lützenkirchen
044 */
045 public abstract class MCRNode {
046 /**
047 * The file object representing this node in the underlying filesystem.
048 */
049 protected FileObject fo;
050
051 /**
052 * The parent node owning this file, a directory or container file
053 */
054 protected MCRNode parent;
055
056 /**
057 * Creates a new node representing a child of the given parent
058 *
059 * @param parent
060 * the parent node
061 * @param fo
062 * the file object representing this node in the underlying
063 * filesystem
064 */
065 protected MCRNode(MCRNode parent, FileObject fo) {
066 this.fo = fo;
067 this.parent = parent;
068 }
069
070 /**
071 * Returns the file or directory name
072 *
073 * @return the node's filename
074 */
075 public String getName() {
076 return fo.getName().getBaseName();
077 }
078
079 /**
080 * Returns the complete path of this node up to the root file collection.
081 * Path always start with a slash, slash is used as directory delimiter.
082 *
083 * @return the absolute path of this node
084 * @throws Exception
085 */
086 public String getPath() throws Exception {
087 if (parent != null)
088 if (parent.parent == null)
089 return "/" + getName();
090 else
091 return parent.getPath() + "/" + getName();
092 else
093 return "/";
094 }
095
096 /**
097 * Returns the parent node containing this node
098 *
099 * @return the parent directory or container file
100 */
101 public MCRNode getParent() {
102 return parent;
103 }
104
105 /**
106 * Returns the root file collection this node belongs to
107 *
108 * @return the root file collection
109 */
110 public MCRFileCollection getRoot() {
111 return parent.getRoot();
112 }
113
114 /**
115 * Returns true if this node is a file
116 *
117 * @return true if this node is a file
118 */
119 public boolean isFile() throws Exception {
120 return fo.getType().equals(FileType.FILE);
121 }
122
123 /**
124 * Returns true if this node is a directory
125 *
126 * @return true if this node is a directory
127 */
128 public boolean isDirectory() throws Exception {
129 return fo.getType().equals(FileType.FOLDER);
130 }
131
132 /**
133 * For file nodes, returns the file content size in bytes, otherwise returns
134 * 0.
135 *
136 * @return the file size in bytes
137 */
138 public long getSize() throws Exception {
139 if (isFile())
140 return fo.getContent().getSize();
141 else
142 return 0;
143 }
144
145 /**
146 * Returns the time this node was last modified, or null if no such time is
147 * defined in the underlying filesystem
148 *
149 * @return the time this node was last modified
150 */
151 public Date getLastModified() throws Exception {
152 FileContent content = fo.getContent();
153 if (content != null)
154 return new Date(content.getLastModifiedTime());
155 else
156 return null;
157 }
158
159 /**
160 * Returns true if this node has child nodes. Directories and container
161 * files like zip or tar may have child nodes.
162 *
163 * @return true if children exist
164 */
165 public boolean hasChildren() throws Exception {
166 return getNumChildren() > 0;
167 }
168
169 /**
170 * Returns the FileObject that is the father of all logical children of this
171 * FileObject. This may not be the current node itself, in case the node is
172 * a container file, because then intermediate FileObject instances are
173 * created by Apache VFS.
174 *
175 * @return the father of this node's children in VFS
176 */
177 private FileObject getFather() throws Exception {
178 if (isDirectory())
179 return fo;
180 else if (getSize() == 0)
181 return null;
182 else if (VFS.getManager().canCreateFileSystem(fo)) {
183 FileObject father = fo;
184 while (VFS.getManager().canCreateFileSystem(father))
185 father = VFS.getManager().createFileSystem(father);
186 return father;
187 } else
188 return null;
189 }
190
191 /**
192 * Returns the number of child nodes of this node.
193 *
194 * @return the number of child nodes of this node.
195 */
196 public int getNumChildren() throws Exception {
197 FileObject father = getFather();
198 if (father == null)
199 return 0;
200 else
201 return father.getChildren().length;
202 }
203
204 /**
205 * Returns the children of this node. Directories and container files like
206 * zip or tar may have child nodes.
207 *
208 * @return a List of child nodes, which may be empty, in undefined order
209 */
210 public List<MCRNode> getChildren() throws Exception {
211 List<MCRNode> children = new ArrayList<MCRNode>();
212 FileObject father = getFather();
213 if (father != null) {
214 FileObject[] childFos = father.getChildren();
215 for (int i = 0; i < childFos.length; i++) {
216 String name = childFos[i].getName().getBaseName();
217 MCRNode child = getChild(name);
218 if (child != null)
219 children.add(child);
220 }
221 }
222 return children;
223 }
224
225 /**
226 * Creates a node instance for the given FileObject, which represents the
227 * child
228 *
229 * @param fo
230 * the FileObject representing the child in the underlying
231 * filesystem
232 * @return the child node
233 */
234 protected abstract MCRNode buildChildNode(FileObject fo) throws Exception;
235
236 /**
237 * Returns the child node with the given filename, or null
238 *
239 * @param name
240 * the name of the child node
241 * @return the child node with that name, or null when no such file exists
242 */
243 public MCRNode getChild(String name) throws Exception {
244 FileObject father = getFather();
245 return (father == null ? null : buildChildNode(getFather().getChild(name)));
246 }
247
248 /**
249 * Returns the node with the given relative or absolute path in the file
250 * collection this node belongs to. Slash is used as directory delimiter.
251 * When the path starts with a slash, it is an absolute path and resolving
252 * is startet at the root file collection. When the path is relative,
253 * resolving starts with the current node. One dot represents the current
254 * node, Two dots represent the parent node, like in paths used by typical
255 * real filesystems.
256 *
257 * @param path
258 * the absolute or relative path of the node to find, may contain
259 * . or ..
260 * @return the node at the given path, or null
261 */
262 public MCRNode getNodeByPath(String path) throws Exception {
263 MCRNode current = path.startsWith("/") ? getRoot() : this;
264 StringTokenizer st = new StringTokenizer(path, "/");
265 while (st.hasMoreTokens() && (current != null)) {
266 String name = st.nextToken();
267 if (name.equals("."))
268 continue;
269 else if (name.equals(".."))
270 current = current.getParent();
271 else
272 current = current.getChild(name);
273 }
274 return current;
275 }
276
277 /**
278 * Returns the content of this node for output. For a directory, it will
279 * return null.
280 *
281 * @return the content of the file
282 */
283 public MCRContent getContent() throws Exception {
284 return (isFile() ? MCRContent.readFrom(fo) : null);
285 }
286
287 /**
288 * Returns the content of this node for random access read. Be sure not to
289 * write to the node using the returned object, use just for reading! For a
290 * directory, it will return null.
291 *
292 * @return the content of this file, for random access
293 */
294 public RandomAccessContent getRandomAccessContent() throws Exception {
295 return (isFile() ? fo.getContent().getRandomAccessContent(RandomAccessMode.READ) : null);
296 }
297 }