|
| 1 | +/* |
| 2 | + * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved. |
| 3 | + * |
| 4 | + * Sun Microsystems, Inc. has intellectual property rights relating to technology embodied in the product |
| 5 | + * that is described in this document. In particular, and without limitation, these intellectual property |
| 6 | + * rights may include one or more of the U.S. patents listed at http://www.sun.com/patents and one or |
| 7 | + * more additional patents or pending patent applications in the U.S. and in other countries. |
| 8 | + * |
| 9 | + * U.S. Government Rights - Commercial software. Government users are subject to the Sun |
| 10 | + * Microsystems, Inc. standard license agreement and applicable provisions of the FAR and its |
| 11 | + * supplements. |
| 12 | + * |
| 13 | + * Use is subject to license terms. Sun, Sun Microsystems, the Sun logo, Java and Solaris are trademarks or |
| 14 | + * registered trademarks of Sun Microsystems, Inc. in the U.S. and other countries. All SPARC trademarks |
| 15 | + * are used under license and are trademarks or registered trademarks of SPARC International, Inc. in the |
| 16 | + * U.S. and other countries. |
| 17 | + * |
| 18 | + * UNIX is a registered trademark in the U.S. and other countries, exclusively licensed through X/Open |
| 19 | + * Company, Ltd. |
| 20 | + */ |
| 21 | +package com.sun.max; |
| 22 | + |
| 23 | +import java.lang.reflect.*; |
| 24 | +import java.util.*; |
| 25 | + |
| 26 | +import com.sun.max.lang.*; |
| 27 | +import com.sun.max.program.*; |
| 28 | + |
| 29 | +/** |
| 30 | + * Describes a package in the com.sun.max project, |
| 31 | + * providing programmatic package information manipulation, |
| 32 | + * which is lacking in java.lang.Package. |
| 33 | + * <p> |
| 34 | + * You must create a class called 'Package extends MaxPackage' |
| 35 | + * in each and every package in this project. |
| 36 | + * <p> |
| 37 | + * For example you can call: |
| 38 | + * |
| 39 | + * new com.sun.max.program.Package().superPackage() |
| 40 | + * |
| 41 | + * Also make sure that you have a file called 'package-info.java' in every package. |
| 42 | + * This is where you can put package-related JavaDoc comments. |
| 43 | + * |
| 44 | + * @author Bernd Mathiske |
| 45 | + * @author Doug Simon |
| 46 | + */ |
| 47 | +public abstract class MaxPackage implements Comparable<MaxPackage> { |
| 48 | + |
| 49 | + private final String packageName; |
| 50 | + private static final Class[] NO_CLASSES = new Class[0]; |
| 51 | + |
| 52 | + protected MaxPackage() { |
| 53 | + packageName = toJava().getName(); |
| 54 | + assert getClass().getSimpleName().equals(Package.class.getSimpleName()); |
| 55 | + } |
| 56 | + |
| 57 | + /** |
| 58 | + * Gets an instance of the class named "Package" in a named package. |
| 59 | + * |
| 60 | + * @param packageName denotes the name of a package which may contain a subclass of {@link MaxPackage} named |
| 61 | + * "Package" |
| 62 | + * @return an instance of the class name "Package" in {@code packageName}. If such a class does not exist or there |
| 63 | + * was an error {@linkplain Class#newInstance() instantiating} it, then {@code null} is returned |
| 64 | + */ |
| 65 | + public static MaxPackage fromName(String packageName) { |
| 66 | + final String name = packageName + "." + Package.class.getSimpleName(); |
| 67 | + if (name.equals(java.lang.Package.class.getName())) { |
| 68 | + // Special case! |
| 69 | + return null; |
| 70 | + } |
| 71 | + try { |
| 72 | + final Class packageClass = Class.forName(name); |
| 73 | + return (MaxPackage) packageClass.newInstance(); |
| 74 | + } catch (ClassNotFoundException e) { |
| 75 | + } catch (InstantiationException e) { |
| 76 | + } catch (IllegalAccessException e) { |
| 77 | + } |
| 78 | + return null; |
| 79 | + } |
| 80 | + |
| 81 | + private MaxPackage(String packageName) { |
| 82 | + this.packageName = packageName; |
| 83 | + } |
| 84 | + |
| 85 | + public static MaxPackage fromJava(String name) { |
| 86 | + return new MaxPackage(name) { |
| 87 | + @Override |
| 88 | + public java.lang.Package toJava() { |
| 89 | + return java.lang.Package.getPackage(name()); |
| 90 | + } |
| 91 | + }; |
| 92 | + } |
| 93 | + |
| 94 | + public static MaxPackage fromClass(Class javaClass) { |
| 95 | + final java.lang.Package javaPackage = javaClass.getPackage(); |
| 96 | + if (javaPackage == null) { |
| 97 | + return null; |
| 98 | + } |
| 99 | + return fromName(javaPackage.getName()); |
| 100 | + } |
| 101 | + |
| 102 | + public java.lang.Package toJava() { |
| 103 | + return getClass().getPackage(); |
| 104 | + } |
| 105 | + |
| 106 | + public String name() { |
| 107 | + return packageName; |
| 108 | + } |
| 109 | + |
| 110 | + public String lastIdentifier() { |
| 111 | + return packageName.substring(packageName.lastIndexOf('.') + 1); |
| 112 | + } |
| 113 | + |
| 114 | + public MaxPackage superPackage() { |
| 115 | + final int end = name().lastIndexOf('.'); |
| 116 | + if (end < 0) { |
| 117 | + return null; |
| 118 | + } |
| 119 | + return fromName(name().substring(0, end)); |
| 120 | + } |
| 121 | + |
| 122 | + /** |
| 123 | + * Gets the subclasses of {@code com.sun.max.unsafe.Word} in this package. |
| 124 | + * The returned array must not include boxed (see com.sun.max.unsafe.Boxed) |
| 125 | + * word types as they are derived from the name of the unboxed types. |
| 126 | + */ |
| 127 | + public Class[] wordSubclasses() { |
| 128 | + return NO_CLASSES; |
| 129 | + } |
| 130 | + |
| 131 | + public MaxPackage subPackage(String... suffices) { |
| 132 | + String name = name(); |
| 133 | + for (String suffix : suffices) { |
| 134 | + name += "." + suffix; |
| 135 | + } |
| 136 | + final MaxPackage subPackage = fromName(name); |
| 137 | + ProgramError.check(subPackage != null, "Could not find sub-package of " + this + " named " + name); |
| 138 | + return subPackage; |
| 139 | + } |
| 140 | + |
| 141 | + public boolean isSubPackageOf(MaxPackage superPackage) { |
| 142 | + return name().startsWith(superPackage.name()); |
| 143 | + } |
| 144 | + |
| 145 | + public List<MaxPackage> getTransitiveSubPackages(Classpath classpath) { |
| 146 | + final Set<String> packageNames = new TreeSet<String>(); |
| 147 | + new ClassSearch() { |
| 148 | + @Override |
| 149 | + protected boolean visitClass(String className) { |
| 150 | + final String pkgName = Classes.getPackageName(className); |
| 151 | + if (pkgName.startsWith(name())) { |
| 152 | + packageNames.add(pkgName); |
| 153 | + } |
| 154 | + return true; |
| 155 | + } |
| 156 | + }.run(classpath, name().replace('.', '/')); |
| 157 | + |
| 158 | + final List<MaxPackage> packages = new ArrayList<MaxPackage>(packageNames.size()); |
| 159 | + for (String pkgName : packageNames) { |
| 160 | + final MaxPackage maxPackage = MaxPackage.fromName(pkgName); |
| 161 | + if (maxPackage == null) { |
| 162 | + System.err.println("WARNING: missing Package class in package: " + pkgName); |
| 163 | + } else { |
| 164 | + packages.add(maxPackage); |
| 165 | + } |
| 166 | + } |
| 167 | + return packages; |
| 168 | + } |
| 169 | + |
| 170 | + private Map<Class<? extends Scheme>, Class<? extends Scheme>> schemeTypeToImplementation; |
| 171 | + |
| 172 | + /** |
| 173 | + * Registers a class in this package that implements a given scheme type. |
| 174 | + * |
| 175 | + * @param schemeType a scheme type |
| 176 | + * @param schemeImplementation a class that implements {@code schemType} |
| 177 | + */ |
| 178 | + public synchronized <S extends Scheme> void registerScheme(Class<S> schemeType, Class<? extends S> schemeImplementation) { |
| 179 | + assert schemeType.isInterface() || Modifier.isAbstract(schemeType.getModifiers()); |
| 180 | + assert schemeImplementation.getPackage().getName().equals(name()) : "cannot register implmentation class from another package: " + schemeImplementation; |
| 181 | + if (schemeTypeToImplementation == null) { |
| 182 | + schemeTypeToImplementation = new IdentityHashMap<Class<? extends Scheme>, Class<? extends Scheme>>(); |
| 183 | + } |
| 184 | + Class<? extends Scheme> oldValue = schemeTypeToImplementation.put(schemeType, schemeImplementation); |
| 185 | + assert oldValue == null; |
| 186 | + } |
| 187 | + |
| 188 | + /** |
| 189 | + * Gets the class within this package implementing a given scheme type (represented as an abstract class or interface). |
| 190 | + * |
| 191 | + * @return the class directly within this package that implements {@code scheme} or null if no such class |
| 192 | + * exists |
| 193 | + */ |
| 194 | + public synchronized <S extends Scheme> Class<? extends S> schemeTypeToImplementation(Class<S> schemeType) { |
| 195 | + if (schemeTypeToImplementation == null) { |
| 196 | + return null; |
| 197 | + } |
| 198 | + final Class< ? extends Scheme> implementation = schemeTypeToImplementation.get(schemeType); |
| 199 | + if (implementation == null) { |
| 200 | + return null; |
| 201 | + } |
| 202 | + return implementation.asSubclass(schemeType); |
| 203 | + } |
| 204 | + |
| 205 | + public static boolean equal(MaxPackage p1, MaxPackage p2) { |
| 206 | + if (p1 == null) { |
| 207 | + return p2 == null; |
| 208 | + } |
| 209 | + return p1.equals(p2); |
| 210 | + } |
| 211 | + |
| 212 | + @Override |
| 213 | + public boolean equals(Object other) { |
| 214 | + if (other == this) { |
| 215 | + return true; |
| 216 | + } |
| 217 | + if (other instanceof MaxPackage) { |
| 218 | + return packageName.equals(((MaxPackage) other).packageName); |
| 219 | + } |
| 220 | + return false; |
| 221 | + } |
| 222 | + |
| 223 | + @Override |
| 224 | + public int hashCode() { |
| 225 | + return name().hashCode(); |
| 226 | + } |
| 227 | + |
| 228 | + public Set<MaxPackage> prerequisites() { |
| 229 | + return Collections.emptySet(); |
| 230 | + } |
| 231 | + |
| 232 | + /** |
| 233 | + * Gets the set of packages excluded by this package. Excluded packages will not be loaded into a system that is |
| 234 | + * configured by package loading (such as the Maxine VM) if the package represented by this object is loaded. Such a |
| 235 | + * system should ensure that configuration fails if any excluded packages encountered on the class path before the |
| 236 | + * package that excludes them. |
| 237 | + */ |
| 238 | + public Set<MaxPackage> excludes() { |
| 239 | + return Collections.emptySet(); |
| 240 | + } |
| 241 | + |
| 242 | + public int compareTo(MaxPackage other) { |
| 243 | + final Set<MaxPackage> myPrerequisites = prerequisites(); |
| 244 | + final Set<MaxPackage> otherPrerequisites = other.prerequisites(); |
| 245 | + if (myPrerequisites.isEmpty()) { |
| 246 | + if (otherPrerequisites.isEmpty()) { |
| 247 | + return packageName.compareTo(other.packageName); |
| 248 | + } |
| 249 | + return -1; |
| 250 | + } |
| 251 | + for (MaxPackage myPrerequisite : myPrerequisites) { |
| 252 | + if (other.equals(myPrerequisite)) { |
| 253 | + return 1; |
| 254 | + } |
| 255 | + } |
| 256 | + if (otherPrerequisites.isEmpty()) { |
| 257 | + return 1; |
| 258 | + } |
| 259 | + for (MaxPackage otherPrerequisite : otherPrerequisites) { |
| 260 | + if (equals(otherPrerequisite)) { |
| 261 | + return -1; |
| 262 | + } |
| 263 | + } |
| 264 | + return packageName.compareTo(other.packageName); |
| 265 | + } |
| 266 | + |
| 267 | + public synchronized <S extends Scheme> Class<? extends S> loadSchemeImplementation(Class<S> schemeType) { |
| 268 | + final Class<? extends S> schemeImplementation = schemeTypeToImplementation(schemeType); |
| 269 | + if (schemeImplementation == null) { |
| 270 | + ProgramError.unexpected("could not find subclass of " + schemeType + " in " + this); |
| 271 | + } else { |
| 272 | + final Class<?> loadedImplementation = Classes.load(schemeType.getClassLoader(), schemeImplementation.getName()); |
| 273 | + return loadedImplementation.asSubclass(schemeType); |
| 274 | + } |
| 275 | + return null; |
| 276 | + } |
| 277 | + |
| 278 | + /** |
| 279 | + * Instantiates the scheme implementation class in this package implementing a given scheme type. |
| 280 | + * |
| 281 | + * @param schemeType the interface or abstract class defining a scheme type |
| 282 | + * @return a new instance of the scheme implementation class |
| 283 | + */ |
| 284 | + public synchronized <S extends Scheme> S loadAndInstantiateScheme(Class<S> schemeType) { |
| 285 | + final Class<? extends S> schemeImplementation = loadSchemeImplementation(schemeType); |
| 286 | + try { |
| 287 | + return schemeImplementation.newInstance(); |
| 288 | + } catch (Throwable throwable) { |
| 289 | + throw ProgramError.unexpected("could not instantiate class: " + schemeImplementation.getName(), throwable); |
| 290 | + } |
| 291 | + } |
| 292 | + |
| 293 | + @Override |
| 294 | + public String toString() { |
| 295 | + return name(); |
| 296 | + } |
| 297 | +} |
0 commit comments