0001: /* Soot - a J*va Optimization Framework
0002: * Copyright (C) 2003 John Jorgensen
0003: *
0004: * This library is free software; you can redistribute it and/or
0005: * modify it under the terms of the GNU Library General Public
0006: * License as published by the Free Software Foundation; either
0007: * version 2 of the License, or (at your option) any later version.
0008: *
0009: * This library is distributed in the hope that it will be useful,
0010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0012: * Library General Public License for more details.
0013: *
0014: * You should have received a copy of the GNU Library General Public
0015: * License along with this library; if not, write to the
0016: * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
0017: * Boston, MA 02111-1307, USA.
0018: */
0019:
0020: package soot.toolkits.exceptions;
0021:
0022: import soot.*;
0023: import soot.options.Options;
0024:
0025: import java.util.*;
0026:
0027: /**
0028: * <p>A class for representing the set of exceptions that an
0029: * instruction may throw.</p>
0030: *
0031: * <p> <code>ThrowableSet</code> does not implement the
0032: * {@link java.util.Set} interface, so perhaps it is misnamed.
0033: * Instead, it provides only the operations that we require for
0034: * determining whether a given statement might throw an exception that
0035: * would be caught by a given handler.</p>
0036: *
0037: * <p>There is a limitation on the combinations of operations
0038: * permitted on a <code>ThrowableSet</code>. The
0039: * <code>ThrowableSet</code>s returned by {@link
0040: * #whichCatchableAs(RefType)} cannot be involved in subsequent
0041: * <code>add()</code> or <code>whichCatchableAs()</code> operations.
0042: * That is, given
0043: *
0044: * <blockquote>
0045: * <code>p = s.whichCatchableAs(r)</code>
0046: * </blockquote>
0047: *
0048: * for any <code>ThrowableSet</code> <code>s</code> and
0049: * {@link soot.RefType RefType} <code>r</code>, and
0050: *
0051: * <blockquote>
0052: * <code>t == p.getUncaught()</code> or
0053: * <code>t == p.getCaught()</code>
0054: * </blockquote>
0055: *
0056: * then calls to
0057: * <code>t.add(r)</code>, <code>t.add(a)</code>, and <code>s.add(t)</code>,
0058: * will throw an {@link ThrowableSet.AlreadyHasExclusionsException}, for any
0059: * <code>RefType</code> <code>r</code>, {@link AnySubType} <code>a</code>,
0060: * and <code>ThrowableSet</code> <code>t</code>.</p>
0061: *
0062: * <p> Actually the restrictions implemented are not quite so strict
0063: * (there are some combinations of <code>whichCatchableAs()</code>
0064: * followed by <code>add()</code> which will not raise an exception),
0065: * but a more accurate description would require reference to the
0066: * internals of the current implementation. The restrictions should
0067: * not be too onerous for <code>ThrowableSet</code>'s anticipated
0068: * uses: we expect <code>ThrowableSet</code>s to grow by accumulating
0069: * all the exception types that a given {@link Unit} may throw, then,
0070: * shrink as the types caught by different exception handlers are
0071: * removed to yield the sets representing exceptions which escape
0072: * those handlers.</p>
0073: *
0074: * <p> The <code>ThrowableSet</code> class is intended to be immutable
0075: * (hence the <code>final</code> modifier on its declaration). It
0076: * does not take the step of guaranteeing immutability by cloning the
0077: * <code>RefLikeType</code> objects it contains, though, because we trust
0078: * {@link Scene} to enforce the existence of only one
0079: * <code>RefLikeType</code> instance with a given name.</p>
0080: */
0081:
0082: public final class ThrowableSet {
0083:
0084: private static final boolean INSTRUMENTING = true;
0085:
0086: /**
0087: * Singleton class for fields and initializers common to all
0088: * ThrowableSet objects (i.e., these would be static fields and
0089: * initializers, in the absence of soot's {@link G} and {@link
0090: * Singletons} classes).
0091: */
0092: public static class Manager {
0093:
0094: /**
0095: * Map from {@link Integer}s representing set size to all
0096: * <code>ThrowableSet</code>s of that size.
0097: */
0098: private final Map<Integer, List> sizeToSets = new HashMap<Integer, List>();
0099:
0100: /**
0101: * <code>ThrowableSet</code> containing no exception classes.
0102: */
0103: public final ThrowableSet EMPTY;
0104:
0105: /**
0106: * <code>ThrowableSet</code> representing all possible
0107: * Throwables.
0108: */
0109: final ThrowableSet ALL_THROWABLES;
0110:
0111: /**
0112: * <code>ThrowableSet</code> containing all the asynchronous
0113: * and virtual machine errors, which may be thrown by any
0114: * bytecode instruction at any point in the computation.
0115: */
0116: final ThrowableSet VM_ERRORS;
0117:
0118: /**
0119: * <code>ThrowableSet</code> containing all the exceptions
0120: * that may be thrown in the course of resolving a reference
0121: * to another class, including the process of loading, preparing,
0122: * and verifying the referenced class.
0123: */
0124: final ThrowableSet RESOLVE_CLASS_ERRORS;
0125:
0126: /**
0127: * <code>ThrowableSet</code> containing all the exceptions
0128: * that may be thrown in the course of resolving a reference
0129: * to a field.
0130: */
0131: final ThrowableSet RESOLVE_FIELD_ERRORS;
0132:
0133: /**
0134: * <code>ThrowableSet</code> containing all the exceptions
0135: * that may be thrown in the course of resolving a reference
0136: * to a non-static method.
0137: */
0138: final ThrowableSet RESOLVE_METHOD_ERRORS;
0139:
0140: /**
0141: * <code>ThrowableSet</code> containing all the exceptions
0142: * which may be thrown by instructions that have the potential
0143: * to cause a new class to be loaded and initialized (including
0144: * UnsatisfiedLinkError, which is raised at runtime rather than
0145: * linking type).
0146: */
0147: final ThrowableSet INITIALIZATION_ERRORS;
0148:
0149: final RefType RUNTIME_EXCEPTION;
0150: final RefType ARITHMETIC_EXCEPTION;
0151: final RefType ARRAY_STORE_EXCEPTION;
0152: final RefType CLASS_CAST_EXCEPTION;
0153: final RefType ILLEGAL_MONITOR_STATE_EXCEPTION;
0154: final RefType INDEX_OUT_OF_BOUNDS_EXCEPTION;
0155: final RefType ARRAY_INDEX_OUT_OF_BOUNDS_EXCEPTION;
0156: final RefType NEGATIVE_ARRAY_SIZE_EXCEPTION;
0157: final RefType NULL_POINTER_EXCEPTION;
0158: final RefType INSTANTIATION_ERROR;
0159:
0160: // counts for instrumenting:
0161: private int registeredSets = 0;
0162: private int addsOfRefType = 0;
0163: private int addsOfAnySubType = 0;
0164: private int addsOfSet = 0;
0165: private int addsInclusionFromMap = 0;
0166: private int addsInclusionFromMemo = 0;
0167: private int addsInclusionFromSearch = 0;
0168: private int addsInclusionInterrupted = 0;
0169: private int addsExclusionWithSearch = 0;
0170: private int addsExclusionWithoutSearch = 0;
0171: private int removesOfAnySubType = 0;
0172: private final int removesFromMap = 0;
0173: private final int removesFromMemo = 0;
0174: private int removesFromSearch = 0;
0175: private int registrationCalls = 0;
0176: private int catchableAsQueries = 0;
0177: private int catchableAsFromMap = 0;
0178: private int catchableAsFromSearch = 0;
0179:
0180: /**
0181: * Constructs a <code>ThrowableSet.Manager</code> for inclusion in
0182: * Soot's global variable manager, {@link G}.
0183: *
0184: * @param g guarantees that the constructor may only be called
0185: * from {@link Singletons}.
0186: */
0187: public Manager(Singletons.Global g) {
0188: // First ensure the Exception classes are represented in Soot.
0189:
0190: // Runtime errors:
0191: RUNTIME_EXCEPTION = Scene.v().getRefType(
0192: "java.lang.RuntimeException");
0193: ARITHMETIC_EXCEPTION = Scene.v().getRefType(
0194: "java.lang.ArithmeticException");
0195: ARRAY_STORE_EXCEPTION = Scene.v().getRefType(
0196: "java.lang.ArrayStoreException");
0197: CLASS_CAST_EXCEPTION = Scene.v().getRefType(
0198: "java.lang.ClassCastException");
0199: ILLEGAL_MONITOR_STATE_EXCEPTION = Scene.v().getRefType(
0200: "java.lang.IllegalMonitorStateException");
0201: INDEX_OUT_OF_BOUNDS_EXCEPTION = Scene.v().getRefType(
0202: "java.lang.IndexOutOfBoundsException");
0203: ARRAY_INDEX_OUT_OF_BOUNDS_EXCEPTION = Scene.v().getRefType(
0204: "java.lang.ArrayIndexOutOfBoundsException");
0205: NEGATIVE_ARRAY_SIZE_EXCEPTION = Scene.v().getRefType(
0206: "java.lang.NegativeArraySizeException");
0207: NULL_POINTER_EXCEPTION = Scene.v().getRefType(
0208: "java.lang.NullPointerException");
0209:
0210: INSTANTIATION_ERROR = Scene.v().getRefType(
0211: "java.lang.InstantiationError");
0212:
0213: EMPTY = registerSetIfNew(null, null);
0214:
0215: Set allThrowablesSet = new HashSet();
0216: allThrowablesSet.add(AnySubType.v(Scene.v().getRefType(
0217: "java.lang.Throwable")));
0218: ALL_THROWABLES = registerSetIfNew(allThrowablesSet, null);
0219:
0220: Set vmErrorSet = new HashSet();
0221: vmErrorSet.add(Scene.v().getRefType(
0222: "java.lang.InternalError"));
0223: vmErrorSet.add(Scene.v().getRefType(
0224: "java.lang.OutOfMemoryError"));
0225: vmErrorSet.add(Scene.v().getRefType(
0226: "java.lang.StackOverflowError"));
0227: vmErrorSet.add(Scene.v().getRefType(
0228: "java.lang.UnknownError"));
0229:
0230: // The Java library's deprecated Thread.stop(Throwable) method
0231: // would actually allow _any_ Throwable to be delivered
0232: // asynchronously, not just java.lang.ThreadDeath.
0233: vmErrorSet.add(Scene.v()
0234: .getRefType("java.lang.ThreadDeath"));
0235:
0236: VM_ERRORS = registerSetIfNew(vmErrorSet, null);
0237:
0238: Set resolveClassErrorSet = new HashSet();
0239: resolveClassErrorSet.add(Scene.v().getRefType(
0240: "java.lang.ClassCircularityError"));
0241: // We add AnySubType(ClassFormatError) so that we can
0242: // avoid adding its subclass,
0243: // UnsupportedClassVersionError, explicitly. This is a
0244: // hack to allow Soot to analyze older class libraries
0245: // (UnsupportedClassVersionError was added in JDK 1.2).
0246: if (!Options.v().j2me())
0247: resolveClassErrorSet.add(AnySubType.v(Scene.v()
0248: .getRefType("java.lang.ClassFormatError")));
0249: resolveClassErrorSet.add(Scene.v().getRefType(
0250: "java.lang.IllegalAccessError"));
0251: resolveClassErrorSet.add(Scene.v().getRefType(
0252: "java.lang.IncompatibleClassChangeError"));
0253: resolveClassErrorSet.add(Scene.v().getRefType(
0254: "java.lang.LinkageError"));
0255: resolveClassErrorSet.add(Scene.v().getRefType(
0256: "java.lang.NoClassDefFoundError"));
0257: resolveClassErrorSet.add(Scene.v().getRefType(
0258: "java.lang.VerifyError"));
0259: RESOLVE_CLASS_ERRORS = registerSetIfNew(
0260: resolveClassErrorSet, null);
0261:
0262: Set resolveFieldErrorSet = new HashSet(resolveClassErrorSet);
0263: resolveFieldErrorSet.add(Scene.v().getRefType(
0264: "java.lang.NoSuchFieldError"));
0265: RESOLVE_FIELD_ERRORS = registerSetIfNew(
0266: resolveFieldErrorSet, null);
0267:
0268: Set resolveMethodErrorSet = new HashSet(
0269: resolveClassErrorSet);
0270: resolveMethodErrorSet.add(Scene.v().getRefType(
0271: "java.lang.AbstractMethodError"));
0272: resolveMethodErrorSet.add(Scene.v().getRefType(
0273: "java.lang.NoSuchMethodError"));
0274: resolveMethodErrorSet.add(Scene.v().getRefType(
0275: "java.lang.UnsatisfiedLinkError"));
0276: RESOLVE_METHOD_ERRORS = registerSetIfNew(
0277: resolveMethodErrorSet, null);
0278:
0279: // The static initializers of a newly loaded class might
0280: // throw any Error (if they threw an Exception---even a
0281: // RuntimeException---it would be replaced by an
0282: // ExceptionInInitializerError):
0283: //
0284: Set initializationErrorSet = new HashSet();
0285: initializationErrorSet.add(AnySubType.v(Scene.v()
0286: .getRefType("java.lang.Error")));
0287: INITIALIZATION_ERRORS = registerSetIfNew(
0288: initializationErrorSet, null);
0289: }
0290:
0291: /**
0292: * Returns the single instance of <code>ThrowableSet.Manager</code>.
0293: *
0294: * @return Soot's <code>ThrowableSet.Manager</code>.
0295: */
0296: public static Manager v() {
0297: return G.v()
0298: .soot_toolkits_exceptions_ThrowableSet_Manager();
0299: }
0300:
0301: /**
0302: * <p>Returns a <code>ThrowableSet</code> representing the set of
0303: * exceptions included in <code>include</code> minus the set
0304: * of exceptions included in <code>exclude</code>. Creates a
0305: * new <code>ThrowableSet</code> only if there was not already
0306: * one whose contents correspond to <code>include</code> -
0307: * <code>exclude</code>.</p>
0308: *
0309: * @param include A set of {@link RefLikeType}
0310: * objects representing exception types included in the result; may
0311: * be <code>null</code> if there are no included types.
0312: *
0313: * @param exclude A set of {@link AnySubType}
0314: * objects representing exception types excluded from the result; may
0315: * be <code>null</code> if there are no excluded types.
0316: *
0317: * @return a <code>ThrowableSet</code> representing the set of
0318: * exceptions corresponding to <code>include</code> -
0319: * <code>exclude</code>.
0320: */
0321: private ThrowableSet registerSetIfNew(Set include, Set exclude) {
0322: if (INSTRUMENTING) {
0323: registrationCalls++;
0324: }
0325: if (include == null) {
0326: include = Collections.EMPTY_SET;
0327: }
0328: if (exclude == null) {
0329: exclude = Collections.EMPTY_SET;
0330: }
0331: int size = include.size() + exclude.size();
0332: Integer sizeKey = new Integer(size);
0333:
0334: List<ThrowableSet> sizeList = sizeToSets.get(sizeKey);
0335: if (sizeList == null) {
0336: sizeList = new LinkedList<ThrowableSet>();
0337: sizeToSets.put(sizeKey, sizeList);
0338: }
0339: for (ThrowableSet set : sizeList) {
0340: if (set.exceptionsIncluded.equals(include)
0341: && set.exceptionsExcluded.equals(exclude)) {
0342: return set;
0343: }
0344: }
0345: if (INSTRUMENTING) {
0346: registeredSets++;
0347: }
0348: ThrowableSet result = new ThrowableSet(include, exclude);
0349: sizeList.add(result);
0350: return result;
0351: }
0352:
0353: /**
0354: * Report the counts collected by instrumentation (for now, at
0355: * least, there is no need to provide access to the individual
0356: * values as numbers).
0357: *
0358: * @return a string listing the counts.
0359: */
0360: public String reportInstrumentation() {
0361: int setCount = 0;
0362: for (List sizeList : sizeToSets.values()) {
0363: setCount += sizeList.size();
0364: }
0365: if (setCount != registeredSets) {
0366: throw new IllegalStateException(
0367: "ThrowableSet.reportInstrumentation() assertion failure: registeredSets != list count");
0368: }
0369: StringBuffer buf = new StringBuffer("registeredSets: ")
0370: .append(setCount).append("\naddsOfRefType: ")
0371: .append(addsOfRefType).append(
0372: "\naddsOfAnySubType: ").append(
0373: addsOfAnySubType).append("\naddsOfSet: ")
0374: .append(addsOfSet).append(
0375: "\naddsInclusionFromMap: ").append(
0376: addsInclusionFromMap).append(
0377: "\naddsInclusionFromMemo: ").append(
0378: addsInclusionFromMemo).append(
0379: "\naddsInclusionFromSearch: ").append(
0380: addsInclusionFromSearch).append(
0381: "\naddsInclusionInterrupted: ").append(
0382: addsInclusionInterrupted).append(
0383: "\naddsExclusionWithoutSearch: ").append(
0384: addsExclusionWithoutSearch).append(
0385: "\naddsExclusionWithSearch: ").append(
0386: addsExclusionWithSearch).append(
0387: "\nremovesOfAnySubType: ").append(
0388: removesOfAnySubType).append(
0389: "\nremovesFromMap: ")
0390: .append(removesFromMap).append(
0391: "\nremovesFromMemo: ").append(
0392: removesFromMemo).append(
0393: "\nremovesFromSearch: ").append(
0394: removesFromSearch).append(
0395: "\nregistrationCalls: ").append(
0396: registrationCalls).append(
0397: "\ncatchableAsQueries: ").append(
0398: catchableAsQueries).append(
0399: "\ncatchableAsFromMap: ").append(
0400: catchableAsFromMap).append(
0401: "\ncatchableAsFromSearch: ").append(
0402: catchableAsFromSearch).append('\n');
0403: return buf.toString();
0404: }
0405:
0406: /**
0407: * A package-private method to provide unit tests with access
0408: * to the collection of ThrowableSets.
0409: */
0410: Map<Integer, List> getSizeToSets() {
0411: return Manager.v().sizeToSets;
0412: }
0413: }
0414:
0415: public static class AlreadyHasExclusionsException extends
0416: IllegalStateException {
0417: public AlreadyHasExclusionsException(String s) {
0418: super (s);
0419: }
0420: }
0421:
0422: /**
0423: * Set of exception types included within the set.
0424: */
0425: private final Set exceptionsIncluded;
0426:
0427: /**
0428: * Set of exception types which, though members of
0429: * exceptionsIncluded, are to be excluded from the types
0430: * represented by this <code>ThrowableSet</code>. To simplify
0431: * the implementation, once a <code>ThrowableSet</code> has
0432: * any excluded types, the various <code>add()</code> methods of
0433: * this class must bar additions of subtypes of those
0434: * excluded types.
0435: */
0436: private final Set exceptionsExcluded;
0437:
0438: /**
0439: * A map from
0440: * ({@link RefLikeType} \\union <code>ThrowableSet</code>)
0441: * to <code>ThrowableSet</code>. If the mapping (k,v) is in
0442: * <code>memoizedAdds</code> and k is a
0443: * <code>ThrowableSet</code>, then v is the set that
0444: * results from adding all elements in k to <code>this</code>. If
0445: * (k,v) is in <code>memoizedAdds</code> and k is a
0446: * {@link RefLikeType}, then v is the set that results from adding
0447: * k to <code>this</code>.
0448: */
0449: private Map<Object, ThrowableSet> memoizedAdds;
0450:
0451: private ThrowableSet getMemoizedAdds(Object key) {
0452: if (memoizedAdds == null) {
0453: memoizedAdds = new HashMap();
0454: }
0455: return memoizedAdds.get(key);
0456: }
0457:
0458: /**
0459: * Constructs a <code>ThrowableSet</code> which contains the
0460: * exception types represented in <code>include</code>, except for
0461: * those which are also in <code>exclude</code>. The constructor
0462: * is private to ensure that the only way to get a new
0463: * <code>ThrowableSet</code> is by adding elements to or removing
0464: * them from an existing set.
0465: *
0466: * @param include The set of {@link RefType} and {@link AnySubType}
0467: * objects representing the types to be included in the set.
0468: * @param exclude The set of {@link AnySubType}
0469: * objects representing the types to be excluded
0470: * from the set.
0471: */
0472: private ThrowableSet(Set include, Set exclude) {
0473: exceptionsIncluded = Collections.unmodifiableSet(include);
0474: exceptionsExcluded = Collections.unmodifiableSet(exclude);
0475: // We don't need to clone include and exclude to guarantee
0476: // immutability since ThrowableSet(Set,Set) is private to this
0477: // class, where it is only called (via
0478: // Manager.v().registerSetIfNew()) with arguments which the
0479: // callers do not subsequently modify.
0480: }
0481:
0482: /**
0483: * Returns a <code>ThrowableSet</code> which contains
0484: * <code>e</code> in addition to the exceptions in
0485: * this <code>ThrowableSet</code>.
0486: *
0487: * <p>Add <code>e</code> as a {@link RefType} when
0488: * you know that the run-time class of the exception you are representing is
0489: * necessarily <code>e</code> and cannot be a subclass of
0490: * <code>e</code>.
0491: *
0492: * <p>For example, if you were
0493: * recording the type of the exception thrown by
0494: *
0495: * <pre>
0496: * throw new IOException("Permission denied");
0497: * </pre>
0498: *
0499: * you would call
0500: *
0501: * <pre>
0502: * <code>add(Scene.v().getRefType("java.lang.Exception.IOException"))</code>
0503: * </pre>
0504: *
0505: * since the class of the exception is necessarily
0506: * <code>IOException</code>.
0507: *
0508: * @param e the exception class
0509: *
0510: * @return a set containing <code>e</code> as well as the
0511: * exceptions in this set.
0512: *
0513: * @throws {@link ThrowableSet.IllegalStateException} if this
0514: * <code>ThrowableSet</code> is the result of a {@link
0515: * #whichCatchableAs(RefType)} operation and, thus, unable to
0516: * represent the addition of <code>e</code>.
0517: */
0518: public ThrowableSet add(RefType e)
0519: throws ThrowableSet.AlreadyHasExclusionsException {
0520: if (INSTRUMENTING) {
0521: Manager.v().addsOfRefType++;
0522: }
0523: if (this .exceptionsIncluded.contains(e)) {
0524: if (INSTRUMENTING) {
0525: Manager.v().addsInclusionFromMap++;
0526: Manager.v().addsExclusionWithoutSearch++;
0527: }
0528: return this ;
0529: } else {
0530: ThrowableSet result = getMemoizedAdds(e);
0531: if (result != null) {
0532: if (INSTRUMENTING) {
0533: Manager.v().addsInclusionFromMemo++;
0534: Manager.v().addsExclusionWithoutSearch++;
0535: }
0536: return result;
0537: } else {
0538: if (INSTRUMENTING) {
0539: Manager.v().addsInclusionFromSearch++;
0540: if (exceptionsExcluded.size() != 0) {
0541: Manager.v().addsExclusionWithSearch++;
0542: } else {
0543: Manager.v().addsExclusionWithoutSearch++;
0544: }
0545: }
0546: FastHierarchy hierarchy = Scene.v()
0547: .getOrMakeFastHierarchy();
0548:
0549: for (Iterator i = exceptionsExcluded.iterator(); i
0550: .hasNext();) {
0551: RefType exclusionBase = ((AnySubType) i.next())
0552: .getBase();
0553: if (hierarchy.canStoreType(e, exclusionBase)) {
0554: throw new AlreadyHasExclusionsException(
0555: "ThrowableSet.add(RefType): adding"
0556: + e.toString()
0557: + " to the set [ "
0558: + this .toString() + "] where "
0559: + exclusionBase.toString()
0560: + " is excluded.");
0561: }
0562: }
0563:
0564: for (Iterator i = exceptionsIncluded.iterator(); i
0565: .hasNext();) {
0566: RefLikeType incumbent = (RefLikeType) i.next();
0567: if (incumbent instanceof AnySubType) {
0568: // Need to use incumbent.getBase() because
0569: // hierarchy.canStoreType() assumes that parent
0570: // is not an AnySubType.
0571: RefType incumbentBase = ((AnySubType) incumbent)
0572: .getBase();
0573: if (hierarchy.canStoreType(e, incumbentBase)) {
0574: memoizedAdds.put(e, this );
0575: return this ;
0576: }
0577: } else if (!(incumbent instanceof RefType)) {
0578: // assertion failure.
0579: throw new IllegalStateException(
0580: "ThrowableSet.add(RefType): Set element "
0581: + incumbent.toString()
0582: + " is neither a RefType nor an AnySubType.");
0583: }
0584: }
0585: Set resultSet = new HashSet(this .exceptionsIncluded);
0586: resultSet.add(e);
0587: result = Manager.v().registerSetIfNew(resultSet,
0588: this .exceptionsExcluded);
0589: memoizedAdds.put(e, result);
0590: return result;
0591: }
0592: }
0593: }
0594:
0595: /**
0596: * Returns a <code>ThrowableSet</code> which contains
0597: * <code>e</code> and all of its subclasses as well as the
0598: * exceptions in this set.
0599: *
0600: * <p><code>e</code> should be an instance of {@link AnySubType}
0601: * if you know that the
0602: * compile-time type of the exception you are representing is
0603: * <code>e</code>, but the exception may be instantiated at run-time
0604: * by a subclass of
0605: * <code>e</code>.
0606: *
0607: * <p>For example, if you were recording the type of
0608: * the exception thrown by
0609: *
0610: * <pre>
0611: * catch (IOException e) {
0612: * throw e;
0613: * }
0614: * </pre>
0615: *
0616: * you would call
0617: *
0618: * <pre>
0619: * <code>add(AnySubtype.v(Scene.v().getRefType("java.lang.Exception.IOException")))</code>
0620: * </pre>
0621: *
0622: * since the handler might rethrow any subclass of
0623: * <code>IOException</code>.
0624: *
0625: * @param e represents a subtree of the exception class hierarchy
0626: * to add to this set.
0627: *
0628: * @return a set containing <code>e</code> and all its subclasses,
0629: * as well as the exceptions represented by this set.
0630: *
0631: * @throws ThrowableSet.AlreadyHasExclusionsException if this
0632: * <code>ThrowableSet</code> is the result of a {@link
0633: * #whichCatchableAs(RefType)} operation and, thus, unable to
0634: * represent the addition of <code>e</code>.
0635: */
0636: public ThrowableSet add(AnySubType e)
0637: throws ThrowableSet.AlreadyHasExclusionsException {
0638: if (INSTRUMENTING) {
0639: Manager.v().addsOfAnySubType++;
0640: }
0641:
0642: ThrowableSet result = getMemoizedAdds(e);
0643: if (result != null) {
0644: if (INSTRUMENTING) {
0645: Manager.v().addsInclusionFromMemo++;
0646: Manager.v().addsExclusionWithoutSearch++;
0647: }
0648: return result;
0649: } else {
0650: FastHierarchy hierarchy = Scene.v()
0651: .getOrMakeFastHierarchy();
0652: RefType newBase = e.getBase();
0653:
0654: if (INSTRUMENTING) {
0655: if (exceptionsExcluded.size() != 0) {
0656: Manager.v().addsExclusionWithSearch++;
0657: } else {
0658: Manager.v().addsExclusionWithoutSearch++;
0659: }
0660: }
0661: for (Iterator i = exceptionsExcluded.iterator(); i
0662: .hasNext();) {
0663: RefType exclusionBase = ((AnySubType) i.next())
0664: .getBase();
0665: if (hierarchy.canStoreType(newBase, exclusionBase)
0666: || hierarchy.canStoreType(exclusionBase,
0667: newBase)) {
0668: if (INSTRUMENTING) {
0669: // To ensure that the subcategories total properly:
0670: Manager.v().addsInclusionInterrupted++;
0671: }
0672: throw new AlreadyHasExclusionsException(
0673: "ThrowableSet.add(" + e.toString()
0674: + ") to the set [ "
0675: + this .toString() + "] where "
0676: + exclusionBase.toString()
0677: + " is excluded.");
0678: }
0679: }
0680:
0681: if (this .exceptionsIncluded.contains(e)) {
0682: if (INSTRUMENTING) {
0683: Manager.v().addsInclusionFromMap++;
0684: }
0685: return this ;
0686:
0687: } else {
0688: if (INSTRUMENTING) {
0689: Manager.v().addsInclusionFromSearch++;
0690: }
0691:
0692: int changes = 0;
0693: boolean addNewException = true;
0694: Set resultSet = new HashSet();
0695:
0696: for (Iterator i = this .exceptionsIncluded.iterator(); i
0697: .hasNext();) {
0698: RefLikeType incumbent = (RefLikeType) i.next();
0699: if (incumbent instanceof RefType) {
0700: if (hierarchy.canStoreType(incumbent, newBase)) {
0701: // Omit incumbent from result.
0702: changes++;
0703: } else {
0704: resultSet.add(incumbent);
0705: }
0706: } else if (incumbent instanceof AnySubType) {
0707: RefType incumbentBase = ((AnySubType) incumbent)
0708: .getBase();
0709: // We have to use the base types in these hierarchy calls
0710: // because we want to know if _all_ possible
0711: // types represented by e can be represented by
0712: // the incumbent, or vice versa.
0713: if (hierarchy.canStoreType(newBase,
0714: incumbentBase)) {
0715: addNewException = false;
0716: resultSet.add(incumbent);
0717: } else if (hierarchy.canStoreType(
0718: incumbentBase, newBase)) {
0719: // Omit incumbent from result;
0720: changes++;
0721: } else {
0722: resultSet.add(incumbent);
0723: }
0724: } else { // assertion failure.
0725: throw new IllegalStateException(
0726: "ThrowableSet.add(AnySubType): Set element "
0727: + incumbent.toString()
0728: + " is neither a RefType nor an AnySubType.");
0729: }
0730: }
0731: if (addNewException) {
0732: resultSet.add(e);
0733: changes++;
0734: }
0735: if (changes > 0) {
0736: result = Manager.v().registerSetIfNew(resultSet,
0737: this .exceptionsExcluded);
0738: } else {
0739: result = this ;
0740: }
0741: memoizedAdds.put(e, result);
0742: return result;
0743: }
0744: }
0745: }
0746:
0747: /**
0748: * Returns a <code>ThrowableSet</code> which contains
0749: * all the exceptions in <code>s</code> in addition to those in
0750: * this <code>ThrowableSet</code>.
0751: *
0752: * @param s set of exceptions to add to this set.
0753: *
0754: * @return the union of this set with <code>s</code>
0755: *
0756: * @throws ThrowableSet.AlreadyHasExclusionsException if this
0757: * <code>ThrowableSet</code> or <code>s</code> is the
0758: * result of a {@link #whichCatchableAs(RefType)} operation, so that
0759: * it is not possible to represent the addition of <code>s</code> to
0760: * this <code>ThrowableSet</code>.
0761: */
0762: public ThrowableSet add(ThrowableSet s)
0763: throws ThrowableSet.AlreadyHasExclusionsException {
0764: if (INSTRUMENTING) {
0765: Manager.v().addsOfSet++;
0766: }
0767: if (exceptionsExcluded.size() > 0
0768: || s.exceptionsExcluded.size() > 0) {
0769: throw new AlreadyHasExclusionsException(
0770: "ThrowableSet.Add(ThrowableSet): attempt to add to ["
0771: + this .toString()
0772: + "] after removals recorded.");
0773: }
0774: ThrowableSet result = getMemoizedAdds(s);
0775: if (result == null) {
0776: if (INSTRUMENTING) {
0777: Manager.v().addsInclusionFromSearch++;
0778: Manager.v().addsExclusionWithoutSearch++;
0779: }
0780: result = this .add(s.exceptionsIncluded);
0781: memoizedAdds.put(s, result);
0782: } else if (INSTRUMENTING) {
0783: Manager.v().addsInclusionFromMemo++;
0784: Manager.v().addsExclusionWithoutSearch++;
0785: }
0786: return result;
0787: }
0788:
0789: /**
0790: * Returns a <code>ThrowableSet</code> which contains all
0791: * the exceptions in <code>addedExceptions</code> in addition to those
0792: * in this <code>ThrowableSet</code>.
0793: *
0794: * @param addedExceptions a set of {@link RefLikeType} and
0795: * {@link AnySubType} objects to be added to the types included in this
0796: * <code>ThrowableSet</code>.
0797: *
0798: * @return a set containing all the <code>addedExceptions</code> as well
0799: * as the exceptions in this set.
0800: */
0801: private ThrowableSet add(Set addedExceptions) {
0802: Set resultSet = new HashSet(this .exceptionsIncluded);
0803: int changes = 0;
0804: FastHierarchy hierarchy = Scene.v().getOrMakeFastHierarchy();
0805:
0806: // This algorithm is O(n m), where n and m are the sizes of the
0807: // two sets, so hope that the sets are small.
0808:
0809: for (Iterator i = addedExceptions.iterator(); i.hasNext();) {
0810: RefLikeType newType = (RefLikeType) i.next();
0811: if (!resultSet.contains(newType)) {
0812: boolean addNewType = true;
0813: if (newType instanceof RefType) {
0814: for (Iterator j = resultSet.iterator(); j.hasNext();) {
0815: RefLikeType incumbentType = (RefLikeType) j
0816: .next();
0817: if (incumbentType instanceof RefType) {
0818: if (newType == incumbentType) {
0819: // assertion failure.
0820: throw new IllegalStateException(
0821: "ThrowableSet.add(Set): resultSet.contains() failed to screen duplicate RefType "
0822: + newType);
0823: }
0824: } else if (incumbentType instanceof AnySubType) {
0825: RefType incumbentBase = ((AnySubType) incumbentType)
0826: .getBase();
0827: if (hierarchy.canStoreType(newType,
0828: incumbentBase)) {
0829: // No need to add this class.
0830: addNewType = false;
0831: }
0832: } else { // assertion failure.
0833: throw new IllegalStateException(
0834: "ThrowableSet.add(Set): incumbent Set element "
0835: + incumbentType
0836: + " is neither a RefType nor an AnySubType.");
0837: }
0838: }
0839: } else if (newType instanceof AnySubType) {
0840: RefType newBase = ((AnySubType) newType).getBase();
0841: for (Iterator j = resultSet.iterator(); j.hasNext();) {
0842: RefLikeType incumbentType = (RefLikeType) j
0843: .next();
0844: if (incumbentType instanceof RefType) {
0845: RefType incumbentBase = (RefType) incumbentType;
0846: if (hierarchy.canStoreType(incumbentBase,
0847: newBase)) {
0848: j.remove();
0849: changes++;
0850: }
0851: } else if (incumbentType instanceof AnySubType) {
0852: RefType incumbentBase = ((AnySubType) incumbentType)
0853: .getBase();
0854: if (newBase == incumbentBase) {
0855: // assertion failure.
0856: throw new IllegalStateException(
0857: "ThrowableSet.add(Set): resultSet.contains() failed to screen duplicate AnySubType "
0858: + newBase);
0859: } else if (hierarchy.canStoreType(
0860: incumbentBase, newBase)) {
0861: j.remove();
0862: changes++;
0863: } else if (hierarchy.canStoreType(newBase,
0864: incumbentBase)) {
0865: // No need to add this class.
0866: addNewType = false;
0867: }
0868: } else { // assertion failure.
0869: throw new IllegalStateException(
0870: "ThrowableSet.add(Set): old Set element "
0871: + incumbentType
0872: + " is neither a RefType nor an AnySubType.");
0873: }
0874: }
0875: } else { // assertion failure.
0876: throw new IllegalArgumentException(
0877: "ThrowableSet.add(Set): new Set element "
0878: + newType
0879: + " is neither a RefType nor an AnySubType.");
0880: }
0881: if (addNewType) {
0882: changes++;
0883: resultSet.add(newType);
0884: }
0885: }
0886: }
0887:
0888: ThrowableSet result = null;
0889: if (changes > 0) {
0890: result = Manager.v().registerSetIfNew(resultSet,
0891: this .exceptionsExcluded);
0892: } else {
0893: result = this ;
0894: }
0895: return result;
0896: }
0897:
0898: /**
0899: * Indicates whether this ThrowableSet includes some
0900: * exception that might be caught by a handler argument of the
0901: * type <code>catcher</code>.
0902: *
0903: * @param catcher type of the handler parameter to be tested.
0904: *
0905: * @return <code>true</code> if this set contains an exception type
0906: * that might be caught by <code>catcher</code>,
0907: * false if it does not.
0908: */
0909: public boolean catchableAs(RefType catcher) {
0910: if (INSTRUMENTING) {
0911: Manager.v().catchableAsQueries++;
0912: }
0913:
0914: FastHierarchy h = Scene.v().getOrMakeFastHierarchy();
0915:
0916: if (exceptionsExcluded.size() > 0) {
0917: if (INSTRUMENTING) {
0918: Manager.v().catchableAsFromSearch++;
0919: }
0920: for (Iterator i = exceptionsExcluded.iterator(); i
0921: .hasNext();) {
0922: AnySubType exclusion = (AnySubType) i.next();
0923: if (h.canStoreType(catcher, exclusion.getBase())) {
0924: return false;
0925: }
0926: }
0927: }
0928:
0929: if (exceptionsIncluded.contains(catcher)) {
0930: if (INSTRUMENTING) {
0931: if (exceptionsExcluded.size() == 0) {
0932: Manager.v().catchableAsFromMap++;
0933: } else {
0934: Manager.v().catchableAsFromSearch++;
0935: }
0936: }
0937: return true;
0938: } else {
0939: if (INSTRUMENTING) {
0940: if (exceptionsExcluded.size() == 0) {
0941: Manager.v().catchableAsFromSearch++;
0942: }
0943: }
0944: for (Iterator i = exceptionsIncluded.iterator(); i
0945: .hasNext();) {
0946: RefLikeType thrownType = (RefLikeType) i.next();
0947: if (thrownType instanceof RefType) {
0948: if (thrownType == catcher) {
0949: // assertion failure.
0950: throw new IllegalStateException(
0951: "ThrowableSet.catchableAs(RefType): exceptions.contains() failed to match contained RefType "
0952: + catcher);
0953: } else if (h.canStoreType(thrownType, catcher)) {
0954: return true;
0955: }
0956: } else {
0957: RefType thrownBase = ((AnySubType) thrownType)
0958: .getBase();
0959: // At runtime, thrownType might be instantiated by any
0960: // of thrownBase's subtypes, so:
0961: if (h.canStoreType(thrownBase, catcher)
0962: || h.canStoreType(catcher, thrownBase)) {
0963: return true;
0964: }
0965: }
0966: }
0967: return false;
0968: }
0969: }
0970:
0971: /**
0972: * Partitions the exceptions in this <code>ThrowableSet</code>
0973: * into those which would be caught by a handler with the passed
0974: * <code>catch</code> parameter type and those which would not.
0975: *
0976: * @param catcher type of the handler parameter to be tested.
0977: *
0978: * @return a pair of <code>ThrowableSet</code>s, one containing the
0979: * types in this <code>ThrowableSet</code> which would be
0980: * be caught as <code>catcher</code> and the other containing
0981: * the types in this <code>ThrowableSet</code> which would
0982: * not be caught as <code>catcher</code>.
0983: */
0984: public Pair whichCatchableAs(RefType catcher) {
0985: if (INSTRUMENTING) {
0986: Manager.v().removesOfAnySubType++;
0987: }
0988:
0989: FastHierarchy h = Scene.v().getOrMakeFastHierarchy();
0990: Set caughtIncluded = null;
0991: Set caughtExcluded = null;
0992: Set uncaughtIncluded = null;
0993: Set uncaughtExcluded = null;
0994:
0995: if (INSTRUMENTING) {
0996: Manager.v().removesFromSearch++;
0997: }
0998:
0999: for (Iterator i = exceptionsExcluded.iterator(); i.hasNext();) {
1000: AnySubType exclusion = (AnySubType) i.next();
1001: RefType exclusionBase = exclusion.getBase();
1002: if (h.canStoreType(catcher, exclusionBase)) {
1003: // Because the add() operations ban additions to sets
1004: // with exclusions, we can be sure no types in this are
1005: // caught by catcher.
1006: return new Pair(ThrowableSet.Manager.v().EMPTY, this );
1007: } else if (h.canStoreType(exclusionBase, catcher)) {
1008: // exclusion wouldn't be in exceptionsExcluded if one
1009: // of its supertypes were not in exceptionsIncluded,
1010: // so we know the next loop will add either that supertype
1011: // or catcher to caughtIncluded. Thus:
1012: caughtExcluded = addExceptionToSet(exclusion,
1013: caughtExcluded);
1014: } else {
1015: uncaughtExcluded = addExceptionToSet(exclusion,
1016: uncaughtExcluded);
1017: }
1018: }
1019:
1020: for (Iterator i = exceptionsIncluded.iterator(); i.hasNext();) {
1021: RefLikeType inclusion = (RefLikeType) i.next();
1022: if (inclusion instanceof RefType) {
1023: if (h.canStoreType(inclusion, catcher)) {
1024: caughtIncluded = addExceptionToSet(inclusion,
1025: caughtIncluded);
1026: } else {
1027: uncaughtIncluded = addExceptionToSet(inclusion,
1028: uncaughtIncluded);
1029: }
1030: } else {
1031: RefType base = ((AnySubType) inclusion).getBase();
1032: if (h.canStoreType(base, catcher)) {
1033: // All subtypes of base will be caught. Any exclusions
1034: // will already have been copied to caughtExcluded by
1035: // the preceding loop.
1036: caughtIncluded = addExceptionToSet(inclusion,
1037: caughtIncluded);
1038: } else if (h.canStoreType(catcher, base)) {
1039: // Some subtypes of base will be caught, and
1040: // we know that not all of those catchable subtypes
1041: // are among exceptionsExcluded, since in that case we
1042: // would already have returned from within the
1043: // preceding loop. So, remove AnySubType(catcher)
1044: // from the uncaught types.
1045: uncaughtIncluded = addExceptionToSet(inclusion,
1046: uncaughtIncluded);
1047: uncaughtExcluded = addExceptionToSet(AnySubType
1048: .v(catcher), uncaughtExcluded);
1049: caughtIncluded = addExceptionToSet(AnySubType
1050: .v(catcher), caughtIncluded);
1051: // Any already excluded subtypes of inclusion
1052: // which are subtypes of catcher will have been
1053: // added to caughtExcluded by the previous loop.
1054: } else {
1055: uncaughtIncluded = addExceptionToSet(inclusion,
1056: uncaughtIncluded);
1057: }
1058: }
1059: }
1060: ThrowableSet caughtSet = Manager.v().registerSetIfNew(
1061: caughtIncluded, caughtExcluded);
1062: ThrowableSet uncaughtSet = Manager.v().registerSetIfNew(
1063: uncaughtIncluded, uncaughtExcluded);
1064: return new Pair(caughtSet, uncaughtSet);
1065: }
1066:
1067: /**
1068: * The return type for {@link ThrowableSet#whichCatchableAs(RefType)},
1069: * consisting of a pair of ThrowableSets.
1070: */
1071: public static class Pair {
1072: private ThrowableSet caught;
1073: private ThrowableSet uncaught;
1074:
1075: /**
1076: * Constructs a <code>ThrowableSet.Pair</code>.
1077: *
1078: * @param caught The set of exceptions to be returned when
1079: * {@link #getCaught()} is called on the constructed
1080: * <code>ThrowableSet.Pair</code>.
1081: *
1082: * @param uncaught The set of exceptions to be returned when
1083: * {@link #getUncaught()} is called on the
1084: * constructed <code>ThrowableSet.Pair</code>.
1085: */
1086: protected Pair(ThrowableSet caught, ThrowableSet uncaught) {
1087: this .caught = caught;
1088: this .uncaught = uncaught;
1089: }
1090:
1091: /**
1092: * @return the set of caught exceptions.
1093: */
1094: public ThrowableSet getCaught() {
1095: return caught;
1096: }
1097:
1098: /**
1099: * @return the set of uncaught exceptions.
1100: */
1101: public ThrowableSet getUncaught() {
1102: return uncaught;
1103: }
1104:
1105: /**
1106: * Indicates whether two {@link Object}s are
1107: * <code>ThrowableSet.Pair</code>s representing the same set of
1108: * caught and uncaught exception types.
1109: *
1110: * @param o the <code>Object</code> to compare to this
1111: * <code>ThrowableSet.Pair</code>.
1112: *
1113: * @return <code>true</code> if <code>o</code> is a
1114: * <code>ThrowableSet.Pair</code> representing the same set of
1115: * caught and uncaught types as this
1116: * <code>ThrowableSet.Pair</code>.
1117: */
1118: public boolean equals(Object o) {
1119: if (o == this ) {
1120: return true;
1121: }
1122: if (!(o instanceof Pair)) {
1123: return false;
1124: }
1125: Pair tsp = (Pair) o;
1126: if (this .caught.equals(tsp.caught)
1127: && this .uncaught.equals(tsp.uncaught)) {
1128: return true;
1129: }
1130: return false;
1131: }
1132:
1133: public int hashCode() {
1134: int result = 31;
1135: result = 37 * result + caught.hashCode();
1136: result = 37 * result + uncaught.hashCode();
1137: return result;
1138: }
1139: }
1140:
1141: /**
1142: * Utility method for building sets of exceptional types for a
1143: * {@link Pair}.
1144: *
1145: * @param e The exceptional type to add to the set.
1146: *
1147: * @param set The <code>Set</code> to which to add the types, or
1148: * <code>null</code> if no <code>Set</code> has yet been
1149: * allocated.
1150: *
1151: * @return A <code>Set</code> containing the elements in <code>set</code>
1152: * plus <code>e</code>.
1153: */
1154: private Set addExceptionToSet(RefLikeType e, Set set) {
1155: if (set == null) {
1156: set = new HashSet();
1157: }
1158: set.add(e);
1159: return set;
1160: }
1161:
1162: /**
1163: * Returns a string representation of this <code>ThrowableSet</code>.
1164: */
1165: public String toString() {
1166: StringBuffer buffer = new StringBuffer(this .toBriefString());
1167: buffer.append(":\n ");
1168: for (Iterator i = exceptionsIncluded.iterator(); i.hasNext();) {
1169: buffer.append('+');
1170: Object o = i.next();
1171: buffer.append(o == null ? "null" : o.toString());
1172: // buffer.append(i.next().toString());
1173: }
1174: for (Iterator i = exceptionsExcluded.iterator(); i.hasNext();) {
1175: buffer.append('-');
1176: buffer.append(i.next().toString());
1177: }
1178: return buffer.toString();
1179: }
1180:
1181: /**
1182: * Returns a cryptic identifier for this <code>ThrowableSet</code>,
1183: * used to identify a set when it appears in a collection.
1184: */
1185: public String toBriefString() {
1186: return super .toString();
1187: }
1188:
1189: /**
1190: * Returns an {@link Iterator} over a {@link Collection} of
1191: * Throwable types which iterates over its elements in a
1192: * consistent order (maintaining an ordering that is consistent
1193: * across different runs makes it easier to compare sets generated
1194: * by different implementations of the CFG classes).
1195: *
1196: * @param coll The collection to iterate over.
1197: *
1198: * @return An iterator which presents the elements of <code>coll</code>
1199: * in order.
1200: */
1201: private static Iterator sortedThrowableIterator(Collection coll) {
1202: if (coll.size() <= 1) {
1203: return coll.iterator();
1204: } else {
1205: Object array[] = coll.toArray();
1206: Arrays.sort(array, new ThrowableComparator());
1207: return Arrays.asList(array).iterator();
1208: }
1209: }
1210:
1211: /**
1212: * Comparator used to implement sortedThrowableIterator().
1213: *
1214: */
1215: private static class ThrowableComparator implements
1216: java.util.Comparator {
1217:
1218: private static RefType baseType(Object o) {
1219: if (o instanceof AnySubType) {
1220: return ((AnySubType) o).getBase();
1221: } else {
1222: return (RefType) o; // ClassCastException if o is not a RefType.
1223: }
1224: }
1225:
1226: public int compare(Object o1, Object o2) {
1227: RefType t1 = baseType(o1);
1228: RefType t2 = baseType(o2);
1229: if (t1.equals(t2)) {
1230: // There should never be both AnySubType(t) and
1231: // t in a ThrowableSet, but if it happens, put
1232: // AnySubType(t) first:
1233: if (o1 instanceof AnySubType) {
1234: if (o2 instanceof AnySubType) {
1235: return 0;
1236: } else {
1237: return -1;
1238: }
1239: } else if (o2 instanceof AnySubType) {
1240: return 1;
1241: } else {
1242: return 0;
1243: }
1244: } else {
1245: return t1.toString().compareTo(t2.toString());
1246: }
1247: }
1248:
1249: public boolean equal(Object o1, Object o2) {
1250: return (o1.equals(o2));
1251: }
1252: }
1253:
1254: /**
1255: * <p>Produce an abbreviated representation of this
1256: * <code>ThrowableSet</code>, suitable for human consumption. The
1257: * abbreviations include:</p>
1258: *
1259: * <ul>
1260: *
1261: * <li>The strings “<code>java.lang.</code>” is
1262: * stripped from the beginning of exception names.</li>
1263: *
1264: * <li>The string “<code>Exception</code>” is stripped from
1265: * the ends of exception names.</li>
1266: *
1267: * <li>Instances of <code>AnySubType</code> are indicated by surrounding
1268: * the base type name with parentheses, rather than with the string
1269: * “<code>Any_subtype_of_</code>”</li>
1270: *
1271: * <li>If this <code>ThrowableSet</code> includes all the elements
1272: * of {@link ThrowableSet.Manager#VM_ERRORS VM_ERRORS}, they are
1273: * abbreviated as “<code>vmErrors</code>” rather than
1274: * listed individually.</li>
1275: *
1276: * @return An abbreviated representation of the contents of this set.
1277: */
1278: public String toAbbreviatedString() {
1279: return toAbbreviatedString(exceptionsIncluded, '+')
1280: + toAbbreviatedString(exceptionsExcluded, '-');
1281: }
1282:
1283: /**
1284: * <p>Utility method which prints the abbreviations of the
1285: * elements in a passed {@link Set} of exception types.</p>
1286: *
1287: * @param s The exceptions to print.
1288: *
1289: * @param connector The character to insert between exceptions.
1290: *
1291: * @return An abbreviated representation of the exceptions.
1292: */
1293: private String toAbbreviatedString(Set s, char connector) {
1294: final String JAVA_LANG = "java.lang.";
1295: final int JAVA_LANG_LENGTH = JAVA_LANG.length();
1296: final String EXCEPTION = "Exception";
1297: final int EXCEPTION_LENGTH = EXCEPTION.length();
1298:
1299: Collection vmErrorThrowables = ThrowableSet.Manager.v().VM_ERRORS.exceptionsIncluded;
1300: boolean containsAllVmErrors = s.containsAll(vmErrorThrowables);
1301: StringBuffer buf = new StringBuffer();
1302:
1303: if (containsAllVmErrors) {
1304: buf.append(connector);
1305: buf.append("vmErrors");
1306: }
1307:
1308: for (Iterator it = sortedThrowableIterator(s); it.hasNext();) {
1309: RefLikeType reflikeType = (RefLikeType) it.next();
1310: RefType baseType = null;
1311: if (reflikeType instanceof RefType) {
1312: baseType = (RefType) reflikeType;
1313: if (vmErrorThrowables.contains(baseType)
1314: && containsAllVmErrors) {
1315: continue; // Already accounted for vmErrors.
1316: } else {
1317: buf.append(connector);
1318: }
1319: } else if (reflikeType instanceof AnySubType) {
1320: buf.append(connector);
1321: buf.append('(');
1322: baseType = ((AnySubType) reflikeType).getBase();
1323: }
1324: String typeName = baseType.toString();
1325: if (typeName.startsWith(JAVA_LANG)) {
1326: typeName = typeName.substring(JAVA_LANG_LENGTH);
1327: }
1328: if (typeName.length() > EXCEPTION_LENGTH
1329: && typeName.endsWith(EXCEPTION)) {
1330: typeName = typeName.substring(0, typeName.length()
1331: - EXCEPTION_LENGTH);
1332: }
1333: buf.append(typeName);
1334: if (reflikeType instanceof AnySubType) {
1335: buf.append(')');
1336: }
1337: }
1338: return buf.toString();
1339: }
1340:
1341: /**
1342: * A package-private method to provide unit tests with access to
1343: * the {@link RefLikeType} objects which represent the
1344: * <code>Throwable</code> types included in this set.
1345: *
1346: * @return an unmodifiable collection view of the
1347: * <code>Throwable</code> types in this set.
1348: */
1349: Collection<Object> typesIncluded() {
1350: return new AbstractCollection() {
1351:
1352: public Iterator iterator() {
1353: return new Iterator() {
1354: private final Iterator i = exceptionsIncluded
1355: .iterator();
1356:
1357: public boolean hasNext() {
1358: return i.hasNext();
1359: }
1360:
1361: public Object next() {
1362: return i.next();
1363: }
1364:
1365: public void remove() {
1366: throw new UnsupportedOperationException();
1367: }
1368: };
1369: }
1370:
1371: public int size() {
1372: return exceptionsIncluded.size();
1373: }
1374: };
1375: }
1376:
1377: /**
1378: * A package-private method to provide unit tests with access to
1379: * the {@link RefLikeType} objects which represent the
1380: * <code>Throwable</code> types excluded from this set.
1381: *
1382: * @return an unmodifiable collection view of the
1383: * <code>Throwable</code> types excluded from this set.
1384: */
1385: Collection<Object> typesExcluded() {
1386: return new AbstractCollection() {
1387:
1388: public Iterator iterator() {
1389: return new Iterator() {
1390: private final Iterator i = exceptionsExcluded
1391: .iterator();
1392:
1393: public boolean hasNext() {
1394: return i.hasNext();
1395: }
1396:
1397: public Object next() {
1398: return i.next();
1399: }
1400:
1401: public void remove() {
1402: throw new UnsupportedOperationException();
1403: }
1404: };
1405: }
1406:
1407: public int size() {
1408: return exceptionsExcluded.size();
1409: }
1410: };
1411: }
1412:
1413: /**
1414: * A package-private method to provide unit tests with access to
1415: * ThrowableSet's internals.
1416: */
1417: Map getMemoizedAdds() {
1418: if (memoizedAdds == null) {
1419: return Collections.EMPTY_MAP;
1420: } else {
1421: return Collections.unmodifiableMap(memoizedAdds);
1422: }
1423: }
1424: }
|