us.bpsm.edn.protocols.C3 Maven / Gradle / Ivy
package us.bpsm.edn.protocols;
import java.util.ArrayList;
import java.util.List;
import us.bpsm.edn.EdnException;
/**
* Implements the C3 superclass
* linearization algorithm.
*/
class C3 {
/**
* Return a linearization for the inheritance hierarchy of the
* {@code class} {@code c}. The linearization will includes
* interfaces as well as classes and considers {@link Object} to
* be the ultimate superclass.
*
* @param c represents an actual class, not an
* interface. Never null.
*
* @return The linearization of c: never null and never empty.
*
* @throws EdnException if the inheritance hiearchy of {@code c}
* makes it impossible to compuate a consistent hierarchy
* for {@code c}.
*/
static List> methodResolutionOrder(Class> c) {
try {
List> result = mro(c);
if (c.getSuperclass() != null) {
result.add(Object.class);
}
return result;
} catch (InconsistentHierarchy e) {
StringBuilder b = new StringBuilder()
.append("Unable to compute a consistent ")
.append("method resolution order for ")
.append(c.getName());
if (c.equals(e.problematicClass)) {
b.append(".");
} else {
b.append(" because ").append(e.problematicClass.getName())
.append(" has no consistent method resolution order.");
}
throw new EdnException(b.toString());
}
}
private static List> mro(Class> c) throws InconsistentHierarchy {
List>> seqsToMerge = new ArrayList>>();
seqsToMerge.add(asList(c));
List> supers = supers(c);
for (Class> s : supers) {
seqsToMerge.add(mro(s));
}
seqsToMerge.add(supers);
try {
return merge(seqsToMerge);
} catch (InconsistentHierarchy e) {
throw new InconsistentHierarchy(c);
}
}
private static List> asList(Class> c) {
List> result = new ArrayList>(1);
result.add(c);
return result;
}
private static List> supers(Class> c) {
Class> sc = c.getSuperclass();
Class>[] interfaces = c.getInterfaces();
List> result = new ArrayList>();
if (sc != null && sc != Object.class) {
result.add(sc);
}
for (Class> i : interfaces) {
result.add(i);
}
return result;
}
private static List> merge(List>> seqsToMerge)
throws InconsistentHierarchy {
List> result = new ArrayList>();
while (!allAreEmpty(seqsToMerge)) {
Class> candidate = findCandidate(seqsToMerge);
if (candidate == null) {
throw new InconsistentHierarchy();
}
result.add(candidate);
removeCandidate(seqsToMerge, candidate);
}
return result;
}
private static boolean allAreEmpty(List>> lists) {
for (List> l : lists) {
if (!l.isEmpty()) {
return false;
}
}
return true;
}
private static Class> findCandidate(List>> seqsToMerge) {
for (List> seq : seqsToMerge) {
if (!seq.isEmpty() && !occursInSomeTail(seqsToMerge, seq.get(0))) {
return seq.get(0);
}
}
return null;
}
private static boolean occursInSomeTail(List>> seqsToMerge,
Object c) {
for (List> seq : seqsToMerge) {
for (int i = 1; i < seq.size(); i++) {
if (c.equals(seq.get(i))) {
return true;
}
}
}
return false;
}
private static void removeCandidate(List>> seqsToMerge,
Class> candidate) {
for (List> seq : seqsToMerge) {
if (!seq.isEmpty() && candidate.equals(seq.get(0))) {
seq.remove(0);
}
}
}
static class InconsistentHierarchy extends Exception {
private static final long serialVersionUID = 1L;
Class> problematicClass;
InconsistentHierarchy(Class> problematicClass) {
super();
this.problematicClass = problematicClass;
}
InconsistentHierarchy() {
super();
}
}
}