/*! ****************************************************************************** * * 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.variables; import org.pentaho.di.core.Const; import org.pentaho.di.core.util.Utils; import org.pentaho.di.core.exception.KettleValueException; import org.pentaho.di.core.row.RowMetaInterface; import org.pentaho.di.core.row.value.ValueMetaBase; import org.pentaho.di.core.util.StringUtil; import org.pentaho.di.version.BuildVersion; import java.util.Hashtable; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** * This class is an implementation of VariableSpace * * @author Sven Boden */ public class Variables implements VariableSpace { private Map<String, String> properties; private VariableSpace parent; private Map<String, String> injection; private boolean initialized; public Variables() { properties = new ConcurrentHashMap<>(); parent = null; injection = null; initialized = false; // The Kettle version properties.put( Const.INTERNAL_VARIABLE_KETTLE_VERSION, BuildVersion.getInstance().getVersion() ); // The Kettle build version String revision = BuildVersion.getInstance().getRevision(); if ( revision == null ) { revision = ""; } properties.put( Const.INTERNAL_VARIABLE_KETTLE_BUILD_VERSION, revision ); // The Kettle build date String buildDate = BuildVersion.getInstance().getBuildDate(); if ( buildDate == null ) { buildDate = ""; } properties.put( Const.INTERNAL_VARIABLE_KETTLE_BUILD_DATE, buildDate ); } @Override public void copyVariablesFrom( VariableSpace space ) { if ( space != null && this != space ) { // If space is not null and this variable is not already // the same object as the argument. String[] variableNames = space.listVariables(); for ( int idx = 0; idx < variableNames.length; idx++ ) { properties.put( variableNames[idx], space.getVariable( variableNames[idx] ) ); } } } @Override public VariableSpace getParentVariableSpace() { return parent; } @Override public void setParentVariableSpace( VariableSpace parent ) { this.parent = parent; } @Override public String getVariable( String variableName, String defaultValue ) { String var = properties.get( variableName ); if ( var == null ) { return defaultValue; } return var; } @Override public String getVariable( String variableName ) { return properties.get( variableName ); } @Override public boolean getBooleanValueOfVariable( String variableName, boolean defaultValue ) { if ( !Utils.isEmpty( variableName ) ) { String value = environmentSubstitute( variableName ); if ( !Utils.isEmpty( value ) ) { return ValueMetaBase.convertStringToBoolean( value ); } } return defaultValue; } @Override public void initializeVariablesFrom( VariableSpace parent ) { this.parent = parent; // Clone the system properties to avoid ConcurrentModificationException while iterating // and then add all of them to properties variable. Set<String> systemPropertiesNames = System.getProperties().stringPropertyNames(); for ( String key : systemPropertiesNames ) { getProperties().put( key, System.getProperties().getProperty( key ) ); } if ( parent != null ) { copyVariablesFrom( parent ); } if ( injection != null ) { properties.putAll( injection ); injection = null; } initialized = true; } @Override public String[] listVariables() { Set<String> keySet = properties.keySet(); return keySet.toArray( new String[0] ); } @Override public void setVariable( String variableName, String variableValue ) { if ( variableValue != null ) { properties.put( variableName, variableValue ); } else { properties.remove( variableName ); } } @Override public String environmentSubstitute( String aString ) { if ( aString == null || aString.length() == 0 ) { return aString; } return StringUtil.environmentSubstitute( aString, properties ); } /** * Substitutes field values in <code>aString</code>. Field values are of the form "?{<field name>}". The values are * retrieved from the specified row. Please note that the getString() method is used to convert to a String, for all * values in the row. * * @param aString * the string on which to apply the substitution. * @param rowMeta * The row metadata to use. * @param rowData * The row data to use * * @return the string with the substitution applied. * @throws KettleValueException * In case there is a String conversion error */ @Override public String fieldSubstitute( String aString, RowMetaInterface rowMeta, Object[] rowData ) throws KettleValueException { if ( aString == null || aString.length() == 0 ) { return aString; } return StringUtil.substituteField( aString, rowMeta, rowData ); } @Override public String[] environmentSubstitute( String[] string ) { String[] retval = new String[string.length]; for ( int i = 0; i < string.length; i++ ) { retval[i] = environmentSubstitute( string[i] ); } return retval; } @Override public void shareVariablesWith( VariableSpace space ) { // not implemented in here... done by pointing to the same VariableSpace // implementation } @Override public void injectVariables( Map<String, String> prop ) { if ( initialized ) { // variables are already initialized if ( prop != null ) { for ( String key : prop.keySet() ) { String value = prop.get( key ); if ( !Utils.isEmpty( key ) ) { properties.put( key, Const.NVL( value, "" ) ); } } injection = null; } } else { // We have our own personal copy, so changes afterwards // to the input properties don't affect us. injection = new Hashtable<String, String>(); for ( String key : prop.keySet() ) { String value = prop.get( key ); if ( !Utils.isEmpty( key ) ) { injection.put( key, Const.NVL( value, "" ) ); } } } } /** * Get a default variable space as a placeholder. Everytime you will get a new instance. * * @return a default variable space. */ public static synchronized VariableSpace getADefaultVariableSpace() { VariableSpace space = new Variables(); space.initializeVariablesFrom( null ); return space; } // Method is defined as package-protected in order to be accessible by unit tests Map<String, String> getProperties() { return properties; } }