package org.revapi.java.compilation; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.TreeSet; import javax.annotation.Nonnull; /** * @author Lukas Krejci * @since 0.2.0 */ final class InnerClassHierarchyConstructor { private final TreeSet<OuterNameInnerNamePair> innerNames = new TreeSet<>(); public void addName(String outerName, String innerName) { innerNames.add(new OuterNameInnerNamePair(outerName, innerName)); } public List<InnerClass> process() { if (innerNames.isEmpty()) { return Collections.emptyList(); } String rootOwner; Iterator<OuterNameInnerNamePair> it = innerNames.iterator(); OuterNameInnerNamePair names = it.next(); if (names.outerName != null && names.innerName != null) { //only take into account non-anonymous, non-local inner classes rootOwner = names.outerName.replace('/', '.'); } else { return Collections.emptyList(); } StringBuilder binaryName = new StringBuilder(rootOwner); StringBuilder canonicalName = new StringBuilder(rootOwner); ArrayList<InnerClass> result = new ArrayList<>(); result.add(new InnerClass(binaryName.toString(), canonicalName.toString())); do { if (names.outerName == null || names.innerName == null) { //we only process non-anonymous, non-local inner classes return Collections.emptyList(); } String name = names.innerName; binaryName.append('$').append(name); canonicalName.append('.').append(name); result.add(new InnerClass(binaryName.toString(), canonicalName.toString())); //yes, the first element needs to be processed twice... if (it.hasNext()) { names = it.next(); } else { break; } } while (true); return result; } private static class OuterNameInnerNamePair implements Comparable<OuterNameInnerNamePair> { final String outerName; final String innerName; private OuterNameInnerNamePair(String outerName, String innerName) { this.outerName = outerName; this.innerName = innerName; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } OuterNameInnerNamePair that = (OuterNameInnerNamePair) o; if (outerName != null ? !outerName.equals(that.outerName) : that.outerName != null) { return false; } if (innerName != null ? !innerName.equals(that.innerName) : that.innerName != null) { return false; } return true; } @Override public int hashCode() { int result = outerName != null ? outerName.hashCode() : 0; result = 31 * result + (innerName != null ? innerName.hashCode() : 0); return result; } @Override public int compareTo(@Nonnull OuterNameInnerNamePair o) { int ret = safeCompare(outerName, o.outerName); return ret != 0 ? ret : safeCompare(innerName, o.innerName); } private static int safeCompare(String a, String b) { int ret; if (a == null) { ret = b == null ? 0 : 1; } else if (b == null) { ret = -1; } else { ret = a.compareTo(b); } return ret; } } }