/*! ****************************************************************************** * * Pentaho Data Integration * * Copyright (C) 2002-2016 by Pentaho : http://www.pentaho.com * ******************************************************************************* * * 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.pentaho.di.core.injection.bean; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.lang.StringUtils; import org.pentaho.di.core.injection.Injection; import org.pentaho.di.core.injection.InjectionSupported; import org.pentaho.di.core.logging.KettleLogStore; import org.pentaho.di.core.logging.LogChannelInterface; import org.pentaho.di.i18n.BaseMessages; /** * Storage for bean annotations info for Metadata Injection and Load/Save. */ public class BeanInjectionInfo { private static LogChannelInterface LOG = KettleLogStore.getLogChannelInterfaceFactory().create( BeanInjectionInfo.class ); protected final Class<?> clazz; private final InjectionSupported clazzAnnotation; private Map<String, Property> properties = new HashMap<>(); private List<Group> groupsList = new ArrayList<>(); /** Used only for fast group search during initialize. */ private Map<String, Group> groupsMap = new HashMap<>(); private Set<String> hideProperties = new HashSet<>(); public static boolean isInjectionSupported( Class<?> clazz ) { InjectionSupported annotation = clazz.getAnnotation( InjectionSupported.class ); return annotation != null; } public BeanInjectionInfo( Class<?> clazz ) { LOG.logDebug( "Collect bean injection info for " + clazz ); try { this.clazz = clazz; clazzAnnotation = clazz.getAnnotation( InjectionSupported.class ); if ( clazzAnnotation == null ) { throw new RuntimeException( "Injection not supported in " + clazz ); } Group gr0 = new Group( "" ); groupsList.add( gr0 ); groupsMap.put( gr0.getName(), gr0 ); for ( String group : clazzAnnotation.groups() ) { Group gr = new Group( group ); groupsList.add( gr ); groupsMap.put( gr.getName(), gr ); } for ( String p : clazzAnnotation.hide() ) { hideProperties.add( p ); } BeanLevelInfo root = new BeanLevelInfo(); root.leafClass = clazz; root.init( this ); properties = Collections.unmodifiableMap( properties ); groupsList = Collections.unmodifiableList( groupsList ); groupsMap = null; } catch ( Throwable ex ) { LOG.logError( "Error bean injection info collection for " + clazz + ": " + ex.getMessage(), ex ); throw ex; } } public String getLocalizationPrefix() { return clazzAnnotation.localizationPrefix(); } public Map<String, Property> getProperties() { return properties; } public List<Group> getGroups() { return groupsList; } protected void addInjectionProperty( Injection metaInj, BeanLevelInfo leaf ) { if ( StringUtils.isBlank( metaInj.name() ) ) { throw new RuntimeException( "Property name shouldn't be blank in the " + clazz ); } String propertyName = calcPropertyName( metaInj, leaf ); if ( properties.containsKey( propertyName ) ) { throw new RuntimeException( "Property '" + propertyName + "' already defined for " + clazz ); } // probably hided if ( hideProperties.contains( propertyName ) ) { return; } Property prop = new Property( propertyName, metaInj.group(), leaf.createCallStack() ); properties.put( prop.name, prop ); Group gr = groupsMap.get( metaInj.group() ); if ( gr == null ) { throw new RuntimeException( "Group '" + metaInj.group() + "' for property '" + metaInj.name() + "' is not defined " + clazz ); } gr.groupProperties.add( prop ); } public String getDescription( String name ) { String description = BaseMessages.getString( clazz, clazzAnnotation.localizationPrefix() + name ); if ( description != null && description.startsWith( "!" ) && description.endsWith( "!" ) ) { Class baseClass = clazz.getSuperclass(); while ( baseClass != null ) { InjectionSupported baseAnnotation = (InjectionSupported) baseClass.getAnnotation( InjectionSupported.class ); if ( baseAnnotation != null ) { description = BaseMessages.getString( baseClass, baseAnnotation.localizationPrefix() + name ); if ( description != null && !description.startsWith( "!" ) && !description.endsWith( "!" ) ) { return description; } } baseClass = baseClass.getSuperclass(); } } return description; } private String calcPropertyName( Injection metaInj, BeanLevelInfo leaf ) { String name = metaInj.name(); while ( leaf != null ) { if ( StringUtils.isNotBlank( leaf.prefix ) ) { name = leaf.prefix + '.' + name; } leaf = leaf.parent; } if ( !name.equals( metaInj.name() ) && !metaInj.group().isEmpty() ) { // group exist with prefix throw new RuntimeException( "Group shouldn't be declared with prefix in " + clazz ); } return name; } public class Property { private final String name; private final String groupName; protected final List<BeanLevelInfo> path; protected final int pathArraysCount; public Property( String name, String groupName, List<BeanLevelInfo> path ) { this.name = name; this.groupName = groupName; this.path = path; int ac = 0; for ( BeanLevelInfo level : path ) { if ( level.dim != BeanLevelInfo.DIMENSION.NONE ) { ac++; } } pathArraysCount = ac; } public String getName() { return name; } public String getGroupName() { return groupName; } public String getDescription() { return BeanInjectionInfo.this.getDescription( name ); } public Class<?> getPropertyClass() { return path.get( path.size() - 1 ).leafClass; } } public class Group { private final String name; protected final List<Property> groupProperties = new ArrayList<>(); public Group( String name ) { this.name = name; } public String getName() { return name; } public List<Property> getGroupProperties() { return Collections.unmodifiableList( groupProperties ); } public String getDescription() { return BeanInjectionInfo.this.getDescription( name ); } } }