package org.mutabilitydetector;
/*
* #%L
* MutabilityDetector
* %%
* Copyright (C) 2008 - 2014 Graham Allan
* %%
* 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.
* #L%
*/
import static java.util.Arrays.asList;
/**
* The various reasons that instances of a class can be considered mutable.
*
*
* @author Graham Allan / Grundlefleck at gmail dot com
*
*/
public enum MutabilityReason implements Reason {
/**
* Class could not be analysed. Possible reasons include not being able to
* load the class correctly.
*/
CANNOT_ANALYSE("Class could not be analysed. Possible reasons include not being able to load the class correctly.",
IsImmutable.COULD_NOT_ANALYSE),
/**
* For an object to be immutable, its fields must also be immutable. By
* assigning an abstract type to a field, it is not known if the concrete
* fields supplied will be immutable or not.
*/
ABSTRACT_TYPE_TO_FIELD("For an object to be immutable, its fields must also be immutable. "
+ "By assigning an abstract type to a field, "
+ "it is not known if the concrete fields supplied will be immutable or not.", IsImmutable.NOT_IMMUTABLE),
/**
*
*/
ABSTRACT_COLLECTION_TYPE_TO_FIELD(
"JDK collection types are defined to be mutable (their interfaces declare mutation methods). Assigning a collection type to "
+ "a field is similar to assigning abstract type, except that there is a common idiom which can be used to guarantee the collection is not mutated. "
+ "That is to copy the collection and wrap in an unmodifiable collection, using one of wrapping methods on java.util.Collections, e.g. unmodifiableList. Unless that idiom is "
+ "used, the field is considered mutable. ", IsImmutable.NOT_IMMUTABLE),
COLLECTION_FIELD_WITH_MUTABLE_ELEMENT_TYPE(
"JDK collection types are defined to be mutable. While there is a very common idiom which "
+ "makes these collections immutable, if the type of the collection elements is mutable, the collection itself also becomes mutable. "
+ "For example, List<Date> dates = Collections.unmodifiableList(new ArrayList<Date>(srcList)), is a mutable list, because its elements "
+ "can be mutated.", IsImmutable.NOT_IMMUTABLE),
/**
* The given class can be subclassed. While this specific class may still be
* immutable, subclasses may not. It is recommended that the class be
* declared final, or all of its constructors declared private. This will
* allow clients to be confident that parameters declared to be this type
* will indeed be of this type at runtime, not an instance of a mutable
* subclass. Note that applying the final keyword to a class does not have
* any effect on the Java Memory Model.
*/
CAN_BE_SUBCLASSED(
"Class can be subclassed. While this specific class may still be immutable, it is recommended that the class "
+ "be declared final, or all of its constructors declared private. This will allow clients to "
+ "be confident that parameters declared to be this type will indeed be of this type at runtime, "
+ "not an instance of a mutable subclass. Note that applying the final keyword to a class does not have any effect on the Java Memory Model.",
IsImmutable.NOT_IMMUTABLE),
/**
* Abstract types (interfaces or abstract classes) are considered to be
* \"Inherently Mutable\" in particular cases. Because the concrete
* implementation cannot be known until compile-time, run-time instances of
* abstract types could be either mutable or immutable.
*/
ABSTRACT_TYPE_INHERENTLY_MUTABLE("Abstract types (interfaces or abstract classes) are considered to be "
+ "\"Inherently Mutable\" in particular cases. Because the concrete implementation cannot be known"
+ "until compile-time, instances of abstract types could be either mutable or immutable.",
IsImmutable.NOT_IMMUTABLE),
/**
* Since an array can be mutated after construction (by modifying what it
* contains) they are inherently mutable. However, since it is possible that
* a field which is an array type is never mutated after construction, it is
* still possible for the containing type to be immutable
*/
ARRAY_TYPE_INHERENTLY_MUTABLE("Since an array can be mutated after construction "
+ "(by modifying what it contains) they are inherently mutable. However, since it is possible "
+ "that a field which is an array type is never mutated after construction, it is still possible "
+ "for the containing type to be immutable.", IsImmutable.NOT_IMMUTABLE),
/**
* A mutable type can be assigned to a field. Since references to the
* mutable field may be kept, the containing type can be mutated after
* construction.
*
* This reason can also indicate a cyclic reference in the fields of
* several types. E.g. type A has a field of type B, and type B has a field
* of type A. Therefore one of these types has to not be completely
* constructed when passed to the other type.
*/
MUTABLE_TYPE_TO_FIELD("A mutable type can be assigned to a field. Since references to the mutable field "
+ "may be kept, the containing type can be mutated after construction.", IsImmutable.NOT_IMMUTABLE),
/**
* [Experimental] The 'this' reference escaped during construction. Whoever
* receives the reference may observe values of the object mutating.
*/
ESCAPED_THIS_REFERENCE(
"[Experimental] The 'this' reference escaped during construction. Whoever receives the reference may observe values of the object mutating.",
IsImmutable.NOT_IMMUTABLE),
/**
* Field is not declared final. If the object is published across threads
* the Java Memory Model does not guarantee that it will be fully
* initialised before it is read. However, if the object is only used in a
* single thread, reads are guaranteed to see the fully initialised fields.
*/
NON_FINAL_FIELD(
"Field is not declared final. If the object is published across threads the Java Memory Model "
+ "does not guarantee that it will be fully initialised before it is read. "
+ "However, if the object is only used in a single thread, reads are guaranteed to see the fully initialised fields.",
IsImmutable.EFFECTIVELY_IMMUTABLE),
/**
* Class has a published, non-final field. Fields of an immutable class may
* not be reassigned after an instance is constructed. If an accessible
* field is not made final, it can be reassigned.
*/
PUBLISHED_NON_FINAL_FIELD("Class has a published, non-final field. Fields of an immutable class may not be "
+ "reassigned after an instance is constructed. "
+ "If an accessible field is not made final, it can be reassigned.", IsImmutable.NOT_IMMUTABLE),
/**
* For a class to be immutable, fields cannot be reassigned once an instance
* is constructed. The most common example of this is JavaBean convention
* "setter" methods.
*/
FIELD_CAN_BE_REASSIGNED(
"For a class to be immutable, fields cannot be reassigned once an instance is constructed.",
IsImmutable.NOT_IMMUTABLE),
/**
* This is a placeholder reason.
*/
NULL_REASON("Placeholder reason for a null checker.", IsImmutable.COULD_NOT_ANALYSE);
private final String description;
private final String code;
private final IsImmutable createsResult;
MutabilityReason(String description, IsImmutable createsResult) {
this.description = description;
this.code = this.name();
this.createsResult = createsResult;
}
@Override
public String description() {
return description;
}
@Override
public String code() {
return code;
}
@Override
public IsImmutable createsResult() {
return createsResult;
}
@Override
public boolean isOneOf(Reason... reasons) {
return asList(reasons).contains(this);
}
}