CovarianceViolationDetector.java
package com.mackenziehigh.autumn.lang.compiler.utils;
import com.google.common.collect.Sets;
import java.util.Collections;
import java.util.Set;
/**
* This class can be used to detect covariance violations in a record-type.
*
* <p>
* Example Violation:
* <ul>
* <li>Let R be a design.</li>
* <li>Let S be a struct.</li>
* <li>S is a subtype of R.</li>
* <li>R defines an element E of type String.</li>
* <li>S defines an element E of type Object.</li>
* </ul>
* <br>
* This is a covariance-violation. because S->E is not a subtype of R->E.
* </p>
*
* @author Mackenzie High
*/
final class CovarianceViolationDetector
{
/**
* This method detects the covariance-violations in a given record-type.
*
* @param record is a representation of the record-type.
* @return an immutable set containing descriptions of the violation.
*/
public static Set<CovarianceViolation> detect(final RecordAnalyzer record)
{
final Set<CovarianceViolation> violations = Sets.newHashSet();
for (SetterMethod setter1 : record.setters)
{
for (SetterMethod setter2 : record.setters)
{
final CovarianceViolation violation = detect(setter1, setter2);
if (violation != null)
{
violations.add(violation);
}
}
}
return Collections.unmodifiableSet(violations);
}
/**
* This method determines whether two setter methods cause a covariance violation.
*
* @param setter1 is the description of a setter method.
* @param setter2 is the description of a setter method.
* @return a non-null value, iff a violation exists.
*/
private static CovarianceViolation detect(final SetterMethod setter1,
final SetterMethod setter2)
{
/**
* Two setters can only conflict, if they set the same element.
*/
if (setter1.name.equals(setter2.name) == false)
{
return null;
}
/**
* setter1 must be declared in a proper subtype of the type that declares setter2.
*/
if (setter1.owner.equals(setter2.owner))
{
return null;
}
if (setter1.owner.isSubtypeOf(setter2.owner) == false)
{
return null;
}
/**
* No problem exists, if setter1 or setter2 can simply redirect to the other.
*/
if (setter1.parameter.isSubtypeOf(setter2.parameter))
{
return null;
}
return new CovarianceViolation(setter1, setter2);
}
}