Skip to content

Commit

Permalink
Merge remote branch 'fork/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
rultor committed Sep 26, 2014
2 parents 8f293da + 847178b commit fa9bf28
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 10 deletions.
15 changes: 7 additions & 8 deletions src/main/java/com/jcabi/aspects/aj/ImmutabilityChecker.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,13 @@ private void check(final Class<?> type)
type.getName()
)
);
} else if (!Modifier.isFinal(type.getModifiers())) {
throw new Violation(
String.format(
"Class '%s' is not final",
type.getName()
)
);
}
try {
this.fields(type);
Expand Down Expand Up @@ -177,14 +184,6 @@ private void fields(final Class<?> type)
)
);
}
if (!Modifier.isPrivate(field.getModifiers())) {
throw new ImmutabilityChecker.Violation(
String.format(
"field '%s' is not private",
field
)
);
}
try {
if (field.getType() != type) {
this.check(field.getType());
Expand Down
128 changes: 126 additions & 2 deletions src/test/java/com/jcabi/aspects/ImmutableTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@

/**
* Test case for {@link Immutable} annotation and its implementation.
*
* @author Yegor Bugayenko ([email protected])
* @version $Id$
* @checkstyle ConstantUsageCheck (500 lines)
Expand Down Expand Up @@ -69,6 +70,15 @@ public void catchedMutableTypesWithInterfaces() {
new MutableWithInterface();
}

/**
* Immutable can catch mutable classes with mutable implementation of
* immutable interfaces.
*/
@Test(expected = IllegalStateException.class)
public void catchedMutableTypesWithImplementationOfImmutableInterface() {
new MutableWithImmutableInterface();
}

/**
* Immutable can pass immutable classes.
*/
Expand All @@ -77,6 +87,22 @@ public void passesImmutableObjects() {
new TruelyImmutable();
}

/**
* Immutable can pass immutable classes.
*/
@Test
public void passesImmutableObjectsWithNonPrivateFields() {
new TruelyImmutableWithNonPrivateFields();
}

/**
* Immutable can catch mutable classes with interfaces.
*/
@Test(expected = IllegalStateException.class)
public void catchedTypesMutableByClassInheritance() {
new MutableByInheritance();
}

/**
* Supposedly immutable class.
*/
Expand Down Expand Up @@ -117,6 +143,47 @@ private static final class MutableWithInterface {
private final transient MutableInterface data = null;
}

/**
* Other vague interface.
*/
@Immutable
private interface ImmutableInterface {
/**
* This function seems to be harmless.
* @param input An input
*/
void willBreakImmutability(int input);
}

/**
* Mutable class implementing immutable interface.
*/
@Immutable
private static final class MutableWithImmutableInterface {
/**
* Supposedly immutable field that is not immutable after all.
*/
private final ImmutableInterface impl = new ImmutableInterface() {
private int state = 1;

/**
* This function breaks the immutability promised by the interface.
*/
@Override
public void willBreakImmutability(final int newstate) {
this.state = newstate;
}
};

/**
* Stupid getter.
* @return A handle to mutate the object. Not good...
*/
public ImmutableInterface getImpl() {
return this.impl;
}
}

/**
* Truely immutable class.
*/
Expand All @@ -125,11 +192,11 @@ private static final class TruelyImmutable {
/**
* Something static final.
*/
private static final Pattern PATTERN = Pattern.compile(".*");
private static final Pattern PATTERN = Pattern.compile(".?");
/**
* Something just static.
*/
private static Pattern ptrn = Pattern.compile(".+");
private static Pattern ptrn = Pattern.compile("\\d+");
/**
* Immutable class member.
*/
Expand All @@ -149,4 +216,61 @@ private static final class TruelyImmutable {
private final transient String[] texts = new String[] {"foo"};
}

/**
* Truely immutable class with non-private fields.
*
* @checkstyle VisibilityModifier (25 lines)
*/
@Immutable
private static final class TruelyImmutableWithNonPrivateFields {
/**
* Something static final.
*/
private static final Pattern PATTERN = Pattern.compile(".*");
/**
* Something just static.
*/
private static Pattern ptrn = Pattern.compile(".+");
/**
* Immutable class member.
*/
private final transient String data = null;
/**
* Another immutable class member.
*/
private final transient int number = 2;
/**
* Another immutable class member.
*/
private final transient String text = "Hello!";
}

/**
* Almost immutable class. It can be inherited, because it is non-final;
* thus methods in the child class can return nonsensical values (e.g.
* getters that do no return the original value of their corresponding
* fields). Moreover, immutability cannot be forced to a subclass.
* See <a href=
* "http://marxsoftware.blogspot.se/2009/09/
* is-java-immutable-class-always-final.html">
* Is java immutable class always final?</a>
*/
@Immutable
private static class MutableByInheritance {
/**
* Immutable class member.
*/
private final transient String data = null;

/**
* Could be overloaded by a child of the class and then return
* nonsensical value.
*
* @return A value that could differ from what is expected if returned by an overriding method
*/
public String getData() {
return this.data;
}
}

}

0 comments on commit fa9bf28

Please sign in to comment.