/* * Nocturne * Copyright (c) 2015-2016, Lapis <https://github.com/LapisBlue> * * The MIT License * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package blue.lapis.nocturne.processor.index; import blue.lapis.nocturne.processor.index.model.IndexedClass; import blue.lapis.nocturne.processor.index.model.IndexedMethod; import blue.lapis.nocturne.util.helper.HierarchyHelper; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Sets; import java.util.HashSet; import java.util.Set; import java.util.stream.Collectors; /** * Hierarchy builder for indexed classes. */ public class ClassHierarchyBuilder { private final ImmutableMap<String, IndexedClass> classes; public ClassHierarchyBuilder(Set<IndexedClass> classes) { this.classes = ImmutableMap.copyOf(classes.stream().collect(Collectors.toMap(IndexedClass::getName, c -> c))); } public void buildHierarchies() { buildClassHierarchies(); buildMethodHierarchies(); classes.values().forEach(c -> { c.finalizeHierarchy(); c.getMethods().values().forEach(IndexedMethod::finalizeHierarchy); }); } private void buildClassHierarchies() { for (IndexedClass clazz : classes.values()) { if (!clazz.getHierarchy().isEmpty()) { Set<IndexedClass> toAdd = new HashSet<>(); for (IndexedClass child : clazz.getHierarchy()) { toAdd.addAll(child.getHierarchy()); } clazz.getHierarchy().addAll(toAdd); } else { Set<IndexedClass> parents = getParents(clazz); clazz.getHierarchy().addAll(parents); } clazz.getHierarchy().forEach(c -> c.getHierarchy().add(clazz)); } } private Set<IndexedClass> getParents(IndexedClass clazz) { return getParents(clazz, true); } private Set<IndexedClass> getParents(IndexedClass clazz, boolean returnEmpty) { Set<IndexedClass> parents = new HashSet<>(); Set<String> parentNames = Sets.newHashSet(clazz.getInterfaces()); parentNames.add(clazz.getSuperclass()); Set<IndexedClass> directParents = parentNames.stream().filter(classes::containsKey) .map(classes::get).collect(Collectors.toSet()); parents.addAll(directParents); for (IndexedClass ic : directParents) { parents.addAll(getParents(ic, false)); } return !parents.isEmpty() ? parents : returnEmpty ? new HashSet<>() : Sets.newHashSet(clazz); } private void buildMethodHierarchies() { for (IndexedClass clazz : classes.values()) { clazz.getMethods().values().stream() .filter(method -> method.getVisibility() != IndexedMethod.Visibility.PRIVATE) .forEach(method -> { method.getHierarchy().addAll(clazz.getHierarchy().stream() .filter(c -> c.getMethods().containsKey(method.getSignature()) && HierarchyHelper.isVisible( clazz.getName(), c.getName(), c.getMethods().get(method.getSignature()).getVisibility())) .collect(Collectors.toSet())); }); } } }