Skip to content

Commit

Permalink
#889 revise source property order
Browse files Browse the repository at this point in the history
  • Loading branch information
elucash committed Dec 27, 2018
1 parent 32c4d04 commit 11cc457
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 61 deletions.
110 changes: 54 additions & 56 deletions generator/src/org/immutables/generator/SourceOrdering.java
Expand Up @@ -31,9 +31,12 @@
import com.google.common.collect.Ordering;
import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.annotation.Nullable;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
Expand Down Expand Up @@ -115,8 +118,8 @@ public Object apply(Element input) {

@Override
public Ordering<Element> enclosedBy(Element element) {
if (element instanceof ElementImpl &&
Iterables.all(element.getEnclosedElements(), Predicates.instanceOf(ElementImpl.class))) {
if (element instanceof ElementImpl
&& Iterables.all(element.getEnclosedElements(), Predicates.instanceOf(ElementImpl.class))) {

ElementImpl implementation = (ElementImpl) element;
if (implementation._binding instanceof SourceTypeBinding) {
Expand Down Expand Up @@ -169,82 +172,82 @@ public interface AccessorProvider {
* without handling complex cases.
* @param elements the elements utility
* @param types the types utility
* @param type the type to traverse
* @param originatingType the type to traverse
* @return provider of all accessors in source order and mapping
*/
public static AccessorProvider getAllAccessorsProvider(
final Elements elements,
final Types types,
final TypeElement type) {

class CollectedOrdering
extends Ordering<Element> {
final TypeElement originatingType) {

class CollectedOrdering extends Ordering<Element> {
class Intratype {
Ordering<String> ordering;
int rank;
final String inType;
final Ordering<String> ordering;
final int rank;

Intratype(String inType, int rank, List<String> accessors) {
this.inType = inType;
this.rank = rank;
this.ordering = Ordering.explicit(accessors);
}

@Override
public String toString() {
return "(<=> " + inType + ", " + rank + ", " + ordering + ")";
}
}

final Map<String, Intratype> accessorOrderings = Maps.newLinkedHashMap();
final List<TypeElement> linearizedTypes = Lists.newArrayList();
final Predicate<String> accessorNotYetInOrderings =
Predicates.not(Predicates.in(accessorOrderings.keySet()));
final Map<String, Intratype> accessorOrderings = new LinkedHashMap<>();
final Set<TypeElement> linearizedTypes = new LinkedHashSet<>();
final ArrayListMultimap<String, TypeElement> accessorMapping = ArrayListMultimap.create();

CollectedOrdering() {
traverse(type);
traverseObjectForInterface();
}

private void traverseObjectForInterface() {
if (type.getKind() == ElementKind.INTERFACE) {
traverse(elements.getTypeElement(Object.class.getName()));
}
traverse(originatingType);
collectAccessors();
}

void traverse(@Nullable TypeElement element) {
if (element == null || isJavaLangObject(element)) {
return;
}
collectEnclosing(element);
traverse(asTypeElement(element.getSuperclass()));
for (TypeMirror implementedInterface : element.getInterfaces()) {
traverse(asTypeElement(implementedInterface));
traverse(toElement(implementedInterface));
}
if (element.getKind().isClass()) {
// collectEnclosing(element);
traverse(toElement(element.getSuperclass()));
}
// we add this after so we start with the deepest
linearizedTypes.add(element);
}

@Nullable
TypeElement asTypeElement(TypeMirror type) {
TypeElement toElement(TypeMirror type) {
if (type.getKind() == TypeKind.DECLARED) {
return (TypeElement) ((DeclaredType) type).asElement();
}
return null;
}

void collectEnclosing(TypeElement type) {
FluentIterable<String> accessorsInType =
FluentIterable.from(SourceOrdering.getEnclosedElements(type))
.filter(IsParameterlessNonstaticNonobject.PREDICATE)
.transform(ToSimpleName.FUNCTION);

for (String accessor : accessorsInType) {
accessorMapping.put(accessor, type);
}

List<String> accessors =
accessorsInType
.filter(accessorNotYetInOrderings)
.toList();

Intratype intratype = new Intratype();
intratype.rank = linearizedTypes.size();
intratype.ordering = Ordering.explicit(accessors);

for (String name : accessors) {
accessorOrderings.put(name, intratype);
void collectAccessors() {
int i = 0;
for (TypeElement type : linearizedTypes) {
List<String> accessorsInType =
FluentIterable.from(SourceOrdering.getEnclosedElements(type))
.filter(IsParameterlessNonstaticNonobject.PREDICATE)
.transform(ToSimpleName.FUNCTION)
.toList();

String typeTag = type.getSimpleName().toString();
Intratype intratype = new Intratype(typeTag, i++, accessorsInType);

for (String name : accessorsInType) {
// we override accessors by the ones redeclared in later types
accessorMapping.put(name, type);
accessorOrderings.put(name, intratype);
}
}

linearizedTypes.add(type);
}

@Override
Expand All @@ -253,10 +256,6 @@ public int compare(Element left, Element right) {
String rightKey = ToSimpleName.FUNCTION.apply(right);
Intratype leftIntratype = accessorOrderings.get(leftKey);
Intratype rightIntratype = accessorOrderings.get(rightKey);
if (leftIntratype == null || rightIntratype == null) {
// FIXME figure out why it happens (null)
return Boolean.compare(leftIntratype == null, rightIntratype == null);
}
return leftIntratype == rightIntratype
? leftIntratype.ordering.compare(leftKey, rightKey)
: Integer.compare(leftIntratype.rank, rightIntratype.rank);
Expand All @@ -267,9 +266,8 @@ public int compare(Element left, Element right) {

final ImmutableList<ExecutableElement> sortedList =
ordering.immutableSortedCopy(
disambiguateMethods(
ElementFilter.methodsIn(
elements.getAllMembers(type))));
disambiguateMethods(ElementFilter.methodsIn(
elements.getAllMembers(originatingType))));

return new AccessorProvider() {
ImmutableListMultimap<String, TypeElement> accessorMapping =
Expand Down Expand Up @@ -304,7 +302,7 @@ private static List<ExecutableElement> disambiguateMethods(
if (values.size() == 1) {
resolvedMethods.addAll(values);
} else {
// Preferebly take the one coming from a class rather than interface
// Preferably take the one coming from a class rather than interface
for (ExecutableElement v : values) {
if (v.getEnclosingElement().getKind().isClass()) {
resolvedMethods.add(v);
Expand Down
Expand Up @@ -19,6 +19,9 @@

interface InheritedInterface {
int b();

int y();

int a();
}

Expand All @@ -27,5 +30,6 @@ public abstract class SourceOrderingEntity implements InheritedInterface {

public abstract int z();

@Override
public abstract int y();
}
10 changes: 5 additions & 5 deletions value-fixture/test/org/immutables/fixture/ValuesTest.java
Expand Up @@ -185,13 +185,13 @@ public void primitiveDefault() {
@Test
public void sourceOrdering() {
SourceOrderingEntity v = ImmutableSourceOrderingEntity.builder()
.a(1)
.b(2)
.y(3)
.z(4)
.b(2) // b from inherited in source order
.a(1) // a from inherited in source order, y skipped because overriden next
.z(4) // z directly declared in source order
.y(3) // y overriden here, in source order
.build();

check(v).hasToString("SourceOrderingEntity{z=4, y=3, b=2, a=1}");
check(v).hasToString("SourceOrderingEntity{b=2, a=1, z=4, y=3}");
}

@Test
Expand Down

0 comments on commit 11cc457

Please sign in to comment.