/* * Copyright 2009 Google Inc. * * 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 com.google.common.css.compiler.ast.testing; import com.google.common.css.compiler.ast.CssNode; import com.google.common.css.testing.UtilityTestCase; import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.Collection; import java.util.Iterator; /** * Utility class for comparison of css nodes. * * @author oana@google.com (Oana Florescu) */ public abstract class AstUtilityTestCase extends UtilityTestCase { /** * Utility method for deep equals comparison between two css nodes. */ public void deepEquals(CssNode node1, CssNode node2) throws IllegalArgumentException, IllegalAccessException { Class<? extends CssNode> class1 = node1.getClass(); Class<? extends CssNode> class2 = node2.getClass(); assertEquals(class1, class2); Class<?> currentClass = class1; assertFieldsEqual(node1, node2, currentClass); while (!CssNode.class.equals(currentClass)) { currentClass = currentClass.getSuperclass(); assertFieldsEqual(node1, node2, currentClass); } } /** * Utility method to assert that the fields of two nodes are equal. The * comparison looks only at the current node and its descendants. Nodes * containing collections of CssNode objects are recursively compared. * * @param node1 Node1 for the comparison * @param node2 Node2 for the comparison * @param currentClass The class for which the fields are taken * @throws IllegalAccessException */ @SuppressWarnings("unchecked") private void assertFieldsEqual(CssNode node1, CssNode node2, Class<?> currentClass) throws IllegalAccessException { Field fields[] = currentClass.getDeclaredFields(); for (Field field : fields) { if ("parent".equals(field.getName())) { continue; } field.setAccessible(true); final Object value1 = field.get(node1); final Object value2 = field.get(node2); if (value1 != value2) { // Recursively compare fields of CssNode if (CssNode.class.isAssignableFrom(field.getType())) { deepEquals((CssNode) value1, (CssNode) value2); continue; } // Recursively compare fields that are collections of CssNodes if (isFieldCollectionOfCssNode(field)) { assertCollectionEqual( (Collection<? extends CssNode>) value1, (Collection<? extends CssNode>) value2); continue; } assertEquals("Field " + field, value1, value2); } } } private boolean isFieldCollectionOfCssNode(Field field) { // There are two ways in which this field can be a collection of CssNodes: // it can be something like Collection<SomethingThatExtendsCssNode>, // or it can be a variable type like Collection<T extends CssNode>. if (Collection.class.isAssignableFrom(field.getType()) && field.getGenericType() instanceof ParameterizedType) { ParameterizedType collectionType = (ParameterizedType) field.getGenericType(); for (Type type : collectionType.getActualTypeArguments()) { // This is a type that inherits from CssNode. if (type instanceof Class && CssNode.class.isAssignableFrom((Class<?>) type)) { return true; } // Type is a variable type that extends CssNode. if (type instanceof TypeVariable) { for (Type t : ((TypeVariable<?>) type).getBounds()) { if (t instanceof Class && CssNode.class.isAssignableFrom((Class<?>) t)) { return true; } } } } } return false; } private void assertCollectionEqual( Collection<? extends CssNode> collection1, Collection<? extends CssNode> collection2) throws IllegalArgumentException, IllegalAccessException { // Recursively compare two collections of {@code CssNode} instances. Iterator<? extends CssNode> it1 = collection1.iterator(); Iterator<? extends CssNode> it2 = collection2.iterator(); while (it1.hasNext() && it2.hasNext()) { CssNode n1 = it1.next(); CssNode n2 = it2.next(); deepEquals(n1, n2); } } }