001/*-------------------------------------------------------------------------+
002|                                                                          |
003| Copyright 2005-2011 The ConQAT Project                                   |
004|                                                                          |
005| Licensed under the Apache License, Version 2.0 (the "License");          |
006| you may not use this file except in compliance with the License.         |
007| You may obtain a copy of the License at                                  |
008|                                                                          |
009|    http://www.apache.org/licenses/LICENSE-2.0                            |
010|                                                                          |
011| Unless required by applicable law or agreed to in writing, software      |
012| distributed under the License is distributed on an "AS IS" BASIS,        |
013| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
014| See the License for the specific language governing permissions and      |
015| limitations under the License.                                           |
016+-------------------------------------------------------------------------*/
017package org.conqat.lib.commons.filesystem;
018
019import java.io.File;
020import java.io.FileFilter;
021import java.io.FilenameFilter;
022import java.io.IOException;
023import java.net.URI;
024
025/**
026 * This class represents files that are guaranteed to be canonical. For this
027 * class methods <code>getPath()</code>, <code>getAbsolutePath()</code> and
028 * <code>getCanonicalPath()</code> all return the same (canonical) path.
029 *
030 * @author deissenb
031 */
032public class CanonicalFile extends File {
033
034        /** Version used for serialization. */
035        private static final long serialVersionUID = 1;
036
037        /**
038         * Create new canonical file from existing file.
039         */
040        public CanonicalFile(File file) throws IOException {
041                super(file.getCanonicalPath());
042        }
043
044        /**
045         * Create new canonical file.
046         *
047         * @see File#File(String)
048         */
049        public CanonicalFile(String pathname) throws IOException {
050                super(new File(pathname).getCanonicalPath());
051        }
052
053        /**
054         * Create new canonical file.
055         *
056         * @see File#File(File, String)
057         */
058        public CanonicalFile(File parent, String child) throws IOException {
059                this(new File(parent, child));
060        }
061
062        /**
063         * Create new canonical file.
064         *
065         * @see File#File(String, String)
066         */
067        public CanonicalFile(String parent, String child) throws IOException {
068                this(new File(parent, child));
069        }
070
071        /**
072         * Create new canonical file.
073         *
074         * @see File#File(URI)
075         */
076        public CanonicalFile(URI uri) throws IOException {
077                super(new File(uri).getCanonicalPath());
078        }
079
080        /**
081         * Returns the canonical file itself. Use {@link #getCanonicalFile()} for
082         * consistency reasons.
083         */
084        @Deprecated
085        @Override
086        public CanonicalFile getAbsoluteFile() {
087                return this;
088        }
089
090        /** Returns the canonical file itself. */
091        @Override
092        public CanonicalFile getCanonicalFile() {
093                return this;
094        }
095
096        /**
097         * Same as {@link File#listFiles()} but returns canonical files. If for some
098         * strange reason the files below a canonical file cannot be canonized, this may
099         * throw an {@link AssertionError}.
100         *
101         * If this abstract pathname does not denote a directory, then this method
102         * returns {@code null}.
103         */
104        @Override
105        public CanonicalFile[] listFiles() {
106                File[] files = super.listFiles();
107                if (files == null) {
108                        return null;
109                }
110                return canonize(files);
111        }
112
113        /**
114         * Same as {@link File#listFiles(FileFilter)} but returns canonical files. If
115         * for some strange reason the files below a canonical file cannot be canonized,
116         * this may throw an {@link AssertionError}.
117         *
118         * If this abstract pathname does not denote a directory, then this method
119         * returns {@code null}.
120         */
121        @Override
122        public CanonicalFile[] listFiles(FileFilter filter) {
123                File[] files = super.listFiles(filter);
124                if (files == null) {
125                        return null;
126                }
127                CanonicalFile[] result = new CanonicalFile[files.length];
128                for (int i = 0; i < result.length; i++) {
129                        result[i] = canonize(files[i].getAbsoluteFile());
130                }
131                return result;
132        }
133
134        /**
135         * Same as {@link File#listFiles(FilenameFilter)} but returns canonical files.
136         * If for some strange reason the files below a canonical file cannot be
137         * canonized, this may throw an {@link AssertionError}.
138         *
139         * If this abstract pathname does not denote a directory, then this method
140         * returns {@code null}.
141         */
142        @Override
143        public CanonicalFile[] listFiles(FilenameFilter filter) {
144                File[] files = super.listFiles(filter);
145                if (files == null) {
146                        return null;
147                }
148                return canonize(files);
149        }
150
151        /**
152         * Same as {@link File#getParentFile()} but returns a canonical file. If for
153         * some strange reason the parent file of a canonical file cannot be canonized,
154         * this may throw an {@link AssertionError}.
155         */
156        @Override
157        public CanonicalFile getParentFile() {
158                File parent = super.getParentFile();
159                if (parent == null) {
160                        return null;
161                }
162                return canonize(parent);
163        }
164
165        /** Checks if this <em>file</em> is a file an can be read. */
166        public boolean isReadableFile() {
167                return isFile() && canRead();
168        }
169
170        /**
171         * This method is overridden to save effort for call to
172         * {@link File#getCanonicalPath()}.
173         */
174        @Override
175        public String getCanonicalPath() {
176                return super.getPath();
177        }
178
179        /**
180         * This method is overridden to save effort for call to
181         * {@link File#getAbsolutePath()}. Use {@link #getCanonicalPath()} for
182         * consistency reasons.
183         */
184        @Deprecated
185        @Override
186        public String getAbsolutePath() {
187                return super.getPath();
188        }
189
190        /**
191         * Use {@link #getCanonicalPath()} for consistency reasons.
192         */
193        @Deprecated
194        @Override
195        public String getPath() {
196                return super.getPath();
197        }
198
199        /**
200         * Returns the extension of the file.
201         *
202         * @return File extension, i.e. "java" for "FileSystemUtils.java", or
203         *         <code>null</code>, if the file has no extension (i.e. if a filename
204         *         contains no '.'), returns the empty string if the '.' is the
205         *         filename's last character.
206         */
207        public String getExtension() {
208                return FileSystemUtils.getFileExtension(this);
209        }
210
211        /**
212         * Canonizes list of filenames. If a file could not be canonized, this throws an
213         * {@link AssertionError}.
214         */
215        private static CanonicalFile[] canonize(File[] files) {
216                CanonicalFile[] result = new CanonicalFile[files.length];
217                for (int i = 0; i < result.length; i++) {
218                        result[i] = canonize(files[i]);
219                }
220                return result;
221
222        }
223
224        /**
225         * Canonizes filename. If a file could not be canonized, this throws an
226         * {@link AssertionError}.
227         */
228        private static CanonicalFile canonize(File file) {
229                try {
230                        return new CanonicalFile(file);
231                } catch (IOException e) {
232                        throw new AssertionError("Problems creating canonical path for " + file + ": " + e.getMessage());
233                }
234        }
235
236}