/* * Copyright 2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.data.jpa.util; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.persistence.AttributeNode; import javax.persistence.EntityGraph; import javax.persistence.Subgraph; import org.hamcrest.Description; import org.hamcrest.TypeSafeMatcher; import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; /** * @author Christoph Strobl */ public class IsAttributeNode<T> extends TypeSafeMatcher<AttributeNode<T>> { private boolean terminatingNodeCheck = false; private List<String> nodes; private List<String> subgraphs; private List<String> errors = new ArrayList<String>(); @Override protected boolean matchesSafely(AttributeNode<T> item) { if (item == null) { errors.add("AttributeNode was null!"); return false; } if (terminatingNodeCheck) { if (!CollectionUtils.isEmpty(item.getSubgraphs())) { errors.add(String.format("'%s' was expected to be a terminating node but has subgraphs %s.", item.getAttributeName(), extractExistingAttributeNames(item.getSubgraphs().values().iterator().next()))); return false; } return true; } if (CollectionUtils.isEmpty(item.getSubgraphs())) { if (!CollectionUtils.isEmpty(nodes)) { errors .add(String.format("Leaf properties %s could not be found. The node does not have any subgraphs.", nodes)); } if (!CollectionUtils.isEmpty(subgraphs)) { errors.add(String.format("Subgraphs %s could not be found. The node does not have any subgraphs.", subgraphs)); } return false; } Subgraph<?> graph = item.getSubgraphs().values().iterator().next(); if (!CollectionUtils.isEmpty(nodes)) { for (String nodeName : nodes) { AttributeNode<?> node = findNode(nodeName, graph.getAttributeNodes()); if (node == null) { errors.add(String.format("AttributeNode '%s' could not be found in subgraph for '%s'. Know nodes are: %s.", nodeName, item.getAttributeName(), extractExistingAttributeNames(graph))); return false; } if (!CollectionUtils.isEmpty(node.getSubgraphs())) { errors.add(String.format("AttributeNode %s of subgraph %s is not a leaf property but has % SubGraph(s).", nodeName, item.getAttributeName(), node.getSubgraphs().size())); return false; } } } if (!CollectionUtils.isEmpty(subgraphs)) { for (String subgraphName : subgraphs) { AttributeNode<?> node = findNode(subgraphName, graph.getAttributeNodes()); if (node == null) { errors.add(String.format("Subgraph '%s' could not be found in SubGraph for '%s'. Know nodes are: %s.", subgraphName, item.getAttributeName(), extractExistingAttributeNames(graph))); return false; } if (CollectionUtils.isEmpty(node.getSubgraphs())) { errors.add(String.format("'%s' of SubGraph '%s' is not a SubGraph.", subgraphName, item.getAttributeName())); return false; } } } return true; } @Override public void describeTo(Description description) { for (String error : errors) { description.appendText(error); } } /** * Lookup the {@link AttributeNode} with given {@literal nodeName} in the root of the given {@literal graph}. * * @param nodeName * @param graph * @return */ public static AttributeNode<?> findNode(String nodeName, EntityGraph<?> graph) { if (graph == null) { return null; } return findNode(nodeName, graph.getAttributeNodes()); } /** * Lookup the {@link AttributeNode} with given {@literal nodeName} in the {@link List} of given {@literal nodes}. * * @param nodeName * @param nodes * @return */ public static AttributeNode<?> findNode(String nodeName, List<AttributeNode<?>> nodes) { if (CollectionUtils.isEmpty(nodes)) { return null; } for (AttributeNode<?> node : nodes) { if (ObjectUtils.nullSafeEquals(node.getAttributeName(), nodeName)) { return node; } } return null; } /** * Lookup the {@link AttributeNode} with given {@literal nodeName} in the first {@link Subgraph} of the given * {@literal node}. * * @param attributeName * @param node * @return */ public static AttributeNode<?> findNode(String attributeName, AttributeNode<?> node) { if (CollectionUtils.isEmpty(node.getSubgraphs())) { return null; } Subgraph<?> subgraph = node.getSubgraphs().values().iterator().next(); return findNode(attributeName, subgraph.getAttributeNodes()); } private List<String> extractExistingAttributeNames(Subgraph<?> graph) { List<String> result = new ArrayList<String>(graph.getAttributeNodes().size()); for (AttributeNode<?> node : graph.getAttributeNodes()) { result.add(node.getAttributeName()); } return result; } /** * Asserts that the fetch graph terminates with {@link AttributeNode}s having the given {@literal nodeNames}. * * @param nodeNames * @return */ public static IsAttributeNode terminatesGraphWith(String... nodeNames) { IsAttributeNode matcher = new IsAttributeNode(); matcher.nodes = Arrays.asList(nodeNames); return matcher; } /** * Asserts that the fetch graph continues with {@link AttributeNode}s having {@link AttributeNode#getSubgraphs()} with * given {@literal subgraphNames}. * * @return */ public static IsAttributeNode hasSubgraphs(String... subgraphNames) { IsAttributeNode matcher = new IsAttributeNode(); matcher.subgraphs = Arrays.asList(subgraphNames); return matcher; } /** * Asserts that the fetch graph terminates with the given {@link AttributeNode} by checking * {@link AttributeNode#getSubgraphs()} is empty. * * @return */ public static IsAttributeNode terminatesGraph() { IsAttributeNode matcher = new IsAttributeNode(); matcher.terminatingNodeCheck = true; return matcher; } }