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.core.BasicClass; 18 import org.as2lib.env.except.IllegalArgumentException; 19 import org.as2lib.env.reflect.PackageNotFoundException; 20 import org.as2lib.env.reflect.Cache; 21 import org.as2lib.env.reflect.PackageInfo; 22 import org.as2lib.env.reflect.ReflectConfig; 23 24 /** 25 * {@code PackageAlgorithm} searches for the specified package and returns the 26 * package info representing the found package. 27 * 28 * <p>To obtain the package info corresponding to package you use this class as 29 * follows. 30 * 31 * <code> 32 * var packageAlgorithm:PackageAlgorithm = new PackageAlgorithm(); 33 * var packageInfoByPackage:PackageInfo = packageAlgorithm.execute(org.as2lib.core); 34 * </code> 35 * 36 * <p>It is also possible to retrieve a package info by name. 37 * 38 * <code> 39 * packageInfoByName:PackageInfo = packageAlgorithm.executeByName("org.as2lib.core"); 40 * </code> 41 * 42 * <p>Already retrieved package infos are stored in a cache. There thus exists only 43 * one {@code PackageInfo} instance per package. The following traces {@code true}. 44 * 45 * <code> 46 * trace(packageInfoByPackage == packageInfoByName); 47 * </code> 48 * 49 * @author Simon Wacker 50 */ 51 class org.as2lib.env.reflect.algorithm.PackageAlgorithm extends BasicClass { 52 53 /** The chache. */ 54 private var c:Cache; 55 56 /** The found packages. */ 57 private var p; 58 59 /** 60 * Constructs a new {@code PackageAlgorithm} instance. 61 */ 62 public function PackageAlgorithm(Void) { 63 } 64 65 /** 66 * Sets the cache that is used by the {@link #execute} method to look whether the 67 * package to find is already stored and where to start the search if not. 68 * 69 * @param cache the new cache 70 */ 71 public function setCache(cache:Cache):Void { 72 c = cache; 73 } 74 75 /** 76 * Returns the cache set via the {@link #setCache} method or the default cache that 77 * is returned by the {@link ReflectConfig#getCache} method. 78 * 79 * @return the currently used cache 80 */ 81 public function getCache(Void):Cache { 82 if (!c) c = ReflectConfig.getCache(); 83 return c; 84 } 85 86 /** 87 * Executes the search for the passed-in package {@code o} and returns information 88 * about this package. 89 * 90 * <p>The returned object has the following properties: 91 * <dl> 92 * <dt>package</dt> 93 * <dd>The package as {@code Object} that has been searched for, this is the 94 * passed-in package {@code o}.</dd> 95 * <dt>name</dt> 96 * <dd>The name as {@code String} of the searched for package.</dd> 97 * <dt>parent</dt> 98 * <dd>The parent represented by a {@ling PackageInfo} instance the searched for 99 * package is a member of.</dd> 100 * </dl> 101 * 102 * <p>{@code null} will be returned if: 103 * <ul> 104 * <li>The passed-in package {@code o} is {@code null} or {@code undefined}.</li> 105 * <li>The searched for package {@code o} could not be found.</li> 106 * </ul> 107 * 108 * <p>The search starts on the package returned by the cache's {@code getRoot} 109 * method, this is by default {@code _global}. 110 * 111 * @param o the package to return information about 112 * @return an object that contains information about the passed-in package 113 */ 114 public function execute(o) { 115 if (o === null || o === undefined) return null; 116 p = null; 117 // must set access permissions because by default all package members in _global are hidden 118 _global.ASSetPropFlags(o, null, 0, true); 119 _global.ASSetPropFlags(o, ["__proto__", "constructor", "__constructor__", "prototype"], 1, true); 120 findAndStore(getCache().getRoot(), o); 121 return p; 122 } 123 124 private function findAndStore(a:PackageInfo, o):Boolean { 125 var b = a.getPackage(); 126 var i:String; 127 for (i in b) { 128 var e:Object = b[i]; 129 if (typeof(e) == "object") { 130 if (e.valueOf() == o.valueOf()) { 131 p = new Object(); 132 p.package = o; 133 p.name = i; 134 p.parent = a; 135 return true; 136 } 137 var d:PackageInfo = c.getPackage(e); 138 if (!d) { 139 d = c.addPackage(new PackageInfo(e, i, a)); 140 } 141 if (!d.isParentPackage(a)) { 142 // todo: replace recursion with loop 143 if (findAndStore(d, o)) { 144 return true; 145 } 146 } 147 } 148 } 149 return false; 150 } 151 152 /** 153 * Returns the package info representing the package corresponding to the passed-in 154 * package name {@code n}. 155 * 156 * <p>The name must be fully qualified, that means it must consist of the package's 157 * path as well as its name. For example 'org.as2lib.core'. 158 * 159 * <p>The search starts on the package returned by the {@link Cache#getRoot} method 160 * of the set cache. If this method returns a package info whose {@code getFullName} 161 * method returns {@code null}, {@code undefined} or an empty string {@code "_global"} 162 * is used instead 163 * 164 * @param n the fully qualified name of the package 165 * @return the package info representing the package corresponding to the passed-in 166 * name 167 * @throws IllegalArgumentException if the passed-in name is {@code null}, 168 * {@code undefined} or an empty string or if the object corresponding to the 169 * passed-in name is not of type {@code "object"} 170 * @throws PackageNotFoundException if a package with the passed-in name could not 171 * be found 172 */ 173 public function executeByName(n:String):PackageInfo { 174 if (!n) throw new IllegalArgumentException("The passed-in package name '" + n + "' is not allowed to be null, undefined or an empty string.", this, arguments); 175 var p:PackageInfo = getCache().getRoot(); 176 var x:String = p.getFullName(); 177 if (!x) x = "_global"; 178 var f:Object = eval(x + "." + n); 179 if (f === null || f === undefined) { 180 throw new PackageNotFoundException("A package with the name '" + n + "' could not be found.", this, arguments); 181 } 182 if (typeof(f) != "object") throw new IllegalArgumentException("The object corresponding to the passed-in package name '" + n + "' is not of type object.", this, arguments); 183 var r:PackageInfo = c.getPackage(f); 184 if (r) return r; 185 var a:Array = n.split("."); 186 var g:Object = p.getPackage(); 187 for (var i:Number = 0; i < a.length; i++) { 188 g = g[a[i]]; 189 p = c.addPackage(new PackageInfo(g, a[i], p)); 190 } 191 return p; 192 } 193 194 }