1 /* 2 * Copyright the original author or authors. 3 * 4 * Licensed under the MOZILLA PUBLIC LICENSE, Version 1.1 (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.mozilla.org/MPL/MPL-1.1.html 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 import org.as2lib.io.file.TextFileFactory; 18 import org.as2lib.io.file.TextFile; 19 import org.as2lib.io.file.File; 20 import org.as2lib.data.type.Byte; 21 import org.as2lib.data.holder.Iterator; 22 import org.as2lib.data.holder.Map; 23 import org.as2lib.env.except.IllegalArgumentException; 24 import org.as2lib.io.file.FileLoader; 25 import org.as2lib.io.file.AbstractFileLoader; 26 import org.as2lib.io.file.SimpleTextFileFactory; 27 import org.as2lib.io.file.FileNotLoadedException; 28 import org.as2lib.app.exec.Executable; 29 30 /** 31 * {@code TextFileLoader} is a implementation of {@link FileLoader} for text resources. 32 * 33 * <p>Any ASCII/Unicode readable resource is ment as "Text resource". {@code TextFileLoader} 34 * is a implementation for those resources and generates a {@code TextFile} 35 * implementation with the loaded content. 36 * 37 * <p>{@link TextFileFactory} allows to generate different {@code TextFile} 38 * implementations for the loaded content. 39 * 40 * <p>{@code TextFileLoader} represents the time consuming part of accessing files 41 * ({@code TextFile} is the handleable part} and therefore contains a event system 42 * to add listeners to listen to the concrete events. It is possible to add 43 * listeners using {@code addListener}. 44 * 45 * <p>Example listener: 46 * <code> 47 * import org.as2lib.io.file.AbstractFileLoader; 48 * import org.as2lib.io.file.LoadProgressListener; 49 * import org.as2lib.io.file.LoadStartListener; 50 * import org.as2lib.io.file.LoadCompleteListener; 51 * import org.as2lib.io.file.LoadErrorListener; 52 * import org.as2lib.io.file.FileLoader; 53 * import org.as2lib.io.file.TextFile; 54 * 55 * class MyFileListener implements 56 * LoadProgressListener, LoadStartListener, 57 * LoadCompleteListener, LoadErrorListener { 58 * 59 * public function onLoadComplete(fileLoader:FileLoader):Void { 60 * var file:TextFile = TextFile(fileLoader.getFile()); 61 * if (file != null) { 62 * // Proper file available 63 * } else { 64 * // Wrong event handled 65 * } 66 * } 67 * 68 * public function onLoadError(fileLoader:FileLoader, errorCode:String, error):Void { 69 * if (errorCode == AbstractFileLoader.FILE_NOT_FOUND) { 70 * var notExistantUrl = error; 71 * // Use that url 72 * } 73 * } 74 * 75 * public function onLoadStart(fileLoader:FileLoader) { 76 * // show that this file just gets loaded 77 * } 78 * 79 * public function onLoadProgress(fileLoader:FileLoader) { 80 * // update the percentage display with resourceLoader.getPercentage(); 81 * } 82 * } 83 * </code> 84 * 85 * <p>Example of the usage: 86 * <code> 87 * import org.as2lib.io.file.TextFileLoader; 88 * 89 * var fileLoader:TextFileLoader = new TextFileLoader(); 90 * fileLoader.addListener(new MyFileListener()); 91 * fileLoader.load("test.txt"); 92 * </code> 93 * 94 * @author Martin Heidegger 95 * @version 1.1 96 */ 97 class org.as2lib.io.file.TextFileLoader extends AbstractFileLoader implements FileLoader { 98 99 /** {@code LoadVars} instance for loading the content. */ 100 private var helper:LoadVars; 101 102 /** Result of the loaded {@code uri}. */ 103 private var textFile:TextFile; 104 105 /** Factory to create the concrete {@code TextFile} instances. */ 106 private var textFileFactory:TextFileFactory; 107 108 /** 109 * Constructs a new {@code TextFileLoader}. 110 * 111 * @param textFileFactory (optional) {@code TextFileFactory to create the 112 * {@code TextFile} implementations, {@link SimpleTextFileFactory} 113 * gets used if no custom {@code TextFileFactory} gets passed-in 114 */ 115 public function TextFileLoader(textFileFactory:TextFileFactory) { 116 if (!textFileFactory) { 117 textFileFactory = new SimpleTextFileFactory(); 118 } 119 this.textFileFactory = textFileFactory; 120 } 121 122 /** 123 * Loads a certain text file by a http request. 124 * 125 * <p>It sends http request by using the passed-in {@code uri}, {@code method} 126 * and {@code parameters}. The responding file will be passed to the set 127 * {@code TextFileFactory}. 128 * 129 * <p>If you only need to listen if the {@code TextFile} finished loading you can 130 * apply a {@code callBack} that gets called if the {@code TextFile} is loaded. 131 * 132 * @param uri location of the resource to load 133 * @param parameters (optional) parameters for loading the resource 134 * @param method (optional) POST/GET as method for submitting the parameters, 135 * default method used if {@code method} was not passed-in is POST. 136 * @param callBack (optional) {@link Executable} to be executed after the 137 * the resource was loaded. 138 */ 139 public function load(uri:String, method:String, parameters:Map, callBack:Executable):Void { 140 super.load(uri, method, parameters, callBack); 141 initHelper(); 142 if (uri == null) { 143 throw new IllegalArgumentException("Url has to be set for starting the process.", this, arguments); 144 } else { 145 if (parameters) { 146 if (method == "POST") { 147 var keys:Iterator = parameters.keyIterator(); 148 while (keys.hasNext()) { 149 var key = keys.next(); 150 helper[key.toString()] = parameters.get(key); 151 } 152 helper["sendAndLoad"](uri, this, method); 153 } else { 154 var result:String = uri; 155 if (uri.indexOf("?") == -1) { 156 result += "?"; 157 } 158 var keys:Iterator = parameters.keyIterator(); 159 while (keys.hasNext()) { 160 var key = keys.next(); 161 uri += _global.encode(key.toString()) + "=" + _global.encode(parameters.get(key).toString()); 162 } 163 helper.load(uri); 164 } 165 } else { 166 helper.load(uri); 167 } 168 sendStartEvent(); 169 } 170 } 171 172 /** 173 * Returns the loaded {@code File}. 174 * 175 * @return {@code File} that has been loaded 176 * @throws FileNotLoadedException if the resource has not been loaded yet 177 */ 178 public function getFile(Void):File { 179 return getFile(); 180 } 181 182 /** 183 * Returns the loaded {@code TextFile}. 184 * 185 * @return {@code TextFile} that has been loaded 186 * @throws FileNotLoadedException if the resource has not been loaded yet 187 */ 188 public function getTextFile(Void):TextFile { 189 if (textFile == null) { 190 throw new FileNotLoadedException("No File has been loaded.", this, arguments); 191 } 192 return textFile; 193 } 194 195 /** 196 * Prepares the helper property {@link helper} for the loading process. 197 */ 198 private function initHelper(Void):Void { 199 var owner:TextFileLoader = this; 200 helper = new LoadVars(); 201 // Watching _bytesLoaded allows realtime events 202 helper.watch( 203 "_bytesLoaded", 204 function(prop, oldValue, newValue) { 205 // Prevent useless events. 206 if(newValue != oldValue && newValue > 0) { 207 owner["handleUpdateEvent"](); 208 } 209 return newValue; 210 } 211 ); 212 213 // Using LoadVars Template to get the onData Event. 214 helper.onData = function(data) { 215 owner["handleDataEvent"](data); 216 }; 217 } 218 219 /** 220 * Returns the total amount of bytes that has been loaded. 221 * 222 * <p>Returns {@code null} if its not possible to get the loaded bytes. 223 * 224 * @return amount of bytes that has been loaded 225 */ 226 public function getBytesLoaded(Void):Byte { 227 var result:Number = helper.getBytesLoaded(); 228 if (result >= 0) { 229 return new Byte(result); 230 } 231 return null; 232 } 233 234 /** 235 * Returns the total amount of bytes that will approximately be loaded. 236 * 237 * <p>Returns {@code null} if its not possible to get the total amount of bytes. 238 * 239 * @return amount of bytes to load 240 */ 241 public function getBytesTotal(Void):Byte { 242 var total:Number = helper.getBytesTotal(); 243 if (total >= 0) { 244 return new Byte(total); 245 } 246 return null; 247 } 248 249 /** 250 * Handles a update event from the helper. 251 * 252 * @see #initHelper 253 */ 254 private function handleUpdateEvent(Void):Void { 255 sendProgressEvent(); 256 } 257 258 /** 259 * Handles a data event from the helper. 260 * 261 * @see #initHelper 262 */ 263 private function handleDataEvent(data:String):Void { 264 finished = true; 265 started = false; 266 endTime = getTimer(); 267 helper.onLoad = function() {}; 268 helper.unwatch("_bytesLoaded"); 269 // Check if the file was not available. 270 if(typeof data == "undefined") { 271 // Dispatching the event for the missing uri. 272 sendErrorEvent(FILE_NOT_FOUND_ERROR, uri); 273 } else { 274 // Correct replacing of special line breaks that don't match the "\n" (Windows & Mac Line Breaks). 275 textFile = textFileFactory.createTextFile(data, getBytesTotal(), uri); 276 // Dispatching the event for the loaded file. 277 sendCompleteEvent(); 278 } 279 } 280 }