/* * Copyright 2015 Red Hat, Inc. and/or its affiliates. * * 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 org.kie.workbench.common.services.datamodel.backend.server.builder.projects; import java.beans.Introspector; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.drools.workbench.models.datamodel.oracle.Annotation; import org.drools.workbench.models.datamodel.oracle.FieldAccessorsAndMutators; import org.drools.workbench.models.datamodel.oracle.ModelField; import org.kie.workbench.common.services.datamodel.backend.server.builder.util.AnnotationUtils; import org.kie.workbench.common.services.datamodel.backend.server.builder.util.BlackLists; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Find information for all "fields" on a class. A "field" is either a public property or a non-public property for which there is a "getter" and/or a "setter" */ public class ClassFieldInspector { private static final Logger log = LoggerFactory.getLogger( ClassFieldInspector.class ); private final Map<String, FieldInfo> fieldTypesFieldInfo = new HashMap<String, FieldInfo>(); public ClassFieldInspector( final Class<?> clazz ) { //Handle fields final List<Field> fields = new ArrayList<Field>( getAllFields( clazz ).values() ); final List<Field> declaredFields = Arrays.asList( clazz.getDeclaredFields() ); final Map<String, Field> inaccessibleFields = new HashMap<>(); for ( Field field : fields ) { if ( BlackLists.isClassMethodBlackListed( clazz, field.getName() ) ) { continue; } if ( BlackLists.isTypeBlackListed( field.getType() ) ) { continue; } if ( Modifier.isPublic( field.getModifiers() ) && !Modifier.isStatic( field.getModifiers() ) ) { ModelField.FIELD_ORIGIN origin = declaredFields.contains( field ) ? ModelField.FIELD_ORIGIN.DECLARED : ModelField.FIELD_ORIGIN.INHERITED; this.fieldTypesFieldInfo.put( field.getName(), new FieldInfo( FieldAccessorsAndMutators.BOTH, field.getGenericType(), field.getType(), origin, AnnotationUtils.getFieldAnnotations( field, origin.equals( ModelField.FIELD_ORIGIN.INHERITED ) ) ) ); } else { inaccessibleFields.put( field.getName(), field ); } } //Handle methods final List<Method> methods = new ArrayList<Method>( getAllMethods( clazz ).values() ); for ( Method method : methods ) { final int modifiers = method.getModifiers(); if ( Modifier.isPublic( modifiers ) && !Modifier.isStatic( method.getModifiers() ) ) { String methodName = null; FieldAccessorsAndMutators accessorsAndMutators = null; if ( isGetter( method ) ) { methodName = method.getName().substring( 3 ); methodName = Introspector.decapitalize( methodName ); accessorsAndMutators = FieldAccessorsAndMutators.ACCESSOR; } else if ( isBooleanGetter( method ) ) { methodName = method.getName().substring( 2 ); methodName = Introspector.decapitalize( methodName ); accessorsAndMutators = FieldAccessorsAndMutators.ACCESSOR; } else if ( isSetter( method ) ) { methodName = method.getName().substring( 3 ); methodName = Introspector.decapitalize( methodName ); accessorsAndMutators = FieldAccessorsAndMutators.MUTATOR; } if ( methodName != null ) { if ( BlackLists.isClassMethodBlackListed( clazz, methodName ) ) { continue; } if ( BlackLists.isTypeBlackListed( method.getReturnType() ) ) { continue; } //Correct accessor information, if a Field has already been discovered if ( this.fieldTypesFieldInfo.containsKey( methodName ) ) { final FieldInfo info = this.fieldTypesFieldInfo.get( methodName ); if ( accessorsAndMutators == FieldAccessorsAndMutators.ACCESSOR ) { if ( info.accessorAndMutator == FieldAccessorsAndMutators.MUTATOR ) { info.accessorAndMutator = FieldAccessorsAndMutators.BOTH; } if ( !inaccessibleFields.containsKey( methodName ) ) { info.genericType = method.getGenericReturnType(); info.returnType = method.getReturnType(); } } else if ( accessorsAndMutators == FieldAccessorsAndMutators.MUTATOR ) { if ( info.accessorAndMutator == FieldAccessorsAndMutators.ACCESSOR ) { info.accessorAndMutator = FieldAccessorsAndMutators.BOTH; } } } else { final ModelField.FIELD_ORIGIN origin = getOrigin( methodName, getNames( fields ), getNames( declaredFields ) ); Field inaccessibleField = inaccessibleFields.get( methodName ); Type genericType = method.getGenericReturnType(); Class<?> type = method.getReturnType(); if ( inaccessibleField != null ) { genericType = inaccessibleField.getGenericType(); type = inaccessibleField.getType(); } this.fieldTypesFieldInfo.put( methodName, new FieldInfo( accessorsAndMutators, genericType, type, origin, AnnotationUtils.getFieldAnnotations( inaccessibleField, origin.equals( ModelField.FIELD_ORIGIN.INHERITED ) ) ) ); } } } } } private boolean isGetter( final Method m ) { String name = m.getName(); int parameterCount = m.getParameterTypes().length; if ( parameterCount != 0 ) { return false; } return ( name.length() > 3 && name.startsWith( "get" ) ); } private boolean isBooleanGetter( final Method m ) { String name = m.getName(); int parameterCount = m.getParameterTypes().length; if ( parameterCount != 0 ) { return false; } return ( name.length() > 2 && name.startsWith( "is" ) && ( Boolean.class.isAssignableFrom( m.getReturnType() ) || Boolean.TYPE == m.getReturnType() ) ); } private boolean isSetter( final Method m ) { String name = m.getName(); int parameterCount = m.getParameterTypes().length; if ( parameterCount != 1 ) { return false; } return ( name.length() > 3 && name.startsWith( "set" ) ); } private List<String> getNames( final List<Field> fields ) { final List<String> names = new ArrayList<String>(); for ( Field field : fields ) { names.add( field.getName() ); } return names; } private ModelField.FIELD_ORIGIN getOrigin( final String methodName, final List<String> fields, final List<String> declaredFields ) { if ( declaredFields.contains( methodName ) ) { return ModelField.FIELD_ORIGIN.DECLARED; } if ( fields.contains( methodName ) ) { return ModelField.FIELD_ORIGIN.INHERITED; } return ModelField.FIELD_ORIGIN.DELEGATED; } public Set<String> getFieldNames() { return this.fieldTypesFieldInfo.keySet(); } public Map<String, FieldInfo> getFieldTypesFieldInfo() { return this.fieldTypesFieldInfo; } //class.getDeclaredField(String) doesn't walk the inheritance tree; this does private Map<String, Field> getAllFields( Class<?> type ) { Map<String, Field> fields = new HashMap<String, Field>(); for ( Class<?> c = type; c != null; c = c.getSuperclass() ) { for ( Field f : c.getDeclaredFields() ) { fields.put( f.getName(), f ); } } return fields; } //class.getDeclaredMethods() doesn't walk the inheritance tree; this does private Map<String, Method> getAllMethods( Class<?> type ) { Map<String, Method> methods = new HashMap<String, Method>(); for ( Class<?> c = type; c != null; c = c.getSuperclass() ) { for ( Method m : c.getDeclaredMethods() ) { methods.put( m.getName(), m ); } } return methods; } public static class FieldInfo { private FieldAccessorsAndMutators accessorAndMutator; private Type genericType; private Class<?> returnType; private ModelField.FIELD_ORIGIN origin; private Set<Annotation> annotations; private FieldInfo( final FieldAccessorsAndMutators accessorAndMutator, final Type genericType, final Class<?> returnType, final ModelField.FIELD_ORIGIN origin, final Set<Annotation> annotations ) { this.accessorAndMutator = accessorAndMutator; this.genericType = genericType; this.returnType = returnType; this.origin = origin; this.annotations = annotations; } public FieldAccessorsAndMutators getAccessorAndMutator() { return accessorAndMutator; } public Type getGenericType() { return genericType; } public Class<?> getReturnType() { return returnType; } public ModelField.FIELD_ORIGIN getOrigin() { return origin; } public Set<Annotation> getAnnotations() { return annotations; } } }