//////////////////////////////////////////////////////////////////////////////// // // 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 mx.automation.tool { import mx.automation.AutomationClass; import mx.automation.IAutomationClass; import mx.automation.IAutomationClass2; import mx.automation.IAutomationEnvironment; import mx.automation.IAutomationEventDescriptor; import mx.automation.IAutomationMethodDescriptor; import mx.automation.IAutomationObject; import mx.resources.IResourceManager; import mx.resources.ResourceManager; [ResourceBundle("automation_agent")] /** * @private */ public class ToolEnvironment implements IAutomationEnvironment { //-------------------------------------------------------------------------- // // Constructor // //-------------------------------------------------------------------------- /** * @private */ public function ToolEnvironment(source:XML) { super(); for (var i:Object in source.ClassInfo) { // populate the class/automationClass map var classInfoXML:XML = source.ClassInfo[i]; var automationClassName:String = classInfoXML.@Name.toString(); var superClassName:String = classInfoXML.@Extends && classInfoXML.@Extends.length != 0 ? classInfoXML.@Extends.toString() : null; var automationClass:ToolAutomationClass = new ToolAutomationClass(automationClassName, superClassName); automationClassName2automationClass[automationClassName] = automationClass; for (var j:Object in classInfoXML.Implementation) { var implementationXML:XML = classInfoXML.Implementation[j]; var realClassName:String = implementationXML.@Class.toString().replace("::", "."); var versionObj:Object = implementationXML.@Version; var versionNum:String = versionObj.toString(); if(versionNum!= "") { realClassName = realClassName+"_"+versionNum; automationClass.implementationVersion = int(versionNum); } else { automationClass.implementationVersion = 0; } className2automationClass[realClassName] = automationClass; automationClass.addImplementationClassName(realClassName); } // for each method for (var k:Object in classInfoXML.TypeInfo.Operation) { var operationXML:XML = classInfoXML.TypeInfo.Operation[k]; var automationMethodName:String = operationXML.@Name.toString(); var eventClassName:String = operationXML.Implementation.@Class.toString(); eventClassName = eventClassName.replace("::", "."); var eventType:String = operationXML.Implementation.@Type.toString(); if (eventType) { var args:Array = []; for (var m:Object in operationXML.Argument) { var argXML:XML = operationXML.Argument[m]; var argName:String = argXML.@Name.toString(); var argType:String = argXML.Type.@VariantType.toString().toLowerCase(); var argCodec:String = argXML.Type.@Codec.toString(); var defaultValue:String = argXML.@DefaultValue.length() > 0 ? argXML.@DefaultValue.toString() : null; var pd:ToolPropertyDescriptor = new ToolPropertyDescriptor( argName, true, true, argType, (argCodec == null || argCodec.length == 0 ? "object" : argCodec), defaultValue); args.push(pd); } var returnType:String = operationXML.ReturnValueType.Type.@VariantType.toString(); var codecName:String = operationXML.ReturnValueType.Type.@Codec.toString(); if(eventClassName) { var ed:IAutomationEventDescriptor = new ToolEventDescriptor( automationMethodName, eventClassName, eventType, args); automationClass.addEvent(ed); } else { var md:IAutomationMethodDescriptor = new ToolMethodDescriptor( automationMethodName, eventType, returnType, codecName, args); automationClass.addMethod(md); } } } for (var p:Object in classInfoXML.Properties.Property) { var propertyXML:XML = classInfoXML.Properties.Property[p]; var propName:String = propertyXML.@Name.toString(); var propType:String = propertyXML.Type.@VariantType.toString().toLowerCase(); var propCodec:String = propertyXML.Type.@Codec.toString(); var pd1:ToolPropertyDescriptor = new ToolPropertyDescriptor( propName, propertyXML.@ForDescription.toString() == "true", propertyXML.@ForVerification.toString() == "true", propType, (propCodec == null || propCodec.length == 0 ? "object" : propCodec)); automationClass.addPropertyDescriptor(pd1); } } } //-------------------------------------------------------------------------- // // Variables // //-------------------------------------------------------------------------- /** * @private */ private var className2automationClass:Object = {}; /** * @private */ private var automationClassName2automationClass:Object = {}; /** * @private * Used for accessing localized Error messages. */ private var resourceManager:IResourceManager = ResourceManager.getInstance(); //-------------------------------------------------------------------------- // // Methods // //-------------------------------------------------------------------------- /** * @private */ public function getAutomationClassByInstance( obj:IAutomationObject):IAutomationClass { var result:IAutomationClass = findClosestAncestor(obj); if (! result) { var message:String = resourceManager.getString( "automation_agent", "autClassNotFound", [AutomationClass.getClassName(obj), obj]); throw new Error(message); } return result; } /** * @private */ public function getAutomationClassByName( automationClass:String):IAutomationClass { return ToolAutomationClass( automationClassName2automationClass[automationClass]); } /** * Finds the closest ancestor to this object about which information was * passed in the environment XML. * @private */ private function findClosestAncestor(obj:Object):IAutomationClass { var className:String = AutomationClass.getClassName(obj).concat('_').concat(AutomationClass.getMajorVersion()); var version:int = int(className.substr(className.length-1)); className = className.substring(0, className.length - 2); for(var i:int = version; i > 0; i--){ var classNameWithVersion:String = className.concat('_').concat(i); if (classNameWithVersion in className2automationClass) return className2automationClass[classNameWithVersion]; } if (className in className2automationClass) return className2automationClass[className]; var ancestors:Array = findAllAncestors(obj); if (ancestors.length != 0) { className2automationClass[className] = getClosestVersionAncestor(ancestors); return className2automationClass[className]; } else { return null; } } /** * @private */ private function getClosestVersionAncestor(ancestors:Array):IAutomationClass { //There can be many ancestors of same class but with different versions //We need to find the ancestor which is closer in version to current version var closeAncestors:Array = []; closeAncestors.push(ancestors[0]); var n:int = ancestors.length; var isAncestor:Boolean = false; for( var i:int = 1; i < n; i++ ) { var firstSuperClass:String = ancestors[0].superClassName; var secondAncestor:IAutomationClass = ancestors[i]; while( firstSuperClass ) { if( firstSuperClass == secondAncestor.name ) { isAncestor = true; break; } else { firstSuperClass = getAutomationClassByName(firstSuperClass).superClassName; } } if( !isAncestor ) { var temp:Array = []; temp.push(ancestors[i]); closeAncestors = temp.concat(closeAncestors); } else { break; } } var currentVersion:int = int(AutomationClass.getMajorVersion()); var closestAncestor:IAutomationClass = closeAncestors[0]; if(closeAncestors[0] is IAutomationClass2) { var currentAncestorVersion:int = IAutomationClass2(closeAncestors[0]).implementationVersion; n = closeAncestors.length; for (i = 1; i < n; i++) { var nextAncestorVersion:int = IAutomationClass2(closeAncestors[i]).implementationVersion; if( currentAncestorVersion > currentVersion ) currentAncestorVersion = nextAncestorVersion; if( nextAncestorVersion <= currentVersion && nextAncestorVersion >= currentAncestorVersion ) { closestAncestor = closeAncestors[i]; currentAncestorVersion = nextAncestorVersion; } } } return closestAncestor; } /** * @private */ private function findAllAncestors(obj:Object):Array { var result:Array = []; for (var i:String in className2automationClass) { var temp:String = i; if(temp.indexOf("_") != -1) temp = temp.substring(0, i.lastIndexOf("_")); var realClass:Class = AutomationClass.getDefinitionFromObjectDomain(obj,temp); if (realClass && obj is realClass) result.push(className2automationClass[i]); } result.sort(sortAncestors); return result; } /** * @private */ private function sortAncestors(a:IAutomationClass, b:IAutomationClass):int { var superClass:IAutomationClass; var x:String = a.superClassName; while (x) { if (x == b.name) return -1; superClass = getAutomationClassByName(x); x = superClass.superClassName; } x = b.superClassName; while (x) { if (x == a.name) return 1; superClass = getAutomationClassByName(x); x = superClass.superClassName; } // Bad things will happen if we return 0... maybe throw an error? return 0; } } }