1+ /*
2+ * Licensed to the Apache Software Foundation (ASF) under one or more
3+ * contributor license agreements. See the NOTICE file distributed with
4+ * this work for additional information regarding copyright ownership.
5+ * The ASF licenses this file to You under the Apache License, Version 2.0
6+ * (the "License"); you may not use this file except in compliance with
7+ * the License. You may obtain a copy of the License at
8+ *
9+ * http://www.apache.org/licenses/LICENSE-2.0
10+ *
11+ * Unless required by applicable law or agreed to in writing, software
12+ * distributed under the License is distributed on an "AS IS" BASIS,
13+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+ * See the License for the specific language governing permissions and
15+ * limitations under the License.
16+ */
17+
18+ package io .microsphere .beans ;
19+
20+ import io .microsphere .annotation .Immutable ;
21+ import io .microsphere .annotation .Nonnull ;
22+
23+ import java .beans .BeanInfo ;
24+ import java .beans .Introspector ;
25+ import java .beans .PropertyDescriptor ;
26+ import java .util .Collection ;
27+ import java .util .Map ;
28+ import java .util .Objects ;
29+
30+ import static io .microsphere .collection .MapUtils .newFixedHashMap ;
31+ import static io .microsphere .lang .function .ThrowableSupplier .execute ;
32+ import static io .microsphere .util .ClassUtils .getTypeName ;
33+ import static io .microsphere .util .StringUtils .uncapitalize ;
34+ import static java .util .Collections .unmodifiableMap ;
35+
36+ /**
37+ * The metadata class of Bean, which is used to represent a Java Bean.
38+ *
39+ * @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
40+ * @see BeanInfo
41+ * @see PropertyDescriptor
42+ * @since 1.0.0
43+ */
44+ public class BeanMetadata {
45+
46+ private final Class <?> beanClass ;
47+
48+ private final BeanInfo beanInfo ;
49+
50+ private final Map <String , PropertyDescriptor > propertyDescriptorsMap ;
51+
52+ protected BeanMetadata (@ Nonnull Class <?> beanClass ) {
53+ this (execute (() -> Introspector .getBeanInfo (beanClass , Object .class )));
54+ }
55+
56+ protected BeanMetadata (BeanInfo beanInfo ) {
57+ this .beanInfo = beanInfo ;
58+ this .propertyDescriptorsMap = buildPropertyDescriptorsMap (beanInfo );
59+ this .beanClass = beanInfo .getBeanDescriptor ().getBeanClass ();
60+ }
61+
62+ @ Nonnull
63+ @ Immutable
64+ static Map <String , PropertyDescriptor > buildPropertyDescriptorsMap (BeanInfo beanInfo ) {
65+ PropertyDescriptor [] propertyDescriptors = beanInfo .getPropertyDescriptors ();
66+ int length = propertyDescriptors .length ;
67+ Map <String , PropertyDescriptor > propertyDescriptorsMap = newFixedHashMap (length );
68+ for (int i = 0 ; i < length ; i ++) {
69+ PropertyDescriptor propertyDescriptor = propertyDescriptors [i ];
70+ String propertyName = uncapitalize (propertyDescriptor .getName ());
71+ propertyDescriptorsMap .put (propertyName , propertyDescriptor );
72+ }
73+ return unmodifiableMap (propertyDescriptorsMap );
74+ }
75+
76+ public BeanInfo getBeanInfo () {
77+ return this .beanInfo ;
78+ }
79+
80+ @ Nonnull
81+ @ Immutable
82+ public Collection <PropertyDescriptor > getPropertyDescriptors () {
83+ return this .propertyDescriptorsMap .values ();
84+ }
85+
86+ /**
87+ * Get the {@link PropertyDescriptor} by property name
88+ *
89+ * @param propertyName the property name, which is usually the uncapitalized name of the property
90+ * (e.g., "propertyName" for a property named "PropertyName")
91+ * @return the {@link PropertyDescriptor} if found, otherwise {@code null}
92+ */
93+ public PropertyDescriptor getPropertyDescriptor (String propertyName ) {
94+ return this .propertyDescriptorsMap .get (propertyName );
95+ }
96+
97+ @ Nonnull
98+ @ Immutable
99+ public Map <String , PropertyDescriptor > getPropertyDescriptorsMap () {
100+ return this .propertyDescriptorsMap ;
101+ }
102+
103+ public Class <?> getBeanClass () {
104+ return this .beanClass ;
105+ }
106+
107+ @ Override
108+ public final boolean equals (Object o ) {
109+ if (!(o instanceof BeanMetadata )) return false ;
110+
111+ BeanMetadata that = (BeanMetadata ) o ;
112+ return Objects .equals (getBeanClass (), that .getBeanClass ());
113+ }
114+
115+ @ Override
116+ public int hashCode () {
117+ return Objects .hashCode (getBeanClass ());
118+ }
119+
120+ @ Override
121+ public String toString () {
122+ return "BeanMetadata{" + "beanClass='" + getTypeName (this .beanClass ) + "'}" ;
123+ }
124+
125+
126+ /**
127+ * Create a {@link BeanMetadata} instance from the specified bean class.
128+ *
129+ * <h3>Example Usage</h3>
130+ * <pre>{@code
131+ * // Example 1: Create BeanMetadata for a simple Java Bean
132+ * BeanMetadata metadata = BeanMetadata.of(Person.class);
133+ *
134+ * // Example 2: Create BeanMetadata for a complex Bean
135+ * BeanMetadata metadata = BeanMetadata.of(Company.class);
136+ * }</pre>
137+ *
138+ * @param beanClass the bean class must not be {@code null}
139+ * @return a {@link BeanMetadata} instance
140+ * @throws RuntimeException if the {@link BeanInfo} cannot be obtained from the specified bean class
141+ * @see Introspector#getBeanInfo(Class, Class)
142+ */
143+ public static BeanMetadata of (@ Nonnull Class <?> beanClass ) throws RuntimeException {
144+ return new BeanMetadata (beanClass );
145+ }
146+
147+ }
0 commit comments