RootClassInfo.java
/*
* Copyright 2006-2026 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
*
* https://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.mybatis.generator.codegen;
import static org.mybatis.generator.internal.util.messages.Messages.getString;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.internal.ObjectFactory;
/**
* Holds information about a class (uses the JavaBeans Introspector to find properties).
*
* @author Jeff Butler
*/
public class RootClassInfo {
private static final Map<String, RootClassInfo> rootClassInfoMap;
static {
rootClassInfoMap = Collections.synchronizedMap(new HashMap<>());
}
public static RootClassInfo getInstance(String className, List<String> warnings) {
return rootClassInfoMap.computeIfAbsent(className, k -> new RootClassInfo(k, warnings));
}
/**
* Clears the internal map containing root class info. This method should be called at the beginning of
* a generation run to clear the cached root class info in case there has been a change.
* For example, when using the eclipse launcher, the cache would be kept until eclipse
* was restarted.
*
*/
public static void reset() {
rootClassInfoMap.clear();
}
private PropertyDescriptor[] propertyDescriptors;
private final String className;
private final List<String> warnings;
private boolean genericMode = false;
private RootClassInfo(String className, List<String> warnings) {
super();
this.className = className;
this.warnings = warnings;
FullyQualifiedJavaType fqjt = new FullyQualifiedJavaType(className);
String nameWithoutGenerics = fqjt.getFullyQualifiedNameWithoutTypeParameters();
if (!nameWithoutGenerics.equals(className)) {
genericMode = true;
}
try {
Class<?> clazz = ObjectFactory.externalClassForName(nameWithoutGenerics);
BeanInfo bi = Introspector.getBeanInfo(clazz);
propertyDescriptors = bi.getPropertyDescriptors();
} catch (Exception e) {
propertyDescriptors = new PropertyDescriptor[0];
warnings.add(getString("Warning.20", className)); //$NON-NLS-1$
}
}
public boolean containsProperty(IntrospectedColumn introspectedColumn) {
if (propertyDescriptors.length == 0) {
return false;
}
boolean found = false;
String propertyName = introspectedColumn.getJavaProperty();
String propertyType = introspectedColumn.getFullyQualifiedJavaType()
.getFullyQualifiedName();
// get method names from class and check against this column definition.
// better yet, have a map of method Names. check against it.
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
if (hasProperty(propertyName, propertyType, propertyDescriptor)) {
found = true;
break;
}
}
return found;
}
private boolean hasProperty(String propertyName, String propertyType, PropertyDescriptor propertyDescriptor) {
return hasCorrectName(propertyName, propertyDescriptor)
&& isProperType(propertyName, propertyType, propertyDescriptor)
&& hasGetter(propertyName, propertyDescriptor)
&& hasSetter(propertyName, propertyDescriptor);
}
private boolean hasCorrectName(String propertyName, PropertyDescriptor propertyDescriptor) {
return propertyDescriptor.getName().equals(propertyName);
}
private boolean isProperType(String propertyName, String propertyType, PropertyDescriptor propertyDescriptor) {
String introspectedPropertyType = propertyDescriptor.getPropertyType().getName();
if (genericMode && introspectedPropertyType.equals("java.lang.Object")) { //$NON-NLS-1$
// OK - but add a warning
warnings.add(getString("Warning.28", propertyName, className)); //$NON-NLS-1$
} else if (!introspectedPropertyType.equals(propertyType)) {
warnings.add(getString("Warning.21", propertyName, className, propertyType)); //$NON-NLS-1$
return false;
}
return true;
}
private boolean hasGetter(String propertyName, PropertyDescriptor propertyDescriptor) {
if (propertyDescriptor.getReadMethod() == null) {
warnings.add(getString("Warning.22", propertyName, className)); //$NON-NLS-1$
return false;
}
return true;
}
private boolean hasSetter(String propertyName, PropertyDescriptor propertyDescriptor) {
if (propertyDescriptor.getWriteMethod() == null) {
warnings.add(getString("Warning.23", propertyName, className)); //$NON-NLS-1$
return false;
}
return true;
}
}