Path: blob/master/test/jdk/java/beans/XMLEncoder/BeanValidator.java
41149 views
/*1* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223import java.beans.IntrospectionException;24import java.beans.Introspector;25import java.beans.PropertyDescriptor;2627import java.lang.reflect.Array;28import java.lang.reflect.Field;29import java.lang.reflect.InvocationTargetException;30import java.lang.reflect.Method;31import java.lang.reflect.Modifier;3233import java.util.ArrayList;34import java.util.Collection;35import java.util.IdentityHashMap;36import java.util.Iterator;37import java.util.List;38import java.util.Map;39import java.util.Queue;40import java.util.SortedMap;41import java.util.SortedSet;4243final class BeanValidator {44private final Map<Object, Object> cache = new IdentityHashMap<Object, Object>();4546public void validate(Object object1, Object object2) {47// compare references48if (object1 == object2) {49return;50}51// check for null52if ((object1 == null) || (object2 == null)) {53throw new IllegalStateException("could not compare object with null");54}55// resolve self references56if (isCyclic(object1, object2)) {57return;58}59// resolve cross references60if (isCyclic(object2, object1)) {61return;62}63Class type = object1.getClass();64if (!type.equals(object2.getClass())) {65// resolve different implementations of the Map.Entry interface66if ((object1 instanceof Map.Entry) && (object2 instanceof Map.Entry)) {67log("!!! special case", "Map.Entry");68Map.Entry entry1 = (Map.Entry) object1;69Map.Entry entry2 = (Map.Entry) object2;70validate(entry1.getKey(), entry2.getKey());71validate(entry1.getValue(), entry2.getValue());72return;73}74throw new IllegalStateException("could not compare objects with different types");75}76// validate elements of arrays77if (type.isArray()) {78int length = Array.getLength(object1);79if (length != Array.getLength(object2)) {80throw new IllegalStateException("could not compare arrays with different lengths");81}82try {83this.cache.put(object1, object2);84for (int i = 0; i < length; i++) {85log("validate array element", Integer.valueOf(i));86validate(Array.get(object1, i), Array.get(object2, i));87}88} finally {89this.cache.remove(object1);90}91return;92}93// special case for collections: do not use equals94boolean ignore = Collection.class.isAssignableFrom(type)95|| Map.Entry.class.isAssignableFrom(type)96|| Map.class.isAssignableFrom(type);97// validate objects using equals()98// we assume that the method equals(Object) can be called,99// if the class declares such method100if (!ignore && isDefined(type, "equals", Object.class)) {101if (object1.equals(object2)) {102return;103}104throw new IllegalStateException("the first object is not equal to the second one");105}106// validate comparable objects using compareTo()107// we assume that the method compareTo(Object) can be called,108// if the class declares such method and implements interface Comparable109if (Comparable.class.isAssignableFrom(type) && isDefined(type, "compareTo", Object.class)) {110Comparable cmp = (Comparable) object1;111if (0 == cmp.compareTo(object2)) {112return;113}114throw new IllegalStateException("the first comparable object is not equal to the second one");115}116try {117this.cache.put(object1, object2);118// validate values of public fields119for (Field field : getFields(type)) {120int mod = field.getModifiers();121if (!Modifier.isStatic(mod)) {122log("validate field", field.getName());123validate(object1, object2, field);124}125}126// validate values of properties127for (PropertyDescriptor pd : getDescriptors(type)) {128Method method = pd.getReadMethod();129if (method != null) {130log("validate property", pd.getName());131validate(object1, object2, method);132}133}134// validate contents of maps135if (SortedMap.class.isAssignableFrom(type)) {136validate((Map) object1, (Map) object2, true);137} else if (Map.class.isAssignableFrom(type)) {138validate((Map) object1, (Map) object2, false);139}140// validate contents of collections141if (SortedSet.class.isAssignableFrom(type)) {142validate((Collection) object1, (Collection) object2, true);143} else if (List.class.isAssignableFrom(type)) {144validate((Collection) object1, (Collection) object2, true);145} else if (Queue.class.isAssignableFrom(type)) {146validate((Collection) object1, (Collection) object2, true);147} else if (Collection.class.isAssignableFrom(type)) {148validate((Collection) object1, (Collection) object2, false);149}150} finally {151this.cache.remove(object1);152}153}154155private void validate(Object object1, Object object2, Field field) {156try {157object1 = field.get(object1);158object2 = field.get(object2);159160validate(object1, object2);161}162catch (IllegalAccessException exception) {163log(exception);164}165}166167private void validate(Object object1, Object object2, Method method) {168try {169object1 = method.invoke(object1);170object2 = method.invoke(object2);171172validate(object1, object2);173}174catch (IllegalAccessException exception) {175log(exception);176}177catch (InvocationTargetException exception) {178log(exception.getCause());179}180}181182private void validate(Collection c1, Collection c2, boolean sorted) {183if (c1.size() != c2.size()) {184throw new IllegalStateException("could not compare collections with different sizes");185}186if (sorted) {187Iterator first = c1.iterator();188Iterator second = c2.iterator();189for (int i = 0; first.hasNext() && second.hasNext(); i++) {190log("validate collection element", Integer.valueOf(i));191validate(first.next(), second.next());192}193if (first.hasNext() || second.hasNext()) {194throw new IllegalStateException("one collection contains more elements than another one");195}196} else {197List list = new ArrayList(c2);198Iterator first = c1.iterator();199for (int i = 0; first.hasNext(); i++) {200Object value = first.next();201log("validate collection element", Integer.valueOf(i));202Iterator second = list.iterator();203for (int j = 0; second.hasNext(); j++) {204log("validate collection element against", Integer.valueOf(j));205try {206validate(value, second.next());207second.remove();208break;209} catch (IllegalStateException exception) {210if (!second.hasNext()) {211throw new IllegalStateException("one collection does not contain some elements from another one", exception);212}213}214}215}216}217}218219private void validate(Map map1, Map map2, boolean sorted) {220validate(map1.entrySet(), map2.entrySet(), sorted);221}222223private boolean isCyclic(Object object1, Object object2) {224Object object = this.cache.get(object1);225if (object == null) {226return false;227}228if (object == object2) {229return true;230}231throw new IllegalStateException("could not resolve cyclic reference");232}233234private boolean isDefined(Class type, String name, Class... params) {235try {236return type.equals(type.getMethod(name, params).getDeclaringClass());237}238catch (NoSuchMethodException exception) {239log(exception);240}241catch (SecurityException exception) {242log(exception);243}244return false;245}246247private static final Field[] FIELDS = {};248249private Field[] getFields(Class type) {250try {251return type.getFields();252}253catch (SecurityException exception) {254log(exception);255}256return FIELDS;257}258259private static final PropertyDescriptor[] DESCRIPTORS = {};260261private PropertyDescriptor[] getDescriptors(Class type) {262try {263return Introspector.getBeanInfo(type, Object.class).getPropertyDescriptors();264}265catch (IntrospectionException exception) {266log(exception);267}268return DESCRIPTORS;269}270271private final StringBuilder sb = new StringBuilder(1024);272273private void log(String message, Object value) {274this.sb.setLength(0);275int size = this.cache.size();276while (0 < size--) {277this.sb.append(" ");278}279this.sb.append(" - ");280this.sb.append(message);281if (value != null) {282this.sb.append(": ");283this.sb.append(value);284}285System.out.println(this.sb.toString());286}287288private void log(Throwable throwable) {289this.sb.setLength(0);290int size = this.cache.size();291while (0 < size--) {292this.sb.append(" ");293}294this.sb.append(" ? ");295this.sb.append(throwable);296System.out.println(this.sb.toString());297}298}299300301