| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // 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 com.flexcapacitor.utils { |
| import com.flexcapacitor.events.InspectorEvent; |
| import com.flexcapacitor.graphics.LayoutLines; |
| import com.flexcapacitor.model.AccessorMetaData; |
| import com.flexcapacitor.model.MetaData; |
| import com.flexcapacitor.model.StyleMetaData; |
| import com.flexcapacitor.model.VisualElementVO; |
| |
| import flash.desktop.Clipboard; |
| import flash.desktop.ClipboardFormats; |
| import flash.display.DisplayObject; |
| import flash.display.DisplayObjectContainer; |
| import flash.events.IEventDispatcher; |
| import flash.utils.describeType; |
| import flash.utils.getQualifiedClassName; |
| import flash.utils.getQualifiedSuperclassName; |
| |
| import mx.collections.ArrayCollection; |
| import mx.collections.XMLListCollection; |
| import mx.core.IVisualElement; |
| import mx.core.IVisualElementContainer; |
| import mx.core.UIComponent; |
| import mx.managers.ISystemManager; |
| import mx.utils.DescribeTypeCache; |
| import mx.utils.DescribeTypeCacheRecord; |
| import mx.utils.NameUtil; |
| import mx.utils.ObjectUtil; |
| |
| import avmplus.DescribeType; |
| |
| public class ClassUtils { |
| |
| |
| public function ClassUtils() { |
| |
| |
| } |
| |
| /** |
| * @copy NameUtil.getUnqualifiedClassName() |
| * */ |
| public static function getClassName(element:Object):String { |
| var name:String = NameUtil.getUnqualifiedClassName(element); |
| return name; |
| } |
| |
| /** |
| * Get unqualified class name of the target object. <br/> |
| * |
| * If target has the id of myImage and include class name is true then the result is |
| * "Image.myImage". If delimiter is "_" then the result is "Image_myImage". |
| * If includeClassName is false then the result is, "myImage". |
| * */ |
| public static function getClassNameOrID(element:Object, includeClassName:Boolean = false, delimiter:String = "."):String { |
| var name:String = NameUtil.getUnqualifiedClassName(element); |
| var id:String = element && "id" in element ? element.id : null; |
| |
| return !id ? name : includeClassName ? name + "." + id : id; |
| } |
| |
| /** |
| * Get unqualified class name of the document of the target object |
| * returns null if element is not a UIComponent |
| * */ |
| public static function getDocumentName(element:Object):String { |
| var name:String; |
| if (element is UIComponent) { |
| name = NameUtil.getUnqualifiedClassName(UIComponent(element).document); |
| } |
| return name; |
| } |
| |
| /** |
| * Get the type of the value passed in |
| * */ |
| public static function getValueType(value:*):String { |
| var type:String = getQualifiedClassName(value); |
| |
| if (type=="int") { |
| if (typeof value=="number") { |
| type = "Number"; |
| } |
| } |
| |
| return type; |
| } |
| |
| /** |
| * Get fully qualified package of an object minus the class name. |
| * |
| * For example, if fully qualified class name is "mx.components::Button" then |
| * this method returns "mx.components". |
| * */ |
| public static function getPackageName(element:Object):String { |
| var name:String = flash.utils.getQualifiedClassName(element); |
| if (name && name.indexOf("::")) { |
| name = name.split("::")[0]; |
| } |
| |
| return name; |
| } |
| |
| /** |
| * Get super class name of the target object |
| * */ |
| public static function getSuperClassName(element:Object):String { |
| var name:String = flash.utils.getQualifiedSuperclassName(element); |
| if (name && name.indexOf("::")) { |
| name = name.split("::")[name.split("::").length-1]; // i'm sure theres a better way to do this |
| } |
| |
| return name; |
| } |
| |
| /** |
| * Get the package of the super class name of the target |
| * */ |
| public static function getSuperClassPackage(element:Object):String { |
| var name:String = flash.utils.getQualifiedSuperclassName(element); |
| if (name && name.indexOf("::")) { |
| name = name.split("::")[0]; |
| } |
| |
| return name; |
| } |
| |
| |
| /** |
| * Gets the ID of the target object |
| * |
| * @param name if id is not available then if true then use name |
| * @param className if id is not available then if true use class name |
| * |
| * returns id or name or class name or null if no ID is specified |
| * */ |
| public static function getIdentifierOrName(element:Object, name:Boolean = false, className:Boolean = false):String { |
| |
| if (element && "id" in element && element.id) { |
| return element.id; |
| } |
| else if (element && name && "name" in element && element.name) { |
| return element.name; |
| } |
| else if (element && className) { |
| return getClassName(element); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Get name of target object or null if not available |
| * */ |
| public static function getName(element:Object):String { |
| var name:String; |
| |
| if (element.hasOwnProperty("name") && element.name) { |
| name = element.name; |
| } |
| |
| return name; |
| } |
| |
| /** |
| * Get qualified class name of the target object. |
| * If normalize is true then replaces, "::" with "."; |
| * |
| * @copy flash.utils.getQualifiedClassName() |
| * */ |
| public static function getQualifiedClassName(element:Object, normalize:Boolean = false):String { |
| var name:String = flash.utils.getQualifiedClassName(element); |
| if (normalize && name) { |
| name = name.replace("::", "."); |
| } |
| return name; |
| } |
| |
| /** |
| * Checks if the source object is the same type as the target object. |
| * */ |
| public static function isSameClassType(source:Object, target:Object):Boolean { |
| if (source==null && target!=null) { |
| return false; |
| } |
| if (target==null && source!=null) { |
| return false; |
| } |
| |
| if (flash.utils.getQualifiedClassName(source) == flash.utils.getQualifiedClassName(target)) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * With the given target it returns a regexp pattern to find the exact instance in MXML |
| * If isScript is true it attempts to returns a pattern to find the exact instance in AS3 |
| * The MXML pattern will find the instance with that ID. If the instance doesn't have an ID it no worky. |
| * NOTE: Press CMD+SHIFT+F to and check regular expression in the Find in Files dialog |
| * */ |
| public static function getRegExpSearchPattern(target:DisplayObject, isScript:Boolean = false):String { |
| var id:String = getIdentifierOrName(target); |
| var className:String = NameUtil.getUnqualifiedClassName(target); |
| var pattern:String; |
| var scriptPattern:String; |
| |
| if (id == null) { |
| pattern = className + "(.*)"; |
| } |
| else { |
| pattern = className + "(.*)id\\s?=\\s?[\"|']" + id + "[\"|']"; |
| scriptPattern = id + ".addEventListener"; |
| } |
| |
| |
| if (isScript) { |
| return scriptPattern; |
| } |
| |
| return pattern; |
| } |
| |
| |
| /** |
| * Clears outline drawn around target display object |
| * */ |
| public static function clearSelection(target:Object, systemManager:ISystemManager, remove:Boolean = false):void { |
| LayoutLines.getInstance().clear(target, systemManager, remove); |
| } |
| |
| /** |
| * Draws outline around target display object |
| * */ |
| public static function drawSelection(target:Object, systemManager:ISystemManager):void { |
| LayoutLines.getInstance().drawLines(target, systemManager); |
| } |
| |
| /** |
| * Copy text to clipboard |
| * */ |
| public static function copyToClipboard(text:String, format:String=ClipboardFormats.TEXT_FORMAT):void { |
| Clipboard.generalClipboard.setData(ClipboardFormats.TEXT_FORMAT, text); |
| } |
| |
| /** |
| * Returns an array of display objects of type VisualElementVO |
| * Optionally returns elements |
| * */ |
| public static function getElementChildrenArray(displayObject:DisplayObject, getElements:Boolean = false, getSkins:Boolean = true):ArrayCollection { |
| var displayObject:DisplayObject = DisplayObject(displayObject); |
| var displayObjectContainer:DisplayObjectContainer; |
| |
| var visualElementContainer:IVisualElementContainer; |
| var visualElement:IVisualElement; |
| |
| var visualElementVO:VisualElementVO = new VisualElementVO(); |
| |
| var children:ArrayCollection = new ArrayCollection(); |
| |
| |
| // attempt to cast to a specific type and assign in the process |
| displayObjectContainer = displayObject as DisplayObjectContainer; |
| visualElementContainer = displayObject as IVisualElementContainer; |
| visualElement = displayObject as IVisualElement; |
| |
| |
| // gather all the display objects on the current display object |
| if (displayObjectContainer) { |
| |
| for (var bb:int = 0; bb < displayObjectContainer.numChildren; bb++) { |
| |
| // visualElementVO = createDisplayObjectVO(displayObjectContainer.getChildAt(bb)); |
| visualElementVO = VisualElementVO.unmarshall(displayObjectContainer.getChildAt(bb)); |
| children.addItem(visualElementVO); |
| } |
| } |
| |
| if (visualElementContainer && getElements) { |
| |
| for (var cc:int = 0; cc < visualElementContainer.numElements; cc++) { |
| //visualElementVO = createDisplayObjectVO(displayObjectContainer.getChildAt(cc)); |
| visualElementVO = VisualElementVO.unmarshall(DisplayObject(visualElementContainer.getElementAt(cc))); |
| children.addItem(visualElementVO); |
| } |
| } |
| |
| return children; |
| } |
| |
| /** |
| * Get ancestors of target |
| * |
| * @param target |
| * @param collection |
| * @param ancestors |
| * @return |
| */ |
| public static function getVisualElementsArray(target:DisplayObject, collection:Array, ancestors:int = 0):Array { |
| var vo:VisualElementVO; |
| |
| |
| // do the worm up the display list |
| while (target && ancestors>-1) { |
| |
| // store display element information |
| vo = VisualElementVO.unmarshall(target); |
| |
| // save reference to display element VO's for tree |
| if (!collection) collection = new Array(); |
| collection.push(vo); |
| |
| target = target.parent; |
| ancestors--; |
| |
| } |
| |
| return collection; |
| } |
| |
| /** |
| * Get the parent of the target that is also a UIComponent |
| * |
| * @return |
| */ |
| public static function getParentUIComponent(target:DisplayObject):UIComponent { |
| var found:Boolean; |
| |
| // run up the display list |
| while (target) { |
| target = target.parent; |
| |
| // check if next parent exists |
| if (!target) { |
| break; |
| } |
| |
| if (target is UIComponent) { |
| found = true; |
| break; |
| } |
| |
| } |
| |
| if (found) return UIComponent(target); |
| return null; |
| } |
| |
| /** |
| * Get the name of the target parent that is also a UIComponent |
| * |
| * @return |
| */ |
| public static function getParentUIComponentName(target:DisplayObject):String { |
| var parent:DisplayObject = getParentUIComponent(target); |
| var className:String = getClassName(parent); |
| return className; |
| } |
| |
| |
| /** |
| * Get parent document name |
| * |
| * @return null if target is not a UIComponent |
| */ |
| public static function getParentDocumentName(target:Object):String { |
| var className:String; |
| if (target is UIComponent) { |
| className = getClassName(target.parentDocument); |
| } |
| return className; |
| } |
| |
| |
| /** |
| * Get parent document name |
| * |
| * @return |
| */ |
| public static function getClassNameAndPackage(target:Object):Array { |
| var className:String; |
| var classPath:String; |
| |
| className = getClassName(target); |
| classPath = getPackageName(target); |
| |
| return [className, classPath]; |
| } |
| |
| |
| /** |
| * Dispatch target change event |
| * |
| * @return |
| */ |
| public static function dispatchTargetChangeEvent(target:Object, source:IEventDispatcher):void { |
| |
| // let other inspectors know there is a new target selected |
| var selectionChangeEvent:InspectorEvent = new InspectorEvent(InspectorEvent.CHANGE); |
| selectionChangeEvent.targetItem = target; |
| |
| // store previous targets in a dictionary |
| |
| if (source) { |
| source.dispatchEvent(selectionChangeEvent); |
| } |
| } |
| |
| |
| /** |
| * Change target. Use this instead of dispatchTargetChangeEvent() |
| * |
| * TODO: Add a weak reference to the old target in a static array for history type navigation |
| * |
| * @return |
| */ |
| public static function updateTarget(target:Object, source:IEventDispatcher):void { |
| // TODO: Add a weak reference to the old target in a static array for history type navigation |
| |
| // let other inspectors know there is a new target selected |
| var selectionChangeEvent:InspectorEvent = new InspectorEvent(InspectorEvent.CHANGE); |
| selectionChangeEvent.targetItem = target; |
| |
| if (source) { |
| source.dispatchEvent(selectionChangeEvent); |
| } |
| } |
| |
| /** |
| * Converts an integer to hexidecimal. |
| * For example, 16117809 returns "#EEEEEE" or something |
| * @return |
| */ |
| public static function convertIntToHex(item:Object):String { |
| var hex:String = Number(item).toString(16); |
| return ("00000" + hex.toUpperCase()).substr(-6); |
| } |
| |
| /** |
| * Get describeType data for the given class. |
| * Can take string, instance or class. |
| * */ |
| public static function getDescribeType(object:Object):XML { |
| var describedTypeRecord:mx.utils.DescribeTypeCacheRecord = mx.utils.DescribeTypeCache.describeType(object); |
| |
| return describedTypeRecord.typeDescription; |
| } |
| |
| /** |
| * Get AccessorMetaData data for the given property. |
| * */ |
| public static function getMetaDataOfProperty(target:Object, property:String):AccessorMetaData { |
| var describedTypeRecord:mx.utils.DescribeTypeCacheRecord = mx.utils.DescribeTypeCache.describeType(target); |
| var accessorMetaData:AccessorMetaData = new AccessorMetaData(); |
| var matches:XMLList = describedTypeRecord.typeDescription.accessor.(@name==property); |
| var node:XML; |
| |
| if (matches.length()>0) { |
| node = matches[0]; |
| accessorMetaData.unmarshall(node, target); |
| return accessorMetaData; |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Get StyleMetaData data for the given style. |
| * */ |
| public static function getMetaDataOfStyle(target:Object, style:String, superType:String = null):StyleMetaData { |
| var describedTypeRecord:mx.utils.DescribeTypeCacheRecord; |
| var matches:XMLList; |
| var styleMetaData:StyleMetaData; |
| var extendsClassList:XMLList; |
| var node:XML; |
| |
| if (superType) { |
| describedTypeRecord = mx.utils.DescribeTypeCache.describeType(superType); |
| } |
| else { |
| describedTypeRecord = mx.utils.DescribeTypeCache.describeType(target); |
| } |
| |
| if (superType || target is String) { |
| matches = describedTypeRecord.typeDescription.factory.metadata.(@name=="Style").arg.(@value==style); |
| } |
| else if (target is Object) { |
| matches = describedTypeRecord.typeDescription.metadata.(@name=="Style").arg.(@value==style); |
| } |
| |
| if (matches && matches.length()==0) { |
| |
| if (superType || target is String) { |
| extendsClassList = describedTypeRecord.typeDescription.factory.extendsClass; |
| } |
| else if (target is Object) { |
| extendsClassList = describedTypeRecord.typeDescription.extendsClass; |
| } |
| |
| var length:int = extendsClassList.length(); |
| |
| for (var i:int;i<length;i++) {
|
| var type:String = extendsClassList[i].@type; |
| if (type=="Class") return null; |
| return getMetaDataOfStyle(target, style, type);
|
| }
|
| |
| } |
| |
| if (matches.length()>0) { |
| node = matches[0].parent(); |
| styleMetaData = new StyleMetaData(); |
| styleMetaData.unmarshall(node, target); |
| return styleMetaData; |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Creates an array of metadata items for the given object type including inherited metadata. |
| * |
| * For example, if you give it a Spark Button class it gets all the |
| * information for it and then gets it's super class ButtonBase and |
| * adds all that information and so on until it gets to Object. <br/><br/> |
| * |
| * Usage:<br/><pre> |
| * var allStyles:XMLList = concatenateMetaDataXMLItems(myButton, "Style", new XMLList()); |
| * </pre> |
| * @param metaType The name of the data in the item name property. Either Style or Event |
| * @param existingItems The list of the data in the item name property |
| * */ |
| public static function concatenateMetaDataXMLItems(object:Object, metaType:String, existingItems:XMLList = null):XMLList { |
| var describedTypeRecord:mx.utils.DescribeTypeCacheRecord = mx.utils.DescribeTypeCache.describeType(object); |
| var typeDescription:* = describedTypeRecord.typeDescription; |
| var hasFactory:Boolean = typeDescription.factory.length()>0; |
| var factory:* = typeDescription.factory; |
| var extendsClass:XMLList = typeDescription.extendsClass; |
| var extendsLength:int = extendsClass.length(); |
| var list:XMLListCollection = new XMLListCollection(typeDescription.*); |
| // can be on typeDescription.metadata or factory.metadata |
| var isRoot:Boolean = object is String ? false : true; |
| var className:String = !isRoot ? object as String : getQualifiedClassName(object); |
| var itemsLength:int; |
| var itemsList:XMLList; |
| var existingItemsLength:int = existingItems ? existingItems.length() : 0; |
| var metaName:String; |
| var duplicateItems:Array = []; |
| |
| if (metaType.toLowerCase()=="style") { |
| metaType = "Style"; |
| } |
| else if (metaType.toLowerCase()=="event") { |
| metaType = "Event"; |
| } |
| |
| if (hasFactory) { |
| //itemsList = factory.metadata.(@name==name); property "name" won't work! no matches found |
| itemsList = factory.metadata.(@name==metaType); |
| } |
| else { |
| itemsList = typeDescription.metadata.(@name==metaType); |
| } |
| |
| itemsList = itemsList.copy(); |
| itemsLength = itemsList.length(); |
| |
| //trace("getting info on class : " + className + " for data on " + nAmEe ); |
| //trace(" items : " + itemsList); |
| |
| |
| for (var i:int;i<itemsLength;i++) { |
| var item:XML = XML(itemsList[i]); |
| metaName = item.arg[0].@value; |
| item.@name = metaName; |
| item.@metadataType = metaType; |
| item.@declaredBy = className; |
| //item.@className = className.indexOf("::")!=-1 ? className.split("::")[1] : className; |
| //continue; |
| |
| for (var j:int=0;j<existingItemsLength;j++) { |
| var existingItem:XML = existingItems[j]; |
| |
| if (metaName==existingItem.@name) { |
| delete itemsList[i]; |
| itemsLength--; |
| i--; |
| //trace("meta name: " + metaName); |
| //trace("Deleting style: " + existingItem.@name); |
| continue; |
| } |
| } |
| } |
| |
| |
| if (itemsLength>0) { |
| existingItems = new XMLList(existingItems.toString()+itemsList.toString()); |
| } |
| |
| if (isRoot && extendsLength>0) { |
| for (i=0;i<extendsLength;i++) { |
| var newClass:String = String(extendsClass[i].@type); |
| existingItems = concatenateMetaDataXMLItems(newClass, metaType, existingItems); |
| } |
| } |
| |
| return existingItems; |
| } |
| |
| |
| /** |
| * Sets the property on the target. Supports styles. |
| * We should probably switch to the set property method |
| * @return |
| */ |
| public static function setTargetProperty(target:Object, property:String, value:*, type:String = "String", isPropertyStyle:Object=null):void { |
| var newAssignedValue:* = TypeUtils.getTypedValue(value, type); |
| TypeUtils.applyProperty(target, property, newAssignedValue, type, isPropertyStyle); |
| } |
| |
| |
| /** |
| * @copy spark.components.gridClasses.GridItemEditor#save(); |
| */ |
| public static function setProperty(target:Object, field:String, value:*):Boolean { |
| |
| var newData:Object = value; |
| var property:String = field; |
| var data:Object = target; |
| var typeInfo:String = ""; |
| |
| for each(var variable:XML in describeType((data).variable)) { |
| if (property == variable.@name.toString()) { |
| typeInfo = variable.@type.toString(); |
| break; |
| } |
| } |
| |
| if (typeInfo == "String") { |
| if (!(newData is String)) |
| newData = newData.toString(); |
| } |
| else if (typeInfo == "uint") { |
| if (!(newData is uint)) |
| newData = uint(newData); |
| } |
| else if (typeInfo == "int") { |
| if (!(newData is int)) |
| newData = int(newData); |
| } |
| else if (typeInfo == "Number") { |
| if (!(newData is Number)) |
| newData = Number(newData); |
| } |
| else if (typeInfo == "Boolean") { |
| if (!(newData is Boolean)) { |
| var strNewData:String = newData.toString(); |
| |
| if (strNewData) { |
| newData = (strNewData.toLowerCase() == "true") ? true : false; |
| } |
| } |
| } |
| |
| if (property && data[property] !== newData) { |
| data[property] = newData; |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Returns <code>true</code> if the object reference specified |
| * is a simple data type. The simple data types include the following: |
| * <ul> |
| * <li><code>String</code></li> |
| * <li><code>Number</code></li> |
| * <li><code>uint</code></li> |
| * <li><code>int</code></li> |
| * <li><code>Boolean</code></li> |
| * <li><code>Date</code></li> |
| * <li><code>Array</code></li> |
| * </ul> |
| * |
| * @param value Object inspected. |
| * |
| * @return <code>true</code> if the object specified |
| * is one of the types above; <code>false</code> otherwise. |
| * */ |
| public static function isSimple(value:*):Boolean { |
| return ObjectUtil.isSimple(value); |
| } |
| } |
| } |