All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.checkerframework.framework.type.QualifierHierarchy Maven / Gradle / Ivy

Go to download

The Checker Framework enhances Java's type system to make it more powerful and useful. This lets software developers detect and prevent errors in their Java programs. The Checker Framework includes compiler plug-ins ("checkers") that find bugs or verify their absence. It also permits you to write your own compiler plug-ins.

The newest version!
package org.checkerframework.framework.type;

import org.checkerframework.checker.mustcall.qual.MustCallUnknown;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.framework.qual.AnnotatedFor;
import org.checkerframework.javacutil.AnnotationMirrorSet;
import org.checkerframework.javacutil.BugInCF;
import org.plumelib.util.StringsPlume;

import java.util.Collection;
import java.util.Map;
import java.util.Set;

import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;

/**
 * Represents multiple type qualifier hierarchies. {@link #getWidth} gives the number of hierarchies
 * that this object represents. Each hierarchy has its own top and bottom, and subtyping
 * relationships exist only within each hierarchy.
 *
 * 

Note the distinction in terminology between a qualifier hierarchy, which has one top and one * bottom, and a {@code QualifierHierarchy}, which represents multiple qualifier hierarchies. * *

All type annotations need to be type qualifiers recognized within this hierarchy. * *

This assumes that every annotated type in a program is annotated with exactly one qualifier * from each hierarchy. */ @AnnotatedFor("nullness") public abstract class QualifierHierarchy { /** The associated type factory. This is used only for checking whether types are relevant. */ protected GenericAnnotatedTypeFactory atypeFactory; /** * Creates a new QualifierHierarchy. * * @param atypeFactory the associated type factory */ public QualifierHierarchy(GenericAnnotatedTypeFactory atypeFactory) { this.atypeFactory = atypeFactory; } /** * Determine whether this QualifierHierarchy is valid. * * @return true if this QualifierHierarchy is valid */ public boolean isValid() { return true; } // ********************************************************************** // Getter methods about this hierarchy // ********************************************************************** /** * Returns the width of this hierarchy, i.e. the expected number of annotations on any valid * type. * * @return the width of this QualifierHierarchy */ public int getWidth() { return getTopAnnotations().size(); } /** * Returns the top (ultimate super) type qualifiers in the type system. The size of this set is * equal to {@link #getWidth}. * * @return the top (ultimate super) type qualifiers in the type system */ public abstract AnnotationMirrorSet getTopAnnotations(); /** * Return the top qualifier for the given qualifier, that is, the qualifier that is a supertype * of {@code qualifier} but no further supertypes exist. * * @param qualifier any qualifier from one of the qualifier hierarchies represented by this * @return the top qualifier of {@code qualifier}'s hierarchy */ public abstract AnnotationMirror getTopAnnotation(AnnotationMirror qualifier); /** * Returns the bottom type qualifiers in the hierarchy. The size of this set is equal to {@link * #getWidth}. * * @return the bottom type qualifiers in the hierarchy */ public abstract AnnotationMirrorSet getBottomAnnotations(); /** * Return the bottom for the given qualifier, that is, the qualifier that is a subtype of {@code * qualifier} but no further subtypes exist. * * @param qualifier any qualifier from one of the qualifier hierarchies represented by this * @return the bottom qualifier of {@code qualifier}'s hierarchy */ public abstract AnnotationMirror getBottomAnnotation(AnnotationMirror qualifier); /** * Returns the polymorphic qualifier for the hierarchy containing {@code qualifier}, or {@code * null} if there is no polymorphic qualifier in that hierarchy. * * @param qualifier any qualifier from one of the qualifier hierarchies represented by this * @return the polymorphic qualifier for the hierarchy containing {@code qualifier}, or {@code * null} if there is no polymorphic qualifier in that hierarchy */ public abstract @Nullable AnnotationMirror getPolymorphicAnnotation(AnnotationMirror qualifier); /** * Returns {@code true} if the qualifier is a polymorphic qualifier; otherwise, returns {@code * false}. * * @param qualifier qualifier * @return {@code true} if the qualifier is a polymorphic qualifier; otherwise, returns {@code * false}. */ public abstract boolean isPolymorphicQualifier(AnnotationMirror qualifier); /** * Returns the parametric qualifier for the hierarchy containing {@code qualifier}, or {@code * null} if there is no parametric qualifier in that hierarchy. * * @param qualifier any qualifier from one of the qualifier hierarchies represented by this * @return the parametric qualifier for the hierarchy containing {@code qualifier}, or {@code * null} if there is no polymorphic qualifier in that hierarchy */ public @Nullable AnnotationMirror getParametricQualifier(AnnotationMirror qualifier) { return null; } /** * Returns {@code true} if the qualifier is a parametric qualifier; otherwise, returns {@code * false}. * * @param qualifier qualifier * @return {@code true} if the qualifier is a parametric qualifier; otherwise, returns {@code * false}. */ public boolean isParametricQualifier(AnnotationMirror qualifier) { return false; } // ********************************************************************** // Qualifier Hierarchy Queries // ********************************************************************** /** * Tests whether {@code subQualifier} is equal to or a sub-qualifier of {@code superQualifier}, * according to the type qualifier hierarchy, ignoring Java basetypes. * *

Clients should generally call {@link #isSubtypeShallow}. However, subtypes should * generally override this method (if needed). * *

This method behaves the same as {@link #isSubtypeQualifiersOnly(AnnotationMirror, * AnnotationMirror)}, which calls this method. This method is for clients inside the framework, * and it has {@code protected} access to prevent use by clients outside the framework. This * makes it easy to find places where code outside the framework is ignoring Java basetypes -- * at calls to {@link #isSubtypeQualifiersOnly}. * * @param subQualifier possible subqualifier * @param superQualifier possible superqualifier * @return true iff {@code subQualifier} is a subqualifier of, or equal to, {@code * superQualifier} */ protected abstract boolean isSubtypeQualifiers( AnnotationMirror subQualifier, AnnotationMirror superQualifier); /** * Tests whether {@code subQualifier} is equal to or a sub-qualifier of {@code superQualifier}, * according to the type qualifier hierarchy, ignoring Java basetypes. * *

This method is for clients outside the framework, and should not be used by framework * code. * * @param subQualifier possible subqualifier * @param superQualifier possible superqualifier * @return true iff {@code subQualifier} is a subqualifier of, or equal to, {@code * superQualifier} */ public final boolean isSubtypeQualifiersOnly( AnnotationMirror subQualifier, AnnotationMirror superQualifier) { return isSubtypeQualifiers(subQualifier, superQualifier); } /** * Tests whether {@code subQualifier} is equal to or a sub-qualifier of {@code superQualifier}, * according to the type qualifier hierarchy. The types {@code subType} and {@code superType} * are not necessarily in a Java subtyping relationship with one another and are only used by * this method for special cases when qualifier subtyping depends on the Java basetype. * *

Clients should usually call {@code isSubtypeShallow()} (this method). Rarely, to ignore * the Java basetype, a client can call {@link #isSubtypeQualifiersOnly}. * *

Subtypes should override {@link #isSubtypeQualifiers} (not this method), unless qualifier * subtyping depends on Java basetypes. * * @param subQualifier possible subqualifier * @param subType the Java basetype associated with {@code subQualifier} * @param superQualifier possible superqualifier * @param superType the Java basetype associated with {@code superQualifier} * @return true iff {@code subQualifier} is a subqualifier of, or equal to, {@code * superQualifier} */ @SuppressWarnings({"nullness", "keyfor"}) // AnnotatedTypeFactory hasn't been annotated. public boolean isSubtypeShallow( AnnotationMirror subQualifier, TypeMirror subType, AnnotationMirror superQualifier, TypeMirror superType) { if (!atypeFactory.isRelevant(subType) || !atypeFactory.isRelevant(superType)) { // At least one of the types is not relevant. return true; } return isSubtypeQualifiers(subQualifier, superQualifier); } /** * Tests whether {@code subQualifier} is equal to or a sub-qualifier of {@code superQualifier}, * according to the type qualifier hierarchy. The type {@code typeMirror} is only used by this * method for special cases when qualifier subtyping depends on the Java basetype. * *

Clients should usually call {@link #isSubtypeShallow(AnnotationMirror, AnnotationMirror, * TypeMirror)} (this method) or {@link #isSubtypeShallow(AnnotationMirror, TypeMirror, * AnnotationMirror, TypeMirror)}. Rarely, to ignore the Java basetype, a client can call {@link * #isSubtypeQualifiersOnly}. * *

Subtypes should override {@link #isSubtypeQualifiers} (not this method), unless qualifier * subtyping depends on Java basetypes. * * @param subQualifier possible subqualifier * @param superQualifier possible superqualifier * @param typeMirror the Java basetype associated with both {@code subQualifier} and {@code * superQualifier} * @return true iff {@code subQualifier} is a subqualifier of, or equal to, {@code * superQualifier} */ public final boolean isSubtypeShallow( AnnotationMirror subQualifier, AnnotationMirror superQualifier, TypeMirror typeMirror) { return isSubtypeShallow(subQualifier, typeMirror, superQualifier, typeMirror); } /** * Tests whether all qualifiers in {@code subQualifiers} are a subqualifier or equal to the * qualifier in the same hierarchy in {@code superQualifiers}. The types {@code subType} and * {@code superType} are not necessarily in a Java subtyping relationship with one another and * are only used by this method for special cases when qualifier subtyping depends on the Java * basetype. * *

Subtypes more often override {@link #isSubtypeShallow(AnnotationMirror, TypeMirror, * AnnotationMirror, TypeMirror)} than this method. * * @param subQualifiers set of qualifiers; exactly one per hierarchy * @param subType the type associated with {@code subQualifiers} * @param superQualifiers set of qualifiers; exactly one per hierarchy * @param superType the type associated with {@code superQualifiers} * @return true iff all qualifiers in {@code subQualifiers} are a subqualifier or equal to the * qualifier in the same hierarchy in {@code superQualifiers} */ public final boolean isSubtypeShallow( Collection subQualifiers, TypeMirror subType, Collection superQualifiers, TypeMirror superType) { assertSameSize(subQualifiers, superQualifiers); for (AnnotationMirror subQual : subQualifiers) { AnnotationMirror superQual = findAnnotationInSameHierarchy(superQualifiers, subQual); if (superQual == null) { throw new BugInCF( "QualifierHierarchy: missing annotation in hierarchy %s. found: %s", subQual, StringsPlume.join(",", superQualifiers)); } if (!isSubtypeShallow(subQual, subType, superQual, superType)) { return false; } } return true; } /** * Tests whether all qualifiers in {@code subQualifiers} are a subqualifier of or equal to the * qualifier in the same hierarchy in {@code superQualifiers}. The type {@code typeMirror} is * only used by this method for special cases when qualifier subtyping depends on the Java * basetype. * *

Subtypes more often override {@link #isSubtypeShallow(AnnotationMirror, TypeMirror, * AnnotationMirror, TypeMirror)} than this method. * * @param subQualifiers a set of qualifiers; exactly one per hierarchy * @param superQualifiers a set of qualifiers; exactly one per hierarchy * @param typeMirror the type associated with both sets of qualifiers * @return true iff all qualifiers in {@code subQualifiers} are a subqualifier or equal to the * qualifier in the same hierarchy in {@code superQualifiers} */ public final boolean isSubtypeShallow( Collection subQualifiers, Collection superQualifiers, TypeMirror typeMirror) { return isSubtypeShallow(subQualifiers, typeMirror, superQualifiers, typeMirror); } /** * Returns the least upper bound (LUB) of the qualifiers {@code qualifier1} and {@code * qualifier2}. Returns {@code null} if the qualifiers are not from the same qualifier * hierarchy. Ignores Java basetypes. * *

Examples: * *

    *
  • For NonNull, leastUpperBound('Nullable', 'NonNull') ⇒ Nullable *
* * @param qualifier1 the first qualifier; may not be in the same hierarchy as {@code qualifier2} * @param qualifier2 the second qualifier; may not be in the same hierarchy as {@code * qualifier1} * @return the least upper bound of the qualifiers, or {@code null} if the qualifiers are from * different hierarchies */ // The fact that null is returned if the qualifiers are not in the same hierarchy is used by the // collection version of LUB below. protected abstract @Nullable AnnotationMirror leastUpperBoundQualifiers( AnnotationMirror qualifier1, AnnotationMirror qualifier2); /** * Returns the least upper bound (LUB) of the qualifiers {@code qualifier1} and {@code * qualifier2}. Returns {@code null} if the qualifiers are not from the same qualifier * hierarchy. Ignores Java basetypes. * *

Examples: * *

    *
  • For NonNull, leastUpperBound('Nullable', 'NonNull') ⇒ Nullable *
* * @param qualifier1 the first qualifier; may not be in the same hierarchy as {@code qualifier2} * @param qualifier2 the second qualifier; may not be in the same hierarchy as {@code * qualifier1} * @return the least upper bound of the qualifiers, or {@code null} if the qualifiers are from * different hierarchies */ // The fact that null is returned if the qualifiers are not in the same hierarchy is used by the // collection version of LUB below. public final @Nullable AnnotationMirror leastUpperBoundQualifiersOnly( AnnotationMirror qualifier1, AnnotationMirror qualifier2) { return leastUpperBoundQualifiers(qualifier1, qualifier2); } /** * Returns the least upper bound (LUB) of the qualifiers {@code qualifier1} and {@code * qualifier2}. Returns {@code null} if the qualifiers are not from the same qualifier * hierarchy. * *

Examples: * *

    *
  • leastUpperBound('Nullable', 'NonNull') ⇒ Nullable *
* * @param qualifier1 the first qualifier; may not be in the same hierarchy as {@code qualifier2} * @param tm1 the type on which qualifier1 appears * @param qualifier2 the second qualifier; may not be in the same hierarchy as {@code * qualifier1} * @param tm2 the type on which qualifier2 appears * @return the least upper bound of the qualifiers, or {@code null} if the qualifiers are from * different hierarchies */ // The fact that null is returned if the qualifiers are not in the same hierarchy is used by the // collection version of LUB below. @SuppressWarnings({"nullness", "keyfor"}) // AnnotatedTypeFactory hasn't been annotated. public @Nullable AnnotationMirror leastUpperBoundShallow( AnnotationMirror qualifier1, TypeMirror tm1, AnnotationMirror qualifier2, TypeMirror tm2) { boolean tm1IsRelevant = atypeFactory.isRelevant(tm1); boolean tm2IsRelevant = atypeFactory.isRelevant(tm2); if (tm1IsRelevant == tm2IsRelevant) { return leastUpperBoundQualifiers(qualifier1, qualifier2); } else if (tm1IsRelevant) { return qualifier1; } else { // if (tm2IsRelevant) { return qualifier2; } } /** * Returns the least upper bound of the two sets of qualifiers. The result is the lub of the * qualifier for the same hierarchy in each set. * * @param qualifiers1 set of qualifiers; exactly one per hierarchy * @param tm1 the type on which qualifiers1 appear * @param qualifiers2 set of qualifiers; exactly one per hierarchy * @param tm2 the type on which qualifiers2 appear * @return the least upper bound of the two sets of qualifiers */ public final Set leastUpperBoundsShallow( Collection qualifiers1, TypeMirror tm1, Collection qualifiers2, TypeMirror tm2) { assertSameSize(qualifiers1, qualifiers2); if (qualifiers1.isEmpty()) { throw new BugInCF( "QualifierHierarchy.leastUpperBounds: tried to determine LUB with empty sets"); } AnnotationMirrorSet result = new AnnotationMirrorSet(); for (AnnotationMirror a1 : qualifiers1) { for (AnnotationMirror a2 : qualifiers2) { AnnotationMirror lub = leastUpperBoundShallow(a1, tm1, a2, tm2); if (lub != null) { result.add(lub); } } } assertSameSize(result, qualifiers1); return result; } /** * Returns the number of iterations dataflow should perform before {@link * #widenedUpperBound(AnnotationMirror, AnnotationMirror)} is called or -1 if it should never be * called. * * @return the number of iterations dataflow should perform before {@link * #widenedUpperBound(AnnotationMirror, AnnotationMirror)} is called or -1 if it should * never be called. */ public int numberOfIterationsBeforeWidening() { return -1; } /** * If the qualifier hierarchy has an infinite ascending chain, then the dataflow analysis might * never reach a fixed point. To prevent this, implement this method such that it returns an * upper bound for the two qualifiers that is a strict super type of the least upper bound. If * this method is implemented, also override {@link #numberOfIterationsBeforeWidening()} to * return a positive number. * *

{@code newQualifier} is newest qualifier dataflow computed for some expression and {@code * previousQualifier} is the qualifier dataflow computed on the last iteration. * *

If the qualifier hierarchy has no infinite ascending chain, returns the least upper bound * of the two annotations. * * @param newQualifier new qualifier dataflow computed for some expression; must be in the same * hierarchy as {@code previousQualifier} * @param previousQualifier the previous qualifier dataflow computed on the last iteration; must * be in the same hierarchy as {@code previousQualifier} * @return an upper bound that is higher than the least upper bound of newQualifier and * previousQualifier (or the lub if the qualifier hierarchy does not require this) */ public AnnotationMirror widenedUpperBound( AnnotationMirror newQualifier, AnnotationMirror previousQualifier) { AnnotationMirror widenedUpperBound = leastUpperBoundQualifiers(newQualifier, previousQualifier); if (widenedUpperBound == null) { throw new BugInCF( "widenedUpperBound(%s, %s): unrelated qualifiers", newQualifier, previousQualifier); } return widenedUpperBound; } /** * Returns the greatest lower bound for the qualifiers qualifier1 and qualifier2. Returns null * if the qualifiers are not from the same qualifier hierarchy. * * @param qualifier1 first qualifier * @param qualifier2 second qualifier * @return greatest lower bound of the two annotations, or null if the two annotations are not * from the same hierarchy */ // The fact that null is returned if the qualifiers are not in the same hierarchy is used by the // collection version of LUB below. protected abstract @Nullable AnnotationMirror greatestLowerBoundQualifiers( AnnotationMirror qualifier1, AnnotationMirror qualifier2); /** * Returns the greatest lower bound for the qualifiers qualifier1 and qualifier2. Returns null * if the qualifiers are not from the same qualifier hierarchy. * * @param qualifier1 first qualifier * @param qualifier2 second qualifier * @return greatest lower bound of the two annotations, or null if the two annotations are not * from the same hierarchy */ // The fact that null is returned if the qualifiers are not in the same hierarchy is used by the // collection version of LUB below. public final @Nullable AnnotationMirror greatestLowerBoundQualifiersOnly( AnnotationMirror qualifier1, AnnotationMirror qualifier2) { return greatestLowerBoundQualifiers(qualifier1, qualifier2); } /** * Returns the greatest lower bound for the qualifiers qualifier1 and qualifier2. Returns null * if the qualifiers are not from the same qualifier hierarchy. * * @param qualifier1 first qualifier * @param tm1 the type that is annotated by qualifier1 * @param qualifier2 second qualifier * @param tm2 the type that is annotated by qualifier2 * @return greatest lower bound of the two annotations, or null if the two annotations are not * from the same hierarchy */ @SuppressWarnings({"nullness", "keyfor"}) // AnnotatedTypeFactory hasn't been annotated. public @Nullable AnnotationMirror greatestLowerBoundShallow( AnnotationMirror qualifier1, TypeMirror tm1, AnnotationMirror qualifier2, TypeMirror tm2) { boolean tm1IsRelevant = atypeFactory.isRelevant(tm1); boolean tm2IsRelevant = atypeFactory.isRelevant(tm2); if (tm1IsRelevant == tm2IsRelevant) { return greatestLowerBoundQualifiers(qualifier1, qualifier2); } else if (tm1IsRelevant) { return qualifier1; } else { // if (tm2IsRelevant) { return qualifier2; } } /** * Returns the greatest lower bound of the two sets of qualifiers. The result is the lub of the * qualifier for the same hierarchy in each set. * * @param qualifiers1 set of qualifiers; exactly one per hierarchy * @param tm1 the type that is annotated by qualifier1 * @param qualifiers2 set of qualifiers; exactly one per hierarchy * @param tm2 the type that is annotated by qualifier2 * @return the greatest lower bound of the two sets of qualifiers */ public final Set greatestLowerBoundsShallow( Collection qualifiers1, TypeMirror tm1, Collection qualifiers2, TypeMirror tm2) { assertSameSize(qualifiers1, qualifiers2); if (qualifiers1.isEmpty()) { throw new BugInCF( "QualifierHierarchy.greatestLowerBounds: tried to determine GLB with empty" + " sets"); } AnnotationMirrorSet result = new AnnotationMirrorSet(); for (AnnotationMirror a1 : qualifiers1) { for (AnnotationMirror a2 : qualifiers2) { AnnotationMirror glb = greatestLowerBoundShallow(a1, tm1, a2, tm2); if (glb != null) { result.add(glb); } } } assertSameSize(qualifiers1, qualifiers2, result); return result; } /** * Returns true if and only if {@link AnnotatedTypeMirror#getAnnotations()} can return a set * with fewer qualifiers than the width of the QualifierHierarchy. * * @param type the type to test * @return true if and only if {@link AnnotatedTypeMirror#getAnnotations()} can return a set * with fewer qualifiers than the width of the QualifierHierarchy */ public static boolean canHaveEmptyAnnotationSet(AnnotatedTypeMirror type) { return type.getKind() == TypeKind.TYPEVAR || type.getKind() == TypeKind.WILDCARD || // TODO: or should the union/intersection be the LUB of the alternatives? type.getKind() == TypeKind.UNION || type.getKind() == TypeKind.INTERSECTION; } /** * Returns the annotation in {@code qualifiers} that is in the same hierarchy as {@code * qualifier}. * *

The default implementation calls {@link #getTopAnnotation(AnnotationMirror)} and then * calls {@link #findAnnotationInHierarchy(Collection, AnnotationMirror)}. So, if {@code * qualifier} is a top qualifier, then call {@link #findAnnotationInHierarchy(Collection, * AnnotationMirror)} directly is faster. * * @param qualifiers set of annotations to search * @param qualifier annotation that is in the same hierarchy as the returned annotation * @return annotation in the same hierarchy as qualifier, or null if one is not found */ public @Nullable AnnotationMirror findAnnotationInSameHierarchy( Collection qualifiers, AnnotationMirror qualifier) { AnnotationMirror top = this.getTopAnnotation(qualifier); return findAnnotationInHierarchy(qualifiers, top); } /** * Returns the annotation in {@code qualifiers} that is in the hierarchy for which {@code top} * is top. * * @param qualifiers set of annotations to search * @param top the top annotation in the hierarchy to which the returned annotation belongs * @return annotation in the same hierarchy as annotationMirror, or null if one is not found */ public @Nullable AnnotationMirror findAnnotationInHierarchy( Collection qualifiers, AnnotationMirror top) { for (AnnotationMirror anno : qualifiers) { if (isSubtypeQualifiers(anno, top)) { return anno; } } return null; } /** * Update a mapping from {@code key} to a set of AnnotationMirrors. If {@code key} is not * already in the map, then put it in the map with a value of a new set containing {@code * qualifier}. If the map contains {@code key}, then add {@code qualifier} to the set to which * {@code key} maps. If that set contains a qualifier in the same hierarchy as {@code * qualifier}, then don't add it and return false. * * @param map the mapping to modify * @param key the key to update or add * @param qualifier the value to update or add * @param type of the map's keys * @return true if the update was done; false if there was a qualifier hierarchy collision */ public boolean updateMappingToMutableSet( Map map, T key, AnnotationMirror qualifier) { // https://github.com/typetools/checker-framework/issues/2000 @SuppressWarnings("nullness:argument.type.incompatible") boolean mapContainsKey = map.containsKey(key); if (mapContainsKey) { @SuppressWarnings("nullness:assignment.type.incompatible") // key is a key for map. @NonNull AnnotationMirrorSet prevs = map.get(key); AnnotationMirror old = findAnnotationInSameHierarchy(prevs, qualifier); if (old != null) { return false; } prevs.add(qualifier); map.put(key, prevs); } else { AnnotationMirrorSet set = new AnnotationMirrorSet(); set.add(qualifier); map.put(key, set); } return true; } /** * Throws an exception if the given collections do not have the same size. * * @param c1 the first collection * @param c2 the second collection */ public static void assertSameSize(Collection c1, Collection c2) { if (c1.size() != c2.size()) { throw new BugInCF( "inconsistent sizes (%d, %d):%n [%s]%n [%s]", c1.size(), c2.size(), StringsPlume.join(",", c1), StringsPlume.join(",", c2)); } } /** * Throws an exception if the result and the inputs do not all have the same size. * * @param c1 the first collection * @param c2 the second collection * @param result the result collection */ public static void assertSameSize( @MustCallUnknown Collection c1, @MustCallUnknown Collection c2, @MustCallUnknown Collection result) { if (c1.size() != result.size() || c2.size() != result.size()) { throw new BugInCF( "inconsistent sizes (%d, %d, %d):%n %s%n %s%n %s", c1.size(), c2.size(), result.size(), StringsPlume.join(",", c1), StringsPlume.join(",", c2), StringsPlume.join(",", result)); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy