/* * Hibernate Validator, declare and validate application constraints * * License: Apache License, Version 2.0 * See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>. */ package org.hibernate.validator.ap.classchecks; import java.util.Collections; import java.util.Map; import java.util.Set; import javax.lang.model.element.ExecutableElement; import org.hibernate.validator.ap.util.CollectionHelper; /** * Represents an inheritance tree of overridden methods. In the head of the tree a node from which we start looking for * overridden methods is located. Also contains some useful methods to walk around overridden methods. * * @author Marko Bekhta * @author Guillaume Smet */ public class MethodInheritanceTree { private final MethodNode rootMethodNode; private final Map<ExecutableElement, MethodNode> methodNodeMapping; private final Set<ExecutableElement> topLevelMethods; private final Set<ExecutableElement> overriddenMethods; /** * Initializes a {@link MethodInheritanceTree}. * * @param rootMethodNode the root method {@link MethodNode} * @param nodeMapping the mapping between the method represented by an {@link ExecutableElement} and the corresponding {@link MethodNode} */ private MethodInheritanceTree(MethodNode rootMethodNode, Map<ExecutableElement, MethodNode> nodeMapping) { this.rootMethodNode = rootMethodNode; this.methodNodeMapping = Collections.unmodifiableMap( nodeMapping ); this.topLevelMethods = buildTopLevelMethodSet(); this.overriddenMethods = buildOverriddenMethodSet(); } /** * Checks if there are any overridden methods in the hierarchy. * * @return {@code true} if there are any overridden methods found, {@code false} otherwise */ public boolean hasOverriddenMethods() { return overriddenMethods.size() > 0; } /** * Returns a set containing all the methods of the hierarchy. * * @return a set containing all the methods of the hierarchy */ public Set<ExecutableElement> getAllMethods() { return Collections.unmodifiableSet( methodNodeMapping.keySet() ); } /** * Returns a set containing all the overridden methods. * * @return a set containing all the overridden methods */ public Set<ExecutableElement> getOverriddenMethods() { return overriddenMethods; } /** * Checks if there are any parallel definitions of the method in the hierarchy. * * @return {@code true} if there are any parallel definitions of the method in the hierarchy, {@code false} otherwise */ public boolean hasParallelDefinitions() { return topLevelMethods.size() > 1; } /** * Returns a set containing all the top level overridden methods. * * @return a set containing all the top level overridden methods */ public Set<ExecutableElement> getTopLevelMethods() { return topLevelMethods; } private Set<ExecutableElement> buildOverriddenMethodSet() { Set<ExecutableElement> overriddenMethods = CollectionHelper.newHashSet(); for ( ExecutableElement method : methodNodeMapping.keySet() ) { if ( !rootMethodNode.getMethod().equals( method ) ) { overriddenMethods.add( method ); } } return Collections.unmodifiableSet( overriddenMethods ); } private Set<ExecutableElement> buildTopLevelMethodSet() { Set<ExecutableElement> methods = CollectionHelper.newHashSet(); for ( MethodNode methodNode : methodNodeMapping.values() ) { if ( !methodNode.isOverriding() ) { methods.add( methodNode.getMethod() ); } } return Collections.unmodifiableSet( methods ); } @Override public String toString() { StringBuilder sb = new StringBuilder() .append( "MethodInheritanceTree [" ) .append( "rootMethodNode=" ).append( rootMethodNode ) .append( "]" ); return sb.toString(); } public static class Builder { private final MethodNode rootMethodNode; private final Map<ExecutableElement, MethodNode> nodeMapping = CollectionHelper.newHashMap(); public Builder(ExecutableElement rootMethod) { rootMethodNode = new MethodNode( rootMethod ); nodeMapping.put( rootMethod, rootMethodNode ); } public void addOverriddenMethod(ExecutableElement overridingMethod, ExecutableElement overriddenMethod) { MethodNode overriddenMethodNode = new MethodNode( overriddenMethod ); nodeMapping.get( overridingMethod ).addOverriddenMethodNode( overriddenMethodNode ); nodeMapping.put( overriddenMethod, overriddenMethodNode ); } public MethodInheritanceTree build() { return new MethodInheritanceTree( rootMethodNode, nodeMapping ); } } /** * Node of the tree that contains information about the method, its enclosing type and its overridden methods. */ private static class MethodNode { private ExecutableElement method; private Set<MethodNode> overriddenMethodNodes; private MethodNode(ExecutableElement method) { this.method = method; this.overriddenMethodNodes = CollectionHelper.newHashSet(); } private boolean isOverriding() { return !overriddenMethodNodes.isEmpty(); } private ExecutableElement getMethod() { return method; } private void addOverriddenMethodNode(MethodNode overriddenMethodNode) { overriddenMethodNodes.add( overriddenMethodNode ); } @Override public String toString() { StringBuilder sb = new StringBuilder() .append( "MethodNode [" ) .append( "method=" ).append( method ) .append( "]" ); return sb.toString(); } } }