package tap.config;
/*
* This file is part of TAPLibrary.
*
* TAPLibrary is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TAPLibrary is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with TAPLibrary. If not, see
* This class implements the Design Pattern "Utility": no instance of this * class can be created, it can not be extended, and it must be used only * thanks to its static classes and attributes. *
* * @author Grégory Mantelet (CDS;ARI) * @version 2.4 (08/2020) * @since 2.0 */ public final class TAPConfiguration { /** Name of the initial parameter to set in the WEB-INF/web.xml file * in order to specify the location and the name of the TAP configuration * file to load. */ public final static String TAP_CONF_PARAMETER = "tapconf"; /** Default TAP configuration file. This file is research automatically * if none is specified in the WEB-INF/web.xml initial parameter * {@value #TAP_CONF_PARAMETER}. */ public final static String DEFAULT_TAP_CONF_FILE = "tap.properties"; /* FILE MANAGER KEYS */ /** Name/Key of the property setting the file manager to use in the TAP * service. */ public final static String KEY_FILE_MANAGER = "file_manager"; /** Value of the property {@value #KEY_FILE_MANAGER} specifying a local file * manager. */ public final static String VALUE_LOCAL = "local"; /** Default value of the property {@value #KEY_FILE_MANAGER}: * {@value #DEFAULT_FILE_MANAGER}. */ public final static String DEFAULT_FILE_MANAGER = VALUE_LOCAL; /** Name/Key of the property setting the local root directory where all TAP * files must be stored. This property is used only if * {@value #KEY_FILE_MANAGER} is set to {@link #VALUE_LOCAL}. */ public final static String KEY_FILE_ROOT_PATH = "file_root_path"; /** Name/Key of the property indicating whether the jobs must be saved by * user or not. If yes, there will be one directory per user. Otherwise, all * jobs are backuped in the same directory (generally * {@value #KEY_FILE_ROOT_PATH}). */ public final static String KEY_DIRECTORY_PER_USER = "directory_per_user"; /** Default value of the property {@link #KEY_DIRECTORY_PER_USER}: * {@value #DEFAULT_DIRECTORY_PER_USER}. */ public final static boolean DEFAULT_DIRECTORY_PER_USER = false; /** Name/Key of the property indicating whether the user directories (in * which jobs of the user are backuped) must be gathered in less * directories. If yes, the groups are generally made using the alphabetic * order. The idea is to reduce the number of apparent directories and to * easier the research of a user directory. */ public final static String KEY_GROUP_USER_DIRECTORIES = "group_user_directories"; /** Default value of the property {@value #KEY_GROUP_USER_DIRECTORIES}: * {@value #DEFAULT_GROUP_USER_DIRECTORIES}. */ public final static boolean DEFAULT_GROUP_USER_DIRECTORIES = false; /** Name/Key of the property specifying the default period (in seconds) * while a job must remain on the server. This value is set automatically to * any job whose the retention period has never been specified by the user. */ public final static String KEY_DEFAULT_RETENTION_PERIOD = "default_retention_period"; /** Name/Key of the property specifying the maximum period (in seconds) * while a job can remain on the server. */ public final static String KEY_MAX_RETENTION_PERIOD = "max_retention_period"; /** Default value of the properties {@value #KEY_DEFAULT_RETENTION_PERIOD} * and {@value #KEY_MAX_RETENTION_PERIOD}: * {@value #DEFAULT_RETENTION_PERIOD}. */ public final static int DEFAULT_RETENTION_PERIOD = 0; /* LOG KEYS */ /** Name/Key of the property specifying the logger to use. * By default, {@link tap.log.DefaultTAPLog} is used. */ public final static String KEY_LOGGER = "logger"; /** Default value of the property {@value #KEY_LOGGER}: * {@value #DEFAULT_LOGGER}. */ public final static String DEFAULT_LOGGER = "default"; /** Name/Key of the property specifying the minimum type of messages * (i.e. DEBUG, INFO, WARNING, ERROR, FATAL) that must be logged by the * default logger. *By default all messages are logged...which is equivalent to set this * property to "DEBUG".
*Note: If {@value #KEY_LOGGER} is set to a value different * from {@value #DEFAULT_LOGGER}, this property is ignored.
*/ public final static String KEY_MIN_LOG_LEVEL = "min_log_level"; /** Name/Key of the property specifying the frequency of the log file * rotation to set in the default logger. *By default the log rotation occurs every day at midnight.
*Note: If {@value #KEY_LOGGER} is set to a value different * from {@value #DEFAULT_LOGGER}, this property is ignored.
*/ public final static String KEY_LOG_ROTATION = "log_rotation"; /** SLF4J logger value: {@value #SLF4J_LOGGER}. * @since 2.3 */ public final static String SLF4J_LOGGER = "slf4j"; /* UWS BACKUP */ /** Name/Key of the property specifying the frequency (in milliseconds) of * jobs backup. This property accepts three types of value: "never" * (default), "user_action" (the backup of a job is done when it is * modified), or a numeric positive value (expressed in milliseconds). */ public final static String KEY_BACKUP_FREQUENCY = "backup_frequency"; /** Value of the property {@value #KEY_BACKUP_FREQUENCY} indicating that * jobs should never be backuped. */ public final static String VALUE_NEVER = "never"; /** Value of the property {@value #KEY_BACKUP_FREQUENCY} indicating that job * backup should occur only when the user * creates or modifies one of his jobs. This value can be used ONLY IF * {@value #KEY_BACKUP_BY_USER} is "true". */ public final static String VALUE_USER_ACTION = "user_action"; /** Default value of the property {@link #KEY_BACKUP_FREQUENCY}: * {@link #DEFAULT_BACKUP_FREQUENCY}. */ public final static long DEFAULT_BACKUP_FREQUENCY = DefaultTAPBackupManager.MANUAL; // = "never" => no UWS backup manager /** Name/Key of the property indicating whether there should be one backup * file per user or one file for all. */ public final static String KEY_BACKUP_BY_USER = "backup_by_user"; /** Default value of the property {@value #KEY_BACKUP_BY_USER}: * {@value #DEFAULT_BACKUP_BY_USER}. This property can be enabled only if a * user identification method is provided. */ public final static boolean DEFAULT_BACKUP_BY_USER = false; /* ASYNCHRONOUS JOBS */ /** Name/Key of the property specifying the maximum number of asynchronous * jobs that can run simultaneously. A negative or null value means * "no limit". */ public final static String KEY_MAX_ASYNC_JOBS = "max_async_jobs"; /** Default value of the property {@value #KEY_MAX_ASYNC_JOBS}: * {@value #DEFAULT_MAX_ASYNC_JOBS}. */ public final static int DEFAULT_MAX_ASYNC_JOBS = 0; /* EXECUTION DURATION */ /** Name/Key of the property specifying the default execution duration (in * milliseconds) set automatically to a job if none has been specified by * the user. */ public final static String KEY_DEFAULT_EXECUTION_DURATION = "default_execution_duration"; /** Name/Key of the property specifying the execution duration (in * milliseconds) of SYNCHRONOUS queries. * @since 2.4 */ public final static String KEY_SYNC_EXECUTION_DURATION = "sync_execution_duration"; /** Name/Key of the property specifying the maximum execution duration * (in milliseconds) that can be set on a job. */ public final static String KEY_MAX_EXECUTION_DURATION = "max_execution_duration"; /** Default value of the property {@value #KEY_DEFAULT_EXECUTION_DURATION}, * {@value #KEY_SYNC_EXECUTION_DURATION} and * {@value #KEY_MAX_EXECUTION_DURATION}: * {@value #DEFAULT_EXECUTION_DURATION}. */ public final static int DEFAULT_EXECUTION_DURATION = 0; /* DATABASE KEYS */ /** Name/Key of the property specifying the database access method to use. */ public final static String KEY_DATABASE_ACCESS = "database_access"; /** Value of the property {@value #KEY_DATABASE_ACCESS} to select the * simple JDBC method. */ public final static String VALUE_JDBC = "jdbc"; /** Value of the property {@value #KEY_DATABASE_ACCESS} to access the * database using a DataSource stored in JNDI. */ public final static String VALUE_JNDI = "jndi"; /** Name/Key of the property specifying the ADQL to SQL translator to use. */ public final static String KEY_SQL_TRANSLATOR = "sql_translator"; /** Value of the property {@value #KEY_SQL_TRANSLATOR} to select a * PostgreSQL translator (no support for geometrical functions). */ public final static String VALUE_POSTGRESQL = "postgres"; /** Value of the property {@value #KEY_SQL_TRANSLATOR} to select a PgSphere * translator. */ public final static String VALUE_PGSPHERE = "pgsphere"; /** Value of the property {@value #KEY_SQL_TRANSLATOR} to select an * SQLServer translator. * @since 2.1 */ public final static String VALUE_SQLSERVER = "sqlserver"; /** Value of the property {@value #KEY_SQL_TRANSLATOR} to select a MySQL * translator. * @since 2.1 */ public final static String VALUE_MYSQL = "mysql"; /** Name/Key of the property specifying by how many rows the library should * fetch a query result from the database. This is the fetch size for to * apply for synchronous queries. */ public final static String KEY_SYNC_FETCH_SIZE = "sync_fetch_size"; /** Default value of the property {@value #KEY_SYNC_FETCH_SIZE}: * {@value #DEFAULT_SYNC_FETCH_SIZE}. */ public final static int DEFAULT_SYNC_FETCH_SIZE = 1000; /** Name/Key of the property specifying by how many rows the library should * fetch a query result from the database. This is the fetch size for to * apply for asynchronous queries. */ public final static String KEY_ASYNC_FETCH_SIZE = "async_fetch_size"; /** Default value of the property {@value #KEY_ASYNC_FETCH_SIZE}: {@value #DEFAULT_ASYNC_FETCH_SIZE}. */ public final static int DEFAULT_ASYNC_FETCH_SIZE = 10000; /** Name/Key of the property specifying whether the fixOnFail option is * enabled or not. This option lets automatically fix the input ADQL query * if its tokenization fails. * @since 2.3 */ public final static String KEY_FIX_ON_FAIL = "fix_on_fail"; /** Default value of the property {@link #KEY_FIX_ON_FAIL}: * {@value #DEFAULT_FIX_ON_FAIL}. * @since 2.3 */ public final static boolean DEFAULT_FIX_ON_FAIL = false; /** Name/Key of the property specifying the name of the DataSource into the * JDNI. */ public final static String KEY_DATASOURCE_JNDI_NAME = "datasource_jndi_name"; /** Name/Key of the property specifying the full class name of the JDBC * driver. Alternatively, a shortcut the most known JDBC drivers can be * used. The list of these drivers is stored in {@link #VALUE_JDBC_DRIVERS}. */ public final static String KEY_JDBC_DRIVER = "jdbc_driver"; /** List of the most known JDBC drivers. For the moment this list contains * 4 drivers: oracle ("oracle.jdbc.OracleDriver"), * postgresql ("org.postgresql.Driver"), mysql ("com.mysql.jdbc.Driver"), * sqlite ("org.sqlite.JDBC") and h2 ("org.h2.Driver"). */ public final static HashMapName/Key of the property specifying the content of the
* /examples
endpoint. It can be a file or a URL. If null, the
* TAP service will not have any /examples
endpoint (which is
* optional in the TAP 1.1 standard.
Note: The specified content must be an XHTML+RDFa document whose * the expected syntax is defined either by TAPNotes 1.0 or DALI 1.0.
* @since 2.1 */ public final static String KEY_EXAMPLES = "examples"; /* PROVIDER KEYS */ /** Name/Key of the property specifying the name of the organization/person * providing the TAP service. */ public final static String KEY_PROVIDER_NAME = "provider_name"; /** Name/Key of the property specifying the description of the TAP service. */ public final static String KEY_SERVICE_DESCRIPTION = "service_description"; /* UPLOAD KEYS */ /** Name/Key of the property indicating whether the UPLOAD feature must be * enabled or not. By default, this feature is disabled. */ public final static String KEY_UPLOAD_ENABLED = "upload_enabled"; /** Name/Key of the property specifying the default limit (in rows or bytes) * on the uploaded VOTable(s). * @deprecated Since 2.3, use the property {@value #KEY_MAX_UPLOAD_LIMIT} * instead. */ @Deprecated public final static String KEY_DEFAULT_UPLOAD_LIMIT = "upload_default_db_limit"; /** Name/Key of the property specifying the maximum limit (in rows or bytes) * on the uploaded VOTable(s). */ public final static String KEY_MAX_UPLOAD_LIMIT = "upload_max_db_limit"; /** Default value of the property {@value #KEY_MAX_UPLOAD_LIMIT} = * {@value #DEFAULT_MAX_UPLOAD_LIMIT}. * @since 2.3 */ public final static String DEFAULT_MAX_UPLOAD_LIMIT = "1000000r"; /** Name/Key of the property specifying the maximum size of all VOTable(s) * uploaded in a query. * @deprecated Since 2.3, use the property {@value #KEY_MAX_UPLOAD_LIMIT} * and {@value #KEY_UPLOAD_MAX_REQUEST_SIZE} instead. */ @Deprecated public final static String KEY_UPLOAD_MAX_FILE_SIZE = "upload_max_file_size"; /** Default value of the property {@value #KEY_UPLOAD_MAX_FILE_SIZE} = * {@value #DEFAULT_UPLOAD_MAX_FILE_SIZE}. * @deprecated Since 2.3, use the property {@value #KEY_MAX_UPLOAD_LIMIT} * and {@value #KEY_UPLOAD_MAX_REQUEST_SIZE} instead. */ @Deprecated public final static int DEFAULT_UPLOAD_MAX_FILE_SIZE = Integer.MAX_VALUE; /** Name/Key of the property specifying the maximum size of a whole HTTP * Multipart Request. * @since 2.3 */ public final static String KEY_UPLOAD_MAX_REQUEST_SIZE = "upload_max_request_size"; /** Default value of the property {@value #KEY_UPLOAD_MAX_REQUEST_SIZE} = * {@value #DEFAULT_UPLOAD_MAX_REQUEST_SIZE}. * @since 2.3 */ public final static int DEFAULT_UPLOAD_MAX_REQUEST_SIZE = 250 * 1024 * 1024; /* OUTPUT KEYS */ /** Name/Key of the property specifying the list of all result output * formats to support. By default all formats provided by the library are * allowed. */ public final static String KEY_OUTPUT_FORMATS = "output_formats"; /** Value of the property {@value #KEY_OUTPUT_FORMATS} which select all * formats that the library can provide. */ public final static String VALUE_ALL = "ALL"; /** Value of the property {@value #KEY_OUTPUT_FORMATS} which select a * VOTable format. The format can be parameterized with the VOTable version * and serialization. */ public final static String VALUE_VOTABLE = "votable"; /** Value of the property {@value #KEY_OUTPUT_FORMATS} which select a * VOTable format. The format can be parameterized with the VOTable version * and serialization. * This value is just an alias of {@link #VALUE_VOTABLE}. */ public final static String VALUE_VOT = "vot"; /** Value of the property {@value #KEY_OUTPUT_FORMATS} which select a FITS * format. */ public final static String VALUE_FITS = "fits"; /** Value of the property {@value #KEY_OUTPUT_FORMATS} which select a JSON * format. */ public final static String VALUE_JSON = "json"; /** Value of the property {@value #KEY_OUTPUT_FORMATS} which select an HTML * format. */ public final static String VALUE_HTML = "html"; /** Value of the property {@value #KEY_OUTPUT_FORMATS} which select a * human-readable table. */ public final static String VALUE_TEXT = "text"; /** Value of the property {@value #KEY_OUTPUT_FORMATS} which select a CSV * format. */ public final static String VALUE_CSV = "csv"; /** Value of the property {@value #KEY_OUTPUT_FORMATS} which select a TSV * format. */ public final static String VALUE_TSV = "tsv"; /** Value of the property {@value #KEY_OUTPUT_FORMATS} which select a * Separated-Value format. * This value must be parameterized with the separator to use. */ public final static String VALUE_SV = "sv"; /** Name/Key of the property specifying the number of result rows that * should be returned if none is specified by the user. */ public final static String KEY_DEFAULT_OUTPUT_LIMIT = "output_default_limit"; /** Name/Key of the property specifying the maximum number of result rows * that can be returned by the TAP service. */ public final static String KEY_MAX_OUTPUT_LIMIT = "output_max_limit"; /* USER IDENTIFICATION */ /** Name/Key of the property specifying the user identification method to * use. None is implemented by the library, so a class must be provided as * value of this property. */ public final static String KEY_USER_IDENTIFIER = "user_identifier"; public final static String KEY_QUERY_EXECUTOR = "query_executor"; /* ADQL RESTRICTIONS */ /** Name/Key of the property specifying the list of all allowed coordinate * systems that can be used in ADQL queries. By default, all are allowed, * but no conversion is done by the library. */ public final static String KEY_COORD_SYS = "coordinate_systems"; /** Name/Key of the property specifying the list of all ADQL geometrical * function that can be used in ADQL queries. By default, all are allowed. */ public final static String KEY_GEOMETRIES = "geometries"; /** Value of {@value #KEY_COORD_SYS} and {@value #KEY_GEOMETRIES} that * forbid all possible values. */ public final static String VALUE_NONE = "NONE"; /** Name/Key of the property that lets declare all User Defined Functions * that must be allowed in ADQL queries. By default, all unknown functions * are rejected. This default behavior can be totally reversed by using the * value {@link #VALUE_ANY} */ public final static String KEY_UDFS = "udfs"; /** Value of {@value #KEY_UDFS} allowing any unknown function in ADQL * queries. Those functions will be considered as UDFs and will be * translated into SQL exactly as they are written in ADQL. */ public final static String VALUE_ANY = "ANY"; /* ADDITIONAL TAP RESOURCES */ /** Name/Key of the property specifying the XSLT stylesheet to use for * /capabilities. */ public final static String KEY_CAPABILITIES_STYLESHEET = "capabilities_stylesheet"; /** Name/Key of the property specifying the XSLT stylesheet to use for * /tables. */ public final static String KEY_TABLES_STYLESHEET = "tables_stylesheet"; /** Name/Key of the property specifying a list of resources to add to the * TAP service (e.g. a ADQL query validator). By default, this list if * empty ; only the default TAP resources exist. */ public final static String KEY_ADD_TAP_RESOURCES = "additional_resources"; /* CUSTOM FACTORY */ /** Name/Key of the property specifying the {@link TAPFactory} class to use * instead of the default {@link ConfigurableTAPFactory}. * Setting a value to this property could disable several properties of * the TAP configuration file. */ public final static String KEY_TAP_FACTORY = "tap_factory"; /** No instance of this class should be created. */ private TAPConfiguration() { } /** * Read the asked property from the given Properties object. *true
if the given value is formatted as class name,
* false
otherwise.
*/
public final static boolean isClassName(final String value) {
return (value != null && value.length() > 2 && value.charAt(0) == '{' && value.charAt(value.length() - 1) == '}');
}
/**
* Fetch the class object corresponding to the class name provided between
* brackets in the given value.
*
* @param value Value which is supposed to contain the class name
* between brackets (see {@link #isClassName(String)}
* for more details)
* @param propertyName Name of the property associated with the parameter
* "value".
* @param expectedType Type of the class expected to be returned ; it is
* also the type which parameterizes this function: C.
*
* @return The corresponding Class object.
*
* @throws TAPException If the class name is incorrect
* or if its type is not compatible with the
* parameterized type C (represented by the parameter
* "expectedType").
*
* @see #isClassName(String)
*/
@SuppressWarnings("unchecked")
public final static true
if the specified class has a constructor with
* the specified parameters,
* false
otherwise.
*
* @throws TAPException If the class name is incorrect
* or if its type is not compatible with the
* parameterized type C (represented by the parameter
* "expectedType").
*
* @since 2.1
*/
public final static * The instance is created using the empty constructor of the specified * class. *
* * @param propValue Value which is supposed to contain the class name * between brackets (see {@link #isClassName(String)} * for more details) * @param propName Name of the property associated with the parameter * "propValue". * @param expectedType Type of the class expected to be returned ; it is * also the type which parameterizes this function: C. * * @return The corresponding instance. * * @throws TAPException If the class name is incorrect * or if its type is not compatible with the * parameterized type C (represented by the parameter * "expectedType") * or if the specified class has no empty constructor * or if an error occurred while calling this constructor. * * @see #isClassName(String) * @see #fetchClass(String, String, Class) */ public final staticIMPORTANT: * The instance is created using the constructor whose the declaration * matches exactly with the given list of parameter types. The number and * types of given parameters MUST match exactly to the list of parameter * types. *
* * @param propValue Value which is supposed to contain the class name * between brackets (see {@link #isClassName(String)} * for more details) * @param propName Name of the property associated with the parameter * "propValue". * @param expectedType Type of the class expected to be returned ; it is * also the type which parameterizes this function: C. * @param pTypes List of each constructor parameter type. Each type * MUST be exactly the type declared in the class * constructor to select. NULL or empty array if no * parameter. * @param parameters List of all constructor parameters. The number of * object MUST match exactly the number of classes * provided in the parameter pTypes. NULL or empty * array if no parameter. * * @return The corresponding instance. * * @throws TAPException If the class name is incorrect * or if its type is not compatible with the * parameterized type C (represented by the parameter * "expectedType") * or if the constructor with the specified parameters * can not be found * or if an error occurred while calling this * constructor. * * @see #isClassName(String) * @see #fetchClass(String, String, Class) */ public final static* Here is the expected syntax: num_val[unit]. * Where unit is optional and should be one of the following values: * r or R, B, kB, MB, GB. If the unit is not specified, it is set by * default to ROWS. *
* *Note: * If the value is strictly less than 0 (whatever is the unit), the * returned value will be -1. *
* * @param value Property value (must follow the limit syntax: * num_val[unit] ; ex: 20kB or 2000 * (for 2000 rows)). * @param propertyName Name of the property which specify the limit. * @param areBytesAllowed Tells whether the unit BYTES is allowed. If not * and a BYTES unit is encountered, then an * exception is thrown. * * @return An array with always 2 items: * [0]=numeric value (of type Integer), * [1]=unit (of type {@link LimitUnit}). * * @throws TAPException If the syntax is incorrect * or if a not allowed unit has been used. * * @see #parseLimit(String, String, boolean, boolean) */ public final static Object[] parseLimit(String value, final String propertyName, final boolean areBytesAllowed) throws TAPException { return parseLimit(value, propertyName, areBytesAllowed, false); } /** * Lets parsing a limit (for output, upload, ...) with its numeric value * and its unit. * ** Here is the expected syntax: num_val[unit]. * Where unit is optional and should be one of the following values: * r or R, B, kB, MB, GB. If the unit is not specified, it is set by * default to ROWS. *
* *Note: * If the value is strictly less than 0 (whatever is the unit), the * returned value will be -1. *
* * @param value Property value (must follow the limit syntax: * num_val[unit] ; ex: 20kB or 2000 * (for 2000 rows)). * @param propertyName Name of the property which specify the limit. * @param areBytesAllowed Tells whether the unit BYTES is allowed. If not * and a BYTES unit is encountered, then an * exception is thrown. * @param longValuetrue
to get the limit as a long,
* false
to get it as an int.
*
* @return An array with always 2 items:
* [0]=numeric value (of type Integer or Long in function of the
* parameter longValue),
* [1]=unit (of type {@link LimitUnit}).
*
* @throws TAPException If the syntax is incorrect
* or if a not allowed unit has been used.
*
* @since 2.3
*/
public final static Object[] parseLimit(String value, final String propertyName, final boolean areBytesAllowed, final boolean longValue) throws TAPException {
// Remove any whitespace inside or outside the numeric value and its unit:
if (value != null)
value = value.replaceAll("\\s", "");
// If empty value, return an infinite limit:
if (value == null || value.length() == 0)
return (longValue ? new Object[]{ new Long(-1L), LimitUnit.rows } : new Object[]{ new Integer(-1), LimitUnit.rows });
// A. Parse the string from the end in order to extract the unit part.
// The final step of the loop is the extraction of the numeric value, when the first digit is encountered.
long numValue = -1;
LimitUnit unit;
StringBuffer buf = new StringBuffer();
for(int i = value.length() - 1; i >= 0; i--) {
// if a digit, extract the numeric value:
if (value.charAt(i) >= '0' && value.charAt(i) <= '9') {
try {
numValue = Long.parseLong(value.substring(0, i + 1));
break;
} catch(NumberFormatException nfe) {
throw new TAPException("Integer expected for the property " + propertyName + " for the substring \"" + value.substring(0, i + 1) + "\" of the whole value: \"" + value + "\"!");
}
}
// if a character, store it for later processing:
else
buf.append(value.charAt(i));
}
// B. Parse the unit.
// if no unit, set ROWS by default:
if (buf.length() == 0)
unit = LimitUnit.rows;
// if the unit is too long, throw an exception:
else if (buf.length() > 2)
throw new TAPException("Unknown limit unit (" + buf.reverse().toString() + ") for the property " + propertyName + ": \"" + value + "\"!");
// try to identify the unit:
else {
// the base unit: bytes or rows
switch(buf.charAt(0)) {
case 'B':
if (!areBytesAllowed)
throw new TAPException("BYTES unit is not allowed for the property " + propertyName + " (" + value + ")!");
unit = LimitUnit.bytes;
break;
case 'r':
case 'R':
unit = LimitUnit.rows;
break;
default:
throw new TAPException("Unknown limit unit (" + buf.reverse().toString() + ") for the property " + propertyName + ": \"" + value + "\"!");
}
// the 10-power of the base unit, if any:
if (buf.length() > 1) {
if (unit == LimitUnit.bytes) {
switch(buf.charAt(1)) {
case 'k':
unit = LimitUnit.kilobytes;
break;
case 'M':
unit = LimitUnit.megabytes;
break;
case 'G':
unit = LimitUnit.gigabytes;
break;
default:
throw new TAPException("Unknown limit unit (" + buf.reverse().toString() + ") for the property " + propertyName + ": \"" + value + "\"!");
}
} else
throw new TAPException("Unknown limit unit (" + buf.reverse().toString() + ") for the property " + propertyName + ": \"" + value + "\"!");
}
}
if (numValue < 0)
return (longValue ? new Object[]{ new Long(-1L), unit } : new Object[]{ new Integer(-1), unit });
else
return (longValue ? new Object[]{ new Long(numValue), unit } : new Object[]{ new Integer((int)numValue), unit });
}
}