package org.checkerframework.checker.units; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.util.Elements; import org.checkerframework.checker.units.qual.Prefix; import org.checkerframework.checker.units.qual.h; import org.checkerframework.checker.units.qual.km2; import org.checkerframework.checker.units.qual.kmPERh; import org.checkerframework.checker.units.qual.m; import org.checkerframework.checker.units.qual.m2; import org.checkerframework.checker.units.qual.mPERs; import org.checkerframework.checker.units.qual.mPERs2; import org.checkerframework.checker.units.qual.mm2; import org.checkerframework.checker.units.qual.s; import org.checkerframework.framework.type.AnnotatedTypeMirror; /*>>> import org.checkerframework.checker.nullness.qual.Nullable; */ /** Default relations between SI units. TODO: what relations are missing? */ public class UnitsRelationsDefault implements UnitsRelations { protected AnnotationMirror m, km, mm, m2, km2, mm2, s, h, mPERs, kmPERh, mPERs2; protected Elements elements; /** * Constructs various AnnotationMirrors representing specific checker-framework provided Units * involved in the rules resolved in this UnitsRelations implementation */ @Override public UnitsRelations init(ProcessingEnvironment env) { elements = env.getElementUtils(); m = UnitsRelationsTools.buildAnnoMirrorWithDefaultPrefix(env, m.class); km = UnitsRelationsTools.buildAnnoMirrorWithSpecificPrefix(env, m.class, Prefix.kilo); mm = UnitsRelationsTools.buildAnnoMirrorWithSpecificPrefix(env, m.class, Prefix.milli); m2 = UnitsRelationsTools.buildAnnoMirrorWithNoPrefix(env, m2.class); km2 = UnitsRelationsTools.buildAnnoMirrorWithNoPrefix(env, km2.class); mm2 = UnitsRelationsTools.buildAnnoMirrorWithNoPrefix(env, mm2.class); s = UnitsRelationsTools.buildAnnoMirrorWithDefaultPrefix(env, s.class); h = UnitsRelationsTools.buildAnnoMirrorWithNoPrefix(env, h.class); mPERs = UnitsRelationsTools.buildAnnoMirrorWithNoPrefix(env, mPERs.class); kmPERh = UnitsRelationsTools.buildAnnoMirrorWithNoPrefix(env, kmPERh.class); mPERs2 = UnitsRelationsTools.buildAnnoMirrorWithNoPrefix(env, mPERs2.class); return this; } /** * Provides rules for resolving the result Unit of the multiplication of checker-framework * provided Units */ @Override public /*@Nullable*/ AnnotationMirror multiplication( AnnotatedTypeMirror lht, AnnotatedTypeMirror rht) { // TODO: does this handle scaling correctly? // length * length => area // checking SI units only if (UnitsRelationsTools.hasSpecificUnitIgnoringPrefix(lht, m) && UnitsRelationsTools.hasSpecificUnitIgnoringPrefix(rht, m)) { if (UnitsRelationsTools.hasNoPrefix(lht) && UnitsRelationsTools.hasNoPrefix(rht)) { // m * m return m2; } Prefix lhtPrefix = UnitsRelationsTools.getPrefix(lht); Prefix rhtPrefix = UnitsRelationsTools.getPrefix(rht); if (bothHaveSpecificPrefix(lhtPrefix, rhtPrefix, Prefix.kilo)) { // km * km return km2; } else if (bothHaveSpecificPrefix(lhtPrefix, rhtPrefix, Prefix.one)) { // m(Prefix.one) * m(Prefix.one) return m2; } else if (bothHaveSpecificPrefix(lhtPrefix, rhtPrefix, Prefix.milli)) { // mm * mm return mm2; } else { return null; } } else if (havePairOfUnitsIgnoringOrder(lht, s, rht, mPERs)) { // s * mPERs or mPERs * s => m return m; } else if (havePairOfUnitsIgnoringOrder(lht, s, rht, mPERs2)) { // s * mPERs2 or mPERs2 * s => mPERs return mPERs; } else if (havePairOfUnitsIgnoringOrder(lht, h, rht, kmPERh)) { // h * kmPERh or kmPERh * h => km return km; } else { return null; } } /** * Provides rules for resolving the result Unit of the division of checker-framework provided * Units */ @Override public /*@Nullable*/ AnnotationMirror division( AnnotatedTypeMirror lht, AnnotatedTypeMirror rht) { if (havePairOfUnits(lht, m, rht, s)) { // m / s => mPERs return mPERs; } else if (havePairOfUnits(lht, km, rht, h)) { // km / h => kmPERh return kmPERh; } else if (havePairOfUnits(lht, m2, rht, m)) { // m2 / m => m return m; } else if (havePairOfUnits(lht, km2, rht, km)) { // km2 / km => km return km; } else if (havePairOfUnits(lht, mm2, rht, mm)) { // mm2 / mm => mm return mm; } else if (havePairOfUnits(lht, m, rht, mPERs)) { // m / mPERs => s return s; } else if (havePairOfUnits(lht, km, rht, kmPERh)) { // km / kmPERh => h return h; } else if (havePairOfUnits(lht, mPERs, rht, s)) { // mPERs / s = mPERs2 return mPERs2; } else if (havePairOfUnits(lht, mPERs, rht, mPERs2)) { // mPERs / mPERs2 => s (velocity / acceleration == time) return s; } else { return null; } } /** * Checks to see if both lhtPrefix and rhtPrefix have the same prefix as specificPrefix * * @param lhtPrefix left hand side prefix * @param rhtPrefix right hand side prefix * @param specificPrefix specific desired prefix to match * @return true if all 3 Prefix are the same, false otherwise */ protected boolean bothHaveSpecificPrefix( Prefix lhtPrefix, Prefix rhtPrefix, Prefix specificPrefix) { if (lhtPrefix == null || rhtPrefix == null || specificPrefix == null) { return false; } return (lhtPrefix.equals(rhtPrefix) && rhtPrefix.equals(specificPrefix)); } /** * Checks to see if lht has the unit ul and if rht has the unit ur all at the same time * * @param lht left hand annotated type * @param ul left hand unit * @param rht right hand annotated type * @param ur right hand unit * @return true if lht has lu and rht has ru, false otherwise */ protected boolean havePairOfUnits( AnnotatedTypeMirror lht, AnnotationMirror ul, AnnotatedTypeMirror rht, AnnotationMirror ur) { return UnitsRelationsTools.hasSpecificUnit(lht, ul) && UnitsRelationsTools.hasSpecificUnit(rht, ur); } /** * Checks to see if lht and rht have the pair of units u1 and u2 regardless of order * * @param lht left hand annotated type * @param u1 unit 1 * @param rht right hand annotated type * @param u2 unit 2 * @return true if lht and rht have the pair of units u1 and u2 regardless of order, false * otherwise */ protected boolean havePairOfUnitsIgnoringOrder( AnnotatedTypeMirror lht, AnnotationMirror u1, AnnotatedTypeMirror rht, AnnotationMirror u2) { return havePairOfUnits(lht, u1, rht, u2) || havePairOfUnits(lht, u2, rht, u1); } }