therian.operator.immutablecheck.DefaultImmutableChecker.java Source code

Java tutorial

Introduction

Here is the source code for therian.operator.immutablecheck.DefaultImmutableChecker.java

Source

/*
 *  Copyright the original author or authors.
 *
 *  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 therian.operator.immutablecheck;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;

import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.StringUtils;

import therian.buildweaver.StandardOperator;

/**
 * Checks for types universally known to be immutable.
 */
@StandardOperator
public class DefaultImmutableChecker extends ImmutableChecker {

    private static final String[] KNOWN_IMMUTABLE_PREFIXES;
    private static final Set<Class<?>> KNOWN_IMMUTABLE_TYPES;

    static {
        KNOWN_IMMUTABLE_PREFIXES = new String[] { "immutable", "unmodifiable", "empty" };
        final HashSet<Class<?>> s = new HashSet<Class<?>>();
        Collections.<Class<?>>addAll(s, String.class, Enum.class, Annotation.class, Class.class,
                Arrays.asList().getClass());
        addTypeTo(s, Collections.emptySet());
        addTypeTo(s, Collections.unmodifiableSet(new HashSet<>()));
        addTypeTo(s, Collections.emptyList());
        addTypeTo(s, Collections.unmodifiableList(new ArrayList<>()));
        addTypeTo(s, Collections.unmodifiableSortedSet(new TreeSet<>()));
        addTypeTo(s, Collections.emptyMap());
        addTypeTo(s, Collections.unmodifiableMap(new HashMap<>()));
        addTypeTo(s, Collections.unmodifiableSortedMap(new TreeMap<>()));

        Collections.addAll(s, Collections.singleton(null).getClass(), Collections.singletonList(null).getClass(),
                Collections.singletonMap(null, null).getClass());

        KNOWN_IMMUTABLE_TYPES = Collections.unmodifiableSet(s);
    }

    private static void addTypeTo(final Set<Class<?>> target, final Collection<?> coll) {
        addImmutableTypeTo(target, coll.getClass());
        addImmutableTypeTo(target, coll.iterator().getClass());
    }

    private static void addTypeTo(final Set<Class<?>> target, final List<?> list) {
        addTypeTo(target, (Collection<?>) list);
        addImmutableTypeTo(target, list.listIterator().getClass());
    }

    private static void addTypeTo(final Set<Class<?>> target, final SortedSet<String> sortedSet) {
        addTypeTo(target, (Collection<?>) sortedSet);
        addTypeTo(target, (Set<String>) sortedSet.headSet("foo"));
        addTypeTo(target, (Set<String>) sortedSet.tailSet("foo"));
        addTypeTo(target, (Set<String>) sortedSet.subSet("foo", "foo"));
    }

    private static void addTypeTo(final Set<Class<?>> target, final Map<?, ?> map) {
        addImmutableTypeTo(target, map.getClass());
        addTypeTo(target, map.keySet());
        addTypeTo(target, map.values());
    }

    private static void addTypeTo(final Set<Class<?>> target, final SortedMap<String, ?> sortedMap) {
        addTypeTo(target, (Map<String, ?>) sortedMap);
        addTypeTo(target, (Map<String, ?>) sortedMap.headMap("foo"));
        addTypeTo(target, (Map<String, ?>) sortedMap.tailMap("foo"));
        addTypeTo(target, (Map<String, ?>) sortedMap.subMap("foo", "foo"));
    }

    private static void addImmutableTypeTo(final Set<Class<?>> target, final Class<?> type) {
        if (target.contains(type)) {
            return;
        }
        Class<?> c = type;
        while (c.isAnonymousClass()) {
            c = c.getEnclosingClass();
        }
        if (target.contains(c) && !target.equals(c)
                || StringUtils.startsWithAny(c.getSimpleName().toLowerCase(Locale.US), KNOWN_IMMUTABLE_PREFIXES)) {
            target.add(type);
        }
    }

    @Override
    protected boolean isImmutable(Object object) {
        if (object == null) {
            return true;
        }
        final Class<?> cls = object.getClass();
        if (cls.isPrimitive()) {
            return true;
        }
        if (ClassUtils.wrapperToPrimitive(cls) != null) {
            return true;
        }
        // quick check:
        if (KNOWN_IMMUTABLE_TYPES.contains(cls)) {
            return true;
        }
        // inheritance too:
        for (final Class<?> type : KNOWN_IMMUTABLE_TYPES) {
            if (type.isInstance(object)) {
                return true;
            }
        }
        return cls.equals(Object.class);
    }
}