Home | History | Annotate | Download | only in gwt
      1 /*******************************************************************************
      2  * Copyright 2011 See AUTHORS file.
      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  ******************************************************************************/
     16 
     17 package com.badlogic.gdx.backends.gwt;
     18 
     19 import java.io.BufferedInputStream;
     20 import java.io.BufferedReader;
     21 import java.io.File;
     22 import java.io.FileFilter;
     23 import java.io.FilenameFilter;
     24 import java.io.IOException;
     25 import java.io.InputStream;
     26 import java.io.InputStreamReader;
     27 import java.io.OutputStream;
     28 import java.io.Reader;
     29 import java.io.UnsupportedEncodingException;
     30 import java.io.Writer;
     31 
     32 import com.badlogic.gdx.Files.FileType;
     33 import com.badlogic.gdx.Gdx;
     34 import com.badlogic.gdx.backends.gwt.preloader.Preloader;
     35 import com.badlogic.gdx.files.FileHandle;
     36 import com.badlogic.gdx.utils.GdxRuntimeException;
     37 
     38 public class GwtFileHandle extends FileHandle {
     39 	public final Preloader preloader;
     40 	private final String file;
     41 	private final FileType type;
     42 
     43 	public GwtFileHandle (Preloader preloader, String fileName, FileType type) {
     44 		if (type != FileType.Internal && type != FileType.Classpath)
     45 			throw new GdxRuntimeException("FileType '" + type + "' Not supported in GWT backend");
     46 		this.preloader = preloader;
     47 		this.file = fixSlashes(fileName);
     48 		this.type = type;
     49 	}
     50 
     51 	public GwtFileHandle (String path) {
     52 		this.type = FileType.Internal;
     53 		this.preloader = ((GwtApplication)Gdx.app).getPreloader();
     54 		this.file = fixSlashes(path);
     55 	}
     56 
     57 	public String path () {
     58 		return file;
     59 	}
     60 
     61 	public String name () {
     62 		int index = file.lastIndexOf('/');
     63 		if (index < 0) return file;
     64 		return file.substring(index + 1);
     65 	}
     66 
     67 	public String extension () {
     68 		String name = name();
     69 		int dotIndex = name.lastIndexOf('.');
     70 		if (dotIndex == -1) return "";
     71 		return name.substring(dotIndex + 1);
     72 	}
     73 
     74 	public String nameWithoutExtension () {
     75 		String name = name();
     76 		int dotIndex = name.lastIndexOf('.');
     77 		if (dotIndex == -1) return name;
     78 		return name.substring(0, dotIndex);
     79 	}
     80 
     81 	/** @return the path and filename without the extension, e.g. dir/dir2/file.png -> dir/dir2/file */
     82 	public String pathWithoutExtension () {
     83 		String path = file;
     84 		int dotIndex = path.lastIndexOf('.');
     85 		if (dotIndex == -1) return path;
     86 		return path.substring(0, dotIndex);
     87 	}
     88 
     89 	public FileType type () {
     90 		return type;
     91 	}
     92 
     93 	/** Returns a java.io.File that represents this file handle. Note the returned file will only be usable for
     94 	 * {@link FileType#Absolute} and {@link FileType#External} file handles. */
     95 	public File file () {
     96 		throw new GdxRuntimeException("Not supported in GWT backend");
     97 	}
     98 
     99 	/** Returns a stream for reading this file as bytes.
    100 	 * @throw GdxRuntimeException if the file handle represents a directory, doesn't exist, or could not be read. */
    101 	public InputStream read () {
    102 		InputStream in = preloader.read(file);
    103 		if (in == null) throw new GdxRuntimeException(file + " does not exist");
    104 		return in;
    105 	}
    106 
    107 	/** Returns a buffered stream for reading this file as bytes.
    108 	 * @throw GdxRuntimeException if the file handle represents a directory, doesn't exist, or could not be read. */
    109 	public BufferedInputStream read (int bufferSize) {
    110 		return new BufferedInputStream(read(), bufferSize);
    111 	}
    112 
    113 	/** Returns a reader for reading this file as characters.
    114 	 * @throw GdxRuntimeException if the file handle represents a directory, doesn't exist, or could not be read. */
    115 	public Reader reader () {
    116 		return new InputStreamReader(read());
    117 	}
    118 
    119 	/** Returns a reader for reading this file as characters.
    120 	 * @throw GdxRuntimeException if the file handle represents a directory, doesn't exist, or could not be read. */
    121 	public Reader reader (String charset) {
    122 		try {
    123 			return new InputStreamReader(read(), charset);
    124 		} catch (UnsupportedEncodingException e) {
    125 			throw new GdxRuntimeException("Encoding '" + charset + "' not supported", e);
    126 		}
    127 	}
    128 
    129 	/** Returns a buffered reader for reading this file as characters.
    130 	 * @throw GdxRuntimeException if the file handle represents a directory, doesn't exist, or could not be read. */
    131 	public BufferedReader reader (int bufferSize) {
    132 		return new BufferedReader(reader(), bufferSize);
    133 	}
    134 
    135 	/** Returns a buffered reader for reading this file as characters.
    136 	 * @throw GdxRuntimeException if the file handle represents a directory, doesn't exist, or could not be read. */
    137 	public BufferedReader reader (int bufferSize, String charset) {
    138 		return new BufferedReader(reader(charset), bufferSize);
    139 	}
    140 
    141 	/** Reads the entire file into a string using the platform's default charset.
    142 	 * @throw GdxRuntimeException if the file handle represents a directory, doesn't exist, or could not be read. */
    143 	public String readString () {
    144 		return readString(null);
    145 	}
    146 
    147 	/** Reads the entire file into a string using the specified charset.
    148 	 * @throw GdxRuntimeException if the file handle represents a directory, doesn't exist, or could not be read. */
    149 	public String readString (String charset) {
    150 		if (preloader.isText(file)) return preloader.texts.get(file);
    151 		try {
    152 			return new String(readBytes(), "UTF-8");
    153 		} catch (UnsupportedEncodingException e) {
    154 			return null;
    155 		}
    156 	}
    157 
    158 	/** Reads the entire file into a byte array.
    159 	 * @throw GdxRuntimeException if the file handle represents a directory, doesn't exist, or could not be read. */
    160 	public byte[] readBytes () {
    161 		int length = (int)length();
    162 		if (length == 0) length = 512;
    163 		byte[] buffer = new byte[length];
    164 		int position = 0;
    165 		InputStream input = read();
    166 		try {
    167 			while (true) {
    168 				int count = input.read(buffer, position, buffer.length - position);
    169 				if (count == -1) break;
    170 				position += count;
    171 				if (position == buffer.length) {
    172 					// Grow buffer.
    173 					byte[] newBuffer = new byte[buffer.length * 2];
    174 					System.arraycopy(buffer, 0, newBuffer, 0, position);
    175 					buffer = newBuffer;
    176 				}
    177 			}
    178 		} catch (IOException ex) {
    179 			throw new GdxRuntimeException("Error reading file: " + this, ex);
    180 		} finally {
    181 			try {
    182 				if (input != null) input.close();
    183 			} catch (IOException ignored) {
    184 			}
    185 		}
    186 		if (position < buffer.length) {
    187 			// Shrink buffer.
    188 			byte[] newBuffer = new byte[position];
    189 			System.arraycopy(buffer, 0, newBuffer, 0, position);
    190 			buffer = newBuffer;
    191 		}
    192 		return buffer;
    193 	}
    194 
    195 	/** Reads the entire file into the byte array. The byte array must be big enough to hold the file's data.
    196 	 * @param bytes the array to load the file into
    197 	 * @param offset the offset to start writing bytes
    198 	 * @param size the number of bytes to read, see {@link #length()}
    199 	 * @return the number of read bytes */
    200 	public int readBytes (byte[] bytes, int offset, int size) {
    201 		InputStream input = read();
    202 		int position = 0;
    203 		try {
    204 			while (true) {
    205 				int count = input.read(bytes, offset + position, size - position);
    206 				if (count <= 0) break;
    207 				position += count;
    208 			}
    209 		} catch (IOException ex) {
    210 			throw new GdxRuntimeException("Error reading file: " + this, ex);
    211 		} finally {
    212 			try {
    213 				if (input != null) input.close();
    214 			} catch (IOException ignored) {
    215 			}
    216 		}
    217 		return position - offset;
    218 	}
    219 
    220 	/** Returns a stream for writing to this file. Parent directories will be created if necessary.
    221 	 * @param append If false, this file will be overwritten if it exists, otherwise it will be appended.
    222 	 * @throw GdxRuntimeException if this file handle represents a directory, if it is a {@link FileType#Classpath} or
    223 	 *        {@link FileType#Internal} file, or if it could not be written. */
    224 	public OutputStream write (boolean append) {
    225 		throw new GdxRuntimeException("Cannot write to files in GWT backend");
    226 	}
    227 
    228 	/** Reads the remaining bytes from the specified stream and writes them to this file. The stream is closed. Parent directories
    229 	 * will be created if necessary.
    230 	 * @param append If false, this file will be overwritten if it exists, otherwise it will be appended.
    231 	 * @throw GdxRuntimeException if this file handle represents a directory, if it is a {@link FileType#Classpath} or
    232 	 *        {@link FileType#Internal} file, or if it could not be written. */
    233 	public void write (InputStream input, boolean append) {
    234 		throw new GdxRuntimeException("Cannot write to files in GWT backend");
    235 	}
    236 
    237 	/** Returns a writer for writing to this file using the default charset. Parent directories will be created if necessary.
    238 	 * @param append If false, this file will be overwritten if it exists, otherwise it will be appended.
    239 	 * @throw GdxRuntimeException if this file handle represents a directory, if it is a {@link FileType#Classpath} or
    240 	 *        {@link FileType#Internal} file, or if it could not be written. */
    241 	public Writer writer (boolean append) {
    242 		return writer(append, null);
    243 	}
    244 
    245 	/** Returns a writer for writing to this file. Parent directories will be created if necessary.
    246 	 * @param append If false, this file will be overwritten if it exists, otherwise it will be appended.
    247 	 * @param charset May be null to use the default charset.
    248 	 * @throw GdxRuntimeException if this file handle represents a directory, if it is a {@link FileType#Classpath} or
    249 	 *        {@link FileType#Internal} file, or if it could not be written. */
    250 	public Writer writer (boolean append, String charset) {
    251 		throw new GdxRuntimeException("Cannot write to files in GWT backend");
    252 	}
    253 
    254 	/** Writes the specified string to the file using the default charset. Parent directories will be created if necessary.
    255 	 * @param append If false, this file will be overwritten if it exists, otherwise it will be appended.
    256 	 * @throw GdxRuntimeException if this file handle represents a directory, if it is a {@link FileType#Classpath} or
    257 	 *        {@link FileType#Internal} file, or if it could not be written. */
    258 	public void writeString (String string, boolean append) {
    259 		writeString(string, append, null);
    260 	}
    261 
    262 	/** Writes the specified string to the file as UTF-8. Parent directories will be created if necessary.
    263 	 * @param append If false, this file will be overwritten if it exists, otherwise it will be appended.
    264 	 * @param charset May be null to use the default charset.
    265 	 * @throw GdxRuntimeException if this file handle represents a directory, if it is a {@link FileType#Classpath} or
    266 	 *        {@link FileType#Internal} file, or if it could not be written. */
    267 	public void writeString (String string, boolean append, String charset) {
    268 		throw new GdxRuntimeException("Cannot write to files in GWT backend");
    269 	}
    270 
    271 	/** Writes the specified bytes to the file. Parent directories will be created if necessary.
    272 	 * @param append If false, this file will be overwritten if it exists, otherwise it will be appended.
    273 	 * @throw GdxRuntimeException if this file handle represents a directory, if it is a {@link FileType#Classpath} or
    274 	 *        {@link FileType#Internal} file, or if it could not be written. */
    275 	public void writeBytes (byte[] bytes, boolean append) {
    276 		throw new GdxRuntimeException("Cannot write to files in GWT backend");
    277 	}
    278 
    279 	/** Writes the specified bytes to the file. Parent directories will be created if necessary.
    280 	 * @param append If false, this file will be overwritten if it exists, otherwise it will be appended.
    281 	 * @throw GdxRuntimeException if this file handle represents a directory, if it is a {@link FileType#Classpath} or
    282 	 *        {@link FileType#Internal} file, or if it could not be written. */
    283 	public void writeBytes (byte[] bytes, int offset, int length, boolean append) {
    284 		throw new GdxRuntimeException("Cannot write to files in GWT backend");
    285 	}
    286 
    287 	/** Returns the paths to the children of this directory. Returns an empty list if this file handle represents a file and not a
    288 	 * directory. On the desktop, an {@link FileType#Internal} handle to a directory on the classpath will return a zero length
    289 	 * array.
    290 	 * @throw GdxRuntimeException if this file is an {@link FileType#Classpath} file. */
    291 	public FileHandle[] list () {
    292 		return preloader.list(file);
    293 	}
    294 
    295 	/** Returns the paths to the children of this directory that satisfy the specified filter. Returns an empty list if this file
    296 	 * handle represents a file and not a directory. On the desktop, an {@link FileType#Internal} handle to a directory on the
    297 	 * classpath will return a zero length array.
    298 	 * @throw GdxRuntimeException if this file is an {@link FileType#Classpath} file. */
    299 	public FileHandle[] list (FileFilter filter) {
    300 		return preloader.list(file, filter);
    301 	}
    302 
    303 	/** Returns the paths to the children of this directory that satisfy the specified filter. Returns an empty list if this file
    304 	 * handle represents a file and not a directory. On the desktop, an {@link FileType#Internal} handle to a directory on the
    305 	 * classpath will return a zero length array.
    306 	 * @throw GdxRuntimeException if this file is an {@link FileType#Classpath} file. */
    307 	public FileHandle[] list (FilenameFilter filter) {
    308 		return preloader.list(file, filter);
    309 	}
    310 
    311 	/** Returns the paths to the children of this directory with the specified suffix. Returns an empty list if this file handle
    312 	 * represents a file and not a directory. On the desktop, an {@link FileType#Internal} handle to a directory on the classpath
    313 	 * will return a zero length array.
    314 	 * @throw GdxRuntimeException if this file is an {@link FileType#Classpath} file. */
    315 	public FileHandle[] list (String suffix) {
    316 		return preloader.list(file, suffix);
    317 	}
    318 
    319 	/** Returns true if this file is a directory. Always returns false for classpath files. On Android, an {@link FileType#Internal}
    320 	 * handle to an empty directory will return false. On the desktop, an {@link FileType#Internal} handle to a directory on the
    321 	 * classpath will return false. */
    322 	public boolean isDirectory () {
    323 		return preloader.isDirectory(file);
    324 	}
    325 
    326 	/** Returns a handle to the child with the specified name.
    327 	 * @throw GdxRuntimeException if this file handle is a {@link FileType#Classpath} or {@link FileType#Internal} and the child
    328 	 *        doesn't exist. */
    329 	public FileHandle child (String name) {
    330 		return new GwtFileHandle(preloader, (file.isEmpty() ? "" : (file + (file.endsWith("/") ? "" : "/"))) + name,
    331 			FileType.Internal);
    332 	}
    333 
    334 	public FileHandle parent () {
    335 		int index = file.lastIndexOf("/");
    336 		String dir = "";
    337 		if (index > 0) dir = file.substring(0, index);
    338 		return new GwtFileHandle(preloader, dir, type);
    339 	}
    340 
    341 	public FileHandle sibling (String name) {
    342 		return parent().child(fixSlashes(name));
    343 	}
    344 
    345 	/** @throw GdxRuntimeException if this file handle is a {@link FileType#Classpath} or {@link FileType#Internal} file. */
    346 	public void mkdirs () {
    347 		throw new GdxRuntimeException("Cannot mkdirs with an internal file: " + file);
    348 	}
    349 
    350 	/** Returns true if the file exists. On Android, a {@link FileType#Classpath} or {@link FileType#Internal} handle to a directory
    351 	 * will always return false. */
    352 	public boolean exists () {
    353 		return preloader.contains(file);
    354 	}
    355 
    356 	/** Deletes this file or empty directory and returns success. Will not delete a directory that has children.
    357 	 * @throw GdxRuntimeException if this file handle is a {@link FileType#Classpath} or {@link FileType#Internal} file. */
    358 	public boolean delete () {
    359 		throw new GdxRuntimeException("Cannot delete an internal file: " + file);
    360 	}
    361 
    362 	/** Deletes this file or directory and all children, recursively.
    363 	 * @throw GdxRuntimeException if this file handle is a {@link FileType#Classpath} or {@link FileType#Internal} file. */
    364 	public boolean deleteDirectory () {
    365 		throw new GdxRuntimeException("Cannot delete an internal file: " + file);
    366 	}
    367 
    368 	/** Copies this file or directory to the specified file or directory. If this handle is a file, then 1) if the destination is a
    369 	 * file, it is overwritten, or 2) if the destination is a directory, this file is copied into it, or 3) if the destination
    370 	 * doesn't exist, {@link #mkdirs()} is called on the destination's parent and this file is copied into it with a new name. If
    371 	 * this handle is a directory, then 1) if the destination is a file, GdxRuntimeException is thrown, or 2) if the destination is
    372 	 * a directory, this directory is copied into it recursively, overwriting existing files, or 3) if the destination doesn't
    373 	 * exist, {@link #mkdirs()} is called on the destination and this directory is copied into it recursively.
    374 	 * @throw GdxRuntimeException if the destination file handle is a {@link FileType#Classpath} or {@link FileType#Internal} file,
    375 	 *        or copying failed. */
    376 	public void copyTo (FileHandle dest) {
    377 		throw new GdxRuntimeException("Cannot copy to an internal file: " + dest);
    378 	}
    379 
    380 	/** Moves this file to the specified file, overwriting the file if it already exists.
    381 	 * @throw GdxRuntimeException if the source or destination file handle is a {@link FileType#Classpath} or
    382 	 *        {@link FileType#Internal} file. */
    383 	public void moveTo (FileHandle dest) {
    384 		throw new GdxRuntimeException("Cannot move an internal file: " + file);
    385 	}
    386 
    387 	/** Returns the length in bytes of this file, or 0 if this file is a directory, does not exist, or the size cannot otherwise be
    388 	 * determined. */
    389 	public long length () {
    390 		return preloader.length(file);
    391 	}
    392 
    393 	/** Returns the last modified time in milliseconds for this file. Zero is returned if the file doesn't exist. Zero is returned
    394 	 * for {@link FileType#Classpath} files. On Android, zero is returned for {@link FileType#Internal} files. On the desktop, zero
    395 	 * is returned for {@link FileType#Internal} files on the classpath. */
    396 	public long lastModified () {
    397 		return 0;
    398 	}
    399 
    400 	public String toString () {
    401 		return file;
    402 	}
    403 
    404 	private static String fixSlashes(String path) {
    405 		path = path.replace('\\', '/');
    406 		if (path.endsWith("/")) {
    407 			path = path.substring(0, path.length() - 1);
    408 		}
    409 		return path;
    410 	}
    411 
    412 }
    413