Path: blob/master/src/java.desktop/share/classes/com/sun/beans/introspect/PropertyInfo.java
41171 views
/*1* Copyright (c) 2014, 2018, 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. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package com.sun.beans.introspect;2627import java.beans.BeanProperty;28import java.lang.reflect.Field;29import java.lang.reflect.Method;30import java.lang.reflect.Modifier;31import java.lang.reflect.Type;32import java.util.ArrayList;33import java.util.Collections;34import java.util.EnumMap;35import java.util.Iterator;36import java.util.List;37import java.util.Map;38import java.util.TreeMap;3940import static com.sun.beans.finder.ClassFinder.findClass;4142public final class PropertyInfo {4344public enum Name {45bound, expert, hidden, preferred, required, visualUpdate, description,46enumerationValues47}4849private static final String VETO_EXCEPTION_NAME = "java.beans.PropertyVetoException";50private static final Class<?> VETO_EXCEPTION;5152static {53Class<?> type;54try {55type = Class.forName(VETO_EXCEPTION_NAME);56} catch (Exception exception) {57type = null;58}59VETO_EXCEPTION = type;60}6162private Class<?> type;63private MethodInfo read;64private MethodInfo write;65private PropertyInfo indexed;66private List<MethodInfo> readList;67private List<MethodInfo> writeList;68private Map<Name,Object> map;6970private PropertyInfo() {71}7273private boolean initialize() {74boolean isInitedToIsGetter = false;75if (this.read != null) {76this.type = this.read.type;77isInitedToIsGetter = isPrefix(this.read.method.getName(), "is");78}79if (!isInitedToIsGetter && this.readList != null) {80for (MethodInfo info : this.readList) {81if ((this.read == null) || this.read.type.isAssignableFrom(info.type)) {82this.read = info;83this.type = info.type;84}85}86this.readList = null;87}88Class<?> writeType = this.type;89if (this.writeList != null) {90for (MethodInfo info : this.writeList) {91if (writeType == null) {92this.write = info;93writeType = info.type;94} else if (writeType.isAssignableFrom(info.type)) {95if ((this.write == null) || this.write.type.isAssignableFrom(info.type)) {96this.write = info;97writeType = info.type;98}99}100}101this.writeList = null;102}103if (this.type == null) {104this.type = writeType;105}106if (this.indexed != null) {107if ((this.type != null) && !this.type.isArray()) {108this.indexed = null; // property type is not an array109} else if (!this.indexed.initialize()) {110this.indexed = null; // cannot initialize indexed methods111} else if ((this.type != null) && (this.indexed.type != this.type.getComponentType())) {112this.indexed = null; // different property types113} else {114this.map = this.indexed.map;115this.indexed.map = null;116}117}118if ((this.type == null) && (this.indexed == null)) {119return false;120}121boolean done = initialize(this.read);122if (!done) {123initialize(this.write);124}125return true;126}127128private boolean initialize(MethodInfo info) {129if (info != null) {130BeanProperty annotation = info.method.getAnnotation(BeanProperty.class);131if (annotation != null) {132if (!annotation.bound()) {133put(Name.bound, Boolean.FALSE);134}135put(Name.expert, annotation.expert());136put(Name.required, annotation.required());137put(Name.hidden, annotation.hidden());138put(Name.preferred, annotation.preferred());139put(Name.visualUpdate, annotation.visualUpdate());140put(Name.description, annotation.description());141String[] values = annotation.enumerationValues();142try {143Object[] array = new Object[3 * values.length];144int index = 0;145for (String value : values) {146Class<?> type = info.method.getDeclaringClass();147String name = value;148int pos = value.lastIndexOf('.');149if (pos > 0) {150name = value.substring(0, pos);151if (name.indexOf('.') < 0) {152String pkg = type.getName();153name = pkg.substring(0, 1 + Math.max(154pkg.lastIndexOf('.'),155pkg.lastIndexOf('$'))) + name;156}157type = findClass(name);158name = value.substring(pos + 1);159}160Field field = type.getField(name);161if (Modifier.isStatic(field.getModifiers()) && info.type.isAssignableFrom(field.getType())) {162array[index++] = name;163array[index++] = field.get(null);164array[index++] = value;165}166}167if (index == array.length) {168put(Name.enumerationValues, array);169}170} catch (Exception ignored) {171ignored.printStackTrace();172}173return true;174}175}176return false;177}178179public Class<?> getPropertyType() {180return this.type;181}182183public Method getReadMethod() {184return (this.read == null) ? null : this.read.method;185}186187public Method getWriteMethod() {188return (this.write == null) ? null : this.write.method;189}190191public PropertyInfo getIndexed() {192return this.indexed;193}194195public boolean isConstrained() {196if (this.write != null) {197if (VETO_EXCEPTION == null) {198for (Class<?> type : this.write.method.getExceptionTypes()) {199if (type.getName().equals(VETO_EXCEPTION_NAME)) {200return true;201}202}203} else if (this.write.isThrow(VETO_EXCEPTION)) {204return true;205}206}207return (this.indexed != null) && this.indexed.isConstrained();208}209210public boolean is(Name name) {211Object value = get(name);212return (value instanceof Boolean)213? (Boolean) value214: Name.bound.equals(name);215}216217public Object get(Name name) {218return this.map == null ? null : this.map.get(name);219}220221private void put(Name name, boolean value) {222if (value) {223put(name, Boolean.TRUE);224}225}226227private void put(Name name, String value) {228if (0 < value.length()) {229put(name, (Object) value);230}231}232233private void put(Name name, Object value) {234if (this.map == null) {235this.map = new EnumMap<>(Name.class);236}237this.map.put(name, value);238}239240private static List<MethodInfo> add(List<MethodInfo> list, Method method, Type type) {241if (list == null) {242list = new ArrayList<>();243}244list.add(new MethodInfo(method, type));245return list;246}247248private static boolean isPrefix(String name, String prefix) {249return name.length() > prefix.length() && name.startsWith(prefix);250}251252private static PropertyInfo getInfo(Map<String,PropertyInfo> map, String key, boolean indexed) {253PropertyInfo info = map.get(key);254if (info == null) {255info = new PropertyInfo();256map.put(key, info);257}258if (!indexed) {259return info;260}261if (info.indexed == null) {262info.indexed = new PropertyInfo();263}264return info.indexed;265}266267public static Map<String,PropertyInfo> get(Class<?> type) {268List<Method> methods = ClassInfo.get(type).getMethods();269if (methods.isEmpty()) {270return Collections.emptyMap();271}272Map<String,PropertyInfo> map = new TreeMap<>();273for (Method method : methods) {274if (!Modifier.isStatic(method.getModifiers())) {275Class<?> returnType = method.getReturnType();276String name = method.getName();277switch (method.getParameterCount()) {278case 0:279if (returnType.equals(boolean.class) && isPrefix(name, "is")) {280PropertyInfo info = getInfo(map, name.substring(2), false);281info.read = new MethodInfo(method, boolean.class);282} else if (!returnType.equals(void.class) && isPrefix(name, "get")) {283PropertyInfo info = getInfo(map, name.substring(3), false);284info.readList = add(info.readList, method, method.getGenericReturnType());285}286break;287case 1:288if (returnType.equals(void.class) && isPrefix(name, "set")) {289PropertyInfo info = getInfo(map, name.substring(3), false);290info.writeList = add(info.writeList, method, method.getGenericParameterTypes()[0]);291} else if (!returnType.equals(void.class) && method.getParameterTypes()[0].equals(int.class) && isPrefix(name, "get")) {292PropertyInfo info = getInfo(map, name.substring(3), true);293info.readList = add(info.readList, method, method.getGenericReturnType());294}295break;296case 2:297if (returnType.equals(void.class) && method.getParameterTypes()[0].equals(int.class) && isPrefix(name, "set")) {298PropertyInfo info = getInfo(map, name.substring(3), true);299info.writeList = add(info.writeList, method, method.getGenericParameterTypes()[1]);300}301break;302}303}304}305Iterator<PropertyInfo> iterator = map.values().iterator();306while (iterator.hasNext()) {307if (!iterator.next().initialize()) {308iterator.remove();309}310}311return !map.isEmpty()312? Collections.unmodifiableMap(map)313: Collections.emptyMap();314}315}316317318