com.google.errorprone.bugpatterns.threadsafety.WellKnownMutability.java Source code

Java tutorial

Introduction

Here is the source code for com.google.errorprone.bugpatterns.threadsafety.WellKnownMutability.java

Source

/*
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.errorprone.bugpatterns.threadsafety;

import static com.google.common.base.Preconditions.checkNotNull;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.common.collect.Sets.SetView;
import com.google.common.primitives.Primitives;
import com.google.errorprone.VisitorState;
import com.google.errorprone.suppliers.Supplier;
import com.google.errorprone.suppliers.Suppliers;
import com.sun.tools.javac.code.Type;
import java.lang.reflect.TypeVariable;
import java.util.HashSet;
import java.util.Set;

/** A collection of types with with known mutability. */
final class WellKnownMutability {

    /** Types that are known to be immutable. */
    static final ImmutableMap<String, ImmutableAnnotationInfo> KNOWN_IMMUTABLE = getBootstrapClasses();

    static class Builder {
        final ImmutableMap.Builder<String, ImmutableAnnotationInfo> mapBuilder = ImmutableMap.builder();

        public Builder addAll(Set<Class<?>> clazzs) {
            for (Class<?> clazz : clazzs) {
                add(clazz);
            }
            return this;
        }

        public Builder add(Class<?> clazz, String... containerOf) {
            ImmutableSet<String> containerTyParams = ImmutableSet.copyOf(containerOf);
            HashSet<String> actualTyParams = new HashSet<>();
            for (TypeVariable<?> x : clazz.getTypeParameters()) {
                actualTyParams.add(x.getName());
            }
            SetView<String> difference = Sets.difference(containerTyParams, actualTyParams);
            if (!difference.isEmpty()) {
                throw new AssertionError(String.format("Container type parameter(s) %s did not exist, expected %s",
                        difference, actualTyParams));
            }
            mapBuilder.put(clazz.getName(),
                    ImmutableAnnotationInfo.create(clazz.getName(), ImmutableList.copyOf(containerOf)));
            return this;
        }

        public Builder add(String className, String... containerOf) {
            mapBuilder.put(className, ImmutableAnnotationInfo.create(className, ImmutableList.copyOf(containerOf)));
            return this;
        }

        public ImmutableMap<String, ImmutableAnnotationInfo> build() {
            return mapBuilder.build();
        }
    }

    // TODO(cushon): share this list with other code analyzing types for immutability
    // TODO(cushon): generate this at build-time to get type-safety without added compile-time deps
    private static ImmutableMap<String, ImmutableAnnotationInfo> getBootstrapClasses() {
        return new Builder().addAll(Primitives.allPrimitiveTypes()).addAll(Primitives.allWrapperTypes())
                .add(java.lang.annotation.Annotation.class).add(java.lang.Class.class).add(java.lang.String.class)
                .add(java.math.BigDecimal.class).add(java.net.InetAddress.class).add(java.util.Locale.class)
                .add(java.util.regex.Pattern.class).add("java.util.Optional", "T").add("org.joda.time.DateTime")
                .add("org.joda.time.DateTimeZone").add("org.joda.time.Duration").add("org.joda.time.Instant")
                .add("org.joda.time.LocalDate").add("org.joda.time.LocalDateTime").add("org.joda.time.Period")
                .add("org.joda.time.format.DateTimeFormatter").add(com.google.common.primitives.UnsignedLong.class)
                .add(com.google.common.base.Converter.class).add("com.google.protobuf.ByteString")
                .add("com.google.protobuf.Descriptors$Descriptor")
                .add("com.google.protobuf.Descriptors$EnumDescriptor")
                .add("com.google.protobuf.Descriptors$EnumValueDescriptor")
                .add("com.google.protobuf.Descriptors$FieldDescriptor")
                .add("com.google.protobuf.Descriptors$FileDescriptor")
                .add("com.google.protobuf.Descriptors$ServiceDescriptor")
                .add("com.google.protobuf.ExtensionRegistry$ExtensionInfo").add("com.google.common.net.MediaType")
                .add("com.google.common.net.Uri").add("com.google.protobuf.Extension")
                .add("com.google.re2j.Pattern").add(com.google.common.collect.ImmutableCollection.class, "E")
                .add(com.google.common.collect.ImmutableSet.class, "E")
                .add(com.google.common.collect.ImmutableList.class, "E")
                .add(com.google.common.collect.ImmutableMultiset.class, "E")
                .add(com.google.common.collect.ImmutableMap.class, "K", "V")
                .add(com.google.common.collect.ImmutableBiMap.class, "K", "V")
                .add(com.google.common.collect.ImmutableMultimap.class, "K", "V")
                .add(com.google.common.collect.ImmutableListMultimap.class, "K", "V")
                .add(com.google.common.collect.ImmutableSetMultimap.class, "K", "V")
                .add(com.google.common.collect.ImmutableSortedMap.class, "K", "V")
                .add(com.google.common.collect.ImmutableRangeMap.class, "K", "V")
                .add(com.google.common.collect.ImmutableTable.class, "R", "C", "V")
                .add(com.google.common.base.Optional.class, "T").add(com.google.common.base.Splitter.class)
                .add(com.google.common.base.Joiner.class).add(com.google.common.collect.Range.class, "C")
                .add("android.net.Uri").add("java.time.Duration").add("java.time.Instant")
                .add("java.time.LocalDate").add("java.time.LocalDateTime").add("java.time.LocalTime")
                .add("java.time.MonthDay").add("java.time.OffsetDateTime").add("java.time.OffsetTime")
                .add("java.time.Period").add("java.time.Year").add("java.time.YearMonth")
                .add("java.time.ZonedDateTime").add("java.time.ZoneId").add("java.time.ZoneOffset")
                .add("java.time.chrono.AbstractChronology").add("java.time.chrono.ChronoLocalDate")
                .add("java.time.chrono.ChronoLocalDateTime", "D").add("java.time.chrono.Chronology")
                .add("java.time.chrono.ChronoPeriod").add("java.time.chrono.ChronoZonedDateTime", "D")
                .add("java.time.chrono.Era").add("java.time.chrono.HijrahChronology")
                .add("java.time.chrono.HijrahDate").add("java.time.chrono.IsoChronology")
                .add("java.time.chrono.JapaneseChronology").add("java.time.chrono.JapaneseDate")
                .add("java.time.chrono.JapaneseEra").add("java.time.chrono.MinguoChronology")
                .add("java.time.chrono.MinguoDate").add("java.time.chrono.ThaiBuddhistChronology")
                .add("java.time.chrono.ThaiBuddhistDate").add("java.time.format.DateTimeFormatter")
                .add("java.time.format.DecimalStyle").add("java.time.temporal.TemporalField")
                .add("java.time.temporal.TemporalUnit").add("java.time.temporal.ValueRange")
                .add("java.time.temporal.WeekFields").add("java.time.zone.ZoneOffsetTransition")
                .add("java.time.zone.ZoneOffsetTransitionRule").add("java.time.zone.ZoneRules")
                .add("java.time.zone.ZoneRulesProvider").build();
    }

    /** Types that are known to be mutable. */
    static final ImmutableSet<String> KNOWN_UNSAFE = getKnownUnsafeClasses();

    private static ImmutableSet<String> getKnownUnsafeClasses() {
        ImmutableSet.Builder<String> result = ImmutableSet.<String>builder();
        for (Class<?> clazz : ImmutableSet.<Class<?>>of(java.lang.Iterable.class, java.lang.Object.class,
                java.util.ArrayList.class, java.util.Collection.class, java.util.List.class, java.util.Map.class,
                java.util.Set.class, java.util.EnumSet.class, java.util.EnumMap.class)) {
            result.add(clazz.getName());
        }
        return result.build();
    }

    // ProtocolSupport matches Message (not MessageLite) for legacy reasons
    private static final Supplier<Type> MESSAGE_TYPE = Suppliers.typeFromString("com.google.protobuf.MessageLite");

    private static final Supplier<Type> MUTABLE_MESSAGE_TYPE = Suppliers
            .typeFromString("com.google.protobuf.MutableMessageLite");

    private static final Supplier<Type> PROTOCOL_MESSAGE_TYPE = Suppliers
            .typeFromString("com.google.io.protocol.ProtocolMessage");

    private static boolean isAssignableTo(Type type, Supplier<Type> supplier, VisitorState state) {
        Type to = supplier.get(state);
        if (to == null) {
            // the type couldn't be loaded
            return false;
        }
        return state.getTypes().isAssignable(type, to);
    }

    /**
     * Compile-time equivalent of
     * {@code com.google.io.protocol.ProtocolSupport#isProto2MessageClass}.
     */
    static boolean isProto2MessageClass(VisitorState state, Type type) {
        checkNotNull(type);
        return isAssignableTo(type, MESSAGE_TYPE, state) && !isAssignableTo(type, PROTOCOL_MESSAGE_TYPE, state);
    }

    /**
     * Compile-time equivalent of
     * {@code com.google.io.protocol.ProtocolSupport#isProto2MutableMessageClass}.
     */
    static boolean isProto2MutableMessageClass(VisitorState state, Type type) {
        checkNotNull(type);
        return isAssignableTo(type, MUTABLE_MESSAGE_TYPE, state)
                && !isAssignableTo(type, PROTOCOL_MESSAGE_TYPE, state);
    }

    /** Returns true if the type is an annotation. */
    static boolean isAnnotation(VisitorState state, Type type) {
        return isAssignableTo(type, Suppliers.ANNOTATION_TYPE, state);
    }
}