blob: 937603ccd32dc3a35a842782e65fe22ffe06043e [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
package UnitTest.Fixtures
{
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.SecurityErrorEvent;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.net.URLRequestMethod;
import mx.utils.LoaderUtil;
/** Gateway class for reading and caching files used by the test harness code.
*/
public class FileRepository
{
/** Get the named file.
* @param fileName name of the file we're looking for
* @return String that contains the content of the file
*/
static public function getFile(baseURL:String, fileName:String):String
{
return CustomURLLoader.getFile(LoaderUtil.createAbsoluteURL(baseURL,fileName));
}
/** Get the named file as an XML object.
* File will be converted assuming whitespace is significant.
* @param fileName of the file we're looking for
* @return String that contains the content of the file
*/
static public function getFileAsXML(baseURL:String, fileName:String):XML
{
var xmlData:XML = null;
var sourceString:String = getFile(baseURL,fileName);
return sourceString ? convertToXML(sourceString) : null;
}
/** Convert from string to XML */
static private function convertToXML(sourceString:String):XML
{
var xmlData:XML = null;
// Convert string data to XML
var originalSettings:Object = XML.settings();
try
{
XML.ignoreProcessingInstructions = false;
XML.ignoreWhitespace = false;
xmlData = new XML(sourceString);
}
finally
{
XML.setSettings(originalSettings);
}
return xmlData;
}
/**
* Reads in a file and set up a handler for when the file read is complete
*/
static public function readFile(baseURL:String, fileName:String, handler:Function = null, errorHandler:Function = null, securityHandler:Function = null, ignoreWhitespace:Boolean = false): void
{
if (fileName == null || fileName.length <= 0)
return;
var tcURL:URLRequest = new URLRequest(LoaderUtil.createAbsoluteURL(baseURL,fileName));
tcURL.method = URLRequestMethod.GET;
var tcLoader:URLLoader = new CustomURLLoader(handler, errorHandler, securityHandler);
tcLoader.load(tcURL);
}
/**
* Returns true if there are pending requests, false if not
*/
static public function pendingRequests(): Boolean
{
return (CustomURLLoader.requestsPending != 0);
}
}
}
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.events.Event;
import flash.events.SecurityErrorEvent;
import flash.events.IOErrorEvent;
import flashx.textLayout.debug.assert;
/** Serves as a single bottleneck for all requests that go through
* FileRepository. Requests come in as URLLoader.load calls, and we
* always listen for completion, error, and security error. If a handler
* for any of these is passed in when the CustomURLLoader is constructed,
* we will in addition call the handler.
*
* Override URLLoader so we can get the request back when we're
* in a completion function. Also tracks outstanding requests so we
* can block until results are complete.
*/
class CustomURLLoader extends URLLoader
{
public var request: URLRequest; // original request for file load
public var completeHandler:Function; // custom completion handler
public var ioErrorHandler:Function; // custom i/o error handler
public var securityErrorHandler:Function; // custom security error handler
/** Cache containing all files that have been requested.
* The key is the name of the file as given in the original request, and the
* value is the contents of the file as a string. */
static private var _fileCache:Object;
/** Number of file read requests that are pending -- i.e., that have neither
* completed successfully nor returned errors.
*/
static private var _requestsPending:int = 0;
static private var FILE_ERROR:String = "$$$NOT_FOUNDXXX" // unique string to signal file read error
/** Constructor
* Note that if you specify all three handlers, one will be called depending on the outcome of the
* read.
*
* @param completeHandler - custom handler called on successful completion of file read
* @param ioErrorHandler - custom handler called on i/o error of file read
* @param securityErrorHandler - custom handler called on security error of file read
*/
public function CustomURLLoader(completeHandler:Function, ioErrorHandler:Function = null, securityErrorHandler:Function = null)
{
this.completeHandler = completeHandler;
this.ioErrorHandler = ioErrorHandler;
this.securityErrorHandler = securityErrorHandler;
if (!_fileCache)
_fileCache = new Object();
}
/** Returns number of file read requests that are pending -- i.e., that have neither
* completed successfully nor returned errors.
*/
static public function get requestsPending():int
{
return _requestsPending;
}
/** Given the name of a file, look it up in the cache and return the contents as a string. */
static public function getFile(fileName:String):String
{
// If it's in the cache, just return it
if (_fileCache[fileName] != null)
return _fileCache[fileName];
// We have a request out, and we're waiting for the result. Unfortunately, there's no
// way we know to wait and still handle the events that would cause the pending status
// to complete. So we return failure here as well.
if (_fileCache.hasOwnProperty(fileName))
return null;
// We've never seen this file
return null;
}
/** Add a new file to the cache. Takes the name of the file, as given in the URLRequest,
* and the file contents as a string. */
static private function addToCache(urlLoader:CustomURLLoader, data:String):void
{
CONFIG::debug { assert(_fileCache[getFileKey(urlLoader)] == null, "Adding over existing cache entry!"); }
_fileCache[getFileKey(urlLoader)] = data;
}
/** Default handler. This will get called on every successful completion of a read that goes
* through CustomURLLoader. It's job is to add the file to the file cache, and call the
* custom completion handler, if one was specified.
*/
static private function defaultCompleteHandler(event:Event):void
{
// Remove the event listener that was attached so this function could get called.
if (event)
event.target.removeEventListener(Event.COMPLETE, defaultCompleteHandler);
// This request handled; is no longer pending
--_requestsPending;
// Add the new file to the cache
var urlLoader:CustomURLLoader = CustomURLLoader(event.target);
addToCache(urlLoader, urlLoader.data);
// Call the custom completion handler
if (urlLoader.completeHandler != null)
urlLoader.completeHandler(event);
urlLoader.close();
}
/** Default handler. This will get called on every security error of a read that goes
* through CustomURLLoader. It's job is to update the file cache, and call the
* custom security error handler, if one was specified.
*/
static private function defaultSecurityErrorHandler(event:SecurityErrorEvent):void
{
if (event)
event.target.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, defaultSecurityErrorHandler);
--_requestsPending;
var urlLoader:CustomURLLoader = CustomURLLoader(event.target);
addToCache(urlLoader, FILE_ERROR);
if (urlLoader.securityErrorHandler != null)
urlLoader.securityErrorHandler(event);
}
/** Default handler. This will get called on every i/o error of a read that goes
* through CustomURLLoader. It's job is to update the file cache, and call the
* custom i/o error handler, if one was specified.
*/
static private function defaultIOErrorHandler(event:IOErrorEvent):void
{
if (event)
event.target.removeEventListener(IOErrorEvent.IO_ERROR, defaultIOErrorHandler);
--_requestsPending;
var urlLoader:CustomURLLoader = CustomURLLoader(event.target);
addToCache(urlLoader, FILE_ERROR);
if (urlLoader.ioErrorHandler != null)
urlLoader.ioErrorHandler(event);
}
/* Start a file read.
* @param request - URL request for the read
*/
override public function load(request:URLRequest):void
{
this.request = request;
// If we have already read this file in, or we are already in the middle of reading it in, don't make another request
if (_fileCache.hasOwnProperty(getFileKey(this)))
{
CONFIG::debug { assert (completeHandler == null, "Load has file cached, won't be calling completeHandler! You should call get() before calling readFile()"); }
return;
}
// Add it to the cache as a null entry, to signal there's a request pending on it
_fileCache[getFileKey(this)] = null;
// Attach listeners so the default handlers get called.
addEventListener(Event.COMPLETE,defaultCompleteHandler, false, 0, true);
addEventListener(SecurityErrorEvent.SECURITY_ERROR,defaultSecurityErrorHandler, false, 0, true);
addEventListener(IOErrorEvent.IO_ERROR, defaultIOErrorHandler, false, 0, true);
++_requestsPending;
super.load(request);
}
/** Given a URLLoader, return the key for access the file cache. Right now, we
* just use the file name for this.
*/
static private function getFileKey(urlLoader:CustomURLLoader):String
{
return urlLoader.request.url;
}
}