// CHECKSTYLE:FileLength:OFF /*! ****************************************************************************** * * Pentaho Data Integration * * Copyright (C) 2002-2017 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; import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.text.StrBuilder; import org.pentaho.di.core.exception.KettleException; import org.pentaho.di.core.row.ValueMetaInterface; import org.pentaho.di.core.util.EnvUtil; import org.pentaho.di.core.util.Utils; import org.pentaho.di.i18n.BaseMessages; import org.pentaho.di.laf.BasePropertyHandler; import org.pentaho.di.version.BuildVersion; import java.awt.Font; import java.awt.GraphicsEnvironment; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.io.StringWriter; import java.math.BigDecimal; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.net.URL; import java.net.URLClassLoader; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.text.NumberFormat; import java.text.ParseException; import java.text.ParsePosition; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.Enumeration; import java.util.List; import java.util.Locale; import java.util.TimeZone; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * This class is used to define a number of default values for various settings throughout Kettle. It also contains a * number of static final methods to make your life easier. * * @author Matt * @since 07-05-2003 * */ public class Const { private static Class<?> PKG = Const.class; // for i18n purposes, needed by Translator2!! /** * Version number * * @deprecated Use {@link BuildVersion#getVersion()} instead */ @Deprecated public static final String VERSION = BuildVersion.getInstance().getVersion(); /** * Copyright year */ public static final String COPYRIGHT_YEAR = "2015"; /** * Release Type */ public enum ReleaseType { RELEASE_CANDIDATE { public String getMessage() { return BaseMessages.getString( PKG, "Const.PreviewRelease.HelpAboutText" ); } }, MILESTONE { public String getMessage() { return BaseMessages.getString( PKG, "Const.Candidate.HelpAboutText" ); } }, PREVIEW { public String getMessage() { return BaseMessages.getString( PKG, "Const.Milestone.HelpAboutText" ); } }, GA { public String getMessage() { return BaseMessages.getString( PKG, "Const.GA.HelpAboutText" ); } }; public abstract String getMessage(); } /** * Sleep time waiting when buffer is empty (the default) */ public static final int TIMEOUT_GET_MILLIS = 50; /** * Sleep time waiting when buffer is full (the default) */ public static final int TIMEOUT_PUT_MILLIS = 50; /** * print update every ... lines */ public static final int ROWS_UPDATE = 50000; /** * Size of rowset: bigger = faster for large amounts of data */ public static final int ROWS_IN_ROWSET = 10000; /** * Fetch size in rows when querying a database */ public static final int FETCH_SIZE = 10000; /** * Sort size: how many rows do we sort in memory at once? */ public static final int SORT_SIZE = 5000; /** * job/trans heartbeat scheduled executor periodic interval ( in seconds ) */ public static final int HEARTBEAT_PERIODIC_INTERVAL_IN_SECS = 10; /** * What's the file systems file separator on this operating system? */ public static final String FILE_SEPARATOR = System.getProperty( "file.separator" ); /** * What's the path separator on this operating system? */ public static final String PATH_SEPARATOR = System.getProperty( "path.separator" ); /** * CR: operating systems specific Carriage Return */ public static final String CR = System.getProperty( "line.separator" ); /** * DOSCR: MS-DOS specific Carriage Return */ public static final String DOSCR = "\n\r"; /** * An empty ("") String. */ public static final String EMPTY_STRING = ""; /** * The Java runtime version */ public static final String JAVA_VERSION = System.getProperty( "java.vm.version" ); /** * Path to the users home directory (keep this entry above references to getKettleDirectory()) * * @deprecated Use {@link Const#getUserHomeDirectory()} instead. */ @Deprecated public static final String USER_HOME_DIRECTORY = NVL( System.getProperty( "KETTLE_HOME" ), System .getProperty( "user.home" ) ); /** * Path to the simple-jndi directory */ public static String JNDI_DIRECTORY = NVL( System.getProperty( "KETTLE_JNDI_ROOT" ), System .getProperty( "org.osjava.sj.root" ) ); /* * The images directory * * public static final String IMAGE_DIRECTORY = "/ui/images/"; */ public static final String PLUGIN_BASE_FOLDERS_PROP = "KETTLE_PLUGIN_BASE_FOLDERS"; /** * the default comma separated list of base plugin folders. */ public static final String DEFAULT_PLUGIN_BASE_FOLDERS = "plugins," + ( Utils.isEmpty( getDIHomeDirectory() ) ? "" : getDIHomeDirectory() + FILE_SEPARATOR + "plugins," ) + getKettleDirectory() + FILE_SEPARATOR + "plugins"; /** * Default minimum date range... */ public static final Date MIN_DATE = new Date( -2208992400000L ); // 1900/01/01 00:00:00.000 /** * Default maximum date range... */ public static final Date MAX_DATE = new Date( 7258114799468L ); // 2199/12/31 23:59:59.999 /** * The default minimum year in a dimension date range */ public static final int MIN_YEAR = 1900; /** * The default maximum year in a dimension date range */ public static final int MAX_YEAR = 2199; /** * Specifies the number of pixels to the right we have to go in dialog boxes. */ public static final int RIGHT = 400; /** * Specifies the length (width) of fields in a number of pixels in dialog boxes. */ public static final int LENGTH = 350; /** * The margin between the different dialog components & widgets */ public static final int MARGIN = 4; /** * The default percentage of the width of screen where we consider the middle of a dialog. */ public static final int MIDDLE_PCT = 35; /** * The default width of an arrow in the Graphical Views */ public static final int ARROW_WIDTH = 1; /** * The horizontal and vertical margin of a dialog box. */ public static final int FORM_MARGIN = 5; /** * The default shadow size on the graphical view. */ public static final int SHADOW_SIZE = 0; /** * The size of relationship symbols */ public static final int SYMBOLSIZE = 10; /** * Max nr. of files to remember */ public static final int MAX_FILE_HIST = 9; // Having more than 9 files in the file history is not compatible with pre // 5.0 versions /** * The default locale for the kettle environment (system defined) */ public static final Locale DEFAULT_LOCALE = Locale.getDefault(); // new Locale("nl", "BE"); /** * The default decimal separator . or , */ public static final char DEFAULT_DECIMAL_SEPARATOR = ( new DecimalFormatSymbols( DEFAULT_LOCALE ) ) .getDecimalSeparator(); /** * The default grouping separator , or . */ public static final char DEFAULT_GROUPING_SEPARATOR = ( new DecimalFormatSymbols( DEFAULT_LOCALE ) ) .getGroupingSeparator(); /** * The default currency symbol */ public static final String DEFAULT_CURRENCY_SYMBOL = ( new DecimalFormatSymbols( DEFAULT_LOCALE ) ) .getCurrencySymbol(); /** * The default number format */ public static final String DEFAULT_NUMBER_FORMAT = ( (DecimalFormat) ( NumberFormat.getInstance() ) ) .toPattern(); /** * Default string representing Null String values (empty) */ public static final String NULL_STRING = ""; /** * Default string representing Null Number values (empty) */ public static final String NULL_NUMBER = ""; /** * Default string representing Null Date values (empty) */ public static final String NULL_DATE = ""; /** * Default string representing Null BigNumber values (empty) */ public static final String NULL_BIGNUMBER = ""; /** * Default string representing Null Boolean values (empty) */ public static final String NULL_BOOLEAN = ""; /** * Default string representing Null Integer values (empty) */ public static final String NULL_INTEGER = ""; /** * Default string representing Null Binary values (empty) */ public static final String NULL_BINARY = ""; /** * Default string representing Null Undefined values (empty) */ public static final String NULL_NONE = ""; /** * Rounding mode, not implemented in {@code BigDecimal}. Method java.lang.Math.round(double) processes this way. <br/> * Rounding mode to round towards {@literal "nearest neighbor"} unless both neighbors are equidistant, in which case * round ceiling. <br/> * Behaves as for {@code ROUND_CEILING} if the discarded fraction is ≥ 0.5; otherwise, behaves as for * {@code ROUND_FLOOR}. Note that this is the most common arithmetical rounding mode. */ public static final int ROUND_HALF_CEILING = -1; /** * The base name of the Chef logfile */ public static final String CHEF_LOG_FILE = "chef"; /** * The base name of the Spoon logfile */ public static final String SPOON_LOG_FILE = "spoon"; /** * The base name of the Menu logfile */ public static final String MENU_LOG_FILE = "menu"; /** * An array of date conversion formats */ private static String[] dateFormats; /** * An array of number conversion formats */ private static String[] numberFormats; /** * Generalized date/time format: Wherever dates are used, date and time values are organized from the most to the * least significant. see also method StringUtil.getFormattedDateTime() */ public static final String GENERALIZED_DATE_TIME_FORMAT = "yyyyddMM_hhmmss"; public static final String GENERALIZED_DATE_TIME_FORMAT_MILLIS = "yyyyddMM_hhmmssSSS"; /** * Default we store our information in Unicode UTF-8 character set. */ public static final String XML_ENCODING = "UTF-8"; /** The possible extensions a transformation XML file can have. */ public static final String[] STRING_TRANS_AND_JOB_FILTER_EXT = new String[] { "*.ktr;*.kjb;*.xml", "*.ktr;*.xml", "*.kjb;*.xml", "*.xml", "*.*" }; /** The descriptions of the possible extensions a transformation XML file can have. */ private static String[] STRING_TRANS_AND_JOB_FILTER_NAMES; /** The extension of a Kettle transformation XML file */ public static final String STRING_TRANS_DEFAULT_EXT = "ktr"; /** The possible extensions a transformation XML file can have. */ public static final String[] STRING_TRANS_FILTER_EXT = new String[] { "*.ktr;*.xml", "*.xml", "*.*" }; /** The descriptions of the possible extensions a transformation XML file can have. */ private static String[] STRING_TRANS_FILTER_NAMES; /** The extension of a Kettle job XML file */ public static final String STRING_JOB_DEFAULT_EXT = "kjb"; /** The possible extensions a job XML file can have. */ public static final String[] STRING_JOB_FILTER_EXT = new String[] { "*.kjb;*.xml", "*.xml", "*.*" }; /** The descriptions of the possible extensions a job XML file can have. */ private static String[] STRING_JOB_FILTER_NAMES; /** Name of the kettle parameters file */ public static final String KETTLE_PROPERTIES = "kettle.properties"; /** Name of the kettle shared data file */ public static final String SHARED_DATA_FILE = "shared.xml"; /** The prefix that all internal kettle variables should have */ public static final String INTERNAL_VARIABLE_PREFIX = "Internal"; /** The version number as an internal variable */ public static final String INTERNAL_VARIABLE_KETTLE_VERSION = INTERNAL_VARIABLE_PREFIX + ".Kettle.Version"; /** The build version as an internal variable */ public static final String INTERNAL_VARIABLE_KETTLE_BUILD_VERSION = INTERNAL_VARIABLE_PREFIX + ".Kettle.Build.Version"; /** The build date as an internal variable */ public static final String INTERNAL_VARIABLE_KETTLE_BUILD_DATE = INTERNAL_VARIABLE_PREFIX + ".Kettle.Build.Date"; /** The job filename directory */ public static final String INTERNAL_VARIABLE_JOB_FILENAME_DIRECTORY = INTERNAL_VARIABLE_PREFIX + ".Job.Filename.Directory"; /** The job filename name */ public static final String INTERNAL_VARIABLE_JOB_FILENAME_NAME = INTERNAL_VARIABLE_PREFIX + ".Job.Filename.Name"; /** The job name */ public static final String INTERNAL_VARIABLE_JOB_NAME = INTERNAL_VARIABLE_PREFIX + ".Job.Name"; /** The job directory */ public static final String INTERNAL_VARIABLE_JOB_REPOSITORY_DIRECTORY = INTERNAL_VARIABLE_PREFIX + ".Job.Repository.Directory"; /** The job run ID */ public static final String INTERNAL_VARIABLE_JOB_RUN_ID = INTERNAL_VARIABLE_PREFIX + ".Job.Run.ID"; /** The job run attempt nr */ public static final String INTERNAL_VARIABLE_JOB_RUN_ATTEMPTNR = INTERNAL_VARIABLE_PREFIX + ".Job.Run.AttemptNr"; /** job/trans heartbeat scheduled executor periodic interval ( in seconds ) */ public static final String VARIABLE_HEARTBEAT_PERIODIC_INTERVAL_SECS = "heartbeat.periodic.interval.seconds"; /** comma-separated list of extension point plugins for which snmp traps should be sent */ public static final String VARIABLE_MONITORING_SNMP_TRAPS_ENABLED = "monitoring.snmp.traps.enabled"; /** The current transformation directory */ public static final String INTERNAL_VARIABLE_ENTRY_CURRENT_DIRECTORY = INTERNAL_VARIABLE_PREFIX + ".Entry.Current.Directory"; /** * All the internal transformation variables */ public static final String[] INTERNAL_TRANS_VARIABLES = new String[] { Const.INTERNAL_VARIABLE_ENTRY_CURRENT_DIRECTORY, Const.INTERNAL_VARIABLE_TRANSFORMATION_FILENAME_DIRECTORY, Const.INTERNAL_VARIABLE_TRANSFORMATION_FILENAME_NAME, Const.INTERNAL_VARIABLE_TRANSFORMATION_NAME, Const.INTERNAL_VARIABLE_TRANSFORMATION_REPOSITORY_DIRECTORY, }; /** * All the internal job variables */ public static final String[] INTERNAL_JOB_VARIABLES = new String[] { Const.INTERNAL_VARIABLE_ENTRY_CURRENT_DIRECTORY, Const.INTERNAL_VARIABLE_JOB_FILENAME_DIRECTORY, Const.INTERNAL_VARIABLE_JOB_FILENAME_NAME, Const.INTERNAL_VARIABLE_JOB_NAME, Const.INTERNAL_VARIABLE_JOB_REPOSITORY_DIRECTORY, Const.INTERNAL_VARIABLE_JOB_RUN_ID, Const.INTERNAL_VARIABLE_JOB_RUN_ATTEMPTNR, }; /* * Deprecated variables array. * Variables in this array will display with the prefix (deprecated) and will be moved * at the bottom of the variables dropdown when pressing ctrl+space * */ public static final String[] DEPRECATED_VARIABLES = new String[] { Const.INTERNAL_VARIABLE_TRANSFORMATION_FILENAME_DIRECTORY, Const.INTERNAL_VARIABLE_TRANSFORMATION_FILENAME_NAME, Const.INTERNAL_VARIABLE_TRANSFORMATION_NAME, Const.INTERNAL_VARIABLE_TRANSFORMATION_REPOSITORY_DIRECTORY }; /** The transformation filename directory */ public static final String INTERNAL_VARIABLE_TRANSFORMATION_FILENAME_DIRECTORY = INTERNAL_VARIABLE_PREFIX + ".Transformation.Filename.Directory"; /** The transformation filename name */ public static final String INTERNAL_VARIABLE_TRANSFORMATION_FILENAME_NAME = INTERNAL_VARIABLE_PREFIX + ".Transformation.Filename.Name"; /** The transformation name */ public static final String INTERNAL_VARIABLE_TRANSFORMATION_NAME = INTERNAL_VARIABLE_PREFIX + ".Transformation.Name"; /** The transformation directory */ public static final String INTERNAL_VARIABLE_TRANSFORMATION_REPOSITORY_DIRECTORY = INTERNAL_VARIABLE_PREFIX + ".Transformation.Repository.Directory"; /** The step partition ID */ public static final String INTERNAL_VARIABLE_STEP_PARTITION_ID = INTERNAL_VARIABLE_PREFIX + ".Step.Partition.ID"; /** The step partition number */ public static final String INTERNAL_VARIABLE_STEP_PARTITION_NR = INTERNAL_VARIABLE_PREFIX + ".Step.Partition.Number"; /** The slave transformation number */ public static final String INTERNAL_VARIABLE_SLAVE_SERVER_NUMBER = INTERNAL_VARIABLE_PREFIX + ".Slave.Transformation.Number"; /** The slave transformation name */ public static final String INTERNAL_VARIABLE_SLAVE_SERVER_NAME = INTERNAL_VARIABLE_PREFIX + ".Slave.Server.Name"; /** The size of the cluster : number of slaves */ public static final String INTERNAL_VARIABLE_CLUSTER_SIZE = INTERNAL_VARIABLE_PREFIX + ".Cluster.Size"; /** The slave transformation number */ public static final String INTERNAL_VARIABLE_STEP_UNIQUE_NUMBER = INTERNAL_VARIABLE_PREFIX + ".Step.Unique.Number"; /** Is this transformation running clustered, on the master? */ public static final String INTERNAL_VARIABLE_CLUSTER_MASTER = INTERNAL_VARIABLE_PREFIX + ".Cluster.Master"; /** * The internal clustered run ID, unique across a clustered execution, important while doing parallel clustered runs */ public static final String INTERNAL_VARIABLE_CLUSTER_RUN_ID = INTERNAL_VARIABLE_PREFIX + ".Cluster.Run.ID"; /** The size of the cluster : number of slaves */ public static final String INTERNAL_VARIABLE_STEP_UNIQUE_COUNT = INTERNAL_VARIABLE_PREFIX + ".Step.Unique.Count"; /** The step name */ public static final String INTERNAL_VARIABLE_STEP_NAME = INTERNAL_VARIABLE_PREFIX + ".Step.Name"; /** The step copy nr */ public static final String INTERNAL_VARIABLE_STEP_COPYNR = INTERNAL_VARIABLE_PREFIX + ".Step.CopyNr"; /** The default maximum for the nr of lines in the GUI logs */ public static final int MAX_NR_LOG_LINES = 5000; /** The default maximum for the nr of lines in the history views */ public static final int MAX_NR_HISTORY_LINES = 50; /** The default fetch size for lines of history. */ public static final int HISTORY_LINES_FETCH_SIZE = 10; /** The default log line timeout in minutes : 12 hours */ public static final int MAX_LOG_LINE_TIMEOUT_MINUTES = 12 * 60; /** UI-agnostic flag for warnings */ public static final int WARNING = 1; /** UI-agnostic flag for warnings */ public static final int ERROR = 2; /** UI-agnostic flag for warnings */ public static final int INFO = 3; /** * The margin between the text of a note and its border. */ public static final int NOTE_MARGIN = 5; /** * The default undo level for Kettle */ public static final int MAX_UNDO = 100; /** * The file that documents these variables. */ public static final String KETTLE_VARIABLES_FILE = "kettle-variables.xml"; /** * If you set this environment variable you can limit the log size of all transformations and jobs that don't have the * "log size limit" property set in their respective properties. */ public static final String KETTLE_LOG_SIZE_LIMIT = "KETTLE_LOG_SIZE_LIMIT"; /** * The name of the variable that defines the log database connection by default for all transformations */ public static final String KETTLE_TRANS_LOG_DB = "KETTLE_TRANS_LOG_DB"; /** * The name of the variable that defines the logging schema for all transformations */ public static final String KETTLE_TRANS_LOG_SCHEMA = "KETTLE_TRANS_LOG_SCHEMA"; /** * The name of the variable that defines the logging table for all transformations */ public static final String KETTLE_TRANS_LOG_TABLE = "KETTLE_TRANS_LOG_TABLE"; /** * The name of the variable that defines the log database connection by default for all jobs */ public static final String KETTLE_JOB_LOG_DB = "KETTLE_JOB_LOG_DB"; /** * The name of the variable that defines the logging schema for all jobs */ public static final String KETTLE_JOB_LOG_SCHEMA = "KETTLE_JOB_LOG_SCHEMA"; /** * The name of the variable that defines the timer used for detecting slave nodes. */ public static final String KETTLE_SLAVE_DETECTION_TIMER = "KETTLE_SLAVE_DETECTION_TIMER"; /** * The name of the variable that defines the logging table for all jobs */ public static final String KETTLE_JOB_LOG_TABLE = "KETTLE_JOB_LOG_TABLE"; /** * The name of the variable that defines the transformation performance log schema by default for all transformations */ public static final String KETTLE_TRANS_PERFORMANCE_LOG_DB = "KETTLE_TRANS_PERFORMANCE_LOG_DB"; /** * The name of the variable that defines the transformation performance log database connection by default for all * transformations */ public static final String KETTLE_TRANS_PERFORMANCE_LOG_SCHEMA = "KETTLE_TRANS_PERFORMANCE_LOG_SCHEMA"; /** * The name of the variable that defines the transformation performance log table by default for all transformations */ public static final String KETTLE_TRANS_PERFORMANCE_LOG_TABLE = "KETTLE_TRANS_PERFORMANCE_LOG_TABLE"; /** * The name of the variable that defines the job entry log database by default for all jobs */ public static final String KETTLE_JOBENTRY_LOG_DB = "KETTLE_JOBENTRY_LOG_DB"; /** * The name of the variable that defines the job entry log schema by default for all jobs */ public static final String KETTLE_JOBENTRY_LOG_SCHEMA = "KETTLE_JOBENTRY_LOG_SCHEMA"; /** * The name of the variable that defines the job entry log table by default for all jobs */ public static final String KETTLE_JOBENTRY_LOG_TABLE = "KETTLE_JOBENTRY_LOG_TABLE"; /** * The name of the variable that defines the steps log database by default for all transformations */ public static final String KETTLE_STEP_LOG_DB = "KETTLE_STEP_LOG_DB"; /** * The name of the variable that defines the steps log schema by default for all transformations */ public static final String KETTLE_STEP_LOG_SCHEMA = "KETTLE_STEP_LOG_SCHEMA"; /** * The name of the variable that defines the steps log table by default for all transformations */ public static final String KETTLE_STEP_LOG_TABLE = "KETTLE_STEP_LOG_TABLE"; /** * The name of the variable that defines the log channel log database by default for all transformations and jobs */ public static final String KETTLE_CHANNEL_LOG_DB = "KETTLE_CHANNEL_LOG_DB"; /** * The name of the variable that defines the log channel log schema by default for all transformations and jobs */ public static final String KETTLE_CHANNEL_LOG_SCHEMA = "KETTLE_CHANNEL_LOG_SCHEMA"; /** * The name of the variable that defines the log channel log table by default for all transformations and jobs */ public static final String KETTLE_CHANNEL_LOG_TABLE = "KETTLE_CHANNEL_LOG_TABLE"; /** * The name of the variable that defines the metrics log database by default for all transformations and jobs */ public static final String KETTLE_METRICS_LOG_DB = "KETTLE_METRICS_LOG_DB"; /** * The name of the variable that defines the metrics log schema by default for all transformations and jobs */ public static final String KETTLE_METRICS_LOG_SCHEMA = "KETTLE_METRICS_LOG_SCHEMA"; /** * The name of the variable that defines the metrics log table by default for all transformations and jobs */ public static final String KETTLE_METRICS_LOG_TABLE = "KETTLE_METRICS_LOG_TABLE"; /** * The name of the variable that defines the checkpoint log database by default for all jobs */ public static final String KETTLE_CHECKPOINT_LOG_DB = "KETTLE_CHECKPOINT_LOG_DB"; /** * The name of the variable that defines the checkpoint log schema by default for all jobs */ public static final String KETTLE_CHECKPOINT_LOG_SCHEMA = "KETTLE_CHECKPOINT_LOG_SCHEMA"; /** * The name of the variable that defines the checkpoint log table by default for all jobs */ public static final String KETTLE_CHECKPOINT_LOG_TABLE = "KETTLE_CHECKPOINT_LOG_TABLE"; /** * Name of the environment variable to set the location of the shared object file (xml) for transformations and jobs */ public static final String KETTLE_SHARED_OBJECTS = "KETTLE_SHARED_OBJECTS"; /** * System wide flag to drive the evaluation of null in ValueMeta. If this setting is set to "Y", an empty string and * null are different. Otherwise they are not. */ public static final String KETTLE_EMPTY_STRING_DIFFERS_FROM_NULL = "KETTLE_EMPTY_STRING_DIFFERS_FROM_NULL"; /** * System wide flag to allow non-strict string to number conversion for backward compatibility. If this setting is set * to "Y", an string starting with digits will be converted successfully into a number. (example: 192.168.1.1 will be * converted into 192 or 192.168 depending on the decimal symbol). The default (N) will be to throw an error if * non-numeric symbols are found in the string. */ public static final String KETTLE_LENIENT_STRING_TO_NUMBER_CONVERSION = "KETTLE_LENIENT_STRING_TO_NUMBER_CONVERSION"; /** * System wide flag to ignore timezone while writing date/timestamp value to the database. See PDI-10749 for details. */ public static final String KETTLE_COMPATIBILITY_DB_IGNORE_TIMEZONE = "KETTLE_COMPATIBILITY_DB_IGNORE_TIMEZONE"; /** * System wide flag to use the root path prefix for a directory reference. See PDI-6779 for details. */ public static final String KETTLE_COMPATIBILITY_IMPORT_PATH_ADDITION_ON_VARIABLES = "KETTLE_COMPATIBILITY_IMPORT_PATH_ADDITION_ON_VARIABLES"; /** * System wide flag to ignore logging table. See BACKLOG-15706 for details. */ public static final String KETTLE_COMPATIBILITY_IGNORE_TABLE_LOGGING = "KETTLE_COMPATIBILITY_IGNORE_TABLE_LOGGING"; /** * System wide flag to set or not append and header options dependency on Text file output step. See PDI-5252 for * details. */ public static final String KETTLE_COMPATIBILITY_TEXT_FILE_OUTPUT_APPEND_NO_HEADER = "KETTLE_COMPATIBILITY_TEXT_FILE_OUTPUT_APPEND_NO_HEADER"; /** * System wide flag to control behavior of the merge rows (diff) step in case of "identical" comparison. (PDI-736) * 'Y' preserves the old behavior and takes the fields from the reference stream * 'N' enables the documented behavior and takes the fields from the comparison stream (correct behavior) */ public static final String KETTLE_COMPATIBILITY_MERGE_ROWS_USE_REFERENCE_STREAM_WHEN_IDENTICAL = "KETTLE_COMPATIBILITY_MERGE_ROWS_USE_REFERENCE_STREAM_WHEN_IDENTICAL"; /** * System wide flag to control behavior of the Memory Group By step in case of SUM and AVERAGE aggregation. (PDI-5537) * 'Y' preserves the old behavior and always returns a Number type for SUM and Average aggregations * 'N' enables the documented behavior of returning the same type as the input fields use (correct behavior). */ public static final String KETTLE_COMPATIBILITY_MEMORY_GROUP_BY_SUM_AVERAGE_RETURN_NUMBER_TYPE = "KETTLE_COMPATIBILITY_MEMORY_GROUP_BY_SUM_AVERAGE_RETURN_NUMBER_TYPE"; /** * You can use this variable to speed up hostname lookup. * Hostname lookup is performed by Kettle so that it is capable of logging the server on which a job or transformation is executed. */ public static final String KETTLE_SYSTEM_HOSTNAME = "KETTLE_SYSTEM_HOSTNAME"; /** * System wide flag to set the maximum number of log lines that are kept internally by Kettle. Set to 0 to keep all * rows (default) */ public static final String KETTLE_MAX_LOG_SIZE_IN_LINES = "KETTLE_MAX_LOG_SIZE_IN_LINES"; /** * System wide flag to set the maximum age (in minutes) of a log line while being kept internally by Kettle. Set to 0 * to keep all rows indefinitely (default) */ public static final String KETTLE_MAX_LOG_TIMEOUT_IN_MINUTES = "KETTLE_MAX_LOG_TIMEOUT_IN_MINUTES"; /** * System wide flag to determine whether standard error will be redirected to Kettle logging facilities. Will redirect * if the value is equal ignoring case to the string "Y" */ public static final String KETTLE_REDIRECT_STDERR = "KETTLE_REDIRECT_STDERR"; /** * System wide flag to determine whether standard out will be redirected to Kettle logging facilities. Will redirect * if the value is equal ignoring case to the string "Y" */ public static final String KETTLE_REDIRECT_STDOUT = "KETTLE_REDIRECT_STDOUT"; /** * This environment variable will set a time-out after which waiting, completed or stopped transformations and jobs * will be automatically cleaned up. The default value is 1440 (one day). */ public static final String KETTLE_CARTE_OBJECT_TIMEOUT_MINUTES = "KETTLE_CARTE_OBJECT_TIMEOUT_MINUTES"; /** * System wide parameter: the maximum number of step performance snapshots to keep in memory. Set to 0 to keep all * snapshots indefinitely (default) */ public static final String KETTLE_STEP_PERFORMANCE_SNAPSHOT_LIMIT = "KETTLE_STEP_PERFORMANCE_SNAPSHOT_LIMIT"; /** * A variable to configure the maximum number of job trackers kept in memory. */ public static final String KETTLE_MAX_JOB_TRACKER_SIZE = "KETTLE_MAX_JOB_TRACKER_SIZE"; /** * A variable to configure the maximum number of job entry results kept in memory for logging purposes. */ public static final String KETTLE_MAX_JOB_ENTRIES_LOGGED = "KETTLE_MAX_JOB_ENTRIES_LOGGED"; /** * A variable to configure the maximum number of logging registry entries kept in memory for logging purposes. */ public static final String KETTLE_MAX_LOGGING_REGISTRY_SIZE = "KETTLE_MAX_LOGGING_REGISTRY_SIZE"; /** * A variable to configure the kettle log tab refresh delay. */ public static final String KETTLE_LOG_TAB_REFRESH_DELAY = "KETTLE_LOG_TAB_REFRESH_DELAY"; /** * A variable to configure the kettle log tab refresh period. */ public static final String KETTLE_LOG_TAB_REFRESH_PERIOD = "KETTLE_LOG_TAB_REFRESH_PERIOD"; /** * The name of the system wide variable that can contain the name of the SAP Connection factory for the test button in * the DB dialog. This defaults to */ public static final String KETTLE_SAP_CONNECTION_FACTORY = "KETTLE_SAP_CONNECTION_FACTORY"; /** * The default SAP ERP connection factory */ public static final String KETTLE_SAP_CONNECTION_FACTORY_DEFAULT_NAME = "org.pentaho.di.trans.steps.sapinput.sap.SAPConnectionFactory"; /** * Name of the environment variable to specify additional classes to scan for plugin annotations */ public static final String KETTLE_PLUGIN_CLASSES = "KETTLE_PLUGIN_CLASSES"; /** * Name of the environment variable to specify additional packaged to scan for plugin annotations (warning: slow!) */ public static final String KETTLE_PLUGIN_PACKAGES = "KETTLE_PLUGIN_PACKAGES"; /** * Name of the environment variable that contains the size of the transformation rowset size. This overwrites values * that you set transformation settings. */ public static final String KETTLE_TRANS_ROWSET_SIZE = "KETTLE_TRANS_ROWSET_SIZE"; /** * A general initial version comment */ public static final String VERSION_COMMENT_INITIAL_VERSION = "Creation of initial version"; /** * A general edit version comment */ public static final String VERSION_COMMENT_EDIT_VERSION = "Modification by user"; /** * The XML file that contains the list of native Kettle steps */ public static final String XML_FILE_KETTLE_STEPS = "kettle-steps.xml"; /** * The name of the environment variable that will contain the alternative location of the kettle-steps.xml file */ public static final String KETTLE_CORE_STEPS_FILE = "KETTLE_CORE_STEPS_FILE"; /** * The XML file that contains the list of native partition plugins */ public static final String XML_FILE_KETTLE_PARTITION_PLUGINS = "kettle-partition-plugins.xml"; /** * The name of the environment variable that will contain the alternative location of the kettle-job-entries.xml file */ public static final String KETTLE_CORE_JOBENTRIES_FILE = "KETTLE_CORE_JOBENTRIES_FILE"; /** * The XML file that contains the list of native Kettle Carte Servlets */ public static final String XML_FILE_KETTLE_SERVLETS = "kettle-servlets.xml"; /** * The XML file that contains the list of native Kettle value metadata plugins */ public static final String XML_FILE_KETTLE_VALUEMETA_PLUGINS = "kettle-valuemeta-plugins.xml"; /** * The XML file that contains the list of native Kettle two-way password encoder plugins */ public static final String XML_FILE_KETTLE_PASSWORD_ENCODER_PLUGINS = "kettle-password-encoder-plugins.xml"; /** * The name of the environment variable that will contain the alternative location of the kettle-valuemeta-plugins.xml * file */ public static final String KETTLE_VALUEMETA_PLUGINS_FILE = "KETTLE_VALUEMETA_PLUGINS_FILE"; /** * Specifies the password encoding plugin to use by ID (Kettle is the default). */ public static final String KETTLE_PASSWORD_ENCODER_PLUGIN = "KETTLE_PASSWORD_ENCODER_PLUGIN"; /** * The name of the environment variable that will contain the alternative location of the kettle-password-encoder-plugins.xml * file */ public static final String KETTLE_PASSWORD_ENCODER_PLUGINS_FILE = "KETTLE_PASSWORD_ENCODER_PLUGINS_FILE"; /** * The XML file that contains the list of native Kettle logging plugins */ public static final String XML_FILE_KETTLE_LOGGING_PLUGINS = "kettle-logging-plugins.xml"; /** * The name of the environment variable that will contain the alternative location of the kettle-logging-plugins.xml * file */ public static final String KETTLE_LOGGING_PLUGINS_FILE = "KETTLE_LOGGING_PLUGINS_FILE"; /** * The name of the environment variable that will contain the alternative location of the kettle-servlets.xml file */ public static final String KETTLE_CORE_SERVLETS_FILE = "KETTLE_CORE_SERVLETS_FILE"; /** * The name of the variable that optionally contains an alternative rowset get timeout (in ms). This only makes a * difference for extremely short lived transformations. */ public static final String KETTLE_ROWSET_GET_TIMEOUT = "KETTLE_ROWSET_GET_TIMEOUT"; /** * The name of the variable that optionally contains an alternative rowset put timeout (in ms). This only makes a * difference for extremely short lived transformations. */ public static final String KETTLE_ROWSET_PUT_TIMEOUT = "KETTLE_ROWSET_PUT_TIMEOUT"; /** * Set this variable to Y if you want to test a more efficient batching row set. (default = N) */ public static final String KETTLE_BATCHING_ROWSET = "KETTLE_BATCHING_ROWSET"; /** * Set this variable to Y to disable standard Kettle logging to the console. (stdout) */ public static final String KETTLE_DISABLE_CONSOLE_LOGGING = "KETTLE_DISABLE_CONSOLE_LOGGING"; /** * The XML file that contains the list of native Kettle job entries */ public static final String XML_FILE_KETTLE_JOB_ENTRIES = "kettle-job-entries.xml"; /** * The XML file that contains the list of native Kettle repository types (DB, File, etc) */ public static final String XML_FILE_KETTLE_REPOSITORIES = "kettle-repositories.xml"; /** * The XML file that contains the list of native Kettle database types (MySQL, Oracle, etc) */ public static final String XML_FILE_KETTLE_DATABASE_TYPES = "kettle-database-types.xml"; /** * The XML file that contains the list of native Kettle compression providers (None, ZIP, GZip, etc.) */ public static final String XML_FILE_KETTLE_COMPRESSION_PROVIDERS = "kettle-compression-providers.xml"; /** * The XML file that contains the list of native Kettle compression providers (None, ZIP, GZip, etc.) */ public static final String XML_FILE_KETTLE_AUTHENTICATION_PROVIDERS = "kettle-authentication-providers.xml"; /** * The XML file that contains the list of native extension points (None by default, this is mostly for OEM purposes) */ public static final String XML_FILE_KETTLE_EXTENSION_POINTS = "kettle-extension-points.xml"; /** * The XML file that contains the list of native extension points (None by default, this is mostly for OEM purposes) */ public static final String XML_FILE_KETTLE_REGISTRY_EXTENSIONS = "kettle-registry-extensions.xml"; /** * The XML file that contains the list of lifecycle listeners */ public static final String XML_FILE_KETTLE_LIFECYCLE_LISTENERS = "kettle-lifecycle-listeners.xml"; /** * The XML file that contains the list of native engines */ public static final String XML_FILE_KETTLE_ENGINES = "kettle-engines.xml"; /** * the value the Pan JVM should return on exit. */ public static final String KETTLE_TRANS_PAN_JVM_EXIT_CODE = "KETTLE_TRANS_PAN_JVM_EXIT_CODE"; /** * The name of the variable containing an alternative default number format */ public static final String KETTLE_DEFAULT_NUMBER_FORMAT = "KETTLE_DEFAULT_NUMBER_FORMAT"; /** * The name of the variable containing an alternative default bignumber format */ public static final String KETTLE_DEFAULT_BIGNUMBER_FORMAT = "KETTLE_DEFAULT_BIGNUMBER_FORMAT"; /** * The name of the variable containing an alternative default integer format */ public static final String KETTLE_DEFAULT_INTEGER_FORMAT = "KETTLE_DEFAULT_INTEGER_FORMAT"; /** * The name of the variable containing an alternative default date format */ public static final String KETTLE_DEFAULT_DATE_FORMAT = "KETTLE_DEFAULT_DATE_FORMAT"; // Null values tweaks public static final String KETTLE_AGGREGATION_MIN_NULL_IS_VALUED = "KETTLE_AGGREGATION_MIN_NULL_IS_VALUED"; public static final String KETTLE_AGGREGATION_ALL_NULLS_ARE_ZERO = "KETTLE_AGGREGATION_ALL_NULLS_ARE_ZERO"; /** * The name of the variable containing an alternative default timestamp format */ public static final String KETTLE_DEFAULT_TIMESTAMP_FORMAT = "KETTLE_DEFAULT_TIMESTAMP_FORMAT"; /** * Variable that is responsible for removing enclosure symbol after splitting the string */ public static final String KETTLE_SPLIT_FIELDS_REMOVE_ENCLOSURE = "KETTLE_SPLIT_FIELDS_REMOVE_ENCLOSURE"; /** * Set this variable to false to preserve global log variables defined in transformation / job Properties -> Log panel. * Changing it to true will clear all global log variables when export transformation / job */ public static final String KETTLE_GLOBAL_LOG_VARIABLES_CLEAR_ON_EXPORT = "KETTLE_GLOBAL_LOG_VARIABLES_CLEAR_ON_EXPORT"; /** * Compatibility settings for {@link org.pentaho.di.core.row.ValueDataUtil#hourOfDay(ValueMetaInterface, Object)}. * * Switches off the fix for calculation of timezone decomposition. */ public static final String KETTLE_COMPATIBILITY_CALCULATION_TIMEZONE_DECOMPOSITION = "KETTLE_COMPATIBILITY_CALCULATION_TIMEZONE_DECOMPOSITION"; /** * Compatibility settings for setNrErrors */ // see PDI-10270 for details. public static final String KETTLE_COMPATIBILITY_SET_ERROR_ON_SPECIFIC_JOB_ENTRIES = "KETTLE_COMPATIBILITY_SET_ERROR_ON_SPECIFIC_JOB_ENTRIES"; // See PDI-15781 for details public static final String KETTLE_COMPATIBILITY_SEND_RESULT_XML_WITH_FULL_STATUS = "KETTLE_COMPATIBILITY_SEND_RESULT_XML_WITH_FULL_STATUS"; /** * The XML file that contains the list of native import rules */ public static final String XML_FILE_KETTLE_IMPORT_RULES = "kettle-import-rules.xml"; private static String[] emptyPaddedSpacesStrings; /** * The release type of this compilation */ public static final ReleaseType RELEASE = ReleaseType.GA; /** * The system environment variable indicating where the alternative location for the Pentaho metastore folder is * located. */ public static final String PENTAHO_METASTORE_FOLDER = "PENTAHO_METASTORE_FOLDER"; /** * The name of the local client MetaStore * */ public static final String PENTAHO_METASTORE_NAME = "Pentaho Local Client Metastore"; /** * A variable to configure turning on/off detailed subjects in log. */ public static final String KETTLE_LOG_MARK_MAPPINGS = "KETTLE_LOG_MARK_MAPPINGS"; /** * A variable to configure jetty option: acceptors for Carte */ public static final String KETTLE_CARTE_JETTY_ACCEPTORS = "KETTLE_CARTE_JETTY_ACCEPTORS"; /** * A variable to configure jetty option: acceptQueueSize for Carte */ public static final String KETTLE_CARTE_JETTY_ACCEPT_QUEUE_SIZE = "KETTLE_CARTE_JETTY_ACCEPT_QUEUE_SIZE"; /** * A variable to configure jetty option: lowResourcesMaxIdleTime for Carte */ public static final String KETTLE_CARTE_JETTY_RES_MAX_IDLE_TIME = "KETTLE_CARTE_JETTY_RES_MAX_IDLE_TIME"; /** * A variable to configure VFS USER_DIR_IS_ROOT option: should be "true" or "false" * {@linkplain org.apache.commons.vfs2.provider.sftp.SftpFileSystemConfigBuilder#USER_DIR_IS_ROOT} */ public static final String VFS_USER_DIR_IS_ROOT = "vfs.sftp.userDirIsRoot"; /** * rounds double f to any number of places after decimal point Does arithmetic using BigDecimal class to avoid integer * overflow while rounding * * @param f * The value to round * @param places * The number of decimal places * @return The rounded floating point value */ public static double round( double f, int places ) { return round( f, places, java.math.BigDecimal.ROUND_HALF_EVEN ); } /** * rounds double f to any number of places after decimal point Does arithmetic using BigDecimal class to avoid integer * overflow while rounding * * @param f * The value to round * @param places * The number of decimal places * @param roundingMode * The mode for rounding, e.g. java.math.BigDecimal.ROUND_HALF_EVEN * @return The rounded floating point value */ public static double round( double f, int places, int roundingMode ) { // We can't round non-numbers or infinite values // if ( Double.isNaN( f ) || f == Double.NEGATIVE_INFINITY || f == Double.POSITIVE_INFINITY ) { return f; } // Do the rounding... // java.math.BigDecimal bdtemp = round( java.math.BigDecimal.valueOf( f ), places, roundingMode ); return bdtemp.doubleValue(); } /** * rounds BigDecimal f to any number of places after decimal point Does arithmetic using BigDecimal class to avoid * integer overflow while rounding * * @param f * The value to round * @param places * The number of decimal places * @param roundingMode * The mode for rounding, e.g. java.math.BigDecimal.ROUND_HALF_EVEN * @return The rounded floating point value */ public static BigDecimal round( BigDecimal f, int places, int roundingMode ) { if ( roundingMode == ROUND_HALF_CEILING ) { if ( f.signum() >= 0 ) { return round( f, places, BigDecimal.ROUND_HALF_UP ); } else { return round( f, places, BigDecimal.ROUND_HALF_DOWN ); } } else { return f.setScale( places, roundingMode ); } } /** * rounds long f to any number of places after decimal point Does arithmetic using BigDecimal class to avoid integer * overflow while rounding * * @param f * The value to round * @param places * The number of decimal places * @param roundingMode * The mode for rounding, e.g. java.math.BigDecimal.ROUND_HALF_EVEN * @return The rounded floating point value */ public static long round( long f, int places, int roundingMode ) { if ( places >= 0 ) { return f; } BigDecimal bdtemp = round( BigDecimal.valueOf( f ), places, roundingMode ); return bdtemp.longValue(); } /* * OLD code: caused a lot of problems with very small and very large numbers. It's a miracle it worked at all. Go * ahead, have a laugh... public static float round(double f, int places) { float temp = (float) (f * * (Math.pow(10, places))); * * temp = (Math.round(temp)); * * temp = temp / (int) (Math.pow(10, places)); * * return temp; * * } */ /** * Convert a String into an integer. If the conversion fails, assign a default value. * * @param str * The String to convert to an integer * @param def * The default value * @return The converted value or the default. */ public static int toInt( String str, int def ) { int retval; try { retval = Integer.parseInt( str ); } catch ( Exception e ) { retval = def; } return retval; } /** * Convert a String into a long integer. If the conversion fails, assign a default value. * * @param str * The String to convert to a long integer * @param def * The default value * @return The converted value or the default. */ public static long toLong( String str, long def ) { long retval; try { retval = Long.parseLong( str ); } catch ( Exception e ) { retval = def; } return retval; } /** * Convert a String into a double. If the conversion fails, assign a default value. * * @param str * The String to convert to a double * @param def * The default value * @return The converted value or the default. */ public static double toDouble( String str, double def ) { double retval; try { retval = Double.parseDouble( str ); } catch ( Exception e ) { retval = def; } return retval; } /** * Convert a String into a date. The date format is <code>yyyy/MM/dd HH:mm:ss.SSS</code>. If the conversion fails, * assign a default value. * * @param str * The String to convert into a Date * @param def * The default value * @return The converted value or the default. */ public static Date toDate( String str, Date def ) { SimpleDateFormat df = new SimpleDateFormat( "yyyy/MM/dd HH:mm:ss.SSS", Locale.US ); try { return df.parse( str ); } catch ( ParseException e ) { return def; } } /** * Determines whether or not a character is considered a space. A character is considered a space in Kettle if it is a * space, a tab, a newline or a cariage return. * * @param c * The character to verify if it is a space. * @return true if the character is a space. false otherwise. */ public static boolean isSpace( char c ) { return c == ' ' || c == '\t' || c == '\r' || c == '\n' || Character.isWhitespace( c ); } /** * Left trim: remove spaces to the left of a String. * * @param source * The String to left trim * @return The left trimmed String */ public static String ltrim( String source ) { if ( source == null ) { return null; } int from = 0; while ( from < source.length() && isSpace( source.charAt( from ) ) ) { from++; } return source.substring( from ); } /** * Right trim: remove spaces to the right of a string * * @param source * The string to right trim * @return The trimmed string. */ public static String rtrim( String source ) { if ( source == null ) { return null; } int max = source.length(); while ( max > 0 && isSpace( source.charAt( max - 1 ) ) ) { max--; } return source.substring( 0, max ); } /** * Trims a string: removes the leading and trailing spaces of a String. * * @param str * The string to trim * @return The trimmed string. */ public static String trim( String str ) { if ( str == null ) { return null; } int max = str.length() - 1; int min = 0; while ( min <= max && isSpace( str.charAt( min ) ) ) { min++; } while ( max >= 0 && isSpace( str.charAt( max ) ) ) { max--; } if ( max < min ) { return ""; } return str.substring( min, max + 1 ); } /** * Right pad a string: adds spaces to a string until a certain length. If the length is smaller then the limit * specified, the String is truncated. * * @param ret * The string to pad * @param limit * The desired length of the padded string. * @return The padded String. */ public static String rightPad( String ret, int limit ) { if ( ret == null ) { return rightPad( new StringBuilder(), limit ); } else { return rightPad( new StringBuilder( ret ), limit ); } } /** * Right pad a StringBuffer: adds spaces to a string until a certain length. If the length is smaller then the limit * specified, the String is truncated. * * MB - New version is nearly 25% faster * * @param ret * The StringBuffer to pad * @param limit * The desired length of the padded string. * @return The padded String. */ public static String rightPad( StringBuffer ret, int limit ) { if ( ret != null ) { while ( ret.length() < limit ) { ret.append( " " ); } ret.setLength( limit ); return ret.toString(); } else { return null; } } /** * Right pad a StringBuilder: adds spaces to a string until a certain length. If the length is smaller then the limit * specified, the String is truncated. * * MB - New version is nearly 25% faster * * @param ret * The StringBuilder to pad * @param limit * The desired length of the padded string. * @return The padded String. */ public static String rightPad( StringBuilder ret, int limit ) { if ( ret != null ) { while ( ret.length() < limit ) { ret.append( " " ); } ret.setLength( limit ); return ret.toString(); } else { return null; } } /** * Replace values in a String with another. * * 33% Faster using replaceAll this way than original method * * @param string * The original String. * @param repl * The text to replace * @param with * The new text bit * @return The resulting string with the text pieces replaced. */ public static String replace( String string, String repl, String with ) { if ( string != null && repl != null && with != null ) { return string.replaceAll( Pattern.quote( repl ), Matcher.quoteReplacement( with ) ); } else { return null; } } /** * Alternate faster version of string replace using a stringbuffer as input. * * 33% Faster using replaceAll this way than original method * * @param str * The string where we want to replace in * @param code * The code to search for * @param repl * The replacement string for code */ public static void repl( StringBuffer str, String code, String repl ) { if ( ( code == null ) || ( repl == null ) || ( code.length() == 0 ) || ( repl.length() == 0 ) || ( str == null ) || ( str.length() == 0 ) ) { return; // do nothing } String aString = str.toString(); str.setLength( 0 ); str.append( aString.replaceAll( Pattern.quote( code ), Matcher.quoteReplacement( repl ) ) ); } /** * Alternate faster version of string replace using a stringbuilder as input (non-synchronized). * * 33% Faster using replaceAll this way than original method * * @param str * The string where we want to replace in * @param code * The code to search for * @param repl * The replacement string for code */ public static void repl( StringBuilder str, String code, String repl ) { if ( ( code == null ) || ( repl == null ) || ( str == null ) ) { return; // do nothing } String aString = str.toString(); str.setLength( 0 ); str.append( aString.replaceAll( Pattern.quote( code ), Matcher.quoteReplacement( repl ) ) ); } /** * Count the number of spaces to the left of a text. (leading) * * @param field * The text to examine * @return The number of leading spaces found. */ public static int nrSpacesBefore( String field ) { int nr = 0; int len = field.length(); while ( nr < len && field.charAt( nr ) == ' ' ) { nr++; } return nr; } /** * Count the number of spaces to the right of a text. (trailing) * * @param field * The text to examine * @return The number of trailing spaces found. */ public static int nrSpacesAfter( String field ) { int nr = 0; int len = field.length(); while ( nr < len && field.charAt( field.length() - 1 - nr ) == ' ' ) { nr++; } return nr; } /** * Checks whether or not a String consists only of spaces. * * @param str * The string to check * @return true if the string has nothing but spaces. */ public static boolean onlySpaces( String str ) { for ( int i = 0; i < str.length(); i++ ) { if ( !isSpace( str.charAt( i ) ) ) { return false; } } return true; } /** * determine the OS name * * @return The name of the OS */ public static String getOS() { return System.getProperty( "os.name" ); } /** * Determine the quoting character depending on the OS. Often used for shell calls, gives back " for Windows systems * otherwise ' * * @return quoting character */ public static String getQuoteCharByOS() { if ( isWindows() ) { return "\""; } else { return "'"; } } /** * Quote a string depending on the OS. Often used for shell calls. * * @return quoted string */ public static String optionallyQuoteStringByOS( String string ) { String quote = getQuoteCharByOS(); if ( Utils.isEmpty( string ) ) { return quote; } // If the field already contains quotes, we don't touch it anymore, just // return the same string... // also return it if no spaces are found if ( string.indexOf( quote ) >= 0 || ( string.indexOf( ' ' ) < 0 && string.indexOf( '=' ) < 0 ) ) { return string; } else { return quote + string + quote; } } /** * @return True if the OS is a Windows derivate. */ public static boolean isWindows() { return getOS().startsWith( "Windows" ); } /** * @return True if the OS is a Linux derivate. */ public static boolean isLinux() { return getOS().startsWith( "Linux" ); } /** * @return True if the OS is an OSX derivate. */ public static boolean isOSX() { return getOS().toUpperCase().contains( "OS X" ); } /** * @return True if KDE is in use. */ public static boolean isKDE() { return StringUtils.isNotBlank( System.getenv( "KDE_SESSION_VERSION" ) ); } private static String cachedHostname; /** * Determine the hostname of the machine Kettle is running on * * @return The hostname */ public static String getHostname() { if ( cachedHostname != null ) { return cachedHostname; } // In case we don't want to leave anything to doubt... // String systemHostname = EnvUtil.getSystemProperty( KETTLE_SYSTEM_HOSTNAME ); if ( !Utils.isEmpty( systemHostname ) ) { cachedHostname = systemHostname; return systemHostname; } String lastHostname = "localhost"; try { Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); while ( en.hasMoreElements() ) { NetworkInterface nwi = en.nextElement(); Enumeration<InetAddress> ip = nwi.getInetAddresses(); while ( ip.hasMoreElements() ) { InetAddress in = ip.nextElement(); lastHostname = in.getHostName(); // System.out.println(" ip address bound : "+in.getHostAddress()); // System.out.println(" hostname : "+in.getHostName()); // System.out.println(" Cann.hostname : "+in.getCanonicalHostName()); // System.out.println(" ip string : "+in.toString()); if ( !lastHostname.equalsIgnoreCase( "localhost" ) && !( lastHostname.indexOf( ':' ) >= 0 ) ) { break; } } } } catch ( SocketException e ) { // Eat exception, just return what you have } cachedHostname = lastHostname; return lastHostname; } /** * Determine the hostname of the machine Kettle is running on * * @return The hostname */ public static String getHostnameReal() { // In case we don't want to leave anything to doubt... // String systemHostname = EnvUtil.getSystemProperty( KETTLE_SYSTEM_HOSTNAME ); if ( !Utils.isEmpty( systemHostname ) ) { return systemHostname; } if ( isWindows() ) { // Windows will always set the 'COMPUTERNAME' variable return System.getenv( "COMPUTERNAME" ); } else { // If it is not Windows then it is most likely a Unix-like operating system // such as Solaris, AIX, HP-UX, Linux or MacOS. // Most modern shells (such as Bash or derivatives) sets the // HOSTNAME variable so lets try that first. String hostname = System.getenv( "HOSTNAME" ); if ( hostname != null ) { return hostname; } else { BufferedReader br; try { Process pr = Runtime.getRuntime().exec( "hostname" ); br = new BufferedReader( new InputStreamReader( pr.getInputStream() ) ); String line; if ( ( line = br.readLine() ) != null ) { return line; } pr.waitFor(); br.close(); } catch ( IOException e ) { return getHostname(); } catch ( InterruptedException e ) { return getHostname(); } } } return getHostname(); } /** * Determins the IP address of the machine Kettle is running on. * * @return The IP address */ public static String getIPAddress() throws Exception { Enumeration<NetworkInterface> enumInterfaces = NetworkInterface.getNetworkInterfaces(); while ( enumInterfaces.hasMoreElements() ) { NetworkInterface nwi = enumInterfaces.nextElement(); Enumeration<InetAddress> ip = nwi.getInetAddresses(); while ( ip.hasMoreElements() ) { InetAddress in = ip.nextElement(); if ( !in.isLoopbackAddress() && in.toString().indexOf( ":" ) < 0 ) { return in.getHostAddress(); } } } return "127.0.0.1"; } /** * Get the primary IP address tied to a network interface (excluding loop-back etc) * * @param networkInterfaceName * the name of the network interface to interrogate * @return null if the network interface or address wasn't found. * * @throws SocketException * in case of a security or network error */ public static String getIPAddress( String networkInterfaceName ) throws SocketException { NetworkInterface networkInterface = NetworkInterface.getByName( networkInterfaceName ); Enumeration<InetAddress> ipAddresses = networkInterface.getInetAddresses(); while ( ipAddresses.hasMoreElements() ) { InetAddress inetAddress = ipAddresses.nextElement(); if ( !inetAddress.isLoopbackAddress() && inetAddress.toString().indexOf( ":" ) < 0 ) { String hostname = inetAddress.getHostAddress(); return hostname; } } return null; } /** * Tries to determine the MAC address of the machine Kettle is running on. * * @return The MAC address. */ public static String getMACAddress() throws Exception { String ip = getIPAddress(); String mac = "none"; String os = getOS(); String s = ""; @SuppressWarnings( "unused" ) Boolean errorOccured = false; // System.out.println("os = "+os+", ip="+ip); if ( os.equalsIgnoreCase( "Windows NT" ) || os.equalsIgnoreCase( "Windows 2000" ) || os.equalsIgnoreCase( "Windows XP" ) || os.equalsIgnoreCase( "Windows 95" ) || os.equalsIgnoreCase( "Windows 98" ) || os.equalsIgnoreCase( "Windows Me" ) || os.startsWith( "Windows" ) ) { try { // System.out.println("EXEC> nbtstat -a "+ip); Process p = Runtime.getRuntime().exec( "nbtstat -a " + ip ); // read the standard output of the command BufferedReader stdInput = new BufferedReader( new InputStreamReader( p.getInputStream() ) ); while ( !procDone( p ) ) { while ( ( s = stdInput.readLine() ) != null ) { // System.out.println("NBTSTAT> "+s); if ( s.indexOf( "MAC" ) >= 0 ) { int idx = s.indexOf( '=' ); mac = s.substring( idx + 2 ); } } } stdInput.close(); } catch ( Exception e ) { errorOccured = true; } } else if ( os.equalsIgnoreCase( "Linux" ) ) { try { Process p = Runtime.getRuntime().exec( "/sbin/ifconfig -a" ); // read the standard output of the command BufferedReader stdInput = new BufferedReader( new InputStreamReader( p.getInputStream() ) ); while ( !procDone( p ) ) { while ( ( s = stdInput.readLine() ) != null ) { int idx = s.indexOf( "HWaddr" ); if ( idx >= 0 ) { mac = s.substring( idx + 7 ); } } } stdInput.close(); } catch ( Exception e ) { errorOccured = true; } } else if ( os.equalsIgnoreCase( "Solaris" ) ) { try { Process p = Runtime.getRuntime().exec( "/usr/sbin/ifconfig -a" ); // read the standard output of the command BufferedReader stdInput = new BufferedReader( new InputStreamReader( p.getInputStream() ) ); while ( !procDone( p ) ) { while ( ( s = stdInput.readLine() ) != null ) { int idx = s.indexOf( "ether" ); if ( idx >= 0 ) { mac = s.substring( idx + 6 ); } } } stdInput.close(); } catch ( Exception e ) { errorOccured = true; } } else if ( os.equalsIgnoreCase( "HP-UX" ) ) { try { Process p = Runtime.getRuntime().exec( "/usr/sbin/lanscan -a" ); // read the standard output of the command BufferedReader stdInput = new BufferedReader( new InputStreamReader( p.getInputStream() ) ); while ( !procDone( p ) ) { while ( ( s = stdInput.readLine() ) != null ) { if ( s.indexOf( "MAC" ) >= 0 ) { int idx = s.indexOf( "0x" ); mac = s.substring( idx + 2 ); } } } stdInput.close(); } catch ( Exception e ) { errorOccured = true; } } // should do something if we got an error processing! return Const.trim( mac ); } private static final boolean procDone( Process p ) { try { p.exitValue(); return true; } catch ( IllegalThreadStateException e ) { return false; } } /** * Looks up the user's home directory (or KETTLE_HOME) for every invocation. This is no longer a static property so * the value may be set after this class is loaded. * * @return The path to the users home directory, or the System property {@code KETTLE_HOME} if set. */ public static String getUserHomeDirectory() { return NVL( System.getenv( "KETTLE_HOME" ), NVL( System.getProperty( "KETTLE_HOME" ), System.getProperty( "user.home" ) ) ); } /** * Determines the Kettle absolute directory in the user's home directory. * * @return The Kettle absolute directory. */ public static String getKettleDirectory() { return getUserHomeDirectory() + FILE_SEPARATOR + getUserBaseDir(); } /** * Determines the Kettle directory in the user's home directory. * * @return The Kettle directory. */ public static String getUserBaseDir() { return BasePropertyHandler.getProperty( "userBaseDir", ".kettle" ); } /** * Returns the value of DI_HOME. */ public static String getDIHomeDirectory() { return System.getProperty( "DI_HOME" ); } /** * Determines the location of the shared objects file * * @return the name of the shared objects file */ public static String getSharedObjectsFile() { return getKettleDirectory() + FILE_SEPARATOR + SHARED_DATA_FILE; } /** * Returns the path to the Kettle local (current directory) repositories XML file. * * @return The local repositories file. */ public static String getKettleLocalRepositoriesFile() { return "repositories.xml"; } /** * Returns the full path to the Kettle repositories XML file. * * @return The Kettle repositories file. */ public static String getKettleUserRepositoriesFile() { return getKettleDirectory() + FILE_SEPARATOR + getKettleLocalRepositoriesFile(); } /** * Returns the path to the Kettle local (current directory) Carte password file: * <p> * ./pwd/kettle.pwd<br> * * @return The local Carte password file. */ public static String getKettleLocalCartePasswordFile() { return "pwd/kettle.pwd"; } /** * Returns the path to the Kettle Carte password file in the home directory: * <p> * $KETTLE_HOME/.kettle/kettle.pwd<br> * * @return The Carte password file in the home directory. */ public static String getKettleCartePasswordFile() { return getKettleDirectory() + FILE_SEPARATOR + "kettle.pwd"; } /** * Provides the base documentation url (top-level help) * * @return the fully qualified base documentation URL */ public static String getBaseDocUrl() { return BaseMessages.getString( PKG, "Const.BaseDocUrl" ); } /** * Provides the documentation url with the configured base + the given URI. * * @param uri * the resource identifier for the documentation (eg. 0L0/0Y0/030/050/000) * * @return the fully qualified documentation URL for the given URI */ public static String getDocUrl( final String uri ) { // initialize the docUrl to point to the top-level doc page String docUrl = getBaseDocUrl(); if ( !Utils.isEmpty( uri ) ) { // if the uri is not empty, use it to build the URL if ( uri.startsWith( "http" ) ) { // use what is provided, it's already absolute docUrl = uri; } else { // the uri provided needs to be assembled docUrl = uri.startsWith( "/" ) ? docUrl + uri.substring( 1 ) : docUrl + uri; } } return docUrl; } /** * Retrieves the content of an environment variable * * @param variable * The name of the environment variable * @param deflt * The default value in case no value was found * @return The value of the environment variable or the value of deflt in case no variable was defined. */ public static String getEnvironmentVariable( String variable, String deflt ) { return System.getProperty( variable, deflt ); } /** * Replaces environment variables in a string. For example if you set KETTLE_HOME as an environment variable, you can * use %%KETTLE_HOME%% in dialogs etc. to refer to this value. This procedures looks for %%...%% pairs and replaces * them including the name of the environment variable with the actual value. In case the variable was not set, * nothing is replaced! * * @param string * The source string where text is going to be replaced. * * @return The expanded string. * @deprecated use StringUtil.environmentSubstitute(): handles both Windows and unix conventions */ @Deprecated public static String replEnv( String string ) { if ( string == null ) { return null; } StringBuilder str = new StringBuilder( string ); int idx = str.indexOf( "%%" ); while ( idx >= 0 ) { // OK, so we found a marker, look for the next one... int to = str.indexOf( "%%", idx + 2 ); if ( to >= 0 ) { // OK, we found the other marker also... String marker = str.substring( idx, to + 2 ); String var = str.substring( idx + 2, to ); if ( var != null && var.length() > 0 ) { // Get the environment variable String newval = getEnvironmentVariable( var, null ); if ( newval != null ) { // Replace the whole bunch str.replace( idx, to + 2, newval ); // System.out.println("Replaced ["+marker+"] with ["+newval+"]"); // The last position has changed... to += newval.length() - marker.length(); } } } else { // We found the start, but NOT the ending %% without closing %% to = idx; } // Look for the next variable to replace... idx = str.indexOf( "%%", to + 1 ); } return str.toString(); } /** * Replaces environment variables in an array of strings. * <p> * See also: replEnv(String string) * * @param string * The array of strings that wants its variables to be replaced. * @return the array with the environment variables replaced. * @deprecated please use StringUtil.environmentSubstitute now. */ @Deprecated public static String[] replEnv( String[] string ) { String[] retval = new String[string.length]; for ( int i = 0; i < string.length; i++ ) { retval[i] = Const.replEnv( string[i] ); } return retval; } /** * Implements Oracle style NVL function * * @param source * The source argument * @param def * The default value in case source is null or the length of the string is 0 * @return source if source is not null, otherwise return def */ public static String NVL( String source, String def ) { if ( source == null || source.length() == 0 ) { return def; } return source; } /** * Return empty string "" in case the given parameter is null, otherwise return the same value. * * @param source * The source value to check for null. * @return empty string if source is null, otherwise simply return the source value. */ public static String nullToEmpty( String source ) { if ( source == null ) { return ""; } return source; } /** * Search for a string in an array of strings and return the index. * * @param lookup * The string to search for * @param array * The array of strings to look in * @return The index of a search string in an array of strings. -1 if not found. */ public static int indexOfString( String lookup, String[] array ) { if ( array == null ) { return -1; } if ( lookup == null ) { return -1; } for ( int i = 0; i < array.length; i++ ) { if ( lookup.equalsIgnoreCase( array[i] ) ) { return i; } } return -1; } /** * Search for strings in an array of strings and return the indexes. * * @param lookup * The strings to search for * @param array * The array of strings to look in * @return The indexes of strings in an array of strings. -1 if not found. */ public static int[] indexsOfStrings( String[] lookup, String[] array ) { int[] indexes = new int[lookup.length]; for ( int i = 0; i < indexes.length; i++ ) { indexes[i] = indexOfString( lookup[i], array ); } return indexes; } /** * Search for strings in an array of strings and return the indexes. If a string is not found, the index is not * returned. * * @param lookup * The strings to search for * @param array * The array of strings to look in * @return The indexes of strings in an array of strings. Only existing indexes are returned (no -1) */ public static int[] indexsOfFoundStrings( String[] lookup, String[] array ) { List<Integer> indexesList = new ArrayList<Integer>(); for ( int i = 0; i < lookup.length; i++ ) { int idx = indexOfString( lookup[i], array ); if ( idx >= 0 ) { indexesList.add( Integer.valueOf( idx ) ); } } int[] indexes = new int[indexesList.size()]; for ( int i = 0; i < indexesList.size(); i++ ) { indexes[i] = ( indexesList.get( i ) ).intValue(); } return indexes; } /** * Search for a string in a list of strings and return the index. * * @param lookup * The string to search for * @param list * The ArrayList of strings to look in * @return The index of a search string in an array of strings. -1 if not found. */ public static int indexOfString( String lookup, List<String> list ) { if ( list == null ) { return -1; } for ( int i = 0; i < list.size(); i++ ) { String compare = list.get( i ); if ( lookup.equalsIgnoreCase( compare ) ) { return i; } } return -1; } /** * Sort the strings of an array in alphabetical order. * * @param input * The array of strings to sort. * @return The sorted array of strings. */ public static String[] sortStrings( String[] input ) { Arrays.sort( input ); return input; } /** * Convert strings separated by a string into an array of strings. * <p> * <code> Example: a;b;c;d ==> new String[] { a, b, c, d } * </code> * * <p> * <b>NOTE: this differs from String.split() in a way that the built-in method uses regular expressions and this one * does not.</b> * * @param string * The string to split * @param separator * The separator used. * @return the string split into an array of strings */ public static String[] splitString( String string, String separator ) { /* * 0123456 Example a;b;c;d --> new String[] { a, b, c, d } */ // System.out.println("splitString ["+path+"] using ["+separator+"]"); List<String> list = new ArrayList<String>(); if ( string == null || string.length() == 0 ) { return new String[] {}; } int sepLen = separator.length(); int from = 0; int end = string.length() - sepLen + 1; for ( int i = from; i < end; i += sepLen ) { if ( string.substring( i, i + sepLen ).equalsIgnoreCase( separator ) ) { // OK, we found a separator, the string to add to the list // is [from, i[ list.add( NVL( string.substring( from, i ), "" ) ); from = i + sepLen; } } // Wait, if the string didn't end with a separator, we still have information at the end of the string... // In our example that would be "d"... if ( from + sepLen <= string.length() ) { list.add( NVL( string.substring( from, string.length() ), "" ) ); } return list.toArray( new String[list.size()] ); } /** * Convert strings separated by a character into an array of strings. * <p> * <code> Example: a;b;c;d == new String[] { a, b, c, d } * </code> * * @param string * The string to split * @param separator * The separator used. * @return the string split into an array of strings */ public static String[] splitString( String string, char separator ) { return splitString( string, separator, false ); } /** * Convert strings separated by a character into an array of strings. * <p> * <code> Example: a;b;c;d == new String[] { a, b, c, d } * </code> * * @param string * The string to split * @param separator * The separator used. * @param escape * in case the separator can be escaped (\;) The escape characters are NOT removed! * @return the string split into an array of strings */ public static String[] splitString( String string, char separator, boolean escape ) { /* * 0123456 Example a;b;c;d --> new String[] { a, b, c, d } */ // System.out.println("splitString ["+path+"] using ["+separator+"]"); List<String> list = new ArrayList<String>(); if ( string == null || string.length() == 0 ) { return new String[] {}; } int from = 0; int end = string.length(); for ( int i = from; i < end; i += 1 ) { boolean found = string.charAt( i ) == separator; if ( found && escape && i > 0 ) { found &= string.charAt( i - 1 ) != '\\'; } if ( found ) { // OK, we found a separator, the string to add to the list // is [from, i[ list.add( NVL( string.substring( from, i ), "" ) ); from = i + 1; } } // Wait, if the string didn't end with a separator, we still have information at the end of the string... // In our example that would be "d"... if ( from + 1 <= string.length() ) { list.add( NVL( string.substring( from, string.length() ), "" ) ); } return list.toArray( new String[list.size()] ); } /** * Convert strings separated by a string into an array of strings. * <p> * <code> * Example /a/b/c --> new String[] { a, b, c } * </code> * * @param path * The string to split * @param separator * The separator used. * @return the string split into an array of strings */ public static String[] splitPath( String path, String separator ) { // // Example /a/b/c --> new String[] { a, b, c } // // Make sure training slashes are removed // // Example /a/b/c/ --> new String[] { a, b, c } // // Check for empty paths... // if ( path == null || path.length() == 0 || path.equals( separator ) ) { return new String[] {}; } // lose trailing separators // while ( path.endsWith( separator ) ) { path = path.substring( 0, path.length() - 1 ); } int sepLen = separator.length(); int nr_separators = 1; int from = path.startsWith( separator ) ? sepLen : 0; for ( int i = from; i < path.length(); i += sepLen ) { if ( path.substring( i, i + sepLen ).equalsIgnoreCase( separator ) ) { nr_separators++; } } String[] spath = new String[nr_separators]; int nr = 0; for ( int i = from; i < path.length(); i += sepLen ) { if ( path.substring( i, i + sepLen ).equalsIgnoreCase( separator ) ) { spath[nr] = path.substring( from, i ); nr++; from = i + sepLen; } } if ( nr < spath.length ) { spath[nr] = path.substring( from ); } // // a --> { a } // if ( spath.length == 0 && path.length() > 0 ) { spath = new String[] { path }; } return spath; } /** * Split the given string using the given delimiter and enclosure strings. * * The delimiter and enclosures are not regular expressions (regexes); rather they are literal strings that will be * quoted so as not to be treated like regexes. * * This method expects that the data contains an even number of enclosure strings in the input; otherwise the results * are undefined * * @param stringToSplit * the String to split * @param delimiter * the delimiter string * @param enclosure * the enclosure string * @return an array of strings split on the delimiter (ignoring those in enclosures), or null if the string to split * is null. */ public static String[] splitString( String stringToSplit, String delimiter, String enclosure ) { return splitString( stringToSplit, delimiter, enclosure, false ); } /** * Split the given string using the given delimiter and enclosure strings. * * The delimiter and enclosures are not regular expressions (regexes); rather they are literal strings that will be * quoted so as not to be treated like regexes. * * This method expects that the data contains an even number of enclosure strings in the input; otherwise the results * are undefined * * @param stringToSplit * the String to split * @param delimiter * the delimiter string * @param enclosure * the enclosure string * @param removeEnclosure * removes enclosure from split result * @return an array of strings split on the delimiter (ignoring those in enclosures), or null if the string to split * is null. */ public static String[] splitString( String stringToSplit, String delimiter, String enclosure, boolean removeEnclosure ) { ArrayList<String> splitList = null; // Handle "bad input" cases if ( stringToSplit == null ) { return null; } if ( delimiter == null ) { return ( new String[] { stringToSplit } ); } // Split the string on the delimiter, we'll build the "real" results from the partial results String[] delimiterSplit = stringToSplit.split( Pattern.quote( delimiter ) ); // At this point, if the enclosure is null or empty, we will return the delimiter split if ( Utils.isEmpty( enclosure ) ) { return delimiterSplit; } // Keep track of partial splits and concatenate them into a legit split StringBuilder concatSplit = null; if ( delimiterSplit != null && delimiterSplit.length > 0 ) { // We'll have at least one result so create the result list object splitList = new ArrayList<String>(); // Proceed through the partial splits, concatenating if the splits are within the enclosure for ( String currentSplit : delimiterSplit ) { if ( !currentSplit.contains( enclosure ) ) { // If we are currently concatenating a split, we are inside an enclosure. Since this // split doesn't contain an enclosure, we can concatenate it (with a delimiter in front). // If we're not concatenating, the split is fine so add it to the result list. if ( concatSplit != null ) { concatSplit.append( delimiter ); concatSplit.append( currentSplit ); } else { splitList.add( currentSplit ); } } else { // Find number of enclosures in the split, and whether that number is odd or even. int numEnclosures = StringUtils.countMatches( currentSplit, enclosure ); boolean oddNumberOfEnclosures = ( numEnclosures % 2 != 0 ); boolean addSplit = false; // This split contains an enclosure, so either start or finish concatenating if ( concatSplit == null ) { concatSplit = new StringBuilder( currentSplit ); // start concatenation addSplit = !oddNumberOfEnclosures; } else { // Check to make sure a new enclosure hasn't started within this split. This method expects // that there are no non-delimiter characters between a delimiter and a starting enclosure. // At this point in the code, the split shouldn't start with the enclosure, so add a delimiter concatSplit.append( delimiter ); // Add the current split to the concatenated split concatSplit.append( currentSplit ); // If the number of enclosures is odd, the enclosure is closed so add the split to the list // and reset the "concatSplit" buffer. Otherwise continue addSplit = oddNumberOfEnclosures; } if ( addSplit ) { String splitResult = concatSplit.toString(); //remove enclosure from resulting split if ( removeEnclosure ) { splitResult = removeEnclosure( splitResult, enclosure ); } splitList.add( splitResult ); concatSplit = null; addSplit = false; } } } } // Return list as array return splitList.toArray( new String[splitList.size()] ); } private static String removeEnclosure( String stringToSplit, String enclosure ) { int firstIndex = stringToSplit.indexOf( enclosure ); int lastIndex = stringToSplit.lastIndexOf( enclosure ); if ( firstIndex == lastIndex ) { return stringToSplit; } StrBuilder strBuilder = new StrBuilder( stringToSplit ); strBuilder.replace( firstIndex, enclosure.length() + firstIndex, "" ); strBuilder.replace( lastIndex - enclosure.length(), lastIndex, "" ); return strBuilder.toString(); } /** * Sorts the array of Strings, determines the uniquely occurring strings. * * @param strings * the array that you want to do a distinct on * @return a sorted array of uniquely occurring strings */ public static String[] getDistinctStrings( String[] strings ) { if ( strings == null ) { return null; } if ( strings.length == 0 ) { return new String[] {}; } String[] sorted = sortStrings( strings ); List<String> result = new ArrayList<String>(); String previous = ""; for ( int i = 0; i < sorted.length; i++ ) { if ( !sorted[i].equalsIgnoreCase( previous ) ) { result.add( sorted[i] ); } previous = sorted[i]; } return result.toArray( new String[result.size()] ); } /** * Returns a string of the stack trace of the specified exception */ public static String getStackTracker( Throwable e ) { return getClassicStackTrace( e ); } public static String getClassicStackTrace( Throwable e ) { StringWriter stringWriter = new StringWriter(); PrintWriter printWriter = new PrintWriter( stringWriter ); e.printStackTrace( printWriter ); String string = stringWriter.toString(); try { stringWriter.close(); } catch ( IOException ioe ) { // is this really required? } return string; } public static String getCustomStackTrace( Throwable aThrowable ) { final StringBuilder result = new StringBuilder(); String errorMessage = aThrowable.toString(); result.append( errorMessage ); if ( !errorMessage.contains( Const.CR ) ) { result.append( CR ); } // add each element of the stack trace // for ( StackTraceElement element : aThrowable.getStackTrace() ) { result.append( element ); result.append( CR ); } return result.toString(); } /** * Check if the string supplied is empty. A String is empty when it is null or when the length is 0 * * @param val * The value to check * @return true if the string supplied is empty * @deprecated * @see org.pentaho.di.core.util.Utils.isEmpty */ @Deprecated public static boolean isEmpty( String val ) { return Utils.isEmpty( val ); } /** * Check if the stringBuffer supplied is empty. A StringBuffer is empty when it is null or when the length is 0 * * @param string * The stringBuffer to check * @return true if the stringBuffer supplied is empty * @deprecated * @see org.pentaho.di.core.util.Utils.isEmpty */ @Deprecated public static boolean isEmpty( StringBuffer val ) { return Utils.isEmpty( val ); } /** * Check if the string array supplied is empty. A String array is empty when it is null or when the number of elements * is 0 * * @param strings * The string array to check * @return true if the string array supplied is empty * @deprecated * @see org.pentaho.di.core.util.Utils.isEmpty */ @Deprecated public static boolean isEmpty( String[] vals ) { return Utils.isEmpty( vals ); } /** * Check if the CharSequence supplied is empty. A CharSequence is empty when it is null or when the length is 0 * * @param string * The stringBuffer to check * @return true if the stringBuffer supplied is empty * @deprecated * @see org.pentaho.di.core.util.Utils.isEmpty */ @Deprecated public static boolean isEmpty( CharSequence val ) { return Utils.isEmpty( val ); } /** * Check if the CharSequence array supplied is empty. A CharSequence array is empty when it is null or when the number of elements * is 0 * * @param strings * The string array to check * @return true if the string array supplied is empty * @deprecated * @see org.pentaho.di.core.util.Utils.isEmpty */ @Deprecated public static boolean isEmpty( CharSequence[] vals ) { return Utils.isEmpty( vals ); } /** * Check if the array supplied is empty. An array is empty when it is null or when the length is 0 * * @param array * The array to check * @return true if the array supplied is empty * @deprecated * @see org.pentaho.di.core.util.Utils.isEmpty */ @Deprecated public static boolean isEmpty( Object[] array ) { return Utils.isEmpty( array ); } /** * Check if the list supplied is empty. An array is empty when it is null or when the length is 0 * * @param list * the list to check * @return true if the supplied list is empty * @deprecated * @see org.pentaho.di.core.util.Utils.isEmpty */ @Deprecated public static boolean isEmpty( List<?> list ) { return Utils.isEmpty( list ); } /** * @return a new ClassLoader */ public static ClassLoader createNewClassLoader() throws KettleException { try { // Nothing really in URL, everything is in scope. URL[] urls = new URL[] {}; URLClassLoader ucl = new URLClassLoader( urls ); return ucl; } catch ( Exception e ) { throw new KettleException( "Unexpected error during classloader creation", e ); } } /** * Utility class for use in JavaScript to create a new byte array. This is surprisingly difficult to do in JavaScript. * * @return a new java byte array */ public static byte[] createByteArray( int size ) { return new byte[size]; } /** * Sets the first character of each word in upper-case. * * @param string * The strings to convert to initcap * @return the input string but with the first character of each word converted to upper-case. */ public static String initCap( String string ) { StringBuilder change = new StringBuilder( string ); boolean new_word; int i; char lower, upper, ch; new_word = true; for ( i = 0; i < string.length(); i++ ) { lower = change.substring( i, i + 1 ).toLowerCase().charAt( 0 ); // Lowercase is default. upper = change.substring( i, i + 1 ).toUpperCase().charAt( 0 ); // Uppercase for new words. ch = upper; if ( new_word ) { change.setCharAt( i, upper ); } else { change.setCharAt( i, lower ); } new_word = false; // Cast to (int) is required for extended characters (SB) if ( !Character.isLetterOrDigit( (int) ch ) && ch != '_' ) { new_word = true; } } return change.toString(); } /** * Create a valid filename using a name We remove all special characters, spaces, etc. * * @param name * The name to use as a base for the filename * @return a valid filename */ public static String createFilename( String name ) { StringBuilder filename = new StringBuilder(); for ( int i = 0; i < name.length(); i++ ) { char c = name.charAt( i ); if ( Character.isUnicodeIdentifierPart( c ) ) { filename.append( c ); } else if ( Character.isWhitespace( c ) ) { filename.append( '_' ); } } return filename.toString().toLowerCase(); } public static String createFilename( String directory, String name, String extension ) { if ( directory.endsWith( Const.FILE_SEPARATOR ) ) { return directory + createFilename( name ) + extension; } else { return directory + Const.FILE_SEPARATOR + createFilename( name ) + extension; } } public static String createName( String filename ) { if ( Utils.isEmpty( filename ) ) { return filename; } String pureFilename = filenameOnly( filename ); if ( pureFilename.endsWith( ".ktr" ) || pureFilename.endsWith( ".kjb" ) || pureFilename.endsWith( ".xml" ) ) { pureFilename = pureFilename.substring( 0, pureFilename.length() - 4 ); } StringBuilder sb = new StringBuilder(); for ( int i = 0; i < pureFilename.length(); i++ ) { char c = pureFilename.charAt( i ); if ( Character.isUnicodeIdentifierPart( c ) ) { sb.append( c ); } else if ( Character.isWhitespace( c ) ) { sb.append( ' ' ); } else if ( c == '-' ) { sb.append( c ); } } return sb.toString(); } /** * <p> * Returns the pure filename of a filename with full path. E.g. if passed parameter is * <code>/opt/tomcat/logs/catalina.out</code> this method returns <code>catalina.out</code>. The method works with the * Environment variable <i>System.getProperty("file.separator")</i>, so on linux/Unix it will check for the last * occurrence of a frontslash, on windows for the last occurrence of a backslash. * </p> * * <p> * To make this OS independent, the method could check for the last occurrence of a frontslash and backslash and use * the higher value of both. Should work, since these characters aren't allowed in filenames on neither OS types (or * said differently: Neither linux nor windows can carry frontslashes OR backslashes in filenames). Just a suggestion * of an improvement ... * </p> * * @param sFullPath * @return */ public static String filenameOnly( String sFullPath ) { if ( Utils.isEmpty( sFullPath ) ) { return sFullPath; } int idx = sFullPath.lastIndexOf( FILE_SEPARATOR ); if ( idx != -1 ) { return sFullPath.substring( idx + 1 ); } else { idx = sFullPath.lastIndexOf( '/' ); // URL, VFS/**/ if ( idx != -1 ) { return sFullPath.substring( idx + 1 ); } else { return sFullPath; } } } /** * Returning the localized date conversion formats. They get created once on first request. * * @return */ public static String[] getDateFormats() { if ( dateFormats == null ) { int dateFormatsCount = toInt( BaseMessages.getString( PKG, "Const.DateFormat.Count" ), 0 ); dateFormats = new String[dateFormatsCount]; for ( int i = 1; i <= dateFormatsCount; i++ ) { dateFormats[i - 1] = BaseMessages.getString( PKG, "Const.DateFormat" + Integer.toString( i ) ); } } return dateFormats; } /** * Returning the localized number conversion formats. They get created once on first request. * * @return */ public static String[] getNumberFormats() { if ( numberFormats == null ) { int numberFormatsCount = toInt( BaseMessages.getString( PKG, "Const.NumberFormat.Count" ), 0 ); numberFormats = new String[numberFormatsCount + 1]; numberFormats[0] = DEFAULT_NUMBER_FORMAT; for ( int i = 1; i <= numberFormatsCount; i++ ) { numberFormats[i] = BaseMessages.getString( PKG, "Const.NumberFormat" + Integer.toString( i ) ); } } return numberFormats; } /** * @return An array of all default conversion formats, to be used in dialogs etc. */ public static String[] getConversionFormats() { String[] dats = Const.getDateFormats(); String[] nums = Const.getNumberFormats(); int totsize = dats.length + nums.length; String[] formats = new String[totsize]; for ( int x = 0; x < dats.length; x++ ) { formats[x] = dats[x]; } for ( int x = 0; x < nums.length; x++ ) { formats[dats.length + x] = nums[x]; } return formats; } public static String[] getTransformationAndJobFilterNames() { if ( STRING_TRANS_AND_JOB_FILTER_NAMES == null ) { STRING_TRANS_AND_JOB_FILTER_NAMES = new String[] { BaseMessages.getString( PKG, "Const.FileFilter.TransformationJob" ), BaseMessages.getString( PKG, "Const.FileFilter.Transformations" ), BaseMessages.getString( PKG, "Const.FileFilter.Jobs" ), BaseMessages.getString( PKG, "Const.FileFilter.XML" ), BaseMessages.getString( PKG, "Const.FileFilter.All" ) }; } return STRING_TRANS_AND_JOB_FILTER_NAMES; } public static String[] getTransformationFilterNames() { if ( STRING_TRANS_FILTER_NAMES == null ) { STRING_TRANS_FILTER_NAMES = new String[] { BaseMessages.getString( PKG, "Const.FileFilter.Transformations" ), BaseMessages.getString( PKG, "Const.FileFilter.XML" ), BaseMessages.getString( PKG, "Const.FileFilter.All" ) }; } return STRING_TRANS_FILTER_NAMES; } public static String[] getJobFilterNames() { if ( STRING_JOB_FILTER_NAMES == null ) { STRING_JOB_FILTER_NAMES = new String[] { BaseMessages.getString( PKG, "Const.FileFilter.Jobs" ), BaseMessages.getString( PKG, "Const.FileFilter.XML" ), BaseMessages.getString( PKG, "Const.FileFilter.All" ) }; } return STRING_JOB_FILTER_NAMES; } /** * Return the current time as nano-seconds. * * @return time as nano-seconds. */ public static long nanoTime() { return new Date().getTime() * 1000; } /** * Return the input string trimmed as specified. * * @param string * String to be trimmed * @param trimType * Type of trimming * * @return Trimmed string. */ public static String trimToType( String string, int trimType ) { switch ( trimType ) { case ValueMetaInterface.TRIM_TYPE_BOTH: return trim( string ); case ValueMetaInterface.TRIM_TYPE_LEFT: return ltrim( string ); case ValueMetaInterface.TRIM_TYPE_RIGHT: return rtrim( string ); case ValueMetaInterface.TRIM_TYPE_NONE: default: return string; } } /** * implemented to help prevent errors in matching up pluggable LAF directories and paths/files eliminating malformed * URLs - duplicate file separators or missing file separators. * * @param dir * @param file * @return concatenated string representing a file url */ public static String safeAppendDirectory( String dir, String file ) { boolean dirHasSeparator = ( ( dir.lastIndexOf( FILE_SEPARATOR ) ) == dir.length() - 1 ); boolean fileHasSeparator = ( file.indexOf( FILE_SEPARATOR ) == 0 ); if ( ( dirHasSeparator && !fileHasSeparator ) || ( !dirHasSeparator && fileHasSeparator ) ) { return dir + file; } if ( dirHasSeparator && fileHasSeparator ) { return dir + file.substring( 1 ); } return dir + FILE_SEPARATOR + file; } /** * Create an array of Strings consisting of spaces. The index of a String in the array determines the number of spaces * in that string. * * @return array of 'space' Strings. */ public static String[] getEmptyPaddedStrings() { if ( emptyPaddedSpacesStrings == null ) { emptyPaddedSpacesStrings = new String[250]; for ( int i = 0; i < emptyPaddedSpacesStrings.length; i++ ) { emptyPaddedSpacesStrings[i] = rightPad( "", i ); } } return emptyPaddedSpacesStrings; } /** * Return the percentage of free memory for this JVM. * * @return Percentage of free memory. */ public static int getPercentageFreeMemory() { Runtime runtime = Runtime.getRuntime(); long maxMemory = runtime.maxMemory(); long allocatedMemory = runtime.totalMemory(); long freeMemory = runtime.freeMemory(); long totalFreeMemory = ( freeMemory + ( maxMemory - allocatedMemory ) ); return (int) Math.round( 100 * (double) totalFreeMemory / maxMemory ); } /** * Return non digits only. * * @return non digits in a string. */ public static String removeDigits( String input ) { if ( Utils.isEmpty( input ) ) { return null; } StringBuilder digitsOnly = new StringBuilder(); char c; for ( int i = 0; i < input.length(); i++ ) { c = input.charAt( i ); if ( !Character.isDigit( c ) ) { digitsOnly.append( c ); } } return digitsOnly.toString(); } /** * Return digits only. * * @return digits in a string. */ public static String getDigitsOnly( String input ) { if ( Utils.isEmpty( input ) ) { return null; } StringBuilder digitsOnly = new StringBuilder(); char c; for ( int i = 0; i < input.length(); i++ ) { c = input.charAt( i ); if ( Character.isDigit( c ) ) { digitsOnly.append( c ); } } return digitsOnly.toString(); } /** * Remove time from a date. * * @return a date without hour. */ public static Date removeTimeFromDate( Date input ) { if ( input == null ) { return null; } // Get an instance of the Calendar. Calendar calendar = Calendar.getInstance(); // Make sure the calendar will not perform automatic correction. calendar.setLenient( false ); // Set the time of the calendar to the given date. calendar.setTime( input ); // Remove the hours, minutes, seconds and milliseconds. calendar.set( Calendar.HOUR_OF_DAY, 0 ); calendar.set( Calendar.MINUTE, 0 ); calendar.set( Calendar.SECOND, 0 ); calendar.set( Calendar.MILLISECOND, 0 ); // Return the date again. return calendar.getTime(); } /** * Escape XML content. i.e. replace characters with &values; * * @param content * content * @return escaped content */ public static String escapeXML( String content ) { if ( Utils.isEmpty( content ) ) { return content; } return StringEscapeUtils.escapeXml( content ); } /** * Escape HTML content. i.e. replace characters with &values; * * @param content * content * @return escaped content */ public static String escapeHtml( String content ) { if ( Utils.isEmpty( content ) ) { return content; } return StringEscapeUtils.escapeHtml( content ); } /** * UnEscape HTML content. i.e. replace characters with &values; * * @param content * content * @return unescaped content */ public static String unEscapeHtml( String content ) { if ( Utils.isEmpty( content ) ) { return content; } return StringEscapeUtils.unescapeHtml( content ); } /** * UnEscape XML content. i.e. replace characters with &values; * * @param content * content * @return unescaped content */ public static String unEscapeXml( String content ) { if ( Utils.isEmpty( content ) ) { return content; } return StringEscapeUtils.unescapeXml( content ); } /** * Escape SQL content. i.e. replace characters with &values; * * @param content * content * @return escaped content */ public static String escapeSQL( String content ) { if ( Utils.isEmpty( content ) ) { return content; } return StringEscapeUtils.escapeSql( content ); } /** * Remove CR / LF from String - Better performance version * - Doesn't NPE * - 40 times faster on an empty string * - 2 times faster on a mixed string * - 25% faster on 2 char string with only CRLF in it * * @param in * input * @return cleaned string */ public static String removeCRLF( String in ) { if ( ( in != null ) && ( in.length() > 0 ) ) { int inLen = in.length(), posn = 0; char[] tmp = new char[ inLen ]; char ch; for ( int i = 0; i < inLen; i++ ) { ch = in.charAt( i ); if ( ( ch != '\n' && ch != '\r' ) ) { tmp[posn] = ch; posn++; } } return new String( tmp, 0, posn ); } else { return ""; } } /** * Remove Character from String - Better performance version * - Doesn't NPE * - 40 times faster on an empty string * - 2 times faster on a mixed string * - 25% faster on 2 char string with only CR/LF/TAB in it * * @param in * input * @return cleaned string */ public static String removeChar( String in, char badChar ) { if ( ( in != null ) && ( in.length() > 0 ) ) { int inLen = in.length(), posn = 0; char[] tmp = new char[ inLen ]; char ch; for ( int i = 0; i < inLen; i++ ) { ch = in.charAt( i ); if ( ch != badChar ) { tmp[posn] = ch; posn++; } } return new String( tmp, 0, posn ); } else { return ""; } } /** * Remove CR / LF from String * * @param in * input * @return cleaned string */ public static String removeCR( String in ) { return removeChar( in, '\n' ); } // removeCR /** * Remove CR / LF from String * * @param in * input * @return cleaned string */ public static String removeLF( String in ) { return removeChar( in, '\r' ); } // removeCRLF /** * Remove horizontal tab from string * * @param in * input * @return cleaned string */ public static String removeTAB( String in ) { return removeChar( in, '\t' ); } /** * Add time to an input date * * @param input * the date * @param time * the time to add (in string) * @param DateFormat * the time format * @return date = input + time */ public static Date addTimeToDate( Date input, String time, String DateFormat ) throws Exception { if ( Utils.isEmpty( time ) ) { return input; } if ( input == null ) { return null; } String dateformatString = NVL( DateFormat, "HH:mm:ss" ); int t = decodeTime( time, dateformatString ); return new Date( input.getTime() + t ); } // Decodes a time value in specified date format and returns it as milliseconds since midnight. public static int decodeTime( String s, String DateFormat ) throws Exception { SimpleDateFormat f = new SimpleDateFormat( DateFormat ); TimeZone utcTimeZone = TimeZone.getTimeZone( "UTC" ); f.setTimeZone( utcTimeZone ); f.setLenient( false ); ParsePosition p = new ParsePosition( 0 ); Date d = f.parse( s, p ); if ( d == null ) { throw new Exception( "Invalid time value " + DateFormat + ": \"" + s + "\"." ); } return (int) d.getTime(); } /** * Get the number of occurrences of searchFor in string. * * @param string * String to be searched * @param searchFor * to be counted string * @return number of occurrences */ public static int getOccurenceString( String string, String searchFor ) { if ( string == null || string.length() == 0 ) { return 0; } int counter = 0; int len = searchFor.length(); if ( len > 0 ) { int start = string.indexOf( searchFor ); while ( start != -1 ) { counter++; start = string.indexOf( searchFor, start + len ); } } return counter; } public static String[] GetAvailableFontNames() { GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); Font[] fonts = ge.getAllFonts(); String[] FontName = new String[fonts.length]; for ( int i = 0; i < fonts.length; i++ ) { FontName[i] = fonts[i].getFontName(); } return FontName; } public static String getKettlePropertiesFileHeader() { StringBuilder out = new StringBuilder(); out.append( BaseMessages.getString( PKG, "Props.Kettle.Properties.Sample.Line01", BuildVersion .getInstance().getVersion() ) + CR ); out.append( BaseMessages.getString( PKG, "Props.Kettle.Properties.Sample.Line02" ) + CR ); out.append( BaseMessages.getString( PKG, "Props.Kettle.Properties.Sample.Line03" ) + CR ); out.append( BaseMessages.getString( PKG, "Props.Kettle.Properties.Sample.Line04" ) + CR ); out.append( BaseMessages.getString( PKG, "Props.Kettle.Properties.Sample.Line05" ) + CR ); out.append( BaseMessages.getString( PKG, "Props.Kettle.Properties.Sample.Line06" ) + CR ); out.append( BaseMessages.getString( PKG, "Props.Kettle.Properties.Sample.Line07" ) + CR ); out.append( BaseMessages.getString( PKG, "Props.Kettle.Properties.Sample.Line08" ) + CR ); out.append( BaseMessages.getString( PKG, "Props.Kettle.Properties.Sample.Line09" ) + CR ); out.append( BaseMessages.getString( PKG, "Props.Kettle.Properties.Sample.Line10" ) + CR ); return out.toString(); } /** * Mask XML content. i.e. protect with CDATA; * * @param content * content * @return protected content */ public static String protectXMLCDATA( String content ) { if ( Utils.isEmpty( content ) ) { return content; } return "<![CDATA[" + content + "]]>"; } /** * Get the number of occurrences of searchFor in string. * * @param string * String to be searched * @param searchFor * to be counted string * @return number of occurrences */ public static int getOcuranceString( String string, String searchFor ) { if ( string == null || string.length() == 0 ) { return 0; } Pattern p = Pattern.compile( searchFor ); Matcher m = p.matcher( string ); int count = 0; while ( m.find() ) { ++count; } return count; } /** * Mask XML content. i.e. replace characters with &values; * * @param content * content * @return masked content */ public static String escapeXml( String content ) { if ( Utils.isEmpty( content ) ) { return content; } return StringEscapeUtils.escapeXml( content ); } /** * New method avoids string concatenation is between 20% and > 2000% faster * depending on length of the string to pad, and the size to pad it to. * For larger amounts to pad, (e.g. pad a 4 character string out to 20 places) * this is orders of magnitude faster. * * @param valueToPad * the string to pad * @param filler * the pad string to fill with * @param size * the size to pad to * @return * the new string, padded to the left * * Note - The original method was flawed in a few cases: * * 1- The filler could be a string of any length - and the returned * string was not necessarily limited to size. So a 3 character pad * of an 11 character string could end up being 17 characters long. * 2- For a pad of zero characters ("") the former method would enter * an infinite loop. * 3- For a null pad, it would throw an NPE * 4- For a null valueToPad, it would throw an NPE */ public static String Lpad( String valueToPad, String filler, int size ) { if ( ( size == 0 ) || ( valueToPad == null ) || ( filler == null ) ) { return valueToPad; } int vSize = valueToPad.length(); int fSize = filler.length(); // This next if ensures previous behavior, but prevents infinite loop // if "" is passed in as a filler. if ( ( vSize >= size ) || ( fSize == 0 ) ) { return valueToPad; } int tgt = ( size - vSize ); StringBuilder sb = new StringBuilder( size ); sb.append( filler ); while ( sb.length() < tgt ) { // instead of adding one character at a time, this // is exponential - much fewer times in loop sb.append( sb ); } sb.append( valueToPad ); return sb.substring( Math.max( 0, sb.length() - size ) ); // this makes sure you have the right size string returned. } /** * New method avoids string concatenation is between 50% and > 2000% faster * depending on length of the string to pad, and the size to pad it to. * For larger amounts to pad, (e.g. pad a 4 character string out to 20 places) * this is orders of magnitude faster. * * @param valueToPad * the string to pad * @param filler * the pad string to fill with * @param size * the size to pad to * @return * The string, padded to the right * * 1- The filler can still be a string of any length - and the returned * string was not necessarily limited to size. So a 3 character pad * of an 11 character string with a size of 15 could end up being 17 * characters long (instead of the "asked for 15"). * 2- For a pad of zero characters ("") the former method would enter * an infinite loop. * 3- For a null pad, it would throw an NPE * 4- For a null valueToPad, it would throw an NPE */ public static String Rpad( String valueToPad, String filler, int size ) { if ( ( size == 0 ) || ( valueToPad == null ) || ( filler == null ) ) { return valueToPad; } int vSize = valueToPad.length(); int fSize = filler.length(); // This next if ensures previous behavior, but prevents infinite loop // if "" is passed in as a filler. if ( ( vSize >= size ) || ( fSize == 0 ) ) { return valueToPad; } int tgt = ( size - vSize ); StringBuilder sb1 = new StringBuilder( size ); sb1.append( filler ); while ( sb1.length() < tgt ) { // instead of adding one character at a time, this // is exponential - much fewer times in loop sb1.append( sb1 ); } StringBuilder sb = new StringBuilder( valueToPad ); sb.append( sb1 ); return sb.substring( 0, size ); } public static boolean classIsOrExtends( Class<?> clazz, Class<?> superClass ) { if ( clazz.equals( Object.class ) ) { return false; } return clazz.equals( superClass ) || classIsOrExtends( clazz.getSuperclass(), superClass ); } public static String getDeprecatedPrefix() { return " " + BaseMessages.getString( PKG, "Const.Deprecated" ); } }