blob: 42bb4f979a9211ba62e774d7a14f39baa11f87f5 [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 org.apache.flex.ant
{
import flash.events.Event;
import flash.filesystem.File;
import flash.filesystem.FileMode;
import flash.filesystem.FileStream;
import org.apache.flex.ant.tags.Project;
import org.apache.flex.xml.XMLTagProcessor;
/**
* An XMLTagProcessor that tries to emulate Apache Ant
*/
public class Ant extends XMLTagProcessor
{
/**
* Constructor
*/
public function Ant()
{
super();
ant = this;
}
/**
* @private
* The file being processed. Used to determine basedir.
*/
private var file:File;
/**
* Open a file, read the XML, create ITagHandlers for every tag, then process them.
* When finished, check the project's status property. If it is true then all
* tasks completed successfully
* @param file File The file to open.
* @param context Object An object containing an optional targets property listing the targets to run.
* @return true if XML file was processed synchronously. If false, then add listener for Event.COMPLETE.
*/
public function processXMLFile(file:File, context:Object = null, callbackMode:Boolean = true):Boolean
{
Ant.ants.push(this);
this.file = file;
var fs:FileStream = new FileStream();
fs.open(file, FileMode.READ);
var s:String = fs.readUTFBytes(fs.bytesAvailable);
var xml:XML = XML(s);
fs.close();
tagMap = antTagProcessors;
if (!context)
context = {};
this.context = context;
var project:Project = processXMLTag(xml) as Project;
var basedir:String = project.basedir;
if (basedir == "")
basedir = ".";
try {
basedir = file.parent.resolvePath(basedir).nativePath;
}
catch (e:Error)
{
ant.output(basedir);
ant.output(e.message);
ant.project.failureMessage = e.message;
ant.project.status = false;
return true;
}
context.basedir = basedir;
this.project = project;
if (!project.execute(callbackMode, context))
{
project.addEventListener(Event.COMPLETE, completeHandler);
return false;
}
if (Ant.ants.length > 1)
{
var status:Boolean = ant.project.status;
var failureMessage:String = ant.project.failureMessage;
Ant.ants.pop();
if (!status)
{
currentAnt.project.status = status;
currentAnt.project.failureMessage = failureMessage;
}
}
return true;
}
private var _functionToCall:Function;
/**
* Set by various classes to defer processing in callbackMode
*/
public function set functionToCall(value:Function):void
{
if (parentAnt)
parentAnt.functionToCall = value;
else
_functionToCall = value;
}
public function get functionToCall():Function
{
return _functionToCall;
}
/**
* If you set callbackMode = true, you must call this method until you receive
* the Event.COMPLETE
*/
public function doCallback():void
{
if (functionToCall != null)
{
var f:Function = functionToCall;
functionToCall = null;
f();
}
}
/**
* the instance of the class dispatching progress events
*/
public var progressClass:Object;
private var context:Object;
public var parentAnt:Ant;
public var ant:Ant;
public var project:Project;
// the stack of ant instances (ant can call <ant/>)
public static var ants:Array = [];
public static function get currentAnt():Ant
{
if (ants.length == 0)
return null;
return ants[ants.length - 1] as Ant;
}
private function completeHandler(event:Event):void
{
if (Ant.ants.length > 1)
{
var status:Boolean = ant.project.status;
var failureMessage:String = ant.project.failureMessage;
Ant.ants.pop();
if (!status)
{
currentAnt.project.status = status;
currentAnt.project.failureMessage = failureMessage;
}
}
event.target.removeEventListener(Event.COMPLETE, completeHandler);
dispatchEvent(event);
}
/**
* The map of XML tags to classes
*/
public static var antTagProcessors:Object = {};
/**
* Adds a class to the map.
*
* @param tagQName String The QName.toString() of the tag
* @param processor Class The class that will process the tag.
*/
public static function addTagProcessor(tagQName:String, processor:Class):void
{
antTagProcessors[tagQName] = processor;
}
/**
* Does string replacements based on properties in the context object.
*
* @param input String The input string.
* @param context Object The object of property values.
* @return String The input string with replaced values.
*/
public function getValue(input:String, context:Object):String
{
var i:int = input.indexOf("${");
while (i != -1)
{
if (i == 0 || (i > 0 && input.charAt(i - 1) != "$"))
{
var c:int = input.indexOf("}", i);
if (c != -1)
{
var token:String = input.substring(i + 2, c);
if (context.hasOwnProperty(token))
{
var rep:String = context[token];
input = input.replace("${" + token + "}", rep);
i += rep.length - token.length - 3;
}
}
}
else if (i > 0 && input.charAt(i - 1) == "$")
{
input = input.substring(0, i - 1) + input.substring(i);
i++;
}
i++;
i = input.indexOf("${", i);
}
return input;
}
public static const spaces:String = " ";
public function formatOutput(tag:String, data:String):String
{
var s:String = spaces.substr(0, Math.max(spaces.length - tag.length - 2, 0)) +
"[" + tag + "] " + data;
return s;
}
public static function log(s:String, level:int):void
{
currentAnt.output(s);
}
/**
* Output for Echo. Defaults to trace().
*/
public var output:Function = function(s:String):void { trace(s) };
}
}