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

framework.src.org.checkerframework.qualframework.poly.PolyQual 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.

There is a newer version: 3.42.0
Show newest version
package org.checkerframework.qualframework.poly;

import java.util.*;

/** A qualifier in the qualifier polymorphism system, with ground qualifier
 * representation {@code Q}.  A {@code PolyQual} represents either a
 * qualifier from the original system ({@code GroundQual}, which simply wraps a
 * {@code Q}), a qualifier variable that ranges over ground qualifiers ({@code
 * QualVar}), or a combination of two or more qualifiers using a {@link
 * CombiningOperation} ({@code Combined}).
 */
public abstract class PolyQual {
    /** Get the greatest lower bound of the possible types of this qualifier
     * under all valid assignments to qualifier variables.  (That is, for all
     * assignments of qualifiers to qualifier variables, {@code
     * pq.getMinimum()} is a subtype of {@code pq}.
     */
    public abstract Q getMinimum();

    /** Get the least upper bound of the possible types of this qualifier under
     * all valid assignments to qualifier variables.
     */
    public abstract Q getMaximum();

    /** Substitute qualifiers for qualifier variables.
     */
    public abstract PolyQual substitute(Map> substs);

    /** Convert this qualifier to an equivalent {@link Combined} qualifier
     * using the given {@link CombiningOperation}.
     */
    public abstract Combined asCombined(CombiningOperation op);

    /** Combine this qualifier with another using the given {@link
     * CombiningOperation}.
     */
    public PolyQual combineWith(PolyQual other, CombiningOperation op) {
        return this.asCombined(op).combineWith(other.asCombined(op));
    }

    /** A wrapped qualifier from the underlying system. */
    public static final class GroundQual extends PolyQual {
        private final Q qual;

        public GroundQual(Q qual) {
            if (qual == null) {
                throw new IllegalArgumentException("qual must not be null");
            }
            this.qual = qual;
        }

        /** Get the underlying qualifier representation. */
        public Q getQualifier() {
            return qual;
        }

        @Override
        public Q getMinimum() {
            return qual;
        }

        @Override
        public Q getMaximum() {
            return qual;
        }

        @Override
        public PolyQual substitute(Map> substs) {
            return this;
        }

        @Override
        public Combined asCombined(CombiningOperation op) {
            return new Combined(op, qual);
        }

        @Override
        public boolean equals(Object o) {
            if (o == null || o.getClass() != this.getClass()) {
                return false;
            }
            @SuppressWarnings("rawtypes")
            GroundQual other = (GroundQual)o;
            return this.qual.equals(other.qual);
        }

        @Override
        public int hashCode() {
            return this.qual.hashCode();
        }

        @Override
        public String toString() {
            return qual.toString();
        }
    }

    /** A qualifier variable. */
    public static final class QualVar extends PolyQual {
        private final String name;
        private final Q lower;
        private final Q upper;

        public QualVar(String name, Q lower, Q upper) {
            if (name == null) {
                throw new IllegalArgumentException("name must not be null");
            }
            if (lower == null || upper == null) {
                throw new IllegalArgumentException("bounds must not be null");
            }
            this.name = name;
            this.lower = lower;
            this.upper = upper;
        }

        /** Get the name of this qualifier variable. */
        public String getName() {
            return name;
        }

        /** Get the lower bound of this qualifier variable. */
        public Q getLowerBound() {
            return lower;
        }

        /** Get the upper bound of this qualifier variable. */
        public Q getUpperBound() {
            return upper;
        }

        @Override
        public Q getMinimum() {
            return lower;
        }

        @Override
        public Q getMaximum() {
            return upper;
        }

        @Override
        public PolyQual substitute(Map> substs) {
            PolyQual value = substs.get(this.name);
            if (value != null) {
                return value;
            } else {
                return this;
            }
        }

        @Override
        public Combined asCombined(CombiningOperation op) {
            return new Combined(op, this);
        }

        @Override
        public boolean equals(Object o) {
            if (o == null || o.getClass() != this.getClass()) {
                return false;
            }
            @SuppressWarnings("rawtypes")
            QualVar other = (QualVar)o;
            return this.name.equals(other.name)
                && this.lower.equals(other.lower)
                && this.upper.equals(other.upper);
        }

        @Override
        public int hashCode() {
            return this.name.hashCode() * 13
                + this.lower.hashCode() * 37
                + this.upper.hashCode() * 59;
        }

        @Override
        public String toString() {
            return "(" + name + " ∈ [" + lower + ".." + upper + "])";
        }
    }

    /** A combination of several qualifiers and qualifier variables, using a
     * combining function. */
    public static class Combined extends PolyQual {
        // We keep only a single `Q` field because combining two Qs can be done
        // immediately using `op` - unlike combining `QualVar`s, which we can't
        // do much with until they have been substituted.
        private final CombiningOperation op;
        private final HashSet> vars;
        private final Q ground;

        public Combined(CombiningOperation op, Collection> vars, Q ground) {
            this.op = op;
            this.vars = new HashSet<>(vars);
            this.ground = ground;
        }

        public Combined(CombiningOperation op, QualVar var, Q ground) {
            this.op = op;
            this.vars = new HashSet<>();
            this.vars.add(var);
            this.ground = ground;
        }

        public Combined(CombiningOperation op, QualVar var) {
            this.op = op;
            this.vars = new HashSet<>();
            this.vars.add(var);
            this.ground = op.identity();
        }

        public Combined(CombiningOperation op, Q ground) {
            this.op = op;
            this.vars = new HashSet<>();
            this.ground = ground;
        }

        /** Like the main {@code Combined} constructor, but returns a simpler
         * PolyQual (GroundQual or QualVar) when possible.
         */
        public static  PolyQual from(CombiningOperation op, Collection> vars, Q ground) {
            if (vars.isEmpty()) {
                return new GroundQual(ground);
            }

            if (vars.size() == 1 && ground.equals(op.identity())) {
                for (QualVar var : vars) {
                    return var;
                }
            }

            return new Combined(op, vars, ground);
        }

        /** Combine two instances of {@code Combined} that were built with the
         * same {@link CombiningOperation}.
         */
        public PolyQual combineWith(Combined other) {
            if (this.op != other.op) {
                throw new IllegalArgumentException(
                        "can't combine two Combined using different CombiningOperations");
            }
            HashSet> newVars = new HashSet<>(this.vars);
            newVars.addAll(other.vars);
            Q newGround = this.op.combine(this.ground, other.ground);
            return Combined.from(op, newVars, newGround);
        }

        public CombiningOperation getOp() {
            return op;
        }

        public Set> getVars() {
            return vars;
        }

        public Q getGround() {
            return ground;
        }

        @Override
        public Q getMinimum() {
            Q result = ground;
            for (QualVar var : vars) {
                result = op.combine(result, var.getMinimum());
            }
            return result;
        }

        @Override
        public Q getMaximum() {
            Q result = ground;
            for (QualVar var : vars) {
                result = op.combine(result, var.getMaximum());
            }
            return result;
        }

        @Override
        public PolyQual substitute(Map> substs) {
            HashSet> newVars = new HashSet<>();
            Q newGround = ground;

            for (QualVar var : vars) {
                Combined substCombined = var.substitute(substs).asCombined(op);
                newVars.addAll(substCombined.vars);
                newGround = op.combine(newGround, substCombined.ground);
            }

            return Combined.from(op, newVars, newGround);
        }

        @Override
        public Combined asCombined(CombiningOperation op) {
            if (op != this.op) {
                // We might have already used `self.op` to combine several
                // GroundQuals.  Switching `op` after that could lead to crazy
                // results.
                throw new IllegalArgumentException(
                        "can't call Combined.asCombined with different CombiningOperation");
            }

            return this;
        }

        @Override
        public boolean equals(Object o) {
            if (o == null || o.getClass() != this.getClass()) {
                return false;
            }
            @SuppressWarnings("rawtypes")
            Combined other = (Combined)o;
            return this.op.equals(other.op)
                && this.vars.equals(other.vars)
                && this.ground.equals(other.ground);
        }

        @Override
        public int hashCode() {
            return this.op.hashCode() * 13
                + this.vars.hashCode() * 37
                + this.ground.hashCode() * 59;
        }

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append(op);
            sb.append("(");
            for (QualVar var : vars) {
                sb.append(var);
                sb.append(", ");
            }
            sb.append(ground);
            sb.append(")");
            return sb.toString();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy